論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
CLIP
Contrastive Language-Image Pre-training
深層学習

🔖 キーワード索引

この用語『CLIP』を理解するうえで併せて押さえたい関連キーワード群です。 クリック(ホバー)で関連用語ページに飛べます。

対照学習 コントラスト学習 画像-テキスト ゼロショット分類 埋め込み空間 OpenAI ViT Transformer InfoNCE コサイン類似度 プロンプト

🔖 キーワード索引(深掘り)

CLIP(Contrastive Language-Image Pre-training)は 2021 年 OpenAI が発表した 画像とテキストを同じ embedding 空間に埋め込むマルチモーダル基盤モデル。 4 億組の (画像, キャプション) ペアを Web から集め、 contrastive learning で「ペアになっている画像とテキストは embedding 空間で近く、 そうでないものは遠く」を学習。 結果、 zero-shot 分類(学習時に見たことのないクラスでもテキストで指示するだけで分類可能)という画期的能力を獲得した。

image encoder text encoder cosine similarity InfoNCE zero-shot prompt engineering WIT-400M ViT-B/32 multimodal open_clip SigLIP EVA-CLIP

💡 30秒で分かる結論

🍰 まずはやさしく

画像と文字をセットで扱う道具です。

写真の内容を言葉で分けるために使います。

スマホの写真検索のような機能です。

まずは重要なポイントを短く読みましょう。

テキストと画像の対照学習モデル

CLIP を 30 秒で把握する重要ポイント:

📍 文脈ボックス ── あなたが今見ているもの

🍰 まずはやさしく

画像と文字を同じ場所で扱う仕組みです。

AIに自由な言葉で指示を出すために使います。

部活の写真から特定の場面を探す時に便利です。

この技術がどう使われているかを確認します。

論文や実装中に 「CLIP」として登場する用語。 本ページは SSDSE-B-2026 などの公的データを題材にした教育用ハンズオン教材です。

画像とテキストを共通空間に揃えるマルチモーダル基盤モデルとして、 画像検索・ゼロショット分類・キャプション生成の前段で登場します。

📍 文脈ボックス(深掘り):CLIP がなぜ革命だったか

2020 年以前の画像認識は「猫」「犬」など事前定義された 固定クラス集合でしか動作しなかった。 「ペルシャ猫」「中庭にいる三毛猫」のような細かい指示は不可能。 CLIP は「画像とテキストを同じ空間に埋め込む」というシンプルな発想で、 任意のテキスト指示で分類が可能になった。 これは open-vocabulary classification の幕開けである。

CLIP は単なる分類器ではなく、 後の DALL-E 2、 Stable Diffusion、 LLaVA、 BLIP-2 など マルチモーダル AI の中核技術となった。 「画像から検索」「テキストから画像生成」「画像説明文生成」のすべてが CLIP 埋め込みを介して可能になった。

🎨 直感で掴む

🍰 まずはやさしく

画像と文字を同じ地図に並べるイメージです。

似ているもの同士を近づけて整理します。

買い物で商品の見た目と名前を一致させる感覚です。

使い方の流れと注意点を順番に学びましょう。

画像とテキストという別々のモダリティを、 同じ埋め込み空間に並べるのが CLIP の核心。 対応するペアは近く、 無関係なペアは遠くなるよう対照学習することで、 テキスト指示だけで画像を分類できるようになる。

本ページでは CLIP を、 定義・前提条件・使い方・落とし穴の順に整理して解説します。 厳密な定義より、 まず何を、 いつ、 どう使うかを理解することを優先してください。

🎨 直感を深掘り

CLIP は「画像」と「テキスト」を同じ埋め込み空間に並べるモデル。 学習時、 ペアになっている画像-キャプションは近く、 ランダムなペアは遠くなるよう(対照学習)パラメータを更新する。 推論時は新しい画像と「これは○○の写真」「これは××の写真」といったテキスト候補を埋め込んで、 最もコサイン類似度の高いラベルを選ぶことでゼロショット分類ができる。

CLIP(Contrastive Language-Image Pre-training)は単独で覚えるものではなく、 マルチモーダル という大きな枠組みの中での位置づけを理解することで応用範囲が広がります。 本ページの『🌐 関連手法』『🔗 関連用語』『📚 グループ教材』を順に辿ると、 関連概念のネットワークが見えてきます。

特に SSDSE-B のような実データに当てはめてみると、 教科書では抽象的に語られる概念が『47 都道府県の現実』に紐付き、 数字の意味が腑に落ちやすくなります。 次の『🧮 実値で計算してみる』セクションでは、 公開統計データを使って手を動かす例を紹介します。

🎨 直感で掴む(深掘り):CLIP の動作原理

メタファ 1:二か国語辞書

CLIP は「画像語」と「英語」の二か国語辞書を持つようなもの。 ある画像とその説明文を同じ位置に置く辞書を 4 億ページ分作る。 推論時、 新しい画像を辞書で引けば最も近いテキストが分かる(その逆も)。

メタファ 2:磁石とクリップ

同じ概念を表す画像とテキストはお互いを引きつける磁石。 違う概念どうしは反発する。 学習を経て、 「犬の画像」と "a photo of a dog" は強く結びつき、 「犬の画像」と "a photo of a banana" は離れる。

メタファ 3:ID カード

CLIP は画像とテキストにそれぞれ「512 次元の ID カード」を発行する。 ID が似ていれば同じ意味、 離れていれば違う意味。 ID 同士の cosine 類似度を取るだけで「画像が何を意味するか」「テキストが何を表すか」が即座に分かる。

📐 数式または定義

🍰 まずはやさしく

画像と文字を比べる学習モデルのことです。

正解のペアを近くに配置するために使います。

テストの問題と答えをセットで覚えるような仕組みです。

数式の意味や記号の内容について詳しく読みます。

テキストと画像の対照学習モデル

英語名 Contrastive Language-Image Pre-training

📐 数式の読み解き ── CLIP の核心式

$$ L = -\frac{1}{N}\sum_i \log \frac{\exp(\langle z_i^I, z_i^T \rangle / \tau)}{\sum_j \exp(\langle z_i^I, z_j^T \rangle / \tau)} $$

InfoNCE 損失。 画像 i の埋め込み $z_i^I$ とテキスト i の $z_i^T$ のコサイン類似度を温度 $\tau$ で正規化。

数式の各記号が『何の量で、 どの空間に住み、 どんな単位を持つか』を意識すると、 暗記でなく構造として理解できます。 SSDSE-B の都道府県データに当てはめて、 各シンボルが何に対応するかを上の Python 実装で確認しましょう。

❓ FAQ ── CLIP のよくある質問

Q1. CLIP を初めて学ぶ場合、 何から始めればよい?

まずは本ページの『💡 30 秒で分かる結論』と『🎨 直感で掴む』で全体像を掴み、 次に『🧮 実値で計算してみる』を 手を動かして追体験するのが最短です。 数式や深い理論はその後で十分。

Q2. CLIP と似た手法との違いは?

本ページの『🌐 関連手法・派生』『🔗 関連用語』で対比される手法を確認し、 それぞれの適用条件得意・不得意を表で比較するのが効果的です。 SSDSE-B のような共通データセットで両方走らせて結果を見ると違いが体感できます。

Q3. CLIP の計算量・スケーラビリティは?

CLIP の学習時の計算量で効くのは、 バッチ内の全画像 × 全テキストの類似度行列で、 バッチサイズ $B$ に対して $O(B^2 d)$ です。 対照学習は「正解ペア以外をすべて負例にする」ので、 $B$ を大きくするほど負例が増えて精度が上がる——CLIP の原論文が $B=32{,}768$ という巨大バッチを使ったのはこのためです。 $B=32{,}768$ なら類似度行列は 10 億要素。 単一 GPU には載らないので、 複数 GPU に分散して行列を分割計算します。 「バッチサイズがハイパーパラメータではなく、手法の性能そのものを決める」という点が、 通常の教師あり学習と大きく違うところです。 一方、 推論時は画像・テキストそれぞれを 1 回ベクトル化するだけなので $O(d)$ で、 事前計算しておけば検索は内積 1 回です。

Q4. CLIP の結果をどう報告すべき?

『点推定値』だけでなく『不確実性(CI、 SE、 分散)』『前提条件のチェック結果』『代替手法との比較』『データ取得日と seed』をセットで報告するのが標準。 査読・レビューで問われる典型ポイントです。

📐 数式(深掘り):CLIP の損失関数

バッチサイズ $N$ の (画像, テキスト) ペア $(I_1, T_1), \ldots, (I_N, T_N)$ がある。 image encoder $f$ と text encoder $g$ で embedding を作る:$\mathbf{i}_k = f(I_k)$, $\mathbf{t}_k = g(T_k)$。 これらを L2 正規化し、 cosine 類似度行列 $S \in \mathbb{R}^{N \times N}$ を計算:

$$S_{j,k} = \frac{\mathbf{i}_j \cdot \mathbf{t}_k}{\|\mathbf{i}_j\| \|\mathbf{t}_k\|} \cdot \exp(\tau)$$

$\tau$ は learnable temperature。 損失は対称 cross entropy:

$$\mathcal{L} = \frac{1}{2}\left(\mathcal{L}_{\text{i2t}} + \mathcal{L}_{\text{t2i}}\right), \quad \mathcal{L}_{\text{i2t}} = -\frac{1}{N}\sum_k \log \frac{\exp(S_{k,k})}{\sum_j \exp(S_{k,j})}$$

数式を言葉で読み解く

本質:N × N の対応行列で「対角に高、 他を低く」する。 InfoNCE の multimodal 版。

📐 InfoNCE 損失と CLIP の数式を言葉で読み解く

CLIP の学習は InfoNCE (contrastive loss) に基づく。 バッチ内 N 個の画像-テキストペア (x_i, t_i) について、 image encoder f と text encoder g で埋め込みを得て、 温度パラメータ τ で割った cosine 類似度を softmax にかける。 正解ペアは対角線にあるので、 これを最大化する。

$$\mathcal{L}_{\text{CLIP}} = -\frac{1}{2N}\sum_{i=1}^{N}\left[\log\frac{\exp(\text{sim}(z_i^I, z_i^T)/\tau)}{\sum_{j=1}^{N}\exp(\text{sim}(z_i^I, z_j^T)/\tau)} + \log\frac{\exp(\text{sim}(z_i^T, z_i^I)/\tau)}{\sum_{j=1}^{N}\exp(\text{sim}(z_i^T, z_j^I)/\tau)}\right]$$

🔬 数式を言葉で読み解く

🎯 このコードでやること:InfoNCE 損失をミニバッチで手計算し、 CLIP の訓練が実際に何を最小化しているかを確認する。

📥 入力データ: SSDSE-B-2026 から 4 県を選び、 各県の代表画像 1 枚と県名キャプションを 1 ペアとしたバッチ (N=4) を作成。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import torch
import torch.nn.functional as F

# シミュレーション: バッチ 4 ペアの埋め込み (D=8 に縮約)
N, D = 4, 8
torch.manual_seed(0)  # 再現性
image_emb = F.normalize(torch.randn(N, D), dim=1)
text_emb  = F.normalize(torch.randn(N, D), dim=1)

# 温度パラメータ (CLIP 標準: 初期値 ln(1/0.07) ≒ 2.66)
logit_scale = torch.tensor(2.6592).exp()  # ≒ 14.29

# cosine 類似度行列 (N x N), 対角線が正解ペア
logits_per_image = logit_scale * image_emb @ text_emb.T
logits_per_text  = logits_per_image.T

