金融、医療、法務、社内ナレッジベースなどの専門領域において、クラウド上の商用APIを利用する際は、データ主権、規制コンプライアンス、機密保持が厳格な障壁となります。この課題を解決するため、オープンウェイト基底モデルをオンプレミスやプライベートクラウド環境で独自データに適合させるドメイン特化型ファインチューニングが、NLPエンジニアリングにおける標準手法となっています。Baichuan Intelligent(百川智能)のBaichuanシリーズ(Baichuan-7B/13BおよびBaichuan2-7B/13BのBase/Chat)は、2.6兆トークンに及ぶ高品質な中英バイリンガル事前学習データ、125,696トークンの大規模語彙、RoPE/ALiBiの使い分け、出力投影層の正規化(NormHead)を備えており、高度な専門領域タスクにおいて高い評価を得ています。
Hugging Faceのパラメータ効率的ファインチューニングライブラリpeft(Parameter-Efficient Fine-Tuning)とLoRA(Low-Rank Adaptation)を組み合わせることで、モデル全体の1%未満のパラメータのみを更新し、24GBのコンシューマ向けGPU(4-bit QLoRA使用時)または単一の80GB A100/H100(bfloat16全精度)上で、効率的にドメイン知識をモデルへ注入・整合させることが可能です。
1. Baichuanのアーキテクチャ特性と設計上の差異
ファインチューニングパイプラインを構築するにあたり、NLPエンジニアは標準的なLLaMAアーキテクチャと異なる以下の要点を把握しておく必要があります。
- 大規模BPE語彙(125,696トークン):Baichuanは125,696規模の大規模BPEボキャブラリを採用しています。これにより専門用語や漢字表現を高密度に圧縮でき、標準的なLLaMAベースのトークナイザと比較してトークン系列長が15〜20%短縮され、訓練時のGPUメモリ消費を削減します。
- 位置エンコーディング(7Bと13Bの違い):
- 7Bモデル:4096コンテキスト長をサポートする標準的な回転位置埋め込み(RoPE: Rotary Position Embedding)を採用。
- 13Bモデル:線形バイアス付きアテンション(ALiBi: Attention with Linear Biases)を実装。ALiBiはクエリとキーに直接位置ベクトルを足し合わせるのではなく、アテンション行列に距離に応じたペナルティを課すため、契約書や電子カルテなどの長大文書に対するコンテキスト外挿性に優れています。
- 出力層の重み正規化(
NormHead):Baichuan 2では、出力投影層(lm_head)の重みを正規化するNormHead技術が導入されています。これにより低頻度語の埋め込みノルム変動がロジット計算に及ぼす影響を抑え、コサイン類似度に基づく安定したセマンティック表現を実現しています。LoRA適用時は、事前学習済みの正規化分布を崩さないよう、原則としてlm_headを訓練対象モジュールに含めないことが推奨されます。 - カスタムコードの実行許可:独自のモデリングロジックとトークナイザが含まれているため、Hugging Face Transformersからロードする際は
trust_remote_code=Trueの指定が必須です。
2. ドメインデータの準備と特殊制御トークンの仕様
企業内の独自データセットは、問い合わせ対応履歴、専門QA、マニュアル、業務要約などの形式で構成されます。指示チューニング(SFT)において、Baichuanモデルは対話ターンを区切るための固有の制御トークンを必要とします。
<reserved_106>(Token ID:195):ユーザー(Human)のプロンプト開始を示すトークン。<reserved_107>(Token ID:196):アシスタント(Assistant)の回答開始を示すトークン。
データセット形式(ShareGPT / 会話スキーマ)
データはJSONまたはJSON Lines(.jsonl)形式で、以下のようにターン構造を明示して整理します。
[
{
"conversations": [
{
"from": "human",
"value": "当社のクロスボーダー資金移動規程において、5万米ドル相当を超える送金に必要な社内承認フローを教えてください。"
},
{
"from": "assistant",
"value": "社内資金管理規程第4条第2項に基づき、5万米ドルを超える海外送金には二名以上の管理職による承認捺印が必要であり、送金実行前に自動AMLスクリーニング完了証および外為申告書の電子登録を完了する必要があります。"
}
]
}
]
プロンプト損失マスキング(Loss Masking)
教師ありファインチューニングにおいて、プロンプトの暗記による過学習を防ぐため、プロンプト部分のトークンをマスク処理することが重要です。<reserved_106>から<reserved_107>までのプロンプト領域のトークンに対して、ラベル(Labels)を-100に設定します。これにより、交差エントロピー損失の逆伝播は、<reserved_107>以降にアシスタントが生成した回答部分と終了トークン(</s>または<reserved_107>)のみに限定されます。
3. Baichuan向けLoRA設定戦略とハイパーパラメータ
LoRAは、重み更新量 $\Delta W$ を低ランク行列の積に分解します:$W = W_0 + \frac{\alpha}{r}(B \cdot A)$。
適用対象モジュール(target_modules)の選定
初期のLoRA実装ではアテンション層のq_projとv_projのみが対象とされていましたが、専門領域の論理や語彙を定着させるためには、すべての線形射影層(All Linear Layers)を対象とすることが実務上の標準となっています。
- アテンション層:
q_proj、k_proj、v_proj、o_proj - MLP(フィードフォワード)層:
gate_proj、up_proj、down_proj
最新のpeftライブラリでは、target_modules="all-linear"と指定することで、Baichuanモデル内のすべての線形モジュールを自動的に抽出できます。
推奨ハイパーパラメータ
| パラメータ名 | 推奨値 | 設定根拠・工学的判断 |
|---|---|---|
| LoRAランク($r$) | 16 または 32 | チェックポイント容量(30MB〜120MB程度)を抑えつつ、専門ドメインの適応能力を十分に確保。 |
| LoRAアルファ($\alpha$) | 32 または 64 | 安定したスケーリングのため、通常 $\alpha = 2 \times r$ を設定。 |
| LoRAドロップアウト | 0.05 〜 0.10 | サンプル数が5,000〜50,000件規模の独自データにおける過学習を抑制。 |
| 演算精度 | bfloat16 | Ampere/Hopper GPUにおいて、BaichuanのNormHeadでのアンダーフローを防ぐためFP16よりBF16を推奨。 |
| 学習率 | 1e-4 〜 2e-4 | LoRAは学習対象パラメータが限られるため、フルファインチューニング(1e-5)より高めに設定。 |
4. エンドツーエンドのファインチューニング実装コード
以下は、Hugging Faceのtransformersとpeftを用い、4-bit量子化(QLoRA)を組み込んだ実践的な学習スクリプトです。
import torch
from datasets import load_dataset
from transformers import (
AutoTokenizer,
AutoModelForCausalLM,
BitsAndBytesConfig,
TrainingArguments,
Trainer,
DataCollatorForSeq2Seq
)
from peft import (
LoraConfig,
get_peft_model,
prepare_model_for_kbit_training,
TaskType
)
model_id = "baichuan-inc/Baichuan2-7B-Base"
# 1. トークナイザの読み込み
tokenizer = AutoTokenizer.from_pretrained(
model_id,
trust_remote_code=True,
use_fast=False
)
if tokenizer.pad_token_id is None:
tokenizer.pad_token_id = tokenizer.eos_token_id
# 2. 4-bit 量子化設定(QLoRA)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True
)
# 3. 基底モデルのロード
model = AutoModelForCausalLM.from_pretrained(
model_id,
quantization_config=bnb_config,
device_map="auto",
trust_remote_code=True,
torch_dtype=torch.bfloat16
)
# 4. 量子化訓練向けの準備
model = prepare_model_for_kbit_training(model)
# 5. LoRA設定の定義
peft_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj"
]
)
model = get_peft_model(model, peft_config)
model.print_trainable_parameters()
# 6. データ前処理とプロンプト損失マスキング
USER_TOKEN_ID = 195 # <reserved_106>
ASSISTANT_TOKEN_ID = 196 # <reserved_107>
def preprocess_function(example):
input_ids = []
labels = []
for message in example["conversations"]:
if message["from"] == "human":
prompt_tokens = [USER_TOKEN_ID] + tokenizer.encode(message["value"], add_special_tokens=False)
input_ids.extend(prompt_tokens)
labels.extend([-100] * len(prompt_tokens))
elif message["from"] == "assistant":
resp_tokens = [ASSISTANT_TOKEN_ID] + tokenizer.encode(message["value"], add_special_tokens=False) + [tokenizer.eos_token_id]
input_ids.extend(resp_tokens)
labels.extend(resp_tokens)
return {"input_ids": input_ids[:2048], "labels": labels[:2048]}
dataset = load_dataset("json", data_files="domain_data.jsonl")
tokenized_dataset = dataset["train"].map(preprocess_function, remove_columns=["conversations"])
# 7. 学習パラメータの設定
training_args = TrainingArguments(
output_dir="./baichuan2-lora-output",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=2e-4,
lr_scheduler_type="cosine",
warmup_ratio=0.05,
logging_steps=10,
num_train_epochs=3,
gradient_checkpointing=True,
bf16=True,
save_strategy="epoch",
report_to="none"
)
# 8. トレーナーの構築と学習実行
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
data_collator=DataCollatorForSeq2Seq(tokenizer, pad_to_multiple_of=8, return_tensors="pt")
)
trainer.train()
# 9. LoRAアダプタの保存
model.save_pretrained("./baichuan2-domain-lora-adapter")
tokenizer.save_pretrained("./baichuan2-domain-lora-adapter")
5. 重みのマージと本番推論環境への展開
本番サービスにおいて動的にLoRAアダプタを重ねて推論を行うと、追加の計算グラフが発生し、vLLMやLMDeployなどの高速推論エンジンのカーネル融合最適化を活用できません。標準的なデプロイ手順では、学習完了後にLoRAアダプタをベース重みに統合(マージ)します。
オフライン重み統合スクリプト(Merge and Unload)
注意事項:4-bit量子化されたモデルオブジェクトに対して直接マージを実行してはいけません。ホストメモリまたは十分なVRAMを持つ環境で、非量子化の
bfloat16基底モデルをロードした上でマージを実行してください。
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
base_model_path = "baichuan-inc/Baichuan2-7B-Base"
adapter_path = "./baichuan2-domain-lora-adapter"
export_path = "./baichuan2-7b-domain-merged"
tokenizer = AutoTokenizer.from_pretrained(base_model_path, trust_remote_code=True)
# 非量子化精度で基底モデルをロード
base_model = AutoModelForCausalLM.from_pretrained(
base_model_path,
torch_dtype=torch.bfloat16,
device_map="cpu", # VRAMに余裕がある場合は "cuda"
trust_remote_code=True
)
peft_model = PeftModel.from_pretrained(base_model, adapter_path)
merged_model = peft_model.merge_and_unload()
# 標準的なHugging Faceフォーマットで保存
merged_model.save_pretrained(export_path, safe_serialization=True)
tokenizer.save_pretrained(export_path)
統合されたモデルは、vLLMを使用して即座に高スループット推論サーバーとして起動できます。
vllm serve ./baichuan2-7b-domain-merged --trust-remote-code
6. エンジニアリング上の選定基準と運用指針
- BaseモデルとChatモデルの選定:
- 固定フォーマットのJSON抽出、SQL生成、情報分類などの厳格なタスクには、不要な対話文脈や安全フィルタの干渉を避けるため**
Baichuan2-Base**の利用を推奨します。 - 一般対話、社内FAQ対応、ロールプレイ型アシスタントなどの用途には、対話の自然さと安全基準があらかじめ整合されている**
Baichuan2-Chat**をベースとして選択します。
- 固定フォーマットのJSON抽出、SQL生成、情報分類などの厳格なタスクには、不要な対話文脈や安全フィルタの干渉を避けるため**
- モデル規模の選定(7B vs. 13B):
- 4,000トークンを超える長文コンテキストの読解が必要な場合は、ALiBiを採用している13Bモデルが適しており、文脈外挿性に優れます。
- レイテンシ短縮や高同時実行性が要求される一般的な社内検索・応答タスクには、消費メモリが少なく軽量な7Bモデル(RoPE)が最適です。
- 破滅的忘却の防止:
- 専門データのみで学習を続けると基礎的な言語理解能力が劣化するリスクがあるため、5〜10%程度の汎用的な指示・推論データを訓練セットに混合することが有効です。
参考文献
- GitHub - baichuan-inc/Baichuan2: A series of large language models developed by Baichuan Intelligent Technology
- GitHub - huggingface/peft: 🤗 PEFT: State-of-the-art Parameter-Efficient Fine-Tuning.
- [2309.10305] Baichuan 2: Open Large-scale Language Models
- [2106.09685] LoRA: Low-Rank Adaptation of Large Language Models