跳到主要内容

数据截至 (上游 commit c187ef3271d5)

训练过程怎么才看得见 — 给一个看不见的过程装上仪表

这一章讲三件事。第一件:上一章把循环交出去之后,冒出来一个新麻烦。 那段代码原来在你眼前,你想打印什么就打印什么;现在它在框架肚子里跑。 而就算它还在你手里,靠打印语句和记性去调那几十个设定,也早就不够用了 —— 书对这种做法的评语很不客气:那是无能者的最后手段。

第二件,是三件工具和它们不重叠的分工。 一件训练时直接盯、一件事后能原样复现、一件在云上一次比很多次。 书自己用一个比方把这三者钉死了:雷达、航海日志、任务控制中心。

第三件,是书自己踩的一个坑。 它演示了「一次把八种参数组合全跑一遍」, 可那段代码一个参数都没扫成 —— 八次跑的是同一套设定。我们逐行核过,写在第 7 节。

在全书链条里的位置: 这一章是收尾三章的中间一环 —— 交给框架(第 13 章)→ 装上仪表(这一章)→ 送出去(第 15 章)。 三个零件在这里一个都没换,换的是「你能看见什么」。

1. 顶层全景:同一次训练,三件工具各记一份

这一章从头到尾只训一个模型:一个认衣服的卷积网络 —— 7 万张 28×28 的灰度图,分成 10 类1书在这一章换了数据集,而且换的理由很直白:这一章的重点不是数据1

同一次训练:7 万张衣服图 → 卷积网络 → 10 类

├─① 往一个文件里写数 ──────→ 起一个本地网页,看曲线 / 看图 / 看网络结构
│ (训练时就能看,像雷达)

├─② 成套地记一次训练 ──────→ 参数 + 逐步的指标 + 产出的文件 + 模型本身
│ (事后能翻回来复现,像航海日志)

└─③ 把同样的东西记到云上 ──→ 团队一起看,并且一次跑很多组参数
(像任务控制中心)

图说:这三件工具不是三选一,是三段不同的用途。 书自己的比方就是这三个词 —— 雷达、日志本、任务控制中心2

2. 蒙眼打架:为什么非装仪表不可

先看现象,而且这个现象你已经撞过很多次了。

你改一个学习率、加一层网络、换一个批大小,然后重跑一遍看结果。 跑了二十遍之后,你还记得第 7 次用的是什么设定吗? 第 12 次那条曲线比第 5 次好,是因为学习率改了,还是因为你顺手把批大小也改了?

书对这种状态的描述是「蒙着眼打架」 —— 你改超参数、调网络结构, 或者干脆没辙了就随便试点什么3

书借书里那句引言给这种做法定了性:那是无能者的最后手段。 它紧接着补了一句正面的:训练深度学习模型的功力,不体现在盲目试错上; 系统地观察、比较、评估,更好也更有效3

这三件工具就是为这件事造的 —— 书的原话是: 它们帮你给本来一团混乱的训练过程带来结构,并且让你事后还能追溯结果4

三件工具的分工,书写得很清楚,值得原样记住4:

工具它在什么时候用书的比方它的名字
第一件训练进行时,直接盯着看雷达TensorBoard
第二件事后翻账:每一个决定和结果都记在案航海日志MLflow
第三件从外部俯视全局,团队一起看任务控制中心Weights & Biases,平时写作 WandB

最后那一列是名字,不是新概念 —— 挂上去只是为了让你认得出。 你合上这份文档去装、去搜、去读别人的代码,撞见的就是这三个词; 认不出那是自己已经懂了的东西,等于白讲。

这一章的目标一句话:把训练从一次「没人管的实验」,变成一个可追溯、可复现的过程5

3. 第一件:训练时直接看的那块屏

它解决什么问题: 训练跑起来之后,你想知道现在怎么样了 —— 而不是等它跑完。 这件工具的名字叫 TensorBoard;往文件里写数的那个类,PyTorch 自己就带, 从 torch.utils.tensorboard 里取6

怎么做,三步,而且这三步互相独立6:

① 训练过程中往一个文件里写数
── 写什么都行:模型的成绩(比如准确率)、超参数(比如学习率)
── 这个文件有个名字,叫事件文件

② 一个程序去读这些文件

③ 它顺手起一个小小的网页服务,你在浏览器里看

图说:这三步之所以要分开讲,是因为 ① 和 ③ 可以不同时发生 —— 训练早就跑完了,你明天再去看那些文件,一样能看。

能看的不只是曲线。 书列了四类,而且第三、四类是这件工具真正区别于「自己画图」的地方7:

看什么有什么用
随时间变化的指标损失、准确率;还能把好几次训练叠在同一张图上比
单个关键数字一眼扫完几个要紧的数走到哪儿了
喂进去的图看输入数据到底长什么样、模型有没有在学该学的东西
网络结构图各层怎么连的,一目了然;可以展开某一块看里面