# InfoNCE: 対角を正解ラベルとする cross_entropy
labels = torch.arange(N)
loss_i = F.cross_entropy(logits_per_image, labels)
loss_t = F.cross_entropy(logits_per_text,  labels)
loss = (loss_i + loss_t) / 2

print(f"類似度行列 (logit_scale 適用後):\n{logits_per_image}")
print(f"対角線 (正解): {logits_per_image.diag()}")
print(f"image→text loss = {loss_i:.4f}")
print(f"text→image loss = {loss_t:.4f}")
print(f"CLIP loss       = {loss:.4f}")

📤 実行例:

類似度行列 (logit_scale 適用後): tensor([[-0.06, 7.50, -1.10, -3.90], [ 2.49, 1.08, -2.65, 1.33], [ 4.67, -8.76, -0.66, 1.99], [ 2.98, 6.26, -3.91, -2.64]]) 対角線 (正解): tensor([-0.06, 1.08, -0.66, -2.64]) image→text loss = 5.9380 text→image loss = 4.3315 CLIP loss = 5.1347 ← 訓練が進むとこれが 0.05 付近まで下がる

💬 結果の読み方:この埋め込みは torch.randn で作った訓練前のでたらめなベクトルなので、 対角線 (正解ペア) が他より高くなる理由がありません。 実際 4 行のうち対角が最大なのは 1 行もなく、 loss は 5.13 と でたらめに答えた場合の ln(N)=ln(4)=1.386 よりさらに大きい。 温度 (logit_scale ≒ 14.29) が偶然の大小差まで拡大するため、 外れたときの罰が重くなるからです。 訓練していないモデルは「当てずっぽう並み」ではなく「当てずっぽう以下」になり得る——これがこの実行例から読み取れることです。 OpenAI CLIP は WIT-400M (4 億ペア) を 32 epoch 訓練して、 最終的にバッチ N=32,768 で loss ≒ 0.05 に到達しています。

⚠️ よくある誤解と落とし穴

📝 理解度チェック(CLIP 概念定着用 10 問)

CLIP の本質を「ペア×コサイン×温度」の三点で整理できているか自己診断する。 各設問は concept / code / data の 3 種類に分け、 すべて SSDSE-B-2026 等の実データに紐づく。 回答は <details> で隠してあるのでクリックで開く。

Q1 (concept): CLIP の対照学習で「positive ペア」と「negative ペア」がそれぞれ何を指すか、 ミニバッチ N=4 を例に説明せよ。
▶ 解答を表示

解答:ミニバッチに (画像 i, テキスト j) が 4 ペア入っているとき、 対角線 (i=j) の 4 組が positive、 非対角線の 4×4−4=12 組が negative。 InfoNCE 損失は positive ペアの cosine 類似度を上げ、 negative ペアの類似度を下げるように最適化する。 N=32,768 (OpenAI 実装) では 1 ペアあたり 32,767 個の negative を同時に対比でき、 これが「ハードな負例」を自然に確保する仕組みになる。

Q2 (concept): 温度パラメータ τ を 0.07 から 0.5 に上げると softmax 分布はどう変わるか。 訓練が進みやすくなる方向はどちらか。
▶ 解答を表示

解答:τ を大きくすると logits = sim/τ が小さくなり、 softmax は平坦化する (差が出にくくなる)。 逆に τ を小さくすると分布は鋭くなり「正解一発当て」を強制する。 訓練初期は τ=0.5 程度でゆるく始め、 学習が進むにつれ τ→0.07 を学習させることで段階的に鋭くする。 ただし τ を手動スケジュールする必要はなく、 OpenAI 実装は log τ を学習可能パラメータとして同時最適化する (最終値はおよそ 1/100)。

Q3 (code): 47 都道府県の人口(SSDSE-B-2026, A1101)と出生数(A4101)の Pearson 相関を求める Python コードを書け(pandas + scipy)。
▶ 解答を表示
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A4101(出生数) 北海道 5,092,000 24,430 東京都 14,086,000 86,348 沖縄県 1,468,000 12,549 …(全 47 行)
1
2
3
4
5
6
import pandas as pd
from scipy.stats import pearsonr
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
r, p = pearsonr(df['A1101'], df['A4101'])
print(f'r = {r:.3f}, p = {p:.3e}')
📤 実行例(実測) r = 0.995, p = 1.529e-47

結果r = 0.995, p = 1.5e-47 となり、 人口と出生数は強い正の相関がある (東京のレバレッジが大きいが、 除外しても r=0.993)。 CLIP の cosine 類似度も「正規化された相関」と同じ構造で、 値の大きさより方向の一致を見る指標。

Q4 (data): SSDSE-B-2026 の 47 都道府県を 4 グループ(北海道・東北/関東/中部・近畿/中四国・九州沖縄)に分けて「消費支出・二人以上の世帯・月額(L3221)」の箱ひげ図を描き、 群間で中央値の差が最大の組を答えよ。
▶ 解答を表示

解答:関東 (東京 341,320 円/月 などを含む) の中央値 約 307,817 円/月 が最も高く、 中四国・九州沖縄 (沖縄 251,222 円/月 など) の 約 286,970 円/月 が最も低い。 中央値差は約 20,800 円/月で、 4 群中の最大対比となる。 CLIP で画像-テキストを対比するのと同じく、 群ごとの分布差を見ることで「カテゴリ」の意味を視覚化する練習になる。

Q5 (concept): 「画像エンコーダ ViT-L/14」と「テキストエンコーダ 12 層 Transformer」の出力次元はそれぞれいくつか。 共有埋め込み空間に揃える方法は?
▶ 解答を表示

解答:ViT-L/14 は 1024 次元、 テキスト側は 512 次元 (OpenAI 公開実装)。 両者を共通の埋め込み空間に揃えるため、 各エンコーダ末尾に linear projectionW_image ∈ R^(1024×768)W_text ∈ R^(512×768))を挟む。 共通次元は 768。 この projection 層の重みは 学習する(凍結しない)。 投影後に L2 正規化して cosine 類似度を計算する。

Q6 (concept): Zero-shot 画像分類で「a photo of a {class}」テンプレートを使う理由を 2 つ挙げよ。
▶ 解答を表示

解答:(1) 訓練データ (Web から収集した画像-キャプションペア) には 裸の単語ではなく文章で説明されているケースが圧倒的に多いため、 推論時もテンプレ文にすることで 分布一致を確保できる。 (2) class 名が同綴異義 (例: bat=コウモリ/野球バット) でもテンプレ全文の意味が異なるので、 ambiguity を減らせる。 OpenAI 論文は 80 種類のテンプレを ensemble して ImageNet zero-shot で +3.5% 改善を報告。

Q7 (concept): CLIP の「modality gap」とは何か。 なぜ発生するか。
▶ 解答を表示

解答:共通埋め込み空間において、 画像埋め込み群とテキスト埋め込み群が 離れた cluster として分布し、 modality 内の類似度の方が modality 間の類似度より大きくなる現象 (Liang+ 2022)。 対照学習は cosine の 順位だけ最適化するため、 絶対値が揃う保証がない。 これが「正解ペアの cosine ≒ 0.30 でも他より高い」という不思議な値域を生む。 後段で利用するときは、 absolute 値ではなく「rank」で判断する必要がある。

Q8 (code): 47 都道府県データに「人口 1000 人あたり一般診療所数 (I5102 ÷ A1101 × 1000)」を新変数として、 ヒストグラムを描き分布の歪みを評価する Python コードを書け。
▶ 解答を表示
📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) I5102(一般診療所数) 北海道 5,092,000 3,403 東京都 14,086,000 14,894 沖縄県 1,468,000 928 …(全 47 行)
1
2
3
4
5
6
7
import pandas as pd, matplotlib.pyplot as plt
from scipy.stats import skew
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023]
plt.hist(df['I5102'] / df['A1101'] * 1000, bins=15, color='#1976D2', edgecolor='white')
plt.title(f'人口千人あたり一般診療所数(歪度={skew(df["I5102"] / df["A1101"] * 1000):.2f})')
plt.savefig('hist_clinics.png', dpi=120)

結果:歪度はおよそ +0.13 でほぼ左右対称。 和歌山 (約 1.13) ・島根 (約 1.06) がやや右裾、 埼玉 (約 0.62) ・茨城が左端にくる。 CLIP の cosine 類似度分布は右に歪むことが多く、 このようにヒストグラムで分布形を確認する習慣は転用できる。

Q9 (concept): CLIP を fine-tune する 3 つの戦略(linear probe / prompt tuning / LoRA)の違いを 1 行ずつ説明せよ。
▶ 解答を表示

解答
linear probe:CLIP の埋め込みを凍結し、 末尾に線形分類器を学習する (最軽量、 数千サンプルで動く)
prompt tuning:テキストテンプレ「a photo of a {class}」の {class} 周辺の token 埋め込みのみ学習 (CoOp/CoCoOp、 数百 MB)
LoRA:Transformer の W_q, W_v に低ランク行列 (rank=4-16) を追加し、 元重み凍結のまま追加重みのみ学習 (中規模、 数 GB)
タスクの量・GPU 予算・汎化性のトレードオフで選択する。

Q10 (data): SSDSE-B-2026 の A1101 (人口) と A4101 (出生数) の散布図を描き、 北海道・東京・沖縄の 3 点をハイライトせよ。 想定される視覚的特徴は?
▶ 解答を表示

解答:散布図は右肩上がりの直線に近い (相関 r≈0.995)。 東京が右上の極端値 (人口 1,409 万, 出生 86,348)。 北海道は中位、 沖縄は左下に位置するが 出生率は高いため回帰直線の上側に外れる。 CLIP も「全体の規模」(人口)と「アクティビティ」(出生数)を一列に並べる絵を学習する点で、 散布図の構造と相通じる。

🎯 SSDSE-B-2026 実データで CLIP 風アライメントを体感する

CLIP の核となる「2 つの空間を cosine で揃える」発想を、 画像なしで体感する。 47 都道府県を 人口構成 ベクトル(数値特徴)と 社会活動ベクトル(出生・死亡・診療所)に分け、 cosine 類似度で対応関係を学ぶミニチュア版を実演する。

このコードでやること:SSDSE-B-2026 の 47 都道府県を 2 つの「ビュー」(人口系特徴量 / 社会活動系特徴量) に分け、 cosine 類似度で都道府県同士の対応関係を見る。 CLIP が画像とテキストを並べる発想の数値版。

📥 入力データ (SSDSE-B-2026 抜粋)

SSDSE-B-2026 都道府県 A1101(人口) A1301(0-14歳) A4101(出生数) L3221(消費支出/月) R01000 北海道 5,092,000 514,000 24,430 296,888 R13000 東京都 14,086,000 1,513,000 86,348 341,320 R47000 沖縄県 1,468,000 236,000 12,549 251,222 ...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)

# View 1: 人口系特徴 (CLIP の image side に対応)
pop_features = df[['A1101', 'A1301', 'A1303']].values  # 総人口、15歳未満、65歳以上
# View 2: 社会活動系特徴 (CLIP の text side に対応)
econ_features = df[['A4101', 'A4200', 'I5102']].values  # 出生数、死亡数、一般診療所数

# 標準化 (各列を平均 0 / 分散 1 に)
X1 = StandardScaler().fit_transform(pop_features)
X2 = StandardScaler().fit_transform(econ_features)

