OpenAI fine-tunes memo
Reference
Doc
Fine-tuningの特徴
- プロンプトデザインよりも高品質な結果
- プロンプトに収まらないより多くの例をトレーニングできる
- プロンプトが短いため、トークンを節約できる
- より低いレイテンシのリクエストになる
プロンプトに収まらないより多くの例をトレーニングすることで、多くのタスクでより良い結果を得ることができるため、few-shot learningを改善できる
手順
- トレーニングデータを準備してアップロードする
- 新しいfine-tunedモデルをトレーニングする
- Fine-tunedモデルを使用する
使えるModel
早い+安い => 遅い+精度良い
- ada
- babbage
- curie
- davinci
a -> b -> c -> dの順
準備するデータ
jsonl形式で用意する
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
{"prompt": "<prompt text>", "completion": "<ideal generated text>"}
...
特徴
- 1対1の例で入力する (詳細な指示は不要)
- 数百の例が必要. データは多いほどいい
データ準備の仕組み
次のコマンドでLOCAL_FILEにjsonlなどを引数に与えるとvalidationしてくれたりする。
openai tools fine_tunes.prepare_data -f <LOCAL_FILE>
モデル作成
openai api fine_tunes.create -t <TRAIN_FILE_ID_OR_PATH> -m <BASE_MODEL>
jobにこれでタスクが入る.
数分以上終了までにかかるので、定期的に確認
# ログを見る
openai api fine_tunes.follow -i <YOUR_FINE_TUNE_JOB_ID>
# JobListをみる
openai api fine_tunes.list
# Jobを指定して情報見る
openai api fine_tunes.get -i <YOUR_FINE_TUNE_JOB_ID>
# Job Cancel
openai api fine_tunes.cancel -i <YOUR_FINE_TUNE_JOB_ID>
モデルを利用
completions apiで利用できる (chat completion apiでは使えない)
curl https://api.openai.com/v1/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": YOUR_PROMPT, "model": FINE_TUNED_MODEL}'
データについて
format
- prompt終了部分にセパレータを用意する (例:
\n\n###\n\n
など他に現れないものにする) - completion開始部分は空白にする
- completion終了部分は固定のstop sequenceを用意してモデルに終了を伝える (例:
\n
,###
など completionに現れないものにする) - 利用する際には、promptをtraining datasetと同じ方法でセパレータを使って、stop sequenceも指定する
例:
{"prompt":"日本の首都は?","completion":"東京"}
↓
{"prompt":"日本の首都は?\n\n###\n\n","completion":" 東京###"}
ベストプラクティス
- 少なくとも数百の高品質な例が必要
- 理想的には人間の専門家によって審査するのが良い
- パフォーマンスを向上させる最良で最も信頼性の高い方法は、通常、例数を増やすこと
- 例が倍になると、線形にパフォーマンスが向上する
分類問題
- 始めやすくて簡単なのでおすすめ
- 分類問題はadaがおすすめ、安くて早い、一般的にはfine-tunedした他のモデルに対してほんの少しだけ悪くなるかんじ
既存のデータセットでの調整
- 不正確なもの、攻撃的なものがないかを手動確認する
- データが大きいときはランダムサンプルで確認
Classification (分類問題)
promptの各入力を決められたいづれかに分類する問題.
- promptの末尾にセパレータを使用(例: \n#####n##n )。このセパレータは、最終的にモデルにリクエストするときにも追加する
- 単一のトークンにマッピングされるクラスを選択。推論時には、分類に必要なのは最初のトークンだけなので、max_tokens=1を指定する
- promptとcompletionが、セパレータを含めて2048 tokenを超えないようにする
- 1クラスあたり最低〜100例程度を目指す
- クラスの対数確率を得るには、モデルを使用する際に 5 classならば、
logprobs=5
を指定する - ファインチューニングに使用するデータセットは、モデルが使用されるものと構造やタスクの種類が非常に類似するようにする
CaseStudy: モデルが事実と異なる記述をしてないか?を確認
例: websiteの広告の文章が製品/企業について正しく言及しているか?を確認。言い換えると製品に言及してないことを確認したい
学習データ:
{ "prompt": "Company: BHFF保険\nProduct: オールラウンド保険\nAd: 保険に関するあらゆるニーズにワンストップでお応えします!\nSupported:", "completion":" yes"}.
{ "prompt": "Company: ロフトコンバージョン専門店\nProduct: -\nAd: 数週間でまっすぐな歯に!\nSupported:", "completion":" no"}.
- 区切り文字: "Supported:"
- model: ada (安いし早いから)
リクエスト:
curl https://api.openai.com/v1/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"prompt": "Company: リライアブル・アカウンタンツ・リミテッド\nProduct: 個人向け税務支援\nAd:街で最高のアドバイスを!\nSupported:",
"max_tokens": 1,
"model": "YOUR_FINE_TUNED_MODEL_NAME"
}'
これでYes/Noが返される
Case study: センチメント分析
ツイートがPositive/Negativeかどうかを調べる。
Dataset
{"prompt":"新しいiPhoneに大喜び! ->", "completion":" positive"}
{"prompt":"@lakers 3夜連続の失望 https://t.co/38EFe43 ->", "completion":" negative"}
logprobs=2
を設定をcompletion requestに設定すると、positive/negativeの相対的な確率が確認できる.
Completion Request:
curl https://api.openai.com/v1/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-d '{
"prompt": "https://t.co/f93xEd2 最新のブログ記事を紹介できて最高です! ->",
"max_tokens": 1,
"model": "YOUR_FINE_TUNED_MODEL_NAME"
}'
Completion Response:
{
"id": "cmpl-COMPLETION_ID",
"object": "text_completion",
"created": 1589498378,
"model": "YOUR_FINE_TUNED_MODEL_NAME",
"choices": [
{
"logprobs": {
"text_offset": [
19
],
"token_logprobs": [
-0.03597255
],
"tokens": [
" positive"
],
"top_logprobs": [
{
" negative": -4.9785037,
" positive": -0.03597255
}
]
},
"text": " positive",
"index": 0,
"finish_reason": "length"
}
]
}
Case Study: Email選別作業に利用するカテゴリ分類
受信したメールをあらかじめ定義された多数のカテゴリのいずれかに分類するタスク
多数のカテゴリーに分類する場合、カテゴリーを数字に変換するがおすすめ (最大500カテゴリーまで有効)
トークン化により、数字の前にスペースを入れると、若干パフォーマンスが向上することが確認されている。学習データを次のように構成するとよい
{"prompt":"Subject: <email_subject>\nFrom:<customer_name>\nDate:<date>\nContent:<email_body>\n\n###\n\n", "completion":" <numerical_category>"}
例:
{"prompt":"Subject: Update my address\nFrom:Joe Doe\nTo:support@ourcompany.com\nDate:2021-06-03\nContent:Hi,\nI would like to update my billing address to match my delivery address.\n\nPlease let me know once done.\n\nThanks,\nJoe\n\n###\n\n", "completion":" 4"}
Conditional generation 条件付き生成
条件付き生成 = 何らかの入力があった場合にコンテンツを生成する必要がある問題
言い換え、要約、固有表現抽出、仕様が与えられた商品説明文作成、チャットボットなど。
- promptの最後にセパレータを使用する。このセパレータは、最終的にモデルにリクエストするときにも追加すること
- completionの最後には終了トークンを使用します。例
END
- 推論時に終了トークンをストップシーケンスとして追加すること(例:stop=[" END"] )
- 最低でも500例以上を目指してください
- prompt + completion がセパレータを含めて2048トークンを超えないようにする
- サンプルは高品質で、同じ希望するフォーマットに従っていることを確認
- ファインチューニングに使用するデータセットは、モデルが使用されるものと構造やタスクの種類が非常に似ていることを確認する。
- 低学習率で1~2回のエポックしか行わない方が、このような使用例では効果的
Case study: Wikipediaの記事をもとに魅力的な広告を書く
生成的なユースケースなので、提供するサンプルは最高品質を保証したい。サンプルは500個くらいから始めるとよい。
Sample dataset:
{"prompt":"<Product Name>\n<Wikipedia description>\n###\n##\n", "completion":" <engaging ad> END"}.
例:
{"prompt": "Samsung Galaxy Feel\nSamsung Galaxy Feelは、サムスン電子が日本市場向けに専用に開発したAndroidスマートフォンです。2017年6月に発売され、NTTドコモから販売された。Android 7.0(Nougat)を搭載し、4.7インチディスプレイ、3000mAhのバッテリーを搭載しています。\nSoftware\nSamsung Galaxy FeelはAndroid 7.0(Nougat)で動作しますが、後にAndroid 8.0(Oreo)にアップデートすることが可能です。\nHardware\nSamsung Galaxy Feelは、4.7インチのSuper AMOLED HDディスプレイ、16MPの背面カメラと5MPの前面カメラを搭載しています。3000mAhのバッテリー、1.6GHzのオクタコアARM Cortex-A53 CPU、ARM Mali-T830 MP1 700MHzのGPUを搭載しています。32GBの内部ストレージを搭載し、microSDで256GBまで拡張可能です。ソフトウェアとハードウェアの仕様だけでなく、サムスンは携帯電話を自分好みにカスタマイズする日本人に対応するため、携帯電話のシェルにユニークな穴を導入しました。また、Galaxy Feelのバッテリーは、市場がより長時間のバッテリー駆動を好むことから、主要なセールスポイントとしてアピールされました。また、別売りのアンテナを使用することで、ワンセグデジタル放送にも対応しています。\n\n###\n\n", "completion": "すべてをこなすスマートフォンをお探しですか?サムスン電子のGalaxy Feelがおすすめです!スリムで洗練されたデザインの最新スマートフォンは、高品質の画像とビデオ機能、そして受賞歴のあるバッテリー寿命を備えています。END" }.
Wikipediaの記事には複数の段落や見出しがあるため、複数行の区切り文字を使用した。
補完がいつ終わるかをモデルが認識できるように、単純な終了トークンも使用。
Case Study: エントリ抽出
言語変換タスクに似ているタスク。
パフォーマンスを向上させるには、異なる抽出エンティティをアルファベット順に並べるか、元のテキストに表示されるのと同じ順序で並べるのが最善。そうすると、生成する必要があるすべてのエンティティをモデルが順番に追跡できるようになる。
Sample Dataset:
{"prompt":"<any text, for example news article>\n\n###\n\n", "completion":" <list of entities, separated by a newline> END"}
{"prompt":"コロナウイルスの感染者が増加し、「いわゆるインド型のネパール変異」が懸念される中、ポルトガルは火曜日から英国のグリーントラベルリストから除外されることになりました。これは、旅行者は訪問を控え、帰国者は10日間隔離しなければならないことを意味するamber listに加わることになる。\n\n###\n\n", "completion":" ポルトガル\n英国\nネパール変異\nインド型 END"}
テキストには複数行が含まれる可能性があるため、複数行の区切り文字が最適。
理想的には、入力プロンプトの種類(ニュース記事、Wikipediaページ、ツイート、法律文書)が多様で、エンティティを抽出する際に遭遇しそうなテキストを反映していることが望ましい。
Case Study: カスタマーサポートchatbot
chatbotは通常、会話に関する関連するコンテキスト(注文の詳細)、これまでの会話の要約、最新のメッセージを含んでいる。
このユースケースでは、同じ過去の会話がデータセットに複数の行を生成することができ、その都度、若干異なるコンテキストで、エージェント生成の完了として生成される。
様々なタイプのリクエストや顧客の問題を扱う可能性があるため、数千の例が必要になる。パフォーマンスが高品質であることを保証するために、エージェントメッセージの品質を保証するために、会話サンプルを吟味することをお勧めする。要約は、別のテキスト変換ファインチューニングモデルで生成することが可能。
Dataset
{"prompt":"Summary: <summary of the interaction so far>\n\nSpecific information:<for example order details in natural language>\n\n###\n\nCustomer: <message1>\nAgent: <response1>\nCustomer: <message2>\nAgent:", "completion":" <response2>\n"}
{"prompt":"Summary: <summary of the interaction so far>\n\nSpecific information:<for example order details in natural language>\n\n###\n\nCustomer: <message1>\nAgent: <response1>\nCustomer: <message2>\nAgent: <response2>\nCustomer: <message3>\nAgent:", "completion":" <response3>\n"}
意図的に異なるタイプの入力情報を分けているが、顧客エージェントのダイアログは、promptとcompletionの間で同じフォーマットで維持されています。補完はすべてエージェントのみとし、推論を行う際の停止シーケンスとして、\n
を使用することができます。
Case study: 技術的な特性リストに基づく製品説明
このタスクでは入力データを自然言語に変換することが重要であり、それが優れた性能につながる可能性が高い。例えば、以下のような形式
{"prompt": "Item=handbag, Color=army_green, price=$99, size=S->", "completion": "This stylish small green handbag will add a unique touch to your look, without costing you a fortune." }.
上記の例は下記と同じようにはうまくいかない
{"prompt":"Item is a handbag. Colour is army green. Price is midrange. Size is small.->", "completion":" This stylish small green handbag will add a unique touch to your look, without costing you a fortune."}
高いパフォーマンスを得るためには、completionが提供された説明に基づいていることを確認する。
外部コンテンツを参照することが多い場合は、自動化された方法でそのようなコンテンツを追加するとパフォーマンスが向上する。また、画像による説明であれば、画像の説明文を抽出するアルゴリズムが有効。completionは1文の長さしかないので、推論中の停止シーケンスとして .
を使用することができる。
Advanced Usage
Analyzing fine-tuned model
jobのidを指定してイベントを見ることができる
openai api fine_tunes.results -i <YOUR_FINE_TUNE_JOB_ID>
- elapsed_tokens: モデルがこれまでに見たトークンの数(繰り返しを含む)
- elapsed_examples: モデルがこれまでに見た例の数(繰り返しを含む)。ただし、1例はバッチ内の1要素である。例えば、batch_size = 4の場合、各ステップでelapsed_examplesは4ずつ増加します。
- training_loss: training_loss: トレーニングバッチの損失
- training_sequence_accuracy: モデルが予測したトークンが真の完了トークンと完全に一致した、トレーニングバッチ内の完了の割合。例えば、batch_sizeが3の場合、データに[[1, 2], [0, 5], [4, 2]]という完成形があり、モデルが[[1, 1], [0, 5], [4, 2]]と予測した場合、この精度は2/3 = 0.67 となる
- training_token_accuracy: モデルが正しく予測した、トレーニングバッチ内のトークンの割合です。例えば、batch_sizeが3の場合、データに[[1, 2], [0, 5], [4, 2]]という補完があり、モデルが[[1, 1], [0, 5], [4, 2]]と予測した場合、この精度は5/6 = 0.83 となる
Validation
データの一部を検証用にわけることができる。検証用ファイルは訓練用ファイルと全く同じフォーマットで、訓練用データと検証用データは相互に排他的にする
fine tune jobの作成時に検証ファイルを含めると、生成される結果ファイルには、fine tune modelが、トレーニング中に一定間隔で検証データに対してどの程度うまく機能するかについての結果が含まれる。
openai api fine_tunes.create -t <TRAIN_FILE_ID_OR_PATH> \
-v <VALIDATION_FILE_ID_OR_PATH> \
-m <MODEL>
検証用ファイルを提供された場合、トレーニング時間中に検証用データのバッチで定期的にメトリクスを計算します。結果ファイルには、以下の追加メトリクスが表示される
- validation_loss: バリデーション・バッチでの損失。
- validation_sequence_accuracy: 検証バッチにおいて、モデルの予測したトークンが真の完了トークンと完全に一致した完了のパーセンテージ。例えば、batch_sizeが3の場合、データに[[1, 2], [0, 5], [4, 2]]という補完があり、モデルが[[1, 1], [0, 5], [4, 2]]と予測した場合、この精度は2/3 = 0.67 となる。
validation_token_accuracy: 検証バッチに含まれるトークンのうち、モデルによって正しく予測されたトークンの割合を示す。例えば、batch_sizeが3の場合、データに[[1, 2], [0, 5], [4, 2]]という補完があり、モデルが[[1, 1], [0, 5], [4, 2]] と予測した場合、この精度は5/6 = 0.83 となる。
Hyperparameters
私たちは様々なユースケースでうまく機能するデフォルトのハイパーパラメータを選出しました。必要なパラメータは、トレーニングファイルのみです。
とはいえ、fine tuneに使用するハイパーパラメータを調整することで、より質の高い出力を生成するモデルになることがよくあります。特に、以下のように設定するとよいでしょう:
- model: モデル:微調整を行うベースモデルの名前。ada、babbage、curie、davinciのいずれかを選択することが可能。これらのモデルの詳細については、ドキュメントを参照
- n_epochs - デフォルトは4。モデルを学習させるエポック数。エポックとは、トレーニングデータセットの1サイクルを指します。
- batch_size - デフォルトは、トレーニングセットのサンプル数の0.2%で、上限は256です。バッチサイズは、1回のフォワードパスとバックワードパスを訓練するために使用される訓練例の数。一般的に、大きなデータセットでは、バッチサイズが大きい方がうまくいく傾向があることが分かっています。
- learning_rate_multiplier - デフォルトは、最終バッチサイズに応じて0.05、0.1、または0.2です。微調整の学習率は、事前学習に使用された元の学習率にこの乗数を掛けたものです。0.02から0.2の範囲で実験して、最良の結果が得られるかどうかを確認することをお勧めします。経験的に、大きな学習率は大きなバッチサイズでより良い結果を出すことが多いことが分かっています。
- compute_classification_metrics - デフォルトはFalseです。Trueの場合、分類タスクの微調整のために、毎エポック終了時に検証セットで分類固有のメトリクス(精度、F-1スコアなど)を計算する。
これらの追加のハイパーパラメーターを設定するには、OpenAI CLIのコマンドラインフラグを使用して渡します
例:
openai api fine_tunes.create \
-t file-JD89ePi5KMsB3Tayeli5ovfW \
-m ada \
--n_epochs 1
ファインチューニングされたモデルからファインチューニングを続行
すでにタスクのためのfine-tuned modelを保有しており、現在追加で取り入れたいトレーニングデータがある場合、そのモデルから微調整を続けることができます。これにより、ゼロから再トレーニングを行うことなく、すべてのトレーニングデータから学習したモデルを作成することができます。
これを行うには、新しいfine tuned jobを作成する際に、fine tuned model名を渡します(例:-m curie:ft-<org>-<date>
)。その他のトレーニングパラメータは変更する必要はありませんが、新しいトレーニングデータが以前のトレーニングデータよりはるかに小さい場合、learning_rate_multiplierを2〜4倍程度に減らすと便利でしょう。