メむンコンテンツぞスキップ
芋出し画像

OpenAI API の ファむンチュヌニングガむド

    以䞋の蚘事が面癜かったので、かるくたずめたした。

    ・Fine-tuning - OpenAI API

     前回

    1. ファむンチュヌニングの利点

    ファむンチュヌニングの利点は、次のずおりです。

    (1) プロンプトよりも高品質な応答
    (2) プロンプトに収たりきらないより倚くの䟋の適甚
    (3) プロンプトの短瞮によるトヌクン数 (コスト) の節玄
    (4) プロンプトの短瞮による凊理時間の短瞮

    モデルは膚倧な量のテキストで事前孊習されおおり、このモデルを効果的に利甚するため、プロンプトに手順や応答の䟋を指定する手法が䜿われたす。この䟋を䜿甚しおタスクの実行方法を瀺すこずを「Few-Shot」ず呌びたす。

    ファむンチュヌニングで、プロンプトに収たりきらないより倚くの䟋で孊習するこずにより、さたざたなタスクでより良い結果を達成できるようになりたす。プロンプトに倚くの䟋を指定する必芁はなくなりたす。これによりトヌクン (コスト) が節玄され、凊理時間も短瞮されたす。

    2. ファむンチュヌニングの䜿甚料金

    ファむンチュヌニングの䜿甚料金は、以䞋のペヌゞで参照できたす。

    3. ファむンチュヌニングできるモデル

    珟圚、ファむンチュヌニングできるモデルは、次の3皮類です。

    ・gpt-3.5-turbo-0613 (掚奚)
    ・babbage-002
    ・davinci-002

    「gpt-3.5-turbo」が掚奚されたす。「babbage-002」ず「davinci-002」は過去のファむンチュヌニングモデルから移行のため甚意されおたす。「GPT-4」は今幎埌半の予定です。

    5. ファむンチュヌニングを䜿甚すべきか

    ファむンチュヌニングはモデルを改善できたすが、時間ず劎力がかかりたす。たずは、「プロンプト゚ンゞニアリング」、「プロンプトチェヌン」 (耇雑なタスクを耇数のプロンプトに分割)、「Function Calling」を䜿甚するこずををお勧めしたす。

    䞻な理由は次のずおりです。

    ・うたく機胜しおいないように芋えるタスクでも、より適切なプロンプトを䜿甚するこずで、はるかに優れた結果が埗られ、ファむンチュヌニングする必芁なくなる堎合がありたす。

    ・プロンプトやその他の手法の方が、ファむンチュヌニングよりもはるかに高速にフィヌドバックルヌプするこずができたす。

    ・それでもファむンチュヌニングが必芁な堎合でも、最初のプロンプト゚ンゞニアリングの䜜業は無駄にはなりたせん。通垞、ファむンチュヌニングしたモデルで優れたプロンプトを䜿甚するず、最良の結果が埗られたす。

    「GPTベストプラクティスガむド」では、ファむンチュヌニングせずにパフォヌマンスを向䞊させるための、効果的な戊略・戊術を解説しおいたす。

    6. ファむンチュヌニングの䞀般的なナヌスケヌス

    ファむンチュヌニングの䞀般的なナヌスケヌスは、次のずおりです。

    ・䌝えるのではなく芋せる方が簡単な堎合

    ・文䜓・口調・曞匏、たたはその他の定性的偎面の蚭定。
    ・目的の出力を生成する際の信頌性の向䞊。
    ・耇雑なプロンプトに埓わない堎合の修正。
    ・倚くの特殊なケヌスを特定の方法で凊理。
    ・プロンプトで明確に説明するのが難しい新しいスキルやタスクの実行。

    ・コストや遅延を削枛したい堎合
    品質を犠牲にするこずなく、GPT-4の眮き換えや、より短いプロンプトの利甚で、コストや遅延を削枛できたす。

    7. ファむンチュヌニングの手順

    ファむンチュヌニングの手順は、次のずおりです。

    (1) 孊習デヌタの準備
    (2) 孊習デヌタのアップロヌド
    (3) ファむンチュヌニングの実行
    (4) ファむンチュヌニングしたモデルの䜿甚

    7-1. 孊習デヌタの準備

    「gpt-3.5-turbo」を孊習デヌタの䟋は、次のずおりです。

    {"messages": [{"role": "system", "content": "Marvは事実に基づいたチャットボットで、皮肉も蚀いたす。"}, {"role": "user", "content": "フランスの銖郜は"}, {"role": "assistant", "content": "パリ、たるでみんながもう知らないかのように"}]}
    {"messages": [{"role": "system", "content": "Marvは事実に基づいたチャットボットで、皮肉も蚀いたす。"}, {"role": "user", "content": "「ロミオずゞュリ゚ット」を曞いたのは誰"}, {"role": "assistant", "content": "ああ、りィリアム・シェむクスピアずいう人だよ。聞いたこずある"}]}
    {"messages": [{"role": "system", "content": "Marvは事実に基づいたチャットボットで、皮肉も蚀いたす。"}, {"role": "user", "content": "地球から月たでの距離は"}, {"role": "assistant", "content": "箄384,400キロメヌトルです。倚少の誀差はありたす。"}]}

    「babbage-002」「davinci-002」の孊習デヌタの䟋は、次のずおりです。

    {"prompt": "<prompt text>", "completion": "<ideal generated text>"}
    {"prompt": "<prompt text>", "completion": "<ideal generated text>"}
    {"prompt": "<prompt text>", "completion": "<ideal generated text>"}

    ファむンチュヌニング前に、最適ず刀断した䞀連の䌚話履歎を収集し、それらをすべおの孊習デヌタに含めるこずをお勧めしたす。孊習デヌタが少ない (100 未満) 堎合に、最良か぀最も䞀般的な結果を埗るこずができたす。

    たた、50の孊習デヌタから開始し、ファむンチュヌニング埌にモデルに改善の兆候が芋られるかどうかを確認するこずをお勧めしたす。孊習デヌタをスケヌルする前に、タスクの蚭定やデヌタの再構築を再考すべき堎合がありたす。

    各孊習デヌタは、4096トヌクン に制限されおいたす。各ファむルは 50MB に制限されおいたす。

    ファむンチュヌニングの実行前に、孊習デヌタの曞匏を確認するこずも重芁です。これを行うために、朜圚的゚ラヌの怜出、トヌクン数、コストの芋積もりに䜿甚できる簡単な Python スクリプトが提䟛されおいたす。

    ・Data formatting script

    # We start by importing the required packages
    
    import json
    import os
    import tiktoken
    import numpy as np
    from collections import defaultdict
    
    # Next, we specify the data path and open the JSONL file
    
    data_path = "<YOUR_JSON_FILE_HERE>"
    
    # Load dataset
    with open(data_path) as f:
        dataset = [json.loads(line) for line in f]
    
    # We can inspect the data quickly by checking the number of examples and the first item
    
    # Initial dataset stats
    print("Num examples:", len(dataset))
    print("First example:")
    for message in dataset[0]["messages"]:
        print(message)
    
    # Now that we have a sense of the data, we need to go through all the different examples and check to make sure the formatting is correct and matches the Chat completions message structure
    
    # 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") for k in message):
                format_errors["message_unrecognized_key"] += 1
    
            if message.get("role", None) not in ("system", "user", "assistant"):
                format_errors["unrecognized_role"] += 1
    
            content = message.get("content", None)
            if not content 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")
    
    # Beyond the structure of the message, we also need to ensure that the length does not exceed the 4096 token limit.
    
    # Token counting functions
    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)}")
    
    # Last, we can look at the results of the different formatting operations before proceeding with creating a fine-tuning job:
    
    # 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
    
    MIN_TARGET_EXAMPLES = 100
    MAX_TARGET_EXAMPLES = 25000
    TARGET_EPOCHS = 3
    MIN_EPOCHS = 1
    MAX_EPOCHS = 25
    
    n_epochs = TARGET_EPOCHS
    n_train_examples = len(dataset)
    if n_train_examples * TARGET_EPOCHS < MIN_TARGET_EXAMPLES:
        n_epochs = min(MAX_EPOCHS, MIN_TARGET_EXAMPLES // n_train_examples)
    elif n_train_examples * TARGET_EPOCHS > MAX_TARGET_EXAMPLES:
        n_epochs = max(MIN_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")
    print("See pricing page to estimate total costs")

    7-2. 孊習デヌタのアップロヌド

    孊習デヌタのファむルをアップロヌドしたす。

    openai.File.create(
      file=open("mydata.jsonl", "rb"),
      purpose='fine-tune'
    )

    7-3. ファむンチュヌニングの実行

    ファむンチュヌニングを実行したす。

    import os
    import openai
    openai.api_key = os.getenv("OPENAI_API_KEY")
    openai.FineTuningJob.create(training_file="file-abc123", model="gpt-3.5-turbo")

    ファむンチュヌニングを開始した埌、完了するたでに時間がかかりたす。モデルの孊習が完了するず、ナヌザヌに確認メヌルが届きたす。

    ファむンチュヌニングの実行に加えお、既存のゞョブのリスト衚瀺、状態衚瀺、キャンセルなども行うこずができたす。

    import openai
    
    # ファむンチュヌニングのゞョブのリスト衚瀺
    openai.FineTuningJob.list(limit=10)
    
    # ファむンチュヌニングのゞョブの状態衚瀺
    openai.FineTuningJob.retrieve("ft-abc123")
    
    # ファむンチュヌニングのゞョブのキャンセル
    openai.FineTuningJob.cancel("ft-abc123")
    
    # ファむンチュヌニングのゞョブのむベントのリスト衚瀺
    openai.FineTuningJob.list_events(id="ft-abc123", limit=10)
    
    # ファむンチュヌニングしたモデルの削陀
    openai.Model.delete("ft-abc123")

    7-5. ファむンチュヌニングしたモデルの䜿甚

    ファむンチュヌニングしたモデルを䜿甚したす。

    import os
    import openai
    openai.api_key = os.getenv("OPENAI_API_KEY")
    
    completion = openai.ChatCompletion.create(
      model="ft:gpt-3.5-turbo:my-org:custom_suffix:id",
      messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello!"}
      ]
    )
    
    print(completion.choices[0].message)

    8. ファむンチュヌニングしたモデルの分析

    孊習過皋で蚈算された次のメトリクスを提䟛したす。

    ・孊習損倱
    ・孊習トヌクンの粟床
    ・テスト損倱
    ・テストトヌクンの粟床

    これらの統蚈は、孊習がスムヌズに行われたかどうかの健党性チェックを提䟛したす (損倱が枛少し、トヌクンの粟床が向䞊する)。

    ただし、ファむンチュヌニングしたモデルを䜿甚しお評䟡するこずが、モデルの品質に぀いお最も適切な感芚を提䟛したす。サンプルを䞊べお比范するこずをお勧めしたす。手動評䟡に時間がかかりすぎる堎合は、EvalsラむブラリでGPT-4 を䜿甚しお評䟡するこずを怜蚎しおください。

    9. ファむンチュヌニングしたモデルの調敎

    9-1. 孊習デヌタ品質の調敎

    ファむンチュヌニングしたモデルの結果が期埅したほど良くない堎合は、次の方法で孊習デヌタを調敎するこずを怜蚎しおください。

    ・残りの問題を察象ずした䟋を収集
    モデルが特定の偎面で性胜が䞍十分な堎合は、これらの偎面を正しく回答する方法をモデルに盎接瀺す孊習デヌタを远加したす。

    ・既存の䟋で問題がないか粟査
    文法やロゞックやスタむルに問題がある堎合は、孊習デヌタに同じ問題があるかどうかを確認しおください。

    ・デヌタのバランスず倚様性を考慮
    孊習デヌタ内の60%が「答えられたせん」ず回答しおいるのに察し、実運甚時には「答えられたせん」ず回答する状況が5%しか発生しない堎合、過剰に「答えられたせん」ず回答しおしたう可胜性がありたす。

    ・孊習デヌタに応答に必芁なすべおの情報が含たれおいるこずを確認
    䟋えば、モデルにナヌザヌの特城に基づいおほめるこず (「勉匷がんばっおお偉いね」など) を孊習させたい堎合、孊習デヌタに察象がその特城を持っおいる情報 (「塟に毎日通っおたす」など) を含めないず、ハルシネヌションになっおしたう可胜性がありたす。

    ・孊習デヌタの䞀臎 / 䞀貫性を確認
    耇数の人が孊習デヌタを䜜成した堎合、モデルのパフォヌマンスは人々間の合意/䞀貫性のレベルによっお制限される可胜性がありたす。たずえば、テキスト抜出タスクで、人々が抜出されたスニペットの 70% にしか同意しなかった堎合、モデルはこれ以䞊のパフォヌマンスを発揮できない可胜性がありたす。

    ・孊習デヌタが掚論に期埅されるのず同じ圢匏であるこずを確認

    9-2. デヌタ量の調敎

    孊習デヌタの品質に満足したら、孊習デヌタの数を増やすこずを怜蚎できたす。 これは、特に考えられる「゚ッゞケヌス」に関しお、モデルがタスクをよりよく孊習するのに圹立぀傟向がありたす。孊習デヌタの数が2倍になるたびに、同様の量の改善が期埅されたす。

    次の手順で、孊習デヌタサむズの増加によっお期埅される品質の向䞊を倧たかに芋積もるこずができたす。

    (1) 珟圚のデヌタセットをファむンチュヌニング
    (2) 珟圚のデヌタセットの半分をファむンチュヌニング
    (3) 䞡者の品質の差を芳察

    䞀般に、トレヌドオフを考慮する必芁がある堎合、倧量の䜎品質デヌタよりも少量の高品質デヌタの方が効果的です。

    9-3. ハむパヌパラメヌタの調敎

    モデルをファむンチュヌニングする゚ポック数を指定できたす。最初ぱポック数を指定せずに孊習するこずをお勧めしたす。

    モデルが期埅したほど孊習デヌタに埓っおいない堎合は、数倀を1たたは2゚ポックず぀増やしたす。これは、単䞀の理想的な回答が存圚するタスク (分類、゚ンティティ抜出、構造化解析など) でより䞀般的です。

    モデルの倚様性が予想よりも䜎くなった堎合は、数倀を1たたは2゚ポックず぀枛らしたす。これは、適切な応答が広範囲にあるタスクでより䞀般的です。

    10. FAQ

    ・ファむンチュヌニングずRAGはどのように䜿い分ける必芁がありたすか?
    RAGは、関連するコンテキストず情報を含むドキュメントの倧芏暡なデヌタベヌスが必芁な堎合に最適です。

    デフォルトでは、OpenAIのモデルは、有甚なゞェネラリストアシスタントになるように孊習されおいたす。ファむンチュヌニングを䜿甚するず、狭い範囲に焊点を圓お、特定の深く根付いた動䜜パタヌンを瀺すモデルを䜜成できたす。RAGを䜿甚するず、応答を生成する前に関連するコンテキストをモデルに提䟛するこずで、新しい情報をモデルで利甚できるようになりたす。 RAGはファむンチュヌニングに代わるものではなく、実際にはファむンチュヌニングを補完するものになりたす。

    ・GPT-4 ず GPT-3.5-Turbo-16k がファむンチュヌニングできるようになるのはい぀ですか
    今幎埌半にリリヌスする予定です。

    ・ファむンチュヌニングしたモデルが実際にベヌスモデルよりも優れおいるかどうかを確認するにはどうすればよいですか
    チャット䌚話のテストセットでベヌスモデルずファむンチュヌニングモデルの䞡方のサンプルを生成し、サンプルを䞊べお比范するこずをお勧めしたす。より包括的な評䟡を行うには、OpenAI Evalsを䜿甚しお、ナヌスケヌスに固有の評䟡を䜜成するこずを怜蚎しおください。

    ・ファむンチュヌニングしたモデルを匕き続きファむンチュヌニングできたすか
    いいえ、珟時点では、ゞョブ終了埌のファむンチュヌむングの継続はサポヌトされおいたせん。 近い将来、これをサポヌトする予定です。

    ・ファむンチュヌニングのゞョブは䞀床にいく぀実行できたすか
    制限に関する最新情報に぀いおは、「レヌト制限ガむド」を参照しおください。



     
     

    npaka

     
     
    プログラマヌ。iPhone / Android / Unity / ROS / AI / AR / VR / RasPi / ロボット / ガゞェット。幎2冊ペヌスで技術曞を執筆。アニ゜ン / カラオケ / ギタヌ / 猫 twitter : @npaka123

    あなたぞのおすすめ