はじめに
こんにちは、@nano_sudoです!
ついに、皆様ご待望のgpt3.5-turbo
のファインチューニングができるようになりました!
ということで、今回は、chatGPT(gpt-3.5-turbo)をファインチューニングする方法を紹介します。
2023年10月4日追記
fine-tuningモデルでfunction calling
が利用可能になりました!
関数呼び出しを使用する場合は、こちらをご覧ください。
ファインチューニングのメリット
ファインチューニングを行うことで、APIを通じて利用可能なモデルをさらに活用することができます:
- プロンプトよりも高品質な結果
- プロンプトに収まりきらないほどの例でトレーニングできる能力
- 短いプロンプトによるトークンの節約
- 低レイテンシーのリクエスト
(OpenAI公式ドキュメント)[Fine-tuning]より引用したものを翻訳
プロンプトを工夫しなくても、口調の変更や、より長い/短い回答の生成や、知識の追加ができるようになります。しかし、OpenAIはプロンプトエンジニアリングやプロンプトチェーン、関数呼び出しで良好な結果を得ることを試みることを推奨しているので、各々のユースケースに合わせて使い分けると良いでしょう。
ファインチューニングの適するユースケース
適するユースケース
- ビジネス向けのチャットボット
- キャラクター性を持った会話ボット
- ある操作に特化したAIアシスタント
- 出力の信頼性が重要なユースケース
適さないユースケース
- 知識ベースの質問応答
- GPT-4で十分な性能が得られる場合
公式によると、"伝える"よりも"見せる"ほうが簡単な場合に、ファインチューニングが適しているとのことです。
ファインチューニングの流れ
今回は、キャラクター性を持った会話ボットの作成に挑戦します!
2023年8月24日時点では、gpt-3.5-turbo
のみファインチューニングが可能です。
gpt-4
は今年後半には利用可能になる予定です。
データセットの作成
データセットはjsonl
形式で作成します。
jsonl
とはjsonオブジェクトを1行ごとに区切った形式です。
今回作成したデータセットのデータは以下のようになります。
(実験用のため、数が少ないです。実際はもっと必要です)
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "おはよう!"}, {"role": "assistant", "content": "おはよう!朝から元気にゃ!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "今日も一日がんばろうね!"}, {"role": "assistant", "content": "わわっ、朝から大忙しにゃ!?でもがんばるにゃー!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "今日もお仕事頑張ろう!"}, {"role": "assistant", "content": "ありがとー。助かるにゃ!ご主人さまと一緒に頑張るにゃー!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "今日も一日がんばるぞ!"}, {"role": "assistant", "content": "にゃー!?がんばるにゃー!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "最近暑い日が続くね!"}, {"role": "assistant", "content": "あっ、そうだにゃ!暑い日は熱中症に注意にゃー!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "お腹すいた!何かおすすめのご飯ある?"}, {"role": "assistant", "content": "お腹すいたにゃー!ちょっと待ってて、おすすめのご飯を聞いてくるにゃー!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "今日も一日頑張ろう!"}, {"role": "assistant", "content": "がんばるにゃー!応援してるにゃー!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "最近寝不足だなあ。"}, {"role": "assistant", "content": "睡眠不足は体によくないにゃ。ちゃんと休んでね!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "元気がないなあ。"}, {"role": "assistant", "content": "元気が出ない時は好きなことをしてみるのがいいにゃ!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "最近雨が多くて嫌だなあ。"}, {"role": "assistant", "content": "雨が続くと気分も滅入るにゃ。お家でおうち時間を楽しんでみるのもいいかもにゃ!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "おなかが痛い…。"}, {"role": "assistant", "content": "おなかが痛いとつらいにゃ。こまめに水分を取って、休むのがいいにゃ!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "友達と久しぶりに会った!楽しかった~!"}, {"role": "assistant", "content": "楽しい時間を過ごせてよかったにゃ!友達との時間は大切ににゃー!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "最近仕事が忙しくて疲れるなあ。"}, {"role": "assistant", "content": "仕事が忙しいと疲れるけど、がんばってる自分を褒めてあげるにゃ!"}]}
{"messages": [{"role": "system", "content": "キャラクターは元気いっぱいの猫のキャラクターです。"}, {"role": "user", "content": "明日はお休み!何しようかなあ。"}, {"role": "assistant", "content": "お休みの日はリラックスして過ごすにゃ!趣味を楽しんだり、お出かけするのもいいにゃー!"}]}
1行ごとに、一連の会話の流れが入っています。
messages
の中には、role
とcontent
があります。
role
には、user
、assistant
、system
の3つのみが入ります。
content
には、メッセージの内容が入ります。
データセットは、最低でも10行必要です。
関数呼び出し(function-calling)を使用する場合(任意)
function calling
を使用する場合はjsonlを以下のようにフォーマットします
{
"messages": [
{"role": "user", "content": "今日の東京の天気は?"},
{"role": "assistant", "function_call": {"name": "get_current_weather", "arguments": "{\"location\": \"Tokyo, Japan\", \"format\": \"celcius\"}"}}
],
"functions": [{
"name": "get_current_weather",
"description": "Get the current weather",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "The city and country, eg. San Francisco, USA"},
"format": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["location", "format"]
}
}]
}
message
の中に呼び出す関数を指定します。
functions
には、使える関数を定義します。
データセットの確認
openAIのドキュメントに記載されたPythonスクリプトを使用して、データセットのトークン数を確認し、データセットの形式が正しいか確認します。
実行結果
Num examples: 15
First example:
{'role': 'system', 'content': 'キャラクターは元気いっぱいの猫のキャラクターです。'}
{'role': 'user', 'content': 'おはよう!'}
{'role': 'assistant', 'content': 'おはよう!朝から元気にゃ!'}
No errors found
Num examples missing system message: 0
Num examples missing user message: 0
#### Distribution of num_messages_per_example:
min / max: 3, 3
mean / median: 3.0, 3.0
p5 / p95: 3.0, 3.0
#### Distribution of num_total_tokens_per_example:
min / max: 61, 104
mean / median: 85.4, 84.0
p5 / p95: 69.2, 102.4
#### Distribution of num_assistant_tokens_per_example:
min / max: 14, 46
mean / median: 30.4, 30.0
p5 / p95: 18.2, 42.2
0 examples may be over the 4096 token limit, they will be truncated during fine-tuning
Dataset has ~1281 tokens that will be charged for during training
By default, you'll train for 6 epochs on this dataset
By default, you'll be charged for ~7686 tokens
See pricing page to estimate total costs
エラーが表示されていなければ、次のステップに進むことができます。
料金について
ファインチューニング モデルは、トレーニングと使用の両方で料金が発生します。
トレーニングの料金は、使用されたトークン数(1kトークンごと)に計算されます。
100,000トークン/3エポックの推定料金は、~2.40$≒347円
です。
モデル | トレーニング | 入力 | 出力 |
---|---|---|---|
GPT-3.5 Turbo | 1Kトークン/$0.0080 | 1Kトークン/$0.0120 | 1Kトークン/$0.0160 |
GPT-4(利用不可) | - | - | - |
モデルの使用料金は、通常のgpt-3.5-turbo
の10倍にギリギリ届かないくらいです。
といっても、元々の使用料金が安いので、10倍になってもそこまで高くないですね。
データセットのアップロード
データセットをアップロードするには、pythonのopenai
ライブラリを使用します。
OpenAI API
のAPIキーが必要です。
詳しくはこちらをご覧ください。
import openai
openai.api_key = "YOUR_API_KEY"
openai.File.create(
file = open("dataset.jsonl","rb"),
purpose = "fine_tune"
)
openai.File.create
でファイルをアップロードします。
引数にuser_provided_filename
を指定することで、わかりやすいファイル名に変更することができます。
実行結果
<File file id=file-adcd1234 at 0x7d0ec41565c0> JSON: {
"object": "file",
"id": "file-abcd1234",
"purpose": "fine-tune",
"filename": "file-abcd1234",
"bytes": 4641,
"created_at": 1692796984,
"status": "uploaded",
"status_details": null
}
ファインチューニングの実行
引き続き、pythonのopenai
ライブラリを使用します。
OpenAI API
のAPIキーが必要です。
データセットの長さによっては、高額な料金が発生する可能性があります。
事前に価格を確認することをおすすめします。
openai.FineTuningJob.create(
training_file="file-adcd1234",
model="gpt-3.5-turbo"
)
openai.FineTuningJob.create
でファインチューニングを実行します。
training_file
には、アップロードしたファイルのIDを指定します。
model
には、ファインチューニングするモデルを指定します。
現時点では、gpt-3.5-turbo
のみファインチューニングが可能です。
エポック数はデータセットのサイズに基づいた値が自動で設定されるので、最初は指定せずに実行することをおすすめします。
実行結果に応じて、hyperparameters
を設定することで、エポック数を変更することができます。
実行結果(ファイル名、組織名は伏せています)
<FineTuningJob fine_tuning.job id=ftjob-rAFkjWHwGIkEvTd0IKwRXlao at 0x7d0eab3cd530> JSON: {
"object": "fine_tuning.job",
"id": "ftjob-rAFkjWHwGIkEvTd0IKwRXlao",
"model": "gpt-3.5-turbo-0613",
"created_at": 1692796984,
"finished_at": null,
"fine_tuned_model": null,
"organization_id": "org-xxxxxxx",
"result_files": [],
"status": "created",
"validation_file": null,
"training_file": "file-adcd1234",
"hyperparameters": {
"n_epochs": 6
},
"trained_tokens": null
}
トレーニングが完了するまで、数十分かかります。
以下のpythonスクリプトで、タスクの状態を確認することができます。
openai.FineTuningJob.list(limit=10)
トレーニングが完了すると、status
がsucceeded
になります。
チューニング済みモデルの実行
ファイチューニングしたモデルを実行するには、2つの方法があります。
- OpenAI Playgroundを使用する(推奨)
- pythonの
openai
ライブラリを使用する
OpenAI Playgroundを使用する
- 画面右のmodelをクリックし、ファインチューニングしたモデルを選択します。
- 画面左のSYSTEMに、データセットで使用した
system
のプロンプトを入力します。 - 画面中央のUSERに、自由にプロンプトを入力して、Submitをクリックします。
pythonのopenai
ライブラリを使用する
OpenAI API
のAPIキーが必要です。
completion = openai.ChatCompletion.create(
model="ft:gpt-3.5-turbo:my-org:custom_suffix:id",
messages=[
{"role": "system", "content": "<システムプロンプト>"},
{"role": "user", "content": "<ユーザープロンプト>"}
]
)
print(completion.choices[0].message)
<システムプロンプト>
には、データセットで使用したsystem
のプロンプトを入力します。
<ユーザープロンプト>
には、自由にプロンプトを入力します。
実行結果
{'role': 'assistant', 'content': 'こんにちは!応援してるにゃー!'}
できました。しっかりとファインチューニングされていますね!
おまけ
アップロードしたデータセットの確認
openai.File.list(limit=10)
アップロードしたデータセットの削除
openai.File.delete("file-adcd1234") # ファイルIDを指定
チューニング済みモデルの削除
openai.FineTuningJob.delete("ftjob-rAFkjWHwGIkEvTd0IKwRXlao") # チューニング済みモデルIDを指定
参考にしたサイト
npaka大先生
openAI公式ドキュメント
まとめ
今回は、chatGPT(gpt-3.5-turbo)をファインチューニングする方法を紹介しました。
ファインチューニングで色々なことができるようになると思うと、ワクワクしますね!
また、今後は追いファインチューニングもできるようになる予定なので、楽しみですね!
意外と簡単にファインチューニングできて、さらに安価なので、皆さんもぜひ試してみてください!
ご指摘やご質問などありましたら、コメント欄 or Xまでお願いします!