#!/usr/bin/env python3 """EXP3 report: per-point two-stage results, the gate trade-off, 21x6 contingency tables and the model inventory the project note asks for.""" from __future__ import annotations import base64, io, json, os, sys, time from html import escape import numpy as np import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import cfs_common as C # track chosen on the command line (sd = BCSD input, ai = raw-coarse learned); # empty = the legacy flat run at exp3/ root. TRACK = sys.argv[1] if len(sys.argv) > 1 else "" D = os.path.join(C.OUT, "AI_Results", "exp3", TRACK) OUT = ("/home/tanpv/Progs/claude/HW_CFS_raw/ehd_cfs_exp3%s.html" % (("_" + TRACK) if TRACK else "")) WK = C.WEEK_NAMES POOLED = {"W1": 0.7874, "W2": 0.6090, "W3": 0.5699, "W4": 0.5602, "W5": 0.5598, "W6": 0.5167} # best pooled AUC, section 2 of the REQ = {"W1": 0.8577, "W2": 0.8924, "W3": 0.8916, # main evaluation report "W4": 0.8873, "W5": 0.8876, "W6": 0.8867} CSS = open(os.path.join(os.path.dirname(__file__), "_report_css.txt")).read() def fig(fn): buf = io.BytesIO(); fn() plt.savefig(buf, format="png", dpi=130, bbox_inches="tight"); plt.close() return "
" % \ base64.b64encode(buf.getvalue()).decode() def load(): out = {} for f in sorted(os.listdir(D)): if f.endswith(".json"): out[int(f[2:4])] = json.load(open(os.path.join(D, f))) return out def cell(k, v): tg = {"POD": (">", .6), "POFD": ("<", .2), "FAR": ("<", .2), "CSI": (">", .4)} if k not in tg or not np.isfinite(v): return "%s" % ("—" if not np.isfinite(v) else "%.3f" % v) op, r = tg[k] ok = v > r if op == ">" else v < r return "%.3f" % ("good" if ok else "bad", v) def main(): R = load() N = R[sorted(R)[0]]["_meta"].get("NMEMBER", 12) if R else 12 tlabel = {"sd": " — nhánh cfs_0.25_sd (BCSD)", "ai": " — nhánh cfs_0.25_AI (học hạ quy mô)"}.get(TRACK, "") H = ["", "EXP3 — mô hình theo từng điểm lưới%s" % (tlabel, CSS), "

EXP3 — mô hình riêng cho từng điểm lưới, quyết định hai tầng bằng " "p = M/N%s

" % tlabel, "

21 điểm (3 điểm × 7 vùng), 6 tuần, đặc trưng local + " "large-scale. Kiểm định 2001/2006/2009.

", "
Mỗi (điểm, tuần): 2360 mẫu huấn luyện / 726 mẫu kiểm " "định, 72 đặc trưng (16 local + 32 meso ±3 và ±8 ô + 16 toàn miền + 4 " "gradient + 4 điều hoà). N = %d mô hình mỗi tầng. Ngưỡng cổng P1 và ngưỡng " "tầng 2 P2 đều chốt trên năm huấn luyện bằng dự báo ngoài nếp gấp " "(5 nếp, chia theo NĂM). Dựng lúc %s.
" % (N, time.strftime("%Y-%m-%d %H:%M"))] if not R: H.append("

chưa có kết quả

"); open(OUT, "w").write("\n".join(H)); return # ---- 1. does per-point modelling beat the pooled ceiling? H.append("

1. Mô hình theo điểm có phá được trần của mô hình gộp không?

") H.append("" "" "" "") med = {} for w in WK: a = {k: [R[i][w][k]["AUC_p1"] for i in R if w in R[i] and k in R[i][w]] for k in ("reg", "clf")} if not a["clf"]: continue med[w] = (float(np.median(a["reg"])), float(np.median(a["clf"])), float(np.max(a["clf"]))) H.append("" "" % (w, med[w][0], med[w][1], med[w][2], POOLED[w], REQ[w], med[w][1] - REQ[w])) H.append("
AUC trên năm kiểm định, trung vị trên 21 điểm
TuầnEXP3 reg (trung vị)EXP3 clf (trung vị)EXP3 clf (tốt nhất/21)Mô hình gộpAUC cầnCòn thiếu
%s%.4f%.4f%.4f%.4f%.4f%+.4f
") def f1(): x = np.arange(len(med)) plt.figure(figsize=(7.5, 3.6)) plt.plot(x, [med[w][1] for w in med], "o-", label="EXP3 theo điểm (clf)") plt.plot(x, [med[w][0] for w in med], "s-", label="EXP3 theo điểm (reg)") plt.plot(x, [POOLED[w] for w in med], "^--", label="mô hình gộp toàn quốc") plt.plot(x, [REQ[w] for w in med], "k-", lw=2, label="AUC cần có") plt.xticks(x, list(med)); plt.ylabel("AUC"); plt.grid(alpha=.3) plt.legend(fontsize=8); plt.ylim(.45, .95) H.append(fig(f1)) # ---- 2. the gate H.append("

