MENU

Knowledge Oasisは主にAIとAWSの知識を共有するブログです。その他ITに関する知識やまれに生活に役立つ知識も共有するかもしれません。

KOふみ
名前はKOふみ(こふみ)。独立系SIerで20年のキャリアを持ち、新人研修の講師から請負開発まで幅広く経験。現在はAIを駆使したソリューション開発に従事。資格は応用情報技術者、データベーススペシャリスト、プロジェクトマネージャー、PMP、簿記2級。AWS学習中で、将来はAWSアンバサダーを目指す。

OpenAIのAPI完全制覇パート2 出力内容制御編

OpenAIのAPI完全制覇パート2 出力内容制御編
  • URLをコピーしました!

API完全制覇パート2ではPlaygroundでは試せないパラメータについて解説していきます。Function callingについてはパート3で解説する予定です。

目次

今回解説するパラメータ

今回解説するパラメータはマーカーを付けた7つです。

  • model (string, 必須): 使用するモデルのID。
  • messages (array, 必須): 会話を構成するメッセージのリスト。
  • temperature (number, オプション): 0から2.0までの範囲。出力のランダム性を制御。
  • top_p (number, オプション): 0から1の範囲。上位の確率質量のトークンのみを考慮。
  • n (integer, オプション): デフォルトは1。各入力メッセージに対して生成される回答の数。
  • stop (string or array, オプション): APIがトークン生成を停止するシーケンス。
  • max_tokens (integer, オプション): 生成される最大トークン数。
  • presence_penalty (number, オプション): -2.0から2.0の範囲。新しいトピックについて話す可能性を制御。
  • frequency_penalty (number, オプション): -2.0から2.0の範囲。繰り返しを減少。
  • logit_bias (map, オプション): トークンの出現確率を調整。
  • logprobs (bool, オプション): 出力トークンの対数確率を返すかどうか
  • top_logprobs (integer, オプション): 各出力トークンについて採用されなかったトークンを出力する
  • response_format (object, オプション): 出力する形式を指定する。 jsonを指定可能。
  • seed (integer, オプション): 2024/05/24時点ではベータ版。seed値を指定する。
  • stream (boolean, オプション): デフォルトはfalse。部分的なメッセージをストリーミングするかどうか。
  • stream_options (object, オプション): stream時の動作を制御するオプション。
  • tools (array, オプション): モデルが呼び出せる関数を定義する。
  • tool_choice (string, オプション): toolsで指定した関数の呼び出しを制御する。
  • user (string, オプション): エンドユーザーの一意の識別子。

LLMの思考を確認するパラメータ

logprobsとtop_logprobsはLLMが採用したトークンの情報を確認することができます。採用されたトークンや採用されなかったトークンとそれぞれの対数確率を確認することでどのような文章が作られる可能性があったかを確認することができます。

logprob

トークンの対数確率を表示するかを制御します。
デフォルト値はFalse。

top_logprobs

LLMが検討したトークンの対数確率が大きい方からいくつを表示するかを指定します。
最大値は20です。

これだけの説明では良く分からないと思うので実際の動きを見てみましょう

logprobとtop_logprobsの動作確認

from openai import OpenAI
client = OpenAI()

completion = client.chat.completions.create(
  model="gpt-4o",
  messages=[
    {"role": "user", "content": "2007年に日本一になった球団を5文字で答えて?"}
  ],
  logprobs=True,
  top_logprobs=2,
)

print(completion)

上記のコードを実行した結果が以下です。見やすいように整形して、きりの良いところで止めています。
APIの回答は「中日ドラゴンズ」でした。最初のtokenを確認すると”中”になっています。top_logprobsを確認すると”中”と”200″が検討されたことが分かります。logprobが対数確率で値が大きい”中”が採用されたことが分かります。
このようにLLMが回答を生成する際に検討した内容を確認することができます。top_logprobs=2 としたので確率の高い上位2件が表示されています。

