首页 / PyTorch 入门教程 / 常用网络层与容器

PyTorch 入门教程

常用网络层与容器

本教程共 60 篇 · 第 10 篇 · 更新于 2026-08-17 · 约 3 分钟阅读

PyTorchtorch.nnLinearConv2dSequentialModuleList网络层

本节目标:认识 LinearConv2d、池化、Dropout 等常用层,学会用 SequentialModuleList 快速拼网络。

全连接层:nn.Linear

nn.Linear(in_features, out_features) 只做一件事:对输入做线性变换,y = xWᵀ + b。输入几维、输出几维,构造时定死。

import torch
import torch.nn as nn

fc = nn.Linear(4, 3)
x = torch.randn(2, 4)   # 2 个样本,每个 4 维
print(fc(x).shape)      # torch.Size([2, 3])

注意输入只要求最后一维等于 4,前面有几维都行。形状 (2, 5, 4) 的张量照样能过,输出 (2, 5, 3)。它天生兼容批处理,这就是「全连接」的含义:每个输出节点和所有输入节点都相连。

输入维数写错是新手第一大类报错。好消息是报错信息会直接告诉你期望的形状,比如 mat1 and mat2 shapes cannot be multiplied,对着改就行。

卷积层:nn.Conv2d

图像任务几乎都从 nn.Conv2d 开始。最常用的四个参数:

  1. in_channels:输入通道数。彩色图是 3,灰度图是 1。
  2. out_channels:输出通道数,也就是这层学多少个卷积核(kernel)。
  3. kernel_size:卷积核大小,写 3 表示 3×3。
  4. stride:核每次滑动几步,默认 1。想快速缩小特征图就设 2。
conv = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3)
img = torch.randn(8, 3, 32, 32)   # 8 张 32×32 彩色图
print(conv(img).shape)            # torch.Size([8, 16, 30, 30])

输出变成了 30×30。卷积核扫过图像时会「吃掉」边缘,3×3 的核每边各少 1。想保持尺寸不变,加 padding=1 在外圈补零:

conv = nn.Conv2d(3, 16, 3, padding=1)
print(conv(torch.randn(8, 3, 32, 32)).shape)  # torch.Size([8, 16, 32, 32])

卷积到底怎么提取特征,第 20 章细讲。这里先记住「怎么用」:通道数、核大小、padding,三个参数来回调,输出尺寸变化用打印验证。

池化层与 Dropout

nn.MaxPool2d(2) 在每个 2×2 小区域里取最大值,宽高各减半。它顺手做了两件事:缩小数据量,让网络对位置变化不那么敏感。

nn.Dropout(p) 训练时以概率 p 随机把神经元的输出清零,逼网络别过度依赖某个神经元,是防过拟合的常用手段。概率一般取 0.2 到 0.5,太大网络学不动,太小没效果。

pool = nn.MaxPool2d(2)
x = torch.randn(4, 16, 32, 32)
print(pool(x).shape)   # torch.Size([4, 16, 16, 16])
Note

Dropout 只在训练时随机清零,评估时自动失效。切换状态靠 model.train()model.eval(),第 16 章训练循环会细说。

还有 nn.Flatten(),把多维特征图压平成二维,是卷积层和全连接层之间的「转换头」。它等价于 x.view(x.size(0), -1),但写进模型结构里更清晰。

池化家族还有一位重要成员:nn.AdaptiveAvgPool2d((1, 1)),自适应平均池化。它不关心输入多大,保证输出你指定的尺寸。用它可以把任意大小的特征图压成 1×1,后面接全连接层时再也不用手算 32 * 7 * 7 这种数。CNN 里它常用来替代「展平 + 手算尺寸」的组合。

输出层常用激活

隐藏层里的 nn.ReLU 你见过了。输出层要按任务挑激活:

  • 二分类:nn.Sigmoid(),输出压到 0 到 1,当概率看。
  • 多分类:nn.Softmax(dim=1),整行输出变成概率分布,加起来等于 1。
  • 回归:不加激活,直接输出数值。

nn.CrossEntropyLoss 时注意一个坑:这个损失函数内部已经包含 Softmax 了,模型输出层别再加 nn.Softmax,加了反而多算一遍,效果打折。这是新手很常见的「画蛇添足」错误。

容器:Sequential 与 ModuleList

网络层一多,逐行写 forward 就啰嗦。nn.Sequential 把层按顺序装起来,数据自动一层层穿过:

model = nn.Sequential(
    nn.Linear(10, 32),
    nn.ReLU(),
    nn.Linear(32, 2),
)
print(model(torch.randn(5, 10)).shape)  # torch.Size([5, 2])

注意 Sequential 里的顺序就是执行顺序。它适合「一条直线」的网络:输入进第一层,输出给第二层,不绕弯。需要跳转结构(比如残差连接)时,就得回到继承 nn.Module 自己写 forward。Sequential 只是省键盘,不是万能。

nn.ModuleList 是另一种容器:像 Python 列表一样装子模块,但它不会自动依次执行,得在 forward 里自己调用:

class Net(nn.Module):
    def __init__(self):
        super().__init__()
        self.layers = nn.ModuleList([nn.Linear(10, 10) for _ in range(3)])

    def forward(self, x):
        for layer in self.layers:
            x = torch.relu(layer(x))
        return x

ModuleList 适合层数不固定、要用循环或条件语句控制的场景。它的兄弟 nn.ModuleDict 用字典存子模块,适合按名字取用。

Warning

别用普通 Python 列表 self.layers = [nn.Linear(...)] 装层!普通列表里的层不会被底座登记,参数会「消失」——loss 不降、parameters() 里找不到。这就是 ModuleList 存在的理由。

拼一个 CNN

把上面这些层拼起来,就是一个经典的小卷积网络:

class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(1, 16, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),           # 28×28 → 14×14
            nn.Conv2d(16, 32, 3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2),           # 14×14 → 7×7
        )
        self.classifier = nn.Sequential(
            nn.Flatten(),
            nn.Linear(32 * 7 * 7, 128),
            nn.ReLU(),
            nn.Dropout(0.5),
            nn.Linear(128, 10),
        )

    def forward(self, x):
        x = self.features(x)
        x = self.classifier(x)
        return x

model = SimpleCNN()
print(model(torch.randn(2, 1, 28, 28)).shape)  # torch.Size([2, 10])

结构分两段:features 管特征提取,classifier 管分类。中间那个 32 * 7 * 7 是两次池化后的特征图大小,这个数算错就会报形状不匹配。第 33 章讲调试时,这会是常见案例。

顺便说一句:torchvision.models 里那些预训练模型(resnet18、mobilenet 之类),本质上都是 nn.Module 的子类。你现在会看它们的结构了,以后用起来就不会陌生。

小结

  • nn.Linear 全连接、nn.Conv2d 卷积、nn.MaxPool2d 池化、nn.Dropout 防过拟合,nn.Flatten 负责压平,nn.AdaptiveAvgPool2d 输出尺寸免手算。
  • 输出层激活按任务选:二分类 Sigmoid,多分类 Softmax,回归不加。
  • nn.Sequential 适合直线网络;要跳转就用继承 + 自己写 forward。
  • 层放进普通列表会丢参数,务必用 nn.ModuleList

下一章进入数据环节:模型要吃数据,数据从哪来、怎么喂,交给 Dataset 和 DataLoader。