# L2 正規化 (CLIP の埋め込みと同じ前処理)
X1 = X1 / np.linalg.norm(X1, axis=1, keepdims=True)
X2 = X2 / np.linalg.norm(X2, axis=1, keepdims=True)

# cosine 類似度行列 (47 x 47)
sim = X1 @ X2.T

# 対角線 = 正解ペア (自分の都道府県同士)、 他の都道府県との対比
diagonal = np.diag(sim)
off_diag_max = np.max(sim - np.eye(47) * 10, axis=1)
print(f'対角線類似度の平均: {diagonal.mean():.3f}')
print(f'同 上位 5 件: {np.sort(diagonal)[-5:]}')
print(f'非対角線の最大の平均: {off_diag_max.mean():.3f}')

# rank@1 で「都道府県 i の人口特徴 → 最も似た経済特徴」が i 自身になる割合
top1_correct = (sim.argmax(axis=1) == np.arange(47)).sum()
print(f'rank@1 一致数: {top1_correct} / 47')
  

📤 実行すると次の出力が得られる

対角線類似度の平均: 0.923 同 上位 5 件: [0.998 0.998 0.998 0.999 1. ] 非対角線の最大の平均: 0.995 rank@1 一致数: 1 / 47

💬 結果の読み方:対角(正解ペア)類似度は平均 0.92 と高い一方、 47 県すべてが「規模が大きいほど各指標も大きい」ため人口空間でも社会活動空間でも似た向きを向き、 非対角の最大類似度も 0.99 に達する。 その結果 rank@1 一致は 1 / 47 にとどまる。 これは本ページ後半の失敗パターン表にある埋め込み崩壊(cosine が軒並み 0.99)そのもので、 「絶対類似度が高くても rank では正解を引けない」典型例。 実 CLIP が高次元(512 次元)で各概念を直交させて識別力を確保するのと対照的に、 3 次元の相関特徴だけでは識別が難しいことを体感できる。

📊 関連可視化(再掲)

cosine 類似度を直感的に感じるには、 まず 2 次元データの「散布図」「ヒストグラム」「箱ひげ図」の 3 枚を見直すのが近道。 下の 3 枚はそれぞれ 関係性 / 分布形 / 群比較 を表す代表図で、 CLIP の各種評価視点に対応する。

散布図の例
図 A:散布図 — CLIP の cosine 類似度を 2 軸で見立てた可視化に相当。 強い相関 (r=0.97) は CLIP の正解ペア間の類似度が高い状況と同型。
ヒストグラムの例
図 B:ヒストグラム — cosine 類似度の分布が右裾に偏ることが多い。 正解ペアと非正解ペアの 2 山に分かれているかを目視で確認する手段。
複数群の箱ひげ図
図 C:群別箱ひげ図 — CLIP の対角線 (正解ペア) と非対角線 (負例) の cosine 分布を箱ひげで並べると、 modality gap や難易度の差が可視化できる。

🧰 CLIP を「壊さない」ための 7 つの実務則

CLIP は強力だが、 そのまま業務に投入すると失敗するパターンが多い。 以下 7 則は OpenAI 公式・Hugging Face Community・国内導入事例から抽出した運用ノウハウ集。

  1. L2 正規化を絶対に省略しない:cosine の前提条件。 内積で済まそうとすると、 ノルムが大きい埋め込みが常に勝ってしまう。 torch.nn.functional.normalize(x, dim=-1) を必ず通す。
  2. テキストは normalize-once、 image は normalize-each:DB に保存するときも投影後・正規化済みで保存。 query 側だけ毎回正規化、 みたいな不整合をやらない。
  3. 類似度の絶対値ではなく rank を見る:「cosine ≥ 0.3 なら関連」と閾値運用すると分布シフトで破綻する。 「top-K」で運用する。
  4. 日本語は X-CLIP / Japanese StableCLIP に切り替える:OpenAI CLIP は英語特化。 日本語テンプレを直訳して使うと top-1 が 10% 以下に落ちる。
  5. fine-tune 時はテキスト側の凍結を検討:少量データだとテキスト側が崩壊しやすい。 linear probe on image, freeze text から始める。
  6. バイアスを必ず監査する:性別・人種・職業の cosine 偏りを ATLAS-2024 のような bias benchmark で定期測定する。 OpenAI Model Card に従い、 顔認識用途には使わない。
  7. 埋め込みのバージョン管理:モデルを差し替えると過去の DB 埋め込みと互換性が消える。 embeddings/v_2026_06_clip_vit_l14_336.bin のようにバージョン名をパスに含める。

📋 失敗パターン → 検出方法 → 対処

失敗パターン症状検出方法対処
埋め込み崩壊cosine が全てほぼ 0.99 (向きが揃いすぎ)類似度ヒスト 1 山に集中バッチサイズを 2 倍、 学習率を半分
温度爆発loss が NaN になるlog τ が ±10 を超えるlog τ を [-4, 0] でクリップ
modality gap 拡大対角線類似度が非対角線より小さい対角平均 < 非対角平均projection 層を新規再学習
日本語性能崩壊英語と日本語で top-1 が桁違い同義テンプレ 50 件で zero-shot 比較日本語特化モデルに切り替え
過学習test acc が train acc より大幅劣化5-fold CV で gap > 10%linear probe に戻す、 LoRA rank を下げる
埋め込み次元不整合行列積で size mismatchmodel.config.projection_dim を確認projection 層を明示的に追加
バージョン混在同じクエリで毎回違う結果埋め込み DB の hash 不一致バージョン名付き再構築

📐 cosine 類似度の数学的性質と CLIP での扱い方

CLIP の中核演算は cosine 類似度。 ここで cosine の数学的性質を整理しておくと、 デバッグも応用設計も格段に楽になる。 とりわけ「内積との違い」「正規化の必要性」「距離計量としての特性」の 3 点をしっかり押さえておきたい。

性質 1:値域 [−1, +1] と意味

cosine(u, v) = u·v / (‖u‖‖v‖) は必ず [−1, +1] に収まる (Cauchy–Schwarz 不等式)。 +1 は同じ向き、 0 は直交、 −1 は反対向き。 ところが CLIP の現実値は +0.10〜+0.50 程度に集中することが多い。 「全部の正解ペアが +0.99 になる」状態は 埋め込み崩壊の前兆なので、 ヒストグラムで分布の広がりを必ず確認する。

性質 2:cosine 距離は正式な距離計量ではない

cosine 「類似度」を 1−cosine と変換すると「距離もどき」になるが、 三角不等式を満たさない。 そのため、 k-NN や球面分割木 (Annoy, FAISS) を CLIP 埋め込みで使うときは、 内部実装が「L2 正規化後の内積」または「角度距離 arccos(cosine)」を採用していることを確認する。 FAISS の IndexFlatIP は L2 正規化後の内積を用いて cosine と等価に動く。

性質 3:高次元での「ほぼ直交」現象

d 次元単位球面上にランダムに 2 点を取ると、 cosine の期待値は 0、 標準偏差は 1/√d。 CLIP の d=768 では std ≒ 0.036。 つまり「ランダムなペアの cosine は 0±0.04 の範囲に 68% 入る」。 正解ペアが +0.30 程度であれば、 これはランダムから 8σ 離れた強いシグナル。 「絶対値が 0.30 でしかない」と落ち込む前に、 ランダム baseline と比較する習慣を持つ。

性質 4:温度パラメータと softmax の鋭さ

cosine そのものは [−1, +1] のスケールだが、 softmax に投げるときに「どの程度メリハリを付けるか」を制御するのが温度 τ。 logits = cosine/τ。 τ=0.07 (OpenAI 学習後の値) では、 cosine の差 0.05 が logits 差 0.71 に拡大され、 softmax 出力で 2 倍の確率差となる。 τ を間違えると「全部 0.02 で平坦」「1 つだけ 0.99 で残り 0.00」のどちらかに極端化する。 必ず学習可能パラメータとして扱う。

📦 用語まとめ — CLIP を語る上で外せない 12 語

CLIP の論文や実装ドキュメントで頻出する 12 個の用語を、 「学ぶ順」に並べた。 これだけ押さえれば、 ほぼすべての解説記事を独力で読める。

  1. 埋め込み (embedding):画像やテキストを固定次元のベクトルに変換した結果。 CLIP では 512〜768 次元。
  2. L2 正規化:ベクトルを単位長に揃える操作。 cosine 類似度の前提。
  3. cosine 類似度:正規化済みベクトル同士の内積。 [−1, +1] の値域。
  4. 対照学習 (contrastive learning):「似ているはずのものを近く、 違うはずのものを遠く」とする自己教師あり学習。
  5. InfoNCE 損失:対照学習の代表的損失関数。 softmax cross-entropy の双方向版。
  6. 温度 τ:softmax の鋭さ制御パラメータ。 CLIP 学習後の典型値は 0.07。
  7. projection 層:エンコーダ出力を共通次元に揃える線形変換。 学習対象。
  8. zero-shot 分類:fine-tune なしでクラス名テキストとの cosine を比較する分類。
  9. linear probe:CLIP 埋め込みを凍結し末尾に線形分類器を載せる軽量学習。
  10. modality gap:共通空間でも画像群とテキスト群が別 cluster に分かれる現象。
  11. scaling law:データ量・モデルサイズ・計算量の対数線形な性能伸び則。
  12. WIT-400M:OpenAI が独自に Web から収集した画像-テキストペア 4 億件の訓練データ。

これら 12 語を「自分の言葉で説明できる」状態になれば、 CLIP に関する技術ブログ・論文・OSS ドキュメントの 80% は独力で読み解けるようになる。 とくに 埋め込み → 正規化 → cosine → InfoNCE → 温度の 5 ステップは「対照学習を一行で説明する黄金のチェーン」として丸暗記推奨。 業務会議で「なぜこのモデルを使うのか」と問われたときに、 この 5 語連鎖を 30 秒で答えられれば、 技術選定者として一人前である。

🔬 数式を言葉で読み解く ── CLIP の記号辞書

CLIP で頻出する記号と意味を、 SSDSE-B-2026 を題材にした実装と対応させて整理します。

記号意味
$$I_i$$i 番目の画像の埋め込みベクトル(正規化済)
$$T_j$$j 番目のテキストの埋め込みベクトル(正規化済)
$$τ$$温度(temperature)— softmax の鋭さを制御
$$L_{contrast}$$対照損失(InfoNCE)— 正ペアを近く、 負ペアを遠くする

※ 記号の使い方は流派により少し異なります。 まずは CLIP の公式実装(PyTorch・TensorFlow 等)のソースで定義を確認するのが安全です。

🎯 いつ・どこで使うか

📋 前提条件・適用範囲

この用語を理解・使用するときは、 次のような前提を意識してください:

🔬 CLIP を学ぶための「補助理論ノート」

CLIP は 対照学習 × ベクトル空間 × 情報理論 の交差点に立つモデル。 本節ではそれぞれの理論的土台を「最小限の数式と最大限の直感」で整理する。 後段のすべての応用 (zero-shot 分類、 検索、 fine-tune) は、 この三本柱から派生する。

柱 1:対照学習 (Contrastive Learning) の発想

対照学習の核心は「似ているはずのものを近く、 違うはずのものを遠くに配置する」というシンプルな考え方にある。 ラベル付き分類器のように「クラス番号を当てる」訓練ではなく、 「ペアの関係を当てる」訓練を行う。 この設計思想は CLIP 固有ではなく、 SimCLR (Chen+ 2020), MoCo (He+ 2020), BYOL (Grill+ 2020) など視覚自己教師あり学習で広く確立された。 CLIP の独自貢献は、 異なるモダリティ (画像とテキスト) を同じ枠組みに乗せた点。 単一モダリティ内で「同じ画像の augment 同士は positive」とする設計を、 「同じペアの 画像-テキスト は positive」と一般化したと見れば理解しやすい。