content = [
    ChatCompletionTokenLogprob(
        token='中', 
        bytes=[228, 184, 173], 
        logprob=-0.31773233, 
        top_logprobs=[
            TopLogprob(token='中', bytes=[228, 184, 173], logprob=-0.31773233),
            TopLogprob(token='200', bytes=[50, 48, 48], logprob=-1.9427323)
        ]
    ), 
    ChatCompletionTokenLogprob(
        token='日', 
        bytes=[230, 151, 165], 
        logprob=-4.5133394e-05, 
        top_logprobs=[
            TopLogprob(token='日', bytes=[230, 151, 165], logprob=-4.5133394e-05),
            TopLogprob(token='日は', bytes=[230, 151, 165, 227, 129, 175], logprob=-10.500045)
        ]
    ), 
    ChatCompletionTokenLogprob(
        token='ドラ', 
        bytes=[227, 131, 137, 227, 131, 169], 
        logprob=-0.004338495, 
        top_logprobs=[
            TopLogprob(token='ドラ', bytes=[227, 131, 137, 227, 131, 169], logprob=-0.004338495),
            TopLogprob(token='\\xe7\\xab', bytes=[231, 171], logprob=-5.6293383)
        ]
    ), 
    ChatCompletionTokenLogprob(
        token='ゴ', 
        bytes=[227, 130, 180], 
        logprob=-1.700133e-05, 
        top_logprobs=[
            TopLogprob(token='ゴ', bytes=[227, 130, 180], logprob=-1.700133e-05),
            TopLogprob(token='ンズ', bytes=[227, 131, 179, 227, 130, 186], logprob=-12.625017)
        ]
    ), 
    ChatCompletionTokenLogprob(
        token='ンズ', 
        bytes=[227, 131, 179, 227, 130, 186], 
        logprob=-0.000203898, 
        top_logprobs=[
            TopLogprob(token='ンズ', bytes=[227, 131, 179, 227, 130, 186], logprob=-0.000203898),
            TopLogprob(token='ン', bytes=[227, 131, 179], logprob=-8.500204)
        ]
    )
]

出力内容を制御するパラメータ

JSON形式で出力させたり、ChatGPTの画面のように出来上がった部分から出力したり、同じ乱数を使用することで極力同じ出力になるように制御するなど出力内容を制御するパラメータについて解説します。

logit_bias

特定のトークンの出現をおさえたり、特定のトークンの出現率を上げたりバイアスをかけるパラメータです。トークンIDに対して-100~100までを指定できます。負数だと出現を抑えて、正数だと出現確率を上げます。注意点としてはトークンIDで指定するという点です。トークンIDはOpenAIのTokenizerから調べることができます。2024年6月5日時点ではGPT-4oのトークンを調べる事ができません。

from openai import OpenAI
client = OpenAI()

completion = client.chat.completions.create(
  model="gpt-4",
  messages=[
    {"role": "user", "content": "2007年に日本一になった球団を5文字で答えて?"}
  ],
  logit_bias={45923:-100, 32131:-100},
)

print(completion)

上記のコードは『ドラ』というトークンを出現させないように制御してみました。GPT-4のトークンIDを調べたのでmodelはGPT-4に変えてあります。実行結果は以下の通り。『ドラゴンズ』が使えないので『龍戦』と言い換えてきました。面白いですね。

ChatCompletion(
    id='chatcmpl-9WVaq1DU0qCOBEesRUJWjsLsfQVSI',
    choices=[
        Choice(
            finish_reason='stop',
            index=0,
            logprobs=None,
            message=ChatCompletionMessage(
                content='中日龍戦',
                role='assistant',
                function_call=None,
                tool_calls=None
            )
        )
    ],
    created=1717534968,
    model='gpt-4-0613',
    object='chat.completion',
    system_fingerprint=None,
    usage=CompletionUsage(
        completion_tokens=6,
        prompt_tokens=30,
        total_tokens=36
    )
)

response_format

出力をJSON形式に変えるパラメータです。注意点としては{ "type": "json_object" }を指定するだけではなく、プロンプトでJSONで出力するように指示しないといけないことです。指示を忘れるとトークンの制限数まで延々と空白を出力し続けるような事象が発生するようです。

from openai import OpenAI
client = OpenAI()

completion = client.chat.completions.create(
  model="gpt-4o",
  messages=[
    {"role": "user", "content": "2007年に日本一になった球団は?JSONで球団名と球場を回答してください。"}
  ],
  response_format={ "type": "json_object" },
)

print(completion)

上記を実行した結果は以下の通りです。正しくJSONで回答されています。JSONの形式を指定したい場合はプロンプトで指示するとその形式で出力してくれます。

ChatCompletion(
    id='chatcmpl-9WXiVZFwQTnc967jI4AeqDcEgnDXt',
    choices=[
        Choice(
            finish_reason='stop',
            index=0,
            logprobs=None,
            message=ChatCompletionMessage(
                content='{\n  "球団名": "中日ドラゴンズ",\n  "球場": "ナゴヤドーム"\n}',
                role='assistant',
                function_call=None,
                tool_calls=None
            )
        )
    ],
    created=1717543131,
    model='gpt-4o-2024-05-13',
    object='chat.completion',
    system_fingerprint='fp_319be4768e',
    usage=CompletionUsage(
        completion_tokens=27,
        prompt_tokens=31,
        total_tokens=58
    )
)

