🙌

Azure OpenAI ServiceのBatch APIを試す

2024/08/12に公開

概要

5 月の Microsoft Build 2024 でもアナウンスされてた Azure OpenAI Service の Batch API がプレビュー版ではありますが、リリースされたので試して見ました。

Azure OpenAI Service リソースの作成

最初に Azure OpenAI Service リソースを作成します。
現在は Batch 用のデプロイメントが作成可能な Azure OpenAI Service のリージョンに限りがあるため、「米国東部(East US)」リージョンでリソースを作成しました。

Batch 用のデプロイメントの作成

続いて、Batch 用のデプロイメントを作成します。
デプロイ名は「gpt-4o-batch」としています。

Python から Batch API を呼び出す

Python から Batch API を呼び出します。

openai パッケージのインストール

openai パッケージをインストールします。

!pip install openai

jsonlファイルの用意

Batch API の入力に使用するjsonlファイルを作成します。
各行が 1 つのリクエストになっています。

今回は以下の内容をsample1.jsonlとして用意します。

{"custom_id": "task-0", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "ASIAN KUNG-FU GENERATION、略して?"}]}}
{"custom_id": "task-1", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "京都の鴨川と高野川が合流する地点を何と呼びますか?"}]}}
{"custom_id": "task-2", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "四畳半神話大系に登場する映画サークルの名前はなんですか?"}]}}

Azure OpenAI Service クライアントの生成

API キーとエンドポイントの情報を指定して、Azure OpenAI Service に接続するクライアントを生成します。
Batch API を利用するためにapi_version2024-07-01-previewとしています。

from openai import AzureOpenAI

# APIキーとエンドポイントの情報
API_KEY=""
AZURE_ENDPOINT=""

# Azure OpenAI Service Clientの生成
client = AzureOpenAI(
    api_key=API_KEY,
    azure_endpoint=AZURE_ENDPOINT,
    api_version="2024-07-01-preview",
    )

jsonl ファイルのアップロード

用意したjsonlファイルをアップロードしておきます。
Batch API で処理するファイルの場合、purposeにはbatchを指定します。
この API はファイルを扱う場合は共通で、ファインチューニングに利用する場合は、purposeにはfine-tuneを指定します。

# Batch APIで処理するためのファイルをアップロード
with open(file="sample1.jsonl", mode="rb") as f:
  file = client.files.create(
    file=f,
    purpose="batch"
  )

print(file.model_dump_json(indent=2))

アップロードしたファイルは Azure OpenAI Studio の「データファイル」タブからも確認できます。

バッチジョブの実行

ファイルの状態が「処理済み」に変化したら、アップロードしたファイルの ID を利用してバッチジョブを開始します。

# ファイルIDの取得
file_id = file.id

# バッチジョブの開始
batch_response = client.batches.create(
    input_file_id=file_id,
    endpoint="/chat/completions",
    completion_window="24h",
)

print(batch_response.model_dump_json(indent=2))

実行したバッチジョブの状態もプレビュー版ではありますが、Azure OpenAI Studio の「バッチジョブ」タブで確認できます。

結果の取得

バッチジョブの状態が「完了済み」に変化したら、バッチジョブの ID を指定して結果を取得できます。

import json

# 結果の取得
batch_response = client.batches.retrieve(batch_id)
file_response = client.files.content(batch_response.output_file_id)
raw_responses = file_response.text.strip().split('\n')

for raw_response in raw_responses:
    json_response = json.loads(raw_response)
    formatted_json = json.dumps(json_response, indent=2)
    print(formatted_json)

バッチジョブの結果もjsonlファイルで取得する形になっています。
結果には、コンテンツフィルターや消費トークンの情報が含まれています。
なお今回のサンプルで用意した質問に対する回答はどれも微妙なものとなりました…。