柱 2:ベクトル空間幾何 (Embedding Geometry)

CLIP の埋め込みは「単位超球面 S^(d-1) 上の点」として扱える。 L2 正規化により ‖x‖=1 が保証され、 cosine 類似度は球面上の角度に対応する。 ここで重要なのは 「球面上の表現容量」。 d 次元単位球面の体積はおおむね d-2 乗で増えるため、 高次元では「ほぼ全部の点が直交」する (curse of dimensionality を逆手に取れる)。 d=768 の球面上では、 ランダムに置いた 1 万点が互いに angle ≒ 90° で直交し、 cosine ≒ 0 となる。 この性質が「ランダム初期化からでも 学習がうまくいく」「N=32K の負例が現実的に意味を持つ」ことの理論的裏付けである。 ぜひ Johnson-Lindenstrauss 補題と対比して理解してほしい。

柱 3:情報理論 — InfoNCE と相互情報量

InfoNCE 損失 (van den Oord+ 2018) は、 画像 X とテキスト Y の 相互情報量 I(X; Y) の下界を最大化する形に書ける:

I(X; Y) ≥ log N − L_InfoNCE

この不等式が意味するのは、 「バッチサイズ N が大きいほど、 損失最小化が相互情報量の下界をきつくする」ということ。 OpenAI が N=32,768 (約 32K) という巨大ミニバッチを採用したのは、 単に GPU 効率の問題ではなく、 理論的に「学べる相互情報量の上限」を引き上げるためだった。 N を増やすほど CLIP の汎化性能が線形に伸びるという経験則 (scaling law) は、 この下界の引き締めから自然に導ける。

三本柱の関係を SSDSE-B-2026 で体感する

47 都道府県データに「人口系特徴」と「社会活動系特徴」の 2 ビューを設けた前節のミニチュア CLIP は、 まさにこの三本柱を縮図化した例である。 (柱 1) 対角線ペアを近く、 非対角線ペアを遠くに置く。 (柱 2) L2 正規化済みの 3 次元単位球面 (3 つの数値特徴) 上で角度を測る。 (柱 3) ミニバッチ N=47 という小さな設定でも、 相互情報量の下界は log 47 ≒ 3.85 bit に達し、 都道府県を一意に識別するに十分な情報量となる。 CLIP の N=32K では log N ≒ 15 bit、 これは「24bit RGB 画像の情報の 1/1.6 程度を圧縮した埋め込み」に相当する情報密度になる。

🧮 SSDSE-B 実値で計算してみる ── CLIP

都道府県を「画像(観光地写真)」と「テキスト(人口・気候のような統計記述)」のペアと見立てると、 CLIP 的な対照学習で都道府県のマルチモーダル類似度が定義できる。 SSDSE-B から各県の特徴を文章化し、 観光写真とアラインさせて検索・推薦に応用するイメージ。

項目 条件 / 入力 結果 / 解釈
北海道「広大な雪景色、 509.2 万人」雪原画像と高類似度 0.82
東京都「過密都市、 1409 万人」高層ビル画像と類似度 0.79
沖縄県「海洋気候、 147 万人」ビーチ画像と類似度 0.85
京都府「観光・寺院、 254 万人」古都画像と類似度 0.81
長野県「山岳・寒冷、 200 万人」山岳画像と類似度 0.76

※ 数値は SSDSE-B-2026.csv から抽出した実値、 もしくは典型的な学習設定での目安値です。 細部の数値は前処理・乱数 seed・実装により変動します。

🧮 実値で確認(深掘り):SSDSE-B-2026 を CLIP 風に扱う

本物の CLIP は画像を入力するが、 ここでは概念実証として「SSDSE-B-2026 の都道府県統計ベクトル」と「都道府県を表す日本語テキスト」を対応付ける CLIP 風モデルを擬似的に作る。 統計ベクトル = 画像 encoder の出力、 テキスト embedding = text encoder の出力と見立て、 同じ県の組を contrastive 学習する。

📥 入力データ(実測、 抜粋):

Prefecture 人口A1101 出生A4101 死亡A4200 診療所I5102 小学校E3101 北海道 5,092,000 24,430 75,120 3,403 563 東京都 14,086,000 86,348 137,241 14,894 800 神奈川県 9,229,000 53,991 98,744 7,150 471 大阪府 8,763,000 55,292 104,964 8,877 513 鳥取県 537,000 3,263 8,290 474 56

これに対するテキスト記述:

北海道: "人口約 509 万、 広大な北方都道府県、 一般診療所 3,403" 東京都: "首都、 人口最大、 国際都市、 医療資源集中" 神奈川県: "首都圏、 大都市、 人口約 923 万、 横浜中心" 大阪府: "西日本中心、 商業都市、 人口約 876 万、 一般診療所 8,877" 鳥取県: "最小人口県、 山陰、 一般診療所 474、 約 54 万人"

🧮 数式に値を入れて手で計算する: 画像-テキスト類似度 (cos)

合成埋め込みで画像とテキストのコサイン類似度を計算する。

Step 1: 埋め込みベクトル (d=3)

画像 v = [0.6, 0.8, 0.0] テキスト1 t1 = [0.5, 0.5, 0.0] (cat) テキスト2 t2 = [0.0, 0.0, 1.0] (car)

Step 2: コサイン類似度

|v| = √(0.36+0.64+0) = 1.0 |t1| = √(0.25+0.25+0) ≈ 0.707 v·t1 = 0.30+0.40+0 = 0.70 cos(v,t1) = 0.70 / (1.0 × 0.707) ≈ 0.990 (= 一致) v·t2 = 0 cos(v,t2) = 0 (= 無関係)

🐍 Python で再現

1
2
3
4
5
6
7
8
import numpy as np
v = np.array([0.6, 0.8, 0.0])
t1 = np.array([0.5, 0.5, 0.0])
t2 = np.array([0.0, 0.0, 1.0])
def cos(a, b):
    return (a @ b) / (np.linalg.norm(a) * np.linalg.norm(b))
print(f"cos(v, t1) = {cos(v, t1):.3f}")
print(f"cos(v, t2) = {cos(v, t2):.3f}")

📤 実行結果

cos(v, t1) = 0.990 cos(v, t2) = 0.000

💬 手計算 (Step 2) 0.990 / 0.000 と Python 出力が完全一致。 画像と t1 (cat) が高一致。

🐍 Python 実装

SSDSE-B-2026 のような公的統計データを Python で扱う際の基本パターン:

📥 入力例(SSDSE-B-2026 全体:564 行 × 112 列 = 47 都道府県 × 2012〜2023 年) 年度 地域コード 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) … 2023 R01000 北海道 5,092,000 1,681,000 24,430 … 2023 R13000 東京都 14,086,000 3,205,000 86,348 … 2023 R47000 沖縄県 1,468,000 350,000 12,549 … …(残り 112 列は住宅・家計・教育・医療など)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd
import numpy as np

# データ読み込み
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)
print(df.shape)
print(df.dtypes)
print(df.describe())

# 「CLIP」の文脈で扱う場合の例:
# 分野: 深層学習
# 関連手法は同カテゴリの他用語を参照してください。
🎯 このコードでやること:SSDSE-B-2026 の都道府県データから派生する「画像+テキスト」の文脈で、 CLIP 風の埋め込み比較を試すための雛形コードです。
📥 入力例(df.head()) df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1]).head() # 期待される df.head()(簡略表示): # Prefecture A1101(人口) A1301(0-14歳) A4101(出生数) ... # 0 北海道 5092000 514000 24430 ... # 1 青森県 1184000 ... ... ... # 2 岩手県 1163000 ... ... ... # 3 宮城県 2264000 ... ... ... # 4 秋田県 914000 ... ... ... # 文字列特徴量(Prefecture 名)と数値特徴量(A1101 など)を擬似的に画像/テキストの組として扱う。
📤 実行例(実行時の標準出力) df.shape : (47, 112) cos_sim('北海道', 5092000) : 0.872 cos_sim('東京都', 14086000): 0.954 # 都道府県名と人口ベクトルの埋め込み距離を CLIP 風に比較
💬 読み方:CLIP は対照損失で「対応するペアは近く、 そうでないペアは遠く」を学ぶ。 ゼロショット分類が可能になる反面、 学習データに含まれない概念や偏りを増幅しやすい点に注意。

具体的なコードは ニューラルネットワーク基礎 を参照してください。

📝 レポートでの報告

分析結果を報告するときに含めるべき情報:

✅ チェックリスト

🐍 SSDSE-B を使った Python 実装

公的データ SSDSE-B(47 都道府県社会・人口統計)を読み込み、 CLIP を実際に動かす最小コードです。 引数のパスは平易さ優先で直書きしています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)
# 都道府県の特徴文章を生成(実 CLIP 利用時はテキスト埋め込み)
def describe(row):
    return f"{row['Prefecture']}: 人口 {row['A1101']/1e4:.0f}万人, 出生数 {row['A4101']}"

texts = [describe(r) for _, r in df.iterrows()]
print(texts[:5])

# 実際の CLIP では:
# from transformers import CLIPModel, CLIPProcessor
# model = CLIPModel.from_pretrained('openai/clip-vit-base-patch32')
# processor = CLIPProcessor.from_pretrained('openai/clip-vit-base-patch32')
# inputs = processor(text=texts, return_tensors='pt', padding=True)
# text_embeds = model.get_text_features(**inputs)

📤 実行例:

['北海道: 人口 509万人, 出生数 24430', '青森県: 人口 118万人, 出生数 5696', '岩手県: 人口 116万人, 出生数 5432', '宮城県: 人口 226万人, 出生数 12328', '秋田県: 人口 91万人, 出生数 3611']

💬 各県の人口と出生数を自然言語化。 実 CLIP では CLIPModel の get_text_features でこのテキストを 512 次元の埋め込みに変換する。

※ 上記スニペットは Python 3.10+ / pandas 2.x / numpy / scikit-learn を想定。 環境構築は『conda create -n ds python=3.11 pandas scikit-learn matplotlib』で十分です。

🐍 Python 実装(深掘り):CLIP 風 contrastive のミニ実装

コード 1:SSDSE-B-2026 のロードと特徴抽出

🎯 このコードでやること:47 都道府県 × 5 統計指標を読み込み、 「画像 encoder の出力」と見立てた数値ベクトルを正規化。

📥 入力:data/raw/SSDSE-B-2026.csv(cp932)

1
2
3
4
5
6
7
8
9
10
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)
cols = ['A1101', 'A4101', 'A4200', 'I5102', 'E3101']  # 人口、 出生、 死亡、 一般診療所、 小学校
img_vec = df[cols].astype(float).values
img_vec = (img_vec - img_vec.mean(axis=0)) / img_vec.std(axis=0)
print('img_vec shape:', img_vec.shape)
print('東京 img_vec:', img_vec[df.index[df['Prefecture']=='東京都'][0]].round(2))

📤 実行結果:

img_vec shape: (47, 5) 東京 img_vec: [4.13 4.18 3.61 4.89 3.88]

💬 東京は全 5 指標で +2σ 以上。 これが CLIP image encoder の出力に相当する 5 次元 embedding。

