AI Hero Club

多模态 RAG · 第 3 章

已自行标记完成0 / 8

仅保存在当前浏览器;完成标记不代表通过项目验收。

本章提纲 4 个小节
全课目录 8 章
完整课程计划 ↗
多模态 RAG / 课程计划 ↗

CHAPTER 03 / 8建议学习 150 分钟

双编码器、对比目标与真正的参数训练

一对图像和文字怎样通过同一个对比目标,更新两侧编码器的全部参数?

本章要解决的 3 个问题
  • 从配对监督推导双向对比交叉熵
  • 计算归一化与logit梯度
  • 训练并检查所有图像和文字参数
课程项目 ZIP ↓

图文编码与双向对比目标

第二章完成了无需训练的词法基线。现在的问题是:能否让图像像素与文字短语通过学习进入同一个可比较空间?这里的“同一个”指编码器联合接受配对目标约束,而不只是输出维度相等。本章先在两对样本上算清损失和梯度,再连接实际的卷积图像编码器与文字编码器。主项目确实更新全部参数,没有把手写向量或类别查表伪装成学习表示。

读者已会矩阵乘法、向量点积和基本导数。链式法则是这里导入的微积分规则:复合函数对参数的导数等于各局部导数按依赖路径相乘并求和。PyTorch负责执行这种自动微分,课程不从实数公理重建微积分,但会在两对样本中独立手算logit梯度,并对一项真实网络参数做中心差分,核对计算图连接。数值检查只能验证选中点,不能成为所有输入的证明。

输入图像是三十二乘三十二的RGB浮点数组,通道值由原始八位像素除二百五十五得到,通道顺序转换为通道、高、宽。图像网络是两个步幅二、填充一的三乘三卷积,通道依次从三到八再到十六,每个卷积后使用取正部的ReLU,最后展平并线性映射到三十二维。填充与步幅的输出尺寸规则给出三十二到十六到八,因此最后线性层输入为十六乘八乘八,而不是凭运行碰巧凑出尺寸。

卷积不是一句架构名:从一个输出像素开始

框架Conv2d采用的具体操作是互相关:按当前位置取输入窗口,与核逐元素相乘后求和,不先翻转核。数学教材有时把先翻转核的运算叫卷积,因此要先固定这里的约定。设单通道输入为4×4,按行写1到16;核为[[1,2],[0,−1]],无填充、步幅一、无偏置。左上窗口[[1,2],[5,6]]给出1×1+2×2+0×5−1×6=−1;向右移一格,窗口[[2,3],[6,7]]给2+6−7=1。这两个数已经说明共享的是核权重,窗口位置仍不同。

Y=[−1137911151719],ReLU⁡(z)=max⁡(0,z)Y=\begin{bmatrix}-1&1&3\\7&9&11\\15&17&19\end{bmatrix},\qquad \operatorname{ReLU}(z)=\max(0,z)
无填充2×2核在4×4输入上产生3×3输出;ReLU只将左上负项归零。

多通道时,每个输出通道有一组覆盖全部输入通道的核。对输出通道o、位置(i,j),先对输入通道c和核坐标(a,b)求和,再加该输出通道的偏置。若填充p、步幅s,输入取值位置是(si+a−p,sj+b−p),越界处按本模型零填充取零。输出的不同通道有不同核,但同一个核在所有空间位置共享。

Yoij=bo+∑c∑a=0k−1∑b=0k−1Wocab Xc, si+a−p, sj+b−p,Hout=⌊H+2p−ks⌋+1Y_{oij}=b_o+\sum_c\sum_{a=0}^{k-1}\sum_{b=0}^{k-1}W_{ocab}\,X_{c,\,si+a-p,\,sj+b-p},\qquad H_{out}=\left\lfloor\frac{H+2p-k}{s}\right\rfloor+1
这里不使用dilation;求和中的b是核横坐标,下标b_o表示偏置,两者角色不同。

代入第一层H=32、p=1、k=3、s=2:floor((32+2−3)/2)+1=floor(15.5)+1=16。第二层将H换16,得到floor(7.5)+1=8;宽度同理。第一层参数为8×3×3×3+8=224,第二层为16×8×3×3+16=1168,展平线性层为32×(16×8×8)+32=32800。图像分支合计34192,文本分支1408,总计35600。改输入尺寸后若得到不同的8×8,展平层输入也要按新尺寸调整。

