Azure OpenAI ServiceでGPT-4oを使ってみた（テキスト×画像編）

2024年5月に開催されたMicrosoft BuildでGPT-4oのGAが発表されました。

そこで、今回はGPT-4oを活用しテキストと画像を組み合わせ、回答を生成する方法を試してみました。

## 注意事項
* Azure OpenAI Serviceのリソース作成には利用申請が必要となります。Azure OpenAI Serviceの利用申請については下記ドキュメントをご参照ください。
  * https://learn.microsoft.com/ja-jp/azure/ai-services/openai/overview
* GPT-4oがすぐに利用できるリージョンは一部のみで、東日本は現在すぐに利用できないことにご注意ください。（2024/5現在）本記事ではEast USリージョンのリソースを利用しています。
  * 詳細については下記公式ドキュメントをご参照ください。

https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models

## GPT-4o
2024年5月にリリースされた新しいGPTモデルで、主に下記3つの特長があります。

* 高速な回答生成
* マルチモーダル対応（テキスト・画像・音声）
  * ただし、現在はまだ音声入力に関してはサポートされていないようです
* 従来のGPT-4 / GPT-4-Turboと比べて安価
  * Microsoft公式の価格表はまだ公開されていませんが、下記のOpenAIの価格表を見るとGPT-4と比べて入力は1/6、出力は1/4の価格とお安くなっています

https://openai.com/api/pricing/


## GPT-4oのデプロイ
従来のAzure OpenAI Serviceが提供しているGPTモデルと同様に、Azure OpenAI Studioでデプロイ可能です。

「モデルのデプロイ」画面で、gpt-4oを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/64061e72-3ccc-8d72-daf9-4a0247f20133.png)

### 試しにPlaygroundで呼び出してでみる
デプロイが完了したため、試しにテキストと画像を使った簡単な質問を答えさせてみます。

画像は「東京23区の世帯数」のグラフ画像を作成してみました。

※下記データを元にグラフを作成
https://www.toukei.metro.tokyo.lg.jp/juukiy/2024/jy24000001.htm

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/cce690ab-2286-e6de-8f10-1f1e1a74128b.png)

このグラフに基づいた質問をGPT-4oにチャットしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/b05687b8-95b0-998c-5262-ccb843749a83.png)

画像を読み取ったうえで質問に回答できていますね。

## API呼び出し
次に、Azure OpenAI ServiceのREST APIを呼び出してテキストと画像を使った質問をする方法を試してみました。

REST APIの詳細は下記ドキュメントをご参照ください。（内容は「GPT-4 Turbo with Vision」となっていますが、APIの形式はGPT-4oも同じです）

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/how-to/gpt-with-vision?tabs=rest%2Csystem-assigned%2Cresource#call-the-chat-completion-apis

### URLおよびヘッダー
下記内容でAzure OpenAI ServiceのAPIを呼び出します。
* URL: POST `https://<Azure OpenAI Serviceリソース名>.openai.azure.com/openai/deployments/<デプロイ名>/chat/completions?api-version=2024-02-15-preview`
  * デプロイ名は「GPT-4oのデプロイ」で指定したモデル名を設定します
* ヘッダー
  * Content-Type: `application/json`
  * api-key: Azure OpenAI Service APIキー

* リクエストボディ: 後述の「リクエスト例」をご参照ください


### 画像のプロンプトについて
プロンプトに画像を含めるには、リクエストボディの`messages` > `content`に画像のURLまたはBase64を指定することで画像をプロンプトに追加することができます。

そのため、画像のURLとBase64を使った方法を両方試してみました。

```json
"content": [
    {
        "type": "image_url",
        "image_url": {
            "url": "<URL形式 or Base64形式>"
        }
    } 
]
```

### 画像URLを使った形式

#### リクエスト例
```json
{
    "messages": [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "世帯数が最も多い区はどこ？"
                },
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "https://<エンドポイント>/東京23区の世帯数.png"
                    }
                }
            ]
        }
    ],
    "max_tokens": 800
}
```

#### レスポンス（一部抜粋）
`choices` > `message`に、AIからの回答が返ってきていることがわかります。REST API形式でも、画像データに基づいた回答ができました。

```json
{
    "choices": [
        {
            "finish_reason": "stop",
            "index": 0,
            "logprobs": null,
            "message": {
                "content": "このグラフによると、世帯数が最も多い区は「世田谷区」です。",
                "role": "assistant"
            }
        }
    ],
    "model": "gpt-4o-2024-05-13",
    "object": "chat.completion",
    "usage": {
        "completion_tokens": 25,
        "prompt_tokens": 369,
        "total_tokens": 394
    }
}
```

### Base64変換して送信
次に、リクエストボディに指定する画像をBase64形式で指定してみます。

URL形式とは異なり画像をWeb上に公開する必要が無いため、試しに確認する程度であればこちらのほうが使いやすそうです。

```
data:<MIMEタイプ>;base64,<Base64文字列>
```

#### リクエスト例
```json
{
    "messages": [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": "世帯数が最も多い区はどこ？"
                },
                {
                    "type": "image_url",
                    "image_url": {
                        "url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAo8AAAGwCAYAAADIVI+eAAAAAXNSR..."
                    }
                }
            ]
        }
    ],
    "max_tokens": 800
}
```

#### レスポンス
（前述のAPIレスポンスと同様の形式のため割愛）

### 画像のプロンプト消費トークンについて
画像をBase64変換するとリクエストの文字数が多くなり、URLでの送信と比べて消費トークン数も多くなるのでは？と思いましたが、**URL形式とBase64形式で入力トークン数は変わりませんでした**。

そのため、画像に関してはリクエストの長さに応じてトークン数が変化することは無さそうです。

| トークン数 | URL形式 | Base64形式 |
| - | - | - |
| **prompt_tokens（入力トークン数）** | **926** | **926** |
| completion_tokens（出力トークン数） | 49 | 37 |
| total_tokens（入力＋出力トークン数） | 984 | 963 |

といいますのも、**画像のトークン数に関しては解像度に応じて変動**するようです。

#### 解像度による消費トークン数の違い
解像度に応じて消費トークン数が本当に変動するかどうか、簡単に検証してみました。

テキストと画像は先ほどと全く同じ内容で、画像を50%リサイズした場合に消費トークン数が軽減されるかを試した結果は下記の通りです。

|  | リサイズ前 | 50%リサイズ後 |
| - | - | - |
| 画像サイズ | 1310 x 863 | 655 x 432 |
| prompt_tokens（入力トークン数） | 926 | **367** |

結果、**リサイズ前と比べて約1/4程度にトークン数が軽減されていました！**

Azure OpenAI Serviceの料金はトークン数に応じて費用が発生するため、余分な金額の発生やトークン超過を回避するためには画像のリサイズが有用そうです。

## 最後に
今回はGPT-4oを使ったテキスト＆画像のマルチモーダルなプロンプトを試してみました。所感としてはやはり従来のGPT-4と比べてかなり回答速度が速く、今回のグラフ画像程度であればそこそこ精度よく回答できていそうです。

今後は（使えるにはまだ先のようですが）音声を使ったプロンプトについても試してみようと思います。