OLMoE 完全ガイド
完全オープン MoE の OLMoE-1B-7B を徹底解剖。AllenAI 製・訓練データまで全公開・RTX 4070 Super で LoRA 学習可能な唯一の MoE
目次
🧪 OLMoE-1B-7B-Instruct — 全体アーキテクチャ
AllenAI (Allen Institute for AI) 製の完全オープン MoE。訓練データ・コード・中間チェックポイント全て公開。 このモデルは あなたが実際に LoRA 学習できる唯一の MoE。
⚖ Qwen3-30B との比較
| 項目 | Qwen3-30B-A3B | 🧪 OLMoE-1B-7B | あなたへの影響 |
|---|---|---|---|
| GGUF サイズ | 18 GB | 4.2 GB | VRAM 余裕 |
| 層数 | 48 | 16 | 学習 3 倍速 |
| Expert 数 | 128 | 64 | 構造は同じ |
| 訓練データ公開 | ❌ 非公開 | ✅ 完全公開 | 研究最強 |
| LoRA 学習 (RTX 4070) | ❌ 困難 (VRAM 不足) | ✅ 数時間で完走 | 個人で fine-tune 可能 |
🔄 推論フロー
入力 "hello"
│
▼ [① Tokenize] BPE で 50,304 語彙に分割
│ "hello" → [19659] ← 1 トークンに収まる (英語が得意)
▼
[② Embedding] token_embd [2048 × 50304]
│ 各 token → 2048 次元ベクトル
▼
┌─────── 16 層ブロックをループ (Qwen3 は 48) ──────┐
│ │
│ [③ Self-Attention (MHA)] │
│ q/k/v 全て 16 head │
│ ※ GQA なし → KV cache が Qwen3 より大きい │
│ │
│ [④ Router] ffn_gate_inp [2048 × 64] │
│ 2048 次元 → 64 スコア → softmax │
│ │
│ [⑤ top-8 選出] │
│ 64 人から最適な 8 人を選ぶ │
│ │
│ [⑥ Expert 8 人実行] │
│ 選ばれた 8 expert だけ FFN (1024 dim) │
│ 残り 56 expert は計算しない = 高速 │
│ │
│ [⑦ 重み付き合成] │
│ 8 人の出力 × routing_weight で足し合わせ │
└───────────────────────────────────────────────────┘
│
▼ [⑧ Final Norm + LM Head]
│ 2048 次元 → 50,304 次元ロジット
▼ [⑨ Sampling] top-p=0.9 で次 token 決定
│
"お" 次のトークン! 📖 OLMoE ソースコードの歩き方 (1500 行)
HuggingFace transformers の modeling_olmoe.py は 1,500 行しかない。
MoE の実装を読む上で最短の教材。読む順番をクラス単位で示します。
| 順 | クラス / 関数 | 行数 | 何をするか |
|---|---|---|---|
| 1 | OlmoeRMSNorm | 15 行 | LayerNorm の軽量版。RMS で正規化 |
| 2 | OlmoeRotaryEmbedding | 40 行 | RoPE 位置埋め込み。token の順序情報を付与 |
| 3 | OlmoeMLP | 20 行 | Expert 1 個の実装。SwiGLU FFN。こいつが 64 個並ぶ |
| 4 | OlmoeAttention | 150 行 | Multi-Head Attention。MoE 前の token 間コミュニケーション |
| ⭐ 5 | OlmoeSparseMoeBlock | 70 行 | ★ MoE の心臓。ここだけ読めば OK ★ Router → top-K → expert 実行 → 合成 |
| 6 | OlmoeDecoderLayer | 50 行 | Attention + MoE + Residual を 1 層にまとめる。これが 16 回ループ |
| 7 | OlmoeModel | 200 行 | Embedding → 16 層ループ → Final Norm の全体制御 |
| 8 | OlmoeForCausalLM | 150 行 | OlmoeModel + LM Head。loss 計算も含む (学習時) |
| 9 | load_balancing_loss_func | 40 行 | expert 偏り防止の補助損失。学習時だけ使用 |
💡 最短ルート: OlmoeSparseMoeBlock の 70 行だけ読めば MoE は理解できる。
他 800 行 (Attention / RoPE / RMSNorm) は普通の LLM と同じ。
🔬 MoE の心臓 70 行を擬似コードで
class OlmoeSparseMoeBlock:
# 入力: [B, T, 2048] ← T トークン分の隠れ状態
# 出力: [B, T, 2048] ← MoE 処理後の隠れ状態
def forward(self, x):
B, T, H = x.shape
x_flat = x.view(B*T, H) # [B*T, 2048]
### ① Router: 64 expert のスコアを計算 ###
router_logits = self.gate(x_flat) # [B*T, 64]
weights = softmax(router_logits, -1) # 確率化
### ② top-K: 上位 8 人を選ぶ ###
w, idx = torch.topk(weights, k=8, dim=-1) # idx: [B*T, 8]
### ③ 選ばれた expert だけ実行 ###
out = torch.zeros_like(x_flat)
for e_id in range(64): # 64 expert 全部ループ
mask = (idx == e_id) # この expert が選ばれた位置
if not mask.any():
continue # 呼ばれなければ skip (高速化)
tok_idx, topk_idx = mask.nonzero(as_tuple=True)
expert_out = self.experts[e_id](x_flat[tok_idx]) # FFN 実行
### ④ 重み付きで元の位置に足し戻す ###
out.index_add_(0, tok_idx,
expert_out * w[tok_idx, topk_idx, None])
return out.view(B, T, H), router_logits
# ★ この 30 行が MoE の全て。Mixtral も Qwen3 も同じパターン。expert 数 (64 or 128) と top-K (8 or 2) が違うだけ。 🔰 よくある疑問: 「2048 次元」って何? — 基礎用語 7 つ
「2048 次元ベクトル」「パラメータ」などが出てきたけど意味が分からない…という時の早見表。
| スカラー | 数字 1 つ。例: 3.14 |
| ベクトル | 数字の並び。例: [3.14, 0.5, -1.2] ← これは 3 次元ベクトル |
| 次元 (dim) | ベクトルの長さ。「2048 次元」= 数字が 2048 個並んでいること。次元が多いほど単語の意味を細かく表現できるが計算・メモリも増える |
| テンソル | ベクトルを何重にも積んだ箱。行列 (2D) → 3D → 4D と積める。例: [50304, 2048] は「2048 次元ベクトルが 50,304 個並んだ行列」= Embedding |
| パラメータ | テンソルの中の「1 個 1 個の数字」= 重み。6.9B パラメータ = 69 億個の数字が GGUF に詰まっている |
| Token | テキストをちぎった最小単位 (≈ 単語や音節)。各 token は整数 ID (0〜50303) に変換されてモデルに入る |
| Embedding | 整数 ID → 2048 次元ベクトルに変換する辞書。似た単語は近いベクトルになる |
🌐 よくある疑問: 他のモデル (DeepSeek-R1) と比べると?
同じ MoE でも桁が違う。OLMoE は勉強・個人用、DeepSeek-R1 は研究所・企業用。
| 項目 | OLMoE-1B-7B | Qwen3-30B-A3B | DeepSeek-R1 | 意味 |
|---|---|---|---|---|
| Total パラメータ | 6.9 B | 30.5 B | 671 B | 全体の重みの数 |
| Active パラメータ | 1.3 B | 3.3 B | 37 B | 1 token 生成で実際に動く量 |
| 層数 | 16 | 48 | 61 | 推論の「深さ」 |
| Expert 総数 | 64 | 128 | 256 + 1 shared | 専門家の人数 |
| GGUF Q4 サイズ | 4.2 GB | 18 GB | 404 GB | ディスクに置くファイルサイズ |
🔍 部品別の役割 — 何がどこで何をしているか
OLMoE-1B-7B を動かす 8 つの部品と、その中で「どこに何が入っているか」を表で整理。「無いとどうなる?」列が初心者の理解に効きます。
| 部品 | 役目 | パラメータ数 | 無いとどうなる? |
|---|---|---|---|
Embeddingtoken_embd | 50,304 語彙 → 2048 次元ベクトルに変換 | 103 M (50304 × 2048) | 文字を計算できない |
Self-Attention (MHA)attn_q/k/v/out | 文脈を混ぜる。OLMoE は 16 head 全部 MHA (GQA なし) | 67 M / 層 × 16 層 | 前後の関係が消える |
Router (Gate)ffn_gate_inp | 64 expert のスコアを計算 → top-8 を選ぶ | 131 K / 層 (2048 × 64) | MoE にならない (全員発火) |
Expert (SwiGLU FFN) × 64ffn_*_exps | 知識を詰め込む場所。64 人の専門家、毎回 8 人が発火 | 6.3 M × 64 × 16 = 6.5 B (全パラの 90%) | 賢くならない |
RMSNormattn_norm / ffn_norm | 各層の計算を安定化 (爆発防止) | 2 K / 層 | 学習が発散して壊れる |
LM Headoutput.weight | 最終層 2048 次元 → 50,304 語彙の logit に射影 | 103 M | 次 token を選べない |
💡 重要: パラメータの 90% は Expert の中
7B パラメータのうち 約 6.5 B が Expert FFN の中。 LoRA 学習で target_modules を Expert (ffn_*_exps) に集中させると、 少ない追加重みで効率的に知識を書き換えられます。
🔤 語彙 50,304 個はどこ?
「50,304」は OLMoE が知っている語彙の種類数。3 つの場所に散らばっています。
| 場所 | 中身 | サイズ |
|---|---|---|
GGUF メタデータtokenizer.ggml.tokens | 50,304 個の文字列配列。["!", "#", …, "hello", …] | 約 1.5 MB |
GGUF tensortoken_embd.weight | 各 ID の意味ベクトル。[2048 × 50,304] の行列 | Q4_K_M: 52 MB |
GGUF tensoroutput.weight (LM head) | 最終層から語彙 50,304 次元に射影する行列 | Q4_K_M: 52 MB |
📊 4.2 GB の内訳 (Q4_K_M)
OLMoE-1B-7B-0125-Instruct-Q4_K_M.gguf (4.2 GB)
├── ヘッダ ~1 KB // "GGUF" magic + version
├── メタデータ ~2 MB // tokenizer + architecture info
│ ├── tokenizer.ggml.tokens // 50304 文字列
│ ├── tokenizer.ggml.merges // BPE 規則
│ └── architecture="olmoe", layers=16, experts=64, …
│
├── テンソル本体 ~4.2 GB
│ │
│ ├── token_embd.weight 52 MB // 埋め込み
│ ├── output.weight 52 MB // LM head
│ │
│ ├── blk.0 〜 blk.15 (16 層)
│ │ ├── attn_q/k/v/output 17 MB × 16 // Attention
│ │ ├── attn_norm / ffn_norm 8 KB × 16
│ │ │
│ │ ├── ffn_gate_inp 0.5 MB × 16 // Router ★小さい
│ │ ├── ffn_gate_exps (×64) 50 MB × 16 // Expert gate
│ │ ├── ffn_up_exps (×64) 50 MB × 16 // Expert up
│ │ └── ffn_down_exps (×64) 50 MB × 16 // Expert down
│ │
│ └── output_norm.weight 8 KB 📊 よくある疑問: 「1B-7B」って何? Total と Active の違いは?
「1B-7B」の数字はモデル名そのもの。→ Total 6.92 B (全 expert を数えた合計) / Active 1.28 B (推論 1 トークンで実際に動く量)
| 部品 | Total | Active |
|---|---|---|
| Token Embedding | 103 M | 103 M |
| Attention × 16 層 | 268 M | 268 M |
| Experts × 16 層 | 6.44 B | 805 M (top-8) |
| 合計 | 6.92 B | 1.28 B |
💪 強くする方法の選び方 — 7 つの手法と意思決定
「モデルを賢くする」には目的別に 7 つの選択肢。何をしたいかで選び方が変わる。まず自分のゴールを決めてから手法を選ぶこと。
| 手法 | 何ができる | VRAM | 所要時間 | RTX 4070S で可能? |
|---|---|---|---|---|
| ① Full fine-tune | 全パラメータ (7B) を更新。最強だが一番重い | 80 GB+ | 数日 | ❌ 無理 |
| ② LoRA (QLoRA) | 追加の小さな重みで style/知識を学習 | 6 GB | 数時間 | ✅ 余裕 |
| ③ Continued Pre-training | 生テキストを追加学習。ドメイン知識注入 | 24 GB+ | 数日 | ❌ ほぼ無理 |
| ④ SFT (Instruct) | 質問→回答ペアで対話能力を強化 | 6 GB | 数時間 | ✅ OK (LoRA と併用) |
| ⑤ DPO | 「こっちの回答の方が良い」で選好を教える | 8 GB | 半日 | ✅ OK |
| ⑥ ORPO | SFT + DPO を一発で (1 段階) | 8 GB | 半日 | ✅ OK |
| ⑦ RLHF (PPO) | 報酬モデル + 強化学習。ChatGPT の手法 | 60 GB+ | 数週間 | ❌ 無理 |
💡 RTX 4070 Super で現実的な選択肢: ② LoRA / ④ SFT / ⑤ DPO / ⑥ ORPO の 4 つ。 このうち LoRA + SFT の組み合わせが 最も費用対効果が高い。
🌲 意思決定フローチャート
Q1: 新しい知識を追加したい? それとも style/振る舞いだけ?
│
├─ style だけ (敬語変更 / キャラ付け / 分野特化返答)
│ │
│ └─→ LoRA + SFT
│ (100〜1000 サンプル、3〜6 時間、VRAM 6GB)
│ ★ 最小コスト、最初に試すべき ★
│
├─ 知識追加 (ドメイン特化・専門用語)
│ │
│ ├─ データが 1 万件未満 → LoRA + SFT (Q&A 形式)
│ ├─ データが 10 万件以上 → Continued Pre-training (クラウド推奨)
│ └─ 最新情報で常に更新したい → RAG (検索連携) が正解
│
├─ 応答の品質/安全性を高めたい
│ │
│ └─→ DPO or ORPO
│ (選好ペア 1000 組、VRAM 8GB)
│
└─ ChatGPT レベルを自作したい
│
└─→ RLHF (PPO)
→ ローカル GPU 不可能、クラウドで 100 万円規模 🏗 プロ級の組み合わせレシピ
Recipe A (シンプル): SFT のみ — チャットボット化。3 時間 / VRAM 6GB
Recipe B (バランス): SFT → DPO の 2 段階 — 品質↑。半日 / VRAM 8GB
Recipe C (最新): ORPO 1 段階 — SFT + DPO 同等の結果を早く。半日 / VRAM 8GB
Recipe D (知識特化): SFT (ドメイン Q&A) + RAG (外部検索) — 事実性↑
🇯🇵 日本語データセット集
OLMoE を日本語で強化するための実際に使える日本語データセット 10 種類。 全て HuggingFace datasets ライブラリで 1 行ダウンロード可能。
| 名前 | 内容 | 件数 | ライセンス | 用途 |
|---|---|---|---|---|
| kunishou/databricks-dolly-15k-ja | Dolly の日本語翻訳版。指示→応答 | 15,015 | CC-BY-SA 3.0 | ⭐ 最初に使うならこれ |
| llm-jp/oasst1-21k-ja | OpenAssistant の日本語会話 | 21,000 | Apache 2.0 | ⭐ マルチターン対話 |
| llm-jp/magpie-sft-v1.0 | LLM-JP 公式 SFT データ | 241,000 | Apache 2.0 | ⭐ 大規模で品質良 |
| cyberagent/chatbot-arena-ja-calm2-7b-chat-experimental | 選好ペア (chosen/rejected) | 10,000 | CC-BY-4.0 | ⭐ DPO 用 |
| kunishou/hh-rlhf-49k-ja | Anthropic HH-RLHF の日本語版 | 49,000 | MIT | DPO / RLHF 用 |
📗 整形レシピ ① Dolly-15k-ja
指示→回答 15,015 件。この 1 つだけで初回 LoRA は完走できる。
# prepare_dolly_ja.py
from datasets import load_dataset
import json
ds = load_dataset("kunishou/databricks-dolly-15k-ja", split="train")
with open("my_data.jsonl", "w", encoding="utf-8") as f:
for row in ds:
user_content = row["instruction"]
if row.get("input"):
user_content += "\n" + row["input"]
messages = [
{"role": "user", "content": user_content},
{"role": "assistant", "content": row["output"]},
]
f.write(json.dumps({"messages": messages}, ensure_ascii=False) + "\n")
print("✅ my_data.jsonl done:", len(ds), "件")
# → ✅ my_data.jsonl done: 15015 件 🎯 初心者推奨コース
- Dolly-15k-ja をダウンロード (30 MB)
- レシピ ① で my_data.jsonl 生成
- 自作 50〜100 件を混ぜる (キャラ付け)
- 次の「LoRA チュートリアル」セクションで実行
2 時間で完走できる。結果が良ければ Magpie で本格学習へ。
🎓 LoRA 学習チュートリアル — RTX 4070 Super 完全手順
あなたの RTX 4070 Super 12GB で実際に動く完全手順。 所要時間 3〜6 時間 (データセット次第)。 OLMoE は個人が本格 fine-tune できる唯一の MoE。
| 設定 | 合計 VRAM | 結果 |
|---|---|---|
| FP16 フル fine-tune | 22 GB | ❌ OOM |
| LoRA (FP16) | 17 GB | ❌ OOM |
| QLoRA (4-bit) | 6.5 GB | ✅ 余裕 |
| Unsloth (最適化) | 5.8 GB | ✅ 最速 |
① 環境構築 (5 分)
Windows の場合は WSL2 Ubuntu 推奨。ネイティブ Windows も可だが bitsandbytes で詰まりやすい。
# venv を作る (Python 3.11 推奨)
python3.11 -m venv ~/olmoe-env
source ~/olmoe-env/bin/activate # Windows: .\olmoe-env\Scripts\activate
# PyTorch (CUDA 12.1 版)
pip install torch --index-url https://download.pytorch.org/whl/cu121
# 方式 A: Unsloth (速い。推奨)
pip install "unsloth[cu121-torch230] @ git+https://github.com/unslothai/unsloth.git"
# 方式 B: 素の HF transformers + PEFT + bitsandbytes
pip install transformers peft datasets accelerate bitsandbytes trl
# 動作確認
python -c "import torch; print('CUDA:', torch.cuda.is_available(), torch.cuda.get_device_name(0))"
# → CUDA: True NVIDIA GeForce RTX 4070 SUPER ③ 学習スクリプト (train_lora.py)
Unsloth 版。コピペでそのまま動く。target_modules は Expert FFN + Attention を指定。
# train_lora.py
from unsloth import FastLanguageModel
from datasets import load_dataset
from trl import SFTTrainer, SFTConfig
# ① モデル読み込み (4-bit 量子化で VRAM 4GB)
model, tok = FastLanguageModel.from_pretrained(
model_name = "allenai/OLMoE-1B-7B-0125-Instruct",
max_seq_length = 2048,
load_in_4bit = True,
dtype = None, # 自動 (bf16 on Ampere+)
)
# ② LoRA 設定 (target_modules に expert を含める)
model = FastLanguageModel.get_peft_model(
model,
r = 16, # LoRA rank (8〜32 が定石)
lora_alpha = 32,
lora_dropout = 0.05,
target_modules = [
"q_proj", "k_proj", "v_proj", "o_proj", # Attention
"gate_proj", "up_proj", "down_proj", # ★ Expert FFN ★
],
use_gradient_checkpointing = "unsloth",
random_state = 3407,
)
# ③ データロード (JSONL → chat template)
def fmt(sample):
return {"text": tok.apply_chat_template(sample["messages"], tokenize=False)}
ds = load_dataset("json", data_files="my_data.jsonl", split="train").map(fmt)
# ④ 学習設定
trainer = SFTTrainer(
model = model,
tokenizer = tok,
train_dataset = ds,
dataset_text_field = "text",
max_seq_length = 2048,
args = SFTConfig(
output_dir = "./olmoe-lora-out",
per_device_train_batch_size = 2,
gradient_accumulation_steps = 4,
num_train_epochs = 3,
learning_rate = 2e-4,
warmup_steps = 10,
logging_steps = 5,
save_steps = 100,
bf16 = True,
optim = "adamw_8bit",
),
)
# ⑤ 実行!
trainer.train()
model.save_pretrained("./olmoe-lora-out") ⑤ LoRA マージ & GGUF 変換
# ① LoRA をベースにマージ
python -c "
from unsloth import FastLanguageModel
model, tok = FastLanguageModel.from_pretrained('./olmoe-lora-out', load_in_4bit=False)
model.save_pretrained_merged('./olmoe-merged', tok, save_method='merged_16bit')
"
# ② GGUF 変換 (llama.cpp の convert スクリプト)
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp && make LLAMA_CUDA=1
python convert_hf_to_gguf.py ../olmoe-merged \
--outfile olmoe-mytuned.gguf \
--outtype f16
# ③ Q4_K_M 量子化 (4.2 GB に圧縮)
./llama-quantize olmoe-mytuned.gguf olmoe-mytuned-Q4_K_M.gguf Q4_K_M
# ④ Ollama 登録
ollama create my-olmoe -f Modelfile
ollama run my-olmoe "自己紹介して" 💎 MoE 特有のチューニング Tips
Router は凍結: ffn_gate_inp を LoRA 対象に入れると expert 選択が崩れやすい。触らないのが安全。
全 Expert に同じ LoRA: Unsloth は 64 個の expert に対して共通の LoRA を適用するので、ファイル数が爆発しない。
rank 16 が sweet spot: 8 だと表現力不足、32 以上は過学習リスク。16 から試す。
epoch 3 まで: それ以上は過学習で style が崩れる。validation loss をモニターして early stop。
🚨 よくあるエラーと対処
| エラー | 原因 | 対処 |
|---|---|---|
| CUDA out of memory | VRAM 不足 | per_device_train_batch_size=1、max_seq_length=1024 |
| loss が nan | 学習率が高すぎ | learning_rate=5e-5 に下げる |
| loss が下がらない | target_modules 不足 | Expert (gate_proj / up_proj / down_proj) を追加 |
| GGUF 変換失敗 | OLMoE 未対応の古い llama.cpp | llama.cpp を最新版に (git pull) |
🔬 Expert 作成・強化 — MoE 特有の深い改造 (上級)
MoE 特有の改造術。単なる LoRA を超えて、Expert そのものを操作する方法。 Expert を新規追加・特定 expert だけ強化・不要 expert を剪定。フルオープンの OLMoE だからこそ可能。
🧬 Expert 1 個の中身 (SwiGLU FFN)
Expert i の forward (入力 x [2048] → 出力 [2048])
x [2048]
│
├──→ gate_proj[i] ──→ gate [1024]
│ (重み [2048→1024])
│ │
├──→ up_proj[i] ──→ up [1024]
│ (重み [2048→1024]) │
│ ▼
│ silu(gate) * up ── hidden [1024]
│ │
│ down_proj[i] │
│ (重み [1024→2048]) ▼
└───────────────────── output [2048]
# OLMoE の場合: Expert 1 個 = 3 × 2048 × 1024 = 6.3M パラ
# × 64 expert × 16 層 = 6.5 B パラ (全体の 90%) 🔧 改造① 特定 Expert だけ強化する
「数学が苦手なモデル」を直したい → 数学で発火する expert を特定して LoRA をかぶせる。
# Step 1: どの expert が何で発火してるか調べる
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
model = AutoModelForCausalLM.from_pretrained("allenai/OLMoE-1B-7B-0125-Instruct",
output_router_logits=True)
tok = AutoTokenizer.from_pretrained("allenai/OLMoE-1B-7B-0125-Instruct")
prompts = ["2+2=?", "Solve x^2=4", "derivative of sin(x)"]
expert_count = torch.zeros(16, 64) # 16 層 × 64 expert
for p in prompts:
inputs = tok(p, return_tensors="pt")
with torch.no_grad():
out = model(**inputs)
for layer, logits in enumerate(out.router_logits):
topk_idx = logits.topk(8, dim=-1).indices
for e in topk_idx.flatten():
expert_count[layer, e] += 1
top_math_experts = expert_count.view(-1).topk(20).indices
print("数学エキスパート候補:", [(i//64, i%64) for i in top_math_experts]) # Step 2: その expert だけに LoRA
from peft import LoraConfig, get_peft_model
cfg = LoraConfig(
r=32,
target_modules=["gate_proj","up_proj","down_proj"],
layers_to_transform=[8,9,10], # 数学で発火した層だけ
task_type="CAUSAL_LM",
)
model = get_peft_model(model, cfg)
# → 数学データで SFT 実行 🧪 改造② 新しい Expert を追加する (上級)
OLMoE は 64 expert。これを 80 にしたい → Router と Expert テンソルをコード側で拡張。
import torch
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained("allenai/OLMoE-1B-7B-0125-Instruct")
config = model.config
NEW_N = 80 # 64 → 80 に増やす
for i, layer in enumerate(model.model.layers):
mlp = layer.mlp # OlmoeSparseMoeBlock
# ① Router (ffn_gate_inp) 拡張 [2048 × 64] → [2048 × 80]
old_w = mlp.gate.weight.data # [64, 2048]
new_w = torch.zeros(NEW_N, old_w.size(1))
new_w[:64] = old_w
new_w[64:] = old_w[:16].clone() # 新 16 個は既存の複製
mlp.gate = torch.nn.Linear(2048, NEW_N, bias=False)
mlp.gate.weight.data = new_w
# ② Expert (ModuleList) を 16 個追加
for j in range(16):
src = mlp.experts[j % 64]
import copy
mlp.experts.append(copy.deepcopy(src))
mlp.num_experts = NEW_N
config.num_experts = NEW_N
model.save_pretrained("./olmoe-80experts")
# ★ 続いて SFT で新 expert を分化させる学習が必要 ✂ 改造③ 使われていない Expert を削除 (モデル圧縮)
# ② 各層で下位 16 個を削除 (64 → 48 expert)
for layer_idx, layer in enumerate(model.model.layers):
mlp = layer.mlp
usage = expert_count[layer_idx] # [64]
keep_idx = usage.topk(48).indices # 上位 48 残す
mlp.experts = torch.nn.ModuleList([mlp.experts[i] for i in keep_idx])
mlp.gate.weight.data = mlp.gate.weight.data[keep_idx]
mlp.num_experts = 48
# ③ 再微調整 (剪定の穴埋め)
# → SFT を軽く回す (2000 サンプル程度)
# 結果: 7B → 5.4B (25% 削減)、品質はほぼ維持 💎 Expert 改造のベストプラクティス
| やること | 理由 |
|---|---|
| Router は基本凍結 | LoRA で触ると expert 選択が崩壊する |
| Expert LoRA は全 64 に適用 | 特定 expert だけ触ると他が相対的に劣化 |
| Aux loss は 0.01〜0.02 | 大きいと本タスクが犠牲、小さいと偏る |
| Expert 追加は複製から | ゼロ初期化は学習が発散する |
| 剪定は必ず再学習付き | 穴埋めしないと精度劇落ち |
🔬 Expert 改造の意義
- LoRA ではできない、構造的な改造が可能
- MoE 研究の最先端は OLMoE のフルオープン性があって初めて触れる
- 学術論文級の実験ができる個人 PC 環境 (他の MoE はクローズド)
初心者はまず「LoRA 学習チュートリアル」から始めて、慣れたらこのページの改造を 1 つずつ試すこと。