天天看點

EasyNLP帶你玩轉CLIP圖文檢索

作者:熊兮、章捷、岑鳴、臨在

導讀

随着自媒體的不斷發展,多種模态資料例如圖像、文本、語音、視訊等不斷增長,創造了網際網路上豐富多彩的世界。為了準确模組化使用者的多模态内容,跨模态檢索是跨模态了解的重要任務,采用一種模态的資料作為資料,檢索另一種模态的資料。其中,圖文檢索是跨模态檢索的一種主流任務,廣泛應用于各種網絡應用中,其難點在于跨模态的表示鴻溝(Representation Gap)。具體來說,文本和圖像的資料處于不同的向量空間,無法直接去度量他們的相似性。OpenAI提出了CLIP(Contrastive Language-Image Pre-training)模型,在大規模圖文資料集上進行了對比學習訓練,在多個資料集上的準确度表明,CLIP優于各種基于ImageNet的模型,也具有良好的零樣本學習(Zero-shot Learning)能力。

EasyNLP是阿裡雲機器學習PAI 團隊基于 PyTorch 開發的易用且豐富的中文NLP算法架構,支援常用的中文預訓練模型和大模型落地技術,并且提供了從訓練到部署的一站式 NLP 開發體驗。EasyNLP 提供了簡潔的接口供使用者開發 NLP 模型,包括NLP應用 AppZoo 和預訓練 ModelZoo,同時提供技術幫助使用者高效的落地超大預訓練模型到業務。由于跨模态了解需求的不斷增加,EasyNLP也将支援各種跨模态模型,特别是中文領域的跨模态模型,推向開源社群,希望能夠服務更多的 NLP 和多模态算法開發者和研究者,也希望和社群一起推動 NLP /多模态技術的發展和模型落地。

本文簡要介紹CLIP的技術解讀,以及如何在EasyNLP架構中玩轉CLIP模型。

CLIP模型詳解

CLIP的模型結構相對比較簡單,展現了“大道至簡”的設計原則,其模型架構圖如下圖所示:

EasyNLP帶你玩轉CLIP圖文檢索

為了建立圖像和文本的關聯性,CLIP首先分别建構了圖像和文本的Encoder,分别對圖像和文本進行特征抽取。對于圖像而言,CLIP使用的Backbone可以是經典的ResNet系列模型,也可以是更先進的Transfomer類模型,例如VIT等;對于文本,CLIP一般使用BERT類模型進行特征抽取,也包括RoBERTa等。在特征抽取之後,CLIP分别對提取的向量進行Normalization,進而可以直接進行内積相似度計算。在模型Loss Function層面,由于圖像和文本向量都進行了Normalization,我們直接使用相乘來計算餘弦距離,使得同一圖文對的結果趨近于1,不同圖文對的結果趨近于0;并且使用對比學習損失InfoNCE進行損失計算。

當模型預訓練結束後,我們可以直接使用CLIP進行圖文的檢索,因為CLIP已經将圖文的表示映射到同一個向量空間。CLIP的另一個優勢在于可以進行Zero-shot Classification。如下圖所示,我們設計輸入文本“A photo of a {object}.”,并且使用目标圖像作為輸出。如果文本“A photo of a dog.”于目前圖像最比對(餘弦相似度最高),我們可以說明,目前圖像的物體是“dog”。由此可見,預訓練後的CLIP模型可以直接用于圖像分類,而不需要額外的訓練。

EasyNLP帶你玩轉CLIP圖文檢索

CLIP模型的訓練過程也可以直接參考原作者給出的僞代碼實作:

EasyNLP帶你玩轉CLIP圖文檢索

EasyNLP中CLIP模型的實作

在EasyNLP架構中,我們在模型層建構了CLIP模型的Backbone,核心代碼如下所示:

self.text_model = CLIPTextTransformer(text_config)
self.vision_model = CLIPVisionTransformer(vision_config)