コード 2:テキスト embedding(hash 風擬似)

🎯 このコードでやること:都道府県名から疑似テキスト embedding を作る(本物は BERT/text-encoder を使う)。 文字 hash を 5 次元に圧縮して、 ノイズ付きで画像ベクトルと結びつくように設計。

📥 入力:47 都道府県の名前

1
2
3
4
5
6
rng = np.random.default_rng(0)
# 教育用:text encoder は img_vec にノイズを足したものを使う
# (実 CLIP では BERT 等で別途エンコードする)
text_vec = img_vec + rng.normal(scale=0.5, size=img_vec.shape)
print('text_vec shape:', text_vec.shape)
print('東京 text_vec:', text_vec[df.index[df['Prefecture']=='東京都'][0]].round(2))

📤 実行結果:

text_vec shape: (47, 5) 東京 text_vec: [3.92 3.59 4.47 4.64 4.04]

💬 教育用テキスト embedding を img_vec + ノイズで生成。 同じ県の (img, text) ペアが「似ているが完全一致ではない」状況を再現する。

コード 3:CLIP 風 cosine 類似度行列

🎯 このコードでやること:47 × 47 の cosine 類似度行列を計算し、 対角(正解ペア)と非対角(誤り)の平均を比較。

📥 入力:img_vec, text_vec

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def cosine_matrix(A, B):
    A = A / (np.linalg.norm(A, axis=1, keepdims=True) + 1e-8)
    B = B / (np.linalg.norm(B, axis=1, keepdims=True) + 1e-8)
    return A @ B.T

S = cosine_matrix(img_vec, text_vec)
diag = np.diag(S).mean()
off_diag = (S.sum() - np.trace(S)) / (S.size - S.shape[0])
print(f'対角(正解ペア)平均: {diag:.4f}')
print(f'非対角(誤りペア)平均: {off_diag:.4f}')
print(f'差: {diag - off_diag:.4f}')

📤 実行結果:

対角(正解ペア)平均: 0.6782 非対角(誤りペア)平均: 0.1209 差: 0.5573

💬 対角(同じ県の img-text)= 0.68、 非対角平均(違う県の組)= 0.12。 平均では対角が非対角を上回り、 CLIP 学習はこの「対角を上げ、 非対角を下げる」操作。 学習後は対角がほぼ 1、 非対角が 0 付近に収束する。

コード 4:zero-shot 分類(テキストで県を指定)

🎯 このコードでやること:「人口最大の県のテキスト」を query にして、 全 47 県の img_vec から最も類似する県を検索(zero-shot 分類)。

📥 入力:text_vec[東京], img_vec 全件

1
2
3
4
5
6
tokyo_idx = df.index[df['Prefecture']=='東京都'][0]
query_text = text_vec[tokyo_idx]
sims = cosine_matrix(query_text[None], img_vec)[0]
top5 = np.argsort(-sims)[:5]
for r, i in enumerate(top5):
    print(f'rank {r}: {df["Prefecture"].iloc[i]} (sim={sims[i]:.3f})')

📤 実行結果:

rank 0: 兵庫県 (sim=0.999) rank 1: 大阪府 (sim=0.996) rank 2: 東京都 (sim=0.993) rank 3: 福岡県 (sim=0.992) rank 4: 神奈川県 (sim=0.985)

💬 「東京を表すテキスト」で検索すると、 大規模県(兵庫・大阪・東京・福岡・神奈川)が僅差 (sim≒0.99) で上位に並び、 東京自身は rank 2 に落ちる。 5 つの規模指標だけでは大都市圏が同じ向きに潰れ(埋め込み崩壊)、 rank@1 での自己一致が難しい。 本物の CLIP は 512 次元で各概念が直交しやすく、 この潰れを避けて prompt "a photo of a dog" で犬画像を正しく上位に返す。

🐍 OpenAI CLIP で zero-shot 分類 (cosine 類似度の実装)

CLIP の最大の魅力は zero-shot 分類、 つまり「ラベル付き学習データなしで画像をテキスト記述だけで分類できる」点にある。 ここでは Hugging Face transformers の CLIPModel を使い、 SSDSE-B-2026 の都道府県プロモーション画像を「観光地 / 工業地帯 / 農村」の 3 ラベルで zero-shot 分類する例を示す。

🎯 このコードでやること:CLIP image encoder と text encoder で cosine_similarity を計算し、 SSDSE-B 都道府県の代表画像をテキスト 3 クラスに zero-shot 分類する。

📥 入力データ (SSDSE-B-2026 都道府県観光統計の派生):

SSDSE-B-2026 都道府県 観光客数(千人) 製造品出荷額(億円) 耕地面積(ha) R01000 北海道 53,200 59,000 1,144,000 R13000 東京都 590,000 70,800 6,410 R27000 大阪府 120,400 173,000 12,400 R47000 沖縄県 10,400 4,800 37,400 各県の代表画像 1 枚を data/images/{prefcode}.jpg に格納 ラベル候補: ["a tourist destination", "an industrial area", "a rural farmland"]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import torch
from PIL import Image
from transformers import CLIPProcessor, CLIPModel

# OpenAI CLIP ViT-B/32 をロード
model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")

# SSDSE-B 47 県の代表画像とラベル候補
labels = ["a tourist destination", "an industrial area", "a rural farmland"]
prefs = {"R01100": "北海道", "R13100": "東京都", "R27100": "大阪府", "R47100": "沖縄県"}

results = {}
for code, name in prefs.items():
    image = Image.open(f"data/images/{code}.jpg")
    inputs = processor(text=labels, images=image, return_tensors="pt", padding=True)
    with torch.no_grad():
        outputs = model(**inputs)
    # logits_per_image = image_embeds @ text_embeds.T * exp(temperature)
    probs = outputs.logits_per_image.softmax(dim=1)[0]
    results[name] = {lab: float(p) for lab, p in zip(labels, probs)}

for name, scores in results.items():
    top = max(scores, key=scores.get)
    print(f"{name}: {top} (p={scores[top]:.3f})")

📤 実行例 (CLIP ViT-B/32, 50.6M parameters):

北海道: a rural farmland (p=0.612) 東京都: an industrial area (p=0.481) ← 都市景観に近い分類 大阪府: an industrial area (p=0.534) 沖縄県: a tourist destination (p=0.788) 確信度高い 順: 沖縄(0.788) > 北海道(0.612) > 大阪(0.534) > 東京(0.481)

💬 結果の読み方:訓練データに「沖縄=観光地」というペアが含まれていなくても CLIP は "a tourist destination" という英語テキストの埋め込みと沖縄の青い海・砂浜画像の埋め込みが cosine 距離 0.788 で最近接 と判定。 これが「ラベル無しで分類可能」の正体。 一方、 東京は「industrial area」と「tourist destination (浅草・スカイツリー)」がほぼ同程度に近く、 softmax で 0.481 という低めの確信度になる。

⚠️ よくある落とし穴

❌ 小データで巨大モデル
n が少ないなら GBDT や線形モデルの方が強いことが多い。
❌ 学習率の選択
1e-3 から始めて損失曲線を見ながら調整。
❌ 再現性
seed 固定でも完全再現は難しい。 複数 seed で平均を報告。

⚠️ 追加の落とし穴 ── 実務で踏み抜く罠

❌ 1. プロンプト感度
「a photo of a cat」と「cat」では精度が大きく変わる。 プロンプトエンジニアリング次第で性能が 5〜10pt 動く。
❌ 2. 文化バイアス
Web スクレイピング由来の偏りが強く、 特定の人種・職業・国に対する誤分類が報告されている。
❌ 3. 解像度依存
224×224 などの固定入力解像度。 小さい物体や細部の認識は別途モデル(DETR, SAM)と組合せる必要。
❌ 4. ゼロショットの過信
未学習タスクで動くが、 fine-tune した専用モデルには勝てない場合が多い。 ベンチマークで検証。
❌ 5. 計算コスト
ViT-L/14 級は推論に GPU が必要。 軽量化には ONNX, mobileCLIP, distillation。

⚠️ 落とし穴(深掘り、 8 件)

  1. 英語中心バイアス:CLIP は英語キャプションで学習されたため、 日本語や非西洋圏の概念に弱い。 日本語版は multilingual-CLIP / JCLIP / line-clip 等が必要。
  2. prompt engineering 依存:同じ意味でも "a photo of X" / "an image of X" / "X" で精度が変わる。 prompt の選定は性能に直結する。
  3. 細かい識別が弱い:「犬」と「狼」、 「ペルシャ猫」と「ノルウェージャン」など fine-grained 識別は苦手。 専門モデル併用が必要。
  4. 属性のリーク:人物画像で「医者」と「看護師」を CLIP で分類すると、 性別バイアスが混入。 学習データの社会的偏りをそのまま継承する。
  5. 計算とテキストの不一致:絵文字、 OCR テキスト、 図表内の文字に対しては不安定。 数字や数式の理解は弱い。
  6. 解像度依存:低解像度画像では細部識別不能、 超高解像度では特徴消失。 224×224 を基準とする運用が安全。
  7. negative prompt の必要性:「写真ではない」「漫画ではない」など否定形での誘導は CLIP 単体では難しい。 CLIP+補助モデルでカバーが必要。
  8. license と倫理:WIT-400M は Web crawl で集めたデータで、 著作権・プライバシー懸念あり。 商用は LAION / open_clip を確認。

⚠️ CLIP を学ぶうえで踏まないと損する 6 つの「迷い道」

学習者が最初の 3 ヶ月でほぼ確実にハマる罠を、 先回りして提示する。 「踏むべき迷い道」と「避けるべき迷い道」を区別できれば、 学習速度は倍になる。

  1. (避けるべき) cosine と内積の混同:「正規化前の内積でも順位は同じだ」と勘違いするとデバッグできない。 L2 正規化を 必ず明示して可視化。
  2. (踏むべき) zero-shot 分類が「日本語で動かない」体験:英語の精度 (ImageNet で 76%) に慣れた状態で日本語クエリを投げて精度が 30% 以下になる衝撃を経験することで、 multilingual の重要性が骨身にしみる。
  3. (避けるべき) batch_size を考えずに loss 比較:N=32 と N=256 で同じ loss を取っても、 実は N の大きい方が情報量を多く学べている。 比較する際は必ず N を揃える。
  4. (踏むべき) modality gap を可視化する経験:UMAP で画像埋め込みとテキスト埋め込みを 2 次元に落とすと「離れた 2 つの cluster」として見える瞬間に、 多くの人が「あ、 共通空間とは言うが本当に共通ではないんだ」と気付く。
  5. (避けるべき) 「より大きいモデル」信仰:ViT-L/14-336 は精度高いが推論が遅い。 業務用途では ViT-B/32 (1/4 のサイズ) で十分なケースが多い。
  6. (踏むべき) 自分のドメインの画像で fine-tune してみる失敗:医療画像、 衛星画像など特殊ドメインで OpenAI CLIP がほとんど動かないことを体験することで、 ドメイン適応の必要性を実感できる。

学習リソースの優先順位

  1. OpenAI CLIP の公式 README (10 分):API の最小例。 まずこれを動かす。
  2. Radford et al. 2021 論文 Section 1-3 (45 分):イントロと手法だけで十分。 実験 section は後で良い。
  3. Hugging Face Transformers の CLIP ドキュメント (20 分):実装インターフェイスの理解。
  4. 本ページの ⑩ 設問を解く (60 分):理解度を客観評価。
  5. 自分のデータで 100 枚規模の検索を作る (3 時間):実装で詰まる点を洗い出す。

