マルチリンガル/マルチモーダルなEmbeddingモデル「Visualized BGE」を試す
本プロジェクトでは、普遍的なマルチモーダル埋め込みモデルであるVisualized-BGEを紹介する。BGEテキスト埋め込みフレームワークに画像トークン埋め込みを組み込むことで、Visualized-BGEはテキストだけでないマルチモーダルデータを処理する柔軟性を獲得する。Visualized-BGEは、主に以下のようなハイブリッド・モーダル検索タスクに使用される:
- マルチモーダル知識検索(クエリ:テキスト、候補:画像とテキストのペア、テキスト、画像) 例:WebQA
- 合成画像検索(クエリ:画像-テキストペア、候補:画像) 例:CIRR、FashionIQ
- マルチモーダルなクエリによる知識検索(クエリ:画像とテキストのペア、候補:テキスト) 例:ReMuQ
さらに、ビジュアライズドBGEは、オリジナルBGEモデルの強力なテキスト埋め込み機能を完全に保持している : )
BAAI/bgeをベースにマルチモーダルに対応したEmbeddingモデルという感じかな。テキストモデルと同様に、英語用とマルチリンガル用がそれぞれ用意されている。
Model Name Dimension Text Embedding Model Language Weight BAAI/bge-visualized-base-en-v1.5 768 BAAI/bge-base-en-v1.5 English 🤗 HF link BAAI/bge-visualized-m3 1024 BAAI/bge-m3 Multilingual 🤗 HF link
READMEを参考にやってみる。Colaboratoryで。CPUでも一応動くので、一旦それで。
レポジトリをクローンしてパッケージをインストール
!git clone https://github.com/FlagOpen/FlagEmbedding.git
%cd FlagEmbedding
!pip install -e .
!pip install torchvision timm einops ftfy
あと、以下も必要だった。
!pip install peft
マルチリンガル用の重みをダウンロード。
!wget --content-disposition https://huggingface.co/BAAI/bge-visualized/resolve/main/Visualized_m3.pth?download=true
モデルをロード。
import torch
from FlagEmbedding.visual.modeling import Visualized_BGE
model = Visualized_BGE(model_name_bge = "BAAI/bge-m3", model_weight="./Visualized_m3.pth")
model.eval()
クローンしたディレクトリ(``FlagEmbedding/FlagEmbedding/visual/imgs`)にサンプルの画像が用意されているのでこれを使って比較を行う。
クエリに使うFlagEmbedding/FlagEmbedding/visual/imgs/cir_query.png
クエリと比較する1つ目の画像。FlagEmbedding/FlagEmbedding/visual/imgs/cir_candi1.png
クエリと比較する2つ目の画像。FlagEmbedding/FlagEmbedding/visual/imgs/cir_candi2.png
クエリの画像とそれぞれの比較用画像の違いとしては、
- クエリ画像と比較画像1の違いは、背景が昼か夜か。
- クエリ画像と比較画像2の違いは、馬がいるかいないか。
になる。
まず画像だけで比較してみる。
with torch.no_grad():
query_emb = model.encode(image="./FlagEmbedding/visual/imgs/cir_query.png")
candi_emb_1 = model.encode(image="./FlagEmbedding/visual/imgs/cir_candi_1.png")
candi_emb_2 = model.encode(image="./FlagEmbedding/visual/imgs/cir_candi_2.png")
sim_1 = query_emb @ candi_emb_1.T
sim_2 = query_emb @ candi_emb_2.T
print(sim_1, sim_2)
tensor([[0.8765]]) tensor([[0.8657]])
比較画像1と近いという結果。馬がいる、ということが違いとなったように思える。
クエリ画像にテキストを追加して比較。
with torch.no_grad():
query_emb = model.encode(image="./FlagEmbedding/visual/imgs/cir_query.png", text="馬が馬車を引いている。")
candi_emb_1 = model.encode(image="./FlagEmbedding/visual/imgs/cir_candi_1.png")
candi_emb_2 = model.encode(image="./FlagEmbedding/visual/imgs/cir_candi_2.png")
sim_1 = query_emb @ candi_emb_1.T
sim_2 = query_emb @ candi_emb_2.T
print(sim_1, sim_2)
tensor([[0.6140]]) tensor([[0.7115]])
比較画像2と近いという結果。テキストで「馬」の存在を追加したのが結果に影響したように思える。
別のテキストに変えてみる。
with torch.no_grad():
query_emb = model.encode(image="./FlagEmbedding/visual/imgs/cir_query.png", text="背景を暗くして、まるで夜に撮影したかのようにする。")
candi_emb_1 = model.encode(image="./FlagEmbedding/visual/imgs/cir_candi_1.png")
candi_emb_2 = model.encode(image="./FlagEmbedding/visual/imgs/cir_candi_2.png")
sim_1 = query_emb @ candi_emb_1.T
sim_2 = query_emb @ candi_emb_2.T
print(sim_1, sim_2)
tensor([[0.7058]]) tensor([[0.6503]])
比較画像1と近いという結果。やはりテキストで「夜」を記載したのが結果に反映されているように思える。
では別の例。こちらではクエリはテキストのみとなるので、比較用の画像が2つ。
比較用画像1。ニューヨークにあるミッドハドソンブリッジの写真。FlagEmbedding/FlagEmbedding/visual/imgs/wiki_candi_1.jpg
比較用画像2。サンフランシスコにあるゴールデンゲートブリッジの写真。FlagEmbedding/FlagEmbedding/visual/imgs/wiki_candi_2.jpg
では比較。クエリはテキストのみで、比較対象は画像とテキストの組み合わせ。
with torch.no_grad():
query_emb = model.encode(text="ミッドハドソンブリッジの両側に歩道はあるか?")
candi_emb_1 = model.encode(text="ポキプシーとハイランドの間のハドソン川に架かるミッドハドソンブリッジ。", image="./FlagEmbedding/visual/imgs/wiki_candi_1.jpg")
candi_emb_2 = model.encode(text="ゴールデンゲートブリッジ", image="./FlagEmbedding/visual/imgs/wiki_candi_2.jpg")
candi_emb_3 = model.encode(text="ミッドハドソンブリッジは1983年、米国土木学会によってニューヨーク州の歴史的土木ランドマークに指定された。この橋は1994年に「フランクリン・デラノ・ルーズベルト・ミッドハドソンブリッジ」と改名された。")
sim_1 = query_emb @ candi_emb_1.T
sim_2 = query_emb @ candi_emb_2.T
sim_3 = query_emb @ candi_emb_3.T
print(sim_1, sim_2, sim_3)
tensor([[0.6168]]) tensor([[0.3869]]) tensor([[0.5400]])
クエリと比較候補についてまとめると、
- クエリ
- テキスト: ミッドハドソンブリッジの構造に関する質問
- テキスト+画像
- テキスト: ミッドハドソンブリッジの所在についての記述
- 画像: ミッドハドソンブリッジの写真
- テキスト+画像
- テキスト: ゴールデンゲートブリッジという名称のみ
- 画像: ゴールデンゲートブリッジの写真
- テキストのみ
- テキスト: ミッドハドソンブリッジの成り立ちについての記述)
クエリからすると1が最も関連性が高い、3はクエリとは一部関連する、2は別、となるので、一応期待した順番でスコアリングされているように思える。
いろいろ試してみる。
with torch.no_grad():
query_emb = model.encode(text="ゴールデンゲートブリッジの色は?")
candi_emb_1 = model.encode(image="./FlagEmbedding/visual/imgs/wiki_candi_1.jpg")
candi_emb_2 = model.encode(image="./FlagEmbedding/visual/imgs/wiki_candi_2.jpg")
sim_1 = query_emb @ candi_emb_1.T
sim_2 = query_emb @ candi_emb_2.T
print(sim_1, sim_2)
tensor([[0.3815]]) tensor([[0.4594]])
with torch.no_grad():
query_emb = model.encode(text="ミッドハドソンブリッジの色は?")
candi_emb_1 = model.encode(image="./FlagEmbedding/visual/imgs/wiki_candi_1.jpg")
candi_emb_2 = model.encode(image="./FlagEmbedding/visual/imgs/wiki_candi_2.jpg")
sim_1 = query_emb @ candi_emb_1.T
sim_2 = query_emb @ candi_emb_2.T
print(sim_1, sim_2)
tensor([[0.3480]]) tensor([[0.3271]])
ゴールデンゲートブリッジとミッドハドソンブリッジについては多少なりとも区別している感がある。
ただし、
with torch.no_grad():
query_emb = model.encode(text="赤い橋")
candi_emb_1 = model.encode(image="./FlagEmbedding/visual/imgs/wiki_candi_1.jpg")
candi_emb_2 = model.encode(image="./FlagEmbedding/visual/imgs/wiki_candi_2.jpg")
sim_1 = query_emb @ candi_emb_1.T
sim_2 = query_emb @ candi_emb_2.T
print(sim_1, sim_2)
tensor([[0.3279]]) tensor([[0.3260]])
with torch.no_grad():
query_emb = model.encode(text="白い橋")
candi_emb_1 = model.encode(image="./FlagEmbedding/visual/imgs/wiki_candi_1.jpg")
candi_emb_2 = model.encode(image="./FlagEmbedding/visual/imgs/wiki_candi_2.jpg")
sim_1 = query_emb @ candi_emb_1.T
sim_2 = query_emb @ candi_emb_2.T
print(sim_1, sim_2)
tensor([[0.3640]]) tensor([[0.3610]])
とかみたいな感じになる場合もあるので、精度についてはベンチマーク評価結果や自分のデータセットで試してみる必要はある。
FAQに以下とある。
Q1: Visualized BGEをクロス・モーダル検索(テキストから画像へ)に使用することは可能か?
A1: 技術的には可能だが、推奨されるユースケースではない。我々のモデルはハイブリッド・モーダル検索タスクを視覚機能で補強することに焦点を当てている。
READMEで紹介されている例と照らし合わせて考えると
- テキストのみ to 画像のみ
- 画像のみ to テキストのみ
というパターンはユースケースには当てはまらないということかもしれない。自分が試した組み合わせには一部これが含まれている。