yeepee-ctr / parallel_v6 /src /lrfeat2.py
Eric-Yyvuw's picture
sync 2026-10-25 14:22 GMT | best val AUC 0.813210
6861f5b verified
Raw History Blame Contribute Delete
4.46 kB
"""F1 (data_dig_v1): history-centroid cosine in an exact low-rank SVD space. Train-only, rebuilt at each fold cutoff C.
Matrix: users x items, binary, rows = each user's last N distinct history clicks (from train events with ts < C),
columns scaled by 1/sqrt(pop+5); exact svds (ARPACK) k=17; item vectors = comps 2..K+1 (popularity comp dropped) * s.
Feature: cos(candidate vector, sum of own-history normalised vectors). Within-event _z and _dmax. Eval: fitted at J27 on train only.
Usage: python lrfeat.py NAME... -> feat_lr/NAME.parquet (rowid, lr{K}_cos/_z/_dmax for K in 10,12,16)"""
import sys, os, time, numpy as np, polars as pl, scipy.sparse as sp
from scipy.sparse.linalg import svds
sys.path.insert(0, '/workspace/huawei/v3')
from features import load
from split import J20, DAY, first_events
TE, TR, EE, ER, NI, NU = load('/workspace/huawei/v3/data')
N = int(os.environ.get('LR_N', 40)); OUT = os.environ.get('LR_OUT', '/workspace/huawei/v2work/feat_lr2'); os.makedirs(OUT, exist_ok=True)
KS = tuple(int(x) for x in os.environ.get('LR_KS', '10,12,16').split(','))
CON = int(os.environ.get('LR_CON', 0))
SPEC = {**{f'f{k}': (J20 + k * DAY, DAY) for k in range(1, 7)}, 'val25': (J20 + 5 * DAY, None), 'val26': (J20 + 6 * DAY, None)}
_V = {}
def vecs(C):
if C in _V: return _V[C]
h = (TE.filter(pl.col('ts') < C).select('u', 'h', 'h_ts').explode(['h', 'h_ts']).drop_nulls('h')
.select('u', pl.col('h').cast(pl.Int32).alias('ii'), pl.col('h_ts').cast(pl.Int64).alias('t')).unique().sort('u', 't', 'ii'))
h = h.with_columns(pl.int_range(pl.len()).over('u').alias('p'), pl.len().over('u').alias('L')).filter(pl.col('L') - pl.col('p') <= N).unique(['u', 'ii'])
M = sp.csr_matrix((np.ones(h.height, np.float32), (h['u'].to_numpy(), h['ii'].to_numpy())), shape=(NU, NI))
pop = np.asarray(M.sum(0)).ravel(); M = M.multiply(1 / np.sqrt(pop + 5)).tocsr()
_, s, vt = svds(M, k=max(max(KS) + 1, 17), random_state=0); o = np.argsort(-s); s, vt = s[o], vt[o]
print(time.strftime('%T'), 'C', C, 'pairs', h.height, 'sv', np.round(s[:15], 2), flush=True)
_V[C] = (s, vt); return _V[C]
def build(C, T_ev, T_rows):
s, vt = vecs(C)
E = T_ev.select('eid', 'h').with_row_index('k'); kk = E.select('eid', 'k')
H = E.select('k', 'h').explode('h').drop_nulls('h'); hk = H['k'].to_numpy(); hi = H['h'].to_numpy().astype(np.int64)
R = T_rows.select('rowid', 'eid', 'ii').join(kk, on='eid', how='left'); rk = R['k'].to_numpy(); ri = R['ii'].to_numpy().astype(np.int64)
F = {}
if CON:
P_ev = TE.filter(pl.col('ts') < C).select('eid', 'u')
ng = TR.filter(pl.col('clicked') == 0).join(P_ev, on='eid', how='inner').select('u', 'ii').unique()
ue = T_ev.select('u').with_row_index('k').join(ng, on='u', how='inner'); nk = ue['k'].to_numpy(); ni = ue['ii'].to_numpy().astype(np.int64)
for K in KS:
V = (vt[1:K + 1].T * s[1:K + 1]); V = V / (np.linalg.norm(V, axis=1, keepdims=True) + 1e-9)
hc = np.zeros((E.height, K)); np.add.at(hc, hk, V[hi])
hcr = hc[rk]; nr = np.linalg.norm(hcr, axis=1)
x = np.where(nr > 0, (V[ri] * hcr).sum(1) / (nr + 1e-9), np.nan)
F[f'lr{K}_cos'] = x.astype(np.float32)
if CON:
nc = np.zeros((E.height, K)); np.add.at(nc, nk, V[ni])
hn = hc / (np.linalg.norm(hc, axis=1, keepdims=True) + 1e-9); nn = nc / (np.linalg.norm(nc, axis=1, keepdims=True) + 1e-9)
for al, nm in ((0.5, '05'), (1.0, '1')):
d = (hn - al * nn)[rk]; dn = np.linalg.norm(d, axis=1)
F[f'lr{K}_con{nm}'] = np.where(dn > 0, (V[ri] * d).sum(1) / (dn + 1e-9), np.nan).astype(np.float32)
R = R.select('rowid', 'eid').with_columns([pl.Series(c, v).fill_nan(None) for c, v in F.items()])
R = R.with_columns([((pl.col(c) - pl.col(c).mean().over('eid')) / (pl.col(c).std().over('eid') + 1e-6)).alias(c.replace('_cos', '_z') if c.endswith('_cos') else c + '_z') for c in F] +
[(pl.col(c) - pl.col(c).max().over('eid')).alias(c.replace('_cos', '_dmax') if c.endswith('_cos') else c + '_dmax') for c in F])
return R.drop('eid')
for n in sys.argv[1:]:
t0 = time.time()
if n == 'eval': R = build(J20 + 7 * DAY, EE, ER)
else:
C, W = SPEC[n]; T_ev = first_events(TE, C, W); R = build(C, T_ev, TR.join(T_ev.select('eid'), on='eid', how='semi'))
R.write_parquet(f'{OUT}/{n}.parquet'); print(n, R.shape, f'{time.time()-t0:.0f}s', flush=True)