Japanese Stable CLIP による画像の分類（a.k.a. 画像によるテキストの検索）、そして画像検索に向けて

【更新】CPUでも動作することを確認しました（2024年7月13日）
![UI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/9596bb9e-2143-88a5-5cf1-0ed4c7b9bda9.png)

## はじめに
Stability AIが開発した[Japanese Stable CLIP](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16)モデルを使用して、画像分類を試してみました。Stability AIが公開している[デモアプリケーション](https://colab.research.google.com/github/Stability-AI/model-demo-notebooks/blob/main/japanese_stable_clip.ipynb)に分類カテゴリーの編集機能を追加するなど若干の変更を加えています。~~本デモアプリケーションは、NVIDIA CUDA 対応の GPU を搭載したハードウェアで稼働させることを前提としています~~（CPUでも動作することを確認しました。ただし、特徴ベクトル（エンベディング）生成時に多くのメモリが必要です。このデモアプリのデフォルトの分類カテゴリーの場合、最大で4.3GB程が必要です。メモリー要件が厳しい場合は分類カテゴリーを減らしてみてください）。なお、UI には Gradio を使用しています。
このブログではまずは**動かしてみる**ことを主眼に設定と操作方法を説明しています。その後に**コードの核心部分を解説**しています。Hugging Face transformers の AutoModel を使った推論コードは簡潔で素晴らしいのですがドキュメントとソースコードを読み込まないとブラックボックス感があってもやもやしていたので少し調べてみた内容を記載しています。同じようなもやもや感を持っている方には参考にしていただける内容かと思います。また、コード解説を読んでいただければテキストや画像のエンベディングの取得方法もわかりますので**テキストによる画像検索**のようなアプリケーションに取り組まれる方のお役にも立てるのではないかと思っています。

https://github.com/kutsushitaneko/Japanese_Stable_CLIP

## Japanese Stable CLIP とは

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/04f6941f-4b60-6664-7c54-cc26afa6ea7a.png)


