OpenAI Cookbook examples : API 使用方法 : レート制限の操作 (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 08/09/2023
* 本ページは、OpenAI Cookbook レポジトリの以下のドキュメントを翻訳した上で適宜、補足説明したものです:
- examples : How to handle rate limits
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- sales-info@classcat.com ; Web: www.classcat.com ; ClassCatJP
OpenAI Cookbook : API 使用方法 : レート制限の操作
OpenAI API を繰り返し呼び出すとき、”429: ‘Too Many Requests'” または “RateLimitError” というエラーメッセージに遭遇するかもしれません。これらのエラーメッセージは API のレート制限を超えたことに由来します。
このガイドはレート制限エラーを回避して処理するためのヒントを共有します。
レート制限エラーを回避するために並列リクエストをスロットルで調整するサンプルスクリプトを見るには、api_request_parallel_processor.py をご覧ください。
何故レート制限があるのでしょう?
レート制限は API のための一般的なプラクティスで、それらは幾つかの異なる理由で導入されています。
- まず、それらは API の悪用や誤用に対して保護するのに役立ちます。例えば、悪意あるアクターは、API をオーバロードさせたりサービスの崩壊を引き起こそうとしてリクエストにより API を送り続けることができるでしょう。レート制限を設定することで、OpenAI はこの種類のアクティビティを防ぐことができます。
- 2 番目に、レート制限は誰もが API への公平なアクセスを持つことを保証することに役立ちます。一人の人や一つの組織が過剰なリクエストを行なう場合、それは他の人すべてに対して API をダウンさせてしまう可能性があります。単一ユーザが行なうことができるリクエスト数をスロットルで調整することにより、OpenAI はすべての人がスローダウンを経験することなく API を使用する機会を持つことを保証します。
- 最後に、レート制限は OpenAI がインフラへの合計ロードを管理することを支援できます。API へのリクエストが劇的に増加する場合、サーバに重い負荷をかけて、パフォーマンス問題を引き起こす可能性があります。レート制限を設定することにより、OpenAI はすべてのユーザへのスムースで一貫したエクスペリエンスを維持することを支援できます。
レート制限に到達することはフラストレーションになる可能性はありますが、レート制限はユーザに対する API の信頼性の高い操作を保護するために存在します。
デフォルトのレート制限
2023年1月現在、デフォルトのレート制限は :
参考まで、1,000 トークンはおよそ 1 ページのテキストです。
他のレート制限リソース
OpenAI のレート制限についてこれらの他のリソースで更に読んでください :
- ガイド: レート制限
- ヘルプセンター: Is API usage subject to any rate limits?
- ヘルプセンター: How can I solve 429: ‘Too Many Requests’ errors?
レート制限の増加のリクエスト
貴方の組織のレート制限を増やしたい場合には、次のフォームに記入してください :
レート制限エラーの例
API リクエストが素早く送られすぎるとレート制限エラーが発生します。OpenAI Python ライブラリを使用する場合、それらは以下のようなものです :
RateLimitError: Rate limit reached for default-codex in organization org-{id} on requests per min. Limit: 20.000000 / min. Current: 24.000000 / min. Contact support@openai.com if you continue to have issues or if you’d like to request an increase.
以下はレート制限エラーをトリガーするサンプルコードです。
import openai # for making OpenAI API requests
# request a bunch of completions in a loop
for _ in range(100):
openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello"}],
max_tokens=10,
)
レート制限エラーを回避する方法
指数バックオフで再試行する
レート制限エラーを回避する一つの簡単な方法は、ランダムな指数 (関数的) バックオフを使用してリクエストを自動的に再試行することです。指数バックオフによる再試行は、レート制限エラーに遭遇したとき短いスリープを実行してから、失敗に終わったリクエストを再試行することを意味します。リクエストが依然として失敗する場合は、スリープする長さが増やされてこのプロセスが繰り返されます。これはリクエストが成功するか、再試行の最大数に到達するまで続きます。
このアプローチは多くの利点があります :
- 自動再試行は、データを破壊したり欠落することなくレート制限エラーからリカバーできることを意味します。
- 指数バックオフは、最初の再試行を素早く試すことができる一方で、最初の幾つかの再試行が失敗した場合でも依然としてより長い遅延からでも恩恵を受けることを意味します。
- 遅延へのランダムな jitter の追加はすべてが同時にヒットすることから再試行を支援します。
失敗したリクエストは毎分あたりの制限に関与しますので、連続したリクエストの再送は動作しないことに注意してください。
以下は幾つかのサンプルソリューションです。
Example #1: Tenacity ライブラリの使用
Tenacity は、Python で書かれた、Apache 2.0 ライセンスの汎用目的の再試行ライブラリで、殆ど何にでも動作の再試行を追加するタスクを単純化します。
指数バックオフをリクエストに追加するため、tenacity.retry デコレータ を使用できます。以下のサンプルは、ランダムな指数バックオフをリクエストに追加するために tenacity.wait_random_exponential 関数を使用しています。
Tenacity ライブラリはサードパーティのツールであり、OpenAI はその信頼性やセキュリティについて保証しないことに注意してください。
import openai # for OpenAI API calls
from tenacity import (
retry,
stop_after_attempt,
wait_random_exponential,
) # for exponential backoff
@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))
def completion_with_backoff(**kwargs):
return openai.Completion.create(**kwargs)
completion_with_backoff(model="text-davinci-002", prompt="Once upon a time,")
<OpenAIObject text_completion id=cmpl-5oowO391reUW8RGVfFyzBM1uBs4A5 at 0x10d8cae00> JSON: { "choices": [ { "finish_reason": "length", "index": 0, "logprobs": null, "text": " a little girl dreamed of becoming a model.\n\nNowadays, that dream" } ], "created": 1662793900, "id": "cmpl-5oowO391reUW8RGVfFyzBM1uBs4A5", "model": "text-davinci-002", "object": "text_completion", "usage": { "completion_tokens": 16, "prompt_tokens": 5, "total_tokens": 21 } }
Example #2: バックオフ・ライブラリの使用
バックオフと再試行のための関数デコレータを提供するもう一つのライブラリは backoff です。
Tenacity のように、backoff ライブラリはサードパーティのツールで、OpenAI はその信頼性やセキュリティについて保証しません。
import backoff # for exponential backoff
import openai # for OpenAI API calls
@backoff.on_exception(backoff.expo, openai.error.RateLimitError)
def completions_with_backoff(**kwargs):
return openai.Completion.create(**kwargs)
completions_with_backoff(model="text-davinci-002", prompt="Once upon a time,")
<OpenAIObject text_completion id=cmpl-5oowPhIdUvshEsF1rBhhwE9KFfI3M at 0x111043680> JSON: { "choices": [ { "finish_reason": "length", "index": 0, "logprobs": null, "text": " two children lived in a poor country village. In the winter, the temperature would" } ], "created": 1662793901, "id": "cmpl-5oowPhIdUvshEsF1rBhhwE9KFfI3M", "model": "text-davinci-002", "object": "text_completion", "usage": { "completion_tokens": 16, "prompt_tokens": 5, "total_tokens": 21 } }
Example 3: 手動のバックオフ実装
サードパーティのライブラリを使用したくないのであれば、独自のバックオフロジックを実装することができます。
# imports
import random
import time
import openai
# define a retry decorator
def retry_with_exponential_backoff(
func,
initial_delay: float = 1,
exponential_base: float = 2,
jitter: bool = True,
max_retries: int = 10,
errors: tuple = (openai.error.RateLimitError,),
):
"""Retry a function with exponential backoff."""
def wrapper(*args, **kwargs):
# Initialize variables
num_retries = 0
delay = initial_delay
# Loop until a successful response or max_retries is hit or an exception is raised
while True:
try:
return func(*args, **kwargs)
# Retry on specified errors
except errors as e:
# Increment retries
num_retries += 1
# Check if max retries has been reached
if num_retries > max_retries:
raise Exception(
f"Maximum number of retries ({max_retries}) exceeded."
)
# Increment the delay
delay *= exponential_base * (1 + jitter * random.random())
# Sleep for the delay
time.sleep(delay)
# Raise exceptions for any errors not specified
except Exception as e:
raise e
return wrapper
@retry_with_exponential_backoff
def completions_with_backoff(**kwargs):
return openai.Completion.create(**kwargs)
completions_with_backoff(model="text-davinci-002", prompt="Once upon a time,")
<OpenAIObject text_completion id=cmpl-5oowRsCXv3AkUgVJyyo3TQrVq7hIT at 0x111024220> JSON: { "choices": [ { "finish_reason": "length", "index": 0, "logprobs": null, "text": " a man decided to greatly improve his karma by turning his life around.\n\n" } ], "created": 1662793903, "id": "cmpl-5oowRsCXv3AkUgVJyyo3TQrVq7hIT", "model": "text-davinci-002", "object": "text_completion", "usage": { "completion_tokens": 16, "prompt_tokens": 5, "total_tokens": 21 } }
レート制限を前提にバッチ処理のスループットを最大化する方法
ユーザからのリアルタイムのリクエストを処理している場合、バックオフと再試行はレート制限エラーを回避しながら遅延を最小化する素晴らしいストラテジーです。
けれども、遅延よりもスループットが重要であるような巨大ボリュームのバッチデータを処理している場合は、バックオフと再試行に加えて貴方ができる幾つかの他のことがあります。
積極的に (先を見越して) リクエスト間の遅延を追加する
繰り返しレート制限に到達し、バックオフし、再度レート制限に達し、そして再度バックオフするような場合、リクエストのバジェットのかなりの部分は再試行される必要があるリクエストで「浪費」される可能性があります。固定レート制限の前提では、これは処理スループットを制限します。
ここで、一つの可能性あるソリューションは、レート制限を計算してその逆数に等しい遅延を追加することです (例えば、レート制限が毎分 20 リクエストである場合、各リクエストに 3-6秒の遅延を追加します)。これは、レート制限に達して無駄なリクエストを発生させることなく、レート制限の上限の近くで操作するのに役立ちます。
リクエストに遅延を追加するサンプル
# imports
import time
import openai
# Define a function that adds a delay to a Completion API call
def delayed_completion(delay_in_seconds: float = 1, **kwargs):
"""Delay a completion by a specified amount of time."""
# Sleep for the delay
time.sleep(delay_in_seconds)
# Call the Completion API and return the result
return openai.Completion.create(**kwargs)
# Calculate the delay based on your rate limit
rate_limit_per_minute = 20
delay = 60.0 / rate_limit_per_minute
delayed_completion(
delay_in_seconds=delay,
model="text-davinci-002",
prompt="Once upon a time,"
)
<OpenAIObject text_completion id=cmpl-5oowVVZnAzdCPtUJ0rifeamtLcZRp at 0x11b2c7680> JSON: { "choices": [ { "finish_reason": "length", "index": 0, "logprobs": null, "text": " there was an idyllic little farm that sat by a babbling brook" } ], "created": 1662793907, "id": "cmpl-5oowVVZnAzdCPtUJ0rifeamtLcZRp", "model": "text-davinci-002", "object": "text_completion", "usage": { "completion_tokens": 16, "prompt_tokens": 5, "total_tokens": 21 } }
リクエストのバッチ処理
OpenAI API は毎分のリクエストと毎分のトークンに対して個別の制限があります。
毎分のリクエストの制限に達していて毎分のトークンには余裕がある場合、複数のタスクを各リクエストにバッチ化することでスループットを増大させることができます。これは、特に小さいモデルで、毎分により多くのトークンを処理することを可能にします。
プロンプトのバッチの送信は通常の API 呼び出しと正確に同じに動作します、単一文字列の代わりに文字列のリストを prompt パラメータに渡すことを除いて。
Warning: response オブジェクトはプロンプトの順序で補完を返さない場合がありますので、index フィールドを使用してレスポンスをプロンプトと一致させることを常に忘れないでください。
バッチ処理なしのサンプル
import openai # for making OpenAI API requests
num_stories = 10
prompt = "Once upon a time,"
# serial example, with one story completion per request
for _ in range(num_stories):
response = openai.Completion.create(
model="curie",
prompt=prompt,
max_tokens=20,
)
# print story
print(prompt + response.choices[0].text)
Once upon a time, before there were grandiloquent tales of the massacre at Fort Mims, there were stories of Once upon a time, a full-sized search and rescue was created. However, CIDIs are the addition of requiring Once upon a time, Schubert was hot with the films. “Schubert sings of honey, flowers, Once upon a time, you could watch these films on your VCR, sometimes years after their initial theatrical release, and there Once upon a time, there was a forest. In that forest, the forest animals ruled. The forest animals had their homes Once upon a time, there were two programs that complained about false positive scans. Peacock and Midnight Manager alike, only Once upon a time, a long, long time ago, tragedy struck. it was the darkest of nights, and there was Once upon a time, when Adam was a perfect little gentleman, he was presented at Court as a guarantee of good character. Once upon a time, Adam and Eve made a mistake. They ate the fruit from the tree of immortality and split the consequences Once upon a time, there was a set of programming fundamental principles known as the "X model." This is a set of
バッチ処理によるサンプル
import openai # for making OpenAI API requests
num_stories = 10
prompts = ["Once upon a time,"] * num_stories
# batched example, with 10 stories completions per request
response = openai.Completion.create(
model="curie",
prompt=prompts,
max_tokens=20,
)
# match completions to prompts by index
stories = [""] * len(prompts)
for choice in response.choices:
stories[choice.index] = prompts[choice.index] + choice.text
# print stories
for story in stories:
print(story)
Once upon a time, there were two sisters, Eliza Pickering and Ariana 'Ari' Lucas. When these lovely Once upon a time, Keene was stung by a worm — actually, probably a python — snaking through his leg Once upon a time, there was a professor of physics during the depression. It was difficult, during this time, to get Once upon a time, before you got sick, you told stories to all and sundry, and your listeners believed in you Once upon a time, there was one very old nice donkey. He was incredibly smart, in a very old, kind of Once upon a time, the property of a common lodging house was a common cup for all the inhabitants. Betimes a constant Once upon a time, in an unspecified country, there was a witch who had an illegal product. It was highly effective, Once upon a time, a long time ago, I turned 13, my beautiful dog Duncan swept me up into his jaws like Once upon a time, as a thoroughly reformed creature from an army of Nazis, he took On Judgement Day myself and his Once upon a time, Capcom made a game for the Atari VCS called Missile Command. While it was innovative at the time
以上