拆解大纲 — 《Python 深度学习及智能车竞赛实践》
这是第二段交付物:节级大纲,一节一行。不是正文。 上一位 agent 的通读笔记在同目录
reading-notes.md与reading-notes-2.md,本大纲的所有事实依据都在那两份里。
切分:16 章 + 总纲
不照搬原书 11 章。 理由是新词密度:原书前 6 章(占全书 44% 篇幅)几乎没有新词, 全是通用编程概念;第 8 章一章就压着前向传播、激活函数、梯度消失、损失函数、五种优化器、 反向传播、链式法则、计算图。照原章序拆会前面注水、后面挤爆。
| 我们的章 | 对应原书 | 为什么这么切 |
|---|---|---|
| 01 这场比赛长什么样,它属于哪门学问 | §1.1 浪潮与流派 + §1.2.1 智能汽车技术概述 + §1.2.2 + §10.1 | 原书把「赛场什么样」拆在第 1 章和第 10 章两处,中间隔了 8 章;读者不先看到车和场地,后面全读不懂,必须合并前置。§1.2.1 那八项关键技术原来漏了,现补进来 —— 它给出「感知 → 决策 → 控制」这个框,而这本书只做感知那一段 |
| 02 为什么是 Python,以及怎么使唤它 | §1.3 + §2.1–2.2 + 第 3 章 + §7.1–7.2 | 「语言分类」在第 1 章、「计算生态」在第 7 章,隔了 5 章其实是一件事 |
| 03 数据怎么进到程序里 | §2.3 + 第 5 章 + §6.1–6.2 | 三处都在讲同一件事:把外面的数据变成程序里的结构 |
| 04 图像在计算机里是什么 | §6.3–6.4 | 单独成章。它是第 10–16 章的地基,压在「文件」那一章里会被淹没 |
| 05 同一段逻辑怎么复用,顺手写出第一个神经元 | 第 4 章 | 落点是感知器,不是语法 |
| 06 让机器自己找规律 | §7.3–7.5 | 书自己说「单层感知器可以简单理解为一个线性回归模型」,所以 05 交棒给 06 是顺的 |
| 07 从一个神经元到一张网 | §8.1–8.2.2 | 第 8 章拆成 07/08/09 三章:07 管「信号往前怎么走」 |
| 08 它怎么知道自己错了,又该往哪儿改 | §8.2.3–8.2.6 | 08 管「误差往回怎么传」。BP 走查横跨 07、08 两章,是同一组数字的前半程和后半程 |
| 09 五步固定动作 | §8.3–8.4 | 09 管「这套东西怎么变成一个跑得起来的项目」。这五步在 10、13、15 各走一遍 |
| 10 图像为什么不能用全连接 | §9.1–9.2 + §9.4 | 拆成 10/11:10 答「为什么要卷积」 |
| 11 五个模型,一部 CNN 进化史 | §9.3 | 11 答「别 人已经卷成了什么样」。合成一章会有 10 个小节,读者找不着北 |
| 12 让车跑起来(一):不训练模型的那条路 | §10.1 后半 + §10.2 | 第 10 章是全书落点,拆成 12/13/14 三章按路线走,不按书的节序流水账 |
| 13 让车跑起来(二):让网络自己吐出一个转角 | §10.3 | 全书技术高潮 |
| 14 让车跑起来(三):逐像素分车道线,以及三条路怎么选 | §10.4 + 我们的对比 | 三条路的对比表是我们加的,书没做过这个对比 |
| 15 认出路上的东西:目标检测怎么做、怎么打分 | §11.1–11.2 | 第 11 章拆成 15/16:15 管算法与指标(约 39 个新词),16 管从数据到板卡 |
| 16 塞进一块小板子:数据集、训练与两条部署路线 | §11.3–11.5 | 16 是全书唯一讲「模型怎么真的跑在硬件上」的地方 |
整体移出、要在总纲「不覆盖什么」里写明的: 第 1 章的中国 AI 发展史与政策罗列(约整章 1/3)、 各章思政插入段、所有 API 手册式表格(math 44 个函数、time 库 12 个函数、turtle 函数清单、 numpy/pandas 方法表、字符串 43 种方法 —— 这些在原书里本就是图片)、jieba 与 wordcloud 两节、 递归与汉诺塔、turtle 绘图本身、复数类型、lambda 函数。
篇幅预估: 16 章平均 18k 字符,合计约 285k。原书可拆的技术正文约 100k 字符 (扣掉习题、思政、政策、API 表格图注),比例 2.9 倍 —— 高于样板的 2.15 倍, 原因是这本书全部代码和公式都在图片里,示范代码要我们自己写。
全书一条主线(总纲第 3 节的骨架)
这本书的主线不是「学 Python」也不是「学深度学习」,而是: 让一辆算力有限的小车,靠一只摄像头,自己把赛道跑完 —— 从零基础一路搭到这件事上。
每一步都是被上一步的结论逼出来的:
- 要让车自己跑,先得能指挥计算机。 于是要一门语言。选 Python 是因为它能把别人用 C/C++ 写好的库直接借来用,自己只当胶水 —— 慢是真的慢,但真正费算力的那部分根本不在 Python 里跑。(→ 02)
- 能指挥了,喂什么给它? 车上的输入只有一张摄像头图,输出只有一个 −1 到 1 的转角。 这一对数据要先变成程序里的结构、再从硬盘读进来、再切成训练集和验证集。(→ 03)
- 可「一张图」对计算机是什么? 是一个 0–255 的数组。这句话是后面全部内容的地基: 卷积、颜色阈值、逐像素分割,全都是在这个数组上动手。(→ 04)
- 有了数据,谁来算? 最小的计算单元是:每个输入乘一个权重、加起来、加个偏置、 过一道激活函数 —— 这就是 1957 年的感知器,一个神经元。(→ 05)
- 可权重从哪儿来? 不是人定的,是学出来的:定一把尺子量「答得有多差」(损失函数), 然后蒙着眼往下坡挪(梯度下降)。这就是机器学习的全部。(→ 06)
- 一个神经元只能画一条直线, 遇到异或这种题就无解 —— 这正是神经网络第一次寒冬的起因。 把神经元叠成多层就能拼出任意形状,但叠高之后必须有激活函数,否则一百层等于一层。(→ 07)
- 叠高了,怎么知道每 个参数该往哪拧? 链式法则让一次回传就算清所有梯度 —— 这就是反向传播。它只负责算梯度,拿梯度改参数的仍然是梯度下降。(→ 08)
- 原理有了,工程上就固定成五步: 数据处理 → 模型设计 → 训练配置 → 评估调优 → 部署。 后面每个项目都是这五步的一次填空。(→ 09)
- 但把这套东西直接用在图像上会炸: 16×16 的小图接三个千神经元隐藏层就要 226 万个参数, 而车上的图是 640×480。三味药 —— 只看局部、共用权重、把图缩小 —— 合起来就是卷积网络。(→ 10、11)
- 现在可以正面回答「怎么让车自己跑」了,而且有三种答法: 不训练模型、靠颜色阈值挑出车道线;训练一个卷积网直接吐出转角;逐像素分割出车道线再提中心线。 三条路的区别不在精度,在于成本落在谁头上 —— 第一条压在「换个光照就得重调一遍参数」上, 第二条压在「得有人开车开出几千张带转角的图」上,第三条压在「得有人一个像素一个像素地描边, 还得换一块更贵的板子」上。选哪条,取决于场地会不会变、有没有人力标注、板子有多少算力。(→ 12、13、14)
- 光会跑还不够,还要认出路上的东西。 这是另一个任务:框出位置 + 说出类别。 而所有这些模型最后都得塞进车上那块算力有限的小板子 —— 边缘部署不是最后一步,它从一开始就在反过来约束模型怎么设计。(→ 15、16)
贯穿全书的那件具体东西: 一张前置摄像头拍的车前道路图(640×480,喂进模型前 resize 到 128×128) 配一个 [−1, 1] 的转角值。它第一次出现在原书第 2 章,最后一次是第 10 章模型的 tanh 输出层。 全书主走查就用这一对数 据。
各章节线
每行是大章「核心原理」层的一节:进来时以为… → 出去时知道…。 每章末尾标出这一章的主走查(一条具体输入贯穿全章)与承重词(要单开一节、从现象讲起的一到两个词)。
01 这场比赛长什么样,它属于哪门学问(§1.1 + §1.2 + §10.1)
- §3.1 进来时以为「智能车」是个比喻 → 出去时知道它是一辆实物车跑在一块实物场地上:600×480cm 的 PU 布赛场、含黄线约 50cm 宽的车道、起点在地图左下角;车模四个电机各驱一轮,后轮用近似麦克纳姆轮可以横向摆动,转向靠前轮双电机差速
- §3.2 进来时以为车靠雷达或者预先写死的路线跑 → 出去时知道它只有一只前置摄像头,车上唯一的输入是一张车前道路图,唯一的输出是一个 −1 到 1 的小数(负数左转、正数右转、0 直行)——全书就是围着这一对数据转的
- §3.3 进来时以为赛场是理想环境 → 出去时知道地图有纹路、有不平整、有边框裂缝、光照会变,而书明说**「这恰恰是比赛的考核项之一」**——这句话解释了后面为什么要做颜色增强、为什么要分时段采数据
- §3.4 进来时以为模型训好了就能用 → 出去时知道训练跑在云上的免费 GPU(AI Studio),推理却必须跑在拧在车上的一块小板子上(组委会指定的 EdgeBoard),这块板子的算力约束反过来决定了后面每一处模型设计 ← 承重词一
- §3.5 进来时以为比赛只有一个任务 → 出去时知道有两个:沿车道线巡航(吐一个连续的转角 = 回归)、认出路上的标志与障碍(框出位置 + 说出类别 = 检测);线上赛给的是 16000 张车载影像,标注是与原图同尺寸的单通道灰度图(背景 0、实车道线 1、虚车道线 2、斑马线 3)
- §3.6 进来时以为「人工智能」「机器学习」「深度学习」是三个可以换着用的时髦词 → 出去时知道它们是一层套一层的四个圈(深度学习 ⊂ 神经网络 ⊂ 机器学习 ⊂ 人工智能),知道每一层各自多大、这本书站在最里面那一圈上 ← 承重词二,单开一节、从读者在新闻里见过的现象讲起
- §3.7 进来时以为 AI 是最近几年才有的 → 出去时知道它走过计算驱动、知识驱动、数据驱动三段(以及两次低谷),知道有符号主义 / 连接主义 / 行为主义三大流派,落点是本书为什么只讲连接主义那一支
- §3.8 进来时以为「自动驾驶」是一件事 → 出去时知道整条链是感知 → 决策 → 控制,书列了八项关键技术(传感器、自动驾驶、机器学习、高精地图与定位、通信、人机交互、数据安全、软件集成),而这本书和这场比赛只做感知那一段 —— 所以后面十五章里从来没有出现过控制器。这一句同时给 14§3.6 那处空白划好边界:不是书漏了,是书的范围就到这儿
- §3.9 进来时不知道这本书怎么走 → 出去时拿到全书施工图:哪几章在铺路(把数据送进程序)、哪几章在讲让机器自己找规律、哪 几章正面回答让车跑起来
主走查: 车停在起点,摄像头拍下第一帧 —— 这一帧要经过哪几道手才变成两个前轮的转速差。 终点那一道手(转角 → 左右轮差速)由 02§3.4 当场兑现,本章只把整条链摆出来。这条链条本身就是全书的目录。 承重词: 边缘计算板卡(EdgeBoard)、深度学习(四个圈)。
02 为什么是 Python,以及怎么使唤它(§1.3 + §2.1–2.2 + 第 3 章 + §7.1–7.2)
- §3.1 进来时以为选哪门语言是口味问题 → 出去时知道语言分低级/高级、编译/解释两条线,而 Python 走混合路:先编成中间形式的字节码再解释执行 —— 这让它比 C 慢一大截 ← 承重词一
- §3.2 进来时以为慢就不该拿来做 AI → 出去时知道真正费算力的矩阵运算根本不在 Python 里跑,Python 只是「胶水」:C/C++ 写的专业库封装成简单接口给它调;270 个标准库、十几万个第三方库、一条 pip 命令,这才是它成为 AI 门面的原因
- §3.3 进来时面对一个问题不知从哪儿下手 → 出去时会用两个思维模具:计算思维(抽象是手段、自动化是目的)和 IPO(把任何题拆成输入什么—怎么处理—输出什么),并用 IPO 拆开本书的第一个实例:x 大于 0 就照原样输出、否则输出 0;拆完当场留名 ——「这个分段函数在深度学习里到处都是,它叫 ReLU;它属于一类叫激活函数的东西。为什么非要有它、为什么偏偏是它,第 07 章讲」(书自己在 2.1.2 就把这两个名字都给了) ← 承重词二
- §3.4 进来时以为「转角 = 0.5」这种数怎么变成车的动作是个黑箱 → 出去时会用 IPO 拆开书的第二个实例差速转向:输入是一个 [−1,1] 的转角,处理是「大于 0 就右轮减速、左轮不变;小于 0 就左轮减速、右轮不变」,输出是两个轮子的转速 —— 01 那条主走查的最后一道手在这里落地。书自己说第 2 章的语法元素全部是通过这个实例讲的
- §3.5 进来时以为缩进只是排版习惯 → 出去时知道 Python 用缩进代替大括号划分层次,并认得赋值、标识符(中文也能当变量名)、input/print、eval 这几件最小零件,以及
eval(input())为什么危险(它会把用户敲进来的任何字符串当代码执行) - §3.6 进来时以为程序就是从上往下跑完 → 出去时知道三种控制结构,会用分支判断「电池 11.5V 该不该上场」,知道 break 只跳最内层、for-else 只在没被 break 打断时才执行
- §3.7 进来时以为程序出错就该崩 → 出去时知道 try-except 把「可预料的意外」和「真正的 bug」分开,以及为什么读一个可能不存在的数据文件必须包在里面
- §3.8 进来时以为随机就是随机 → 出去时知道它是梅森旋转算出来的伪随机(周期 2¹⁹⁹³⁷−1,书明说不适合加密),同一个种子必然给出同一串数 —— 这是后面「数据集乱序还能复现」靠的东西
主走查: 「电池电压 11.5V,这辆车该不该上场」。input 读进字符串 → eval 变成数 → 分支比对 10–13V 的工作区间 → print 结论;再改成循环连读 5 次取平均,并把「文件读不到」包进 try。 书自己给了这个实例的现场:2023 年赛前一晚有 学生给电池充了一整夜,第二天车跑得出奇地慢 —— 从这个现象起头。 副走查(§3.4,允许另起): 转角 −0.4 进来 → 判为左转 → 左轮 60 降到 36、右轮仍 60 → 输出 (36, 60)。 承重词: 解释执行与字节码、IPO。 留名账: ReLU / 激活函数(§3.3 留名,07§3.5–3.6 兑现)。
03 数据怎么进到程序里(§2.3 + 第 5 章 + §6.1–6.2)
- §3.1 进来时看到
f.read(5)、img.convert()这种写法不知道那个点号是什么 → 出去时知道 Python 里几乎所有东西都是对象:一份数据 + 一组只对这份数据有效的操作;点号左边是那份数据,右边是对它下的命令(数据那部分叫属性,操作那部分叫方法)。这一小块原来排在 05§3.3,太晚了 —— 03、04 两章通篇都在调方法,而这一章讲的正是「数据在程序里长什么样」,放这儿最顺;「怎么自己写一个类」仍然留给 05 - §3.2 进来时以为一个变量存一个值就够 → 出去时看到差别:统计 5 类标签的数量,「a1 a2 a3 a4 a5」和「一个结构 + 索引」写出来的代码长得不一样,而只有后者扩得到 16000 张图
- §3.3 进来时以为数值就是数值 → 出去时知道
0.1+0.2打印出来是0.30000000000000004,浮点只保证 15 位准确 —— 这决定了「转角 0.5」这种数在程序里永远只是「差不多 0.5」 - §3.4 进来时以为字符串就 是一串字 → 出去时会用正反双索引和切片从
20230801_0137.jpg里切出编号,知道 Python 字符串按 Unicode 存,中英文都算一个字符 - §3.5 进来时以为列表、元组、集合、字典是四种差不多的容器 → 出去时知道三分法(有序的序列 / 无序去重的集合 / 键值对的映射)各解决什么,以及
lt = ls不产生新列表、改一个另一个跟着变这个最容易踩的坑 - §3.6 进来时以为「图片名 → 转角」用两个列表对着记就行 → 出去时知道字典是 Python 唯一的内建映射类型、键必须不可变,并看到同一张图被录了三次时怎么存成列表再取平均 ← 全书主走查的那对数据在这里第一次有了程序里的形状
- §3.7 进来时以为文件就是打开读完 → 出去时知道文本文件与二进制文件的分野只在「怎么解释这堆字节」(所有文件本质都是二进制),句柄有个会移动的指针,以及为什么处理大数据集必须逐行读而不是一次 readlines() ← 承重词一
- §3.8 进来时以为数据格式是随便定的 → 出去时知道一维用分隔符(三条口径)、二维用 CSV(先行后列)、三维及以上统一用键值对(这种格式叫 JSON,后面的巡航数据集、检测标注全是它);并知道 CSV 这种表格另有一个专门的库替你读 —— 一句
read_csv()就变成一张带列名的表(这个库叫 pandas,书说数据预处理是机器学习里最耗时间的环节,pandas 就是为这件事做的);还看懂书里那个「不改代码、只改数据文件就画出另一条赛道」的例子为什么算「定义了一个接口」 - §3.9 进来时以为拿到数据就能训练 → 出去时会走完那套固定四步:解压 → 从文件夹名/文件名提标签 → 乱序 → 按比例切训练集与验证集(书取 9:1);并知道不乱序为什么致命 —— 解压出来的数据「相同类型基本依次排列」,不打乱模型会连着看几百张汽车再连着看几百张货车 ← 承重词二
主走查: Vehicle_Data.zip,从真实项目里摘的 300 个样本。解压 → 提标签 → 乱序 → 切成 270 训练 / 30 验证 → 落成两个 txt。
中间嵌一段句柄指针的小走查:f.read(5) 读到「全国大学生」,再 f.read() 只剩「智能汽车竞赛」,而 tell() 返回的是 15 不是 5,因为 UTF-8 下一个中文占 3 字节。
承重词: 文件句柄与它的指针、训练集/验证集的划分(含「为什么必须乱序」)。
从 06 挪进来的: pandas 那一半(读 csv 变成表格)并进 §3.8;数组那一半挪去 04;画曲线那一半挪去 09§3.5。
04 图像在计算机里是什么(§6.3–6.4)
- §3.1 进来时以为图片就是「一张图」 → 出去时知道它是一个数组:图切成像素,每个像素一个 0–255 的灰度值,一张灰度图就是一个二维数组,一张彩色图是三个同尺寸灰度图叠起来 ← 承重词一,第 10–14 章全部建在这一句上
- §3.2 进来时以为「数组」就是 Python 的列表 → 出去时知道处理图像用的是另一种东西:numpy 的多维数组(ndarray) —— 里面的数必须是同一种类型,换来的好处是可以整块一起算(
img / 255一句就把六十万个像素全除了一遍,不用写循环)。这一小块原来排在 06,太晚了 —— 这一章的第 一句就是「图像是一个数组」,正需要给这个东西正名,而 12§3.4「蒙版就是一个 numpy 数组」到时才接得上 - §3.3 进来时以为拿哪个库读图都一样 → 出去时知道 PIL 给的通道顺序是 RGB、OpenCV 给的是 BGR,不转换就会看到一张蓝红颠倒的图,以及两边怎么互转 —— 这是实战最容易翻车的一处
- §3.4 进来时以为图能随便存成任何格式 → 出去时知道 png 是 RGBA 四通道、jpg 只有三通道,直接 save 会报
cannot write mode RGBA as JPEG,要先 convert;以及thumbnail((150,100))给出的是 133×100,因为它等比缩放 - §3.5 进来时以为缩放旋转翻转都只是「改图」 → 出去时知道有一条分界线:几何变换只改像素的位置、不改像素的值;滤波与增强反过来只改值不改位置 —— 这条线到第 13 章会直接决定「哪种数据增强能用、哪种会毁掉数据」
- §3.6 进来时以为颜色就是 RGB → 出去时知道三套坐标各为谁而设:GRAY 一个通道 256 级;RGB 256³ ≈ 1677 万色,远超人眼能分辨的;HSV(色调/饱和度/亮度)是 1978 年为视觉感知设计的,而 RGB 是为硬件设计的 —— 这就是第 12 章调车道线阈值时调的是 HSV 而不是 RGB ← 承重词二
- §3.7 进来时以为提取车道线要写复杂算法 → 出去时知道最朴素的两下就能出东西:
convert()二值化、ImageFilter.CONTOUR提轮廓、ImageEnhance.Contrast拉对比度 —— 第 12 章那条「不训练模型」的路线就是从这里长出来的
主走查: 同一张 640×480 的车前道路图,五次变身 —— PIL 打开取某个像素得到 (R,G,B);cv2 打开同一点得到 (B,G,R);转灰度变成一个 480×640 的 0–255 数组;resize 到 128×128;再转成 HSV。每一步 写出这一个像素点的具体三元组。 (这些像素值是我们为演示编的,不是书里的数 —— 书的图片本体不在清洗文本里。) 承重词: 像素与灰度值(图像 = 数组)、色彩空间(RGB / BGR / GRAY / HSV)。
05 同一段逻辑怎么复用,顺手写出第一个神经元(第 4 章)
- §3.1 进来时以为复制粘贴也能写完程序 → 出去时知道复用靠「拆成模块 + 让模块之间尽量松耦合」,而函数是最小的那个模块;并知道形参和实参不是一回事
- §3.2 进来时以为传参就是按顺序填 → 出去时知道位置/关键字/默认/可变四种参数各管什么场合,以及函数里改一个列表为什么会连带改到外面那个(组合类型没被真正重建时,动的就是全局那一份)
- §3.3 进来时会用现成对象的方法(03§3.1 讲过点号是什么),但不知道自己怎么造一个 → 出去时会写一个类:
class起头、__init__在实例化时先跑一遍、每个方法第一个参数必须是 self(它代表「当前这一个实例」);并知道 Python 的「私有属性」只是君子协定,外面照样调得到 ← 承重词一。「对象 = 数据 + 只对它有效的操作」这一小块已经前移到 03§3.1,这里不重讲 - §3.4 进来时以为神经网络高深莫测 → 出去时知道它的最小单元只有两步:每个输入乘一个权重、全部加起来再加一个偏置(z = w₁x₁ + w₂x₂ + … + b),然后把 z 交给一个激活函数决定输出什么;这就是 1957 年罗森布拉特提出的单层感知器,一个二分类的线性模型,灵感来自生物神经元(树突接收、细胞体加权求和、轴突控制输出) ← 承重词二
- §3.5 进来时以为函数式和面向对象是两套本事 → 出去时看到同一个感知器被写了两遍(实例 4.16 函数版、实例 4.22 类版),书自己说「功能完全一样,只是编程的思路转变了」——差别只在「数据跟着谁走」
- §3.6 进来时以为「智能车的数据怎么来」很复杂 → 出去时知道就一条流水线:人遥控车沿赛道走 → 车头摄像头按帧率拍前方 → 同时记下此刻手上的转向幅度 → 归一化到 [−1,1] → 存成「图片名 → 转角」;第 13 章训练用的数据就是这么来的。「归一化」这个词在这里首现,就地讲透最朴素那一层:把量程不同的数按同一把尺子压到同一个区间,这里是把摇杆能转的幅度压成 −1 到 1 之间的一个小数,好处是换个手柄、换台车,数字的含义不变;它对训练还有另外两个好处,09§3.3 讲。 本章是采集流水线唯一讲一次的地方 —— 13§3.1 不再重讲,只回指这里,然后讲这个任务自己的新东西
主走查: 书里实例 8.1 那四个点 —— 标 +1 的 (5,4) 和 (4,5),标 −1 的 (1,2) 和 (3,2)。 取一组权重和偏置(w=(1,1)、b=−6 是我们为演示定的,不是书给的),逐点算 z 再过 sgn: (5,4) → z = 5+4−6 = 3 → +1;(1,2) → z = 1+2−6 = −3 → −1。同一笔账用函数版走一遍、再用类版走一遍,结果一模一样。 走查末尾必须留一句许诺并记账:「这组权重是我们硬塞进去的;它本来该是学出来的 —— 怎么学,第 06 章讲。」 承重词: 类与实例、单层感知器(权重 、偏置、激活函数)。 留名账: 归一化(§3.6 就地解释,09§3.3 讲它对训练的两个好处)。
06 让机器自己找规律(§7.3–7.5)
- §3.1 进来时以为「让机器学习」是让它读书 → 出去时知道它只做三件事:①划定一堆候选函数(选模型,「模型就是一组函数的集合」)②定一把尺子量答得有多差(损失函数)③在候选里找那把尺子读数最小的(优化,书说这是「最重要也是最困难的」);并知道目标不是把训练数据答对,是泛化
- §3.2 进来时以为机器学习任务五花八门 → 出去时会按两个轴给任何任务归位:按任务分回归/分类/聚类(以及降维、密度估计、排序学习),按有没有标签分有监督学习 / 无监督学习 / 半监督学习(书还给了半监督成立的前提:「数据的分布必然不会是完全随机的」);并知道本书两个任务各在哪一格(巡航 = 有监督的回归,检测 = 有监督的分类 + 定位)
- §3.3 进来时以为「这条线画得好不好」只能凭眼睛 → 出去时看到书用三步把损失函数逼出来:直接算预测减真实会正负抵消、误差趋近 0 → 取绝对值又在 0 点不可导(左导 −1、右导 +1)→ 所以取平方。每一步都是被上一步的毛病逼出来的 ← 承重词一
- §3.4 进来时以为找最优参数要靠解方程 → 出去时知道是蒙着眼下山:看脚下哪个方向最陡,朝反方向挪一小步,再重新看;「一小步有多小」就是学习率,太小走不到、太大在谷底来回振荡 ← 承重词二
- §3.5 进来时以为参数可以一个一个轮流更新 → 出去时知道必须同步更新:两个参数的更新公式里都同时含着对方,先更新一个再拿它算另一个,等于用被污染过的值 —— 这条口径第 08 章还会再用一次
- §3.6 进来时以为 05 章那个感知器的权重是人定的 → 出去时看到它被学出来的全过程:书 8.1.2 给的更新式是「权重 += 学习率 × (真值 − 预测) × 输入」,拿实例 8.1 那四个点(标 +1 的 (5,4)(4,5)、标 −1 的 (1,2)(3,2))从 w=(0,0)、b=0 起手,一个点一个点地更新,几轮之后那条分界线自己站到了两组点中间。这一节补的是 05 和 07 之间的缝 —— 05 只讲了结构、07 从多层起步,而「权重从哪儿来」正是主线的第 5 步(书给了数据和 η 的范围 (0,1),中间每一步的数是我们按这条式子算出来的,要当场写明)
- §3.7 进来时以为线性回归是教科书习题 → 出去时看到它在这本书里的真实用途:把直道上采到的 42 个路径中心点拟合成一条车道中心线,供车跟着走
主走查: 直道上采到的那批路径中心点 (x, y)。同一组点上把三版损失函数各算一遍 (第一版算出来接近 0、骗人;第二版有值但 0 点不可导;第三版平方损失可导又光滑), 再用梯度下降走三步,每步写出 θ₀、θ₁ 和损失的具体数值。 (具体坐标在书里是 csv 文件、不在正文,所以走查用的点是我们编的,要当场写明。 42 个点这个数是书给的。) 副走查(§3.6,允许另起): 感知器在实例 8.1 那四个点上把权重学出来。 承重词: 损失函数、梯度下降与学习率。 已移出本章: numpy 数组 → 04§3.2;pandas 读表 → 03§3.8;matplotlib 画损失曲线 → 09§3.5(那里才第一次真的要靠曲线判断该不该停)。
07 从一个神经元到一张网(§8.1–8.2.2)
- §3.1 进来时以为一个感知器就够用 → 出去时知道它只画得出一条直线,遇到异或这种要两条线才分得开的题就无解 —— 1969 年明斯基和佩珀特把这件事写成书,神经网络的第一个冬天从这里开始。(书第 1 章讲了低谷、第 4 章讲了感知器,但没把这两件事连起来 —— 这一节补上,依据我们书架上的 genius-makers)
- §3.2 进来时以为「多层」只是把感知器摞高 → 出去时知道摞高之后能拼出任意复杂的函数;书拿傅里叶变换和泰勒展开作类比说「有理论支撑」,但没点名 —— 它指的是通用逼近定理,而书自己的参考文献 [15] 就是 Cybenko 1989;同时知道这条定理只保证「表示得出来」,不保证「学得到」
- §3.3 进来时以为「全连接」是个形容词 → 出去时知道它是字面意思:除输入层和输出层外,每一层的每个节点都与上下层节点全部相连;并知道「深度」的两个含义(层数深 / 学到的特征深)、浅层网络大多 3–5 个隐藏层、隐藏层过多会过拟合 —— 这个词在这里首现,就地给一段:模型把训练那批数据背下来了,在训练集上分数很漂亮,一换成没见过的数据就掉下来;层数越多、参数越多,越容易背而不是学(怎么治它,11§3.3 讲 Dropout;数据这一侧怎么治,13§3.2–3.3 讲)
- §3.4 进来时以为信号在网络里怎么走很神秘 → 出去时能自己把一组数从输入一路算到输出:两个输入、 一个两神经元的隐藏层、一个两神经元的输出层,十个参数全都给了,一步一个具体数字
- §3.5 进来时以为激活函数是可有可无的装饰 → 出去时知道它的全部职责就是掰弯:去掉它,整张网无论多少层都只是输入的线性组合,一百层等于一层;所以激活函数必须是非线性的。拿主走查那张网把 sigmoid 换成恒等函数,当场把退化后的输出算给读者看 ← 承重词一(「非线性」这个词在这一节引入并讲透,后面 §3.6、第 10 章都要用)
- §3.6 进来时以为 sigmoid 挺好用 → 出去时知道它两头压得太平(饱和性),导数趋近 0,误差信号往回传几层就没了 —— 这就是梯度消失;ReLU 正是为此而来 —— 就是第 02 章 §3.3 留过名的那个 max(0, x):大于 0 原样输出、小于等于 0 输出 0。它在 x>0 那一侧的导数恒为 1,信号传多少层都不衰减,代价是负半轴梯度为 0、会「死」掉一批神经元,于是又有了 Leaky-ReLU 和 ELU ← 承重词二。第 02 章埋的 ReLU 伏笔在这一节收
- §3.7 进来时以为分类模型直接吐类别 → 出去时知道最后一层用 Softmax 把一串数压成加起来等于 1 的概率分布,并知道它为什么总和交叉熵配对出现(理由留到第 09 章讲,那里才第一次真的用上)
主走查: 书里 BP 实例的前半程。i₁=0.02、i₂=0.7,w₁…w₈ 与 b₁、b₂ 全部给定, 算出 net_h1 = 0.442 → out_h1 = 0.6087 → … → 第一轮输出 [0.62586, 0.59613],与期望 [0, 1] 差得很远。 (两端的数是书给的,中间值是我们按书给的初值自己算出来的 —— 公式本体在书里是图片。已核过:算出来对得上书给的输出。) 承重词: 激活函数(为什么非有不可)、梯度消失。
08 它怎么知道自己错了,又该往哪儿改(§8.2.3–8.2.6)
- §3.1 进来时以为「错了多少」很好算 → 出去时看到上一章那个输出 [0.62586, 0.59613] 离期望 [0, 1] 到底差多少:用第 06 章那把平方尺子当场量出 0.27741(两个神经元各算一笔再相加),并知道这个数不只是给人看的分数 —— 它本身就是反向传播的起点。末尾一句话交代:另有一整类尺子量的不是「两个数隔多远」,而是「两个概率分布差多远」,那一类里最常用的叫交叉熵,09§3.4 真正用上时再讲(MSE / L1 / Smooth / Huber 那张清单退回原文地图表格,MSE 在 13§3.7 用得上时正名)
- §3.2 进来时以为梯度下降就是「把所有样本算一遍」 → 出去时知道那样太慢,于是有三档:全部算一遍(准但慢)→ 每次随机抽一个(SGD,快,但一个样本的方向很抖,容易困在局部最优和鞍点)→ 折中取一小批(mini-batch,书说今天讲梯度下降默认就是它)
- §3.3 进来时以为步子只能一直照梯度走 → 出去时知道还能给它加三样东西:加惯性(Momentum,γ 常取 0.9,方向一致就加速、不一致就衰减,代价是会「盲目跟随」冲出坡底)→ 先按惯性探一步再在那儿算梯度(Nesterov)→ 每个参数各自调自己的步长(Adam,论文取 β₁=0.9、β₂=0.999、α=0.001,是今天的默认选择)
- §3.4 进来时以为一万个参数每个都得单独试一遍 → 出去时知道链式法则让一次回传就算清所有参数各 自该往哪挪:每个节点都是一个复合函数,把沿路的导数乘起来就行 ← 承重词一
- §3.5 进来时以为「反向传播」就是训练算法 → 出去时知道它只负责算梯度,真正拿梯度去改参数的是梯度下降 —— 书特意澄清了这一点(「这里梯度下降法才是学习算法」),而这是最常被搞混的一处 ← 承重词二
- §3.6 进来时以为反向传播的公式看不懂 → 出去时能自己更新一个权重:∂E/∂w₅ 拆成三项相乘 —— 总误差怎么随输出变 × sigmoid 的导数 × 净输入怎么随这个权重变 —— 每一项都是一个具体的数
- §3.7 进来时以为靠近输入端的权重同理可得 → 出去时知道它多一层:隐藏层那个神经元的输出同时流向两个输出神经元,所以两条误差路径要相加;并知道一轮之内所有参数都拿更新前的旧值算(同步更新,和第 06 章那条口径是同一条)
- §3.8 进来时以为改一次就该看到效果 → 出去时看到真实的数字:更新一次把总误差从 0.27741 压到 0.26537(只降了 4%),更新一万次才压到 0.000063612,输出从 [0.62586, 0.59613] 变成 [0.00793, 0.99198] —— 期望是 [0, 1]。本章一律说「更新一次 / 更新一万次」,不用「轮」这个字 —— 「一轮 = 整个训练集过一遍」是另一件事,09§3.2 才正名
- §3.9 进来时以为这些式子只能一行行手推 → 出去时知道把计算过程画成图(节点是数据、边是操作)之后,求导就变成沿着图往回走一遍 —— 书引入计算图的理由很直白:「仅使用公式来描述的话会很复杂,如 8.2.5 小节所示」;而这张图正是 PyTorch / TensorFlow / 飞桨共同的底座
主走查: 接第 07 章那张网的后半程,同一组数字一路走到一万轮。 每个承重 机制在这条走查上各占一步:损失函数 = 算出 0.27741 那一步;链式法则 = 拆成三项那一步; 反向传播 = 把梯度算出来那一步;梯度下降 = 拿梯度改 w₅ 那一步;同步更新 = 算 w₁ 时 w₅ 还用旧值那一步。 承重词: 链式法则、反向传播(它只算梯度,不是学习算法)。 我们复现的两件书没写的事(写正文时必须交代): 这条走查我们完整重算过 —— 学习率取 0.5、并且那一万次里只更新八个权重、两个偏置一直没动,才能一分不差地得到书给的 [0.00793003941675692, 0.9919789331998798];换成连偏置一起更新,第一次更新后的总误差是 0.2641 而不是书写的 0.26537。这两件事书里都没写,是我们算出来的,要明写成我们的复现结论。
09 五步固定动作:把原理变成一个跑得起来的项目(§8.3–8.4)
- §3.1 进来时以为要自己写反向传播 → 出去时知道三个框架替你干了这件事:TensorFlow(Google 大脑出品,2015-11-09 开源,名字直译就是「张量在数据流图上流动」——张量在这里首现,就地一句:标量、向量、矩阵一路往上推的统称,说白了就是一堆按维度码好的数,一张彩色图就是一个三维张量)、PyTorch(书说「可以看作是加入了 GPU 支持的 numpy」)、飞桨 PaddlePaddle;并知道本书全程只用飞桨的真实原因(赛事指定 + 编者立场),这是要和技术论断分开看的一处
- §3.2 进来时以为每个项目都得从头设计 → 出去时拿到一套五步固定动作:数据处理 → 模型设计 → 模型训练 → 评估调优 → 部署应用;第 10、13、15 章的每个项目都是这五步的一次填空。「轮(epoch)」这个词在这里正名并写死口径:一轮 = 整个训练集完整过一遍(区别于 08 章那种「更新一次 = 拿一批数据改一次参数」);13 与 15 之后一律写「轮(epoch)」,不再换叫法
- §3.3 进来时以为「数据处理」就是把图读进来 → 出去时知道它是五件事:导入、形状变换、划分、归一化、封装成批;而归一化最容易被跳过、后果最大 —— 书给了两个具体好处:不同参数的损失曲线变规则、学习率能设成统一的值;以及每个特征的权重大小可以直接当作这个特征的贡献度 ← 承重词一
- §3.4 进来时以为分类任务的损失随便挑一个 → 出去时知道为什么用交叉熵:模型吐出三个概率 [0.7, 0.2, 0.1],正确答案是第一类,交叉熵给的惩罚是 −ln(0.7) ≈ 0.357;答对得越有把握罚得越轻,答错得越笃定罚得越狠 —— 这就是第 07 章 Softmax 那一节欠下的账 ← 承重词二
- §3.5 进来时以为跑一次就知道模型好不好 → 出去时知道要用没参与训练的那份数据去评估,而且要把损失画成一条随轮次下降的曲线来看(这条曲线用 matplotlib 画,从 06 挪过来的:一块画板 figure,上面摆一张或几张子图 axes;这里才第一次真的要靠它判断该不该停);指标不止「准确率」一个,精确率与召回率完整在 15§3.5–3.6 讲。并记住书自己那句老实话:单张图片的结果并不能反映模型的效果
(原 §3.6「这本书的代码全是图片」不再单占一节 —— 它不是机制,降为「边界与局限」里的一段。)
主走 查: VOC2005 里的一张汽车图走完五步 —— 读进来 → resize + 归一化 → 按 9:1 切训练/验证 → 送进全连接网 → 交叉熵损失 + Adam 优化器(lr = 0.1)训练 → 预测输出「汽车 1.00 / 摩托车 0.00 / 货车 0.00」。 承重词: 归一化、交叉熵。
10 图像为什么不能用全连接(§9.1–9.2 + §9.4)
- §3.1 进来时以为全连接网加大就能处理图像 → 出去时会自己算这笔账:16×16 的小图接三个千神经元的隐藏层,权重 2 266 000 + 偏置 3 010 = 2 269 010 个参数;而车上的图是 640×480。参数爆炸不是形容词,是加法的结果 ← 主走查起点
- §3.2 进来时以为参数多只是费显存 → 出去时知道它还带来另外两个病:结构不灵活(图必须先拉成一条线,像素之间的上下左右关系全丢了)、极易过拟合
- §3.3 进来时以为每个神经元都得看整张图 → 出去时知道只让它看一小块就够 —— 书的原话是「通过猫鼻子这一块区域就能识别出图片中是猫」;640×480 全连接一个神经元要更新 307 200 个参数,只看 16×16 的一小块只要 256 个。这一小块在输入图上对应的区域就是感受野 ← 承重词一
- §3.4 进来时以为一小块一小块地看、参数还是会累加 → 出去时知道让所有小块共用同一组权重(权值共享),于是「3×3 的输入配一个 2×2 的核」只学 4 个参数,同规模的全连接要 36 个
- §3.5 进来时以为参数少了模型就变笨 → 出去时知道书自己提了这个问题也自己答了:一个卷积核只提一种特征,那就并排放很多个核(多核卷积);再加上多通道输入各自卷完再累加成一张图(多通道卷积),凑出实际用的「多通道多核卷积」 ← 承重词二
- §3.6 进来时以为卷积输出尺寸得死记 → 出去时会算:输出边长 =(输入 − 核 + 2×填充)÷ 步长 + 1;6×6 输入配 3×3 核、步长 1、不填充 → 4×4,当场核一遍;并知道卷积层实现的其实是数学上的互相关运算,卷积核也叫滤波器
- §3.7 进来时以为一直卷下去总能看到全局 → 出去时知道卷积核就 3×3、5×5,卷很多层视野仍然有限(书的话:「假设只采到了一个轮胎的范围,那么怎么可能只通过这个轮胎来识别出整辆车」);池化把图缩小,4×4 特征图经 2×2 池化后一个像素的感受野从 3×3 涨到 4×4;最大池化留最突出的、平均池化做模糊;并知道书的一条判断:网络浅时池化可能更好,网络深时用带步长的卷积替代池化可能更好
- §3.8 进来时以为卷积网最后还要人来判断 → 出去时知道特征图被拉平成一维向量送进全连接层,再交给 Softmax(分类)或 tanh(回归);并走一遍斑马线二分类项目 —— 含斑马线的马路 vs 不含斑马线的马路,五步固定动作的第二次填空
主走查: 一张 16×16 的手写数字图。先按全连接算出 2 269 010 个参数(书给的数,我们核过加得上), 再换成只看一小块(256 个)、再共用权重(4 个 vs 36 个),最后走一遍 6×6 → 3×3 核 → 4×4 → 2×2 池化 → 2×2。 同一张图,参数从二百多万降到几个 —— 这条降幅本身就是这一章的论点。 承重词: 感受野、多核 / 多通道卷积。
11 五个模型,一部 CNN 进化史(§9.3)
- §3.1 进来时以为 CNN 是一次发明出来的 → 出去时知道它是一路被问题推着长的,而每一代解决的都是上一代留下的具体毛病
- §3.2 进来时以为「卷积网络」是个抽象结构 → 出去时能把一张 32×32 的手写数字图在 LeNet-5 里逐层走完(原 §3.7 那两处勘误就地并进这一节,不再单占一节:书写 LeNet 是 1988 年,而书自己的参考文献 [17] 是 LeCun 等 1998 年;同一段里先说 S2 用最大池化、后说「采用平均池化后,均值乘一个权值参数加一个偏置」,前后矛盾 —— LeNet-5 用的是带权重与偏置的平均池化):C1 六个 5×5 核 → 6 张 28×28(156 个参数)→ S2 下采样 → C3 → 16 张 10×10(1516 个参数)→ C5 一百二十个核(48 120 个参数)→ 输出层(850 个参数);并看到一条重要观察:卷积层参数少,大量参数堆在全连接层
- §3.3 进来时以为 2012 年那次夺冠靠的是新结构 → 出去时知道 AlexNet 真正带来的是四件工程手段:数据增强(拿现有图片变着花样造出更多训练样本 —— 怎么增强、什么增强会毁掉数据,第 13 章讲)、ReLU 换掉 sigmoid、局部响应归一化、Dropout;Dropout 治的正是第 07 章 §3.3 讲过的过拟合,做法是训练时随机让一部分神经元当次不参与,逼着网络不能只靠某几个神经元的固定组合 ← 承重词一
- §3.4 进来时以为卷积核越大看得越多 → 出去时知道 VGG 反过来全用 3×3:两个 3×3 的感受野等于一个 5×5,参数却只要 18 个 vs 25 个(72%);三个 3×3 等于一个 7×7。VGG 也证明了深度有好处但有上限,最后停在 16–19 层、1.38 亿参数
- §3.5 进来时以为一层里必须先选定用哪种核 → 出去时知道 GoogleNet 的答案是「都要」:1×1、3×3、5×5 卷积和 3×3 池化并联成一个 Inception 模块,再用 1×1 卷积把通道厚度压回去;结果 22 层的它只有约 500 万参数,是 VGG16 的 1/27
- §3.6 进来时以为网络越深越准 → 出去时看到那个反直觉的现象:56 层比 20 层的误差还大(退化问题);ResNet 加一条跳过两层的直连,让这一段至少有能力「什么都不做」,H(x) = F(x) + x;并看到 Bottleneck 把同一段的参数从 1 179 648 压到 69 632 ← 承重词二
主走查: 一张 32×32 的手写数字图,先在 LeNet-5 里逐层走完(每层的参数数都是书给的), 再拿同一张图问「如果换成 AlexNet / VGG16 / GoogleNet / ResNet,它会经历什么、代价是多少参数」—— 6000 万 / 1.38 亿 / 500 万 / (BasicBlock 1 179 648 vs Bottleneck 69 632)。 承重词: Dropout(以及它治的过拟合)、残差连接。
12 让车跑起来(一):不训练模型的那条路(§10.1 后半 + §10.2)
- §3.1 进来时以为「让车自己跑」只有训练模型一条路 → 出去时知道这一章起有三条并列的路,而这本书三条全走了、还全都部署到了实车上 —— 这是它在整个书架上不可替代的地方;第一条路一个模型都不训
- §3.2 进来时以为车道线检测很难 → 出去时知道赛 场是用绿色把车道线和背景分开的,于是「挑出跟周围颜色不一样的那几条」就够用;并知道创意赛用纯摄像头方案,书自己说这可以理解成对特斯拉、Mobileye 那类纯视觉方案的模拟
- §3.3 进来时以为把图转成灰度就能看到车道线 → 出去时看到灰度图里除了车道线还有标识物、障碍物、背景元素,必须先把不关心的地方挡掉 ← 承重词
- §3.4 进来时以为「挡掉」要写复杂算法 → 出去时知道蒙版就是一个 numpy 数组:不关心的位置置 0、关心的置 255;而因为摄像头是斜着往前看的,车道线在画面里是一个近大远小的梯形,蒙版就照这个形状挖;套上去剩下的那块叫感兴趣区域(ROI)
- §3.5 进来时以为阈值随便定一个就行 → 出去时知道阈值要在 HSV 空间上调(第 04 章讲过 HSV 是为视觉感知设计的),而且要开两个窗口 —— 原图和二值图并排 —— 拖着参数调节器一点点试,调好的数字手抄进
follow.py - §3.6 进来时以为图上方检测不准是算法不行 → 出去时拿到书给的四条对策:缩小 ROI、调阈值、加滤波、改用自适应阈值;并知道这条路线的真正代价 —— 换个光照、换块场地就得重调一遍,参数是人调的、不是学的
- §3.7 进来时以为跑起来就算完 → 出去时知道实车还有三步校准:摄像头位置(后轮抵住起始框,调到画面里刚好看见白色启动区、左右车道线大致对称)、颜色阈值、巡线参数(正常速度、看到地标后的减速值、直行与转弯时角速度各自的放大比例)
主走查: 一张 640×480 的车前道路图 —— 转灰度 → 挖一个梯形蒙版 → 套出 ROI → 在 HSV 上做阈值 → 得到一张只有 0 和 255 的二值图,里面只剩车道线 → 由它算 出偏差 → 乘上放大比例变成角速度。 承重词: 蒙版与感兴趣区域(ROI)。
13 让车跑起来(二):让网络自己吐出一个转角(§10.3)
- §3.1 进来时以为深度学习方案总得有人先坐下来一张张画标注 → 出去时知道这个任务根本不需要人工标注:标签是开车开出来的 —— 采集那条流水线 05§3.6 已经完整讲过(手柄→归一化→配成「图 ↔ 转角」),这里只回指一句,然后讲这一章自己的新东西:手柄经 USB 扩展器接到板卡;采集速度设在 20 左右(要同时迁就人的反应速度和摄像头帧率);尽量不要出现跳跃性的大幅度转向(否则模型学不出光滑的控制);最后按 8:2 随机切训练集与验证集
- §3.2 进来时以为数据不够就做增强(翻转、平移、缩放) → 出去时知道几何变换在这个任务上会毁掉数据:把图水平翻过来,画面里该左转了,标签却还写着右转。书的原话是「自动巡航模型需要根据图像中车道的位置、角度等信息来匹配其与小车转向角度之间的关系,因此,几何变换在此并不适用」——这是全书最漂亮的一处推理
- §3.3 进来时以为那就没法增强了 → 出去时知道颜色变换全部可用而且正对症:赛场的光照、地图反光、印刷色差都会大幅影响预测,所以加滤镜、改色调、调对比度/亮度/饱和度五类都上,而且要随机地上,免得产生新的特征集中
- §3.4 进来时以为分类模型改改就能预测转角 → 出去时知道差别只在最后一层:分类是接到 n 个神经元(n = 类别数)上、输出一组概率;回归是接到一个神经元上、输出一个连续值 ← 承重词一
- §3.5 进来时以为直接拿 AlexNet 或 ResNet 来用就好 → 出去时知道为什么要自己设计一个小网络:模型要跑在车上那块算力有限的板子上;所以输入压到 128×128(远小于 AlexNet),参数量只有它的约 1/4 —— 第 01 章埋的那条「边缘算力反过来约束模型设计」在这里第一次真的兑现
- §3.6 进来时以为填充(padding)只是为了凑尺寸 → 出去时拿到书给的具体理由:3×3 卷积下,图像边缘的部分像素最少只被卷过 1 次,而中心区域的像素能被卷到 9 次,不填充就等于承认边缘信息提取不充分;何况不填充特征图还会越缩越小
- §3.7 进来时以为模型结构只能照抄 → 出去时能把一张图逐层走完,并知道四个设计选择各自的理由:激活用 leaky-ReLU 而不是 ReLU(避免负半轴整片神经元不再更新)、加 dropout 且丢弃率只取 0.1(网络本来就小)、最后加 tanh(因为标签本来就归一化在 [−1,1])、损失用均方差而不是绝对误差(损失大时梯度大、趋近 0 时梯度也趋近 0,收尾更精确) ← 承重词二
- §3.8 进来时以为损失越低模型越好 → 出去时记住这本书最值钱的一句经验:训练 300 轮(epoch,一轮 = 一万多张图整个过一遍,口径见 09§3.2)约 180 分钟,损失从 0.219 降到 0.019,250 轮左右就趋于平稳;训到 400 轮损失更低,可实车跑起来反而更差
- §3.9 进来时以为训练完就能上车 → 出去时知道部署要走一遍:导出 model(结构)与 params(参数)两个文件 → SSH 连上车上的板子 → 上传 → 改配置里的 模型路径与摄像头编码 → 跑起来;验证也分两层,先对静态图片预测转角并把数字打在图上,再实车路试;并记住书的老实话 ——「无论何种模型,在应用时都难以获得 100% 的成功率」
主走查: 一张 128×128×3 的车前道路图 —— conv1 32×65×65 → pool1 32×64×64 → conv2 32×33×33 → pool2 32×32×32 → conv3 64×17×17 → conv4 64×10×10 → conv5 128×6×6 → pool5 128×5×5 → 拉平成一维 → 三层全连接(leaky-ReLU,中间夹 dropout)→ 最后一层只有 1 个输出 → tanh → 一个 [−1,1] 的数。 (逐层尺寸全部是书给的。) 承重词: 回归输出(为什么最后一层只有一个神经元、为什么要 tanh)、这个小网络的四个设计选择。
14 让车跑起来(三):逐像素分出车道线,以及三条路怎么选(§10.4 + 我们的对比)
- §3.1 进来时以为「认出车道线」就是画几条线 → 出去时知道还有第三种问法:给图上每一个像素判一个类别 —— 背景 0、实车道线 1、虚车道线 2、斑马线 3,输出是一张和原图同尺寸的灰度图;这就是线上赛那 16000 张标注数据的形状 ← 承重词一
- §3.2 进来时以为分割只有一种 → 出去时能区分三种:语义分割(每个像素判类别)、实例分割(先检测再在每个框里勾出实例)、全景分割(两者都要,同类的不同个体也分开)
- §3.3 进来时以为标注就是画框 → 出去时知道这里要沿目标边缘画闭合多边形,并拿到几个会让人白干一天的坑:自动保存要开、把图像数据一起存要关、手标背景时类别名必须写成
_background_(否则格式转换直接出错)、有孔洞的目标要先标外轮廓再沿孔洞画 - §3.4 进来时以为逐像素分类跟分类一样便宜 → 出去时知道它运算量大得多,而减运算量只有两条路且都伤精度:缩小图片(丢细节)或降低模型复杂度(削弱特征提取);BisenetV2 的解法是分两支 —— 一支专管精细的局部信息,一支专管全局上下文,最后用一个融合层合起来 ← 承重词二
- §3.5 进来时以为部署都一样 → 出去时知道这条路换了硬件(Jetson Nano),而且装框架要按 JetPack 的版本去官网找对应的预编译包 —— 版本对不上就装不上;这也说明部署环境本身就是一项技术约束,不是「照着敲命令」
- §3.6 进来时以为分割做完车就会走了 → 出去时知道这条链在书里断在中间:书的最后一句只说「借助推理输出的图像,辅以相关算法(如提取中心线),即可完成小车的自动巡航功能」就打住了。边界要划准 —— 最后那一步(转角 → 左右轮转速)书是给了的,就是第 2 章那个差速转向;真正没交代的是中间两步:怎么从分割图提出中心线、怎么从「车偏了多少」算出该打多大的转角(闭环控制,PID 只在第 4 章的一道习题里出现过)。而按 01§3.8 那张图,这本书的范围本来就只到感知那一段
- §3.7 进来时不知道该选哪条路 → 出去时拿到一张三条路的对比表:要不要标数据 / 要不要训练 / 换个场地要重做什么 / 算力吃多少 / 输出的是什么 / 部署在哪块板子上,以及各自在什么情况下是对的选择。(这张表是我们做的,书把三条路并排讲了却没有做过比较)
主走查: 还是那张车前道路图。这一次它的输出不是一个数,而是一张同尺寸的灰度图 —— 每个像素上写着 0/1/2/3。把它和第 12 章的二值图、第 13 章的那个 [−1,1] 的数摆在一起, 同一张输入图,三种输出形状 —— 这就是三条路线的根本区别。 承重词: 语义分割(逐像素分类)、BisenetV2 的双分支。
15 认出路上的东西:目标检测怎么做、怎么打分(§11.1–11.2)
- §3.1 进来时以为「认出路上的东西」是一件事 → 出去时知道它是两件:定位(框出在哪儿)+ 分类(说出是什么);不同场景侧重不同 —— 超市商品识别重分类、工厂消防检测重定位、医疗病变检测两个都得高
- §3.2 进来时以为检测难在算法 → 出去时知道第一道难关是图像质量:摄像头成像能力、光照与拍摄角度、雨雪雾霾扬尘,以及同一张图里目标的尺度、密集程度、遮挡程度都不一样
- §3.3 进来时以为传统方法和深度学习方法只是新旧之别 → 出去时知道分水岭在特征是谁设计的:传统方法靠人手工设计特征(Haar、方向梯度直方图、SIFT)再交给分类器;深度方法让网络自己学,代价是要大量样本和算力,好处是方便增删类别、样本越多越准
- §3.4 进 来时以为深度检测器都差不多 → 出去时知道两条路线的取舍:两阶段(先选出候选区再分类,R-CNN → Faster R-CNN → Mask R-CNN)精度高但慢,单阶段(一次同时定位和分类,SSD / YOLO)牺牲些许精度换实时性,「更受产业界青睐」—— 而车上要的正是实时
- §3.5 进来时以为「模型答对没有」只有对错两种结果 → 出去时看懂那张 2×2 的表:横着看是真相(这个东西到底在不在图里),竖着看是模型说的(它报了没报),四个格子分别是报了且报对(TP)、报了但根本没有(FP)、没报也确实没有(TN)、有却漏报了(FN);把走查里那几个框当场填进去。并就在这一节指出书这里印错了 —— 它把 FP 写成「正样本识别为负样本」、把 FN 写成「负样本识别为正样本」,两个说明互换了(
text/12-ch11-11-cnn.txt:71),照书上的定义去推后面两个比值会全错 ← 承重词一 - §3.6 进来时以为「准确率高」就是模型好 → 出去时知道那是个陷阱:正样本占 99% 时,不管三七二十一全判成正也有 0.99 的准确率;于是要在上一节那张表上算两个比值 —— 精确率 = TP /(TP+FP)(它报的那些里有几个真的对)、召回率 = TP /(TP+FN)(该找出来的里面找到了几个),两者分子相同、分母不同
- §3.7 进来时以为看两个数就够 → 出去时知道调参很难让两个数一起高,所以画 P-R 曲线,再把曲线下的面积压成一个数 AP,多类别再取平均得到 mAP —— 书的原话是「数字比曲线图更能体现一个模型的好坏」
- §3.8 进来时以为跑得快慢是硬件的事 → 出去时知道两个指标各管一头:FLOPS 只跟模型本身有关,FPS 由硬件、运行环境、编译、语言共同决定 —— 所以书要求报 FPS 必须同时报处理器型号、主频、内存、操作系统、软件版本和语言
- §3.9 进来时以为「一次同时定位和分类」是句口号 → 出去时知道单阶段检测器具体怎么做到:先在几个不同尺度的特征图上分头找(浅的那几层看得细、管小目标,深的那几层视野大、管大目标)→ 每个位置事先摆好几个固定尺寸的框,模型只学「把这个框往哪儿挪、放大缩小多少」,这些事先摆好的框叫锚框 → 判断两个框是不是框的同一个东西,靠交并比(IoU):两个框重叠部分的面积除以它们合起来占的面积,1 就是完全重合、0 就是完全不挨着 → 同一个目标上重复报的框用非极大值抑制剔掉(留分最高的那个,把和它 IoU 超过阈值的都删掉)← 承重词二。锚框与 IoU 讲完,本章主走查和 16§3.4 那个配置文件才接得上
- §3.10 进来时以为 YOLO 就是一个模型 → 出去时看到它一路被改出来的账:YOLO 首次把检测当回归问题做(448×448 输入切 7×7 网格,每格预测 2 个框)→ V2 换骨干网络(就是打头那一段专门提特征的网络)、每层加批归一化、用 k-means 按 IoU 聚出 5 个锚框尺寸、每 10 轮(epoch)换一次输入尺寸 → V3 换成带残差的 Darknet53;SSD 走的是同一条路的另一种摆法(6 个尺度、VGG16 打底)
主走查: 一张同时含斑马线和指示牌的车前道路图。模型吐出若干候选框 → 非极大值抑制剔重 → 逐个和真值框比对,数出 TP / FP / FN → 算精确率与召回率 → 画 P-R 曲线 → 压成 AP → 两个类别取平均得 mAP。 (这一走查的框数与分数是我们为演示编的,书没给具体数 —— 要当场写明。) 承重词: 精确率与召回率(以及准确率那个陷阱)、非极大值抑制与多尺度候选框。