BFT名古屋 TECH BLOG

日々の業務で得た知識を所属するエンジニアたちがアウトプットしていきます。

【ChatGPT】学習モデルの微調整、fine-tuningのやり方

こんにちは! BFT名古屋支店の佐野です。

今回はchatGPT、ないしopenAIの学習に関連するお話、fine-tuning(ファインチューニング)について扱っていきます。
特に社内向けAIを構築する場合に、その社内の環境独自のデータをAIに学習させたい場合にどうすればよいか、その手法と手順も解説します。

Fine-tuning


fine-tuning(ファインチューニング)ってそもそも何?

fine-tuningとは既に大規模学習モデルを学習したAIに対し、新たな学習モデルを追加して学習させることです。
AIに対して新たな、特定の知識を覚えてもらうこととも言えます。

ChatGPTに触れたことのある方ならご存じの通り、サービスとしてのChatGPTは一定の時期(2022年1月まで等)までの情報しか有しておらず、それ以降の情報を回答として出すことはできません。
またChatGPTが出す回答の元はインターネットから来る情報であるため、企業やその他組織の内々的な(インターネットに公開されていない)情報は有しておらず、そうした内容に関わる質問を投じても望む答えを返してはくれません。

昨今においては、企業や組織内の問い合わせやFAQの回答をChatGPTのようなAIに任せたい動きが出てきており、過去の問い合わせ回答記録や部署ごとの在任者情報などを学習させたいという話を聞くようになっています。 しかしサービスとして公開されているChatGPTに学習させることはできず、たとえできるとしてもそんなことをすれば単純に情報漏洩となってしまうため、ChatGPTのような機能を持つ内部専用のAIを導入し、そのAIに情報を与えることになります。
そのような場合にはAIの導入時に学習モデルとして過去の情報を入れるということも検討できますが、社内の情報やその問い合わせは日々増えていくもので、一度学習すれば終わりというものではありません。
なので既存の学習モデルを学習しているAIに追加学習をさせるために、fine-tuningという手段が必要となるのです。

過去の問い合わせ回答記録や部署ごとの在任者情報など組織を構成する情報以外でfine-tuningで追加学習させるとよいデータは特に以下が挙げられます。

  1. 組織内に蓄積された独自データ
    企業内のデータは組織活動の中で一環として日々作られていくものですし、その中にはインターネットで公開されている情報にも含まれていない独自のものもあるでしょう。
    特に組織内で生じた特有のケースに言及した情報はその最もたるものです。
    蓄積された独自のナレッジを学習させておけば、同様の事象が起きた際にもAIが対応方法を提示してくれます。

  2. 世間に公開されて間もない最新情報
    前述したとおり、サービスとしてのChatGPTの情報は一定の時期までのものしかなく、今後サービスとしてのChatGPTが情報を更新しない限り状況は変わりません。
    またサービスとしてのChatGPTでない、インターネット上に存在する情報を収集した学習モデルであっても本当の意味で最新の情報まで網羅しているとは限りません。
    そのため常に最新の情報をカバーし、リアルタイムに近い現状に関する回答が必要となる環境では追加学習を行なうべきデータとなります。

fine-tuningに必要なもの

fine-tuningを行なうにあたり、必要なもの・使うものを以下に挙げます。

  1. OpenAI CLI(+Python 3)
  2. (AIに学習させる)学習データ

学習データはいわずもがなですが、学習データを入れたり学習させるためにもOpenAI CLIが必要です。
fine-tuningを実施しようという時にはふたつとも用意している・できるだろうものなので、実施ハードル自体は高くはありません。

学習データはJSONL(JSON LINES)形式でなければなりませんが、中身がJSONLに変換できるような形になってさえいるものであればPythonコードを使うなどの方法でcsv形式から変換することもできます。

最もハードルとなりえるのは、学習データの内容の量です。
fine-tuningに使用する学習データ中では、以下のようなAIに与える役割と、その役割を持ったAIへ向ける質問とそれに対応した回答の組み合わせを1セットとします。

