book-sft-pipeline について
Book SFT Pipelineは、書籍テキストから教師ありファインチューニング用データセットを作り、文体変換モデルを訓練するスキルです。研究者やモデル訓練者向けに、分割からLoRA訓練と検証までを扱います。
主な機能
- 01ePubからのテキスト抽出
- 02意味のまとまりに沿った分割
- 03多様な訓練指示の生成
- 04メッセージ形式のデータセット構築
- 05TinkerによるLoRA訓練
- 06新しいシナリオと独自性の検証
活用シーン
- 01文学作品からファインチューニング用データセットを作成
- 02作家の文体を模倣するモデルの訓練
- 03長文向けの分割工程を設計
- 04モデルが筋書きでなく文体を学んだか評価
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ muratcankoylan/ agent-skills-for-context-engineering --skill book-sft-pipelineインストール後、Claude Code で/book-sft-pipelineを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdexamples/gertrude-stein/dataset_sample.jsonlREADME.mdsample_outputs.mdtraining_config.jsonreferences/segmentation-strategies.mdtinker-format.mdtinker.txtscripts/pipeline_example.py詳細
Book SFT パイプライン
書籍を SFT データセットに変換し、文体変換モデルをトレーニングするための完全なシステムです。このスキルでは、生の ePub から、あらゆる作家の文体で文章を書くモデルまでのパイプラインを説明します。
有効化するタイミング
次の場合にこのスキルを有効にしてください。
- 文学作品からファインチューニング用データセットを構築するとき
- 作家の声や文体を変換するモデルを作成するとき
- Tinker などの SFT プラットフォーム用のトレーニングデータを準備するとき
- 長文コンテンツのテキスト分割パイプラインを設計するとき
- 限られたデータで小規模モデル(8B 以下)をトレーニングするとき
中核となる概念
書籍 SFT を支える 3 つの柱
1. インテリジェントな分割 テキストのチャンクは意味的に一貫している必要があります。文の途中で分割すると、モデルは断片的な出力を生成するよう学習してしまいます。目標は、常に自然な区切りで 150~400 語にすることです。
2. 多様な指示の生成 過学習を防ぐために、複数のプロンプトテンプレートとシステムプロンプトを使用します。単一のプロンプト形式では、暗記につながります。15 個以上のプロンプトテンプレートと 5 個以上のシステムプロンプトを使用します。
3. 内容より文体 目標は、筋書きを暗記させることではなく、作家のリズムや語彙のパターンを学習させることです。合成指示では、テキストを引用せずに何が起こるかを説明します。
パイプラインのアーキテクチャ
┌─────────────────────────────────────────────────────────────────┐
│ ORCHESTRATOR AGENT │
│ Coordinates pipeline phases, manages state, handles failures │
└──────────────────────┬──────────────────────────────────────────┘
│
┌───────────────┼───────────────┬───────────────┐
▼ ▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ EXTRACTION │ │ SEGMENTATION │ │ INSTRUCTION │ │ DATASET │
│ AGENT │ │ AGENT │ │ AGENT │ │ BUILDER │
│ ePub → Text │ │ Text → Chunks│ │ Chunks → │ │ Pairs → │
│ │ │ 150-400 words│ │ Prompts │ │ JSONL │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
│
┌───────────────┴───────────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ TRAINING │ │ VALIDATION │
│ AGENT │ │ AGENT │
│ LoRA on │ │ AI detector │
│ Tinker │ │ Originality │
└──────────────┘ └──────────────┘
フェーズ 1:テキストの抽出
重要なルール
- PDF よりも必ず ePub を使用する - OCR エラーは学習されるパターンになります
- 段落単位で抽出する -
<p>タグから抽出して改行を保持します - 前付けと後付けを削除する - 著作権表示や目次がデータセットに混入します
# Extract text from ePub paragraphs
from epub2 import EPub
from bs4 import BeautifulSoup
def extract_epub(path):
book = EPub(path)
chapters = []
for item in book.flow:
html = book.get_chapter(item.id)
soup = BeautifulSoup(html, 'html.parser')
paragraphs = [p.get_text().strip() for p in soup.find_all('p')]
chapters.append('\n\n'.join(p for p in paragraphs if p))
return '\n\n'.join(chapters)
フェーズ 2:インテリジェントな分割
小さなチャンクとオーバーラップ
小さなチャンク(150~400 語)は、大きなチャンク(250~650 語)よりも多くのトレーニング例を生成し、文体変換の精度も高めます。
def segment(text, min_words=150, max_words=400):
paragraphs = text.split('\n\n')
chunks, buffer, buffer_words = [], [], 0
for para in paragraphs:
words = len(para.split())
if buffer_words + words > max_words and buffer_words >= min_words:
chunks.append('\n\n'.join(buffer))
# Keep last paragraph for overlap
buffer = [buffer[-1], para] if buffer else [para]
buffer_words = sum(len(p.split()) for p in buffer)
else:
buffer.append(para)
buffer_words += words
if buffer:
chunks.append('\n\n'.join(buffer))
return chunks
期待される結果
86,000 語の書籍の場合:
- 従来の方法(250~650 語):約 150 チャンク
- 新しい方法(150~400 語+オーバーラップ):約 300 チャンク
- チャンクごとに 2 つのバリエーションを使用:600 個以上のトレーニング例
フェーズ 3:多様な指示の生成
重要なポイント
単一のプロンプトテンプレートを使用すると、暗記につながります。多様なテンプレートによって、根底にある文体を学習できます。
SYSTEM_PROMPTS = [
"You are an expert creative writer capable of emulating specific literary styles.",
"You are a literary writer with deep knowledge of classic prose styles.",
"You are a creative writer skilled at emulating distinctive authorial voices.",
"You write prose that captures the essence of modernist literature.",
"You are a talented writer who can channel classic American authors.",
]
PROMPT_TEMPLATES = [
"Write a passage in the style of {author}: {desc}",
"Channel {author}'s voice to write about: {desc}",
"In {author}'s distinctive prose style, describe: {desc}",
"Write this scene as {author} would have: {desc}",
"Using {author}'s repetitive technique, describe: {desc}",
"Capture the rhythm of {author} in this passage: {desc}",
"Write like {author}: {desc}",
"In the voice of {author}, write: {desc}",
"This is a literary exercise. Write like {author}: {desc}",
"Can you write in {author}'s style? {desc}",
]
指示の生成
INSTRUCTION_PROMPT = """Describe what is happening in this excerpt in 2-3 sentences.
Focus on: characters present, actions, emotions, setting.
Do NOT quote the text directly.
Excerpt:
{text}
"""
# Use a fast, cheap LLM (e.g., Gemini Flash)
instruction = llm_call(INSTRUCTION_PROMPT.format(text=chunk))
フェーズ 4:データセットの構築
メッセージ形式
{
"messages": [
{"role": "system", "content": "You are an expert creative writer..."},
{"role": "user", "content": "Write in the style of Author: Scene description..."},
{"role": "assistant", "content": "The actual book text from chunk..."}
]
}
チャンクごとの複数のバリエーション
def build_examples(chunk, instruction, author, variants=2):
examples = []
for i in range(variants):
system = SYSTEM_PROMPTS[i % len(SYSTEM_PROMPTS)]
template = PROMPT_TEMPLATES[(chunk.id + i) % len(PROMPT_TEMPLATES)]
user = template.format(author=author, desc=instruction)
examples.append({"messages": [
{"role": "system", "content": system},
{"role": "user", "content": user},
{"role": "assistant", "content": chunk.text}
]})
return examples
フェーズ 5:Tinker での LoRA トレーニング
設定
CONFIG = {
"model_name": "Qwen/Qwen3-8B-Base", # Base, not instruct
"lora_rank": 32, # 352MB adapter
"learning_rate": 5e-4, # Higher for LoRA
"batch_size": 4,
"epochs": 3,
}
ベースモデルを使用する理由
指示チューニング済みモデルではなく、ベースモデル(事前学習済み)を使用します。
- ベースモデルは、新しい文体に適応させやすいです
- Instruct モデルには、上書きされにくいパターンがあります
- 文体は低レベルのパターンであり、ベースモデルのほうが適切に捉えられます
トレーニングループ
import tinker
from tinker import types
training_client = await service_client.create_lora_training_client_async(
base_model="Qwen/Qwen3-8B-Base",
rank=32
)
for epoch in range(3):
for batch in batches:
await training_client.forward_backward_async(batch, loss_fn="cross_entropy")
await training_client.optim_step_async(types.AdamParams(learning_rate=5e-4))
result = await training_client.save_weights_for_sampler_async(name="final")
フェーズ 6:検証
現代のシナリオによるテスト
原作には存在し得なかったシナリオでテストします。
TEST_PROMPTS = [
"Write about a barista making lattes",
"Describe lovers communicating through text messages",
"Write about someone anxious about climate change",
]
モデルが現代のシナリオにも文体の特徴を適用するなら、内容ではなく文体を学習しています。
オリジナリティの検証
# Search training data for output phrases
grep "specific phrase from output" dataset.jsonl
# Should return: No matches
AI 検出ツールによるテスト
出力を GPTZero、Pangram、または ZeroGPT でテストします。
既知の問題と解決策
登場人物名の漏出
症状:モデルが新しいシナリオで原作の登場人物名を使用します。 原因:1 冊の書籍では名前の多様性が限られています。 解決策:複数の書籍を使ってトレーニングするか、合成例を追加します。
モデルがフレーズをそのまま繰り返す
症状:出力にトレーニングデータとまったく同じ文が含まれます。 原因:プロンプトのバリエーションが少なすぎるか、エポック数が多すぎます。 解決策:15 個以上のテンプレートを使用し、エポック数を 3 に制限します。
断片的な出力
症状:文が不完全に感じられます。 原因:思考の途中で分割する不適切なセグメンテーションです。 解決策:必ず段落の境界で分割します。
ガイドライン
- PDF よりも必ず ePub を使う - OCR の誤りが学習されたパターンになるためです
- 文の途中で分割しない - 境界は文法的に完結している必要があります
- 多様なプロンプトを使う - 15 種類以上のテンプレート、5 種類以上のシステムプロンプトを使います
- ベースモデルを使う - instruct バージョンは使いません
- チャンクを小さくする - より多くの例を作成するため、150~400 語にします
- テストセットを確保する - 最低 50 個の例を用意します
- 最新のシナリオでテストする - 記憶ではなくスタイル転移であることを証明します
- 独自性を確認する - 出力フレーズが学習データに含まれていないか grep で検索します
期待される結果
| 指標 | 値 |
|---|---|
| 学習例 | 書籍あたり 500~1000 個 |
| モデル | Qwen/Qwen3-8B-Base |
| LoRA ランク | 32 |
| アダプターのサイズ | 約 350 MB |
| 学習時間 | 約 15 分 |
| 損失の減少率 | 90% 以上 |
| スタイル転移の成功率 | 完全な成功が約 50% |
コストの見積もり
| コンポーネント | コスト |
|---|---|
| LLM(指示の生成) | 約 $0.50 |
| Tinker の学習(15 分) | 約 $1.50 |
| 合計 | 約 $2.00 |
コンテキストエンジニアリングスキルとの統合
この例では、Agent Skills for Context Engineering コレクションに含まれる複数のスキルを適用しています。
project-development
このパイプラインでは、段階的で冪等性のあるアーキテクチャパターンに従います。
- 取得:ePub からテキストを抽出します
- 準備:テキストを学習用チャンクに分割します
- 処理:合成指示を生成します
- 解析:メッセージ形式を構築します
- レンダリング:Tinker 対応の JSONL を出力します
- 学習:LoRA でファインチューニングします
- 検証:最新のシナリオでテストします
各フェーズは再開可能で、デバッグに使える中間成果物を生成します。
context-compression
分割は、学習におけるコンテキスト圧縮の一形態です。context-compression の重要な知見は、情報量よりも情報密度が重要だということです。大きく薄められたチャンクよりも、小さくまとまりのあるチャンク(150~400 語)のほうが、スタイル転移の質が向上します。
2 段階の戦略は、コンテキスト圧縮の評価方法を反映しています。
- Tier 1:高速で決定論的な圧縮
- Tier 2:エッジケース向けの LLM 支援
multi-agent-patterns
このパイプラインでは、スーパーバイザー/オーケストレーターパターンを使用します。
- オーケストレーターは各フェーズを調整し、状態を管理します
- 専門エージェント(抽出、分割、指示、ビルダー)は、それぞれ独立したコンテキストを持ちます
- 各エージェントには、そのタスクに必要な情報だけを渡します
これは、サブエージェントは役割を模倣するためではなく、主にコンテキストを分離するために存在するという原則に合致しています。
evaluation
検証では、最終状態の評価パターンに従います。
- 機能テスト:出力が期待されるスタイルの指標と一致するか
- 独自性の検証:コンテンツが実際に生成されたものか
- 外部検証:AI 検出器のスコア
「最新のシナリオ」テストは、汎化能力を証明する分布外評価の一種です。
context-fundamentals
プロンプトに多様性を持たせることで、単一のパターンに対するアテンションの崩壊を防ぎます。同一のプロンプト構造で学習すると、モデルは指示と応答の対応関係を記憶します。多様なテンプレートを使うことで、スタイルパターン自体にアテンションが向くようになります。
参考資料
内部資料:
- 分割戦略 - テキストをチャンクに分割するパターン
- Tinker 形式の仕様 - Datum の構造
- Tinker API ドキュメント - API の完全なリファレンス
Agent Skills for Context Engineering の関連スキル:
- project-development - パイプラインのアーキテクチャパターン
- context-compression - 圧縮戦略
- multi-agent-patterns - エージェントの連携
- evaluation - 評価フレームワーク
- context-fundamentals - アテンションと情報密度
外部リソース:
- 研究論文 - Chakrabarty et al. 2025
- Hugging Face のデータセット
- Gertrude Stein のケーススタディ - 動作する完全な例
スキルのメタデータ
作成日:2025-12-26 最終更新日:2025-12-28 作成者:Muratcan Koylan バージョン:2.0.0 単独で利用可能:はい(メインのコンテキストエンジニアリングコレクションとは別です)