看一个权重如何受像素影响。上面右移一格的输出等于1,ReLU局部导数为一。对核右上角权重W01求导,只留下该窗口右上输入3,因此输出对W01的导数为3。左上输出为−1,经过ReLU后对同权重的局部梯度为零。如果损失同时依赖多个输出,同一共享权重的梯度要把全部窗口贡献相加;不是只拿一个窗口的像素。ReLU在零点不可微,框架选择的子梯度不能拿来当处处光滑的推导。

独立运行手算互相关、ReLU与单权重梯度,独立对照真实conv2d及float64差分。
import torch
import torch.nn.functional as F
torch.set_num_threads(1)
X=torch.arange(1,17,dtype=torch.float64).reshape(1,1,4,4)
W=torch.tensor([[[[1.,2.],[0.,-1.]]]],dtype=torch.float64,requires_grad=True)
Y=F.conv2d(X,W)
expected=torch.tensor([[-1.,1.,3.],[7.,9.,11.],[15.,17.,19.]],dtype=torch.float64)
assert torch.equal(Y[0,0],expected)
loss=Y.relu()[0,0,0,1];loss.backward()
assert W.grad[0,0,0,1].item()==3.0
plus,minus=W.detach().clone(),W.detach().clone();h=1e-6
plus[0,0,0,1]+=h;minus[0,0,0,1]-=h
finite=(F.conv2d(X,plus).relu()[0,0,0,1]-F.conv2d(X,minus).relu()[0,0,0,1])/(2*h)
assert abs(finite.item()-3)<1e-8
# 不运行网络即可计算输出尺寸;用框架只做最后交叉检查。
size=lambda H,k,s,p:(H+2*p-k)//s+1
assert size(32,3,2,1)==16 and size(16,3,2,1)==8
print('output',Y.tolist(),'weight derivative',W.grad.tolist(),'finite',finite.item())

第一层一个输出覆盖3×3输入窗口;第二层组合相距两像素的三个第一层位置,因此一个第二层位置的有效感受野边长是3+(3−1)×2=7,邻接第二层输出在原图相距四像素。卷积前两层共享局部核,却不保证后面的展平线性层平移不变:第一个空间格与第二个空间格对应不同线性权重。第7章平移失败可以据此提出机制假设,但要把唯一原因归于展平仍需受控结构消融,当前失败不能单独证明这一点。

文本输入采用严格受限的词表,只匹配红、绿、蓝、黄四种颜色词,三种几何形状词,以及小、大和拉丁字母串;没有匹配的普通中文不被自动理解。每条短语变成平均词袋向量,再经过线性、双曲正切和线性两层映射到三十二维。平均意味着重复与词序信息可能丢失,例如交换“红色”和“小”的顺序不改变向量。这是模型假设,不是所有自然语言表达都与词序无关。

令未归一化图像输出为a,文本输出为b,它们均为三十二维实向量。通过除以各自欧氏长度得到u与v,使非零向量长度为一;库函数在极小长度处用阈值避免除零,下面解析推导只在长度大于阈值的区域成立。两向量点积就是余弦相似度,范围负一到一。温度是固定正数零点一,用于缩放相似度,当前代码没有可学习温度参数。

ui=ai∥ai∥2,vj=bj∥bj∥2,Sij=uiTvjτ,τ=0.1u_i=\frac{a_i}{\|a_i\|_2},\quad v_j=\frac{b_j}{\|b_j\|_2},\quad S_{ij}=\frac{u_i^Tv_j}{\tau},\quad \tau=0.1
i为图像行,j为文本列;每个批次中第i张图与第i条文字构成正确配对。

计算交叉熵还需数值稳定约定。直接对大分数指数化可能溢出,先取一行最大值mm,将每项减mm,再在最后加回mm。这是代数恒等变换,不是近似softmax;有限精度下它避免许多溢出,但仍有舍入误差。若行内出现NaN或无穷,需要上游拒绝或明确处理,不能认为稳定形式会修复坏输入。