日本語に特化した画像言語特徴抽出モデルです。マルチモーダル埋め込みモデルとも呼ばれます。画像の特徴ベクトル（エンベディング）とテキストの特徴ベクトル（エンベディング）を同じ埋め込み空間内に抽出（生成）することができます。つまり、モデル単体で任意の日本語テキストから画像を検索する画像検索やその逆であるテキスト検索（分類）が可能です（画像を説明するキャプションの生成（Image Captioning）や画像に関する質問に答えるVisual Question Answering (VQA)が可能な Vision-Language モデルとは異なります。stability ai のVision-Language モデルには、Japanese Stable VLM があります）。
### CLIP とは
モデル名の最後の "CLIP" は、"Contrastive Language-Image Pre-training" の略でオリジナルのモデルは2021年にOpenAIによって公開されました（OpenAIの[ブログ](https://openai.com/index/clip/)、[実装](https://github.com/OpenAI/CLIP)）。CLIPは、インターネット等から収集された大規模な画像とテキストのペア（4億ペア）を用いて学習しています。学習は以下のような行われます。

<table>
<tr>
<td>
<img src="https://images.ctfassets.net/kftzwdyauwt9/fbc4f633-9ad4-4dc2-3809c22df5e0/0bd2d5abf90d052731538613e4a42668/overview-a.svg" alt="Figure 1. Summary of our approach. While standard image models jointly train an image feature extractor and a linear classifier to predict some label, CLIP jointly trains an image encoder and a text encoder to predict the correct pairings of a batch of (image, text) training examples. At test time the learned text encoder synthesizes a zero-shot linear classifier by embedding the names or descriptions of the target dataset’s classes.">
</td>
</tr>
<tr>
<td>
Learning Transferable Visual Models From Natural Language Supervision
(https://arxiv.org/abs/2103.00020) Figure 1. Summary of our approach.
</td>
</tr>
</table>

- 収集したデータセットからN個の画像-テキストのペアをピックアップします（オリジナルの CLIP では、32768 のミニバッチ毎）
- N個の画像とN個のテキストそれぞれの特徴ベクトルを計算します（画像とテキストは同一の埋め込み空間にマッピングされます）
- N個の画像の特徴ベクトルとN個のテキストの特徴ベクトルのすべての組み合わせの類似度を計算します（N^2組）
- 画像とテキストの組み合わせが関連のあるペアである場合（上図の対角の水色のN組）に類似度が高くなり、無関係な組み合わせである場合（N^2 - N組）には類似度が低くなるように学習します

テキストの特徴ベクトルを生成するテキストエンコーダーには transformer を、画像の特徴ベクトルを生成する画像エンコーダーには、ResNet（CNNをベースにしたモデル）かVision Transformer (ViT)を利用することができます。

従来の画像分類モデルは分類可能なカテゴリー（分類クラス）が学習に用いられたラベルだけに限定されていましたが、CLIPは大規模な画像・テキストのペアを学習していることからFine-tuningをすることなく任意の分類カテゴリーに対する分類が可能です。これはゼロショット画像分類と呼ばれています（任意のテキストを用いた画像検索も可能です）。
### Japanese Stable CLIP の特徴
Japanese Stable CLIP では、画像エンコーダーには、ViTを使用し、学習手法に[SigLIP（Sigmoid loss for Language-Image Pre-training）](https://arxiv.org/abs/2303.15343)が採用されています。従来の対照学習が正規化のためにバッチ内のすべての画像・テキストペアの情報を必要とするsoftmax関数に基づいていたのに対して、SigLIPでは、各画像-テキストペアを独立してSigmoid 関数を適用することで効率的な学習を実現しています。この効率的な学習により画像と言語の特徴抽出の精度が向上することを期待できます。また、[Conceptual 12M(CC12M)](https://github.com/google-research-datasets/conceptual-12m)のキャプションを日本語に翻訳したデータセットなどを用いることで日本語に対応しています。詳しくは、Stability AI社の[ブログ](https://ja.stability.ai/blog/japanese-stable-clip)と [Hugging Face の Model Card](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16) を参照してください。

## 本デモアプリケーションの機能
- 画像のテキスト分類（画像をクエリーとしたテキスト検索）
- 分類したい画像（クエリーとなる画像）をアップロード、または、サンプル画像から選択
- カスタマイズ可能な分類カテゴリ（検索対象となるテキスト群）
- 分類結果（検索結果）の表示（上位3カテゴリ）
- Gradio による UI
- サンプル画像は、Wikimedia Commons でライセンスが "制限なし" となっているものを使用させていただいています

## 前提条件
- Hugging Face のアカウントがあること（[Hugging Face アカウント作成ページ](https://huggingface.co/join) で無料で登録することができます）
- Hugging Face の User Access Token を取得していること（[Hugging Face User Access Token 作成ページ](https://huggingface.co/settings/tokens) で無料で取得できます。"Type" は、"READ"）
- Python 3.10以上（3.11.9にて動作確認） - venv などの Python 仮想環境を推奨
- （オプション）NVIDIA CUDA 対応の GPU を搭載したハードウェア
- （オプション）CUDA Toolkit がインストールされていること
    - CUDA対応の GPU を搭載している環境で CUDA Toolkit 未インストールの場合は、[PyTorch Get-Startedページ](https://pytorch.org/get-started/locally/) で PyTorch がサポートする CUDA Toolkit バージョンを確認して、CUDA Toolkit をインストールします。CUDA Toolkit の最新バージョンは、[こちら](https://developer.nvidia.com/cuda-downloads)、以前のバージョンは[こちら](https://developer.nvidia.com/cuda-toolkit-archive)です。なお、インストールする CUDA Toolkit のバージョンは、nvidia-smi で表示される CUDA Version 以下（同じか未満）である必要があります。以下は、nvidia-smi の出力例（一部）です

        ```
        nvidia-smi
        ```
        ```
        Thu Jul  4 00:21:03 2024       
        +---------------------------------------------------------------------------------------+
        | NVIDIA-SMI 546.33                 Driver Version: 546.33       CUDA Version: 12.3     |
        |-----------------------------------------+----------------------+----------------------+
        | GPU  Name                     TCC/WDDM  | Bus-Id        Disp.A | Volatile Uncorr. ECC |
        | Fan  Temp   Perf          Pwr:Usage/Cap |         Memory-Usage | GPU-Util  Compute M. |
        |                                         |                      |               MIG M. |
        |=========================================+======================+======================|
        |   0  NVIDIA GeForce RTX 2060      WDDM  | 00000000:09:00.0  On |                  N/A |
        | 33%   34C    P8              12W / 160W |   1033MiB /  6144MiB |      1%      Default |
        |                                         |                      |                  N/A |
        +-----------------------------------------+----------------------+----------------------+
        ```
- PyTorch パッケージがインストールされていること（CPUを使用する場合は、CPU用の PyTorchを、GPUを使用する場合は適切な CUDA バージョンに対応したPyTorchをインストールします ）
    - GPUを使用する場合はインストールされている CUDA Toolkit のバージョンは次のコマンドで確認できます
        ```
        nvcc --version
        ```
    - PyTorch のインストール方法は [PyTorch Get-Startedページ](https://pytorch.org/get-started/locally/) でOS情報、CUDAバージョン or CPUなどを選択するとインストールに使用するコマンドを確認できます
        ![PyTorch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/f8a063e2-d0c7-cb7b-cf82-27676d740973.png)

        この例では、インストールすべき PyTorch は CUDAバージョン 11.8 に対応したものでインストールコマンドは次のようになります。
        ```
        pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu118
        ```
- [stabilityai/japanese-stable-clip-vit-l-16](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16) でコンタクト情報の登録が完了していること
    ![stability_regist_contact.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/a6eb7761-3461-5a08-47a4-cd55d6baa051.png)

## セットアップ

- リポジトリをクローンします

    ```
    git clone https://github.com/kutsushitaneko/Japanese_Stable_CLIP.git
    cd Japanese_Stable_CLIP
    ```

## アプリケーションの起動方法

### Jupyter Notebook の場合

1. Jupyter Notebookを起動し、`JSClipImageClassification.ipynb`を開きます

2. ノートブックのセルを順番に実行します

3. 「Hugging Face へのログイン」で OCI Data Science ノートブック環境以外の場合は、`notebook_login()` のセルを実行し、Hugging Face のロゴが表示されたらトークン（User Access Token）を入力します。
    ![HF.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/7c29638a-e837-c446-ad81-a8e2bef49034.png)

    ここで、トークンを Git の認証情報として追加するかを選択します。どちらでも本アプリケーションの実行には影響ありません
    "Login" ボタンをクリックしてログインします。

    OCI Data Scienceの場合は、Launcher から Terminal を開いて、[Python スクリプトの場合の 2.](https://qiita.com/yuji-arakawa/items/042937eaf16fa00cf491#python-%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E5%A0%B4%E5%90%88:~:text=Hugging%20Face%20%E3%81%AB%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%97%E3%81%BE%E3%81%99) の`huggingface-cli login`を実行します。
    後続のノートブックのセルを順番に実行します
    途中 "error displaying widget: model not found" のようなエラーメッセーじが表示される場合は、モデルなどをダウンロードする際のプログレスバーが表示できない環境（OCI Data Science Notebook）であるということですので無視してください。

4. 最後のセルを実行すると、次のようなメッセージが表示されます。同時にセルの中とブラウザの両方にGradioのインターフェースが表示されます。どちらも同じ機能を使うことができます
    ```
    Running on local URL:  http://127.0.0.1:7860
    Running on public URL: https://0xxxxxxxxxxxxxxxxxxxxxxx.gradio.live

    This share link expires in 72 hours. For free permanent hosting and GPU upgrades, run `gradio deploy` from Terminal to deploy to Spaces (https://huggingface.co/spaces)
    ```

    環境によっては自動的にブラウザが立ち上がらないことがあります。その場合は、ブラウザを起動して、アプリケーションをローカルで起動している場合は local URL: `http://127.0.0.1:7860` にアクセスしてください。
    また、クラウドサービスなどリモートで起動している場合は、public URL: `https://xxxxxxxxxxxxxxxxxxxxxxx.gradio.live` にアクセスしてください。

5. 画像をアップロードするか、サンプル画像を選択して「分類実行」ボタンをクリックします

6. 必要に応じて、分類カテゴリを編集できます

### Python スクリプトの場合


1. 必要なライブラリをインストールします：
    ```
    pip install -U ftfy transformers sentencepiece gradio huggingface_hub accelerate protobuf
    ```
2. Hugging Face にログインします
    ターミナル（コマンドプロンプト）で次のコマンドを実行します
    ```
    huggingface-cli login
    ```
    Hugging Face のバナーが表示されてトークン（User Access Token）の入力を促されます。
    ```
    (.venv) Japanese_Stable_VLM $ huggingface-cli login

        _|    _|  _|    _|    _|_|_|    _|_|_|  _|_|_|  _|      _|    _|_|_|      _|_|_|_|    _|_|      _|_|_|  _|_|_|_|
        _|    _|  _|    _|  _|        _|          _|    _|_|    _|  _|            _|        _|    _|  _|        _|
        _|_|_|_|  _|    _|  _|  _|_|  _|  _|_|    _|    _|  _|  _|  _|  _|_|      _|_|_|    _|_|_|_|  _|        _|_|_|
        _|    _|  _|    _|  _|    _|  _|    _|    _|    _|    _|_|  _|    _|      _|        _|    _|  _|        _|
        _|    _|    _|_|      _|_|_|    _|_|_|  _|_|_|  _|      _|    _|_|_|      _|        _|    _|    _|_|_|  _|_|_|_|

        To login, `huggingface_hub` requires a token generated from https://huggingface.co/settings/tokens .
    Token can be pasted using 'Right-Click'.
    Enter your token (input will not be visible):
    ```
    ここで Hugging Face のトークン（User Access Token）を入力します
    ```
    Add token as git credential? (Y/n)
    ```
    ここで、トークンを Git の認証情報として追加するかを選択します。どちらでも本アプリケーションの実行には影響ありません
    トークンが正しければ次のように表示されます
    ```
    Token is valid (permission: read).
    Your token has been saved to C:\Users\yujim\.cache\huggingface\token
    Login successful
    ```

3. ターミナルで `JSClipImageClassification.py` を起動します
    ```
    python JSClipImageClassification.py
    ```
4. 画像をアップロードするか、サンプル画像を選択して「分類実行」ボタンをクリックします

5. 必要に応じて、分類カテゴリを編集できます

6. アプリケーションを終了するには、ターミナルで Ctrl-C キーを入力してください

## 使い方
アプリケーションを起動すると次のような画面が表示されているはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/dab284e2-f3d4-1487-74c7-6ac17258d32a.png)

### Examples 画像の分類
画面左下の "Examples" から好きな画像をクリックして選択します。選択した画像が左上の領域に表示されます。例えば、右から2番目の画像を選択してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/f94270bb-20e3-7e8b-ba7c-79be3a7967ea.png)
左下 "Examples" の上にある"分類実行"をクリックすると分類タスクが開始されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/3d7ccd97-c386-35c3-5f38-b0688cfc32bc.png)
分類タスクが完了すると右上に分類結果が表示されます。ここでは、"犬"と分類されました。

### 分類カテゴリーの確認と編集
分類先のカテゴリは、分類結果の下にある"分類先のカテゴリー一覧"をクリックすると確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/630f0538-d9b9-7fad-0cf0-975d0c3dae4d.png)
デフォルトでは、「配達員, 営業, 消防士, 救急隊員, 自衛隊, スポーツ選手, 警察官, 動物, 猫, 犬, うさぎ, ライオン, 虎, 乗り物, 飛行機, 自動車, 自転車, タクシー, バイク, ロケット, 宇宙船, 兵器, 戦闘機, 戦車, 空母, 護衛艦, 潜水艦, ミサイル, 植物, 鳥, 魚, 花, 樹木, 人間, 子供, 老人, 女性, 男性, 少女, 少年, サッカー選手, アスリート, アニメキャラクター, ぬいぐるみ, 妖精, エルフ, 天使, 魔性使い, 魔法少女, 歌姫, 歌手」がセットされています。
この分類カテゴリーは編集することができます。好きな単語を半角カンマ区切りで列挙します。
例えば、「白猫,黒猫,三毛猫,ライオン,虎,キュウべえ」などと入力して "カテゴリーを更新" をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/1162e88f-ad6a-eb67-d771-2c3829ae0485.png)
今度は、"Examples" で左から2番目の画像を選択して分類実行をクリックしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/33bb158c-2397-c329-3ac9-17d95df4573e.png)
黒猫と分類されました。
カテゴリーは、"デフォルトに戻す"をクリックすると初期設定に戻ります。なお、"カテゴリーを更新"で編集したカテゴリーはセッション中にだけ有効で、アプリケーションを再起動するとデフォルトに戻ります。また、同時に起動している別のセッションにも影響を与えません（反映されません）。
### 画像のアップロード
画像の右上の![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/6edf6ba7-80f6-3eb9-bb59-0052e0be6a03.png)をクリックして画像を閉じるか、画像下の上矢印![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/11c6681e-f337-c070-099f-8590cbe672b2.png)をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/5111fc91-68de-7f52-bb0f-83dffce4d81d.png)
この領域に画像ファイルをドラッグ＆ドロップするかクリックしてアップロードします（写真は我が家のアイドルはなちゃんです）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/eb57aaac-03ff-4758-33cb-10a0e86d1350.png)
## 分類例
Examples の右端の画像をカテゴリーを変えて分類してみます。
- 性別（男性,女性）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/a6e1c14a-ad31-273f-685b-498db4959b43.png)
- 子供,大人,高齢者
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/eb649f18-8a3d-1c11-56ec-487dae47d12c.png)
- ハンドバッグ, バックパック, ショルダーバッグ, ウェストポーチ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/e522a651-99e5-7af5-7954-77f16567563b.png)