JSON形式で出力できることで後続処理へ連携しやすくなります。

{ "type": "json_object" }を指定するだけではなく、プロンプトでJSONで出力する必要がある。
指示を忘れるとトークンの制限数まで延々と空白を出力し続ける可能性がある。

seed

2024年6月5日時点ではベータ版の機能です。

シード値を指定することで同じ回答となるように最善を尽くしてくれます。決定論的な動作は保証されていません。

from openai import OpenAI
client = OpenAI()

completion = client.chat.completions.create(
  model="gpt-4o",
  messages=[
    {"role": "user", "content": "2007年に日本一になった球団は?"}
  ],
  seed=1,
)

print(completion)

上記を3回実行した結果が以下になります。最初の2回は同じ回答で最後の1回は微妙に違っていました。しかし、回答の方向性や雰囲気はほぼ同じでした。

  • 2007年に日本一になった野球チームは中日ドラゴンズです。中日ドラゴンズは、北海道日本ハ ムファイターズを4勝1敗で下して日本シリーズを制し、53年ぶりの日本一を達成しました。
  • 2007年に日本一になった野球チームは中日ドラゴンズです。中日ドラゴンズは、北海道日本ハ ムファイターズを4勝1敗で下して日本シリーズを制し、53年ぶりの日本一を達成しました。
  • 2007年に日本一になった野球チームは中日ドラゴンズです。中日ドラゴンズは、北海道日本ハ ムファイターズを4勝1敗で下して、53年ぶりの日本一に輝きました。

stream

Trueを設定することで回答を一括で返すのではなく採用されたトークンから次々と返すようにできます。ChatGPTの画面で回答がリアルタイムで画面に表示されるようなイメージです。

stream_options

stream=Trueの場合に設定できるオプションです。{"include_usage": true}を指定することでstreamの最後にトークン数の情報を付加することができます。

from openai import OpenAI
client = OpenAI()

completion = client.chat.completions.create(
  model="gpt-4o",
  messages=[
    {"role": "user", "content": "2007年に日本一になった球団は?"}
  ],
  stream=True,
  stream_options={"include_usage": True},
)

for chunk in completion:
  print(chunk.choices)
  print(chunk.usage)

上記のコードを動かした結果は以下の通りです。長いので途中を省略しています。”200″、”7″、”年”、”に”とトークンごとに回答が出力されていることが分かります。一番最後にトークン数の情報が出力されていることも確認できます。

[Choice(delta=ChoiceDelta(content='200', function_call=None, role=None, tool_calls=None), finish_reason=None, index=0, logprobs=None)]     
None
[Choice(delta=ChoiceDelta(content='7', function_call=None, role=None, tool_calls=None), finish_reason=None, index=0, logprobs=None)]       
None
[Choice(delta=ChoiceDelta(content='年', function_call=None, role=None, tool_calls=None), finish_reason=None, index=0, logprobs=None)]      
None
[Choice(delta=ChoiceDelta(content='に', function_call=None, role=None, tool_calls=None), finish_reason=None, index=0, logprobs=None)]      
None

~中略~

[Choice(delta=ChoiceDelta(content='ました', function_call=None, role=None, tool_calls=None), finish_reason=None, index=0, logprobs=None)]
None
[Choice(delta=ChoiceDelta(content='。', function_call=None, role=None, tool_calls=None), finish_reason=None, index=0, logprobs=None)]      
None
[Choice(delta=ChoiceDelta(content=None, function_call=None, role=None, tool_calls=None), finish_reason='stop', index=0, logprobs=None)]    
None
[]
CompletionUsage(completion_tokens=60, prompt_tokens=19, total_tokens=79)

チャットボットのアプリなどを作るときにはstream=Trueにして回答を順次表示することで利用者を待たせることなくレスポンスを返すことができるようになります。

まとめ

いかがだったでしょうか? 出力内容を制御するパラメータについて解説しました。APIリファレンスだけ読んでいても動きがイメージできないと思います。実際に動くコードと結果をペアで確認することで動作をイメージして頂けたのではないでしょうか。

次回はtoolsとtool_choiceを用いた関数呼び出し(Function Calling)について解説していきます。

  • URLをコピーしました!

コメント

コメントする

目次