{"custom_id": "task-0", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "ASIAN KUNG-FU GENERATIONは、しばしば「AKG」と略されます。もし他に関連する情報や質問があれば、お答えします。", "role": "assistant"}}], "created": 1723169231, "id": "chatcmpl-9u9K3nFM89CijcOqxLPCdi57T9xGt", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 38, "prompt_tokens": 34, "total_tokens": 72}}, "request_id": "ba16e409-d15c-4213-a755-504d0c897591", "status_code": 200}, "error": null}
{"custom_id": "task-1", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "京都の鴨川(かもがわ)と高野川(たかのがわ)が合流する地点は「出町(でまち)」と呼ばれます。この地点は、京都市左京区に位置し、三角公園や出町柳駅の近くにあります。出町柳は、京阪電車のターミナル駅であり、観光スポットとしても知られています。", "role": "assistant"}}], "created": 1723169230, "id": "chatcmpl-9u9K2cbo8nMhsNR3apuELNajtgeRI", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 97, "prompt_tokens": 45, "total_tokens": 142}}, "request_id": "3c03f488-d9a6-4219-ac5b-9f52f381e389", "status_code": 200}, "error": null}
{"custom_id": "task-2", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "映画サークルの名前は「みそぎ」に登場する「陸上部」と呼ばれるサークルです。このサークルは、主人公が所属することになるさまざまなサークルのうちの一つであり、映画制作に取り組んでいます。", "role": "assistant"}}], "created": 1723169231, "id": "chatcmpl-9u9K3pHA2ZqBYE0nunF03jdsn6CRt", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 67, "prompt_tokens": 45, "total_tokens": 112}}, "request_id": "5d9d600a-df09-4bed-865a-2910bd2e83e1", "status_code": 200}, "error": null}

結果ファイルは Azure OpenAI Studio のバッチジョブの詳細画面からも取得できます。

パラメータを変化させてリクエストを送信する

ここまではサンプルを追う形式を取ってきましたが、少し変化をつけて Batch API を利用してみます。
jsonlファイルの各行ではパラメータを変化させて処理できます。
試しにtemperatureの値を 0 から 1 まで 0.2 ずつ変化させたリクエストを送信してみます。

{"custom_id": "task-0", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "ASIAN KUNG-FU GENERATION、略して?"}], "max_tokens": 100, "temperature": 0}}
{"custom_id": "task-1", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "ASIAN KUNG-FU GENERATION、略して?"}], "max_tokens": 100, "temperature": 0.2}}
{"custom_id": "task-2", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "ASIAN KUNG-FU GENERATION、略して?"}], "max_tokens": 100, "temperature": 0.4}}
{"custom_id": "task-3", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "ASIAN KUNG-FU GENERATION、略して?"}], "max_tokens": 100, "temperature": 0.6}}
{"custom_id": "task-4", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "ASIAN KUNG-FU GENERATION、略して?"}], "max_tokens": 100, "temperature": 0.8}}
{"custom_id": "task-5", "method": "POST", "url": "/chat/completions", "body": {"model": "gpt-4o-batch", "messages": [{"role": "system", "content": "You are an AI assistant that helps people find information."}, {"role": "user", "content": "ASIAN KUNG-FU GENERATION、略して?"}], "max_tokens": 100, "temperature": 1}}

結果としては以下のようになりました。

