Skip to content
Published on

RLHFからDPOまで:LLMアライメント技術の論文深層分析

Authors
  • Name
    Twitter
LLM Alignment Survey

はじめに

大規模言語モデル(LLM)は膨大なコーパスから驚異的な言語能力を獲得するが、事前学習だけでは人間の意図や価値観に沿った出力を保証できない。有害なコンテンツの生成、ユーザー指示の無視、事実でない情報の確信的な記述など、問題が頻繁に発生する。この乖離を解消するための研究分野が**LLMアライメント(Alignment)**である。

2022年にOpenAIのInstructGPT論文がRLHF(Reinforcement Learning from Human Feedback)パイプラインを体系化して以来、AnthropicのConstitutional AI、StanfordのDPO(Direct Preference Optimization)、そしてKTO、IPO、ORPOなど多様な後続手法が登場した。本記事では、これらの主要論文の数学的基盤、実装方式、実務適用時の注意点を体系的に分析する。

LLMアライメント問題の定義

なぜアライメントが必要か

事前学習されたLLMはインターネットテキストの次トークン予測を最適化する。この目標は、人間が望む「役立つ(Helpful)、正直な(Honest)、無害な(Harmless)」出力とは直接対応しない。具体的に三つの核心課題がある。

  1. 有用性(Helpfulness):ユーザーの指示を正確に理解し実行する能力
  2. 正直性(Honesty):知らないことを認め、幻覚を最小化する能力
  3. 無害性(Harmlessness):有害または偏向したコンテンツの生成を拒否する能力

アライメントの数学的フレームワーク

アライメント問題は報酬関数 r(x, y) に基づく方策最適化問題として形式化される。プロンプト x が与えられた時、方策 pi が生成する応答 y に対する報酬を最大化しつつ、事前学習された参照方策との乖離を制限する。

maxπExD,yπ(x)[r(x,y)]βKL[π(x)πref(x)]\max_{\pi} \mathbb{E}_{x \sim \mathcal{D}, y \sim \pi(\cdot|x)} \left[ r(x, y) \right] - \beta \, \text{KL}\left[\pi(\cdot|x) \| \pi_{\text{ref}}(\cdot|x)\right]

ここで beta はKLダイバージェンスペナルティの強度を調整するハイパーパラメータである。beta が小さすぎると報酬ハッキングが発生し、大きすぎると学習が遅くなる。

RLHFパイプラインの深層分析

InstructGPTの3段階パイプライン

Ouyang et al.(2022)のInstructGPT論文はRLHFパイプラインを3段階に体系化した。

第1段階:教師ありファインチューニング(SFT)

人間のラベラーが作成したデモンストレーションデータで基本モデルをファインチューニングする。この段階でモデルは指示に従う基本能力を学習する。

第2段階:報酬モデル(RM)学習

同じプロンプトに対して複数の応答を生成し、人間のラベラーが選好順位を付ける。このデータで報酬モデルを学習する。

第3段階:PPOによる強化学習

報酬モデルのフィードバックを利用してPPOアルゴリズムで方策を最適化する。

報酬モデル学習の実装

報酬モデルはBradley-Terryモデルに基づき、選好ペア(chosen、rejected)に対するログ確率差を最大化するよう学習される。

import torch
import torch.nn as nn
from transformers import AutoModelForSequenceClassification, AutoTokenizer

class RewardModel(nn.Module):
    def __init__(self, model_name="gpt2"):
        super().__init__()
        self.model = AutoModelForSequenceClassification.from_pretrained(
            model_name, num_labels=1
        )

    def forward(self, input_ids, attention_mask):
        outputs = self.model(input_ids=input_ids, attention_mask=attention_mask)
        return outputs.logits.squeeze(-1)

def reward_model_loss(reward_model, chosen_ids, chosen_mask, rejected_ids, rejected_mask):
    """Bradley-Terry選好モデルに基づく報酬モデル損失関数"""
    r_chosen = reward_model(chosen_ids, chosen_mask)
    r_rejected = reward_model(rejected_ids, rejected_mask)

    # 選好応答の報酬が非選好応答を上回るよう学習
    loss = -torch.log(torch.sigmoid(r_chosen - r_rejected)).mean()
    accuracy = (r_chosen > r_rejected).float().mean()

    return loss, accuracy

