Files
jsc-plugins/tools/corpus/make_batches.py
T
Jeffery (jiantw83)andClaude Sonnet 5.5 ea6b9ca7ef feat: 全量標註的檢查、重標與合併工具 (#5)
verify_batches.py 逐批比對 id;make_relabel.py 挑出需重標的 none;
merge_labels.py 支援多個目錄覆蓋與 confidence 縮寫;worry 加入情緒白名單。

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 17:52:50 +00:00

75 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""把對白切成給標註 sub-agent 的批次檔。
用法:
make_batches.py <dialogues.jsonl> <輸出目錄> --pilot N # 試跑:分層抽樣 N 句
make_batches.py <dialogues.jsonl> <輸出目錄> --all # 全量:所有對白(規則高信心也只有約八成準,所以一併標註)
每批一個 jsonl,一行一句,附前後各 2 句對白當輪流發言的線索。
批次檔不含規則判定的說話者,讓標註獨立,之後才能拿來和規則比對。
"""
import argparse
import json
import random
from collections import defaultdict
from pathlib import Path
def short(rec, n=60):
t = rec["text"]
return {"id": rec["id"], "text": t if len(t) <= n else t[:n] + "…"}
def with_neighbors(recs):
by_chapter = defaultdict(list)
for r in recs:
by_chapter[(r["volume"], r["chapter"])].append(r)
index = {}
for rows in by_chapter.values():
for i, r in enumerate(rows):
index[r["id"]] = {"prev": [short(x) for x in rows[max(0, i - 2):i]], "next": [short(x) for x in rows[i + 1:i + 3]]}
return index
def pilot_sample(recs, n, seed):
rng = random.Random(seed)
unlabeled = [r for r in recs if not r["speaker"]]
medium = [r for r in recs if r["confidence"] == "medium"]
high = [r for r in recs if r["confidence"] == "high"]
take = [(unlabeled, n // 2), (medium, n // 4), (high, n - n // 2 - n // 4)]
out = []
for pool, k in take:
out += rng.sample(pool, min(k, len(pool)))
rng.shuffle(out)
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("dialogues")
ap.add_argument("outdir")
ap.add_argument("--pilot", type=int)
ap.add_argument("--all", action="store_true")
ap.add_argument("--batch-size", type=int, default=25)
ap.add_argument("--seed", type=int, default=7)
a = ap.parse_args()
if bool(a.pilot) == a.all:
ap.error("--pilot N 與 --all 擇一")
recs = [json.loads(line) for line in Path(a.dialogues).read_text(encoding="utf-8").splitlines()]
nb = with_neighbors(recs)
chosen = pilot_sample(recs, a.pilot, a.seed) if a.pilot else recs
out = Path(a.outdir)
out.mkdir(parents=True, exist_ok=True)
for i in range(0, len(chosen), a.batch_size):
path = out / f"batch-{i // a.batch_size + 1:03d}.jsonl"
with path.open("w", encoding="utf-8") as fh:
for r in chosen[i:i + a.batch_size]:
item = {k: r[k] for k in ("id", "text", "before", "after", "prev_par", "next_par", "candidates", "suspect_duplicate")}
item.update(nb[r["id"]])
fh.write(json.dumps(item, ensure_ascii=False) + "\n")
print(f"{len(chosen)} 句 -> {(len(chosen) + a.batch_size - 1) // a.batch_size} 批 -> {out}")
if __name__ == "__main__":
main()