让车跑起来(二):让网络自己吐出一个转角
这一章讲三件事: 第二条路怎么走 —— 一张图进去,一个 −1 到 1 的数出来,中间不用人拼; 一处全书最漂亮的推理 —— 「把图片翻一下」这个到处都在用的做法,在这个任务上会毁掉数据; 以及一条最值钱的经验 —— 训到 400 轮损失更低,实车跑起来反而更差。 它在全书链条里的位置是第 11 级台阶的第二段,也是全书的技术高潮。 这一章的主走查是一张 128×128×3 的车前道路图,逐层走到那个 tanh 输出。 遇到的生词都在当场解释。
1. 顶层全景
第 12 章那条路: 图 → 转灰度 → 蒙版 → HSV 阈值 → 二值图 → 算偏差 → 角速度
↑ 每一步都是人写死的规则,每个数都是人调的
这一章这条路: 图 ──────────▶ 一个卷积网 ──────────▶ 一个 −1 到 1 的数
↑ 中间那些步骤全没了,网络自己学出来一套
图说:**中间那一整段消失 了,不是被简化了,是被换掉了。**
换来的代价在第 2 节:得有人真的开着车跑出几千张带转角的图。
这一章的全程:
① 数据从哪来 ── 人遥控车跑,同时记下手上的转角(第 2 节)
│ ⚠ 这个任务**根本不需要人工标注**
▼
② 数据增强 ── 通用的两大类里,**有一类在这里会毁掉数据**(第 3、4 节)
│
▼
③ 模型怎么改 ── 和分类只差最后一层:接到 **1 个**神经元上(第 5 节)
│
├── 为什么要自己设计一个小网络(第 6 节)—— 板子扛不动
├── 为什么非要填充不 可(第 7 节)—— 边缘像素只被卷过 1 次
└── 四个设计选择各自的理由(第 9 节)
│
▼
④ 逐层走一遍 ── 128×128×3 一路走到一个数(第 8 节)
│
▼
⑤ 训练 ── 300 轮 vs 400 轮那个教训(第 10 节)
│
▼
⑥ 部署 ── 两个文件、一根网线、一个配置项(第 11 节)
2. 这个任务不需要任何人工标注
这是这条路最反直觉的一点,也是它相对第三条路最大的优势。
现象先行:深度学习不是都得先有人标数据吗
通常是的。 第 09 章那个车辆分类,得有人一张张标「这是汽车、这是货车」; 第 14 章那条路更狠,得有人一个像素一个像素地描边。
这个任务不用。理由一句话:标签是开车开出来的。
采集流水线,第 05 章已经完整讲过
这里只回指一句: 人拿手柄遥控车沿赛道走 → 车头顶端的摄像头按帧率拍前方道路图 → 同一时刻程序记下手上的转向幅度 → 归一化到 [−1, 1] → 存成「图片名 → 转角」1。
人在开车的同时,标注就已经完成了。 没有第二道工序。
这一章真正的新东西:三条现场经验
书在这一节给了三个只有真做过才写得出来的细节。
① 手柄怎么接上去:控制手柄通过 USB 扩展器与 EdgeBoard 板卡相连1。 —— 也就是说,采数据用的就是跑模型那块板子,不是另接一台电脑。
② 采集速度压在 20 左右。 书给了理由: 车速可在 0—100 的范围内自由设置,为了兼顾控制手柄时人的反应速度以及摄像头采集帧率, 速度一般设置在 20 左右1。
车开太快会怎样(两头都出问题):
人这一头 ── 反应跟不上,转角是慌乱中打的,和路况对不上
相机这一头 ── 帧率固定,车越快两帧之间挪得越远,
相邻两张图之间会有大段路没被拍到
⇒ **20 是这两个约束夹出来的数,不是随便定的。**
(「两头出问题」这个拆法是我们补的;书给的是结论和两个理由。)
③ 尽量不要出现跳跃性的大幅度转向。 书的理由是: 为了保证最后小车转向控制模型的光滑连续性,采集时要保证小车能够顺滑地连续移动1。
为什么(我们补): 模型学的是「这张图 ↔ 这个转角」的对应关系。 你猛地打一把方向,那一帧的图和上一帧几乎一样,标签却差了很多 —— 等于给模型喂了一对自相矛盾的样本。这种样本多了,模型学出来的输出就会一顿一顿的。
④ 最后按 8:2 随机划分训练集与验证集2。 (注意和第 09 章那个项目的 9:1 不一样 —— 书没有解释为什么改了比例。)
中间还有一道格式转换,顺带解释一件事
采集出来的原始数据是键值对形式的:键是图片编号(比如 001.jpg),值是转向角度,存成 JSON1;
然后要转成「图片名 + 转角」的文本格式,理由是「与后续网络训练时所需要的数据格式匹配」3。
第 03 章讲过 JSON 是什么(三维及以上的数据统一用键值对来写)。 那句理由本身比这道工序更值钱:格式转换不是为了好看,是为了迁就下游那个数据加载器认得的格式。 —— 这类「无聊但必须做」的一步,在真实项目里占的时间比想象中多得多。
3. 全书最漂亮的一处推理:翻转会毁掉数据
这是本章第一个承重点,也是这本书最值得单独带走的一条。
现象先行:数据不够,通用做法是什么
第 11 章讲 AlexNet 时提过一味药:数据增强 —— 拿现有的图片变着花样造出更多训练样本。
书在这里把它分成两大类4:
| 类 | 具体手段 | 改的是什么 |
|---|---|---|
| 几何变换 | 水平翻转、垂直翻转、平移、缩放 | 像素的位置 |
| 颜色变换 | 调滤镜、色调、对比度、亮度、加噪声 | 像素的值 |
第 04 章讲过这条分界线 —— 几何变换只改位置不改值,滤波与增强只改值不改位置。 那一章埋的账在这里还,而且还得非常漂亮。
书的判断:几何变换在这里不适用
书的原话是:
自动巡航模型需要根据图像中车道的位置、角度等信息来匹配其与小车转向角度之间的关系, 因此,几何变换在此并不适用4。
把这句话拆开,拿一个具体例子走一遍:
原样本: 一张右前方有弯道的图 标签 **+0.6**(往右打)
做一次水平翻转(几何变换):
图变了 —— 弯道跑到左前方去了
标签呢?**还是 +0.6**,因为翻转只动图片、不动标签
⇒ 现在这一对数据在说:「看到左边有弯道,请往右打。」
**这是一条错误的样本,而且是被你亲手造出来的。**
图说:注意这不是「增强效果不好」,是**造假数据**。
几何变换在分类任务上没问题(一只猫翻过来还是猫),
**在这个任务上不行,因为标签本身带方向。**
这条推理值得从这本书里单独带走,它的一般形式是:
任何「通用最佳实践」都要先问一句:它和我这个任务的标签冲不冲突。
但书漏了一个办法
判断( 我们的,不是书里的):水平翻转其实是能用的 —— 只要把标签也取负号。
图翻过来的时候,把 +0.6 改成 −0.6,这一对数据就重新对得上了 —— 画面里弯道在左边,标签也说往左打。
而且这一手在这个任务上格外划算: 赛道是一个环,人绕着跑一圈, 左转的样本和右转的样本天然不一样多;翻转 + 取负正好把两边配平。
书没有提这个办法。 它的推理没错(翻转会毁数据),但结论下得比必要的更死。
如果错,会错在: 如果车模本身左右不对称(比如摄像头没装正、或者前轮左右响应不一致), 那么翻转出来的图对应的真实转角就不是简单的取负,这一手会引入新的误差。 判据是:摄像头装正之后,同一条弯道左右两个方向跑,记录下来的转角是不是大小相等符号相反。
4. 那就只做颜色变换,而且这一类正对症
书给了现场证据,不是凭空说好
书的理由是从调试里来的:
在实际调试过程中会发现,背景环境的光照条件、现场地图反光程度和印刷色差等因素 会对模型预测结果有非常大的影 响4。
注意这三样和第 12 章那条路的死穴是同一批东西。 第 12 章那条路对付它们的办法是「重新调阈值」;这条路的办法是 「训练时就把这些变化喂给模型,让它自己学会不在乎」。
这是两条路在同一个问题上的分野,值得记一笔。
五类手段,全部只改像素的值
书用的是五类:加不同滤镜、改变色调、调整对比度、调整亮度、改变饱和度4。
工具也交代了: 滤镜用 OpenCV;色调、对比度、亮度、饱和度的变换用 PIL 里的 ImageEnhance 类;
而这些变换实际上是改变图像像素的 RGB 值,所以要先用 numpy 把图片的像素信息转成矩阵形式再算5。
第 04 章那句「图像就是一个数组」在这里第三次兑现 —— 调亮度这种听起来很「图形」的操作,落到程序里就是对一堆数做算术。