選好データセットの構築

RLHFパイプラインの核心は高品質な選好データセットの構築である。各サンプルはプロンプト、選好応答、非選好応答の三つ組で構成される。

from datasets import Dataset

def build_preference_dataset(prompts, model, tokenizer, num_samples=4):
    """プロンプトごとに複数応答を生成し選好ペアを構築するパイプライン"""
    preference_data = []

    for prompt in prompts:
        inputs = tokenizer(prompt, return_tensors="pt")
        responses = []

        # 温度サンプリングで多様な応答を生成
        for _ in range(num_samples):
            output = model.generate(
                **inputs,
                max_new_tokens=256,
                temperature=0.8,
                do_sample=True,
            )
            response = tokenizer.decode(output[0], skip_special_tokens=True)
            responses.append(response)

        # 本番パイプラインでは人間ラベラーが順位付け
        # ここでは報酬モデルスコアで代替
        scored = [(r, reward_model_score(prompt, r)) for r in responses]
        scored.sort(key=lambda x: x[1], reverse=True)

        # 最上位・最下位ペアを選好データとして構築
        preference_data.append({
            "prompt": prompt,
            "chosen": scored[0][0],
            "rejected": scored[-1][0],
        })

    return Dataset.from_list(preference_data)

Constitutional AI:AIフィードバックによるアライメント

Anthropicのアプローチ

Bai et al.(2022)のConstitutional AIは、RLHFの人間ラベリング依存を軽減するための革新的アプローチである。核心アイデアは、AI自らが自身の出力を批判し改善するというもので、事前定義された原則(Constitution)によってガイドされる。

2段階プロセス

教師あり学習段階(自己批判と修正)

  1. モデルが有害な可能性のある応答を生成する
  2. 憲法原則に基づいて自身の応答を批判する
  3. 批判を反映した修正応答を生成する
  4. 修正応答でSFTを実施する

RL段階(RLAIF - RL from AI Feedback)

  1. 二つの応答のどちらが原則により適合するかをAIが判断する
  2. このAI選好データで報酬モデルを学習する
  3. 報酬モデルを利用してPPOで方策を最適化する

Constitutional AIの最大の利点は、人間ラベリングコストを大幅に削減しながらも、原則に基づく透明で解釈可能なアライメントを達成することである。

DPO:数学的基盤と実装

RLHF目的関数の解析解

Rafailov et al.(2023)のDPOはRLHFの最もエレガントな代替手法である。核心的洞察は、KL制約付き報酬最大化問題に解析解が存在するということだ。

最適方策は次の形式をとる。

π(yx)=1Z(x)πref(yx)exp(1βr(x,y))\pi^*(y|x) = \frac{1}{Z(x)} \pi_{\text{ref}}(y|x) \exp\left(\frac{1}{\beta} r(x, y)\right)

これを逆に解くと、報酬関数を方策の比率で表現できる。

r(x,y)=βlogπ(yx)πref(yx)+βlogZ(x)r(x, y) = \beta \log \frac{\pi^*(y|x)}{\pi_{\text{ref}}(y|x)} + \beta \log Z(x)

これをBradley-Terryモデルに代入すると、報酬モデルなしで直接方策を最適化するDPO損失関数が導出される。

LDPO(πθ;πref)=E(x,yw,yl)[logσ(βlogπθ(ywx)πref(ywx)βlogπθ(ylx)πref(ylx))]\mathcal{L}_{\text{DPO}}(\pi_\theta; \pi_{\text{ref}}) = -\mathbb{E}_{(x, y_w, y_l)} \left[\log \sigma\left(\beta \log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \beta \log \frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)}\right)\right]

DPO学習ループの実装

import torch
import torch.nn.functional as F
from transformers import AutoModelForCausalLM, AutoTokenizer

