我先讲一个我经历过的崩溃瞬间。有一段时间我在做一个图像描述的小项目,交叉熵损失一路下降,验证集上的loss也正常,但把生成的句子拉出来一看,几乎全是“a person is riding a motorcycle”这类车轱辘话。换个场景,比如“一只猫站在窗台上看向窗外”,模型照样说摩托。最让我费解的是训练损失明明在下降。后来我才意识到,不是模型笨,是训练目标和评测指标根本不在一个世界。

交叉熵的训练方式是teacher forcing:每一步都拿着真实的前一个词当输入,让模型预测下一个词。这等于是在教模型“猜词”。但测试时你要的是它“写句子”,而且打分的CIDEr、BLEU看的不是猜中概率,而是生成句子和人工标注句子的n-gram重合。你让一个学生反复做选择题,考试却考作文,他不会,你就怪他?
交叉熵教模型猜词,考试却考它写句子
为什么这是个无解的局?因为交叉熵是词级别的监督,每一步只看“当前位子该填哪个词”。CIDEr是句子级别的度量,计算生成句子与极少数标注句子之间的整体相似度。两者对“好”的定义根本不同。交叉熵把概率分配给那些“在上一词出现后最自然的词”,可自然不等于有用。一个句子很流畅但信息量少,交叉熵损失照样低,CIDEr就未必好看了。所以文章开头那段情况,其实不是偶然,是目标函数错位后的必然。
那能不能直接优化CIDEr?理论上可以,但CIDEr是离散的句子计算出来的分数,对参数不可微。常规梯度下降都失效了。这时候,强化学习登场。
强化学习本来是个方向,但被critic绊了一跤
强化学习里的策略梯度专门解决“目标不可微、只能靠采样”的问题。把“生成句子”视为一次决策,“得到的CIDEr”视为奖励,那么梯度可以写成:
gradient = (reward(sample) - baseline) * gradient_log_prob(sample)
这里baseline是干嘛用的?直观理解:如果没有baseline,所有奖励都是正数,那么策略会无差别地给所有采样句子加概率。那采样到烂句子也会被鼓励,好句子又被淹没在大多数普通句子里。减去baseline之后,只有比baseline好的句子获得正信号,差的获得负信号,模型才能真正学会“择优”。
baseline的理想值是该状态下奖励的期望。最标准的做法是额外训练一个critic网络,去估计期望奖励,把它当成baseline。听起来很顺,实际操作却让人想摔键盘:critic和policy两个网络互相影响,训练常不稳定,调超参数调到头秃。我当时就在坑里,感觉强化学习就是一个“大力出奇迹”的黑洞。
自己的beam search,就是最合适的镜子
直到我看到Rennie他们在CVPR 2017上那篇论文:Self-Critical Sequence Training for Image Captioning。他们的idea让我拍大腿——为什么基线不能直接用模型自己beam search出来的句子?
简单说,训练时同时让模型生成两条句子:一条用beam search得到,代表当前模型能力的“正常水平”;另一条用随机采样得到,代表“探索”。然后计算两条句子的CIDEr得分,用采样句子的得分减去beam search句子的得分,得到优势。这个优势直接用于更新采样子句子的概率。得分比beam search高,就提高概率;低,就降低。整个过程不需要任何critic。
这个名字起得好——“自我批评”。模型不是在跟外部老师学,而是在用自己最新的表现当镜子,检查刚才这一步是进步还是退步。
我最早觉得这个想法有点自恋:自己骂自己,能有多少长进?后来健身的时候突然想通了:你昨天卧推推起80公斤,今天推到85公斤,你当然知道这是个飞跃;今天只推70公斤,你也知道状态差了。你不需要一个教练拿着数学模型告诉你该怎么调整,你的历史成绩就是最自然的baseline。SCST就是在拿模型的历史最佳beam search水平当杠杆。
五步训练法,简单到不可思议
整套算法的实现朴素得让人感动,步骤就五步:
- 对一张图片,用当前模型跑一次beam search,得到一条基线句子。
- 同一张图片,再用当前模型按多项式分布采样一条句子。
- 分别计算这两条句子的CIDEr分数(或其他指标)。
- 计算奖励差:采样句子的分数减去基线句子的分数。
- 用这个奖励差乘以采样句子的对数概率梯度,回传更新模型。
你发现没有,这里面没有调参游戏。连学习率都可以沿用交叉熵阶段的值。和需要精心设计价值网络的方法比起来,SCST的成本几乎为零。
一张表看清交叉熵和SCST的差别
| 交叉熵训练 | SCST强化学习 | |
|---|---|---|
| 优化目标 | 逐词似然 | 序列级评价指标 |
| 输入信号 | 真实词序列 | 生成句子的评分 |
| 训练方式 | teacher forcing | 模型自生成 + beam search |
| 基线 | 无 | beam search输出 |
| 与测评一致性 | 不一致 | 直接对齐 |
| 额外网络 | 无 | 不需要critic |
这种对比能看出来,SCST本质上不是在“换一个神经网络”,而是换了一个目标函数。网络结构不变,训练代码改几行,评测指标却肉眼可见地涨。Rennie等人在MSCOCO上报告,用SCST微调比纯交叉熵训练的CIDEr能提高好几甚至十个点,同时生成结果也更像人话。
SCST不是万能药:指标会钓鱼,模型会上钩
不过,直接优化一个自动评价指标,必然催生“刷分”。比如CIDEr倾向于鼓励n-gram重叠,模型可能学会了反复使用“a person is …”这种安全句式,因为它在标注里出现频率高,换了新词反而拿不到分。翻译和摘要领域也有类似问题,生成的文本越来越平庸,因为最稳妥的输出往往就是最俗套的输出。
我自己还踩过一个坑:一开始跳过交叉熵预训练,直接从零开始跑SCST。结果模型连语法都没学会,采样的句子全是乱码,beam search也差不多乱,奖励差毫无意义。论文里的做法是先训练一个像样的基础模型,再用SCST微调。因为策略梯度只有在“已有能力”之上才谈得上评分优势。
另外,beam size的大小也值得琢磨。beam size太小,基线质量差,优势估计可能偏激;太大了,训练变慢,优势也会变得过于保守。现在很多工作在使用SCST时,会逐渐提高beam size,或者加入随机温度采样,让探索更充分。
那些后来改进的算法,比如batch-wise baseline、多个样本对比、或者把基线换成移动平均奖励,本质上都是在给SCST的“自我批评”添砖加瓦。它已经成为一个思想源头:不要外部评价,用自己模型的生成结果互相纠偏。
一点必要的术语补充
- 策略梯度(Policy Gradient)
- 直接对策略参数求梯度,以最大化期望奖励的一类强化学习方法。
- 基线(Baseline)
- 从奖励中减去的一个标量,能在不改变梯度期望的前提下降低方差。SCST中的基线就是beam search句子的奖励。
- REINFORCE
- 一种蒙特卡洛策略梯度算法,用采样轨迹近似期望梯度,是SCST的理论底座。
- CIDEr
- 图像描述常用评价指标,计算生成句子与参考句子中n-gram的TF-IDF加权重合度,分数优先。
- Beam Search
- 序列生成中保留多条候选路径的近似解码算法。在SCST中它输出“当前模型认为最好的句子”。
SCST和actor-critic是一回事吗?
不是。actor-critic用一个独立的critic网络去近似价值函数,SCST直接用beam search输出当基线,省掉了critic。可以理解为一种更轻量、更自洽的REINFORCE。
SCST只用于图像描述吗?
不是。只要任务是一个自回归生成模型,且有一个能自动计算的评分指标,就能用同一套思路。机器翻译、文本摘要、对话生成里都有类似应用。只是不同指标的“可刷性”不同,需要警惕。
走到这里,你大概明白了,SCST最了不起的地方不是发明了一种新网络,而是重新回答了“模型应该向谁学习”这个问题。它说:向一个比自己好一点点、但又是同一套参数下的自己学习。这个方法朴素到了极点,却又因为朴素而迷人。
但它也有清醒的边界:模型学的毕竟是考试技巧,不是真正的理解。CIDEr涨了,不代表模型看见了你看见的世界。如果哪一天我们希望模型写出的描述真的能打动人,也许需要换一杆“人性的尺子”,让模型不是批评分数,而是批评意义。而SCST告诉我们——只要尺子够好,模型会自己学会用它。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/575.html