2. Cổng tầng 1 — đóng được bao nhiêu mẫu, mất bao nhiêu sự kiện

") H.append("

Đây là con số quyết định EXP3 có đáng dùng không. " "Đóng 60% mẫu mà chỉ mất 3% sự kiện là món hời; đóng 77% mà mất 71% " "sự kiện thì không hơn đóng ngẫu nhiên bao nhiêu.

") H.append("" "" "" "") for w in WK: g = [R[i][w]["clf"]["gate"] for i in R if w in R[i] and "clf" in R[i][w]] if not g: continue p1 = np.median([x["P1"] for x in g]) clo = np.median([1 - x["kept_frac_train"] for x in g]) kep = np.median([x["kept_frac"] for x in g]) lost = np.median([x["events_lost"] / max(x["events_total"], 1) for x in g]) H.append("" "" % (w, p1, 100 * clo, 100 * kep, "good" if lost < .1 else "bad", 100 * lost, 100 * (1 - kep))) H.append("
Trung vị trên 21 điểm, đường đánh đổi ngoài nếp gấp " "trên năm huấn luyện (ensemble clf)
TuầnP1 chọn được% mẫu đóng (train)% mẫu giữ (test)% sự kiện test bị cổng giếtso với đóng ngẫu nhiên
%s%.2f%.1f%%%.1f%%%.1f%%%.1f%%
") # ---- 3. per point x week contingency H.append("

3. Bảng contingency — 21 điểm × 6 tuần

") H.append("

a = trúng, b = báo động sai, c = bỏ sót, d = đúng-không. " "Kết quả hai tầng đầy đủ (mẫu bị cổng loại tính là báo không).

") for i in sorted(R): m = R[i]["_meta"] H.append("

Điểm %d — ô lưới %d, vùng S%d, %.2f°N %.2f°E

" % (i, m["point"], m["region"], m["lat"], m["lon"])) H.append("" "" "") for w in WK: r = R[i].get(w, {}) if r.get("skip") or "clf" not in r: H.append("" % w); continue s = r["clf"]["final"] H.append("%s%s%s" "" % (w, s["base"], "".join("" % s[k] for k in ("a", "b", "c", "d")), "".join(cell(k, s[k]) for k in ("POD", "POFD", "FAR")), "%s" % (s["FBI"], cell("CSI", s["CSI"]), s["SEDI"]), r["clf"]["AUC_p1"])) H.append("
TuầnnềnabcdPODPOFDFARFBICSISEDIAUC
%s—
%s%.3f%.3f
%d%.3f%.3f
") # ---- 4. model inventory H.append("

4. Danh mục mô hình AI dùng ở cả hai lần dự báo

") any_pt = R[sorted(R)[0]] for w in WK: if w in any_pt and "clf" in any_pt[w]: m1 = any_pt[w]["clf"]["members1"] m2 = (any_pt[w]["clf"]["stage2"] or {}).get("members", []) H.append("

Tầng 1 (N = %d): %s

" % (len(m1), escape(", ".join(m1)))) H.append("

Tầng 2 (N = %d): %s

" % (len(m2), escape(", ".join(m2)) or "—")) break H.append("
Cùng một bộ họ mô hình cho cả hai ensemble, nên khác " "biệt giữa reg và clf chỉ đến từ TARGET " "(hồi quy Sev_obs so với phân loại sự kiện), không phải từ lớp mô hình. " "lgbm = LightGBM, etree = ExtraTrees, rforest = RandomForest, " "hgb = HistGradientBoosting, mlp = mạng nơ-ron, " "ridge/enet/logreg = tuyến tính, knn = láng giềng gần nhất.
") H.append("") open(OUT, "w").write("\n".join(H)) print("viet %s (%.2f MB)" % (OUT, os.path.getsize(OUT) / 1e6)) if __name__ == "__main__": main()