pytorch api文档:nn.Linear类(线性层/全连接层,y=wx+b)

介绍

nn.Linear 类(线性层/全连接层),是深度学习中最基础也最核心的层,用于实现线性变换 y = x * W^T + b,广泛应用于分类、回归、特征映射等场景。

作用

是 PyTorch 封装的线性层,核心作用是对输入张量执行线性变换:

y = x \cdot W^T + b

  • x:输入张量(形状 [..., in_features],... 代表批量/序列等任意前置维度);
  • W:权重矩阵(形状 [out_features, in_features],可训练参数);
  • b:偏置向量(形状 [out_features],可训练参数,可选);
  • y:输出张量(形状 [..., out_features])。

关键特征

  • 输入要求:最后一维必须等于 in_features(线性层的输入特征数);
  • 输出特征:最后一维变为 out_features(线性层的输出特征数);
  • 参数自动初始化:权重默认用 Kaiming 均匀分布初始化,偏置默认初始化为 0。

语法

nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)

参数

参数 作用 注意事项
in_features 输入特征数(输入张量最后一维的维度) 必传,需与输入数据的最后一维匹配
out_features 输出特征数(输出张量最后一维的维度) 必传,决定线性层的输出维度
bias 是否启用偏置(默认 True) 设为 False 时,线性变换为 $y = xW^T$,无偏置项 b
device 指定层参数的存储设备(cpu/cuda) 可省略,后续用 model.to(device) 统一设置更方便
dtype 指定参数的数据类型(float32/float64) 可省略,默认与输入数据类型一致

本质

PyTorch 中 nn.Linear 层的底层计算逻辑是线性变换 y = kx + b(矩阵形式为 y = xW + b)

参数解释:

  • x:输入张量(形状为 [batch_size, in_features]);
  • W:权重矩阵(nn.Linear 的 weight 参数,形状为 [out_features, in_features]);
  • b:偏置向量(nn.Linear 的 bias 参数,形状为 [out_features]);
  • y:输出张量(形状为 [batch_size, out_features])。

提示:标量的 y = kx + b 是一维特例,nn.Linear 是高维通用形式,计算时会做矩阵乘法 + 向量加法(广播)。


nn.Linear 的参数维度

以 nn.Linear(in_features=2, out_features=3) 为例:

  • in_features=2:输入是2维向量;
  • out_features=3:输出是3维向量;
  • 权重 weight:形状为 [3, 2](输出维度 × 输入维度);
  • 偏置 bias:形状为 [3](输出维度);
  • 计算逻辑:y = x @ W.T + b(@ 是矩阵乘法,W.T 是权重矩阵的转置,PyTorch 内部会自动处理转置,无需手动操作)。

代码验证

手动复现 nn.Linear 的计算

步骤1:定义 nn.Linear 层并固定参数

为了结果可复现,我们手动设置 weight 和 bias 的值,避免随机初始化:

import torch
import torch.nn as nn

# 1. 定义Linear层:输入2维,输出3维
linear_layer = nn.Linear(in_features=2, out_features=3)

# 2. 手动固定weight和bias(方便验证)
# weight形状:[3, 2],对应3个输出维度×2个输入维度
linear_layer.weight = nn.Parameter(torch.tensor([
    [1.0, 2.0],   # 第1个输出维度的权重
    [3.0, 4.0],   # 第2个输出维度的权重
    [5.0, 6.0]    # 第3个输出维度的权重
]))
# bias形状:[3],对应3个输出维度的偏置
linear_layer.bias = nn.Parameter(torch.tensor([0.1, 0.2, 0.3]))

# 3. 定义输入x:batch_size=1,输入2维(简化为单个样本)
x = torch.tensor([[10.0, 20.0]])  # 形状:[1, 2]
步骤2:用 nn.Linear 计算输出
# Linear层的输出
y_linear = linear_layer(x)
print("nn.Linear 输出:", y_linear)
# 输出:tensor([[ 50.1000, 110.2000, 170.3000]], grad_fn=<AddmmBackward0>)