【例(gpt-3.5-turbo向け)】
{"messages": [{"role": "system", "content": "あなたは社内向けアプリのコンシェルジュです。"}, {"role": "user", "content": "画面が真っ白になったり、表示が消えたりしてしまったりした時の対処を教えてください。"}, {"role": "assistant", "content": "1. 通信環境をお確かめの上、お客様用フリーwi-fiへの接続をお試しください。\n2. アプリを一旦終了し、再度立ち上げをお試しください。ホーム画面に戻るだけでは終了とはなりません。\n3. スマートフォン自体の再起動もお試しください。\n4. 通信環境の良いところでアプリの再ダウンロードをお試しください。\n5. アプリ内コンテンツデータを削除後、アプリの再起動をお試しください。"}]}

このセットをいくつも記載するのですが、以下のOpenAI公式のドキュメントによれば、少なくとも数百個のセットを記載するのがよいとされています。

openai.itshinan.jp

最低限必要なセット数は10個くらいと言われることもありますが、セットの数が多ければ多いほど回答の質は上がっていきます。
このセットは学習を実施するユーザーの手で作成しなければならず、来るだろう質問とそれに対する回答を正確に想定して幾多のセットを作り上げるのは困難です。
また、たとえ実際に行われた質問と回答の双方を蓄積しているなどベースとなる情報が十分な量あったとしても、それを人の手で入力するのは大変です。
学習データの作成にあたっても、効率的に用意するには何かしらの一工夫が必要でしょう。

fine-tuningの手順

ここからは実際にfine-tuningを行なう際の手順を解説していきます。
必要なものの取り揃えから、学習モデルの適用までを行なっていきましょう。

OpenAI CLI(とPython 3)をインストールする

OpenAI CLIをインストールしていない場合は、まずOpenAI CLIおよびその前提となるPython 3をインストールします。
Pythonは以下の公式サイトからインストーラーをダウンロードできます。
Windowsにてfine-tuningを行なうなら、インストーラーを使ってインストールを実施しましょう。

www.python.org

Linux、例えばCentOSを使うなら、以下のコマンドを実行することでPython3をインストールできます。

# パッケージリスト更新
sudo yum update
# Python 3のインストール
sudo yum install python3

Pythonがインストールされている、またはインストールしたならば、OpenAI CLIのインストールに入ります。
以下のコマンドを実行することでOpenAI CLIがインストールされます。

pip install openai

OpenAI CLIをインストールしたら、OpenAIのAPIキーの設定を行ないます。
OpenAI CLIを利用するには、OpenAIのAPIキーが必要であり、APIキーは以下OpenAIの公式サイトでアカウントを作成すれば取得することができます。

openai.com

APIキーを取得したら、以下のコマンドを使用してAPIキーを環境変数として設定します。 (YOUR_OPENAI_API_KEY部分を、取得したAPIキーに置き換えてください)

【Windows PowerShellの場合】
[System.Environment]::SetEnvironmentVariable('OPENAI_API_KEY', 'YOUR_OPENAI_API_KEY', [System.EnvironmentVariableTarget]::User)

設定後、念のために環境変数が正しく設定されたか以下のコマンドで確認しましょう。

【Windows PowerShellの場合】
echo $Env:OPENAI_API_KEY

最後にOpenAIのバージョン確認を行ない、OpenAI CLIが正しく動作しているか確認しましょう。
バージョン情報が表示されれば、OpenAI CLIのインストールと設定は成功しています。

openai --version

学習データを用意する(csvの作成とjsonlへの変換)

次にfine-tuningに使用する学習データを作成します。
前述の通り、学習データの形式はjsonlでなければならないので、学習データを他の形式で作成した場合はjsonl形式に変換する必要があります。
作成する場合はcsv形式とするのが簡単なため、csv形式にて作成ののち、jsonl形式に変換する手順を解説します。

① 質問(prompt)と回答(completion)を記載したcsvを作成する

