コンテキストアウェアリフレクティブ方式QAデータセット生成システムの実装

## はじめに

このノートブックでは、LLMを使用して高品質なQAデータセットを生成するシステムを実装します。特徴は以下の通りです：

- コンテキストアウェアな質問生成
- リフレクティブな品質評価・改善
- 自動的なデータセットのアップロード

## 環境セットアップ

```python
!pip install -q litellm tqdm loguru wikipedia transformers
!pip install -q datasets huggingface-hub
```

必要なライブラリをインストールします。主な依存関係：
- litellm: LLMとの対話用
- tqdm: プログレスバー表示
- loguru: ロギング
- wikipedia: Wikipedia APIクライアント
- transformers & datasets: Hugging Face関連

## 基本設定とインポート

```python
import wikipedia
import json
from typing import List, Dict, Any
from loguru import logger
import re
from tqdm import tqdm
from datetime import datetime
from pathlib import Path

# 基本パラメータ
MODEL_NAME = "ollama/llama3.1:8b-instruct-fp16"
DEFAULT_CHUNK_SIZE = 200
DEFAULT_OVERLAP_SIZE = 500
DEFAULT_QA_PAIRS_PER_CHUNK = 5
API_BASE = "http://localhost:11434"
```

主な設定項目の説明：
- MODEL_NAME: 使用するLLMモデル
- DEFAULT_CHUNK_SIZE: テキスト分割サイズ
- DEFAULT_OVERLAP_SIZE: チャンク間のオーバーラップ
- DEFAULT_QA_PAIRS_PER_CHUNK: 1チャンクあたりのQA生成数

## WikiTextProcessor クラスの実装

```python
class WikiTextProcessor:
    """Wikipediaテキストの処理とチャンク分割を行うクラス"""
    
    @staticmethod
    def get_wiki_text(topic: str, lang: str = "ja") -> str:
        """指定されたトピックのWikipedia記事を取得"""
        wikipedia.set_lang(lang)
        try:
            page = wikipedia.page(topic)
            return page.content
        except Exception as e:
            logger.error(f"Error fetching {topic}: {e}")
            return ""

    @staticmethod
    def clean_text(text: str) -> str:
        """テキストのクリーニング"""
        text = re.sub(r'\[\d+\]', '', text)  # 参照記号の削除
        text = re.sub(r'\n\s*\n', '\n', text)  # 余分な改行の削除
        return text.strip()
```

WikiTextProcessorの機能：
- Wikipedia記事の取得
- テキストのクリーニング
- チャンク分割とコンテキスト付与

## QAGenerator クラス

```python
class QAGenerator:
    """Q&Aペアの生成を担当するクラス"""
    
    @staticmethod
    def generate_qa_pairs_with_context(
        chunk_data: Dict[str, str],
        num_pairs: int = DEFAULT_QA_PAIRS_PER_CHUNK,
        max_retries: int = 3
    ) -> List[Dict[str, str]]:
        # プロンプトの構築
        prompt = f"""
以下のテキストから質問と回答のペアを{num_pairs}つ生成してください。

## 全体の文脈:
{chunk_data['summary']}

## テキストの位置:
テキスト全体の{int(chunk_data['position'] * 100)}%付近

## メインテキスト:
{chunk_data['chunk_text']}

出力形式:
{{
    "qa_pairs": [
        {{"question": "具体的な質問1？", "answer": "回答1"}},
        {{"question": "具体的な質問2？", "answer": "回答2"}}
    ]
}}
"""
        # LLMを使用してQAペアを生成
```

QAGeneratorの主な特徴：
- コンテキストを考慮したQA生成
- 固有名詞の明示的な使用
- 質問の独立性確保

## リフレクティブな評価と改善

```python
@dataclass
class QAEvaluation:
    """Q&Aペアの評価結果を保持するデータクラス"""
    score: float
    feedback: str
    improvement_suggestions: List[str]
    factuality_score: float
    question_quality_score: float
    answer_completeness_score: float

def evaluate_qa_pair(self, qa_pair: Dict[str, str], context: Dict[str, str]) -> QAEvaluation:
    """Q&Aペアを評価し、詳細なフィードバックを生成"""
    evaluation_prompt = f"""
質問: {qa_pair['question']}
回答: {qa_pair['answer']}

以下の基準で評価してください:
1. 事実性 (0-1)
2. 質問の質 (0-1)
3. 回答の完全性 (0-1)
"""
    # LLMを使用して評価を実行
```

評価システムのポイント：
- 複数の評価基準
- 具体的な改善提案
- 反復的な品質向上

## データセット生成とアップロード

```python
def create_dataset(self, topics: List[str], output_file: str = "qa_dataset.json") -> None:
    """データセット生成のメインプロセス"""
    all_qa_pairs = []
    
    for topic in topics:
        # Wikipedia記事の取得と処理
        text = self.wiki_processor.get_wiki_text(topic)
        text = self.wiki_processor.clean_text(text)
        
        # チャンク分割とQA生成
        chunks = self.wiki_processor.split_into_chunks_with_context(text)
        for chunk in chunks:
            qa_pairs = self.qa_generator.generate_qa_pairs_with_reflection(chunk)
            all_qa_pairs.extend(qa_pairs)
    
    # 結果の保存
    with open(output_file, 'w', encoding='utf-8') as f:
        json.dump(all_qa_pairs, f, ensure_ascii=False, indent=2)
```

最後に、生成したデータセットをHugging Faceにアップロードする機能も実装されています。

## パラメータチューニングのヒント

効果的なデータセット生成のためのパラメータ調整のコツ：

1. チャンクサイズ（chunk_size）
   - 推奨: 200-300文字
   - 長すぎると文脈が散漫に
   - 短すぎると文脈が失われる

2. オーバーラップサイズ（overlap_size）
   - 推奨: チャンクサイズの40-60%
   - 文脈の連続性維持が重要

3. 品質閾値（quality_threshold）
   - 推奨: 0.8以上
   - 厳しすぎると生成数が減少
   - 緩すぎると品質が低下

## 使用例

```python
# データセット生成の実行
creator = DatasetCreator(config={
    "chunk_size": 200,
    "overlap_size": 500,
    "qa_pairs_per_chunk": 5,
    "quality_threshold": 0.8,
    "max_improvement_attempts": 2
})

topics = ["霊烏路空", "物部布都"]
creator.create_dataset(topics=topics, output_file="touhou_qa.json")
```

このシステムの特長は、単なるQA生成だけでなく、生成された各QAペアに対して品質評価と改善を行う点です。これにより、より高品質なデータセットの作成が可能になります。

## 📒ノートブック

https://colab.research.google.com/drive/1OYdgAuXHbl-0LUJgkLl_VqknaAEmAm0S?usp=sharing