图文结合-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。

文本的计算如下:
w ^ i = w o r d e m b e d ( w i ) \hat{w}_i=wordembed(w_i) w^i=wordembed(wi)
μ ^ i = i d x e m b e d ( i ) \hat{\mu}_i=idxembed(i) μ^i=idxembed(i)
h i = l a y e r n o r m ( w ^ i + μ ^ i ) h_i=layernorm(\hat{w}_i+\hat{\mu}_i) hi=layernorm(w^i+μ^i)

其中i表示位置信息。

图片的计算如下:
f ^ j = l a y e r n o r m ( W F f j + b F ) \hat{f}_j=layernorm(W_Ff_j+b_F) f^j=layernorm(WFfj+bF)
p ^ j = l a y e r n o r m ( W P p j + b P ) \hat{p}_j=layernorm(W_Pp_j+b_P) p^j=layernorm(WPpj+bP)
v j = ( f ^ j + p ^ j ) / 2 v_j=(\hat{f}_j+\hat{p}_j)/2 vj=(f^j+p^j)/2
其中, p j p_j pj表示region的坐标信息, f j f_j fj表示2048-dim的ROI特征。至于为什么最后需要除以2,论文里并没有说明。猜测可能由于在求图片编码时,前面都使用了LN,最后相加除2是为了保证值域范围与前面相同,并且也能与文本的输出相同。

图片的编码器的实现如下:

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放在前面,论文并没有说明,应该是根据猜想和实验结果来的,先对两种模态进行交叉,再对交叉后的进行自处理,这种结构可以充分实现不同模态之间的交互),其计算公式为:
h ^ i k = c r o s s a t t n L − > R ( h i k − 1 , { v 1 k − 1 , . . . , v m k − 1 } ) \hat{h}^k_i=crossattn_{L->R}(h_i^{k-1},\{v_1^{k-1},...,v_m^{k-1}\}) h^ik=crossattnL>R(hik1,{v1k1,...,vmk1})
v ^ j k = c r o s s a t t n R − > L ( v j k − 1 , { h 1 k − 1 , . . . , h m k − 1 } ) \hat{v}^k_j=crossattn_{R->L}(v_j^{k-1},\{h_1^{k-1},...,h_m^{k-1}\}) v^jk=crossattnR>L(vjk1,{h1k1,...,hmk1})

为了进一步建立内部连接,在模型设计时,作者将自注意力模块应用于cross-attn的输出,如下所示:
h ~ i k = s e l f a t t n L − > L ( h ~ i k , { h ~ 1 k , . . . , h ~ n k } ) \tilde{h}_i^k=selfattn_{L->L}(\tilde{h}_i^k,\{\tilde{h}_1^k,...,\tilde{h}_n^k\}) h~ik=selfattnL>L(h~ik,{h~1k,...,h~nk})
v ~ j k = s e l f a t t n R − > R ( v ~ j k , { v ~ 1 k , . . . , v ~ m k } ) \tilde{v}_j^k=selfattn_{R->R}(\tilde{v}_j^k,\{\tilde{v}_1^k,...,\tilde{v}_m^k\}) v~jk=selfattnR>R(v~jk,{v~1k,...,v~mk})

其中k表示网络的层数。

作者将交互层分成了三个部分,文本编码层(l)、视觉编码层®和交叉编码层(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的命名很奇怪,不知道为什么这样命名,有误导的概率)由于在模型训练时,根据矩阵的计算规则,如 A B AB AB会计算 a i a_i ai B B B的所有元素,因此当文本在前时,计算的是每个字和所有视觉特征之间的注意力,反之同理。所以,需要计算两边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作为损失函数,计算输出与对应的 f i f_i fi之间的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对比实验:

对于 N L C R 2 NLCR^2 NLCR2数据集,由于其一个样本存在两个图片+一个文本,所以其无法直接使用LXMERT进行预测,对此,本文的做法是,分别将图片和文本组合,然后concat两个图文的对的输出,然后建立分类器。
具体的计算方式如下:
x 0 = L X M E R T ( i m g 0 , s ) x_0=LXMERT(img_0,s) x0=LXMERT(img0,s)
x 1 = L X M E R T ( i m g 1 , s ) x_1=LXMERT(img_1,s) x1=LXMERT(img1,s)
z 0 = W 0 [ x 0 ; x 1 ] + b 0 z^0=W_0[x_0;x_1]+b_0 z0=W0[x0;x1]+b0
z 1 = L a y e r N o r m ( G e L U ( z 0 ) ) z^1=LayerNorm(GeLU(z^0)) z1=LayerNorm(GeLU(z0))
p r o b = σ ( W 1 z 1 + b 1 ) prob=\sigma{(W_1z^1+b_1)} prob=σ(W1z1+b1)

其中 σ \sigma σ是sigmoid函数,使用CE作为loss,计算方式为:
L = − y ∗ l o g p r o b − ( 1 − y ∗ ) l o g ( 1 − p r o b ) L=-y^*logprob-(1-y^*)log(1-prob) L=ylogprob(1y)log(1prob)

总结

这篇论文发表的很早,使用的方法也被后面很多人借鉴。不过,该论文作为一篇经典的双流模型论文,其与单流模型最大的不同就在于模型的结构。单流模型直接在输入层拼接两者的emb,所以在交互层就不需要更多的设计,上一个Transformer就足够了。但是,双流层不同,其需要通过cross-attn对交互层进行精心的设计,比如本文交互层,将cross-attn放在self-attn前,这种网络层顺序的确定,就是一个会对结果产生决定性影响的因素。

所以,这样来看,貌似单流模型更为简单,不需要对模型结构进行更多的思考,但是,同样的,其上限就摆在那,双流模型如果能够设计一个完美的结构,将可以对信息实现完美的交互,进而实现sota。

【往期内容】

图文结合-UNITER

图文结合-ViLT

图文结合-SOHO

图文结合-imageBERT

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值