AIは赤い丸が好き！ビジュアル・プロンプト・エンジニアリングでマルチモーダルをレベルアップ！

# はじめに
ChatGPT の登場以来、生成AIでご飯代やおやつ代を稼いている僕たちにとって必須スキルだったプロンプトエンジニアリング。最近では、AIの自己最適化や高度な自然言語理解能力の向上によって「**プロンプトエンジニアリングは死んだ**」とか言っている人たちもいますが、今日時点では、どれだけAIが役立ってくれるかはまだまだプロンプト次第ですね（もちろん、データはもっと大事ですけど）。それはそうとプロンプトにはテキストプロンプトだけではなくてビジュアル・プロンプトというものもあります。ちょっと初歩的なビジュアル・プロンプト・エンジニアリングをお試ししてみたところ面白い結果が出ましたのでこの記事はそのお話しです。

# お試しに使ったコード
以前画像の分類の記事を書いたのですがそのときのコードを1ナノメートルも変えずに使っています。

https://qiita.com/yuji-arakawa/items/042937eaf16fa00cf491

GitHub の repo はこちらです。

https://github.com/kutsushitaneko/Japanese_Stable_CLIP

# ちょっと解説
## ビジュアル・プロンプト・エンジニアリングの概要
ビジュアル・プロンプト・エンジニアリング（Visual Prompt Engineering）は、視覚情報とテキスト情報を組み合わせて、モデルの性能を向上させる手法です。この技術は、特に CLIP などの VLM（Vision-Language Model）において、特定のタスクに対するモデルの応答を改善するために使用されます。具体的には、**視覚的なプロンプト**を用いることで、モデルが注目すべき特徴を強調し、より正確な予測を行うことが可能になります。ここで出て来た視覚的なプロンプトという言葉がちょっとピンときませんが例えば赤丸で囲うといった意外にシンプルなことです。

## CLIPにおける効果
OpenAI が開発したCLIP（Contrastive Language–Image Pretraining）は、画像とテキストの関連性を学習した強力なマルチモーダルモデルです。CLIPは、視覚的な情報とテキスト情報を同時に処理する能力を持ち、ゼロショットで画像を新しいカテゴリを分類することができます。この特性は、Visual Prompt Engineeringによってさらに強化されることが示されています。具体的には、適切な視覚的プロンプトを提供することで、CLIPのゼロショット性能が向上し、画像分類などのタスクにおいて精度が向上することが報告されています。
（[Shtedritski らの論文：What does CLIP know about a red circle? Visual prompt engineering for VLMs](https://arxiv.org/abs/2304.06712)）

# 試してみた（フィギュアの髪の色の分類）
ここで試したタスクはに推しの子の星野アイのフィギュアの画像から Japanese Stable CLIP で生成した埋め込みを使って、フィギュアの髪の色を分類するものです。
髪の色を青い髪, 紫色の髪, 緑色の髪, ピンク色の髪, 赤い髪, 銀色の髪, 金色の髪のいずれであるかを判定させています。

## （１）ビジュアル・プロンプト無しの場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/0e7d3991-a142-4abe-9871-e3a58e31537f.png)
このスクリーンショットは、ビジュアル・プロンプト無しで判定させてものです。フィギュアの髪の色は紫色ですがモデルによる分類結果は「赤い髪」で見事に外れています。

それでは、これをビジュアル・プロンプト・エンジニアリングで精度向上できるでしょうか？

## （２）ビジュアル・プロンプトあり（線幅20pixelの赤い円で囲った）の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/efd6eff6-3389-4c2f-99f5-9d8d7f85f2ec.png)
またしても、「赤い髪」と分類されてしまいました。しかし、よく見てみると「紫色の髪」が順位を上げています。
赤い丸で髪を囲って強調した効果があるようです。
ひょっとしたら、赤丸の線の太さを変えたら効果が変わるのではないでしょうか？我々もパワポの資料などに太い赤線で囲まれている部分があると注意を引かれますよね？

## （３）ビジュアル・プロンプトあり（線幅30pixelの赤い円で囲った）の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/00a574e6-87d9-4b67-9ce4-6affcbcde7c6.png)
今回も「赤い髪」と分類されましたが「赤い髪」と「紫色の髪」のスコアが拮抗して確率（スコアをsoftmaxで確率分布に変換したもの）が同程度になっています。

**これはもしかして！**
## （４）ビジュアル・プロンプトあり（線幅40pixelの赤い円で囲った）の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/dee86c1d-0465-4aaf-8fe7-b72e37c06aa1.png)

ついにそのときが来ました！
**正解の「紫色の髪」と分類されました！**

# 試してみた（写真の鯉のぼりを正しく分類できるか）
次のタスクは、写真に写っている鯉のぼりを「旗, 洗濯物, ペナント, タペストリー, のぼり, 鯉のぼり」の中から「鯉のぼり」と正しく分類できるかを試すものです。

## ビジュアル・プロンプト無しの場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/d526645d-03a9-4467-983d-20902f106c8c.png)
おしい！鯉のぼりは２位でした。

## ビジュアル・プロンプトありの場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/f86f2700-e2a1-41b1-89c3-39e4f960c864.png)
**見事！「鯉のぼり」に分類されました。**

「のぼり」と「鯉のぼり」が分類カテゴリーにあるのはちょっといじわるですが、ビジュアル・プロンプトがあると分類精度が上がることは確かなようです。ちなみに、Japanese Stable CLIP の名誉のために書き添えておきますと「のぼり」ではなく「のぼり旗」とするとビジュアル・プロンプトが無くても「鯉のぼり」と正しく分類できました。

# まとめ
簡単なビジュアル・プロンプト・エンジニアリング（赤い丸で囲うだけ）で、CLIP の精度を向上できることがわかりました。
線幅は恐らく絶対値が問題なのではなくて画像のサイズに対する相対的な太さだと思いますので実際には画像毎に調整が必要かと思います。

今回は手元にあって貧弱なRTX2060のPCでもすぐ動かせる Japanese Stable CLIP による画像分類だけを試してみましたが LVLM（Large Vision-Language Model）/MLLM を使って画像のキャプションを生成したり VQA(Visual Question Answering)をするときにも役立ちそうです。マルチモーダルRAG の精度向上のヒントにもなりそうです。

# FAQ
- 関連論文について
    - Q：ビジュアル・プロンプト・エンジニアリングに関連した論文はありますか？
    - A：以下の２つをご紹介しておきます
        - [What does CLIP know about a red circle? Visual prompt engineering for VLMs](https://arxiv.org/abs/2304.06712)
        - [Attention Prompting on Image for Large Vision-Language Models](https://arxiv.org/abs/2409.17143)
- 使用した画像について
    - Q：推しの子では、アイ推しなんですか？それともダジャレですか？
    - A：いいえ、黒川あかね推しです。次はルビーです。このフィギュアはこれ↓がしたかったのですが、右のキャラクターのフィギュアが開発中のステータスのままなかなか出てこないので、まだ並べられていません。ということで活躍の場を提供しただけです。
    ![20240707_121122~2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74534/ce3f3f00-4781-4fff-ba95-26e017859c5b.jpeg)