スプレッドシートExcelなどの表計算ツールを使用し、A列に“prompt“、B列に“completion”と記載し、それぞれに質問と回答の内容を以下のように入力します。

学習データの内容をcsvで作成する

実務においては数百件くらいのケースを記載しますが、以上の例では学習までの流れと手順を確認するためのサンプルとして質問と回答のセットを10件作成しました。
この状態で任意のファイル名をつけてcsv形式で保存します。

CSVファイルをJSONLに変換する

学習データをcsv形式で作成したら、JSONL形式への変換しましょう。
JSONLへの変換において、ベースモデルにgpt-3.5-turboを使う場合はpythonコードを使ってcsvファイルからjsonファイルへの変換を行なう必要があります。
(pandasが必要なため、インストールしていない場合は pip install pandas でインストールしましょう)
(YOUR_CSV_FILE.csv部分を作成したcsv形式の学習データがあるファイルパスに、OUTPUT.jsonl部分をjsonl形式の学習データのファイル名と出力する先のファイルパスに置き換えてください)

import pandas as pd
import json

# CSVファイルを読み込む
input_data = pd.read_csv('YOUR_CSV_FILE.csv', encoding = 'utf-8')

# JSONLファイル保存先を指定する
jsonl_file_path = 'OUTPUT.jsonl'

# JSONL形式に変換して保存
with open(jsonl_file_path, 'w', encoding='utf-8') as jsonl_file:
    for index, row in input_data.iterrows():
        data = {"messages": [
                 {"role": "system", "content": "あなたは社内向けアプリのコンシェルジュです。"},
                 {"role": "user", "content": row["prompt"]},
                 {"role": "assistant", "content": row["completion"]}
               ]}
        jsonl_line = json.dumps(data, ensure_ascii = False) + '\n'
        jsonl_file.write(jsonl_line)

print(f'JSONLへの変換が完了しました。配置先: {jsonl_file_path}')

無事に処理が終了すると、「fine-tuningに必要なもの」項で示したような内容にフォーマットされたJSONLファイルが出力されます。

JSONファイルが出力できたら、学習データの用意は完了です。

別解:ChatGPT 4を使ってcsvをfine-tuning用jsonlへの変換)

ChatGPT4を利用できるのであれば、作成したcsvファイルのjsonl変換をやってもらうこともできます。
「① 質問(prompt)と回答(completion)を記載したcsvを作成する」で作成したcsvファイルをやり取りの中でChatGPT 4に渡してjsonlの変換を命令すれば、命令したとおりにいい感じに作成してくれます。
参考として、以下にChatGPT 4と実際に行ったやりとりの内容を掲載します。

ChatGPT 4 と行なったやり取り(クリックすると展開します)


