MergeKitをマスターする:高価なGPUなしでLLMモデルを「ミックス」する究極の技

Artificial Intelligence tutorial - IT technology blog
Artificial Intelligence tutorial - IT technology blog

なぜトレーニングに没頭するのではなく、モデルをマージするのか?

Llama 3やQwenなどのモデルを扱っていると、「モデルAはコード生成に非常に優れているが、モデルBの方が自然な日本語を話す」といった状況に遭遇することがあります。これら両方の強みを併せ持った「ハイブリッドモデル」が欲しいと思うのは当然でしょう。

これまでの一般的な解決策は、ファインチューニング(Fine-tuning)でした。しかし、実際にはこれには莫大なコストがかかります。H100 GPUクラスターをレンタルすると、現在1時間あたり4〜5ドルほどかかります。さらに、新しいスキルを学習する一方で以前の知識を完全に忘れてしまう「破滅的忘却(catastrophic forgetting)」という現象に直面するリスクもあります。

そこで、スマートな近道として登場したのがモデルマージ(Model Merging)です. ゼロから教え直すのではなく、既存のモデルの「重み」をアルゴリズムで混合します。このプロセスはGPUコストがほとんどかかりません。通常のRAMやCPUだけでも実行可能です。

比較:モデルマージ vs ファインチューニング

実際のチャットボット開発プロジェクトの経験に基づき、選択の助けとなるよう違いをまとめました:

  • ファインチューニング: 従業員を専門的な研修に送り出すようなものです。授業料(GPUコスト)がかかり、時間がかかり、さらには「丸暗記」してしまうリスクも高いです。
  • モデルマージ: 二人の専門家の経験を組み合わせるようなものです。10〜30分という超高速で完了し、元の知能を維持したまま、コストはほぼゼロです。

私は以前、RAG(検索拡張生成)システムにこの手法を適用しました。高価なファインチューニングの代わりに、対話特化型モデルと情報抽出特化型モデルをマージしました。その結果、モデルは単語の繰り返しや文脈の喪失を起こすことなく、正確に回答できるようになりました。

代表的な3つのモデル「ブレンド」アルゴリズム

MergeKitは多くの手法をサポートしていますが、特に覚えておくべき3つを紹介します:

1. SLERP (Spherical Linear Interpolation)

同じ構造を持つ2つのモデルを混合するための最も一般的な選択肢です。単純な平均を取るのではなく、ベクトル間の角度に基づいて計算します。これにより、重みを混乱させることなく、各モデルの独自の特性を維持できます。

2. TIES (Trimming, Electing, and Merging)

3つ以上のモデルを混合したい場合は、TIESを使用します。このアルゴリズムは、微細な変化(ノイズ)を自動的に排除し、最も重要なエッセンスだけを保持することで、構成モデル間の衝突を根本的に解決します。

3. DARE (Drop and Rescale)

DAREは、冗長な重みを削除するというより大胆なアプローチを取ります。マージ後のモデルは非常にコンパクトになり、マルチタスクのファインチューニングに近いパフォーマンスを発揮することが多いです。

Linux環境でのMergeKitの実装

Linuxサーバー(Ubuntu 22.04が理想的)が必要です。ハードウェアに関しては、GPUはそれほど重要ではありませんが、大容量のRAMが必要です。私の経験では、RAMは入力モデルの合計サイズよりも大きい必要があります。2つの7Bモデル(各約15GB)をマージするには、少なくとも32GBから64GBのRAMを準備することをお勧めします。

ステップ1:環境構築

ライブラリの衝突を避けるため、システムに直接インストールせず、常に仮想環境を使用してください。

# システムの更新
sudo apt update && sudo apt upgrade -y

# python venvのインストール
sudo apt install python3-venv -y

# 仮想環境の作成と有効化
python3 -m venv mergekit_env
source mergekit_env/bin/activate

# ソースからMergeKitをインストール
git clone https://github.com/arcee-ai/mergekit.git
cd mergekit
pip install -e .

ステップ2:構成ファイルの作成 (YAML形式)

YAMLファイルでこの「結婚」の定義を記述します。ここでは、日本語能力と推論能力を最適化するために、同じLlama-3-8Bシリーズの2つのモデルをマージする例を挙げます。

slices:
  - sources:
      - model: meta-llama/Meta-Llama-3-8B-Instruct
        layer_range: [0, 32]
      - model: NousResearch/Hermes-2-Theta-Llama-3-8B
        layer_range: [0, 32]
merge_method: slerp
base_model: meta-llama/Meta-Llama-3-8B-Instruct
parameters:
  t:
    - filter: self_attn
      value: [0, 0.5, 0.3, 0.7, 1]
    - filter: mlp
      value: [1, 0.5, 0.7, 0.3, 0]
    - value: 0.5
dtype: bfloat16

ヒント: モデルは同じレイヤー構造と隠れ層のサイズ(hidden size)を持っている必要があります。この方法で7Bモデルと70Bモデルを混ぜることはできません。

ステップ3:マージの実行

準備が整いました。次のコマンドを実行して、MergeKitに魔法をかけさせましょう:

mergekit-yaml config.yaml ./my-merged-model --copy-tokenizer --allow-crimes

フラグの簡単な説明:

  • ./my-merged-model: 完成品の保存先。
  • --copy-tokenizer: 元のモデルからトークナイザーを保持します。
  • --allow-crimes: 構造にわずかな差異がある場合の処理を許可します(慎重に使用してください)。

ステップ4:成果の確認

完了すると、出力ディレクトリに .safetensors ファイルが生成されます。transformers ライブラリを使用して、新しいモデルの応答能力を素早くテストできます。

from transformers import AutoModelForCausalLM, AutoTokenizer

model_path = "./my-merged-model"
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path, device_map="auto")

# 難しい質問でテスト
inputs = tokenizer("相対性理論を簡単な日本語で説明してください", return_tensors="pt").to("cuda")
outputs = model.generate(**inputs, max_new_tokens=150)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))

モデルマージにおける教訓

強力ではありますが、モデルマージは「万能の鍵」ではありません。多くの試行錯誤を経て、いくつか注意すべき点を見つけました:

メリット:

  • 高メモリのVPS上で非常に素早く新しいモデルを作成できる。
  • Hugging Faceコミュニティの力を最大限に活用できる。
  • YAML設定を通じて柔軟にカスタマイズ可能。

デメリット:

  • 結果が運任せになることがある。構成要素の衝突が強すぎると、モデルが支離滅裂になる可能性がある。
  • 最高のパフォーマンスを得るために、パラメータ(比率t)の微調整に時間がかかる。
  • トークナイザーが一致しない場合、チャットテンプレートのエラーが頻繁に発生する。

開発者の皆さんへのメッセージ

MergeKitは、限られた予算で本格的なAI開発を行いたい人々にとってゲームチェンジャーとなるツールです。GPUの軍拡競争に参加する代わりに、賢くモデルを選択し、組み合わせることに集中しましょう。

まずはSLERP手法を使って、2つの小さな7Bまたは8Bモデルから始めてみてください。慣れてきたら、TIESのような複雑な手法に進み、Open LLM Leaderboardで上位を狙えるような真の「モンスターモデル」を作り上げてみましょう。

Share: