想象你走进一座美术馆,大厅中央挂着一幅油画:远处是绿油油的草坪,近处一只棕色小狗正在奔跑。一位讲解员只扫了一眼,便顺畅地开口讲解:“一只小狗在公园的草地上奔跑。”他的厉害之处不是背书,而是把“看到什么”和“说出什么”轻松打通。AI 里的“图生文”任务——Image Captioning——正是要训练这样一位“讲解员”:给它一张图,它还你一句通顺、准确的自然语言描述。这个能力是无障碍阅读、相册管理、内容审核等产品的核心引擎。 要培养这样一位讲解员,需要四样东西:一双看得准的“眼睛”(视觉编码器),一张会讲故事的“嘴”(语言解码器),一种边讲边落点的“目光”(注意力机制),还有一套“陪练—打分—上岗”的成长体系(训练优化、评估指标与工程部署)。下面我们就沿着这条“讲解员养成记”主线,从零把 Image Captioning 看透。

岗位关联

掌握 Image Captioning,你就能为无障碍辅助、相册搜索、内容审核等真实产品装上“自动配文”能力,让机器学会看图说话——这是多模态 AI 落地的高频刚需技能,也是通往更复杂图文理解任务的第一块跳板。

目录
  1. 可视化演示
  2. 任务定义:讲解员接下了什么活儿
  3. 视觉编码器:讲解员的眼睛
  4. 语言解码器:讲解员的嘴
  5. 注意力机制:讲解员的目光
  6. 训练与优化:陪练和实战演练
  7. 评估指标:观众席上的打分表
  8. 部署优化:上岗之后的修炼
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示图像字幕生成从视觉编码器提取特征到语言解码器逐词生成的完整流程,以及注意力机制在生成不同词语时动态关注图像不同区域的可视化。

本节配有交互动效(进入视口后自动加载,约 3KB)。

图像字幕生成流程 视觉编码器将图像编码为特征向量序列 语言解码器自回归逐词生成描述文本 注意力机制连接视觉与语言模态 P(y|x) = Π P(y_t|y<t,x) 图生文核心是视觉到语言的端到端生成。

任务定义:讲解员接下了什么活儿

讲解员上岗前,馆长先给他立规矩:“你的活儿不是给画分类,也不是报出一串物品清单,而是要看着画面,说出一句完整、自然、能让人听懂的话。”这句话点出了 Image Captioning 与图像分类、目标检测的本质区别:输出不再是单个标签或一堆框,而是一段长度不固定的句子。更妙的是,讲解员面对同一幅画,可以讲出无数种合理的说法,但总有一种是“最贴合画面”的。

从数学上看,这个任务被形式化为一个条件概率问题:给定图像 I,我们想找到那个条件概率最高的句子 C。就像讲解员在心里快速排练“我该怎么说最稳”一样,模型在所有可能的句子中挑选一个概率最大的。这个“输入图像、输出句子”的映射,就是经典的编码器—解码器框架——前半段把画“看进去”,后半段把话“讲出来”。

明白了任务,接下来就得给讲解员配齐装备:一双眼睛、一张嘴和一种会移动的目光。

C^=arg⁡max⁡CP(C∣I)\displaystyle \hat{C} = \mathop{\arg\max}_{C} P(C \mid I)
这个公式说明:图像字幕的“正确答案”不唯一,模型要做的是在所有合法句子中挑出与图像最匹配、概率最高的一句——就像讲解员选出一段最自然又最贴画的讲解词。
  1. C(候选字幕)由多个词按顺序组成的一句话,长度不固定,短则三五词,长则一二十词。
    C=(y1,y2,…,yT)\text{C} = (y_1, y_2, \ldots, y_T)
  2. I(输入图像)由成千上万个像素排成的矩阵,是计算机眼中的“画布”。
    I∈RH×W×3I \in \mathbb{R}^{H \times W \times 3}
  3. P(C|I)(条件概率)在“看到这幅画”的前提下,某句字幕合理的概率有多大。讲解员越懂画面,好句子的概率越高。
    P(C∣I)P(\text{C} \mid I)
  4. argmax(取最优)遍历所有可能的句子,挑出条件概率最高的那句,作为最终输出的“最自信讲解”。
    C^=arg⁡max⁡CP(C∣I)\hat{C} = \mathop{\arg\max}_{C} P(C \mid I)

视觉编码器:讲解员的眼睛

讲解员的厉害之处,不在于他把每一个像素都记下来,而在于他扫一眼就能抓住“草坪”“小狗”“奔跑”这些关键要素。视觉编码器干的就是这件事:把上百万个像素组成的图像,浓缩成一组紧凑的语义特征。它最早用卷积神经网络(CNN)实现——卷积层像一堆小放大镜,从横竖边缘开始,逐步拼出纹理、轮廓,最后看到“这是一个生物”这样的大概念。

为了让讲解员“从小见过世面”,工程师通常先用 ImageNet 这样的大规模画库训练好 CNN,再把它搬过来当编码器用。这个“先实习再上岗”的策略就是预训练迁移。编码器的输出往往不是一整根向量,而是一张“区域特征图”:图像被切成若干网格,每个网格用一个向量描述,相当于把画布编成一本按区域索引的“图注目录”。目录里每一条都对应画面的一小块地方,后面解码器讲故事时,就可以精准“引用”了。

眼睛已经把画面反反复复看了个透,心里有了底稿。接下来,讲解员该张嘴了。

v={v1,v2,…,vL},vi=CNN(I)i∈RD\displaystyle v = \{v_1, v_2, \ldots, v_L\}, \quad v_i = \text{CNN}(I)_i \in \mathbb{R}^{D}
一句话:视觉编码器把“图像”翻译成“一批区域向量”,每个向量描述画面上一个小区域的内容,供后续的语言模块按需查阅。
  1. CNN(I)(卷积特征提取)图像经过多层卷积与池化,逐层抽象出从边缘到物体的语义。
    v=CNN(I)v = \text{CNN}(I)
  2. L(区域数量)特征图被划分为 L 个空间区域,每个区域对应画面的一块位置,类似于把画布打上网格。
    L=Hf×WfL = H_f \times W_f
  3. v_i(区域向量)第 i 个区域的 D 维语义描述,D 一般在 256 到 2048 之间,是“这一小块画面像什么”的浓缩表达。
    vi∈RD,i=1,2,…,Lv_i \in \mathbb{R}^{D}, \quad i = 1, 2, \ldots, L

语言解码器:讲解员的嘴

讲解员不会把“图注目录”照本宣科地念出来,他会组织成一句人话。而且请注意,没有人是一口气把整句话说完的——都是先蹦出“一只”,再补上“小狗”,边说边在脑子里保留刚才说的内容。语言解码器就是这张“嘴”:它一个词一个词地往外交付,每说出一个词,就把它作为“已说的内容”记下来,再决定下一个词。

具体来说,解码器每一步都会在词典上产生一个概率分布——每一个候选词有多大概率成为下一个词。选中最合适的词之后,这个词会被送进“词嵌入”层:把“cat“这样的符号变成一串向量,就像查字典把汉字变成拼音谱。随后 LSTM(长短期记忆网络)更新自己的内部记忆,把”已经讲过什么“和”图像特征“融合成下一步的线索,直到模型输出一个特殊的结束符 <EOS>,讲解段子才算讲完。这种”边说边根据前文决定后文”的机制叫自回归生成。

嘴会说了,但讲到“小狗”时,讲解员的目光若一直停在天空上,谁也听不下去。于是,注意力机制出场。

p(yt∣y<t,v)=softmax(W ht+b)\displaystyle p(y_t \mid y_{<t}, v) = \text{softmax}(W\, h_t + b)
每一步,解码器都在回答“下一个词该说什么”:它根据已说的话和画面特征,给词典里每个词打分,再用 softmax 变成概率,挑出最合适的一个。
  1. y_t(当前词)解码器第 t 步要吐出的词,来自词典 V。
    yt∈Vy_t \in \mathcal{V}
  2. y_{<t}(已生成的词)前面所有词拼起来的“已说话语”,是嘴当前的语言记忆。
    y<t=(y1,y2,…,yt−1)y_{<t} = (y_1, y_2, \ldots, y_{t-1})
  3. h_t(隐藏状态)LSTM 的内部笔记,把图像上下文和已说话语融合成“下一句怎么说”的线索。
    ht=LSTM(yt−1,ht−1,v)h_t = \text{LSTM}(y_{t-1}, h_{t-1}, v)
  4. softmax(归一化)把打分变成词典上所有词的合法概率,总和为 1,然后从中采样或取最高分。
    p(yt∣⋅)=softmax(Wht+b)p(y_t \mid \cdot) = \text{softmax}(W h_t + b)

注意力机制:讲解员的目光

想象讲解员手里有一支激光笔:讲到“小狗”时,红光点落在小狗身上;讲到“草坪”时,光点又滑到画面下方。他不可能一次把所有细节都塞进嘴里,而是讲到哪儿,目光就聚焦到哪儿。注意力机制(Attention)就是这根“激光笔”,它让解码器在生成每一步时,动态地决定“现在该看图像的哪个区域”。

实现上称为软注意力:解码器先用“当前想说的内容”(上一步的隐藏状态)与每个图像区域的特征做一次匹配打分,分数越高说明两者越相关;接着把这些分数用 softmax 归一化成一组和为 1 的权重;最后按权重把所有区域特征加权求和,得到一个“上下文向量”。这个上下文向量就像“这一句话里最该看的那块画面”,被喂给解码器,帮助它挑出正确的词。于是,“讲什么看什么、看什么讲什么”形成了闭环。

眼睛、嘴、目光三件套齐了,讲解员可以在训练厅里试讲了。效果好不好,取决于陪练和评分。

αt,i=exp⁡(et,i)∑j=1Lexp⁡(et,j),ct=∑i=1Lαt,ivi\displaystyle \alpha_{t,i} = \frac{\exp(e_{t,i})}{\sum_{j=1}^{L} \exp(e_{t,j})}, \qquad c_t = \sum_{i=1}^{L} \alpha_{t,i} v_i
注意力机制把“整张图”变成了“按需取用的一扇窗”:每生成一个词,模型都重新选择关注哪个区域,再把这些区域的信息按权重混合成一份精确的上下文。
  1. e_{t,i}(相关度打分)把“当前想说的”(上一步隐藏状态 h_{t-1})与“第 i 个图像区域”做匹配打分,分高说明这一步正讲到它。
    et,i=wa⊤tanh⁡(Waht−1+Uavi)\displaystyle e_{t,i} = w_a^\top \tanh(W_a h_{t-1} + U_a v_i)
  2. α_{t,i}(注意力权重)对 L 个区域的打分做 softmax 归一化,得到和为 1 的“目光分布”。
    αt,i=exp⁡(et,i)∑j=1Lexp⁡(et,j)\displaystyle \alpha_{t,i} = \frac{\exp(e_{t,i})}{\sum_{j=1}^{L} \exp(e_{t,j})}
  3. c_t(上下文向量)把所有区域特征按权重加权求和,得到“这一步要看的内容摘要”。
    ct=∑i=1Lαt,ivic_t = \sum_{i=1}^{L} \alpha_{t,i} v_i

训练与优化:陪练和实战演练

新讲解员上台前,要先做“影子跟读”:陪练说一句标准讲解词,他跟着学一句,逐词模仿。这在 AI 里叫交叉熵损失(Cross-Entropy Loss)配合 teacher forcing——训练时把人类写的参考字幕一个词一个词地喂给模型,要求模型在每个位置都预测出“标准答案”这个词。哪个词的概率给低了,就惩罚它,逼它把对的词的概率抬上去。

但影子跟读有个漏洞:模型从未“自己完整讲一句”再被纠错。等到真正生成时,它拿自己上一步的输出当输入,一旦某个词说偏了,错误就像滚雪球一样越滚越大——这就是“曝光偏差”(Exposure Bias)。为了弥补,业界会再加一轮“实战演练”:让模型自由发挥生成整句,再请一位严格评委——比如 CIDEr 指标——给整句话打分,把分数当作“奖励”来指导模型。由于这种奖励是离散、不可导的,没法直接求梯度,于是用强化学习中的策略梯度来近似优化:讲得好就鼓励,讲得差就抑制。

强化学习阶段则用另一种目标:

陪练和实战都练完了,讲解员终于要接受观众检验。评分表长什么样?

L(θ)=−∑t=1Tlog⁡pθ(yt∗∣y<t∗,v)\displaystyle \mathcal{L}(\theta) = -\sum_{t=1}^{T} \log p_\theta(y_t^* \mid y_{<t}^*, v)
交叉熵损失就像“逐词跟读计分”:参考字幕里每个正确单词被模型的概率越高,总惩罚越小;它保证了模型先学会说“人话”。
  1. r(ŷ)(奖励评分)模型自生成的句子 ŷ 与参考字幕计算 CIDEr 等指标得到的分数。
    r(y^)∈Rr(\hat{y}) \in \mathbb{R}
  2. b(基线)一个参考分数(常用贪心解码的得分),用来判断“这次讲得比平时好还是差”,降低训练方差。
    J(θ)=Ey^∼pθ[r(y^)]J(\theta) = \mathbb{E}_{\hat{y} \sim p_\theta}[r(\hat{y})]
  3. 近似梯度通过采样句子来估计梯度,让模型朝着“得分比基线高”的方向调整参数。
    ∇θJ(θ)≈(r(y^)−b) ∇θlog⁡pθ(y^∣v)\nabla_\theta J(\theta) \approx (r(\hat{y}) - b)\, \nabla_\theta \log p_\theta(\hat{y} \mid v)

评估指标:观众席上的打分表

讲解员讲完一段,观众席上有四类评审各拿一张打分表。第一位评审 BLEU 只关心“用词贴不贴”:你讲的短语组合(n-gram)和人写的参考字幕有多少重合,重合越多分越高;但如果你讲得过于简洁,它还会扣“简短惩罚”分。第二位评审 ROUGE 更看重“要点覆盖”,参考字幕里出现的重要词你有没有讲到,讲得越全越好。第三位评审 CIDEr 最聪明,它用 TF-IDF 给每个词加权——“小狗”“奔跑”这种画面关键信息权重高,而“一个”“在”这种常见词权重低,因此它的打分更贴近人类对“重点”的判断。第四位评审 METEOR 则更宽容,允许同义词替换,比如“狗”换成“汪星人”它也能认出来。

实际项目中,大家从不当“唯 BLEU 论”的独裁者,而是把多个指标列在一起看,再配合人工抽评。毕竟评分表是死的,观众的感受是活的。

分数再漂亮,终究要回到真实世界里接受考验。讲解员正式上岗,得学会“轻装上阵”。

