👋

OpenAI の API を使って python で ChagGPT の Fine-Tuning 機能を使う

2024/05/14に公開

主旨

2024/05 時点での python を使って Fine-Tuning する手順のメモです。基本、公式の通りやればできます。なお、手順や方法は頻繁に変更されるため、公式を参照するほうが確実です。また、Fine-Tuning 自体は python を使わなくても可能です。詳しくは下記を参照してください。

実行環境の準備

$ conda create -n finetuning python=3.12
$ conda activate finetuning
$ pip install openai python-dotenv

python-dotenv.env に API キーを保存するためだけに使用します。API KEY 管理を別の方法でする場合は必要ありません。

Fine-Tuning 手順の概要

OpenAI の Fine Tuning は、学習の処理自体は OpenAI のサーバで行います。ローカルで行う必要のある作業は、下記の6つです。

  • OpenAI の API_KEY を取得する
  • 学習用ファイル (jsonl) を作成する
  • 学習用のファイルを OpenAI のサーバに登録してファイルIDを取得する。
  • ファイルIDを指定してサーバ上で Tuning の処理を開始させる
  • Tuning の途中経過を確認し、終了したら学習済みのモデルIDを取得する
  • 学習済みのモデルIDを使用して文章生成のテストをする

いずれの処理も python のプログラムを使用せずとも行えますが、ここでは python を使用する方法について記述します。python を使わない方法については公式を参照してください。

OpenAI の API_KEY の取得と設定

OpenAI にログインして、API Keys ページへ行って "Create new secret key" を押して Project API Key を作成します。作成したら、python のプロジェクトを置くフォルダに .env ファイルを作成し、下記の形式で貼り付けます。

.env
OPENAI_API_KEY=sk-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

Project API Key は st- から始まる文字列です。このキーが漏洩すると、OpenAI のサービスを他人に自由につ買われて、無限に課金されてしまう可能性があります。管理には十分な注意が必要です。

github などでコードを管理する場合は、.gitignore.env ファイルを追加して、API Key が github に push されてしまわないようにします。

.gitignore
.env

設定できたら、下記のプログラムを実行して、.env に設定した API Key が読み込めるかどうか確認しておきます。

apikey.py
import os
from dotenv import load_dotenv

load_dotenv()

print(os.environ['OPENAI_API_KEY'])

.env 内の OPENAI_API_KEY に設定した文字列が表示されたら成功です。

$ python apikey.py
sk-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

学習用ファイルの作成

学習用ファイルは jsonl 形式で作成します。jsonl は各行に JSON の値またはオブジェクトが含まれるファイルのことです。OpenAI の Fine-Tuning では、messages[1].content に質問を記述し、messages[2].content に回答を記述します。それ以外のプロパティは、下記のフォーマットに従うようにします。

{"messages": [{"role": "system", "content": "<ここに何をするチャットボットなのかを書きます>"}, {"role": "user", "content": "<ここにユーザ側の発言を書きます>"}, {"content": "<ここにユーザ側の発言に対する返事を書きます>"}]}

質疑応答をさせるための学習データの具体例を、以下に示します。

{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "KiCad7で基板データを作成する時の最初のステップは何ですか"}, {"role": "assistant", "content": "PCB エディタを起動して、ネットリストのインポートをします。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "基板の設計ルールはどのように設定しますか"}, {"role": "assistant", "content": "設計ルールは、PCB エディタで設定できます。配線(トラック)の幅や部品、基板端からのクリアランスなどの制約を設定できます。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "配線(トラック)はどのように開始できますか"}, {"role": "assistant", "content": "配線は画面右側のメニューから「配線(X)」のアイコンをクリックするか、エディタの画面上で X を押すことで開始できます。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "KiCad7でデザインルールを検証するツールは何ですか"}, {"role": "assistant", "content": "デザインルールチェック(DRC)ツールです。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "基板上にシルクを配置する目的は何ですか"}, {"role": "assistant", "content": "基板の実装やテストの段階で、基板の上に配置される部品の位置、種類、向きをわかりやすくすることが主な目的です。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "配線後にDRCを使う理由は何ですか?"}, {"role": "assistant", "content": "基板データに、基板の製造上問題となるようなエラーや設計ルール違反がないことを確認し、基板が問題なく機能する状態で製造されるようにするためです。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "KiCad7では基板の両面にフットプリントを配置できますか"}, {"role": "assistant", "content": "はい、基板の表面と裏面の両方にフットプリントを配置できます。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "ネットリストは設計プロセスで何に使用されますか"}, {"role": "assistant", "content": "ネットリストとはは、フットプリントのパッド間の接続状態を論理的に表現したデータ(接続関係のリスト)のことです。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "デザインルールを設定する理由は何ですか"}, {"role": "assistant", "content": "エディタでの捜査中にデザインルールの一部が自動的に適用されるようになるため、ユーザはデザインルールに違反しない基板データを作成できます。"}]}
{"messages": [{"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"}, {"role": "user", "content": "DRCツールを使う目的は何ですか"}, {"role": "assistant", "content": "設計者が目で見ただけではわかりにくい、配線、フットプリント、シルク、ベタなどの配置ミス、配置漏れなどのエラーを発見できます。"}]}

