0Pricing
AI Engineering Academy · レッスン

高品質な学習データセットを準備する

Alpaca形式とShareGPT形式で指示追従データを収集、クリーニング、整形し、データの重複除去を行って、学習セットと検証セットに分割します。

「高品質な学習データセットを準備する」はCoddyKit上の無料AI Engineering Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはAI Engineering Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 AI Engineering Academyコースには全4レッスンが含まれています。

データはファインチューニングで最も重要な要素です

ファインチューニングでは、トレーニングデータの品質が、ハイパーパラメーター、アーキテクチャの選択、トレーニング手法のいずれよりも重要です。高品質な例100件は、質の低い例10,000件を上回ります。ゴミを入れればゴミが出るように、ファインチューニング済みモデルは、データに存在するパターンを、誤り、バイアス、形式の不一致も含めて忠実に再現します。データ品質への投資は、ファインチューニングプロジェクトで最も効果の大きい取り組みです。

指示追従の形式

指示追従タスクのファインチューニングでは、通常、system、user、assistantのロールを持つ会話形式のメッセージフォーマットを使用します。OpenAIのファインチューニングAPIでは、各行が完全な会話例となるJSONLファイルを使用します。Alpaca形式(instruction/input/output)やShareGPT形式(conversationsリスト)も広く使われています。使用する予定のファインチューニングフレームワークに合った形式を選んでください。

import json

# OpenAI fine-tuning format (JSONL)
# Each line is one training example
openai_example = {
    'messages': [
        {'role': 'system', 'content': 'You are a JSON extraction agent.'},
        {'role': 'user', 'content': 'Extract: "John Smith, age 32, from Seattle, joined 2023-01-15"'},
        {'role': 'assistant', 'content': '{"name": "John Smith", "age": 32, "city": "Seattle", "join_date": "2023-01-15"}'}
    ]
}

# Alpaca format
alpaca_example = {
    'instruction': 'Extract structured data from the following text.',
    'input': 'John Smith, age 32, from Seattle, joined 2023-01-15',
    'output': '{"name": "John Smith", "age": 32, "city": "Seattle", "join_date": "2023-01-15"}'
}

# Write as JSONL
with open('train.jsonl', 'w') as f:
    f.write(json.dumps(openai_example) + '\n')
    # Add more examples here...

トレーニングデータの収集:3つの方法

ファインチューニング用データセットを構築する主な方法は3つあります。人手による生成:専門家が理想的な例を手作業で作成します。品質は最も高い一方、時間と費用がかかります。LLMによる生成:強力なモデル(GPT-4o)が例を生成し、人間が検証します。はるかに速く安価で、検証を行えば十分な品質を確保できます。ログからのマイニング:既存の本番ログから入出力のペアを抽出し、ユーザー評価やLLM-as-judgeによるスコアリングなどの品質シグナルを使って高品質な例を選別します。

from openai import OpenAI

client = OpenAI()

def generate_training_example_with_gpt4o(task_description: str, example_input: str) -> dict:
    '''Use GPT-4o to generate a training example for a smaller model.'''
    prompt = f'''You are creating a training example for fine-tuning a smaller model.
Task: {task_description}

Given this input:
{example_input}

Write the ideal assistant response that demonstrates the correct behavior for this task.
Be specific, accurate, and follow the expected format precisely.'''
    
    response = client.chat.completions.create(
        model='gpt-4o',  # teacher model
        messages=[{'role': 'user', 'content': prompt}]
    )
    
    return {
        'messages': [
            {'role': 'system', 'content': task_description},
            {'role': 'user', 'content': example_input},
            {'role': 'assistant', 'content': response.choices[0].message.content}
        ]
    }

品質のフィルタリングと検証

すべてのトレーニング例は、採用前に品質検証を通過させる必要があります。レスポンスが正しい形式であること、事実に関するタスクではレスポンスが正確であること、レスポンスにハルシネーションや有害な内容が含まれていないこと、指示とレスポンスの組み合わせに一貫性があること、本番のユースケースを代表する例であることを検証してください。形式のチェックには自動検証を使用し、内容の品質にはLLM-as-judgeを使い、サンプルについては人間によるレビューを行います。

import json

def validate_training_example(example: dict, schema_validator=None) -> dict:
    issues = []
    
    # Format check
    if 'messages' not in example:
        issues.append('Missing messages field')
        return {'valid': False, 'issues': issues}
    
    messages = example['messages']
    if not any(m['role'] == 'assistant' for m in messages):
        issues.append('No assistant message found')
    
    # Check assistant message quality
    assistant_content = next((m['content'] for m in messages if m['role'] == 'assistant'), '')
    
    if len(assistant_content) < 5:
        issues.append('Assistant response too short')
    
    # Schema validation for JSON output tasks
    if schema_validator:
        try:
            parsed = json.loads(assistant_content)
            schema_validator(parsed)  # raises if invalid
        except json.JSONDecodeError:
            issues.append('Assistant response is not valid JSON')
        except Exception as e:
            issues.append(f'Schema validation failed: {str(e)}')
    
    return {'valid': len(issues) == 0, 'issues': issues}