第三类值得多说一句,因为它是排查问题最快的一条路。 书举的用途是:看看输入的图像、自编码器还原出来的图像、或者那些特征图 —— 读一遍就知道模型是不是在学你想让它学的东西7。 (第 07 章那次「第 1 轮糊、第 12 轮清晰」的对照图,正是这类东西。)

写图片那一步有一个细节:多张图会被拼成一个网格再写进去。 这一章拼的是 4 × 4 = 16 张; 另外还单独写了 4 张带类别名的图,便于对着看标签对不对8

看的时候在命令行起一条命令,它会把网址打印出来 —— 这一章那个服务开在 6006 端口9那条命令就写成 tensorboard --logdir=runs —— 后半截必须指向存事件文件的那个目录, 指错了面板就是空的。

还有一个小动作值得记:每一轮结束时把缓冲区里的东西刷进文件。 不刷的话,你在训练进行中打开网页,可能什么都看不到 —— 因为数还没落盘10

4. 第二件:事后能翻回来的那本账

先看现象: 上一件工具解决了「现在怎么样」。可三个月后你还记得这次训练用了什么吗?

这一件的定位不一样。 书说得很准:与第一件工具相比, 它关注的不只是把某一次训练画出来,而是系统地管理这些实验 —— 你可以把不同的模型版本、超参数和结果存在一个中心位置做对比11这一件的名字叫 MLflow —— 第 05 章那次数羊的训练里,有一行环境变量把记录写去了本地, 挂的就是它;当时我们说「这套工具第 14 章专门讲」,就是这一节。

4.1 两级结构:实验 → 运行

这是这一节最要紧的一个概念,而且它对应的是硬盘上真实的文件夹结构12:

mlruns/ ← 你指定的那个记录位置
└── 323771178417362930/ ← 一个「实验」,这里叫「Fashion MNIST」
└── 62ce5eb...fe438/ ← 这个实验下的一次「运行」
├── artifacts/ ← 这次跑出来的文件(含模型本身)
├── metrics/ ← 逐步记下的指标
├── params/ ← 这次用了什么参数
└── tags/

图说:「实验」是一个课题,「运行」是这个课题下的一次尝试 —— 你为了调好一个模型跑了二十遍,那就是一个实验下面的二十次运行。 (上面那两串编号是书里真实印出来的12。)

记的时候分三类,各有各的方法13:

记什么什么时候记例子
参数开跑前记一次训练几轮、学习率、批大小、用了哪把尺、哪个优化器
指标每一步都要记,而且要带上「这是第几步」训练损失、验证准确率
产出的文件跑完记配置文件、可视化图、报告;这一章记的是一份网络结构说明

第二类那个「带上第几步」是必须的 —— 因为这些数在训练过程中每一批、每一轮都在变, 不带步号,记下来的就是一堆没有顺序的数13

4.2 「存下来」和「登记进注册表」是两回事

这是这一节最容易被读漏、也最有实用价值的一条区分14:

存下来登记进注册表
它属于谁绑在这一次运行上独立于运行,自己有名字
能不能有多个版本不能能 —— 同一个名字下第 1 版、第 2 版……
书说适合谁小实验、小团队生产系统

「注册表」这个词一句话讲清:它是一份按名字组织、带版本号的模型清单。 书里那个例子跑完之后,同一个名字下已经有了两个版本15它的全名叫模型注册表(model registry)14 —— 你在 MLflow 的文档和界面上撞见的就是这个词。

为什么这个区分重要: 上线的时候你要说的是 「用叫 X 的那个模型的第 2 版」,而不是 「用那次编号 62ce5eb… 的运行产出的文件」。前者别人听得懂,后者只有你自己知道。 (这一句是我们补的判断依据,书只给了「适合生产系统」这个结论。)

要把模型取回来,靠的是一个地址 —— 书把它比作一个文件名; 拿这个地址去加载,加载回来的模型可以直接拿来预测16

看的时候同样是起一个本地服务 —— 这一章开在 8080 端口17。 界面上左边是所有实验,选中一个能看到它下面做过的运行和各自的指标; 运行多到找不着时,可以用筛选18

5. 第三件:云上的那块大屏

先看现象: 前两件工具都跑在你自己的机器上。可你的同事看不到。

书对这一件的定位:实验一复杂,光在本地画图、写一本日志就不够了 —— 开发团队需要把结果集中起来、能一起用的工具19。 它是跑在云上的,除了存指标和文件,重点在协作19这一件的名字叫 Weights & Biases,平时都写成 WandB19;代码里那个包就叫 wandb20

用法上和前一件很像:登录 → 给这次运行归到一个项目下、 顺手把这次的超参数打包成一份配置交上去 → 训练循环里往外记数20

记文件有两种做法,书分得很细21:

做法干什么
直接存文件一句话把某个文件传上去
打包成产出物先声明这是什么类型(模型 / 表格),再把文件或表格加进去,最后整个记上去

