标签 深度学习 下的文章

ALBEF paper : http://arxiv.org/abs/2107.07651

BLIP paper : http://arxiv.org/abs/2201.12086

BLIP code : https://github.com/salesforce/BLIP

ALBEF

网络结构如下,这篇算得上BLIP/BLIP2的前身了,其三个Loss一直延续至今(当然MLM变成了LM)。具体三个Loss的介绍可以看BLIP2 - JJJYmmm Blog,有以下几个特别点:

  • ITC Loss采用了MOCO的形式,即通过一个momentum encoder来扩大负样本的数量,这也是多了一个momentum model的原因。论文还从模型蒸馏的角度对momentum model做了进一步改进,例如在计算ITC和MLM时引入了伪标签
  • 这里对于文本理解任务,采用的是MLM而不是LM,可能是因为MLM任务相比于LM任务更简单,因为模型只需要预测被mask的单词即可
  • 在计算ITC时得到的Image-Text Similarity可以挑选出hard negatives,专门去做ITM;这个方法在BLIP和BLIP2都用到
  • 在这里三个Encoder都不共享参数,算是典型的双流模型

BLIP

image-20231104162902822

网络结构如上,可以看到不同Encoder之间共享参数。loss则与ALBEF没什么区别。其他值得注意的点有:

  • 对于单模态Encoder,无论是文本还是图片,都是采用self attention提取特征;对于Image-grounded Text Encoder,主要添加了一个cross attention层,KVs是Image Embedding;对于Image-grounded Text Decoder,替换了self attention层(其实就是mask改成casual mask吧~) ; 三个text相关的encoder/decoder都共用FFN;关于参数共享的细节,可以看消融实验
  • 其实一直都有一个问题,为什么Image-grounded要让Image Embeddings作为cross attention的KVs呢?这样text作为query,cross attn的结果不就是通过text加权得到的image features?这样得到的特征应该更多与image有关而不是text有关吧(我能想到的一个原因是auto-regressive限制了decoder的input必须是text)
  • BLIP的主要亮点是对数据集的处理,这里引入了半监督bootstrap的做法,具体看下面这张图就懂了~这里论文同样从模型蒸馏的角度来说明bootstrap的有效性,在后续的实验中也表明,每次对于清洗/扩充后的数据集,都应该从头对模型进行pre-train,这符合模型蒸馏中的学生模型不应该继承教师模型参数的常识。

image-20231104164756650

Repository:https://github.com/salesforce/LAVIS/tree/main/projects/blip2

预训练结构

第一阶段

网络结构如下图。

  • 对于图像特征,采用DETR类似的思路,使用Learned Queries作为输入,Image Features作为cross attention的KVs,希望通过可学习的参数来抽取与文本更相关的视觉特征
  • 对于文本特征,采用传统的Bert Encoder思路
  • 对于多模态特征的融合,与双流模型不同,这里两个Encoder的self attention层是共享参数的,当然与单流模型也不同,因为FFN不共享参数,且视觉特征提取时还会走cross attention层

image-20231104154128875

训练采用的三个Loss函数(ITG/ITM/ITC)主要参考之前的ALBEF工作。

  • Image-Text Contrastive : 这部分的目的主要是对齐图像特征和文本特征的单模态特征,计算方法类似CLIP。与ALBEF不同的是,这里的negative pairs直接采用in-batc方式得到,并没有像ALBEF那样借鉴MOCO得到一个较大的Dictionary
  • Image-Text Matching : 这部分的目的是学习图像特征与文本特征的细粒度对齐,通过外接二分类器计算Loss。这一阶段Query和Text可以互相关注
  • Image-Grounded Text Generation : 这一部分主要是训练Queries捕获有关文本所有信息的视觉特征,因为在这一步Query是无法看到Text信息的,而Text可以通过self attention层看到Query并输出结果,所以Query只能从Image Feature中尽可能提取与文本相关的视觉特征,才能生成一个质量比较高的Text(一个直观理解的explanation)

第二阶段

第二阶段的网络结构如下,通过一个FC对齐Qformer与LLM的维度,并对Qformer进一步微调。Qformer的输出主要作为LLM的一个soft visual prompts,提示LLM的输出。

image-20231104155834896

源码

BLIP2的model文件在lavis/models/blip2_models下,之后默认以此为根目录