BLEU=BP⋅exp⁡(∑n=1Nwnlog⁡pn)\displaystyle \text{BLEU} = \text{BP} \cdot \exp\left(\sum_{n=1}^{N} w_n \log p_n\right)
BLEU 的核心是“候选字幕里有多少 n 元短语和参考字幕重合”,同时用简短惩罚防止模型耍小聪明少说保平安。
  1. p_n(n-gram 精确率)候选字幕里长度为 n 的连续短语,有多少也出现在参考字幕中。
    pn=∑ngram∈candCountclip(ngram)∑ngram∈candCount(ngram)\displaystyle p_n = \frac{\sum_{\text{ngram} \in \text{cand}} \text{Count}_{\text{clip}}(\text{ngram})}{\sum_{\text{ngram} \in \text{cand}} \text{Count}(\text{ngram})}
  2. w_n(加权平均)对 n=1 到 4 的精确率取加权平均,通常取均匀权重。
    ∑n=1Nwnlog⁡pn\displaystyle \sum_{n=1}^{N} w_n \log p_n
  3. BP(简短惩罚)候选字幕比参考字幕短时打折扣,防止模型“故意少说”。
    BP={1c>re1−r/cc≤r\displaystyle \text{BP} = \begin{cases} 1 & c > r \\ e^{1 - r/c} & c \le r \end{cases}

部署优化:上岗之后的修炼

美术馆终于把讲解员派到了各个分馆,但要求很苛刻:观众按下耳机按钮,讲解词必须 0.5 秒内送到;而且手机端的芯片又小又省电,容不下一个“大块头”模型。于是上岗后的第一课就是“减肥”。最常见的手段是权重量化:把原来用 32 位浮点数存成的权重,压缩成 8 位整数。这就像把一本厚重的精装画册改印成口袋本,字小了点,但关键信息都在,翻起来却快得多、轻得多。

另一招是知识蒸馏:让一个“小学员”(小模型)整天跟着“大师”(大模型)学,模仿大师的讲解风格和软概率输出,最后小学员也能讲出八九十分的效果。工程上还会用 ONNX、TensorRT 这类推理加速框架做算子融合,配合束搜索(beam search)在生成时一次保留多个候选句子,减少重复和次优解。经过这一轮“减肥—提速—调参”,讲解员才算真正走上岗位,并且还会定期用新画作数据回流迭代,越讲越好。

到此,“讲解员养成记”告一段落:眼睛看懂画面,嘴巴组织语言,目光精准聚焦,陪练打磨水平,评分指明方向,上岗轻装上阵。接下来我们看看这套能力在真实世界里如何大显身手。

xq=⌊xq⌉,q=∣x∣max⁡2b−1−1\displaystyle x_q = \left\lfloor \frac{x}{q} \right\rceil, \qquad q = \frac{|x|_{\max}}{2^{b-1} - 1}
量化就是给浮点权重“画格子”:把一个大范围连续数值映射到少数几个整数台阶上,存储从 4 字节变 1 字节,推理自然快得多。
  1. q(量化步长)把权重取值范围切成 2^b 份的小格子宽度,b 是量化位数(如 8 位)。
    q=∣x∣max⁡2b−1−1\displaystyle q = \frac{|x|_{\max}}{2^{b-1} - 1}
  2. x_q(量化后的整数)原浮点权重除以步长并四舍五入,变成小整数,存储和计算都大幅变快。
    xq=⌊xq⌉x_q = \left\lfloor \frac{x}{q} \right\rceil
  3. 反量化(还原近似)用 x_q 乘以步长近似还原原值,精度略有损失,但整体字幕质量基本可控。
    x^=xq⋅q\hat{x} = x_q \cdot q

AI 应用场景

无障碍辅助:让视障用户“看见”图片

阶段:推理部署 + 语音合成联动

解决的问题:视障用户刷朋友圈、逛淘宝时,读屏软件只能播报“图片”两个字,画面信息完全丢失。传统人工描述成本高、延迟大,根本无法覆盖海量图片。Image Captioning 能自动为每张图片生成一句话描述,再交给语音合成(TTS)朗读出来。

具体案例:某无障碍产品为手机相册接入图像字幕模型,用户浏览照片时耳机里传出“一只棕色小狗在绿色草地上奔跑,画面明亮”;社交 App 在信息流中为每张图片自动填充 alt 文本,满足 WCAG 无障碍规范,视障用户也能平等获取图文信息。

相册管理:自然语言搜索与主题成册

阶段:离线批处理 + 向量检索

解决的问题:手机相册动辄上万张照片,人工整理费时费力。传统图像分类只能打“猫”“沙滩”这类标签,无法描述“小狗在沙滩上追浪花”这种场景关系。图像字幕把照片转成一句句描述,自动聚类成主题相册。

具体案例:相册 App 对每张照片生成字幕并落库,用户直接输入“去年夏天海边烧烤”就能搜出对应照片;系统还会按字幕自动聚合出“露营”“宝宝学步”“宠物洗澡”等故事性合集,让整理相册像翻日记一样自然。

内容审核:辅助违规图片排查

阶段:在线推理 + 人机协同

解决的问题:平台每天新增上亿张图片,纯规则引擎漏检多、误杀高。图像字幕可以先把画面“翻译”成文字描述,再叠加敏感词规则和场景知识,判断是否涉及违规。

具体案例:内容安全系统对疑似图片先生成一句话字幕,再结合目标检测置信度,输出结构化的审核单,例如“深夜街头多人聚集,画面中有车辆和灯光”,将高风险项优先送入人工复核队列,大幅提升审核效率和召回率。

Python 演示

python91 行
"""图生文迷你演示:带注意力机制的图像字幕解码器"""import torchimport torch.nn as nnimport torch.optim as optim VOCAB = ["<SOS>", "<EOS>", "cat", "dog", "park", "grass", "runs", "sleeps", "in"]word2id = {w: i for i, w in enumerate(VOCAB)}VOCAB_SIZE = len(VOCAB)ID_SOS, ID_EOS = 0, 1  class AttnDecoder(nn.Module):    """讲解员的嘴:每步看一眼图像特征,再吐一个词"""     def __init__(self, vocab_size, embed_dim=16, hidden_dim=32, feat_dim=8):        super().__init__()        self.embedding = nn.Embedding(vocab_size, embed_dim)        self.lstm_cell = nn.LSTMCell(embed_dim + feat_dim, hidden_dim)        self.attn_line = nn.Linear(hidden_dim + feat_dim, 1)        self.out_line = nn.Linear(hidden_dim, vocab_size)        self.max_len = 8     def forward(self, img_feats, caption_ids=None):        # img_feats: (batch, 区域数, feat_dim) 即视觉编码器的输出        batch = img_feats.size(0)        h = img_feats.new_zeros(batch, self.lstm_cell.hidden_size)        c = img_feats.new_zeros(batch, self.lstm_cell.hidden_size)        if caption_ids is not None:            tokens = caption_ids[:, 0]          # 训练时第一步输入 <SOS>        else:            tokens = torch.full((batch,), ID_SOS, dtype=torch.long)        logits_list = []        for t in range(self.max_len):            word_vec = self.embedding(tokens)   # 词嵌入:词符号变成向量            attn_score = self.attn_line(                torch.cat([h.unsqueeze(1).expand(-1, img_feats.size(1), -1), img_feats], dim=-1)            ).squeeze(-1)            attn_w = torch.softmax(attn_score, dim=1)   # 注意力权重归一化            ctx = torch.bmm(attn_w.unsqueeze(1), img_feats).squeeze(1)  # 上下文向量            h, c = self.lstm_cell(torch.cat([word_vec, ctx], dim=1), (h, c))            logits = self.out_line(h)           # 预测词典上每个词的概率            logits_list.append(logits)            if caption_ids is not None and t < self.max_len - 1:                tokens = caption_ids[:, t + 1]  # teacher forcing:喂真实词            else:                tokens = logits.argmax(-1)      # 自回归:喂自己的预测        return torch.stack(logits_list, dim=1)  def main():    torch.manual_seed(42)    # 4 张图,每张由视觉编码器压缩成 6 个区域特征    img_feats = torch.randn(4, 6, 8)    captions = torch.tensor([        [ID_SOS, word2id['cat'], word2id['runs'], word2id['in'], word2id['park'], ID_EOS, 0, 0],        [ID_SOS, word2id['dog'], word2id['sleeps'], word2id['in'], word2id['grass'], ID_EOS, 0, 0],        [ID_SOS, word2id['cat'], word2id['sleeps'], word2id['in'], word2id['grass'], ID_EOS, 0, 0],        [ID_SOS, word2id['dog'], word2id['runs'], word2id['in'], word2id['park'], ID_EOS, 0, 0],    ])    model = AttnDecoder(VOCAB_SIZE)    loss_fn = nn.CrossEntropyLoss(ignore_index=0)    opt = optim.Adam(model.parameters(), lr=0.01)     for epoch in range(30):        logits = model(img_feats, captions)     # (4, 8, vocab_size)        loss = loss_fn(            logits[:, :-1].reshape(-1, VOCAB_SIZE),            captions[:, 1:].reshape(-1)        )        opt.zero_grad()        loss.backward()        opt.step()        if epoch % 10 == 0:            print('epoch %2d  loss %.3f' % (epoch, loss.item()))     model.eval()    with torch.no_grad():        logits = model(img_feats)        for i in range(2):            ids = logits[i].argmax(-1).tolist()            words = []            for w in ids:                if w == ID_EOS:                    break                if w != ID_SOS:                    words.append(VOCAB[w])            print('生成字幕:', ' '.join(words))  if __name__ == '__main__':    main()
目的:用 PyTorch 实现一个带软注意力的 LSTM 语言解码器,在 4 张“迷你图”的区域特征上训练后,能逐词生成"cat runs in park"这类字幕,完整演示视觉特征 + 注意力 + 语言生成的小闭环。
  1. self.lstm_cell = nn.LSTMCell(embed_dim + feat_dim, hidden_dim)解码器核心零件,输入是“词向量 + 上下文向量”的拼接。
  2. attn_score = self.attn_line(...)给每个图像区域打“相关度分”,分高说明这一步该看它。
  3. attn_w = torch.softmax(attn_score, dim=1)把分数归一化成和为 1 的“目光权重”。
  4. ctx = torch.bmm(attn_w.unsqueeze(1), img_feats).squeeze(1)按权重加权求和所有区域,得到这一步的上下文向量。
  5. h, c = self.lstm_cell(torch.cat([word_vec, ctx], dim=1), (h, c))LSTM 更新内部记忆,用“看到的内容 + 已说的话”决定下一步。
  6. logits = self.out_line(h)把记忆状态映射成词典上每个词的打分。
  7. tokens = caption_ids[:, t + 1]teacher forcing,训练时把真实词喂给下一步,就像陪练给标准答案。
  8. tokens = logits.argmax(-1)推理时自回归,拿自己上一轮最自信的词作为下一步输入。
  9. loss_fn = nn.CrossEntropyLoss(ignore_index=0)忽略填充位(padding),只对真实字幕位置计算损失。

常见误区

误区我以为 Image Captioning 就是先做目标检测,把检测到的“猫、公园、跑”塞进预设模板,输出“图片里有猫和公园”这种固定句式的句子。

正解2015 年之后的现代方法都是端到端的编码器—解码器生成框架,语言解码器逐词自回归地决定“说什么、怎么说”,注意力机制让词汇选择与图像区域动态对齐,能表达“猫在公园里跑”这种带关系、带场景的完整语义,而不是死板的模板填充。检测+模板这类老路线早已被淘汰。

为何常见:2014 年前确实流行“检测 + 模板”的老套路,很多科普文章和早期开源代码还停留在那个时代,容易让人形成“字幕 = 填空”的印象。

误区既然上线用 BLEU、CIDEr 评价,为什么训练时不直接拿它们当损失函数?这样岂不是“考什么练什么”?

正解BLEU、CIDEr 这类指标是离散、不可导的——n-gram 是否重合、TF-IDF 相似度算出来都是一个数,无法对神经网络逐层回传梯度。业界通行做法是:先用交叉熵做预训练,让模型学会说人话;再用强化学习把 CIDEr 当奖励、通过策略梯度采样来微调。离散指标虽不能直接求导,却可以当“奖励信号”引导训练。

为何常见:“用什么考核就用什么训练”的直觉非常合理,但它忽略了一个硬约束——梯度无法从离散的统计指标流回网络参数,这正是强化学习登场的原因。

误区项目上线只看 BLEU,分数高就代表字幕质量高。多高算好?能到 0.5 就放心了?

正解BLEU 只统计 n-gram 词汇重合,同一句意思换个说法可能分数大跌;它还天然惩罚合理的简洁表达。业内普遍用 BLEU、ROUGE、CIDEr、METEOR 多指标联合看,再辅以人工评估。CIDEr 因为用 TF-IDF 加权,更贴近人类对“重点信息”的判断,往往比 BLEU 更有参考价值。

为何常见:BLEU 从机器翻译时代引入最早、名声最大,很多人只盯着这一个数字,忽略了它“重词汇、轻语义”的本质缺陷。

误区注意力热力图显示模型在输出 dog 时确实看向小狗区域,所以注意力权重就是因果解释——模型“先看到哪,才说了哪”。

正解注意力权重只是软对齐(soft alignment)的分布,反映的是“相关性”而非“因果性”。已有研究发现,同一模型用不同随机种子训练多次,注意力图可以差异很大,但生成结果却差不多。要做严谨解释,还得配合积分梯度、消融实验等归因方法,不能仅凭一张热力图下结论。

为何常见:注意力可视化太直观了,“红色代表模型在关注”的直觉极其强烈,让人误以为看到了模型内部的真实决策链条。

面试题

Q1. Image Captioning 的任务本质是什么?

单选题 · easy

  • A.把图像压缩成低维向量
  • B.为图像生成一句自然语言描述← 正确答案
  • C.将图像划分成不同的区域
  • D.对图像中的物体进行分类

答案:B。图像字幕的核心是“输入图像、输出完整句子”,如同讲解员看图说话。压缩向量是表示学习,划分区域是分割,分类计数是图像分类,它们都只做了一部分,只有 B 覆盖了图到文的完整映射。

Q2. 视觉编码器的主要作用是什么?

单选题 · easy

  • A.提取图像的高层语义特征← 正确答案
  • B.输出自然语言的句子文本
  • C.计算训练时的损失函数
  • D.人工评审生成字幕的质量

答案:A。视觉编码器相当于讲解员的眼睛,把像素矩阵浓缩成语义特征,供语言模块使用。输出句子是解码器的活,算损失和评审都不属于编码器的职责。

Q3. 语言解码器生成字幕时,通常如何输出?

单选题 · mid

  • A.一次性并行生成整个句子
  • B.从句子末尾向开头输出
  • C.按顺序逐词自回归生成← 正确答案
  • D.随机挑选词填充进句子

答案:C。讲解员讲故事是一个词接一个词地说,解码器同样按顺序逐词自回归生成,每一步把已生成的词作为下一步输入,直到 <EOS>。一次性生成缺乏“已说内容”的记忆,从后往前和随机填充都不符合语言生成的自然结构。

Q4. 注意力机制对解码器最重要的帮助是?

单选题 · mid

  • A.减小模型的参数量级
  • B.生成时聚焦图像的相关区域← 正确答案
  • C.增加训练数据的规模
  • D.扩大输出词典的规模

答案:B。注意力就像讲解员的激光笔,每生成一个词就动态地把目光落到画面最相关的区域,从而提取精准的上下文向量。它不改变参数量、数据量和词典大小,只改变“看哪里”。

Q5. 下列哪些是图像字幕常用的自动评估指标?

多选题 · mid

  • A.BLEU 指标← 正确答案
  • B.CIDEr 指标← 正确答案
  • C.分类准确率
  • D.ROUGE 指标← 正确答案

答案:ABD。字幕评估用的是文本相似度指标:BLEU 看 n-gram 重合、ROUGE 看要点召回、CIDEr 看 TF-IDF 加权相似度。分类准确率是图像分类任务的指标,与字幕的“句子质量”无关。

Q6. 交叉熵训练中 teacher forcing 的含义是?

单选题 · mid

  • A.用真实单词作为每一步输入← 正确答案
  • B.用模型上一步的输出作为输入
  • C.用编码器提取的图像特征
  • D.不向解码器提供任何输入

答案:A。teacher forcing 就像陪练不断给标准答案:即使模型上一轮说错了,训练时也强行喂入真实词,让每一步都“对着标准答案学”。选项 B 描述的是自回归生成(推理时)而非训练时的 teacher forcing。

Q7. 关于强化学习优化字幕生成,下列说法正确的有?

多选题 · hard

  • A.完全取代交叉熵的预训练阶段
  • B.直接优化不可导的评估指标← 正确答案
  • C.可缓解曝光偏差带来的偏差← 正确答案
  • D.通常先预训练再做强化学习← 正确答案

答案:BCD。强化学习把 CIDEr 这类不可导指标当奖励采样优化;它让模型用自己的输出做输入,因此缓解了曝光偏差;实践中都是先交叉熵预训练再强化学习微调。A 错在“完全取代”——没有预训练的冷启动往往学不稳。

Q8. CIDEr 指标的核心思想是?

单选题 · hard

  • A.统计 n-gram 出现的频率
  • B.计算 TF-IDF 加权的相似度← 正确答案
  • C.衡量字幕的情感倾向性
  • D.比较字幕的长度信息

答案:B。CIDEr 对 n-gram 做 TF-IDF 加权:常见词降权、画面关键信息加权,再计算候选与参考字幕的余弦相似度。它比 BLEU 更重视“重点信息”,因此和人类判断的相关性更高。

Q9. 推理时使用束搜索(beam search)的主要作用是?

单选题 · hard

  • A.每步保留多个候选降低重复← 正确答案
  • B.加速视觉特征的快速提取
  • C.对生成字幕进行情感分类
  • D.提升图像编码器的精度

答案:A。束搜索在每一步同时保留多个概率最高的候选序列,而不是只留一个,能显著减少重复词和次优解,相当于讲解员在脑子里同时排练几个版本,最后选最好的那句。它不涉及编码器和分类。

Q10. 部署图像字幕模型时,常见的加速手段有哪些?

多选题 · easy

  • A.对模型权重做量化压缩← 正确答案
  • B.用知识蒸馏训练小模型← 正确答案
  • C.扩大模型的输出词典规模
  • D.工程化推理框架的优化← 正确答案

答案:ABD。量化把 FP32 权重压成 int8,蒸馏让小模型学大模型的输出,推理框架做算子融合,三者都能显著降低延迟和显存。扩大词典反而增加计算量、拖慢推理,与“加速”目标背道而驰。