「产出物」这个词书当场给了定义:在一次实验的语境下被用到的那些文件21打包的好处是它带类型 —— 界面上能按「模型」「表格」分开找,而不是一堆散文件。

6. 主走查:同一次训练,三件工具各占一步

下面每个数都来自书。

第 ① 步,数据。 7 万张 28×28 的灰度衣物图,10 个类别,各类数量均衡; 书特意说它比那份更有名的手写数字数据更难训1。 (对照第 07 章:那次用的正是手写数字,同样是 28×28 灰度。换成衣服,难度上了一档。)

第 ② 步,超参数。 10 轮、批大小 64、学习率 0.00122

第 ③ 步,网络。 这一章的写法值得单记一笔:它把两种写法合在一起用了 —— 外面是一个模型类,里面用「按顺序排一串层」的方式分成两块23:

输入 [64, 1, 28, 28]

├── features 这一块(负责把特征抠出来)
│ 卷积(1 → 32 个通道,3×3,边上补一圈)→ [64, 32, 28, 28]
│ ReLU
│ 2×2 最大池化 → [64, 32, 14, 14]
│ 卷积(32 → 64 个通道,3×3,边上补一圈)→ [64, 64, 14, 14]
│ ReLU
│ 2×2 最大池化 → [64, 64, 7, 7]

└── classifier 这一块(负责判类别)
展平 → [64, 3136] ← 64×7×7
线性层(3136 → 128)→ ReLU
线性层(128 → 10) → [64, 10]

图说:「边上补一圈」是为什么 28 卷完还是 28 —— 尺寸只在池化那两步减半。 分成两块的好处在第 ⑦ 步会兑现:网络结构图上可以整块展开或折叠。

第 ④ 步,开始记 —— 第一件工具。 训练之前先写一次网络结构图(要给它一张样例图当引子); 每一轮开头写一次 4×4 的图片网格;每一轮结束写四个数:训练损失、训练准确率、验证损失、验证准确率; 每一轮末尾刷一次盘,跑完关掉写入器24

第 ⑤ 步,同一次训练,再记一份 —— 第二件工具。 整段训练被包在一次「运行」里;开头记一次参数(6 项:轮数、学习率、批大小、 用了哪把尺、用哪个指标、哪个优化器),每一轮记四个带步号的指标, 最后把一份网络结构说明写成文件记上去25

第 ⑥ 步,把模型也交出去。 先存一份绑在这次运行上的, 再把它登记进注册表 —— 于是同一个名字下多了一个版本15

第 ⑦ 步,看。 起本地服务,四个页签各看一样东西7:

页签这一次能看到什么
随时间变化的指标十轮的训练准确率曲线,可缩放
单个关键数字四个数各自的走势
图片那个 4×4 网格,和 4 张带类别名的单图
网络结构features 和 classifier 两块,可以点开看里面的层

第 ⑧ 步,记到云上 —— 第三件工具。 同样的东西再记一遍, 这一次批大小换成了 32,并且给这次运行写了一句备注26跑的时候就能在网页上实时看;点进某一次运行,几张图是联动的 —— 选中某一轮,各图都跳到那一轮27

第 ⑨ 步,把产出的东西也传上去。 损失列表存成文件传上去; 模型的权重打包成「模型」类型的产出物;训练损失和准确率做成一张表, 打包成「表格」类型的产出物28

走到这里,同一次训练已经在三个地方各留了一份记录 —— 而三份的用途互不重叠:一份用来盯、一份用来复现、一份用来一起看。

7. 另起一处:那个什么也没扫的参数网格

这一处和主走查用的是同一份数据,但它是一段独立的代码。 而它是这一章最值得写下来的地方 —— 因为它没跑对。

先说它想干什么。 书的开场问得很好:到目前为止你都是手动调参数、靠自己的经验; 可怎么才能自动地把参数组合试一遍?29

做法叫网格搜索:给每个参数列出几个候选值,把所有组合都跑一遍。 书给的配置是三个参数各两个候选29:

训练轮数: 5 或 10
批大小: 32 或 64
学习率: 0.001 或 0.0001

组合数:2 × 2 × 2 = 8 个格点

图说:「网格」这个名字就是从这里来的 —— 三个参数张成一个空间, 每一种组合是这个空间里的一个点。

做法是两步:先把这份配置登记一下拿到一个编号,再启动一个「跑腿的」按这个编号去跑30

判断(我们的,不是书里的):这段代码一个参数都没扫成,八个格点跑出来是同一套设定。 我们逐行核过那个被反复调用的函数31: 它初始化这次运行时交上去的,是脚本开头那个写死的配置(轮数 10、批大小 32、学习率 0.001); 而它的训练循环读的是脚本开头那个写死的轮数变量,不是这一次格点分配下来的值。

也就是说:那个「跑腿的」确实会按网格调用这个函数八次, 但八次里面用的参数完全一样。 界面上会出现八条曲线, 而它们除了随机性之外没有任何区别。

