はじめに
GoogleのEmbeddingGemma 2を、OpenCLIPに近いインターフェースで扱えるPythonライブラリ「openclip-gemma」を開発し、PyPIに公開しました。
GitHub: https://github.com/kotadymo/openclip-gemma
PyPI: https://pypi.org/project/openclip-gemma/
pip install openclip-gemma
openclip-gemmaは、EmbeddingGemma 2を使った画像・テキストの埋め込み生成を、create_model_and_transforms()、get_tokenizer()、encode_image()、encode_text()といった、OpenCLIPでおなじみのAPIで実行できるようにするライブラリです。
画像とテキストの類似度計算、画像検索、Zero-shot分類などの処理を、比較的シンプルなPythonコードで試せることを目指しています。
今回は、開発した背景、ライブラリの特徴、インストール方法、使い方、現時点での制約について紹介します。
追記
本ライブラリの名称「openclip-gemma」は、OpenCLIP風のAPIでEmbeddingGemma 2を利用できるという特徴から付けたものです。
本ライブラリは個人開発の非公式プロジェクトであり、Google、Google DeepMind、OpenCLIPプロジェクトとは提携・協力関係になく、公式に承認されたものでもありません。
既存プロジェクトの名称や商標への配慮から、今後ライブラリ名を変更する可能性があります。変更する場合は、GitHubで移行方法をご案内します。
1. なぜ作ったのか
本音
この記事はほぼすべてAIに書かせましたが、こちらに作った理由の本音を記載しておきます。
個人製作の中でよくCLIPを使用しています。特定のシーンを検出するのにチューニングに苦労しているのですが、先々週このEmbeddingGemma 2というモデルが登場しました。いろいろと調べていると、CLIPと同様の画像とテキストの比較ができるということがわかりました。しかし、CLIPとは異なる考えのもと作成されているので、CLIPと同様に使えるわけではありません。
なので、「CLIPと同じような感覚でEmbeddingGemma 2を扱えるライブラリがあったら便利じゃね?」という安易な考えから、作ってみることにしました。ちなみに、今回の開発ではコードの実装をほぼすべてAIに任せています。私自身、Pythonライブラリを公開するのは初めてです。
OpenCLIPの使いやすいAPI
OpenCLIPは、画像とテキストを同じ埋め込み空間で扱うCLIP系モデルを利用するためのライブラリです。
例えば、モデルの初期化、画像の前処理、テキストのトークン化、埋め込み生成を次のような流れで実行できます。
import open_clip
model, _, preprocess = open_clip.create_model_and_transforms(
"ViT-B-32",
pretrained="..."
)
tokenizer = open_clip.get_tokenizer("ViT-B-32")
このインターフェースは直感的で、画像とテキストを組み合わせた処理を実装しやすいと感じています。
一方で、EmbeddingGemma 2はOpenCLIPのモデルとしてそのまま扱えるわけではありません。
そこで、EmbeddingGemma 2の公式な推論処理を維持しながら、OpenCLIPに近い書き方で利用できないかと考えました。
EmbeddingGemma 2を利用したい
EmbeddingGemma 2は、Googleの画像・テキスト対応の埋め込みモデルです。
画像とテキストの両方を768次元の特徴ベクトルに変換でき、ベクトル間の類似度を利用することで、画像と文章の関連性を計算できます。
例えば、次のような用途を想定できます。
- テキストから関連する画像を検索する
- 画像に近い説明文を選択する
- 複数の文章候補と画像の類似度を比較する
- 画像とテキストを利用した検索システムを試作する
そこで、OpenCLIPに似たAPIでEmbeddingGemma 2を扱える、推論専用のラッパーライブラリを作成しました。
2. openclip-gemmaの特徴
主な特徴は以下のとおりです。
| 項目 | 内容 |
|---|---|
| 対応モデル | Google EmbeddingGemma 2 |
| 画像入力 | 対応 |
| テキスト入力 | 対応 |
| 日本語テキスト | 対応 |
| 埋め込み次元 | 768次元 |
| API | OpenCLIP風 |
| バッチ推論 | 対応 |
| Zero-shot類似度計算 | 対応 |
| 推論エンジン | PyTorch |
| 学習・Fine-tuning | 非対応 |
| 動画・音声 | 本ライブラリでは非対応(モデル自体は対応) |
公式モデルの画像前処理、トークナイザー、プロンプト、プーリング、正規化の処理を維持するように実装しています。
また、モデル重みはパッケージに同梱していません。実行時にHugging Faceから取得します。
モデルには特定のリビジョンを指定しており、モデル側の更新によって意図せず推論結果が変化するリスクを抑えています。
なお、OpenCLIPの完全な互換実装ではありません。 OpenCLIPのすべてのモデルやAPIを実行できるわけではなく、EmbeddingGemma 2専用の推論ラッパーです。
3. インストール
現時点で検証済みの環境は以下です。
- Windows 11 x64
- Python 3.12
- CPU(FP32およびBF16)
Linux、macOS、CUDAについては、v0.1.0時点では動作検証が完了していません。
まず仮想環境を作成します。
python -m venv .venv
Windowsの場合は以下で有効化できます。
.\.venv\Scripts\Activate.ps1
続いてインストールします。
python -m pip install openclip-gemma==0.1.0
PyTorchなど、推論に必要な依存ライブラリもインストールされます。
初回のモデル読み込みでは、Hugging Faceからモデルファイルを取得するため、ネットワーク接続とダウンロード時間、ディスク容量が必要です。
4. 基本的な使い方
4.1 モデルの読み込み
まず、EmbeddingGemma 2を読み込みます。
import torch
import openclip_gemma
device = "cpu"
model, _, preprocess = (
openclip_gemma.create_model_and_transforms(
"embeddinggemma-2",
device=device,
)
)
model.eval()
create_model_and_transforms()は、OpenCLIPに近い形式でモデルと画像前処理関数を返します。
今回はCPUを明示しています。
4.2 画像を768次元のベクトルに変換する
Pillowで画像を読み込み、画像の埋め込みを生成します。
from PIL import Image
image = Image.open("sample.jpg").convert("RGB")
image_tensor = (
preprocess(image)
.unsqueeze(0)
.to(device)
)
with torch.inference_mode():
image_features = model.encode_image(
image_tensor,
normalize=True,
)
print(image_features.shape)
出力例:
torch.Size([1, 768])
1枚の画像から768次元の特徴ベクトルが得られます。
ここで注意したいのは、画像に対してライブラリが返すpreprocessを使うことです。
EmbeddingGemma 2固有の画像前処理を想定しているため、別のCLIPモデル用に正規化したテンソルをそのまま渡すことはできません。
4.3 日本語テキストをベクトルに変換する
続いて、テキストの埋め込みを生成します。
tokenizer = openclip_gemma.get_tokenizer(
"embeddinggemma-2"
)
texts = [
"机の上に置かれたノートパソコン",
"公園を走る犬",
"青空の下にある建物",
]
tokens = tokenizer(texts).to(device)
with torch.inference_mode():
text_features = model.encode_text(
tokens,
normalize=True,
)
print(text_features.shape)
出力例:
torch.Size([3, 768])
3つの日本語テキストそれぞれについて、768次元の特徴ベクトルが得られます。
get_tokenizer()を利用することで、モデルが必要とするトークン化を実行できます。
5. 画像と文章の類似度を計算する
ここが今回のライブラリで特に試していただきたい部分です。
画像と複数の説明文を比較し、どの文章が画像に近いかを計算します。
import torch
from PIL import Image
import openclip_gemma
device = "cpu"
# モデルの読み込み
model, _, preprocess = (
openclip_gemma.create_model_and_transforms(
"embeddinggemma-2",
device=device,
)
)
model.eval()
# 画像の読み込み
image = Image.open("sample.jpg").convert("RGB")
image_tensor = preprocess(image).unsqueeze(0).to(device)
# 比較したいテキスト
candidate_texts = [
"机の上に置かれたノートパソコン",
"公園を走る犬",
"青空の下にある建物",
]
# 分類タスク用の公式プロンプトを使用
tokenizer = openclip_gemma.get_tokenizer(
"embeddinggemma-2",
prompt_name="Classification",
)
text_tokens = tokenizer(candidate_texts).to(device)
# 埋め込み生成と類似度計算
with torch.inference_mode():
image_features = model.encode_image(
image_tensor,
normalize=True,
)
text_features = model.encode_text(
text_tokens,
normalize=True,
)
similarities = image_features @ text_features.T
# 結果表示
scores = similarities[0].tolist()
for text, score in zip(candidate_texts, scores):
print(f"{text}: {score:.4f}")
best_index = similarities[0].argmax().item()
print("最も類似度が高い文章:")
print(candidate_texts[best_index])
image_features @ text_features.Tは、正規化された画像・テキストの特徴ベクトル間の内積を計算しています。
これはコサイン類似度として利用できます。
類似度が高いほど、その画像と文章がモデルの埋め込み空間上で近いことを意味します。
ただし、類似度は分類確率ではありません。
例えばスコアが0.75であっても、「75%の確率で正解」という意味ではありません。
また、最も高いスコアの文章が常に正しいとは限りません。用途ごとに候補文の設計や精度評価が必要です。
6. OpenCLIPからの移行イメージ
既存のOpenCLIPを利用した推論コードでは、次のような書き方をしていることがあります。
import open_clip
model, _, preprocess = (
open_clip.create_model_and_transforms(
"ViT-B-32",
pretrained="...",
)
)
tokenizer = open_clip.get_tokenizer("ViT-B-32")
openclip-gemmaの場合は次のようになります。
import openclip_gemma as open_clip
model, _, preprocess = (
open_clip.create_model_and_transforms(
"embeddinggemma-2",
device="cpu",
)
)
tokenizer = open_clip.get_tokenizer(
"embeddinggemma-2"
)
初期化部分の構造が似ていることが分かると思います。
さらに、次のAPIも利用できます。
model.encode_image(...)
model.encode_text(...)
このため、画像とテキストを別々にエンコードして類似度を計算するような処理を、比較的近い構成で記述できます。
ただし、モデルごとの前処理、トークン化、プロンプト、埋め込み特性は異なります。
既存のOpenCLIPコードをimport文だけ変更すれば必ず動くというものではありません。
詳細は互換性レポートに整理しています。
7. 実装・精度検証について
今回の開発では、APIを似せるだけではなく、公式の推論結果と大きく乖離しないことを重要視しました。
そのため、公式のSentence Transformers経由の推論結果と、openclip-gemmaの出力を比較しています。
Windows 11、Python 3.12、CPU FP32で測定した最大絶対誤差は、以下のとおりでした。
| 比較対象 | 最大絶対誤差 |
|---|---|
| 画像埋め込み | 約2.74 × 10⁻⁷ |
| テキスト埋め込み | 約2.81 × 10⁻⁶ |
| 画像・テキスト類似度 | 約1.19 × 10⁻⁶ |
特定のモデルリビジョンとテスト入力・実行環境における比較結果ですが、公式の推論経路と非常に近い値が得られています。
また、リリース前には以下を確認しています。
- pytest:75件成功、9件スキップ
- 実モデルを使ったCPU統合テスト:7件成功、CUDA専用の2件をスキップ
- Ruffによる静的チェック
- wheel・sdistのビルドと内容検査
- TestPyPIからクリーン環境へのインストール
- 画像・日本語テキストを利用した実モデル推論
- GitHub ActionsによるCIとPyPIへの自動公開
ただし、これらは推論処理の整合性や配布の正常動作を確認するものであり、個別の画像分類・検索タスクにおける精度を保証するものではありません。
8. 現時点での制約
v0.1.0は初期リリースのため、機能を限定しています。
特に次の点には注意してください。
対応モデルはEmbeddingGemma 2のみです。
任意のCLIPモデルを読み込むことはできません。
推論専用です。
学習やFine-tuningには対応していません。
動画・音声には対応していません。
現在は静止画像とテキストの埋め込み生成を対象としています。
CUDA、Linux、macOSは未検証です。
CUDA向けのコードは実装していますが、GPU実機での動作確認は今後の課題です。
OpenCLIPの完全互換ではありません。
forward()、get_logits()など、一部のOpenCLIPで使われる機能は提供していません。また、OpenCLIPのlogit_scaleを再現するものではありません。
9. 今後について
今後は、実際の利用状況を見ながら、対応環境の拡大や使い勝手の改善を検討したいと考えています。
例えば、CUDA環境での実機検証、Linux・macOSでの動作確認、サンプルコードやドキュメントの充実などが考えられます。
初期バージョンのため、不具合や改善点があればGitHub Issuesで報告いただけるとうれしいです。
おわりに
今回は、EmbeddingGemma 2をOpenCLIP風のAPIで扱えるPythonライブラリ「openclip-gemma」を紹介しました。
画像とテキストの埋め込み生成や類似度計算を、できるだけシンプルなコードで実行できるようにしたライブラリです。
EmbeddingGemma 2を試してみたい方や、OpenCLIPに近い書き方で画像・テキスト埋め込みを扱いたい方の参考になれば幸いです。
まだv0.1.0の初期段階ですが、ぜひ試してみてください。
GitHub: https://github.com/kotadymo/openclip-gemma
PyPI: https://pypi.org/project/openclip-gemma/
なお、本ライブラリは個人開発の非公式プロジェクトであり、Google、Google DeepMind、OpenCLIPの公式実装・公式配布物ではありません。ライブラリ本体はApache-2.0で公開しています。モデル重みは同梱しておらず、利用時にはGoogleのモデルに適用される別途の利用条件をご確認ください。