log⁡∑jesj=m+log⁡∑jesj−m,m=max⁡jsj\log\sum_j e^{s_j}=m+\log\sum_j e^{s_j-m},\quad m=\max_j s_j
先将公共因子e^m提出再取对数;至少一个指数为一,其他不超过一。

网络所有可训练参数构成向量θ\theta,更新目标是当前二十对固定训练样本的平均损失L(θ)L(\theta)。梯度下降仅保证局部下降方向的解释,在适当步长与光滑邻域才可用一阶展开;ReLU网络非凸,Adam也没有在这里获得全局最优证明。真实损失下降是本次有限轨迹的观测,不能由目标定义直接推出必然学会所有属性组合。

批次含B个唯一图文配对。对图像i,行softmax把各文本分数指数化并按该行求和归一,得到在这批候选中选文本j的条件分布。训练标签把正确列指定为i,负对数概率惩罚选不到真配对的情形。交换行列再做一次,就是文字选图像。双向损失是两种平均负对数概率的算术平均;这只是闭集配对任务的目标,输出并非某事实在现实中成立的概率。

L=12B∑i=1B[−Sii+log⁡∑j=1BeSij−Sii+log⁡∑j=1BeSji]L=\frac{1}{2B}\sum_{i=1}^{B}\left[-S_{ii}+\log\sum_{j=1}^{B}e^{S_{ij}}-S_{ii}+\log\sum_{j=1}^{B}e^{S_{ji}}\right]
两个对数项分别按行、按列归一;使用自然对数,损失单位为nat。

算例一:两对样本的损失与每个logit梯度

取B等于二,所有四个分数都为零。每一行和每一列的归一概率都是二分之一,单向损失为log二,双向平均仍是log二,不会因为算了两遍变成两倍。对分数S的行损失求导,负对角项给负标签,log求和指数项给softmax概率;再除批大小。列项同理。双向均匀点的对角梯度是负四分之一,非对角是正四分之一,因此梯度下降会提高正确配对分数、降低错配分数。

∂L∂Sij=pijrow+pijcol−21[i=j]2B\frac{\partial L}{\partial S_{ij}}=\frac{p^{row}_{ij}+p^{col}_{ij}-2\mathbf1[i=j]}{2B}
行概率按固定i归一,列概率按固定j归一;指示函数在i=j时为一,否则为零。

检查每行梯度和在均匀对称例子中为零,表示给整张分数表加常数不会改变任何归一概率。一般情形只有全表梯度和必为零,不能由这个特殊对称例子推断每行总为零,因为列softmax项会耦合不同行。训练测试既核对log二,也逐项核对四个梯度,以免只比较损失下降就错过方向错误。

在课程项目目录运行直接调用项目损失,但期望值来自手算,避免函数自我验证。
import math
import torch
from rag_lab.encoder import symmetric_contrastive_loss
s=torch.zeros(2,2,dtype=torch.float64,requires_grad=True)
loss=symmetric_contrastive_loss(s);loss.backward()
assert abs(float(loss)-math.log(2))<1e-14
expected=torch.tensor([[-.25,.25],[.25,-.25]],dtype=torch.float64)
assert torch.allclose(s.grad,expected,atol=1e-14)
print(float(loss),s.grad.tolist())

算例二:对归一化输入的梯度为什么不只拉大长度

若u等于a除长度r,对a作微小改动时,长度本身也变化。长度对a的梯度是a除r,即u;商法则把导数写成单位矩阵除r,减去a与u外积再除r平方,化简为单位矩阵减uu转置后除r。该矩阵把径向分量消掉。因此归一化空间里的目标主要调整方向;简单放大a的长度不会增加余弦分数。这里r必须非零,阈值区域的库实现是另一段函数。

∂u∂a=I−uuTr,∂Sij∂ai=(I−uiuiT)vjτri\frac{\partial u}{\partial a}=\frac{I-uu^T}{r},\quad \frac{\partial S_{ij}}{\partial a_i}=\frac{(I-u_iu_i^T)v_j}{\tau r_i}
I是三十二维单位矩阵;右式对每个j求得局部梯度,再乘损失对S的导数并累加。