计算过程:

                  [[1.0, 2.0],
[[10.0, 20.0]]  *  [3.0, 4.0],    +  [0.1, 0.2, 0.3]
                   [5.0, 6.0]]

执行结果:

10.0 * 1.0 + 20.0 * 2.0 + 0.1 = 50.1
10.0 * 3.0 + 20.0 * 4.0 + 0.2 = 101.2
10.0 * 5.0 + 20.0 * 6.0 + 0.3 = 170.3
步骤3:手动计算 xW + b 验证
# 手动提取weight和bias
W = linear_layer.weight  # 形状[3, 2]
b = linear_layer.bias    # 形状[3]

# 手动计算:x @ W.T + b(W.T 转置为[2,3],才能和x[1,2]做矩阵乘法)
y_manual = x @ W.T + b
print("手动计算 xW + b 输出:", y_manual)
# 输出:tensor([[ 50.1000, 110.2000, 170.3000]], grad_fn=<AddBackward0>)
步骤4:对比结果
# 验证两者是否完全相等(忽略梯度信息,只看数值)
is_equal = torch.allclose(y_linear, y_manual)
print("两者结果是否一致:", is_equal)  # 输出:True

例子

先通过极简示例掌握 nn.Linear 的核心用法:

import torch
import torch.nn as nn

# 步骤1:定义线性层(输入特征数=5,输出特征数=3,启用偏置)
linear = nn.Linear(in_features=5, out_features=3, bias=True)

# 步骤2:准备输入(最后一维必须=in_features=5,前置维度任意)
# 示例1:单样本(shape [5])
x1 = torch.randn(5)  # 1个样本,5维特征
y1 = linear(x1)
print("单样本输出形状:", y1.shape)  # torch.Size([3])

# 示例2:批量样本(shape [4,5] → 4个样本,5维特征)
x2 = torch.randn(4, 5)
y2 = linear(x2)
print("批量样本输出形状:", y2.shape)  # torch.Size([4, 3])

# 示例3:高维输入(shape [2,4,5] → 2个序列,4个样本,5维特征)
x3 = torch.randn(2, 4, 5)
y3 = linear(x3)
print("高维输入输出形状:", y3.shape)  # torch.Size([2, 4, 3])(仅最后一维变换)

参数示例(禁用偏置/指定设备)

# 示例1:禁用偏置的线性层
linear_no_bias = nn.Linear(5, 3, bias=False)
print("是否有偏置参数:", hasattr(linear_no_bias, 'bias'))  # False

# 示例2:指定设备(需CUDA环境)
if torch.cuda.is_available():
    linear_cuda = nn.Linear(5, 3, device="cuda")
    print("权重设备:", linear_cuda.weight.device)  # cuda:0
    print("偏置设备:", linear_cuda.bias.device)    # cuda:0
else:
    print("无CUDA环境,参数默认在CPU")

核心特性:可训练参数

nn.Linear 的权重(weight)和偏置(bias)是 nn.Parameter 类型的可训练参数,会自动注册到模型参数列表中:

# 查看线性层的可训练参数
print("=== 线性层参数 ===")
# 权重:shape [out_features, in_features] = [3,5]
print("权重形状:", linear.weight.shape)
print("权重初始化值:\n", linear.weight)

# 偏置:shape [out_features] = [3]
print("\n偏置形状:", linear.bias.shape)
print("偏置初始化值:", linear.bias)  # 默认为0

# 遍历参数(模型训练时优化器会更新这些参数)
for name, param in linear.named_parameters():
    print(f"\n参数名:{name},形状:{param.shape},是否可求梯度:{param.requires_grad}")
# 输出:
# 参数名:weight,形状:torch.Size([3, 5]),是否可求梯度:True
# 参数名:bias,形状:torch.Size([3]),是否可求梯度:True

实战场景:集成到自定义模型

