|
Download parallel/report.md from Eric-Yyvuw/yeepee-ctr: direct link, hf CLI and curl.
- Browser
- Download file 9.8 kB
-
https://huggingface.co/Eric-Yyvuw/yeepee-ctr/resolve/main/parallel/report.md
- Command line
-
hf download hf://Eric-Yyvuw/yeepee-ctr/parallel/report.md
-
curl -L -o report.md https://huggingface.co/Eric-Yyvuw/yeepee-ctr/resolve/main/parallel/report.md
9.8 kB
| # v2work vs v3: leakage audit and like-for-like comparison (parallel check) | |
| Started 23 Oct 2026 12:35 (Europe/Dublin). v2work and v3 were only read. Everything here lives in /workspace/huawei/parallel/. | |
| The source snapshot comes from v2work/out/solution_v3_src.zip (06:24) and is unzipped in parallel/src_snapshot/. | |
| Current v2work code differs from it only by the added `d{k}` fold specs (12:27 edits), so the snapshot is what produced submission_v3. | |
| ## Status log | |
| - 12:45 Audit of the code path done (see below). Like-for-like scoring done (score_val.json). Submission check done (subcheck.json). | |
| ## 1. Leakage audit of the v2work "ne" (no-eval) path | |
| Citations point to parallel/src_snapshot/v2work/* (identical to what produced out/submission_v3.csv). | |
| C is the cutoff. "Own history" is the history list that is a given input field on every row, including eval rows. | |
| **a. Are training rows only events before the cutoff? YES.** | |
| - v3fold.py L11β12 SPEC: f_k = first event per user in [J20+kΒ·d, +1d). | |
| - In val mode, combo.py L44 trains on f1..f4 only (cut25). All target ts < Jan25. | |
| - Val25 is the first event per user at or after Jan25 (W=None), the same as our v3/split. | |
| - Final (combo.py L68, FOLDS unset in solution_v3.py) uses f1..f6, whose targets fall in Jan21..Jan26 (<Jan27). | |
| - build_folds.py L9β12: val25 is (Jan25, 2d), and h1..h4 are 1-day windows. | |
| **b. Are label-derived features computed only from labels before the cutoff? YES.** | |
| - v3feat_ne.py L109β110: P_ev and P_rows are filtered to ts<C. All CTR, impression and click stats come from these. | |
| - feats.py L40β41: same filter. | |
| - extra.py L22β24 and extra2.py L29β31 assert `CL.ts.max()<C and P_rows.ts.max()<C`. | |
| - Cluster CTR (extra.py L60) and user state (extra2.py L53β59) come from P_rows and P_ev. | |
| **c. Does any feature use the unlabelled eval rows? Only their own history, and only for user-level features.** | |
| - Item, global, co-occurrence and SVD stats come from CLg = click_log(..., T_ev.head(0)), which is train only: | |
| - v3feat_ne.py L113β114 (CL vs CLg) | |
| - L128 (item ip) | |
| - L173 (cooc) | |
| - L195 (SVD from CLg) | |
| - feats.py L54β55 poolg β L65, L87, L96 | |
| - extra.py L31 (idf from CLg) | |
| - extra2.py L35β39 (SVD) | |
| - User-level stats use each row's own pre-C history: | |
| - v3feat_ne.py L209β212 and L222 (user fold-in onto train-only item factors) | |
| - feats.py L49β50 and L117β122 | |
| - extra.py L36 | |
| - extra2.py L41 | |
| - This is standard: every eval row carries its own history. It doesn't pool across eval rows. No eval labels exist or are used. | |
| - Two minor transductive touches, label-free with pre-C clicks only: | |
| - extra.py L51 builds item-side vectors iC = Scg.T @ Un. Scg is train-only clicks, but Un includes the target users' folded-in vectors. | |
| - extra.py L55β57 fits KMeans on Un over active users, which can include target users. | |
| - Neither uses labels or post-C data. Expected effect is small. | |
| - write_sub.py reads evaluation.csv only for rowid order and validation. | |
| **d. Is the validation set the same as ours? YES.** | |
| - val25: 439,640 rows, rowid sets identical, 0 label mismatches. | |
| - val26: 351,380 rows, identical (score_val.json). | |
| **Does submission_v3.csv come from the ne path? YES.** | |
| - It equals v2work/eval_pred_v3.parquet (max abs diff 5e-9) and differs from the old eval_pred.parquet by up to 0.34. | |
| - eval_pred_v3 was written by `combo.py final 400 3 notime` with XF=1 XF2=1 (ne_final_v3.log: 2,105,880 rows = f1..f6, 325 feats, 3 seeds). | |
| - All input tables (feat_v3 at 04:06β04:24, feat at 04:06β04:12, feat_x at 04:06β04:15, feat_x2 at 04:16β04:27) were written by the ne_* runs after the no-eval edits (feats.py and v3feat_ne.py at 04:03). | |
| **FLAG: our own v3 is less strict than v2work here.** | |
| - v3/features.py builds item stats (L127), co-occurrence (L172) and the SVD matrix (L194) from CL, which includes every target row's own history. | |
| - For eval predictions this means our submission_v2.csv pools all eval rows' (pre-cutoff, label-free) histories into item-level stats. | |
| - That's the same pattern Grok Bot labelled a leak and superseded in its old v2. | |
| - It's label-free and pre-Jan27, so it's not label leakage. But it is transductive use of the eval rows. Whether that's allowed is Eric's call. | |
| ## 2. Like-for-like scoring (our metric: global ROC AUC, v4/common.report), identical rows | |
| v3 preds come from re-predicting the saved models v3/out/full25.txt and full26_lr05.txt in parquet row order. | |
| The saved v3 *_valpred.npy files have an unrecoverable row order because of an unstable sort in v3/train.py. The re-prediction reproduces 0.75554 and 0.77444. | |
| | set | model | AUC | eval-weighted | stale β€1d | stale 1β2d | event AUC* | Spearman vs v3 | | |
| |---|---|---|---|---|---|---|---| | |
| | val25 (439,640) | **v3 (ours)** | 0.75554 | 0.75554 | 0.76059 | 0.73424 | 0.64894 | β | | |
| | val25 | **v2work ne notime 325f** (combo_val25_notime.npy, e-fold run, 300 rounds) | **0.77749** | 0.77748 | 0.78199 | 0.75830 | 0.65403 | 0.899 | | |
| | val25 | v2work notime_x2 (pre-ne config, 04:26) | 0.77536 | 0.77535 | 0.77968 | 0.75708 | 0.65328 | 0.899 | | |
| | val25 | v2work screen, base, 25%-user train, 1 seed | 0.77174 | 0.77173 | 0.77642 | 0.75176 | 0.59153* | 0.888 | | |
| | val26 (351,380) | **v3 (ours)** | 0.77444 | 0.77444 | 0.77444 | β | 0.65666 | β | | |
| | val26 | v2work screen, base, 25%-user train | **0.78921** | 0.78921 | 0.78921 | β | 0.59846* | 0.912 | | |
| *Event AUC = AUC of max(y) vs sum(p) per event. The screen preds are raw scores, so their sum isn't comparable; ignore those two event numbers. | |
| Notes: | |
| - The final config (f-folds, 400 rounds) scored 0.77748 in ne_combo25.log, but its preds were overwritten by the e-fold run (0.77749 at 300). The two are equivalent proxies. | |
| - There are no full-data v2work val26 preds. The 25% screen alone beats our full v3 by +0.015. | |
| - The gain is uniform across staleness buckets: +0.021 at β€1d and +0.024 at 1β2d. It isn't concentrated where leakage would show. | |
| **Final submissions:** Spearman(v2work/out/submission_v3.csv, /workspace/huawei/submission_v2.csv) = **0.918** (Pearson 0.929) over 529,390 rows (all joined). Mean p is 0.0625 vs 0.0616. | |
| ## 3. Reproduction and lag-1d leak test (parallel/lag/, one job at a time, 3 threads, seed 42, deterministic) | |
| **Reproduction.** | |
| - Retrained v2work's exact final config (snapshot combo.py PARAMS, notime, XF=1 XF2=1, f1..f4, 400 rounds) on v2work's on-disk tables, read only. | |
| - val25 AUC was 0.77684, 0.77724 and **0.77748** at 200, 300 and 400 rounds. That's identical to ne_combo25.log, so 0.7775 is real and reproducible. | |
| - I also rebuilt the val25 tables from the snapshot code (lag/repro_cmp2.json): | |
| - feat_x2 is exact. | |
| - feat (v2) has 31 of 122 cols with tiny diffs (corr β₯0.9997). | |
| - feat_v3 has 60 of 169 co-occurrence/rank cols differing on β€2.5% of rows (corr β₯0.989). | |
| - feat_x has clctr cols differing on β€5% of rows (corr β₯0.996). | |
| - These diffs are consistent with thread-dependent randomized SVD/KMeans and tie-breaking. They don't point to a different (eval-pooled) build. | |
| **Lag-1d test.** | |
| - Same target rows (val25 = first event per user at or after Jan25, labels unchanged). All four feature builders were rerun with the cutoff frozen at **Jan24**, so no stat or label from [Jan24, Jan25) is used. | |
| - Predicted with the same model (lag/lagtest.py): | |
| | val25 features | AUC | stale β€1d | stale 1β2d | event AUC | | |
| |---|---|---|---|---| | |
| | as built (C=Jan25) | 0.77748 | 0.78184 | 0.75895 | 0.65358 | | |
| | lag-1d (C=Jan24) | 0.76470 | 0.76946 | 0.74430 | 0.64837 | | |
| - The drop is β0.0128, gradual, with no cliff. It's smaller than v3's own lag-1d drop (0.7556β0.7391, β0.0165; v3/out/leakcheck.jsonl). | |
| - Even with 1-day-stale features, v2work (0.7647) beats our v3 with fresh features (0.7555). | |
| - A target-day leak would collapse towards ~0.65β0.70 when the target day is removed. That doesn't happen. | |
| ## 4. Verdict | |
| **v2work's ne pipeline is genuinely better than our v3.** | |
| - val25: +0.022 AUC (0.7775 vs 0.7555) on identical rows and labels. | |
| - val26 screen: +0.015, even with a 25% train sample. | |
| - The gain holds in every staleness bucket and survives the lag-1d test. | |
| The audit found no label leakage: | |
| - Training targets are before C. | |
| - All label stats come from ts<C, with asserts. | |
| - Item and global stats are train only. | |
| - Eval rows contribute only their own given pre-cutoff history to user-level features. | |
| Grok Bot's claims all check out: | |
| - The ne variant files. | |
| - 0.7775 from ne_combo25 and ne_combo25_eft. | |
| - submission_v3.csv built by the 06:30 combo final run (it equals eval_pred_v3.parquet). | |
| - The old v2work submission_v2 is superseded. | |
| Correlation with our submission: Spearman 0.918. | |
| **Flags for Eric:** | |
| 1. **v2work/out/submission_v3.csv appears to have been uploaded already.** v2work/out/submission_log.csv has a row "submission_v3.csv.zip (public score) β¦ 0.7775, 0.788757", so public AUC was 0.7888. This parallel check didn't upload or submit anything. | |
| 2. Our v3 (and so /workspace/huawei/submission_v2.csv) builds item, co-occurrence and SVD stats from all target and eval rows' histories (v3/features.py L127/L172/L194). That's label-free but transductive, and stricter rules would call it eval-row use. v2work removed this. | |
| 3. Minor: v2work's extra.py L51 and L55β57 still fold target users' (pre-C, label-free) vectors into item-side aggregates and KMeans. | |
| **Final CSV:** it exists. v2work/out/submission_v3.csv: sha256 466f3d64β¦, 529,390 rows, written 06:37, plus solution_v3.py and src zip. | |
| - Rebuilding from scratch at β€3 threads would take about 40β50 min: feature tables ~25 min, then final fit on 2.1M rows Γ 3 seeds Γ 400 rounds ~12β20 min (the 1.4M-row fit here took ~3 min). | |
| - If all tables are reused, only the final fit is needed: ~15β20 min. I didn't build one. | |