第 2 章 · 模型组建营 本章要回答的三个问题:为什么 PyTorch 要用"类"来组织模型,而不是一个函数?现成的层够用了,为什么还要会写自定义层?组装好的模型,参数到底藏在哪、怎么被找到?回答这三个问题的过程,就是组建远征队的过程。 为什么会有这一章 第 1 章你拿到了装备——张量。但没有人会直接操纵几万个张量做前向计算:层数一多、结构一复杂,手工管理权重矩阵必然失控。模型组建要解决的就是"组织"问题:把成百上千个张量按结构封装起来,让它们像一个整体那样响应调用。 PyTorch 给出的答案只有一个核心类: 。整个框架的模型——从两层的感知机到上亿参数的 Transformer——都是它的子类。这意味着学会"怎么写一个 Module",就等于学会了阅读几乎所有开源模型的源码。
本章要回答的三个问题:为什么 PyTorch 要用"类"来组织模型,而不是一个函数?现成的层够用了,为什么还要会写自定义层?组装好的模型,参数到底藏在哪、怎么被找到?回答这三个问题的过程,就是组建远征队的过程。
第 1 章你拿到了装备——张量。但没有人会直接操纵几万个张量做前向计算:层数一多、结构一复杂,手工管理权重矩阵必然失控。模型组建要解决的就是"组织"问题:把成百上千个张量按结构封装起来,让它们像一个整体那样响应调用。
PyTorch 给出的答案只有一个核心类:nn.Module。整个框架的模型——从两层的感知机到上亿参数的 Transformer——都是它的子类。这意味着学会"怎么写一个 Module",就等于学会了阅读几乎所有开源模型的源码。这一章的投资回报率极高,因为语法不变,变的只是层的组合方式。
组建营的另一个现实任务:预置层覆盖不了所有结构。论文里一个新奇的注意力变体、一个特殊的归一化方案,都得你自己动手写。所以本章最后会带你从零实现一个自定义层——不是练习题,而是第 6 章实战里真的会用到的组件。
本章知识点清单,逐条可自检:
sum(p.numel() for p in model.parameters()) 的输出核对;nn.Module 的三段式:__init__ 里声明子层、forward 里描述数据流、调用时直接用 model(x) 而不是 model.forward(x);nn.Parameter 与普通张量的区别,解释为什么把权重存成普通属性会"查无此参数";| 节 | 回答哪个问题 | 关键产出 |
|---|---|---|
| 2.1 神经网络基础 | 层在数学上做了什么 | 手写一个最小前向计算 |
| 2.2 nn.Module 组装远征队 | 模型如何被组织与登记 | 完整的两层分类模型 |
| 2.3 常用层巡礼 | 预置层各自管什么 | 选层决策依据 |
| 2.4 自定义层与模块 | 框架没给的能力怎么补 | 一个能用的自定义层 |
四节的推进逻辑是"先懂原理,再学容器,再认货架,最后自己造货架"。
nn.Module 就是应用题;坑一:forward 里现造层。 有人把 nn.Linear 直接写在 forward 里,每次调用都生成新层——参数不在登记表上,优化器永远看不见,训练静默失效。规矩是:__init__ 声明,forward 只消费。这个坑的隐蔽在于代码完全能跑、损失却纹丝不动,很多新手会在学习率上浪费一下午。
坑二:子层被放进 Python 列表。 用普通 list 装一堆层,登记表不会递归进去,参数照样失踪。要用 nn.ModuleList 或 nn.Sequential——它们才是登记表认识容器。写循环生成层的场景(比如残差堆叠)特别容易中招。
坑三:激活函数当模块存两遍。 ReLU 没有参数,重复声明无害但徒增登记表噪音;真正要小心的是有状态的层(BatchNorm、Dropout)被多个 forward 路径共享时的行为耦合。2.3 节的两副面孔会展开这条。
三个坑的共同根源都是"登记机制"没进脑子——这也是本章把 2.2 节单独留出来讲机制、而不是直接堆层清单的原因。
队伍在本章末尾集结完毕:一个能做前向、参数可被枚举的完整模型。但前向只是算出预测——预测得好不好,需要第 3 章的损失函数来定价。你在 2.2 节写的那个模型,从第 3 章起会一路用到第 5 章,训练它、调它、存它。也就是说,本章结束,远征队正式有了自己的名字和成员名单。