阅读顺序(主要类与函数)如下:

  • ../base_model.py : BaseModel(nn.Module)
  • blip2.py : Blip2Basecompute_sim_matrix
  • Qformer.py : BertEmbeddings, BertLayer, BertEncoder, BertPooler, BertModel, BertOnlyMLMHead, BertLMHeadModel
  • blip2_qformer.py : Blip2Qformer,

关注点主要是Qformer的实现,其实就是一个魔改的Bert

  • Learned Queries和Text会一起进入Encoder做attention,两者之间的交互由self-attention层的mask控制,具体mask信息参照论文Figure 2
  • 魔改的Bert会每隔1个Encoder Layer()就在该层self-attention层后添加一个cross-attention层,其KVs是Image Encoder的输出即视觉特征
  • 只有Query部分激活cross-attention层进行计算,会通过input[:, :query_length, :]进行截取;同理,如果输入只有Text(Unimodal)则根本不会进行cross-attn计算
  • past_key_values或者说KV cache是在自回归decoder推理时加速的trick,具体来说就是decoder上一次运算各层attention的结果KVs(即当前所有token的embedding信息)会被保存,下次运算时,只需要输入新的token(seq_len=1),进行attention计算时加入之前保存的KVs即可。这样做的可行性主要来自自回归模式的无后效性,token做attention时只会和自己以及之前的token交互
  • 在Qformer第一阶段训练中,past_key_values只使用一次:单模态阶段分别encode图像(Query)特征和文本特征,在encode图像(Query)特征时保存各个attn层的KVs;在计算ITG损失时,由于Query和Text都可以看到所有Query,于是这里就使用了当时单模态计算Query特征时的KVs,模型就只需要跑Text部分即可(使用past_key_values后,Query不需要也不能拼接在Text前面作为输入)
  • 一般来说计算ITM损失时也可以利用计算单模态Query时的产生的KVs,代码里之所以没这么做,是因为计算ITM时额外使用了hard negatives mining,输入已经发生变化

网络

原始网络结构

​ 原始网络采用24层卷积层进行特征的抽取,这部分参数在ImageNet数据集上预训练来初始化。

​ Head部分采用两个全连接层实现,首先将7*7*1024的Tensor Flatten,并送入输出为4096的全连接层;再接一个输出为1470的全连接层。

​ 最后将1470的向量reshape成7*7*30.

image-20230216131611641

输出解析

​ 输出为SxSx(B*5+C)的tensor,其中SxS为grid cell的个数.

​ 每个grid cell预测B个bounding box.每个bounding box有5个参数----中心点的坐标x和y,box的宽w和高h以及这个box包含物体的置信度confidence.

​ C是物体各个类别的概率(这里是20),这里的概率是条件概率,即在这个grid cell预测的某个bounding box预测物体时,各个类别的概率.

$$ P(class)=P(class|obj)*confidence $$

image-20230216132659702

损失函数

​ 损失函数loss共分为五个部分:

  • 第一部分是负责检测物体的bounding box的中心点定位误差

如何确认负责检测物体的box?

对于训练数据,确定gtbox的中心点,由这些中心点落在的grid cell负责预测该物体.而一个grid cell将预测B个box,将从这B个box中选出与gtbox IOU值最大的那个box作为预测框.

这个cell的其他B-1个box和那些不预测物体的cell产生的box一样,不计入xywh的损失,只计算他们的置信度误差(c=0)

  • 第二部分是负责检测物体的bounding box的宽高定位误差
这里开根号是为了降低大框的loss权重,让网络更关注小框
  • 三四部分都是置信度回归误差.计算预测值C与标签值的平方和误差

对于预测物体的bounding box,标签值这里取得是 C = Pr(obj)*IOU 其实对于预测物体的bouding box Pr(obj)=1 所以标签值就是IOU

对于其他的box,标签值就是0.

  • 最后一部分是类别预测的误差.对于那些预测物体的grid cell 计算各个概率的平方和误差

image-20230216133352645

loss中$\lambda$是各个损失的权值,比如应该更关注预测物体的box的误差 $\lambda_{coord}$而相对忽略不预测物体的box误差$\lambda_{noobj}$

网络改进

网络改进主要有以下几点:

①改进骨干网络

​ 官方的YOLOv1的主干网络是参考了GoogLeNet设计的(没有inception结构),这里我们直接替换成ResNet18。关于ResNet18的网络结构,如下图所示

image-20230216135227066

