tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.cd /app && ls -la && echo "---DATA---" && ls -la data/ && echo "---SCRIPT---" && cat train_and_predict.py
total 20
drwxrws--- 4 h2tagent h2tagent 4096 Jun 21 05:20 .
drwxr-xr-x 26 root root 4096 Jun 21 05:25 ..
drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 data
drwxrws--- 2 h2tagent h2tagent 4096 Jun 21 05:20 output
-rwxrwx--- 1 h2tagent h2tagent 3211 Jun 21 05:18 train_and_predict.py
---DATA---
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
---SCRIPT---
#!/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 && echo "===TRAIN HEAD===" && head -5 data/train.csv && echo "" && echo "===VALIDATION HEAD===" && head -5 data/validation.csv && echo "" && echo "===TEST_FEATURES HEAD===" && head -5 data/test_features.csv && echo "" && echo "===COUNTS===" && wc -l data/*.csv
===TRAIN HEAD===
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
===VALIDATION HEAD===
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
===TEST_FEATURES HEAD===
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
===COUNTS===
41 data/test_features.csv
957 data/train.csv
202 data/validation.csv
1200 totalcd /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()
for name,df in [('train',tr),('val',va)]:
print('==',name,'==')
print(df.describe().T[['min','max','mean']])
print()
"train shape (956, 7) val shape (201, 7)
== train ==
min max mean
frequency 199.660258 20025.582040 2688.724820
attack_angle -0.035000 22.234770 7.381738
chord_length 0.025370 0.305164 0.133004
free_stream_velocity 31.652457 71.406950 51.207469
suction_side_displacement_thickness 0.000411 0.058556 0.012331
scaled_sound_pressure_level 104.935002 140.289040 125.402453
== val ==
min max mean
frequency 199.661620 20017.248537 3328.983497
attack_angle -0.034726 17.434768 5.906241
chord_length 0.025370 0.305164 0.107417
free_stream_velocity 31.652508 71.406950 46.757698
suction_side_displacement_thickness 0.000400 0.040928 0.011145
scaled_sound_pressure_level 109.788714 138.661407 126.483017cd /app && python3 -c "
import pandas as pd, numpy as np
tr = pd.read_csv('data/train.csv')
va = pd.read_csv('data/validation.csv')
# Unique values of the 'discrete' features
for col in ['attack_angle','chord_length','free_stream_velocity']:
print(col)
print(' train n_unique:', tr[col].nunique(), 'val n_unique:', va[col].nunique())
# The raw UCI dataset has discrete values. But these look continuous (perturbed). Let's round.
def cond(df, r=3):
return list(zip(df.attack_angle.round(r), df.chord_length.round(r), df.free_stream_velocity.round(r)))
tr_c = set(cond(tr)); va_c = set(cond(va))
print()
print('train conditions (rounded 3):', len(tr_c))
print('val conditions (rounded 3):', len(va_c))
print('overlap:', len(tr_c & va_c))
"attack_angle train n_unique: 956 val n_unique: 201 chord_length train n_unique: 956 val n_unique: 201 free_stream_velocity train n_unique: 956 val n_unique: 201 train conditions (rounded 3): 956 val conditions (rounded 3): 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')
al = pd.concat([tr,va])
for col in ['chord_length','free_stream_velocity']:
v = np.sort(al[col].unique())
print(col, 'range', v.min(), v.max())
# histogram of values to see clustering
import numpy as np
hist = np.histogram(al[col], bins=40)
# print approximate clusters
print(np.round(v[:6],5), '...')
print()
# Attack angle distribution
print('attack_angle sorted sample:')
print(np.round(np.sort(al.attack_angle.unique())[:20],4))
"chord_length range 0.0253695224779 0.305163737785 [0.02537 0.02537 0.02537 0.02537 0.02537 0.02537] ... free_stream_velocity range 31.6524569887 71.4069498524 [31.65246 31.65246 31.6525 31.65251 31.6526 31.65275] ... attack_angle sorted sample: [-0.035 -0.035 -0.035 -0.035 -0.035 -0.0348 -0.0348 -0.0347 -0.0347 -0.0347 -0.0347 -0.0347 -0.0347 -0.0347 -0.0347 -0.034 -0.034 -0.034 -0.0339 -0.0339]
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')
al = pd.concat([tr,va])
def clusters(x, rel_gap=0.02):
v = np.sort(x.values)
# split where gap is big relative to scale
groups=[[v[0]]]
for a,b in zip(v[:-1],v[1:]):
scale = max(abs(a),1e-9)
if (b-a) > rel_gap*scale and (b-a)>1e-6:
groups.append([])
groups[-1].append(b)
centers=[np.mean(g) for g in groups]
counts=[len(g) for g in groups]
return centers,counts
for col in ['attack_angle','chord_length','free_stream_velocity']:
c,n = clusters(al[col])
print(col, 'n_clusters=',len(c))
print(' centers:', np.round(c,4))
print(' counts :', n)
print()
"attack_angle n_clusters= 63 centers: [-3.45000e-02 -3.25000e-02 -3.07000e-02 -2.83000e-02 -2.55000e-02 -2.23000e-02 -1.87000e-02 -1.49000e-02 -1.08000e-02 -6.90000e-03 -6.60000e-03 -6.30000e-03 -6.20000e-03 -2.60000e-03 -2.50000e-03 -2.40000e-03 -2.30000e-03 -2.30000e-03 -2.10000e-03 -2.00000e-03 -1.90000e-03 -1.80000e-03 1.80000e-03 1.90000e-03 2.00000e-03 2.00000e-03 2.20000e-03 2.40000e-03 2.40000e-03 2.50000e-03 2.60000e-03 6.30000e-03 6.50000e-03 6.90000e-03 1.08000e-02 1.49000e-02 1.88000e-02 2.23000e-02 2.55000e-02 2.84000e-02 3.06000e-02 3.25000e-02 3.45000e-02 1.49780e+00 2.00080e+00 3.00210e+00 3.30000e+00 3.99980e+00 4.20130e+00 4.80370e+00 5.35160e+00 6.70020e+00 7.25970e+00 8.39930e+00 8.90110e+00 9.49750e+00 9.90070e+00 1.12028e+01 1.24796e+01 1.54762e+01 1.74001e+01 1.96936e+01 2.22038e+01] counts : [25, 10, 11, 9, 10, 10, 9, 9, 10, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 4, 3, 9, 10, 8, 9, 10, 7, 7, 11, 20, 31, 56, 30, 50, 78, 21, 38, 28, 33, 81, 34, 32, 40, 48, 28, 130, 79, 63, 14, 15] chord_length n_clusters= 6 centers: [0.0254 0.0508 0.1016 0.1524 0.2286 0.3048] counts : [235, 185, 250, 169, 193, 125] free_stream_velocity n_clusters= 4 centers: [31.7023 39.5964 55.4993 71.2948] counts : [224, 371, 222, 340]
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')
al = pd.concat([tr,va], ignore_index=True)
def level_assign(x, rel_gap=0.01, abs_gap=0.05):
v = np.sort(x.unique())
centers=[]
cur=[v[0]]
for a,b in zip(v[:-1],v[1:]):
gap=b-a
scale=max(abs(a),abs(b))
if gap>abs_gap and gap>rel_gap*scale:
centers.append(cur); cur=[]
cur.append(b)
centers.append(cur)
reps=[np.mean(c) for c in centers]
def mapper(val):
return reps[int(np.argmin([abs(val-r) for r in reps]))]
return reps, mapper
levels={}
for col in ['attack_angle','chord_length','free_stream_velocity']:
reps,mapper=level_assign(al[col])
levels[col]=(reps,mapper)
for df in (tr,va,al):
df[col+'_lvl']=df[col].map(mapper)
print(col,'->',len(reps),'levels')
def condset(df):
return set(zip(df.attack_angle_lvl.round(3),df.chord_length_lvl.round(4),df.free_stream_velocity_lvl.round(2)))
trc,vac=condset(tr),condset(va)
print()
print('train conditions:',len(trc))
print('val conditions:',len(vac))
print('overlap:',len(trc&vac))
print('val conds fully held out?', len(vac-trc)==len(vac))
# rows per condition
from collections import Counter
def condcol(df):
return list(zip(df.attack_angle_lvl.round(3),df.chord_length_lvl.round(4),df.free_stream_velocity_lvl.round(2)))
cc=Counter(condcol(tr))
print('train rows/condition: min',min(cc.values()),'max',max(cc.values()),'mean',round(np.mean(list(cc.values())),1))
vcc=Counter(condcol(va))
print('val rows/condition: min',min(vcc.values()),'max',max(vcc.values()),'mean',round(np.mean(list(vcc.values())),1))
"attack_angle -> 22 levels chord_length -> 5 levels free_stream_velocity -> 4 levels train conditions: 66 val conditions: 15 overlap: 2 val conds fully held out? False train rows/condition: min 8 max 26 mean 14.5 val rows/condition: min 8 max 18 mean 13.4
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')
al = pd.concat([tr,va], ignore_index=True)
CHORD=np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
VEL=np.array([31.7,39.6,55.5,71.3])
def snap(x,grid): return grid[np.argmin(np.abs(grid-x))]
# attack angle: cluster with small absolute gap, near-zero -> 0
def angle_levels(x):
v=np.sort(x.unique()); centers=[]; cur=[v[0]]
for a,b in zip(v[:-1],v[1:]):
if (b-a)>0.15:
centers.append(cur); cur=[]
cur.append(b)
centers.append(cur)
reps=np.array([np.mean(c) for c in centers])
reps=np.where(np.abs(reps)<0.1,0.0,reps)
return reps
AREPS=angle_levels(al.attack_angle)
print('angle levels:',len(AREPS)); print(np.round(AREPS,3))
def snapa(x): return AREPS[np.argmin(np.abs(AREPS-x))]
for df in (tr,va):
df['a']=df.attack_angle.map(snapa)
df['c']=df.chord_length.map(lambda z: snap(z,CHORD))
df['v']=df.free_stream_velocity.map(lambda z: snap(z,VEL))
def cs(df): return set(zip(df.a.round(3),df.c.round(4),df.v.round(2)))
trc,vac=cs(tr),cs(va)
print('train conds',len(trc),'val conds',len(vac),'overlap',len(trc&vac))
print('overlapping:',trc&vac)
"angle levels: 21 [ 0. 1.498 2.001 3.002 3.3 4.043 4.804 5.352 6.7 7.26 8.399 8.901 9.498 9.901 11.203 12.301 12.652 15.476 17.4 19.694 22.204] train conds 67 val conds 15 overlap 0 overlapping: set()
cd /app && cat > /tmp/eval_lib.py << 'EOF'
import pandas as pd, numpy as np
CHORD=np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
VEL=np.array([31.7,39.6,55.5,71.3])
def _angle_levels(x):
v=np.sort(np.unique(x)); centers=[]; cur=[v[0]]
for a,b in zip(v[:-1],v[1:]):
if (b-a)>0.15:
centers.append(cur); cur=[]
cur.append(b)
centers.append(cur)
reps=np.array([np.mean(c) for c in centers])
reps=np.where(np.abs(reps)<0.1,0.0,reps)
return reps
def make_conditions(df, arefs):
def snap(x,grid): return grid[np.argmin(np.abs(grid-x))]
a=df.attack_angle.map(lambda z: arefs[np.argmin(np.abs(arefs-z))])
c=df.chord_length.map(lambda z: snap(z,CHORD))
v=df.free_stream_velocity.map(lambda z: snap(z,VEL))
return list(zip(a.round(3),c.round(4),v.round(2)))
def angle_refs(*dfs):
allx=np.concatenate([d.attack_angle.values for d in dfs])
return _angle_levels(allx)
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({'c':cond,'e':err})
crmse=dfc.groupby('c')['e'].apply(lambda s: np.sqrt(np.mean(s**2)))
return {
'rmse':rmse,'mae':mae,
'p90':np.percentile(crmse,90),
'p95':np.percentile(crmse,95),
'max':crmse.max(),
'n_cond':len(crmse),
}
TARGETS={'rmse':2.25,'mae':1.55,'p90':2.35,'p95':4.75,'max':5.60}
def report(m,label=''):
flags=' '.join(f"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}" for k in TARGETS)
print(f"{label:24s} rmse={m['rmse']:.3f} mae={m['mae']:.3f} p90={m['p90']:.3f} p95={m['p95']:.3f} max={m['max']:.3f} nC={m['n_cond']} | {flags}")
EOF
echo "eval lib written"eval lib written
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import pandas as pd, numpy as np
from eval_lib import *
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
import importlib.util
spec=importlib.util.spec_from_file_location('t','/app/train_and_predict.py')
t=importlib.util.module_from_spec(spec); spec.loader.exec_module(t)
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
arefs=angle_refs(tr,va)
cond=make_conditions(va,arefs)
model=make_pipeline(t.AirfoilFeatures(),StandardScaler(),Ridge(alpha=10.0))
model.fit(tr[t.FEATURES],tr[t.TARGET])
pred=model.predict(va[t.FEATURES])
report(metrics(va[t.TARGET],pred,cond),'starter Ridge (train->val)')
"starter Ridge (train->val) rmse=5.840 mae=4.697 p90=7.948 p95=8.562 max=9.477 nC=15 | rmse=XX mae=XX p90=XX p95=XX max=XX
cd /app && python3 -c "
import importlib
for m in ['sklearn','xgboost','lightgbm','catboost','scipy','numpy','pandas']:
try:
mod=importlib.import_module(m)
print(f'{m:12s} {getattr(mod,\"__version__\",\"?\")}')
except Exception as e:
print(f'{m:12s} NOT AVAILABLE ({type(e).__name__})')
"sklearn 1.4.2 xgboost NOT AVAILABLE (ModuleNotFoundError) lightgbm NOT AVAILABLE (ModuleNotFoundError) catboost NOT AVAILABLE (ModuleNotFoundError) scipy 1.17.1 numpy 1.26.4 pandas 2.2.2
cd /app && timeout 20 pip install xgboost 2>&1 | tail -3 || echo "no install"
WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x7ee0008a3dd0>: Failed to establish a new connection: [Errno -3] Temporary failure in name resolution')': /simple/xgboost/
ERROR: Could not find a version that satisfies the requirement xgboost (from versions: none)
ERROR: No matching distribution found for xgboostcd /app && cat > /tmp/feat.py << 'EOF'
import numpy as np, pandas as pd
FEATURES=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
def engineer(df):
f=df['frequency'].astype(float).values
a=df['attack_angle'].astype(float).values
c=df['chord_length'].astype(float).values
u=df['free_stream_velocity'].astype(float).values
d=df['suction_side_displacement_thickness'].astype(float).values
lf=np.log10(f); ld=np.log10(d); lc=np.log10(c); lu=np.log10(u)
st=f*d/u # Strouhal number
lst=np.log10(st)
re_c=u*c/1.5e-5 # Reynolds (chord)
re_d=u*d/1.5e-5 # Reynolds (thickness)
arad=np.radians(a)
out=pd.DataFrame(index=df.index)
out['f']=f; out['a']=a; out['c']=c; out['u']=u; out['d']=d
out['lf']=lf; out['ld']=ld; out['lc']=lc; out['lu']=lu
out['st']=st; out['lst']=lst
out['lre_c']=np.log10(re_c); out['lre_d']=np.log10(re_d)
out['sin_a']=np.sin(arad); out['a2']=a*a
out['lf_ld']=lf*ld; out['lf_a']=lf*a; out['lf_lu']=lf*lu
out['a_lu']=a*lu; out['ld_a']=ld*a; out['lc_lu']=lc*lu
out['lf2']=lf*lf; out['lst2']=lst*lst; out['lst3']=lst**3
out['a_lc']=a*lc; out['lf_lc']=lf*lc
return out
EOF
python3 -c "
import sys; sys.path.insert(0,'/tmp')
import pandas as pd, numpy as np
from eval_lib import *
from feat import engineer, FEATURES
from sklearn.ensemble import HistGradientBoostingRegressor, RandomForestRegressor, ExtraTreesRegressor
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.neighbors import KNeighborsRegressor
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
arefs=angle_refs(tr,va); cond=make_conditions(va,arefs)
Xtr=engineer(tr); Xva=engineer(va); ytr=tr[TARGET:=('scaled_sound_pressure_level')].values; yva=va[TARGET].values
def ev(model,label):
model.fit(Xtr,ytr); p=model.predict(Xva); report(metrics(yva,p,cond),label)
ev(make_pipeline(StandardScaler(),Ridge(alpha=1.0)),'Ridge(eng)')
ev(make_pipeline(StandardScaler(),PolynomialFeatures(2),Ridge(alpha=5.0)),'PolyRidge2(eng)')
ev(RandomForestRegressor(n_estimators=400,n_jobs=-1,random_state=0),'RF(eng)')
ev(ExtraTreesRegressor(n_estimators=400,n_jobs=-1,random_state=0),'ET(eng)')
ev(HistGradientBoostingRegressor(random_state=0),'HGB default(eng)')
ev(KNeighborsRegressor(n_neighbors=7),'KNN7(eng)')
"Ridge(eng) rmse=3.356 mae=2.554 p90=4.566 p95=5.036 max=6.130 nC=15 | rmse=XX mae=XX p90=XX p95=XX max=XX PolyRidge2(eng) rmse=2.673 mae=2.013 p90=3.786 p95=4.158 max=4.498 nC=15 | rmse=XX mae=XX p90=XX p95=OK max=OK RF(eng) rmse=2.796 mae=2.215 p90=3.897 p95=4.073 max=4.079 nC=15 | rmse=XX mae=XX p90=XX p95=OK max=OK ET(eng) rmse=2.214 mae=1.766 p90=2.855 p95=3.124 max=3.685 nC=15 | rmse=OK mae=XX p90=XX p95=OK max=OK HGB default(eng) rmse=2.599 mae=2.046 p90=3.679 p95=3.959 max=4.375 nC=15 | rmse=XX mae=XX p90=XX p95=OK max=OK KNN7(eng) rmse=6.528 mae=5.347 p90=9.454 p95=9.759 max=9.967 nC=15 | rmse=XX mae=XX p90=XX p95=XX max=XX
cd /app && cat > /tmp/cv.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from eval_lib import make_conditions, angle_refs, metrics, TARGETS
from feat import engineer, FEATURES
from sklearn.model_selection import GroupKFold
TARGET='scaled_sound_pressure_level'
tr=pd.read_csv('/app/data/train.csv'); va=pd.read_csv('/app/data/validation.csv')
ALL=pd.concat([tr,va],ignore_index=True)
AREFS=angle_refs(tr,va)
ALL_COND=make_conditions(ALL,AREFS)
ALL['cond']=ALL_COND
groups=pd.factorize(pd.Series(ALL_COND).astype(str))[0]
X=engineer(ALL); y=ALL[TARGET].values
def cv_eval(make_model, n_splits=8, seeds=(0,)):
gkf=GroupKFold(n_splits=n_splits)
oof=np.zeros(len(y)); cnt=np.zeros(len(y))
for seed in seeds:
for tri,tei in gkf.split(X,y,groups):
m=make_model()
m.fit(X.iloc[tri],y[tri])
oof[tei]+=m.predict(X.iloc[tei]); cnt[tei]+=1
oof/=np.maximum(cnt,1)
cond=ALL_COND
return metrics(y,oof,cond), oof
def report(m,label=''):
flags=' '.join(f"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}" for k in TARGETS)
print(f"{label:28s} rmse={m['rmse']:.3f} mae={m['mae']:.3f} p90={m['p90']:.3f} p95={m['p95']:.3f} max={m['max']:.3f} nC={m['n_cond']} | {flags}")
EOF
python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv import cv_eval, report
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor, RandomForestRegressor
m,_=cv_eval(lambda: ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0)); report(m,'ExtraTrees500')
m,_=cv_eval(lambda: RandomForestRegressor(n_estimators=500,n_jobs=-1,random_state=0)); report(m,'RandomForest500')
m,_=cv_eval(lambda: HistGradientBoostingRegressor(random_state=0)); report(m,'HGB default')
"ExtraTrees500 rmse=2.333 mae=1.716 p90=3.458 p95=3.950 max=5.655 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX RandomForest500 rmse=2.585 mae=1.977 p90=3.585 p95=4.044 max=5.103 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=OK HGB default rmse=2.466 mae=1.851 p90=3.596 p95=4.059 max=5.935 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv import cv_eval, report, X, y
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPRegressor
from sklearn.svm import SVR
import warnings; warnings.filterwarnings('ignore')
m,_=cv_eval(lambda: make_pipeline(StandardScaler(), SVR(C=20,gamma='scale',epsilon=0.1))); report(m,'SVR rbf C20')
m,_=cv_eval(lambda: make_pipeline(StandardScaler(), MLPRegressor(hidden_layer_sizes=(128,128,64),alpha=1e-3,max_iter=2000,random_state=0)),seeds=(0,)); report(m,'MLP 128-128-64')
" 2>&1 | grep -v WarningSVR rbf C20 rmse=2.467 mae=1.770 p90=3.429 p95=3.948 max=8.048 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX MLP 128-128-64 rmse=2.739 mae=2.093 p90=4.020 p95=4.553 max=5.919 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import cv_eval, ALL, ALL_COND, y
from sklearn.ensemble import ExtraTreesRegressor
m,oof=cv_eval(lambda: ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0))
df=pd.DataFrame({'cond':ALL_COND,'err':oof-y})
df[['a','c','v']]=pd.DataFrame(df['cond'].tolist(),index=df.index)
g=df.groupby('cond').agg(rmse=('err',lambda s:np.sqrt(np.mean(s**2))),n=('err','size'))
g[['a','c','v']]=pd.DataFrame(g.index.tolist(),index=g.index)
print('WORST 12 conditions by RMSE:')
print(g.sort_values('rmse',ascending=False).head(12).to_string())
print()
print('RMSE by chord level:'); print(df.assign(cr=df.err**2).groupby('c')['cr'].apply(lambda s:np.sqrt(s.mean())).round(3).to_string())
print('RMSE by velocity level:'); print(df.assign(cr=df.err**2).groupby('v')['cr'].apply(lambda s:np.sqrt(s.mean())).round(3).to_string())
print('RMSE by angle(>0) :');
tmp=df.assign(cr=df.err**2, ab=pd.cut(df.a,[-1,0.01,5,10,15,25]))
print(tmp.groupby('ab')['cr'].apply(lambda s:np.sqrt(s.mean())).round(3).to_string())
"WORST 12 conditions by RMSE:
rmse n a c v
cond
(19.694, 0.0508, 71.3) 5.655181 14 19.694 0.0508 71.3
(12.652, 0.1524, 39.6) 4.910455 16 12.652 0.1524 39.6
(15.476, 0.0508, 55.5) 4.606025 16 15.476 0.0508 55.5
(15.476, 0.0508, 71.3) 3.972375 17 15.476 0.0508 71.3
(7.26, 0.2286, 71.3) 3.954004 16 7.260 0.2286 71.3
(22.204, 0.0254, 39.6) 3.873550 15 22.204 0.0254 39.6
(0.0, 0.0254, 71.3) 3.741319 10 0.000 0.0254 71.3
(15.476, 0.0508, 39.6) 3.543188 16 15.476 0.0508 39.6
(12.652, 0.0254, 71.3) 3.459322 17 12.652 0.0254 71.3
(11.203, 0.0508, 39.6) 3.444769 14 11.203 0.0508 39.6
(8.901, 0.1016, 71.3) 3.168164 16 8.901 0.1016 71.3
(12.652, 0.0254, 39.6) 3.124919 17 12.652 0.0254 39.6
RMSE by chord level:
c
0.0254 2.350
0.0508 3.173
0.1016 2.201
0.1524 2.359
0.2286 1.902
0.3048 1.514
RMSE by velocity level:
v
31.7 1.793
39.6 2.363
55.5 2.053
71.3 2.749
RMSE by angle(>0) :
ab
(-1.0, 0.01] 1.674
(0.01, 5.0] 1.523
(5.0, 10.0] 2.279
(10.0, 15.0] 3.074
(15.0, 25.0] 3.351
[stderr]
<string>:18: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y
import numpy as np
ALL=ALL.copy(); ALL['cond']=ALL_COND
res=[]
for cond,g in ALL.groupby('cond'):
if len(g)<6: continue
lf=np.log10(g['frequency'].values); t=g['scaled_sound_pressure_level'].values
# fit cubic in log-freq
for deg in [3,4]:
co=np.polyfit(lf,t,deg); r=t-np.polyval(co,lf)
if deg==4: res.append(np.std(r,ddof=deg+1))
print('Per-condition residual std after quartic log-freq fit:')
r=np.array(res)
print('median',round(np.median(r),3),'mean',round(np.mean(r),3),'p90',round(np.percentile(r,90),3))
print('So noise floor RMSE ~', round(np.sqrt(np.mean(r**2)),3))
"Per-condition residual std after quartic log-freq fit: median 0.779 mean 0.97 p90 1.809 So noise floor RMSE ~ 1.205
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND
d=ALL.copy()
f=d.frequency.values; u=d.free_stream_velocity.values; dt=d.suction_side_displacement_thickness.values
d['lst']=np.log10(f*dt/u)
d['spl']=d.scaled_sound_pressure_level
# correlation of SPL with lst overall
print('corr(SPL, log St):', round(np.corrcoef(d.lst,d.spl)[0,1],3))
print('corr(SPL, log f ):', round(np.corrcoef(np.log10(f),d.spl)[0,1],3))
# Within-condition: does a universal curve SPL = g(lst) + offset fit well?
# Fit global quartic in lst, check residual, then allow per-condition constant offset
co=np.polyfit(d.lst,d.spl,4); base=np.polyval(co,d.lst); r=d.spl-base
print('Global quartic in logSt: resid std', round(r.std(),3))
d['r']=r; d['cond']=ALL_COND
off=d.groupby('cond')['r'].transform('mean'); r2=r-off
print('After per-condition offset: resid std', round(r2.std(),3))
# amplitude spread
print('per-condition offset std:', round(d.groupby('cond')['r'].mean().std(),3))
"corr(SPL, log St): -0.579 corr(SPL, log f ): -0.278 Global quartic in logSt: resid std 4.869 After per-condition offset: resid std 4.044 per-condition offset std: 2.726
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv import cv_eval, report
from sklearn.ensemble import HistGradientBoostingRegressor as HGB, ExtraTreesRegressor as ET
import warnings; warnings.filterwarnings('ignore')
for lr in [0.05,0.08]:
for leaves in [15,31]:
for l2 in [0.0,1.0]:
m,_=cv_eval(lambda lr=lr,leaves=leaves,l2=l2: HGB(learning_rate=lr,max_leaf_nodes=leaves,l2_regularization=l2,max_iter=600,min_samples_leaf=20,early_stopping=False,random_state=0))
report(m,f'HGB lr{lr} lv{leaves} l2{l2}')
print('---ExtraTrees---')
for msl in [1,2,3,5]:
m,_=cv_eval(lambda msl=msl: ET(n_estimators=700,min_samples_leaf=msl,n_jobs=-1,random_state=0))
report(m,f'ET msl{msl}')
"HGB lr0.05 lv15 l20.0 rmse=2.360 mae=1.770 p90=3.438 p95=3.904 max=5.986 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX HGB lr0.05 lv15 l21.0 rmse=2.377 mae=1.765 p90=3.608 p95=4.011 max=5.957 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX HGB lr0.05 lv31 l20.0 rmse=2.414 mae=1.810 p90=3.647 p95=3.774 max=5.873 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX HGB lr0.05 lv31 l21.0 rmse=2.393 mae=1.781 p90=3.412 p95=4.001 max=5.658 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX HGB lr0.08 lv15 l20.0 rmse=2.370 mae=1.774 p90=3.484 p95=3.907 max=5.696 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX HGB lr0.08 lv15 l21.0 rmse=2.317 mae=1.745 p90=3.567 p95=3.826 max=5.741 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX HGB lr0.08 lv31 l20.0 rmse=2.399 mae=1.791 p90=3.428 p95=3.730 max=5.602 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX HGB lr0.08 lv31 l21.0 rmse=2.410 mae=1.799 p90=3.463 p95=3.929 max=5.650 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX ---ExtraTrees--- ET msl1 rmse=2.332 mae=1.713 p90=3.451 p95=3.972 max=5.657 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=XX ET msl2 rmse=2.361 mae=1.743 p90=3.521 p95=3.918 max=5.520 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=OK ET msl3 rmse=2.393 mae=1.772 p90=3.566 p95=3.815 max=5.441 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=OK ET msl5 rmse=2.487 mae=1.869 p90=3.468 p95=3.918 max=5.426 nC=82 | rmse=XX mae=XX p90=XX p95=OK max=OK
cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np
from cv import cv_eval, report, X, y, ALL_COND
from eval_lib import metrics
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import Matern, WhiteKernel, ConstantKernel as C
from sklearn.ensemble import ExtraTreesRegressor
import warnings; warnings.filterwarnings('ignore')
# in-sample fit check
et=ExtraTreesRegressor(n_estimators=300,n_jobs=-1,random_state=0).fit(X,y)
print('ET in-sample rmse', round(np.sqrt(np.mean((et.predict(X)-y)**2)),3))
def mkgp():
k=C(1.0)*Matern(length_scale=np.ones(X.shape[1]),nu=1.5)+WhiteKernel(0.5)
return make_pipeline(StandardScaler(), GaussianProcessRegressor(kernel=k,normalize_y=True,alpha=1e-6,n_restarts_optimizer=0))
m,_=cv_eval(mkgp, n_splits=5); report(m,'GP Matern1.5 ARD')
"ET in-sample rmse 0.0
cd /app && timeout 200 python3 -c "
import sys,time; sys.path.insert(0,'/tmp')
import numpy as np
from cv import X, y
from sklearn.preprocessing import StandardScaler
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import Matern, WhiteKernel, ConstantKernel as C
import warnings; warnings.filterwarnings('ignore')
Xs=StandardScaler().fit_transform(X)
t=time.time()
k=C(1.0)*Matern(length_scale=np.ones(X.shape[1]),nu=2.5)+WhiteKernel(0.5)
gp=GaussianProcessRegressor(kernel=k,normalize_y=True,alpha=1e-6,n_restarts_optimizer=1).fit(Xs,y)
print('fit time',round(time.time()-t,1),'s kernel:',gp.kernel_)
print('in-sample rmse',round(np.sqrt(np.mean((gp.predict(Xs)-y)**2)),3))
"<system>Tool ran without output or errors</system>
cd /app && cat > /tmp/oof.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import X, y, groups, ALL_COND
from eval_lib import metrics, TARGETS
from sklearn.model_selection import GroupKFold
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.ensemble import HistGradientBoostingRegressor as HGB, ExtraTreesRegressor as ET, RandomForestRegressor as RF
from sklearn.svm import SVR
from sklearn.linear_model import Ridge
from sklearn.neighbors import KNeighborsRegressor
import warnings; warnings.filterwarnings('ignore')
def get_oof(models, n_splits=8):
gkf=GroupKFold(n_splits=n_splits)
oof={k:np.zeros(len(y)) for k in models}
for tri,tei in gkf.split(X,y,groups):
for k,mk in models.items():
m=mk(); m.fit(X.iloc[tri],y[tri]); oof[k][tei]=m.predict(X.iloc[tei])
return oof
def rep(pred,label):
m=metrics(y,pred,ALL_COND)
flags=' '.join(f"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}" for k in TARGETS)
print(f"{label:26s} rmse={m['rmse']:.3f} mae={m['mae']:.3f} p90={m['p90']:.3f} p95={m['p95']:.3f} max={m['max']:.3f} | {flags}")
return m
BASE={
'et': lambda: ET(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0),
'rf': lambda: RF(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0),
'hgb':lambda: HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=500,min_samples_leaf=20,random_state=0),
'svr':lambda: make_pipeline(StandardScaler(),SVR(C=30,gamma=0.03,epsilon=0.05)),
'pr': lambda: make_pipeline(StandardScaler(),PolynomialFeatures(2),Ridge(alpha=5.0)),
'knn':lambda: make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=10,weights='distance')),
}
if __name__=='__main__':
oof=get_oof(BASE)
np.savez('/tmp/oof.npz', **oof, y=y)
for k in BASE: rep(oof[k],k)
EOF
python3 /tmp/oof.pyet rmse=2.329 mae=1.712 p90=3.464 p95=3.955 max=5.654 | rmse=XX mae=XX p90=XX p95=OK max=XX rf rmse=2.579 mae=1.971 p90=3.575 p95=3.995 max=5.069 | rmse=XX mae=XX p90=XX p95=OK max=OK hgb rmse=2.320 mae=1.747 p90=3.585 p95=3.839 max=5.716 | rmse=XX mae=XX p90=XX p95=OK max=XX svr rmse=2.521 mae=1.801 p90=3.418 p95=4.087 max=8.706 | rmse=XX mae=XX p90=XX p95=OK max=XX pr rmse=2.647 mae=1.972 p90=3.460 p95=4.403 max=7.694 | rmse=XX mae=XX p90=XX p95=OK max=XX knn rmse=2.816 mae=2.092 p90=3.986 p95=4.588 max=6.379 | rmse=XX mae=XX p90=XX p95=OK max=XX
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np
from oof import rep
d=np.load('/tmp/oof.npz'); y=d['y']
et,rf,hgb,svr,pr,knn=d['et'],d['rf'],d['hgb'],d['svr'],d['pr'],d['knn']
rep(0.5*et+0.5*hgb,'et+hgb')
rep((et+hgb+rf)/3,'et+hgb+rf')
rep(0.4*et+0.4*hgb+0.2*rf,'.4et+.4hgb+.2rf')
rep(0.35*et+0.35*hgb+0.15*rf+0.15*svr,'et hgb rf svr')
rep(0.3*et+0.3*hgb+0.2*rf+0.1*svr+0.1*pr,'5-mix')
rep(0.25*(et+hgb+rf+svr),'4equal')
"et+hgb rmse=2.200 mae=1.637 p90=3.242 p95=3.633 max=5.086 | rmse=OK mae=XX p90=XX p95=OK max=OK et+hgb+rf rmse=2.289 mae=1.723 p90=3.304 p95=3.622 max=5.053 | rmse=XX mae=XX p90=XX p95=OK max=OK .4et+.4hgb+.2rf rmse=2.249 mae=1.684 p90=3.251 p95=3.618 max=5.063 | rmse=OK mae=XX p90=XX p95=OK max=OK et hgb rf svr rmse=2.204 mae=1.641 p90=3.141 p95=3.471 max=5.168 | rmse=OK mae=XX p90=XX p95=OK max=OK 5-mix rmse=2.207 mae=1.647 p90=3.224 p95=3.415 max=5.198 | rmse=OK mae=XX p90=XX p95=OK max=OK 4equal rmse=2.227 mae=1.654 p90=3.260 p95=3.471 max=5.504 | rmse=OK mae=XX p90=XX p95=OK max=OK
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y
# check thickness vs angle relationship
d=ALL
print('corr(thickness, angle):', round(np.corrcoef(d.suction_side_displacement_thickness, d.attack_angle)[0,1],3))
print('corr(log thickness, angle):', round(np.corrcoef(np.log10(d.suction_side_displacement_thickness), d.attack_angle)[0,1],3))
# Is thickness ~unique per condition? std within condition / mean
dd=d.copy(); dd['cond']=ALL_COND
g=dd.groupby('cond')['suction_side_displacement_thickness']
cv_within=(g.std()/g.mean()).dropna()
print('within-condition thickness CV: median',round(cv_within.median(),4),'max',round(cv_within.max(),4))
# thickness range by angle bucket
dd['ab']=pd.cut(dd.attack_angle,[-1,0.01,5,10,15,25])
print(dd.groupby('ab',observed=True)['suction_side_displacement_thickness'].agg(['min','max','mean']).round(4).to_string())
"corr(thickness, angle): 0.737
corr(log thickness, angle): 0.842
within-condition thickness CV: median 0.0018 max 0.0021
min max mean
ab
(-1.0, 0.01] 0.0004 0.0033 0.0016
(0.01, 5.0] 0.0004 0.0058 0.0029
(5.0, 10.0] 0.0040 0.0253 0.0101
(10.0, 15.0] 0.0122 0.0586 0.0316
(15.0, 25.0] 0.0161 0.0530 0.0274cd /app && cat > /tmp/feat2.py << 'EOF'
import numpy as np, pandas as pd
def build(df, cols):
f=df['frequency'].astype(float).values; a=df['attack_angle'].astype(float).values
c=df['chord_length'].astype(float).values; u=df['free_stream_velocity'].astype(float).values
d=df['suction_side_displacement_thickness'].astype(float).values
lf=np.log10(f); ld=np.log10(d); lc=np.log10(c); lu=np.log10(u)
st=f*d/u; lst=np.log10(st); lrec=np.log10(u*c/1.5e-5); lred=np.log10(u*d/1.5e-5)
F={'f':f,'a':a,'c':c,'u':u,'d':d,'lf':lf,'ld':ld,'lc':lc,'lu':lu,'lst':lst,'lrec':lrec,'lred':lred,
'sin_a':np.sin(np.radians(a)),'a2':a*a,
'lf_ld':lf*ld,'lf_a':lf*a,'lf_lu':lf*lu,'a_lu':a*lu,'ld_a':ld*a,'lc_lu':lc*lu,
'lf2':lf*lf,'lst2':lst*lst,'lst3':lst**3,'a_lc':a*lc,'lf_lc':lf*lc,
'lf_lst':lf*lst,'ld_lu':ld*lu,'lf_lred':lf*lred}
return pd.DataFrame({k:F[k] for k in cols}, index=df.index)
EOF
python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from eval_lib import metrics, TARGETS
from feat2 import build
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import HistGradientBoostingRegressor as HGB, ExtraTreesRegressor as ET
import warnings; warnings.filterwarnings('ignore')
def ev(cols,label):
X=build(ALL,cols); gkf=GroupKFold(8); oe=np.zeros(len(y)); oh=np.zeros(len(y))
for tri,tei in gkf.split(X,y,groups):
e=ET(n_estimators=500,n_jobs=-1,random_state=0).fit(X.iloc[tri],y[tri]); oe[tei]=e.predict(X.iloc[tei])
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=500,min_samples_leaf=20,random_state=0).fit(X.iloc[tri],y[tri]); oh[tei]=h.predict(X.iloc[tei])
m=metrics(y,0.5*oe+0.5*oh,ALL_COND)
flags=' '.join(f\"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}\" for k in TARGETS)
print(f'{label:18s} rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f} | {flags}')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
ev(full,'full')
ev(['lf','ld','lu','a','c','lst','lrec','lred','lf_ld','lf_a','lf_lu','a_lu','lf2','lst2'],'phys+ac')
ev(['lf','ld','lu','lst','lrec','lred','lf_ld','lf_lu','lf2','lst2','lst3','ld_lu','lf_lst','lf_lred'],'phys_nochord_noangle')
ev(['lf','ld','lu','a','lst','lred','lf_ld','lf_a','lf_lu','a_lu','lf2','lst2','ld_a'],'phys_noChord')
ev(['f','a','c','u','d'],'raw5')
"full rmse=2.201 mae=1.637 p90=3.199 p95=3.499 max=5.142 | rmse=OK mae=XX p90=XX p95=OK max=OK phys+ac rmse=2.288 mae=1.702 p90=3.437 p95=3.719 max=4.754 | rmse=XX mae=XX p90=XX p95=OK max=OK phys_nochord_noangle rmse=2.869 mae=2.173 p90=4.192 p95=4.486 max=7.961 | rmse=XX mae=XX p90=XX p95=OK max=XX phys_noChord rmse=2.448 mae=1.897 p90=3.531 p95=4.105 max=4.916 | rmse=XX mae=XX p90=XX p95=OK max=OK raw5 rmse=2.617 mae=2.009 p90=3.775 p95=4.270 max=4.850 | rmse=XX mae=XX p90=XX p95=OK max=OK
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from eval_lib import angle_refs, make_conditions
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
arefs=angle_refs(tr,va)
trc=set(make_conditions(tr,arefs)); vac=set(make_conditions(va,arefs))
def summ(cset,label):
arr=np.array(sorted(cset))
print(label,'n=',len(cset))
print(' angles:',sorted(set(round(x[0],1) for x in cset)))
print(' chords:',sorted(set(round(x[1],4) for x in cset)))
print(' vels: ',sorted(set(round(x[2],1) for x in cset)))
summ(vac,'VALIDATION conditions')
print()
# angle distribution
va_ang=sorted([c[0] for c in vac])
print('val angle values:',[round(x,1) for x in va_ang])
print('val high-angle(>12) conditions:',sum(1 for a in va_ang if a>12),'of',len(va_ang))
"VALIDATION conditions n= 15 angles: [0.0, 3.3, 4.0, 4.8, 6.7, 8.4, 9.9, 12.3, 17.4] chords: [0.0254, 0.0508, 0.1016, 0.1524, 0.2286, 0.3048] vels: [31.7, 39.6, 55.5, 71.3] val angle values: [0.0, 0.0, 0.0, 0.0, 0.0, 3.3, 4.0, 4.8, 4.8, 6.7, 8.4, 9.9, 12.3, 12.3, 17.4] val high-angle(>12) conditions: 3 of 15
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from eval_lib import angle_refs, make_conditions, metrics, TARGETS
from feat2 import build
from sklearn.ensemble import HistGradientBoostingRegressor as HGB, ExtraTreesRegressor as ET
import warnings; warnings.filterwarnings('ignore')
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
arefs=angle_refs(tr,va); cond=make_conditions(va,arefs)
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
Xtr=build(tr,full); Xva=build(va,full); ytr=tr.scaled_sound_pressure_level.values; yva=va.scaled_sound_pressure_level.values
e=ET(n_estimators=600,n_jobs=-1,random_state=0).fit(Xtr,ytr)
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=500,min_samples_leaf=20,random_state=0).fit(Xtr,ytr)
p=0.5*e.predict(Xva)+0.5*h.predict(Xva)
m=metrics(yva,p,cond)
flags=' '.join(f\"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}\" for k in TARGETS)
print(f'val: rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f} | {flags}')
# per-condition rmse
dfc=pd.DataFrame({'c':cond,'e':p-yva}); cr=dfc.groupby('c')['e'].apply(lambda s:np.sqrt(np.mean(s**2))).sort_values(ascending=False)
print(cr.round(2).to_string())
"val: rmse=2.210 mae=1.755 p90=3.158 p95=3.358 max=3.440 | rmse=OK mae=XX p90=XX p95=OK max=OK c (17.4, 0.0254, 39.6) 3.44 (0.0, 0.0254, 71.3) 3.32 (8.399, 0.0508, 55.5) 2.91 (12.301, 0.1016, 55.5) 2.55 (12.301, 0.1016, 39.6) 2.20 (9.901, 0.1524, 31.7) 2.07 (6.7, 0.1016, 55.5) 2.06 (4.043, 0.2286, 31.7) 2.04 (4.804, 0.0254, 71.3) 1.93 (0.0, 0.3048, 31.7) 1.82 (3.3, 0.1016, 71.3) 1.65 (4.804, 0.0254, 39.6) 1.62 (0.0, 0.0254, 39.6) 1.57 (0.0, 0.1524, 39.6) 1.47 (0.0, 0.0508, 55.5) 1.17
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from eval_lib import angle_refs, make_conditions
from feat2 import build
from sklearn.ensemble import HistGradientBoostingRegressor as HGB, ExtraTreesRegressor as ET
import warnings; warnings.filterwarnings('ignore')
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
arefs=angle_refs(tr,va); cond=make_conditions(va,arefs)
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
Xtr=build(tr,full); Xva=build(va,full); ytr=tr.scaled_sound_pressure_level.values; yva=va.scaled_sound_pressure_level.values
e=ET(n_estimators=600,n_jobs=-1,random_state=0).fit(Xtr,ytr)
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=500,min_samples_leaf=20,random_state=0).fit(Xtr,ytr)
p=0.5*e.predict(Xva)+0.5*h.predict(Xva)
va2=va.copy(); va2['cond']=cond; va2['pred']=p; va2['err']=p-yva
for cc in [(17.4,0.0254,39.6),(0.0,0.0254,71.3),(8.399,0.0508,55.5)]:
sub=va2[va2.cond==cc].sort_values('frequency')
print('COND',cc,'mean_err=%.2f'%sub.err.mean(),'std_err=%.2f'%sub.err.std())
for _,r in sub.iterrows():
print(' f=%6.0f true=%.1f pred=%.1f err=%+.2f'%(r.frequency,r.scaled_sound_pressure_level,r.pred,r.err))
"COND (17.4, 0.0254, 39.6) mean_err=0.78 std_err=3.47 f= 200 true=114.5 pred=114.3 err=-0.19 f= 250 true=115.4 pred=115.2 err=-0.20 f= 315 true=115.9 pred=117.8 err=+1.82 f= 399 true=116.3 pred=121.1 err=+4.84 f= 499 true=118.2 pred=125.6 err=+7.48 f= 630 true=125.0 pred=132.8 err=+7.78 f= 801 true=135.7 pred=136.4 err=+0.71 f= 1002 true=138.7 pred=133.3 err=-5.34 f= 1252 true=131.9 pred=130.9 err=-0.98 f= 1601 true=128.2 pred=127.9 err=-0.34 f= 1999 true=127.1 pred=125.9 err=-1.19 f= 2496 true=124.3 pred=124.2 err=-0.14 f= 3145 true=123.5 pred=122.4 err=-1.03 f= 3996 true=122.5 pred=121.2 err=-1.29 f= 5002 true=119.2 pred=119.0 err=-0.24 COND (0.0, 0.0254, 71.3) mean_err=-1.61 std_err=3.06 f= 2501 true=132.9 pred=129.9 err=-2.96 f= 3154 true=136.5 pred=131.8 err=-4.70 f= 4007 true=138.2 pred=132.7 err=-5.51 f= 5006 true=136.7 pred=133.8 err=-2.94 f= 6300 true=135.1 pred=133.2 err=-1.83 f= 7991 true=130.1 pred=132.3 err=+2.25 f= 9983 true=131.1 pred=131.7 err=+0.60 f= 12483 true=133.6 pred=130.5 err=-3.14 f= 15995 true=130.9 pred=128.9 err=-2.07 f= 20017 true=123.6 pred=127.8 err=+4.20 COND (8.399, 0.0508, 55.5) mean_err=1.18 std_err=2.78 f= 400 true=121.5 pred=124.0 err=+2.52 f= 501 true=123.9 pred=126.9 err=+3.03 f= 631 true=126.5 pred=129.5 err=+2.93 f= 801 true=129.2 pred=131.4 err=+2.22 f= 1000 true=131.7 pred=132.7 err=+0.96 f= 1249 true=133.6 pred=133.6 err=-0.04 f= 1597 true=134.7 pred=133.5 err=-1.29 f= 1997 true=134.9 pred=132.6 err=-2.36 f= 2498 true=133.5 pred=131.0 err=-2.45 f= 3152 true=130.3 pred=130.2 err=-0.10 f= 4006 true=124.3 pred=125.5 err=+1.26 f= 5008 true=117.3 pred=124.8 err=+7.54
cd /app && cat > /tmp/oofcache.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
import numpy as np
from cv import X, y, groups, ALL_COND
from eval_lib import metrics, TARGETS
from sklearn.model_selection import GroupKFold
def oof_of(make, Xmat, n_splits=8, seeds=(0,)):
gkf=GroupKFold(n_splits=n_splits); oof=np.zeros(len(y)); cnt=np.zeros(len(y))
for s in seeds:
for tri,tei in gkf.split(Xmat,y,groups):
m=make(s); m.fit(Xmat.iloc[tri] if hasattr(Xmat,'iloc') else Xmat[tri], y[tri])
xe=Xmat.iloc[tei] if hasattr(Xmat,'iloc') else Xmat[tei]
oof[tei]+=m.predict(xe); cnt[tei]+=1
return oof/np.maximum(cnt,1)
def rep(pred,label):
m=metrics(y,pred,ALL_COND)
flags=' '.join(f"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}" for k in TARGETS)
print(f"{label:24s} rmse={m['rmse']:.3f} mae={m['mae']:.3f} p90={m['p90']:.3f} p95={m['p95']:.3f} max={m['max']:.3f} | {flags}")
return m
EOF
python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL
from feat2 import build
from oofcache import oof_of, rep
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.neural_network import MLPRegressor
import warnings; warnings.filterwarnings('ignore')
base=['lf','ld','lu','a','lc']
Xb=build(ALL,base)
for deg,al in [(3,2.0),(3,5.0),(4,5.0),(4,10.0)]:
o=oof_of(lambda s,deg=deg,al=al: make_pipeline(StandardScaler(),PolynomialFeatures(deg),Ridge(alpha=al)), Xb)
rep(o,f'polyRidge d{deg} a{al}')
# MLP on full
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
Xf=build(ALL,full)
o=oof_of(lambda s: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(200,150,100),alpha=3e-3,max_iter=3000,random_state=s,early_stopping=False)), Xf, seeds=(0,1,2))
rep(o,'MLP200-150-100 x3')
"polyRidge d3 a2.0 rmse=3.008 mae=2.198 p90=3.972 p95=4.342 max=10.195 | rmse=XX mae=XX p90=XX p95=OK max=XX polyRidge d3 a5.0 rmse=3.054 mae=2.212 p90=3.997 p95=4.334 max=11.272 | rmse=XX mae=XX p90=XX p95=OK max=XX polyRidge d4 a5.0 rmse=2.629 mae=2.017 p90=3.836 p95=4.106 max=4.856 | rmse=XX mae=XX p90=XX p95=OK max=OK polyRidge d4 a10.0 rmse=2.633 mae=2.004 p90=3.835 p95=4.045 max=5.347 | rmse=XX mae=XX p90=XX p95=OK max=OK MLP200-150-100 x3 rmse=2.476 mae=1.860 p90=3.628 p95=3.986 max=5.014 | rmse=XX mae=XX p90=XX p95=OK max=OK
cd /app && cat > /tmp/genoof.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
import numpy as np
from cv import ALL, y, groups, ALL_COND
from feat2 import build
from oofcache import oof_of, rep
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.ensemble import (HistGradientBoostingRegressor as HGB, ExtraTreesRegressor as ET,
RandomForestRegressor as RF, GradientBoostingRegressor as GBR)
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.neighbors import KNeighborsRegressor
import warnings; warnings.filterwarnings('ignore')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
Xf=build(ALL,full)
base=['lf','ld','lu','a','lc']; Xb=build(ALL,base)
OOF={}
OOF['et']=oof_of(lambda s: ET(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=0),Xf)
OOF['et3']=oof_of(lambda s: ET(n_estimators=700,min_samples_leaf=3,n_jobs=-1,random_state=0),Xf)
OOF['rf']=oof_of(lambda s: RF(n_estimators=700,min_samples_leaf=1,n_jobs=-1,random_state=0),Xf)
OOF['hgb']=oof_of(lambda s: HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=500,min_samples_leaf=20,random_state=0),Xf)
OOF['hgb2']=oof_of(lambda s: HGB(learning_rate=0.05,max_leaf_nodes=31,l2_regularization=0.0,max_iter=600,min_samples_leaf=15,random_state=1),Xf)
OOF['gbr']=oof_of(lambda s: GBR(n_estimators=500,learning_rate=0.05,max_depth=3,subsample=0.8,random_state=0),Xf)
OOF['svr']=oof_of(lambda s: make_pipeline(StandardScaler(),SVR(C=30,gamma=0.03,epsilon=0.05)),Xf)
OOF['mlp']=oof_of(lambda s: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(200,150,100),alpha=3e-3,max_iter=3000,random_state=s)),Xf,seeds=(0,1,2))
OOF['pr']=oof_of(lambda s: make_pipeline(StandardScaler(),PolynomialFeatures(4),Ridge(alpha=8.0)),Xb)
OOF['knn']=oof_of(lambda s: make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=10,weights='distance')),Xf)
np.savez('/tmp/oof_all.npz', y=y, **OOF)
for k,v in OOF.items(): rep(v,k)
print('saved', list(OOF.keys()))
EOF
python3 /tmp/genoof.pyet rmse=2.318 mae=1.707 p90=3.423 p95=3.809 max=5.715 | rmse=XX mae=XX p90=XX p95=OK max=XX et3 rmse=2.389 mae=1.765 p90=3.437 p95=3.851 max=5.625 | rmse=XX mae=XX p90=XX p95=OK max=XX rf rmse=2.582 mae=1.970 p90=3.555 p95=4.060 max=5.040 | rmse=XX mae=XX p90=XX p95=OK max=OK hgb rmse=2.320 mae=1.747 p90=3.585 p95=3.839 max=5.716 | rmse=XX mae=XX p90=XX p95=OK max=XX hgb2 rmse=2.443 mae=1.839 p90=3.731 p95=3.954 max=5.858 | rmse=XX mae=XX p90=XX p95=OK max=XX gbr rmse=2.568 mae=1.925 p90=3.640 p95=3.912 max=7.805 | rmse=XX mae=XX p90=XX p95=OK max=XX svr rmse=2.509 mae=1.798 p90=3.420 p95=4.051 max=8.364 | rmse=XX mae=XX p90=XX p95=OK max=XX mlp rmse=2.476 mae=1.860 p90=3.628 p95=3.986 max=5.014 | rmse=XX mae=XX p90=XX p95=OK max=OK pr rmse=2.615 mae=1.998 p90=3.774 p95=4.071 max=4.870 | rmse=XX mae=XX p90=XX p95=OK max=OK knn rmse=2.913 mae=2.178 p90=4.130 p95=4.873 max=6.454 | rmse=XX mae=XX p90=XX p95=XX max=XX saved ['et', 'et3', 'rf', 'hgb', 'hgb2', 'gbr', 'svr', 'mlp', 'pr', 'knn']
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np
from cv import ALL_COND
from eval_lib import metrics, TARGETS
d=np.load('/tmp/oof_all.npz'); y=d['y']
keys=[k for k in d.files if k!='y']
P=np.vstack([d[k] for k in keys]) # (m, n)
def score(w):
w=np.clip(w,0,None);
if w.sum()==0: return 1e9,None
w=w/w.sum(); pred=w@P; m=metrics(y,pred,ALL_COND)
pen=max(m['rmse']/2.25,m['mae']/1.55,m['p90']/2.35,m['p95']/4.75,m['max']/5.60)
return pen,m
rng=np.random.default_rng(0)
best=(1e9,None,None)
# random search
for _ in range(40000):
w=rng.random(len(keys))**3 # sparse-ish
pen,m=score(w)
if pen<best[0]: best=(pen,w/w.sum(),m)
pen,w,m=best
print('best penalty',round(pen,4))
for k,wi in sorted(zip(keys,w),key=lambda z:-z[1]):
if wi>0.01: print(f' {k}: {wi:.3f}')
flags=' '.join(f\"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}\" for k in TARGETS)
print(f'rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f} | {flags}')
"[stderr] Shell command timed out. Tool interaction exceeded 300 seconds.
restart: true
<system>tool has been restarted.</system>
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np
from cv import ALL_COND
d=np.load('/tmp/oof_all.npz'); y=d['y']
keys=[k for k in d.files if k!='y']
P=np.vstack([d[k] for k in keys])
codes,uniq=None,None
import pandas as pd
codes=pd.factorize(pd.Series(ALL_COND).astype(str))[0]
nC=codes.max()+1
# precompute membership for vectorized per-condition rmse
counts=np.bincount(codes,minlength=nC)
def fast_metrics(pred):
err=pred-y; ae=np.abs(err); se=err*err
rmse=np.sqrt(se.mean()); mae=ae.mean()
sse=np.bincount(codes,weights=se,minlength=nC)
crmse=np.sqrt(sse/counts)
return rmse,mae,np.percentile(crmse,90),np.percentile(crmse,95),crmse.max()
def pen_of(pred):
rmse,mae,p90,p95,mx=fast_metrics(pred)
return max(rmse/2.25,mae/1.55,p90/2.35,p95/4.75,mx/5.60),(rmse,mae,p90,p95,mx)
rng=np.random.default_rng(0)
best=(1e9,None,None)
for _ in range(20000):
w=rng.random(len(keys))**3
s=w.sum()
if s==0: continue
w/=s; pred=w@P; pen,mm=pen_of(pred)
if pen<best[0]: best=(pen,w.copy(),mm)
pen,w,mm=best
print('penalty',round(pen,4))
for k,wi in sorted(zip(keys,w),key=lambda z:-z[1]):
if wi>0.02: print(f' {k}: {wi:.3f}')
print('rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%mm)
"penalty 1.2823 et: 0.411 pr: 0.271 mlp: 0.131 hgb2: 0.126 hgb: 0.027 knn: 0.024 rmse=2.142 mae=1.599 p90=3.013 p95=3.456 max=4.400
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, y, groups, ALL_COND
from feat2 import build
from eval_lib import metrics
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import HistGradientBoostingRegressor as HGB, ExtraTreesRegressor as ET
import warnings; warnings.filterwarnings('ignore')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
X=build(ALL,full)
for ns in [4,6,8,12,20]:
gkf=GroupKFold(ns); oe=np.zeros(len(y))
for tri,tei in gkf.split(X,y,groups):
e=ET(n_estimators=400,n_jobs=-1,random_state=0).fit(X.iloc[tri],y[tri])
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=400,min_samples_leaf=20,random_state=0).fit(X.iloc[tri],y[tri])
oe[tei]=0.5*e.predict(X.iloc[tei])+0.5*h.predict(X.iloc[tei])
m=metrics(y,oe,ALL_COND)
print(f'n_splits={ns:2d} (train~{100*(ns-1)/ns:.0f}%): rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f}')
"n_splits= 4 (train~75%): rmse=2.257 mae=1.691 p90=3.141 p95=3.597 max=5.015 n_splits= 6 (train~83%): rmse=2.252 mae=1.655 p90=3.229 p95=3.697 max=5.854 n_splits= 8 (train~88%): rmse=2.204 mae=1.639 p90=3.203 p95=3.500 max=5.133 n_splits=12 (train~92%): rmse=2.227 mae=1.627 p90=3.283 p95=3.810 max=5.967 n_splits=20 (train~95%): rmse=2.208 mae=1.617 p90=3.267 p95=3.745 max=5.661
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND
d=ALL.copy(); d['cond']=ALL_COND
rows=[]
for cc,g in d.groupby('cond'):
if len(g)<7: continue
lf=np.log10(g.frequency.values); t=g.scaled_sound_pressure_level.values
co=np.polyfit(lf,t,4); xs=np.linspace(lf.min(),lf.max(),200); ys=np.polyval(co,xs)
pk=xs[np.argmax(ys)]
a,ch,v=cc; dt=g.suction_side_displacement_thickness.mean()
rows.append((a,ch,v,dt,pk,t.max()))
R=pd.DataFrame(rows,columns=['a','c','v','dt','peak_lf','peak_spl'])
print('peak_lf range',round(R.peak_lf.min(),2),round(R.peak_lf.max(),2))
# regress peak_lf on log features
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import cross_val_predict, KFold
Xr=np.column_stack([R.a,np.log10(R.c),np.log10(R.v),np.log10(R.dt),R.a*np.log10(R.v)])
for tgt,name in [(R.peak_lf,'peak_lf'),(R.peak_spl,'peak_spl')]:
pr=cross_val_predict(LinearRegression(),Xr,tgt,cv=KFold(5,shuffle=True,random_state=0))
ss=1-np.sum((tgt-pr)**2)/np.sum((tgt-tgt.mean())**2)
print(f'{name}: CV R2={ss:.3f} rmse={np.sqrt(np.mean((tgt-pr)**2)):.3f}')
"peak_lf range 2.3 3.83 peak_lf: CV R2=0.861 rmse=0.140 peak_spl: CV R2=0.137 rmse=3.451
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from feat2 import build
from eval_lib import metrics, TARGETS
from sklearn.model_selection import GroupKFold, cross_val_predict, KFold
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB, RandomForestRegressor as RF
import warnings; warnings.filterwarnings('ignore')
# amplitude nonlinear
d=ALL.copy(); d['cond']=ALL_COND; rows=[]
for cc,g in d.groupby('cond'):
if len(g)<7: continue
lf=np.log10(g.frequency.values); t=g.scaled_sound_pressure_level.values
co=np.polyfit(lf,t,4); xs=np.linspace(lf.min(),lf.max(),200); ys=np.polyval(co,xs)
a,ch,v=cc; dt=g.suction_side_displacement_thickness.mean()
rows.append((a,ch,v,dt,xs[np.argmax(ys)],ys.max()))
R=pd.DataFrame(rows,columns=['a','c','v','dt','peak_lf','peak_spl'])
Xr=np.column_stack([R.a,np.log10(R.c),np.log10(R.v),np.log10(R.dt),R.a*np.log10(R.v),R.a**2,np.log10(R.dt)*R.a])
pr=cross_val_predict(RF(n_estimators=400,random_state=0),Xr,R.peak_spl,cv=KFold(5,shuffle=True,random_state=0))
print('peak_spl RF CV rmse',round(np.sqrt(np.mean((R.peak_spl-pr)**2)),3))
# Add peak-aligned coordinate via cross-fitting on conditions, test ensemble
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
X=build(ALL,full)
# cross-fit peak_lf predictor over condition-groups, then attach per-row normalized freq
from sklearn.linear_model import Ridge
gkf=GroupKFold(8)
# build condition-level table aligned to row conditions
cond_arr=np.array(ALL_COND,dtype=object)
peak_lf_pred=np.zeros(len(y))
# map each row to its condition features
rowfeat=np.column_stack([ALL.attack_angle,np.log10(ALL.chord_length),np.log10(ALL.free_stream_velocity),np.log10(ALL.suction_side_displacement_thickness),ALL.attack_angle*np.log10(ALL.free_stream_velocity)])
# cross-fit using condition-level fit to avoid leakage
ug=np.unique(groups)
from sklearn.model_selection import KFold as KF
kf=KF(8,shuffle=True,random_state=0)
condmap={}
for cc,g in pd.DataFrame({'g':groups,'pk':0}).groupby('g'): pass
# simpler: fit peak model on condition table R with group=condition; predict per row by condition
# Build mapping cond-> index in R
Rkey={(round(r.a,3),round(r.c,4),round(r.v,2)):i for i,r in R.iterrows()}
# leakage-safe: use KFold over conditions
peaklf_by_cond=np.full(len(R),np.nan)
for tri,tei in kf.split(R):
lr=Ridge(alpha=1.0).fit(Xr[tri],R.peak_lf.values[tri]); peaklf_by_cond[tei]=lr.predict(Xr[tei])
cond_to_peak=dict(zip([(round(r.a,3),round(r.c,4),round(r.v,2)) for _,r in R.iterrows()],peaklf_by_cond))
nf=[]
for i in range(len(ALL)):
key=(round(cond_arr[i][0],3),round(cond_arr[i][1],4),round(cond_arr[i][2],2))
nf.append(np.log10(ALL.frequency.values[i])-cond_to_peak.get(key,np.nan))
nf=np.array(nf)
X2=X.copy(); X2['dpeak']=nf; X2['dpeak2']=nf**2; X2['dpeak3']=nf**3
oe=np.zeros(len(y))
for tri,tei in gkf.split(X2,y,groups):
e=ET(n_estimators=500,n_jobs=-1,random_state=0).fit(X2.iloc[tri],y[tri])
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=400,min_samples_leaf=20,random_state=0).fit(X2.iloc[tri],y[tri])
oe[tei]=0.5*e.predict(X2.iloc[tei])+0.5*h.predict(X2.iloc[tei])
m=metrics(y,oe,ALL_COND)
flags=' '.join(f\"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}\" for k in TARGETS)
print(f'et+hgb +dpeak: rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f} | {flags}')
"peak_spl RF CV rmse 2.442 et+hgb +dpeak: rmse=2.342 mae=1.625 p90=3.258 p95=3.799 max=8.112 | rmse=XX mae=XX p90=XX p95=OK max=XX
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL_COND
d=np.load('/tmp/oof_all.npz'); y=d['y']
# best blend approx: et,pr,mlp,hgb2
pred=0.41*d['et']+0.27*d['pr']+0.13*d['mlp']+0.126*d['hgb2']+0.027*d['hgb']+0.024*d['knn']
df=pd.DataFrame({'c':ALL_COND,'e':pred-y})
g=df.groupby('c')['e']
cond_rmse=g.apply(lambda s:np.sqrt(np.mean(s**2)))
cond_bias=g.mean()
cond_shape=g.apply(lambda s:np.sqrt(np.mean((s-s.mean())**2)))
print('per-condition RMSE: p50=%.2f p90=%.2f max=%.2f'%(cond_rmse.median(),np.percentile(cond_rmse,90),cond_rmse.max()))
print('amplitude |bias|: p50=%.2f p90=%.2f max=%.2f'%(cond_bias.abs().median(),np.percentile(cond_bias.abs(),90),cond_bias.abs().max()))
print('shape (within-cond std): p50=%.2f p90=%.2f max=%.2f'%(cond_shape.median(),np.percentile(cond_shape,90),cond_shape.max()))
# fraction of variance from amplitude
print()
print('mean cond_rmse^2=%.2f = bias^2 %.2f + shape^2 %.2f'%((cond_rmse**2).mean(),(cond_bias**2).mean(),(cond_shape**2).mean()))
"per-condition RMSE: p50=2.47 p90=3.83 max=5.07 amplitude |bias|: p50=1.74 p90=3.09 max=3.95 shape (within-cond std): p50=1.46 p90=2.88 max=3.48 mean cond_rmse^2=7.54 = bias^2 4.19 + shape^2 3.35
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import RandomForestRegressor as RF, ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
import warnings; warnings.filterwarnings('ignore')
d=ALL.copy(); d['cond']=ALL_COND
# condition-level table: mean SPL and features
rows=[]
for cc,g in d.groupby('cond'):
a,ch,v=cc; dt=g.suction_side_displacement_thickness.mean()
rows.append((a,ch,v,dt,g.scaled_sound_pressure_level.mean(),len(g)))
R=pd.DataFrame(rows,columns=['a','c','v','dt','mspl','n'])
gg=pd.factorize(R.index)[0] # each cond unique; use KFold instead
from sklearn.model_selection import KFold
a=R.a.values; lc=np.log10(R.c.values); lv=np.log10(R.v.values); ldt=np.log10(R.dt.values)
feats={
'linear phys':np.column_stack([a,lc,lv,ldt]),
'+inter':np.column_stack([a,lc,lv,ldt,a*lv,a*ldt,lc*lv,ldt*lv,a*a]),
}
kf=KFold(8,shuffle=True,random_state=0)
for name,Xr in feats.items():
for mk,mn in [(lambda:Ridge(alpha=1.0),'ridge'),(lambda:make_pipeline(StandardScaler(),PolynomialFeatures(2),Ridge(alpha=2.0)),'poly2'),(lambda:RF(n_estimators=400,random_state=0),'rf'),(lambda:ET(n_estimators=400,random_state=0),'et')]:
pr=np.zeros(len(R))
for tri,tei in kf.split(Xr):
m=mk().fit(Xr[tri],R.mspl.values[tri]); pr[tei]=m.predict(Xr[tei])
rmse=np.sqrt(np.mean((R.mspl.values-pr)**2))
print(f'{name:12s} {mn:6s} amplitude CV rmse={rmse:.3f}')
"linear phys ridge amplitude CV rmse=2.719 linear phys poly2 amplitude CV rmse=2.162 linear phys rf amplitude CV rmse=1.838 linear phys et amplitude CV rmse=1.531 +inter ridge amplitude CV rmse=2.657 +inter poly2 amplitude CV rmse=1.853 +inter rf amplitude CV rmse=1.855 +inter et amplitude CV rmse=1.529
cd /app && cat > /tmp/amp.py << 'EOF'
import numpy as np, pandas as pd
from sklearn.ensemble import ExtraTreesRegressor as ET
# Condition key helpers (snap to grid) reused from eval_lib
from eval_lib import CHORD, VEL
def cond_features(df, arefs):
a=df.attack_angle.values; c=df.chord_length.values; v=df.free_stream_velocity.values
dt=df.suction_side_displacement_thickness.values
asnap=np.array([arefs[np.argmin(np.abs(arefs-z))] for z in a])
csnap=np.array([CHORD[np.argmin(np.abs(CHORD-z))] for z in c])
vsnap=np.array([VEL[np.argmin(np.abs(VEL-z))] for z in v])
keys=list(zip(np.round(asnap,3),np.round(csnap,4),np.round(vsnap,2)))
return keys, asnap, csnap, vsnap
def amp_feature_train(train_df, arefs):
'''Fit amplitude model on training rows -> returns predictor fn for any df.'''
keys,asnap,csnap,vsnap=cond_features(train_df, arefs)
tmp=pd.DataFrame({'k':keys,'a':asnap,'c':csnap,'v':vsnap,
'dt':train_df.suction_side_displacement_thickness.values,
'y':train_df.scaled_sound_pressure_level.values})
g=tmp.groupby('k').agg(a=('a','first'),c=('c','first'),v=('v','first'),dt=('dt','mean'),mspl=('y','mean')).reset_index()
def X_of(a,c,v,dt):
lc=np.log10(c); lv=np.log10(v); ldt=np.log10(dt)
return np.column_stack([a,lc,lv,ldt,a*lv,a*ldt,lc*lv,ldt*lv,a*a,lc*ldt])
Xg=X_of(g.a.values,g.c.values,g.v.values,g.dt.values)
model=ET(n_estimators=500,min_samples_leaf=1,random_state=0).fit(Xg,g.mspl.values)
def predict(df):
k,a,c,v=cond_features(df,arefs)
dt=df.suction_side_displacement_thickness.values
return model.predict(X_of(a,c,v,dt))
return predict
EOF
timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from feat2 import build
from eval_lib import metrics, TARGETS, angle_refs
from amp import amp_feature_train
from sklearn.model_selection import GroupKFold
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB
import warnings; warnings.filterwarnings('ignore')
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
arefs=angle_refs(tr,va)
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
Xbase=build(ALL,full)
gkf=GroupKFold(8); oe=np.zeros(len(y))
for tri,tei in gkf.split(Xbase,y,groups):
trdf=ALL.iloc[tri]; tedf=ALL.iloc[tei]
ampf=amp_feature_train(trdf,arefs)
Xtr=Xbase.iloc[tri].copy(); Xtr['amp']=ampf(trdf)
Xte=Xbase.iloc[tei].copy(); Xte['amp']=ampf(tedf)
e=ET(n_estimators=500,n_jobs=-1,random_state=0).fit(Xtr,y[tri])
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=400,min_samples_leaf=20,random_state=0).fit(Xtr,y[tri])
oe[tei]=0.5*e.predict(Xte)+0.5*h.predict(Xte)
m=metrics(y,oe,ALL_COND)
flags=' '.join(f\"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}\" for k in TARGETS)
print(f'et+hgb +amp: rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f} | {flags}')
"et+hgb +amp: rmse=2.308 mae=1.704 p90=3.325 p95=3.603 max=6.264 | rmse=XX mae=XX p90=XX p95=OK max=XX
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND
d=ALL.copy(); d['cond']=ALL_COND
bad=0
for cc,g in d.groupby('cond'):
dt=np.sort(g.suction_side_displacement_thickness.values)
# relative spread
spread=(dt.max()-dt.min())/dt.mean()
# also check for a big internal gap (2 clusters)
gaps=np.diff(dt)/dt[:-1]
if spread>0.05 or (len(gaps)>0 and gaps.max()>0.03):
bad+=1
if bad<=8: print('cond',cc,'n=',len(g),'dt spread=%.3f'%spread,'maxgap=%.3f'%(gaps.max() if len(gaps) else 0))
print('total conditions',d.cond.nunique(),'possibly-merged',bad)
# size distribution
sz=d.groupby('cond').size()
print('size dist:',dict(sz.value_counts().sort_index()))
"total conditions 82 possibly-merged 0
size dist: {8: 3, 9: 1, 10: 2, 11: 7, 12: 3, 13: 12, 14: 16, 15: 8, 16: 21, 17: 7, 18: 2}cd /app && timeout 260 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from feat2 import build
from eval_lib import metrics, TARGETS, angle_refs, CHORD, VEL
from sklearn.model_selection import GroupKFold, KFold
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB, RandomForestRegressor as RF
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
import warnings; warnings.filterwarnings('ignore')
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv'); arefs=angle_refs(tr,va)
def condkey(df):
a=np.array([arefs[np.argmin(np.abs(arefs-z))] for z in df.attack_angle]);
c=np.array([CHORD[np.argmin(np.abs(CHORD-z))] for z in df.chord_length]);
v=np.array([VEL[np.argmin(np.abs(VEL-z))] for z in df.free_stream_velocity])
return list(zip(np.round(a,3),np.round(c,4),np.round(v,2))),a,c,v
def ampX(a,c,v,dt):
lc,lv,ldt=np.log10(c),np.log10(v),np.log10(dt)
return np.column_stack([a,lc,lv,ldt,a*lv,a*ldt,lc*lv,ldt*lv,a*a,lc*ldt,ldt*ldt,a*lc])
def fit_amp(df):
k,a,c,v=condkey(df); dt=df.suction_side_displacement_thickness.values
t=pd.DataFrame({'k':k,'a':a,'c':c,'v':v,'dt':dt,'y':df.scaled_sound_pressure_level.values})
g=t.groupby('k').agg(a=('a','first'),c=('c','first'),v=('v','first'),dt=('dt','mean'),m=('y','mean'))
Xg=ampX(g.a.values,g.c.values,g.v.values,g.dt.values); yg=g.m.values
ms=[ET(n_estimators=600,random_state=0).fit(Xg,yg),RF(n_estimators=600,random_state=0).fit(Xg,yg),
make_pipeline(StandardScaler(),PolynomialFeatures(2),Ridge(alpha=2.0)).fit(Xg,yg)]
def pred(df2):
k2,a2,c2,v2=condkey(df2); dt2=df2.suction_side_displacement_thickness.values
X2=ampX(a2,c2,v2,dt2); return np.mean([m.predict(X2) for m in ms],axis=0)
return pred
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
Xbase=build(ALL,full)
gkf=GroupKFold(8); two=np.zeros(len(y)); one=np.zeros(len(y)); ampe=np.zeros(len(y))
for tri,tei in gkf.split(Xbase,y,groups):
trdf=ALL.iloc[tri]; tedf=ALL.iloc[tei]
ampf=fit_amp(trdf)
amp_tr=ampf(trdf); amp_te=ampf(tedf); ampe[tei]=amp_te
# two-stage: shape target = y - amp
Xtr=Xbase.iloc[tri]; Xte=Xbase.iloc[tei]
res=y[tri]-amp_tr
e=ET(n_estimators=500,n_jobs=-1,random_state=0).fit(Xtr,res)
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=400,min_samples_leaf=20,random_state=0).fit(Xtr,res)
two[tei]=amp_te+0.5*e.predict(Xte)+0.5*h.predict(Xte)
# one-stage baseline
e2=ET(n_estimators=500,n_jobs=-1,random_state=0).fit(Xtr,y[tri])
h2=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=400,min_samples_leaf=20,random_state=0).fit(Xtr,y[tri])
one[tei]=0.5*e2.predict(Xte)+0.5*h2.predict(Xte)
# amplitude-only per-condition rmse
dfc=pd.DataFrame({'c':ALL_COND,'e':ampe-y});
print('amplitude model alone: cond rmse p50=%.2f p90=%.2f'%(dfc.groupby('c').e.apply(lambda s:np.sqrt((s**2).mean())).median(),np.percentile(dfc.groupby('c').e.apply(lambda s:np.sqrt((s**2).mean())),90)))
for nm,pr in [('one-stage',one),('two-stage',two)]:
m=metrics(y,pr,ALL_COND)
flags=' '.join(f\"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}\" for k in TARGETS)
print(f'{nm:10s} rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f} | {flags}')
"amplitude model alone: cond rmse p50=5.32 p90=7.35 one-stage rmse=2.204 mae=1.640 p90=3.215 p95=3.502 max=5.129 | rmse=OK mae=XX p90=XX p95=OK max=OK two-stage rmse=2.193 mae=1.620 p90=3.203 p95=3.511 max=5.544 | rmse=OK mae=XX p90=XX p95=OK max=OK
cd /app && timeout 260 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from feat2 import build
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB
import warnings; warnings.filterwarnings('ignore')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
X=build(ALL,full)
res=[]
for nhold,reps in [(15,12)]:
gss=GroupShuffleSplit(n_splits=reps,test_size=nhold,random_state=1)
for tri,tei in gss.split(X,y,groups):
e=ET(n_estimators=400,n_jobs=-1,random_state=0).fit(X.iloc[tri],y[tri])
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=400,min_samples_leaf=20,random_state=0).fit(X.iloc[tri],y[tri])
p=0.5*e.predict(X.iloc[tei])+0.5*h.predict(X.iloc[tei])
cc=[ALL_COND[i] for i in tei]
m=metrics(y[tei],p,cc); res.append((m['rmse'],m['mae'],m['p90'],m['p95'],m['max']))
R=np.array(res)
names=['rmse','mae','p90','p95','max']; tg=[2.25,1.55,2.35,4.75,5.60]
for i,nm in enumerate(names):
print(f'{nm:4s}: mean={R[:,i].mean():.2f} min={R[:,i].min():.2f} median={np.median(R[:,i]):.2f} max={R[:,i].max():.2f} target={tg[i]} passrate={(R[:,i]<=tg[i]).mean():.0%}')
print('all-pass rate:', np.mean([(R[j]<=tg).all() for j in range(len(R))]))
"rmse: mean=2.36 min=1.78 median=2.34 max=3.35 target=2.25 passrate=25% mae : mean=1.74 min=1.42 median=1.70 max=2.47 target=1.55 passrate=17% p90 : mean=3.42 min=2.43 median=3.26 max=5.48 target=2.35 passrate=0% p95 : mean=3.85 min=2.62 median=3.79 max=5.76 target=4.75 passrate=83% max : mean=4.44 min=2.72 median=4.55 max=6.25 target=5.6 passrate=92% all-pass rate: 0.0
cd /app && timeout 260 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from eval_lib import metrics, TARGETS
from scipy.interpolate import RBFInterpolator
from sklearn.model_selection import GroupShuffleSplit
import warnings; warnings.filterwarnings('ignore')
a=ALL.attack_angle.values; c=ALL.chord_length.values; v=ALL.free_stream_velocity.values
f=ALL.frequency.values; dt=ALL.suction_side_displacement_thickness.values
lf=np.log10(f); lc=np.log10(c); lv=np.log10(v); ldt=np.log10(dt); lst=np.log10(f*dt/v)
def Z(cols):
M=np.column_stack(cols); mu=M.mean(0); sd=M.std(0); return (M-mu)/sd,mu,sd
coordsets={
'a_lc_lv_lf_ldt':[a,lc,lv,lf,ldt],
'a_lc_lv_lf':[a,lc,lv,lf],
'a_lc_lv_lst_lf':[a,lc,lv,lst,lf],
}
gss=GroupShuffleSplit(n_splits=8,test_size=15,random_state=1)
for nm,cols in coordsets.items():
for kernel,smooth in [('thin_plate_spline',1.0),('thin_plate_spline',5.0),('multiquadric',2.0)]:
M,mu,sd=Z(cols)
res=[]
for tri,tei in gss.split(M,y,groups):
try:
rbf=RBFInterpolator(M[tri],y[tri],kernel=kernel,smoothing=smooth,**({'epsilon':1.0} if kernel=='multiquadric' else {}))
p=rbf(M[tei])
except Exception as ex:
p=np.full(len(tei),y[tri].mean())
cc=[ALL_COND[i] for i in tei]; m=metrics(y[tei],p,cc)
res.append((m['rmse'],m['mae'],m['p90'],m['p95'],m['max']))
R=np.array(res).mean(0)
print(f'{nm:16s} {kernel[:10]:10s} s={smooth}: rmse={R[0]:.2f} mae={R[1]:.2f} p90={R[2]:.2f} p95={R[3]:.2f} max={R[4]:.2f}')
"a_lc_lv_lf_ldt thin_plate s=1.0: rmse=2.85 mae=2.02 p90=3.54 p95=4.50 max=6.31 a_lc_lv_lf_ldt thin_plate s=5.0: rmse=2.93 mae=2.11 p90=3.58 p95=4.52 max=6.36 a_lc_lv_lf_ldt multiquadr s=2.0: rmse=3.00 mae=2.20 p90=3.78 p95=4.57 max=6.04 a_lc_lv_lf thin_plate s=1.0: rmse=3.05 mae=2.16 p90=3.72 p95=4.80 max=6.98 a_lc_lv_lf thin_plate s=5.0: rmse=3.08 mae=2.23 p90=3.82 p95=4.83 max=6.79 a_lc_lv_lf multiquadr s=2.0: rmse=3.14 mae=2.30 p90=3.96 p95=4.80 max=6.40 a_lc_lv_lst_lf thin_plate s=1.0: rmse=2.72 mae=1.93 p90=3.36 p95=4.24 max=6.01 a_lc_lv_lst_lf thin_plate s=5.0: rmse=2.77 mae=1.99 p90=3.39 p95=4.29 max=6.11 a_lc_lv_lst_lf multiquadr s=2.0: rmse=2.80 mae=2.04 p90=3.56 p95=4.30 max=5.77
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND
d=ALL.copy(); d['cond']=ALL_COND
floors=[]
for cc,g in d.groupby('cond'):
n=len(g); lf=np.log10(g.frequency.values); t=g.scaled_sound_pressure_level.values
deg=min(4, n-2)
co=np.polyfit(lf,t,deg); r=t-np.polyval(co,lf)
# unbiased-ish residual rmse
floors.append(np.sqrt(np.sum(r**2)/max(n-(deg+1),1)))
floors=np.array(floors)
print('ORACLE per-condition residual RMSE (noise floor):')
print(' median=%.2f p75=%.2f p90=%.2f p95=%.2f max=%.2f'%(np.median(floors),np.percentile(floors,75),np.percentile(floors,90),np.percentile(floors,95),floors.max()))
print(' global RMSE if oracle: %.2f'%np.sqrt(np.mean(floors**2)))
# how many conditions have floor > 2.35?
print(' conditions with floor>2.35:',(floors>2.35).sum(),'of',len(floors))
"ORACLE per-condition residual RMSE (noise floor): median=0.78 p75=1.20 p90=1.81 p95=2.12 max=3.72 global RMSE if oracle: 1.21 conditions with floor>2.35: 3 of 82
cd /app && timeout 260 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from eval_lib import metrics, TARGETS
from sklearn.model_selection import GroupShuffleSplit
import warnings; warnings.filterwarnings('ignore')
a=ALL.attack_angle.values; c=ALL.chord_length.values; v=ALL.free_stream_velocity.values
lf=np.log10(ALL.frequency.values); lc=np.log10(c); lv=np.log10(v)
cond=np.array(ALL_COND,dtype=object)
def fit_curves(tri):
# per training condition: quartic curve + lf range + condition coords
df=pd.DataFrame({'c':[cond[i] for i in tri],'lf':lf[tri],'y':y[tri],'a':a[tri],'lc':lc[tri],'lv':lv[tri]})
curves={}
for cc,g in df.groupby('c'):
n=len(g); deg=min(4,n-2)
co=np.polyfit(g.lf.values,g.y.values,deg)
curves[cc]=(co,g.lf.min(),g.lf.max(),g.a.iloc[0],g.lc.iloc[0],g.lv.iloc[0])
return curves
def predict(curves, idx, K, hfac, wa, wc, wv):
# standardization scales for condition coords
keys=list(curves.keys())
A=np.array([curves[k][3] for k in keys]); LC=np.array([curves[k][4] for k in keys]); LV=np.array([curves[k][5] for k in keys])
sa,sc,sv=A.std(),LC.std(),LV.std()
out=np.zeros(len(idx))
for j,i in enumerate(idx):
d2=wa*((A-a[i])/sa)**2+wc*((LC-lc[i])/sc)**2+wv*((LV-lv[i])/sv)**2
# kernel bandwidth from Kth nearest
order=np.argsort(d2); sel=order[:K]
h=d2[order[min(K,len(order)-1)]]*hfac+1e-9
w=np.exp(-d2[sel]/h)
vals=np.array([np.polyval(curves[keys[m]][0], np.clip(lf[i],curves[keys[m]][1],curves[keys[m]][2])) for m in sel])
out[j]=np.sum(w*vals)/np.sum(w)
return out
gss=GroupShuffleSplit(n_splits=8,test_size=15,random_state=1)
for K in [6,8,12]:
for hfac in [0.3,0.6]:
res=[]
for tri,tei in gss.split(lf,y,groups):
cv=fit_curves(tri); p=predict(cv,tei,K,hfac,1.0,1.0,1.0)
cc=[ALL_COND[i] for i in tei]; m=metrics(y[tei],p,cc)
res.append((m['rmse'],m['mae'],m['p90'],m['p95'],m['max']))
R=np.array(res).mean(0)
print(f'K={K} hfac={hfac}: rmse={R[0]:.2f} mae={R[1]:.2f} p90={R[2]:.2f} p95={R[3]:.2f} max={R[4]:.2f}')
"[stderr]
Traceback (most recent call last):
File "<string>", line 44, in <module>
File "<string>", line 17, in fit_curves
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 618, in get_iterator
splitter = self._get_splitter(data, axis=axis)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 629, in _get_splitter
ids, _, ngroups = self.group_info
^^^^^^^^^^^^^^^
File "properties.pyx", line 36, in pandas._libs.properties.CachedProperty.__get__
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 745, in group_info
comp_ids, obs_group_ids = self._get_compressed_codes()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/ops.py", line 769, in _get_compressed_codes
return ping.codes, np.arange(len(ping._group_index), dtype=np.intp)
^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/grouper.py", line 691, in codes
return self._codes_and_uniques[0]
^^^^^^^^^^^^^^^^^^^^^^^
File "properties.pyx", line 36, in pandas._libs.properties.CachedProperty.__get__
File "/usr/local/lib/python3.12/dist-packages/pandas/core/groupby/grouper.py", line 835, in _codes_and_uniques
codes, uniques = algorithms.factorize( # type: ignore[assignment]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/algorithms.py", line 795, in factorize
codes, uniques = factorize_array(
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/pandas/core/algorithms.py", line 595, in factorize_array
uniques, codes = table.factorize(
^^^^^^^^^^^^^^^^
File "pandas/_libs/hashtable_class_helper.pxi", line 7281, in pandas._libs.hashtable.PyObjectHashTable.factorize
File "pandas/_libs/hashtable_class_helper.pxi", line 7195, in pandas._libs.hashtable.PyObjectHashTable._unique
TypeError: unhashable type: 'numpy.ndarray'cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
import warnings; warnings.filterwarnings('ignore')
a=ALL.attack_angle.values; lf=np.log10(ALL.frequency.values); lc=np.log10(ALL.chord_length.values); lv=np.log10(ALL.free_stream_velocity.values)
conds=np.array([f'{x[0]}_{x[1]}_{x[2]}' for x in ALL_COND])
def fit_curves(tri):
curves={}
for cc in np.unique(conds[tri]):
m=tri[conds[tri]==cc]
n=len(m); deg=min(4,n-2)
co=np.polyfit(lf[m],y[m],deg)
curves[cc]=(co,lf[m].min(),lf[m].max(),a[m][0],lc[m][0],lv[m][0])
keys=list(curves.keys())
A=np.array([curves[k][3] for k in keys]);LC=np.array([curves[k][4] for k in keys]);LV=np.array([curves[k][5] for k in keys])
return curves,keys,A,LC,LV,A.std(),LC.std(),LV.std()
def predict(fc, idx, K, hfac):
curves,keys,A,LC,LV,sa,sc,sv=fc
CO=[curves[k][0] for k in keys]; LO=np.array([curves[k][1] for k in keys]); HI=np.array([curves[k][2] for k in keys])
out=np.zeros(len(idx))
for j,i in enumerate(idx):
d2=((A-a[i])/sa)**2+((LC-lc[i])/sc)**2+((LV-lv[i])/sv)**2
order=np.argsort(d2); sel=order[:K]
h=d2[order[min(K,len(order)-1)]]*hfac+1e-9
w=np.exp(-d2[sel]/h)
vals=np.array([np.polyval(CO[m], min(max(lf[i],LO[m]),HI[m])) for m in sel])
out[j]=np.sum(w*vals)/np.sum(w)
return out
gss=GroupShuffleSplit(n_splits=10,test_size=15,random_state=1)
tg=np.array([2.25,1.55,2.35,4.75,5.60])
for K in [5,8,12,16]:
for hfac in [0.25,0.5,1.0]:
res=[]
for tri,tei in gss.split(lf,y,groups):
fc=fit_curves(tri); p=predict(fc,tei,K,hfac)
cc=[ALL_COND[i] for i in tei]; mm=metrics(y[tei],p,cc)
res.append([mm['rmse'],mm['mae'],mm['p90'],mm['p95'],mm['max']])
R=np.array(res); Rm=R.mean(0)
allpass=np.mean([(R[j]<=tg).all() for j in range(len(R))])
print(f'K={K:2d} h={hfac}: rmse={Rm[0]:.2f} mae={Rm[1]:.2f} p90={Rm[2]:.2f} p95={Rm[3]:.2f} max={Rm[4]:.2f} | allpass={allpass:.0%}')
"K= 5 h=0.25: rmse=3.34 mae=2.53 p90=4.53 p95=5.13 max=6.11 | allpass=0% K= 5 h=0.5: rmse=3.29 mae=2.51 p90=4.44 p95=5.04 max=6.05 | allpass=0% K= 5 h=1.0: rmse=3.33 mae=2.54 p90=4.50 p95=5.12 max=6.08 | allpass=0% K= 8 h=0.25: rmse=3.29 mae=2.52 p90=4.41 p95=4.99 max=5.96 | allpass=0% K= 8 h=0.5: rmse=3.34 mae=2.58 p90=4.57 p95=5.04 max=5.80 | allpass=0% K= 8 h=1.0: rmse=3.43 mae=2.67 p90=4.77 p95=5.17 max=5.74 | allpass=0% K=12 h=0.25: rmse=3.32 mae=2.56 p90=4.54 p95=5.05 max=5.87 | allpass=0% K=12 h=0.5: rmse=3.48 mae=2.71 p90=4.85 p95=5.23 max=5.85 | allpass=0% K=12 h=1.0: rmse=3.63 mae=2.84 p90=5.03 p95=5.41 max=6.08 | allpass=0% K=16 h=0.25: rmse=3.41 mae=2.65 p90=4.68 p95=5.12 max=5.87 | allpass=0% K=16 h=0.5: rmse=3.67 mae=2.88 p90=5.05 p95=5.43 max=6.11 | allpass=0% K=16 h=1.0: rmse=3.86 mae=3.05 p90=5.37 p95=5.73 max=6.38 | allpass=0%
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL
lf=np.log10(ALL.frequency.values)
v=np.sort(lf)
# cluster bands
centers=[v[0]]; groups=[[v[0]]]
for x in v[1:]:
if x-groups[-1][-1]>0.03: groups.append([]);
groups[-1].append(x)
cen=[np.mean(g) for g in groups]
print('n bands:',len(cen))
print('band freqs:',[int(round(10**x)) for x in cen])
print('counts:',[len(g) for g in groups])
"n bands: 21 band freqs: [200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600, 2000, 2500, 3150, 4000, 5000, 6300, 8001, 10002, 12505, 16006, 20006] counts: [29, 36, 44, 54, 61, 69, 75, 77, 77, 79, 81, 80, 79, 78, 72, 67, 38, 31, 17, 9, 4]
cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
from sklearn.ensemble import ExtraTreesRegressor as ET
from scipy.interpolate import RBFInterpolator
import warnings; warnings.filterwarnings('ignore')
lf=np.log10(ALL.frequency.values); a=ALL.attack_angle.values
lc=np.log10(ALL.chord_length.values); lv=np.log10(ALL.free_stream_velocity.values); ldt=np.log10(ALL.suction_side_displacement_thickness.values)
bandc=np.array([2.301,2.398,2.498,2.602,2.699,2.799,2.903,3.0,3.097,3.204,3.301,3.398,3.498,3.602,3.699,3.799,3.903,4.0,4.097,4.204,4.301])
def band_idx(x): return np.argmin(np.abs(bandc-x))
bi=np.array([band_idx(x) for x in lf])
def run_model(kind):
gss=GroupShuffleSplit(n_splits=10,test_size=15,random_state=1)
tg=np.array([2.25,1.55,2.35,4.75,5.60]); res=[]
for tri,tei in gss.split(lf,y,groups):
tri=np.array(tri); tei=np.array(tei); p=np.zeros(len(tei))
for b in np.unique(bi[tei]):
trb=tri[bi[tri]==b]; teb_local=np.where(bi[tei]==b)[0]; teb=tei[teb_local]
if len(trb)<5:
# fallback: nearest band with data - use global ET on (a,lc,lv,ldt,lf)
p[teb_local]=y[tri].mean(); continue
Xtr=np.column_stack([a[trb],lc[trb],lv[trb]]); Xte=np.column_stack([a[teb],lc[teb],lv[teb]])
if kind=='et':
m=ET(n_estimators=300,random_state=0).fit(np.column_stack([a[trb],lc[trb],lv[trb],ldt[trb]]),y[trb])
p[teb_local]=m.predict(np.column_stack([a[teb],lc[teb],lv[teb],ldt[teb]]))
else:
mu=Xtr.mean(0); sd=Xtr.std(0)+1e-9
try:
rbf=RBFInterpolator((Xtr-mu)/sd,y[trb],kernel='thin_plate_spline',smoothing=0.5)
p[teb_local]=rbf((Xte-mu)/sd)
except Exception:
p[teb_local]=y[trb].mean()
cc=[ALL_COND[i] for i in tei]; mm=metrics(y[tei],p,cc)
res.append([mm['rmse'],mm['mae'],mm['p90'],mm['p95'],mm['max']])
R=np.array(res); Rm=R.mean(0); allpass=np.mean([(R[j]<=tg).all() for j in range(len(R))])
print(f'per-band {kind}: rmse={Rm[0]:.2f} mae={Rm[1]:.2f} p90={Rm[2]:.2f} p95={Rm[3]:.2f} max={Rm[4]:.2f} | allpass={allpass:.0%}')
run_model('et')
run_model('rbf')
"per-band et: rmse=2.77 mae=1.95 p90=3.93 p95=4.41 max=5.26 | allpass=0% per-band rbf: rmse=3.13 mae=2.21 p90=4.22 p95=5.02 max=6.15 | allpass=0%
cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, y, groups, ALL_COND
from feat2 import build
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.neural_network import MLPRegressor
import warnings; warnings.filterwarnings('ignore')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
base=['lf','ld','lu','a','lc']
Xf=build(ALL,full); Xb=build(ALL,base)
def models():
return {
'et':(ET(n_estimators=500,n_jobs=-1,random_state=0),Xf),
'hgb':(HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=450,min_samples_leaf=20,random_state=0),Xf),
'pr':(make_pipeline(StandardScaler(),PolynomialFeatures(4),Ridge(alpha=8.0)),Xb),
'mlp':(make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(200,150,100),alpha=3e-3,max_iter=2500,random_state=0)),Xf),
}
W={'et':0.40,'hgb':0.25,'pr':0.15,'mlp':0.20}
gss=GroupShuffleSplit(n_splits=12,test_size=15,random_state=77)
tg=np.array([2.25,1.55,2.35,4.75,5.60]); res=[]
for tri,tei in gss.split(Xf,y,groups):
preds={}
for k,(mdl,Xd) in models().items():
mdl.fit(Xd.iloc[tri],y[tri]); preds[k]=mdl.predict(Xd.iloc[tei])
p=sum(W[k]*preds[k] for k in W)
cc=[ALL_COND[i] for i in tei]; m=metrics(y[tei],p,cc)
res.append([m['rmse'],m['mae'],m['p90'],m['p95'],m['max']])
R=np.array(res)
for i,nm in enumerate(['rmse','mae','p90','p95','max']):
print(f'{nm:4s}: mean={R[:,i].mean():.2f} median={np.median(R[:,i]):.2f} min={R[:,i].min():.2f} max={R[:,i].max():.2f} pass={ (R[:,i]<=tg[i]).mean():.0%}')
print('all-pass rate:',np.mean([(R[j]<=tg).all() for j in range(len(R))]))
"rmse: mean=2.27 median=2.25 min=1.59 max=2.84 pass=50% mae : mean=1.69 median=1.72 min=1.28 max=2.00 pass=33% p90 : mean=3.17 median=3.03 min=2.14 max=4.17 pass=8% p95 : mean=3.44 median=3.50 min=2.23 max=4.55 pass=100% max : mean=3.84 median=3.81 min=2.31 max=5.20 pass=100% all-pass rate: 0.08333333333333333
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL_COND
d=np.load('/tmp/oof_all.npz'); y=d['y']
keys=[k for k in d.files if k!='y']
P=np.vstack([d[k] for k in keys])
codes=pd.factorize(pd.Series(ALL_COND).astype(str))[0]; nC=codes.max()+1
counts=np.bincount(codes,minlength=nC)
def pen(pred,wp90=1.0):
err=pred-y; se=err*err
rmse=np.sqrt(se.mean()); mae=np.abs(err).mean()
cr=np.sqrt(np.bincount(codes,weights=se,minlength=nC)/counts)
p90=np.percentile(cr,90); p95=np.percentile(cr,95); mx=cr.max()
return max(rmse/2.25,mae/1.55,(p90/2.35),p95/4.75,mx/5.60),(rmse,mae,p90,p95,mx)
# coordinate ascent on weights
rng=np.random.default_rng(3)
w=np.ones(len(keys))/len(keys); best,_=pen(w@P)
for it in range(3000):
i=rng.integers(len(keys)); delta=rng.normal()*0.05
w2=w.copy(); w2[i]=max(0,w2[i]+delta)
if w2.sum()==0: continue
w2/=w2.sum(); pn,_=pen(w2@P)
if pn<best: best=pn; w=w2
pn,mm=pen(w@P)
print('opt penalty',round(pn,3))
print({k:round(wi,3) for k,wi in zip(keys,w) if wi>0.02})
print('rmse=%.3f mae=%.3f p90=%.3f p95=%.3f max=%.3f'%mm)
np.save('/tmp/blendw.npy',w); import json; open('/tmp/blendkeys.json','w').write(json.dumps(keys))
"opt penalty 1.262
{'et': 0.336, 'et3': 0.043, 'hgb2': 0.081, 'mlp': 0.188, 'pr': 0.27, 'knn': 0.082}
rmse=2.161 mae=1.608 p90=2.966 p95=3.542 max=4.479command: create
path: /app/train_and_predict.py
file_text: #!/usr/bin/env python3
"""Airfoil self-noise surrogate.
Trains from the labeled public data (train.csv [+ validation.csv]) and writes
predictions for the grader-staged feature rows at /app/data/test_features.csv.
Model: a blend of diverse regressors on physics-informed features
(log-frequency, log-thickness, Strouhal number, Reynolds numbers and
interactions). The split holds out whole aerodynamic condition groups
(attack_angle, chord_length, free_stream_velocity); blending de-correlated
learners reduces variance and the per-condition error tail.
"""
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.ensemble import (
ExtraTreesRegressor,
HistGradientBoostingRegressor,
RandomForestRegressor,
)
from sklearn.linear_model import Ridge
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, 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",
]
KINEMATIC_VISCOSITY = 1.5e-5 # air, m^2/s (only a constant scale for Reynolds)
def engineer(df: pd.DataFrame, cols) -> pd.DataFrame:
"""Build physics-informed features from the five raw inputs."""
f = df["frequency"].astype(float).to_numpy()
a = df["attack_angle"].astype(float).to_numpy()
c = df["chord_length"].astype(float).to_numpy()
u = df["free_stream_velocity"].astype(float).to_numpy()
d = df["suction_side_displacement_thickness"].astype(float).to_numpy()
# Guard against non-positive values before taking logs.
f = np.clip(f, 1e-6, None)
c = np.clip(c, 1e-9, None)
u = np.clip(u, 1e-6, None)
d = np.clip(d, 1e-12, None)
lf = np.log10(f)
ld = np.log10(d)
lc = np.log10(c)
lu = np.log10(u)
lst = np.log10(f * d / u) # Strouhal number
lrec = np.log10(u * c / KINEMATIC_VISCOSITY) # chord Reynolds
lred = np.log10(u * d / KINEMATIC_VISCOSITY) # thickness Reynolds
allf = {
"f": f, "a": a, "c": c, "u": u, "d": d,
"lf": lf, "ld": ld, "lc": lc, "lu": lu,
"lst": lst, "lrec": lrec, "lred": lred,
"sin_a": np.sin(np.radians(a)), "a2": a * a,
"lf_ld": lf * ld, "lf_a": lf * a, "lf_lu": lf * lu,
"a_lu": a * lu, "ld_a": ld * a, "lc_lu": lc * lu,
"lf2": lf * lf, "lst2": lst * lst, "lst3": lst ** 3,
"a_lc": a * lc, "lf_lc": lf * lc,
}
return pd.DataFrame({k: allf[k] for k in cols}, index=df.index)
FULL = [
"f", "a", "c", "u", "d", "lf", "ld", "lc", "lu", "lst", "lrec", "lred",
"sin_a", "a2", "lf_ld", "lf_a", "lf_lu", "a_lu", "ld_a", "lc_lu", "lf2",
"lst2", "lst3", "a_lc", "lf_lc",
]
BASE = ["lf", "ld", "lu", "a", "lc"]
def build_models():
"""Return dict name -> (estimator, feature_columns, blend_weight)."""
return {
"et": (
ExtraTreesRegressor(
n_estimators=800, min_samples_leaf=1, n_jobs=-1, random_state=0
),
FULL, 0.34,
),
"hgb": (
HistGradientBoostingRegressor(
learning_rate=0.08, max_leaf_nodes=15, l2_regularization=1.0,
max_iter=500, min_samples_leaf=20, random_state=0,
),
FULL, 0.22,
),
"rf": (
RandomForestRegressor(
n_estimators=600, min_samples_leaf=1, n_jobs=-1, random_state=0
),
FULL, 0.07,
),
"pr": (
make_pipeline(
StandardScaler(), PolynomialFeatures(4), Ridge(alpha=8.0)
),
BASE, 0.17,
),
"mlp": (
make_pipeline(
StandardScaler(),
…[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 2>&1 | tail -5 && echo "---OUTPUT---" && head -5 output/predictions.csv && echo "..." && wc -l output/predictions.csv && 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), (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()
print('VALID: rows',len(p),'| pred range',round(p.iloc[:,1].min(),2),'-',round(p.iloc[:,1].max(),2),'| nunique',p.iloc[:,1].nunique())
"---OUTPUT--- sample_id,predicted_scaled_sound_pressure_level af_val_0000,116.85742040949978 af_val_0001,125.00481021310746 af_val_0002,132.85109113413628 af_val_0003,124.4578826100014 ... 41 output/predictions.csv VALID: rows 40 | pred range 114.65 - 137.63 | nunique 40 [stderr] real 0m22.106s user 0m28.000s sys 0m0.590s
cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, ALL_COND, y, groups
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
from sklearn.ensemble import RandomForestRegressor as RF, ExtraTreesRegressor as ET
import warnings; warnings.filterwarnings('ignore')
lf=np.log10(ALL.frequency.values); z=lf-3.0
a=ALL.attack_angle.values; lc=np.log10(ALL.chord_length.values); lv=np.log10(ALL.free_stream_velocity.values); ldt=np.log10(ALL.suction_side_displacement_thickness.values)
conds=np.array([f'{x[0]}_{x[1]}_{x[2]}' for x in ALL_COND])
DEG=4
def condX(a_,lc_,lv_,ldt_):
return np.column_stack([a_,lc_,lv_,ldt_,a_*lv_,a_*ldt_,lc_*lv_,ldt_*lv_,a_*a_,lc_*ldt_,a_*lc_,ldt_*ldt_])
def run(deg, mk):
gss=GroupShuffleSplit(n_splits=12,test_size=15,random_state=77)
tg=np.array([2.25,1.55,2.35,4.75,5.60]); res=[]
for tri,tei in gss.split(lf,y,groups):
tri=np.array(tri); tei=np.array(tei)
# per train condition: fit poly coeffs + store cond features
rows=[]; coefs=[]
for cc in np.unique(conds[tri]):
m=tri[conds[tri]==cc]
n=len(m); d=min(deg,n-1)
co=np.polyfit(z[m],y[m],d)
co=np.concatenate([np.zeros(deg+1-len(co)),co]) # pad to deg+1
coefs.append(co); rows.append([a[m][0],lc[m][0],lv[m][0],ldt[m][0]])
rows=np.array(rows); coefs=np.array(coefs)
Xc=condX(rows[:,0],rows[:,1],rows[:,2],rows[:,3])
models=[mk().fit(Xc,coefs[:,k]) for k in range(deg+1)]
# predict test
Xt=condX(a[tei],lc[tei],lv[tei],ldt[tei])
pcoef=np.column_stack([models[k].predict(Xt) for k in range(deg+1)])
zt=np.clip(z[tei], z.min(), z.max())
p=np.array([np.polyval(pcoef[i], zt[i]) for i in range(len(tei))])
cc2=[ALL_COND[i] for i in tei]; mm=metrics(y[tei],p,cc2)
res.append([mm['rmse'],mm['mae'],mm['p90'],mm['p95'],mm['max']])
R=np.array(res); Rm=R.mean(0); ap=np.mean([(R[j]<=tg).all() for j in range(len(R))])
print(f'coef-model deg={deg} {mk().__class__.__name__}: rmse={Rm[0]:.2f} mae={Rm[1]:.2f} p90={Rm[2]:.2f} p95={Rm[3]:.2f} max={Rm[4]:.2f} | allpass={ap:.0%}')
run(4, lambda: RF(n_estimators=400,min_samples_leaf=1,random_state=0))
run(4, lambda: ET(n_estimators=400,min_samples_leaf=1,random_state=0))
run(3, lambda: RF(n_estimators=400,min_samples_leaf=1,random_state=0))
run(5, lambda: ET(n_estimators=400,min_samples_leaf=1,random_state=0))
"coef-model deg=4 RandomForestRegressor: rmse=12.54 mae=6.03 p90=19.00 p95=26.92 max=37.02 | allpass=0% coef-model deg=4 ExtraTreesRegressor: rmse=8.63 mae=4.21 p90=11.07 p95=17.69 max=27.97 | allpass=0% coef-model deg=3 RandomForestRegressor: rmse=5.51 mae=3.67 p90=8.24 p95=10.29 max=12.84 | allpass=0% coef-model deg=5 ExtraTreesRegressor: rmse=31.82 mae=10.38 p90=39.49 p95=65.24 max=105.95 | allpass=0%
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, y, groups, ALL_COND
from feat2 import build
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB, RandomForestRegressor as RF
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.neural_network import MLPRegressor
from sklearn.neighbors import KNeighborsRegressor
import warnings; warnings.filterwarnings('ignore')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
base=['lf','ld','lu','a','lc']
Xf=build(ALL,full); Xb=build(ALL,base)
def fit_preds(tri,tei):
P={}
P['et']=ET(n_estimators=600,n_jobs=-1,random_state=0).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
P['hgb']=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=500,min_samples_leaf=20,random_state=0).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
P['rf']=RF(n_estimators=500,n_jobs=-1,random_state=0).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
P['pr']=make_pipeline(StandardScaler(),PolynomialFeatures(4),Ridge(alpha=8.0)).fit(Xb.iloc[tri],y[tri]).predict(Xb.iloc[tei])
P['mlp']=make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(200,150,100),alpha=3e-3,max_iter=2500,random_state=0)).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
P['knn']=make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=10,weights='distance')).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
return P
blends={
'prod':{'et':.34,'hgb':.22,'rf':.07,'pr':.17,'mlp':.20},
'pr-heavy':{'et':.33,'pr':.27,'mlp':.19,'hgb':.13,'knn':.08},
'noknn-prheavy':{'et':.36,'pr':.24,'mlp':.20,'hgb':.20},
}
tg=np.array([2.25,1.55,2.35,4.75,5.60])
gss=GroupShuffleSplit(n_splits=12,test_size=15,random_state=123)
agg={b:[] for b in blends}
for tri,tei in gss.split(Xf,y,groups):
tri=np.array(tri);tei=np.array(tei); P=fit_preds(tri,tei); cc=[ALL_COND[i] for i in tei]
for b,w in blends.items():
p=sum(w[k]*P[k] for k in w); m=metrics(y[tei],p,cc)
agg[b].append([m['rmse'],m['mae'],m['p90'],m['p95'],m['max']])
for b in blends:
R=np.array(agg[b]); Rm=R.mean(0); ap=np.mean([(R[j]<=tg).all() for j in range(len(R))])
print(f'{b:16s} rmse={Rm[0]:.2f} mae={Rm[1]:.2f} p90={Rm[2]:.2f}(min{R[:,2].min():.2f}) p95={Rm[3]:.2f} max={Rm[4]:.2f} | p90pass={ (R[:,2]<=2.35).mean():.0%} allpass={ap:.0%}')
"prod rmse=2.31 mae=1.73 p90=3.24(min2.44) p95=3.67 max=4.17 | p90pass=0% allpass=0% pr-heavy rmse=2.31 mae=1.73 p90=3.19(min2.58) p95=3.59 max=4.20 | p90pass=0% allpass=0% noknn-prheavy rmse=2.29 mae=1.71 p90=3.15(min2.41) p95=3.58 max=4.19 | p90pass=0% allpass=0%
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL_COND
d=np.load('/tmp/oof_all.npz'); y=d['y']
w={'et':.336,'et3':.043,'hgb2':.081,'mlp':.188,'pr':.27,'knn':.082}
pred=sum(w.get(k,0)*d[k] for k in d.files if k!='y')
err=pred-y; ae=np.abs(err)
print('PER-ROW |error| percentiles: p50=%.2f p90=%.2f p95=%.2f p99=%.2f max=%.2f'%(np.percentile(ae,50),np.percentile(ae,90),np.percentile(ae,95),np.percentile(ae,99),ae.max()))
# grouped by reconstructed condition
dfc=pd.DataFrame({'c':[str(x) for x in ALL_COND],'e':err})
cr=dfc.groupby('c').e.apply(lambda s:np.sqrt(np.mean(s**2)))
print('GROUPED per-condition rmse: p90=%.2f p95=%.2f max=%.2f'%(np.percentile(cr,90),np.percentile(cr,95),cr.max()))
"PER-ROW |error| percentiles: p50=1.22 p90=3.50 p95=4.58 p99=6.48 max=9.43 GROUPED per-condition rmse: p90=2.97 p95=3.54 max=4.48
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, y, groups, ALL_COND
from feat2 import build
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.neural_network import MLPRegressor
import warnings; warnings.filterwarnings('ignore')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
base=['lf','ld','lu','a','lc']
Xf=build(ALL,full); Xb=build(ALL,base)
lf=np.log10(ALL.frequency.values); conds=np.array([str(x) for x in ALL_COND])
def denoise(tri):
yd=y.copy()
for cc in np.unique(conds[tri]):
m=tri[conds[tri]==cc]; n=len(m); deg=min(4,n-2)
if deg<1: continue
co=np.polyfit(lf[m],y[m],deg); yd[m]=np.polyval(co,lf[m])
return yd
W={'et':.34,'hgb':.22,'pr':.17,'mlp':.20,'rf':.07}
def preds(tri,tei,yt):
from sklearn.ensemble import RandomForestRegressor as RF
P={}
P['et']=ET(n_estimators=600,n_jobs=-1,random_state=0).fit(Xf.iloc[tri],yt[tri]).predict(Xf.iloc[tei])
P['hgb']=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=500,min_samples_leaf=20,random_state=0).fit(Xf.iloc[tri],yt[tri]).predict(Xf.iloc[tei])
P['rf']=RF(n_estimators=500,n_jobs=-1,random_state=0).fit(Xf.iloc[tri],yt[tri]).predict(Xf.iloc[tei])
P['pr']=make_pipeline(StandardScaler(),PolynomialFeatures(4),Ridge(alpha=8.0)).fit(Xb.iloc[tri],yt[tri]).predict(Xb.iloc[tei])
P['mlp']=make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(200,150,100),alpha=3e-3,max_iter=2500,random_state=0)).fit(Xf.iloc[tri],yt[tri]).predict(Xf.iloc[tei])
return sum(W[k]*P[k] for k in W)
tg=np.array([2.25,1.55,2.35,4.75,5.60])
gss=GroupShuffleSplit(n_splits=12,test_size=15,random_state=123)
A={'raw':[],'denoised':[]}
for tri,tei in gss.split(Xf,y,groups):
tri=np.array(tri);tei=np.array(tei); cc=[ALL_COND[i] for i in tei]
for nm,yt in [('raw',y),('denoised',denoise(tri))]:
p=preds(tri,tei,yt); m=metrics(y[tei],p,cc)
A[nm].append([m['rmse'],m['mae'],m['p90'],m['p95'],m['max']])
for nm in A:
R=np.array(A[nm]); Rm=R.mean(0); ap=np.mean([(R[j]<=tg).all() for j in range(len(R))])
print(f'{nm:9s} rmse={Rm[0]:.2f} mae={Rm[1]:.2f} p90={Rm[2]:.2f}(min{R[:,2].min():.2f}) p95={Rm[3]:.2f} max={Rm[4]:.2f} | p90pass={ (R[:,2]<=2.35).mean():.0%} allpass={ap:.0%}')
"<system>Tool ran without output or errors</system>
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, y, groups, ALL_COND
from feat2 import build
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
import warnings; warnings.filterwarnings('ignore')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
base=['lf','ld','lu','a','lc']
Xf=build(ALL,full); Xb=build(ALL,base)
lf=np.log10(ALL.frequency.values); conds=np.array([str(x) for x in ALL_COND])
def denoise(tri):
yd=y.copy().astype(float)
for cc in np.unique(conds[tri]):
m=tri[conds[tri]==cc]; n=len(m); deg=min(4,n-2)
if deg>=1:
co=np.polyfit(lf[m],y[m],deg); yd[m]=np.polyval(co,lf[m])
return yd
W={'et':.45,'hgb':.30,'pr':.25}
def preds(tri,tei,yt):
e=ET(n_estimators=500,n_jobs=-1,random_state=0).fit(Xf.iloc[tri],yt[tri]).predict(Xf.iloc[tei])
h=HGB(learning_rate=0.08,max_leaf_nodes=15,l2_regularization=1.0,max_iter=450,min_samples_leaf=20,random_state=0).fit(Xf.iloc[tri],yt[tri]).predict(Xf.iloc[tei])
p=make_pipeline(StandardScaler(),PolynomialFeatures(4),Ridge(alpha=8.0)).fit(Xb.iloc[tri],yt[tri]).predict(Xb.iloc[tei])
return W['et']*e+W['hgb']*h+W['pr']*p
tg=np.array([2.25,1.55,2.35,4.75,5.60])
gss=GroupShuffleSplit(n_splits=10,test_size=15,random_state=123)
A={'raw':[],'denoised':[]}
for tri,tei in gss.split(Xf,y,groups):
tri=np.array(tri);tei=np.array(tei); cc=[ALL_COND[i] for i in tei]
for nm,yt in [('raw',y.astype(float)),('denoised',denoise(tri))]:
m=metrics(y[tei],preds(tri,tei,yt),cc)
A[nm].append([m['rmse'],m['mae'],m['p90'],m['p95'],m['max']])
for nm in A:
R=np.array(A[nm]); Rm=R.mean(0)
print(f'{nm:9s} rmse={Rm[0]:.2f} mae={Rm[1]:.2f} p90={Rm[2]:.2f}(min{R[:,2].min():.2f}) p95={Rm[3]:.2f} max={Rm[4]:.2f} | p90pass={ (R[:,2]<=2.35).mean():.0%}')
"raw rmse=2.30 mae=1.75 p90=3.24(min2.39) p95=3.60 max=3.98 | p90pass=0% denoised rmse=2.35 mae=1.80 p90=3.26(min2.40) p95=3.58 max=3.97 | p90pass=0%
cd /app && timeout 290 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, y, groups, ALL_COND
from feat2 import build
from eval_lib import metrics
from sklearn.model_selection import GroupShuffleSplit
from sklearn.ensemble import ExtraTreesRegressor as ET, HistGradientBoostingRegressor as HGB, RandomForestRegressor as RF
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.neural_network import MLPRegressor
from sklearn.neighbors import KNeighborsRegressor
import warnings; warnings.filterwarnings('ignore')
full=['f','a','c','u','d','lf','ld','lc','lu','lst','lrec','lred','sin_a','a2','lf_ld','lf_a','lf_lu','a_lu','ld_a','lc_lu','lf2','lst2','lst3','a_lc','lf_lc']
base=['lf','ld','lu','a','lc']
Xf=build(ALL,full); Xb=build(ALL,base)
def P_all(tri,tei):
P={}
P['et']=ET(n_estimators=600,min_samples_leaf=1,n_jobs=-1,random_state=0).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
P['et3']=ET(n_estimators=600,min_samples_leaf=3,n_jobs=-1,random_state=1).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
P['hgb2']=HGB(learning_rate=0.05,max_leaf_nodes=31,l2_regularization=0.0,max_iter=600,min_samples_leaf=15,random_state=1).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
P['mlp']=np.mean([make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(200,150,100),alpha=3e-3,max_iter=2500,random_state=s)).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei]) for s in (0,1)],axis=0)
P['pr']=make_pipeline(StandardScaler(),PolynomialFeatures(4),Ridge(alpha=8.0)).fit(Xb.iloc[tri],y[tri]).predict(Xb.iloc[tei])
P['knn']=make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=10,weights='distance')).fit(Xf.iloc[tri],y[tri]).predict(Xf.iloc[tei])
return P
blends={
'oofopt':{'et':.336,'et3':.043,'hgb2':.081,'mlp':.188,'pr':.27,'knn':.082},
'simple':{'et':.38,'hgb2':.17,'mlp':.22,'pr':.23},
}
tg=np.array([2.25,1.55,2.35,4.75,5.60]); agg={b:[] for b in blends}
gss=GroupShuffleSplit(n_splits=10,test_size=15,random_state=2024)
for tri,tei in gss.split(Xf,y,groups):
tri=np.array(tri);tei=np.array(tei); P=P_all(tri,tei); cc=[ALL_COND[i] for i in tei]
for b,w in blends.items():
p=sum(w[k]*P[k] for k in w); m=metrics(y[tei],p,cc)
agg[b].append([m['rmse'],m['mae'],m['p90'],m['p95'],m['max']])
for b in blends:
R=np.array(agg[b]); Rm=R.mean(0)
print(f'{b:8s} rmse={Rm[0]:.2f} mae={Rm[1]:.2f} p90={Rm[2]:.2f}(min{R[:,2].min():.2f}) p95={Rm[3]:.2f} max={Rm[4]:.2f} | p90pass={ (R[:,2]<=2.35).mean():.0%} maepass={ (R[:,1]<=1.55).mean():.0%}')
"oofopt rmse=2.20 mae=1.65 p90=3.07(min2.60) p95=3.37 max=3.74 | p90pass=0% maepass=20% simple rmse=2.18 mae=1.62 p90=3.01(min2.51) p95=3.30 max=3.73 | p90pass=0% maepass=40%
command: str_replace
path: /app/train_and_predict.py
old_str: def build_models():
"""Return dict name -> (estimator, feature_columns, blend_weight)."""
return {
"et": (
ExtraTreesRegressor(
n_estimators=800, min_samples_leaf=1, n_jobs=-1, random_state=0
),
FULL, 0.34,
),
"hgb": (
HistGradientBoostingRegressor(
learning_rate=0.08, max_leaf_nodes=15, l2_regularization=1.0,
max_iter=500, min_samples_leaf=20, random_state=0,
),
FULL, 0.22,
),
"rf": (
RandomForestRegressor(
n_estimators=600, min_samples_leaf=1, n_jobs=-1, random_state=0
),
FULL, 0.07,
),
"pr": (
make_pipeline(
StandardScaler(), PolynomialFeatures(4), Ridge(alpha=8.0)
),
BASE, 0.17,
),
"mlp": (
make_pipeline(
StandardScaler(),
MLPRegressor(
hidden_layer_sizes=(200, 150, 100), alpha=3e-3,
max_iter=3000, random_state=0,
),
),
FULL, 0.20,
),
}
new_str: def build_models():
"""Return dict name -> (estimator, feature_columns, blend_weight).
The blend mixes de-correlated learners: ExtraTrees and a
HistGradientBoosting for flexible interpolation, a degree-4 polynomial
ridge in log-physics coordinates that extrapolates smoothly at the grid
edges (which dominate the per-condition error tail), and a seed-averaged
MLP for a smooth global fit. Weights were selected with grouped
(held-out-condition) cross-validation.
"""
models = {
"et": (
ExtraTreesRegressor(
n_estimators=800, min_samples_leaf=1, n_jobs=-1, random_state=0
),
FULL, 0.38,
),
"hgb": (
HistGradientBoostingRegressor(
learning_rate=0.05, max_leaf_nodes=31, l2_regularization=0.0,
max_iter=600, min_samples_leaf=15, random_state=1,
),
FULL, 0.17,
),
"pr": (
make_pipeline(
StandardScaler(), PolynomialFeatures(4), Ridge(alpha=8.0)
),
BASE, 0.23,
),
}
# Seed-averaged MLP (0.22 total) for a stable, smooth component.
mlp_seeds = (0, 1, 2)
for s in mlp_seeds:
models[f"mlp{s}"] = (
make_pipeline(
StandardScaler(),
MLPRegressor(
hidden_layer_sizes=(200, 150, 100), alpha=3e-3,
max_iter=3000, random_state=s,
),
),
FULL, 0.22 / len(mlp_seeds),
)
return modelsThe file /app/train_and_predict.py has been edited successfully.
command: str_replace
path: /app/train_and_predict.py
old_str: from sklearn.ensemble import (
ExtraTreesRegressor,
HistGradientBoostingRegressor,
RandomForestRegressor,
)
new_str: from sklearn.ensemble import (
ExtraTreesRegressor,
HistGradientBoostingRegressor,
)The file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py 2>&1 | tail -3 && 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']
assert len(p)==len(t) and set(p.sample_id.astype(str))==set(t.sample_id.astype(str))
assert np.isfinite(p.iloc[:,1]).all()
print('VALID rows',len(p),'range',round(p.iloc[:,1].min(),2),'-',round(p.iloc[:,1].max(),2),'nunique',p.iloc[:,1].nunique())
"VALID rows 40 range 114.58 - 137.47 nunique 40 [stderr] real 0m28.227s user 0m28.670s sys 0m0.640s
cd /app && timeout 200 python3 -c "
import sys, importlib.util; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from eval_lib import angle_refs, make_conditions, metrics, TARGETS
spec=importlib.util.spec_from_file_location('tp','/app/train_and_predict.py'); tp=importlib.util.module_from_spec(spec); spec.loader.exec_module(tp)
import warnings; warnings.filterwarnings('ignore')
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
arefs=angle_refs(tr,va); cond=make_conditions(va,arefs)
y=tr[tp.TARGET].to_numpy(float)
pred=np.zeros(len(va)); tw=0.0
for nm,(est,cols,w) in tp.build_models().items():
est.fit(tp.engineer(tr,cols),y); pred+=w*est.predict(tp.engineer(va,cols)); tw+=w
pred/=tw
m=metrics(va[tp.TARGET].to_numpy(float),pred,cond)
flags=' '.join(f\"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}\" for k in TARGETS)
print(f'TRAIN->VAL (production blend): rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f}')
print('targets: rmse<=2.25 mae<=1.55 p90<=2.35 p95<=4.75 max<=5.60')
print('flags:',flags)
"TRAIN->VAL (production blend): rmse=2.226 mae=1.718 p90=3.113 p95=3.506 max=4.037 targets: rmse<=2.25 mae<=1.55 p90<=2.35 p95<=4.75 max<=5.60 flags: rmse=OK mae=XX p90=XX p95=OK max=OK
cd /app && cat train_and_predict.py
#!/usr/bin/env python3
"""Airfoil self-noise surrogate.
Trains from the labeled public data (train.csv [+ validation.csv]) and writes
predictions for the grader-staged feature rows at /app/data/test_features.csv.
Model: a blend of diverse regressors on physics-informed features
(log-frequency, log-thickness, Strouhal number, Reynolds numbers and
interactions). The split holds out whole aerodynamic condition groups
(attack_angle, chord_length, free_stream_velocity); blending de-correlated
learners reduces variance and the per-condition error tail.
"""
from pathlib import Path
import numpy as np
import pandas as pd
from sklearn.ensemble import (
ExtraTreesRegressor,
HistGradientBoostingRegressor,
)
from sklearn.linear_model import Ridge
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import PolynomialFeatures, 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",
]
KINEMATIC_VISCOSITY = 1.5e-5 # air, m^2/s (only a constant scale for Reynolds)
def engineer(df: pd.DataFrame, cols) -> pd.DataFrame:
"""Build physics-informed features from the five raw inputs."""
f = df["frequency"].astype(float).to_numpy()
a = df["attack_angle"].astype(float).to_numpy()
c = df["chord_length"].astype(float).to_numpy()
u = df["free_stream_velocity"].astype(float).to_numpy()
d = df["suction_side_displacement_thickness"].astype(float).to_numpy()
# Guard against non-positive values before taking logs.
f = np.clip(f, 1e-6, None)
c = np.clip(c, 1e-9, None)
u = np.clip(u, 1e-6, None)
d = np.clip(d, 1e-12, None)
lf = np.log10(f)
ld = np.log10(d)
lc = np.log10(c)
lu = np.log10(u)
lst = np.log10(f * d / u) # Strouhal number
lrec = np.log10(u * c / KINEMATIC_VISCOSITY) # chord Reynolds
lred = np.log10(u * d / KINEMATIC_VISCOSITY) # thickness Reynolds
allf = {
"f": f, "a": a, "c": c, "u": u, "d": d,
"lf": lf, "ld": ld, "lc": lc, "lu": lu,
"lst": lst, "lrec": lrec, "lred": lred,
"sin_a": np.sin(np.radians(a)), "a2": a * a,
"lf_ld": lf * ld, "lf_a": lf * a, "lf_lu": lf * lu,
"a_lu": a * lu, "ld_a": ld * a, "lc_lu": lc * lu,
"lf2": lf * lf, "lst2": lst * lst, "lst3": lst ** 3,
"a_lc": a * lc, "lf_lc": lf * lc,
}
return pd.DataFrame({k: allf[k] for k in cols}, index=df.index)
FULL = [
"f", "a", "c", "u", "d", "lf", "ld", "lc", "lu", "lst", "lrec", "lred",
"sin_a", "a2", "lf_ld", "lf_a", "lf_lu", "a_lu", "ld_a", "lc_lu", "lf2",
"lst2", "lst3", "a_lc", "lf_lc",
]
BASE = ["lf", "ld", "lu", "a", "lc"]
def build_models():
"""Return dict name -> (estimator, feature_columns, blend_weight).
The blend mixes de-correlated learners: ExtraTrees and a
HistGradientBoosting for flexible interpolation, a degree-4 polynomial
ridge in log-physics coordinates that extrapolates smoothly at the grid
edges (which dominate the per-condition error tail), and a seed-averaged
MLP for a smooth global fit. Weights were selected with grouped
(held-out-condition) cross-validation.
"""
models = {
"et": (
ExtraTreesRegressor(
n_estimators=800, min_samples_leaf=1, n_jobs=-1, random_state=0
),
FULL, 0.38,
),
"hgb": (
HistGradientBoostingRegressor(
learning_rate=0.05, max_leaf_nodes=31, l2_regularization=0.0,
max_iter=600, min_samples_leaf=15, random_state=1,
),
FULL, 0.17,
),
"pr": (
make_pipeline(
StandardScaler(), PolynomialFeatures(4), Ridge(alpha=8
…[truncated]cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL_COND
from eval_lib import metrics, TARGETS
d=np.load('/tmp/oof_all.npz'); y=d['y']
w={'et':.38,'hgb2':.17,'mlp':.22,'pr':.23}
pred=sum(w[k]*d[k] for k in w)
# per-condition means
dfc=pd.DataFrame({'c':[str(x) for x in ALL_COND],'p':pred,'y':y})
g=dfc.groupby('c').agg(pm=('p','mean'),ym=('y','mean'))
# global shrinkage slope
slope,inter=np.polyfit(g.pm,g.ym,1)
gm=y.mean()
print('condition-mean regression: true = %.3f*pred + %.3f (gm=%.2f)'%(slope,inter,gm))
print('corr pred/true cond-mean:',round(np.corrcoef(g.pm,g.ym)[0,1],3))
def ev(expand,label):
p2=gm+(pred-gm)*expand
m=metrics(y,p2,ALL_COND)
f=' '.join(f\"{k}={'OK' if m[k]<=TARGETS[k] else 'XX'}\" for k in TARGETS)
print(f'{label:12s} exp={expand:.2f} rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f} | {f}')
for e in [1.0,1.03,1.06,1.10,1.15]:
ev(e,'deshrink')
"condition-mean regression: true = 1.046*pred + -5.704 (gm=125.59) corr pred/true cond-mean: 0.957 deshrink exp=1.00 rmse=2.145 mae=1.598 p90=3.085 p95=3.469 max=4.461 | rmse=OK mae=XX p90=XX p95=OK max=OK deshrink exp=1.03 rmse=2.129 mae=1.584 p90=3.046 p95=3.503 max=4.427 | rmse=OK mae=XX p90=XX p95=OK max=OK deshrink exp=1.06 rmse=2.129 mae=1.587 p90=3.025 p95=3.564 max=4.394 | rmse=OK mae=XX p90=XX p95=OK max=OK deshrink exp=1.10 rmse=2.154 mae=1.617 p90=3.003 p95=3.609 max=4.354 | rmse=OK mae=XX p90=XX p95=OK max=OK deshrink exp=1.15 rmse=2.223 mae=1.690 p90=3.224 p95=3.607 max=4.310 | rmse=OK mae=XX p90=XX p95=OK max=OK
cd /app && timeout 200 python3 -c "
import sys, importlib.util; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from eval_lib import angle_refs, make_conditions, metrics, TARGETS
spec=importlib.util.spec_from_file_location('tp','/app/train_and_predict.py'); tp=importlib.util.module_from_spec(spec); spec.loader.exec_module(tp)
import warnings; warnings.filterwarnings('ignore')
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
arefs=angle_refs(tr,va); cond=make_conditions(va,arefs)
y=tr[tp.TARGET].to_numpy(float); yv=va[tp.TARGET].to_numpy(float)
pred=np.zeros(len(va)); tw=0.0
for nm,(est,cols,w) in tp.build_models().items():
est.fit(tp.engineer(tr,cols),y); pred+=w*est.predict(tp.engineer(va,cols)); tw+=w
pred/=tw
gm=y.mean()
for e in [1.0,1.03,1.05,1.08]:
p2=gm+(pred-gm)*e; m=metrics(yv,p2,cond)
print(f'exp={e:.2f} rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.3f} p95={m[\"p95\"]:.3f} max={m[\"max\"]:.3f}')
"exp=1.00 rmse=2.226 mae=1.718 p90=3.113 p95=3.506 max=4.037 exp=1.03 rmse=2.203 mae=1.722 p90=3.073 p95=3.460 max=3.962 exp=1.05 rmse=2.196 mae=1.731 p90=3.052 p95=3.434 max=3.915 exp=1.08 rmse=2.200 mae=1.750 p90=3.031 p95=3.403 max=3.851
command: str_replace
path: /app/train_and_predict.py
old_str: preds += weight * p
total_w += weight
preds /= total_w
# Final safety: clip to a generous plausible dB range and ensure finite.
new_str: preds += weight * p
total_w += weight
preds /= total_w
# De-shrink: tree/MLP learners regress predictions toward the training
# mean, under/over-predicting the most extreme held-out conditions (which
# dominate the per-condition error tail). A small, principled expansion
# about the training mean counteracts this. The factor was estimated from
# grouped cross-validation (condition-mean slope ~1.05) and verified to
# help on the held-out validation conditions; it is kept conservative.
center = float(np.mean(y))
preds = center + (preds - center) * 1.04
# Final safety: clip to a generous plausible dB range and ensure finite.The file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && python3 train_and_predict.py 2>&1 | tail -2 && 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']
assert len(p)==len(t) and set(p.sample_id.astype(str))==set(t.sample_id.astype(str))
assert np.isfinite(p.iloc[:,1]).all() and p.iloc[:,1].nunique()>1
print('VALID rows',len(p),'range',round(p.iloc[:,1].min(),2),'-',round(p.iloc[:,1].max(),2))
"VALID rows 40 range 114.14 - 137.95
cd /app && timeout 290 python3 -c "
import sys, importlib.util; sys.path.insert(0,'/tmp')
import numpy as np, pandas as pd
from cv import ALL, y, groups, ALL_COND
from eval_lib import metrics
spec=importlib.util.spec_from_file_location('tp','/app/train_and_predict.py'); tp=importlib.util.module_from_spec(spec); spec.loader.exec_module(tp)
from sklearn.model_selection import GroupShuffleSplit
import warnings; warnings.filterwarnings('ignore')
tg=np.array([2.25,1.55,2.35,4.75,5.60]); res=[]
gss=GroupShuffleSplit(n_splits=8,test_size=15,random_state=999)
for tri,tei in gss.split(ALL,y,groups):
tri=np.array(tri);tei=np.array(tei)
trdf=ALL.iloc[tri]; tedf=ALL.iloc[tei]; yt=y[tri]
pred=np.zeros(len(tei)); tw=0.0
for nm,(est,cols,w) in tp.build_models().items():
est.fit(tp.engineer(trdf,cols),yt); pred+=w*est.predict(tp.engineer(tedf,cols)); tw+=w
pred/=tw; c=yt.mean(); pred=c+(pred-c)*1.04
cc=[ALL_COND[i] for i in tei]; m=metrics(y[tei],pred,cc)
res.append([m['rmse'],m['mae'],m['p90'],m['p95'],m['max']])
R=np.array(res)
print('FINAL production pipeline , honest held-out-condition simulation (8 draws):')
for i,nm in enumerate(['rmse','mae','p90','p95','max']):
print(f' {nm:4s}: mean={R[:,i].mean():.2f} median={np.median(R[:,i]):.2f} min={R[:,i].min():.2f} max={R[:,i].max():.2f} target<={tg[i]} pass={ (R[:,i]<=tg[i]).mean():.0%}')
"FINAL production pipeline , honest held-out-condition simulation (8 draws): rmse: mean=2.35 median=2.42 min=1.97 max=2.59 target<=2.25 pass=25% mae : mean=1.74 median=1.77 min=1.45 max=2.01 target<=1.55 pass=25% p90 : mean=3.45 median=3.52 min=2.76 max=3.94 target<=2.35 pass=0% p95 : mean=3.70 median=3.77 min=3.10 max=4.08 target<=4.75 pass=100% max : mean=3.97 median=4.08 min=3.23 max=4.57 target<=5.6 pass=100%
command: str_replace
path: /app/train_and_predict.py
old_str: labeled = load_labeled_data()
evaluation = load_evaluation_features()
y = labeled[TARGET].to_numpy(dtype=float)
new_str: labeled = load_labeled_data()
evaluation = load_evaluation_features().copy()
# Defensive imputation: the hidden rows share the schema and should be
# clean, but guard against any missing feature values so predictions are
# always finite and aligned with every requested sample_id.
for col in FEATURES:
if col not in evaluation.columns:
evaluation[col] = np.nan
evaluation[col] = pd.to_numeric(evaluation[col], errors="coerce")
if evaluation[col].isna().any():
evaluation[col] = evaluation[col].fillna(float(labeled[col].median()))
y = labeled[TARGET].to_numpy(dtype=float)The file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && python3 train_and_predict.py 2>&1 | tail -2 && 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']
assert len(p)==len(t) and list(p.sample_id.astype(str))==list(t.sample_id.astype(str))
assert np.isfinite(p.iloc[:,1]).all() and p.iloc[:,1].nunique()>1
print('FINAL VALID: rows',len(p),'| finite & non-constant | range',round(p.iloc[:,1].min(),2),'-',round(p.iloc[:,1].max(),2))
"FINAL VALID: rows 40 | finite & non-constant | range 114.14 - 137.95
cd /app && timeout 120 python3 -c "
import importlib.util, numpy as np, pandas as pd
spec=importlib.util.spec_from_file_location('tp','/app/train_and_predict.py'); tp=importlib.util.module_from_spec(spec); spec.loader.exec_module(tp)
import warnings; warnings.filterwarnings('ignore')
# Build a synthetic 'hidden' staged feature set: 123 rows drawn from train features, new ids, shuffled, one NaN injected
tr=pd.read_csv('data/train.csv').sample(123,random_state=5).reset_index(drop=True)
syn=tr[tp.FEATURES].copy(); syn.insert(0,'sample_id',[f'hidden_{i:04d}' for i in range(len(syn))])
syn.loc[3,'frequency']=np.nan # inject a gap
labeled=tp.load_labeled_data(); y=labeled[tp.TARGET].to_numpy(float)
ev=syn.copy()
for col in tp.FEATURES:
ev[col]=pd.to_numeric(ev[col],errors='coerce')
if ev[col].isna().any(): ev[col]=ev[col].fillna(float(labeled[col].median()))
pred=np.zeros(len(ev)); tw=0.0
for nm,(est,cols,w) in tp.build_models().items():
est.fit(tp.engineer(labeled,cols),y); pred+=w*est.predict(tp.engineer(ev,cols)); tw+=w
pred/=tw; c=float(np.mean(y)); pred=c+(pred-c)*1.04
out=pd.DataFrame({'sample_id':syn.sample_id.astype(str),'pred':pred})
assert len(out)==len(syn) and list(out.sample_id)==list(syn.sample_id)
assert np.isfinite(out.pred).all()
print('Synthetic hidden-set alignment OK:',len(out),'rows, all finite, ids aligned, pred range',round(pred.min(),1),'-',round(pred.max(),1))
print('Input files untouched (no writes performed).')
"Synthetic hidden-set alignment OK: 123 rows, all finite, ids aligned, pred range 108.1 - 136.7 Input files untouched (no writes performed).
cd /app && echo "Input file sizes (expect train 97417, validation 20351, test_features 3595):" && stat -c '%s %n' data/train.csv data/validation.csv data/test_features.csv && echo "" && echo "Deliverable:" && stat -c '%s %n' output/predictions.csv && echo "" && echo "Head of predictions:" && head -3 output/predictions.csv
Input file sizes (expect train 97417, validation 20351, test_features 3595): 97417 data/train.csv 20351 data/validation.csv 3595 data/test_features.csv Deliverable: 1282 output/predictions.csv Head of predictions: sample_id,predicted_scaled_sound_pressure_level af_val_0000,116.53552683509434 af_val_0001,124.92914500861342
cd /app && python3 -m py_compile train_and_predict.py && echo "compiles OK" && sed -n '150,210p' train_and_predict.py
compiles OK
def main() -> None:
labeled = load_labeled_data()
evaluation = load_evaluation_features().copy()
# Defensive imputation: the hidden rows share the schema and should be
# clean, but guard against any missing feature values so predictions are
# always finite and aligned with every requested sample_id.
for col in FEATURES:
if col not in evaluation.columns:
evaluation[col] = np.nan
evaluation[col] = pd.to_numeric(evaluation[col], errors="coerce")
if evaluation[col].isna().any():
evaluation[col] = evaluation[col].fillna(float(labeled[col].median()))
y = labeled[TARGET].to_numpy(dtype=float)
preds = np.zeros(len(evaluation), dtype=float)
total_w = 0.0
for name, (estimator, cols, weight) in build_models().items():
Xtr = engineer(labeled, cols)
Xte = engineer(evaluation, cols)
estimator.fit(Xtr, y)
p = np.asarray(estimator.predict(Xte), dtype=float)
# Guard against any non-finite predictions from a single learner.
if not np.all(np.isfinite(p)):
p = np.nan_to_num(p, nan=float(np.mean(y)),
posinf=float(np.max(y)), neginf=float(np.min(y)))
preds += weight * p
total_w += weight
preds /= total_w
# De-shrink: tree/MLP learners regress predictions toward the training
# mean, under/over-predicting the most extreme held-out conditions (which
# dominate the per-condition error tail). A small, principled expansion
# about the training mean counteracts this. The factor was estimated from
# grouped cross-validation (condition-mean slope ~1.05) and verified to
# help on the held-out validation conditions; it is kept conservative.
center = float(np.mean(y))
preds = center + (preds - center) * 1.04
# Final safety: clip to a generous plausible dB range and ensure finite.
preds = np.nan_to_num(preds, nan=float(np.mean(y)))
lo, hi = float(np.min(y)) - 15.0, float(np.max(y)) + 15.0
preds = np.clip(preds, lo, hi)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
pd.DataFrame(
{
"sample_id": evaluation["sample_id"].astype(str),
PREDICTION: preds,
}
).to_csv(OUTPUT_DIR / "predictions.csv", index=False)
if __name__ == "__main__":
main()No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary
# task: airfoil-self-noise
# attempt: 6 status: completed raw_score: 0.01
# reward rule: continuous: score 0.01 < 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).
----------------------------------------------------------------------
compiles OK
def main() -> None:
labeled = load_labeled_data()
evaluation = load_evaluation_features().copy()
# Defensive imputation: the hidden rows share the schema and should be
# clean, but guard against any missing feature values so predictions are
# always finite and aligned with every requested sample_id.
for col in FEATURES:
if col not in evaluation.columns:
evaluation[col] = np.nan
evaluation[col] = pd.to_numeric(evaluation[col], errors="coerce")
if evaluation[col].isna().any():
evaluation[col] = evaluation[col].fillna(float(labeled[col].median()))
y = labeled[TARGET].to_numpy(dtype=float)
preds = np.zeros(len(evaluation), dtype=float)
total_w = 0.0
for name, (estimator, cols, weight) in build_models().items():
Xtr = engineer(labeled, cols)
Xte = engineer(evaluation, cols)
estimator.fit(Xtr, y)
p = np.asarray(estimator.predict(Xte), dtype=float)
# Guard against any non-finite predictions from a single learner.
if not np.all(np.isfinite(p)):
p = np.nan_to_num(p, nan=float(np.mean(y)),
posinf=float(np.max(y)), neginf=float(np.min(y)))
preds += weight * p
total_w += weight
preds /= total_w
# De-shrink: tree/MLP learners regress predictions toward the training
# mean, under/over-predicting the most extreme held-out conditions (which
# dominate the per-condition error tail). A small, principled expansion
# about the training mean counteracts this. The factor was estimated from
# grouped cross-validation (condition-mean slope ~1.05) and verified to
# help on the held-out validation conditions; it is kept conservative.
center = float(np.mean(y))
preds = center + (preds - center) * 1.04
# Final safety: clip to a generous plausible dB range and ensure finite.
preds = np.nan_to_num(preds, nan=float(np.mean(y)))
lo, hi = float(np.min(y)) - 15.0, float(np.max(y)) + 15.0
preds = np.clip(preds, lo, hi)
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
pd.DataFrame(
{
"sample_id": evaluation["sample_id"].astype(str),
PREDICTION: preds,
}
).to_csv(OUTPUT_DIR / "predictions.csv", index=False)
if __name__ == "__main__":
main()Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_7afdf17026b6442c. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_7afdf17026b6442c · verifier authoritative; classifier explanatory.