书对这段的评语是:这么做能省下大量手工,还能轻松从跑过的记录里挑出最有希望的那组参数32 —— 可这份记录里根本没有不同的参数组。

如果错,会错在: 如果那个「跑腿的」在调用之前就把当次格点的值写进了那个模块级的配置对象 (也就是说那个写死的配置在运行时会被就地改掉),那么代码是对的、我们错了。 判据是可查的:那个函数体里出现的是模块级的变量名,而不是「从这次运行的配置里取」的写法; 这两种写法在这个工具里是两回事。书里印出来的代码就是前者。

8. 书里的立场与证据

书里给了证据的:

  • 硬盘上真实的文件夹结构 —— 实验编号、运行编号、四个子目录,原样印了出来;
  • 注册表里那条记录 —— 连模型的存放地址和版本号 2 都印了;
  • 四个页签各看什么 —— 有截图。

作者的经验判断(书里没给证据):

  • 三件工具的分工(雷达 / 日志本 / 任务控制中心) —— 一个很好的框架, 但它是作者的归纳,不是任何标准;
  • 「存下来适合小实验,登记进注册表适合生产系统」 —— 经验判断,没有展开为什么;
  • 「换成衣服比手写数字更难训」 —— 定性说法,这一章没有给出准确率,所以无从对照

书里没交代的:

  • 这三件工具各自出自哪里、什么时候出现的,一字未提;
  • 三件工具能不能一起用、会不会打架,没有讲 —— 而这一章的代码实际上是把同一段训练 分别改了三遍。

书里出问题的地方: 那个参数网格 —— 见第 7 节末的判断块。

9. 边界与局限

这本书对的地方先说清: 这一章的开篇是全书最好的一段开篇 —— 它不从工具讲起,而从「你正在蒙眼打架」这个处境讲起。 三件工具的分工也切得干净:雷达 / 日志本 / 任务控制中心,读一遍就分得清该用哪件。 「实验 → 运行」那两级结构配上真实的目录树,比任何抽象描述都清楚。

但有三处要当心:

  1. 参数网格什么也没扫 —— 见第 7 节末的判断块;
  2. 这一章换了数据集,而且换得很突然。 前面十一章一直在用各章自己的数据, 这一章突然换成一份衣物图 —— 书给的理由只有半句「这一章的重点不在数据集上」1;
  3. 全章没有报过一次成绩。 训了 10 轮,准确率是多少、和瞎猜比怎么样,一个数都没有 —— 而这恰恰是第 03 章立下的规矩。在一章讲「怎么评估」的章里没有评估,是有点讽刺的。

这一章没覆盖的:

  • 怎么用这些记录做决定,没有讲。 三件工具都讲了「怎么记」, 但「记完之后怎么读出结论」全章零命中 —— 比如两条曲线怎么比、什么样的差距才算真的差距;
  • 除了网格搜索,没有别的试参办法。 网格搜索的组合数随参数个数指数增长 (3 个参数各 2 个候选就是 8 次,再加一个参数就是 16 次), 而更省的办法(比如随机抽、或者按前几次的结果决定下一次试什么)一字未提;
  • 模型上线之后怎么继续看,没有讲。 这一章的英文标题里有「监控」这个词, 可全章讲的都是训练期间的观测 —— 上线后的模型表现会不会随时间变差,零命中;
  • 三件工具的花费和数据去向没有提。 第三件是跑在别人云上的, 你的指标和产出物会离开你的机器 —— 这件事全章没有提醒过一句;
  • 本地那两件工具怎么在团队里共享,也没讲。

10. 可带走的

主走查一行写完: 同一次 FashionMNIST 训练(7 万张 28×28 灰度、10 类)→ 卷积网络分 features / classifier 两块 → 10 轮、批 64、学习率 0.001 → 第一件(TensorBoard)写事件文件(4 个标量 + 4×4 图网格 + 网络结构图), tensorboard --logdir=runs 起来、在 6006 端口看 → 第二件(MLflow)把整段训练包成一次「运行」(6 项参数 + 4 个带步号的指标 + 一份产出文件 + 模型), 在 8080 端口看,并登记进注册表拿到第 2 版 → 第三件(WandB)把同样的东西记到云上并做产出物 → 最后那个 2×2×2 的参数网格什么也没扫成。

  1. 循环交出去之后,训练变成了一个看不见的过程 —— 所以要装仪表;
  2. 靠打印语句和记性调参,书的评语是「无能者的最后手段」;
  3. 三件工具分工不重叠:雷达 = TensorBoard(训练时盯)、日志本 = MLflow(事后复现)、 任务控制中心 = WandB(团队协作);
  4. 第一件的三步是独立的:写文件 → 读文件 → 起网页 —— 所以训练跑完了照样能看;
  5. 它能看的不只是曲线:还有喂进去的图、和网络结构图;
  6. 看图是排查问题最快的一条路 —— 一眼就知道模型在不在学该学的东西;
  7. 每一轮末尾要刷一次盘,否则训练进行中打开网页可能什么都看不到;
  8. 「实验 → 运行」是两级:实验是一个课题,运行是这个课题下的一次尝试;
  9. 指标必须带步号记 —— 不带步号,记下来的就是一堆没有顺序的数;
  10. 「存下来」和「登记进注册表」是两回事:前者绑在这次运行上,后者独立、带版本号;
  11. 上线时该说「叫 X 的模型的第 2 版」,而不是某次运行的编号;
  12. 产出物 = 一次实验里用到的那些文件,打包时带类型(模型 / 表格),界面上好找;
  13. 网格搜索 = 每个参数列几个候选,所有组合都跑一遍;组合数是各候选数相乘;
  14. 书里那个 2×2×2 的网格什么也没扫 —— 函数读的是写死的变量,八次跑的是同一套设定;
  15. 这一章从头到尾没报过一次准确率 —— 一章讲评估的章里没有评估。