- オートバイ,自動車,自転車,三輪車
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/8b8cfbc2-df23-4576-9f86-8d2411f6ad40.png)
- 川,海,湖,貯水池
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/9f5684a4-2afe-a90f-3152-2e5202cca9d8.png)
- 高速道路, 橋, 陸橋
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/b2f8cd56-574d-5ada-6e8a-b9dffc2c8401.png)

- 高層ビル,マンション,戸建て,教会,寺院,神社
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/a9a1770a-eff6-45b6-a4c0-0d564cbad748.png)
- 晴天,曇天,雨天
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/b357069b-f926-d6c9-afc9-3f8260ae4e39.png)
- 快晴, 晴れ, 薄曇り, 曇天, 小雨, 雨天, 霧雨, 濃霧, 雪
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/60df0ef9-7de6-ad82-f2d3-81d77aa3b7ef.png)

次は、左から3番目の画像を分類してみます
- プロペラ機, ジェット機, ヘリコプター, 可変翼機
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/fe14775a-f7fd-7993-8250-b320da8b0f80.png)
- プライベートジェット, 旅客機, 軽飛行機, 戦闘機, 爆撃機, 哨戒機, 早期警戒機, 空中給油機
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/9939f3aa-ec07-51de-8162-d84a207e926d.png)
- 陸地, 海上, 滑走路, 草原, 砂漠, 飛行甲板
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/ca2685f2-ae20-9f85-c6ba-3c6a8fb72034.png)
- 離陸, 着陸, 水平飛行, 墜落
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/edfe5c8d-1703-d3e0-3a2d-a6b62ef80be4.png)
- ギア・アップ, ギア・ダウン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/cfb66f59-341f-f947-1bbc-b93162a74de1.png)
- エアバス, ボーイング, ボンバルディア, コムアク, ダッソー・アビアシオン, 三菱重工業
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/7f44b3ac-8f8c-2bba-de37-a1de829eabe4.png)
- 快晴, 晴れ, 薄曇り, 曇天, 小雨, 雨天, 霧雨, 濃霧, 雪
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/b618e697-f5d4-0b57-3f5c-80f90d1901b1.png)

