使用 PyTorch 构建模型
torch.nn.Module 与 torch.nn.Parameter
在本视频中,我们将探讨 PyTorch 为构建深度学习网络提供的部分工具。
除 Parameter 外,我们讨论的所有类均是 torch.nn.Module 的子类。这是 PyTorch 的基类,用于封装 PyTorch 模型及其组件的特有行为。
torch.nn.Module 的一个核心行为是参数注册:如果某个 Module 子类包含可学习权重,这些权重会被表示为 torch.nn.Parameter 实例。Parameter 类是 torch.Tensor 的子类,其特殊之处在于:当它被赋值为 Module 的属性时,会自动加入该模块的参数列表,可通过 Module 类的 parameters() 方法访问这些参数。
以下是一个简单示例:包含两个线性层和一个激活函数的极简模型。我们将创建模型实例并查看其参数:
import torch
class TinyModel(torch.nn.Module):
def __init__(self):
super(TinyModel, self).__init__()
self.linear1 = torch.nn.Linear(100, 200)
self.activation = torch.nn.ReLU()
self.linear2 = torch.nn.Linear(200, 10)
self.softmax = torch.nn.Softmax()
def forward(self, x):
x = self.linear1(x)
x = self.activation(x)
x = self.linear2(x)
x = self.softmax(x)
return x
tinymodel = TinyModel()
print('The model:')
print(tinymodel)
print('\n\nJust one layer:')
print(tinymodel.linear2)
print('\n\nModel params:')
for param in tinymodel.parameters():
print(param)
print('\n\nLayer params:')
for param in tinymodel.linear2.parameters():
print(param)
输出结果:
The model:
TinyModel(
(linear1): Linear(in_features=100, out_features=200, bias=True)
(activation): ReLU()
(linear2): Linear(in_features=200, out_features=10, bias=True)
(softmax): Softmax(dim=None)
)
Just one layer:
Linear(in_features=200, out_features=10, bias=True)
Model params:
Parameter containing:
tensor([[-0.0325, 0.0925, 0.0762, ..., -0.0461, -0.0054, -0.0105],
[-0.0427, -0.0640, 0.0435, ..., 0.0433, 0.0916, -0.0133],
[-0.0473, -0.0265, -0.0956, ..., 0.0372, 0.0948, -0.0595],
...,
[-0.0199, -0.0486, -0.0992, ..., 0.0489, 0.0565, 0.0477],
[-0.0683, -0.0132, 0.0483, ..., -0.0446, 0.0588, -0.0445],
[ 0.0228, 0.0845, 0.0379, ..., -0.0771, -0.0376, -0.0193]],
requires_grad=True)
Parameter containing:
tensor([ 0.0777, 0.0467, 0.0190, -0.0037, 0.0860, -0.0509, -0.0807, 0.0310,
-0.0194, -0.0489, -0.0647, 0.0686, 0.0410, -0.0566, -0.0538, -0.0696,
-0.0907, 0.0282, 0.0475, 0.0838, 0.0895, 0.0517, 0.0170, -0.0803,
0.0415, -0.0524, -0.0582, -0.0762, -0.0583, -0.0032, -0.0860, -0.0393,
-0.0134, -0.0790, 0.0419, 0.0626, 0.0341, 0.0739, 0.0603, 0.0698,
-0.0665, 0.0251, 0.0908, -0.0642, 0.0536, -0.0369, -0.0818, 0.0255,
0.0792, -0.0415, -0.0886, 0.0825, -0.0054, 0.0737, 0.0421, -0.0795,
0.0038, -0.0563, 0.0573, 0.0677, -0.0697, 0.0237, 0.0594, 0.0543,
0.0835, 0.0051, -0.0188, 0.0354, 0.0106, -0.0148, 0.0009, -0.0281,
-0.0766, 0.0983, -0.0359, -0.0079, -0.0010, -0.0358, -0.0609, 0.0635,
0.0991, -0.0616, 0.0189, 0.0392, -0.0892, -0.0068, 0.0909, -0.0107,
-0.0554, 0.0348, -0.0978, 0.0040, 0.0684, -0.0270, -0.0904, 0.0211,
0.0345, 0.0983, -0.0087, -0.0335, -0.0231, -0.0837, 0.0347, -0.0970,
0.0488, -0.0597, -0.0401, -0.0139, -0.0641, -0.0790, 0.0482, -0.0706,
-0.0793, -0.0033, -0.0973, 0.0670, -0.0678, 0.0403, 0.0707, 0.0683,
0.0053, -0.0666, 0.0535, -0.0662, 0.0303, -0.0118, 0.0726, -0.0831,
0.0549, -0.0277, 0.0696, -0.0032, 0.0572, 0.0043, -0.0362, -0.0566,
-0.0827, -0.0393, 0.0035, 0.0370, -0.0402, -0.0677, 0.0745, -0.0344,
0.0992, -0.0715, -0.0954, 0.0048, -0.0418, 0.0039, 0.0306, 0.0870,
-0.0736, -0.0577, -0.0796, 0.0211, 0.0168, 0.0533, 0.0133, -0.0145,
0.0186, -0.0635, 0.0111, 0.0384, 0.0234, -0.0463, -0.0422, 0.0619,
0.0507, 0.0908, 0.0912, -0.0227, 0.0442, -0.0536, -0.0587, 0.0065,
0.0539, -0.0354, -0.0954, 0.0985, 0.0645, 0.0718, -0.0706, 0.0139,
-0.0793, -0.0998, -0.0227, 0.0739, -0.0217, -0.0213, -0.0416, -0.0943,
-0.0012, 0.0423, 0.0323, -0.0839, -0.0244, -0.0991, 0.0973, 0.0134],
requires_grad=True)
Parameter containing:
tensor([[-0.0378, 0.0120, -0.0634, ..., -0.0130, -0.0370, 0.0552],
[-0.0607, -0.0181, 0.0372, ..., -0.0564, 0.0311, 0.0431],
[-0.0271, -0.0468, -0.0190, ..., -0.0386, -0.0475, 0.0432],
...,
[ 0.0580, 0.0439, -0.0165, ..., 0.0058, -0.0386, -0.0543],
[ 0.0665, 0.0706, -0.0084, ..., -0.0480, -0.0147, -0.0007],
[-0.0245, 0.0570, 0.0129, ..., 0.0079, 0.0124, -0.0635]],
requires_grad=True)
Parameter containing:
tensor([-0.0126, 0.0066, -0.0381, -0.0654, 0.0264, -0.0689, -0.0204, -0.0653,
-0.0593, 0.0483], requires_grad=True)
Layer params:
Parameter containing:
tensor([[-0.0378, 0.0120, -0.0634, ..., -0.0130, -0.0370, 0.0552],
[-0.0607, -0.0181, 0.0372, ..., -0.0564, 0.0311, 0.0431],
[-0.0271, -0.0468, -0.0190, ..., -0.0386, -0.0475, 0.0432],
...,
[ 0.0580, 0.0439, -0.0165, ..., 0.0058, -0.0386, -0.0543],
[ 0.0665, 0.0706, -0.0084, ..., -0.0480, -0.0147, -0.0007],
[-0.0245, 0.0570, 0.0129, ..., 0.0079, 0.0124, -0.0635]],
requires_grad=True)
Parameter containing:
tensor([-0.0126, 0.0066, -0.0381, -0.0654, 0.0264, -0.0689, -0.0204, -0.0653,
-0.0593, 0.0483], requires_grad=True)
这展示了 PyTorch 模型的核心结构:
__init__()方法:定义模型的层和其他组件;forward()方法:实现模型的计算逻辑。
注:可通过打印模型/子模块查看其结构。
常见层类型
线性层(Linear Layers)
线性层(也称为全连接层)是最基础的神经网络层:每个输入都会通过层权重影响所有输出。若模型有 m 个输入和 n 个输出,权重矩阵的维度为 m×n。示例:
lin = torch.nn.Linear(3, 2)
x = torch.rand(1, 3)
print('Input:')
print(x)
print('\n\nWeight and Bias parameters:')
for param in lin.parameters():
print(param)
y = lin(x)
print('\n\nOutput:')
print(y)
输出结果:
Input:
tensor([[0.8703, 0.5852, 0.7673]])
Weight and Bias parameters:
Parameter containing:
tensor([[-0.1071, 0.0400, -0.2025],
[ 0.5296, 0.4683, 0.1641]], requires_grad=True)
Parameter containing:
tensor([-0.0167, -0.3568], requires_grad=True)
Output:
tensor([[-0.2420, 0.5041]], grad_fn=<AddmmBackward0>)
将输入 x 与线性层权重做矩阵乘法并加上偏置,结果与输出 y 完全一致。
关键特性:
lin.weight是Parameter(Tensor 子类),默认开启 Autograd 梯度追踪(这是 Parameter 与普通 Tensor 的核心区别)。
线性层广泛应用于深度学习模型,尤其在分类器中——分类器的最后通常会有一个/多个线性层,最后一层的输出维度等于分类任务的类别数。
卷积层(Convolutional Layers)
卷积层专为处理具有强空间相关性的数据设计,广泛应用于计算机视觉(检测局部特征并组合为高层特征),也用于 NLP(捕捉单词的上下文语义)。
以下是早期视频中 LeNet5 模型的卷积层实现:
import torch.nn.functional as F
class LeNet(torch.nn.Module):
def __init__(self):
super(LeNet, self).__init__()
# 1个输入图像通道(黑白),6个输出通道,5x5卷积核
self.conv1 = torch.nn.Conv2d(1, 6, 5)
self.conv2 = torch.nn.Conv2d(6, 16, 3)
# 仿射变换:y = Wx + b
self.fc1 = torch.nn.Linear(16 * 6 * 6, 120) # 6*6 来自图像维度
self.fc2 = torch.nn.Linear(120, 84)
self.fc3 = torch.nn.Linear(84, 10)
def forward(self, x):
# 2x2 窗口的最大池化
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
# 若池化窗口为正方形,可仅指定单个数值
x = F.max_pool2d(F.relu(self.conv2(x)), 2)
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # 排除批次维度的所有维度
num_features = 1
for s in size:
num_features *= s
return num_features
卷积层解析(以 conv1 为例)
- 输入通道:第一个参数为输入通道数。LeNet5 处理 1×32×32 的黑白图像,故输入通道为 1;若处理RGB图像,该值为 3。
- 输出通道:第二个参数为输出通道数(即卷积层要学习的特征数),此处为 6。
- 卷积核尺寸:第三个参数为卷积核(窗口)尺寸,此处的 5 表示 5×5 卷积核;若需非正方形核,可传入元组(如 (3,5) 表示 3×5 核)。
卷积层的输出是激活图(输入张量中特征分布的空间表示):
- conv1 输出维度为 6×28×28(6个特征,28×28的空间维度;28 来自 32-5+1);
- 经 ReLU 激活和 2×2 最大池化后,维度变为 6×14×14(池化将 2×2 区域合并为1个值,取最大值);
- conv2 接收 6 个输入通道,输出 16 个通道,3×3 核,输出 16×12×12;经池化后变为 16×6×6,展平为 576 维向量后传入线性层。
扩展:PyTorch 提供 1D/2D/3D 卷积层,还支持步长(stride)、填充(padding)等可选参数,详见官方文档。
循环层(Recurrent Layers)
循环神经网络(RNN)用于处理序列数据(如时间序列、自然语言、DNA序列),通过维护隐藏状态(类似“记忆”)记录序列上下文信息。
RNN 及其变体(LSTM/GRU)的内部结构较复杂,以下是基于 LSTM 的词性标注器示例(分类单词的词性:名词/动词等):
class LSTMTagger(torch.nn.Module):
def __init__(self, embedding_dim, hidden_dim, vocab_size, tagset_size):
super(LSTMTagger, self).__init__()
self.hidden_dim = hidden_dim
self.word_embeddings = torch.nn.Embedding(vocab_size, embedding_dim)
# LSTM 接收词嵌入作为输入,输出维度为 hidden_dim 的隐藏状态
self.lstm = torch.nn.LSTM(embedding_dim, hidden_dim)
# 将隐藏状态映射到标签空间的线性层
self.hidden2tag = torch.nn.Linear(hidden_dim, tagset_size)
def forward(self, sentence):
embeds = self.word_embeddings(sentence)
lstm_out, _ = self.lstm(embeds.view(len(sentence), 1, -1))
tag_space = self.hidden2tag(lstm_out.view(len(sentence), -1))
tag_scores = F.log_softmax(tag_space, dim=1)
return tag_scores
构造函数参数说明
vocab_size:输入词汇表大小(每个单词对应 vocab_size 维的独热向量);tagset_size:输出标签集大小(词性类别数);embedding_dim:词嵌入空间维度(将高维独热向量映射到低维空间,语义相近的词在空间中距离更近);hidden_dim:LSTM 隐藏状态(记忆)的维度。
前向传播逻辑
- 输入为单词的独热向量索引,经嵌入层映射到 embedding_dim 维空间;
- LSTM 迭代处理嵌入序列,输出 hidden_dim 维的隐藏状态;
- 线性层将隐藏状态映射到标签空间,
log_softmax将输出归一化为概率分布(表示单词对应各标签的概率)。
扩展:可参考 PyTorch 官方教程《Sequence Models and LSTM Networks》查看完整运行示例。
Transformer
Transformer 是通用型网络,凭借 BERT 等模型成为 NLP 领域的SOTA(最优性能)方案。其架构超出本视频范围,但 PyTorch 提供 Transformer 类,可定义注意力头数、编码器/解码器层数、dropout、激活函数等参数(甚至可通过该类构建 BERT 模型)。
torch.nn.Transformer 还包含细分组件类:
TransformerEncoder/TransformerDecoder(编码器/解码器);TransformerEncoderLayer/TransformerDecoderLayer(编码层/解码层)。
详见 Transformer 官方文档。
其他层与函数
数据操作层
这类层不参与学习,但对模型至关重要:
1. 池化层(Pooling Layers)
最大池化/最小池化通过合并张量元素实现降维,取区域内最大值/最小值作为输出:
my_tensor = torch.rand(1, 6, 6)
print(my_tensor)
maxpool_layer = torch.nn.MaxPool2d(3)
print(maxpool_layer(my_tensor))
输出结果:
tensor([[[0.8055, 0.4216, 0.3058, 0.9001, 0.7154, 0.0885],
[0.1511, 0.7800, 0.3969, 0.8882, 0.7142, 0.0940],
[0.9166, 0.3576, 0.9560, 0.7981, 0.2106, 0.0578],
[0.3761, 0.0980, 0.5340, 0.3139, 0.0251, 0.7075],
[0.0139, 0.4401, 0.7147, 0.5136, 0.4683, 0.6043],
[0.1791, 0.6990, 0.7465, 0.8742, 0.1573, 0.6789]]])
tensor([[[0.9560, 0.9001],
[0.7465, 0.8742]]])
输出的每个值对应输入 6×6 张量中 3×3 区域的最大值。
2. 归一化层(Normalization Layers)
对层输出做中心化和归一化,可避免梯度消失/爆炸,支持更高的学习率:
my_tensor = torch.rand(1, 4, 4) * 20 + 5
print(my_tensor)
print(my_tensor.mean())
norm_layer = torch.nn.BatchNorm1d(4)
normed_tensor = norm_layer(my_tensor)
print(normed_tensor)
print(normed_tensor.mean())
输出结果:
tensor([[[24.5083, 21.6152, 20.5365, 16.9337],
[19.2096, 24.8286, 8.5177, 13.6296],
[22.0705, 13.4739, 13.3103, 9.9620],
[ 5.4103, 14.9501, 6.9635, 21.0814]]])
tensor(16.0626)
tensor([[[ 1.3316, 0.2644, -0.1335, -1.4625],
[ 0.4369, 1.3586, -1.3170, -0.4785],
[ 1.6450, -0.2748, -0.3113, -1.0590],
[-1.0582, 0.4505, -0.8126, 1.4202]]],
grad_fn=<NativeBatchNormBackward0>)
tensor(1.1921e-07, grad_fn=<MeanBackward0>)
归一化后张量均值接近 0,数值分布更集中,激活函数能在梯度最陡的区域工作,提升学习效率。
3. Dropout 层
训练时随机屏蔽部分输入(推理时关闭),迫使模型学习稀疏表示,提升泛化能力:
my_tensor = torch.rand(1, 4, 4)
dropout = torch.nn.Dropout(p=0.4)
print(dropout(my_tensor))
print(dropout(my_tensor))
输出结果:
tensor([[[1.0502, 0.7134, 0.0000, 1.3411],
[0.0000, 0.0923, 0.7351, 1.3475],
[1.5547, 0.0000, 0.7461, 0.0676],
[0.4339, 1.2999, 1.5323, 0.0000]]])
tensor([[[0.0000, 0.7134, 0.0000, 1.3411],
[0.0580, 0.0923, 0.7351, 1.3475],
[1.5547, 0.1532, 0.7461, 0.0000],
[0.4339, 0.0000, 0.0000, 0.0000]]])
p参数:单个权重被屏蔽的概率(默认 0.5);- 两次输出的屏蔽位置不同(随机特性)。
激活函数
激活函数是深度学习的核心:若仅做线性变换,多层网络等价于单层矩阵乘法,无法拟合复杂函数。非线性激活函数使模型能逼近任意函数。
torch.nn.Module 封装了主流激活函数:
- ReLU 及其变体(LeakyReLU、PReLU 等);
- Tanh、Hardtanh、Sigmoid;
- Softmax(常用于模型输出层)。
损失函数
损失函数衡量模型预测与真实标签的差距。PyTorch 提供多种损失函数:
- MSE(均方误差,L2 范数);
- 交叉熵损失(Cross Entropy Loss)、负对数似然损失(Negative Likelihood Loss)——适用于分类任务;
- 其他自定义损失函数。
脚本总运行时间:0分0.027秒
总结
- 核心基类:
torch.nn.Module是所有模型/层的基类,Parameter自动注册为模型可学习参数; - 基础层类型:
- 线性层:全连接,适用于分类器输出层;
- 卷积层:处理空间相关数据(图像/NLP);
- 循环层:处理序列数据(LSTM/GRU);
- Transformer:NLP 领域 SOTA 方案;
- 辅助层:池化(降维)、归一化(稳定训练)、Dropout(防止过拟合);
- 关键组件:激活函数(引入非线性)、损失函数(衡量预测误差)。