ResNet18网络更轻,使用了诸如residual connection、batch normalization等结构,性能上要更强于原先的backbone网络。这里,我们没必要换更大的ResNet网络,如ResNet50、101等,18即可满足要求。

②增加Neck

​ 对于给定输入的416x416x3的图像,经过ResNet18网络处理后,最后会输出一张13x13x512的特征图。这里,我们添加一个Neck结构,对特征图中的信息进行更好地处理,这里,我们选择性价比极高的SPP

image-20230216135425604

注意,SPP接受的输入特征图大小是13x13x512,经过四个maxpooling分支处理后,再汇合到一起,那么得到的是一个13x13x2048的特征图,这里,我们会再接一个1x1的卷积层(conv1x1+BN+LeakyReLU)将通道压缩一下,这个1x1的卷积层没有在图中体现出来。

最终Neck部分的输出同样是13x13x512的特征图。

③改进Detection head

​ 官方的YOLOv1中这一部分使用了全连接层,也就是先将特征图flatten成一维向量,然后接全连接层得到4096维的一维向量。这里,我们抛掉flatten操作,而是在SPP输出的13x13x512的特征图上使用若干层卷积来处理,类似于RetinaNet那样。这里,我们使用非常简单的1x1卷积和3x3卷积重复堆叠的方式,如下图所示:

image-20230216135520838

④改进prediction层

​ 官方的YOLOv1最后使用全连接层来做预测,我们直接替换成当下主流的做法:用1x1卷积在特征图上去做预测,具体来说,head输出一个13x13x512大小的特征图,然后用1x1卷积(不接BN层,不接激活函数)去得到一个13x13x(1+C+4)的特征图,其中1对应YOLO中的objectness预测,C对应类别预测(PASCAL VOC上,C=20;COCO上,C=80),4则是bbox预测.

注意!!! 这里,每个grid处只预测一个bbox,而不是B个

image-20230216135613907

​ 如上图所示,objectness分支我们使用sigmoid来输出,class分支则用softmax来输出,这三个预测,我们稍微展开讲一下。

objectness预测

​ 不同于官方YOLOv1中的使用预测框和真实框之间的IoU作为优化目标,我们直接采用最简单的01标签即可。无需在训练过程中计算IoU。

class预测

​ 不同于官方YOLOv1的线性输出,我们使用更为合理的softmax来输出。

bbox预测

​ 在YOLOv1中,bbox分支就是学习中心点的偏移量 $c_x,c_y$ 和归一化的边界框的宽高w,h ,但是不论是哪个量,YOLOv1均使用线性函数来输出,未加任何约束限制,很明显会有以下两点问题:

a) 由于偏移量$c_x,c_y$是介于01范围内的数,因此,其本身就是有上下界的,而线性输出并没有上下界,这就容易导致在学习的初期,网络可能预测的值非常大,导致bbox分支学习不稳定。因此,在YOLOv2的时候,作者就添加了sigmoid函数来将其映射到01范围内。

这里,我们也采用同样的办法,对于偏移量部分,我们使用sigmoid来输出,并将其符号改为 $t_x,t_y$

img

b) 边界框的宽高显然是个非负数,而线性输出不能保证这一点,输出一个负数,是没有意义的。一种解决办法是约束输出为非负,如用ReLU函数,但这种办法就会隐含一个约束条件,这并不利于优化,而且ReLU的0区间无法回传梯度;另一个办法就是使用exp-log方法,具体来说,就是将 w,h 用log函数来处理一下:

$tw=log(w)$

$t_h=log(h) $

网络去学习 $t_w,t_h$ ,由于这两个量的值域是实数全域,没有上下界,因此就无需担心约束条件对优化带来的影响。然后,网络对于预测的$t_w,t_h$ 使用exp函数即可得到

$w=exp(t_w) $

$h=exp(t_h) $

⑤损失函数

image-20230216140726820

除此之外还使用了Batch norm (卷积+BN+激活函数)

最终网络

image-20230216140920400

这里B=1,S=13

复现

项目结构

项目结构如下图,其中:

  • 根目录下的test.py/train.py分别用于模型的测试与训练
  • backbone文件夹存放了骨干网络resnet的网络结构,并提供了预训练选项。网络结构见resnet论文精读)
  • data文件夹存放了VOC数据集和COCO数据集的DataSet实现,其中tranform.py保存了一些比较常见的数据增广函数,如随机剪裁、随机翻转、HSV调节等
  • evaluator文件夹下主要是评估模型的evaluator,通过mAP衡量模型的accu
  • models文件夹存放了yolov1的网络实现,loss.py存放了损失函数的实现
  • utils文件夹存放了计算模型参数容量的工具

