Workspace Price Tiers the competition, step by step

v3 · Colab run · part 5 of 7

Operator smoothing

The notebook exactly as it ran on Colab's T4 GPU, with its real outputs.

Download the executed notebook

Left: what & whyRight: the code and its real output

5. Level-2 stacker: operator smoothing (H5)

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}")
▶ level-2 operator stacker   [█████████████████████████░░░░░ 18/21 (86%)]
✓ level-2 operator stacker: took 2.7 min | [███████████████████████████░░░ 19/21 (90%)] | elapsed 29.1 min | ETA ~0 min + 2 steps not timed yet
stack      grouped CV macro F1 (plain argmax): 0.7204