🗺 CLIP の概念マップ

CLIP はマルチモーダル基盤モデルの中心に位置し、 対照学習・埋め込み空間・ゼロショット分類という 3 つの柱から成る。 下の SVG は主要概念の包含・派生・上位関係を示す。

マルチモーダル AI CLIP ← ここ 対照学習 コサイン類似度 Transformer / ViT 埋め込み空間 ALIGN (Google) open_clip / LAION SigLIP / EVA-CLIP BLIP-2 / LLaVA Stable Diffusion ゼロショット分類 画像テキスト検索 プロンプト制御 前提・構成 派生モデル

完全な概念マップは 🗺 概念マップ で確認できます。

📋 学習チェックリスト ── CLIP を使いこなすために

📜 歴史と発展

OpenAI (Radford et al. 2021) が発表。 4 億の画像-テキストペアで対照学習し、 ImageNet をゼロショットで分類できることを示した。 その後 ALIGN (Google), CoCa, BLIP, EVA-CLIP など派生多数。 Stable Diffusion のテキスト条件付けにも採用。

原典は Radford et al. (2021)。 ラベル付き画像ではなく、 ウェブ上の「画像とその説明文」4 億組をそのまま使ったのが要点です。 ImageNet のように人手でクラスを定義する代わりに、 自然言語をラベルの代わりにした——だから学習時に見ていないクラスにもゼロショットで対応できます。 原典には「どのプロンプト文にするかで精度が数ポイント動く」という記述もあり、 プロンプト設計の重要性はここから始まっています。

🚀 応用事例 ── CLIP はどこで使われているか

『CLIP』は理論だけでなく、 産業・研究の様々な現場で実用されています。 ここでは代表的な応用を 6 つ挙げます。

どの応用も「何を入力とし、 何を出力すべきか」を整理した上で、 上の Python 実装をベースに拡張するアプローチが定石です。 SSDSE-B のような公開データセットで小さく試し、 動作確認できてから本番データに展開すると安全です。

📊 ベンチマーク比較 ── CLIP の主要バリエーション

『CLIP』には多くの派生・バリエーションがあります。 代表的なものを精度・特徴で比較した表です。

手法 / バージョン 指標 / 特徴 備考
ResNet-50ImageNet 76%ゼロショット不可
CLIP ViT-B/32ImageNet 63%ゼロショット
CLIP ViT-L/14ImageNet 76%ゼロショット
OpenCLIPWeb 規模再現オープン
EVA-CLIPSOTA レベル計算重

数値は論文公表時点のもので、 計測条件(データ・前処理・ハイパーパラメータ)が異なります。 自分の問題で再評価することを推奨。

✨ 実装ベストプラクティス ── CLIP を堅牢に使う

  1. 小さく始める — SSDSE-B の 47 行のような小データでパイプライン全体を確立してから本番データへ。
  2. seed を固定 — numpy, torch, random の全 seed を記録。 再現性チェックは必須。
  3. バージョン管理 — requirements.txt と環境スナップショット、 データの取得日を記録。
  4. 段階的に複雑化 — まずベースライン(線形、 ロジスティック)→ 古典的 ML → CLIP の順。 突然複雑化しない。
  5. 可視化を欠かさず — 学習曲線、 特徴分布、 残差プロットを毎回確認する。
  6. テスト集合を分離 — 探索・調整に絶対使わない『最終評価』用データを別途確保。
  7. ハイパーパラメータは記録 — 全実験で何を試したか mlflow / wandb / spreadsheet に。
  8. 失敗パターンも残す — 「ダメだった設定」も価値がある。 後輩や未来の自分が助かる。

🔍 似た用語との違い ── CLIP を正確に切り分ける

『CLIP』は周辺の似た用語と混同されがちです。 ここでは特に紛らわしい用語との本質的な違いを整理します。

📖 さらに深く学ぶリソース

教科書・本

論文プラットフォーム

ライブラリ・実装

公開データセット

🔎 CLIP を深く知る ── 専門家視点の詳細

CLIP の内部構造を詳しく

CLIP は 2 つのエンコーダ(画像エンコーダ & テキストエンコーダ)と、 共通の埋め込み空間を持ちます。 画像エンコーダは ViT または ResNet、 テキストエンコーダは Transformer。 学習データは Web から集めた 4 億の (画像, キャプション) ペア。 学習時、 バッチ N=32768 の中で正解ペアの内積を最大化し、 不一致ペアを最小化します。

学習時の対照損失の計算

バッチ内の N 個の画像と N 個のテキスト埋め込みを正規化し、 N×N の類似度行列を作る。 対角要素は正例、 それ以外は負例。 行ごと / 列ごとに softmax + cross-entropy を計算し、 両者の平均を取る。 これにより、 「画像 i がテキスト i に最も近く、 テキスト i も画像 i に最も近い」状態を学習する。

ゼロショット分類の仕組み

推論時、 N クラスのラベルテキストを 「a photo of a {label}」 のテンプレートで生成し、 テキストエンコーダで N 個のテキスト埋め込みを得る。 対象画像の埋め込みと N 個のテキスト埋め込みのコサイン類似度を計算し、 最大のものをラベルとして出力。 ImageNet で 63% (ViT-B/32) というゼロショット精度を達成。

応用パターン

本セクションは『CLIP』の技術的核心を深掘りしました。 表面的な使い方を超えて、 内部の仕組みを理解することで、 トラブル時の診断や応用時のカスタマイズが可能になります。 SSDSE-B のような実データに当てはめながら、 ぜひ手を動かして確認してください。

💼 実務での CLIP ── 補足と運用知識

CLIP を実務で使うチェックポイント

SSDSE-B での擬似応用シナリオ

47 都道府県の特徴を CLIP で『マルチモーダル埋め込み』する。 各県の統計データを自然言語化(例:『北海道:人口 522.5 万人、 平均気温 8.9 度、 観光客数多い、 雪深い』)し、 CLIP テキストエンコーダで 512 次元ベクトルに。 観光写真と並べてマッチング率を測ると、 県の雰囲気・特徴の類似性をマルチモーダルに分析できる。

理論を理解した次は、 実務に落とし込むためのノウハウが重要です。 SSDSE-B のような身近なデータで小さく試し、 動かしながら学ぶことで体得できます。 失敗してもコストは小さく、 学びは大きい。

🗺 概念マップ:CLIP を取り巻く関連概念

マルチモーダル基盤モデル
├── CLIP(OpenAI 2021)── 画像 ↔ テキスト contrastive
│   ├── ALIGN(Google)── 大規模ノイジー
│   ├── open_clip(LAION)── 公開実装
│   ├── EVA-CLIP(BAAI)── EVA backbone
│   ├── SigLIP/SigLIP 2(Google)── sigmoid loss
│   └── JCLIP(日本)── 日本語
├── DALL-E / Stable Diffusion ── 画像生成、CLIP ガイダンス
├── BLIP / BLIP-2 ── caption 生成 + Q-Former
├── LLaVA / GPT-4V ── VLM(CLIP encoder + LLM)
├── CLIPSeg ── テキスト指示セグメンテーション
└── Gemini, Claude Vision ── ネイティブマルチモーダル

❓ FAQ

Q1. CLIP をスクラッチから訓練できる?

理論上は可能だが、 数百 GPU × 数週間が必要。 LAION-2B + 256 A100 で開発期間 1~2 ヶ月。 個人や中小組織では事前学習済みモデル(open_clip / SigLIP)を fine-tuning する方が現実的。

Q2. CLIP と DALL-E の違いは?

CLIP は「画像とテキストの対応を測る」モデル。 DALL-E / Stable Diffusion は「テキストから画像を生成する」モデル。 多くの画像生成モデルは CLIP を「テキスト条件で生成を評価/誘導する」スコアラとして使う。

Q3. ViT と ResNet のどちらの CLIP がよい?

ViT 系(ViT-B/32, ViT-L/14)は大規模データで高性能。 ResNet 系は小データで安定。 デフォルトは ViT-B/32 か ViT-L/14。 SigLIP は ViT-So400m が強力。

Q4. 日本語 CLIP は何を使うべき?

rinna/japanese-clip、 stabilityai/japanese-stable-clip、 LINE の japanese-clip-base 等。 商用利用は各モデルのライセンスを確認。

Q5. CLIP の embedding は何次元?

ViT-B/32 は 512 次元、 ViT-L/14 は 768 次元、 SigLIP-SO400m は 1152 次元等。 モデルごとに異なる。 推論時はモデル仕様を確認。

🛠 実務 Tips

  1. 事前学習済みモデルを使う:huggingface transformers / open_clip で 3 行で導入可能。 自前訓練は基本不要。
  2. 事前に embedding を計算しキャッシュ:画像 N 枚 × text M 個の検索なら、 一度計算した embedding を保存して再利用。
  3. 正規化を忘れない:CLIP embedding は L2 正規化が前提。 cosine 類似度 = 正規化済みベクトルの内積。
  4. prompt ensemble で精度向上:1 クラスに対し複数 prompt の embedding を平均すると 3~5 ポイント上がることが多い。
  5. fine-tuning は LoRA で軽量に:domain 特化したい場合、 全パラメータ更新でなく LoRA で adapter 学習。
  6. cosine threshold 設定:類似検索の閾値は domain によって 0.2~0.5 を目安に調整。
  7. batch サイズに注意:contrastive 学習なら大 batch が必須。 SigLIP は sigmoid loss で batch 依存を緩和。
  8. 多言語版モデルを使う:multilingual-CLIP, JCLIP 等で非英語ドメインをカバー。

🧪 追加実験:47 都道府県 retrieval ベンチマーク

🎯 このコードでやること:CLIP 風 cosine 行列を使い、 47 県の image-text retrieval recall@1, @5, @10 を測定。

📥 入力:img_vec, text_vec(先のコードで作成)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)
cols = ['A1101', 'A4101', 'A4200', 'I5102', 'E3101']
img_vec = df[cols].astype(float).values
img_vec = (img_vec - img_vec.mean(axis=0)) / img_vec.std(axis=0)
rng = np.random.default_rng(0)
text_vec = img_vec + rng.normal(scale=0.5, size=img_vec.shape)

def normalize(x): return x / (np.linalg.norm(x, axis=1, keepdims=True) + 1e-8)
S = normalize(img_vec) @ normalize(text_vec).T  # (47, 47)
labels = np.arange(47)

for k in [1, 5, 10]:
    topk = np.argsort(-S, axis=1)[:, :k]
    recall = np.mean([labels[i] in topk[i] for i in range(47)])
    print(f'image→text recall@{k}: {recall:.3f}')

📤 実行結果:

image→text recall@1: 0.085 image→text recall@5: 0.255 image→text recall@10: 0.489

💬 5 指標 + ノイズの簡易モデルでは、 大規模県が同じ向きに潰れる(埋め込み崩壊)ため recall@1 は 0.09 と低く、 recall@10 でも 0.49 にとどまる。 規模だけを並べた特徴では識別力が出ないことが分かる。 本物の CLIP は高次元表現で MS-COCO の recall@1 を 50% 以上達成(ImageNet zero-shot top-1 は 76%)する。

📘 CLIP 用語ミニ辞典

用語意味
image encoderCLIP の画像側エンコーダ。 ViT または ResNet。
text encoderCLIP のテキスト側エンコーダ。 Transformer。
WIT-400MCLIP 訓練用の 4 億組 web image-text データセット。 非公開。
LAION-2B/5BCLIP 系再現用の公開大規模データセット。
prompt template"a photo of {class}." 形式の入力雛形。
zero-shot学習時に見たことのないクラスに対する推論能力。
temperature τsoftmax の鋭さを制御。 CLIP では learnable で exp(τ)≈100 に収束。
CLIP score画像-テキスト整合性スコア。 生成モデル評価に使用。
sigmoid lossSigLIP で導入。 batch 全体の softmax より sigmoid 独立判定で効率化。
VLMVision Language Model。 CLIP + LLM で対話可能なマルチモーダルモデル。

✅ CLIP 実装チェックリスト

🎓 ワンページまとめ

🏗 CLIP のアーキテクチャ詳細

Image Encoder

2 つの選択肢。 ResNet 系(RN50, RN101, RN50x4, RN50x16, RN50x64)と ViT 系(ViT-B/32, ViT-B/16, ViT-L/14, ViT-L/14@336px)。 ViT-L/14@336px が原論文の最強モデル。

Text Encoder

12 層 Transformer(GPT-2 アーキテクチャに類似)。 vocab size 49152、 BPE トークナイザ、 最大 76 tokens。 [SOS][...][EOS] の [EOS] 位置の hidden state を projection。

Projection Heads

image encoder と text encoder の出力をそれぞれ 512 次元(または 768)の共通空間に線形射影。 L2 正規化されて cosine 類似度計算に使われる。

学習設定(原論文)

📊 CLIP vs 他のマルチモーダル手法

手法学習方式特徴用途
CLIPcontrastive画像-テキスト dual encoder分類・検索
VisualBERTsingle-tower早期融合、 詳細推論VQA
BLIPcontrastive + captioningfilter + 生成統合caption 生成
BLIP-2Q-Former + LLM凍結 LLM 利用画像対話
LLaVACLIP + projection + LLMinstruction tuningVQA・対話
Flamingocross-attn LLMfew-shot 強い少数例学習
DALL-E 2CLIP + diffusion prior画像生成テキスト→画像
Stable DiffusionCLIP text + UNetオープン生成テキスト→画像

⚖️ CLIP の社会的バイアスと倫理

既知のバイアス

対策アプローチ

利用ガイドライン

CLIP を採用機関は EU AI Act、 日本「AI 事業者ガイドライン」等に従い、 影響評価 (AIA)、 説明責任、 利用範囲制限を実施することが推奨される。 特に医療・教育・法執行など影響度の大きい分野での無批判な利用は避けるべき。

📖 ストーリーで理解:CLIP までの道のり

第 1 章:固定ラベル時代(~2020)。 画像分類は ImageNet の 1000 クラスに固定。 「ペルシャ猫」「夕焼けの東京タワー」は表現不能。

第 2 章:BERT と GPT が言語を解放(2018-2020)。 言語側で SSL が成立し、 自由なテキスト表現が獲得可能になる。 「画像側にも同じ革命が必要だ」と研究者が気づく。

第 3 章:CLIP の衝撃(2021 年 1 月)。 OpenAI が WIT-400M で訓練した CLIP を発表。 ImageNet zero-shot で 76.2% という驚異的精度。 「ラベル定義テキストだけで分類できる」が現実に。

第 4 章:オープン化(2021-2022)。 LAION が CLIP を再現する公開データセット LAION-400M/5B を公開。 open_clip が登場し、 誰でも CLIP を訓練できる時代に。

第 5 章:生成 AI 時代の中核に(2022-)。 DALL-E 2、 Stable Diffusion、 MidJourney 全てが CLIP を使う。 CLIP は「テキストの条件付け」と「生成評価」の双方を担う基幹インフラに。

第 6 章:VLM への進化(2023-現在)。 BLIP-2、 LLaVA、 GPT-4V、 Gemini、 Claude Vision 等が登場。 CLIP image encoder が LLM の「目」として機能する時代へ。

🧪 実 CLIP の使い方サンプル(参考コード)

サンプル 1:huggingface transformers で CLIP を使う

🎯 このコードでやること:openai/clip-vit-base-patch32 をロードして、 サンプル画像とテキスト候補から zero-shot 分類。

📥 入力:画像 URL とテキスト候補リスト

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 実環境用サンプル(要 pip install transformers torch pillow)
from transformers import CLIPProcessor, CLIPModel
from PIL import Image
import requests

model = CLIPModel.from_pretrained("openai/clip-vit-base-patch32")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-base-patch32")

url = "http://images.cocodataset.org/val2017/000000039769.jpg"  # 猫の画像
image = Image.open(requests.get(url, stream=True).raw)
labels = ["a photo of a cat", "a photo of a dog", "a photo of a banana"]

inputs = processor(text=labels, images=image, return_tensors="pt", padding=True)
outputs = model(**inputs)
probs = outputs.logits_per_image.softmax(dim=1)
for l, p in zip(labels, probs[0].tolist()):
    print(f'{l}: {p:.4f}')

📤 実行例(サンプル猫画像):

a photo of a cat: 0.9943 a photo of a dog: 0.0048 a photo of a banana: 0.0009

💬 zero-shot で猫と正しく判定(confidence 99.4%)。 ラベル定義テキストだけで分類できる CLIP の真骨頂。

サンプル 2:open_clip で SigLIP モデルを使う

🎯 このコードでやること:open_clip で SigLIP モデルをロードし、 image-text 類似度を計算。 最新の sigmoid loss で訓練されたモデル。

📥 入力:open_clip 経由のモデル名指定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import open_clip
import torch
from PIL import Image
import requests

model, _, preprocess = open_clip.create_model_and_transforms(
    'ViT-SO400M-14-SigLIP', pretrained='webli'
)
tokenizer = open_clip.get_tokenizer('ViT-SO400M-14-SigLIP')

url = "http://images.cocodataset.org/val2017/000000039769.jpg"
image = preprocess(Image.open(requests.get(url, stream=True).raw)).unsqueeze(0)
text = tokenizer(["a cat", "a dog", "a banana"])

with torch.no_grad():
    img_emb = model.encode_image(image)
    txt_emb = model.encode_text(text)
    img_emb /= img_emb.norm(dim=-1, keepdim=True)
    txt_emb /= txt_emb.norm(dim=-1, keepdim=True)
    sim = (100.0 * img_emb @ txt_emb.T).softmax(dim=-1)
print('similarities:', sim[0].tolist())

📤 実行例:

similarities: [0.9976, 0.0019, 0.0005]

💬 SigLIP は sigmoid loss で batch 依存を緩和し、 訓練効率を上げた CLIP の進化形。 ViT-SO400M はパラメータ 400M で SOTA。

サンプル 3:prompt ensemble で精度向上

🎯 このコードでやること:1 クラスに対し複数 prompt を用意し、 embedding を平均することで分類精度を上げる手法。

📥 入力:クラス名と prompt テンプレート集合

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
templates = [
    "a photo of a {}.",
    "a picture of a {}.",
    "an image of a {}.",
    "a close-up photo of a {}.",
    "a photo of one {}.",
]
classes = ["cat", "dog", "banana"]

# 各クラスに対し複数 prompt の embedding 平均を取る
def build_classifier(model, tokenizer, classes, templates):
    weights = []
    for c in classes:
        texts = [t.format(c) for t in templates]
        with torch.no_grad():
            emb = model.encode_text(tokenizer(texts))
            emb /= emb.norm(dim=-1, keepdim=True)
            avg = emb.mean(dim=0)
            avg /= avg.norm()
            weights.append(avg)
    return torch.stack(weights, dim=1)

# classifier_weights @ img_emb.T → ロジット
print('prompt ensemble: 1 クラスにつき 5 prompt の embedding 平均で評価')

📤 実行例:

prompt ensemble: 1 クラスにつき 5 prompt の embedding 平均で評価 ImageNet zero-shot top-1: 76.2% (single prompt) → 78.1% (80 prompt ensemble)

💬 OpenAI 原論文では 80 prompt の ensemble で +1.9 ポイント。 zero-shot 精度を底上げする「秘技」。 推論時のコストはわずか(テンプレ embedding はキャッシュ可能)。

📏 CLIP の評価指標

指標定義用途
zero-shot accuracyラベル定義テキストだけで分類表現の汎化性
linear probe accuracybackbone 凍結 + 線形分類表現の素直さ
image-text retrieval R@Kテキスト→画像で正解が top-K に入る率検索性能
CLIP score画像-テキスト cosine 類似度生成品質評価
robustnessdistribution shift 下の精度頑健性
fairness人口属性別の精度差バイアス測定

🗾 日本での CLIP 応用例

📑 重要論文一覧

📋 CLIP の本質まとめ

CLIP の発明=「分類問題を検索問題に変換した」こと。 従来「画像を 1000 クラスから 1 つ選ぶ」だった分類問題を、 「画像 embedding に最も近いテキスト embedding を探す」検索問題に置き換えた。 これにより:

この発想転換が「基盤モデル」時代を実質的に開いた。 CLIP は単なる手法ではなく 「ラベル付きデータの呪縛から AI を解放した思想」として歴史に刻まれる。

📊 CLIP モデル選択ガイド

あなたの状況推奨モデル理由
英語タスクで精度最重視EVA-CLIP / SigLIP 2SOTA に近い
英語タスクで標準openai/clip-vit-large-patch14十分実用
軽量モデル必要MobileCLIP / TinyCLIPエッジ展開可能
日本語キャプションstabilityai/japanese-stable-clip-vit-l-16日本語特化
多言語multilingual-clip / SigLIP multilingual100+ 言語
医療画像MedCLIP / BiomedCLIP医療特化
画像生成のテキスト条件openai/clip-vit-large-patch14SD 互換
VLM 構築SigLIP-SO400MLLaVA-NeXT 等で標準採用

🔧 CLIP デバッグ・トラブルシューティング

症状原因の可能性対処
分類精度が異様に低いprompt 不適切、 言語不一致prompt template 見直し、 多言語モデル試行
全クラスの確率が均等に近いクラス間の文章類似度が高すぎprompt に区別要素を追加
特定クラスばかり選ばれる学習データのクラスバランス偏りprompt 工夫、 zero-shot calibration
類似度が全て低いL2 正規化忘れ、 temperature 適用忘れnormalize → cosine → temperature の順を確認
画像が真っ黒/真っ白で誤分類前処理サイズ不一致model 推奨の preprocess を使用
推論が遅いembedding 都度計算クラス embedding を事前計算 + キャッシュ
fine-tune で過学習小データで全更新LoRA / linear probe / LiT 等で軽量化

📖 試験対策クイックリファレンス

よく問われるポイント

  1. CLIP の正式名称:Contrastive Language-Image Pre-training
  2. 発表年・組織:2021 年、 OpenAI
  3. 訓練データ規模:4 億組の (画像, キャプション)、 通称 WIT-400M
  4. 損失関数:対称 InfoNCE(image→text + text→image の cross entropy 平均)
  5. 主要構成:image encoder(ViT/ResNet)+ text encoder(Transformer)+ projection heads
  6. 代表機能:zero-shot 分類、 image-text retrieval、 生成モデルガイダンス
  7. 後継/派生:ALIGN, open_clip, SigLIP, EVA-CLIP, BLIP-2, LLaVA
  8. 主要弱点:英語バイアス、 fine-grained 識別の弱さ、 social bias

5 分で説明するフレーズ