# Run validation on all examples before training
examples = load_training_examples('raw_dataset.jsonl')
valid_examples = [e for e in examples if validate_training_example(e)['valid']]
print(f'Valid examples: {len(valid_examples)}/{len(examples)}')

データの重複排除

トレーニングデータ内の重複またはほぼ重複した例は有害です。特定の例に対する過学習を引き起こし、多様なパターンの学習に使えるトレーニング容量を無駄にします。データセットを確定する前に重複排除を実行してください。完全重複排除では、ハッシュを使って同一の例を見つけます。近似重複排除では、MinHashや埋め込みの類似度を使って、些細な違いしかない例を見つけます。

import hashlib
from datasketch import MinHash, MinHashLSH

def exact_deduplicate(examples: list[dict]) -> list[dict]:
    seen_hashes = set()
    unique = []
    
    for ex in examples:
        # Hash the user and assistant messages
        content = str(ex['messages'])
        h = hashlib.md5(content.encode()).hexdigest()
        if h not in seen_hashes:
            seen_hashes.add(h)
            unique.append(ex)
    
    print(f'Exact dedup: {len(examples)} -> {len(unique)} ({len(examples)-len(unique)} removed)')
    return unique

def near_deduplicate_by_input(examples: list[dict], similarity_threshold=0.85) -> list[dict]:
    # Build index of input texts
    inputs = [next((m['content'] for m in ex['messages'] if m['role'] == 'user'), '') for ex in examples]
    
    lsh = MinHashLSH(threshold=similarity_threshold, num_perm=128)
    unique_indices = set()
    
    for i, text in enumerate(inputs):
        m = MinHash(num_perm=128)
        for word in text.lower().split():
            m.update(word.encode('utf-8'))
        if not lsh.query(m):  # no similar items found
            lsh.insert(str(i), m)
            unique_indices.add(i)
    
    return [examples[i] for i in sorted(unique_indices)]

トレーニング/検証分割

ファインチューニングを開始する前に、データセットをトレーニングセットと検証セットに分割してください。検証セットは、トレーニング中の過学習を監視するために使用します(トレーニング損失が減少する一方で検証損失が増加している場合、モデルは過学習しています)。一般的な分割比率は、トレーニング90%、検証10%です。データセットに意味のあるカテゴリがある場合は、分割をランダムかつ層化して、両方のセットにすべての意図タイプが均等に含まれるようにしてください。

import random
import json

def split_dataset(examples: list[dict], val_fraction=0.1, seed=42) -> tuple[list, list]:
    random.seed(seed)  # reproducible split
    shuffled = examples.copy()
    random.shuffle(shuffled)
    
    n_val = max(1, int(len(shuffled) * val_fraction))
    val_set = shuffled[:n_val]
    train_set = shuffled[n_val:]
    
    print(f'Train: {len(train_set)} examples, Validation: {len(val_set)} examples')
    return train_set, val_set

def save_jsonl(examples: list[dict], path: str):
    with open(path, 'w') as f:
        for ex in examples:
            f.write(json.dumps(ex) + '\n')

# Split and save
examples = load_training_examples('clean_dataset.jsonl')
train, val = split_dataset(examples, val_fraction=0.1)
save_jsonl(train, 'train.jsonl')
save_jsonl(val, 'validation.jsonl')
print(f'Saved train.jsonl ({len(train)}) and validation.jsonl ({len(val)})')

データセットのバランス調整

不均衡なデータセットでは、ファインチューニング済みモデルがよくあるケースに特化しすぎ、まれでも重要なケースで失敗することがあります。データセットにカテゴリAの例が900件、カテゴリBの例が100件ある場合、モデルは常にAを予測するよう学習する可能性があります。データセットのバランスを調整するには、少数カテゴリのオーバーサンプリング(まれな例を複製する)、多数カテゴリのアンダーサンプリング、またはGPT-4oを使った少数派ケースの合成例の生成を行います。

from collections import Counter
import random

def analyze_distribution(examples: list[dict], category_extractor) -> dict:
    categories = [category_extractor(ex) for ex in examples]
    counts = Counter(categories)
    print('Category distribution:')
    for cat, count in counts.most_common():
        print(f'  {cat}: {count} ({100*count/len(examples):.1f}%)')
    return counts

def oversample_minority(examples: list[dict], category_extractor, target_count: int) -> list[dict]:
    by_category = {}
    for ex in examples:
        cat = category_extractor(ex)
        by_category.setdefault(cat, []).append(ex)
    
    balanced = []
    for cat, cat_examples in by_category.items():
        if len(cat_examples) < target_count:
            # Oversample with replacement
            oversampled = random.choices(cat_examples, k=target_count)
            balanced.extend(oversampled)
        else:
            # Undersample to target_count
            balanced.extend(random.sample(cat_examples, target_count))
    
    random.shuffle(balanced)
    return balanced

データのクリーニングと正規化