def compute_dpo_loss(
    policy_model,
    reference_model,
    chosen_ids,
    chosen_mask,
    rejected_ids,
    rejected_mask,
    beta=0.1,
):
    """DPO損失関数:報酬モデルなしで直接方策を最適化"""
    # 方策モデルのログ確率計算
    policy_chosen_logps = get_log_probs(policy_model, chosen_ids, chosen_mask)
    policy_rejected_logps = get_log_probs(policy_model, rejected_ids, rejected_mask)

    # 参照モデルのログ確率計算(勾配不要)
    with torch.no_grad():
        ref_chosen_logps = get_log_probs(reference_model, chosen_ids, chosen_mask)
        ref_rejected_logps = get_log_probs(reference_model, rejected_ids, rejected_mask)

    # DPOログ比率の計算
    chosen_logratios = policy_chosen_logps - ref_chosen_logps
    rejected_logratios = policy_rejected_logps - ref_rejected_logps

    # DPO損失:-log(sigmoid(beta * (chosen_logratio - rejected_logratio)))
    logits = beta * (chosen_logratios - rejected_logratios)
    loss = -F.logsigmoid(logits).mean()

    # メトリクス:選好応答の暗黙的報酬が高い割合
    reward_accuracy = (logits > 0).float().mean()

    return loss, reward_accuracy

def get_log_probs(model, input_ids, attention_mask):
    """シーケンスのトークン別ログ確率を合算"""
    outputs = model(input_ids=input_ids, attention_mask=attention_mask)
    logits = outputs.logits[:, :-1, :]
    labels = input_ids[:, 1:]
    log_probs = F.log_softmax(logits, dim=-1)
    selected_log_probs = torch.gather(
        log_probs, dim=-1, index=labels.unsqueeze(-1)
    ).squeeze(-1)
    mask = attention_mask[:, 1:]
    return (selected_log_probs * mask).sum(dim=-1)

DPOの長所と短所

長所

  • 報酬モデルの学習が不要でパイプラインが簡素化
  • PPO比でメモリ使用量が少ない(2モデル vs 4モデル)
  • ハイパーパラメータ調整が比較的容易

短所

  • 参照モデルとの分布シフト(distribution shift)に脆弱
  • オフラインデータに依存し探索(exploration)が不足
  • 選好データの品質に非常に敏感

PPO学習の安定性

RLHF におけるPPO:実装の核心

PPO(Proximal Policy Optimization)はRLHFで最も広く使用される強化学習アルゴリズムである。核心はクリッピングメカニズムで、方策更新の大きさを制限し学習の安定性を保証する。

import torch
import torch.nn.functional as F

def ppo_step(
    policy_model,
    value_model,
    old_log_probs,
    states,
    actions,
    rewards,
    advantages,
    clip_epsilon=0.2,
    value_clip=0.2,
    kl_coeff=0.1,
    ref_log_probs=None,
):
    """PPO更新ステップ(LLM RLHF用)"""
    # 現在の方策のログ確率
    new_log_probs = policy_model.get_log_probs(states, actions)

    # 重要度比の計算
    ratio = torch.exp(new_log_probs - old_log_probs)

    # クリッピングされた代理目的関数
    surr1 = ratio * advantages
    surr2 = torch.clamp(ratio, 1.0 - clip_epsilon, 1.0 + clip_epsilon) * advantages
    policy_loss = -torch.min(surr1, surr2).mean()

    # 価値関数損失(クリッピング適用)
    new_values = value_model(states)
    value_loss = F.mse_loss(new_values, rewards)

    # KLダイバージェンスペナルティ(参照モデル対比)
    kl_penalty = 0.0
    if ref_log_probs is not None:
        kl_div = (old_log_probs - ref_log_probs).mean()
        kl_penalty = kl_coeff * kl_div

    total_loss = policy_loss + 0.5 * value_loss + kl_penalty

    return total_loss, {
        "policy_loss": policy_loss.item(),
        "value_loss": value_loss.item(),
        "kl_penalty": kl_penalty if isinstance(kl_penalty, float) else kl_penalty.item(),
        "mean_ratio": ratio.mean().item(),
    }

PPO学習の主な不安定性要因

  1. 報酬ハッキング(Reward Hacking):報酬モデルの弱点を悪用し、実際には質の低い出力に高い報酬を得る現象
  2. KLダイバージェンス爆発:参照モデルとの距離が急激に増加し、意味のないテキストを生成
  3. 価値関数推定エラー:異常に大きな報酬推定値が学習を不安定にする
  4. 長いシーケンスの問題:トークン数が多いほどクレジット割り当て(credit assignment)問題が深刻化

最新アライメント手法:KTO、IPO、ORPO

KTO(Kahneman-Tversky Optimization)