11. 原文地图

主题原书章原文位置
蒙眼打架与三件工具的分工Chapter 12text/14-ch12-chapter-12.txt:6(搜「fighting while blindfolded」) · text/14-ch12-chapter-12.txt:10(搜「complement each other」) · text/14-ch12-chapter-12.txt:14(搜「traceable, reproducible process」)
第一件工具的三步与事件文件Chapter 12text/14-ch12-chapter-12.txt:33(搜「Logging」) · text/14-ch12-chapter-12.txt:39(搜「Providing dashboard access」)
四个页签Chapter 12text/14-ch12-chapter-12.txt:310(搜「Time Series」) · text/14-ch12-chapter-12.txt:316(搜「Images」) · text/14-ch12-chapter-12.txt:323(搜「Graphs」)
数据集与网络结构Chapter 12text/14-ch12-chapter-12.txt:92(搜「70,000 grayscale images」) · text/14-ch12-chapter-12.txt:114(搜「FashionMnistCnn」) · text/14-ch12-chapter-12.txt:118(搜「features and classifiers」)
图片网格与结构图Chapter 12text/14-ch12-chapter-12.txt:154(搜「4 × 4 images」) · text/14-ch12-chapter-12.txt:206(搜「log the entire model graph」)
训练里的四个记录动作Chapter 12text/14-ch12-chapter-12.txt:225(搜「writer.add_scalar」) · text/14-ch12-chapter-12.txt:228(搜「writer.flush()」)
起服务与端口Chapter 12text/14-ch12-chapter-12.txt:298(搜「tensorboard --logdir=runs」)
第二件工具的定位Chapter 12text/14-ch12-chapter-12.txt:347(搜「tracking, comparing and reproducing」)
实验、运行与目录结构Chapter 12text/14-ch12-chapter-12.txt:360(搜「set_tracking_uri」) · text/14-ch12-chapter-12.txt:385(搜「Experiment ID」)
三类记录Chapter 12text/14-ch12-chapter-12.txt:401(搜「log_params」) · text/14-ch12-chapter-12.txt:403(搜「log_metric」) · text/14-ch12-chapter-12.txt:408(搜「log_artifact」)
存模型 vs 登记进注册表Chapter 12text/14-ch12-chapter-12.txt:448(搜「log_model」) · text/14-ch12-chapter-12.txt:451(搜「register_model」) · text/14-ch12-chapter-12.txt:473(搜「RegisteredModel」)
取回模型与看板Chapter 12text/14-ch12-chapter-12.txt:486(搜「model URI」) · text/14-ch12-chapter-12.txt:512(搜「mlflow ui --host」) · text/14-ch12-chapter-12.txt:518(搜「filters can help you」)
第三件工具的定位与初始化Chapter 12text/14-ch12-chapter-12.txt:544(搜「no longer enough to just visualize」) · text/14-ch12-chapter-12.txt:549(搜「mission control」) · text/14-ch12-chapter-12.txt:577(搜「wandb.init()」)
产出物两种做法Chapter 12text/14-ch12-chapter-12.txt:607(搜「Logging Artifacts」) · text/14-ch12-chapter-12.txt:620(搜「files that are used in the context of an experiment」)
参数网格Chapter 12text/14-ch12-chapter-12.txt:663(搜「Sweeps」) · text/14-ch12-chapter-12.txt:667(搜「grid search」) · text/14-ch12-chapter-12.txt:708(搜「wandb.sweep()」)
勘误 网格什么也没扫Chapter 12text/14-ch12-chapter-12.txt:690(搜「def sweep_run():」) · text/14-ch12-chapter-12.txt:697(搜「for epoch in range(EPOCHS)」) · text/14-ch12-chapter-12.txt:720(搜「save us a lot of manual effort」)

