EasyNLP帶你玩轉CLIP圖文檢索

語言: CN / TW / HK

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

導讀

隨著自媒體的不斷髮展,多種模態資料例如影象、文字、語音、視訊等不斷增長,創造了網際網路上豐富多彩的世界。為了準確建模使用者的多模態內容,跨模態檢索是跨模態理解的重要任務,採用一種模態的資料作為資料,檢索另一種模態的資料。其中,圖文檢索是跨模態檢索的一種主流任務,廣泛應用於各種網路應用中,其難點在於跨模態的表示鴻溝(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的模型結構相對比較簡單,體現了“大道至簡”的設計原則,其模型框架圖如下圖所示:

image

為了建立影象和文字的關聯性,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模型可以直接用於影象分類,而不需要額外的訓練。

image

 

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

image

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 img2txt mean 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和多模態演算法庫!

Github地址:https://github.com/alibaba/EasyNLP

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

阿里靈傑回顧