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的模型結構相對比較簡單,體現了「大道至簡」的設計原則,其模型框架圖如下圖所示:

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 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對齊(詳見參考文獻),進行了復現,結果如下所示:

1655170708308_64D5C1F2-3503-4dde-B108-420880485D06.png

由上述結果可見,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
//atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com/release/tutorials/CLIP/MUGE_MR_train_base64_part.tsv

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

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

# image
//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 

2.提取圖片特徵

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地址://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

阿里靈傑回顧