あなた:
CSVからFinetunig用のJSONファイルを作成できますか?
ChatGPT:
はい、CSVファイルからFine-tuning用のJSONファイルを作成することが可能です。
Fine-tuningに用いるJSONファイルは、特定のフォーマットに従ってデータを整理する必要があります。
これには、例えばテキストペア(質問と回答、文章とその要約など)を含めることが一般的です。
このプロセスを進めるためには、まずCSVファイルが必要です。
CSVファイルをアップロードいただければ、それをJSONフォーマットに変換する手順を進めることができます。
CSVファイルにどのようなデータが含まれており、どのような形式でJSONに変換したいかを教えてください。
あなた:
次のCSVをchatGPT3.5 turbo用でお願いします。(YOUR_CSV_FILE.csvをアップロード)
ChatGPT:
アップロードされたCSVファイル「YOUR_CSV_FILE.csv」を使用して、ChatGPT-3.5 TurboのFine-tuning用のJSONファイルを作成します。
まずはファイルの内容を確認し、その後適切なJSON形式に変換する手順を進めます。ファイルを読み込んで内容を確認してみましょう。
CSVファイルを読み込もうとした際に、UnicodeDecodeErrorが発生しました。
これは、ファイルがUTF-8以外のエンコーディングで保存されている可能性があることを示しています。
日本語のデータを含むCSVファイルの場合、しばしばShift_JISやCP932といったエンコーディングが使用されます。
これらのエンコーディングを試してファイルを読み込むことで、問題を解決できる可能性があります。
それでは、これらのエンコーディングでファイルを読み込んでみましょう。
CSVファイルは「Shift_JISエンコーディングを使用して正常に読み込まれました。
このファイルには、質問(「prompt」列)とそれに対する回答(「completion」列)の形式でデータが含まれています。
これはChatGPT-3.5 TurboのFine-tuningに適した形式です。 次に、このデータをJSON形式に変換します。
各行は、質問と回答のペアを持つ辞書に変換され、これらの辞書はJSONファイルに保存されます。変換を行い、JSONファイルを作成しましょう。
Fine-tuning用のJSONファイルが作成されました。以下のリンクからダウンロードできます:
(YOUR_CSV_FILE_finetuning.jsonのダウンロード)
このJSONファイルは、アップロードされたCSVファイルのデータを元に作成されており、各行が質問と回答のペアを表しています。
これをChatGPT-3.5 TurboのFine-tuningに使用することができます。
あなた:
以下の様に作れますか?
{"messages": [{"role": "system", "content": "あなたは社内向けアプリのコンシェルジュです。"},
{"role": "user", "content": "画面が真っ白になったり、表示が消えたりしてしまったりした時の対処を教えてください。"},
{"role": "assistant", "content": "1. 通信環境をお確かめの上、お客様用フリーwi-fiへの接続をお試しください。\n2. アプリを一旦終了し、再度立ち上げをお試しください。ホーム画面に戻るだけでは終了とはなりません。\n3. スマートフォン自体の再起動もお試しください。\n4. 通信環境の良いところでアプリの再ダウンロードをお試しください。\n5. アプリ内コンテンツデータを削除後、アプリの再起動をお試しください。"}]}
ChatGPT:
はい、要望に沿った形式でJSONファイルを作成することができます。提供されたCSVデータを使って、指定された形式のJSONファイルを作成します。
このフォーマットでは、各行が「system」、「user」、または「assistant」のロールを持つメッセージオブジェクトに変換されます。
CSVファイルの各行は「user」(質問)と「assistant」(回答)のペアで構成されますが、「system」のメッセージはファイルの始めに一度だけ追加されます。
それでは、指定されたフォーマットに従ってJSONファイルを作成しましょう。
指定されたフォーマットに従ったJSONファイルが作成されました。以下のリンクからダウンロードできます: (YOUR_CSV_FILE_formatted_for_finetuning.jsonのダウンロード)
このJSONファイルは、「system」、そしてCSVデータから取り出した「user」と「assistant」のメッセージを含んでいます。
これをChatGPT-3.5 TurboのFine-tuningに使用することができます。
あなた:
JSONL形式にしてください。
ChatGPT:
了解しました。JSONL形式では、各行が独立したJSONオブジェクトとして記述されます。
指定されたフォーマットのJSONデータをJSONL形式に変換します。
これには、先ほど作成した「messages」オブジェクトを各行に分割して保存することが含まれます。
それでは、JSONLファイルを作成しましょう。
JSONL形式のファイルが作成されました。以下のリンクからダウンロードできます:
(YOUR_CSV_FILE_for_finetuning.jsonlのダウンロード)
このファイルは、各行が独立したJSONオブジェクトとして記述されており、ChatGPT-3.5 TurboのFine-tuningに適しています。
あなた:
{"messages": [{"role": "system", "content": "あなたは社内向けアプリのコンシェルジュです。"},
{"role": "user", "content": "画面が真っ白になったり、表示が消えたりしてしまったりした時の対処を教えてください。"},
{"role": "assistant", "content": "1. 通信環境をお確かめの上、お客様用フリーwi-fiへの接続をお試しください。\n2. アプリを一旦終了し、再度立ち上げをお試しください。ホーム画面に戻るだけでは終了とはなりません。\n3. スマートフォン自体の再起動もお試しください。\n4. 通信環境の良いところでアプリの再ダウンロードをお試しください。\n5. アプリ内コンテンツデータを削除後、アプリの再起動をお試しください。"}]}
の形式でかつJSONL形式でお願いします。
ChatGPT:
了解しました。提供された例に従い、それぞれの「messages」オブジェクトを独立したJSONオブジェクトとしてJSONL形式でファイルに保存します。
JSONオブジェクトは、一つの「system」メッセージと、それに続く「user」および「assistant」のメッセージペアを含むことになります。
それでは、指定された形式のJSONLファイルを作成しましょう。
指定された形式のJSONLファイルが作成されました。各行には「system」メッセージとそれに続く「user」および「assistant」のメッセージペアが含まれています。
以下のリンクからダウンロードできます:
(YOUR_CSV_FILE_formatted_for_finetuning.jsonlのダウンロード)
このファイルはChatGPT-3.5 TurboのFine-tuningに適しています。


