第3編 — CLIPにおける多言語の呪いとその影響の検証
はじめに
こんにちは、しゅんです。
第2編では、MetaCLIP 2 の実力をより深く理解するために、COCO val2017 データセットを用いてゼロショット評価を行いました。さらに、評価結果を数値とグラフで視覚化し、MetaCLIP 2 の性能が他のCLIP系モデルとどのように異なるかを比較しました。今回は多言語の呪いを検証する前に、まずその背景と、CLIPではどういう風に多言語の呪いが現れるかを紹介します。
第2編の記事はこちらです。
そもそも多言語の呪い(the curse of multilinguality)とは
多言語の呪いとは、異なる言語を使用することでモデルのパフォーマンスが低下する現象を指します。特に、異なる言語で訓練したモデルが、新しい言語に対して最適に機能しないことがあります。これは、各言語の文法や構造の違い、文化的な背景、そして単語の意味の曖昧さが原因です。
多言語モデルにおいて、使用する言語が増えると、以下のような問題が生じやすくなります:
- 異なる言語間で表現方法が異なり、意味を正確に捉えることが難しくなる
- 訓練データに含まれる言語間でのバランスの不均衡が精度に影響を与える
- 複数言語を同時に学習させることによる知識の分散
by GPT and 参考資料
本記事では、CLIP における多言語の呪いを、実際にゼロショットで実行し、どのような影響があるのかを検証していきます。
Code
import requests
import torch
from PIL import Image
from transformers import AutoModel, AutoProcessor
import matplotlib.pyplot as plt
import japanize_matplotlib
# =========================
# モデル・画像の準備
# =========================
model_name = "openai/clip-vit-large-patch14"
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,
torch_dtype=preferred_dtype, # ← dtype → torch_dtype に修正
attn_implementation="sdpa",
).to(device)
model.eval()
processor = AutoProcessor.from_pretrained(model_name, use_fast=True)
# 例の COCO 画像(MetaCLIP の例と同じ)
url = "http://images.cocodataset.org/val2017/000000039769.jpg"
image = Image.open(requests.get(url, stream=True).raw).convert("RGB")
# =========================
# 評価関数 + ログ用リスト
# =========================
results = [] # グラフ用に各プロンプトセットの指標を溜める
def run_eval(title: str, labels: list[str]) -> None:
"""Run zero-shot with given labels and print probabilities + log summary."""
# Processor に image + text を入れる
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)
# 画像→テキストの類似度スコア(logits)
probs = outputs.logits_per_image.softmax(dim=1)[0] # (num_labels,)
probs_cpu = probs.detach().cpu()
# Top-1
best_idx = probs_cpu.argmax().item()
max_prob = probs_cpu[best_idx].item()
# 2位との差 (margin)
if probs_cpu.numel() > 1:
sorted_probs, sorted_idx = torch.sort(probs_cpu, descending=True)
top2_gap = (sorted_probs[0] - sorted_probs[1]).item()
else:
top2_gap = float("nan")
# エントロピー(分布がどれだけバラけているか)
# H(p) = - Σ p log(p)
entropy = -(probs_cpu * probs_cpu.log()).sum().item()
# コンソール出力
print(f"\n[{title}] (num_labels = {len(labels)})")
print(f"Top: {labels[best_idx]} ({max_prob:.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": max_prob,
"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言語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)
# =========================
# グラフ描画
# =========================
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]
# 1. Top-1 確率
plt.figure(figsize=(10, 4))
plt.bar(x, max_probs, color='skyblue')
plt.xticks(x, titles, rotation=90, ha="center") # ラベルを90度に変更
plt.ylabel("Top-1 probability")
plt.title("Effect of multilingual prompts on CLIP confidence")
plt.tight_layout()
plt.show()
# 2. エントロピー(大きいほど分布が広がっている)
plt.figure(figsize=(10, 4))
plt.bar(x, entropies, color='lightcoral')
plt.xticks(x, titles, rotation=90, ha="center") # ラベルを90度に変更
plt.ylabel("Entropy of label distribution")
plt.title("Entropy vs. prompt set (more languages / more labels?)")
plt.tight_layout()
plt.show()
# 3. おまけ:ラベル数との関係(色で区別)
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()
plt.show()
実行方法
以下のコマンドを使ってベンチマークを実行します。パスは適切な場所に置き換えてください。
cd path/to/meta/metaclip
python main_clip_ja.py
実行結果
[日本語シンプル] (num_labels = 3)
Top: 猫の写真 (0.993) / top2 gap: 0.990, entropy: 0.045
- 猫の写真: 0.993
- 犬の写真: 0.003
- 車の写真: 0.003
[日本語ながめ] (num_labels = 3)
Top: 公園のベンチで寝そべる茶色い猫の写真 (0.997) / top2 gap: 0.995, entropy: 0.019
- 公園のベンチで寝そべる茶色い猫の写真: 0.997
- 道路を走る赤い車の写真: 0.003
- 芝生で遊ぶ黒い犬の写真: 0.000
[日本語語順崩し] (num_labels = 3)
Top: 写真 猫 (0.995) / top2 gap: 0.991, entropy: 0.037
- 写真 猫: 0.995
- 写真 犬: 0.003
- 写真 車: 0.002
[英語シンプル] (num_labels = 3)
Top: a photo of a cat (0.999) / top2 gap: 0.998, entropy: 0.009
- a photo of a cat: 0.999
- a photo of a dog: 0.001
- a photo of a car: 0.000
[英日併記] (num_labels = 3)
Top: 猫の写真 (a photo of a cat) (0.998) / top2 gap: 0.997, entropy: 0.014
- 猫の写真 (a photo of a cat): 0.998
- 車の写真 (a photo of a car): 0.001
- 犬の写真 (a photo of a dog): 0.001
[レア/紛らわしい動物] (num_labels = 5)
Top: 猫の写真 (0.722) / top2 gap: 0.513, entropy: 0.819
- 猫の写真: 0.722
- キツネの写真: 0.209
- ヒョウの写真: 0.035
- トラの写真: 0.019
- オセロットの写真: 0.016
[カタカナ表記ゆれ] (num_labels = 3)
Top: ネコ ノ シャシン (0.836) / top2 gap: 0.732, entropy: 0.554
- ネコ ノ シャシン: 0.836
- イヌ ノ シャシン: 0.104
- クルマ ノ シャシン: 0.060
[ひらがなだけ] (num_labels = 3)
Top: ねこのしゃしん (0.912) / top2 gap: 0.864, entropy: 0.357
- ねこのしゃしん: 0.912
- いぬのしゃしん: 0.048
- くるまのしゃしん: 0.039
[助詞多め/冗長] (num_labels = 3)
Top: ベンチの横で丸まっている猫が写った夜の写真です (0.929) / top2 gap: 0.858, entropy: 0.258
- ベンチの横で丸まっている猫が写った夜の写真です: 0.929
- 芝生の上でボールをくわえている犬を写した写真です: 0.071
- 都市の道路を走る赤い車を写した写真です: 0.000
[英語だけ長文] (num_labels = 3)
Top: a long exposure night photo of a brown cat curled up on a park bench (0.995) / top2 gap: 0.991, entropy: 0.029
- a long exposure night photo of a brown cat curled up on a park bench: 0.995
- a photo of a red car driving on a city street: 0.004
- a photo of a black dog playing on the grass with a ball: 0.000
[多言語4カテゴリx3言語] (num_labels = 12)
Top: photo d'un chat (0.546) / top2 gap: 0.195, entropy: 0.995
- photo d'un chat: 0.546
- a photo of a cat: 0.352
- 猫の写真: 0.078
- キツネの写真: 0.022
- photo d'une voiture: 0.000
- 犬の写真: 0.000
- a photo of a dog: 0.000
- 車の写真: 0.000
- photo d'un chien: 0.000
- a photo of a car: 0.000
- a photo of a fox: 0.000
- photo d'un renard: 0.000
[多言語ネコ科イヌ科] (num_labels = 18)
Top: photo d'un chat (0.470) / top2 gap: 0.168, entropy: 1.363
- photo d'un chat: 0.470
- a photo of a cat: 0.303
- photo d'un tigre: 0.111
- 猫の写真: 0.067
- キツネの写真: 0.019
- photo d'un léopard: 0.017
- a photo of a tiger: 0.006
- ヒョウの写真: 0.003
- トラの写真: 0.002
- photo d'un loup: 0.001
- オオカミの写真: 0.000
- 犬の写真: 0.000
- a photo of a dog: 0.000
- photo d'un chien: 0.000
- a photo of a leopard: 0.000
- a photo of a wolf: 0.000
- a photo of a fox: 0.000
- photo d'un renard: 0.000
[多言語5言語x4カテゴリ] (num_labels = 20)
Top: photo d'un chat (0.411) / top2 gap: 0.147, entropy: 1.509
- photo d'un chat: 0.411
- a photo of a cat: 0.265
- ein Foto eines Fuchses: 0.148
- ein Foto einer Katze: 0.091
- 猫の写真: 0.059
- キツネの写真: 0.017
- una foto de un gato: 0.006
- ein Foto eines Autos: 0.001
- ein Foto eines Hundes: 0.001
- photo d'une voiture: 0.000
- 犬の写真: 0.000
- a photo of a dog: 0.000
- 車の写真: 0.000
- photo d'un chien: 0.000
- una foto de un zorro: 0.000
- a photo of a car: 0.000
- una foto de un coche: 0.000
- a photo of a fox: 0.000
- photo d'un renard: 0.000
- una foto de un perro: 0.000
[多言語スクリプト混在] (num_labels = 28)
Top: photo d'un chat (0.454) / top2 gap: 0.162, entropy: 1.480
- photo d'un chat: 0.454
- a photo of a cat: 0.292
- ein Foto einer Katze: 0.101
- 猫の写真: 0.065
- 一只猫的照片: 0.047
- 고양이 사진: 0.022
- una foto de un gato: 0.006
- ein Foto eines Wolfes: 0.003
- 一只狗的照片: 0.002
- 一只狼的照片: 0.002
- ein Foto eines Autos: 0.001
- ein Foto eines Hundes: 0.001
- 一辆车的照片: 0.001
- photo d'un loup: 0.001
- 늑대 사진: 0.001
- 개 사진: 0.001
- photo d'une voiture: 0.000
- オオカミの写真: 0.000
- 犬の写真: 0.000
- a photo of a dog: 0.000
- 車の写真: 0.000
- photo d'un chien: 0.000
- a photo of a car: 0.000
- a photo of a wolf: 0.000
- una foto de un coche: 0.000
- una foto de un lobo: 0.000
- 자동車 사진: 0.000
- una foto de un perro: 0.000
グラフ
実行結果に基づいて作成した以下のグラフを使って、多言語の呪いがCLIPモデルに与える影響を可視化しました。
1. Top-1確率のグラフ
このグラフは、各プロンプトセットごとのTop-1確率を示しています。Top-1確率は、モデルが最も自信を持って予測したラベルが正解である確率を示します。
-
色の意味:
グラフの棒の色は、color='skyblue'(淡い青色)で、全てのプロンプトに共通しています。色分けにより、確信度が高いものを視覚的に強調できますが、このグラフでは色の違いはありません。主に確信度を示すための基本的な情報を提供します。 -
解釈:
- 日本語シンプルや英語シンプルなど、シンプルなプロンプトセットでは、確信度が0.99以上で非常に高い値を示しており、モデルはほぼ完璧な予測をしています。
- 一方、多言語対応のプロンプト(例えば「多言語4カテゴリx3言語」)では、確信度が低く(0.4~0.5程度)、予測の信頼性が低下しています。これにより、多言語の呪いがモデルのパフォーマンスに強く影響を与えていることが示唆されます。
2. エントロピーのグラフ
エントロピーは、予測の確率分布がどれだけ広がっているかを示す指標です。エントロピーが大きいほど、モデルが複数の選択肢に対して均等に分布している、すなわち予測に自信がないことを意味します。逆に、エントロピーが小さいと、モデルは一つの予測に強い自信を持っています。
-
色の意味:
このグラフの色は、color='lightcoral'(薄い赤色)で統一されています。色を統一することで、エントロピーの変動が目立ちやすくなり、視覚的に分かりやすくなります。 -
解釈:
- 日本語シンプルや英語シンプルのようなシンプルなプロンプトセットでは、エントロピーが非常に低く(0.045〜0.045)、モデルは予測に強い確信を持っていることがわかります。
- 多言語対応のプロンプト(例えば、「多言語4カテゴリx3言語」)では、エントロピーが高く(0.995~1.5の範囲)なり、予測がバラついていることが観察できます。これは、多言語の呪いがモデルの予測を広げ、確信度が低くなったことを示しています。
3. Top-1確率 vs. ラベル数のグラフ
このグラフは、Top-1確率とラベル数の関係を示しています。ラベル数が増えることで、モデルの予測がどのように変化するかを視覚的に確認できます。
-
色の意味:
この散布図は、ラベル数を示すために、c=num_labels(ラベル数)を指定して色分けしています。色はcmap='viridis'(ヴァイリディス色)で指定しており、色の変化がラベル数の増加に従って変わるように設定しています。Viridisは視認性が高い色のスケールで、色の変化によってラベル数の増加が視覚的に強調されます。 -
解釈:
- ラベル数が少ないプロンプト(例えば「日本語シンプル」や「英語シンプル」)では、Top-1確率が非常に高く(0.99以上)、モデルが高い確信度を持って予測しています。
-
ラベル数が多いプロンプト(例えば「多言語4カテゴリx3言語」や「多言語スクリプト混在」)では、Top-1確率が顕著に低下し、確信度が分散しています。これにより、多言語の呪いの影響が現れ、モデルがより多くの選択肢を処理しようとするため、予測が不安定になることが示されています。
結論
実行したグラフから得られた結果をもとに、以下の重要な点が確認できました:
- Top-1確率のグラフでは、多言語の呪いの影響を受けたプロンプトで確信度が低下していることが示されました。言語数が増えると、モデルが最も自信のある予測を行うことが難しくなり、精度が低下する傾向があります。
- エントロピーのグラフは、多言語の呪いが強く作用すると、予測が複数のラベルに広がり、モデルが自信を持って予測できない状態に陥ることを示しています。特に、ラベル数が多いほど、モデルの予測分布が広がる傾向が見られます。
- Top-1確率 vs. ラベル数のグラフでは、ラベル数が増えることでTop-1確率が低下し、モデルの信頼性が分散することが確認できました。
これらの結果から、多言語の呪いがCLIPモデルに与える影響が顕著であることが確認できました。特に、複数言語や表記の異なるデータを同時に処理する際に、予測精度が低下することが示されています。
今回も最後まで見てくれてありがとうございます。


