去年 (2024/9/16) 我儲值 10 美元購買 OpenAI API 實支實付 (Pay As You Go) 付費方案以便能透過 API 進行基本的文字聊天測試, 但不久因為分心玩 ESP32 暫停下來. 最近在讀 LangChain 的書時, 覺得還是要先對 OpenAI API 做過完整的呼叫測試後再來學 LangChain 較好, 所以今天回頭來測試 OpenAI API 的參數.
OpenAI API 網址如下 :
檢視 Billing 發現, 使用至今還有 9.99 美元可用額度 (僅用到少量文字聊天而已) :
# OpenAI API 基礎必修課 (蔡文龍, 碁峰 2024)
# ChatGPT 開發手冊 (2023)
# ChatGPT 開發手冊 Turbo x Vision 進化版 (旗標 2024)
# 駕馭 ChatGPT 4 (柯克 陳葵懋, 博碩 2023)
OpenAI API 的常用參數摘要說明如下表 :
OpenAI API 參數 | 說明 |
---|---|
max_tokens | 設定回應之 tokens 上限, 預設 None, 實際長度由模型自行而定 |
n | 設定要生成幾組 completion 回應內容 (1~128), 預設 1 |
stop | 設定何時停止生成內容, (串列, 最多可設 4 個字串元素), 預設 None |
temperature | 設定回應的隨機性 (0~1), 值越高回應越有創意 |
top_p | 設定回應的隨機性 (0~1), 只將前 top_p 的可能 token 列入生成候選名單 |
frequency_penalty | 懲罰重複性 (0~2), 懲罰 token 出現頻率, 出現越多次再次出現機會越低 |
presence_penalty | 懲罰重複性 (0~2), 懲罰 token 是否已生成過, 即使只出現一次再次出現機會越低 |
logprobs | 設定回應時每個 token 的最大 log 機率 (最多返回 5 個 token 機率), 預設 None |
echo | 是否回傳提示詞 (prompt) 本身, 預設 false |
best_of | 只能在 n=1 時使用, 模型會產生 best_of 個回應, 然後傳回機率最高者 |
logit_bias | 調整特定 token 的出現機率, 可用來偏好或避免某些詞, 格式為字典 |
user | 用於識別請求使用者, 方便監控與管理 API 使用 |
注意 frequence_penalty 與 presence_penalty 的細微差異 :
- frequence_penalty :
懲罰 "出現頻率", 已出現過越多次者再次出現之機會就越低 (避免詞彙重複出現) - presence_penalty :
懲罰 "存在性", 只要已出現過 (即使只有一次) 再次出現之機會就越低 (強迫使用新詞).
其次, temperature 不宜與 top_p 同時設定.
本篇先來測試與回應訊息的數量有關的 max_tokens, stop, 與 n 這三個參數, 在測試之前先匯入 OpenAI 類別並建立 OpenAI 物件 :
>>> from openai import OpenAI
>>> api_key='填入 API key'
>>> client=OpenAI(api_key=api_key)
>>> type(client)
<class 'openai.OpenAI'>
1. max_tokens 參數 :
此參數用來限制回應訊息的 token 數量上限, 當回應的 token 即將超過這值時便會被停止回應 (實際回應之 token 數會小於等於此上限), 首先不傳遞 max_tokens 參數 (預設 None 表示限制為所使用之模型的回應上限) :
>>> chat_completion=client.chat.completions.create(
messages=[{'role': 'user', 'content': '你是誰?'}],
model='gpt-3.5-turbo'
)
>>> print(chat_completion.choices[0].message.content)
我是一個基於人工智慧技術的對話型機器人,可以和您進行對話、回答問題和提供信息。我是一個虛擬助手,讓您在需要時找到需要的答案和支持。有什麼我可以幫到您的嗎?
如果傳入 max_tokens 限制回應 token 不可超過 4 個 :
>>> chat_completion=client.chat.completions.create(
messages=[{'role': 'user', 'content': '你是誰?'}],
model='gpt-3.5-turbo',
max_tokens=4
)
>>> print(chat_completion.choices[0].message.content)
我是一
可見回應受到 token 限制只傳回最前面的三個字 '我是一', 可用 finish_reason 屬性檢視回應完成原因 :
>>> print(chat_completion.choices[0].finish_reason)
length
檢視回應 '我是一' 之 token 數剛好是 4 個 :
>>> print(chat_completion.usage.completion_tokens)
4
可以參考下面這篇用 tiktoken 模組來檢視 '我是一' 的 token 編碼 :
>>> import tiktoken
>>> encoder=tiktoken.encoding_for_model('gpt-3.5-turbo')
>>> tokens=encoder.encode('我是一')
>>> tokens
[37046, 21043, 15120]
可見 '我是一' 對應到 [37046, 21043, 15120] 這三個 token, 這與上面 completion_tokens=4 差了一個, 此乃 API 會在生成回應的結尾自動添加一個特殊的 end-of-sequence (EOS) token, 這樣就剛好到達 max_tokens=4 限制而結束回應, 如果要再回應下一個字 '個' 的話就會超過了, 因為 '個' 占兩個 token :
>>> tokens=encoder.encode('我是一個')
>>> tokens
[37046, 21043, 15120, 20022, 233]
最後面的 [20022, 233] 就是 '個' 的 token 代碼, 可以用 decode() 方法反查確認 :
>>> print(encoder.encode("個"))
[20022, 233]
>>> encoder.decode([20022, 233])
'個'
2. stop 參數 :
stop 參數用來指定一個黑名單字串串列 (最多 4 個), 當生成的字串出現在此黑名單中時就停止生成, 例如問 '你是誰?' 通常會回答 '我是 ...' :
>>> chat_completion=client.chat.completions.create(
messages=[{'role': 'user', 'content': '你是誰?'}],
model='gpt-3.5-turbo'
)
>>> chat_completion.choices[0].message.content
'我是一個語言模型助手,可以回答你的問題和提供信息。有什麼我可以幫助你的嗎?'
若傳入 stop='是' 參數 :
>>> chat_completion=client.chat.completions.create(
messages=[{'role': 'user', 'content': '你是誰?'}],
model='gpt-3.5-turbo',
stop=['是']
)
>>> chat_completion.choices[0].message.content
'我'
可見生成到 '是' 時發現此字出現在 stop 黑名單中, 於是模型就停止生成了, 檢視 finish_reason 可知停止生成的原因是 stop 參數所致 :
>>> chat_completion.choices[0].finish_reason
'stop'
下面是傳入參數 stop 有四個元素的範例 :
>>> chat_completion=client.chat.completions.create(
messages=[{'role': 'user', 'content': '你是誰?'}],
model='gpt-3.5-turbo',
stop=['智能', '機器', '人工', '聊天']
)
>>> chat_completion.choices[0].message.content
'我是一個'
後面生成的字串可能是這四個元素之一, 因此就停止生成了.
但有時候 stop 黑名單也會破功攔不住, 例如 :
>>> chat_completion=client.chat.completions.create(
messages=[{'role': 'user', 'content': '你是誰?'}],
model='gpt-3.5-turbo',
stop=['個']
)
>>> chat_completion.choices[0].message.content
'我是一個語言模型助手,可以回答你的問題和提供信息。有什麼我可以幫忙你的嗎?'
確切原因還不清楚.
3. n 參數 :
此參數用來設定要模型生成幾個回應 (預設是 1), 例如 :
>>> chat_completion=client.chat.completions.create(
messages=[{'role': 'user', 'content': '你是誰?'}],
model='gpt-3.5-turbo',
n=2
)
>>> type(chat_completion)
<class 'openai.types.chat.chat_completion.ChatCompletion'>
此處我們傳入 n=2 要求生成兩個回應, 如果直接用 print() 檢視回應資料會擠成一團 :
>>> print(chat_completion)
ChatCompletion(id='chatcmpl-B7yuztfouzLr1XlOCvL6wppNN22wf', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='我是一个语言模型助手,可以回答你关于各种主题的问题。有什么我可以帮助你的吗?', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None)), Choice(finish_reason='stop', index=1, logprobs=None, message=ChatCompletionMessage(content='我是一個能夠回答問題、提供信息和進行對話的人工智能助手。我是專為幫助人們解決問題和提供信息而設計的。您有什麼問題需要我的幫助嗎?', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=None))], created=1741242165, model='gpt-3.5-turbo-0125', object='chat.completion', service_tier='default', system_fingerprint=None, usage=CompletionUsage(completion_tokens=128, prompt_tokens=12, total_tokens=140, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0)))
為了能呈現回應資料的結構, 我們改用第三方套件 rich 的 print() 函式來顯示回應, 關於 rich 用法參考 :
>>> from rich import print as pprint
>>> pprint(chat_completion)
ChatCompletion(
id='chatcmpl-B7yuztfouzLr1XlOCvL6wppNN22wf',
choices=[
Choice(
finish_reason='stop',
index=0,
logprobs=None,
message=ChatCompletionMessage(
content='我是一个语言模型助手,可以回答你
关于各种主题的问题。有什么我可以帮助你的吗?',
refusal=None,
role='assistant',
audio=None,
function_call=None,
tool_calls=None
)
),
Choice(
finish_reason='stop',
index=1,
logprobs=None,
message=ChatCompletionMessage(
content='我是一個能夠回答問題、提供信息
和進行對話的人工智能助手。我是專為幫助人們解決問題和提供信息而設計的。您有什麼
refusal=None,
role='assistant',
audio=None,
function_call=None,
tool_calls=None
)
)
],
created=1741242165,
model='gpt-3.5-turbo-0125',
object='chat.completion',
service_tier='default',
system_fingerprint=None,
usage=CompletionUsage(
completion_tokens=128,
prompt_tokens=12,
total_tokens=140,
completion_tokens_details=CompletionTokensDetails(
accepted_prediction_tokens=0,
audio_tokens=0,
reasoning_tokens=0,
rejected_prediction_tokens=0
),
prompt_tokens_details=PromptTokensDetails(
audio_tokens=0,
cached_tokens=0
)
)
)
可見生成的兩個回應是放在 choices 串列的 message 物件的 content 屬性裡, 可以用迴圈一一擷取出來 :
>>> for choice in chat_completion.choices:
print(choice.index, '. ', choice.message.content)
0. 我是一个语言模型助手,可以回答你关于各种主题的问题。有什么我可以帮助你的吗?
1. 我是一個能夠回答問題、提供信息和進行對話的人工智能助手。我是專為幫助人們解決問題和提供信息而設計的。您有什麼問題需要我的幫助嗎?
當然也可以直接使用索引取得 :
>>> chat_completion.choices[0].message.content
'我是一个语言模型助手,可以回答你关于各种主题的问题。有什么我可以帮助你的吗?'
>>> chat_completion.choices[1].message.content
'我是一個能夠回答問題、提供信息和進行對話的人工智能助手。我是專為幫助人們解決問題和提供信息而設計的。您有什麼問題需要我的幫助嗎?'
沒有留言 :
張貼留言