この用語『CLIP』を理解するうえで併せて押さえたい関連キーワード群です。 クリック(ホバー)で関連用語ページに飛べます。
CLIP(Contrastive Language-Image Pre-training)は 2021 年 OpenAI が発表した 画像とテキストを同じ embedding 空間に埋め込むマルチモーダル基盤モデル。 4 億組の (画像, キャプション) ペアを Web から集め、 contrastive learning で「ペアになっている画像とテキストは embedding 空間で近く、 そうでないものは遠く」を学習。 結果、 zero-shot 分類(学習時に見たことのないクラスでもテキストで指示するだけで分類可能)という画期的能力を獲得した。
🍰 まずはやさしく
画像と文字をセットで扱う道具です。
写真の内容を言葉で分けるために使います。
スマホの写真検索のような機能です。
まずは重要なポイントを短く読みましょう。
テキストと画像の対照学習モデル
CLIP を 30 秒で把握する重要ポイント:
🍰 まずはやさしく
画像と文字を同じ場所で扱う仕組みです。
AIに自由な言葉で指示を出すために使います。
部活の写真から特定の場面を探す時に便利です。
この技術がどう使われているかを確認します。
論文や実装中に 「CLIP」として登場する用語。 本ページは SSDSE-B-2026 などの公的データを題材にした教育用ハンズオン教材です。
画像とテキストを共通空間に揃えるマルチモーダル基盤モデルとして、 画像検索・ゼロショット分類・キャプション生成の前段で登場します。
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 は「画像語」と「英語」の二か国語辞書を持つようなもの。 ある画像とその説明文を同じ位置に置く辞書を 4 億ページ分作る。 推論時、 新しい画像を辞書で引けば最も近いテキストが分かる(その逆も)。
同じ概念を表す画像とテキストはお互いを引きつける磁石。 違う概念どうしは反発する。 学習を経て、 「犬の画像」と "a photo of a dog" は強く結びつき、 「犬の画像」と "a photo of a banana" は離れる。
CLIP は画像とテキストにそれぞれ「512 次元の ID カード」を発行する。 ID が似ていれば同じ意味、 離れていれば違う意味。 ID 同士の cosine 類似度を取るだけで「画像が何を意味するか」「テキストが何を表すか」が即座に分かる。
🍰 まずはやさしく
画像と文字を比べる学習モデルのことです。
正解のペアを近くに配置するために使います。
テストの問題と答えをセットで覚えるような仕組みです。
数式の意味や記号の内容について詳しく読みます。
テキストと画像の対照学習モデル
英語名 Contrastive Language-Image Pre-training。
InfoNCE 損失。 画像 i の埋め込み $z_i^I$ とテキスト i の $z_i^T$ のコサイン類似度を温度 $\tau$ で正規化。
数式の各記号が『何の量で、 どの空間に住み、 どんな単位を持つか』を意識すると、 暗記でなく構造として理解できます。 SSDSE-B の都道府県データに当てはめて、 各シンボルが何に対応するかを上の Python 実装で確認しましょう。
まずは本ページの『💡 30 秒で分かる結論』と『🎨 直感で掴む』で全体像を掴み、 次に『🧮 実値で計算してみる』を 手を動かして追体験するのが最短です。 数式や深い理論はその後で十分。
本ページの『🌐 関連手法・派生』『🔗 関連用語』で対比される手法を確認し、 それぞれの適用条件と得意・不得意を表で比較するのが効果的です。 SSDSE-B のような共通データセットで両方走らせて結果を見ると違いが体感できます。
CLIP の学習時の計算量で効くのは、 バッチ内の全画像 × 全テキストの類似度行列で、 バッチサイズ $B$ に対して $O(B^2 d)$ です。 対照学習は「正解ペア以外をすべて負例にする」ので、 $B$ を大きくするほど負例が増えて精度が上がる——CLIP の原論文が $B=32{,}768$ という巨大バッチを使ったのはこのためです。 $B=32{,}768$ なら類似度行列は 10 億要素。 単一 GPU には載らないので、 複数 GPU に分散して行列を分割計算します。 「バッチサイズがハイパーパラメータではなく、手法の性能そのものを決める」という点が、 通常の教師あり学習と大きく違うところです。 一方、 推論時は画像・テキストそれぞれを 1 回ベクトル化するだけなので $O(d)$ で、 事前計算しておけば検索は内積 1 回です。
『点推定値』だけでなく『不確実性(CI、 SE、 分散)』『前提条件のチェック結果』『代替手法との比較』『データ取得日と seed』をセットで報告するのが標準。 査読・レビューで問われる典型ポイントです。
バッチサイズ $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 版。
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}") |
📤 実行例:
💬 結果の読み方:この埋め込みは 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 の本質を「ペア×コサイン×温度」の三点で整理できているか自己診断する。 各設問は concept / code / data の 3 種類に分け、 すべて SSDSE-B-2026 等の実データに紐づく。 回答は <details> で隠してあるのでクリックで開く。
解答:ミニバッチに (画像 i, テキスト j) が 4 ペア入っているとき、 対角線 (i=j) の 4 組が positive、 非対角線の 4×4−4=12 組が negative。 InfoNCE 損失は positive ペアの cosine 類似度を上げ、 negative ペアの類似度を下げるように最適化する。 N=32,768 (OpenAI 実装) では 1 ペアあたり 32,767 個の negative を同時に対比でき、 これが「ハードな負例」を自然に確保する仕組みになる。
解答:τ を大きくすると logits = sim/τ が小さくなり、 softmax は平坦化する (差が出にくくなる)。 逆に τ を小さくすると分布は鋭くなり「正解一発当て」を強制する。 訓練初期は τ=0.5 程度でゆるく始め、 学習が進むにつれ τ→0.07 を学習させることで段階的に鋭くする。 ただし τ を手動スケジュールする必要はなく、 OpenAI 実装は log τ を学習可能パラメータとして同時最適化する (最終値はおよそ 1/100)。
A1101)と出生数(A4101)の Pearson 相関を求める Python コードを書け(pandas + scipy)。
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.5e-47 となり、 人口と出生数は強い正の相関がある (東京のレバレッジが大きいが、 除外しても r=0.993)。 CLIP の cosine 類似度も「正規化された相関」と同じ構造で、 値の大きさより方向の一致を見る指標。
L3221)」の箱ひげ図を描き、 群間で中央値の差が最大の組を答えよ。
解答:関東 (東京 341,320 円/月 などを含む) の中央値 約 307,817 円/月 が最も高く、 中四国・九州沖縄 (沖縄 251,222 円/月 など) の 約 286,970 円/月 が最も低い。 中央値差は約 20,800 円/月で、 4 群中の最大対比となる。 CLIP で画像-テキストを対比するのと同じく、 群ごとの分布差を見ることで「カテゴリ」の意味を視覚化する練習になる。
解答:ViT-L/14 は 1024 次元、 テキスト側は 512 次元 (OpenAI 公開実装)。 両者を共通の埋め込み空間に揃えるため、 各エンコーダ末尾に linear projection(W_image ∈ R^(1024×768) と W_text ∈ R^(512×768))を挟む。 共通次元は 768。 この projection 層の重みは 学習する(凍結しない)。 投影後に L2 正規化して cosine 類似度を計算する。
解答:(1) 訓練データ (Web から収集した画像-キャプションペア) には 裸の単語ではなく文章で説明されているケースが圧倒的に多いため、 推論時もテンプレ文にすることで 分布一致を確保できる。 (2) class 名が同綴異義 (例: bat=コウモリ/野球バット) でもテンプレ全文の意味が異なるので、 ambiguity を減らせる。 OpenAI 論文は 80 種類のテンプレを ensemble して ImageNet zero-shot で +3.5% 改善を報告。
解答:共通埋め込み空間において、 画像埋め込み群とテキスト埋め込み群が 離れた cluster として分布し、 modality 内の類似度の方が modality 間の類似度より大きくなる現象 (Liang+ 2022)。 対照学習は cosine の 順位だけ最適化するため、 絶対値が揃う保証がない。 これが「正解ペアの cosine ≒ 0.30 でも他より高い」という不思議な値域を生む。 後段で利用するときは、 absolute 値ではなく「rank」で判断する必要がある。
I5102 ÷ A1101 × 1000)」を新変数として、 ヒストグラムを描き分布の歪みを評価する Python コードを書け。
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 類似度分布は右に歪むことが多く、 このようにヒストグラムで分布形を確認する習慣は転用できる。
解答:
・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 予算・汎化性のトレードオフで選択する。
A1101 (人口) と A4101 (出生数) の散布図を描き、 北海道・東京・沖縄の 3 点をハイライトせよ。 想定される視覚的特徴は?
解答:散布図は右肩上がりの直線に近い (相関 r≈0.995)。 東京が右上の極端値 (人口 1,409 万, 出生 86,348)。 北海道は中位、 沖縄は左下に位置するが 出生率は高いため回帰直線の上側に外れる。 CLIP も「全体の規模」(人口)と「アクティビティ」(出生数)を一列に並べる絵を学習する点で、 散布図の構造と相通じる。
CLIP の核となる「2 つの空間を cosine で揃える」発想を、 画像なしで体感する。 47 都道府県を 人口構成 ベクトル(数値特徴)と 社会活動ベクトル(出生・死亡・診療所)に分け、 cosine 類似度で対応関係を学ぶミニチュア版を実演する。
このコードでやること:SSDSE-B-2026 の 47 都道府県を 2 つの「ビュー」(人口系特徴量 / 社会活動系特徴量) に分け、 cosine 類似度で都道府県同士の対応関係を見る。 CLIP が画像とテキストを並べる発想の数値版。
📥 入力データ (SSDSE-B-2026 抜粋):
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.92 と高い一方、 47 県すべてが「規模が大きいほど各指標も大きい」ため人口空間でも社会活動空間でも似た向きを向き、 非対角の最大類似度も 0.99 に達する。 その結果 rank@1 一致は 1 / 47 にとどまる。 これは本ページ後半の失敗パターン表にある埋め込み崩壊(cosine が軒並み 0.99)そのもので、 「絶対類似度が高くても rank では正解を引けない」典型例。 実 CLIP が高次元(512 次元)で各概念を直交させて識別力を確保するのと対照的に、 3 次元の相関特徴だけでは識別が難しいことを体感できる。
cosine 類似度を直感的に感じるには、 まず 2 次元データの「散布図」「ヒストグラム」「箱ひげ図」の 3 枚を見直すのが近道。 下の 3 枚はそれぞれ 関係性 / 分布形 / 群比較 を表す代表図で、 CLIP の各種評価視点に対応する。
CLIP は強力だが、 そのまま業務に投入すると失敗するパターンが多い。 以下 7 則は OpenAI 公式・Hugging Face Community・国内導入事例から抽出した運用ノウハウ集。
torch.nn.functional.normalize(x, dim=-1) を必ず通す。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 mismatch | model.config.projection_dim を確認 | projection 層を明示的に追加 |
| バージョン混在 | 同じクエリで毎回違う結果 | 埋め込み DB の hash 不一致 | バージョン名付き再構築 |
CLIP の中核演算は cosine 類似度。 ここで cosine の数学的性質を整理しておくと、 デバッグも応用設計も格段に楽になる。 とりわけ「内積との違い」「正規化の必要性」「距離計量としての特性」の 3 点をしっかり押さえておきたい。
cosine(u, v) = u·v / (‖u‖‖v‖) は必ず [−1, +1] に収まる (Cauchy–Schwarz 不等式)。 +1 は同じ向き、 0 は直交、 −1 は反対向き。 ところが CLIP の現実値は +0.10〜+0.50 程度に集中することが多い。 「全部の正解ペアが +0.99 になる」状態は 埋め込み崩壊の前兆なので、 ヒストグラムで分布の広がりを必ず確認する。
cosine 「類似度」を 1−cosine と変換すると「距離もどき」になるが、 三角不等式を満たさない。 そのため、 k-NN や球面分割木 (Annoy, FAISS) を CLIP 埋め込みで使うときは、 内部実装が「L2 正規化後の内積」または「角度距離 arccos(cosine)」を採用していることを確認する。 FAISS の IndexFlatIP は L2 正規化後の内積を用いて cosine と等価に動く。
d 次元単位球面上にランダムに 2 点を取ると、 cosine の期待値は 0、 標準偏差は 1/√d。 CLIP の d=768 では std ≒ 0.036。 つまり「ランダムなペアの cosine は 0±0.04 の範囲に 68% 入る」。 正解ペアが +0.30 程度であれば、 これはランダムから 8σ 離れた強いシグナル。 「絶対値が 0.30 でしかない」と落ち込む前に、 ランダム baseline と比較する習慣を持つ。
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 個の用語を、 「学ぶ順」に並べた。 これだけ押さえれば、 ほぼすべての解説記事を独力で読める。
これら 12 語を「自分の言葉で説明できる」状態になれば、 CLIP に関する技術ブログ・論文・OSS ドキュメントの 80% は独力で読み解けるようになる。 とくに 埋め込み → 正規化 → cosine → InfoNCE → 温度の 5 ステップは「対照学習を一行で説明する黄金のチェーン」として丸暗記推奨。 業務会議で「なぜこのモデルを使うのか」と問われたときに、 この 5 語連鎖を 30 秒で答えられれば、 技術選定者として一人前である。
CLIP で頻出する記号と意味を、 SSDSE-B-2026 を題材にした実装と対応させて整理します。
| 記号 | 意味 |
|---|---|
| $$I_i$$ | i 番目の画像の埋め込みベクトル(正規化済) |
| $$T_j$$ | j 番目のテキストの埋め込みベクトル(正規化済) |
| $$τ$$ | 温度(temperature)— softmax の鋭さを制御 |
| $$L_{contrast}$$ | 対照損失(InfoNCE)— 正ペアを近く、 負ペアを遠くする |
※ 記号の使い方は流派により少し異なります。 まずは CLIP の公式実装(PyTorch・TensorFlow 等)のソースで定義を確認するのが安全です。
この用語を理解・使用するときは、 次のような前提を意識してください:
CLIP は 対照学習 × ベクトル空間 × 情報理論 の交差点に立つモデル。 本節ではそれぞれの理論的土台を「最小限の数式と最大限の直感」で整理する。 後段のすべての応用 (zero-shot 分類、 検索、 fine-tune) は、 この三本柱から派生する。
対照学習の核心は「似ているはずのものを近く、 違うはずのものを遠くに配置する」というシンプルな考え方にある。 ラベル付き分類器のように「クラス番号を当てる」訓練ではなく、 「ペアの関係を当てる」訓練を行う。 この設計思想は CLIP 固有ではなく、 SimCLR (Chen+ 2020), MoCo (He+ 2020), BYOL (Grill+ 2020) など視覚自己教師あり学習で広く確立された。 CLIP の独自貢献は、 異なるモダリティ (画像とテキスト) を同じ枠組みに乗せた点。 単一モダリティ内で「同じ画像の augment 同士は positive」とする設計を、 「同じペアの 画像-テキスト は positive」と一般化したと見れば理解しやすい。
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 補題と対比して理解してほしい。
InfoNCE 損失 (van den Oord+ 2018) は、 画像 X とテキスト Y の 相互情報量 I(X; Y) の下界を最大化する形に書ける:
この不等式が意味するのは、 「バッチサイズ N が大きいほど、 損失最小化が相互情報量の下界をきつくする」ということ。 OpenAI が N=32,768 (約 32K) という巨大ミニバッチを採用したのは、 単に GPU 効率の問題ではなく、 理論的に「学べる相互情報量の上限」を引き上げるためだった。 N を増やすほど CLIP の汎化性能が線形に伸びるという経験則 (scaling law) は、 この下界の引き締めから自然に導ける。
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 程度を圧縮した埋め込み」に相当する情報密度になる。
都道府県を「画像(観光地写真)」と「テキスト(人口・気候のような統計記述)」のペアと見立てると、 CLIP 的な対照学習で都道府県のマルチモーダル類似度が定義できる。 SSDSE-B から各県の特徴を文章化し、 観光写真とアラインさせて検索・推薦に応用するイメージ。
| 項目 | 条件 / 入力 | 結果 / 解釈 |
|---|---|---|
| 北海道 | 「広大な雪景色、 509.2 万人」 | 雪原画像と高類似度 0.82 |
| 東京都 | 「過密都市、 1409 万人」 | 高層ビル画像と類似度 0.79 |
| 沖縄県 | 「海洋気候、 147 万人」 | ビーチ画像と類似度 0.85 |
| 京都府 | 「観光・寺院、 254 万人」 | 古都画像と類似度 0.81 |
| 長野県 | 「山岳・寒冷、 200 万人」 | 山岳画像と類似度 0.76 |
※ 数値は SSDSE-B-2026.csv から抽出した実値、 もしくは典型的な学習設定での目安値です。 細部の数値は前処理・乱数 seed・実装により変動します。
本物の CLIP は画像を入力するが、 ここでは概念実証として「SSDSE-B-2026 の都道府県統計ベクトル」と「都道府県を表す日本語テキスト」を対応付ける CLIP 風モデルを擬似的に作る。 統計ベクトル = 画像 encoder の出力、 テキスト embedding = text encoder の出力と見立て、 同じ県の組を contrastive 学習する。
📥 入力データ(実測、 抜粋):
これに対するテキスト記述:
合成埋め込みで画像とテキストのコサイン類似度を計算する。
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}") |
💬 手計算 (Step 2) 0.990 / 0.000 と Python 出力が完全一致。 画像と t1 (cat) が高一致。
SSDSE-B-2026 のような公的統計データを Python で扱う際の基本パターン:
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(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)
|
📤 実行例:
💬 各県の人口と出生数を自然言語化。 実 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』で十分です。
🎯 このコードでやること: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)) |
📤 実行結果:
💬 東京は全 5 指標で +2σ 以上。 これが CLIP image encoder の出力に相当する 5 次元 embedding。
🎯 このコードでやること:都道府県名から疑似テキスト 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)) |
📤 実行結果:
💬 教育用テキスト embedding を img_vec + ノイズで生成。 同じ県の (img, text) ペアが「似ているが完全一致ではない」状況を再現する。
🎯 このコードでやること: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}') |
📤 実行結果:
💬 対角(同じ県の img-text)= 0.68、 非対角平均(違う県の組)= 0.12。 平均では対角が非対角を上回り、 CLIP 学習はこの「対角を上げ、 非対角を下げる」操作。 学習後は対角がほぼ 1、 非対角が 0 付近に収束する。
🎯 このコードでやること:「人口最大の県のテキスト」を 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})') |
📤 実行結果:
💬 「東京を表すテキスト」で検索すると、 大規模県(兵庫・大阪・東京・福岡・神奈川)が僅差 (sim≒0.99) で上位に並び、 東京自身は rank 2 に落ちる。 5 つの規模指標だけでは大都市圏が同じ向きに潰れ(埋め込み崩壊)、 rank@1 での自己一致が難しい。 本物の CLIP は 512 次元で各概念が直交しやすく、 この潰れを避けて prompt "a photo of a dog" で犬画像を正しく上位に返す。
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 都道府県観光統計の派生):
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):
💬 結果の読み方:訓練データに「沖縄=観光地」というペアが含まれていなくても CLIP は "a tourist destination" という英語テキストの埋め込みと沖縄の青い海・砂浜画像の埋め込みが cosine 距離 0.788 で最近接 と判定。 これが「ラベル無しで分類可能」の正体。 一方、 東京は「industrial area」と「tourist destination (浅草・スカイツリー)」がほぼ同程度に近く、 softmax で 0.481 という低めの確信度になる。
学習者が最初の 3 ヶ月でほぼ確実にハマる罠を、 先回りして提示する。 「踏むべき迷い道」と「避けるべき迷い道」を区別できれば、 学習速度は倍になる。
CLIP はマルチモーダル基盤モデルの中心に位置し、 対照学習・埋め込み空間・ゼロショット分類という 3 つの柱から成る。 下の SVG は主要概念の包含・派生・上位関係を示す。
完全な概念マップは 🗺 概念マップ で確認できます。
OpenAI (Radford et al. 2021) が発表。 4 億の画像-テキストペアで対照学習し、 ImageNet をゼロショットで分類できることを示した。 その後 ALIGN (Google), CoCa, BLIP, EVA-CLIP など派生多数。 Stable Diffusion のテキスト条件付けにも採用。
原典は Radford et al. (2021)。 ラベル付き画像ではなく、 ウェブ上の「画像とその説明文」4 億組をそのまま使ったのが要点です。 ImageNet のように人手でクラスを定義する代わりに、 自然言語をラベルの代わりにした——だから学習時に見ていないクラスにもゼロショットで対応できます。 原典には「どのプロンプト文にするかで精度が数ポイント動く」という記述もあり、 プロンプト設計の重要性はここから始まっています。
『CLIP』は理論だけでなく、 産業・研究の様々な現場で実用されています。 ここでは代表的な応用を 6 つ挙げます。
どの応用も「何を入力とし、 何を出力すべきか」を整理した上で、 上の Python 実装をベースに拡張するアプローチが定石です。 SSDSE-B のような公開データセットで小さく試し、 動作確認できてから本番データに展開すると安全です。
『CLIP』には多くの派生・バリエーションがあります。 代表的なものを精度・特徴で比較した表です。
| 手法 / バージョン | 指標 / 特徴 | 備考 |
|---|---|---|
| ResNet-50 | ImageNet 76% | ゼロショット不可 |
| CLIP ViT-B/32 | ImageNet 63% | ゼロショット |
| CLIP ViT-L/14 | ImageNet 76% | ゼロショット |
| OpenCLIP | Web 規模再現 | オープン |
| EVA-CLIP | SOTA レベル | 計算重 |
数値は論文公表時点のもので、 計測条件(データ・前処理・ハイパーパラメータ)が異なります。 自分の問題で再評価することを推奨。
『CLIP』は周辺の似た用語と混同されがちです。 ここでは特に紛らわしい用語との本質的な違いを整理します。
data/raw/SSDSE-B-2026.csv。 47 都道府県の社会・人口指標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 のような実データに当てはめながら、 ぜひ手を動かして確認してください。
47 都道府県の特徴を CLIP で『マルチモーダル埋め込み』する。 各県の統計データを自然言語化(例:『北海道:人口 522.5 万人、 平均気温 8.9 度、 観光客数多い、 雪深い』)し、 CLIP テキストエンコーダで 512 次元ベクトルに。 観光写真と並べてマッチング率を測ると、 県の雰囲気・特徴の類似性をマルチモーダルに分析できる。
理論を理解した次は、 実務に落とし込むためのノウハウが重要です。 SSDSE-B のような身近なデータで小さく試し、 動かしながら学ぶことで体得できます。 失敗してもコストは小さく、 学びは大きい。
マルチモーダル基盤モデル ├── 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 ── ネイティブマルチモーダル
理論上は可能だが、 数百 GPU × 数週間が必要。 LAION-2B + 256 A100 で開発期間 1~2 ヶ月。 個人や中小組織では事前学習済みモデル(open_clip / SigLIP)を fine-tuning する方が現実的。
CLIP は「画像とテキストの対応を測る」モデル。 DALL-E / Stable Diffusion は「テキストから画像を生成する」モデル。 多くの画像生成モデルは CLIP を「テキスト条件で生成を評価/誘導する」スコアラとして使う。
ViT 系(ViT-B/32, ViT-L/14)は大規模データで高性能。 ResNet 系は小データで安定。 デフォルトは ViT-B/32 か ViT-L/14。 SigLIP は ViT-So400m が強力。
rinna/japanese-clip、 stabilityai/japanese-stable-clip、 LINE の japanese-clip-base 等。 商用利用は各モデルのライセンスを確認。
ViT-B/32 は 512 次元、 ViT-L/14 は 768 次元、 SigLIP-SO400m は 1152 次元等。 モデルごとに異なる。 推論時はモデル仕様を確認。
🎯 このコードでやること: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}') |
📤 実行結果:
💬 5 指標 + ノイズの簡易モデルでは、 大規模県が同じ向きに潰れる(埋め込み崩壊)ため recall@1 は 0.09 と低く、 recall@10 でも 0.49 にとどまる。 規模だけを並べた特徴では識別力が出ないことが分かる。 本物の CLIP は高次元表現で MS-COCO の recall@1 を 50% 以上達成(ImageNet zero-shot top-1 は 76%)する。
| 用語 | 意味 |
|---|---|
| image encoder | CLIP の画像側エンコーダ。 ViT または ResNet。 |
| text encoder | CLIP のテキスト側エンコーダ。 Transformer。 |
| WIT-400M | CLIP 訓練用の 4 億組 web image-text データセット。 非公開。 |
| LAION-2B/5B | CLIP 系再現用の公開大規模データセット。 |
| prompt template | "a photo of {class}." 形式の入力雛形。 |
| zero-shot | 学習時に見たことのないクラスに対する推論能力。 |
| temperature τ | softmax の鋭さを制御。 CLIP では learnable で exp(τ)≈100 に収束。 |
| CLIP score | 画像-テキスト整合性スコア。 生成モデル評価に使用。 |
| sigmoid loss | SigLIP で導入。 batch 全体の softmax より sigmoid 独立判定で効率化。 |
| VLM | Vision Language Model。 CLIP + LLM で対話可能なマルチモーダルモデル。 |
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 が原論文の最強モデル。
12 層 Transformer(GPT-2 アーキテクチャに類似)。 vocab size 49152、 BPE トークナイザ、 最大 76 tokens。 [SOS][...][EOS] の [EOS] 位置の hidden state を projection。
image encoder と text encoder の出力をそれぞれ 512 次元(または 768)の共通空間に線形射影。 L2 正規化されて cosine 類似度計算に使われる。
| 手法 | 学習方式 | 特徴 | 用途 |
|---|---|---|---|
| CLIP | contrastive | 画像-テキスト dual encoder | 分類・検索 |
| VisualBERT | single-tower | 早期融合、 詳細推論 | VQA |
| BLIP | contrastive + captioning | filter + 生成統合 | caption 生成 |
| BLIP-2 | Q-Former + LLM | 凍結 LLM 利用 | 画像対話 |
| LLaVA | CLIP + projection + LLM | instruction tuning | VQA・対話 |
| Flamingo | cross-attn LLM | few-shot 強い | 少数例学習 |
| DALL-E 2 | CLIP + diffusion prior | 画像生成 | テキスト→画像 |
| Stable Diffusion | CLIP text + UNet | オープン生成 | テキスト→画像 |
CLIP を採用機関は EU AI Act、 日本「AI 事業者ガイドライン」等に従い、 影響評価 (AIA)、 説明責任、 利用範囲制限を実施することが推奨される。 特に医療・教育・法執行など影響度の大きい分野での無批判な利用は避けるべき。
第 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 の「目」として機能する時代へ。
🎯 このコードでやること: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}') |
📤 実行例(サンプル猫画像):
💬 zero-shot で猫と正しく判定(confidence 99.4%)。 ラベル定義テキストだけで分類できる CLIP の真骨頂。
🎯 このコードでやること: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()) |
📤 実行例:
💬 SigLIP は sigmoid loss で batch 依存を緩和し、 訓練効率を上げた CLIP の進化形。 ViT-SO400M はパラメータ 400M で SOTA。
🎯 このコードでやること: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 平均で評価') |
📤 実行例:
💬 OpenAI 原論文では 80 prompt の ensemble で +1.9 ポイント。 zero-shot 精度を底上げする「秘技」。 推論時のコストはわずか(テンプレ embedding はキャッシュ可能)。
| 指標 | 定義 | 用途 |
|---|---|---|
| zero-shot accuracy | ラベル定義テキストだけで分類 | 表現の汎化性 |
| linear probe accuracy | backbone 凍結 + 線形分類 | 表現の素直さ |
| image-text retrieval R@K | テキスト→画像で正解が top-K に入る率 | 検索性能 |
| CLIP score | 画像-テキスト cosine 類似度 | 生成品質評価 |
| robustness | distribution shift 下の精度 | 頑健性 |
| fairness | 人口属性別の精度差 | バイアス測定 |
CLIP の発明=「分類問題を検索問題に変換した」こと。 従来「画像を 1000 クラスから 1 つ選ぶ」だった分類問題を、 「画像 embedding に最も近いテキスト embedding を探す」検索問題に置き換えた。 これにより:
この発想転換が「基盤モデル」時代を実質的に開いた。 CLIP は単なる手法ではなく 「ラベル付きデータの呪縛から AI を解放した思想」として歴史に刻まれる。
| あなたの状況 | 推奨モデル | 理由 |
|---|---|---|
| 英語タスクで精度最重視 | EVA-CLIP / SigLIP 2 | SOTA に近い |
| 英語タスクで標準 | openai/clip-vit-large-patch14 | 十分実用 |
| 軽量モデル必要 | MobileCLIP / TinyCLIP | エッジ展開可能 |
| 日本語キャプション | stabilityai/japanese-stable-clip-vit-l-16 | 日本語特化 |
| 多言語 | multilingual-clip / SigLIP multilingual | 100+ 言語 |
| 医療画像 | MedCLIP / BiomedCLIP | 医療特化 |
| 画像生成のテキスト条件 | openai/clip-vit-large-patch14 | SD 互換 |
| VLM 構築 | SigLIP-SO400M | LLaVA-NeXT 等で標準採用 |
| 症状 | 原因の可能性 | 対処 |
|---|---|---|
| 分類精度が異様に低い | prompt 不適切、 言語不一致 | prompt template 見直し、 多言語モデル試行 |
| 全クラスの確率が均等に近い | クラス間の文章類似度が高すぎ | prompt に区別要素を追加 |
| 特定クラスばかり選ばれる | 学習データのクラスバランス偏り | prompt 工夫、 zero-shot calibration |
| 類似度が全て低い | L2 正規化忘れ、 temperature 適用忘れ | normalize → cosine → temperature の順を確認 |
| 画像が真っ黒/真っ白で誤分類 | 前処理サイズ不一致 | model 推奨の preprocess を使用 |
| 推論が遅い | embedding 都度計算 | クラス embedding を事前計算 + キャッシュ |
| fine-tune で過学習 | 小データで全更新 | LoRA / linear probe / LiT 等で軽量化 |
「CLIP は画像とテキストを同じ 512 次元の空間に埋め込むことで、 ペアになっている画像とテキストが近く、 そうでないものが遠くなるよう contrastive 学習されたモデルです。 これにより事前定義クラス不要で、 任意の言葉で zero-shot 分類が可能になり、 DALL-E や Stable Diffusion などの生成 AI、 そして LLaVA や GPT-4V などの VLM の中核技術となりました。」
補足:「同じ空間に埋め込む」というアイデア自体は Word2Vec や DeViSE が先にあるが、 CLIP は規模(4 億ペア)と contrastive 訓練の徹底で実用性を一気に押し上げた点で歴史的な分岐点となった。
「CLIP」は画像 × 言語の同一埋め込み空間を学習するモデルで、 上流の対照学習データセット設計と下流のゼロショット分類・検索のセットでこそ威力を発揮する。
上流で対照学習用のキャプション付き画像を整え、 並列の CNN/ViT 単独モデルと精度比較し、 下流でゼロショット検索や画像生成のガイダンスに使えば、 ラベル無しデータに対する分類・検索パイプラインを実現できる。
「CLIP(画像と言葉の対応付け)」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。
CLIP は「画像と言葉を同じ空間に置く」道具。 検索や候補出しに強く、 厳密な判定を任せる用途には向かない。
CLIP の核は「画像とテキストを同じ地図(埋め込み空間)に置き、 コサイン類似度が最も高いラベルを選ぶ」ことです。 下のデモでは、 いくつかのアイコン的な画像とテキストラベルにあらかじめ埋め込みベクトルを与え、 類似度行列(ヒートマップ)から「猫の画像には『猫』ラベルが一番近い」といったゼロショット分類を体感できます。 ラベル候補を切り替えたり(分類が変わる)、 ラベル点をドラッグしたり(埋め込みの偏り)、 対照学習の学習前後を切り替えたり(正しいペアが近づく)できます。
チェックを付けたラベルだけが照合候補になります。 正解ラベルを外すと、 画像は次に近い(別の)ラベルへ誤分類されます(例:「太陽」を外すと太陽画像が「犬」に化ける)。 これが「ラベル語彙(プロンプト)次第で結果が変わる」というゼロショットの性質です。
■=画像、 ●=ラベルを同じ 2 次元地図に配置。 各画像から最も近いラベルへ線を引きます(緑=正しい照合/赤破線=誤照合)。 「対照学習」ボタンで学習前↔学習後を切り替えると、 埋め込みが未分化な状態(照合が曖昧)から、 正しいペアが近く・誤ペアが遠くへ分離していく様子が見えます。 ●(ラベル)はドラッグで動かせます(指でもOK)── 埋め込みを少しずらすだけで分類が壊れる「偏り」を体感してください。
CLIP のコサイン類似度が意味を持つのは、 埋め込みをL2 正規化して「長さを捨て、 角度だけを見る」設計のおかげです。 本節では既存セクション(InfoNCE・プロンプト・modality gap)とは別の角度、 すなわち前処理と埋め込み幾何の視点から、 SSDSE-B-2026 の実測値で「正規化を誤ると cosine は壊れる」ことを確認します。
同じ「正規化」でも、 データ行列に対する方向がまったく違う 2 種類があります。
2 つは直交する操作で、 片方だけでは足りないことがある。 特徴群に共通の「大きさ」成分が支配的だと、 全サンプルのベクトルがほぼ同じ方向を向いてしまい、 L2 正規化して角度を測っても全部が cosine ≒ 1 になるからです。 これを次で実データで見ます。
SSDSE-B-2026 の 2023 年・47 都道府県から 5 特徴 A1101(総人口)・A4101(出生数)・A4200(死亡数)・I5102(一般診療所数)・E3101(小学校数)で県ベクトルを作り、 生の値のままコサイン類似度を計算すると(実測):
生の 5 特徴はどれも人口規模にほぼ比例し(5 特徴間の相関は最低でも r = 0.922、 実測)、 47 本のベクトルがすべて同じ「規模方向」を向くため、 角度差が消えてしまう。 z-score で列を揃えて初めて「東京と鳥取は正反対」「鳥取の隣人は石川・福井」という構造の違いが現れます。 再現コード(実測値の出典):
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 |
ここから引き出せる教訓は 3 つ:
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 正規化そのものの単独ページは本用語集には未収録のため、 詳細は上記「コサイン類似度」内の解説を参照してください。