Ethayarajh et al.(2024)のKTOは、ペアワイズ選好データなしでアライメントを実行する。行動経済学のプロスペクト理論を援用し、各応答が「望ましい/望ましくない」の二値信号のみで学習する。

KTOの核心は、人間の意思決定が損失に対してより敏感である**損失回避(Loss Aversion)**特性を反映した点である。望ましくない応答に対するペナルティを、望ましい応答に対する報酬より大きく設定する。

IPO(Identity Preference Optimization)

Azar et al.(2024)のIPOは、DPOの過学習(overfitting)問題を解決するため正則化項を追加する。DPOは学習データに過学習すると、選好応答の確率を極端に高める傾向がある。IPOはこれを防止する。

LIPO=(logπθ(ywx)πref(ywx)logπθ(ylx)πref(ylx)12β)2\mathcal{L}_{\text{IPO}} = \left(\log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \log \frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)} - \frac{1}{2\beta}\right)^2

ORPO(Odds Ratio Preference Optimization)

Hong et al.(2024)のORPOは、SFTと選好最適化を単一段階に統合する。参照モデルが不要でメモリと計算コストを大幅に削減する。核心はオッズ比(Odds Ratio)ベースのペナルティ項である。

import torch
import torch.nn.functional as F

def compute_orpo_loss(model, chosen_ids, chosen_mask, rejected_ids, rejected_mask, alpha=1.0):
    """ORPO損失:SFT + オッズ比ベースの選好最適化統合"""
    # SFT損失(選好応答に対するNLL)
    chosen_logits = model(input_ids=chosen_ids, attention_mask=chosen_mask).logits
    sft_loss = F.cross_entropy(
        chosen_logits[:, :-1, :].reshape(-1, chosen_logits.size(-1)),
        chosen_ids[:, 1:].reshape(-1),
        reduction="mean",
    )

    # ログ確率の計算
    chosen_logps = get_sequence_log_probs(model, chosen_ids, chosen_mask)
    rejected_logps = get_sequence_log_probs(model, rejected_ids, rejected_mask)

    # オッズ比の計算
    chosen_odds = chosen_logps - torch.log1p(-torch.exp(chosen_logps))
    rejected_odds = rejected_logps - torch.log1p(-torch.exp(rejected_logps))
    log_odds_ratio = chosen_odds - rejected_odds

    # ORPO最終損失
    orpo_loss = sft_loss - alpha * F.logsigmoid(log_odds_ratio).mean()

    return orpo_loss

比較分析

各アライメント手法の核心特性を比較すると以下の通りである。

手法データ要件モデル数計算コスト安定性性能
RLHF (PPO)ペアワイズ選好4(方策、参照、報酬、価値)非常に高い低い(チューニング必要)高い(最適チューニング時)
DPOペアワイズ選好2(方策、参照)中程度高い高い
KTO二値フィードバック(非ペアワイズ)2(方策、参照)中程度高い中~高
IPOペアワイズ選好2(方策、参照)中程度非常に高い中~高
ORPOペアワイズ選好1(方策のみ)低い高い中~高

核心的トレードオフ

  • RLHF:最高の性能ポテンシャルを持つが、学習不安定性とエンジニアリング複雑度が高い
  • DPO:実装の簡素さと性能のバランスに優れる。現在最も広く採用されている手法
  • KTO:ペアワイズデータ構築が困難な環境で強力な代替手段
  • IPO:DPOの過学習問題が深刻な小規模データセットに適合
  • ORPO:計算リソースが制限された環境で効率的な選択

実務適用ガイド

手法選択基準

実務でアライメント手法を選択する際は、以下の基準を考慮すべきである。

  1. データ形式:ペアワイズ選好データがあるか?二値フィードバックのみか?
  2. 計算リソース:GPUメモリと学習時間の制約は?
  3. チーム能力:PPOチューニングの経験はあるか?
  4. モデルサイズ:7B以下ならDPO、70B以上ならRLHFを検討

評価メトリクスの実装

アライメント結果を評価するための核心メトリクスは以下の通りである。

import numpy as np
from typing import List, Dict

