5-2留下的作业是“旋钮(潜变量)[注]从哪里来”。这次只看数据来找旋钮。我们用600个火柴人场景,制作把1,196个数字折叠成k个的编码器,以及把k个数字重新展开成1,196个的解码器。即使折叠成16个数字再展开,动作也能恢复99.8%。接着用这些折叠后的数字去除抖动、补全被遮挡的手肘、用更少的数据让5-1的乱扔垃圾检测更准确,并且无需旋钮就完成5-2的生成。
5-2的生成器 D 之所以获胜,是因为事先知道三个旋钮(速度·位置·大小)。真实数据不会附带旋钮。这次的任务是只看600个场景,自己找出旋钮。
编码器把左边的原始动作折叠成k个数字(中间的紫色柱),解码器再把它展开成1,196个(右边)。逐步减小瓶颈大小k,看看最多能折叠到什么程度。
| 瓶颈 k | 压缩比 | 解释方差 | 重建误差(RMSE) | 骨长误差 |
|---|---|---|---|---|
| 1 | 1,196倍 | 85.72% | 0.1021 | 6.16% |
| 2 | 598倍 | 95.07% | 0.0598 | 7.65% |
| 3 | 398.7倍 | 97.84% | 0.0395 | 2.78% |
| 5 | 239.2倍 | 99.02% | 0.0266 | 1.95% |
| 8 | 149.5倍 | 99.46% | 0.0195 | 1.86% |
| 16 | 74.8倍 | 99.80% | 0.0123 | 1.50% |
| 32 | 37.4倍 | 99.90% | 0.0088 | 1.43% |
测试场景中混有姿态估计的抖动(σ = 0.008)。k = 32时的误差0.0088与这一抖动大小几乎相同。误差再往下降,记住的就不是动作而是抖动了。
收集600个场景,依次找出“动作变化最大的方向”(主成分分析,PCA)。编码器从场景中减去平均场景,再投影到这k个方向上,得到k个数字。解码器则在平均场景上按这些数字加上k个方向。这是只用加法和乘法的最简单的自编码器。用神经网络构建的自编码器借助层和激活函数,连弯曲的关系也能表达(3-7)。本页的16个方向直接使用了代码 1 计算出的值。
这是用旋钮1·2标出600个场景的地图。点击一个点选择场景,再转动下面的四个旋钮。旋钮没有名字。看看什么在变化,亲自给它们起个名字吧。
同类场景会在地图上聚在一起。没有人告诉它“这是扔出”,只是折叠了一下,就出现了这样的结果。旋钮值以训练场景的标准差(σ)为单位。±2σ以外是训练数据中几乎没有出现过的区域,展开后的姿态会变得别扭。
| 旋钮 | 变化最大的部位 | 观察后起的外号 | 各类场景平均(代码 1) |
|---|---|---|---|
| 1(占动作的85.7%) | 所有关节的 x,场景后段 | “结束时在哪里” — 留在垃圾桶前,还是向右走开 | 放进 −15.3 · 扔出 +8.3 · 路过 +6.4 · 掉落 +6.7 |
| 2 (9.4%) | 右手腕·手肘和脚踝的 x,场景前段 | “一开始在哪里、右臂放在哪一侧” | 路过 −4.4 · 挥手 +4.1 |
| 3 (2.8%) | 右手腕·手肘·头部的 y | “是抬起手臂,还是弯下腰” | 挥手 +2.6 · 捡起 −2.1 |
| 4 (1.2%) | 右手腕和脚踝的 x,场景中段 | “中间时段手臂·腿的左右摆动” | 捡起 +1.3 · 扔出 −0.9 |
5-2的旋钮(速度·位置·大小)是人在制作场景时定下的。这里的旋钮则是依次挑出数据变化最大的方向得到的。所以它们没有名字,一个旋钮里混着好几种含义(旋钮 1 既表示位置,也表示“有没有走开”)。但好处是,即使人什么都不告诉它,也能找到。这就是潜在空间。
在真实的姿态估计中,关节点会抖动,手臂被身体挡住时还会丢失点。漏斗只让“长得像人类动作的东西”通过,所以折叠再展开后,抖动被滤掉,空缺也会被合理地补上。
抖动 σ = 0.01时误差0.0100 → 0.0082,σ = 0.03时0.0300 → 0.0090,σ = 0.05时0.0501 → 0.0104。抖动越大,效果越明显。因为抖动的方向杂乱无章,几乎落不到漏斗的16个方向上。
只用可见的格子以最小二乘法拟合出16个潜在数字,再用解码器展开,连空格也能补上。位置误差方面,“用肩膀和手腕的中点凑合”为0.0230,“用解码器补全”为0.0108,精确约2.1倍。5-1中提到的“手臂被身体挡住就找不到关节”就是这样弥补的。
5-1中说过“直接学习坐标需要多得多的数据”。这次先用编码器折叠关节和易拉罐的坐标(1,288个),再用折叠后的k个数字训练最简单的逻辑回归。
| 训练场景(每类) | 1,288个原样 + MLP | 折叠为 k = 8 + 逻辑回归 | 折叠为 k = 16 + 逻辑回归 |
|---|---|---|---|
| 25(共150) | 96.7% | 95.0% | 99.7% |
| 50(共300) | 97.0% | 95.3% | 100.0% |
| 100(共600) | 98.0% | 98.0% | 100.0% |
k = 16 + 逻辑回归:每类只用25个(共150个),就在300个测试场景中答对了299个。16个数字里同时折叠了“易拉罐最后在哪里”和“人去了哪里”,所以只用一条直线也能把它们分开。
k = 8 不够:易拉罐最后位置这类细小的动作藏在第9个以后的方向里,折叠成8个就会丢掉一部分。一味压缩得更狠并不是正确答案。
与5-1的92.3%不同的原因:5-1把“没有物体”标为坐标 −9,这里标为 (0, 0)。仅仅改变输入的标记方式,MLP就能升到98%。本表三列都使用相同的输入进行比较。
编码器是在没有标签的情况下只看场景做出来的(用了全部600个,但没有看乱扔·正常的正确答案)。这样得到的“动作摘要”滤掉了噪声、数字又少,所以即使带正确答案的场景很少,分类器也不会迷失方向。先用无标签数据学习表示,再用少量正确答案收尾,这种方式与当今大模型训练的基本流程相同。不过,这些数字是合成数据上的结果。在真实视频中会比这低。
5-2的评审员又来了。这次的生成器 E 不知道人定下的旋钮。它在潜在地图上查看扔出动作聚集的位置(平均值与标准差),抽取16个新点,再用解码器展开。
| 生成器 | 是否事先知道旋钮 | 骨长误差 | 抖动 | 手腕最大速度 | 多样性 | 新颖性 |
|---|---|---|---|---|---|---|
| A 单一平均 | — | 1.7% | 0.0045 | 1.36 | 0.000 | 0.033 |
| B 逐坐标掷骰子 | — | 44.2% | 0.2659 | 10.00 | 0.149 | 0.109 |
| C 挑一个复制 | — | 4.7% | 0.0300 | 2.75 | 0.125 | 0.000 |
| D 抽取 3 个旋钮 | 知道(人定下的) | 0.0% | 0.0125 | 2.71 | 0.123 | 0.022 |
| E 从16个潜在数字中抽取 | 不知道(数据找到的) | 3.7% | 0.0122 | 1.76 | 0.114 | 0.047 |
E 做到的:不知道旋钮,也没有像 B 那样散架(骨长误差44.2% → 3.7%,抖动0.27 → 0.012)。既有多样性(0.114),也没有照抄(0.047)。
E 没做到的:手腕最大速度只有1.76,扔的力道不足。它只是在潜在空间的平均值附近抽样,再把直线方向相加,所以快速挥动的短暂瞬间被抹平了。骨长也不如真实数据(0%)那么准确。
编码器·解码器同时提升了检测和生成。但仍有遗留的问题。
两段代码都只使用 Colab 默认安装的库。“下载笔记本(.ipynb)”后,在 Colab 中通过文件 → 上传笔记本打开,或者复制后粘贴到新笔记本的单元格中。
用600个场景构建 encode·decode,测量不同k下的重建误差、去除抖动、补全被遮挡的手肘(fill)。本页的16个方向就来自这里。想换成自己的数据,就在 S 的位置放入形状为(场景数, 数字数)的自己的数据。传感器·日志数据也能用同样的方法折叠。
改变训练场景数,比较5-1乱扔检测的“原样 + MLP”与“折叠 + 逻辑回归”,并用5-2评审员的评分来衡量生成器 E。第5·6节的表格直接由这段输出生成。
这是粘贴到 ChatGPT、Qwen、Claude 等 AI 助手中的提示词。点击“复制”,所选代码会一起附在提示词后面。把〔 〕里的内容换成你自己的情况即可。
AI 的回答只是起点。它建议的论文·数值·库函数,一定要对照原文和官方文档进行确认。使用真人动作数据时,要先确认拍摄同意和数据集的使用条件。
与正文中黄色虚线下划线相同的词典。点击节名即可跳到首次出现的位置。
比喻是理解的第一步,也可能是误解的种子。与人类知觉相比的比喻标注了强度。直接 有证据表明实际上在做同样的事 · 部分 只有部分性质相似 · 无 尚不知道有对应的东西
| 概念 | 日常比喻 | 人类知觉比喻 | 比喻失效之处 |
|---|---|---|---|
| 编码器·解码器 | 把衣服叠好装进旅行箱,到了住处再展开 | 部分 把一段长舞蹈记成“左脚、拍手、转圈”这样几个词,之后再重新跳出来 | 衣服叠起来还是原样,但漏斗会丢掉细小的褶皱(抖动)。丢掉的东西再展开也回不来 |
| 瓶颈大小 k | 摘要的字数限制 | 无 | 摘要是由人挑选重要内容,而这个漏斗挑的是“变化大的东西”。它可能漏掉虽小却重要的差异(易拉罐的最后位置) |
| 潜在空间 | 把餐馆标在“价格 · 辣度”两条轴的地图上 | 无 | 餐馆地图的轴由人决定,而潜在空间的轴由数据决定,而且没有名字 |
| 遮挡复原 | 根据上下文猜出被擦掉的字来阅读 | 部分 人的知觉也会根据全身的姿势猜出被挡住的手臂 | 猜测只在学过的动作范围内才准确。遇到第一次见的姿势,会错误地往已知姿势的方向补全 |
| 从潜在空间抽取 | 在地图上点一下“这一带附近”,然后去看看 | 无 | 如果点在地图上的空地(没有训练数据的地方),就会到达奇怪的地方 |
E-1 作者简介 · E-2 学术参考文献 · E-3 图片与数字的出处
这是写给讲师、在职培训(OJT)负责人和辅导老师的教学指南。时间只是预估,请根据实际课堂调整。
| 对象 | 学完5-1·5-2的大学生·职场人士。处理传感器·视频·日志等规模大、噪声多的数据的从业者 |
|---|---|
| 预备知识 | 5-2的“动作是表格”与生成器评分、向量的内积(相同位置相乘再相加) |
| 学习目标 | ① 用漏斗图解释编码器·解码器和瓶颈 k 的含义 ② 举例说明折叠再展开分别如何用于去除抖动·遮挡复原·少量数据学习·生成 ③ 说明线性编码器的局限以及需要 VAE 的原因 |
| 预计时间 | 阅读35~45分钟 · 动画演示25分钟 · Colab 实践30分钟(代码 1 10分钟,代码 2 运行1分钟 + 解读19分钟) |
导入5分钟(第1节,5-2的作业)→ 第2节 减小漏斗 k 12分钟 → 第3节 转动旋钮10分钟 → 第4节 抖动·遮挡8分钟 → 第5·6节 读表格10分钟 → 练习题5分钟。
第2节15分钟 → 讨论我们的数据(传感器、日志)的“数字个数”与“真正独立的数字个数”15分钟 → 用第4·5节讲解去噪·少量正确答案学习的案例20分钟 → 把我们的数据放进代码 1 中30分钟 → 第7节总结局限10分钟。
入门者只看第2·4节。中级者先手动跟踪代码 1 的 encode·decode,再画出 Vt[0],确认第3节的旋钮 1 为什么是“结束时的位置”。
| 节 | 停下来提问 | 操作提示 |
|---|---|---|
| 2 漏斗 | “需要几个数字,扔出动作才看起来像扔出?” | 把 k 按 1 → 3 → 8 逐步调高,观察右边的结果 |
| 3 潜在地图 | “没人告诉它,为什么同类会聚在一起?” | 选一个红点(扔出),把旋钮 1 转到 −2σ |
| 4 抖动·遮挡 | “漏斗如何区分抖动和动作?” | 同时打开 σ = 0.05 和手肘遮挡 |
| 5 乱扔检测 | “为什么 k = 8 不如 k = 16?” | 先读表格中25个那一行 |
| 6 生成 | “E 比 D 少知道什么?又在哪方面做得不如 D?” | 点击“重新生成”,查看手腕最大速度一栏 |
| 误解 | 正确解释 | 要展示的场景 |
|---|---|---|
| 压缩会减少信息,因此性能会下降 | 如果丢掉的主要是噪声,反而会变好。去除抖动、少量数据学习就是例子 | 第4节、第5节 |
| k越大越好 | k大了连抖动也会装进去;k小了会漏掉重要的细小动作。要根据目的来选择 | 第2节表格的 k = 32,第5节的 k = 8 |
| 每个潜在数字都有人能理解的含义 | 方向由数据决定,没有名字。外号是人事后加上的解读 | 第3节表格 |
| 自编码器必须是神经网络 | 最简单的线性自编码器做的事与 PCA 相同。神经网络还能表达弯曲的关系 | 第2节下方的讲解 |
| 从潜在空间随便抽一个点,都能得到像样的动作 | 只有在训练数据附近才是这样。让这个空间变得均匀,正是 VAE 的工作 | 第3节 ±2σ 以外,第6节 |
1,196 ÷ 16 = 74.75,约74.8倍。代码 1 中解释方差为99.80%。
编码器:z = (x·v₁, x·v₂) = (3, −2)。解码器:3v₁ − 2v₂ = (3, −2, 0)。第三个值0.4不落在这两个方向上,所以消失了。这就是“抖动被去除”的原理。
手肘是弯曲的,所以位于中点外侧。解码器利用从600个场景中学到的“这种姿势下手肘通常在这里”的关系(16个方向),与其他可见关节一起拟合。代码 1 中误差从0.0230降到了0.0108。
编码器只学习了动作的结构(哪些部位一起动),把1,288个数字变成了16个数字的摘要。摘要噪声少、数字少,所以即使带正确答案的场景只有150个,简单的分类器也不会迷失方向。
因为它是在潜在空间中扔出动作的平均值附近抽样,而解码器只是把各个方向直线相加。时机各有差异的快速挥动,无法用少数几个直线方向来表示,于是被抹平了。需要非线性解码器或处理时间轴的模型。
在代码 1 中,把遮挡的关节从右手肘换成左膝(9号 → 10号关节),并把遮挡区间从21帧延长到40帧。用表格报告“用解码器补全”的误差如何变化。
制作训练中没有的动作“摔倒”(在 pose 函数中把 lean 增大到1.4的场景),测量它的重建误差比六类场景大多少。并给出阈值,提出能否用重建误差做出“第一次见到的动作”警报。
| 入门者 | 只用第2节的 k 按钮和第4节的三幅图,也能领会“折叠再展开就能过滤” |
|---|---|
| 进阶 | 把代码 1 的线性编码器换成 Keras 自编码器(1,196 → 64 → 16 → 64 → 1,196),比较重建误差和生成器 E 的手腕速度 |
| 下一篇 | 5-4 从词语到动作 — 把语言也变成同一张地图上的点 |
你已经读完了《5-3 编码器·解码器》篇。研究室成员们各说一句,为你做个总结。