tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.cat /app/train_and_predict.py
#!/usr/bin/env python3
"""Starter baseline for the Airfoil Self-Noise surrogate task.
This deliberately modest Ridge model is useful as a working end-to-end
baseline, but it is not strong enough to clear the hidden grouped split.
"""
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
DATA_DIR = Path("/app/data")
OUTPUT_DIR = Path("/app/output")
TARGET = "scaled_sound_pressure_level"
PREDICTION = "predicted_scaled_sound_pressure_level"
FEATURES = [
"frequency",
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
class AirfoilFeatures(BaseEstimator, TransformerMixin):
def fit(self, X, y=None):
return self
def transform(self, X):
frame = pd.DataFrame(X, columns=FEATURES if not isinstance(X, pd.DataFrame) else X.columns)
frequency = frame["frequency"].astype(float)
angle = frame["attack_angle"].astype(float)
chord = frame["chord_length"].astype(float)
velocity = frame["free_stream_velocity"].astype(float)
thickness = frame["suction_side_displacement_thickness"].astype(float)
log_frequency = np.log10(frequency)
log_thickness = np.log10(thickness)
out = pd.DataFrame(index=frame.index)
out["frequency"] = frequency
out["attack_angle"] = angle
out["chord_length"] = chord
out["free_stream_velocity"] = velocity
out["suction_side_displacement_thickness"] = thickness
out["log_frequency"] = log_frequency
out["log_thickness"] = log_thickness
out["freq_velocity"] = log_frequency * velocity
out["freq_angle"] = log_frequency * angle
out["angle_velocity"] = angle * velocity
out["thickness_angle"] = log_thickness * angle
out["chord_velocity"] = chord * velocity
return out.to_numpy(dtype=float)
def load_labeled_data() -> pd.DataFrame:
parts = [pd.read_csv(DATA_DIR / "train.csv")]
validation_path = DATA_DIR / "validation.csv"
if validation_path.exists():
parts.append(pd.read_csv(validation_path))
return pd.concat(parts, ignore_index=True)
def load_evaluation_features() -> pd.DataFrame:
test_path = DATA_DIR / "test_features.csv"
if test_path.exists():
return pd.read_csv(test_path)
validation = pd.read_csv(DATA_DIR / "validation.csv")
return validation.drop(columns=[TARGET])
def main() -> None:
labeled = load_labeled_data()
evaluation = load_evaluation_features()
model = make_pipeline(
AirfoilFeatures(),
StandardScaler(),
Ridge(alpha=10.0),
)
model.fit(labeled[FEATURES], labeled[TARGET])
predictions = model.predict(evaluation[FEATURES])
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
pd.DataFrame(
{
"sample_id": evaluation["sample_id"].astype(str),
PREDICTION: predictions,
}
).to_csv(OUTPUT_DIR / "predictions.csv", index=False)
if __name__ == "__main__":
main()cd /app && ls -la data/ && echo "---TRAIN---" && head -5 data/train.csv && echo "..." && wc -l data/train.csv && echo "---VALIDATION---" && head -5 data/validation.csv && wc -l data/validation.csv && echo "---TEST---" && head -5 data/test_features.csv && wc -l data/test_features.csv
total 128 drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 . drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 05:20 .. -rw-rw---- 1 h2tagent h2tagent 3595 Jun 21 05:18 test_features.csv -rw-rw---- 1 h2tagent h2tagent 97417 Jun 21 05:18 train.csv -rw-rw---- 1 h2tagent h2tagent 20351 Jun 21 05:18 validation.csv ---TRAIN--- sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness,scaled_sound_pressure_level af_train_0000,5003.72368377,0.034997843085,0.101695197262,71.2730786718,0.00120791730525,131.375168508 af_train_0001,1997.22544774,15.6337827466,0.101571096822,71.3314610261,0.0436885053477,119.435701427 af_train_0002,1248.10938712,9.47742633424,0.0253695328899,31.6576914656,0.00461706652486,131.137445156 af_train_0003,200.228488983,15.5929878809,0.101720824729,39.5544682279,0.0527780201324,124.741187675 ... 957 data/train.csv ---VALIDATION--- sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness,scaled_sound_pressure_level af_val_0000,1998.53240303,12.3252028109,0.101492415739,39.5853154533,0.040726689388,115.836432573 af_val_0001,629.718430551,0.0186262062548,0.152498614376,39.5692331074,0.00193529208188,124.213347611 af_val_0002,12482.9663422,0.0105284256024,0.0253720007765,71.3111171362,0.000400617998197,133.621156597 af_val_0003,1600.32755515,0.0338230474173,0.0253792825467,39.6448710656,0.000429527467403,123.534534482 202 data/validation.csv ---TEST--- sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness af_val_0000,1998.53240303,12.3252028109,0.101492415739,39.5853154533,0.040726689388 af_val_0001,629.718430551,0.0186262062548,0.152498614376,39.5692331074,0.00193529208188 af_val_0002,12482.9663422,0.0105284256024,0.0253720007765,71.3111171362,0.000400617998197 af_val_0003,1600.32755515,0.0338230474173,0.0253792825467,39.6448710656,0.000429527467403 41 data/test_features.csv
cd /app && python3 -c "
import sys
print('python', sys.version)
for m in ['numpy','pandas','sklearn','scipy','xgboost','lightgbm','catboost','torch']:
try:
mod=__import__(m)
print(m, getattr(mod,'__version__','?'))
except Exception as e:
print(m, 'NOT AVAILABLE')
"python 3.12.3 (main, Mar 23 2026, 19:04:32) [GCC 13.3.0] numpy 1.26.4 pandas 2.2.2 sklearn 1.4.2 scipy 1.17.1 xgboost NOT AVAILABLE lightgbm NOT AVAILABLE catboost NOT AVAILABLE torch NOT AVAILABLE
cd /app && python3 -c "
import pandas as pd, numpy as np
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
print('train shape', tr.shape, 'val shape', va.shape)
print()
print('TRAIN describe:')
print(tr.describe().T)
print()
print('target stats train:', tr['scaled_sound_pressure_level'].min(), tr['scaled_sound_pressure_level'].max(), tr['scaled_sound_pressure_level'].mean(), tr['scaled_sound_pressure_level'].std())
print('target stats val: ', va['scaled_sound_pressure_level'].min(), va['scaled_sound_pressure_level'].max(), va['scaled_sound_pressure_level'].mean(), va['scaled_sound_pressure_level'].std())
"train shape (956, 7) val shape (201, 7)
TRAIN describe:
count ... max
frequency 956.0 ... 20025.582040
attack_angle 956.0 ... 22.234770
chord_length 956.0 ... 0.305164
free_stream_velocity 956.0 ... 71.406950
suction_side_displacement_thickness 956.0 ... 0.058556
scaled_sound_pressure_level 956.0 ... 140.289040
[6 rows x 8 columns]
target stats train: 104.935001617 140.289040194 125.40245300891945 6.70414571717735
target stats val: 109.788714074 138.661406742 126.48301733910945 6.979769251741283cd /app && python3 -c "
import pandas as pd, numpy as np
pd.set_option('display.width',200)
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
feats=['frequency','attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']
for c in feats:
print(c, 'n_unique train', tr[c].nunique(), 'val', va[c].nunique())
print()
# Condition = (attack_angle, chord_length, free_stream_velocity)
def cond(df):
return set(zip(np.round(df['attack_angle'],3), np.round(df['chord_length'],5), np.round(df['free_stream_velocity'],2)))
ctr=cond(tr); cva=cond(va)
print('n conditions train (rounded)', len(ctr))
print('n conditions val (rounded)', len(cva))
print('overlap', len(ctr & cva))
"frequency n_unique train 956 val 201 attack_angle n_unique train 956 val 201 chord_length n_unique train 956 val 201 free_stream_velocity n_unique train 956 val 201 suction_side_displacement_thickness n_unique train 956 val 201 n conditions train (rounded) 956 n conditions val (rounded) 201 overlap 0
cd /app && python3 -c "
import pandas as pd, numpy as np
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
alld=pd.concat([tr,va],ignore_index=True)
for c in ['chord_length','free_stream_velocity']:
v=np.sort(alld[c].values)
print('===',c,'===')
# find gaps
d=np.diff(v)
# cluster by gap threshold
thr=(v.max()-v.min())*0.02
groups=[]; cur=[v[0]]
for i in range(1,len(v)):
if v[i]-v[i-1]>thr:
groups.append(cur); cur=[v[i]]
else:
cur.append(v[i])
groups.append(cur)
print('n clusters', len(groups))
for g in groups:
print(f' center={np.mean(g):.5f} n={len(g)} min={min(g):.5f} max={max(g):.5f} spread={max(g)-min(g):.6f}')
"=== chord_length === n clusters 6 center=0.02540 n=235 min=0.02537 max=0.02543 spread=0.000061 center=0.05080 n=185 min=0.05074 max=0.05086 spread=0.000121 center=0.10160 n=250 min=0.10148 max=0.10172 spread=0.000243 center=0.15240 n=169 min=0.15222 max=0.15258 spread=0.000363 center=0.22860 n=193 min=0.22833 max=0.22887 spread=0.000546 center=0.30481 n=125 min=0.30444 max=0.30516 spread=0.000728 === free_stream_velocity === n clusters 4 center=31.70228 n=224 min=31.65246 max=31.74755 spread=0.095093 center=39.59638 n=371 min=39.54061 max=39.65940 spread=0.118794 center=55.49930 n=222 min=55.41676 max=55.58321 spread=0.166454 center=71.29478 n=340 min=71.19306 max=71.40695 spread=0.213888
cd /app && python3 -c "
import pandas as pd, numpy as np
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
alld=pd.concat([tr,va],ignore_index=True)
c='attack_angle'
v=np.sort(alld[c].values)
thr=0.2
groups=[]; cur=[v[0]]
for i in range(1,len(v)):
if v[i]-v[i-1]>thr:
groups.append(cur); cur=[v[i]]
else:
cur.append(v[i])
groups.append(cur)
print('n clusters', len(groups))
for g in groups:
print(f' center={np.mean(g):.4f} n={len(g)} spread={max(g)-min(g):.4f}')
"n clusters 21 center=-0.0015 n=228 spread=0.0700 center=1.4978 n=31 spread=0.0697 center=2.0008 n=56 spread=0.0700 center=3.0021 n=30 spread=0.0697 center=3.3000 n=50 spread=0.0700 center=4.0426 n=99 spread=0.2698 center=4.8037 n=38 spread=0.0698 center=5.3516 n=28 spread=0.1675 center=6.7002 n=33 spread=0.0700 center=7.2597 n=81 spread=0.1700 center=8.3993 n=34 spread=0.0698 center=8.9011 n=32 spread=0.0698 center=9.4975 n=40 spread=0.0700 center=9.9007 n=48 spread=0.0700 center=11.2028 n=28 spread=0.0698 center=12.3014 n=64 spread=0.0700 center=12.6523 n=66 spread=0.1698 center=15.4762 n=79 spread=0.2700 center=17.4001 n=63 spread=0.0700 center=19.6936 n=14 spread=0.0628 center=22.2038 n=15 spread=0.0672
cd /app && python3 -c "
import pandas as pd, numpy as np
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
def snap1d(values, thr_rel=0.01, thr_abs=0.15):
# cluster sorted values, return mapping array of cluster centers
order=np.argsort(values); v=values[order]
centers=np.empty_len=len(v)
labels=np.empty(len(v),dtype=int)
cur=0; start=0
rng=v.max()-v.min()
thr=max(rng*thr_rel, thr_abs)
for i in range(1,len(v)):
if v[i]-v[i-1]>thr:
cur+=1
labels[i]=cur
out=np.empty(len(v))
res=np.empty(len(values))
for lab in range(cur+1):
mask=labels==lab
c=v[mask].mean()
res[order[mask]]=c
return res
alld=pd.concat([tr,va],ignore_index=True)
for c,thr in [('attack_angle',0.12),('chord_length',0.005),('free_stream_velocity',0.5)]:
alld[c+'_s']=snap1d(alld[c].values, thr_rel=0.0, thr_abs=thr)
alld['cond']=list(zip(np.round(alld['attack_angle_s'],3),np.round(alld['chord_length_s'],5),np.round(alld['free_stream_velocity_s'],2)))
ntr=len(tr)
ctr=set(alld['cond'][:ntr]); cva=set(alld['cond'][ntr:])
print('n cond train', len(ctr), 'n cond val', len(cva), 'overlap', len(ctr&cva))
print('total distinct conditions', len(ctr|cva))
# rows per condition in val
from collections import Counter
cc=Counter(alld['cond'][ntr:])
print('val rows per condition: min',min(cc.values()),'max',max(cc.values()),'mean',np.mean(list(cc.values())))
print('n val conditions', len(cc))
"n cond train 69 n cond val 16 overlap 0 total distinct conditions 85 val rows per condition: min 1 max 18 mean 12.5625 n val conditions 16
cd /app && python3 -c "
import pandas as pd, numpy as np
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
def snap1d(values, thr):
order=np.argsort(values); v=values[order]; labels=np.zeros(len(v),dtype=int); cur=0
for i in range(1,len(v)):
if v[i]-v[i-1]>thr: cur+=1
labels[i]=cur
res=np.empty(len(values))
for lab in range(cur+1):
m=labels==lab; res[order[m]]=v[m].mean()
return res
alld=pd.concat([tr,va],ignore_index=True)
for c,thr in [('attack_angle',0.12),('chord_length',0.005),('free_stream_velocity',0.5)]:
alld[c+'_s']=snap1d(alld[c].values,thr)
alld['cond']=list(zip(np.round(alld['attack_angle_s'],3),np.round(alld['chord_length_s'],5),np.round(alld['free_stream_velocity_s'],2)))
# within condition, how much does thickness vary relative to mean?
g=alld.groupby('cond')['suction_side_displacement_thickness']
rel=(g.std()/g.mean()).dropna()
print('within-condition thickness rel-std: median',rel.median(),'max',rel.max())
# how many freq per condition
print('freqs per cond: mean', alld.groupby('cond').size().mean())
# Does thickness map 1:1 to condition? check unique thickness snapped per cond
print()
# range of freq
print('freq range', alld['frequency'].min(), alld['frequency'].max())
"within-condition thickness rel-std: median 0.0017892622970018947 max 0.002060770659878632 freqs per cond: mean 14.109756097560975 freq range 199.660257517 20025.5820397
cd /app && cat > /app/harness.py << 'PY'
import numpy as np, pandas as pd
from pathlib import Path
DATA_DIR = Path("/app/data")
TARGET = "scaled_sound_pressure_level"
FEATURES = ["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
def snap1d(values, thr):
order=np.argsort(values); v=values[order]; labels=np.zeros(len(v),dtype=int); cur=0
for i in range(1,len(v)):
if v[i]-v[i-1]>thr: cur+=1
labels[i]=cur
res=np.empty(len(values))
for lab in range(cur+1):
m=labels==lab; res[order[m]]=v[m].mean()
return res
def add_conditions(df):
df=df.copy()
a=snap1d(df['attack_angle'].values,0.12)
c=snap1d(df['chord_length'].values,0.005)
v=snap1d(df['free_stream_velocity'].values,0.5)
df['cond']=list(zip(np.round(a,3),np.round(c,5),np.round(v,2)))
return df
def metrics(y_true, y_pred, cond):
y_true=np.asarray(y_true,float); y_pred=np.asarray(y_pred,float)
err=y_pred-y_true
rmse=np.sqrt(np.mean(err**2)); mae=np.mean(np.abs(err))
dfc=pd.DataFrame({'e':err,'c':cond})
per=dfc.groupby('c')['e'].apply(lambda s: np.sqrt(np.mean(s.values**2)))
return {
'rmse':rmse,'mae':mae,
'p90':np.percentile(per.values,90),
'p95':np.percentile(per.values,95),
'max':per.max(),
'n_cond':len(per),
}
TARGETS={'rmse':2.25,'mae':1.55,'p90':2.35,'p95':4.75,'max':5.60}
def report(name, m):
flags=''.join('OK ' if m[k]<=TARGETS[k] else 'XX ' for k in ['rmse','mae','p90','p95','max'])
print(f"{name:28s} rmse={m['rmse']:.3f} mae={m['mae']:.3f} p90={m['p90']:.3f} p95={m['p95']:.3f} max={m['max']:.3f} | {flags}")
def load():
tr=pd.read_csv(DATA_DIR/'train.csv'); va=pd.read_csv(DATA_DIR/'validation.csv')
return tr,va
PY
echo writtenwritten
cd /app && cat > /app/exp.py << 'PY'
import numpy as np, pandas as pd
from harness import *
from sklearn.ensemble import RandomForestRegressor, ExtraTreesRegressor, HistGradientBoostingRegressor, GradientBoostingRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import Ridge
from sklearn.svm import SVR
def fe(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); v=df['free_stream_velocity'].astype(float)
t=df['suction_side_displacement_thickness'].astype(float)
lf=np.log10(f); lt=np.log10(t); lc=np.log10(c)
out=pd.DataFrame(index=df.index)
out['lf']=lf; out['a']=a; out['lc']=lc; out['v']=v; out['lt']=lt
out['c']=c; out['f']=f; out['t']=t
# strouhal-like
out['st']=lf+lt # log(f*delta)
out['st2']=lf+lt-np.log10(v)
out['lf2']=lf**2; out['lf3']=lf**3
out['a_lf']=a*lf; out['a2']=a**2
out['lc_lf']=lc*lf; out['lt_lf']=lt*lf
out['v_lf']=v*lf
out['mach']=v/340.0
return out.values
tr,va=load()
va=add_conditions(va)
Xtr=fe(tr); ytr=tr[TARGET].values
Xva=fe(va); yva=va[TARGET].values
models={
'Ridge': make_pipeline(StandardScaler(),Ridge(alpha=10.0)),
'RF': RandomForestRegressor(n_estimators=400,min_samples_leaf=2,n_jobs=-1,random_state=0),
'ExtraTrees': ExtraTreesRegressor(n_estimators=500,min_samples_leaf=1,n_jobs=-1,random_state=0),
'HGB': HistGradientBoostingRegressor(max_iter=500,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0),
'GBR': GradientBoostingRegressor(n_estimators=500,learning_rate=0.05,max_depth=3,subsample=0.8,random_state=0),
'MLP': make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),activation='relu',alpha=1e-3,max_iter=2000,random_state=0)),
'SVR': make_pipeline(StandardScaler(),SVR(C=30,gamma='scale',epsilon=0.1)),
}
for name,mdl in models.items():
mdl.fit(Xtr,ytr)
p=mdl.predict(Xva)
report(name, metrics(yva,p,va['cond'].values))
PY
python3 /app/exp.pyRidge rmse=4.559 mae=3.541 p90=6.448 p95=7.103 max=7.202 | XX XX XX XX XX RF rmse=2.597 mae=2.044 p90=3.579 p95=3.718 max=3.775 | XX XX XX OK OK ExtraTrees rmse=2.070 mae=1.589 p90=3.072 p95=3.371 max=3.402 | OK XX XX OK OK HGB rmse=2.397 mae=1.896 p90=3.493 p95=3.607 max=3.784 | XX XX XX OK OK GBR rmse=2.594 mae=2.013 p90=3.816 p95=3.945 max=3.984 | XX XX XX OK OK MLP rmse=2.732 mae=2.031 p90=4.019 p95=4.909 max=6.131 | XX XX XX XX XX SVR rmse=2.743 mae=1.934 p90=4.057 p95=4.606 max=5.695 | XX XX XX OK XX
cd /app && python3 -c "
import numpy as np, pandas as pd
from harness import *
from exp import fe
from sklearn.ensemble import ExtraTreesRegressor
tr,va=load(); va=add_conditions(va)
Xtr=fe(tr); ytr=tr[TARGET].values; Xva=fe(va); yva=va[TARGET].values
m=ExtraTreesRegressor(n_estimators=500,min_samples_leaf=1,n_jobs=-1,random_state=0).fit(Xtr,ytr)
p=m.predict(Xva)
va=va.copy(); va['e']=p-yva
per=va.groupby('cond').apply(lambda d: pd.Series({'rmse':np.sqrt(np.mean(d['e']**2)),'n':len(d),'mean_spl':d[TARGET].mean(),'ang':d['attack_angle'].mean(),'chord':d['chord_length'].mean(),'vel':d['free_stream_velocity'].mean()}))
per=per.sort_values('rmse',ascending=False)
pd.set_option('display.width',200)
print(per)
"Ridge rmse=4.559 mae=3.541 p90=6.448 p95=7.103 max=7.202 | XX XX XX XX XX
RF rmse=2.597 mae=2.044 p90=3.579 p95=3.718 max=3.775 | XX XX XX OK OK
ExtraTrees rmse=2.070 mae=1.589 p90=3.072 p95=3.371 max=3.402 | OK XX XX OK OK
HGB rmse=2.397 mae=1.896 p90=3.493 p95=3.607 max=3.784 | XX XX XX OK OK
GBR rmse=2.594 mae=2.013 p90=3.816 p95=3.945 max=3.984 | XX XX XX OK OK
MLP rmse=2.732 mae=2.031 p90=4.019 p95=4.909 max=6.131 | XX XX XX XX XX
SVR rmse=2.743 mae=1.934 p90=4.057 p95=4.606 max=5.695 | XX XX XX OK XX
rmse n ... chord vel
cond ...
(17.399, 0.0254, 39.6) 3.401998 15.0 ... 0.025400 39.582282
(-0.0, 0.0254, 71.3) 3.358273 10.0 ... 0.025399 71.354087
(12.301, 0.1016, 55.51) 2.641714 16.0 ... 0.101604 55.514674
(12.301, 0.1016, 39.6) 2.445574 16.0 ... 0.101606 39.584861
(-0.0, 0.30485, 31.7) 2.076861 18.0 ... 0.304850 31.696343
(6.682, 0.1016, 55.51) 2.027149 8.0 ... 0.101618 55.497174
(4.801, 0.0254, 39.6) 1.978967 14.0 ... 0.025402 39.581086
(8.404, 0.0508, 55.51) 1.947643 12.0 ... 0.050814 55.542907
(4.002, 0.2286, 31.7) 1.774858 15.0 ... 0.228600 31.682930
(4.801, 0.0254, 71.3) 1.365986 11.0 ... 0.025397 71.304755
(-0.0, 0.0254, 39.6) 1.330480 11.0 ... 0.025397 39.627032
(3.312, 0.1016, 71.3) 1.210592 12.0 ... 0.101572 71.263363
(-0.0, 0.0508, 55.51) 1.142106 13.0 ... 0.050792 55.497693
(9.901, 0.1524, 31.7) 1.077247 16.0 ... 0.152411 31.714249
(-0.0, 0.1524, 39.6) 1.073233 14.0 ... 0.152387 39.622576
[15 rows x 6 columns]
[stderr]
<string>:11: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.cd /app && cat > /app/cv.py << 'PY'
import numpy as np, pandas as pd
from harness import *
from exp import fe
from sklearn.model_selection import GroupKFold
def get_all():
tr,va=load()
alld=pd.concat([tr,va],ignore_index=True)
alld=add_conditions(alld)
return alld
def cv_eval(make_model, n_splits=10, seed=0, transform=fe, verbose=True):
alld=get_all()
X=transform(alld); y=alld[TARGET].values
groups=alld['cond'].values
uniq=pd.unique(groups)
# map conditions to folds
rng=np.random.RandomState(seed)
perm=rng.permutation(len(uniq))
fold_of={c:(i%n_splits) for i,c in enumerate(uniq[perm])}
folds=np.array([fold_of[c] for c in groups])
oof=np.zeros(len(y))
for f in range(n_splits):
trm=folds!=f; tem=folds==f
m=make_model()
m.fit(X[trm],y[trm])
oof[tem]=m.predict(X[tem])
mt=metrics(y,oof,groups)
return mt, oof, alld
if __name__=='__main__':
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor, RandomForestRegressor
mk={
'ExtraTrees': lambda: ExtraTreesRegressor(n_estimators=500,min_samples_leaf=1,n_jobs=-1,random_state=0),
'ET_leaf2': lambda: ExtraTreesRegressor(n_estimators=500,min_samples_leaf=2,n_jobs=-1,random_state=0),
'HGB': lambda: HistGradientBoostingRegressor(max_iter=500,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0),
'RF': lambda: RandomForestRegressor(n_estimators=400,min_samples_leaf=2,n_jobs=-1,random_state=0),
}
for name,f in mk.items():
mt,_,_=cv_eval(f)
report(name+' [CV]', mt)
PY
python3 /app/cv.pyRidge rmse=4.559 mae=3.541 p90=6.448 p95=7.103 max=7.202 | XX XX XX XX XX RF rmse=2.597 mae=2.044 p90=3.579 p95=3.718 max=3.775 | XX XX XX OK OK ExtraTrees rmse=2.070 mae=1.589 p90=3.072 p95=3.371 max=3.402 | OK XX XX OK OK HGB rmse=2.397 mae=1.896 p90=3.493 p95=3.607 max=3.784 | XX XX XX OK OK GBR rmse=2.594 mae=2.013 p90=3.816 p95=3.945 max=3.984 | XX XX XX OK OK MLP rmse=2.732 mae=2.031 p90=4.019 p95=4.909 max=6.131 | XX XX XX XX XX SVR rmse=2.743 mae=1.934 p90=4.057 p95=4.606 max=5.695 | XX XX XX OK XX ExtraTrees [CV] rmse=2.131 mae=1.569 p90=3.077 p95=3.401 max=5.807 | OK XX XX OK XX ET_leaf2 [CV] rmse=2.172 mae=1.607 p90=3.194 p95=3.465 max=5.904 | OK XX XX OK XX HGB [CV] rmse=2.287 mae=1.675 p90=3.776 p95=4.136 max=5.054 | XX XX XX OK OK RF [CV] rmse=2.371 mae=1.794 p90=3.414 p95=3.730 max=4.776 | XX XX XX OK OK
cd /app && cat >> /app/harness.py << 'PY'
def fe2(df):
f=df['frequency'].astype(float).values; a=df['attack_angle'].astype(float).values
c=df['chord_length'].astype(float).values; v=df['free_stream_velocity'].astype(float).values
t=df['suction_side_displacement_thickness'].astype(float).values
lf=np.log10(f); lt=np.log10(t); lc=np.log10(c); lv=np.log10(v)
out={}
out['lf']=lf; out['a']=a; out['lc']=lc; out['v']=v; out['lt']=lt
out['c']=c; out['t']=t
# physics ratios (logs) -- help tree models that cannot form ratios
out['st_delta']=lf+lt # log(f*delta)
out['st_delta_U']=lf+lt-lv # log(f*delta/U) Strouhal
out['st_chord']=lf+lc # log(f*c)
out['st_chord_U']=lf+lc-lv # log(f*c/U)
out['delta_c']=lt-lc # log(delta/c)
out['Re_c']=lc+lv # log(c*U) ~ Reynolds_chord
out['Re_delta']=lt+lv # log(delta*U)
out['mach']=v/340.0
# nonlinearity in freq & angle
out['lf2']=lf**2; out['lf3']=lf**3
out['a2']=a**2; out['a_lf']=a*lf
out['st_delta_U2']=out['st_delta_U']**2
out['a_st']=a*out['st_delta_U']
return np.column_stack([out[k] for k in out])
PY
cat > /app/cv2.py << 'PY'
import numpy as np, pandas as pd
from harness import *
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor, RandomForestRegressor
def get_all():
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True); return add_conditions(alld)
def cv_eval(make_model, transform, n_splits=10, seed=0):
alld=get_all(); X=transform(alld); y=alld[TARGET].values; groups=alld['cond'].values
uniq=pd.unique(groups); rng=np.random.RandomState(seed); perm=rng.permutation(len(uniq))
fold_of={c:(i%n_splits) for i,c in enumerate(uniq[perm])}
folds=np.array([fold_of[c] for c in groups]); oof=np.zeros(len(y))
for f in range(n_splits):
trm=folds!=f; tem=folds==f
m=make_model(); m.fit(X[trm],y[trm]); oof[tem]=m.predict(X[tem])
return metrics(y,oof,groups), oof, alld
if __name__=='__main__':
for tname,tf in [('fe2',fe2)]:
for name,f in {
'ExtraTrees': lambda: ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0),
'HGB': lambda: HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0),
'RF': lambda: RandomForestRegressor(n_estimators=500,min_samples_leaf=2,n_jobs=-1,random_state=0),
}.items():
mt,_,_=cv_eval(f,tf); report(f'{name} [{tname}]',mt)
PY
python3 /app/cv2.pyExtraTrees [fe2] rmse=2.037 mae=1.496 p90=2.727 p95=3.634 max=5.117 | OK OK XX OK OK HGB [fe2] rmse=2.157 mae=1.594 p90=3.539 p95=3.742 max=4.234 | OK XX XX OK OK RF [fe2] rmse=2.358 mae=1.757 p90=3.257 p95=4.244 max=5.530 | XX XX XX OK OK
cd /app && cat > /app/cv3.py << 'PY'
import numpy as np, pandas as pd
from harness import *
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor, RandomForestRegressor, GradientBoostingRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVR
from sklearn.neighbors import KNeighborsRegressor
def get_all():
tr,va=load(); alld=pd.concat([tr,va],ignore_index=True); return add_conditions(alld)
ALL=get_all()
GROUPS=ALL['cond'].values
Y=ALL[TARGET].values
def make_folds(n_splits,seed):
uniq=pd.unique(GROUPS); rng=np.random.RandomState(seed); perm=rng.permutation(len(uniq))
fold_of={c:(i%n_splits) for i,c in enumerate(uniq[perm])}
return np.array([fold_of[c] for c in GROUPS])
def cv_oof(builders, transform, n_splits=10, seed=0, weights=None):
# builders: list of (callable->model, transform) OR use shared transform
X=transform(ALL); y=Y; folds=make_folds(n_splits,seed)
preds=np.zeros((len(y),len(builders)))
for f in range(n_splits):
trm=folds!=f; tem=folds==f
for j,b in enumerate(builders):
m=b(); m.fit(X[trm],y[trm]); preds[tem,j]=m.predict(X[tem])
if weights is None: weights=np.ones(len(builders))
weights=np.array(weights,float); weights/=weights.sum()
oof=preds@weights
return metrics(y,oof,GROUPS), oof, preds
def et(seed=0,**k): return ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=seed,**k)
def hgb(seed=0,**k): return HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=seed,**k)
def gbr(seed=0,**k): return GradientBoostingRegressor(n_estimators=500,learning_rate=0.05,max_depth=3,subsample=0.8,random_state=seed,**k)
def mlp(seed=0): return make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),alpha=1e-3,max_iter=3000,random_state=seed))
def svr(): return make_pipeline(StandardScaler(),SVR(C=30,gamma='scale',epsilon=0.1))
def knn(): return make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=7,weights='distance'))
if __name__=='__main__':
# single ET baseline
report('ET', cv_oof([lambda:et(0)], fe2)[0])
# ET + HGB
report('ET+HGB', cv_oof([lambda:et(0), lambda:hgb(0)], fe2, weights=[2,1])[0])
# ET + HGB + GBR
report('ET+HGB+GBR', cv_oof([lambda:et(0), lambda:hgb(0), lambda:gbr(0)], fe2, weights=[2,1,1])[0])
# ET + HGB + MLP
report('ET+HGB+MLP', cv_oof([lambda:et(0), lambda:hgb(0), lambda:mlp(0)], fe2, weights=[2,1,1])[0])
# ET + HGB + SVR + KNN
report('ET+HGB+SVR+KNN', cv_oof([lambda:et(0), lambda:hgb(0), svr, knn], fe2, weights=[3,1,1,1])[0])
PY
python3 /app/cv3.py[stderr] ET rmse=2.037 mae=1.496 p90=2.727 p95=3.634 max=5.117 | OK OK XX OK OK ET+HGB rmse=1.988 mae=1.461 p90=2.756 p95=3.653 max=4.511 | OK OK XX OK OK ET+HGB+GBR rmse=2.016 mae=1.489 p90=2.706 p95=3.678 max=4.865 | OK OK XX OK OK ET+HGB+MLP rmse=2.001 mae=1.470 p90=2.699 p95=3.739 max=5.202 | OK OK XX OK OK Shell command timed out. Tool interaction exceeded 300 seconds.
restart: true
<system>tool has been restarted.</system>
cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd
from harness import *
from cv3 import cv_oof, et, hgb, ALL, GROUPS, Y
mt,oof,_=cv_oof([lambda:et(0), lambda:hgb(0)], fe2, weights=[2,1])
report('ET+HGB',mt)
df=ALL.copy(); df['e']=oof-Y
per=df.groupby('cond')['e'].agg(['mean','std','count'])
per['rmse']=np.sqrt(per['mean']**2+per['std']**2)
per['bias2']=per['mean']**2; per['var']=per['std']**2
per=per.sort_values('rmse',ascending=False)
pd.set_option('display.width',200)
print(per.head(12))
# overall: how much of MSE is bias vs variance across worst conditions
top=per.head(12)
print('among worst 12: mean bias^2', top['bias2'].mean(), 'mean var', top['var'].mean())
print('fraction of RMSE^2 from bias (worst12):', top['bias2'].sum()/(top['bias2'].sum()+top['var'].sum()))
"ET+HGB rmse=1.988 mae=1.461 p90=2.756 p95=3.653 max=4.511 | OK OK XX OK OK
mean std ... bias2 var
cond ...
(12.652, 0.1524, 39.6) 3.350654 3.119680 ... 11.226879 9.732402
(19.694, 0.0508, 71.29) 3.711052 2.176121 ... 13.771909 4.735502
(-0.002, 0.0254, 71.29) -0.238403 4.182954 ... 0.056836 17.497107
(22.204, 0.0254, 39.6) -2.399480 3.148312 ... 5.757503 9.911868
(7.26, 0.2286, 71.29) -2.405515 2.881558 ... 5.786503 8.303377
(12.652, 0.0254, 39.6) -1.983277 2.419456 ... 3.933389 5.853767
(17.4, 0.0254, 39.6) 0.116969 3.081340 ... 0.013682 9.494658
(12.301, 0.1016, 71.29) -1.946189 2.278424 ... 3.787652 5.191216
(11.203, 0.0508, 71.29) -2.407142 1.431135 ... 5.794334 2.048148
(4.201, 0.0508, 71.29) 1.637140 2.122622 ... 2.680228 4.505525
(8.901, 0.1016, 39.6) -1.580477 2.126715 ... 2.497906 4.522916
(17.4, 0.0254, 55.5) 2.276308 1.278477 ... 5.181579 1.634503
[12 rows x 6 columns]
among worst 12: mean bias^2 5.0406999392688565 mean var 6.9525825118030395
fraction of RMSE^2 from bias (worst12): 0.4202936068447504cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd
from harness import *
from cv3 import cv_oof, et, hgb, ALL, GROUPS, Y
mt,oof,_=cv_oof([lambda:et(0), lambda:hgb(0)], fe2, weights=[2,1])
df=ALL.copy(); df['e']=oof-Y
per=df.groupby('cond')['e'].apply(lambda s:np.sqrt(np.mean(s.values**2))).sort_values()
print('n conditions', len(per))
for thr in [1.0,1.5,2.0,2.35,3.0,4.0]:
print(f' frac RMSE<= {thr}: {(per<=thr).mean():.3f} count {(per<=thr).sum()}')
print('percentiles: p50 %.3f p75 %.3f p90 %.3f p95 %.3f'%(np.percentile(per,50),np.percentile(per,75),np.percentile(per,90),np.percentile(per,95)))
# Are the worst conditions the high-angle / edge ones? tabulate by angle bin
df2=ALL.drop_duplicates('cond')[['cond','attack_angle','chord_length','free_stream_velocity']].set_index('cond')
per=per.to_frame('rmse').join(df2)
print()
print('corr rmse vs |angle|:', np.corrcoef(per['rmse'], per['attack_angle'])[0,1])
print('mean rmse angle>12:', per[per['attack_angle']>12]['rmse'].mean(), 'n', (per['attack_angle']>12).sum())
print('mean rmse angle<=12:', per[per['attack_angle']<=12]['rmse'].mean(), 'n', (per['attack_angle']<=12).sum())
"n conditions 82 frac RMSE<= 1.0: 0.110 count 9 frac RMSE<= 1.5: 0.463 count 38 frac RMSE<= 2.0: 0.671 count 55 frac RMSE<= 2.35: 0.756 count 62 frac RMSE<= 3.0: 0.927 count 76 frac RMSE<= 4.0: 0.976 count 80 percentiles: p50 1.582 p75 2.240 p90 2.756 p95 3.653 corr rmse vs |angle|: 0.5354621659812655 mean rmse angle>12: 2.558023440592634 n 19 mean rmse angle<=12: 1.533604133121049 n 63
cd /app && cat >> /app/harness.py << 'PY'
def fe3(df):
f=df['frequency'].astype(float).values; a=df['attack_angle'].astype(float).values
c=df['chord_length'].astype(float).values; v=df['free_stream_velocity'].astype(float).values
t=df['suction_side_displacement_thickness'].astype(float).values
lf=np.log10(f); lt=np.log10(t); lc=np.log10(c); lv=np.log10(v)
st=lf+lt-lv # strouhal (delta)
stc=lf+lc-lv # strouhal (chord)
o={}
o['lf']=lf; o['a']=a; o['lc']=lc; o['v']=v; o['lt']=lt; o['c']=c; o['t']=t
o['st_delta']=lf+lt; o['st']=st; o['st_chord']=stc; o['st_chord_abs']=lf+lc
o['delta_c']=lt-lc; o['Re_c']=lc+lv; o['Re_delta']=lt+lv; o['mach']=v/340.0
o['lf2']=lf**2; o['lf3']=lf**3
o['a2']=a**2; o['a3']=a**3
o['a_lf']=a*lf; o['a_st']=a*st; o['a_stc']=a*stc
o['a_lc']=a*lc; o['a_lv']=a*lv; o['a_lt']=a*lt
o['st2']=st**2; o['st3']=st**3
o['a2_st']=a**2*st
o['delta_a']=lt*a
return np.column_stack([o[k] for k in o])
PY
timeout 250 python3 -c "
from harness import *
from cv3 import cv_oof, et, hgb
for nm,tf in [('fe2',fe2),('fe3',fe3)]:
report('ET '+nm, cv_oof([lambda:et(0)], tf)[0])
report('ET+HGB '+nm, cv_oof([lambda:et(0),lambda:hgb(0)], tf, weights=[2,1])[0])
"ET fe2 rmse=2.037 mae=1.496 p90=2.727 p95=3.634 max=5.117 | OK OK XX OK OK ET+HGB fe2 rmse=1.988 mae=1.461 p90=2.756 p95=3.653 max=4.511 | OK OK XX OK OK ET fe3 rmse=2.085 mae=1.511 p90=2.721 p95=3.779 max=5.155 | OK OK XX OK OK ET+HGB fe3 rmse=2.067 mae=1.483 p90=2.799 p95=3.764 max=5.429 | OK OK XX OK OK
cd /app && timeout 250 python3 -c "
import numpy as np, pandas as pd
from harness import *
from cv3 import cv_oof, et, hgb, ALL, GROUPS, Y
mt,oof,_=cv_oof([lambda:et(0),lambda:hgb(0)], fe2, weights=[2,1])
report('raw ET+HGB',mt)
lf=np.log10(ALL['frequency'].values)
def smooth(oof, deg, blend=1.0):
out=oof.copy()
for c in pd.unique(GROUPS):
m=GROUPS==c
x=lf[m]; yv=oof[m]
if m.sum()>=deg+2 and x.max()-x.min()>1e-6:
d=min(deg, m.sum()-1)
coef=np.polyfit(x,yv,d)
fit=np.polyval(coef,x)
out[m]=blend*fit+(1-blend)*yv
return out
for deg in [2,3,4,5]:
s=smooth(oof,deg,1.0)
report(f'smooth deg{deg}', metrics(Y,s,GROUPS))
for bl in [0.5,0.7]:
s=smooth(oof,3,bl)
report(f'smooth deg3 blend{bl}', metrics(Y,s,GROUPS))
"raw ET+HGB rmse=1.988 mae=1.461 p90=2.756 p95=3.653 max=4.511 | OK OK XX OK OK [stderr] Traceback (most recent call last): File "<string>", line 22, in <module> File "<string>", line 12, in smooth ValueError: operands could not be broadcast together with shapes (1157,) (3,)
cd /app && timeout 250 python3 -c "
import numpy as np, pandas as pd
from harness import *
from cv3 import cv_oof, et, hgb, ALL, GROUPS, Y
mt,oof,_=cv_oof([lambda:et(0),lambda:hgb(0)], fe2, weights=[2,1])
report('raw ET+HGB',mt)
lf=np.log10(ALL['frequency'].values)
def smooth(oof, deg, blend=1.0):
out=oof.copy()
for c in pd.unique(GROUPS):
idx=np.where(GROUPS==c)[0]
x=lf[idx]; yv=oof[idx]
if len(idx)>=deg+2 and x.max()-x.min()>1e-6:
coef=np.polyfit(x,yv,deg)
fit=np.polyval(coef,x)
out[idx]=blend*fit+(1-blend)*yv
return out
for deg in [2,3,4,5]:
report(f'smooth deg{deg}', metrics(Y,smooth(oof,deg,1.0),GROUPS))
for bl in [0.5,0.6,0.75]:
report(f'smooth deg4 blend{bl}', metrics(Y,smooth(oof,4,bl),GROUPS))
"raw ET+HGB rmse=1.988 mae=1.461 p90=2.756 p95=3.653 max=4.511 | OK OK XX OK OK [stderr] Traceback (most recent call last): File "<string>", line 21, in <module> File "<string>", line 12, in smooth ValueError: operands could not be broadcast together with shapes (1157,) (3,)
cd /app && timeout 250 python3 -c "
import numpy as np, pandas as pd
from harness import *
from cv3 import cv_oof, et, hgb, ALL, GROUPS, Y
mt,oof,_=cv_oof([lambda:et(0),lambda:hgb(0)], fe2, weights=[2,1])
report('raw ET+HGB',mt)
lf=np.log10(ALL['frequency'].values)
groups_idx=list(pd.Series(range(len(GROUPS))).groupby(ALL['cond'].values).apply(list))
def smooth(oof, deg, blend=1.0):
out=oof.copy()
for idx in groups_idx:
idx=np.array(idx); x=lf[idx]; yv=oof[idx]
if len(idx)>=deg+2 and x.max()-x.min()>1e-6:
coef=np.polyfit(x,yv,deg); fit=np.polyval(coef,x)
out[idx]=blend*fit+(1-blend)*yv
return out
for deg in [2,3,4,5,6]:
report(f'smooth deg{deg}', metrics(Y,smooth(oof,deg,1.0),GROUPS))
for bl in [0.5,0.7]:
report(f'smooth deg4 bl{bl}', metrics(Y,smooth(oof,4,bl),GROUPS))
" 2>&1 | grep -v Warningraw ET+HGB rmse=1.988 mae=1.461 p90=2.756 p95=3.653 max=4.511 | OK OK XX OK OK smooth deg2 rmse=2.327 mae=1.746 p90=3.305 p95=4.003 max=5.065 | XX XX XX OK OK smooth deg3 rmse=2.152 mae=1.587 p90=2.882 p95=3.938 max=4.785 | OK XX XX OK OK smooth deg4 rmse=2.092 mae=1.535 p90=2.927 p95=3.644 max=4.562 | OK OK XX OK OK smooth deg5 rmse=2.049 mae=1.503 p90=2.838 p95=3.697 max=4.512 | OK OK XX OK OK smooth deg6 rmse=2.028 mae=1.490 p90=2.770 p95=3.693 max=4.512 | OK OK XX OK OK smooth deg4 bl0.5 rmse=2.016 mae=1.478 p90=2.765 p95=3.631 max=4.494 | OK OK XX OK OK smooth deg4 bl0.7 rmse=2.041 mae=1.496 p90=2.916 p95=3.633 max=4.498 | OK OK XX OK OK
cd /app && cat > /app/mlp_test.py << 'PY'
import numpy as np, pandas as pd
from harness import *
from sklearn.base import BaseEstimator, RegressorMixin, clone
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from cv3 import cv_oof, et, hgb
class BaggedMLP(BaseEstimator, RegressorMixin):
def __init__(self, hidden=(128,64), alpha=1e-3, n=5, activation='relu', max_iter=800, seed=0):
self.hidden=hidden; self.alpha=alpha; self.n=n; self.activation=activation; self.max_iter=max_iter; self.seed=seed
def fit(self,X,y):
self.models_=[]
for i in range(self.n):
m=make_pipeline(StandardScaler(), MLPRegressor(hidden_layer_sizes=self.hidden, alpha=self.alpha,
activation=self.activation, max_iter=self.max_iter, random_state=self.seed+i,
early_stopping=True, n_iter_no_change=20, validation_fraction=0.1))
m.fit(X,y); self.models_.append(m)
return self
def predict(self,X):
return np.mean([m.predict(X) for m in self.models_],axis=0)
if __name__=='__main__':
import sys
configs=[
('MLP relu(128,64) a1e-3', lambda: BaggedMLP((128,64),1e-3,5,'relu')),
('MLP tanh(128,64) a1e-3', lambda: BaggedMLP((128,64),1e-3,5,'tanh')),
('MLP relu(256,128,64) a1e-3', lambda: BaggedMLP((256,128,64),1e-3,5,'relu')),
]
for nm,b in configs:
report(nm, cv_oof([b], fe2, n_splits=8)[0])
PY
timeout 280 python3 /app/mlp_test.py 2>&1 | grep -v Warning[stderr] Terminated
cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd, time
from harness import *
from mlp_test import BaggedMLP
tr,va=load(); va=add_conditions(va)
Xtr=fe2(tr); ytr=tr[TARGET].values; Xva=fe2(va); yva=va[TARGET].values
for nm,mk in [
('relu(128,64) x5', lambda:BaggedMLP((128,64),1e-3,5,'relu',800)),
('tanh(128,64) x5', lambda:BaggedMLP((128,64),1e-3,5,'tanh',800)),
('relu(200,100) x5', lambda:BaggedMLP((200,100),3e-4,5,'relu',800)),
]:
t=time.time(); m=mk().fit(Xtr,ytr); p=m.predict(Xva)
report(nm+f' [{time.time()-t:.0f}s]', metrics(yva,p,va['cond'].values))
" 2>&1 | grep -v Warningrelu(128,64) x5 [12s] rmse=3.078 mae=2.201 p90=4.643 p95=5.756 max=7.489 | XX XX XX XX XX warnings.warn( warnings.warn( warnings.warn( warnings.warn( tanh(128,64) x5 [17s] rmse=5.012 mae=3.898 p90=7.075 p95=7.267 max=7.389 | XX XX XX XX XX relu(200,100) x5 [24s] rmse=2.496 mae=1.872 p90=3.876 p95=4.247 max=4.330 | XX XX XX OK OK
cd /app && timeout 100 python3 -c "
import numpy as np, pandas as pd
from harness import *
tr,va=load(); alld=add_conditions(pd.concat([tr,va],ignore_index=True))
# pick 3 conditions with many rows, show SPL vs freq sorted
cnts=alld['cond'].value_counts()
for c in list(cnts.index[:3]):
d=alld[alld['cond']==c].sort_values('frequency')
print('cond',c,'n',len(d))
spl=d['scaled_sound_pressure_level'].values
lf=np.log10(d['frequency'].values)
# smoothness: residual from cubic fit
coef=np.polyfit(lf,spl,3); res=spl-np.polyval(coef,lf)
print(' SPL range %.1f..%.1f, cubic-fit resid std %.3f'%(spl.min(),spl.max(),res.std()))
print(' freqs:', np.round(d['frequency'].values[:8]).astype(int))
print(' spl :', np.round(spl[:8],1))
"cond (-0.002, 0.30481, 31.7) n 18 SPL range 110.8..129.1, cubic-fit resid std 1.093 freqs: [ 200 250 315 400 501 631 801 1000] spl : [116.7 118.1 122.2 124.5 126.8 128.8 129.1 127.5] cond (4.0, 0.30481, 71.29) n 18 SPL range 109.0..132.2, cubic-fit resid std 0.644 freqs: [ 250 315 400 499 629 799 1000 1251] spl : [126.3 128. 129.4 130.6 131.7 132.2 131.8 130.5] cond (4.0, 0.2286, 71.29) n 17 SPL range 110.1..132.0, cubic-fit resid std 1.316 freqs: [ 500 630 801 1002 1252 1600 1998 2496] spl : [126.1 128.7 130.6 131.8 132. 129.9 126.7 125. ]
cd /app && timeout 280 python3 -c "
import numpy as np, pandas as pd
from harness import *
from sklearn.ensemble import ExtraTreesRegressor
from cv3 import cv_oof
def mk(msl,mf,boot,ms):
def f():
kw=dict(n_estimators=500,min_samples_leaf=msl,max_features=mf,n_jobs=-1,random_state=0,bootstrap=boot)
if boot: kw['max_samples']=ms
return ExtraTreesRegressor(**kw)
return f
for msl in [1,2,3]:
for mf in [0.5,0.7,1.0]:
report(f'ET msl{msl} mf{mf}', cv_oof([mk(msl,mf,False,None)], fe2)[0])
" 2>&1 | grep -v WarningET msl1 mf0.5 rmse=2.075 mae=1.516 p90=2.805 p95=3.764 max=5.443 | OK OK XX OK OK ET msl1 mf0.7 rmse=2.058 mae=1.504 p90=2.757 p95=3.663 max=5.353 | OK OK XX OK OK ET msl1 mf1.0 rmse=2.041 mae=1.499 p90=2.758 p95=3.665 max=5.119 | OK OK XX OK OK ET msl2 mf0.5 rmse=2.119 mae=1.555 p90=2.909 p95=3.733 max=5.518 | OK XX XX OK OK ET msl2 mf0.7 rmse=2.102 mae=1.533 p90=2.784 p95=3.809 max=5.476 | OK OK XX OK OK ET msl2 mf1.0 rmse=2.095 mae=1.533 p90=2.764 p95=3.761 max=5.400 | OK OK XX OK OK ET msl3 mf0.5 rmse=2.194 mae=1.613 p90=2.943 p95=3.867 max=5.598 | OK XX XX OK OK ET msl3 mf0.7 rmse=2.177 mae=1.599 p90=2.841 p95=3.837 max=5.614 | OK XX XX OK XX ET msl3 mf1.0 rmse=2.151 mae=1.584 p90=2.828 p95=3.884 max=5.404 | OK XX XX OK OK
cd /app && timeout 250 python3 -c "
import numpy as np, pandas as pd, time, warnings
warnings.filterwarnings('ignore')
from harness import *
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, ConstantKernel as C, WhiteKernel, Matern
from sklearn.preprocessing import StandardScaler
tr,va=load(); va=add_conditions(va)
Xtr=fe2(tr); ytr=tr[TARGET].values; Xva=fe2(va); yva=va[TARGET].values
sc=StandardScaler().fit(Xtr); Xtr2=sc.transform(Xtr); Xva2=sc.transform(Xva)
ym=ytr.mean()
d=Xtr2.shape[1]
for nm,kern in [
('RBF iso', C(50)*RBF(2.0)+WhiteKernel(1.0)),
('RBF ard', C(50)*RBF([2.0]*d)+WhiteKernel(1.0)),
('Matern1.5 ard', C(50)*Matern([2.0]*d,nu=1.5)+WhiteKernel(1.0)),
]:
t=time.time()
gp=GaussianProcessRegressor(kernel=kern,normalize_y=True,n_restarts_optimizer=0,alpha=1e-6)
gp.fit(Xtr2,ytr); p=gp.predict(Xva2)
report(nm+f' [{time.time()-t:.0f}s]', metrics(yva,p,va['cond'].values))
"RBF iso [4s] rmse=2.695 mae=2.047 p90=4.419 p95=5.198 max=5.308 | XX XX XX XX OK RBF ard [131s] rmse=2.378 mae=1.816 p90=3.868 p95=4.404 max=4.983 | XX XX XX OK OK
cd /app && cat > /app/oof_bank.py << 'PY'
import numpy as np, pandas as pd, warnings, pickle
warnings.filterwarnings('ignore')
from harness import *
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor, RandomForestRegressor, GradientBoostingRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.neighbors import KNeighborsRegressor
from sklearn.kernel_ridge import KernelRidge
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
GROUPS=ALL['cond'].values; Y=ALL[TARGET].values
uniq=pd.unique(GROUPS)
rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10
fold_of={c:(i%NS) for i,c in enumerate(uniq[perm])}
FOLDS=np.array([fold_of[c] for c in GROUPS])
def oof_for(build, transform):
X=transform(ALL); oof=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
m=build(); m.fit(X[trm],Y[trm]); oof[tem]=m.predict(X[tem])
return oof
bank={}
defs={
'et2': (lambda:ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0), fe2),
'et2b':(lambda:ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=1,max_features=0.7), fe2),
'et3': (lambda:ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0), fe3),
'hgb': (lambda:HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0), fe2),
'rf': (lambda:RandomForestRegressor(n_estimators=500,min_samples_leaf=2,n_jobs=-1,random_state=0), fe2),
'gbr': (lambda:GradientBoostingRegressor(n_estimators=500,learning_rate=0.05,max_depth=3,subsample=0.8,random_state=0), fe2),
'poly2':(lambda:make_pipeline(StandardScaler(),PolynomialFeatures(2),Ridge(alpha=5.0)), fe2),
'poly3':(lambda:make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)), fe2),
'knn': (lambda:make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=10,weights='distance')), fe2),
'krr': (lambda:make_pipeline(StandardScaler(),KernelRidge(alpha=0.5,kernel='rbf',gamma=0.05)), fe2),
}
for nm,(b,tf) in defs.items():
bank[nm]=oof_for(b,tf)
report(nm, metrics(Y,bank[nm],GROUPS))
pickle.dump({'bank':bank,'Y':Y,'GROUPS':GROUPS}, open('/app/oof.pkl','wb'))
print('saved', list(bank))
PY
timeout 290 python3 /app/oof_bank.py 2>&1 | grep -v Warninget2 rmse=2.037 mae=1.496 p90=2.727 p95=3.634 max=5.117 | OK OK XX OK OK et2b rmse=2.065 mae=1.513 p90=2.742 p95=3.652 max=5.351 | OK OK XX OK OK et3 rmse=2.085 mae=1.511 p90=2.721 p95=3.779 max=5.155 | OK OK XX OK OK hgb rmse=2.157 mae=1.594 p90=3.539 p95=3.742 max=4.234 | OK XX XX OK OK rf rmse=2.358 mae=1.757 p90=3.257 p95=4.244 max=5.530 | XX XX XX OK OK gbr rmse=2.325 mae=1.731 p90=3.530 p95=3.820 max=6.925 | XX XX XX OK XX poly2 rmse=2.705 mae=2.046 p90=3.711 p95=4.181 max=7.844 | XX XX XX OK XX poly3 rmse=2.568 mae=1.907 p90=3.921 p95=4.226 max=6.038 | XX XX XX OK XX knn rmse=2.646 mae=1.983 p90=3.548 p95=4.392 max=6.648 | XX XX XX OK XX krr rmse=7.272 mae=4.693 p90=9.618 p95=11.711 max=36.396 | XX XX XX XX XX saved ['et2', 'et2b', 'et3', 'hgb', 'rf', 'gbr', 'poly2', 'poly3', 'knn', 'krr']
cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd, pickle, itertools
from harness import metrics, report, TARGETS
d=pickle.load(open('/app/oof.pkl','rb')); bank=d['bank']; Y=d['Y']; G=d['GROUPS']
keys=['et2','et3','et2b','hgb','rf','gbr','poly3','knn']
M=np.column_stack([bank[k] for k in keys])
rng=np.random.RandomState(0)
best=None
# include zero weights; random Dirichlet-ish search
for _ in range(40000):
w=rng.rand(len(keys))
w=np.where(rng.rand(len(keys))<0.5,0,w) # sparsify
if w.sum()==0: continue
w=w/w.sum()
p=M@w
m=metrics(Y,p,G)
# score: must pass all; minimize p90 primarily then rmse
ok=all(m[k]<=TARGETS[k] for k in TARGETS)
score=(0 if ok else 1, m['p90'], m['rmse'])
if best is None or score<best[0]:
best=(score,w.copy(),m)
print('best weights:')
for k,wv in zip(keys,best[1]):
if wv>1e-3: print(f' {k}: {wv:.3f}')
report('BEST BLEND', best[2])
print('all pass:', all(best[2][k]<=TARGETS[k] for k in TARGETS))
"best weights: et2: 0.480 hgb: 0.215 poly3: 0.304 BEST BLEND rmse=1.938 mae=1.455 p90=2.580 p95=3.152 max=3.927 | OK OK XX OK OK all pass: False
cd /app && timeout 250 python3 -c "
import numpy as np, pandas as pd, warnings, pickle
warnings.filterwarnings('ignore')
from harness import *
from sklearn.preprocessing import StandardScaler, SplineTransformer
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.compose import ColumnTransformer
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
G=ALL['cond'].values; Y=ALL[TARGET].values
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
def spfeat(df):
f=df['frequency'].astype(float).values; a=df['attack_angle'].astype(float).values
c=df['chord_length'].astype(float).values; v=df['free_stream_velocity'].astype(float).values
t=df['suction_side_displacement_thickness'].astype(float).values
lf=np.log10(f); lt=np.log10(t); lc=np.log10(c); lv=np.log10(v)
st=lf+lt-lv
return np.column_stack([lf,st,a,lc,lv,lt,v]), lf, st, a, lc, lv, v
def build_model(nk,deg,alpha):
st_=SplineTransformer(n_knots=nk,degree=deg)
ct=ColumnTransformer([('sp',st_,[0,1,2,3,4,5,6])])
return make_pipeline(StandardScaler(), ct, Ridge(alpha=alpha))
def oof(build,transform):
X=transform(ALL)[0]; o=np.zeros(len(Y))
for fdx in range(NS):
trm=FOLDS!=fdx; tem=FOLDS==fdx
m=build(); m.fit(X[trm],Y[trm]); o[tem]=m.predict(X[tem])
return o
for nk in [5,7,10]:
for alpha in [1.0,5.0]:
o=oof(lambda nk=nk,alpha=alpha: build_model(nk,3,alpha), spfeat)
report(f'spline nk{nk} a{alpha}', metrics(Y,o,G))
"spline nk5 a1.0 rmse=3.912 mae=2.918 p90=5.445 p95=7.754 max=8.234 | XX XX XX XX XX spline nk5 a5.0 rmse=4.011 mae=3.037 p90=5.699 p95=7.257 max=8.335 | XX XX XX XX XX spline nk7 a1.0 rmse=3.791 mae=2.849 p90=5.409 p95=7.287 max=8.108 | XX XX XX XX XX spline nk7 a5.0 rmse=3.912 mae=2.968 p90=5.663 p95=7.406 max=8.105 | XX XX XX XX XX spline nk10 a1.0 rmse=3.824 mae=2.867 p90=5.406 p95=6.984 max=8.147 | XX XX XX XX XX spline nk10 a5.0 rmse=3.913 mae=2.968 p90=5.753 p95=7.204 max=8.058 | XX XX XX XX XX
cd /app && timeout 290 python3 -c "
import numpy as np, pandas as pd, warnings, pickle
warnings.filterwarnings('ignore')
from harness import *
from sklearn.svm import SVR
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.neighbors import KNeighborsRegressor
d=pickle.load(open('/app/oof.pkl','rb')); bank=d['bank']; Y=d['Y']; G=d['GROUPS']
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
def oof(build,transform):
X=transform(ALL); o=np.zeros(len(Y))
for fdx in range(NS):
trm=FOLDS!=fdx; tem=FOLDS==fdx
m=build(); m.fit(X[trm],Y[trm]); o[tem]=m.predict(X[tem])
return o
new={
'svr1':(lambda:make_pipeline(StandardScaler(),SVR(C=50,gamma=0.03,epsilon=0.05)),fe2),
'svr2':(lambda:make_pipeline(StandardScaler(),SVR(C=100,gamma=0.02,epsilon=0.1)),fe2),
'poly2i':(lambda:make_pipeline(StandardScaler(),PolynomialFeatures(2,interaction_only=False),Ridge(alpha=2.0)),fe2),
'knn2':(lambda:make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=15,weights='distance')),fe3),
}
for nm,(b,tf) in new.items():
bank[nm]=oof(b,tf); report(nm, metrics(Y,bank[nm],G))
pickle.dump({'bank':bank,'Y':Y,'GROUPS':G}, open('/app/oof.pkl','wb'))
print('updated bank:', list(bank))
"svr1 rmse=2.491 mae=1.763 p90=3.379 p95=3.594 max=9.694 | XX XX XX OK XX svr2 rmse=2.517 mae=1.797 p90=3.337 p95=3.699 max=9.591 | XX XX XX OK XX poly2i rmse=2.722 mae=2.058 p90=3.970 p95=4.190 max=7.177 | XX XX XX OK XX knn2 rmse=2.669 mae=1.960 p90=3.862 p95=4.605 max=5.898 | XX XX XX OK XX updated bank: ['et2', 'et2b', 'et3', 'hgb', 'rf', 'gbr', 'poly2', 'poly3', 'knn', 'krr', 'svr1', 'svr2', 'poly2i', 'knn2']
cd /app && cat > /app/blend_search.py << 'PY'
import numpy as np, pandas as pd, pickle
from harness import metrics, report, TARGETS
d=pickle.load(open('/app/oof.pkl','rb')); bank=d['bank']; Y=d['Y']; G=d['GROUPS']
def blend_metrics(M,w,Y,G):
return metrics(Y, M@(w/w.sum()), G)
def search(keys, n_iter=60000, seed=0, sparsify=0.5):
M=np.column_stack([bank[k] for k in keys]); rng=np.random.RandomState(seed); best=None
for _ in range(n_iter):
w=rng.rand(len(keys))
w=np.where(rng.rand(len(keys))<sparsify,0,w)
if w.sum()==0: continue
m=blend_metrics(M,w,Y,G)
ok=all(m[k]<=TARGETS[k] for k in TARGETS)
score=(0 if ok else 1, m['p90'], m['rmse'])
if best is None or score<best[0]: best=(score,w/w.sum(),m)
return best
def bootstrap_check(keys, w, nboot=300, seed=1):
M=np.column_stack([bank[k] for k in keys]); p=M@w
df=pd.DataFrame({'e':p-Y}); df['c']=G
per=df.groupby('c')['e'].apply(lambda s:np.sqrt(np.mean(s.values**2)))
conds=per.index.values; rng=np.random.RandomState(seed)
res={k:[] for k in ['rmse','mae','p90','p95','max']}
# full-data global rmse/mae fixed; bootstrap condition set for tail metrics
rowerr=df['e'].values
for _ in range(nboot):
samp=rng.choice(conds,size=len(conds),replace=True)
vals=per.loc[samp].values
res['p90'].append(np.percentile(vals,90)); res['p95'].append(np.percentile(vals,95)); res['max'].append(vals.max())
return {k:(np.mean(v),np.percentile(v,90)) for k,v in res.items() if v}
if __name__=='__main__':
for keys in [
['et2','et3','hgb','poly3','svr1'],
['et2','et3','hgb','poly3','svr1','knn2'],
['et2','hgb','poly3'],
['et2','et3','et2b','hgb','gbr','poly3','svr1','knn2'],
]:
b=search(keys)
wd={k:round(float(wv),3) for k,wv in zip(keys,b[1]) if wv>1e-3}
report('BLEND '+str(wd), b[2])
bc=bootstrap_check(keys,b[1])
print(' boot p90(mean,p90):',tuple(round(x,3) for x in bc['p90']),'p95:',tuple(round(x,3) for x in bc['p95']),'max:',tuple(round(x,3) for x in bc['max']))
PY
timeout 290 python3 /app/blend_search.pyBLEND {'et2': 0.319, 'et3': 0.156, 'hgb': 0.217, 'poly3': 0.308} rmse=1.943 mae=1.456 p90=2.579 p95=3.115 max=3.967 | OK OK XX OK OK
boot p90(mean,p90): (2.726, 3.088) p95: (3.156, 3.528) max: (3.817, 3.967)cd /app && timeout 100 python3 -c "
import numpy as np, pandas as pd
from harness import *
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lf=np.log10(ALL['frequency'].values)
rows=[]
for c,idx in ALL.groupby('cond').groups.items():
idx=np.array(idx);
if len(idx)<6: continue
x=lf[idx]; y=ALL[TARGET].values[idx]
deg=min(4,len(idx)-2)
res=y-np.polyval(np.polyfit(x,y,deg),x)
rows.append((ALL.loc[idx,'attack_angle'].mean(), np.std(res), len(idx)))
d=pd.DataFrame(rows,columns=['angle','noise','n'])
print('overall noise-floor std: median %.3f mean %.3f'%(d['noise'].median(),d['noise'].mean()))
print('high angle>12 noise: mean %.3f'%d[d.angle>12]['noise'].mean())
print('low angle<=12 noise: mean %.3f'%d[d.angle<=12]['noise'].mean())
print('noise floor p90 across conditions: %.3f'%np.percentile(d['noise'],90))
"overall noise-floor std: median 0.636 mean 0.783 high angle>12 noise: mean 1.357 low angle<=12 noise: mean 0.611 noise floor p90 across conditions: 1.500
cd /app && timeout 280 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
G=ALL['cond'].values; Y=ALL[TARGET].values; A=ALL['attack_angle'].values
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
X2=fe2(ALL)
def oofw(build, w):
o=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
m=build(); m.fit(X2[trm],Y[trm],sample_weight=None if w is None else w[trm]); o[tem]=m.predict(X2[tem])
return o
def et(): return ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0)
for k in [0.0,0.05,0.1,0.2]:
w=1.0+k*np.maximum(0,A-8.0)
report(f'ET whi k={k}', metrics(Y,oofw(et,w),G))
"ET whi k=0.0 rmse=2.037 mae=1.496 p90=2.727 p95=3.634 max=5.117 | OK OK XX OK OK ET whi k=0.05 rmse=2.058 mae=1.508 p90=2.727 p95=3.677 max=5.202 | OK OK XX OK OK ET whi k=0.1 rmse=2.067 mae=1.515 p90=2.756 p95=3.753 max=5.069 | OK OK XX OK OK ET whi k=0.2 rmse=2.089 mae=1.532 p90=2.795 p95=3.826 max=5.223 | OK OK XX OK OK
cd /app && timeout 280 python3 -c "
import numpy as np, pandas as pd, warnings, pickle
warnings.filterwarnings('ignore')
from harness import *
from sklearn.preprocessing import StandardScaler, SplineTransformer
from sklearn.linear_model import Ridge
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
G=ALL['cond'].values; Y=ALL[TARGET].values
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
def base(df):
f=df['frequency'].astype(float).values; a=df['attack_angle'].astype(float).values
c=df['chord_length'].astype(float).values; v=df['free_stream_velocity'].astype(float).values
t=df['suction_side_displacement_thickness'].astype(float).values
lf=np.log10(f); lt=np.log10(t); lc=np.log10(c); lv=np.log10(v); st=lf+lt-lv
return lf,st,a,lc,lv,lt,v
def make_feats(df, sp_st, sp_lf, fit=False):
lf,st,a,lc,lv,lt,v=base(df)
if fit:
sp_st.fit(st.reshape(-1,1)); sp_lf.fit(lf.reshape(-1,1))
Bst=sp_st.transform(st.reshape(-1,1)); Blf=sp_lf.transform(lf.reshape(-1,1))
cond=np.column_stack([a,a**2,lc,lv,lt,v,np.ones_like(a)])
# interactions: each spline col times each condition feature
inter=[]
for j in range(Bst.shape[1]):
inter.append(Bst[:,[j]]*cond)
blocks=[Bst,Blf,cond]+inter
return np.hstack(blocks)
def oof(nk,alpha):
o=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
sp_st=SplineTransformer(n_knots=nk,degree=3); sp_lf=SplineTransformer(n_knots=nk,degree=3)
Xtr=make_feats(ALL[trm],sp_st,sp_lf,fit=True)
sc=StandardScaler().fit(Xtr)
m=Ridge(alpha=alpha).fit(sc.transform(Xtr),Y[trm])
Xte=make_feats(ALL[tem],sp_st,sp_lf,fit=False)
o[tem]=m.predict(sc.transform(Xte))
return o
for nk in [5,6,8]:
for alpha in [2.0,10.0]:
o=oof(nk,alpha); report(f'splineX nk{nk} a{alpha}', metrics(Y,o,G))
if nk==6 and alpha==10.0:
pickle.dump(o, open('/app/splineX_oof.pkl','wb'))
"splineX nk5 a2.0 rmse=3.253 mae=2.450 p90=4.711 p95=5.287 max=7.213 | XX XX XX XX XX splineX nk5 a10.0 rmse=3.227 mae=2.451 p90=4.479 p95=5.244 max=6.759 | XX XX XX XX XX splineX nk6 a2.0 rmse=3.284 mae=2.473 p90=4.648 p95=5.284 max=7.437 | XX XX XX XX XX splineX nk6 a10.0 rmse=3.239 mae=2.460 p90=4.700 p95=5.198 max=6.780 | XX XX XX XX XX splineX nk8 a2.0 rmse=3.238 mae=2.432 p90=4.616 p95=5.285 max=7.325 | XX XX XX XX XX splineX nk8 a10.0 rmse=3.208 mae=2.425 p90=4.716 p95=5.342 max=6.850 | XX XX XX XX XX
cd /app && cat > /app/blend_model.py << 'PY'
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import fe2, fe3, TARGET
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
class Blend:
def __init__(self, weights=None):
# weights for [et2, et3, hgb, poly3]
self.weights = weights or [0.32,0.16,0.22,0.30]
def fit(self, df, y):
X2=fe2(df); X3=fe3(df)
self.et2=ExtraTreesRegressor(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=0).fit(X2,y)
self.et3=ExtraTreesRegressor(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=1).fit(X3,y)
self.hgb=HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0).fit(X2,y)
self.poly=make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)).fit(X2,y)
return self
def predict(self, df):
X2=fe2(df); X3=fe3(df)
ps=np.column_stack([self.et2.predict(X2),self.et3.predict(X3),self.hgb.predict(X2),self.poly.predict(X2)])
w=np.array(self.weights); w=w/w.sum()
return ps@w
if __name__=='__main__':
from harness import load, add_conditions, metrics, report
tr,va=load(); va=add_conditions(va)
b=Blend().fit(tr, tr[TARGET].values)
p=b.predict(va)
report('BLEND train->val', metrics(va[TARGET].values, p, va['cond'].values))
# also report individual on val
from harness import metrics as M
for nm,pred in [('et2',b.et2.predict(fe2(va))),('poly3',b.poly.predict(fe2(va))),('hgb',b.hgb.predict(fe2(va)))]:
report(nm+' train->val', M(va[TARGET].values,pred,va['cond'].values))
PY
timeout 200 python3 /app/blend_model.pyBLEND train->val rmse=2.156 mae=1.628 p90=3.110 p95=3.569 max=4.299 | OK XX XX OK OK et2 train->val rmse=2.119 mae=1.615 p90=3.065 p95=3.328 max=3.862 | OK XX XX OK OK poly3 train->val rmse=3.016 mae=2.258 p90=4.551 p95=5.430 max=6.671 | XX XX XX XX XX hgb train->val rmse=2.504 mae=1.810 p90=3.877 p95=4.217 max=4.561 | XX XX XX OK OK
cd /app && timeout 250 python3 -c "
import numpy as np, pandas as pd, warnings, time
warnings.filterwarnings('ignore')
from harness import *
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
tr,va=load(); va=add_conditions(va)
Xtr=fe2(tr); ytr=tr[TARGET].values; Xva=fe2(va); yva=va[TARGET].values
class BagLB:
def __init__(s,hid,alpha,n): s.hid=hid; s.alpha=alpha; s.n=n
def fit(s,X,y):
s.ms=[make_pipeline(StandardScaler(),MLPRegressor(s.hid,solver='lbfgs',alpha=s.alpha,activation='tanh',max_iter=2000,random_state=i)).fit(X,y) for i in range(s.n)]; return s
def predict(s,X): return np.mean([m.predict(X) for m in s.ms],0)
for hid in [(64,),(128,64),(64,32)]:
for alpha in [0.01,0.1,1.0]:
t=time.time(); m=BagLB(hid,alpha,5).fit(Xtr,ytr); p=m.predict(Xva)
report(f'MLP-lbfgs {hid} a{alpha} [{time.time()-t:.0f}s]', metrics(yva,p,va['cond'].values))
"MLP-lbfgs (64,) a0.01 [10s] rmse=2.979 mae=2.063 p90=4.612 p95=6.502 max=8.695 | XX XX XX XX XX MLP-lbfgs (64,) a0.1 [10s] rmse=2.788 mae=1.878 p90=4.233 p95=6.122 max=8.467 | XX XX XX XX XX MLP-lbfgs (64,) a1.0 [10s] rmse=2.420 mae=1.682 p90=3.914 p95=5.141 max=6.075 | XX XX XX XX XX MLP-lbfgs (128, 64) a0.01 [47s] rmse=2.125 mae=1.605 p90=3.241 p95=3.960 max=4.661 | OK XX XX OK OK MLP-lbfgs (128, 64) a0.1 [43s] rmse=2.224 mae=1.689 p90=3.242 p95=4.131 max=5.017 | OK XX XX OK OK MLP-lbfgs (128, 64) a1.0 [44s] rmse=2.148 mae=1.592 p90=3.237 p95=4.332 max=5.148 | OK XX XX OK OK MLP-lbfgs (64, 32) a0.01 [18s] rmse=2.334 mae=1.697 p90=3.456 p95=4.720 max=6.076 | XX XX XX OK XX MLP-lbfgs (64, 32) a0.1 [18s] rmse=2.128 mae=1.500 p90=3.135 p95=4.123 max=5.338 | OK OK XX OK OK MLP-lbfgs (64, 32) a1.0 [17s] rmse=2.233 mae=1.553 p90=3.467 p95=4.870 max=5.988 | OK XX XX XX XX
cd /app && cat > /app/val_blend.py << 'PY'
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
def fit_all(tr):
y=tr[TARGET].values; X2=fe2(tr); X3=fe3(tr)
M={}
M['et2']=ExtraTreesRegressor(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=0).fit(X2,y)
M['et3']=ExtraTreesRegressor(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=1).fit(X3,y)
M['hgb']=HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0).fit(X2,y)
M['poly3']=make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)).fit(X2,y)
M['mlpA']=make_pipeline(StandardScaler(),MLPRegressor((128,64),solver='lbfgs',alpha=0.03,activation='tanh',max_iter=2000,random_state=0)).fit(X2,y)
M['mlpB']=make_pipeline(StandardScaler(),MLPRegressor((64,32),solver='lbfgs',alpha=0.1,activation='tanh',max_iter=2000,random_state=1)).fit(X2,y)
return M
def preds(M,df):
X2=fe2(df); X3=fe3(df)
return {k:(m.predict(X3) if k=='et3' else m.predict(X2)) for k,m in M.items()}
if __name__=='__main__':
tr,va=load(); va=add_conditions(va)
M=fit_all(tr); P=preds(M,va); yva=va[TARGET].values; cond=va['cond'].values
for k in M: report(k, metrics(yva,P[k],cond))
print('--- blends ---')
import itertools
blends={
'et2+mlpA': {'et2':1,'mlpA':1},
'et2+et3+mlpA+mlpB': {'et2':1,'et3':1,'mlpA':1,'mlpB':1},
'et2+et3+hgb+mlpA+mlpB': {'et2':1.5,'et3':1,'hgb':1,'mlpA':1,'mlpB':1},
'all6': {'et2':1.5,'et3':1,'hgb':1,'mlpA':1,'mlpB':1,'poly3':0.7},
'trees+mlp+poly': {'et2':2,'hgb':1,'mlpA':1.5,'poly3':1},
}
for nm,w in blends.items():
ks=list(w); ws=np.array([w[k] for k in ks],float); ws/=ws.sum()
p=sum(ws[i]*P[ks[i]] for i in range(len(ks)))
report(nm, metrics(yva,p,cond))
PY
timeout 200 python3 /app/val_blend.pyet2 rmse=2.119 mae=1.615 p90=3.065 p95=3.328 max=3.862 | OK XX XX OK OK et3 rmse=2.117 mae=1.632 p90=2.841 p95=3.250 max=4.050 | OK XX XX OK OK hgb rmse=2.504 mae=1.810 p90=3.877 p95=4.217 max=4.561 | XX XX XX OK OK poly3 rmse=3.016 mae=2.258 p90=4.551 p95=5.430 max=6.671 | XX XX XX XX XX mlpA rmse=3.869 mae=2.583 p90=6.320 p95=8.934 max=11.077 | XX XX XX XX XX mlpB rmse=2.765 mae=1.968 p90=4.006 p95=5.239 max=6.486 | XX XX XX XX XX --- blends --- et2+mlpA rmse=2.594 mae=1.868 p90=4.598 p95=5.855 max=6.072 | XX XX XX XX XX et2+et3+mlpA+mlpB rmse=2.141 mae=1.520 p90=3.345 p95=4.236 max=5.275 | OK OK XX OK OK et2+et3+hgb+mlpA+mlpB rmse=2.099 mae=1.508 p90=3.106 p95=3.683 max=4.868 | OK OK XX OK OK all6 rmse=2.077 mae=1.502 p90=3.003 p95=3.655 max=4.916 | OK OK XX OK OK trees+mlp+poly rmse=2.255 mae=1.636 p90=3.642 p95=4.469 max=5.097 | XX XX XX OK OK
cd /app && cat > /app/robust_blend.py << 'PY'
import numpy as np, pandas as pd, warnings, pickle
warnings.filterwarnings('ignore')
from harness import *
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.svm import SVR
from sklearn.neighbors import KNeighborsRegressor
KEYS=['et2','et3','hgb','poly3','svr1','knn2']
def build(k):
if k=='et2': return ExtraTreesRegressor(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=0),fe2
if k=='et3': return ExtraTreesRegressor(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=1),fe3
if k=='hgb': return HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0),fe2
if k=='poly3': return make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)),fe2
if k=='svr1': return make_pipeline(StandardScaler(),SVR(C=50,gamma=0.03,epsilon=0.05)),fe2
if k=='knn2': return make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=15,weights='distance')),fe3
d=pickle.load(open('/app/oof.pkl','rb')); bank=d['bank']; Ycv=d['Y']; Gcv=d['GROUPS']
tr,va=load(); va=add_conditions(va); yva=va[TARGET].values; cva=va['cond'].values
valp={}
for k in KEYS:
m,tf=build(k); m.fit(tf(tr),tr[TARGET].values); valp[k]=m.predict(tf(va))
def ev(w):
ws=np.array([w.get(k,0) for k in KEYS],float); ws=ws/ws.sum()
pcv=sum(ws[i]*bank[KEYS[i]] for i in range(len(KEYS)))
pva=sum(ws[i]*valp[KEYS[i]] for i in range(len(KEYS)))
return metrics(Ycv,pcv,Gcv), metrics(yva,pva,cva)
cands={
'et2': {'et2':1},
'et3': {'et3':1},
'et2+et3': {'et2':1,'et3':1},
'et2+et3+hgb': {'et2':1,'et3':1,'hgb':1},
'2et+hgb+poly.3': {'et2':1,'et3':1,'hgb':1,'poly3':0.3},
'2et+hgb+poly.6': {'et2':1,'et3':1,'hgb':1,'poly3':0.6},
'2et+hgb+svr.5': {'et2':1,'et3':1,'hgb':1,'svr1':0.5},
'2et+hgb+poly.4+svr.4': {'et2':1,'et3':1,'hgb':1,'poly3':0.4,'svr1':0.4},
'2et+hgb+poly.4+knn.4': {'et2':1,'et3':1,'hgb':1,'poly3':0.4,'knn2':0.4},
'all': {'et2':1,'et3':1,'hgb':1,'poly3':0.4,'svr1':0.4,'knn2':0.3},
}
print('%-22s | %-38s | %-38s'%('blend','CV (82 cond)','VAL (16 cond)'))
for nm,w in cands.items():
mc,mv=ev(w)
f=lambda m:'r%.2f a%.2f p90_%.2f p95_%.2f mx%.2f'%(m['rmse'],m['mae'],m['p90'],m['p95'],m['max'])
print('%-22s | %-38s | %-38s'%(nm,f(mc),f(mv)))
pickle.dump(valp, open('/app/valp.pkl','wb'))
PY
timeout 200 python3 /app/robust_blend.pyblend | CV (82 cond) | VAL (16 cond) et2 | r2.04 a1.50 p90_2.73 p95_3.63 mx5.12 | r2.12 a1.61 p90_3.07 p95_3.33 mx3.86 et3 | r2.08 a1.51 p90_2.72 p95_3.78 mx5.15 | r2.12 a1.63 p90_2.84 p95_3.25 mx4.05 et2+et3 | r2.05 a1.50 p90_2.71 p95_3.70 mx5.07 | r2.11 a1.62 p90_2.95 p95_3.29 mx3.95 et2+et3+hgb | r2.00 a1.46 p90_2.77 p95_3.69 mx4.55 | r2.18 a1.63 p90_3.29 p95_3.57 mx3.96 2et+hgb+poly.3 | r1.96 a1.44 p90_2.65 p95_3.64 mx4.21 | r2.15 a1.60 p90_3.19 p95_3.46 mx4.01 2et+hgb+poly.6 | r1.94 a1.44 p90_2.60 p95_3.43 mx4.02 | r2.13 a1.61 p90_3.14 p95_3.45 mx4.09 2et+hgb+svr.5 | r1.99 a1.45 p90_2.72 p95_3.65 mx4.88 | r2.15 a1.60 p90_3.26 p95_3.52 mx3.93 2et+hgb+poly.4+svr.4 | r1.96 a1.45 p90_2.68 p95_3.58 mx4.43 | r2.13 a1.60 p90_3.17 p95_3.43 mx4.02 2et+hgb+poly.4+knn.4 | r1.98 a1.46 p90_2.69 p95_3.67 mx4.19 | r2.17 a1.63 p90_3.12 p95_3.39 mx3.98 all | r1.98 a1.46 p90_2.75 p95_3.63 mx4.47 | r2.15 a1.62 p90_3.15 p95_3.42 mx3.99
cd /app && timeout 250 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
def canon_levels(values, thr):
v=np.sort(values); labels=np.zeros(len(v),int); cur=0
for i in range(1,len(v)):
if v[i]-v[i-1]>thr: cur+=1
labels[i]=cur
return np.array([v[labels==l].mean() for l in range(cur+1)])
def snap_to(values, levels):
idx=np.argmin(np.abs(values[:,None]-levels[None,:]),axis=1); return levels[idx]
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
# build canonical levels from ALL (robust)
lv_freq=canon_levels(ALL['frequency'].values, 15.0)
lv_a=canon_levels(ALL['attack_angle'].values,0.12)
lv_c=canon_levels(ALL['chord_length'].values,0.005)
lv_v=canon_levels(ALL['free_stream_velocity'].values,0.5)
print('n levels freq,a,c,v =',len(lv_freq),len(lv_a),len(lv_c),len(lv_v))
def snap_df(df):
d=df.copy()
d['frequency']=snap_to(df['frequency'].values,lv_freq)
d['attack_angle']=snap_to(df['attack_angle'].values,lv_a)
d['chord_length']=snap_to(df['chord_length'].values,lv_c)
d['free_stream_velocity']=snap_to(df['free_stream_velocity'].values,lv_v)
# snap thickness to per-condition mean (denoise)
d['suction_side_displacement_thickness']=df.groupby(add_conditions(df)['cond'])['suction_side_displacement_thickness'].transform('mean').values if False else df['suction_side_displacement_thickness'].values
return d
G=ALL['cond'].values; Y=ALL[TARGET].values
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
ALLs=snap_df(ALL)
for nm,Xall in [('raw',fe2(ALL)),('snapped',fe2(ALLs))]:
o=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
m=ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0).fit(Xall[trm],Y[trm]); o[tem]=m.predict(Xall[tem])
report('ET '+nm+' [CV]', metrics(Y,o,G))
# val check
tr2=tr.copy(); va2=va.copy()
trs=snap_df(tr); vas=snap_df(va)
for nm,(Xtr,Xva) in [('raw',(fe2(tr),fe2(va))),('snap',(fe2(trs),fe2(vas)))]:
m=ExtraTreesRegressor(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0).fit(Xtr,tr[TARGET].values)
report('ET '+nm+' [val]', metrics(va[TARGET].values,m.predict(Xva),add_conditions(va)['cond'].values))
"n levels freq,a,c,v = 23 23 6 4 ET raw [CV] rmse=2.037 mae=1.496 p90=2.727 p95=3.634 max=5.117 | OK OK XX OK OK ET snapped [CV] rmse=2.042 mae=1.493 p90=2.778 p95=3.676 max=5.213 | OK OK XX OK OK ET raw [val] rmse=2.129 mae=1.622 p90=3.085 p95=3.348 max=3.874 | OK XX XX OK OK ET snap [val] rmse=2.050 mae=1.545 p90=2.860 p95=3.336 max=4.004 | OK OK XX OK OK
cd /app && cat > /app/snap_util.py << 'PY'
import numpy as np, pandas as pd
from harness import add_conditions
def canon_levels(values, thr):
v=np.sort(np.asarray(values,float)); labels=np.zeros(len(v),int); cur=0
for i in range(1,len(v)):
if v[i]-v[i-1]>thr: cur+=1
labels[i]=cur
return np.array([v[labels==l].mean() for l in range(cur+1)])
def fit_snapper(df):
return {
'frequency': canon_levels(df['frequency'].values,15.0),
'attack_angle': canon_levels(df['attack_angle'].values,0.12),
'chord_length': canon_levels(df['chord_length'].values,0.005),
'free_stream_velocity': canon_levels(df['free_stream_velocity'].values,0.5),
}
def _snap(values, levels):
idx=np.argmin(np.abs(np.asarray(values,float)[:,None]-levels[None,:]),axis=1); return levels[idx]
def apply_snapper(df, levels, snap_thickness=True):
d=df.copy()
for k,lv in levels.items():
d[k]=_snap(df[k].values, lv)
if snap_thickness:
cond=add_conditions(d)['cond']
d['suction_side_displacement_thickness']=d.groupby(cond)['suction_side_displacement_thickness'].transform('mean').values
return d
PY
timeout 260 python3 -c "
import numpy as np, pandas as pd, warnings, pickle
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lev=fit_snapper(ALL)
G=ALL['cond'].values; Y=ALL[TARGET].values
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
def models():
return {
'et2':(ExtraTreesRegressor(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=0),fe2),
'et3':(ExtraTreesRegressor(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=1),fe3),
'hgb':(HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0),fe2),
'poly3':(make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)),fe2),
}
# snapped feature matrices
ALLs=apply_snapper(ALL,lev)
Xs={k:tf(ALLs) for k,(m,tf) in models().items()}
oofs={}
for k,(m,tf) in models().items():
o=np.zeros(len(Y)); X=Xs[k]
from sklearn.base import clone
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
mm=clone(m); mm.fit(X[trm],Y[trm]); o[tem]=mm.predict(X[tem])
oofs[k]=o
# val
trs=apply_snapper(add_conditions(tr),lev); vas=apply_snapper(va,lev)
valp={}
for k,(m,tf) in models().items():
from sklearn.base import clone
mm=clone(m); mm.fit(tf(trs),tr[TARGET].values); valp[k]=mm.predict(tf(vas))
yva=va[TARGET].values; cva=add_conditions(va)['cond'].values
def ev(w):
ws={k:w.get(k,0) for k in oofs}; s=sum(ws.values())
pc=sum(ws[k]/s*oofs[k] for k in oofs); pv=sum(ws[k]/s*valp[k] for k in oofs)
return metrics(Y,pc,G), metrics(yva,pv,cva)
for nm,w in {'et2':{'et2':1},'et3':{'et3':1},'et2+et3':{'et2':1,'et3':1},'2et+hgb':{'et2':1,'et3':1,'hgb':1},'2et+hgb+poly.4':{'et2':1,'et3':1,'hgb':1,'poly3':0.4}}.items():
mc,mv=ev(w)
f=lambda m:'r%.2f a%.2f p90_%.2f p95_%.2f mx%.2f'%(m['rmse'],m['mae'],m['p90'],m['p95'],m['max'])
print('%-16s | CV %-36s | VAL %s'%(nm,f(mc),f(mv)))
pickle.dump({'oofs':oofs,'valp':valp,'Y':Y,'G':G,'yva':yva,'cva':cva}, open('/app/snap_oof.pkl','wb'))
"et2 | CV r2.04 a1.49 p90_2.79 p95_3.60 mx5.27 | VAL r2.06 a1.55 p90_2.85 p95_3.29 mx4.01 et3 | CV r2.07 a1.49 p90_2.80 p95_3.66 mx5.03 | VAL r2.05 a1.56 p90_2.69 p95_3.17 mx4.06 et2+et3 | CV r2.05 a1.49 p90_2.79 p95_3.63 mx5.15 | VAL r2.05 a1.55 p90_2.77 p95_3.23 mx4.03 2et+hgb | CV r2.01 a1.45 p90_2.77 p95_3.60 mx4.92 | VAL r2.08 a1.55 p90_3.07 p95_3.40 mx4.02 2et+hgb+poly.4 | CV r1.96 a1.43 p90_2.76 p95_3.54 mx4.31 | VAL r2.05 a1.54 p90_3.00 p95_3.42 mx4.06
cd /app && timeout 260 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lev=fit_snapper(ALL)
trs=apply_snapper(add_conditions(tr),lev); vas=apply_snapper(va,lev)
yva=va[TARGET].values; cva=add_conditions(va)['cond'].values
X2tr,X3tr=fe2(trs),fe3(trs); X2va,X3va=fe2(vas),fe3(vas)
def etbag(nseed2,nseed3,ntrees=700):
preds=[]
for s in range(nseed2):
m=ExtraTreesRegressor(n_estimators=ntrees,min_samples_leaf=1,n_jobs=-1,random_state=s).fit(X2tr,tr[TARGET].values); preds.append(m.predict(X2va))
for s in range(nseed3):
m=ExtraTreesRegressor(n_estimators=ntrees,min_samples_leaf=1,n_jobs=-1,random_state=100+s).fit(X3tr,tr[TARGET].values); preds.append(m.predict(X3va))
return np.mean(preds,0)
for n2,n3 in [(1,0),(2,2),(3,2),(4,3)]:
report(f'ETbag fe2x{n2} fe3x{n3} [val]', metrics(yva,etbag(n2,n3),cva))
# also ETbag + small hgb
hgb=HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,random_state=0).fit(X2tr,tr[TARGET].values)
ph=hgb.predict(X2va); pe=etbag(3,2)
for wh in [0.15,0.3]:
report(f'ETbag+hgb*{wh} [val]', metrics(yva,(1-wh)*pe+wh*ph,cva))
"ETbag fe2x1 fe3x0 [val] rmse=2.057 mae=1.553 p90=2.852 p95=3.292 max=4.010 | OK XX XX OK OK ETbag fe2x2 fe3x2 [val] rmse=2.067 mae=1.562 p90=2.806 p95=3.235 max=4.039 | OK XX XX OK OK ETbag fe2x3 fe3x2 [val] rmse=2.066 mae=1.559 p90=2.825 p95=3.254 max=4.037 | OK XX XX OK OK ETbag fe2x4 fe3x3 [val] rmse=2.064 mae=1.557 p90=2.822 p95=3.251 max=4.036 | OK XX XX OK OK ETbag+hgb*0.15 [val] rmse=2.071 mae=1.553 p90=2.944 p95=3.317 max=4.026 | OK XX XX OK OK ETbag+hgb*0.3 [val] rmse=2.085 mae=1.551 p90=3.073 p95=3.392 max=4.021 | OK XX XX OK OK
cd /app && timeout 260 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lev=fit_snapper(ALL)
trs=apply_snapper(add_conditions(tr),lev); vas=apply_snapper(va,lev)
yva=va[TARGET].values; cva=add_conditions(va)['cond'].values
X2tr,X3tr=fe2(trs),fe3(trs); X2va,X3va=fe2(vas),fe3(vas)
pe=np.mean([ExtraTreesRegressor(700,min_samples_leaf=1,n_jobs=-1,random_state=s).fit(X2tr,tr[TARGET].values).predict(X2va) for s in range(3)]+
[ExtraTreesRegressor(700,min_samples_leaf=1,n_jobs=-1,random_state=100+s).fit(X3tr,tr[TARGET].values).predict(X3va) for s in range(2)],0)
poly=make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)).fit(X2tr,tr[TARGET].values)
pp=poly.predict(X2va)
ava=vas['attack_angle'].values
report('ETbag', metrics(yva,pe,cva))
for a0,span,cap in [(10,15,0.4),(8,14,0.5),(12,12,0.5),(10,12,0.6)]:
g=np.clip((ava-a0)/span,0,cap)
report(f'angleblend a0{a0} span{span} cap{cap}', metrics(yva,(1-g)*pe+g*pp,cva))
"ETbag rmse=2.066 mae=1.559 p90=2.825 p95=3.254 max=4.037 | OK XX XX OK OK angleblend a010 span15 cap0.4 rmse=2.051 mae=1.531 p90=2.939 p95=3.476 max=4.037 | OK OK XX OK OK angleblend a08 span14 cap0.5 rmse=2.038 mae=1.515 p90=2.962 p95=3.553 max=4.037 | OK OK XX OK OK angleblend a012 span12 cap0.5 rmse=2.098 mae=1.567 p90=3.033 p95=3.513 max=4.037 | OK XX XX OK OK angleblend a010 span12 cap0.6 rmse=2.070 mae=1.534 p90=3.064 p95=3.637 max=4.037 | OK OK XX OK OK
cd /app && timeout 290 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.ensemble import ExtraTreesRegressor
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lev=fit_snapper(ALL); ALLs=apply_snapper(ALL,lev)
G=ALL['cond'].values; Y=ALL[TARGET].values
X2=fe2(ALLs); X3=fe3(ALLs)
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
oof=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
ps=[]
for s in range(3): ps.append(ExtraTreesRegressor(600,min_samples_leaf=1,n_jobs=-1,random_state=s).fit(X2[trm],Y[trm]).predict(X2[tem]))
for s in range(2): ps.append(ExtraTreesRegressor(600,min_samples_leaf=1,n_jobs=-1,random_state=100+s).fit(X3[trm],Y[trm]).predict(X3[tem]))
oof[tem]=np.mean(ps,0)
m=metrics(Y,oof,G); report('snapped ETbag [CV]',m)
df=pd.DataFrame({'e':oof-Y,'c':G}); per=df.groupby('c')['e'].apply(lambda s:np.sqrt(np.mean(s.values**2)))
print('n cond',len(per))
for thr in [2.0,2.35,2.5]: print(f' frac<= {thr}: {(per<=thr).mean():.3f}')
print('percentiles p85 %.3f p88 %.3f p90 %.3f p92 %.3f'%tuple(np.percentile(per,[85,88,90,92])))
"snapped ETbag [CV] rmse=2.045 mae=1.485 p90=2.747 p95=3.660 max=5.161 | OK OK XX OK OK n cond 82 frac<= 2.0: 0.634 frac<= 2.35: 0.793 frac<= 2.5: 0.829 percentiles p85 2.523 p88 2.692 p90 2.747 p92 2.910
cd /app && timeout 290 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.ensemble import ExtraTreesRegressor
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lev=fit_snapper(ALL); ALLs=apply_snapper(ALL,lev)
G=ALL['cond'].values; Y=ALL[TARGET].values; A=ALLs['attack_angle'].values
X2=fe2(ALLs); X3=fe3(ALLs)
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
def bag(Xtr,ytr,Xte,seeds,off=0):
return np.mean([ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=off+s).fit(Xtr,ytr).predict(Xte) for s in range(seeds)],0)
oof_g=np.zeros(len(Y)); oof_s=np.zeros(len(Y))
THR=9.0
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
oof_g[tem]=0.5*bag(X2[trm],Y[trm],X2[tem],3)+0.5*bag(X3[trm],Y[trm],X3[tem],2,100)
# specialist: train on high-angle rows only
hi=trm&(A>THR)
oof_s[tem]=0.5*bag(X2[hi],Y[hi],X2[tem],3)+0.5*bag(X3[hi],Y[hi],X3[tem],2,100)
report('global', metrics(Y,oof_g,G))
for w in [0.3,0.5,0.7]:
blend=oof_g.copy(); m=A>THR
blend[m]=(1-w)*oof_g[m]+w*oof_s[m]
report(f'global+spec(a>{THR}) w{w}', metrics(Y,blend,G))
# show high-angle condition improvement
df=pd.DataFrame({'eg':oof_g-Y,'c':G,'a':A});
perg=df.groupby('c').apply(lambda d:np.sqrt(np.mean(d['eg']**2)))
print('high-angle cond mean RMSE global:', perg[df.groupby('c')['a'].mean()>THR].mean())
"global rmse=2.047 mae=1.486 p90=2.747 p95=3.670 max=5.144 | OK OK XX OK OK global+spec(a>9.0) w0.3 rmse=2.058 mae=1.490 p90=2.764 p95=3.670 max=5.160 | OK OK XX OK OK global+spec(a>9.0) w0.5 rmse=2.069 mae=1.496 p90=2.777 p95=3.670 max=5.171 | OK OK XX OK OK global+spec(a>9.0) w0.7 rmse=2.082 mae=1.506 p90=2.874 p95=3.670 max=5.182 | OK OK XX OK OK high-angle cond mean RMSE global: 2.3128988257528182
cd /app && timeout 290 python3 -c "
import numpy as np, pandas as pd, warnings, pickle
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
tr,va=load(); ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lev=fit_snapper(ALL); ALLs=apply_snapper(ALL,lev)
G=ALL['cond'].values; Y=ALL[TARGET].values
X2=fe2(ALLs); X3=fe3(ALLs)
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=10; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
def bag(Xtr,ytr,Xte):
return np.mean([ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=s).fit(Xtr,ytr).predict(Xte) for s in range(3)]+
[ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=100+s).fit(Xtr,ytr).predict(Xte) for s in range(2)],0)
# wait need fe3 too; do separately
oe=np.zeros(len(Y)); op=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
pe2=np.mean([ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=s).fit(X2[trm],Y[trm]).predict(X2[tem]) for s in range(3)],0)
pe3=np.mean([ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=100+s).fit(X3[trm],Y[trm]).predict(X3[tem]) for s in range(2)],0)
oe[tem]=0.5*pe2+0.5*pe3
op[tem]=make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)).fit(X2[trm],Y[trm]).predict(X2[tem])
# val
trs=apply_snapper(add_conditions(tr),lev); vas=apply_snapper(va,lev)
X2t,X3t=fe2(trs),fe3(trs); X2v,X3v=fe2(vas),fe3(vas)
ve=0.5*bag(X2t,tr[TARGET].values,X2v)+0.5*np.mean([ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=200+s).fit(X3t,tr[TARGET].values).predict(X3v) for s in range(2)],0)
vp=make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)).fit(X2t,tr[TARGET].values).predict(X2v)
yva=va[TARGET].values; cva=add_conditions(va)['cond'].values
for w in [0.0,0.1,0.15,0.2,0.3]:
mc=metrics(Y,(1-w)*oe+w*op,G); mv=metrics(yva,(1-w)*ve+w*vp,cva)
fo2=lambda m:'r%.2f a%.2f p90_%.2f p95_%.2f mx%.2f'%(m['rmse'],m['mae'],m['p90'],m['p95'],m['max'])
print('poly w%.2f | CV %-34s | VAL %s'%(w,fo2(mc),fo2(mv)))
"poly w0.00 | CV r2.05 a1.49 p90_2.75 p95_3.67 mx5.14 | VAL r2.07 a1.56 p90_2.83 p95_3.26 mx4.04 poly w0.10 | CV r1.99 a1.46 p90_2.71 p95_3.61 mx4.56 | VAL r2.04 a1.55 p90_2.81 p95_3.30 mx4.06 poly w0.15 | CV r1.98 a1.45 p90_2.75 p95_3.58 mx4.28 | VAL r2.04 a1.55 p90_2.81 p95_3.34 mx4.09 poly w0.20 | CV r1.96 a1.45 p90_2.80 p95_3.56 mx4.20 | VAL r2.05 a1.56 p90_2.82 p95_3.38 mx4.14 poly w0.30 | CV r1.96 a1.46 p90_2.82 p95_3.42 mx4.13 | VAL r2.08 a1.58 p90_2.85 p95_3.49 mx4.29
command: create
path: /app/train_and_predict.py
file_text: #!/usr/bin/env python3
"""Airfoil self-noise surrogate.
Trains on the labeled public data (train + validation) and writes predictions
for the grader-staged evaluation features at /app/data/test_features.csv.
Modeling summary
----------------
* The split holds out complete aerodynamic *conditions*
(attack_angle, chord_length, free_stream_velocity). The measured inputs are
small jitters around a discrete physical grid, so we first "snap" the inputs
back onto the recovered canonical grid levels. This denoises the features and
removes jitter-induced prediction wiggle within a condition.
* Physics-motivated ratio features (Strouhal, Reynolds, Mach numbers) are added
because tree models cannot form ratios of raw inputs on their own.
* The estimator is a variance-reduced bag of ExtraTrees over two feature sets,
blended with a small smooth polynomial-ridge component that regularizes the
worst (extreme-angle) held-out conditions and keeps the per-condition tail in
check.
The workflow is fully self-contained (numpy / pandas / scikit-learn only).
"""
from pathlib import Path
import warnings
import numpy as np
import pandas as pd
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
warnings.filterwarnings("ignore")
DATA_DIR = Path("/app/data")
OUTPUT_DIR = Path("/app/output")
TARGET = "scaled_sound_pressure_level"
PREDICTION = "predicted_scaled_sound_pressure_level"
FEATURES = [
"frequency",
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
# Physically plausible clip range for the calibrated SPL target (observed
# labeled range is ~[105, 140]); this is only a safety net against any wild
# extrapolation and is deliberately well outside the observed range.
CLIP_LO, CLIP_HI = 90.0, 150.0
# Gap thresholds used to recover the discrete physical grid for each input.
SNAP_THRESHOLDS = {
"frequency": 15.0,
"attack_angle": 0.12,
"chord_length": 0.005,
"free_stream_velocity": 0.5,
}
# --------------------------------------------------------------------------- #
# Feature engineering
# --------------------------------------------------------------------------- #
def _raw_cols(df):
f = df["frequency"].astype(float).values
a = df["attack_angle"].astype(float).values
c = df["chord_length"].astype(float).values
v = df["free_stream_velocity"].astype(float).values
t = df["suction_side_displacement_thickness"].astype(float).values
# Guard against non-positive values before taking logs.
f = np.clip(f, 1e-6, None)
c = np.clip(c, 1e-9, None)
v = np.clip(v, 1e-6, None)
t = np.clip(t, 1e-12, None)
return f, a, c, v, t
def fe2(df):
"""Primary feature set: raw inputs + physics ratio features."""
f, a, c, v, t = _raw_cols(df)
lf, lt, lc, lv = np.log10(f), np.log10(t), np.log10(c), np.log10(v)
out = {}
out["lf"] = lf
out["a"] = a
out["lc"] = lc
out["v"] = v
out["lt"] = lt
out["c"] = c
out["t"] = t
out["st_delta"] = lf + lt # log10(f * delta)
out["st_delta_U"] = lf + lt - lv # log10(f * delta / U) ~ Strouhal
out["st_chord"] = lf + lc # log10(f * c)
out["st_chord_U"] = lf + lc - lv # log10(f * c / U)
out["delta_c"] = lt - lc # log10(delta / c)
out["Re_c"] = lc + lv # log10(c * U) ~ Reynolds (chord)
out["Re_delta"] = lt + lv # log10(delta * U)
out["mach"] = v / 340.0
out["lf2"] = lf ** 2
out["lf3"] = lf ** 3
out["a2"] = a ** 2
out["a_lf"] = a * lf
out["st_delta_U2"] = out["st_delta_U"] ** 2
out["a_st"] = a * out["st_delta_U"]
return np.column_stack([out[k] for k in out])
def fe3(df):
"""Secondary feature s
…[truncated]Existing file was successfully overwritten at /app/train_and_predict.py
cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py && echo "---HEAD---" && head -5 output/predictions.csv && echo "---CHECK---" && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv'); t=pd.read_csv('data/test_features.csv')
print('cols', list(p.columns))
print('n_pred', len(p), 'n_test', len(t))
print('ids match (order):', (p['sample_id'].astype(str).values==t['sample_id'].astype(str).values).all())
print('ids match (set):', set(p['sample_id'].astype(str))==set(t['sample_id'].astype(str)))
print('all finite:', np.isfinite(p['predicted_scaled_sound_pressure_level']).all())
print('n unique preds:', p['predicted_scaled_sound_pressure_level'].nunique())
print('range:', p['predicted_scaled_sound_pressure_level'].min(), p['predicted_scaled_sound_pressure_level'].max())
"Wrote 40 predictions to /app/output/predictions.csv ---HEAD--- sample_id,predicted_scaled_sound_pressure_level af_val_0000,116.07600972218142 af_val_0001,124.45264283517491 af_val_0002,133.5125146187534 af_val_0003,123.91075671387547 ---CHECK--- cols ['sample_id', 'predicted_scaled_sound_pressure_level'] n_pred 40 n_test 40 ids match (order): True ids match (set): True all finite: True n unique preds: 40 range: 114.74622686372572 137.76526503487824 [stderr] real 0m29.330s user 0m42.280s sys 0m1.540s
cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
import train_and_predict as S
from harness import add_conditions, metrics, report, TARGET
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
m=S.AirfoilSurrogate().fit(tr[S.FEATURES], tr[TARGET].values)
pred=m.predict(va[S.FEATURES])
vc=add_conditions(va)
report('FINAL model train->val', metrics(va[TARGET].values, pred, vc['cond'].values))
# per-condition distribution
df=pd.DataFrame({'e':pred-va[TARGET].values,'c':vc['cond'].values})
per=df.groupby('c')['e'].apply(lambda s:np.sqrt(np.mean(s.values**2))).sort_values()
print('val n_cond',len(per),'| frac<=2.35:',round((per<=2.35).mean(),3))
print('worst 3:', [round(x,2) for x in per.values[-3:]])
"FINAL model train->val rmse=2.028 mae=1.540 p90=2.803 p95=3.297 max=4.006 | OK OK XX OK OK val n_cond 15 | frac<=2.35: 0.8 worst 3: [2.52, 2.99, 4.01]
cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
import train_and_predict as S
from harness import add_conditions, metrics, report, TARGET
from sklearn.ensemble import ExtraTreesRegressor
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
lev=S.fit_snapper(tr[S.FEATURES]); trs=S.apply_snapper(tr[S.FEATURES],lev); vas=S.apply_snapper(va[S.FEATURES],lev)
X2t,X3t=S.fe2(trs),S.fe3(trs); X2v,X3v=S.fe2(vas),S.fe3(vas)
y=tr[TARGET].values; vc=add_conditions(va)['cond'].values; yv=va[TARGET].values
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
pp=make_pipeline(StandardScaler(),PolynomialFeatures(3),Ridge(alpha=20.0)).fit(X2t,y).predict(X2v)
for msl in [1,2,3]:
pe=np.mean([ExtraTreesRegressor(700,min_samples_leaf=msl,n_jobs=-1,random_state=s).fit(X2t,y).predict(X2v) for s in range(4)]+
[ExtraTreesRegressor(700,min_samples_leaf=msl,n_jobs=-1,random_state=1000+s).fit(X3t,y).predict(X3v) for s in range(3)],0)
for w in [0.10]:
report(f'msl{msl} poly{w}', metrics(yv,np.clip((1-w)*pe+w*pp,90,150),vc))
"msl1 poly0.1 rmse=2.027 mae=1.540 p90=2.800 p95=3.296 max=4.009 | OK OK XX OK OK msl2 poly0.1 rmse=2.082 mae=1.592 p90=2.976 p95=3.398 max=3.913 | OK XX XX OK OK msl3 poly0.1 rmse=2.175 mae=1.661 p90=3.115 p95=3.534 max=4.023 | OK XX XX OK OK
cd /app && echo "=== imports (should be stdlib/numpy/pandas/sklearn only) ===" && grep -nE "^(import|from)" train_and_predict.py && echo "=== self-contained check: run in isolated dir without dev files on path ===" && mkdir -p /tmp/isotest && cp train_and_predict.py /tmp/isotest/ && cd /tmp/isotest && python3 -c "
import ast,sys
src=open('train_and_predict.py').read()
mods=set()
for n in ast.walk(ast.parse(src)):
if isinstance(n,ast.Import):
for a in n.names: mods.add(a.name.split('.')[0])
elif isinstance(n,ast.ImportFrom):
mods.add((n.module or '').split('.')[0])
print('top-level imports:',sorted(m for m in mods if m))
local={'harness','exp','cv','cv2','cv3','oof_bank','blend_search','blend_model','val_blend','robust_blend','snap_util','mlp_test'}
print('depends on dev files:', sorted(mods & local) or 'NONE')
"=== imports (should be stdlib/numpy/pandas/sklearn only) === 24:from pathlib import Path 25:import warnings 27:import numpy as np 28:import pandas as pd 29:from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor 30:from sklearn.linear_model import Ridge 31:from sklearn.pipeline import make_pipeline 32:from sklearn.preprocessing import PolynomialFeatures, StandardScaler === self-contained check: run in isolated dir without dev files on path === top-level imports: ['numpy', 'pandas', 'pathlib', 'sklearn', 'warnings'] depends on dev files: NONE
command: str_replace path: /app/train_and_predict.py old_str: from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor new_str: from sklearn.ensemble import ExtraTreesRegressor
The file /app/train_and_predict.py has been edited successfully.
cd /tmp/isotest && cp /app/train_and_predict.py . && python3 train_and_predict.py >/dev/null 2>&1 && cp /app/output/predictions.csv /tmp/run1.csv && python3 train_and_predict.py >/dev/null 2>&1 && cp /app/output/predictions.csv /tmp/run2.csv && echo "determinism (identical across runs):" && diff -q /tmp/run1.csv /tmp/run2.csv && echo "IDENTICAL" && md5sum /tmp/run1.csv /tmp/run2.csv
determinism (identical across runs): IDENTICAL 8ba55488dfcb1517a80392218e2eba81 /tmp/run1.csv 8ba55488dfcb1517a80392218e2eba81 /tmp/run2.csv
cd /app && echo "input hashes BEFORE (recorded at start via git? no) - verify not writable-modified by timestamps:" && ls -la --time-style=+%H:%M:%S data/*.csv && rm -f /app/*.pkl /app/splineX_oof.pkl && echo "removed pkl scratch" && rm -f output/predictions.csv && python3 train_and_predict.py && echo "=== final validation of output ===" && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv'); t=pd.read_csv('data/test_features.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level'], p.columns
assert len(p)==len(t)==40
assert (p['sample_id'].astype(str).values==t['sample_id'].astype(str).values).all()
v=p['predicted_scaled_sound_pressure_level'].values
assert np.isfinite(v).all() and p['predicted_scaled_sound_pressure_level'].nunique()>1
print('OK: columns, count, id-alignment, finite, non-constant all pass')
print('pred range [%.2f, %.2f], n_unique=%d'%(v.min(),v.max(),len(np.unique(v))))
"input hashes BEFORE (recorded at start via git? no) - verify not writable-modified by timestamps: -rw-rw---- 1 h2tagent h2tagent 3595 05:18:47 data/test_features.csv -rw-rw---- 1 h2tagent h2tagent 97417 05:18:47 data/train.csv -rw-rw---- 1 h2tagent h2tagent 20351 05:18:47 data/validation.csv removed pkl scratch Wrote 40 predictions to /app/output/predictions.csv === final validation of output === OK: columns, count, id-alignment, finite, non-constant all pass pred range [114.75, 137.77], n_unique=40
cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
def build_spectra(dftr, ytr):
d=dftr.copy(); d['y']=ytr; d=add_conditions(d)
specs={}; cx=[]; ckeys=[]
for c,grp in d.groupby('cond'):
lf=np.log10(grp['frequency'].values); order=np.argsort(lf)
specs[c]=(lf[order], grp['y'].values[order])
a=grp['attack_angle'].mean(); c_=np.log10(grp['chord_length'].mean()); v=np.log10(grp['free_stream_velocity'].mean()); t=np.log10(grp['suction_side_displacement_thickness'].mean())
cx.append([a,c_,v,t]); ckeys.append(c)
return specs, np.array(cx), ckeys
def predict_struct(dftr,ytr,dfte,K=6,scale=None,power=2.0):
specs,cx,ckeys=build_spectra(dftr,ytr)
mu=cx.mean(0); sd=cx.std(0)+1e-9
if scale is not None: sd=sd/np.array(scale)
cxn=(cx-mu)/sd
te=dfte.copy();
A=te['attack_angle'].values; C=np.log10(te['chord_length'].values); V=np.log10(te['free_stream_velocity'].values); T=np.log10(te['suction_side_displacement_thickness'].values)
Q=(np.column_stack([A,C,V,T])-mu)/sd
lf_q=np.log10(te['frequency'].values)
out=np.empty(len(te))
for i in range(len(te)):
dist=np.sqrt(((cxn-Q[i])**2).sum(1))
idx=np.argsort(dist)[:K]
w=1.0/(dist[idx]**power+1e-6); w/=w.sum()
val=0.0
for j,ci in enumerate(idx):
lf_n,spl_n=specs[ckeys[ci]]
val+=w[j]*np.interp(lf_q[i], lf_n, spl_n) # clamps at edges
out[i]=val
return out
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
lev=fit_snapper(pd.concat([tr,va],ignore_index=True))
trs=apply_snapper(tr,lev); vas=apply_snapper(va,lev)
vc=add_conditions(va)['cond'].values; yv=va[TARGET].values
for K in [4,6,8,10]:
p=predict_struct(trs,tr[TARGET].values,vas,K=K)
report(f'struct-interp K{K} [val]', metrics(yv,p,vc))
"struct-interp K4 [val] rmse=2.995 mae=2.287 p90=4.223 p95=4.760 max=5.022 | XX XX XX XX OK struct-interp K6 [val] rmse=3.034 mae=2.386 p90=3.686 p95=4.025 max=4.750 | XX XX XX OK OK struct-interp K8 [val] rmse=3.153 mae=2.491 p90=3.772 p95=4.087 max=4.712 | XX XX XX OK OK struct-interp K10 [val] rmse=3.259 mae=2.570 p90=3.990 p95=4.218 max=4.716 | XX XX XX OK OK
cd /app && sed -n '200,260p' train_and_predict.py && echo "=== py compile check ===" && python3 -m py_compile train_and_predict.py && echo "compiles OK" && echo "=== final run ===" && rm -f output/predictions.csv && python3 train_and_predict.py && wc -l output/predictions.csv
ds = apply_snapper(df, self.levels_)
X2 = fe2(ds)
X3 = fe3(ds)
y = np.asarray(y, dtype=float)
self.models_fe2_ = [self._et(s).fit(X2, y) for s in range(self.n_seeds_fe2)]
self.models_fe3_ = [self._et(1000 + s).fit(X3, y) for s in range(self.n_seeds_fe3)]
self.poly_ = make_pipeline(
StandardScaler(), PolynomialFeatures(3), Ridge(alpha=20.0)
).fit(X2, y)
return self
def predict(self, df):
ds = apply_snapper(df, self.levels_)
X2 = fe2(ds)
X3 = fe3(ds)
preds = [m.predict(X2) for m in self.models_fe2_]
preds += [m.predict(X3) for m in self.models_fe3_]
et_pred = np.mean(preds, axis=0)
poly_pred = self.poly_.predict(X2)
w = self.poly_weight
out = (1.0 - w) * et_pred + w * poly_pred
return np.clip(out, CLIP_LO, CLIP_HI)
# --------------------------------------------------------------------------- #
# Data I/O
# --------------------------------------------------------------------------- #
def load_labeled_data():
parts = [pd.read_csv(DATA_DIR / "train.csv")]
validation_path = DATA_DIR / "validation.csv"
if validation_path.exists():
parts.append(pd.read_csv(validation_path))
return pd.concat(parts, ignore_index=True)
def load_evaluation_features():
test_path = DATA_DIR / "test_features.csv"
if test_path.exists():
return pd.read_csv(test_path)
validation = pd.read_csv(DATA_DIR / "validation.csv")
return validation.drop(columns=[TARGET])
def main():
labeled = load_labeled_data()
evaluation = load_evaluation_features()
model = AirfoilSurrogate()
model.fit(labeled[FEATURES], labeled[TARGET].values)
predictions = model.predict(evaluation[FEATURES])
predictions = np.asarray(predictions, dtype=float)
# Final safety: replace any non-finite value with the training mean.
if not np.all(np.isfinite(predictions)):
fill = float(np.mean(labeled[TARGET].values))
predictions = np.where(np.isfinite(predictions), predictions, fill)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
pd.DataFrame(
{
=== py compile check ===
compiles OK
=== final run ===
Wrote 40 predictions to /app/output/predictions.csv
41 output/predictions.csvcd /app && timeout 260 python3 -c "
import numpy as np, pandas as pd, warnings
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
lev=fit_snapper(pd.concat([tr,va],ignore_index=True))
trs=apply_snapper(tr,lev); vas=apply_snapper(va,lev)
X2t=fe2(trs); X2v=fe2(vas); y=tr[TARGET].values
vc=add_conditions(va)['cond'].values; yv=va[TARGET].values
def bagmlp(hid,alpha,n):
ps=[make_pipeline(StandardScaler(),MLPRegressor(hid,solver='lbfgs',alpha=alpha,activation='tanh',max_iter=3000,random_state=i)).fit(X2t,y).predict(X2v) for i in range(n)]
return np.mean(ps,0)
# ET reference (snapped)
pe=np.mean([ExtraTreesRegressor(700,min_samples_leaf=1,n_jobs=-1,random_state=s).fit(X2t,y).predict(X2v) for s in range(3)],0)
report('ET ref', metrics(yv,pe,vc))
for hid,alpha in [((96,48),3e-3),((96,48),1e-2),((128,64),1e-2),((64,32),3e-3)]:
pm=bagmlp(hid,alpha,8)
report(f'MLP snap {hid} a{alpha}', metrics(yv,pm,vc))
report(f' 0.6ET+0.4MLP', metrics(yv,0.6*pe+0.4*pm,vc))
"ET ref rmse=2.065 mae=1.556 p90=2.882 p95=3.316 max=4.014 | OK XX XX OK OK MLP snap (96, 48) a0.003 rmse=2.225 mae=1.674 p90=3.707 p95=4.549 max=4.662 | OK XX XX OK OK 0.6ET+0.4MLP rmse=1.955 mae=1.492 p90=2.654 p95=3.170 max=4.104 | OK OK XX OK OK MLP snap (96, 48) a0.01 rmse=2.211 mae=1.664 p90=3.017 p95=3.983 max=5.290 | OK XX XX OK OK 0.6ET+0.4MLP rmse=1.975 mae=1.506 p90=2.499 p95=3.100 max=4.344 | OK OK XX OK OK MLP snap (128, 64) a0.01 rmse=2.237 mae=1.690 p90=3.514 p95=4.254 max=5.150 | OK XX XX OK OK 0.6ET+0.4MLP rmse=1.940 mae=1.502 p90=2.286 p95=2.875 max=4.238 | OK OK OK OK OK
cd /app && timeout 295 python3 -c "
import numpy as np, pandas as pd, warnings, pickle
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lev=fit_snapper(ALL); ALLs=apply_snapper(ALL,lev)
G=ALL['cond'].values; Y=ALL[TARGET].values; X2=fe2(ALLs); X3=fe3(ALLs)
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=8; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
oe=np.zeros(len(Y)); om=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
pe=[ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=s).fit(X2[trm],Y[trm]).predict(X2[tem]) for s in range(3)]
pe+=[ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=100+s).fit(X3[trm],Y[trm]).predict(X3[tem]) for s in range(2)]
oe[tem]=np.mean(pe,0)
pm=[make_pipeline(StandardScaler(),MLPRegressor((96,48),solver='lbfgs',alpha=0.01,activation='tanh',max_iter=2500,random_state=i)).fit(X2[trm],Y[trm]).predict(X2[tem]) for i in range(4)]
om[tem]=np.mean(pm,0)
pickle.dump({'oe':oe,'om':om,'Y':Y,'G':G},open('/app/etmlp_oof.pkl','wb'))
report('ET only [CV]', metrics(Y,oe,G))
report('MLP only [CV]', metrics(Y,om,G))
for w in [0.3,0.4,0.5]:
report(f'{1-w:.1f}ET+{w:.1f}MLP [CV]', metrics(Y,(1-w)*oe+w*om,G))
"<system>Tool ran without output or errors</system>
cd /app && python3 -c "
import numpy as np, pickle
from harness import metrics, report
d=pickle.load(open('/app/etmlp_oof.pkl','rb')); oe=d['oe']; om=d['om']; Y=d['Y']; G=d['G']
report('ET only [CV]', metrics(Y,oe,G))
report('MLP only [CV]', metrics(Y,om,G))
import numpy as np
for w in [0.25,0.3,0.35,0.4,0.45,0.5]:
report(f'{1-w:.2f}ET+{w:.2f}MLP [CV]', metrics(Y,(1-w)*oe+w*om,G))
# distribution for best
import pandas as pd
for w in [0.4]:
p=(1-w)*oe+w*om
per=pd.DataFrame({'e':p-Y,'c':G}).groupby('c')['e'].apply(lambda s:np.sqrt(np.mean(s.values**2)))
print('frac<=2.35:',round((per<=2.35).mean(),3),'n_cond',len(per))
"[stderr] Traceback (most recent call last): File "<string>", line 4, in <module> FileNotFoundError: [Errno 2] No such file or directory: '/app/etmlp_oof.pkl'
cd /app && timeout 295 python3 -c "
import numpy as np, pandas as pd, warnings, pickle, time
warnings.filterwarnings('ignore')
from harness import *
from snap_util import fit_snapper, apply_snapper
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import ExtraTreesRegressor
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
ALL=add_conditions(pd.concat([tr,va],ignore_index=True))
lev=fit_snapper(ALL); ALLs=apply_snapper(ALL,lev)
G=ALL['cond'].values; Y=ALL[TARGET].values; X2=fe2(ALLs); X3=fe3(ALLs)
uniq=pd.unique(G); rng=np.random.RandomState(0); perm=rng.permutation(len(uniq))
NS=5; fo={c:(i%NS) for i,c in enumerate(uniq[perm])}; FOLDS=np.array([fo[c] for c in G])
t0=time.time()
oe=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
pe=[ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=s).fit(X2[trm],Y[trm]).predict(X2[tem]) for s in range(3)]
pe+=[ExtraTreesRegressor(500,min_samples_leaf=1,n_jobs=-1,random_state=100+s).fit(X3[trm],Y[trm]).predict(X3[tem]) for s in range(2)]
oe[tem]=np.mean(pe,0)
print('ET done %.0fs'%(time.time()-t0))
pickle.dump({'oe':oe,'Y':Y,'G':G},open('/app/et_oof.pkl','wb'))
om=np.zeros(len(Y))
for f in range(NS):
trm=FOLDS!=f; tem=FOLDS==f
pm=[make_pipeline(StandardScaler(),MLPRegressor((64,32),solver='lbfgs',alpha=0.01,activation='tanh',max_iter=1500,random_state=i)).fit(X2[trm],Y[trm]).predict(X2[tem]) for i in range(4)]
om[tem]=np.mean(pm,0)
print('MLP done %.0fs'%(time.time()-t0))
pickle.dump({'oe':oe,'om':om,'Y':Y,'G':G},open('/app/etmlp_oof.pkl','wb'))
report('ET [CV5]',metrics(Y,oe,G)); report('MLP [CV5]',metrics(Y,om,G))
for w in [0.3,0.4,0.5]: report(f'{1-w:.1f}ET+{w:.1f}MLP',metrics(Y,(1-w)*oe+w*om,G))
"ET done 49s MLP done 101s ET [CV5] rmse=2.111 mae=1.545 p90=2.736 p95=3.705 max=5.508 | OK OK XX OK OK MLP [CV5] rmse=2.689 mae=1.898 p90=4.468 p95=4.790 max=8.064 | XX XX XX XX XX 0.7ET+0.3MLP rmse=2.054 mae=1.477 p90=2.778 p95=3.567 max=5.342 | OK OK XX OK OK 0.6ET+0.4MLP rmse=2.084 mae=1.490 p90=2.869 p95=3.688 max=5.705 | OK OK XX OK XX 0.5ET+0.5MLP rmse=2.138 mae=1.519 p90=3.047 p95=3.890 max=6.079 | OK OK XX OK XX
cd /app && rm -f /app/*.pkl && rm -rf /tmp/isotest /tmp/run1.csv /tmp/run2.csv && echo "=== final files in /app ===" && ls -la /app/*.py /app/output/ && echo && echo "=== confirm inputs untouched (original 05:18 timestamps) ===" && ls -la --time-style=+%H:%M data/*.csv && echo && echo "=== final output sanity ===" && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('/app/output/predictions.csv')
print('columns:', list(p.columns))
print('rows:', len(p), '| finite:', bool(np.isfinite(p.iloc[:,1]).all()), '| unique preds:', p.iloc[:,1].nunique())
print('pred range: [%.2f, %.2f]'%(p.iloc[:,1].min(), p.iloc[:,1].max()))
"=== final files in /app === -rw-r--r-- 1 h2tagent h2tagent 1816 Jun 21 06:15 /app/blend_model.py -rw-r--r-- 1 h2tagent h2tagent 2079 Jun 21 06:07 /app/blend_search.py -rw-r--r-- 1 h2tagent h2tagent 1589 Jun 21 05:28 /app/cv.py -rw-r--r-- 1 h2tagent h2tagent 1402 Jun 21 05:30 /app/cv2.py -rw-r--r-- 1 h2tagent h2tagent 2738 Jun 21 05:32 /app/cv3.py -rw-r--r-- 1 h2tagent h2tagent 2049 Jun 21 05:27 /app/exp.py -rw-r--r-- 1 h2tagent h2tagent 3871 Jun 21 05:40 /app/harness.py -rw-r--r-- 1 h2tagent h2tagent 1463 Jun 21 05:47 /app/mlp_test.py -rw-r--r-- 1 h2tagent h2tagent 2342 Jun 21 06:01 /app/oof_bank.py -rw-r--r-- 1 h2tagent h2tagent 2506 Jun 21 06:20 /app/robust_blend.py -rw-r--r-- 1 h2tagent h2tagent 1123 Jun 21 06:23 /app/snap_util.py -rwxrwx--- 1 h2tagent h2tagent 9187 Jun 21 06:39 /app/train_and_predict.py -rw-r--r-- 1 h2tagent h2tagent 2118 Jun 21 06:20 /app/val_blend.py /app/output/: total 10 drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 06:42 . drwxrws--- 5 h2tagent h2tagent 4096 Jun 21 06:55 .. -rw-r--r-- 1 h2tagent h2tagent 1280 Jun 21 06:42 predictions.csv === confirm inputs untouched (original 05:18 timestamps) === -rw-rw---- 1 h2tagent h2tagent 3595 05:18 data/test_features.csv -rw-rw---- 1 h2tagent h2tagent 97417 05:18 data/train.csv -rw-rw---- 1 h2tagent h2tagent 20351 05:18 data/validation.csv === final output sanity === columns: ['sample_id', 'predicted_scaled_sound_pressure_level'] rows: 40 | finite: True | unique preds: 40 pred range: [114.75, 137.77]
cd /app && rm -f harness.py exp.py cv.py cv2.py cv3.py oof_bank.py blend_search.py blend_model.py val_blend.py robust_blend.py snap_util.py mlp_test.py && echo "=== /app/*.py now ===" && ls /app/*.py && echo && echo "=== grader-style re-run: discard output, re-run script ===" && rm -f /app/output/predictions.csv && cd / && python3 /app/train_and_predict.py && echo && echo "=== verify against test_features ===" && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('/app/output/predictions.csv'); t=pd.read_csv('/app/data/test_features.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level']
assert len(p)==len(t)
assert set(p['sample_id'].astype(str))==set(t['sample_id'].astype(str))
assert np.isfinite(p['predicted_scaled_sound_pressure_level']).all()
assert p['predicted_scaled_sound_pressure_level'].nunique()>1
print('FINAL CHECK PASSED: one finite, non-constant prediction per sample_id; columns exact.')
"=== /app/*.py now === /app/train_and_predict.py === grader-style re-run: discard output, re-run script === Wrote 40 predictions to /app/output/predictions.csv === verify against test_features === FINAL CHECK PASSED: one finite, non-constant prediction per sample_id; columns exact.
cd /app && sed -n '160,200p' train_and_predict.py
return np.array([v[labels == l].mean() for l in range(cur + 1)])
def fit_snapper(df):
return {k: canon_levels(df[k].values, thr) for k, thr in SNAP_THRESHOLDS.items()}
def apply_snapper(df, levels):
d = df.copy()
for k, lv in levels.items():
vals = np.asarray(df[k].values, dtype=float)
idx = np.argmin(np.abs(vals[:, None] - lv[None, :]), axis=1)
d[k] = lv[idx]
# Thickness is left as measured: its jitter is negligible (~0.2%) and leaving
# it raw avoids any dependence on clustering an arbitrary evaluation set.
return d
# --------------------------------------------------------------------------- #
# Model
# --------------------------------------------------------------------------- #
class AirfoilSurrogate:
"""Snapped-feature ExtraTrees bag blended with a small smooth component."""
def __init__(self, n_seeds_fe2=4, n_seeds_fe3=3, n_trees=800, poly_weight=0.10):
self.n_seeds_fe2 = n_seeds_fe2
self.n_seeds_fe3 = n_seeds_fe3
self.n_trees = n_trees
self.poly_weight = poly_weight
def _et(self, seed):
return ExtraTreesRegressor(
n_estimators=self.n_trees,
min_samples_leaf=1,
n_jobs=-1,
random_state=seed,
)
def fit(self, df, y):
self.levels_ = fit_snapper(df)
ds = apply_snapper(df, self.levels_)No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary
# task: airfoil-self-noise
# attempt: 2 status: completed raw_score: 0.49
# reward rule: continuous: score 0.49 < 0.5
# => reward = 0.0 (FAIL)
#
# Note: these tasks score via an external hidden grader; no pytest/verifier
# block is embedded in the agent transcript. The block below is the last
# sizable tool-output captured in the run (supporting evidence).
----------------------------------------------------------------------
return np.array([v[labels == l].mean() for l in range(cur + 1)])
def fit_snapper(df):
return {k: canon_levels(df[k].values, thr) for k, thr in SNAP_THRESHOLDS.items()}
def apply_snapper(df, levels):
d = df.copy()
for k, lv in levels.items():
vals = np.asarray(df[k].values, dtype=float)
idx = np.argmin(np.abs(vals[:, None] - lv[None, :]), axis=1)
d[k] = lv[idx]
# Thickness is left as measured: its jitter is negligible (~0.2%) and leaving
# it raw avoids any dependence on clustering an arbitrary evaluation set.
return d
# --------------------------------------------------------------------------- #
# Model
# --------------------------------------------------------------------------- #
class AirfoilSurrogate:
"""Snapped-feature ExtraTrees bag blended with a small smooth component."""
def __init__(self, n_seeds_fe2=4, n_seeds_fe3=3, n_trees=800, poly_weight=0.10):
self.n_seeds_fe2 = n_seeds_fe2
self.n_seeds_fe3 = n_seeds_fe3
self.n_trees = n_trees
self.poly_weight = poly_weight
def _et(self, seed):
return ExtraTreesRegressor(
n_estimators=self.n_trees,
min_samples_leaf=1,
n_jobs=-1,
random_state=seed,
)
def fit(self, df, y):
self.levels_ = fit_snapper(df)
ds = apply_snapper(df, self.levels_)Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_f1edf582707444c7. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_f1edf582707444c7 · verifier authoritative; classifier explanatory.