次は、推しの子・星野アイのフィギュアです（画像は自分で撮った写真をアップロードしています）。
- フィギュア, ぬいぐるみ, ドール, 人間
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/ec237abe-3027-7cad-81b7-f46d89e12637.png)
- 黒髪, 赤い髪, 青い髪, 金髪, 銀髪, ピンク色の髪, 緑色の髪, 紫色の髪
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/c07cd2cf-80f5-8243-00b8-4661d6d0129c.png)
- 黒い衣装, 赤い衣装, 青い衣装, 金色の衣装, 銀色の衣装, ピンク色の衣装, 緑色の衣装 紫色の衣装, 白い衣装
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/3643bc6d-25ce-2163-e0f0-614f021bdb42.png)
- 靴を履いている, サンダルを履いている, 裸足
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/32550e2d-bcec-5f21-972f-c3f1e20298d6.png)
- アイドル, 歌手, 俳優, 女優, 声優, 音楽家, アスリート, 政治家
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/68433d31-100a-2507-cff2-6eddf556f2aa.png)
- 両足で立っている, 片足で立っている, 飛び上がっている, 寝ている, 座っている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/0e06b157-6f18-681a-894c-afe8ba1bd76e.png)


## コードの説明
Hugging Face Transformers の AutoModel を使ったモデルのロードとインスタンス化の裏側を少し説明してみたいと思います。インスタンス化したモデルでは何ができるのか、どのようなメソッドがあるのかを知りたいと思ったら まずはModel Hub にあるサンプルコードやモデルプロバイダーが公開しているデモコードを確認するかと思います。でも、これらのサンプルに無い機能・メソッドには何があるのか知りたいと思っても途方に暮れてしまうことはないでしょうか？そんな動機で裏側を調べてみました。