def compute_alignment_metrics(
    model_responses: List[str],
    reference_responses: List[str],
    reward_model,
    tokenizer,
) -> Dict[str, float]:
    """アライメント品質評価のための総合メトリクス計算"""
    metrics = {}

    # 1. 報酬モデルスコア
    rewards = []
    for response in model_responses:
        tokens = tokenizer(response, return_tensors="pt")
        score = reward_model(**tokens).item()
        rewards.append(score)
    metrics["mean_reward"] = np.mean(rewards)
    metrics["reward_std"] = np.std(rewards)

    # 2. 勝率(win rate) - 参照モデル対比
    wins = 0
    for model_resp, ref_resp in zip(model_responses, reference_responses):
        model_tokens = tokenizer(model_resp, return_tensors="pt")
        ref_tokens = tokenizer(ref_resp, return_tensors="pt")
        model_score = reward_model(**model_tokens).item()
        ref_score = reward_model(**ref_tokens).item()
        if model_score > ref_score:
            wins += 1
    metrics["win_rate"] = wins / len(model_responses)

    # 3. 応答長分布(長さハッキング検出)
    lengths = [len(r.split()) for r in model_responses]
    metrics["mean_length"] = np.mean(lengths)
    metrics["length_std"] = np.std(lengths)

    # 4. 多様性指標(モード崩壊検出)
    unique_bigrams = set()
    total_bigrams = 0
    for response in model_responses:
        words = response.split()
        for i in range(len(words) - 1):
            bigram = (words[i], words[i + 1])
            unique_bigrams.add(bigram)
            total_bigrams += 1
    metrics["distinct_2"] = len(unique_bigrams) / max(total_bigrams, 1)

    return metrics

失敗事例と教訓

1. 報酬ハッキング(Reward Hacking)

RLHFで最も一般的な失敗モードである。報酬モデルが「長い応答 = 良い応答」という偽の相関を学習すると、方策モデルは内容に関係なく過度に長い応答を生成する。InstructGPT論文でもこの問題を報告しており、長さ正規化とKLペナルティ強化で部分的に解決した。

教訓:報酬モデルのバイアスを継続的にモニタリングし、多様なプロキシメトリクスで交差検証すべきである。

2. モード崩壊(Mode Collapse)

PPO学習中に方策が報酬モデルで高スコアを得る少数のパターンのみを繰り返し生成する現象である。多様な質問に対して同一構造の応答を出力したり、特定の表現を過度に繰り返す。

教訓:KLダイバージェンス制約を適切に維持し、応答多様性メトリクスを学習中にモニタリングすべきである。

3. DPOの分布シフト(Distribution Shift)

DPOはオフライン選好データで学習するため、学習が進むにつれ現在の方策が生成する分布と学習データの分布が乖離する。これにより学習後半に性能が低下する可能性がある。

教訓:反復的(iterative)DPOを適用し、各ラウンドで現在の方策から新たな選好データを生成するか、オンラインDPO変種を使用すべきである。

4. Constitutional AIの原則衝突

憲法に含まれる原則同士が衝突するケースが発生する。例えば「役に立て」と「有害な情報を提供するな」という原則が特定の文脈で相反する可能性がある。

教訓:原則間の優先順位を明確に定義し、境界事例(edge cases)に対する別途のガイドラインを整備すべきである。

参考文献

  1. Ouyang, L., et al. (2022). "Training language models to follow instructions with human feedback." NeurIPS 2022. https://arxiv.org/abs/2203.02155

  2. Rafailov, R., et al. (2023). "Direct Preference Optimization: Your Language Model is Secretly a Reward Model." NeurIPS 2023. https://arxiv.org/abs/2305.18290

  3. Bai, Y., et al. (2022). "Constitutional AI: Harmlessness from AI Feedback." Anthropic. https://arxiv.org/abs/2212.08073

  4. Ethayarajh, K., et al. (2024). "KTO: Model Alignment as Prospect Theoretic Optimization." https://arxiv.org/abs/2402.01306

  5. Hong, J., et al. (2024). "ORPO: Monolithic Preference Optimization without Reference Model." https://arxiv.org/abs/2403.07691

  6. Azar, M. G., et al. (2024). "A General Theoretical Paradigm to Understand Learning from Human Feedback." (IPO) https://arxiv.org/abs/2310.12036

  7. Schulman, J., et al. (2017). "Proximal Policy Optimization Algorithms." https://arxiv.org/abs/1707.06347

  8. Hugging Face Blog. "Preference Tuning LLMs with Direct Preference Optimization Methods." https://huggingface.co/blog/pref-tuning