fine-tuningを実行する(学習モデルの生成)

学習データを用意したらfine-tuningの実行に入りますが、可能であれば用意したJSONLファイルのフォーマットが正しいか、以下のページで提供されているPythonコードを用いてチェックと行なうとよいでしょう。
これはOpenAIが提供しているスクリプトで、フォーマットチェックだけでなくトークン数の確認やfine-tuning時の発生料金の見積もりも行えます。

cookbook.openai.com

チェック用Pythonコード全文(クリックすると展開します)


import json
import tiktoken # for token counting
import numpy as np
from collections import defaultdict

data_path = "OUTPUT.jsonl"

# Load the dataset
with open(data_path, 'r', encoding='utf-8') as f:
    dataset = [json.loads(line) for line in f]

# Initial dataset stats
print("Num examples:", len(dataset))
print("First example:")
for message in dataset[0]["messages"]:
    print(message)

# Format error checks
format_errors = defaultdict(int)

for ex in dataset:
    if not isinstance(ex, dict):
        format_errors["data_type"] += 1
        continue
        
    messages = ex.get("messages", None)
    if not messages:
        format_errors["missing_messages_list"] += 1
        continue
        
    for message in messages:
        if "role" not in message or "content" not in message:
            format_errors["message_missing_key"] += 1
        
        if any(k not in ("role", "content", "name", "function_call") for k in message):
            format_errors["message_unrecognized_key"] += 1
        
        if message.get("role", None) not in ("system", "user", "assistant", "function"):
            format_errors["unrecognized_role"] += 1
            
        content = message.get("content", None)
        function_call = message.get("function_call", None)
        
        if (not content and not function_call) or not isinstance(content, str):
            format_errors["missing_content"] += 1
    
    if not any(message.get("role", None) == "assistant" for message in messages):
        format_errors["example_missing_assistant_message"] += 1

if format_errors:
    print("Found errors:")
    for k, v in format_errors.items():
        print(f"{k}: {v}")
else:
    print("No errors found")

encoding = tiktoken.get_encoding("cl100k_base")

# not exact!
# simplified from https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
def num_tokens_from_messages(messages, tokens_per_message=3, tokens_per_name=1):
    num_tokens = 0
    for message in messages:
        num_tokens += tokens_per_message
        for key, value in message.items():
            num_tokens += len(encoding.encode(value))
            if key == "name":
                num_tokens += tokens_per_name
    num_tokens += 3
    return num_tokens

def num_assistant_tokens_from_messages(messages):
    num_tokens = 0
    for message in messages:
        if message["role"] == "assistant":
            num_tokens += len(encoding.encode(message["content"]))
    return num_tokens

def print_distribution(values, name):
    print(f"\n#### Distribution of {name}:")
    print(f"min / max: {min(values)}, {max(values)}")
    print(f"mean / median: {np.mean(values)}, {np.median(values)}")
    print(f"p5 / p95: {np.quantile(values, 0.1)}, {np.quantile(values, 0.9)}")