JSON の場合は、複数のオブジェクトを並べるときは [] でくくって配列にする必要がありますが、jsonl の場合は配列化する必要はありません。ただし、オブジェクトを一行の中に収めて記述する必要があります。

学習に使う jsonl ファイルは、最低10行のデータを含む必要があります。上限は 4096トークン または 50MB です。

学習用ファイルの登録

下記のプログラムで、作成した jsonl ファイルを登録します。

registration.py
from openai import OpenAI
from dotenv import load_dotenv
import os

load_dotenv()
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])

res = client.files.create(
  file=open("data.jsonl", "rb"),
  purpose="fine-tune"
)

print( res.id )

学習データを含む data.jsonl ファイルと、API_KEY の書かれた .env ファイルが、プログラムを実行するフォルダと同じフォルダに配置されている必要があります。

実行すると、ターミナルに file- で始まる文字列(File ID)が表示されます。この文字列を記録しておきます。

$ python registration.py
file-xxxxxxxxxxxxxxxxxxxxxxxxx

上記プログラムを実行して、API キー関連のエラーが出る場合は、下記をチェックします。

  • .env ファイルが同じフォルダ内にあるか
  • .env ファイルの記述形式が間違っていないか
  • OpenAI のサイトからの API_KEY の文字列のコピーに失敗していないか

ファイル ID が分からなくなった時は、OpenAI のサイトの Storage のメニューで確認することもできます。

image

上記のWeb画面で、ファイルの登録(Upload)や削除を行うことも可能です。

Fine-Tuning の実行

登録したファイルIDを使って、Fine-Tuning を実行します。

tuning.py
from openai import OpenAI
from dotenv import load_dotenv
import os

load_dotenv()
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])

res = client.fine_tuning.jobs.create(
  training_file="file-xxxxxxxxxxxxxxxxxxxx", # ここに file ID をコピー
  model="gpt-3.5-turbo"
)
print( res.id )

実行すると、ftjob- で始まる JOB ID が表示されるので、これを控えておきます。

JOB ID は OpenAI のページの FineTuning の項目で確認することもできます。

Tuning の状態確認

Fine-Tuning は実行を開始したあと、完了するまでにしばらく時間がかかります。下記のプログラムで、Tuning の状態を確認できます。

from openai import OpenAI
from dotenv import load_dotenv
import os

load_dotenv()
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])

res = client.fine_tuning.jobs.retrieve("ftjob-xxxxxxxxxxxxxxxx") # ここに JOB ID をコピーする

if res.error.code is not None:
  print( "Error Code:", res.error.code )
  print( "Error Message:", res.error.message )
else:
  print( "Status:", res.status )
  print( "Fine Tuned Model ID: ", res.fine_tuned_model )

Tuning が正常に進んでいる時は、"Status: running" と表示されます。Tuning が完了すると "Status: succeeded" と表示され、次いで Fine Tuned Model ID (ft:gpt-3.5-turbo- で始まる文字列) が表示されます。このModel IDを控えておきます。

Tuning の途中でエラーが発生したときは、Error Code と Error Message が表示されます。エラーが発生する原因のほとんどは、登録したファイルのフォーマットのエラーであることが多いです。ファイルを見直してください。

Tuning 中のプロセスをキャンセルする

開始した Tuning のプロセスを停止したい場合は、下記のプログラムを使います。

from openai import OpenAI
from dotenv import load_dotenv
import os

load_dotenv()
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])

res = client.fine_tuning.jobs.cancel("ftjob-xxxxxxxxxxxxxxxx") # ここに JOB ID をコピーする

print( res )

Tuning したモデルの性能確認

Tuning したモデルを使って、実際に文章を生成してみます。

generate.py
from openai import OpenAI
from dotenv import load_dotenv
import os

load_dotenv()
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])
completion = client.chat.completions.create(
  model="ft:gpt-3.5-turbo-0125:yyyyyyyy", # ここに model ID をコピー
  messages=[
    {"role": "system", "content": "このチャットボットはKicad8に関する質問に答えます。"},
    {"role": "user", "content": "基板の設計ルールはどのように設定しますか"}
  ]
)

for res in completion.choices:
  print(res.message.content)

実行すると、生成された文章が表示されます。例えば、下記のような回答が得られます。

$ python generate.py
設計ルールは、[基板エディタ]のメニューから[設計ルールエディタ]を選択すると、幅、間隔、ネ ットクラスなどのルールを設定できます。
$ python generate.py
設計ルールは、PCB エディタでネットリストを生成する際に、ツール→基板エディタ の設定→デフォルトの設計ルールのエディタをクリックして、基板エディタで設定します。
$ python generate.py
基板エディタで設計ルールエディタを開き、トラック幅やクリアランスなどのルールを設定できます。

学習済みモデルの削除

下記のプログラムで削除できます。

delete.py
from openai import OpenAI
from dotenv import load_dotenv
import os

load_dotenv()
client = OpenAI(api_key=os.environ['OPENAI_API_KEY'])

client.models.delete("ft:gpt-3.5-turbo:xxxxxxxxxxxxxxxe") # ここに model ID をコピー

OpenAI の WEb ページから削除することもできます。

Discussion