ワークショップ:合成PIIデータと拡張
最終更新日: September 15, 2026
この合成データ演習はPython 3.12で実行でき、モデルの重み、AWSアカウント、GPUは必要ありません。
拡張では、ファイル数を増やすだけでなく、システムが実際に失敗する入力条件を追加するべきです。ドキュメントを複製したり、ラベルが不整合なデータを生成したりすると、学習作業が増える一方で、評価の信頼性が損なわれるおそれがあります。
まずアノテーション方針を定め、ドキュメントファミリーを分割してから、ラベルを保持する変換を適用します。小規模なCPU演習から始め、最後のセクションを参考に、より大規模なドメインデータセットを計画してください。
1. 学習対象を定義する
ソーステキスト、エンティティアノテーション、モデルのターゲットは一致している必要があります。
{
"id": "synthetic-train-example",
"source_text": "신청인 김가상, 이메일 synthetic.01@example.com",
"entities": [
{"type": "PERSON", "original": "김가상"},
{"type": "EMAIL", "original": "synthetic.01@example.com"}
],
"target_tsv": "PERSON\t김가상\nEMAIL\tsynthetic.01@example.com"
}source_textは入力ドキュメントで、target_tsvは教師データとして使うアシスタントの出力です。entitiesは監査と評価に使用します。これは、マスキング済みドキュメント全体を生成するようモデルを学習させる方法とは異なります。
まずアノテーションガイドを作成してください。
| 判断事項 | 例 | 曖昧なままにした場合の問題 |
|---|---|---|
| タイプの意味 | この方針では個人の連絡先はPHONE、会社の代表電話は対象外 | 類似した文字列に相反するラベルが付く |
| 境界の厳密さ | ソースの氏名の空白や電話番号の区切りを維持する | ソースとターゲットの不一致 |
| 文脈に応じた分類 | 文書番号と口座番号を区別する | すべての数字を機微情報として扱う |
| 繰り返し/重複 | 繰り返される氏名、住所に含まれる氏名 | アノテーションと置換の不一致 |
| エンティティなし | entities=[], target_tsv="" | すべてのドキュメントから何かを抽出するよう学習してしまう |
| 不確実なケース | 判読不能または未対応の入力、曖昧な文脈 | 推測した正解ラベルが学習に混入する |
代表電話に関するルールは、この例の方針であり、組織共通のルールではありません。ドキュメント単位の(TYPE, ORIGINAL)ラベルでは、同じ文字列が異なる意味で使われている各出現箇所を個別にアノテーションできません。出現箇所ごとに意味が異なる場合は、オフセット/スパンのスキーマ、評価器、置換コードをまとめて拡張してください。
2. 合成と拡張を区別する
- 合成:テンプレート、合成値、または生成モデルから新しいドキュメントを作成すること。
- 拡張:承認済みの学習例の意図されたラベルを保持したまま、値、表現、レイアウト、またはノイズを変更すること。
- 検証:変換後もソースとターゲットが一致していることを確認すること。生成に成功しても、ラベルが正しいとは限りません。
外部モデルの出力も候補データとして扱ってください。ソースの値を外部APIに送ったり、生成された出力をレビューせず正解ラベルとして採用したりすることを、安易に既定の運用にしないでください。
3. 拡張の前にファミリーを分割する
元データと、その翻訳、OCRバリアント、言い換えを、同じfamily_idの下にまとめます。まずファミリーをtrain/validation/testに割り当て、その後、trainのファミリーに対してのみ派生データを生成してください。
family-001 → train → base + context + layout
family-002 → validation → base
family-003 → test → base拡張の後にランダム分割すると、ほぼ同一のドキュメントがtrainとtestに入る可能性があります。グループ化キーには来歴情報が必要です。新しいレコードIDを割り当てても、独立した顧客やソースファミリーになるわけではありません。
| 分離の軸 | 対処するリーク | 追加で必要な評価 |
|---|---|---|
| ファミリー | 元データと派生データが別々のsplitに入ること | 新しい変換/ノイズ |
| エンティティ/顧客 | 同じ人物/識別子が両方のsplitに現れること | 未知の氏名、識別子、顧客 |
| テンプレート | レイアウトの記憶 | 未知のテンプレート |
| ドメイン/時期 | ビジネス/時期の分布への依存 | 新しいドメインまたは将来の期間 |
このCPU演習では、ファミリーの分離と、NFC正規化後に完全一致するソースの重複を確認します。氏名、テンプレート、ドメインの一部は共通しているため、エンティティ/テンプレート/ドメインの独立性が確立されたわけではありません。
4. 小規模な拡張演習を実行する
リポジトリのルートから始めてください。ジェネレーターが受け付けるのは新しい出力ディレクトリだけなので、すでに作成済みのディレクトリ自体を--output-dirに渡さないでください。
cd examples/ai-ml/qwen-pii-finetuning
mkdir -p "${XDG_CACHE_HOME:-$HOME/.cache}/qwen-pii-lab"
export PII_AUG_RUN="$(mktemp -d "${XDG_CACHE_HOME:-$HOME/.cache}/qwen-pii-lab/run.XXXXXX")"
export PII_AUG_DIR="$PII_AUG_RUN/dataset"
python3 -m data.augmentation_lab --output-dir "$PII_AUG_DIR" --seed 42
python3 -m data.augmentation_lab --audit-dir "$PII_AUG_DIR"
python3 - <<'PY'
import json
import os
from collections import Counter
from pathlib import Path
root = Path(os.environ["PII_AUG_DIR"])
for split in ("train", "validation", "test"):
rows = [
json.loads(line)
for line in (root / f"{split}.jsonl").read_text().splitlines()
]
print(split, {
"records": len(rows),
"families": len({r["provenance"]["family_id"] for r in rows}),
"negative": sum(not r["entities"] for r in rows),
"languages": dict(Counter(r["language"] for r in rows)),
})
PY出力はtrain.jsonl、validation.jsonl、test.jsonl、augmentation-manifest.jsonです。
| Split | ファミリー数 | レコード数 | 韓国語/英語 | ネガティブレコード数 |
|---|---|---|---|---|
| train | 24 | 72 | 36 / 36 | 36 |
| validation | 8 | 8 | 4 / 4 | 4 |
| test | 8 | 8 | 4 / 4 | 4 |
40のファミリーは言語とポジティブ/ネガティブの状態で層化され、各層に6/2/2ファミリーが割り当てられています。trainにはベースデータと2つのバリアントが含まれ、validation/testにはベースデータのみが含まれます。ネガティブデータ50%、言語比率50/50という構成は、演習の比較可能性を高めるためのものであり、本番環境での割合を推定するものではありません。
この小規模演習が扱うのはPERSON、EMAIL、PHONEです。過去の9タイプのジェネレーターとは目的が異なります。この小規模セットに含まれないRRN、CARD、その他のタイプの品質は評価できません。
5. 系譜と変換を確認する
既存ローダーのフィールドは維持し、provenanceを追加してください。
| フィールド | 意味 |
|---|---|
id | 各バリアントに付与する一意のID |
language, domain | 集計/エラー分析のカテゴリ |
provenance.family_id | 元データとバリアントに共通するファミリー |
provenance.split | バリアント生成前に行った割り当て |
provenance.parent_id | バリアントの元となるベースレコード |
provenance.augmentation | 適用した変換名 |
来歴とポジティブ/ネガティブの状態は監査用メタデータです。既存ローダーと同様に、ユーザープロンプトにはsource_textのみを入れてください。split、正解データの有無、正解ラベルのヒントを追加してはいけません。教師ありのアシスタント出力にはtarget_tsvを入れてください。
この実行可能な例には、2種類のバリアントがあります。
- 文脈ラッパー:PIIの値とアノテーション上の意味を保ちながら、合成した周辺の文脈を変更します。
- レイアウト/Unicode:フィールドの順序/レイアウトを変更し、韓国語のバリアントにはNFDを使用します。ソースと元データを一緒にレンダリングしてから、新しいソースの順序でTSVを再生成します。
NFDはハングル字母を含む分解表現を使用し、NFCは正準合成を使用します。見た目が同じテキストでもバイト列が異なる場合があるため、ソースの完全一致を比較する前にNFCへ正規化します。
ネガティブレコードは変換後もアノテーションとターゲットを空のまま維持します。並べ替えやUnicode正規化によってエンティティを新たに作り出してはいけません。これらのバリアントは、実際のスキャナーやOCRエンジンのノイズ分布を再現すると主張するものではありません。
6. エンティティ置換ではソースとラベルを再レンダリングする
以下は、新しい学習候補を作るための、同じタイプの合成置換パターンの最小例です。汎用の無制限な文字列置換ルーチンではありません。
def render_training_candidate(person, email):
source = f"신청인: {person}\n이메일: {email}"
entities = [
{"type": "PERSON", "original": person},
{"type": "EMAIL", "original": email},
]
target = "\n".join(
f"{entity['type']}\t{entity['original']}" for entity in entities
)
assert all(entity["original"] in source for entity in entities)
return {"source_text": source, "entities": entities, "target_tsv": target}
base = render_training_candidate("김가상", "synthetic.base@example.com")
variant = render_training_candidate("이샘플", "synthetic.variant@example.com")
assert base["source_text"] != variant["source_text"]実際の拡張パイプラインでは、承認済みの学習用エンティティバンクを使用し、ファミリー、親データ、seed、オペレーターのバージョンを記録してください。holdoutの値や失敗例をそのバンクに取り込むと、分離方針が無効になるおそれがあります。この独立した教材用の例は、先ほどのCLIで自動実行される3つ目のオペレーターではありません。
7. オペレーターを明示的なラベルチェックで拡張する
| 手法 | 対象とする失敗条件 | 必要なチェック |
|---|---|---|
| 同タイプの置換 | 氏名/番号の記憶 | タイプを維持し、すべての出現箇所と正解データを更新する |
| レイアウトのレンダリング | 表、フォーム、サポート対応の説明文 | フィールド順/TSVの一致とフィールド欠落のチェック |
| 範囲を限定したOCR/空白処理 | 区切り、改行、Unicode形式 | 変換後のソースで正解データと境界を確認する |
| 文脈の言い換え | 1つの文テンプレートへの依存 | PIIの追加/削除と意味の一貫性 |
| 逆翻訳 | 表現の多様性不足 | 氏名/住所/番号の変更と再アノテーション |
| ハードネガティブ | 文書番号/金額の誤分類 | 異なる文脈で表層形式が似た例を使う |
| 希少タイプの補強 | RRN/CARDの例が少ない | 対応する各タイプの件数と文脈の多様性 |
| 長文/境界ケース | 切り詰めとチャンク境界での見落とし | トークン長、オフセット、重複/境界 |
| 埋め込み命令 | ソーステキストを命令として実行すること | ソースをデータとして扱い、TSVの契約を維持する |
言い換えや逆翻訳では、自動チェックをすり抜けてアノテーションエラーが残る場合があります。ソース文字列の存在だけでは、意味的な正しさや正解データの網羅性は保証されません。候補データを学習に含める前に、レビューして承認してください。
8. 監査で何が拒否されるのかを理解する
--audit-dirは、このラボで生成されたデータの契約をチェックします。
- ファイルのハッシュ/マニフェストと、split/言語/ネガティブ件数。
- IDの重複と、NFC正規化後に完全一致するソース。
- ファミリーのsplit所有権と、親データ/変換の関係。
- train用のバリアントが誤ってvalidation/testに置かれていないこと。
- 許可されたラベル、ソース内の存在、entitiesとTSVの一致。
- seedからの決定的な再生成結果との一致。
最後のチェックでは、ファイルハッシュを再計算していても、正解ラベルの欠落があれば拒否できます。これは固定された合成演習に対する再現性チェックであり、任意の顧客ドキュメントに使える汎用の正解ラベル検証器ではありません。ハッシュは、データ生成者を認証する署名ではありません。
学習前にこれらの条件を満たさないデータを除外しておけば、後から汚染データをモデルの問題と誤診することを避けられます。近似重複、アノテーションの意味、トークナイザーの長さ、PIIの網羅性については、別途チェックが必要です。
9. 過去の2,200レコードのコーパスを解釈する
パート2では、ジェネレーター1.0.0、seed 42、元のsplitハッシュを維持しています。このレビューでCPUによる再生成を行ったところ、これらのハッシュが再現されました。
過去のコーパス全体のソースをNFC正規化して比較した結果、次の完全一致が確認されました。
| Splitの組み合わせ | 共通するNFCソース |
|---|---|
| train / validation | 22 |
| train / test | 36 |
| validation / test | 9 |
2つ以上のsplitにまたがるソースグループは、重複を除いて51件あります。3つすべてのsplitに含まれるソースは、splitごとの共通件数で複数回数えられるため、各組み合わせの合計は重複を除いたグループ総数とは一致しません。これは、過去のコーパスをリークのない評価セットとみなしてはいけない具体的な理由です。
新しい演習は過去のデータを上書きせず、改訂後のモデル性能も公開しません。9タイプを扱う本番データセットには、ファミリー/顧客/テンプレートの来歴と新しいマニフェスト/評価バージョンが必要です。古い値やテンプレートを無条件にランダム再分割するのではなく、それらを用意してください。
10. SageMaker入力リリース向けにデータを拡張する
ラボのJSONLには、既存のsrc.datasetのprompt/completionヘルパーが使用するフィールドが含まれています。拡張マニフェストは、アップローダーのdataset-manifest.jsonとそのまま置き換えられるものではありません。
実際の学習リリースでは、次の項目をまとめて準備してください。
- 承認済みのtrain/validation/testと、アノテーション/拡張の方針。
- 実際の件数とSHA-256値を記載した、アップローダー用の入力マニフェスト。
- データセットのサイズ、モデル、学習予算を反映した
config/experiment.yaml。 - その設定と、対応するローカル提出設定を含むソースバンドル。
- 検証済みのサポート対象ランタイムと、ジョブ内で観測した入力ハッシュ/件数。
現在のバンドラー/アップローダーは固定されたパッケージパスを使用しています。--output-dirディレクトリを生成しても、そのディレクトリが自動的にSageMakerの学習に接続されるわけではありません。別の作業用コピーで新しいデータセットリリースを統合・検証してから、QLoRAワークショップと実行契約に従ってください。
元データと拡張データの学習を比較する際は、同じvalidationセット、デコード方法、比較予算を使用してください。データサイズ/長さが変われば、step数が同じでも露出する例数やトークン数が異なる場合があります。例数/トークン数の予算も記録してください。
理解度を確認する
- 拡張後に分割すると、なぜリスクがあるのでしょうか? 同じソースのバリアントが、学習データとholdoutの境界をまたぐ可能性があるためです。
- 新しいIDを付ければ顧客リークはなくなりますか? いいえ。顧客とソースの系譜を確認してください。
- 氏名を変えてもTSVはそのままでよいですか? いいえ。ソースと正解データを一緒に変更する必要があります。
- このラボでは、未知のテンプレートでの性能が確立されますか? いいえ。テンプレートは共通しており、モデルの評価も行いません。
- 拡張で品質が悪化した場合はどうしますか? ラベル、重複、分布の変化、学習予算を調べ、validationで比較してください。