nn.Linear 几乎是所有自定义模型的基础组件,以下是分类任务的典型用法:

# 示例:自定义简单分类模型(输入28×28=784维,输出10类)
class SimpleClassifier(nn.Module):
    def __init__(self):
        super().__init__()
        # 线性层1:784 → 128(隐藏层)
        self.fc1 = nn.Linear(784, 128)
        # 线性层2:128 → 64(隐藏层)
        self.fc2 = nn.Linear(128, 64)
        # 线性层3:64 → 10(输出层,对应10个类别)
        self.fc3 = nn.Linear(64, 10)
        # 激活函数(线性层后需加非线性激活,否则多层等价于单层)
        self.relu = nn.ReLU()

    def forward(self, x):
        # 输入:[batch, 28, 28] → 展平为 [batch, 784]
        x = x.flatten(1)  # 从第1维开始展平(保留batch维度)
        # 前向传播:线性层 + 激活函数
        x = self.relu(self.fc1(x))
        x = self.relu(self.fc2(x))
        x = self.fc3(x)  # 输出层不激活(交给CrossEntropyLoss处理)
        return x

# 测试模型
model = SimpleClassifier()
# 模拟输入:4个样本,28×28像素
x = torch.randn(4, 28, 28)
output = model(x)
print("模型输出形状:", output.shape)  # torch.Size([4, 10])(4个样本,10个类别logits)

# 查看模型所有线性层参数
print("\n=== 模型所有参数 ===")
for name, param in model.named_parameters():
    print(f"{name}: {param.shape}")

常见易错点与技巧

(1)输入维度不匹配(最常见错误)

# 错误示例:输入最后一维≠in_features
linear = nn.Linear(5, 3)
x_bad = torch.randn(4, 4)  # 最后一维=4≠5
# y_bad = linear(x_bad)  # 报错:size mismatch, m1: [4 x 4], m2: [5 x 3]

# 解决:确保输入最后一维等于in_features
x_good = torch.randn(4, 5)
y_good = linear(x_good)  # 正常运行

(2)手动初始化参数

默认初始化可能不满足需求,可手动重置权重/偏置:

# 手动初始化权重(正态分布)
nn.init.normal_(linear.weight, mean=0.0, std=0.01)
# 手动初始化偏置(常数1)
nn.init.constant_(linear.bias, 1.0)

print("手动初始化后权重:\n", linear.weight)
print("手动初始化后偏置:", linear.bias)

(3)冻结线性层参数(不更新)

训练时若想冻结某层参数,设置 requires_grad=False:

# 冻结fc1层的权重(仅更新其他层)
for param in model.fc1.parameters():
    param.requires_grad = False

# 优化器仅更新未冻结的参数
optimizer = torch.optim.SGD(
    [p for p in model.parameters() if p.requires_grad],
    lr=0.1
)

(4)线性层的“维度变换”特性

nn.Linear 仅关注最后一维,因此可处理任意前置维度的输入(如序列、图像展平后的数据):

# 图像数据:[batch, channel, H, W] → 展平为 [batch, C×H×W]
img = torch.randn(4, 1, 28, 28)  # 4张灰度图,28×28
img_flat = img.flatten(1)  # [4, 784]
linear_img = nn.Linear(784, 10)
output_img = linear_img(img_flat)
print("图像输入输出形状:", output_img.shape)  # [4, 10]

总结

  1. nn.Linear(in_features, out_features) 实现线性变换 $y = xW^T + b$,核心是将输入最后一维从 in_features 映射到 out_features。
  2. 关键参数:in_features 匹配输入最后一维,out_features 定义输出维度,bias 控制是否启用偏置。
  3. 核心特性:权重/偏置是可训练的 nn.Parameter,自动注册到模型参数列表,支持梯度更新。
  4. 实战技巧:线性层后需加非线性激活(如ReLU);输入需展平为二维(batch, features);可手动初始化参数或冻结部分层。

原文出处:/show_1GW2cIbomFfg.html