第4編 — MetaCLIP 2 の日本語と多言語プロンプトに対する応答性能の評価
はじめに
こんにちは、しゅんです。
第3編の記事はこちらです。
多言語の呪い(the curse of multilinguality)は第三編で記載したので省略します。
第3編の記事はCLIPにおける多言語の呪いの影響をゼロショットで検証し、特に日本語と英語での反応の違いを観察しました。その結果、CLIPでは多言語データを一緒に学習する際に、異なる言語間でのパフォーマンス低下が確認されました。具体的には、同じ概念を異なる言語でプロンプトした場合、CLIPの応答において確信度やパフォーマンスが低下する傾向が見られました。
それを踏まえ、今回はMetaCLIP 2における多言語の呪いがどのように現れるのかを検証します。MetaCLIP 2は、CLIPのこの多言語の呪いを解決できたのが特徴と言われてる。この性能が実際にどのように発揮されるのか、特に日本語と他言語での反応において、多言語の呪いが影響を与えるかどうかをゼロショット評価を通じて調査します。
Code
from pathlib import Path
import matplotlib.pyplot as plt
import requests
import torch
from PIL import Image
from transformers import AutoModel, AutoProcessor
import japanize_matplotlib # 日本語ラベル用
# MetaCLIP2 を日本語/多言語プロンプトで試す簡単な実験
model_name = "facebook/metaclip-2-worldwide-giant-378"
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
preferred_dtype = torch.bfloat16 if device.type == "cuda" else torch.float32
model = AutoModel.from_pretrained(
model_name,
dtype=preferred_dtype,
attn_implementation="sdpa",
).to(device)
processor = AutoProcessor.from_pretrained(model_name, use_fast=True)
url = "http://images.cocodataset.org/val2017/000000039769.jpg" # same image for comparability
image = Image.open(requests.get(url, stream=True).raw).convert("RGB")
results: list[dict] = []
def run_eval(title: str, labels: list[str]) -> None:
"""Run zero-shot with given labels, print, and log summary."""
inputs = processor(text=labels, images=image, return_tensors="pt", padding=True)
inputs = {k: v.to(device) if isinstance(v, torch.Tensor) else v for k, v in inputs.items()}
with torch.inference_mode():
outputs = model(**inputs)
probs = outputs.logits_per_image.softmax(dim=1)[0]
probs_cpu = probs.detach().cpu()
best_idx = probs_cpu.argmax().item()
if probs_cpu.numel() > 1:
sorted_probs, _ = torch.sort(probs_cpu, descending=True)
top2_gap = (sorted_probs[0] - sorted_probs[1]).item()
else:
top2_gap = float("nan")
entropy = -(probs_cpu * probs_cpu.log()).sum().item()
print(f"\n[{title}] (num_labels = {len(labels)})")
print(f"Top: {labels[best_idx]} ({probs_cpu[best_idx]:.3f}) / top2 gap: {top2_gap:.3f}, entropy: {entropy:.3f}")
for label, p in sorted(zip(labels, probs_cpu.tolist()), key=lambda x: x[1], reverse=True):
print(f"- {label}: {p:.3f}")
results.append(
{
"title": title,
"num_labels": len(labels),
"max_prob": probs_cpu[best_idx].item(),
"top2_gap": top2_gap,
"entropy": entropy,
}
)
prompt_sets = [
("日本語シンプル", ["猫の写真", "犬の写真", "車の写真"]),
("日本語ながめ", ["公園のベンチで寝そべる茶色い猫の写真", "芝生で遊ぶ黒い犬の写真", "道路を走る赤い車の写真"]),
("日本語語順崩し", ["写真 猫", "写真 犬", "写真 車"]),
("英語シンプル", ["a photo of a cat", "a photo of a dog", "a photo of a car"]),
("英日併記", ["猫の写真 (a photo of a cat)", "犬の写真 (a photo of a dog)", "車の写真 (a photo of a car)"]),
# 以下は「言語の呪い」を感じやすくするための追加パターン
("レア/紛らわしい動物", ["トラの写真", "ヒョウの写真", "オセロットの写真", "キツネの写真", "猫の写真"]),
("カタカナ表記ゆれ", ["ネコ ノ シャシン", "イヌ ノ シャシン", "クルマ ノ シャシン"]),
("ひらがなだけ", ["ねこのしゃしん", "いぬのしゃしん", "くるまのしゃしん"]),
("助詞多め/冗長", ["ベンチの横で丸まっている猫が写った夜の写真です", "芝生の上でボールをくわえている犬を写した写真です", "都市の道路を走る赤い車を写した写真です"]),
("英語だけ長文", [
"a long exposure night photo of a brown cat curled up on a park bench",
"a photo of a black dog playing on the grass with a ball",
"a photo of a red car driving on a city street",
]),
# 多言語混在(同じ概念を多言語で羅列して確信度の分散を観察)
("多言語4カテゴリx3言語", [
"猫の写真", "a photo of a cat", "photo d'un chat", # cat
"犬の写真", "a photo of a dog", "photo d'un chien", # dog
"車の写真", "a photo of a car", "photo d'une voiture", # car
"キツネの写真", "a photo of a fox", "photo d'un renard", # fox
]),
# 多言語かつ紛らわしい動物(ネコ科/イヌ科を多言語で詰め込み)
("多言語ネコ科イヌ科", [
"猫の写真", "a photo of a cat", "photo d'un chat",
"トラの写真", "a photo of a tiger", "photo d'un tigre",
"ヒョウの写真", "a photo of a leopard", "photo d'un léopard",
"キツネの写真", "a photo of a fox", "photo d'un renard",
"犬の写真", "a photo of a dog", "photo d'un chien",
"オオカミの写真", "a photo of a wolf", "photo d'un loup",
]),
# 5言語(英/日/仏/西/独)で4カテゴリを重複羅列
("多言語5言語x4カテゴリ", [
# cat
"猫の写真", "a photo of a cat", "photo d'un chat", "una foto de un gato", "ein Foto einer Katze",
# dog
"犬の写真", "a photo of a dog", "photo d'un chien", "una foto de un perro", "ein Foto eines Hundes",
# car
"車の写真", "a photo of a car", "photo d'une voiture", "una foto de un coche", "ein Foto eines Autos",
# fox
"キツネの写真", "a photo of a fox", "photo d'un renard", "una foto de un zorro", "ein Foto eines Fuchses",
]),
# スクリプト混在(英/日/仏/西/独/中/韓)
("多言語スクリプト混在", [
# cat
"猫の写真", "a photo of a cat", "photo d'un chat", "una foto de un gato", "ein Foto einer Katze", "一只猫的照片", "고양이 사진",
# dog
"犬の写真", "a photo of a dog", "photo d'un chien", "una foto de un perro", "ein Foto eines Hundes", "一只狗的照片", "개 사진",
# car
"車の写真", "a photo of a car", "photo d'une voiture", "una foto de un coche", "ein Foto eines Autos", "一辆车的照片", "자동차 사진",
# wolf (紛らわしさ追加)
"オオカミの写真", "a photo of a wolf", "photo d'un loup", "una foto de un lobo", "ein Foto eines Wolfes", "一只狼的照片", "늑대 사진",
]),
]
for title, labels in prompt_sets:
run_eval(title, labels)
# Plot summary metrics and save to files
titles = [r["title"] for r in results]
x = range(len(results))
max_probs = [r["max_prob"] for r in results]
entropies = [r["entropy"] for r in results]
num_labels = [r["num_labels"] for r in results]
out_dir = Path(__file__).parent
# 1. Top-1 probability
fig1 = plt.figure(figsize=(10, 4))
plt.bar(x, max_probs, color="skyblue")
plt.xticks(x, titles, rotation=90, ha="center")
plt.ylabel("Top-1 probability")
plt.title("Effect of multilingual prompts on MetaCLIP confidence")
plt.tight_layout()
top1_path = out_dir / "main_metaclip_ja_top1.png"
plt.savefig(top1_path, bbox_inches="tight")
plt.close(fig1)
print(f"Top-1 plot saved to: {top1_path.resolve()}")
# 2. Entropy
fig2 = plt.figure(figsize=(10, 4))
plt.bar(x, entropies, color="lightcoral")
plt.xticks(x, titles, rotation=90, ha="center")
plt.ylabel("Entropy of label distribution")
plt.title("Entropy vs. prompt set (more languages / more labels?)")
plt.tight_layout()
entropy_path = out_dir / "main_metaclip_ja_entropy.png"
plt.savefig(entropy_path, bbox_inches="tight")
plt.close(fig2)
print(f"Entropy plot saved to: {entropy_path.resolve()}")
# 3. Scatter: labels count vs top-1 prob
fig3 = plt.figure(figsize=(10, 4))
scatter = plt.scatter(num_labels, max_probs, c=num_labels, cmap="viridis")
plt.colorbar(scatter, label="Number of labels")
plt.xlabel("Number of labels in prompt set")
plt.ylabel("Top-1 probability")
plt.title("Top-1 probability vs. number of labels")
plt.tight_layout()
scatter_path = out_dir / "main_metaclip_ja_scatter.png"
plt.savefig(scatter_path, bbox_inches="tight")
plt.close(fig3)
print(f"Scatter plot saved to: {scatter_path.resolve()}")
実行方法
以下のコマンドを使ってベンチマークを実行します。パスは適切な場所に置き換えてください。
cd path/to/meta/metaclip
python main_metaclip_ja.py
実行結果
[日本語シンプル] (num_labels = 3)
Top: 猫の写真 (0.995) / top2 gap: 0.989, entropy: 0.034
- 猫の写真: 0.995
- 犬の写真: 0.005
- 車の写真: 0.000
[日本語ながめ] (num_labels = 3)
Top: 公園のベンチで寝そべる茶色い猫の写真 (0.999) / top2 gap: 0.998, entropy: 0.008
- 公園のベンチで寝そべる茶色い猫の写真: 0.999
- 芝生で遊ぶ黒い犬の写真: 0.001
- 道路を走る赤い車の写真: 0.000
[日本語語順崩し] (num_labels = 3)
Top: 写真 猫 (0.991) / top2 gap: 0.983, entropy: 0.049
- 写真 猫: 0.991
- 写真 犬: 0.008
- 写真 車: 0.000
[英語シンプル] (num_labels = 3)
Top: a photo of a cat (0.996) / top2 gap: 0.992, entropy: 0.028
- a photo of a cat: 0.996
- a photo of a dog: 0.004
- a photo of a car: 0.000
[英日併記] (num_labels = 3)
Top: 猫の写真 (a photo of a cat) (0.995) / top2 gap: 0.991, entropy: 0.031
- 猫の写真 (a photo of a cat): 0.995
- 犬の写真 (a photo of a dog): 0.005
- 車の写真 (a photo of a car): 0.000
[レア/紛らわしい動物] (num_labels = 5)
Top: 猫の写真 (0.610) / top2 gap: 0.296, entropy: 0.909
- 猫の写真: 0.610
- オセロットの写真: 0.314
- トラの写真: 0.059
- ヒョウの写真: 0.016
- キツネの写真: 0.002
[カタカナ表記ゆれ] (num_labels = 3)
Top: ネコ ノ シャシン (0.984) / top2 gap: 0.976, entropy: 0.093
- ネコ ノ シャシン: 0.984
- イヌ ノ シャシン: 0.008
- クルマ ノ シャシン: 0.008
[ひらがなだけ] (num_labels = 3)
Top: ねこのしゃしん (0.937) / top2 gap: 0.889, entropy: 0.267
- ねこのしゃしん: 0.937
- いぬのしゃしん: 0.049
- くるまのしゃしん: 0.014
[助詞多め/冗長] (num_labels = 3)
Top: ベンチの横で丸まっている猫が写った夜の写真です (0.999) / top2 gap: 0.999, entropy: 0.005
- ベンチの横で丸まっている猫が写った夜の写真です: 0.999
- 芝生の上でボールをくわえている犬を写した写真です: 0.000
- 都市の道路を走る赤い車を写した写真です: 0.000
[英語だけ長文] (num_labels = 3)
Top: a long exposure night photo of a brown cat curled up on a park bench (1.000) / top2 gap: 1.000, entropy: 0.001
- a long exposure night photo of a brown cat curled up on a park bench: 1.000
- a photo of a black dog playing on the grass with a ball: 0.000
- a photo of a red car driving on a city street: 0.000
[多言語4カテゴリx3言語] (num_labels = 12)
Top: photo d'un chat (0.877) / top2 gap: 0.794, entropy: 0.481
- photo d'un chat: 0.877
- a photo of a cat: 0.083
- 猫の写真: 0.030
- photo d'un chien: 0.007
- photo d'un renard: 0.001
- a photo of a dog: 0.000
- a photo of a fox: 0.000
- photo d'une voiture: 0.000
- 犬の写真: 0.000
- キツネの写真: 0.000
- a photo of a car: 0.000
- 車の写真: 0.000
[多言語ネコ科イヌ科] (num_labels = 18)
Top: photo d'un chat (0.845) / top2 gap: 0.765, entropy: 0.664
- photo d'un chat: 0.845
- a photo of a cat: 0.080
- 猫の写真: 0.029
- photo d'un tigre: 0.025
- photo d'un chien: 0.007
- photo d'un léopard: 0.007
- トラの写真: 0.003
- photo d'un loup: 0.002
- photo d'un renard: 0.001
- ヒョウの写真: 0.001
- a photo of a tiger: 0.001
- a photo of a dog: 0.000
- a photo of a fox: 0.000
- オオカミの写真: 0.000
- a photo of a leopard: 0.000
- 犬の写真: 0.000
- キツネの写真: 0.000
- a photo of a wolf: 0.000
[多言語5言語x4カテゴリ] (num_labels = 20)
Top: photo d'un chat (0.776) / top2 gap: 0.682, entropy: 0.844
- photo d'un chat: 0.776
- una foto de un gato: 0.094
- a photo of a cat: 0.073
- 猫の写真: 0.026
- ein Foto einer Katze: 0.021
- photo d'un chien: 0.006
- photo d'un renard: 0.001
- una foto de un perro: 0.000
- a photo of a dog: 0.000
- a photo of a fox: 0.000
- photo d'une voiture: 0.000
- 犬の写真: 0.000
- una foto de un zorro: 0.000
- ein Foto eines Hundes: 0.000
- キツネの写真: 0.000
- ein Foto eines Fuchses: 0.000
- una foto de un coche: 0.000
- a photo of a car: 0.000
- ein Foto eines Autos: 0.000
- 車の写真: 0.000
[多言語スクリプト混在] (num_labels = 28)
Top: photo d'un chat (0.738) / top2 gap: 0.649, entropy: 1.008
- photo d'un chat: 0.738
- una foto de un gato: 0.089
- a photo of a cat: 0.070
- 고양이 사진: 0.046
- 猫の写真: 0.025
- ein Foto einer Katze: 0.020
- photo d'un chien: 0.006
- 一只猫的照片: 0.002
- photo d'un loup: 0.002
- 개 사진: 0.000
- una foto de un perro: 0.000
- a photo of a dog: 0.000
- オオカミの写真: 0.000
- photo d'une voiture: 0.000
- 犬の写真: 0.000
- ein Foto eines Hundes: 0.000
- 늑대 사진: 0.000
- una foto de un lobo: 0.000
- 一只狗的照片: 0.000
- a photo of a wolf: 0.000
- ein Foto eines Wolfes: 0.000
- una foto de un coche: 0.000
- a photo of a car: 0.000
- 一只狼的照片: 0.000
- 一辆车的照片: 0.000
- ein Foto eines Autos: 0.000
- 車の写真: 0.000
- 자동차 사진: 0.000
グラフ
第三編と同じくやり方で実行して、結果に基づいて作成した以下のグラフを使って、多言語の呪いがMetaCLIP 2 モデルに与える影響を可視化しました。グラフの説明も第三編に参照してください。
1. Top-1確率のグラフ
2. エントロピーのグラフ
3. Top-1確率 vs. ラベル数のグラフ
考察
今回の実験結果をもとに、MetaCLIP 2の多言語プロンプトに対する応答性能を評価しました。以下のポイントに注目して考察します。
日本語シンプルと日本語ながめの違い
まず、日本語シンプルと日本語ながめでの結果を見てみると、いずれもトップ1の予測確率が非常に高い(0.995以上)という結果が得られました。特に日本語ながめの「公園のベンチで寝そべる茶色い猫の写真」は、確信度が0.999と非常に高く、精度の高さが確認できます。これにより、MetaCLIP 2が日本語に対しても高い性能を発揮し、細かな表現にも適切に対応できていることが示されています。
日本語語順崩し
日本語語順崩しでは、入力プロンプトの語順が崩れているにもかかわらず、トップ1の予測確率は0.991であり、依然として高い精度を維持しています。確かに語順が崩れるとモデルのパフォーマンスに影響を与える可能性はありますが、MetaCLIP 2はその影響を最小限に抑えていると考えられます。
多言語プロンプト
多言語プロンプトについても注目するべき結果が得られました。多言語4カテゴリx3言語(日本語、英語、フランス語)での評価では、最も高い確信度を持った予測がフランス語で行われました(photo d'un chat: 0.877)。日本語や英語よりもフランス語が優れたパフォーマンスを示した点が興味深いです。この結果は、MetaCLIP 2が複数の言語を統合し、言語間の相互補完により、より良い予測をする能力を示唆しています。
特に、多言語ネコ科イヌ科のような多言語かつ複雑なカテゴリーでの入力においても、フランス語が有利であり、MetaCLIP 2が異なる言語間で高いパフォーマンスを維持していることが分かります。具体的には、最初にフランス語の「photo d'un chat」が0.845という高い確信度を持ち、次に英語や日本語が続く結果となっています。これにより、異なる言語間でも情報を補完し合うことができる点がMetaCLIP 2の強みであることが証明されました。
エントロピーと確信度の関係
各プロンプトセットのエントロピー(確信度の分散)を見てみると、レア/紛らわしい動物のように多くのカテゴリがあるプロンプトでは、エントロピーが高く、確信度が分散しています。例えば、トラやオセロットなどが混在する場合、トップ1の予測確率は猫の写真が最も高いものの、確信度の差(top2 gap)が0.296と小さく、モデルが混乱していることが分かります。
これに対して、日本語シンプルや日本語ながめのようなシンプルなプロンプトでは、エントロピーが低く、トップ1の確信度が高い結果が得られています。これにより、シンプルなプロンプトの方がMetaCLIP 2の性能を最大限に引き出せることが確認できました。
多言語スクリプト混在
多言語スクリプト混在では、異なる言語のスクリプトが同時に混在した入力においても、MetaCLIP 2がしっかりと予測を行っています。確信度が高い「photo d'un chat」(フランス語)を予測し、次に英語や韓国語、さらには日本語が続くという結果となりました。これは、MetaCLIP 2が複数のスクリプトを適切に処理できる能力を持っていることを示しており、他のモデルと比較して非常に強力な性能を発揮していることが分かります。
結論
MetaCLIP 2は、複数の言語やスクリプトを効果的に扱う能力を持つモデルであり、今回の実験結果において、その性能が確認されました。特に日本語や英語、さらにはフランス語など、多言語に対して高いパフォーマンスを示しました。また、プロンプトの形式(シンプルなものから冗長なもの、異なる言語間での比較)にかかわらず、高い確信度を維持しており、言語間での補完が行われることが分かります。
実験では、多言語4カテゴリx3言語や多言語スクリプト混在など、異なる言語やスクリプトが混在する複雑なシナリオでも安定した応答が得られました。これにより、MetaCLIP 2は、多言語環境において優れた柔軟性と適応力を持つことが証明されました。
今後は、さらに複雑なプロンプトや多言語環境での応答性能を検証し、特に言語間の相互作用や補完メカニズムについての理解を深めることが求められます。
今回も最後まで見てくれてありがとうございます。


