Skip to content

Instantly share code, notes, and snippets.

@kzinmr
Last active June 3, 2024 17:34
Show Gist options
  • Save kzinmr/cb9b513f27c04741df05f239d0b7ce7f to your computer and use it in GitHub Desktop.
Save kzinmr/cb9b513f27c04741df05f239d0b7ce7f to your computer and use it in GitHub Desktop.

LLMを数分でファインチューニング (ft. Mixtral, LLaMA, Mistral)

このガイドでは、modalaxolotlを使って、任意のLLMを素早くファインチューニングする方法を紹介します。

サーバーレスなaxolotl

Modalは、人気のあるaxolotl LLMファインチューニングライブラリにサーバーレスの機能を提供します。 Modalのクラウドインフラストラクチャでファインチューニングジョブを実行すると、Dockerイメージの扱いや高価なGPU VMのアイドル状態を気にすることなく、モデルをトレーニングできます。

また、Modalで書かれたアプリケーションは、複数のGPUに簡単にスケールできます。 これは、並列で動作する複数のH100サーバーでファインチューニングを行う場合や、 本番環境の推論のために何百ものA100やA10Gインスタンスを実行する場合に便利です。

効率とパフォーマンスを重視した設計

サンプル設定では、axolotlがサポートしている効率的でパフォーマンスの高いトレーニングのための最新の最適化手法を多数使用しています。

  • Deepspeed ZeROを使用して、設定した戦略に従って複数のGPUをトレーニング中に活用。
  • 高速でパラメータ効率の良いファインチューニングのためのLoRAアダプタ
  • トレーニング中の高速でメモリ効率の良いアテンション計算のためのFlashアテンション

クイックスタート

クイックスタートの例では、概念実証として、テキストからSQLへのデータセットの非常に小さなサブサンプルで7Bモデルをオーバーフィットさせています。 また、DeepSpeed ZeRO-3 Offloadを使用して、モデルとオプティマイザの状態を2つのA100に分散しています。

オーバーフィットは、トレーニング設定をテストするための優れた方法です。 それは、短時間(5分以内!)かつ最小限のデータで実行でき、実際のトレーニングプロセスに近いからです。

ファインチューニングされたモデルでの推論は、出力構造([SQL] ... [/SQL])にフィットしていることを示しています。より良い結果を得るには、より多くのデータを使用する必要があります! 開発 セクションを参照してください。

  1. セットアップ インフラストラクチャ用のModalへの認証、モデル用のHugging Face、およびトレーニングの可観測性のためのWeights & Biases(オプション)の設定:
セットアップ 1. [Modal](https://modal.com/)アカウントを作成します。 2. 現在のPython仮想環境に`modal`をインストールします(`pip install modal`)。 3. 環境にModalトークンを設定します(`python3 -m modal setup`)。 4. ワークスペースに`huggingface`という名前の[シークレット](https://modal.com/docs/guide/secrets#secrets)が必要です。Modalダッシュボードで、[HuggingFaceテンプレートを使って新しいシークレットを作成](https://modal.com/secrets)します。HuggingFace(`settings/tokens`)からのキーを使用して`HF_TOKEN`を入力し、名前を`my-huggingface-secret`から`huggingface`に変更します。 5. 一部のLLaMAモデルでは、Hugging Faceページ(例:[LLaMA 3 8Bのページ](https://huggingface.co/meta-llama/Meta-Llama-3-8B))にアクセスし、利用規約に同意する必要があります(すぐに許可されます)。 6. [Weights & Biases](https://wandb.ai)をロギングに使用する場合は、ワークスペースに`wandb`という名前のシークレットも必要です。これも[テンプレートから作成](https://modal.com/secrets)できます。適切なログなしでのトレーニングは難しいため、試してみるか、[MLFlow](https://mlflow.org/)との`axolotl`の統合について調べることをお勧めします!
  1. このリポジトリをクローンします

    git clone https://github.com/modal-labs/llm-finetuning.git
    cd llm-finetuning
  2. ファインチューニングジョブを起動します

    export ALLOW_WANDB=true  # Weights & Biasesを使用する場合
    modal run --detach src.train --config=config/mistral-memorize.yml --data=data/sqlqa.subsample.jsonl

この例のトレーニングスクリプトは、開始を容易にするために独自の方針を採用しています。必要に応じて自由に適応させてください。

  1. 推論を実行します 今トレーニングしたモデルに対して:
# テスト推論を1回実行
modal run -q src.inference --prompt "[INST] Using the schema context below, generate a SQL query that answers the question.
CREATE TABLE head (name VARCHAR, born_state VARCHAR, age VARCHAR)
List the name, born state and age of the heads of departments ordered by name.[/INST]"
# 🤖:  [SQL] SELECT name, born_state, age FROM head ORDER BY name [/SQL]
# 🧠: Effective throughput of 36.27

※ prompt:

"[INST] 以下のスキーマコンテキストを使用して、質問に答えるSQLクエリを生成します。
CREATE TABLE head (name VARCHAR, born_state VARCHAR, age VARCHAR)
部門の長の名前、出身州、年齢を名前順に一覧表示します。[/INST]"
# サーバーレス推論サービスをデプロイ
modal deploy src.inference
curl https://YOUR_MODAL_USERNAME--example-axolotl-inference-web.modal.run?input=%5BINST%5Dsay%20hello%20in%20SQL%5B%2FINST%5D
# [SQL] Select 'Hello' [/SQL]

フラット化されたデータの検査

axolotlの主要な機能の1つは、JSONLファイルからデータを設定ファイルで指定したプロンプトテンプレート形式にフラット化することです。 トークン化とプロンプトテンプレートは、ファインチューニングでミスが発生しやすい箇所です。

新しいデータセットでモデルをファインチューニングする際は、必ずデータを最初に検査することを強くお勧めします。 データを検査し、正しくフラット化されていることを確認する方法については、nbs/inspect_data.ipynbノートブックを参照してください。

  • dataset definition
  • check preprocessing ( --preproc-only )
  • download and decode the preprocessed data

※ For tokenization in axolotl, see: https://github.com/OpenAccess-AI-Collective/axolotl/blob/05b0bd08d229ee28cd3f11098d5b178f2ce441b6/src/axolotl/utils/models.py

開発

NOTE: このModalアプリは、axolotlのようにすべての設定をCLI経由で明示的に渡していません。代わりに、設定ファイルですべての必要なオプションを指定します。

コードの概要

ファインチューニングのロジックはtrain.pyにあります。重要な関数は以下の通りです:

  • launchは、新しいトレーニングジョブ用のトレーニング設定とデータを/runsボリュームの新しいフォルダに準備します。また、ベースモデルがHuggingFaceからダウンロードされていることを確認します。
  • trainは、準備されたフォルダを受け取り、設定とデータを使用してトレーニングジョブを実行します。

trainコマンドに関する注意事項:

  • --dataフラグは、データセットをaxolotlに渡すために使用します。このデータセットは、設定ファイルで指定されたdatasets.pathに書き込まれます。
    • すでにdatasets.pathにデータセットがある場合は、データセットが正しくロードされるように、同じパスを--dataに渡す必要があります。
  • --no-merge-loraは、LoRAアダプタの重みをベースモデルの重みにマージしないようにします。

inference.pyファイルには、以前のトレーニングジョブからの事前トレーニング済みまたはファインチューニング済みモデル用のvLLM推論サーバーが含まれています。

See. https://modal.com/docs/examples/vllm_inference#fast-inference-with-vllm-mistral-7b

設定

configには、異なるモデルですぐに開始できるサンプル設定があります。axolotlの設定オプションの概要はこちらを参照してください。

考慮すべき最も重要なオプションは以下の通りです:

モデル

base_model: mistralai/Mistral-7B-v0.1

データセット(すべてのデータセットオプションはこちらを参照)

datasets:
  # これは、データがクラウドのボリュームに保存されるときに使用されるパスになります。
  - path: data.jsonl
    ds_type: json
    type:
      # JSONLファイルには、行ごとにquestion、context、answerフィールドが含まれています。
      # これは、instruction、input、output axolotlタグにマッピングされます。
      field_instruction: question
      field_input: context
      field_output: answer
      # フォーマットは、axolotlがプロンプトを生成するために使用します。  
      format: |-
        [INST] 以下のスキーマコンテキストを使用して、質問に答えるSQLクエリを生成します。
        {input}
        {instruction} [/INST]    

LoRA

adapter: lora # qloraの場合。フルファインチューニングの場合は空白のままにします(より多くのGPUメモリが必要)。
lora_r: 16
lora_alpha: 32 # alphaは2×rankが適切な目安です。
lora_dropout: 0.05
lora_target_linear: true # すべての線形層を対象とします。

カスタムデータセット

axolotl多くのデータセット形式をサポートしています。カスタムデータセットをdataフォルダの.jsonlファイルとして追加し、設定を適切に変更することをお勧めします。

Weights and Biasesでのロギング

Weights and Biasesでトレーニング実行を追跡するには、config.ymlwandb設定情報を追加します:

wandb_project: code-7b-sql-output # プロジェクト名を設定
wandb_watch: gradients # 勾配のヒストグラムを追跡

そして、トレーニングジョブを起動するときにALLOW_WANDB環境変数をtrueに設定します:

ALLOW_WANDB=true modal run --detach src.train --config=... --data=...  

マルチGPUトレーニング

マルチGPUトレーニングにはセットアップが簡単なDeepSpeedを推奨します。必要なのはどの設定を使用するかを指定することだけです:

まず、.ymlのDeepSpeed設定を編集します:

deepspeed: /root/axolotl/deepspeed_configs/zero3_bf16.json

そして、トレーニングジョブを起動するときに、使用するGPU設定をGPU_CONFIG環境変数に設定します:

GPU_CONFIG=a100-80gb:4 modal run --detach src.train --config=... --data=...

重みの検索と使用

すべての実行結果は、CLIで以下のようにして検索できます:

modal volume ls example-runs-vol

または、Modalダッシュボードで確認できます。

トレーニング実行によって作成されたアーティファクトは、以下のコマンドで参照できます。これは、トレーニング実行のログの最後にも出力されます:

modal volume ls example-runs-vol <run id>
# 例: modal volume ls example-runs-vol axo-2024-04-13-19-13-05-0fb0

デフォルトでは、Modal axolotl Trainer は自動的にLoRAアダプタの重みをベースモデルの重みにマージします。

完了した実行のディレクトリは、このようになります:

$ modal volume ls example-runs-vol axo-2024-04-13-19-13-05-0fb0/

Directory listing of 'axo-2024-04-13-19-13-05-0fb0/' in 'example-runs-vol'
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃ filename                                       ┃ type ┃ created/modified          ┃ size    ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩
│ axo-2024-04-13-19-13-05-0fb0/last_run_prepared │ dir  │ 2024-04-13 12:13:39-07:00 │ 32 B    │
│ axo-2024-04-13-19-13-05-0fb0/mlruns            │ dir  │ 2024-04-13 12:14:19-07:00 │ 7 B     │
│ axo-2024-04-13-19-13-05-0fb0/lora-out          │ dir  │ 2024-04-13 12:20:55-07:00 │ 178 B   │
│ axo-2024-04-13-19-13-05-0fb0/logs.txt          │ file │ 2024-04-13 12:19:52-07:00 │ 133 B   │
│ axo-2024-04-13-19-13-05-0fb0/data.jsonl        │ file │ 2024-04-13 12:13:05-07:00 │ 1.3 MiB │
│ axo-2024-04-13-19-13-05-0fb0/config.yml        │ file │ 2024-04-13 12:13:05-07:00 │ 1.7 KiB │
└────────────────────────────────────────────────┴──────┴───────────────────────────┴─────────┘

LoRAアダプタはlora-outに保存されます。マージされた重みはlora-out/mergedに保存されます。多くの推論フレームワークは、マージされた重みしかロードできないことに注意してください!

過去のトレーニングジョブからモデルで推論を実行するには、コマンドラインでrun nameを指定できます:

modal run -q src.inference --run-name=...

よくあるエラー

CUDA Out of Memory (OOM)

これは、トレーニング中にGPUのメモリが不足したことを意味します。解決するには、マルチGPUトレーニングでGPUの数やメモリ容量を増やすか、config.ymlの以下の値を減らしてみてください: micro_batch_size,eval_batch_size,gradient_accumulation_steps,sequence_len

self.state.epoch = epoch + (step + 1 + steps_skipped) / steps_in_epoch ZeroDivisionError: division by zero

これは、トレーニングデータセットが小さすぎることを意味しています。

CLIでmodal runを使用するときに設定オプションがない

modalクライアントが0.55.4164以上であることを確認してください(pip install --upgrade modalで最新バージョンにアップグレードしてください)。

AttributeError: 'Accelerator' object has no attribute 'deepspeed_config'

設定からwandb_log_modelオプションを削除してみてください。#4143を参照してください。

Warning

The following values were not passed to `accelerate launch` and had defaults used instead:
        `--num_processes` was set to a value of `2`
                More than one GPU was found, enabling multi-GPU training.
        `--num_machines` was set to a value of `1`
        `--mixed_precision` was set to a value of `'no'`
        `--dynamo_backend` was set to a value of `'no'`
To avoid this warning pass in values for each of the problematic parameters or run `accelerate config`.
@kzinmr
Copy link
Author

kzinmr commented Jun 3, 2024

追加した処理

from huggingface_hub import login
login(token="hf_xxx")
from transformers import AddedToken

def add_special_tokens(tokenizer: AutoTokenizer, cfg: dict):

    additional_special_tokens = None
    if 'special_tokens' in cfg:
        special_tokens = cfg['special_tokens']
        additional_special_tokens = special_tokens.pop(
            "additional_special_tokens", None
        )
        # lora_modules_to_save = get_linear_embedding_layers(model_config.model_type)
        for k, val in special_tokens.items():
            # check if new special token is not already in tokenizer and
            # is adapter training to make sure lora_modules_to_save is set
            # pylint: disable=too-many-boolean-expressions
            # if (
            #     (getattr(tokenizer, k) is None or getattr(tokenizer, k) != val)
            #     and (len(tokenizer.encode(val, add_special_tokens=False)) > 2)
            #     and cfg.adapter
            #     and (
            #         not cfg.lora_modules_to_save
            #         or not all(
            #             x in cfg.lora_modules_to_save for x in lora_modules_to_save
            #         )
            #     )
            # ):
            #     lora_modules_to_save = ", ".join(
            #         [f"`{x}`" for x in lora_modules_to_save]
            #     )
            #     raise ValueError(
            #         f"Please set lora_modules_to_save to [{lora_modules_to_save}] when using an adapter and changing the special tokens."
            #     )

            tokenizer.add_special_tokens(
                {k: AddedToken(val, rstrip=False, lstrip=False, normalized=False)}
            )

        # If we add bos_token and eos_token, we need to update the post processor to
        # handle them correctly.
        # https://github.com/huggingface/transformers/pull/24132
        bos_or_eos_in_special_tokens = (
            "bos_token" in cfg['special_tokens'] and "eos_token" in cfg['special_tokens']
        )
        if (
            tokenizer.__class__.__name__
            in (
                "LlamaTokenizerFast",
                "CodeLlamaTokenizerFast",
            )
            and bos_or_eos_in_special_tokens
        ):
            tokenizer.update_post_processor()

    if 'tokens' in cfg and cfg['tokens']:
        tokenizer.add_tokens(
            [
                AddedToken(token, rstrip=False, lstrip=False, normalized=False)
                for token in cfg['tokens']
            ]
        )

    # Additional special tokens are a List, and need to be treated differently than regular special
    # tokens. We add them after we have called `add_tokens` in case these additional special tokens
    # are new tokens.
    #
    # Usage:
    #
    # ```py
    # special_tokens:
    #   additional_special_tokens: ["<|im_start|>", "<|im_end|>"]
    # ```
    if additional_special_tokens is not None:
        tokenizer.add_special_tokens(
            {"additional_special_tokens": additional_special_tokens}
        )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment