Lesson 6 of 10
Operator smoothing
A level-2 stacker that uses the predictions of each operator's other listings.
Outputs will appear here after the full run. The code is final.
Left: what & whyRight: the code and its real output
6. Level-2 stacker: operator smoothing
For each listing, we average the level-1 predictions of the same operator's other listings (leave-one-out), and of other listings at the same coordinates. A small LightGBM learns how much to trust these, per metro and access type. With grouped folds every operator sits entirely inside one validation fold, so this is measured honestly.
prog.start("level-2 operator stacker") PROB_MODELS = [k for k in OOF if OOF[k].shape[1] == K] REG_MODELS = [k for k in OOF if OOF[k].shape[1] == 1] def loo_mean(P, key): _, inv = np.unique(key, return_inverse=True) S = np.zeros((inv.max() + 1, P.shape[1])); np.add.at(S, inv, P) other = np.bincount(inv)[inv] - 1 M = (S[inv] - P) / np.maximum(other, 1)[:, None] M[other == 0] = np.nan return M, other def stack_inputs(store, host_ids, bld_ids, Xsrc): parts = [store[k] for k in PROB_MODELS + REG_MODELS] avg = np.mean([store[k] for k in PROB_MODELS], axis=0) summary = np.column_stack([avg] + [store[k] for k in REG_MODELS]) H, hn = loo_mean(summary, host_ids) B, bn = loo_mean(summary, bld_ids) ctx = Xsrc[:, [ALL_COLS.index(c) for c in ("metro", "access_type", "max_seats")]] return np.column_stack(parts + [H, hn, np.log1p(hn), B, bn, ctx]).astype(np.float32) Z = stack_inputs(OOF, host[:n_tr], bld[:n_tr] * FOLDS + fold_id, X_full) # buildings: only rows predicted together Zt = stack_inputs(TEST, host[n_tr:], bld[n_tr:], Xt_full) stack_name = f"{TAG}_{SET_TAG}_stack_{'-'.join(MODELS)}" ck = ck_load(stack_name) if ck is None: P2 = dict(objective="multiclass", num_class=K, learning_rate=0.05, num_leaves=31, min_child_samples=200, feature_fraction=0.9, bagging_fraction=0.8, bagging_freq=1, verbose=-1, seed=SEED, num_threads=N_THREADS) oof2, test2 = np.zeros((n_tr, K)), np.zeros((len(Zt), K)) for f, v in folds: m = lgb.train(P2, lgb.Dataset(Z[f], y[f]), 3000, valid_sets=[lgb.Dataset(Z[v], y[v])], callbacks=[lgb.early_stopping(100, verbose=False)]) oof2[v] = m.predict(Z[v], num_iteration=m.best_iteration) test2 += m.predict(Zt, num_iteration=m.best_iteration) / FOLDS ck = {"oof": oof2, "test": test2} ck_save(stack_name, **ck) prog.finish() else: prog.finish(from_checkpoint=True) OOF["stack"], TEST["stack"] = ck["oof"], ck["test"] print(f"stack grouped CV macro F1 (plain argmax): {fast_f1(y, OOF['stack'].argmax(1)):.4f}")