深度学习计算:模型工程的抽象
本章来源:本文整理自《动手学深度学习》(zh.d2l.ai)第 5 章《深度学习计算》,原书作者 Aston Zhang、李沐、Zachary C. Lipton、Alexander J. Smola。
本章讲什么
前面的章节讲了模型怎么学(前向、反向、优化)。这一章转向工程层面:模型在框架里到底是怎么构造和管理的?理解了这些抽象,你才能真正看懂一个复杂的深度学习模型是怎么搭起来的。
核心概念是块(block)——它是框架里组织网络的基本单元。
层与块
块是一个统一的抽象:它接受输入、生成输出、携带可调参数(有些块没有参数)。一个神经元是一个块,一个层是一个块,多个层的组合是一个块,整个模型也是一个块。块可以递归地组合成更大的块。
就像乐高积木:用重复的模式(层组)就能拼出极其复杂的网络。比如 ResNet-152 有数百层,但由重复的残差块组成,代码反而很简洁。
从编程的角度看,块就是一个类:你只需要定义"前向传播"(输入如何变成输出)并声明参数,反向传播和参数初始化由框架自动完成。
块还带来了灵活性:你可以在前向函数里执行任意代码——用 if/while 控制流、复用同一个层(参数共享)、持有不参与训练的常数参数。这些都是固定的顺序容器做不到的。
参数管理
训练完的模型,参数就是价值的载体。需要能访问、初始化、共享它们。
- 访问:按层索引、按参数名访问。嵌套块里的参数像一棵有名字的树,可以按路径找到,比如"第一块 → 第二块 → 偏置"。
- 初始化:框架有默认方案,也可以用内置初始化器(正态分布、常数、Xavier)对特定层施加不同方案,甚至自定义初始化。
- 参数绑定(共享):让多个层共享同一个参数对象(不只是值相等,是同一个张量),改一个另一个跟着变。反向传播时共享层的梯度会相加。参数绑定的价值:省内存、强制一致性、表达结构先验。
延后初始化
搭建网络时可以完全不知道输入维度——框架会把初始化推迟到数据第一次流过模型时才做,动态推断每层参数的形状。这解决了"先有鸡还是先有蛋"的问题:不用手算层间维度就能搭网络,对 CNN 尤其有价值(输入分辨率影响后续每一层的维度)。
机制上:实例化网络时参数存在但形状未知;真正初始化发生在首次前向传播时,一旦知道输入维度,就逐层往后推,全部确定后统一初始化。
自定义层
框架没有我们需要的层时,可以自己定义。分两类:
- 不带参数的层:只做确定性的数据变换,比如把输入减去均值。
- 带参数的层:自定义一个全连接层,用框架的参数创建函数声明权重和偏置,就能自动获得访问、初始化、共享、保存加载等全部管理能力。
关键洞察:自定义层和内置层的用法完全一致,可以嵌进任何模型组合使用。神经网络的灵活性正源于"继承 + 实现前向传播"这种极简的扩展机制——研究者发明了专用于图像、文本、序列的各种层,自定义层让这种创新没有边界。
读写文件
训练好的模型要能保存、恢复和部署。理解模型持久化的核心是分清两件事:
- 模型参数:可以存盘(框架保存的是"参数字典",包含所有权重和偏置)。
- 模型架构:含任意代码(控制流、自定义逻辑),难以序列化,必须用代码重建。
所以保存模型时,框架存的是参数而不是模型本身;恢复模型时,必须先用代码重建架构,再加载参数。
实际动机很实在:长时间训练要定期保存中间结果(像游戏存档),防止断电丢失几天算力;部署时要用训练好的参数。
GPU 计算
GPU 性能二十年增长千倍,深度学习高度依赖它。核心概念是设备:每个张量都属于某个设备(默认 CPU),参与同一运算的所有数据必须在同一设备上,否则报错。
性能的关键:设备间传输数据比计算慢得多。要尽量减少拷贝——这是并行化的主要瓶颈。经验法则:
- 多个小操作远不如一个大操作。
- 频繁把 GPU 上的小数据搬回 CPU 会触发 Python 全局解释器锁(GIL),阻塞 GPU。应该在 GPU 内累积、只移动大的。
- 显存有限,不能创建超出显存的数据。
书里有个生活化类比:设备间零散传输像到店排队买咖啡,批量传输像电话预先下单、到店即取——"多个小操作比一个大操作糟糕得多"。
小结
- 块是组织网络的基本抽象,可以递归组合,核心只需定义前向传播。
- 参数管理解决访问、初始化、共享三大需求。
- 延后初始化让搭网络时无需手算维度。
- 自定义层继承了框架的全部参数管理能力。
- 模型持久化存的是参数,架构靠代码重建。
- GPU 加速的关键是减少设备间数据拷贝。
关键术语
| 术语 | 一句话解释 |
|---|---|
| 块(block) | 接受输入、生成输出、携带参数的组件,可递归组合 |
| 顺序容器 | 按顺序串行执行子块的模型容器 |
| 参数绑定 / 共享 | 多个层共用同一个参数对象 |
| 延后初始化 | 首次前向传播时才推断参数形状 |
| 自定义层 | 继承基类、实现前向传播的层 |
| 参数字典 | 模型全部参数的映射,可存盘 |
| 设备 | 张量与计算所在的硬件(CPU/GPU) |
| 显存 | GPU 专用内存,容量有限 |
| GIL | Python 全局解释器锁,小操作频繁搬数据会拖垮 GPU |