[LLM] LoRA / PEFT(軽量ファインチューニング)

### ファインチューニング(fine-tuning) とは?
すでに学習済みのLLM(例:LLaMA, GPT, Mistralなど)を
自分のデータで「再訓練」して、特定の用途に特化させること。

e.g.
ChatGPTを「医療専門AI」にしたい
LLaMAを「企業マニュアルの質問回答AI」にしたい
Mistralを「社内チャットボット」に最適化したい

### why it need fine tuning
LLMは汎用的なので、例えば次のような弱点があります。

通常のLLM チューニング後
「契約書の条文っぽい文章」を知らない 法律文体で出力できる
「企業内の独自用語」を知らない 社内専門語で答える
指定フォーマットを守らない JSON形式で必ず回答する

### LoRA / PEFTとは?(軽量ファインチューニング)
通常のファインチューニングは、
巨大モデル全体を再学習するため、
GPU・時間・コストが非常に高くつきます(数十GB〜数百GB)。
→ そこで登場したのが LoRA や PEFT。

🔹 LoRA(Low-Rank Adaptation)
LoRAは、「モデル全体ではなく一部の重みだけを微調整」する仕組みです。
📘 ざっくり言うと:
「LLM本体は凍結(変更しない)」
「一部の行列(重み)だけを低次元(rank低い)で学習」

これにより👇
項目 通常ファインチューニング LoRA
学習パラメータ数 数百億 数百万(1〜3%)
必要GPUメモリ 数百GB 数GB
コスト 非常に高い 低コスト
再利用 不可(モデルごと) 可能(LoRA層だけ切り替え可)

元の重み W に、小さな変化 ΔW を足してチューニングする。
ΔW = A × B (A,Bは小さい行列=低ランク)

🔹 PEFT(Parameter-Efficient Fine-Tuning)
LoRAを含む「軽量ファインチューニング技術の総称」です。
PEFTには以下のような手法があります:

手法 概要
LoRA 一部の重みを低ランクで更新
Prefix Tuning プロンプトに「学習したベクトル」を前置
Prompt Tuning 入力部分だけ学習(モデル本体は凍結)
Adapter Tuning モデルの中間層に追加ネットワークを挿入
➡️ Hugging Face の peft ライブラリ はこれらを統一的に扱えます。

from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model

# ベースモデル
model_name = "meta-llama/Llama-2-7b-hf"
model = AutoModelForCausalLM.from_pretrained(model_name)
tokenizer = AutoTokenizer.from_pretrained(model_name)

# LoRA設定
lora_config = LoraConfig(
    r=8,               # rank(低次元のサイズ)
    lora_alpha=16,     # 学習率スケーリング
    target_modules=["q_proj", "v_proj"],  # attention層だけチューニング
    lora_dropout=0.05,
)

# LoRAモデルを作成
model = get_peft_model(model, lora_config)

# 学習用データ(例)
texts = [
    {"input": "会社の理念を説明してください。", "output": "私たちは顧客の信頼を第一とします。"},
]

# トークナイズと学習処理(実際はTrainerを使用)
# model.fit(...)

# 学習済みLoRAを保存(ベースモデルと分離)
model.save_pretrained("my_lora_adapter/")