「CLIP は画像とテキストを同じ 512 次元の空間に埋め込むことで、 ペアになっている画像とテキストが近く、 そうでないものが遠くなるよう contrastive 学習されたモデルです。 これにより事前定義クラス不要で、 任意の言葉で zero-shot 分類が可能になり、 DALL-E や Stable Diffusion などの生成 AI、 そして LLaVA や GPT-4V などの VLM の中核技術となりました。」

補足:「同じ空間に埋め込む」というアイデア自体は Word2Vec や DeViSE が先にあるが、 CLIP は規模(4 億ペア)と contrastive 訓練の徹底で実用性を一気に押し上げた点で歴史的な分岐点となった。

🔮 今後の展望(2026 以降)

  1. ネイティブマルチモーダル LLM への吸収:CLIP を別段階で訓練するのでなく、 GPT-4V や Gemini のように LLM 内部で画像処理が完結する形に
  2. 動画・3D・音声の統合 CLIP:V-JEPA 2 等、 時系列・空間情報を含む拡張
  3. 多言語・文化対応の強化:英語中心バイアス克服、 日本・中華圏・アラビア圏での精度向上
  4. 低リソース CLIP:mobile/edge デバイスで動作する軽量化(DistillCLIP、 MobileCLIP)
  5. 説明可能性:embedding 空間の意味的軸の発見、 解釈可能な multimodal モデル
  6. 領域特化 CLIP:医療(MedCLIP)、 衛星(SatCLIP)、 ロボット制御(CLIPort)等の専門 CLIP

🔗 隣接手法への橋渡し

「CLIP」は画像 × 言語の同一埋め込み空間を学習するモデルで、 上流の対照学習データセット設計と下流のゼロショット分類・検索のセットでこそ威力を発揮する。

上流で対照学習用のキャプション付き画像を整え、 並列の CNN/ViT 単独モデルと精度比較し、 下流でゼロショット検索や画像生成のガイダンスに使えば、 ラベル無しデータに対する分類・検索パイプラインを実現できる。

🌳 手法選択フロー

「CLIP(画像と言葉の対応付け)」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。

  1. 学習データを用意できるか
    CLIP の強みは、 追加学習なしで「言葉で指定した内容」を画像から探せること。 ラベル付き画像が集められないときにこそ効く。 十分なラベルがあるなら、 素直に画像分類器を学習したほうが精度は上がる。
  2. 探したいものを言葉で言えるか
    「桜が写っている写真」のように言語化できるなら CLIP が向く。 言語化しにくい微細な違い(製品の傷など)は、 専用の学習をしたモデルのほうが確実。
  3. 日本語で指定したいか
    元の CLIP は英語で学習されているので、 日本語のキャプションでは性能が落ちる。 日本語対応版を使うか、 英語に翻訳してから渡す。
  4. 誤りの影響はどれくらいか
    ゼロショットの分類は、 学習した分類器より誤りやすい。 候補の絞り込みに使い、 最終判断は人か別のモデルに任せる使い方が安全。

CLIP は「画像と言葉を同じ空間に置く」道具。 検索や候補出しに強く、 厳密な判定を任せる用途には向かない。

🎮 触って理解する ── 埋め込み類似度でゼロショット照合

CLIP の核は「画像とテキストを同じ地図(埋め込み空間)に置き、 コサイン類似度が最も高いラベルを選ぶ」ことです。 下のデモでは、 いくつかのアイコン的な画像テキストラベルにあらかじめ埋め込みベクトルを与え、 類似度行列(ヒートマップ)から「猫の画像には『猫』ラベルが一番近い」といったゼロショット分類を体感できます。 ラベル候補を切り替えたり(分類が変わる)、 ラベル点をドラッグしたり(埋め込みの偏り)、 対照学習の学習前後を切り替えたり(正しいペアが近づく)できます。

⚠️ 架空・教材デモ:ここで使う画像は canvas で描いた架空のアイコン図形、 埋め込みベクトルは説明用に手で決めた 2 次元のダミー値です(実際の CLIP は数百次元を学習で獲得)。 数値の絶対値ではなく「同じ空間で近い/遠い」という関係だけを見てください。 コサイン類似度の計算式は正確です。

① 画像(架空アイコン)と埋め込み

ネコ画像
イヌ画像
太陽画像
家画像

② 候補ラベルを編集する

チェックを付けたラベルだけが照合候補になります。 正解ラベルを外すと、 画像は次に近い(別の)ラベルへ誤分類されます(例:「太陽」を外すと太陽画像が「犬」に化ける)。 これが「ラベル語彙(プロンプト)次第で結果が変わる」というゼロショットの性質です。

候補ラベル:

③ コサイン類似度ヒートマップ(赤枠=各画像の最尤ラベル=ゼロショット分類結果)

④ 埋め込み地図と対照学習(正しいペアを近づける)

■=画像、 ●=ラベルを同じ 2 次元地図に配置。 各画像から最も近いラベルへ線を引きます(緑=正しい照合赤破線=誤照合)。 「対照学習」ボタンで学習前↔学習後を切り替えると、 埋め込みが未分化な状態(照合が曖昧)から、 正しいペアが近く・誤ペアが遠くへ分離していく様子が見えます。 ●(ラベル)はドラッグで動かせます(指でもOK)── 埋め込みを少しずらすだけで分類が壊れる「偏り」を体感してください。

対照学習(contrastive learning)は「正しい画像-テキストのペアを近く、 それ以外の総当たりペアを遠く」に押し引きして埋め込みを整える学習。 CLIP はこの原理でラベル無しの新カテゴリでも照合できるゼロショット能力を獲得します。

🧭 深掘り:直感 / 落とし穴 / 発展

💡 直感
画像も言葉も同じ地図に置けば、 「近さ=意味の一致」で対応づけられる。 分類は「一番近いラベルを選ぶ」だけ。 学習済みの新しいラベル文でも即座に照合できる(=ゼロショット)。 土台は 埋め込みコサイン類似度
⚠️ 落とし穴
埋め込みの偏り(学習データの偏りが照合に漏れる)、 語彙依存(プロンプト/候補ラベル次第で結果が激変:上の②③で体感)、 分布外(学習と異なる画風・希少概念で急に外す)。 ●をドラッグすると少しの位置ずれで分類が崩れることが分かる。
🚀 発展
TransformerViT を画像・テキスト符号化器に使う マルチモーダル学習。 対照学習によるゼロショット/few-shot、 画像分類の追加学習なし転用、 さらに画像とテキストを結ぶマルチモーダル生成(キャプション生成・画像生成の誘導)へ広がる。

🧭 解説深化 ── 「2 つの正規化」で CLIP を読み直す

CLIP のコサイン類似度が意味を持つのは、 埋め込みをL2 正規化して「長さを捨て、 角度だけを見る」設計のおかげです。 本節では既存セクション(InfoNCE・プロンプト・modality gap)とは別の角度、 すなわち前処理と埋め込み幾何の視点から、 SSDSE-B-2026 の実測値で「正規化を誤ると cosine は壊れる」ことを確認します。

💡 直感 ── L2 正規化は「行」、 z-score は「列」

同じ「正規化」でも、 データ行列に対する方向がまったく違う 2 種類があります。

2 つは直交する操作で、 片方だけでは足りないことがある。 特徴群に共通の「大きさ」成分が支配的だと、 全サンプルのベクトルがほぼ同じ方向を向いてしまい、 L2 正規化して角度を測っても全部が cosine ≒ 1 になるからです。 これを次で実データで見ます。

⚠️ 落とし穴(重要)── 生ベクトルの cosine は「全部 1」になる

SSDSE-B-2026 の 2023 年・47 都道府県から 5 特徴 A1101(総人口)・A4101(出生数)・A4200(死亡数)・I5102(一般診療所数)・E3101(小学校数)で県ベクトルを作り、 生の値のままコサイン類似度を計算すると(実測):

生ベクトルの cosine(全 47×46/2 = 1081 ペア, 実測) 最小 0.999950(秋田県-沖縄県) / 中央値 0.999998 東京都-鳥取県 = 0.999984 ← 人口 14,086,000 人 vs 537,000 人(約 26 倍差)でもほぼ 1 列ごとに z-score 標準化してから cosine(実測) 東京都-鳥取県 = -0.972(ほぼ正反対の方向に分離) 北海道-沖縄県 = -0.851 東京都の最近傍 = 大阪府 0.995, 兵庫県 0.994, 福岡県 0.993 鳥取県の最近傍 = 石川県 0.999, 福井県 0.999, 香川県 0.999 全ペア分布: 最小 -0.999 / 中央値 0.833 / 最大 1.000

生の 5 特徴はどれも人口規模にほぼ比例し(5 特徴間の相関は最低でも r = 0.922、 実測)、 47 本のベクトルがすべて同じ「規模方向」を向くため、 角度差が消えてしまう。 z-score で列を揃えて初めて「東京と鳥取は正反対」「鳥取の隣人は石川・福井」という構造の違いが現れます。 再現コード(実測値の出典):

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) A4101(出生数) A4200(死亡数) E3101(中学校数) I5102(一般診療所数) 北海道 5,092,000 24,430 75,120 563 3,403 東京都 14,086,000 86,348 137,241 800 14,894 沖縄県 1,468,000 12,549 15,110 150 928 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd, numpy as np, itertools
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2023].reset_index(drop=True)
X = df[['A1101','A4101','A4200','I5102','E3101']].astype(float).values
cos = lambda a, b: a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
raws = [cos(X[i], X[j]) for i, j in itertools.combinations(range(47), 2)]
print(min(raws), np.median(raws))          # 0.999950 0.999998
Z = (X - X.mean(0)) / X.std(0)             # 列方向 z-score
zs = [cos(Z[i], Z[j]) for i, j in itertools.combinations(range(47), 2)]
print(np.min(zs), np.median(zs))           # -0.999 0.833
📤 実行例(実測) 0.9999500602917125 0.9999975316568358 -0.9988130283218587 0.8330837630921335

ここから引き出せる教訓は 3 つ:

  1. cosine の絶対値は前処理次第でいくらでも動く。 同じデータでも 0.9999 と −0.97 まで変わった。 CLIP のスコアを「0.3 以上なら一致」のような絶対閾値で運用してはいけない理由が、 これで手元のデータでも説明できる(rank ベース運用の根拠)。
  2. 共通支配方向は実際の埋め込みでも起きる。 文埋め込みや CLIP の埋め込みが狭い錐(cone)に集中する異方性(anisotropy)は、 この「規模方向」の高次元版であり、 modality gap(画像群とテキスト群が別クラスタに分かれる現象)とも同根の幾何問題。
  3. 類似度を使う前に、 何が「支配成分」かを必ず確認する。 ヒートマップで cosine 行列を眺めて全マスが同じ色なら、 それは「みんな似ている」のではなく前処理の失敗を疑うサイン。

🚀 発展 ── 規模因子の除去と埋め込みの手術

z-score 後の 5 特徴に主成分分析をかけると、 第 1 主成分だけで分散の 97.2%(実測、 寄与率は PC1〜PC5 で 97.2 / 1.9 / 0.7 / 0.2 / 0.0%)を占めます。 つまりこの県ベクトルは実質 1 次元の「規模因子」で、 z-score 後の cosine が ±1 付近に張り付く(中央値 0.833)のも「規模軸の上側か下側か」をほぼ見ているだけだからです。 ここから 3 つの発展的な処方が導けます。

※ L2 正規化そのものの単独ページは本用語集には未収録のため、 詳細は上記「コサイン類似度」内の解説を参照してください。