# Warnings and tokens counts
n_missing_system = 0
n_missing_user = 0
n_messages = []
convo_lens = []
assistant_message_lens = []

for ex in dataset:
    messages = ex["messages"]
    if not any(message["role"] == "system" for message in messages):
        n_missing_system += 1
    if not any(message["role"] == "user" for message in messages):
        n_missing_user += 1
    n_messages.append(len(messages))
    convo_lens.append(num_tokens_from_messages(messages))
    assistant_message_lens.append(num_assistant_tokens_from_messages(messages))
    
print("Num examples missing system message:", n_missing_system)
print("Num examples missing user message:", n_missing_user)
print_distribution(n_messages, "num_messages_per_example")
print_distribution(convo_lens, "num_total_tokens_per_example")
print_distribution(assistant_message_lens, "num_assistant_tokens_per_example")
n_too_long = sum(l > 4096 for l in convo_lens)
print(f"\n{n_too_long} examples may be over the 4096 token limit, they will be truncated during fine-tuning")

# Pricing and default n_epochs estimate
MAX_TOKENS_PER_EXAMPLE = 4096

TARGET_EPOCHS = 3
MIN_TARGET_EXAMPLES = 100
MAX_TARGET_EXAMPLES = 25000
MIN_DEFAULT_EPOCHS = 1
MAX_DEFAULT_EPOCHS = 25

