Skip to content

Commit 228d479

Browse files
committed
Add V135 runtime asset compiler
1 parent 5d0385f commit 228d479

1 file changed

Lines changed: 56 additions & 0 deletions

File tree

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#!/usr/bin/env python3
2+
from pathlib import Path
3+
import argparse,json
4+
import numpy as np
5+
from scipy.sparse import csr_matrix,hstack
6+
from sklearn.feature_extraction.text import HashingVectorizer
7+
from sklearn.linear_model import LogisticRegression
8+
from sklearn.model_selection import GroupKFold
9+
from v75_canonical_trajectory import load_training,trajectory_views,SEED
10+
from v71_mastery_events import load_transcript
11+
from v94_related_control import segmented_control
12+
from v135_nested_supported_stack import prior_apply,feats,fit_stack,ALPHA,EPS
13+
14+
BASE_C=.25
15+
16+
def fit_base_model(X,y): return LogisticRegression(C=BASE_C,max_iter=300,solver='liblinear',random_state=SEED).fit(X,y)
17+
def pred(m,X): return np.clip(m.predict_proba(X)[:,1],EPS,1-EPS)
18+
19+
def main(a):
20+
f=load_training(a.features,a.labels).reset_index(drop=True); y=f.target.to_numpy(int)
21+
sessions=f.session_id.astype(str).to_numpy(); support=f.learning_objective.astype(str).to_numpy()
22+
cache={sid:load_transcript(a.transcripts/f'{sid}.csv') for sid in np.unique(sessions)}
23+
views=[]; nums=[]; rt=[]; rz=[]
24+
for i,r in f.iterrows():
25+
v,n,_=trajectory_views(cache[str(r.session_id)],str(r.learning_objective)); views.append(v); nums.append(n)
26+
t,z=segmented_control(cache[str(r.session_id)],str(r.learning_objective),'related');rt.append(t);rz.append(z)
27+
if (i+1)%5000==0: print('ROWS',i+1,flush=True)
28+
hv75=HashingVectorizer(n_features=2**18,alternate_sign=False,norm='l2',ngram_range=(1,2),lowercase=True)
29+
parts=[hv75.transform([f'[OBJECTIVE] {x}' for x in f.learning_objective])]
30+
for k in views[0].keys(): parts.append(hv75.transform([f'[{k.upper()}] '+v[k] for v in views]))
31+
N=np.vstack(nums).astype(float); nmean=N.mean(0); nstd=N.std(0)+1e-6; parts.append(csr_matrix((N-nmean)/nstd)); X75=hstack(parts,format='csr')
32+
hvr=HashingVectorizer(n_features=2**17,alternate_sign=False,norm='l2',ngram_range=(1,2),lowercase=True)
33+
R=np.vstack(rz).astype(float); rmean=R.mean(0); rstd=R.std(0)+1e-6; Xr=hstack([hvr.transform(rt),csr_matrix((R-rmean)/rstd)],format='csr')
34+
print('MATRICES',X75.shape,Xr.shape,flush=True)
35+
# Session-OOF component field for leakage-safe final stack training.
36+
p75=np.zeros(len(y)); pr=np.zeros(len(y)); pp=np.zeros(len(y)); cc=np.zeros(len(y)); seen=np.zeros(len(y),bool)
37+
for k,(tr,va) in enumerate(GroupKFold(4).split(np.zeros(len(y)),y,sessions),1):
38+
m75=fit_base_model(X75[tr],y[tr]); mr=fit_base_model(Xr[tr],y[tr]); p75[va]=pred(m75,X75[va]); pr[va]=pred(mr,Xr[va])
39+
q,c,s=prior_apply(y,tr,va,support);pp[va]=q;cc[va]=c;seen[va]=s;print('OOF',k,flush=True)
40+
stack=fit_stack(feats(p75[seen],pr[seen],pp[seen],cc[seen],True),y[seen])
41+
final75=fit_base_model(X75,y); finalr=fit_base_model(Xr,y)
42+
global_mean=float(y.mean()); sums={};counts={}
43+
for k,v in zip(support,y): sums[k]=sums.get(k,0.0)+float(v);counts[k]=counts.get(k,0)+1
44+
out=Path(a.out);out.mkdir(parents=True,exist_ok=True)
45+
np.savez_compressed(out/'v135_assets.npz',v75_coef=final75.coef_.ravel(),v75_intercept=final75.intercept_,v75_num_mean=nmean,v75_num_std=nstd,
46+
related_coef=finalr.coef_.ravel(),related_intercept=finalr.intercept_,related_num_mean=rmean,related_num_std=rstd,
47+
stack_coef=stack.coef_.ravel(),stack_intercept=stack.intercept_)
48+
man={'protocol':'V141_V135_RUNTIME_ASSETS','rows':len(y),'base_C':BASE_C,'stack_C':0.10,'prior_alpha':ALPHA,'global_mean':global_mean,
49+
'objective_counts':counts,'objective_sums':sums,'stack_training':'4-fold session-grouped OOF supported rows only','seed':SEED}
50+
(out/'manifest.json').write_text(json.dumps(man,indent=2))
51+
# Save a small exact component fixture for parity checks.
52+
idx=np.arange(min(512,len(y)))
53+
np.savez_compressed(out/'parity_fixture.npz',idx=idx,p75=p75[idx],related=pr[idx],prior=pp[idx],count=cc[idx],seen=seen[idx],y=y[idx])
54+
print(json.dumps({'rows':len(y),'supported_oof':int(seen.sum()),'assets':str(out)},indent=2),flush=True)
55+
if __name__=='__main__':
56+
p=argparse.ArgumentParser();p.add_argument('--features',type=Path,required=True);p.add_argument('--labels',type=Path,required=True);p.add_argument('--transcripts',type=Path,required=True);p.add_argument('--out',type=Path,required=True);main(p.parse_args())

0 commit comments

Comments
 (0)