Footnotes

  1. 出处:「Chapter 12」第 90-95 段(text/14-ch12-chapter-12.txt:92,搜「70,000 grayscale images」)。原文:此时我们的重点不在某个特定的数据集上,但训练显然需要数据,所以我们选 FashionMNIST 来创建训练与验证数据;这是一份 7 万张衣物灰度图的数据集,分辨率 28×28,属于 10 个类别之一,类别是均衡的,而且通常比更有名的 MNIST 更难训练。 2 3 4

  2. 出处:「Chapter 12」第 10-12 段(text/14-ch12-chapter-12.txt:10,搜「complement each other」)与第 549 段(text/14-ch12-chapter-12.txt:549,搜「mission control」)。原文:这几个框架互相补充,用途从「训练时用 TensorBoard 直接观察」,到「把所有决定与结果记进 MLflow 这本航海日志」,再到「用 WandB 做任务控制 —— 从外部保持全局视野并支持协作」。后一处又说:如果把 TensorBoard 想成雷达、MLflow 想成航海日志,那么 WandB 就是任务控制中心。

  3. 出处:「Chapter 12」第 6-9 段(text/14-ch12-chapter-12.txt:6,搜「fighting while blindfolded」)。原文:训练模型常常像蒙着眼睛打架 —— 你改超参数、调模型结构,或者干脆因为不知道还能做什么而随便试试;这就是那句引言里说的「暴力」,一种因为缺少合适工具而不得不采取的绝望之举。原文接着说:训练深度学习模型的功力,不体现在盲目试错上,系统的观察、比较和评估更好也更有效。书页首引的那句话出自阿西莫夫《基地》。 2

  4. 出处:「Chapter 12」第 10-12 段(text/14-ch12-chapter-12.txt:10,搜「bring structure to the otherwise chaotic」)。原文:TensorBoard、MLflow 和 Weights & Biases 就是为此而生的,它们帮助给本来混乱的模型训练过程带来结构,并让你事后还能追踪结果。 2

  5. 出处:「Chapter 12」第 13-18 段(text/14-ch12-chapter-12.txt:14,搜「traceable, reproducible process」)。原文:这一章聚焦于如何评估、记录和监控模型,把训练从一次不受控的实验,变成一个可追溯、可复现的过程。

  6. 出处:「Chapter 12」第 31-41 段(text/14-ch12-chapter-12.txt:33,搜「Logging」与 text/14-ch12-chapter-12.txt:39,搜「Providing dashboard access」)。原文三条:训练期间它记录重要的值(也就是把它们记下来供之后分析,并存进一个文件 —— 事件文件),这些可以是模型指标(比如准确率)或超参数(比如学习率);它读取这些日志文件,在一个好看的面板里可视化;当它运行时会起一个小小的网页服务,让你能访问这个面板。与第 58-62 段(text/14-ch12-chapter-12.txt:60,搜「torch.utils.tensorboard」):写数的那个 SummaryWriter 类来自 PyTorch 自带的 torch.utils.tensorboard 模块,它就是你用来和 TensorBoard 打交道的接口。 2

  7. 出处:「Chapter 12」第 308-325 段(text/14-ch12-chapter-12.txt:310,搜「Time Series」、text/14-ch12-chapter-12.txt:316,搜「Images」、text/14-ch12-chapter-12.txt:323,搜「Graphs」)。原文四个页签:随时间变化的指标(损失或准确率,而且把多次训练叠在一起看特别有助于发现趋势与差异);单个关键数字的快速总览;训练期间记录的图像数据(比如视觉任务的输入图、自编码器的重建图、或特征图 —— 这一块让你能检查输入数据长什么样、模型是不是真的在学你想要的结构);以及把模型显示成一张图,让你很容易理解各层之间怎么连接。原文还说这些图通常是可交互的,可以自由缩放(text/14-ch12-chapter-12.txt:328,搜「freely zoom」)。 2 3

  8. 出处:「Chapter 12」第 152-192 段(text/14-ch12-chapter-12.txt:154,搜「4 × 4 images」)。原文:先从数据加载器里取出图片和对应的标签,再拼成一个 4×4 的图片网格记录下去;除了网格之外还可以记录单张图片和它对应的标签,代码里记了前 4 张。

  9. 出处:「Chapter 12」第 294-305 段(text/14-ch12-chapter-12.txt:298,搜「tensorboard --logdir=runs」)。原文:训练之后或训练当中,只要在命令行执行那条命令就能打开面板;必须传日志目录参数并确保它指向 runs;输出里会给出访问服务的网址。打印出来的地址指向本机的 6006 端口。

  10. 出处:「Chapter 12」第 228-234 段(text/14-ch12-chapter-12.txt:228,搜「writer.flush()」)。原文:这个方法确保所有缓冲中的日志都被写进事件文件;当我们交互式地使用 TensorBoard 时这很有帮助,因为它保证我们能在每一轮结束时「实时」看到当前的模型结果。另一个方法在训练循环之外调用,用来保存最新数据并同时关掉写入器。

  11. 出处:「Chapter 12」第 346-353 段(text/14-ch12-chapter-12.txt:347,搜「tracking, comparing and reproducing」)。原文:MLflow 是一个用于追踪、比较和复现实验的平台;与 TensorBoard 相比,它关注的不只是单次训练的可视化,还有对实验的系统性管理 —— 你可以在一个中心位置存放并比较不同的模型版本、超参数和结果;它还确保研究与开发始终可追溯。

  12. 出处:「Chapter 12」第 359-398 段(text/14-ch12-chapter-12.txt:360,搜「set_tracking_uri」与 text/14-ch12-chapter-12.txt:385,搜「Experiment ID」)。原文:记录之前必须先初始化 —— 这个记录位置可以是本地文件、本地服务器、远端服务器或数据库;这里用的是本地的 mlruns 文件夹,所有实验、运行和指标都存在那里以便日后追溯;用另一个方法可以给实验起一个唯一好懂的名字,这里叫「Fashion MNIST」。原文印出来的目录树:实验编号 323771178417362930 下面是运行编号 62ce5eb539404b30947b9a5782dfe438,再往下是 artifactsmetricsparamstags 四个子目录;另有一个 models 目录,里面 fashion_mnist_model 下有 version-1 和 version-2。 2

  13. 出处:「Chapter 12」第 399-423 段(text/14-ch12-chapter-12.txt:401,搜「log_params」、text/14-ch12-chapter-12.txt:403,搜「log_metric」、text/14-ch12-chapter-12.txt:408,搜「log_artifact」)。原文三类:记参数;记损失或准确率这类指标 —— 它需要一个名字和一个值,而且由于这些指标在训练中每一批或每一轮都在变,必须通过一个参数传入当前是第几步;记实验期间产生的额外文件 —— 可以是配置文件、训练好的模型、可视化图或报告,这一章记的是写进文件的那份网络结构说明。代码里记的参数有六项:轮数、学习率、批大小、损失函数名、指标函数名、优化器。 2

  14. 出处:「Chapter 12」第 446-462 段(text/14-ch12-chapter-12.txt:448,搜「log_model」与 text/14-ch12-chapter-12.txt:451,搜「register_model」)。原文两种做法:一种用来保存当前这次运行的模型 —— 模型绑在这次运行上,这种做法更适合小型实验和小团队;另一种是把模型登记进模型注册表 —— 同一个模型可以有多个版本,这种做法适合生产系统。 2

  15. 出处:「Chapter 12」第 466-483 段(text/14-ch12-chapter-12.txt:473,搜「RegisteredModel」)。原文打印出来的那条注册记录里,模型名是 fashion_mnist_model,version=2,并带着它的存放地址与所属运行编号。 2

  16. 出处:「Chapter 12」第 484-502 段(text/14-ch12-chapter-12.txt:486,搜「model URI」)。原文:模型可以存在你本地的系统上(如果你那样搭建,也可以存在服务器上);要加载模型,你需要知道它的位置,为此使用模型地址 —— 它类似于一个文件名;用加载方法把这个地址传进去,模型就可以用了,可以直接拿它做预测。

  17. 出处:「Chapter 12」第 507-514 段(text/14-ch12-chapter-12.txt:512,搜「mlflow ui --host」)。原文:使用面板之前必须在命令行启动本地服务,然后通过给出的网址访问;打印出来的地址指向本机的 8080 端口。

  18. 出处:「Chapter 12」第 516-522 段(text/14-ch12-chapter-12.txt:518,搜「filters can help you」)。原文:图 12.5 左上角列出所有实验;一个实验包含一次或多次已完成的运行,选中的那次运行的指标会被展示出来;如果运行数量非常多,筛选可以帮你重新找到某一次特定的运行。

  19. 出处:「Chapter 12」第 543-549 段(text/14-ch12-chapter-12.txt:544,搜「no longer enough to just visualize」)。原文:当你的实验变复杂之后,只在本地可视化、或只写一本日志就不够了;开发团队需要能把结果集中起来、并且能一起使用的工具,而这正是 WandB 的位置 —— 它是一个基于云的平台,不仅存指标和产出物,更把重点放在协作上。 2 3

  20. 出处:「Chapter 12」第 557-594 段(text/14-ch12-chapter-12.txt:577,搜「wandb.init()」)。原文:必须加载这个包并登录;然后创建超参数、把它们放进一个配置字典并记录这份配置;当前这次模型运行总是隶属于某个项目,必须先初始化它 —— 你要给它一个名字,而且把超参数传给配置参数是合理的。记指标是在训练循环里调用记录方法。 2

  21. 出处:「Chapter 12」第 607-651 段(text/14-ch12-chapter-12.txt:607,搜「Logging Artifacts」与 text/14-ch12-chapter-12.txt:620,搜「files that are used in the context of an experiment」)。原文:除了经典的指标之外,还可以用一个方法保存文件;而产出物(也就是在一次实验的语境下被用到的那些文件)的保存方式略有不同 —— WandB 区分文件产出物与模型产出物,记录产出物的步骤是:先保存 Python 对象、再初始化产出物、用一个方法把文件加进去、最后把产出物记上去。表格也可以按同样的四步存成产出物。 2

  22. 出处:「Chapter 12」第 82-88 段(text/14-ch12-chapter-12.txt:84,搜「EPOCHS = 10」)。三个超参数:10 轮、批大小 64、学习率 0.001。

  23. 出处:「Chapter 12」第 114-147 段(text/14-ch12-chapter-12.txt:114,搜「FashionMnistCnn」与 text/14-ch12-chapter-12.txt:118,搜「features and classifiers」)。原文:前面各章里我们见过用「按顺序排一串层」的方式描述模型结构,但多数情况下我们是用一个单独的类定义来建模型类;这里把两种做法结合起来,用前者给网络更多结构 —— 于是网络在最上层被分成 features 和 classifier 两块:features 里用卷积层和最大池化层把特征抠出来,classifier 里用抠出来的特征算出各类别的分数。代码里两次卷积是 1→32 和 32→64、卷积核 3、边上补一圈,池化都是 2×2 步长 2,展平后第一个线性层的输入是 64×7×7。上面那串维度是我们按这些参数推出来的,书没有逐层打印。

  24. 出处:「Chapter 12」第 206-234 段(text/14-ch12-chapter-12.txt:206,搜「log the entire model graph」与 text/14-ch12-chapter-12.txt:225,搜「writer.add_scalar」)。原文:记录整个模型结构图也很有帮助 —— 先从数据加载器里取一张样例图,记录时除了图还要把模型传进去;训练里最要紧的几处是:每一轮开头调用那个记图片的函数;在批次循环里更新训练与验证指标,跑完最后一批用一个方法把这一轮的聚合值算出来、然后重置;调用记标量的方法记单个指标或超参数,要传一个标签、一个数值和当前是第几步(这里是轮次)。

  25. 出处:「Chapter 12」第 413-438 段(text/14-ch12-chapter-12.txt:413,搜「with mlflow.start_run():」)。原文:整段模型训练在一次运行的范围内进行,开头记六项参数,每一轮记四个带步号的指标,最后把网络结构说明写进文件并记录下来。

  26. 出处:「Chapter 12」第 563-581 段(text/14-ch12-chapter-12.txt:566,搜「BATCH_SIZE = 32」)。这一节的超参数:10 轮、批大小 32、学习率 0.001;初始化这次运行时还写了一句备注(「用于 Fashion MNIST 的卷积网络,批大小调小了」)。

  27. 出处:「Chapter 12」第 596-605 段(text/14-ch12-chapter-12.txt:596,搜「view this information live online」)。原文:训练期间就能在线实时看到这些信息;总览页显示项目里所有的运行以及超参数和模型结果;点进某一次运行能看到它的指标,而且各张图是互相联动的,所以可以一眼看到选中的那一轮上的全部信息。

  28. 出处:「Chapter 12」第 607-651 段(text/14-ch12-chapter-12.txt:611,搜「train_loss_list.pth」与 text/14-ch12-chapter-12.txt:646,搜「train_loss_table」)。代码里:先把损失列表存成文件再传上去;把模型权重存成文件、打包成「模型」类型的产出物;把训练损失和训练准确率做成一张表、打包成「表格」类型的产出物。

  29. 出处:「Chapter 12」第 663-681 段(text/14-ch12-chapter-12.txt:663,搜「Sweeps」与 text/14-ch12-chapter-12.txt:667,搜「grid search」)。原文:到目前为止你都是手动调模型参数、把自己的经验用进去;可是怎么才能做复杂的优化、自动测试参数组合?这就是 sweep 登场的地方。原文的配置里为各个超参数定义了可能的取值(比如轮数取 5 和 10);网格搜索随后创建参数的所有可能组合(也就是在超参数空间里张出一个网格),再为所有这些组合训练并验证模型。配置里三项:轮数 [5, 10]、批大小 [32, 64]、学习率 [0.001, 0.0001]。 2

  30. 出处:「Chapter 12」第 708-714 段(text/14-ch12-chapter-12.txt:708,搜「wandb.sweep()」)。原文:先用一个方法初始化网格搜索拿到编号,再用另一个方法按这个编号启动它,由它去反复调用那个训练函数。

  31. 出处:「Chapter 12」第 685-706 段(text/14-ch12-chapter-12.txt:690,搜「def sweep_run():」与 text/14-ch12-chapter-12.txt:697,搜「for epoch in range(EPOCHS)」)。原文说这个函数用于「跑一次训练并记录」。而代码里:初始化这次运行时传的是 config=config(脚本开头那个写死的字典),训练循环写的是 for epoch in range(EPOCHS)(脚本开头那个写死的常量)—— 两处都不是「从这次运行分配到的配置里取值」。 因此网格里的八个格点跑出来是同一套超参数。

  32. 出处:「Chapter 12」第 716-722 段(text/14-ch12-chapter-12.txt:720,搜「save us a lot of manual effort」)。原文:这样能省下大量手工,而且我们能很轻松地从面板里跑过的那些运行中筛出最有希望的那组参数组合。