n_epochs = TARGET_EPOCHS
n_train_examples = len(dataset)
if n_train_examples * TARGET_EPOCHS < MIN_TARGET_EXAMPLES:
    n_epochs = min(MAX_DEFAULT_EPOCHS, MIN_TARGET_EXAMPLES // n_train_examples)
elif n_train_examples * TARGET_EPOCHS > MAX_TARGET_EXAMPLES:
    n_epochs = max(MIN_DEFAULT_EPOCHS, MAX_TARGET_EXAMPLES // n_train_examples)

n_billing_tokens_in_dataset = sum(min(MAX_TOKENS_PER_EXAMPLE, length) for length in convo_lens)
print(f"Dataset has ~{n_billing_tokens_in_dataset} tokens that will be charged for during training")
print(f"By default, you'll train for {n_epochs} epochs on this dataset")
print(f"By default, you'll be charged for ~{n_epochs * n_billing_tokens_in_dataset} tokens")

また学習データはローカルからfine-tuningできるわけではなく、一旦OpenAIにファイルAPIを使ってアップロードする必要があります。
ファイルをアップロードするには、 以下のPythonコードを実行します。
(TRAIN_FILE_ID_OR_PATH部分を作成したjsonl形式の学習データがあるファイルパスに置き換えてください)

from openai import OpenAI
client = OpenAI()

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

正しくファイルのアップロードが行なえたかどうかはOpenAIのファイル確認用のAPIをキックすれば確認できます。
またファイルIDはfine-tuning実行時にも必要なので、以下の「#ファイルのリストを表示する」でアップロードしたファイルを確認し、ファイルIDを確認・取得しておきましょう。
その他、ファイル削除やジョブ確認などの機能に対応するOpenAIのAPIをキックすることで、それらを行なうことができます。
OpenAIのAPIをキックすればできることのうち、fine-tuningに関わるだろう機能とそれを実行するためのコマンドを以下に並べます。
powershellで実施する場合の例です。UPLOAD_FILE_ID部分をファイルIDに、JOB_IDをジョブIDに、MODEL_IDをモデルIDに置き換えてください)

# APIキーを変数に登録(最初に行なうこと)
$apiKey = "API_KEY"

# ファイルのリストを表示する
Invoke-RestMethod -Uri "https://api.openai.com/v1/files" -Method Get -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# アップロードしたファイルに関する情報を取得する
Invoke-RestMethod -Uri "https://api.openai.com/v1/files/UPLOAD_FILE_ID" -Method Get -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# アップロードしたファイルを削除する
Invoke-RestMethod -Uri "https://api.openai.com/v1/files/UPLOAD_FILE_ID" -Method Delete -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# 実行中のfine-tuningジョブをリストする(最大表示数を10に指定)
Invoke-RestMethod -Uri "https://api.openai.com/v1/fine_tuning/jobs?limit=10" -Method Get -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# 特定のfine-tuningジョブの情報を取得する
Invoke-RestMethod -Uri "https://api.openai.com/v1/fine_tuning/jobs/JOB_ID" -Method Get -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# 特定のfine-tuningジョブで発生したイベントの情報を取得する(最大表示数を10に指定)
Invoke-RestMethod -Uri "https://api.openai.com/v1/fine_tuning/jobs/JOB_ID/events?limit=10" -Method Get -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# 特定のfine-tuningジョブの実行をキャンセルする
Invoke-RestMethod -Uri "https://api.openai.com/v1/fine_tuning/jobs/JOB_ID/cancel" -Method Post -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# 使用できる学習モデルの一覧を表示
Invoke-RestMethod -Uri "https://api.openai.com/v1/models" -Method Get -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# 特定の学習モデルの情報を取得する
Invoke-RestMethod -Uri "https://api.openai.com/v1/models/MODEL_ID" -Method Get -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

# fine-tuningした特定の学習モデルの情報を削除する
Invoke-RestMethod -Uri "https://api.openai.com/v1/models/MODEL_ID" -Method Delete -Headers @{ "Authorization" = "Bearer $apiKey" } | ft -Wrap

その他、利用できるAPIとコマンドは以下の公式ドキュメントから参照できます。

https://platform.openai.com/docs/api-reference

学習データをアップロードし、ファイルIDの確認を行なったら、fine-tuningを開始しましょう。
以下のPythonコードを実行すると、作成した学習データを使ったfine-tuningジョブが開始されます。
(UPLOAD_FILE_ID部分をファイルIDに、置き換えてください)
(ベースにするモデルはgpt-3.5-turbo、babbage-002、davinci-002の中から選べます。今回はgpt-3.5-turboの例です)

from openai import OpenAI
client = OpenAI()

client.fine_tuning.jobs.create(
  training_file="UPLOAD_FILE_ID", 
  model="gpt-3.5-turbo"
)

fine-tuningジョブは開始してから完了するまで時間がかかる場合が多く、おおむね数分から数時間の間で変動します。
とはいえ何もせずただ待っている必要はなく、ジョブが完了するとAPIキー作成に使ったアカウントのメールアドレスにお知らせのメールが届きます。
既存のジョブの一覧表示やジョブステータスの確認、実行中のジョブのキャンセルは前述したAPIを呼び出して行ってください。

生成した学習モデルを使ってみる

fine-tuningジョブが完了したら、動作確認として実際に対話を行なってみましょう。
下記のPythonコードを実行して対話してみます。
(以下の例はgpt-3.5-turboをベースモデルとした場合です。MODEL_IDをモデルIDに置き換えてください)

from openai import OpenAI
client = OpenAI()

response = client.chat.completions.create(
  model="MODEL_ID",
  messages=[
    {"role": "system", "content": "あなたは社内向けアプリのコンシェルジュです。"},
    {"role": "user", "content": "画面が真っ白になったり、表示が消えたりしてしまったりした時の対処を教えてください。"}
  ]
)
print(response.choices[0].message)

実行してみたところ、質問に対して正確であるとは言えないものの学習させた内容を含んだ回答がAIから返されました。
同じ内容でサービスとしてのChatGPT 3.5に命令を投げたところ全く異なる回答を返したので、その違いから上記のAIにはfine-tuningがしっかりと行なわれていることが分かりました。

最後に

これでfine-tuningのやり方とその結果を一通り解説できました。
10ケースだけのfine-tuningであり、学習もそこまで進んでいなかったのでこの結果ですが、実務で想定する数百、数千のケースで行なえばより良い結果が出てくるでしょう。
fine-tuning自体はこの通り意外と簡単に行えるため、おためしだけでも一度やってみるとよさげです。

それでは今回はここまで。
次の記事でお会いしましょう!