HassanB4 commited on
Commit
f1ccdee
·
verified ·
1 Parent(s): cd10c00

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +45 -0
  2. ensemble_weights.json +11 -0
  3. s11_ensemble_9model.py +151 -0
README.md ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ tags:
3
+ - arabic-nlp
4
+ - genre-classification
5
+ - ensemble
6
+ ---
7
+
8
+ # AraGenre 2026 — S33: 9-model ensemble
9
+
10
+ No weights of its own — a combination of 9 component models' dev scores
11
+ (softmax-normalized per model, then weighted-summed). This is S29's 8-model blend
12
+ plus S32 (E5-large, X-GENRE + synthetic-data augmented).
13
+
14
+ ## Dev result
15
+ hier_f1 = 0.9735 (reference: 0.9917, recorded only in a commit message
16
+ before the original scripts were pruned — the exact original 9-way weights were
17
+ never recorded, so these weights were re-derived via a dev-validated random
18
+ search seeded near S29's known weights, not copied from the original run)
19
+
20
+ ## Components and weights (this run)
21
+ ```json
22
+ {
23
+ "s01_bge_m3_zeroshot": 0.1029,
24
+ "s02_bge_m3_augdefs": 0.3108,
25
+ "s03_e5_cosine_8ep": 0.0047,
26
+ "s04_e5_cosine_10ep": 0.1406,
27
+ "s05_e5_mnrl_xgenre_aragenre": 0.1193,
28
+ "s06_e5_mnrl_augdefs": 0.1761,
29
+ "s07_e5_mnrl_xgenre_phase1": 0.1032,
30
+ "s08_multiseed_ensemble": 0.0347,
31
+ "s09_synth_augmented": 0.0077
32
+ }
33
+ ```
34
+
35
+ Standalone components with their own published repos: S24
36
+ (`HassanB4/s24-e5-mnrl`), S24b
37
+ (`HassanB4/s24b-e5-mnrl-xgenre`), S28
38
+ (`HassanB4/s28-seed{42,123,777}`), S32
39
+ (`HassanB4/s32-synth-augmented`). The remaining components (S1, S10,
40
+ S17-pipeline, S23, S23b) never cleared 0.90 individually and exist only as cached
41
+ score files (`rerun_2026/scores/`) plus the s01-s05 scripts.
42
+
43
+ ## Usage
44
+ Requires cached dev score files from running s01 through s09 first, then
45
+ `python s11_ensemble_9model.py`.
ensemble_weights.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "s01_bge_m3_zeroshot": 0.1029,
3
+ "s02_bge_m3_augdefs": 0.3108,
4
+ "s03_e5_cosine_8ep": 0.0047,
5
+ "s04_e5_cosine_10ep": 0.1406,
6
+ "s05_e5_mnrl_xgenre_aragenre": 0.1193,
7
+ "s06_e5_mnrl_augdefs": 0.1761,
8
+ "s07_e5_mnrl_xgenre_phase1": 0.1032,
9
+ "s08_multiseed_ensemble": 0.0347,
10
+ "s09_synth_augmented": 0.0077
11
+ }
s11_ensemble_9model.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ S33 — 9-model ensemble: the S29 8-model blend (S1+S10+S17+S23+S23b+S24+S24b+S28) plus
3
+ S32 (synthetic-augmented E5-large). The original run's exact 9-way weights were never
4
+ recorded (only the resulting 0.9917 hier_f1 survived, in a commit message) — so this
5
+ script re-derives weights the same way the original pipeline did for S25/S29: a
6
+ dev-validated grid/random search over the weight simplex. This is weight tuning via
7
+ held-out validation (standard ML practice, and how S29's own weights were originally
8
+ found), not the dev-ID rule hardcoding that got S26 thrown out.
9
+
10
+ Requires s01..s09 to have already been run (cached dev scores in rerun_2026/scores/).
11
+
12
+ Run: python s11_ensemble_9model.py
13
+ """
14
+
15
+ import json
16
+ import shutil
17
+
18
+ import numpy as np
19
+
20
+ from common import (
21
+ DATA_DIR, OUT_DIR, load_json, genre_list, specific_to_broad, softmax,
22
+ hier_f1_report, scores_to_predictions, save_submission_and_zip,
23
+ load_scores, score_dict_to_matrix, push_to_hf,
24
+ )
25
+
26
+ NAME = "s11_ensemble_9model"
27
+
28
+ MODELS = [
29
+ "s01_bge_m3_zeroshot", # S1
30
+ "s02_bge_m3_augdefs", # S10
31
+ "s03_e5_cosine_8ep", # S17 (pipeline)
32
+ "s04_e5_cosine_10ep", # S23
33
+ "s05_e5_mnrl_xgenre_aragenre", # S23b
34
+ "s06_e5_mnrl_augdefs", # S24
35
+ "s07_e5_mnrl_xgenre_phase1", # S24b
36
+ "s08_multiseed_ensemble", # S28
37
+ "s09_synth_augmented", # S32
38
+ ]
39
+
40
+ # S29's recorded 8-way weights, used as the search's starting point (extended with a
41
+ # small initial weight for the new S32 slot).
42
+ S29_WEIGHTS = [0.053, 0.158, 0.0, 0.263, 0.211, 0.105, 0.158, 0.053]
43
+ N_TRIALS = 4000
44
+ SEED = 42
45
+
46
+
47
+ def evaluate_weights(weights, softmax_mats, gold_specific, gold_broad, dev_genres, dev_s2b, dev_ids):
48
+ combined = np.zeros_like(softmax_mats[0])
49
+ for w, mat in zip(weights, softmax_mats):
50
+ combined += w * mat
51
+ _, pred_specific, pred_broad = scores_to_predictions(dev_ids, combined, dev_genres, dev_s2b)
52
+ hf1, _, _ = hier_f1_report(gold_specific, pred_specific, gold_broad, pred_broad, print_report=False)
53
+ return hf1, combined
54
+
55
+
56
+ def main():
57
+ dev_texts = load_json(DATA_DIR / "dev.json")
58
+ dev_gold = load_json(DATA_DIR / "dev_gold.json")
59
+ dev_defs = load_json(DATA_DIR / "dev_genre_definitions.json")
60
+
61
+ dev_genres = genre_list(dev_defs)
62
+ dev_s2b = specific_to_broad(dev_defs)
63
+ dev_gold_by_id = {r["id"]: r for r in dev_gold}
64
+ dev_ids = [r["id"] for r in dev_texts]
65
+ gold_specific = [dev_gold_by_id[i]["specific_genre"] for i in dev_ids]
66
+ gold_broad = [dev_gold_by_id[i]["broad_genre"] for i in dev_ids]
67
+
68
+ softmax_mats = []
69
+ for model_name in MODELS:
70
+ genres, scores_by_id = load_scores(model_name)
71
+ assert genres == dev_genres, f"{model_name} genre order mismatch"
72
+ mat = score_dict_to_matrix(dev_ids, dev_genres, scores_by_id)
73
+ softmax_mats.append(softmax(mat))
74
+
75
+ rng = np.random.RandomState(SEED)
76
+ init_weights = np.array(S29_WEIGHTS + [0.10])
77
+ init_weights = init_weights / init_weights.sum()
78
+
79
+ best_w, best_hf1, best_mat = init_weights, -1.0, None
80
+ hf1, mat = evaluate_weights(init_weights, softmax_mats, gold_specific, gold_broad, dev_genres, dev_s2b, dev_ids)
81
+ best_hf1, best_mat = hf1, mat
82
+ print(f"[search] init weights hier_f1={hf1:.4f}", flush=True)
83
+
84
+ # random search around a Dirichlet prior centred on the S29 weights (+ S32 slot)
85
+ alpha = np.maximum(init_weights * 20, 0.5)
86
+ for t in range(N_TRIALS):
87
+ w = rng.dirichlet(alpha)
88
+ hf1, mat = evaluate_weights(w, softmax_mats, gold_specific, gold_broad, dev_genres, dev_s2b, dev_ids)
89
+ if hf1 > best_hf1:
90
+ best_hf1, best_w, best_mat = hf1, w, mat
91
+ print(f"[search] trial {t}: new best hier_f1={hf1:.4f} weights={np.round(w,3).tolist()}", flush=True)
92
+
93
+ print(f"\nBest 9-model weights found: "
94
+ f"{dict(zip(MODELS, np.round(best_w, 4).tolist()))}", flush=True)
95
+
96
+ submission, pred_specific, pred_broad = scores_to_predictions(dev_ids, best_mat, dev_genres, dev_s2b)
97
+ hier_f1, _, _ = hier_f1_report(gold_specific, pred_specific, gold_broad, pred_broad)
98
+
99
+ save_submission_and_zip(NAME, submission)
100
+ print(f"\nFINAL {NAME}: hier_f1={hier_f1:.4f} (SESSION_MEMORY.md / commit reference: 0.9917)", flush=True)
101
+
102
+ best_weights_dict = dict(zip(MODELS, np.round(best_w, 4).tolist()))
103
+ if hier_f1 >= 0.90:
104
+ package_dir = OUT_DIR / NAME / "system"
105
+ package_dir.mkdir(parents=True, exist_ok=True)
106
+ with open(package_dir / "ensemble_weights.json", "w", encoding="utf-8") as f:
107
+ json.dump(best_weights_dict, f, indent=2)
108
+ shutil.copy(__file__, package_dir / "s11_ensemble_9model.py")
109
+
110
+ readme = f"""---
111
+ tags:
112
+ - arabic-nlp
113
+ - genre-classification
114
+ - ensemble
115
+ ---
116
+
117
+ # AraGenre 2026 — S33: 9-model ensemble
118
+
119
+ No weights of its own — a combination of 9 component models' dev scores
120
+ (softmax-normalized per model, then weighted-summed). This is S29's 8-model blend
121
+ plus S32 (E5-large, X-GENRE + synthetic-data augmented).
122
+
123
+ ## Dev result
124
+ hier_f1 = {hier_f1:.4f} (reference: 0.9917, recorded only in a commit message
125
+ before the original scripts were pruned — the exact original 9-way weights were
126
+ never recorded, so these weights were re-derived via a dev-validated random
127
+ search seeded near S29's known weights, not copied from the original run)
128
+
129
+ ## Components and weights (this run)
130
+ ```json
131
+ {json.dumps(best_weights_dict, indent=2)}
132
+ ```
133
+
134
+ Standalone components with their own published repos: S24
135
+ (`HassanB4/s24-e5-mnrl`), S24b
136
+ (`HassanB4/s24b-e5-mnrl-xgenre`), S28
137
+ (`HassanB4/s28-seed{{42,123,777}}`), S32
138
+ (`HassanB4/s32-synth-augmented`). The remaining components (S1, S10,
139
+ S17-pipeline, S23, S23b) never cleared 0.90 individually and exist only as cached
140
+ score files (`rerun_2026/scores/`) plus the s01-s05 scripts.
141
+
142
+ ## Usage
143
+ Requires cached dev score files from running s01 through s09 first, then
144
+ `python s11_ensemble_9model.py`.
145
+ """
146
+ (package_dir / "README.md").write_text(readme, encoding="utf-8")
147
+ push_to_hf(package_dir, "s33-ensemble", hier_f1)
148
+
149
+
150
+ if __name__ == "__main__":
151
+ main()