📘 动手学深度学习 - 5.2 多层感知器的实现
5.2 多层感知器的实现(MLP)
在前面我们学习了多层感知器(MLP)的概念,
本节将正式动手实践,亲自从零开始实现 MLP,并对比使用高级API的简洁实现。
你将掌握:
-
如何初始化MLP的参数
-
如何编写前向传播过程
-
如何使用内置API快速搭建MLP
5.2.1 从头开始实现
5.2.1.1 初始化模型参数
在Fashion-MNIST分类任务中:
-
输入是28x28的灰度图像,展开成784维向量。
-
输出是10个类别。
我们设计一个简单的MLP:
-
一个隐藏层,256个隐藏单元。
为此,我们需要:
-
输入层到隐藏层的权重矩阵
W1
和偏置b1
-
隐藏层到输出层的权重矩阵
W2
和偏置b2
所有权重随机初始化,小的正态分布;偏置初始化为0。
self.W1 = nn.Parameter(torch.randn(num_inputs, num_hiddens) * sigma)
self.b1 = nn.Parameter(torch.zeros(num_hiddens))
self.W2 = nn.Parameter(torch.randn(num_hiddens, num_outputs) * sigma)
self.b2 = nn.Parameter(torch.zeros(num_outputs))
理论理解
-
Fashion-MNIST输入为28×28图像,展开为784维向量。
-
MLP需要管理两组权重和偏置:
-
输入层到隐藏层(784→256)
-
隐藏层到输出层(256→10)
-
-
初始化权重时通常乘以一个小的标准差(如0.01),防止初期数值过大。
-
参数数量增长迅速,因此合理初始化非常重要,避免梯度爆炸或消失。
企业实战理解
-
Google Research在训练大规模Vision Transformer时,初始化策略对收敛速度和稳定性有极大影响,因此采用特制的初始化(如正态分布缩放)。
-
字节跳动广告系统中,初期阶段采用更保守的小幅随机初始化,避免CTR预估模型在冷启动阶段剧烈波动。
-
NVIDIA在大规模GAN训练中,对生成器和判别器分别使用不同初始化策略(如He初始化与Xavier初始化),确保稳定对抗训练。
5.2.1.2 模型
我们自己手写一个简单版的ReLU激活函数:
def relu(X):
a = torch.zeros_like(X)
return torch.max(X, a)
前向传播流程:
-
输入
X
reshape成(batch_size, 784); -
计算隐藏层激活值
H = relu(X @ W1 + b1)
; -
输出层计算
Output = H @ W2 + b2
。
整个前向传播过程非常简洁直观。
理论理解
-
自定义ReLU实现,体现激活函数将线性组合打破为非线性映射的本质。
-
前向传播流程:
-
输入→隐藏层仿射变换+ReLU激活→输出层仿射变换
-
-
明确每一步的数据流动和维度变化是理解深度学习网络搭建的基础。
企业实战理解
-
OpenAI GPT模型中的Transformer Block前向流程同样遵循“仿射变换→激活→仿射变换”的基本模式(虽然中间用的是GELU)。
-
NVIDIA自动驾驶感知系统中,手动重写前向函数有助于细粒度控制特征提取、加速优化推理效率。
-
字节跳动内容推荐系统中,曾手动优化前向传播逻辑,以减少不必要的数据拷贝和加速训练。
5.2.1.3 训练
训练过程与之前softmax回归几乎一样:
-
定义模型
-
准备数据
-
配置训练器
-
调用
trainer.fit(model, data)
开始训练
这种一致性让我们可以将注意力集中在模型架构设计上,而不用反复处理训练细节。
理论理解
-
训练流程高度统一:定义模型、准备数据、设置优化器、训练循环。
-
从模型定义到训练的标准化接口,让我们只需更改网络结构,而无需改动训练过程。
-
这体现了深度学习框架的重要设计思想 —— 把建模(定义结构)和训练(优化过程)解耦。
企业实战理解
-
Google TensorFlow 2.0大力推广Keras风格,正是为了将"定义模型"与"执行训练"彻底分开,提升工程开发效率。
-
字节跳动机器学习平台的AutoML模块,也是基于"固定训练逻辑+灵活结构搜索"的思路进行自动模型调优。
-
Amazon SageMaker允许用户上传只定义好的模型结构,自动匹配最佳优化器、学习率策略、数据增广策略等。
5.2.2 简洁实现
5.2.2.1 模型
如果用PyTorch高级API,MLP实现可以更加简洁。
只需要用nn.Sequential
快速堆叠各层:
self.net = nn.Sequential(
nn.Flatten(),
nn.LazyLinear(num_hiddens),
nn.ReLU(),
nn.LazyLinear(num_outputs)
)
特点:
-
Flatten
把输入图片展开成一维向量。 -
第一个
LazyLinear
是隐藏层,全连接+ReLU激活。 -
第二个
LazyLinear
是输出层,直接输出10分类得分。
无需手动定义forward逻辑,框架自动连接每层。
理论理解
-
通过
nn.Sequential
实现模块化搭建:-
输入展开(Flatten)
-
隐藏层(全连接+ReLU)
-
输出层(全连接)
-
-
这种模块式、流水线式设计思想,大大提升了模型定义的简洁性与可维护性。
-
每个子模块只需专注于自己那一小步,不必关心整体。
企业实战理解
-
NVIDIA TensorRT推理加速器对Sequential结构模型可以自动做图优化,合并冗余运算,提高推理速度。
-
Google AutoML Vision内部流水线模块化(Data Aug → Feature Extractor → Classifier)也是典型Sequential结构设计。
-
字节跳动火山引擎平台在AutoNN组件中,用户通过Sequential模块快速拼装不同模型组合,极大降低了业务上线周期。
5.2.2.2 训练
训练过程完全与之前一样,只需:
model = MLP(num_outputs=10, num_hiddens=256, lr=0.1)
trainer.fit(model, data)
这种模块化设计极大提升了开发效率,也便于后续快速扩展、迭代、调参。
理论理解
-
模型换了,但训练代码无需变化。
-
这得益于良好的接口设计,只要模型符合
forward()
输入输出规范,训练器无需关心其内部细节。 -
体现了深度学习中训练流程标准化、模型定义灵活化的工程思路。
企业实战理解
-
OpenAI CLIP大规模训练就是通过接口标准化,将不同图像编码器、文本编码器分别替换而无需改动训练脚本。
-
DeepMind AlphaZero强化学习平台中,神经网络可以替换成不同规模的结构而复用同一套训练、对弈、评估流程。
-
字节跳动短视频推荐模型升级时,经常只需换一个新MLP或Transformer子模块,不动训练脚本就能完成新版本上线。
5.2.3 小结
通过本节,我们学习了:
-
如何手动实现多层感知器。
-
如何用高级API快速搭建MLP。
-
训练逻辑(loss、optimizer、data loader)在深度学习中可以通用复用,重点在于定义不同模型结构。
虽然自己从头搭建MLP让我们理解了底层细节,
但在实际工程中,我们通常使用框架提供的高级模块,便于维护和扩展。
至此,你已经掌握了1980年代末深度学习的技术水平(完全连接深度网络)。
接下来的篇章,我们将进一步探索卷积网络(CNN)和高效计算技巧,正式走向现代深度学习时代!
理论理解
-
核心收获:多层感知器(MLP)的实现并不比单层复杂太多,最重要的是理解前向传播数据流动和各层参数组织。
-
从0实现虽然繁琐,但有助于深入理解本质。
-
高级API(如Sequential)让开发更快,但理解底层机制依然重要,特别是后续深入研究优化器、自定义损失、自定义层时。
企业实战理解
-
NVIDIA在推理部署时,为了极限优化,常常需要回溯到低层次理解模型的每一层运算细节。
-
OpenAI训练GPT-3时,在分布式并行(模型并行+数据并行)中,需要了解每一层权重和激活的具体形态,以实现优化通信策略。
-
字节跳动图像搜索引擎项目中,为了支持千亿级数据检索,需要对神经网络结构进行特殊优化,比如分层量化、前向剪枝等。
🎯 小节总结表
小节 | 内容总结 |
---|---|
5.2.1 从头实现MLP | 初始化权重偏置,自己写ReLU与前向传播 |
5.2.2 简洁实现MLP | 使用Sequential快速搭建网络 |
5.2.3 小结 | 从单层到多层,训练过程保持一致,模型设计为关键焦点 |