8.7 Image Captioning 圖像描述
前言
圖像描述是CV與NLP結合的一個典型任務,也是CV與NLP橋樑,本節將介紹圖像描述模型的訓練及使用,包括經典的CNN+RNN,以及現在流行的多模態模型。
本節內容將包括:
- 圖像描述的概念,發展歷史,常用資料集,BLUE評價指標
- 基於CNN+RNN+attention機制的圖像描述模型訓練
- 基於Clip+GPT2的圖像描述模型訓練
圖像描述簡介
Image Captioning (圖像描述)是對圖像採用文字進行描述的過程,Image Captioning又稱圖像圖像字幕生成、圖像標注、圖像說明等,目前應用在醫療診斷、智慧客服、人機交互等領域。
圖像描述是一個交叉領域,將圖片的視覺特徵和自然語言處理結合起來,實現自動化的圖片描述。
- 2014年之前,主流方法是採用數位影像處理進行特徵提取,然後通過對特徵的描述,實現image captioning。
- 2014年,Google發表了《Show and Tell: Lessons Learned from the 2015 MSCOCO Image Captioning Challenge?,首次採用CNN+RNN的形式進行端到端的深度學習模型訓練,並且獲得2015 COCO 圖像描述的冠軍。
- 2015年2月,Bengio領銜的團隊針對Show and Tell改進,加入了attention機制,發表了《Show, Attend and Tell》, 該方法在文本生成模組,加入注意力機制,挖掘單詞與圖像區域的關聯關係。
- 2019年,得益于Transformer廣泛應用,圖像描述開啟了Transformer一統天下的時代,先後有Attention on Attention for Image Captioning、Image Captioning: Transforming Objects into Words和Entangled Transformer for Image Captioning等論文採用了Transformer進行圖像描述,簡要看了論文,發現模型結構圖略顯負責,不夠優雅,這裡就不貼圖了,因為後續的超大預訓練模型將會開啟圖像描述新範式。
- 2021年,隨著圖文、文本任務的預訓練模型(pre-training model)的成功,學者迅速將其應用於圖像描述,2021年有ClipCap,2022年BLIP, 2023年1月BLIPv2,目前BLIP系列已經達到較好效果,也將是本案例的重點。
簡單來說,圖像描述主流方法的發展,先經過CNN+RNN,及其變體,再到Transformer,再到VLP(visual language pre-training)模式,隨著ChatGPT等預訓練模型的成功,多模態任務也將有更多應用。
圖像描述資料集
圖像描述常用的資料集有Flickr8K、Flickr30K、Conceptual Captions (CC),COCO2014,資料集中語料庫的高頻詞雲如下圖所示:
圖片來源:《2021-07-From Show to Tell A Survey on Deep Learning-Based Image Captioning》
本案例將採用COCO2014,該資料集train有82783張,val有40504張,test有40775張,每張圖片對應有5~7句的caption。為了線下比較模型的性能,會把train和val經過karpathy分割後,train變成113287張,val變成5000張,test變成5000張,而線上測試的test不變,仍為40775張。
標注規則為:
- 描述這個場景的所有重要部分;
- 不描述不重要的細節。
- 不要描述在未來或過去可能發生的事情。
- 不描述一個人可能會說什麼。
- 不提供專有的人名。
- 這些句子應該至少包含8個單詞。
更多資料集介紹可參考Image Caption 2021最新整理:資料集 / 文獻 / 代碼
圖像描述評價指標
圖像描述的評價,可以參考機器翻譯的評價,都是比較兩個句子之間的相似度。
機器翻譯中常用的評價指標有,BLEU1-4, METEOR, ROUGE-L, and CIDEr等,這裡介紹最常見的BLEU1-4。
BLEU是IBM在2002年提出的,用於機器翻譯任務的評價,發表在ACL,引用次數10000+,原文題目是“BLEU: a Method for Automatic Evaluation of Machine Translation”。
它的總體思想就是準確率,假如給定標準譯文reference,模型生成的句子是candidate,句子長度為n,candidate中有m個單詞出現在reference,m/n就是bleu的1-gram的計算公式。
當統計不再是一個單詞,而是連續的N個單詞時,就有了n-gram的概念,片語的概念稱為n-gram,片語長度通常選擇1, 2, 3, 4
舉一個例子來看看實際的計算:
candinate: the cat sat on the mat
reference: the cat is on the mat
BLEU-1: 5/6 = 0.83
BLEU-2: 3/5 = 0.6
BLEU-3: 1/4 = 0.25
BLEU-4: 0/3 = 0
Copy
分子表示candidate中預測到了的片語的次數,如BLEU-1中,5分別表示, the, cat, on, the, mat預測中了。BLEU-2中,3分別表示, the cat, on the, the mat預測中了。以此類推。
針對BLEU還有些改進計算方法,可參考BLEU詳解
BLEU的優點在於它考慮的是n-gram級別的匹配,而不是詞級別的匹配,因此可以考慮更長的匹配資訊,從而更好地評估翻譯的品質。
但是,BLEU的缺點在於無論哪種n-gram被匹配上了,都會被同等對待,這可能會導致一些問題。例如,動詞的匹配在翻譯中可能比冠詞更重要,但是在BLEU中,它們被同等地看待,這可能不太合適。
CNN+RNN 代碼實現
接下來採用CNN+RNN結構,並配合attention機制,實現圖像描述模型訓練, 在coco2014上可實現23.1的BlEU-4 。
代碼來自github,論文可參考《Show, Attend and Tel》
資料採用github上推薦的下載連結,coco2014,資料集劃分採用 Andrej Karpathy劃分好的json檔。
先看效果圖,這是一張測試圖片,模型可以輸出 a brown teddy bear sitting on top of a pair of shoes,能對圖中的泰迪、鞋子進行表述。
資料模組
資料下載與轉換
首先下載資料,並轉換資料為pytorch的dataset讀取的形式
- 下載圖像資料,val2014, train2014 資料夾,並將其放置到xxx/coco2014/images下
- 下載標注數據,caption_datasets.zip,其中包含coco, flickr8k, flick30k的標籤,這裡只使用dataset_coco.json
- 在配套代碼00_create_input_files.py中設置以下路徑,運行後獲得相應的資料
create_input_files(dataset='coco',
karpathy_json_path=r'G:\deep_learning_data\coco_2014\image-caption-json\dataset_coco.json',
image_folder=r'G:\deep_learning_data\coco_2014\images',
captions_per_image=5,
min_word_freq=5,
output_folder=r'G:\deep_learning_data\coco_2014\dataset-created',
max_len=50)
Copy
獲得的資料是經過預處理轉換的,下面介紹對資料是如何處理的。
數據預處理
文本資料需要進行一系列的預處理,例如,將一張圖片對應的5句不等長度的描述,整理為可以batch輸入資料,這裡涉及一些NLP常見的操作,下面通過代碼進行剖析。
- 對描述的開頭和結尾,加入起始、停止詞, a man holds a football
- 將句子填充至等長,如100個詞, a man holds a football ....
- 創建詞映射,將詞映射為編號, 如 [9488, 1, 20, 64, 3, 60, 57, 69, 35, 66, 14, 67, 17, 1, 68, 9489, 0,.., 0],其中9488和9489,0分別表示
上述資訊,通過00_create_input_files.py獲得,資料處理後,分別獲得:
- HDF5檔,包含了所有圖片,資料形狀為 N, 3, 256, 256,但hdf5在pytorch的dataloader中無法啟用多進程,因此本案例改用保存圖片路徑的方式,在dataset中再讀取圖片
- CATIONS*.json,包含每個描述句子添加起始詞、填充、映射後的索引,為 N_c * I 形式, N_c表示所有描述句子的梳理,I表示圖像的數量。由於coco固定了一張圖片有5個描述,因此N_c == 5.
- CAPLENS*.json,包含每句描述的長度,N_c * I , N_c表示所有描述句子的梳理,I表示圖像的數量。
- WORDMAP*.json,所以一個字典,包含了單詞到索引的映射關係。
- xxx_paths.pkl:包含每張圖片的路徑,用於在dataset中進行圖片讀取
原代碼採用HDF5進行圖片讀取,這樣無法採用num_worker>1,因此在這裡我將代碼改為基於圖片路徑形式進行讀取,可以充分利用cpu載入資料。詳細內容參見dataset的編寫。
模型模組
模型部分主要有encoder, decoder, attention三個模組。
- encoder為resnet101將圖片變為14x14x2048的特徵圖,並且經linear層變換到向量形式,便於與文本特徵拼接
- attention模組由多個linear層構成,注意力權重最終經sigmoid函數得到0-1區間的注意力權重,並且是1x196的向量,對應著14x14的圖像區域。
- decoder為標準的LSTM,其輸入由詞嵌入向量1x512 + attention的特徵1x2048構成
- output模組採用LSTM的hiddent feature,經過linear層輸出1x9490的分類概率向量,9490表示單詞庫中總共有9490個單詞。
模型結構如下圖所示,本圖對github原圖進行了詳細補充,對每一個資料維度及流向進行了標記:
訓練與推理
在配套代碼01_train.py代碼中僅需要配置資料所在資料夾data_folder,執行 python 01_train.py即可。 在epoch=14左右會得到最優BLEU-4, 22.7。
關於超參數,num_worker可以設置大於1,batchsize設為了256,採用的是1080ti 11GB,顯存佔用8G+,耗時大約1.5小時一個epoch。
訓練代碼比較常規,只是文本任務在資料處理上有一個比較特殊的操作就是組batch時,先對文本長度進行排序,然後依次取batch送入LSTM。組batch的操作,具體如github所示:
推理觀察
訓練到14個epoch時可以將模型拿來試試了,將 BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth.tar的路徑配置到02_inference.py
- args.img目前支援圖片以及資料夾形式的推理
- args.model 是ckpt的路徑
- args.word_map是單詞庫,模型預測出來的9490個類別需要對應到具體的單詞,用的就是這個字典。
- out_dir是輸出圖片的資料夾
args.img = r'G:\deep_learning_data\coco_2014\images\val2014' #img path or dir
args.model = 'BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth.tar' # model checkpoint
args.word_map = r'G:\deep_learning_data\coco_2014\dataset-created\WORDMAP_coco_5_cap_per_img_5_min_word_freq.json'
out_dir = './output_img'
Copy
效果如下圖所示
訓練好的模型權重下載:連結:https://pan.baidu.com/s/1fLS0_EPqfj0x_PX3JLN1Eg 提取碼:ta3v
到這裡,快速地實現了一個效果還不錯的圖像描述模型,裡邊有一些新知識值得學習:
- 圖像可經過模型提取特徵,變為特徵向量與文本特徵向量融合,實現圖文多模態的處理
- LSTM訓練時,將句子長度排序,便可獲得batch size依次遞減的訓練樣本
- coco資料訓練時,一個樣本為 (一張圖片,一個句描述,句子長度),因此共5x113287=566435個訓練樣本
隨著Transformer不斷的落地應用,以及多模態模型langueage-visual模型的成功,基Transformer體系的圖像描述模型成為主流。
下面介紹一款”親民“的模型,CLIPCap,親民指它在1080上1天就可以訓練,並且仍舊使用了強大的Transformer模型,論文idea值得學習。
CLIPCap 代碼實現
接下來,借助強大的多模態模型的特徵提取能力實現圖像描述。
這裡採用CLIP對圖像的理解能力,獲取圖像編碼特徵embedding向量,再經過一個生成器模型,實現圖像描述。這個工作就是2021年11月發表的ClipCap。
ClipCap提出一種輕量化的方法,可以結合 CLIP的image encoder 和 GPT-2 ,實現圖像描述。
ClipCap有三個部分,分別是image encoder, mapping network, gpt2。其中image encoder和gpt2都是在超大規模資料集上預訓練過的,可以直接用。
在學習ClipCap前,先來瞭解什麼是CLIP,什麼是GPT2。
CLIP簡介
CLIP(Contrastive Language-Image Pre-training),基於對比學習的文圖預訓練模型,該模型可實現zero-shot的圖像分類、檢測等下游任務,也可以作為圖像檢索、圖像生成、圖像描述任務的backbone,是圖文多模態模型領域中劃時代意義的一個作品。
CLIP於2021年2月由openAI發表,並開源了模型,模型由4億的圖文資料,採用對比學習方式進行訓練得到,由於對比學習與超大規模的資料集加持,使CLIP模型很好的理解了自然圖像,在眾多資料集上表現出了優異的zero-shot性能,同時在表徵學習(representation learning)中也很好。
CLIP模型由text encoder和image encoder組成,分別對文本和圖像進行特徵提取,獲得特徵向量,隨後進行對比學習,即圖像1與文本1是一對資料,I1向量要與T1越接近越好,I1與其它的T向量越不接近越好,對於一個batch的資料來說,可以構成一個方陣,對角線上是正樣本,非對角線是負樣本。
訓練偽代碼如下:
# 分別提取圖像特徵和文本特徵
I_f = image_encoder(I) #[n, d_i]
T_f = text_encoder(T) #[n, d_t]
# 對兩個特徵進行線性投射,得到相同維度的特徵,並進行l2歸一化
I_e = l2_normalize(np.dot(I_f, W_i), axis=1)
T_e = l2_normalize(np.dot(T_f, W_t), axis=1)
# 計算縮放的余弦相似度:[n, n]
logits = np.dot(I_e, T_e.T) * np.exp(t)
# 對稱的對比學習損失:等價於N個類別的cross_entropy_loss
labels = np.arange(n) # 對角線元素的labels
loss_i = cross_entropy_loss(logits, labels, axis=0)
loss_t = cross_entropy_loss(logits, labels, axis=1)
loss = (loss_i + loss_t)/2
Copy
text encoder採用的是標準的text transformer。
image encoder則有多個模型,主要是ResNet系列,包含5個不同大小的模型:ResNet50,ResNet101,RN50x4,RN50x16和RNx64(後面三個模型是按照EfficientNet縮放規則對ResNet分別增大4x,16x和64x得到),ViT系列,3個不同大小的模型:ViT-B/32,ViT-B/16和ViT-L/14。所有的模型都訓練32個epochs,採用AdamW優化器,而且訓練過程採用了一個較大的batch size:32768。由於資料量較大,最大的ResNet模型RN50x64需要在592個V100卡上訓練18天,而最大ViT模型ViT-L/14需要在256張V100卡上訓練12天,都需要幾千個V100天。
模型訓練好之後,神奇之處在於其可以zero-shot的進行圖像分類,這個方式很具有創新性。具體步驟是
- 人為設定一批候選類別的文本描述,例如:A photo of {label}, 然後label分別填入候選類別的單詞,假設有N個類別,則得到N個句子
- 送入text encoder,得到N個文本特徵向量
- 圖像送入image encoder,得到圖像特徵向量
- 圖像特徵向量與N個文本特徵向量進行比較,找到最近的那個特徵向量,即可得到類別輸出
使用CLIP進行zero-shot分類,另外一個比較重要的地方是文本描述的生成,上面的例子我們採用A photo of {label},但其實也有其它選擇,比如我們直接用類別標籤,這其實屬於最近NLP領域比較火的一個研究:prompt learning或者prompt engineering,具體可以見這篇綜述論文:Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing,這裡就不再進行闡述。
感興趣可參考官方的ImageNet分類的Prompt engineering,採用80個不同的prompt來進行集成,發現在ImageNet資料集上能帶來3.5%的提升,具體見CLIP公開的notebook。
到這裡大體瞭解CLIP中有一個對自然圖像理解能力很強的image encoder,可以獲得很好的圖像特徵向量,接下來需要一個能接收embedding向量,輸出文本描述的強大模型,GPT當之無愧作為首選。
GPT2簡介
GPT2(Generative Pre-trained 2),是由OpenAI開發的生成式自然語言模型,鑒於chatGPT的火爆,這裡不過多介紹GPT1,2,3,3.5,4的差別。
在這裡需要瞭解gpt2是一個生成式模型,根據輸入的文本資訊,可以生成一系列文本,如輸入一個問題句子,gpt將句子變為text embedding,輸入到模型中,然後一個一個單詞的輸出,最終輸出一句回答。其中,人類輸入的問題句子,可以看成是prefix embedding,gpt根據首碼資訊,依次生成內容。
Prefix embeddings是指在GPT模型中,為每一個輸入詞添加一個首碼,然後將添加首碼後的詞轉化為向量表示。這個首碼是指輸入詞前面的所有詞,它可以為模型提供更多的上下文資訊,説明模型更好地理解輸入文本的含義。
舉個例子,假設輸入文本是“我喜歡吃蘋果”,對於“蘋果”這個詞,它的首碼是“我喜歡吃”,添加首碼後的詞就是“我喜歡吃蘋果”。這個添加首碼後的詞可以被轉化為向量表示,然後作為GPT模型的輸入。
在CLIPCap中,正式利用了gpt2強大的文本生成能力進行圖像描述,但圖像資訊如何輸入到gpt呢?接下來就看看CLIPCap的創新。
CLIP Captioning
2021年11月,ClipCap提出一種輕量化的方法,可以結合 CLIP的image encoder 和 GPT-2 ,實現圖像描述。
ClipCap有三個部分,分別是image encoder, mapping network, gpt2。其中image encoder和gpt2都是在超大規模資料集上預訓練過的,可以直接用。
由於CLIP和GPT2不好訓練,所以設計一個mapping network,圖像embedding特徵向文本embedding特徵的轉換,從而巧妙的銜接了CLIP與GPT2,並且可以僅訓練mapping nework,這一點與當前BLIP v2中的QFormer是一樣的。
結合上圖,來看看模型到底是如何實現圖像描述的。
第一步,一張圖片及其對應的描述文本,會被輸入到CLIP中,得到image embedding向量:512維,文本則通過gpt2的字典轉換為tokens,gpt2字典有50257個詞。
第二步:圖像特徵經過maping network,獲得40x768的特徵,可以理解為將圖像翻譯為了40個768的特徵向量
第三步:文本tokens經過word2emb獲得 text embedding向量40x768維度,這裡的40表示句子最長有40個單詞,如果補足40,填充即可。
第四步:圖像與文本特徵拼接,輸入到gpt2進行訓練,gpt2輸出80個50257維的分類概率向量,其中取後40個向量進行輸出分類的單詞,最終形成句子。
上述為訓練過程,其中CLIP和GPT2是可以凍結的,詳情可看代碼。
在推理的時候,GPT2 word2emb的這一分支是沒有的,gpt2僅拿到圖像的40x768向量進行推理,逐步生成每一個單詞,最終形成句子。
CLIP Captioning 訓練代碼實現
第一步,資料集下載及準備
首先準備資料集,這裡採用coco 2014,需要下載的檔有
- 預處理過的標籤文件:train_caption.json
- 原始影像檔夾:train2014,val2014
組織為以下目錄形式
- ROOT_PROJECT / data / annotations / train_caption.json
- ROOT_PROJECT / data / train2014
- ROOT_PROJECT / data / val2014
第二步,CLIP模型特徵提取,運行配套代碼00_parse_coco.py
python 00_parse_coco.py --clip_model_type ViT-B/32
Copy
由於不涉及CLIP的訓練,因此CLIP對每一張圖片的輸出是固定的,所以可以把訓練集中的566757個樣本對進行特徵提取及文本tokens的映射。
執行以下代碼,預計需要3小時完成56萬多張樣本對的處理。
結果將保存在
- ROOT_PROJECDT / data / oscar_split_ViT-B_32_train.pkl
- ROOT_PROJECDT / data / oscar_split_ViT-B_32_train_tokens.pkl
第三步,模型訓練,運行配套代碼01-train.py
python 01-train.py --data ./data/coco/oscar_split_ViT-B_32_train.pkl --out_dir ./coco_train/
Copy
到這裡就可以開始訓練了,在1080ti上訓練10個epoch,耗時16小時,模型將保存在 ROOT_PROJECDT / coco_train 資料夾下
提供一個預訓練權重,模型權重下載連結提取碼:mqri
CLIP Captioning 推理代碼實現
推理示例,運行配套代碼02-inference.py,需要配置下面三個路徑即可。
ckpt_path = r'coco_prefix-009-2023-0411.pt'
path_img = r'G:\deep_learning_data\coco_2014\images\val2014'
out_dir = './inference_output'
Copy
在推理代碼中,配置好模型路徑、測試圖片/資料夾路徑,輸出路徑,運行即可得到結果
推理僅需要0.2s即可獲得一例輸出,速度還是可以接受的。
下圖為val2014中的一些推理結果示意圖
到這裡Clip Cap就結束了,簡單回顧一下Clip Cap整體內容。
- 借力:Clip Cap 站在了兩個巨人的肩膀上,分別是CLIP和GPT2
- 磨合:為了讓兩個模組可以更好的融合使用,提出mapping network模組將CLIP的輸出轉換為GPT2能接收的特徵向量形式, 40x768的40個"單詞"的特徵向量形式。
- 親民:在1080ti上一天時間就可以訓練coco資料集,並且還能用上大模型,這個motivation不得不說很巧妙
如果想將Clip Cap運用于中文,也推薦大家閱讀ClipCap-Chinese
小結
Image Captioning 是一個CV+NLP的典型應用,是從一幅圖輸出一句話的過程,並且具有較高的商業價值,如字幕生成、圖像標注、圖像說明等。
本文介紹了圖像描述的概念,發展歷史,常用資料集,BLUE評價指標,並通過代碼實現兩種主流的圖像描述演算法。
CNN+RNN架構
- CNN負責提取特徵圖,並變為特徵向量1x512作為h0輸入到RNN中
- RNN逐步輸出單詞
Clip Cap
- 借助大模型——CLIP和GPT2,效果比CNN+RNN好很多,這也是基於Transformer的深度學習模型廣泛應用的原因之一
- 巧妙的將圖像特徵與NLP模型嫁接起來,後續更為強大的BLIP v2同樣採用了此操作
關於圖像描述、文圖、圖文等多模態任務,十分推薦大家學習以下內容
https://github.com/salesforce/BLIP
https://github.com/salesforce/LAVIS