itschool / build_data.py
phamluan's picture
Claude Opus 5
ITSchool-V1.0 QA API: 6 mo hinh Transformer + du lieu thuc nghiem
414b5da
Raw History Blame Contribute Delete
10.8 kB
"""Sinh toan bo file du lieu cho Space tu du an goc.
python build_data.py "/duong/dan/Thom Paper"
Ghi ra space/data/:
dataset.json 10.961 cap hoi dap (train + dev + test)
facets.json gia tri cho cac bo loc
lessons.json cay lop > chu de > bai hoc
metrics.json chi so thuc nghiem + bao cao ro ri du lieu
testset.json 1.073 cau test kem du doan cua 6 mo hinh
errors.json vi du phan tich loi, nhom theo kieu
recorded.jsonl du doan da ghi — dung khi chua co checkpoint fine-tune
built.json thoi diem build
"""
import csv
import json
import re
import sys
from datetime import datetime, timezone
from pathlib import Path
HERE = Path(__file__).parent
OUT = HERE / "data"
MODELS = [
("phobert", "PhoBERT", "extractive"),
("mbert", "mBERT", "extractive"),
("xlmr", "XLM-RoBERTa", "extractive"),
("vit5", "ViT5", "generative"),
("bartpho", "BARTpho", "generative"),
("gpt", "GPT", "generative"),
]
LABELS = [label for _, label, _ in MODELS]
NEEDED = [
"data/processed/train.csv",
"data/processed/dev.csv",
"data/processed/test.csv",
"results/final_results.json",
"results/results_by_grade.csv",
"results/du_doan_6_mo_hinh.csv",
"results/vi_du_phan_tich_loi.csv",
"results/data_split_report.json",
]
def desegment(value):
"""Du lieu PhoBERT duoc tach tu nen co dau gach duoi — bo di khi hien thi."""
return re.sub(r"\s+", " ", str(value or "").replace("_", " ")).strip()
def num(value):
if value in ("", None):
return None
try:
return float(value)
except ValueError:
return None
def read_csv(root: Path, rel: str):
with (root / rel).open(encoding="utf-8-sig", newline="") as fh:
return list(csv.DictReader(fh))
def read_json(root: Path, rel: str):
return json.loads((root / rel).read_text(encoding="utf-8"))
def write(name: str, value) -> None:
path = OUT / name
path.write_text(json.dumps(value, ensure_ascii=False), encoding="utf-8")
print(f" {name:<16} {path.stat().st_size / 1024:,.0f} KB")
# ----------------------------------------------------------------- dataset
def build_dataset(root: Path):
items = []
for split in ("train", "dev", "test"):
for r in read_csv(root, f"data/processed/{split}.csv"):
items.append(
{
"id": r["id"],
"grade": int(r["grade"]),
"topic": r["topic"],
"lesson": r["lesson"],
"page": r["page"],
"section": r["section"],
"contextId": r["context_id"],
"context": r["context"],
"question": r["question"],
"answer": r["answer"],
"answerStart": int(r["answer_start"]) if r["answer_start"] else None,
"answerEnd": int(r["answer_end"]) if r["answer_end"] else None,
"intent": r["intent"],
"difficulty": r["difficulty"],
"questionType": r["question_type"],
"source": r["source"],
"split": split,
}
)
return items
def build_lessons(items):
grades = {}
for it in items:
topics = grades.setdefault(it["grade"], {})
lessons = topics.setdefault(it["topic"], {})
entry = lessons.setdefault(
it["lesson"], {"lesson": it["lesson"], "page": it["page"], "questions": 0, "contexts": set()}
)
entry["questions"] += 1
entry["contexts"].add(it["contextId"])
out = []
for grade in sorted(grades):
topics = []
for topic in sorted(grades[grade]):
lessons = sorted(grades[grade][topic].values(), key=lambda l: l["lesson"])
topics.append(
{
"topic": topic,
"lessons": [
{"lesson": l["lesson"], "page": l["page"], "questions": l["questions"], "contexts": len(l["contexts"])}
for l in lessons
],
}
)
out.append({"grade": grade, "topics": topics})
return out
def build_facets(items):
uniq = lambda key: sorted({it[key] for it in items if it[key]})
return {
"grades": sorted({it["grade"] for it in items}),
"topics": uniq("topic"),
"difficulties": uniq("difficulty"),
"questionTypes": uniq("questionType"),
"splits": ["train", "dev", "test"],
"total": len(items),
}
def build_stats(items):
def count(key):
out = {}
for it in items:
out[str(it[key])] = out.get(str(it[key]), 0) + 1
return out
contexts = {it["contextId"]: it["context"] for it in items}
return {
"total": len(items),
"contexts": len(contexts),
"byGrade": count("grade"),
"bySplit": count("split"),
"byDifficulty": count("difficulty"),
"byQuestionType": count("questionType"),
"answerLengthAvg": round(sum(len(it["answer"] or "") for it in items) / len(items), 1),
"contextLengthAvg": round(sum(len(c) for c in contexts.values()) / len(contexts)),
}
# ------------------------------------------------------------------ metrics
def build_metrics(root: Path):
final = read_json(root, "results/final_results.json")
split = read_json(root, "results/data_split_report.json")
models = []
for key, label, kind in MODELS:
block = final.get("extractive", {}).get(label) or final.get("generative", {}).get(label) or {}
models.append(
{
"key": key,
"label": label,
"kind": kind,
"em": round(block["EM"], 2) if "EM" in block else None,
"f1": round(block["F1"], 2) if "F1" in block else None,
"bleu": round(block["BLEU"], 2) if "BLEU" in block else None,
"rougeL": round(block["ROUGE-L"], 2) if "ROUGE-L" in block else None,
}
)
per_grade = [
{
"label": r["Mô hình"],
"grade": int(r["Lớp"]),
"em": num(r["EM"]),
"f1": num(r["F1"]),
"bleu": num(r["BLEU"]),
"rougeL": num(r["ROUGE-L"]),
}
for r in read_csv(root, "results/results_by_grade.csv")
]
return {
"models": models,
"perGrade": per_grade,
"counts": {"train": final["n_train"], "dev": final["n_dev"], "test": final["n_test"]},
"split": {
"rowsRaw": split["rows_raw"],
"rowsClean": split["rows_after_cleaning"],
"contextClusters": split["context_clusters"],
"uniqueContexts": split["unique_context_texts"],
"seed": split["seed"],
"jaccard": split["jaccard_threshold"],
"leakage": split["leakage_checks"],
},
}
# ---------------------------------------------- testset + du doan da ghi
def build_testset_and_recorded(root: Path):
rows = read_csv(root, "results/du_doan_6_mo_hinh.csv")
testset = []
recorded = []
for r in rows:
preds = {}
for key, label, _ in MODELS:
preds[key] = {
"answer": desegment(r.get(label)),
"em": num(r.get(f"{label}_em")),
"f1": num(r.get(f"{label}_f1")),
}
testset.append(
{
"id": r["id"],
"grade": int(r["grade"]),
"topic": r.get("topic", ""),
"lesson": r.get("lesson", ""),
"question": r["question"],
"context": r["context"],
"gold": desegment(r["gold"]),
"preds": preds,
"extCorrect": num(r.get("n_ext_dung")),
"genF1Avg": num(r.get("gen_f1_tb")),
}
)
recorded.append(
{
"id": r["id"],
"grade": r["grade"],
"question": r["question"],
"context": r["context"],
"gold": r["gold"],
"lesson": r.get("lesson", ""),
"topic": r.get("topic", ""),
"predictions": {label: (r.get(label) or "").strip() for label in LABELS},
"f1": {label: round(float(r[f"{label}_f1"]), 4) for label in LABELS if num(r.get(f"{label}_f1")) is not None},
}
)
return testset, recorded
def build_errors(root: Path):
groups = {}
for r in read_csv(root, "results/vi_du_phan_tich_loi.csv"):
name = r["nhóm"]
group = groups.setdefault(name, {"group": name, "items": []})
group["items"].append(
{
"id": r["id"],
"grade": int(r["lớp"]),
"lesson": r["bài"],
"question": r["câu hỏi"],
"gold": desegment(r["đáp án"]),
"context": r["ngữ cảnh"],
"preds": {
key: {"answer": desegment(r.get(label)), "f1": num(r.get(f"{label}_F1"))}
for key, label, _ in MODELS
},
}
)
return list(groups.values())
# -------------------------------------------------------------------- main
def main() -> int:
root = Path(sys.argv[1] if len(sys.argv) > 1 else HERE.parent.parent).expanduser().resolve()
missing = [f for f in NEEDED if not (root / f).exists()]
if missing:
print(f"Khong tim thay trong {root}:")
for f in missing:
print(f" - {f}")
print('\nChay lai voi duong dan goc, vi du:\n python build_data.py "/Volumes/MyData/paper/Thom Paper"')
return 1
OUT.mkdir(exist_ok=True)
print(f"Doc tu: {root}")
items = build_dataset(root)
testset, recorded = build_testset_and_recorded(root)
write("dataset.json", items)
write("facets.json", build_facets(items))
write("stats.json", build_stats(items))
write("lessons.json", build_lessons(items))
write("metrics.json", build_metrics(root))
write("testset.json", testset)
write("errors.json", build_errors(root))
path = OUT / "recorded.jsonl"
with path.open("w", encoding="utf-8") as fh:
for row in recorded:
fh.write(json.dumps(row, ensure_ascii=False) + "\n")
print(f" {'recorded.jsonl':<16} {path.stat().st_size / 1024:,.0f} KB")
write(
"built.json",
{"builtAt": datetime.now(timezone.utc).isoformat(), "source": str(root), "items": len(items)},
)
print(f"\nXong — {len(items):,} cap hoi dap, {len(testset):,} cau test.")
return 0
if __name__ == "__main__":
raise SystemExit(main())