写在最前面
对ALBEF任务核心的理解
利用知识蒸馏的机制(作者使用了动量损失,把它称作动量蒸馏),把预训练好的ViT模型和Bert模型,蒸馏到自己的ALBEF模型中,实现了两个模型的融合。
1.什么是知识
不要被这个名字给唬住了,在深度学习中,知识就是神经网络中的权重和偏置等可学习参数。
2.什么是知识蒸馏
就是把一个神经网络中的权重和偏置等可学习参数,利用loss.backward()的方法,转移到另一个神经网络中,以达到如下目的:
- 把一个大的模型,迁移到一个小的模型中,类似剪枝、量化等,不过技术路线不同,功能一致;
- 把多个模型的功能,迁移到一个模型中,相当于一个模型集成了多个模型的能力;
- 数据安全,如果说A公司训练了一个web-scale级别的数据集,但是实际上商用功能只需要部分数据,可以利用知识蒸馏,推出多个模型,避免原始模型数据泄露;
3.为什么用loss.backward()蒸馏到另一个模型中
神经网络都是具有“惰性”的,就是说,如果输入和输出是没有偏差的,神经网络学到的输出就是输入,这也就是为什么GAN容易发生模型崩溃的原因,所以,这对知识(即权重和偏置等可学习参数)的蒸馏方法中,会寻找一个能代知识的参数,但是有具有一定偏差的参数,例如:软标签、分类标签、边界框等,然后利用这个参数的反向传播,实现一步步地蒸馏。
4.知识蒸馏有哪些呢?
蒸馏的种类太多,有兴趣的同学可以去看看相关综述,这里作为ALBEF的前言部分,只介绍在线知识蒸馏和离线知识蒸馏。
4.1离线知识蒸馏
离线知识蒸馏和离线训练大概就是一回事,具体操作就是:
- 教师模型(知识的来源),已经被训练好,例如,我要训练自己的ViT-based OT模型,用了ViT_base的pth文件,但是我又有检测特定目标的需求,那么,这个就是我的教师模型
- 准备好我target-specify的数据集,例如,酒瓶子检测数据集
- 冻结教师模型的参数,关闭梯度,即 with torch.no_grad()和teacher_model.eval()
- 数据分别输入teacher_model和student_model,得到teacher_out和student_out,然后计算两个模型之间的损失loss_teacher_and_student和学生模型学习这个数据集中的损失loss_data
- 两个损失分别反向传播,累加到学生模型上
# 其实可以利用权重把两个损失加在一起然后反向传播,但是为了和在线知识蒸馏产生对比,这里分开了
student_optimizer.zero_grad()
# 由于有with torch.no_grad()和teacher_model.eval(),这里的反向传播过程,不会传播到teacher_model中
loss_teacher_and_student.backward()
student_optimizer.step()
student_optimizer.zero_grad()
loss_data.backward()
student_optimizer.step()
4.2在线知识蒸馏
在线知识蒸馏和离线最大的区别就是:teacher_model也在这个数据集上进行更新训练,其他的和离线知识蒸馏都相同
- 教师模型(知识的来源),已经被训练好,例如,我要训练自己的ViT-based OT模型,用了ViT_base的pth文件,但是我又有检测特定目标的需求,那么,这个就是我的教师模型
- 准备好我target-specify的数据集,例如,酒瓶子检测数据集
- 不冻结教师模型的参数,不关闭梯度,teacher_model.train()
- 数据分别输入teacher_model和student_model,得到teacher_out和student_out,然后计算两个模型之间的损失loss_teacher_and_student和学生模型学习这个数据集中的损失loss_data
- 两个损失分别反向传播,累加到学生模型上
student_optimizer.zero_grad()
teacher_optimizer.zero_grad()
# 由于有teacher_model.train(),这里的反向传播过程会把loss同时传播到teacher_model和student_model两个模型中
loss_teacher_and_student.backward()
student_optimizer.step()
teacher_optimizer.step()
student_optimizer.zero_grad()
# 这里是单独计算student_model的损失,只传播到student_model中,不会传播到teacher_model中
loss_data.backward()
student_optimizer.step()
ALBEL诞生的背景
Vision-and-Language Pre-training(VLP),即视觉语言预训练模型,旨在从大规模的图像-文本对的数据中,学习到多模态数据特征的表达方法,以提升下游视觉语言(VL)任务的表现,通行的做法是设计并训练一个多模态的编码器,能同时理解并提取图像和文本特征,例如smasked language modeling (MLM) and image-text matching(ITM)。这种做法也有一些限制:
- 图像特征和语言的Token embedding分别处于不同的向量空间之中,多模态编码器去学习这两个向量空间的向量之间的互动关系,这本身就是一个巨大挑战;
- Vision-and-Language Pre-training(VLP)中的目标检测器是既需要大量注释,又需要大量计算资源的,因为它在预训练期间需要边界框标注,并且在推理期间需要高分辨率的图像;
- 广泛使用的图像-文本对数据集是从网络收集的,是有噪声的,现有的预训练目标(MLM)可能会过度拟合这些噪声文本,从而降低模型的泛化性能;
所以,作者提出ALBEF,以缓解甚至解决这三个问题,
- 使用无检测器的图像编码器和文本编码器独立地对图像和文本进行编码;
- 使用多模态编码器通过跨模态注意力机制将图像特征与文本特征融合;
- 引入中间的图像-文本对比(ITC)损失,作用于单模态编码器的表示
优点:
- 对齐图像特征和文本特征,使得多模态编码器更容易执行跨模态学习;
- 提高了单模态编码器理解图像和文本语义含义的能力;
- 学习一个共同的低维空间来嵌入图像和文本,这使得图像-文本匹配目标能够通过我们的对比硬负样本挖掘找到更多信息丰富的样本;
- 引入动量知识蒸馏(Momentum Distillation, MoD)的方法,使模型能够利用更大的未筛选的网络数据集
ALBEF架构如何
ALBEF框架本身,并不难以理解,就是结合了ViT和Bert两个模型,进行离线知识蒸馏的训练。
如图所示,ALBEF包含
Student Model | Teacher Model | |
---|---|---|
参数和梯度 | 保存梯度,参数根据loss更新 | 无梯度,参数冻结,不更新 |
图像编码 | ViT-B/16构成的image_encoder | ViT-B/16构成的image_encoder_m |
文档编码 | Bert前六层构成的text_encoder | Bert前六层构成的text_encoder_m |
图像和文档融合编码 | Bert的后六层构成Multimodal_encoder | Bert的后六层构成Multimodal_encoder_m |
为什么代码中没有体现Multimodal_encoder?
- 一个image_encoder,由ViT-B/16构成,给定一张图片作为输入,被其编码成encoder_hidden_states;
- 一个text_encoder,由BERT的前6层BertLayer构成,给定图片的描述,被其编码成hidden_states;
- 通过配置文件中的fusion_layer=6,传入xbert.py的470行中的self.has_cross_attention = (layer_num > config.fusion_layer),来判定从Bert的第几层进行分割;
- 一个multimodal encoder,由BERT的后6层构成,image_encoder和text_encoder的输出encoder_hidden_states和hidden_states全部送入这里,通过Bert的cross_attention(BertAttention)进行多模态特征融合(说是crossattention,实际上是经过一层attention后做了加和);
ALBEF损失
L i m a g e _ t e x t _ c o n t r a s t i v e = 1 2 E ( I , T ) ∼ D [ H ( y i 2 t ( I ) , p i 2 t ( I ) ) + H ( y t 2 i ( T ) , p t 2 i ( T ) ) ] L m a s k _ l a n g u a g e _ m o d e l = E ( I , T ^ ) ∼ D [ H ( y m a s k , p m a s k ( I , T ^ ) ) ] L i m a g e _