
仰止
2022/12/16阅读:73主题:默认主题
图文结合-LXMERT
本文介绍一篇图文结合的经典论文,论文发布于2019年,算是最早出来的一批模型
论文信息

论文题目:
LXMERT: Learning Cross-Modality Encoder Representations from Transformers
论文地址:
https://arxiv.org/abs/1908.07490
代码地址:
https://github.com/airsplay/lxmert
主要内容
1、模型结构
LXMERT是典型的双流模型结构,其结构图如下:
(1)input embedding
有word-level sentence embedding和object-level image embedding。
文本的计算如下:
其中i表示位置信息。
图片的计算如下:
图片的编码器的实现如下:
class VisualFeatEncoder(nn.Module):
def __init__(self, config):
super().__init__()
feat_dim = VISUAL_CONFIG.visual_feat_dim
pos_dim = VISUAL_CONFIG.visual_pos_dim
# Object feature encoding
self.visn_fc = nn.Linear(feat_dim, config.hidden_size)
self.visn_layer_norm = BertLayerNorm(config.hidden_size, eps=1e-12)
# Box position encoding
self.box_fc = nn.Linear(pos_dim, config.hidden_size)
self.box_layer_norm = BertLayerNorm(config.hidden_size, eps=1e-12)
self.dropout = nn.Dropout(config.hidden_dropout_prob)
def forward(self, visn_input):
feats, boxes = visn_input
x = self.visn_fc(feats)
x = self.visn_layer_norm(x)
y = self.box_fc(boxes)
y = self.box_layer_norm(y)
output = (x + y) / 2
output = self.dropout(output)
return output
这里的Object feature表示ROI特征,box pos应该是region的7维坐标特征,论文称之为“bounding box coordinates”。
(2)encoder
作者主要根据两种注意力机制(self-attention & cross attention)构建编码器。LXMERT包含单模态编码器(self-attn)和交叉模态编码器(cross-attn),我们重点介绍下交叉模态编码器。
一个cross-modality encoder包括两个self-attention、两个FFN、以及一个双向cross-attention。其中cross-attention在最前面(至于为什么将cross-attn放在前面,论文并没有说明,应该是根据猜想和实验结果来的,先对两种模态进行交叉,再对交叉后的进行自处理,这种结构可以充分实现不同模态之间的交互),其计算公式为:
为了进一步建立内部连接,在模型设计时,作者将自注意力模块应用于cross-attn的输出,如下所示:
其中k表示网络的层数。
作者将交互层分成了三个部分,文本编码层(l)、视觉编码层(r)和交叉编码层(x)。其中,文本编码层和视觉编码层与bert一致,交叉编码层的实现如下:
class LXRTXLayer(nn.Module):
def __init__(self, config):
super().__init__()
# The cross-attention Layer
self.visual_attention = BertCrossattLayer(config)
# Self-attention Layers
self.lang_self_att = BertSelfattLayer(config)
self.visn_self_att = BertSelfattLayer(config)
# Intermediate and Output Layers (FFNs)
self.lang_inter = BertIntermediate(config)
self.lang_output = BertOutput(config)
self.visn_inter = BertIntermediate(config)
self.visn_output = BertOutput(config)
def cross_att(self, lang_input, lang_attention_mask, visn_input, visn_attention_mask):
# Cross Attention
lang_att_output = self.visual_attention(lang_input, visn_input, ctx_att_mask=visn_attention_mask)
visn_att_output = self.visual_attention(visn_input, lang_input, ctx_att_mask=lang_attention_mask)
return lang_att_output, visn_att_output
def self_att(self, lang_input, lang_attention_mask, visn_input, visn_attention_mask):
# Self Attention
lang_att_output = self.lang_self_att(lang_input, lang_attention_mask)
visn_att_output = self.visn_self_att(visn_input, visn_attention_mask)
return lang_att_output, visn_att_output
def output_fc(self, lang_input, visn_input):
# FC layers
lang_inter_output = self.lang_inter(lang_input)
visn_inter_output = self.visn_inter(visn_input)
# Layer output
lang_output = self.lang_output(lang_inter_output, lang_input)
visn_output = self.visn_output(visn_inter_output, visn_input)
return lang_output, visn_output
def forward(self, lang_feats, lang_attention_mask,
visn_feats, visn_attention_mask):
lang_att_output = lang_feats
visn_att_output = visn_feats
lang_att_output, visn_att_output = self.cross_att(lang_att_output, lang_attention_mask,
visn_att_output, visn_attention_mask)
lang_att_output, visn_att_output = self.self_att(lang_att_output, lang_attention_mask,
visn_att_output, visn_attention_mask)
lang_output, visn_output = self.output_fc(lang_att_output, visn_att_output)
return lang_output, visn_output
通过上面一段代码,我们可以知道cross-model的建立主要是cross-attn和self-attn,(上面对cross-attn的命名很奇怪,不知道为什么这样命名,有误导的概率)由于在模型训练时,根据矩阵的计算规则,如 会计算 和 的所有元素,因此当文本在前时,计算的是每个字和所有视觉特征之间的注意力,反之同理。所以,需要计算两边cross-attn。
同时,还有一个需要注意的是,在计算文本对图片注意力时,mask的是图片,也就是说对于图片的填充部分没有必要算,反之同理。这部分的mask应该是类比文本中的对padding部分的mask,有点疑惑的是,为什么不同时对两者进行mask,猜测可能是文本的空白,其对应的图片不一定空白,反之亦然,所以这样可以更有利于建立图文之间的语义联系,实现语义上的补充。论文里将这一部分称作“双向cross-attn”。
交叉注意力的实现如下:
def forward(self, hidden_states, context, attention_mask=None):
mixed_query_layer = self.query(hidden_states)
mixed_key_layer = self.key(context)
mixed_value_layer = self.value(context)
query_layer = self.transpose_for_scores(mixed_query_layer)
key_layer = self.transpose_for_scores(mixed_key_layer)
value_layer = self.transpose_for_scores(mixed_value_layer)
可以看到,当target和source不同的时候,对两着进行attention计算,得到的就是两个向量表征的cross-attn。
由于模型在结构上是先经过cross-attn,然后才是各自的self- attn,所以在上面类的代码中也显示的很清楚,先cross-attn的输出作为self-attn的输入。
2、预训练任务