### Transformers AutoModel クラスについて
```JSClipImageClassification.ipynb
device = "cuda" if torch.cuda.is_available() else "cpu"
model_path = "stabilityai/japanese-stable-clip-vit-l-16"
model = AutoModel.from_pretrained(model_path, trust_remote_code=True).eval().to(device)
```
ここでは、`AutoModel`クラスの `from_pretrained` メソッドを使ってモデルのロードとインスタンス化を行っています。
- `AutoModel.from_pretrained()`は、指定されたモデルをインスタンス化します
    - モデル名もしくはモデルパスを引数に指定します。指定するモデルは、Hugging Face の [モデルハブ](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16)に登録されているものである必要があります
    - `trust_remote_code=True`は、モデルに関連する追加のカスタムコードをリモートソース（通常はHugging Face Hub）からダウンロードして実行することを許可するものです。Japanese Stable CLIP に固有のコードを[モデルハブ](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16)からダウンロードして実行するために指定します

`AutoModel.from_pretrained()`が呼び出されると裏側では次のような処理が自動的に行われます。
- `AutoModel.from_pretrained()`は、引数で渡されたモデル名もしくはモデルパスを元に`AutoConfig.from_pretrained()` によって[config.json](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16/blob/main/config.json)を取得します

- `config.json` の `auto_map` ディクショナリの `AutoModel` に指定されているカスタムモデルの実装を取得します

    ```config.json
    "auto_map": {
        "AutoModel": "modeling_japanese_stable_clip.JapaneseStableCLIPModel",
        "AutoConfig": "configuration_japanese_stable_clip.JapaneseStableCLIPConfig"
    },
    ```

    - このモデルの場合には、[modeling_japanese_stable_clip.JapaneseStableCLIPModel](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16/blob/main/config.json#:~:text=modeling_japanese_stable_clip.JapaneseStableCLIPModel) が指定されていますので、[modeling_japanese_stable_clip.py](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16/blob/main/modeling_japanese_stable_clip.py) の `class JapaneseStableCLIPModel(CLIPModel)` をインスタンス化します

        ```modeling_japanese_stable_clip.py
        class JapaneseStableCLIPModel(CLIPModel):
        ```
    - このクラスは CLIPModel を継承し、日本語に特化した CLIP モデルを実装しています。このプロセスは自動的に行われ、ユーザーが直接 modeling_japanese_stable_clip.py を呼び出す必要はありません。これにより、標準の Transformers ライブラリの実装とは異なるカスタムモデルを簡単に使用することができます

### JapaneseStableCLIPModel クラスについて
ここまでたどり着けばモデルにどのような機能があるのかは JapaneseStableCLIPModel クラスの実装から知ることができます
- `class JapaneseStableCLIPModel(CLIPModel)` の[コード](https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16/blob/main/modeling_japanese_stable_clip.py#:~:text=class%20JapaneseStableCLIPModel(CLIPModel)%3A)を見てみると以下のようなメソッドが定義されています
    - `get_text_features()` : 入力テキストの特徴ベクトルを抽出します。テキストモデルの出力から最初のトークン（[CLS]）の表現を使用して（トークンのシーケンス全体を代表する特徴ベクトルと見做して）、プロジェクション層を通して最終的な特徴ベクトルを生成します。このときテキストベクトル（Japanese Stable CLIPでは、内部的には768次元）と画像ベクトル（Japanese Stable CLIPでは、内部的には1024次元）の次元数を一致させるための射影（3次元の物体と2次元の平面に投影するのと同様に次元を削減すること）も行われます。射影後の次元数は768次元です。なお、`get_text_features()`で抽出できる特徴ベクトルは正規化されていません（ベクトルの大きさが1になっていません）
    - `get_image_features()` : 入力画像の特徴ベクトルを抽出します。ビジョンモデルの出力から最初のトークン（[CLS]）の表現を使用して（トークンのシーケンス全体を代表する特徴ベクトルと見做して）最終的な特徴ベクトルを生成します。このときテキストベクトル（Japanese Stable CLIPでは、内部的には768次元）と画像ベクトル（Japanese Stable CLIPでは、内部的には1024次元）の次元数を一致させるための射影も行われます。射影後の次元数は768次元です。なお、`get_image_features()`で抽出できる特徴ベクトルは正規化されていません（ベクトルの大きさが1になっていません）
    - `forward()` :  JapaneseStableCLIPModel のインスタンスに入力として複数の画像と複数のテキストを渡して画像とテキストの全ての組み合わせの類似度を計算する際に呼び出される予測関数です。JapaneseStableCLIPModel は、CLIPModel を継承しており、CLIPModel は PyTorch の torch.nn.Module クラスを継承しているため予測関数は forward() という名前で定義されています。戻り値には以下のようなものがあります（一部）
        - `text_embeds` : L2正規化（ユークリッドノルムによる正規化）されたテキストエンベディング
        - `image_embeds` : L2正規化（ユークリッドノルムによる正規化）された画像エンベディング
        - `logits_per_text` : 各テキストに対する各画像の類似度スコア
        - `logits_per_image` : 各画像に対する各テキストの類似度スコア

### 正規化について
JapaneseStableCLIPModelクラスのソースコードの表記にならって正規化前のベクトルを特徴ベクトル、正規化された特徴ベクトルをエンベディングと表記しています。正規化とはベクトルの大きさ（長さ）を"1"に統一することです。後に2つのベクトルの類似性を評価する際にベクトル間の内積を計算しますがベクトルの大きさが異なると正しく類似性を評価できないことがあるためこの問題を回避するために行う処理です。つまり、内積で類似性を評価する場合、内積が大きなベクトルペアは類似性が高いと考えますが、ベクトルの大きさも内積の計算結果に影響するため類似性評価の中にベクトルの大きさの評価が混在してしまいます。正規化（単位ベクトル化）した場合は内積がコサインに一致しますのでベクトル間の角度による類似性評価となり大きさによる影響を排除できます（角度だけの評価で良いのかという別の問題がありますが多くの用途ではコサイン類似度が採用されています）。なお、モデルが出力するベクトルが正規化されているかされていないかを知っておくことは精度の面だけでなく計算量の面でも重要と考えられます。つまりベクトルがあらかじめ正規化されているにも関わらずコサインを改めて計算することは無駄に計算量を増やすことになりますので、そのような場合には内積だけを計算すればよいこととなります。
以下の2つの関数で正規化が行われています


```JSClipImageClassification.ipynb
def compute_text_embeddings(text):
  if isinstance(text, str):
    text = [text]
  text = tokenize(texts=text)
  text_features = model.get_text_features(**text.to(device))
  text_features = text_features / text_features.norm(p=2, dim=-1, keepdim=True)
  del text
  return text_features.cpu().detach()

def compute_image_embeddings(image):
  image = processor(images=image, return_tensors="pt").to(device)
  with torch.no_grad():
    image_features = model.get_image_features(**image)
  image_features = image_features / image_features.norm(p=2, dim=-1, keepdim=True)
  del image
  return image_features.cpu().detach()
```

`.norm(p=2, dim=-1, keepdim=True)` はベクトルの長さ（L2ノルム（ユークリッドノルム））を計算しています。ベクトルをベクトルの長さで割ることで正規化（単位ベクトル化）を行っています。

### `get_text_features()`、`get_image_features()` と `forward()` の使いわけについて
- `get_text_features()`、`get_image_features()` は、テキスト、もしくは、画像の単体の特徴ベクトル（正規化前）を取得するものです。１つの画像をクエリーとして多数のテキストから類似したテキストを検索するような場合やその逆といった一方のモーダルの特徴ベクトル抽出が大量に必要な場合に効率的に処理することができます。ただし、得られる特徴ベクトルは正規化されていませんので、アプリケーションコードの中で正規化する必要があります
- `forward()` は、複数の画像と複数のテキストを渡して、それらのすべての組み合わせの正規化されたエンベディングや類似度を取得できますので多数の画像・テキストに対するクロスモーダルな処理を簡潔に記述できます。ただし、すべての画像・テキストとそれらのベクトルがメモリに載りきる必要があります

本デモアプリケーションは、あらかじめ複数のテキストのエンベディングを用意しておいて、単一の画像のエンベディングとの類似性を評価するものですので、`get_text_features()`、`get_image_features()` を使用しています。つまり、`compute_text_embeddings()` で `get_text_features()`を使ってカテゴリーのテキスト群の特徴ベクトルを抽出して、それらをL2ノルムで割ることにより正規化されたテキストエンベディングを取得しています。また、画像も`compute_image_embeddings` で同様に処理しています。ここでは分類カテゴリーは編集しない限り固定で、かつ、画像は1つだけですので推論の都度、複数の画像と複数のテキストのすべての組み合わせを同時に処理する `forward()` ではなくあらかじめ `get_text_features()`を使ってテキストのエンベディングを用意しておき、選択された画像を都度`get_image_features()` を使ってエンベディングを計算する方法が向いていることになります

残りの eval と to(device) には以下の機能があります。
- `eval()` は、モデルを評価モード（訓練時にだけ必要な処理を行わないモード）に設定し、ドロップアウトなどの学習時固有の動作を無効化します
- `to(device)` は、モデルを指定されたデバイス（CPUまたはGPU）に移動します

## 検索に向けて
今回は画像の分類を行いましたが画像からテキストを検索したり、その逆にテキストから画像を検索したいケースもあります。むしろ後者の画像検索が一番ポピュラーなユースケースかもしれません。
現在のコードは少数のカテゴリーへの分類タスクですので、すべてのカテゴリーとの類似度を計算した後、その結果にsoftmax関数を適用しています。
```JSClipImageClassification.ipynb - def inference_fn(img, state)
similarity = (100.0 * image_embeds @ text_embeds.T).softmax(dim=-1)
```

この式では以下の処理を行っています：

1. 画像エンベディングとすべてのテキストエンベディングの内積を一度に計算

    - `image_embeds @ text_embeds.T` は行列乗算を表し、各カテゴリーとの類似度を同時に計算します

2. 数値の安定性のために100.0を乗算
3. 計算結果全体にsoftmax関数を適用（dim=-1は最後の次元に沿って計算）

この方法では、まずすべてのカテゴリーとの類似度（内積）を計算し、その後でsoftmax関数を使用して確率分布に変換しています。softmax関数は入力値を指数関数で変換し、その結果を総和で割ることで0から1の範囲に正規化します。これにより、各カテゴリーの相対的な確率を得ています。

### 大規模テキスト検索への拡張時の課題

1. **計算量の問題**:
   - クエリベクトル（画像エンベディング）の数が1で、検索対象のベクトル（テキストエンベディング）の数がNの場合、**すべての内積を計算する**ため、計算量はO(MN)となります（Mは画像エンベディングの次元数）。
   - Nが非常に大きい場合（例：数百万以上のテキスト）、すべての内積を計算することは時間とリソースの面で非現実的になります

2. **メモリ使用量**:
   - 大量のテキストエンベディングをメモリに保持し、すべての内積を同時に計算する必要があるため、システムのメモリ制限に達する可能性があります

### 解決策

1. **近似最近傍検索（Approximate Nearest Neighbor Search）**
   - 完全な内積計算を行わずに効率的に類似ベクトルを見つけることができます
   - 例：HNSW（Hierarchical Navigable Small World）、IVF（Inverted File with Vocabulary Tree）など

2. **ベクトルデータベースの活用**
   - 大規模なベクトルデータの効率的な保存と検索を可能にします
   - 近似最近傍検索を利用することができます
   - 例：FAISS、Pinecone、Weaviate、Qdrant、Chroma、Oracle Database 23ai のAI Vector Searchなど

Oracle Database のベクトル検索機能 Oracle AI Vector Search を使ったマルチモーダル画像検索の記事を書きました。

https://qiita.com/yuji-arakawa/items/9cd485debd5b0d18aca2

## えっと、あとがきです
ちなみに、魔法少女まどか☆マギカやマギアレコードに登場するキュウべえのことは知らないようです。彼は白猫じゃなくて人類にとっては必ずしも味方ではない存在なので知らなくていいです(o・∇・o)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/dd245f2a-6ea5-e8d3-581d-45cc401aaef4.png)
Japanese Stable CLIP は、2023年11月に発表されていますのでこの記事の執筆時点で既に7カ月以上が経過していますのでタイムリーではないのですが GPU メモリーのフットプリントが小さく我が家の RTX 2060 6GB でも楽々動作する手軽さから Japanese Stable CLIP を試してみました。その後も様々なモデルが発表されているため、今後が楽しみです。

### マルチモーダル埋め込みモデル
画像とテキストを同一の埋め込み空間にマッピングしてベクトル化（エンベディングの生成）が可能なモデルの一覧です。商用利用も可能（有償、無償）なモデルをピックアップしていますが、ご利用にあたってはそれぞれのモデルのライセンス条件などを確認してください。
|時期|開発者|モデル名|モデル|記事 <br> プレスリリース|
|---|---|---|---|---|
|2023年11月|stability ai|Japanese Stable CLIP|https://huggingface.co/stabilityai/japanese-stable-clip-vit-l-16|https://ja.stability.ai/blog/japanese-stable-clip|
|2024年1月|リクルート|japanese-clip-vit-b-32-roberta-base|https://huggingface.co/recruit-jp/japanese-clip-vit-b-32-roberta-base|https://blog.recruit.co.jp/data/articles/japanese-clip/|
|2024年5月|LINE|clip-japanese-base|https://huggingface.co/line-corporation/clip-japanese-base|https://techblog.lycorp.co.jp/ja/20240514b|

### Vision-Languageモデル
Image Captioning や Visual Question Answering (VQA)のようなタスクに利用可能な画像からテキストを生成する Vision-Language Model も続々と登場しています。商用利用も可能（有償、無償）なモデルをピックアップしていますが、ご利用にあたってはそれぞれのモデルのライセンス条件などを確認してください。
|時期|開発者|モデル名|モデル|記事 <br> プレスリリース|
|---|---|---|---|---|
|2023年11月|stability ai|Japanese Stable VLM|https://huggingface.co/stabilityai/japanese-stable-vlm|https://ja.stability.ai/blog/japanese-stable-vlm|
|2024年1月|Haotian Liu, Chunyuan Li, Yuheng Li, Bo Li, Yuanhan Zhang, Sheng Shen, Yong Jae Lee|LLaVA-NeXT|https://github.com/haotian-liu/LLaVA/blob/main/docs/MODEL_ZOO.md|https://github.com/LLaVA-VL/LLaVA-NeXT|
|2024年5月|openbmb|MiniCPM-Llama3-V-2_5|https://huggingface.co/openbmb/MiniCPM-Llama3-V-2_5|https://github.com/OpenBMB/MiniCPM-V <br> https://unfoldai.com/minicpm-llama3-v-2-5-review/|
|2024年6月|CyberAgent|llava-calm2-siglip|https://huggingface.co/cyberagent/llava-calm2-siglip|https://www.cyberagent.co.jp/news/detail/id=30344|
|2024年7月|InternLM|InternLM-XComposer-2.5|https://github.com/InternLM/InternLM-XComposer|https://github.com/InternLM/InternLM-XComposer <br> https://www.youtube.com/watch?v=8tYpiQNOJww|
|2024年8月|Sakana AI|Llama-3-EvoVLM-JP-v2|https://huggingface.co/SakanaAI/Llama-3-EvoVLM-JP-v2|https://sakana.ai/evovlm-jp/|
|2024年8月|Axcxept co., ltd.|Llama-3-EZO-VLM-1|https://huggingface.co/HODACHI/Llama-3-EZO-VLM-1|https://prtimes.jp/main/html/rd/p/000000006.000129878.html|

### Encoder-Free Vision-Language モデル
そして、もしかするとラスボス登場かもという感じで気になるのは、最近の主要な LLM 同様にエンコーダーを持たずデコーダーのみで構成される Encoder-Free Vision-Language モデルも登場しています。ビジョンエンコーダーで画像がばらばらのパッチに分解されたり、アスペクト比や解像度の調整のために情報が失われることはなくなるので、ビジネスの世界で不可欠な図表や表の認識精度の向上に近い将来貢献してくれるのではないかと期待（妄想）しています。
|時期|開発者|モデル名|モデル|記事 <br> プレスリリース <BR> 論文|
|---|---|---|---|---|
|2024年6月|BAAI|EVE|https://huggingface.co/collections/BAAI/eve-668682c01119f961e557072b|https://github.com/baaivision/EVE <BR> https://arxiv.org/abs/2406.11832|

終わりだよ～(o・∇・o)

## おまけ
他にもいろいろ記事を書いていますので良かったらお立ち寄りください。

https://qiita.com/yuji-arakawa/items/28f30a5434ba429f3f16

https://qiita.com/yuji-arakawa/items/8d065ae3df536f5cd559

https://qiita.com/yuji-arakawa/items/fd4fd0c026ecfa664d97

https://qiita.com/yuji-arakawa/items/1a8cfeff8f81ba808389

https://qiita.com/yuji-arakawa/items/654d936ca616334286b0

https://qiita.com/yuji-arakawa/items/1a8cfeff8f81ba808389

https://qiita.com/yuji-arakawa/items/9cd485debd5b0d18aca2

https://qiita.com/yuji-arakawa/items/6d0299c505315bc3cdb0

https://qiita.com/yuji-arakawa/items/05e3455572d3b09a53dc

https://qiita.com/yuji-arakawa/items/2d4f6eff17a5410dba2d

https://qiita.com/yuji-arakawa/items/597c4bd9f3d5b4212b51

https://aws.amazon.com/jp/blogs/news/leveraging-pinecone-on-aws-marketplace-as-a-knowledge-base-for-amazon-bedrock/