{"custom_id": "task-0", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "ASIAN KUNG-FU GENERATIONは、一般的に「アジカン」と略されます。", "role": "assistant"}}], "created": 1723211165, "id": "chatcmpl-9uKEPC1rmNzpXnT4GVD4InT05YNwG", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 23, "prompt_tokens": 34, "total_tokens": 57}}, "request_id": "ac378a13-aa63-41fc-afb0-2b5b85c62efe", "status_code": 200}, "error": null}
{"custom_id": "task-1", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "ASIAN KUNG-FU GENERATIONは、一般的に「アジカン」と略されます。", "role": "assistant"}}], "created": 1723211165, "id": "chatcmpl-9uKEPWj5LCeG0Neu1vgMtHQ2scNH3", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 23, "prompt_tokens": 34, "total_tokens": 57}}, "request_id": "fff2d344-95e7-4579-8caf-b79400c9a67d", "status_code": 200}, "error": null}
{"custom_id": "task-2", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "ASIAN KUNG-FU GENERATIONは、一般的に「AKG」と略されます。この日本のロックバンドは、2000年代初頭から活動しており、多くの人気曲を持っています。", "role": "assistant"}}], "created": 1723211165, "id": "chatcmpl-9uKEPn32Vkd00iIV7H4b9um9ShC8U", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 48, "prompt_tokens": 34, "total_tokens": 82}}, "request_id": "c047969e-1a5b-4f25-9df9-8d4f161cd0f4", "status_code": 200}, "error": null}
{"custom_id": "task-3", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "ASIAN KUNG-FU GENERATIONは、一般的に「AKG」や「アジカン」と略されることが多いです。", "role": "assistant"}}], "created": 1723211165, "id": "chatcmpl-9uKEPr2e03o9R1CEr6akjg9nmN8x8", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 33, "prompt_tokens": 34, "total_tokens": 67}}, "request_id": "90d5027b-1a6a-441c-8b9e-3af17fc1f3c0", "status_code": 200}, "error": null}
{"custom_id": "task-4", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "ASIAN KUNG-FU GENERATION(アジアン・カンフー・ジェネレーション)は、一般的に「アジカン」と略されます。", "role": "assistant"}}], "created": 1723211165, "id": "chatcmpl-9uKEPt8IFkkJhs3FMIllOAwDb9cE7", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 37, "prompt_tokens": 34, "total_tokens": 71}}, "request_id": "ee49963a-6005-45ed-b208-065c5b23af65", "status_code": 200}, "error": null}
{"custom_id": "task-5", "response": {"body": {"choices": [{"content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}, "finish_reason": "stop", "index": 0, "logprobs": null, "message": {"content": "ASIAN KUNG-FU GENERATION(アジアン・カンフー・ジェネレーション)は、しばしば「AKG」と略されます。", "role": "assistant"}}], "created": 1723211165, "id": "chatcmpl-9uKEP9UsMqH5O3uGJFsQhBvl65mEY", "model": "gpt-4o-2024-05-13", "object": "chat.completion", "prompt_filter_results": [{"prompt_index": 0, "content_filter_results": {"hate": {"filtered": false, "severity": "safe"}, "jailbreak": {"filtered": false, "detected": false}, "self_harm": {"filtered": false, "severity": "safe"}, "sexual": {"filtered": false, "severity": "safe"}, "violence": {"filtered": false, "severity": "safe"}}}], "system_fingerprint": "fp_f5a70d8dc9", "usage": {"completion_tokens": 36, "prompt_tokens": 34, "total_tokens": 70}}, "request_id": "66591101-cd9d-472f-9822-e59a1cbaf152", "status_code": 200}, "error": null}

複数のパラメータでの結果が見たい場合などは結構使いやすいかなと感じました。

今後に期待したい点

Batch API を試してみて、今後に期待したい点としては以下です。

Embedding API への対応

現在、Azure OpenAI Service の Batch API では、text-embedding-ada-002text-embedding-3-largeなどの埋め込みモデルには対応していません。
埋め込みモデルに対応できると、RAG などで文書データを Azure AI Search や Cosmos DB などに投入する処理で活用できる見込みがあるので期待したいところです。
本家の OpenAI 側では、埋め込みモデルに対応しているので、対応されるのは時間の問題ではあると思われます。

Event Grid でのシステムトピックのサポート

こちらについては期待が薄い部分ですが、Azure Event Grid のシステムトピックでステータスのバッチステータス変更などのイベントがサブスクライブできると、ステータス監視の手間が軽減され、アプリケーションに Batch API が組み込みやすくなるというのがあります。
この辺は本家 OpenAI ではなく、Azure 側の資産として期待したい部分です。

Discussion