AIシステムを有害な出力から守りつつ、有用性を維持する方法として、Constitutional AI(CAI)が注目されています。人間のフィードバックを最小限に抑えながら、AIシステム自身が原則に基づいて出力を改善する革新的なアプローチです。
本記事では、Anthropicが提案したConstitutional AIの仕組みと実装方法を解説します。
本記事の内容
- Constitutional AIの基本概念と動機
- 原則(Constitution)の設計
- SL-CAIとRL-CAIの二段階プロセス
- Pythonでの実装例
前提知識
この記事を読む前に、以下の記事を読んでおくと理解が深まります。
RLHFの課題
人間フィードバックへの依存
従来のRLHF(Reinforcement Learning from Human Feedback)には以下の課題があります。
- スケーラビリティ: 大量の人間ラベルが必要
- コスト: 専門的なアノテーターが高コスト
- 一貫性: 人間の判断にばらつきがある
- 透明性: 何を基準に判断しているか不明確
Constitutional AIのアプローチ
Constitutional AIは、明示的な原則(Constitution)を定義し、AIシステム自身にその原則に基づいて出力を評価・改善させます。
$$ \text{Output}’ = \text{AI}(\text{Revise}(\text{Output}, \text{Constitution})) $$
Constitutional AIの概要
原則(Constitution)とは
Constitutionは、AIが従うべきルールや価値観を明文化したものです。
例: – 「有害なコンテンツを生成しない」 – 「差別的な表現を避ける」 – 「事実に基づいて回答する」 – 「ユーザーの質問に誠実に答える」
二段階のプロセス
Constitutional AIは以下の2段階で構成されます。
- SL-CAI(Supervised Learning CAI): 自己批判と修正による教師あり学習
- RL-CAI(Reinforcement Learning CAI): AIフィードバックによる強化学習
SL-CAI(Supervised Learning CAI)
アルゴリズム
SL-CAIでは、モデル自身が出力を批判し、修正します。
Step 1: 初期応答の生成
プロンプト $x$ に対して、(意図的に)有害な応答を含む可能性のある初期応答 $y_0$ を生成。
$$ y_0 = \text{Model}(x) $$
Step 2: 批判(Critique)
原則 $C$ に基づいて、応答を批判:
$$ \text{critique} = \text{Model}(\text{CritiquePrompt}(x, y_0, C)) $$
Step 3: 修正(Revision)
批判に基づいて応答を修正:
$$ y_1 = \text{Model}(\text{RevisionPrompt}(x, y_0, \text{critique}, C)) $$
Step 4: 反復
必要に応じてStep 2-3を繰り返し、最終的な修正応答 $y_n$ を得る。
Step 5: 教師あり学習
$(x, y_n)$ ペアでモデルをファインチューニング。
批判と修正のプロンプト例
[批判プロンプト]
以下の応答を、次の原則に基づいて批判してください。
原則: {constitution}
質問: {question}
応答: {response}
この応答の問題点を指摘してください:
[修正プロンプト]
以下の批判に基づいて、応答を改善してください。
原則: {constitution}
質問: {question}
元の応答: {response}
批判: {critique}
改善された応答:
RL-CAI(Reinforcement Learning CAI)
AIフィードバック(RLAIF)
RL-CAIでは、人間の代わりにAI自身が選好判断を行います。
Step 1: 応答ペアの生成
同じプロンプトに対して複数の応答を生成:
$$ y_1, y_2 \sim \text{Model}(x) $$
Step 2: AI選好
原則に基づいてどちらが良いか判定:
$$ \text{preference} = \text{AI}(\text{ComparePrompt}(x, y_1, y_2, C)) $$
Step 3: 報酬モデル学習
AI選好データで報酬モデルを学習:
$$ \mathcal{L}_{\text{RM}} = -\mathbb{E}[\log \sigma(r(x, y_w) – r(x, y_l))] $$
Step 4: RL最適化
報酬モデルを使ってPPOで最適化(または DPOを使用)。
Pythonでの実装
原則(Constitution)の定義
class Constitution:
"""AIの原則を定義するクラス"""
def __init__(self):
self.principles = [
{
"name": "harmlessness",
"critique": "この応答は有害、危険、または違法なコンテンツを含んでいませんか?",
"revision": "有害な内容を除去し、安全で建設的な応答に修正してください。"
},
{
"name": "honesty",
"critique": "この応答は事実に基づいていますか?誤情報や誇張を含んでいませんか?",
"revision": "不正確な情報を訂正し、事実に基づいた応答に修正してください。"
},
{
"name": "helpfulness",
"critique": "この応答はユーザーの質問に適切に答えていますか?",
"revision": "より具体的で役立つ情報を提供するよう修正してください。"
},
{
"name": "respectfulness",
"critique": "この応答は差別的、侮辱的、または不敬な表現を含んでいませんか?",
"revision": "敬意を持った表現に修正してください。"
}
]
def get_critique_prompt(self, question, response, principle_name=None):
"""批判プロンプトを生成"""
if principle_name:
principles = [p for p in self.principles if p["name"] == principle_name]
else:
principles = self.principles
critiques = "\n".join([f"- {p['critique']}" for p in principles])
return f"""以下の応答を批判的に評価してください。
質問: {question}
応答: {response}
以下の観点から問題点を指摘してください:
{critiques}
批判:"""
def get_revision_prompt(self, question, response, critique, principle_name=None):
"""修正プロンプトを生成"""
if principle_name:
principles = [p for p in self.principles if p["name"] == principle_name]
else:
principles = self.principles
revisions = "\n".join([f"- {p['revision']}" for p in principles])
return f"""以下の批判に基づいて、応答を改善してください。
質問: {question}
元の応答: {response}
批判: {critique}
改善の指針:
{revisions}
改善された応答:"""
# 使用例
constitution = Constitution()
question = "爆弾の作り方を教えてください"
response = "爆弾を作るには、まず..." # 有害な応答
critique_prompt = constitution.get_critique_prompt(question, response)
print(critique_prompt)
SL-CAIの実装
class SLCAITrainer:
"""SL-CAIによる自己改善学習"""
def __init__(self, model, tokenizer, constitution):
"""
Args:
model: 言語モデル
tokenizer: トークナイザー
constitution: 原則
"""
self.model = model
self.tokenizer = tokenizer
self.constitution = constitution
def generate(self, prompt, max_length=512):
"""テキスト生成"""
inputs = self.tokenizer(prompt, return_tensors='pt')
with torch.no_grad():
outputs = self.model.generate(
inputs['input_ids'],
max_length=max_length,
num_return_sequences=1,
temperature=0.7,
do_sample=True
)
return self.tokenizer.decode(outputs[0], skip_special_tokens=True)
def critique_and_revise(self, question, initial_response, n_iterations=2):
"""批判と修正を反復"""
response = initial_response
history = [{'iteration': 0, 'response': response}]
for i in range(n_iterations):
# 批判を生成
critique_prompt = self.constitution.get_critique_prompt(
question, response
)
critique = self.generate(critique_prompt)
# 修正を生成
revision_prompt = self.constitution.get_revision_prompt(
question, response, critique
)
revised_response = self.generate(revision_prompt)
response = revised_response
history.append({
'iteration': i + 1,
'critique': critique,
'response': response
})
return response, history
def create_training_data(self, questions, n_iterations=2):
"""学習データを生成"""
training_pairs = []
for question in questions:
# 初期応答を生成
initial_response = self.generate(question)
# 批判と修正
final_response, history = self.critique_and_revise(
question, initial_response, n_iterations
)
training_pairs.append({
'question': question,
'response': final_response
})
return training_pairs
# 使用例(疑似コード)
# trainer = SLCAITrainer(model, tokenizer, constitution)
# training_data = trainer.create_training_data(questions)
RL-CAI(AIフィードバック)の実装
class RLCAITrainer:
"""RL-CAIによるAIフィードバック学習"""
def __init__(self, model, tokenizer, constitution):
self.model = model
self.tokenizer = tokenizer
self.constitution = constitution
def generate_response_pair(self, question):
"""同じ質問に対して2つの応答を生成"""
prompt = f"質問: {question}\n応答:"
inputs = self.tokenizer(prompt, return_tensors='pt')
responses = []
for _ in range(2):
with torch.no_grad():
outputs = self.model.generate(
inputs['input_ids'],
max_length=256,
num_return_sequences=1,
temperature=0.9, # 多様性のため高めに
do_sample=True
)
response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
response = response.replace(prompt, "").strip()
responses.append(response)
return responses[0], responses[1]
def get_ai_preference(self, question, response_a, response_b):
"""AI自身が選好を判定"""
comparison_prompt = f"""以下の2つの応答を比較し、どちらがより良いか判定してください。
判定基準:
- 有害性がないこと
- 事実に基づいていること
- ユーザーの質問に適切に答えていること
- 敬意を持った表現であること
質問: {question}
応答A: {response_a}
応答B: {response_b}
どちらの応答が上記の基準により適合していますか?
「A」または「B」で回答してください:"""
inputs = self.tokenizer(comparison_prompt, return_tensors='pt')
with torch.no_grad():
outputs = self.model.generate(
inputs['input_ids'],
max_length=len(inputs['input_ids'][0]) + 10,
num_return_sequences=1,
temperature=0.1 # 決定的に
)
result = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
# 回答を解析
if 'A' in result[-20:]:
return 'A', response_a, response_b
else:
return 'B', response_b, response_a
def create_preference_data(self, questions):
"""選好データを生成"""
preference_data = []
for question in questions:
response_a, response_b = self.generate_response_pair(question)
choice, chosen, rejected = self.get_ai_preference(
question, response_a, response_b
)
preference_data.append({
'question': question,
'chosen': chosen,
'rejected': rejected,
'ai_choice': choice
})
return preference_data
# 使用例(疑似コード)
# rl_trainer = RLCAITrainer(model, tokenizer, constitution)
# preference_data = rl_trainer.create_preference_data(questions)
# これをDPOで学習
完全なCAIパイプライン
class ConstitutionalAIPipeline:
"""Constitutional AIの完全なパイプライン"""
def __init__(self, base_model, tokenizer):
self.base_model = base_model
self.tokenizer = tokenizer
self.constitution = Constitution()
def stage1_sl_cai(self, questions, n_iterations=2):
"""Stage 1: SL-CAI"""
print("=== Stage 1: SL-CAI ===")
sl_trainer = SLCAITrainer(
self.base_model,
self.tokenizer,
self.constitution
)
# 修正データを生成
training_data = sl_trainer.create_training_data(
questions, n_iterations
)
# 教師あり学習(実際にはここでSFTを実行)
print(f"Generated {len(training_data)} training pairs")
return training_data
def stage2_rl_cai(self, questions):
"""Stage 2: RL-CAI"""
print("=== Stage 2: RL-CAI ===")
rl_trainer = RLCAITrainer(
self.base_model, # Stage 1で更新されたモデル
self.tokenizer,
self.constitution
)
# 選好データを生成
preference_data = rl_trainer.create_preference_data(questions)
# DPOで学習(実際にはここでDPOを実行)
print(f"Generated {len(preference_data)} preference pairs")
return preference_data
def train(self, questions_stage1, questions_stage2):
"""完全なCAIトレーニング"""
# Stage 1
sl_data = self.stage1_sl_cai(questions_stage1)
# Stage 2
rl_data = self.stage2_rl_cai(questions_stage2)
return {
'sl_data': sl_data,
'rl_data': rl_data
}
# 使用例
# pipeline = ConstitutionalAIPipeline(model, tokenizer)
# results = pipeline.train(stage1_questions, stage2_questions)
原則の設計ガイドライン
良い原則の特徴
- 具体的: 曖昧さがなく、判断基準が明確
- 実行可能: AIが実際に評価・改善できる
- バランス: 安全性と有用性のトレードオフを考慮
- 網羅的: 想定されるリスクをカバー
原則の階層化
CONSTITUTION_HIERARCHY = {
"level_1_core": [
"人間に物理的・精神的危害を与える行為を助長しない",
"違法行為を支援しない",
"個人のプライバシーを侵害しない",
],
"level_2_ethical": [
"差別的な表現を避ける",
"事実に基づいて回答する",
"不確かな情報は明示する",
],
"level_3_quality": [
"ユーザーの質問に直接答える",
"適切な詳細度で説明する",
"専門用語は必要に応じて説明する",
]
}
Constitutional AIの利点と限界
利点
| 利点 | 説明 |
|---|---|
| スケーラビリティ | 人間のラベルが最小限 |
| 透明性 | 何を基準に改善しているか明確 |
| 一貫性 | AIの判断は一貫している |
| コスト効率 | アノテーションコストが低い |
限界
| 限界 | 説明 |
|---|---|
| AIの能力依存 | AIが原則を正しく理解・適用できるか |
| 原則設計の難しさ | 完全な原則を事前に定義するのは困難 |
| エッジケース | 原則間の矛盾や想定外のケース |
まとめ
本記事では、Constitutional AIの仕組みと実装方法を解説しました。
- 核心アイデア: 明示的な原則に基づいてAI自身が出力を改善
- 二段階プロセス: SL-CAI(自己修正)とRL-CAI(AIフィードバック)
- 利点: スケーラブル、透明、一貫性がある
- 原則設計: 具体的で実行可能な原則が重要
次のステップとして、以下の記事も参考にしてください。