图文编码与双向对比目标
第二章完成了无需训练的词法基线。现在的问题是:能否让图像像素与文字短语通过学习进入同一个可比较空间?这里的“同一个”指编码器联合接受配对目标约束,而不只是输出维度相等。本章先在两对样本上算清损失和梯度,再连接实际的卷积图像编码器与文字编码器。主项目确实更新全部参数,没有把手写向量或类别查表伪装成学习表示。
读者已会矩阵乘法、向量点积和基本导数。链式法则是这里导入的微积分规则:复合函数对参数的导数等于各局部导数按依赖路径相乘并求和。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。这两个数已经说明共享的是核权重,窗口位置仍不同。
多通道时,每个输出通道有一组覆盖全部输入通道的核。对输出通道o、位置(i,j),先对输入通道c和核坐标(a,b)求和,再加该输出通道的偏置。若填充p、步幅s,输入取值位置是(si+a−p,sj+b−p),越界处按本模型零填充取零。输出的不同通道有不同核,但同一个核在所有空间位置共享。
代入第一层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在零点不可微,框架选择的子梯度不能拿来当处处光滑的推导。
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,使非零向量长度为一;库函数在极小长度处用阈值避免除零,下面解析推导只在长度大于阈值的区域成立。两向量点积就是余弦相似度,范围负一到一。温度是固定正数零点一,用于缩放相似度,当前代码没有可学习温度参数。
计算交叉熵还需数值稳定约定。直接对大分数指数化可能溢出,先取一行最大值,将每项减,再在最后加回。这是代数恒等变换,不是近似softmax;有限精度下它避免许多溢出,但仍有舍入误差。若行内出现NaN或无穷,需要上游拒绝或明确处理,不能认为稳定形式会修复坏输入。
网络所有可训练参数构成向量,更新目标是当前二十对固定训练样本的平均损失。梯度下降仅保证局部下降方向的解释,在适当步长与光滑邻域才可用一阶展开;ReLU网络非凸,Adam也没有在这里获得全局最优证明。真实损失下降是本次有限轨迹的观测,不能由目标定义直接推出必然学会所有属性组合。
批次含B个唯一图文配对。对图像i,行softmax把各文本分数指数化并按该行求和归一,得到在这批候选中选文本j的条件分布。训练标签把正确列指定为i,负对数概率惩罚选不到真配对的情形。交换行列再做一次,就是文字选图像。双向损失是两种平均负对数概率的算术平均;这只是闭集配对任务的目标,输出并非某事实在现实中成立的概率。
算例一:两对样本的损失与每个logit梯度
取B等于二,所有四个分数都为零。每一行和每一列的归一概率都是二分之一,单向损失为log二,双向平均仍是log二,不会因为算了两遍变成两倍。对分数S的行损失求导,负对角项给负标签,log求和指数项给softmax概率;再除批大小。列项同理。双向均匀点的对角梯度是负四分之一,非对角是正四分之一,因此梯度下降会提高正确配对分数、降低错配分数。
检查每行梯度和在均匀对称例子中为零,表示给整张分数表加常数不会改变任何归一概率。一般情形只有全表梯度和必为零,不能由这个特殊对称例子推断每行总为零,因为列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必须非零,阈值区域的库实现是另一段函数。
取二维a为三、四,r等于五,u为零点六、零点八,文字方向v为一、零,温度一。点积分数为零点六。把v在u方向的分量减去,得到零点六四、负零点四八;再除五,梯度为零点一二八、负零点零九六。它与a点积为零,独立核对径向不变性。真实网络层数更多,自动微分沿卷积和线性层继续传播,但局部归一化机制相同。
训练集由二十四种颜色、形状、大小组合中的二十种构成,四种组合留出。各基础词都出现在训练中,所以这检验的是有限合成域的属性组合迁移,并非遇到新词、新背景、新对象的开放语义理解。每一步使用全部二十对作为批次,标签唯一;若两张图配完全相同短语,交叉熵仍强迫它们竞争对角唯一标签,会出现假负例,应改多正例目标而不是宣称训练数据自然而然无问题。
固定随机种子二十三、CPU单线程、Adam学习率零点零零三,实际更新二百二十步。模型共有三万五千六百个可训练参数,首尾训练交叉熵约三点零九一八六四与零点零一六七一三。独立测试检查每个参数张量梯度存在、有限且总绝对值非零,再对真实文本线性层的一项权重作双精度中心差分。梯度非零检查说明本次图像和文字分支接入了目标,不意味着每个单独坐标都在每一步改变。
学习结果必须与词法基线共同保存。在固定二十四候选图库中,四个留出组合双向检索全中,但样本只有四个,且训练和测试共享颜色原型、固定背景与居中构图;这不是跨分布证明。第七章会把相同冻结模型放到真实生成的平移、变暗、灰度图片上,观察极明显的失败。不能用训练交叉熵很低替代这一步,也不能只报告最好的扰动分支。
TRY IT YOURSELF
练习一:均匀批次
B为五且所有分数零,双向损失是多少?若平均号误写成求和会发生什么?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习二:温度
保持归一向量不动,温度从零点一改为零点二,logit和局部相似度导数怎样改变?能否断言最终梯度恰好减半?
提示 1
提示 2
对照参考解答
TRY IT YOURSELF
练习三:留出与泄漏
四个留出组合的图片出现在最终图库中,是否等同训练泄漏?怎样说明评价合同?