なぜトレーニングに没頭するのではなく、モデルをマージするのか?
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で上位を狙えるような真の「モンスターモデル」を作り上げてみましょう。

