しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Azure OpenAI Service の GPT-4 Turbo with Vision と Azure AI Service Enhancement を試した

OpenAI 本家の提供から数週間遅れましたが、Azure OpenAI Service でも GPT-4 Turbo with Vision が一部リージョンで利用できるようになりました。本家とは異なり Azure AI Service Enhancement という Azure OpenAI 専用の機能も同時にリリースされています。

詳細は後述しますが、Azure AI Service Enhancement は現時点では若干挙動がおかしい部分があるので、利用には少し注意が必要になっています。

現時点では Japan East で利用できないため West US で利用するケースが多いと思います。レイテンシ的には West US の方が Australia East より有利な可能性があり、実際にネットワークのレイテンシは West US の方が Australia East より小さいです。

GPT-4 Turbo with Vision は GPT-4 Turbo が持っている機能全てがサポートされているわけではないので、ユースケースによっては追加のプロンプトを書く必要も出てきます。

実際に Azure OpenAI Service で GPT-4 Turbo with Vision を使う方法は以下のドキュメントに全てまとまっています。画像の入力方法は HTTP/HTTPS 指定あるいは Data URL を利用した Base64 での指定が必要です。

モデルのデプロイは vision-preview バージョンを指定して gpt-4 をデプロイします。vision-preview は通常のバージョンと独立してデプロイできるようになっています。

GPT-4 Turbo with Vision と Azure AI Service Enhancement は Azure OpenAI Studio を使うとサクッと試すことが出来ますが、最終的にはアプリケーションに組み込むので SDK から利用してみます。

C# SDK から GPT-4 Turbo with Vision を利用する

最新の Azure OpenAI SDK for .NET では GPT-4 Turbo with Vision の早期サポートが追加されているため、以下のパッケージをインストールあるいは更新すると利用できます。使い方もこれまでとほぼ同じなので経験があれば悩むことはないでしょう。

サンプルコードを載せておきますが、基本的にはテキストベースの時と変わっていません。テキストの代わりに画像の URL を指定する点が唯一の違いとも言えます。

using Azure;
using Azure.AI.OpenAI;

var openAIClient = new OpenAIClient(new Uri("https://****.openai.azure.com/"), new AzureKeyCredential("****"));

var response = await openAIClient.GetChatCompletionsAsync(new ChatCompletionsOptions
{
    DeploymentName = "gpt-4v",
    Messages =
    {
        new ChatRequestSystemMessage("You are an AI assistant that helps people find information."),
        new ChatRequestUserMessage(
            new ChatMessageTextContentItem("この画像を日本語で説明してください。"),
            new ChatMessageImageContentItem(new Uri("https://***.blob.core.windows.net/kazuakix.jpg")))
    },
    MaxTokens = 1024
});

Console.WriteLine(response.Value.Choices[0].Message.Content);

当然ながら URL で指定した画像はパブリックからのアクセスが可能である必要があります。

このコードを実行すると、指定した URL の画像を利用して結果が生成されます。非常に高い精度で画像の説明が生成されていることに驚きますね。

先ほどのサンプルではパブリックアクセス可能な Blob を用意して URL を指定しましたが、 実際の要件としては画像を外部からアクセス可能な状態にはしたくないケースの方が多いはずです。一応 SAS 付きの Blob URL を指定することでアクセス制限された Blob を使って応答を生成させる事は出来ましたが、ネットワーク的な制限がかかっている場合には対応出来ません。

そのようなケースでは Base64 化した画像をリクエストに含めて送信するのが解決策となりそうです。Data URL を生成する便利メソッドは用意されていないので、手動で組み立てる形になります。

using Azure;
using Azure.AI.OpenAI;

Uri ToDataUrl(string filePath, string contentType) => new($"data:{contentType};base64,{Convert.ToBase64String(File.ReadAllBytes(filePath))}");

var openAIClient = new OpenAIClient(new Uri("https://****.openai.azure.com/"), new AzureKeyCredential("****"));

var response = await openAIClient.GetChatCompletionsAsync(new ChatCompletionsOptions
{
    DeploymentName = "gpt-4v",
    Messages =
    {
        new ChatRequestSystemMessage("You are an AI assistant that helps people find information."),
        new ChatRequestUserMessage(
            new ChatMessageTextContentItem("この画像を日本語で説明してください。"),
            new ChatMessageImageContentItem(ToDataUrl(@".\kazuakix.jpg", "image/jpeg")))
    },
    MaxTokens = 1024
});

Console.WriteLine(response.Value.Choices[0].Message.Content);

Uri クラスで扱う URL は 65519 バイト以下である必要があるので、大きな画像は Uri クラスを利用している限り扱えません。この辺りは C# SDK の制約という感じです。

OpenAI としては長い会話の場合は URL を指定するように推奨しているのと、アップロード可能な POST サイズには制限がありそうなので、可能な限り URL で渡した方が効率的だと考えられます。

Vision Enhancement (OCR / Grounding) を利用する

基本的な GPT-4 Turbo with Vision の利用が出来たので、次は Azure AI Service Enhancement を試していきます。多少名称がブレている気がしますが、Azure AI Service Enhancement は Vision Enhancement とも呼ばれているようなので、どちらも正解だと思っています。

GPT-4 Turbo with Vision は画像内のテキストも割と認識してくれるので意外かもしれませんが、基本的にテキストを認識する機能は持ち合わせていないようです。特に日本語などは苦手としているので、その部分を Azure AI Service で補うのが Vision Enhancement となります。

今回はサンプルとして Azure OpenAI Service のモデル一覧のテーブルを Markdown のテーブルに変換する処理をやらせてみます。まずは Azure OpenAI Studio 上で Vision Enhancement を無効化した状態で試してみると、以下のように画像の解析は出来ないと断られてしまいました。

何回か試すと頑張ってくれるケースもあるのですが、実利用するには不安定なので難しいです。このあたりの挙動を Azure AI Vision (Azure Computer Vision) を利用することで改善可能です。

実際に画像に対して Vision Enhancement を有効化するサンプルコードは以下のようになります。少し OpenAI とは関係のない余計な実装が付いているのは後述します。

using Azure;
using Azure.AI.OpenAI;

using Mono.Reflection;

Uri ToDataUrl(string filePath, string contentType) => new($"data:{contentType};base64,{Convert.ToBase64String(File.ReadAllBytes(filePath))}");

AzureChatExtensionsOptions CreateExtensionsOptions(AzureChatEnhancementConfiguration enhancementConfiguration)
{
    var extensionOptions = new AzureChatExtensionsOptions();

    var property = typeof(AzureChatExtensionsOptions).GetProperty(nameof(AzureChatExtensionsOptions.EnhancementOptions));
    var field = property.GetBackingField();

    field.SetValue(extensionOptions, enhancementConfiguration);

    return extensionOptions;
}

var openAIClient = new OpenAIClient(new Uri("https://****.openai.azure.com/"), new AzureKeyCredential("****"));

var options = new ChatCompletionsOptions
{
    DeploymentName = "gpt-4v",
    AzureExtensionsOptions = CreateExtensionsOptions(new AzureChatEnhancementConfiguration
    {
        Grounding = new AzureChatGroundingEnhancementConfiguration(true),
        Ocr = new AzureChatOCREnhancementConfiguration(true)
    }),
    Messages =
    {
        new ChatRequestSystemMessage("You are an AI assistant that helps people find information."),
        new ChatRequestUserMessage(
            new ChatMessageTextContentItem("この画像に含まれているテーブルを Markdown に変換してください。"),
            new ChatMessageImageContentItem(ToDataUrl(@"***.png", "image/png")))
    },
    MaxTokens = 1024
};

var response = await openAIClient.GetChatCompletionsAsync(options);

Console.WriteLine(response.Value.Choices[0].Message.Content);

先程のコードとの違いは AzureExtensionsOptions を追加している部分だけです。余計なコードが付いているのは、現状の Azure OpenAI SDK に不具合があるため、その部分をリフレクションで無理やり回避しているためです。Issue は作成済みなので次のバージョンで直ることを期待しています。

サンプルコードの実行結果は以下となりますが、ちゃんとテキストを認識して Markdown として出力されていることが確認できます。テーブル構造の認識に失敗していますが、結合されたセルの扱いは苦手のようです。

次は日本語も含んだ大きなテーブルを試してみます。例として App Service の料金テーブルを画像として保存して、同じように Markdown に変換させてみた結果が以下の通りです。

問題なく日本語も認識していますが、空のセルがある場合に正しく認識されていないことが分かります。Azure AI Vision によって OCR された結果が悪いのか、OpenAI に渡すタイミングで構造が失われているのかは分かっていませんが、配置などの情報は渡されていない予感です。

ここまで Vision Enhancement を試してきましたが、実は Azure AI Vision の利用に必要な設定を追加していないことに気が付かれた方もいるかもしません。

Azure OpenAI Studio 上ではトグルスイッチをオンにして、Azure OpenAI Service と同じリージョンにデプロイした Azure AI Vision を指定していたように、SDK から利用する場合も同じ手順が必要になるはずですが、現状は指定せずとも動作しています。

ドキュメント上も Azure AI Vision が必要と書かれていますが、未指定で OCR などの設定を有効化するだけで動作しています。Azure OpenAI Service だけでは不可能な Grounding も動作していることを確認しましたので、Azure AI Vison が裏側では使われているようです。

To use Vision enhancement, you need a Computer Vision resource, and it must be in the same Azure region as your GPT-4 Turbo with Vision resource.

How to use the GPT-4 Turbo with Vision model - Azure OpenAI Service | Microsoft Learn

逆に Azure AI Vision を利用する設定を追加して、リクエストを投げてみましたがメトリックを見る限り呼び出されている形跡がありませんでした。ドキュメントとは異なる挙動なので不具合なのだと思いますが、正直なところ別リソース用意するのではなく、今のように内部でよしなにやっていてほしいです。

特にネットワーク制限が必要な環境の場合は、Azure OpenAI Service から Azure AI Vision へのアクセスが問題となるケースも発生するため、内部で閉じておいて貰った方が扱いやすくなります。

REST API を直接利用する際の注意点

通常の GPT-4 Turbo with Vision を利用する際にはこれまで通りの REST API エンドポイントで問題なく動作するのですが、Azure AI Service Enhancement を利用する際にはエンドポイントが変わっているので、間違うと常にバリデーションエラーが出ることになります。

ドキュメントのエンドポイントを注意深く見るとパスに extensions が追加されたものになっています。

Azure AI Service Enhancement 無し
https://{RESOURCE_NAME}.openai.azure.com/openai/deployments/{DEPLOYMENT_NAME}/chat/completions?api-version=2023-12-01-preview

Azure AI Service Enhancement 有り
https://{RESOURCE_NAME}.openai.azure.com/openai/deployments/{DEPLOYMENT_NAME}/extensions/chat/completions?api-version=2023-12-01-preview

2 つのエンドポイントがあることはドキュメントからは読み取りにくいため、バリデーションエラーが出た場合はエンドポイントのミスを疑ってみてください。