self.visual_projection = nn.Linear(self.vision_embed_dim, self.projection_dim, bias=False)
self.text_projection = nn.Linear(self.text_embed_dim, self.projection_dim, bias=False      

其中,CLIPTextTransformer和CLIPVisionTransformer分别是基于BERT和VIT的特征提取器。前向傳播的過程也比較簡潔:

vision_outputs = self.vision_model(...)
text_outputs = self.text_model(...)

image_embeds = vision_outputs[1]
image_embeds = self.visual_projection(image_embeds)
image_embeds = image_embeds / image_embeds.norm(dim=-1, keepdim=True)

text_embeds = text_outputs[1]
text_embeds = self.text_projection(text_embeds)
text_embeds = text_embeds / text_embeds.norm(dim=-1, keepdim=True)

logit_scale = self.logit_scale.exp()
logits_per_text = torch.matmul(text_embeds, image_embeds.t()) * logit_scale
loss = clip_loss(logits_per_text)      

此外,由于CLIP模型本身具備文本和圖像的編碼器,我們直接調用他們的前向推理函數就可以實作特征的提取。對于文本我們有:

text_outputs = self.text_model(...)
pooled_output = text_outputs[1]
text_features = self.text_projection(pooled_output)      

對圖像的操作也與文本類似:

vision_outputs = self.vision_model(...)
pooled_output = vision_outputs[1]
image_features = self.visual_projection(pooled_output)      

此外,我們在多個公開資料集上驗證了EasyNLP架構中CLIP模型在各種任務上的精度。以零樣本學習為例,我們使用EasyNLP加載了開源的openai/clip-vit-large-patch14模型,對比了Top-1精度和CLIP官方論文的結果,如下所示:

資料集 Top-1 Accuracy (複現結果) CLIP 論文彙報結果
Food101 90.9 92.9
CIFAR100 78.6 77.9
EuroSAT 60.1 59.9
Oxford Pets 93.0 93.5
Fllickr30k-TR 85.3 88.0
Fllickr30k-IR 65.0 68.7

我們的實驗也說明,如果采用特定資料集的資料對CLIP進行進一步Fine-tune,CLIP能取得更好的效果。以Fllickr30k資料集為例,CLIP模型在零樣本學習和Fine-tune對比結果如下:

img2txt

(r1/r5/r10)

img2txt mean

txt2img

(r1/r5/r10)

txt2img mean
CLIP Fine-tune 91.0/99.0/99.7 95.57 76.38/94.06/97.28 89.24
CLIP Zero-shot 85.3/97.40/99.2 94.0 65.02/87.2/92.0 81.41

我們也在中文資料集上進行了預訓練,并且評測了模型在COCO-CN和Fllickr30k-CN資料集上的效果。模型的設定與WukongViT對齊(詳見參考文獻),進行了複現,結果如下所示:

資料集 模型 img2txt mean txt2img mean
COCO-CN WukongViT 96.4 89.8
CLIP 96.1 88.4
Fllickr30k-CN WukongViT 85.9 87.8
CLIP 86.0 86.1

由上述結果可見,EasyNLP架構訓練的CLIP模型在下遊任務的Finetune結果與WukongViT基本對齊。結果少量差異性的原因在于:1. MindSpore與PyTorch的内部實作差異性(WukongViT作者采用MindSpore實作)以及2. 超參數和随機種子的選擇。

為了友善使用者的使用,EasyNLP進一步提供了AppZoo層面的接口,使得使用者可以在不實作任何代碼的情況下調用CLIP模型,這一部分内容在下一節介紹。

CLIP模型使用教程

以下簡要介紹在EasyNLP架構使用CLIP模型。由于使用者資料一般于CLIP預訓練資料在分布上存在差距。我們提供CLIP模型的訓練和向量提取功能

安裝EasyNLP

使用者可以直接參考​​連結​​的說明安裝EasyNLP算法架構。

資料準備

首先準備訓練資料與驗證資料,為tsv檔案。這一檔案包含以制表符\t分隔的兩列,第一列為文本,第二列為圖檔的base64編碼。用于提取向量接入向量檢索系統的輸入檔案為單列,僅包含文本或圖檔的base64編碼。

為了友善開發者,我們也提供了轉換圖檔到base64編碼的示例代碼:

import base64
from io import BytesIO
from PIL import Image

img = Image.open(fn)
img_buffer = BytesIO()
img.save(img_buffer, format=img.format)
byte_data = img_buffer.getvalue()
base64_str = base64.b64encode(byte_data) # bytes      

下列檔案已經完成預處理,可用于測試:

# train
https://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/CLIP/MUGE_MR_train_base64_part.tsv

# valid
https://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/CLIP/MUGE_MR_valid_base64_part.tsv

# text
https://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/CLIP/MUGE_MR_test_base64_part_text.tsv

# image
https://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/CLIP/MUGE_MR_test_base64_part_image.tsv      

模型訓練和評測

我們采用以下指令對CLIP模型進行fine-tune:

easynlp \
  --mode train \
  --worker_gpu=1 \
  --tables=./MUGE_MR_train_base64_part.tsv,./MUGE_MR_valid_base64_part.tsv \
  --input_schema=text:str:1,image:str:1 \
  --first_sequence=text \
  --second_sequence=image \
  --checkpoint_dir=./clip_model/ \
  --learning_rate=1e-4  \
  --epoch_num=1  \
  --random_seed=42 \
  --logging_steps=100 \
  --save_checkpoint_steps 200 \
  --sequence_length=32 \
  --micro_batch_size=32 \
  --app_name=clip \
  --save_all_checkpoints \
  --user_defined_parameters='pretrain_model_name_or_path=clip_chinese_roberta_large_with_vit_large fix_vision=True mode=finetune'      

訓練完成後模型被儲存到./clip_model/。訓練結束後,我們可以對模型進行評估:

easynlp \
  --mode evaluate \
  --worker_gpu=1 \
  --tables=./MUGE_MR_valid_base64_part.tsv \
  --input_schema=text:str:1,image:str:1 \
  --first_sequence=text \
  --second_sequence=image \
  --checkpoint_dir=./clip_model/ \
  --random_seed=42 \
  --logging_steps=100 \
  --save_checkpoint_steps=500 \
  --sequence_length=32 \
  --micro_batch_size=32 \
  --app_name=clip      

文本或圖檔特征提取

模型訓練完畢後,我們可以将其用于文本或圖檔的特征提取,示例如下:

  1. 提取文本特征
easynlp \
      --mode predict \
      --worker_gpu=1 \
      --tables=./MUGE_MR_test_base64_part_text.tsv \
      --input_schema=text:str:1 \
      --output_schema=text_feat \
      --outputs=./text_feat.tsv \
      --first_sequence=text \
      --checkpoint_dir=./clip_model/ \
      --random_seed=42 \
      --logging_steps=100 \
      --save_checkpoint_steps=500 \
      --sequence_length=32 \
      --micro_batch_size=2 \
      --app_name=clip      
  1. 提取圖檔特征
easynlp \
      --mode predict \
      --worker_gpu=1 \
      --tables=./MUGE_MR_test_base64_part_image.tsv \
      --input_schema=image:str:1 \
      --output_schema=image_feat \
      --outputs=./image_feat.tsv \
      --first_sequence=image \
      --checkpoint_dir=./clip_model/ \
      --random_seed=42 \
      --logging_steps=100 \
      --save_checkpoint_steps=500 \
      --sequence_length=32 \
      --micro_batch_size=2 \
      --app_name=clip      

提取出的特征存儲在一個tsv檔案中,每行對應輸入中的一個文本或一個圖檔,次元之間采用制表符\t分隔。

未來展望

在未來,我們計劃在EasyNLP架構中公開以PyTorch實作的CLIP模型,覆寫各個常見中文領域,敬請期待。我們也将在EasyNLP架構中內建更多SOTA模型(特别是中文模型),來支援各種NLP和多模态任務。此外,阿裡雲機器學習PAI團隊也在持續推進中文多模态模型的自研工作,歡迎使用者持續關注我們,也歡迎加入我們的開源社群,共建中文NLP和多模态算法庫!

Reference

  1. Alec Radford, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal Girish Sastry, Amanda Askell, Pamela Mishkin, Jack Clark, Gretchen Krueger. Ilya Sutskever. Learning transferable visual models from natural language supervision. arXiv
  2. Chengyu Wang, Minghui Qiu, Taolin Zhang, Tingting Liu, Lei Li, Jianing Wang, Ming Wang, Jun Huang, Wei Lin. EasyNLP: A Comprehensive and Easy-to-use Toolkit for Natural Language Processing. arXiv
  3. Jiaxi Gu, Xiaojun Meng, Guansong Lu, Lu Hou, Minzhe Niu, Xiaodan Liang, Lewei Yao, Runhui Huang, Wei Zhang, Xin Jiang, Chunjing Xu, Hang Xu.Wukong: 100 Million Large-scale Chinese Cross-modal Pre-training Dataset and A Foundation Framework. arXiv

阿裡靈傑回顧

  • ​​阿裡靈傑:阿裡雲機器學習PAI開源中文NLP算法架構EasyNLP,助力NLP大模型落地​​
  • ​​阿裡靈傑:預訓練知識度量比賽奪冠!阿裡雲PAI釋出知識預訓練工具​​