image-20230217162257465

项目依赖

  • torch
  • torchvision
  • opencv-python
  • thop
  • scipy
  • matplotlib
  • numpy
  • pycocotools

开源代码

JJJYmmm的github

运行效果

训练:train.py

1.可选参数

image-20230218070234086

2.训练过程

image-20230218070803677

从头开始训练

python train.py --cuda -d voc -ms -bs 64 -accu 4 --lr 0.001 --max_epoch 150 --lr_epoch 90 120

keep-training(继续训练)

python train.py --cuda -d voc -ms -bs 64 -accu 4 --lr 0.001 --max_epoch 150 --lr_epoch 90 120 --start_epoch 120 -r ~/d2l/myYOLO/weights/voc/yolo/yolo_epoch_111_64.3.pth

测试:test.py

1.可选参数如下

image-20230218070702810

2.预测效果

image-20230218080645418

image-20230218080829254

python test.py --cuda -d voc --weight weights/voc/yolo/yolo.pt

评估:eval.py

1.评估结果如下,在voc2007测试集上mAP=0.6632,大于原论文yolov1的mAP=0.634

image-20230218081631051

python eval.py --cuda -d voc --weight 权重文件路径 -size 输入图像尺寸

Abstract

​ 本文提出了一个开源的可拓展的知识抽取工具包——DeepKE,支持多场景(少资源、文档级别、多模态)下的知识数据库填充。

Introduction

​ KBP的提出是为了从文本语料库中抽取知识来补充知识库(KBs)中缺失的元素,即对知识图谱进行补充

​ DeepKE支持标准监督设置三种复杂场景下的知识抽取任务(命名实体识别、关系抽取和属性提取)。

Core Functions

​ 这一节简要介绍了一下三种知识抽取任务的概念和效果。略。

Toolkit Design and Implementation

​ DeepKE的三大特性:

  • 统一框架,在数据、模型和核心组件方面,不同的任务对象使用相同的框架。
  • 灵活使用,提供自动超参数调整等工具,提高工作效率
  • 现成模型,提供预训练的语言模型。

image-20230321145344239

Data Module

​ 数据模块主要完成数据的预处理和加载。其中的Tokenizer负责实现中文/英文的标记化。图像等其他视觉对象在多模态设置下先转化成视觉信息如标记或image patch。

image-20230321151815504

Model Module

​ 模型模块包含了三个核心任务需要用到的主要神经网络,如CNN/RNN/Transformer。DeepKE使用统一的Model Loader和saver实现BasicModel类,以此集成多种网络模型。

Core Module

​ 核心模块主要包括了train/validate/predict方面的代码。train/predict中的各种参数(epoch/data/optimizer)可以具体指定。

Framework Module

​ 框架模块整合了上述三个模块和不同的场景,支持多种功能实现,包括数据处理/模型构建/模型实现。通过修改yaml文件也可以对任务进一步客制化。

Toolkit Usage

​ 本节介绍了不同场景下DeepKE用到的一些技术。

​ 针对多模态场景,使用基于Transformer的识别抽取方法——IFAformer。具体来说,IFAformer同时将文本信息和视觉特征放在每个注意力层的key-value对中,隐式融合了文本和视觉特征。

​ 其他场景略。

image-20230321152800104

Experiment and Evaluation

​ 将DeepKE和其他现有的KBP进行对比。略。

Conclusion

​ 在实际应用中,知识库的填充需要处理低资源、文档级和多模态的场景。为此,文章提出了一个开源的可扩展知识抽取工具DeepKE。他们进行了广泛的实验,证明通过DeepKE实现的模型可以实现与一些最先进的方法相当的性能。此外,我们提供了一个在线系统,支持实时抽取(与预定义的模式),无需训练。我们将提供长期的维护来修复错误,解决问题,添加文档(教程)和满足新的要求。

Toolkit Usage Details

​ 本节主要介绍了一下DeepKE的使用。包括各个任务的数据生成,源码修改等。

​ 不同任务/场景支持的语言如下表。

image-20230321153152022

​ 自动超参调整使用了Weight&Biases这个工具。