SigLIP2によるゼロショット画像分類
SigLIP2(Vision Transformerベース)を用いて事前学習なしの単語等による画像分類を行うゼロショット学習。SigLIP2の4種類のモデル(base-patch16-224からso400m-patch14-384)の性能比較ができるPythonプログラム、画像とテキストの埋め込み空間や類似度計算などの解説付き。


目次
基本概念
2.1 ゼロショット学習の原理
ゼロショット学習は、画像とテキストを同一の高次元空間(埋め込み空間)にマッピングし、その空間内での類似度を計算することで分類を行う[1]。
処理フロー:
[入力画像] → [画像encoder] → [画像特徴ベクトル] ↘
[類似度計算] → [分類結果]
[テキストラベル] → [テキストencoder] → [テキスト特徴ベクトル] ↗
2.2 SigLIP2(Sigmoid Loss for Language-Image Pre-training 2)
SigLIP2は、SigLIPの訓練レシピを基に構築された多言語ビジョン言語エンコーダーである。デコーダーベース事前訓練、自己蒸留、マスク予測を含む統合レシピにより、密な予測タスクでの大幅な改善を実現している[2]。
2.3 Vision Transformer(ViT)
SigLIP2で使用されるViTは、画像を固定サイズのパッチに分割し、各パッチを線形埋め込みによってベクトルに変換する。その後、標準的なTransformerエンコーダで処理する[3]。
2.4 特徴量正規化
特徴量正規化は、ベクトルの大きさを1に統一する処理である。これにより、類似度計算時にベクトルの方向のみが重要となり、より安定した分類が可能になる。
用語集
SigLIP2: SigLIPの改良版で、多言語対応、密な特徴量、改良されたセマンティック理解を提供するビジョン言語モデル。
Vision Transformer (ViT): 画像をパッチに分割し、Transformerアーキテクチャを用いて処理する画像認識モデル。
ゼロショット学習: 訓練時に見たことのないクラスに対しても分類を行う機械学習手法。
対照学習: 類似したデータペアの表現を近づけ、異なるデータペアの表現を遠ざけることで学習を行う手法。
埋め込み空間: 高次元データを低次元の連続ベクトル空間にマッピングした表現空間。
Python開発環境,ライブラリ類
ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.htmlで詳しく解説しているので、必要に応じて参照してください。
Python 3.12 のインストール
インストール済みの場合は実行不要。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要である。
REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul
【関連する外部ページ】
Python の公式ページ: https://www.python.org/
AI エディタ Windsurf のインストール
Pythonプログラムの編集・実行には、AI エディタの利用を推奨する。ここでは,Windsurfのインストールを説明する。
管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行して、Windsurfをシステム全体にインストールする。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。
winget install --scope machine Codeium.Windsurf -e --silent
【関連する外部ページ】
Windsurf の公式ページ: https://windsurf.com/
必要なライブラリのインストール
コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する
pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
pip install transformers pillow accelerate opencv-python
SigLIP2画像分類プログラム
概要
このプログラムは、画像の内容を理解し、与えられたテキストラベルとの関連性を数値化する能力を持つ。SigLIP2モデルを用いて画像とテキストを共通の特徴空間に埋め込み、その類似度を計算することで、画像が各ラベルにどの程度合致するかを判定する。
主要技術
- SigLIP2 (Sigmoid Loss for Language-Image Pre-training 2)
Google Researchが開発した視覚言語モデル。Sigmoid損失関数を用いた対照学習により、画像とテキストの対応関係を学習する。109言語に対応し、dense feature extractionによるマッチングを実現する[1]。
- Transformerアーキテクチャ
自己注意機構を用いた深層学習モデル。SigLIP2では画像エンコーダーとテキストエンコーダーの両方にTransformerを採用し、効率的な特徴抽出を行う[2]。
参考文献
- [1] Zhai, X., Mustafa, B., Kolesnikov, A., & Beyer, L. (2025). SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding. arXiv preprint arXiv:2501.09893.
- [2] Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, Ł., & Polosukhin, I. (2017). Attention is all you need. Advances in neural information processing systems, 30.
主要機能: SigLIP2を使用したカスタムラベルによる画像分類(ゼロショット分類)
使用技術・手法
- SigLIP2: Sigmoid Loss for Language-Image Pre-training 2の実装
- Vision Transformer (ViT): 画像エンコーダー
- Transformerテキストエンコーダー: テキスト処理
- コサイン類似度計算: 画像とテキスト特徴量のマッチング
- Dual-encoder architecture: 画像とテキストを別々に処理
# プログラム名: SigLIP2による画像とテキストの類似性判定
# 特徴技術名: SigLIP2 (Sigmoid Loss for Language-Image Pre-training 2)
# 出典: Zhai, X., et al. (2025). "SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding". arXiv:2501.09893
# 特徴機能: Sigmoid損失関数による効率的な対照学習とdense feature extractionにより、109言語対応の高精度な画像-テキストマッチングを実現
# 学習済みモデル: SigLIP2モデルファミリー(Base/Large/SO400M)、109言語対応の視覚言語エンコーダー、https://huggingface.co/google/siglip2-base-patch16-224
# 方式設計:
# - 関連利用技術: PyTorch(深層学習フレームワーク)、Transformers(モデル読み込み・推論)、PIL(画像読み込み・前処理)、tkinter(ファイル選択UI)
# - 入力と出力: 入力: 画像(ユーザは「0:画像ファイル,1:カメラ,2:サンプル画像」のメニューで選択.0:画像ファイルの場合はtkinterで複数ファイル選択可能.1の場合はOpenCVでカメラが開き,スペースキーで撮影(複数回可能).2の場合はhttps://github.com/opencv/opencv/raw/master/samples/data/fruits.jpg とhttps://github.com/opencv/opencv/raw/master/samples/data/messi5.jpgとhttps://github.com/opencv/opencv/raw/master/samples/data/aero3.jpgを使用)、出力: 処理結果が画像化できる場合にはOpenCV画面でリアルタイムに表示.OpenCV画面内に処理結果をテキストで表示.さらに,1秒間隔で,print()で処理結果を表示.プログラム終了時にprint()で表示した処理結果をresult.txtファイルに保存し,「result.txtに保存」したことをprint()で表示.プログラム開始時に,プログラムの概要,ユーザが行う必要がある操作(もしあれば)をprint()で表示.
# - 処理手順: 1)画像とテキストラベルの入力、2)SigLIP2による特徴量抽出、3)画像・テキスト埋め込みの正規化、4)コサイン類似度計算とスケーリング、5)Sigmoid関数適用によるスコア算出
# - 前処理、後処理: 前処理: RGB変換(グレースケール・RGBA対応)、後処理: スコアのソートと表示
# - 追加処理: 英単語ラベルへの "a photo of" プレフィックス追加(CLIPスタイルのプロンプトエンジニアリング)、温度パラメータ(SCALE=100.0)によるスコア調整
# - 調整を必要とする設定値: SCALE(温度パラメータ、デフォルト100.0、スコアの分散を制御)
# 将来方策: 複数画像の同時処理とバッチ推論により、最適なSCALE値を自動探索する機能の実装
# その他の重要事項: float16精度での推論により高速化、device_map="auto"によるGPU/CPU自動選択
# 前準備: pip install -U torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126
# pip install transformers pillow accelerate opencv-python
import cv2
import tkinter as tk
from tkinter import filedialog
import urllib.request
import os
import torch
from transformers import AutoModel, AutoProcessor
from PIL import Image, ImageDraw, ImageFont
import re
import numpy as np
from datetime import datetime
# 設定定数
MODELS = {
'google/siglip2-base-patch16-224': 'Base(86M パラメータ、224x224解像度、高速処理、軽量、日本語を含む109言語対応)',
'google/siglip2-large-patch16-256': 'Large(303M パラメータ、256x256解像度、バランス型、中程度の処理速度、日本語を含む109言語対応)',
'google/siglip2-large-patch16-384': 'Large-384(303M パラメータ、384x384解像度、高解像度処理、詳細な画像分析、日本語を含む109言語対応)',
'google/siglip2-so400m-patch14-384': 'SO400M(400M パラメータ、384x384解像度、最高精度、大規模モデル、日本語を含む109言語対応)'
}
SCALE = 100.0 # 温度パラメータ(スコアの分散を制御)
FONT_PATH = 'C:/Windows/Fonts/msgothic.ttc' # 日本語フォントパス
FONT_SIZE = 20 # フォントサイズ
# 処理結果を保存するリスト
results = []
def is_english_word(text):
"""文字列が英単語(英数字とスペースのみ)かどうかを判定"""
return bool(re.match(r'^[a-zA-Z0-9\s]+$', text.strip()))
# プログラム開始時の説明表示
print('=== SigLIP2による画像とテキストの類似性判定プログラム ===')
print('概要: 画像を入力し、ユーザが指定したラベルとの類似度を計算します')
print('操作方法:')
print('1. モデルを選択')
print('2. 画像入力方法を選択')
print('3. ラベルを入力(カンマ区切りで複数可能)')
print('4. 結果が画像とテキストで表示されます')
print('5. プログラム終了時に結果がresult.txtに保存されます\n')
# モデル選択
print('利用可能なモデル:')
model_list = list(MODELS.keys())
for i, (model_name, description) in enumerate(MODELS.items()):
print(f'{i+1}. {model_name}')
print(f' {description}')
print()
choice = input('モデル番号を選択 (1-4): ')
if not choice.isdigit() or not (1 <= int(choice) <= 4):
print('無効な選択です')
exit()
model_name = model_list[int(choice)-1]
# モデル初期化
print(f'選択されたモデル: {model_name}')
print('モデル読み込み中...')
model = AutoModel.from_pretrained(model_name, torch_dtype=torch.float16, device_map='auto')
processor = AutoProcessor.from_pretrained(model_name)
# デバイスの取得
device = next(model.parameters()).device
def image_processing(img, labels, formatted_labels, image_name=''):
"""画像を処理してSigLIP2で分類"""
# OpenCV画像をPIL画像に変換
if isinstance(img, np.ndarray):
img_pil = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img_pil = Image.fromarray(img_pil)
else:
img_pil = img
# RGBに変換(グレースケール・RGBA画像対応)
if img_pil.mode != 'RGB':
img_pil = img_pil.convert('RGB')
# SigLIP2で処理
inputs = processor(text=formatted_labels, images=img_pil, return_tensors='pt')
# inputsを適切なデバイスに移動
inputs = {k: v.to(device) if isinstance(v, torch.Tensor) else v for k, v in inputs.items()}
with torch.no_grad():
outputs = model(**inputs)
# 画像とテキストの埋め込みを取得
image_embeds = outputs.image_embeds
text_embeds = outputs.text_embeds
# 正規化
image_embeds = image_embeds / image_embeds.norm(dim=-1, keepdim=True)
text_embeds = text_embeds / text_embeds.norm(dim=-1, keepdim=True)
# コサイン類似度の計算(SCALEを適用)
similarity = (SCALE * image_embeds @ text_embeds.T).squeeze(0)
# sigmoidを適用してスコアを取得
scores = torch.sigmoid(similarity)
# 結果を画像に描画(RGB形式のまま処理)
result_img = np.array(img_pil)
# スコアでソート(降順)
sorted_indices = scores.argsort(descending=True)
# 結果をコンソールに出力
print('\n=== 類似度スコア ===')
print(f'画像: {image_name}' if image_name else '画像処理結果:')
result_text = f'処理時刻: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n'
result_text += f'画像: {image_name}\n' if image_name else '画像処理結果:\n'
result_text += f'使用モデル: {model_name}\n'
result_text += '各ラベルとのスコア:\n'
for idx in sorted_indices:
score_line = f'{labels[idx]}: {scores[idx].item():.4f}'
print(score_line)
result_text += score_line + '\n'
top_label = labels[sorted_indices[0]]
result_text += f'最も高いスコアのラベル: {top_label}\n'
result_text += '-' * 50 + '\n'
print(f'最も高いスコアのラベル: {top_label}\n')
# 結果を保存
results.append(result_text)
# 画像への結果描画(BGR形式に変換してからOpenCVで描画)
result_img_bgr = cv2.cvtColor(result_img, cv2.COLOR_RGB2BGR)
# 英数字表示はOpenCVで
top_score = scores[sorted_indices[0]].item()
cv2.putText(result_img_bgr, f'Score: {top_score:.4f}', (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# 日本語を含むテキスト表示はPILで
try:
font = ImageFont.truetype(FONT_PATH, FONT_SIZE)
img_pil = Image.fromarray(cv2.cvtColor(result_img_bgr, cv2.COLOR_BGR2RGB))
ImageDraw.Draw(img_pil).text((10, 70), f'Label: {top_label}', font=font, fill=(255, 255, 255))
result_img_bgr = cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
except OSError:
# フォントが見つからない場合は英数字のみ表示
cv2.putText(result_img_bgr, 'Label: [Japanese text]', (10, 70), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
# OpenCV形式(BGR)で返す
return result_img_bgr
def show_processed_image(img, labels, formatted_labels, window_name):
if img is None:
print('画像の読み込みに失敗しました')
return
processed_img = image_processing(img, labels, formatted_labels, window_name)
cv2.imshow(window_name, processed_img)
cv2.waitKey(0)
def save_results():
"""処理結果をresult.txtに保存"""
if results:
with open('result.txt', 'w', encoding='utf-8') as f:
f.write('=== SigLIP2 画像分類結果 ===\n\n')
for result in results:
f.write(result)
print('result.txtに保存しました')
print('\n=== 画像入力 ===')
print('0: 画像ファイル')
print('1: カメラ')
print('2: サンプル画像')
choice = input('選択: ')
# ラベル入力(画像処理前に一度だけ実行)
print('\n=== ラベル設定 ===')
print('画像を分類するラベルを入力してください(例: dog, cat, car, 犬, 猫, 車)')
labels_input = input('ラベル(カンマ区切り): ')
if not labels_input.strip():
print('ラベルが入力されませんでした')
exit()
# 個別のラベル処理
labels = [label.strip() for label in labels_input.split(',')]
# ラベルのフォーマット(英単語の場合のみ "a photo of" を追加)
formatted_labels = []
for label in labels:
if is_english_word(label):
formatted_labels.append(f'a photo of {label}')
else:
formatted_labels.append(label)
print(f'使用するラベル: {formatted_labels}')
if choice == '0':
root = tk.Tk()
root.withdraw()
paths = filedialog.askopenfilenames()
if not paths:
exit()
for path in paths:
show_processed_image(cv2.imread(path), labels, formatted_labels, 'Image')
elif choice == '1':
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
try:
print('カメラが起動しました。スペースキーで撮影、qキーで終了')
while True:
cap.grab()
ret, frame = cap.retrieve()
if not ret:
break
cv2.imshow('Camera', frame)
key = cv2.waitKey(1) & 0xFF
if key == ord(' '):
show_processed_image(frame, labels, formatted_labels, 'Image')
elif key == ord('q'):
break
finally:
cap.release()
elif choice == '2':
urls = [
'https://github.com/opencv/opencv/raw/master/samples/data/fruits.jpg',
'https://github.com/opencv/opencv/raw/master/samples/data/messi5.jpg',
'https://github.com/opencv/opencv/raw/master/samples/data/aero3.jpg'
]
downloaded_files = []
for i, url in enumerate(urls):
filename = f'sample_{i}.jpg'
try:
urllib.request.urlretrieve(url, filename)
downloaded_files.append(filename)
show_processed_image(cv2.imread(filename), labels, formatted_labels, 'Sample Image')
except Exception as e:
print(f'画像のダウンロードに失敗しました: {url}')
print(f'エラー: {e}')
continue
for filename in downloaded_files:
try:
os.remove(filename)
except OSError:
pass
cv2.destroyAllWindows()
save_results()
実行結果


実行手順
- プログラムを実行
- モデル番号を選択(1-4)
- 分類ラベルを入力
モデル選択指針と実験アイデア
モデル選択指針
| モデル | パラメータ数 | 推論速度 | 精度 | 推奨用途 |
|--------|-------------|----------|------|----------|
| base-patch16-224 | 86M | 高速 | 基本 | 初回実験、プロトタイプ |
| large-patch16-256 | 303M | 中速 | 良好 | 一般的な応用 |
| large-patch16-384 | 303M | 低速 | 高精度 | 詳細分類 |
| so400m-patch14-384 | 400M | 最低速 | 最高精度 | 研究・精密実験 |
実験のアイデア
- 軽量モデル vs 高精度モデル: 同一画像・同一ラベルでbase-patch16-224とso400m-patch14-384の出力を比較し、精度向上と処理時間増加のトレードオフを分析する
- 抽象概念 vs 具体概念: 「猫」「犬」のような具体的ラベルと「幸せな雰囲気」「静寂」のような抽象的ラベルで分類精度を比較し、SigLIP2の理解の限界を探る
- 文脈情報の効果: 「犬」と「家庭の犬」「警察犬」のように文脈を含むラベルでの分類差異を観察し、文脈理解能力を評価する
- 詳細度の段階: 「動物」→「犬」→「柴犬」のように詳細度を変えたラベルでの認識精度変化を分析する
- 画像の複雑さと精度: 単一オブジェクトの画像と複数オブジェクトが混在する画像での分類精度差を比較する
- 撮影条件の影響: 同じ対象を異なる照明・角度・背景で撮影した画像での分類安定性を検証する
- 画質と分類性能: 高解像度画像と低解像度画像での分類精度変化を観察し、画質の影響度を評価する
これらの実験を通じて、AIがどのように画像を理解し、言語との対応付けを行っているかの新たな発見が期待できる。
参考文献
[1] Radford, A., et al. "Learning Transferable Visual Representations from Natural Language Supervision." ICML 2021.
[2] Google Research. "SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding, Localization, and Dense Features." arXiv:2502.14786, 2025.
[3] Dosovitskiy, A., et al. "An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale." ICLR 2021.
[4] Zhai, X., et al. "Sigmoid Loss for Language Image Pre-Training." ICCV 2023.