取二维a为三、四,r等于五,u为零点六、零点八,文字方向v为一、零,温度一。点积分数为零点六。把v在u方向的分量减去,得到零点六四、负零点四八;再除五,梯度为零点一二八、负零点零九六。它与a点积为零,独立核对径向不变性。真实网络层数更多,自动微分沿卷积和线性层继续传播,但局部归一化机制相同。

训练集由二十四种颜色、形状、大小组合中的二十种构成,四种组合留出。各基础词都出现在训练中,所以这检验的是有限合成域的属性组合迁移,并非遇到新词、新背景、新对象的开放语义理解。每一步使用全部二十对作为批次,标签唯一;若两张图配完全相同短语,交叉熵仍强迫它们竞争对角唯一标签,会出现假负例,应改多正例目标而不是宣称训练数据自然而然无问题。

固定随机种子二十三、CPU单线程、Adam学习率零点零零三,实际更新二百二十步。模型共有三万五千六百个可训练参数,首尾训练交叉熵约三点零九一八六四与零点零一六七一三。独立测试检查每个参数张量梯度存在、有限且总绝对值非零,再对真实文本线性层的一项权重作双精度中心差分。梯度非零检查说明本次图像和文字分支接入了目标,不意味着每个单独坐标都在每一步改变。

学习结果必须与词法基线共同保存。在固定二十四候选图库中,四个留出组合双向检索全中,但样本只有四个,且训练和测试共享颜色原型、固定背景与居中构图;这不是跨分布证明。第七章会把相同冻结模型放到真实生成的平移、变暗、灰度图片上,观察极明显的失败。不能用训练交叉熵很低替代这一步,也不能只报告最好的扰动分支。

TRY IT YOURSELF

练习一:均匀批次

B为五且所有分数零,双向损失是多少?若平均号误写成求和会发生什么?

提示 1
所有分数为零时,每行与每列softmax各给多大正配对概率?
提示 2
依次保留批内平均与双向平均两个除数,再分析改成求和对loss和梯度尺度的作用。
对照参考解答
每行正确概率为五分之一,负对数为log五,行平均和列平均都等于log五,双向再平均仍为log五。若只把批内平均改为求和,损失和梯度都放大五倍;若双向也少除二,再多一倍。缩放会改变相同学习率下的更新尺度,所以不能只说最优点相同就认为实现等价。

TRY IT YOURSELF

练习二:温度

保持归一向量不动,温度从零点一改为零点二,logit和局部相似度导数怎样改变?能否断言最终梯度恰好减半?

提示 1
温度改变既影响余弦到logit的局部因子,也影响softmax概率。
提示 2
先比较两次logit,再按链式法则写上游loss导数,检查是否有理由把它视为不变。
对照参考解答
logit是点积除温度,因此逐项减半;分数对未缩放余弦的导数由十变五。但softmax概率也随logit改变,损失对分数的梯度不是固定的,完整链式梯度一般不恰好减半。只有额外固定该上游梯度时才有这种比例,不能把局部因子冒充总结果。

TRY IT YOURSELF

练习三:留出与泄漏

四个留出组合的图片出现在最终图库中,是否等同训练泄漏?怎样说明评价合同?

提示 1
检索图库包含正确答案,与训练更新读取该图片/标签是同一条件吗?
提示 2
分别列训练ID与测试候选图库,公开候选总数,再核检查点记录的训练像素来源。
对照参考解答
检索任务允许测试时图库包含正确答案,关键是编码器训练更新没有读取留出图片与配对标签。当前测试用二十四候选图库,四个查询属于未训练组合,因此不是只在四张留出图之间比较。训练函数仅加载二十个train条目,检查点保存训练ID及像素散列。图库设置必须公开;它不允许据此声称泛化到任意自然图片。

CHECK YOUR UNDERSTANDING

检验理解

先作答,再决定是否展开解析。选择题用于发现理解缺口;独立实现与迁移任务仍需另行检验。

1. 双向损失在均匀B对样本上为?

查看解析两方向各为log B,再取平均。
2. 当前文本编码器能可靠处理任意中文吗?

查看解析未匹配的普通中文不进入语义表示。
3. 余弦归一化对纯放大非零向量有什么影响?

查看解析正比例放大不改方向。
4. 全参数学习的证据应包含什么?

查看解析实际参数计算图和更新需要独立检查。

自行标记用于记录学习进度,不代表通过项目验收。