トレーニングデータには、ファインチューニングに悪影響を与える不一致が含まれていることがよくあります。出力フィールドでの大文字・小文字の混在、末尾の空白、引用符の使い方の不統一、数値形式の混在、JSONキーの命名規則のばらつきなどです。トレーニング前にこれらを正規化してください。ファインチューニング済みモデルは、データに存在する正確な形式を学習します。データに不一致があれば、モデルもそれを再現します。

import json
import re

def normalize_json_output_example(example: dict) -> dict:
    '''Normalize JSON output in assistant messages for consistency.'''
    messages = example.get('messages', [])
    normalized = []
    
    for msg in messages:
        if msg['role'] == 'assistant':
            content = msg['content'].strip()
            
            # Try to parse and re-serialize JSON for consistent formatting
            try:
                parsed = json.loads(content)
                # Normalize: sort keys, consistent spacing
                content = json.dumps(parsed, ensure_ascii=False, sort_keys=True)
            except json.JSONDecodeError:
                pass  # Not JSON output - leave as is
            
            normalized.append({'role': 'assistant', 'content': content})
        else:
            normalized.append(msg)
    
    return {'messages': normalized}

def normalize_dataset(examples: list[dict]) -> list[dict]:
    return [normalize_json_output_example(ex) for ex in examples]

データセット品質の指標測定

ファインチューニング用のデータを提出する前に、データセット全体の品質指標を計算してください。例ごとの平均トークン数と最大トークン数(長い例はトレーニングコストが高く、途中で切り詰められる可能性があります)、語彙のカバレッジ(データセットが本番入力の多様性全体をカバーしているか)、一貫性スコア(類似した入力に類似した出力が返されるか)を確認します。ほとんどのファインチューニングプロバイダーには、トレーニングの失敗に対する課金が発生する前に形式エラーを確認できるデータ検証エンドポイントがあります。

import tiktoken

def analyze_dataset_quality(examples: list[dict], model='gpt-4o-mini') -> dict:
    encoder = tiktoken.encoding_for_model(model)
    
    token_counts = []
    for ex in examples:
        total_tokens = sum(
            len(encoder.encode(m['content']))
            for m in ex['messages']
        )
        token_counts.append(total_tokens)
    
    report = {
        'total_examples': len(examples),
        'avg_tokens_per_example': sum(token_counts) / len(token_counts),
        'max_tokens': max(token_counts),
        'min_tokens': min(token_counts),
        'examples_over_4k_tokens': sum(1 for t in token_counts if t > 4096),
        'estimated_training_tokens': sum(token_counts),
        'estimated_cost': sum(token_counts) / 1_000_000 * 8.0  # ~$8/1M tokens for gpt-4o-mini
    }
    
    for key, value in report.items():
        print(f'{key}: {value}')
    return report

データセットの反復的な改善

データセットの準備は反復的に行います。まず初期データセットで小規模なモデルをファインチューニングし、学習に使用していない例で評価して、失敗パターンを特定し、それらをデータセットの不足や品質上の問題までさかのぼって調査します。その後、データを修正して再トレーニングします。このエラー駆動型のデータ改善ループは、本番環境でのファインチューニングにおける標準的な実践であり、最初から完璧なデータセットを一度で収集しようとするよりもはるかに効果的です。

例全体でのシステムプロンプトの一貫性

本番環境で微調整済みモデルが常に同じシステムプロンプトを使用する場合は、そのシステムプロンプトを完全に同じ形で、すべての学習例に含めてください。システムプロンプトなしでモデルを動作させたい場合は、システムプロンプトを使わずに学習してください。学習時と推論時の条件の不一致は、微調整が期待外れの結果になる主な原因です。モデルは、学習中に見た正確なプロンプト構造に応じた動作を学習します。

理解度チェック

このレッスンで学んだ、微調整用学習データセットの準備についての理解度を確認しましょう。

レッスンのまとめ

このレッスンでは、微調整ではデータの量より質が重要であること、つまり優れた例100件は平凡な例10,000件を上回ることを学びました。また、重複除去、検証、バランシング、正規化が、学習を実行する前に行う4つの重要なデータクリーニング手順であること、そして学習用データと検証用データの分割が、実環境での性能が低下する前に学習中の過学習を検出するために不可欠であることも学びました。次は、Hugging Face PEFTを使ってLoRAの微調整を実行します。

よくある質問

「高品質な学習データセットを準備する」レッスンは無料ですか?

はい。「高品質な学習データセットを準備する」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、AI Engineering Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 AI Engineering Academyコースには全4レッスンが含まれています。

「高品質な学習データセットを準備する」で何を学びますか?

Alpaca形式とShareGPT形式で指示追従データを収集、クリーニング、整形し、データの重複除去を行って、学習セットと検証セットに分割します。 ブラウザで直接実行するハンズオンコードでAI Engineering Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

AI Engineering Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのAI Engineering Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「高品質な学習データセットを準備する」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このAI Engineering Academyレッスンでコードを書いて実行できますか?

はい。すべてのAI Engineering Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. プロンプトよりファインチューニングが有効な場合
  2. 高品質な学習データセットを準備する
  3. Hugging Face PEFTによるLoRAファインチューニング
  4. ファインチューニング済みモデルの評価とデプロイ
← AI Engineering Academyに戻る