(1)Masked Cross-Modality LM
和BERT的MLM类似的任务,不过在对masked进行预测时(假设文本masked),不仅会使用周围未被mask的文本,还会用到视觉语义信息,对其进行masked,以解决存在的歧义问题,有助于建立视觉信息到语言信息之间的联系。同时,作者使用BERT的参数,对LXMERT进行初始化,发现这一操作会产生负作用,因为BERT仅仅使用了文本信息,使得参数并不带有对应的多模态联系信息,所以最后作者从头开始训练的预训练模型。
(关于这一点,我觉得不能一概而论,记得之前时UNITER还是ViLT做过实验,发现BERT的参数相比于随机化是有所提升的,因为其富含丰富的语义信息,所以,个人认为,能不能提升需要结合具体的数据集和模型结构来判断,并以最后的实验结果为准)
(2)Masked Obecjt Predicition
该认为与LM类似,将从图片提取的ROI特征部分置零,实现mask。一是为了学习图片region之间的语义关系,二是为了学习多模态之间的对齐。为此,作者设计了两个子任务。
ROI-feature Regression
回归任务,以L2 loss作为损失函数,计算输出与对应的 之间的L2损失。
Dctected-Label Classification
分类任务,学习masked区域的label信息,使用CE作为损失函数。同时,作者提到,虽然大多数预训练图像都有注释,但是,注释对象的真实标签在不同的数据集中是不一致的,(比如不同的数据集,同一类型的图片可能有不同的编号),因此,作者最后使用经过Faster-RCNN检测得到的标签作为对应图片的标签,并实验证明这一点有利于最后的结果。
作者对视觉预训练任务进行了消融实验,结果如下:
(3)Cross-Modality Tasks
多模态任务一共有两个子任务
Cross-Modality Matching
图文匹配任务,正负比例1:1
Image Question Answering
根据图片和question预测对应的答案。作者根据实验的表现,只在后10个epoch对该任务进行预训练,因为该任务的收敛更快,并且根据经验需要更小的学习率。作者对QA的效果进行了消融实验: 如上表的row2和row4,加入QA的预训练后,整体提升较大。
预训练数据的选择如下:

对于LXMERT在预训练时涉及的多个loss,作者最后给与相同的权重。
针对上面提到的随机初始化还是使用BERT参数进行初始化,作者在进行预训练消融实验时,也对结果进行了展示,如下:
3、下游任务
LXMERT与当时的SOTA对比实验:
对于 数据集,由于其一个样本存在两个图片+一个文本,所以其无法直接使用LXMERT进行预测,对此,本文的做法是,分别将图片和文本组合,然后concat两个图文的对的输出,然后建立分类器。 具体的计算方式如下:
其中 是sigmoid函数,使用CE作为loss,计算方式为:
总结
这篇论文发表的很早,使用的方法也被后面很多人借鉴。不过,该论文作为一篇经典的双流模型论文,其与单流模型最大的不同就在于模型的结构。单流模型直接在输入层拼接两者的emb,所以在交互层就不需要更多的设计,上一个Transformer就足够了。但是,双流层不同,其需要通过cross-attn对交互层进行精心的设计,比如本文交互层,将cross-attn放在self-attn前,这种网络层顺序的确定,就是一个会对结果产生决定性影响的因素。
所以,这样来看,貌似单流模型更为简单,不需要对模型结构进行更多的思考,但是,同样的,其上限就摆在那,双流模型如果能够设计一个完美的结构,将可以对信息实现完美的交互,进而实现sota。
【往期内容】
作者介绍
