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 "=== VALIDATION HEAD ===" && head -5 data/validation.csv && echo "=== TEST FEATURES ===" && cat data/test_features.csv && 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 === 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 af_val_0004,4991.52268156,4.79375092775,0.0254026486661,71.338126346,0.000846568200014 af_val_0005,2495.86144742,0.0184944330227,0.0253695232334,39.6589100769,0.000429048074288 af_val_0006,2498.46919499,-0.0221311526004,0.0507598806268,55.4715562727,0.00076139950933 af_val_0007,400.055023138,4.02558783258,0.22886423046,31.7065298471,0.00508038188852 af_val_0008,1252.05696213,-0.033956960167,0.0508312723036,55.4216023091,0.000760162781301 af_val_0009,3153.0620092,-0.0187379247997,0.304951229779,31.7266222719,0.0033172021384 af_val_0010,6291.52560924,4.80686739476,0.0253900657548,71.3076410261,0.000846622561216 af_val_0011,2499.48153179,-0.0283046278268,0.304802654945,31.714108322,0.00332017638188 af_val_0012,3993.42797965,0.00242905842755,0.050739046381,55.519571195,0.00076333790294 af_val_0013,801.236174603,4.8347627987,0.0253800677813,39.628200731,0.000906448464204 af_val_0014,2504.24785431,3.33499766708,0.101486169411,71.2230178916,0.00203328705957 af_val_0015,4005.91664864,8.43054742545,0.050781496492,55.5508996708,0.0054472470328 af_val_0016,5005.51202734,4.76602056341,0.0254146764802,39.5428889537,0.000909692343177 af_val_0017,3154.55089729,12.3017464063,0.101570667133,39.6195959364,0.0407647946879 af_val_0018,3149.80872579,4.77187008506,0.0254243421119,71.3870262412,0.000848159837222 af_val_0019,998.303428181,6.67736699996,0.101654592175,55.5085985719,0.00522649318285 af_val_0020,500.450561494,6.71452065018,0.101720542321,55.5692532656,0.00520929310826 af_val_0021,800.787357893,17.4061955392,0.025425336639,39.5642882537,0.017263191882 af_val_0022,2001.20577618,-0.0305587984668,0.0507820242688,55.4501707401,0.000760512564219 af_val_0023,1248.70725388,3.31526608552,0.101606401399,71.1937166404,0.0020275259469 af_val_0024,3155.2828154,9.87155886369,0.152415248344,31.7212258081,0.0253334961215 af_val_0025,314.994039148,12.2780378494,0.101717191597,39.5690644654,0.0408329489427 af_val_0026,798.931879537,6.68878460505,0.101694222023,55.5321482532,0.00522281130998 af_val_0027,998.333264845,12.334777031,0.101554460172,55.5374339048,0.0368430722189 af_val_0028,400.019432519,8.36524539981,0.0507588951123,55.4640708311,0.00543869067677 af_val_0029,3154.19523204,-0.0305966105622,0.152271629543,39.6592773927,0.00192823733416 af_val_0030,629.878625292,17.4184416215,0.0254162514745,39.5795196568,0.0172534371927 af_val_0031,3996.45293142,17.3779773937,0.0253787213084,39 …[truncated]
cd /app && python3 -c "
import pandas as pd, numpy as np
tr = pd.read_csv('data/train.csv')
va = pd.read_csv('data/validation.csv')
print('train shape', tr.shape, 'val shape', va.shape)
print('--- TRAIN describe ---')
print(tr.describe().T)
print('--- target stats train ---')
print(tr['scaled_sound_pressure_level'].describe())
print('--- target stats val ---')
print(va['scaled_sound_pressure_level'].describe())
for c in ['attack_angle','chord_length','free_stream_velocity']:
print(c, 'train unique approx:', tr[c].round(2).nunique(), 'val:', va[c].round(2).nunique())
"
train shape (956, 7) val shape (201, 7)
--- TRAIN describe ---
count ... max
frequency 956.0 ... 20025.582040
attack_angle 956.0 ... 22.234770
chord_length 956.0 ... 0.305164
free_stream_velocity 956.0 ... 71.406950
suction_side_displacement_thickness 956.0 ... 0.058556
scaled_sound_pressure_level 956.0 ... 140.289040
[6 rows x 8 columns]
--- target stats train ---
count 956.000000
mean 125.402453
std 6.704146
min 104.935002
25% 121.083472
50% 126.104163
75% 130.491660
max 140.289040
Name: scaled_sound_pressure_level, dtype: float64
--- target stats val ---
count 201.000000
mean 126.483017
std 6.979769
min 109.788714
25% 122.161017
50% 127.178549
75% 131.746459
max 138.661407
Name: scaled_sound_pressure_level, dtype: float64
attack_angle train unique approx: 182 val: 56
chord_length train unique approx: 7 val: 7
free_stream_velocity train unique approx: 64 val: 52cd /app && python3 -c "
import pandas as pd, numpy as np
tr = pd.read_csv('data/train.csv')
va = pd.read_csv('data/validation.csv')
all_ = pd.concat([tr,va])
print('chord_length sorted unique rounded 3:')
print(np.sort(all_['chord_length'].round(3).unique()))
print('velocity rounded 0:')
print(np.sort(all_['free_stream_velocity'].round(0).unique()))
print('velocity rounded 1 nunique:', all_['free_stream_velocity'].round(1).nunique())
print('attack_angle rounded 0 unique:')
print(np.sort(all_['attack_angle'].round(0).unique()))
print('freq min max:', all_['frequency'].min(), all_['frequency'].max())
print('thickness min max:', all_['suction_side_displacement_thickness'].min(), all_['suction_side_displacement_thickness'].max())
"
chord_length sorted unique rounded 3: [0.025 0.051 0.101 0.102 0.152 0.153 0.228 0.229 0.304 0.305] velocity rounded 0: [32. 40. 55. 56. 71.] velocity rounded 1 nunique: 10 attack_angle rounded 0 unique: [ 0. 1. 2. 3. 4. 5. 7. 8. 9. 10. 11. 12. 13. 15. 16. 17. 20. 22.] freq min max: 199.660257517 20025.5820397 thickness min max: 0.000399693758684 0.0585560895273
cd /app && python3 -c "
import sklearn; print('sklearn', sklearn.__version__)
import numpy, pandas, scipy; print('numpy', numpy.__version__, 'pandas', pandas.__version__, 'scipy', scipy.__version__)
for m in ['xgboost','lightgbm','catboost','torch','tensorflow']:
try:
mod=__import__(m); print(m, getattr(mod,'__version__','?'))
except Exception as e:
print(m, 'NOT available')
"
sklearn 1.4.2 numpy 1.26.4 pandas 2.2.2 scipy 1.17.1 xgboost NOT available lightgbm NOT available catboost NOT available torch NOT available tensorflow NOT available
cd /app && python3 -c "
import pandas as pd, numpy as np
tr = pd.read_csv('data/train.csv'); va = pd.read_csv('data/validation.csv')
tf = pd.read_csv('data/test_features.csv')
# Is placeholder == validation features?
vm = va[tf.columns].reset_index(drop=True)
print('placeholder equals first 40 val rows:', np.allclose(vm.iloc[:40,1:].values, tf.iloc[:,1:].values))
print('ids match:', list(tf.sample_id[:3]), list(va.sample_id[:3]))
all_=pd.concat([tr,va])
ang=np.sort(all_['attack_angle'].values)
# cluster angles
clusters=[]; cur=[ang[0]]
for a in ang[1:]:
if a-cur[-1]>0.5: clusters.append(cur); cur=[a]
else: cur.append(a)
clusters.append(cur)
print('num angle clusters:', len(clusters))
print('angle cluster centers:', [round(np.mean(c),2) for c in clusters])
print('angle cluster spread(max-min):', [round(max(c)-min(c),3) for c in clusters][:20])
"
placeholder equals first 40 val rows: True ids match: ['af_val_0000', 'af_val_0001', 'af_val_0002'] ['af_val_0000', 'af_val_0001', 'af_val_0002'] num angle clusters: 14 angle cluster centers: [-0.0, 1.82, 3.19, 4.04, 5.04, 7.1, 8.64, 9.72, 11.2, 12.48, 15.48, 17.4, 19.69, 22.2] angle cluster spread(max-min): [0.07, 0.57, 0.37, 0.27, 0.67, 0.67, 0.57, 0.47, 0.07, 0.47, 0.27, 0.07, 0.063, 0.067]
cd /app && cat > /tmp/cv_harness.py << 'EOF'
import pandas as pd, numpy as np
from sklearn.model_selection import GroupKFold
CHORDS=np.array([0.0254,0.0508,0.1016,0.1524,0.2286,0.3048])
VELS=np.array([31.7,39.6,55.5,71.3])
ANGLES=np.array([0.0,1.5,3.0,4.0,5.3,6.7,7.3,8.9,9.9,11.0,12.3,12.6,15.4,15.6,17.4,19.7,22.2])
def snap(v, grid):
return grid[np.argmin(np.abs(grid[None,:]-np.asarray(v)[:,None]),axis=1)]
def condition_key(df):
c=snap(df['chord_length'].values,CHORDS)
u=snap(df['free_stream_velocity'].values,VELS)
a=snap(df['attack_angle'].values,ANGLES)
return pd.Series([f"{ai:.1f}_{ci:.4f}_{ui:.1f}" for ai,ci,ui in zip(a,c,u)],index=df.index)
def metrics(y_true, y_pred, cond):
y_true=np.asarray(y_true); y_pred=np.asarray(y_pred)
err=y_pred-y_true
rmse=np.sqrt(np.mean(err**2)); mae=np.mean(np.abs(err))
dfm=pd.DataFrame({'c':np.asarray(cond),'e':err})
cr=dfm.groupby('c')['e'].apply(lambda e: np.sqrt(np.mean(e**2)))
return dict(rmse=rmse,mae=mae,p90=np.percentile(cr,90),p95=np.percentile(cr,95),maxc=cr.max(),ncond=len(cr))
def group_cv(make_model, X, y, groups, cond, n_splits=5, seed=0):
gkf=GroupKFold(n_splits=n_splits)
oof=np.zeros(len(y))
for tri,tei in gkf.split(X,y,groups):
m=make_model()
m.fit(X.iloc[tri],y.iloc[tri])
oof[tei]=m.predict(X.iloc[tei])
return metrics(y,oof,cond), oof
EOF
echo "harness written"harness written
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *
import pandas as pd, numpy as np
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
all_=pd.concat([tr,va],ignore_index=True)
# check snapping residuals
c=snap(all_['chord_length'].values,CHORDS); u=snap(all_['free_stream_velocity'].values,VELS); a=snap(all_['attack_angle'].values,ANGLES)
print('max chord snap resid %:', np.max(np.abs(c-all_.chord_length)/c)*100)
print('max vel snap resid %:', np.max(np.abs(u-all_.free_stream_velocity)/u)*100)
print('max angle snap resid (deg):', np.max(np.abs(a-all_.attack_angle)))
key=condition_key(all_)
print('num conditions total:', key.nunique(), 'rows:', len(all_))
print('rows per cond: min/median/max', key.value_counts().min(), key.value_counts().median(), key.value_counts().max())
# overlap train/val conditions
ktr=condition_key(tr); kva=condition_key(va)
print('train conds:', ktr.nunique(), 'val conds:', kva.nunique(), 'overlap:', len(set(ktr)&set(kva)))
"
max chord snap resid %: 0.11999999980314066 max vel snap resid %: 0.149999966919185 max angle snap resid (deg): 0.5349999137200001 num conditions total: 82 rows: 1157 rows per cond: min/median/max 8 14.0 18 train conds: 67 val conds: 15 overlap: 0
cd /app && cat > /tmp/features.py << 'EOF'
import numpy as np, pandas as pd
RAW=["frequency","attack_angle","chord_length","free_stream_velocity","suction_side_displacement_thickness"]
def make_features(df):
f=df['frequency'].astype(float); a=df['attack_angle'].astype(float)
c=df['chord_length'].astype(float); u=df['free_stream_velocity'].astype(float)
d=df['suction_side_displacement_thickness'].astype(float)
o=pd.DataFrame(index=df.index)
lf=np.log10(f); ld=np.log10(d); lc=np.log10(c); lu=np.log10(u)
o['log_freq']=lf; o['attack_angle']=a; o['log_chord']=lc; o['log_vel']=lu; o['log_thick']=ld
# physics: Strouhal number St = f*delta/U ; Reynolds ~ c*U
o['log_strouhal']=np.log10(f*d/u)
o['log_reynolds']=np.log10(c*u)
o['log_reynolds_d']=np.log10(d*u)
# interactions
o['lf_angle']=lf*a
o['lf_logthick']=lf*ld
o['lf_logchord']=lf*lc
o['lf_logvel']=lf*lu
o['angle_logthick']=a*ld
o['angle_logchord']=a*lc
o['lf2']=lf*lf
o['angle2']=a*a
o['raw_freq']=f
return o
def Xy(df):
return make_features(df), df['scaled_sound_pressure_level']
EOF
python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *; from features import *
import pandas as pd, numpy as np
from sklearn.linear_model import Ridge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
Xtr,ytr=Xy(tr); Xva,yva=Xy(va); condva=condition_key(va)
m=make_pipeline(StandardScaler(),Ridge(alpha=10.0)).fit(Xtr,ytr)
print('Ridge(phys feats) val:',{k:round(v,3) for k,v in metrics(yva,m.predict(Xva),condva).items()})
"
Ridge(phys feats) val: {'rmse': 4.727, 'mae': 3.56, 'p90': 6.702, 'p95': 7.609, 'maxc': 8.486, 'ncond': 15}cd /app && cat > /tmp/bench.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *; from features import *
import pandas as pd, numpy as np
from sklearn.ensemble import HistGradientBoostingRegressor, RandomForestRegressor, ExtraTreesRegressor, GradientBoostingRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
Xtr,ytr=Xy(tr); Xva,yva=Xy(va); condva=condition_key(va)
alld=pd.concat([tr,va],ignore_index=True)
Xall,yall=Xy(alld); condall=condition_key(alld); groups=condall.values
def ev(name, make):
m=make(); m.fit(Xtr,ytr)
mv=metrics(yva,m.predict(Xva),condva)
cvm,_=group_cv(make,Xall,yall,groups,condall,n_splits=5)
print(f"{name:28s} VAL rmse={mv['rmse']:.3f} mae={mv['mae']:.3f} p90={mv['p90']:.2f} p95={mv['p95']:.2f} max={mv['maxc']:.2f} | CV rmse={cvm['rmse']:.3f} mae={cvm['mae']:.3f} p90={cvm['p90']:.2f} p95={cvm['p95']:.2f} max={cvm['maxc']:.2f}")
ev('HGB default', lambda: HistGradientBoostingRegressor(random_state=0))
ev('HGB l2=1,lr.05,leaf31', lambda: HistGradientBoostingRegressor(max_iter=600,learning_rate=0.05,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=20,random_state=0))
ev('RF 500', lambda: RandomForestRegressor(n_estimators=500,n_jobs=-1,random_state=0))
ev('ExtraTrees 500', lambda: ExtraTreesRegressor(n_estimators=500,n_jobs=-1,random_state=0))
ev('KNN10', lambda: make_pipeline(StandardScaler(),KNeighborsRegressor(n_neighbors=10,weights='distance')))
ev('SVR rbf', lambda: make_pipeline(StandardScaler(),SVR(C=20,gamma='scale',epsilon=0.1)))
ev('MLP', lambda: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),max_iter=2000,alpha=1e-3,random_state=0)))
EOF
python3 /tmp/bench.pyHGB default VAL rmse=2.603 mae=2.052 p90=3.39 p95=3.82 max=4.53 | CV rmse=2.665 mae=1.964 p90=4.01 p95=4.74 max=6.50 HGB l2=1,lr.05,leaf31 VAL rmse=2.552 mae=1.991 p90=3.48 p95=3.83 max=4.39 | CV rmse=2.572 mae=1.879 p90=3.85 p95=4.42 max=5.77 RF 500 VAL rmse=2.795 mae=2.219 p90=3.80 p95=3.93 max=4.00 | CV rmse=2.668 mae=2.009 p90=3.92 p95=4.32 max=5.29 ExtraTrees 500 VAL rmse=2.282 mae=1.802 p90=3.08 p95=3.33 max=3.64 | CV rmse=2.395 mae=1.783 p90=3.53 p95=3.93 max=5.71 KNN10 VAL rmse=3.187 mae=2.436 p90=4.29 p95=4.84 max=5.62 | CV rmse=3.239 mae=2.455 p90=4.92 p95=5.54 max=6.88 SVR rbf VAL rmse=2.619 mae=1.892 p90=3.92 p95=4.16 max=4.32 | CV rmse=2.694 mae=1.902 p90=3.79 p95=4.57 max=8.63 MLP VAL rmse=2.717 mae=2.167 p90=3.91 p95=4.03 max=4.18 | CV rmse=2.868 mae=2.186 p90=4.10 p95=4.77 max=6.50
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *
import pandas as pd, numpy as np
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
key=condition_key(alld)
alld['k']=key; alld['lf']=np.log10(alld.frequency)
res=[]
for k,g in alld.groupby('k'):
if len(g)<6: continue
x=g['lf'].values; y=g['scaled_sound_pressure_level'].values
# fit cubic poly in log-frequency
deg=min(4,len(g)-2)
co=np.polyfit(x,y,deg); yp=np.polyval(co,x)
res.append(y-yp)
res=np.concatenate(res)
print('within-condition residual std (deg<=4 fit): %.3f'%res.std())
print('within-condition residual RMSE: %.3f'%np.sqrt(np.mean(res**2)))
print('mean abs: %.3f'%np.mean(np.abs(res)))
"
within-condition residual std (deg<=4 fit): 1.014 within-condition residual RMSE: 1.014 mean abs: 0.672
cd /app && cat > /tmp/bench2.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *; from features import *
import pandas as pd, numpy as np
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor, RandomForestRegressor
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler, SplineTransformer
from sklearn.linear_model import Ridge
from sklearn.base import BaseEstimator, RegressorMixin, clone
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
Xtr,ytr=Xy(tr); Xva,yva=Xy(va); condva=condition_key(va)
alld=pd.concat([tr,va],ignore_index=True)
Xall,yall=Xy(alld); condall=condition_key(alld); groups=condall.values
class Avg(BaseEstimator,RegressorMixin):
def __init__(self, models, weights=None):
self.models=models; self.weights=weights
def fit(self,X,y):
self.fitted_=[clone(m).fit(X,y) for m in self.models]; return self
def predict(self,X):
P=np.column_stack([m.predict(X) for m in self.fitted_])
w=np.ones(P.shape[1]) if self.weights is None else np.array(self.weights)
return P@w/w.sum()
def ev(name, make):
m=make(); m.fit(Xtr,ytr)
mv=metrics(yva,m.predict(Xva),condva)
cvm,_=group_cv(make,Xall,yall,groups,condall,n_splits=5)
print(f"{name:26s} VAL rmse={mv['rmse']:.3f} mae={mv['mae']:.3f} p90={mv['p90']:.2f} p95={mv['p95']:.2f} max={mv['maxc']:.2f} | CV rmse={cvm['rmse']:.3f} mae={cvm['mae']:.3f} p90={cvm['p90']:.2f} p95={cvm['p95']:.2f} max={cvm['maxc']:.2f}")
et=lambda: ExtraTreesRegressor(n_estimators=800,min_samples_leaf=1,max_features=0.6,n_jobs=-1,random_state=0)
hgb=lambda: HistGradientBoostingRegressor(max_iter=800,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0)
svr=lambda: make_pipeline(StandardScaler(),SVR(C=30,gamma='scale',epsilon=0.05))
mlp=lambda: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),max_iter=3000,alpha=1e-3,random_state=0))
spl=lambda: make_pipeline(StandardScaler(),SplineTransformer(n_knots=5,degree=3),Ridge(alpha=1.0))
ev('ET tuned', et)
ev('HGB tuned', hgb)
ev('SVR tuned', svr)
ev('Spline-Ridge', spl)
ev('ENS ET+HGB', lambda: Avg([et(),hgb()]))
ev('ENS ET+HGB+SVR', lambda: Avg([et(),hgb(),svr()]))
ev('ENS ET+HGB+SVR+MLP', lambda: Avg([et(),hgb(),svr(),mlp()]))
ev('ENS ET2+HGB+SVR', lambda: Avg([et(),hgb(),svr()],weights=[2,1,1]))
EOF
python3 /tmp/bench2.pyET tuned VAL rmse=2.312 mae=1.811 p90=2.97 p95=3.21 max=3.71 | CV rmse=2.458 mae=1.830 p90=3.45 p95=4.08 max=5.92 HGB tuned VAL rmse=2.664 mae=2.063 p90=3.39 p95=3.88 max=4.91 | CV rmse=2.586 mae=1.900 p90=3.83 p95=4.37 max=6.43 SVR tuned VAL rmse=2.650 mae=1.902 p90=4.09 p95=4.31 max=4.34 | CV rmse=2.721 mae=1.911 p90=3.77 p95=4.50 max=8.36 Spline-Ridge VAL rmse=3.035 mae=2.360 p90=4.17 p95=4.52 max=5.09 | CV rmse=3.104 mae=2.328 p90=4.42 p95=5.07 max=6.50 ENS ET+HGB VAL rmse=2.356 mae=1.879 p90=3.25 p95=3.48 max=3.56 | CV rmse=2.409 mae=1.772 p90=3.49 p95=3.86 max=5.56 ENS ET+HGB+SVR VAL rmse=2.312 mae=1.808 p90=3.31 p95=3.50 max=3.68 | CV rmse=2.272 mae=1.644 p90=3.32 p95=3.87 max=5.90 ENS ET+HGB+SVR+MLP VAL rmse=2.284 mae=1.811 p90=3.25 p95=3.45 max=3.54 | CV rmse=2.209 mae=1.620 p90=3.25 p95=3.79 max=5.04 ENS ET2+HGB+SVR VAL rmse=2.279 mae=1.789 p90=3.29 p95=3.45 max=3.48 | CV rmse=2.274 mae=1.660 p90=3.27 p95=3.84 max=5.43
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *; from features import *
import pandas as pd, numpy as np
from sklearn.ensemble import ExtraTreesRegressor
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
Xtr,ytr=Xy(tr); Xva,yva=Xy(va); condva=condition_key(va)
m=ExtraTreesRegressor(n_estimators=800,max_features=0.6,n_jobs=-1,random_state=0).fit(Xtr,ytr)
pred=m.predict(Xva)
va['err']=pred-yva; va['k']=condva.values
cr=va.groupby('k').apply(lambda g: pd.Series({'rmse':np.sqrt(np.mean(g.err**2)),'bias':g.err.mean(),'n':len(g),
'angle':g.attack_angle.mean(),'chord':g.chord_length.mean(),'vel':g.free_stream_velocity.mean()}))
print(cr.sort_values('rmse',ascending=False).round(2).to_string())
"
rmse bias n angle chord vel k 17.4_0.0254_39.6 3.71 0.04 15.0 17.40 0.03 39.58 0.0_0.0254_71.3 3.00 -0.18 10.0 0.02 0.03 71.35 12.3_0.1016_55.5 2.93 -1.90 16.0 12.30 0.10 55.51 12.3_0.1016_39.6 2.75 -2.50 16.0 12.30 0.10 39.58 6.7_0.1016_55.5 2.70 -1.53 8.0 6.68 0.10 55.50 0.0_0.3048_31.7 2.48 1.96 18.0 0.00 0.30 31.70 8.9_0.0508_55.5 2.47 -0.08 12.0 8.40 0.05 55.54 5.3_0.0254_39.6 2.32 0.98 14.0 4.80 0.03 39.58 4.0_0.2286_31.7 1.83 -0.93 15.0 4.00 0.23 31.68 0.0_0.0508_55.5 1.71 -0.20 13.0 -0.00 0.05 55.50 5.3_0.0254_71.3 1.69 -0.87 11.0 4.80 0.03 71.30 0.0_0.0254_39.6 1.59 0.13 11.0 -0.01 0.03 39.63 0.0_0.1524_39.6 1.22 0.26 14.0 -0.01 0.15 39.62 3.0_0.1016_71.3 1.12 0.16 12.0 3.31 0.10 71.26 9.9_0.1524_31.7 1.09 0.04 16.0 9.90 0.15 31.71 [stderr] <string>:11: DeprecationWarning: DataFrameGroupBy.apply operated on the grouping columns. This behavior is deprecated, and in a future version of pandas the grouping columns will be excluded from the operation. Either pass `include_groups=False` to exclude the groupings or explicitly select the grouping columns after groupby to silence this warning.
cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *; from features import *
import pandas as pd, numpy as np
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.model_selection import KFold
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
Xall,yall=Xy(alld); condall=condition_key(alld)
# Random KFold (interpolation: conditions seen)
kf=KFold(5,shuffle=True,random_state=0); oof=np.zeros(len(yall))
for tri,tei in kf.split(Xall):
m=ExtraTreesRegressor(n_estimators=600,max_features=0.6,n_jobs=-1,random_state=0).fit(Xall.iloc[tri],yall.iloc[tri])
oof[tei]=m.predict(Xall.iloc[tei])
print('RANDOM KFold (interpolation):',{k:round(v,3) for k,v in metrics(yall,oof,condall).items()})
cvm,_=group_cv(lambda:ExtraTreesRegressor(n_estimators=600,max_features=0.6,n_jobs=-1,random_state=0),Xall,yall,condall.values,condall)
print('GROUP KFold (extrapolation):',{k:round(v,3) for k,v in cvm.items()})
"
RANDOM KFold (interpolation): {'rmse': 1.514, 'mae': 1.076, 'p90': 2.218, 'p95': 2.693, 'maxc': 3.637, 'ncond': 82}
GROUP KFold (extrapolation): {'rmse': 2.463, 'mae': 1.831, 'p90': 3.455, 'p95': 4.098, 'maxc': 5.99, 'ncond': 82}cd /app && python3 -c "
import pandas as pd, numpy as np
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
f=alld.frequency; a=alld.attack_angle; c=alld.chord_length; u=alld.free_stream_velocity; d=alld.suction_side_displacement_thickness
y=alld.scaled_sound_pressure_level
St=f*d/u
# BPM-ish base amplitude
base=10*np.log10(d)+50*np.log10(u/340.0)
resid=y-base
import numpy as np
# correlation of resid with log St
lSt=np.log10(St)
print('corr(y, 10log d):', np.corrcoef(y,10*np.log10(d))[0,1].round(3))
print('corr(y, 50log u):', np.corrcoef(y,50*np.log10(u))[0,1].round(3))
print('corr(resid, logSt):', np.corrcoef(resid,lSt)[0,1].round(3))
# fit resid ~ poly(logSt) and check
for deg in [2,3,4,5]:
co=np.polyfit(lSt,resid,deg); r=resid-np.polyval(co,lSt)
print(f'resid~poly(logSt) deg{deg}: R2={1-r.var()/resid.var():.3f} rmse={np.sqrt((r**2).mean()):.3f}')
# full: y ~ poly(logSt) deg5 + linear(10logd,50logu,angle,logc)
import numpy as np
from numpy.linalg import lstsq
X=np.column_stack([np.ones_like(y)]+[lSt**k for k in range(1,6)]+[np.log10(d),np.log10(u),a,np.log10(c)])
coef,_,_,_=lstsq(X,y,rcond=None); yp=X@coef; r=y-yp
print('y~poly5(logSt)+phys linear: rmse=%.3f mae=%.3f R2=%.3f'%(np.sqrt((r**2).mean()),np.abs(r).mean(),1-r.var()/y.var()))
"
corr(y, 10log d): -0.348 corr(y, 50log u): 0.085 corr(resid, logSt): -0.56 resid~poly(logSt) deg2: R2=0.346 rmse=9.691 resid~poly(logSt) deg3: R2=0.353 rmse=9.641 resid~poly(logSt) deg4: R2=0.353 rmse=9.639 resid~poly(logSt) deg5: R2=0.354 rmse=9.634 y~poly5(logSt)+phys linear: rmse=4.476 mae=3.372 R2=0.562
cd /app && cat > /tmp/ens_oof.py << 'EOF'
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *; from features import *
import pandas as pd, numpy as np
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
Xall,yall=Xy(alld); condall=condition_key(alld); groups=condall.values
def makers():
return [
('ET',lambda: ExtraTreesRegressor(n_estimators=800,max_features=0.6,n_jobs=-1,random_state=0)),
('HGB',lambda: HistGradientBoostingRegressor(max_iter=800,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0)),
('SVR',lambda: make_pipeline(StandardScaler(),SVR(C=30,gamma='scale',epsilon=0.05))),
('MLP',lambda: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),max_iter=3000,alpha=1e-3,random_state=0))),
]
gkf=GroupKFold(5)
oofs={n:np.zeros(len(yall)) for n,_ in makers()}
for tri,tei in gkf.split(Xall,yall,groups):
for n,mk in makers():
m=mk().fit(Xall.iloc[tri],yall.iloc[tri]); oofs[n][tei]=m.predict(Xall.iloc[tei])
ens=np.mean([oofs[n] for n,_ in makers()],axis=0)
m=metrics(yall,ens,condall)
print('ENSEMBLE group CV:',{k:round(v,3) for k,v in m.items()})
# per-condition rmse distribution
dfm=pd.DataFrame({'c':condall.values,'e':ens-yall.values})
cr=dfm.groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2))).sort_values()
print('num conditions:',len(cr))
print('frac <=2.35:',(cr<=2.35).mean().round(3))
print('count in (2.35,3.5]:',((cr>2.35)&(cr<=3.5)).sum())
print('worst 12:'); print(cr.tail(12).round(2).to_string())
np.save('/tmp/oof_ens.npy',ens); np.save('/tmp/yall.npy',yall.values)
EOF
python3 /tmp/ens_oof.pyENSEMBLE group CV: {'rmse': 2.209, 'mae': 1.62, 'p90': 3.252, 'p95': 3.791, 'maxc': 5.036, 'ncond': 82}
num conditions: 82
frac <=2.35: 0.72
count in (2.35,3.5]: 16
worst 12:
c
8.9_0.0508_55.5 2.82
8.9_0.1016_71.3 2.97
4.0_0.0508_71.3 3.10
12.6_0.1524_71.3 3.27
15.6_0.1016_71.3 3.28
12.3_0.1016_71.3 3.55
17.4_0.0254_31.7 3.56
17.4_0.0254_39.6 3.80
7.3_0.2286_71.3 3.92
19.7_0.0508_71.3 4.14
22.2_0.0254_39.6 4.91
12.6_0.1524_39.6 5.04cd /app && cat > /tmp/features2.py << 'EOF'
import numpy as np, pandas as pd
def make_features(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)
nu=1.5e-5
o={}
o['log_freq']=lf; o['attack_angle']=a; o['log_chord']=lc; o['log_vel']=lu; o['log_thick']=ld
o['log_St_d']=np.log10(f*d/u) # displacement-thickness Strouhal
o['log_St_c']=np.log10(f*c/u) # chord Strouhal
o['log_Re_c']=np.log10(c*u/nu) # chord Reynolds
o['log_Re_d']=np.log10(d*u/nu) # thickness Reynolds
o['mach']=u/340.0
o['d_over_c']=d/c # relative BL thickness
o['log_d_over_c']=np.log10(d/c)
# interactions
o['lf_angle']=lf*a; o['lf_ld']=lf*ld; o['lf_lc']=lf*lc; o['lf_lu']=lf*lu
o['a_ld']=a*ld; o['a_lc']=a*lc; o['a_lu']=a*lu
o['lf2']=lf*lf; o['angle2']=a*a; o['ld2']=ld*ld
o['raw_freq']=f; o['raw_thick']=d
o['sin_a']=np.sin(np.radians(a))
return pd.DataFrame(o,index=df.index)
def Xy(df):
return make_features(df), df['scaled_sound_pressure_level']
EOF
python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *
import features2 as F
import pandas as pd, numpy as np
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
Xall,yall=F.Xy(alld); condall=condition_key(alld); groups=condall.values
makers=[
lambda: ExtraTreesRegressor(n_estimators=800,max_features=0.6,n_jobs=-1,random_state=0),
lambda: HistGradientBoostingRegressor(max_iter=800,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0),
lambda: make_pipeline(StandardScaler(),SVR(C=30,gamma='scale',epsilon=0.05)),
lambda: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),max_iter=3000,alpha=1e-3,random_state=0)),
]
gkf=GroupKFold(5); oofs=[np.zeros(len(yall)) for _ in makers]
for tri,tei in gkf.split(Xall,yall,groups):
for i,mk in enumerate(makers):
oofs[i][tei]=mk().fit(Xall.iloc[tri],yall.iloc[tri]).predict(Xall.iloc[tei])
ens=np.mean(oofs,axis=0)
m=metrics(yall,ens,condall)
print('ENS richfeat group CV:',{k:round(v,3) for k,v in m.items()})
cr=pd.DataFrame({'c':condall.values,'e':ens-yall.values}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print('frac<=2.35:',(cr<=2.35).mean().round(3))
"
ENS richfeat group CV: {'rmse': 2.176, 'mae': 1.594, 'p90': 3.352, 'p95': 3.646, 'maxc': 4.928, 'ncond': 82}
frac<=2.35: 0.744cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp')
from cv_harness import *
import pandas as pd, numpy as np
ens=np.load('/tmp/oof_ens.npy'); yall=np.load('/tmp/yall.npy')
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
cond=condition_key(alld).values; lf=np.log10(alld.frequency.values)
print('RAW ENS:',{k:round(v,3) for k,v in metrics(yall,ens,cond).items()})
def smooth(pred, deg):
out=pred.copy()
for c in np.unique(cond):
idx=np.where(cond==c)[0]
if len(idx)<deg+2: continue
x=lf[idx]; y=pred[idx]; co=np.polyfit(x,y,min(deg,len(idx)-1)); out[idx]=np.polyval(co,x)
return out
for deg in [2,3,4,5]:
sm=smooth(ens,deg)
m=metrics(yall,sm,cond)
cr=pd.DataFrame({'c':cond,'e':sm-yall}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print(f'SMOOTH deg{deg}:',{k:round(v,3) for k,v in m.items()},'frac<=2.35:',round((cr<=2.35).mean(),3))
"
RAW ENS: {'rmse': 2.209, 'mae': 1.62, 'p90': 3.252, 'p95': 3.791, 'maxc': 5.036, 'ncond': 82}
SMOOTH deg2: {'rmse': 2.43, 'mae': 1.828, 'p90': 3.353, 'p95': 4.129, 'maxc': 5.135, 'ncond': 82} frac<=2.35: 0.622
SMOOTH deg3: {'rmse': 2.299, 'mae': 1.688, 'p90': 3.191, 'p95': 4.153, 'maxc': 5.276, 'ncond': 82} frac<=2.35: 0.695
SMOOTH deg4: {'rmse': 2.26, 'mae': 1.657, 'p90': 3.252, 'p95': 3.937, 'maxc': 5.102, 'ncond': 82} frac<=2.35: 0.707
SMOOTH deg5: {'rmse': 2.235, 'mae': 1.64, 'p90': 3.227, 'p95': 3.933, 'maxc': 5.042, 'ncond': 82} frac<=2.35: 0.72cd /app && python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import features2 as F
import pandas as pd, numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.kernel_ridge import KernelRidge
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import RBF, WhiteKernel, ConstantKernel, DotProduct, Matern
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
Xall,yall=F.Xy(alld); condall=condition_key(alld); groups=condall.values
def ev(name,make):
cvm,_=group_cv(make,Xall,yall,groups,condall,5)
print(f'{name:22s} CV rmse={cvm[\"rmse\"]:.3f} mae={cvm[\"mae\"]:.3f} p90={cvm[\"p90\"]:.2f} p95={cvm[\"p95\"]:.2f} max={cvm[\"maxc\"]:.2f}')
ev('KernelRidge rbf', lambda: make_pipeline(StandardScaler(),KernelRidge(alpha=0.1,kernel='rbf',gamma=0.05)))
ev('KernelRidge rbf2', lambda: make_pipeline(StandardScaler(),KernelRidge(alpha=0.3,kernel='rbf',gamma=0.1)))
k=ConstantKernel(1.0)*Matern(length_scale=np.ones(Xall.shape[1]),nu=2.5)+WhiteKernel(0.5)
ev('GP Matern-ARD', lambda: make_pipeline(StandardScaler(),GaussianProcessRegressor(kernel=k,alpha=1e-6,normalize_y=True,n_restarts_optimizer=0)))
ev('RF tuned', lambda: RandomForestRegressor(n_estimators=600,max_features=0.5,min_samples_leaf=2,n_jobs=-1,random_state=0))
ev('GB', lambda: GradientBoostingRegressor(n_estimators=500,learning_rate=0.03,max_depth=3,subsample=0.8,random_state=0))
"
[stderr] KernelRidge rbf CV rmse=8.678 mae=4.679 p90=9.16 p95=15.07 max=53.81 KernelRidge rbf2 CV rmse=16.588 mae=9.835 p90=18.77 p95=35.88 max=83.77Shell command timed out. Tool interaction exceeded 300 seconds.
restart: true
<system>tool has been restarted.</system>
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import features2 as F
import pandas as pd, numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.kernel_ridge import KernelRidge
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
Xall,yall=F.Xy(alld); condall=condition_key(alld); groups=condall.values
def ev(name,make):
cvm,_=group_cv(make,Xall,yall,groups,condall,5)
print(f'{name:22s} CV rmse={cvm[\"rmse\"]:.3f} mae={cvm[\"mae\"]:.3f} p90={cvm[\"p90\"]:.2f} p95={cvm[\"p95\"]:.2f} max={cvm[\"maxc\"]:.2f}')
for g,a in [(0.01,0.1),(0.02,0.5),(0.03,1.0),(0.05,2.0)]:
ev(f'KRR g{g} a{a}', lambda g=g,a=a: make_pipeline(StandardScaler(),KernelRidge(alpha=a,kernel='rbf',gamma=g)))
"
KRR g0.01 a0.1 CV rmse=3.573 mae=2.423 p90=4.28 p95=5.23 max=18.40 KRR g0.02 a0.5 CV rmse=5.631 mae=3.602 p90=6.85 p95=8.99 max=30.92 KRR g0.03 a1.0 CV rmse=8.051 mae=5.160 p90=9.61 p95=14.33 max=41.72 KRR g0.05 a2.0 CV rmse=13.062 mae=8.558 p90=17.76 p95=26.36 max=59.08
cd /app && cat > /tmp/make_oof.py << 'EOF'
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import features2 as F
import pandas as pd, numpy as np
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor, RandomForestRegressor, GradientBoostingRegressor
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
Xall,yall=F.Xy(alld); cond=condition_key(alld); groups=cond.values
Xnr=Xall.drop(columns=['raw_freq','raw_thick'])
learners={
'ET': (Xall, lambda: ExtraTreesRegressor(n_estimators=800,max_features=0.6,n_jobs=-1,random_state=0)),
'ETnr': (Xnr, lambda: ExtraTreesRegressor(n_estimators=800,max_features=0.6,n_jobs=-1,random_state=1)),
'HGB': (Xall, lambda: HistGradientBoostingRegressor(max_iter=800,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0)),
'RF': (Xall, lambda: RandomForestRegressor(n_estimators=600,max_features=0.5,min_samples_leaf=2,n_jobs=-1,random_state=0)),
'GB': (Xnr, lambda: GradientBoostingRegressor(n_estimators=500,learning_rate=0.03,max_depth=3,subsample=0.8,random_state=0)),
'SVR': (Xnr, lambda: make_pipeline(StandardScaler(),SVR(C=30,gamma='scale',epsilon=0.05))),
'MLP0': (Xnr, lambda: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),max_iter=3000,alpha=1e-3,random_state=0))),
'MLP1': (Xnr, lambda: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(200,100),max_iter=3000,alpha=3e-3,random_state=1))),
'MLP2': (Xnr, lambda: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),max_iter=3000,alpha=1e-3,random_state=2))),
}
gkf=GroupKFold(5)
splits=list(gkf.split(Xall,yall,groups))
oof={}
for name,(Xd,mk) in learners.items():
p=np.zeros(len(yall))
for tri,tei in splits:
p[tei]=mk().fit(Xd.iloc[tri],yall.iloc[tri]).predict(Xd.iloc[tei])
oof[name]=p
m=metrics(yall,p,cond)
print(f'{name:6s} rmse={m["rmse"]:.3f} mae={m["mae"]:.3f} p90={m["p90"]:.2f} p95={m["p95"]:.2f} max={m["maxc"]:.2f}',flush=True)
np.savez('/tmp/oof_all.npz', y=yall.values, cond=cond.values, **oof)
print('DONE')
EOF
nohup python3 /tmp/make_oof.py > /tmp/oof_log.txt 2>&1 &
echo "started pid $!"started pid 887
sleep 60 && cat /tmp/oof_log.txt
ET rmse=2.369 mae=1.745 p90=3.39 p95=4.02 max=5.44 ETnr rmse=2.407 mae=1.775 p90=3.37 p95=3.99 max=5.63 HGB rmse=2.422 mae=1.774 p90=3.67 p95=4.05 max=5.33
sleep 75 && cat /tmp/oof_log.txt
ET rmse=2.369 mae=1.745 p90=3.39 p95=4.02 max=5.44 ETnr rmse=2.407 mae=1.775 p90=3.37 p95=3.99 max=5.63 HGB rmse=2.422 mae=1.774 p90=3.67 p95=4.05 max=5.33 RF rmse=2.609 mae=1.946 p90=3.88 p95=4.38 max=5.50 GB rmse=2.700 mae=2.006 p90=3.94 p95=4.32 max=8.78 SVR rmse=2.856 mae=1.999 p90=3.93 p95=4.89 max=8.74 MLP0 rmse=3.086 mae=2.281 p90=4.44 p95=4.80 max=9.60 MLP1 rmse=2.800 mae=2.117 p90=3.97 p95=4.48 max=7.54 MLP2 rmse=2.896 mae=2.192 p90=4.19 p95=4.90 max=7.37 DONE
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import pandas as pd, numpy as np
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
cond=condition_key(alld); y=alld['scaled_sound_pressure_level']
f=alld.frequency.values; a=alld.attack_angle.values; c=alld.chord_length.values
u=alld.free_stream_velocity.values; d=alld.suction_side_displacement_thickness.values
lf=np.log10(f); ld=np.log10(d); lc=np.log10(c); lu=np.log10(u)
feats={
'freq,d,U(+St,Re)': np.column_stack([lf,ld,lu,np.log10(f*d/u),np.log10(d*u/1.5e-5),u/340]),
'+angle': np.column_stack([lf,ld,lu,np.log10(f*d/u),np.log10(d*u/1.5e-5),u/340,a]),
'+angle+chord': np.column_stack([lf,ld,lu,lc,np.log10(f*d/u),np.log10(f*c/u),np.log10(d*u/1.5e-5),np.log10(c*u/1.5e-5),u/340,a]),
'no_thick(a,c,U,f)': np.column_stack([lf,a,lc,lu,np.log10(f*c/u),np.log10(c*u/1.5e-5)]),
}
for name,Xm in feats.items():
Xd=pd.DataFrame(Xm)
cvm,_=group_cv(lambda:ExtraTreesRegressor(n_estimators=600,max_features=0.7,n_jobs=-1,random_state=0),Xd,y,cond.values,cond,5)
cr=pd.DataFrame({'c':cond.values,'e':_-y.values}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print(f'ET [{name:22s}] rmse={cvm[\"rmse\"]:.3f} mae={cvm[\"mae\"]:.3f} p90={cvm[\"p90\"]:.2f} p95={cvm[\"p95\"]:.2f} max={cvm[\"maxc\"]:.2f} frac<=2.35={ (cr<=2.35).mean():.2f}')
"
ET [freq,d,U(+St,Re) ] rmse=4.148 mae=3.195 p90=6.22 p95=7.30 max=7.68 frac<=2.35=0.24 ET [+angle ] rmse=2.944 mae=2.315 p90=4.28 p95=4.56 max=4.98 frac<=2.35=0.39 ET [+angle+chord ] rmse=2.398 mae=1.795 p90=3.51 p95=3.93 max=5.52 frac<=2.35=0.62 ET [no_thick(a,c,U,f) ] rmse=2.927 mae=2.191 p90=4.05 p95=5.02 max=7.38 frac<=2.35=0.45
cd /app && cat > /tmp/val_eval.py << 'EOF'
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import features2 as F
import pandas as pd, numpy as np
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor, RandomForestRegressor
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
Xtr,ytr=F.Xy(tr); Xva,yva=F.Xy(va); cva=condition_key(va)
defs={
'ET': lambda: ExtraTreesRegressor(n_estimators=1000,max_features=0.6,n_jobs=-1,random_state=0),
'HGB': lambda: HistGradientBoostingRegressor(max_iter=800,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0),
'RF': lambda: RandomForestRegressor(n_estimators=800,max_features=0.5,min_samples_leaf=2,n_jobs=-1,random_state=0),
'SVR': lambda: make_pipeline(StandardScaler(),SVR(C=30,gamma='scale',epsilon=0.05)),
'MLP': lambda: make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=(128,128,64),max_iter=3000,alpha=1e-3,random_state=0)),
}
P={}
for n,mk in defs.items():
P[n]=mk().fit(Xtr,ytr).predict(Xva)
m=metrics(yva,P[n],cva)
print(f'{n:5s} VAL rmse={m["rmse"]:.3f} mae={m["mae"]:.3f} p90={m["p90"]:.2f} p95={m["p95"]:.2f} max={m["maxc"]:.2f}')
for combo in [['ET','HGB'],['ET','HGB','RF'],['ET','HGB','SVR','MLP'],['ET','HGB','RF','SVR','MLP']]:
pr=np.mean([P[n] for n in combo],axis=0); m=metrics(yva,pr,cva)
print(f'BLEND {"+".join(combo):20s} rmse={m["rmse"]:.3f} mae={m["mae"]:.3f} p90={m["p90"]:.2f} p95={m["p95"]:.2f} max={m["maxc"]:.2f}')
np.savez('/tmp/valP.npz', y=yva.values, cond=cva.values, **P)
EOF
timeout 250 python3 /tmp/val_eval.pyET VAL rmse=2.319 mae=1.791 p90=3.33 p95=3.48 max=3.67 HGB VAL rmse=2.677 mae=1.967 p90=4.08 p95=4.36 max=4.83 RF VAL rmse=2.780 mae=2.177 p90=3.93 p95=4.06 max=4.15 SVR VAL rmse=2.699 mae=1.930 p90=4.21 p95=4.41 max=4.43 MLP VAL rmse=2.475 mae=1.868 p90=3.94 p95=4.13 max=4.15 BLEND ET+HGB rmse=2.403 mae=1.829 p90=3.73 p95=3.89 max=3.91 BLEND ET+HGB+RF rmse=2.495 mae=1.915 p90=3.73 p95=3.83 max=3.96 BLEND ET+HGB+SVR+MLP rmse=2.207 mae=1.670 p90=3.22 p95=3.38 max=3.65 BLEND ET+HGB+RF+SVR+MLP rmse=2.282 mae=1.735 p90=3.34 p95=3.46 max=3.67
cd /app && timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import numpy as np, pandas as pd
d=np.load('/tmp/valP.npz')
y=d['y']; cond=d['cond']
pred=np.mean([d['ET'],d['HGB'],d['SVR'],d['MLP']],axis=0)
err=pred-y
df=pd.DataFrame({'c':cond,'e':err})
rows=[]
for c,g in df.groupby('c'):
e=g.e.values; rmse=np.sqrt(np.mean(e**2)); bias=e.mean(); shape=np.sqrt(np.mean((e-bias)**2))
rows.append((c,len(e),rmse,bias,shape))
r=pd.DataFrame(rows,columns=['c','n','rmse','bias','shape']).sort_values('rmse',ascending=False)
print(r.round(2).to_string(index=False))
print()
print('mean |bias|=%.3f mean shape=%.3f'%(r.bias.abs().mean(),r.shape.mean()))
print('conditions with rmse>2.35: %d of %d'%((r.rmse>2.35).sum(),len(r)))
print('of those, bias-dominated (|bias|>shape):',((r.rmse>2.35)&(r.bias.abs()>r.shape)).sum())
"
[stderr]
Traceback (most recent call last):
File "<string>", line 6, in <module>
File "/usr/local/lib/python3.12/dist-packages/numpy/lib/npyio.py", line 256, in __getitem__
return format.read_array(bytes,
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.12/dist-packages/numpy/lib/format.py", line 795, in read_array
raise ValueError("Object arrays cannot be loaded when "
ValueError: Object arrays cannot be loaded when allow_pickle=Falsecd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd
d=np.load('/tmp/valP.npz',allow_pickle=True)
y=d['y']; cond=d['cond']
pred=np.mean([d['ET'],d['HGB'],d['SVR'],d['MLP']],axis=0)
err=pred-y
df=pd.DataFrame({'c':cond,'e':err})
rows=[]
for c,g in df.groupby('c'):
e=g.e.values; rmse=np.sqrt(np.mean(e**2)); bias=e.mean(); shape=np.sqrt(np.mean((e-bias)**2))
rows.append((c,len(e),rmse,bias,shape))
r=pd.DataFrame(rows,columns=['c','n','rmse','bias','shape']).sort_values('rmse',ascending=False)
print(r.round(2).to_string(index=False))
print('mean |bias|=%.3f mean shape=%.3f'%(r.bias.abs().mean(),r.shape.mean()))
print('conditions with rmse>2.35: %d of %d; bias-dominated:'%((r.rmse>2.35).sum(),len(r)),((r.rmse>2.35)&(r.bias.abs()>r.shape)).sum())
"
c n rmse bias shape 17.4_0.0254_39.6 15 3.65 -0.02 3.65 0.0_0.0254_71.3 10 3.26 -0.63 3.20 12.3_0.1016_55.5 16 3.15 -1.99 2.45 8.9_0.0508_55.5 12 2.91 0.56 2.85 6.7_0.1016_55.5 8 2.75 -1.56 2.27 12.3_0.1016_39.6 16 2.44 -1.83 1.62 4.0_0.2286_31.7 15 1.74 -0.93 1.48 0.0_0.3048_31.7 18 1.67 1.37 0.95 0.0_0.1524_39.6 14 1.60 0.87 1.35 5.3_0.0254_71.3 11 1.56 -1.10 1.10 5.3_0.0254_39.6 14 1.55 0.25 1.53 0.0_0.0254_39.6 11 1.36 0.81 1.09 3.0_0.1016_71.3 12 1.23 0.11 1.23 0.0_0.0508_55.5 13 0.98 0.23 0.95 9.9_0.1524_31.7 16 0.90 -0.26 0.86 [stderr] Traceback (most recent call last): File "<string>", line 14, in <module> AttributeError: 'tuple' object has no attribute 'mean'
cd /app && cat > /tmp/val_eval2.py << 'EOF'
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import features2 as F
import pandas as pd, numpy as np
from sklearn.ensemble import HistGradientBoostingRegressor, ExtraTreesRegressor
from sklearn.svm import SVR
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
Xtr,ytr=F.Xy(tr); Xva,yva=F.Xy(va); cva=condition_key(va)
P={}
P['ET']=ExtraTreesRegressor(n_estimators=1000,max_features=0.6,n_jobs=-1,random_state=0).fit(Xtr,ytr).predict(Xva)
P['HGB']=HistGradientBoostingRegressor(max_iter=800,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0).fit(Xtr,ytr).predict(Xva)
P['SVR']=make_pipeline(StandardScaler(),SVR(C=30,gamma='scale',epsilon=0.05)).fit(Xtr,ytr).predict(Xva)
mlps=[]
for s in range(6):
arch=[(128,128,64),(200,100),(150,150,75),(256,128),(128,96,64,32),(180,120,60)][s]
pr=make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=arch,max_iter=4000,alpha=1e-3,random_state=s,early_stopping=False)).fit(Xtr,ytr).predict(Xva)
mlps.append(pr)
P['MLP']=np.mean(mlps,axis=0)
def show(name,pr):
m=metrics(yva,pr,cva); print(f'{name:24s} rmse={m["rmse"]:.3f} mae={m["mae"]:.3f} p90={m["p90"]:.2f} p95={m["p95"]:.2f} max={m["maxc"]:.2f}')
show('MLPx6',P['MLP'])
for combo in [['ET','HGB','SVR','MLP'],['ET','MLP'],['ET','HGB','MLP'],['ET','SVR','MLP']]:
show('+'.join(combo),np.mean([P[n] for n in combo],axis=0))
# weight search (coarse) over ET,HGB,SVR,MLP minimizing composite
best=None
import itertools
names=['ET','HGB','SVR','MLP']
for ws in itertools.product([0,1,2,3],repeat=4):
if sum(ws)==0: continue
w=np.array(ws)/sum(ws); pr=sum(w[i]*P[names[i]] for i in range(4))
m=metrics(yva,pr,cva)
score=(m['p90']/2.35)+(m['mae']/1.55)+(m['rmse']/2.25)+0.3*(m['maxc']/5.6)
if best is None or score<best[0]: best=(score,ws,m)
print('BEST weights',best[1],{k:round(v,3) for k,v in best[2].items()})
np.savez('/tmp/valP2.npz', y=yva.values, cond=cva.values, **P)
EOF
timeout 280 python3 /tmp/val_eval2.pyMLPx6 rmse=2.219 mae=1.695 p90=3.28 p95=3.61 max=3.85
ET+HGB+SVR+MLP rmse=2.224 mae=1.669 p90=3.44 p95=3.55 max=3.61
ET+MLP rmse=2.159 mae=1.623 p90=3.11 p95=3.27 max=3.48
ET+HGB+MLP rmse=2.196 mae=1.664 p90=3.28 p95=3.43 max=3.64
ET+SVR+MLP rmse=2.236 mae=1.654 p90=3.29 p95=3.39 max=3.62
BEST weights (1, 1, 0, 3) {'rmse': 2.132, 'mae': 1.596, 'p90': 3.014, 'p95': 3.169, 'maxc': 3.528, 'ncond': 15}cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import numpy as np, pandas as pd
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
alld['k']=condition_key(alld).values; alld['lf']=np.log10(alld.frequency)
# for each condition find peak freq (argmax SPL) and shape
rows=[]
for k,g in alld.groupby('k'):
g=g.sort_values('lf')
i=g.scaled_sound_pressure_level.values.argmax()
rows.append((k,len(g),g.lf.values[i],g.scaled_sound_pressure_level.max(),g.scaled_sound_pressure_level.min(),
g.attack_angle.mean(),g.chord_length.mean(),g.free_stream_velocity.mean(),g.suction_side_displacement_thickness.mean()))
r=pd.DataFrame(rows,columns=['k','n','lf_peak','smax','smin','a','c','u','d'])
# how often is peak at the lowest frequency (monotonic decreasing)?
print('fraction peak at lowest freq bin:', (r.groupby('k').head(1).lf_peak.values==[alld[alld.k==k].lf.min() for k in r.k]).mean().round(2) if False else 'skip')
lfmins=alld.groupby('k').lf.min();
r['at_low']= [abs(lp-lfmins[k])<0.05 for k,lp in zip(r.k,r.lf_peak)]
print('frac peak at lowest bin:',r.at_low.mean().round(2))
print('lf_peak range:',r.lf_peak.min().round(2),r.lf_peak.max().round(2))
# regress lf_peak on log features
import numpy as np
X=np.column_stack([np.ones(len(r)),np.log10(r.d),np.log10(r.u),np.log10(r.c),r.a])
b,_,_,_=np.linalg.lstsq(X,r.lf_peak,rcond=None); pr=X@b; resid=r.lf_peak-pr
print('lf_peak regression R2=%.2f resid_std=%.3f'%(1-resid.var()/r.lf_peak.var(),resid.std()))
print('dynamic range (smax-smin) median:',(r.smax-r.smin).median().round(1))
"
fraction peak at lowest freq bin: skip frac peak at lowest bin: 0.0 lf_peak range: 2.4 3.9 lf_peak regression R2=0.89 resid_std=0.120 dynamic range (smax-smin) median: 16.7
cd /app && cat > /tmp/features3.py << 'EOF'
import numpy as np, pandas as pd
import sys; sys.path.insert(0,'/tmp')
from cv_harness import condition_key
def base_feats(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); nu=1.5e-5
o={}
o['log_freq']=lf; o['attack_angle']=a; o['log_chord']=lc; o['log_vel']=lu; o['log_thick']=ld
o['log_St_d']=np.log10(f*d/u); o['log_St_c']=np.log10(f*c/u)
o['log_Re_c']=np.log10(c*u/nu); o['log_Re_d']=np.log10(d*u/nu)
o['mach']=u/340.0; o['d_over_c']=d/c; o['log_d_over_c']=np.log10(d/c)
o['lf_angle']=lf*a; o['lf_ld']=lf*ld; o['lf_lc']=lf*lc; o['lf_lu']=lf*lu
o['a_ld']=a*ld; o['a_lc']=a*lc; o['a_lu']=a*lu
o['lf2']=lf*lf; o['angle2']=a*a; o['ld2']=ld*ld
o['sin_a']=np.sin(np.radians(a))
return pd.DataFrame(o,index=df.index), dict(lf=lf,ld=ld,lc=lc,lu=lu,a=a)
from sklearn.base import BaseEstimator, TransformerMixin
class PeakRel(BaseEstimator, TransformerMixin):
"""Adds peak-relative log-frequency features. Fits a peak-location regression from training labels."""
def fit(self, df, y):
df=df.reset_index(drop=True); y=np.asarray(y)
k=condition_key(df).values
rows=[];
import pandas as pd
g=pd.DataFrame({'k':k,'lf':np.log10(df['frequency'].astype(float).values),'y':y,
'ld':np.log10(df['suction_side_displacement_thickness'].astype(float).values),
'lu':np.log10(df['free_stream_velocity'].astype(float).values),
'lc':np.log10(df['chord_length'].astype(float).values),
'a':df['attack_angle'].astype(float).values})
feats=[]; targ=[]
for key,gg in g.groupby('k'):
if len(gg)<4: continue
# smooth peak: fit quadratic in lf, take vertex within range
co=np.polyfit(gg.lf,gg.y,2)
if co[0]<0:
lfp=-co[1]/(2*co[0]); lfp=min(max(lfp,gg.lf.min()),gg.lf.max())
else:
lfp=gg.lf.values[gg.y.values.argmax()]
feats.append([1,gg.ld.mean(),gg.lu.mean(),gg.lc.mean(),gg.a.mean(),gg.a.mean()**2]); targ.append(lfp)
A=np.array(feats); b=np.array(targ)
self.coef_,_,_,_=np.linalg.lstsq(A,b,rcond=None)
return self
def _predict_peak(self, parts):
A=np.column_stack([np.ones_like(parts['lf']),parts['ld'],parts['lu'],parts['lc'],parts['a'],parts['a']**2])
# use row-level feature means? peak depends on condition-level; row features give same since condition-constant-ish
return A@self.coef_
def transform(self, df):
base,parts=base_feats(df)
lfp=self._predict_peak(parts)
rel=parts['lf']-lfp
base['rel_lf']=rel; base['rel_lf2']=rel*rel; base['rel_lf3']=rel**3
base['rel_pos']=np.maximum(rel,0); base['rel_neg']=np.minimum(rel,0)
return base.values
EOF
echo "written"
timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
from features3 import PeakRel
import pandas as pd, numpy as np
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.pipeline import make_pipeline
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
ytr=tr['scaled_sound_pressure_level']; yva=va['scaled_sound_pressure_level']; cva=condition_key(va)
for name,mk in [('ET',lambda:ExtraTreesRegressor(n_estimators=1000,max_features=0.6,n_jobs=-1,random_state=0)),
('HGB',lambda:HistGradientBoostingRegressor(max_iter=800,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0))]:
pipe=make_pipeline(PeakRel(),mk()); pipe.fit(t
…[truncated]written ET+PeakRel VAL rmse=2.236 mae=1.658 p90=3.41 p95=3.75 max=4.06 HGB+PeakRel VAL rmse=2.389 mae=1.815 p90=3.55 p95=3.92 max=4.38
cd /app && cat > /tmp/cv_final.py << 'EOF'
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import features2 as F
import pandas as pd, numpy as np
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
Xall,yall=F.Xy(alld); cond=condition_key(alld); groups=cond.values
gkf=GroupKFold(5); splits=list(gkf.split(Xall,yall,groups))
def oof(mk,Xd):
p=np.zeros(len(yall))
for tri,tei in splits: p[tei]=mk().fit(Xd.iloc[tri],yall.iloc[tri]).predict(Xd.iloc[tei])
return p
def rep(name,p):
m=metrics(yall,p,cond); cr=pd.DataFrame({'c':groups,'e':p-yall.values}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print(f'{name:26s} rmse={m["rmse"]:.3f} mae={m["mae"]:.3f} p90={m["p90"]:.2f} p95={m["p95"]:.2f} max={m["maxc"]:.2f} frac<=2.35={(cr<=2.35).mean():.2f}',flush=True)
return p
et=oof(lambda:ExtraTreesRegressor(n_estimators=800,max_features=0.6,n_jobs=-1,random_state=0),Xall); rep('ET',et)
etr=oof(lambda:ExtraTreesRegressor(n_estimators=800,max_features=0.6,min_samples_leaf=3,n_jobs=-1,random_state=0),Xall); rep('ET msl3',etr)
hgb=oof(lambda:HistGradientBoostingRegressor(max_iter=800,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0),Xall); rep('HGB',hgb)
mlp=np.mean([oof(lambda s=s:make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=[(128,128,64),(200,100),(150,150,75)][s],max_iter=4000,alpha=1e-3,random_state=s)),Xall) for s in range(3)],axis=0); rep('MLPx3',mlp)
rep('ET+HGB+MLP',(et+hgb+mlp)/3)
rep('1ET+1HGB+3MLP',(et+hgb+3*mlp)/5)
rep('2ET+1HGB+2MLP',(2*et+hgb+2*mlp)/5)
rep('ETr+HGB+MLP',(etr+hgb+mlp)/3)
np.savez('/tmp/oof_final.npz',y=yall.values,cond=groups,et=et,etr=etr,hgb=hgb,mlp=mlp)
print('DONE')
EOF
nohup python3 /tmp/cv_final.py > /tmp/cvf_log.txt 2>&1 &
echo "pid $!"pid 1367
sleep 120 && cat /tmp/cvf_log.txt
ET rmse=2.369 mae=1.745 p90=3.39 p95=4.02 max=5.44 frac<=2.35=0.68 ET msl3 rmse=2.462 mae=1.818 p90=3.55 p95=4.17 max=5.46 frac<=2.35=0.66 HGB rmse=2.422 mae=1.774 p90=3.67 p95=4.05 max=5.33 frac<=2.35=0.67 MLPx3 rmse=2.570 mae=1.895 p90=4.02 p95=4.36 max=5.22 frac<=2.35=0.60 ET+HGB+MLP rmse=2.201 mae=1.611 p90=3.23 p95=3.45 max=4.57 frac<=2.35=0.70 1ET+1HGB+3MLP rmse=2.267 mae=1.669 p90=3.35 p95=3.64 max=4.64 frac<=2.35=0.67 2ET+1HGB+2MLP rmse=2.216 mae=1.625 p90=3.19 p95=3.38 max=4.70 frac<=2.35=0.70 ETr+HGB+MLP rmse=2.228 mae=1.634 p90=3.23 p95=3.49 max=4.57 frac<=2.35=0.68 DONE
cd /app && timeout 120 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import numpy as np, pandas as pd
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
alld['k']=condition_key(alld).values
for col in ['attack_angle','chord_length','free_stream_velocity','suction_side_displacement_thickness']:
cv=alld.groupby('k')[col].apply(lambda s: s.std()/ (abs(s.mean())+1e-9))
print(f'{col:36s} within-cond CoV median={cv.median():.4f} max={cv.max():.4f}')
# is delta* constant within condition (ignoring noise)?
d=alld.groupby('k')['suction_side_displacement_thickness'].agg(['mean','std','min','max'])
print('delta* within-cond relative spread (max-min)/mean median:', ((d['max']-d['min'])/d['mean']).median().round(3))
"
attack_angle within-cond CoV median=0.0038 max=44.3050 chord_length within-cond CoV median=0.0009 max=0.0010 free_stream_velocity within-cond CoV median=0.0010 max=0.0012 suction_side_displacement_thickness within-cond CoV median=0.0018 max=0.0021 delta* within-cond relative spread (max-min)/mean median: 0.005
cd /app && timeout 250 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
from features3 import PeakRel
import features2 as F
import pandas as pd, numpy as np
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
tr=pd.read_csv('data/train.csv'); va=pd.read_csv('data/validation.csv')
ytr=tr['scaled_sound_pressure_level']; yva=va['scaled_sound_pressure_level']; cva=condition_key(va)
# MLP with PeakRel
def mlp_pr(s,arch):
return make_pipeline(PeakRel(),StandardScaler(),MLPRegressor(hidden_layer_sizes=arch,max_iter=4000,alpha=1e-3,random_state=s))
archs=[(128,128,64),(200,100),(150,150,75)]
prs=[]
for s in range(3):
p=mlp_pr(s,archs[s]); p.fit(tr,ytr); prs.append(p.predict(va))
mlp_pr_pred=np.mean(prs,axis=0)
m=metrics(yva,mlp_pr_pred,cva); print('MLPx3+PeakRel VAL',{k:round(v,3) for k,v in m.items()})
# compare plain MLP rich
Xtr,_=F.Xy(tr); Xva,_=F.Xy(va)
prs2=[make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=archs[s],max_iter=4000,alpha=1e-3,random_state=s)).fit(Xtr,ytr).predict(Xva) for s in range(3)]
mlp_plain=np.mean(prs2,axis=0)
print('MLPx3 plain VAL',{k:round(v,3) for k,v in metrics(yva,mlp_plain,cva).items()})
" 2>&1 | grep -v WarningMLPx3+PeakRel VAL {'rmse': 2.454, 'mae': 1.801, 'p90': 3.572, 'p95': 3.64, 'maxc': 3.789, 'ncond': 15}
MLPx3 plain VAL {'rmse': 2.328, 'mae': 1.786, 'p90': 3.335, 'p95': 3.757, 'maxc': 4.258, 'ncond': 15}cd /app && timeout 280 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import features2 as F
import pandas as pd, numpy as np
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('data/train.csv'),pd.read_csv('data/validation.csv')],ignore_index=True)
Xall,yall=F.Xy(alld); cond=condition_key(alld); groups=cond.values
for nf in [5,10,20]:
gkf=GroupKFold(nf);
et=np.zeros(len(yall)); hg=np.zeros(len(yall))
for tri,tei in gkf.split(Xall,yall,groups):
et[tei]=ExtraTreesRegressor(n_estimators=600,max_features=0.6,n_jobs=-1,random_state=0).fit(Xall.iloc[tri],yall.iloc[tri]).predict(Xall.iloc[tei])
hg[tei]=HistGradientBoostingRegressor(max_iter=600,learning_rate=0.04,max_leaf_nodes=31,l2_regularization=1.0,min_samples_leaf=15,random_state=0).fit(Xall.iloc[tri],yall.iloc[tri]).predict(Xall.iloc[tei])
bl=(et+hg)/2
m=metrics(yall,bl,cond); cr=pd.DataFrame({'c':groups,'e':bl-yall.values}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print(f'{nf}-fold (train~{int(82*(nf-1)/nf)} conds) ET+HGB: rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.2f} p95={m[\"p95\"]:.2f} max={m[\"maxc\"]:.2f} frac<=2.35={(cr<=2.35).mean():.2f}')
"
5-fold (train~65 conds) ET+HGB: rmse=2.288 mae=1.667 p90=3.30 p95=3.97 max=4.99 frac<=2.35=0.70 10-fold (train~73 conds) ET+HGB: rmse=2.210 mae=1.612 p90=3.37 p95=3.81 max=4.92 frac<=2.35=0.71 20-fold (train~77 conds) ET+HGB: rmse=2.141 mae=1.575 p90=2.92 p95=3.62 max=4.74 frac<=2.35=0.77
cd /app && timeout 100 python3 -c "
import numpy as np, pandas as pd, itertools, sys; sys.path.insert(0,'/tmp')
from cv_harness import metrics
d=np.load('/tmp/oof_final.npz',allow_pickle=True)
y=d['y']; cond=d['cond']; et=d['et']; hgb=d['hgb']; mlp=d['mlp']
def sc(p):
m=metrics(y,p,cond); return m
best=[]
for we,wh,wm in itertools.product(range(0,5),repeat=3):
if we+wh+wm==0: continue
p=(we*et+wh*hgb+wm*mlp)/(we+wh+wm); m=sc(p)
best.append((m['p90'],m['rmse'],m['mae'],m['p95'],m['maxc'],(we,wh,wm)))
best.sort()
print('Top by p90 (5-fold OOF):')
for b in best[:6]:
print(f' w={b[5]} p90={b[0]:.3f} rmse={b[1]:.3f} mae={b[2]:.3f} p95={b[3]:.3f} max={b[4]:.3f}')
# balanced score
bal=sorted(best,key=lambda b:max(b[1]/2.25,b[2]/1.55,b[0]/2.35,b[3]/4.75,b[4]/5.6))
print('Top by bottleneck ratio:')
for b in bal[:6]:
print(f' w={b[5]} p90={b[0]:.3f} rmse={b[1]:.3f} mae={b[2]:.3f} p95={b[3]:.3f} max={b[4]:.3f} bneck={max(b[1]/2.25,b[2]/1.55,b[0]/2.35):.3f}')
"
Top by p90 (5-fold OOF): w=(2, 0, 1) p90=3.123 rmse=2.262 mae=1.667 p95=3.427 max=4.981 w=(4, 0, 2) p90=3.123 rmse=2.262 mae=1.667 p95=3.427 max=4.981 w=(2, 2, 1) p90=3.135 rmse=2.213 mae=1.617 p95=3.584 max=4.564 w=(4, 4, 2) p90=3.135 rmse=2.213 mae=1.617 p95=3.584 max=4.564 w=(4, 1, 2) p90=3.143 rmse=2.227 mae=1.634 p95=3.364 max=4.817 w=(4, 1, 3) p90=3.153 rmse=2.227 mae=1.636 p95=3.347 max=4.802 Top by bottleneck ratio: w=(2, 0, 1) p90=3.123 rmse=2.262 mae=1.667 p95=3.427 max=4.981 bneck=1.329 w=(4, 0, 2) p90=3.123 rmse=2.262 mae=1.667 p95=3.427 max=4.981 bneck=1.329 w=(2, 2, 1) p90=3.135 rmse=2.213 mae=1.617 p95=3.584 max=4.564 bneck=1.334 w=(4, 4, 2) p90=3.135 rmse=2.213 mae=1.617 p95=3.584 max=4.564 bneck=1.334 w=(4, 1, 2) p90=3.143 rmse=2.227 mae=1.634 p95=3.364 max=4.817 bneck=1.337 w=(4, 1, 3) p90=3.153 rmse=2.227 mae=1.636 p95=3.347 max=4.802 bneck=1.342
command: create
path: /app/train_and_predict.py
file_text: #!/usr/bin/env python3
"""Airfoil Self-Noise surrogate.
Trains a physics-informed ensemble (ExtraTrees + HistGradientBoosting + MLP)
on the labeled public data and writes calibrated predictions for the
grader-staged evaluation features.
The task holds out *complete* aerodynamic condition groups
``(attack_angle, chord_length, free_stream_velocity)``, so the model is
designed and blended to generalize to unseen physical regimes rather than to
interpolate nearby rows. Feature engineering follows the Brooks-Pope-Marcolini
(BPM) self-noise scalings (Strouhal/Reynolds numbers, log amplitude terms),
which extrapolate more gracefully than raw coordinates. The ensemble averages a
low-bias tree model, a boosted tree model, and smooth neural nets to shrink the
per-condition error tail that the grader penalizes (p90/p95/max condition RMSE).
"""
from pathlib import Path
import warnings
import numpy as np
import pandas as pd
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
warnings.filterwarnings("ignore")
DATA_DIR = Path("/app/data")
OUTPUT_DIR = Path("/app/output")
TARGET = "scaled_sound_pressure_level"
PREDICTION = "predicted_scaled_sound_pressure_level"
FEATURES = [
"frequency",
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
def make_features(df: pd.DataFrame) -> pd.DataFrame:
"""Physics-informed feature map (BPM self-noise scalings + interactions)."""
f = np.asarray(df["frequency"], dtype=float)
a = np.asarray(df["attack_angle"], dtype=float)
c = np.asarray(df["chord_length"], dtype=float)
u = np.asarray(df["free_stream_velocity"], dtype=float)
d = np.asarray(df["suction_side_displacement_thickness"], dtype=float)
# Guard against non-positive values before taking logs.
eps = 1e-12
f = np.clip(f, eps, None)
c = np.clip(c, eps, None)
u = np.clip(u, eps, None)
d = np.clip(d, eps, None)
lf = np.log10(f)
ld = np.log10(d)
lc = np.log10(c)
lu = np.log10(u)
nu = 1.5e-5 # kinematic viscosity of air (m^2/s)
o = {}
# Primary (log) coordinates.
o["log_freq"] = lf
o["attack_angle"] = a
o["log_chord"] = lc
o["log_vel"] = lu
o["log_thick"] = ld
# Dimensionless BPM-style groups.
o["log_St_d"] = np.log10(f * d / u) # displacement-thickness Strouhal
o["log_St_c"] = np.log10(f * c / u) # chord Strouhal
o["log_Re_c"] = np.log10(c * u / nu) # chord Reynolds
o["log_Re_d"] = np.log10(d * u / nu) # thickness Reynolds
o["mach"] = u / 340.0
o["d_over_c"] = d / c
o["log_d_over_c"] = np.log10(d / c)
# Interactions (let smooth models capture coupled trends).
o["lf_angle"] = lf * a
o["lf_ld"] = lf * ld
o["lf_lc"] = lf * lc
o["lf_lu"] = lf * lu
o["a_ld"] = a * ld
o["a_lc"] = a * lc
o["a_lu"] = a * lu
o["lf2"] = lf * lf
o["angle2"] = a * a
o["ld2"] = ld * ld
o["sin_a"] = np.sin(np.radians(a))
o["raw_freq"] = f
o["raw_thick"] = d
out = pd.DataFrame(o, index=df.index)
return out.replace([np.inf, -np.inf], np.nan).fillna(0.0)
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))
data = pd.concat(parts, ignore_index=True)
# Keep only rows with a usable (finite) target.
data = data[np.isfinite(pd.to_numeric(data[TARGET], errors="coerce"))]
return data.reset_index(drop=True)
def load_evaluation_features() -> pd.DataFrame:
test_path = DATA_DIR / "test_features.csv"
if test_path.exists():
return pd.read_csv(test_path)
# Fallback for local runs without a
…[truncated]Existing file was successfully overwritten at /app/train_and_predict.py
cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py && echo "---OUTPUT---" && head -5 output/predictions.csv && echo "..." && wc -l output/predictions.csv
---OUTPUT--- sample_id,predicted_scaled_sound_pressure_level af_val_0000,116.4554807596162 af_val_0001,124.95713396017285 af_val_0002,132.88160505368134 af_val_0003,124.4287633508136 ... 41 output/predictions.csv [stderr] real 0m29.495s user 0m32.360s sys 0m0.950s
cd /app && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv')
tf=pd.read_csv('data/test_features.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level'], p.columns
assert len(p)==len(tf), (len(p),len(tf))
assert p['sample_id'].astype(str).tolist()==tf['sample_id'].astype(str).tolist(), 'id mismatch/order'
assert np.isfinite(p['predicted_scaled_sound_pressure_level']).all(), 'non-finite'
assert p['predicted_scaled_sound_pressure_level'].nunique()>5, 'looks constant'
print('Format checks PASSED. rows=',len(p))
print('pred range:',p.iloc[:,1].min().round(2),'to',p.iloc[:,1].max().round(2),'std',p.iloc[:,1].std().round(2))
# Compare to known validation labels (same ids) just as a magnitude sanity check
va=pd.read_csv('data/validation.csv').set_index('sample_id')['scaled_sound_pressure_level']
m=p.set_index('sample_id').join(va)
err=m['predicted_scaled_sound_pressure_level']-m['scaled_sound_pressure_level']
print('vs val labels (in-sample-ish) rmse=%.3f mae=%.3f'%(np.sqrt((err**2).mean()),err.abs().mean()))
"Format checks PASSED. rows= 40 pred range: 114.85 to 137.8 std 6.38 vs val labels (in-sample-ish) rmse=0.662 mae=0.470
cd /app && cat > /tmp/cv_blend20.py << 'EOF'
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
sys.path.insert(0,'/app')
import train_and_predict as T
import pandas as pd, numpy as np
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
X=T.make_features(alld[T.FEATURES]); y=alld[T.TARGET].to_numpy(float); cond=condition_key(alld); groups=cond.values
gkf=GroupKFold(20); preds=np.zeros(len(y)); tw=0.0
mods=T.build_models()
for est,w in mods: tw+=w
for tri,tei in gkf.split(X,y,groups):
acc=np.zeros(len(tei))
for est,w in T.build_models():
est.fit(X.iloc[tri],y[tri]); acc+=w*est.predict(X.iloc[tei])
preds[tei]=acc/tw
m=metrics(y,preds,cond); cr=pd.DataFrame({'c':groups,'e':preds-y}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print('FULL BLEND 20-fold:',{k:round(v,3) for k,v in m.items()},'frac<=2.35=%.2f'%(cr<=2.35).mean())
EOF
nohup python3 /tmp/cv_blend20.py > /tmp/blend20_log.txt 2>&1 &
echo "blend20 pid $!"
# meanwhile: lean vs rich features for ET (10-fold)
timeout 200 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
import pandas as pd, numpy as np
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=alld['scaled_sound_pressure_level'].to_numpy(float); cond=condition_key(alld); groups=cond.values
f=alld.frequency.values;a=alld.attack_angle.values;c=alld.chord_length.values;u=alld.free_stream_velocity.values;d=alld.suction_side_displacement_thickness.values
lf=np.log10(f);ld=np.log10(d);lc=np.log10(c);lu=np.log10(u)
lean=pd.DataFrame(dict(lf=lf,a=a,lc=lc,lu=lu,ld=ld,Std=np.log10(f*d/u),Stc=np.log10(f*c/u),Rec=np.log10(c*u/1.5e-5),Red=np.log10(d*u/1.5e-5),mach=u/340,doc=d/c))
import train_and_predict as T; rich=T.make_features(alld[T.FEATURES])
for name,X in [('lean',lean),('rich',rich)]:
gkf=GroupKFold(10); p=np.zeros(len(y))
for tri,tei in gkf.split(X,y,groups):
p[tei]=ExtraTreesRegressor(n_estimators=800,max_features=0.6,n_jobs=-1,random_state=0).fit(X.iloc[tri],y[tri]).predict(X.iloc[tei])
m=metrics(y,p,cond); cr=pd.DataFrame({'c':groups,'e':p-y}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print(f'ET {name:4s} 10fold rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.2f} p95={m[\"p95\"]:.2f} max={m[\"maxc\"]:.2f} frac<=2.35={(cr<=2.35).mean():.2f}')
"blend20 pid 1920 ET lean 10fold rmse=2.378 mae=1.758 p90=3.52 p95=3.92 max=5.64 frac<=2.35=0.70 ET rich 10fold rmse=2.292 mae=1.662 p90=3.37 p95=4.01 max=5.49 frac<=2.35=0.71
sleep 90 && cat /tmp/blend20_log.txt
<system>Tool ran without output or errors</system>
sleep 120 && cat /tmp/blend20_log.txt
<system>Tool ran without output or errors</system>
sleep 150 && cat /tmp/blend20_log.txt && echo "---" && ps aux | grep cv_blend20 | grep -v grep | wc -l
--- 1
sleep 180 && cat /tmp/blend20_log.txt && echo "---running:" && ps aux | grep cv_blend20 | grep -v grep | wc -l
FULL BLEND 20-fold: {'rmse': 2.111, 'mae': 1.546, 'p90': 3.07, 'p95': 3.335, 'maxc': 4.992, 'ncond': 82} frac<=2.35=0.77
---running:
0cd /app && cat > /tmp/mlp_test.py << 'EOF'
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
sys.path.insert(0,'/app'); import train_and_predict as T
import pandas as pd, numpy as np
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
X=T.make_features(alld[T.FEATURES]); y=alld[T.TARGET].to_numpy(float); cond=condition_key(alld); groups=cond.values
def cvpred(builder,nf=10):
gkf=GroupKFold(nf); p=np.zeros(len(y))
for tri,tei in gkf.split(X,y,groups):
acc=np.zeros(len(tei)); ms=builder()
for m in ms: m.fit(X.iloc[tri],y[tri]); acc+=m.predict(X.iloc[tei])
p[tei]=acc/len(ms)
return p
def rep(name,p):
m=metrics(y,p,cond); cr=pd.DataFrame({'c':groups,'e':p-y}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print(f'{name:22s} rmse={m["rmse"]:.3f} mae={m["mae"]:.3f} p90={m["p90"]:.2f} p95={m["p95"]:.2f} max={m["maxc"]:.2f} frac={ (cr<=2.35).mean():.2f}',flush=True)
return p
def relu(): return [make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=a,alpha=1e-3,max_iter=4000,random_state=s)) for s,a in enumerate([(128,128,64),(200,100),(150,150,75),(256,128,64),(180,120,60)])]
def tanh(): return [make_pipeline(StandardScaler(),MLPRegressor(hidden_layer_sizes=a,activation='tanh',alpha=3e-3,max_iter=4000,random_state=s)) for s,a in enumerate([(128,128,64),(200,100),(150,150,75),(256,128,64),(180,120,60)])]
pr=rep('MLP relu x5',cvpred(relu))
pt=rep('MLP tanh x5',cvpred(tanh))
rep('MLP relu+tanh',(pr+pt)/2)
np.savez('/tmp/mlp_oof.npz',relu=pr,tanh=pt,y=y,cond=groups)
EOF
nohup python3 /tmp/mlp_test.py > /tmp/mlp_log.txt 2>&1 &
echo "pid $!"; sleep 200; cat /tmp/mlp_log.txtpid 2572
sleep 120 && cat /tmp/mlp_log.txt && echo "running:" && ps aux|grep mlp_test|grep -v grep|wc -l
MLP relu x5 rmse=2.584 mae=1.882 p90=3.91 p95=4.28 max=7.06 frac=0.59 running: 1
cd /app && timeout 120 python3 -c "
import numpy as np, pandas as pd, sys; sys.path.insert(0,'/tmp')
from cv_harness import metrics
d=np.load('/tmp/oof_all.npz',allow_pickle=True)
y=d['y']; cond=d['cond']
names=[k for k in d.files if k not in ('y','cond')]
P={n:d[n] for n in names}
def sc(p):
m=metrics(y,p,cond)
return m
# greedy forward selection minimizing p90 (tie-break rmse), allow repeats (weights)
sel=[];
cur=np.zeros(len(y))
best_metric=1e9
for step in range(8):
bestn=None; bestm=None; bestp=None
for n in names:
p=(cur*len(sel)+P[n])/(len(sel)+1)
m=sc(p)
key=(m['p90'],m['rmse'],m['mae'])
if bestm is None or key<bestm:
bestm=key; bestn=n; bestp=p; bestmet=m
sel.append(bestn); cur=bestp
print(f'step{step+1} add {bestn:5s} -> rmse={bestmet[\"rmse\"]:.3f} mae={bestmet[\"mae\"]:.3f} p90={bestmet[\"p90\"]:.2f} p95={bestmet[\"p95\"]:.2f} max={bestmet[\"maxc\"]:.2f}')
from collections import Counter
print('selection counts:',Counter(sel))
"step1 add ETnr -> rmse=2.407 mae=1.775 p90=3.37 p95=3.99 max=5.63
step2 add HGB -> rmse=2.311 mae=1.687 p90=3.31 p95=3.97 max=5.09
step3 add MLP1 -> rmse=2.249 mae=1.636 p90=3.31 p95=3.72 max=4.95
step4 add SVR -> rmse=2.203 mae=1.594 p90=3.12 p95=3.60 max=5.59
step5 add HGB -> rmse=2.201 mae=1.590 p90=3.12 p95=3.52 max=5.50
step6 add SVR -> rmse=2.221 mae=1.595 p90=3.05 p95=3.68 max=6.00
step7 add SVR -> rmse=2.261 mae=1.617 p90=3.04 p95=3.80 max=6.37
step8 add ET -> rmse=2.228 mae=1.601 p90=3.00 p95=3.63 max=6.03
selection counts: Counter({'SVR': 3, 'HGB': 2, 'ETnr': 1, 'MLP1': 1, 'ET': 1})command: str_replace
path: /app/train_and_predict.py
old_str: def build_models():
"""(estimator, integer blend weight) pairs."""
models = []
# Low-bias randomized trees (two seeds for variance reduction).
for seed in (0, 1):
models.append((
ExtraTreesRegressor(
n_estimators=1200, max_features=0.6, min_samples_leaf=1,
n_jobs=-1, random_state=seed,
),
1,
))
# Boosted trees (bias reduction, different inductive bias).
models.append((
HistGradientBoostingRegressor(
max_iter=800, learning_rate=0.04, max_leaf_nodes=31,
l2_regularization=1.0, min_samples_leaf=15, random_state=0,
),
2,
))
# Smooth neural nets (graceful extrapolation of physical trends).
archs = [(128, 128, 64), (200, 100), (150, 150, 75)]
for seed, arch in enumerate(archs):
models.append((
make_pipeline(
StandardScaler(),
MLPRegressor(
hidden_layer_sizes=arch, activation="relu", alpha=1e-3,
max_iter=4000, random_state=seed,
),
),
1,
))
return models
new_str: def build_models():
"""(estimator, integer blend weight) pairs.
The ensemble balances total weight between low-variance randomized trees
(ExtraTrees), bias-correcting boosted trees (HistGBR), and smooth neural
nets (MLP). Multiple seeds per family reduce variance, which tightens the
per-condition error tail (p90/p95) on held-out aerodynamic regimes.
SVR/kernel models are deliberately excluded: they gave occasional large
single-condition blow-ups that endanger the max-condition-RMSE budget.
"""
models = []
# Low-bias randomized trees (several seeds for variance reduction).
for seed in (0, 1, 2):
models.append((
ExtraTreesRegressor(
n_estimators=1200, max_features=0.6, min_samples_leaf=1,
n_jobs=-1, random_state=seed,
),
1,
))
# Boosted trees (two complementary configs for diversity).
models.append((
HistGradientBoostingRegressor(
max_iter=800, learning_rate=0.04, max_leaf_nodes=31,
l2_regularization=1.0, min_samples_leaf=15, random_state=0,
),
1,
))
models.append((
HistGradientBoostingRegressor(
max_iter=1000, learning_rate=0.03, max_leaf_nodes=63,
l2_regularization=2.0, min_samples_leaf=20, random_state=1,
),
1,
))
# Smooth neural nets (graceful extrapolation of physical trends).
archs = [(128, 128, 64), (200, 100), (150, 150, 75), (256, 128, 64)]
for seed, arch in enumerate(archs):
models.append((
make_pipeline(
StandardScaler(),
MLPRegressor(
hidden_layer_sizes=arch, activation="relu", alpha=1e-3,
max_iter=4000, random_state=seed,
),
),
1,
))
return modelsThe file /app/train_and_predict.py has been edited successfully.
cd /app && rm -f output/predictions.csv && time python3 train_and_predict.py && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv'); tf=pd.read_csv('data/test_features.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level']
assert p['sample_id'].astype(str).tolist()==tf['sample_id'].astype(str).tolist()
assert np.isfinite(p.iloc[:,1]).all() and p.iloc[:,1].nunique()>5
print('OK rows',len(p),'range',round(p.iloc[:,1].min(),2),round(p.iloc[:,1].max(),2))
"OK rows 40 range 114.82 137.86 [stderr] real 1m9.675s user 0m56.690s sys 0m1.450s
cd /app && cat > /tmp/cv_final2.py << 'EOF'
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
sys.path.insert(0,'/app'); import train_and_predict as T
import pandas as pd, numpy as np
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
X=T.make_features(alld[T.FEATURES]); y=alld[T.TARGET].to_numpy(float); cond=condition_key(alld); groups=cond.values
tw=sum(w for _,w in T.build_models())
for nf in [10]:
gkf=GroupKFold(nf); preds=np.zeros(len(y))
for tri,tei in gkf.split(X,y,groups):
acc=np.zeros(len(tei))
for est,w in T.build_models():
est.fit(X.iloc[tri],y[tri]); acc+=w*est.predict(X.iloc[tei])
preds[tei]=acc/tw
m=metrics(y,preds,cond); cr=pd.DataFrame({'c':groups,'e':preds-y}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print(f'ENHANCED BLEND {nf}-fold:',{k:round(v,3) for k,v in m.items()},'frac<=2.35=%.2f'%(cr<=2.35).mean())
tgt=dict(rmse=2.25,mae=1.55,p90=2.35,p95=4.75,maxc=5.60)
print('pass:',{k:(round(m[k],3),'<=',tgt[k],m[k]<=tgt[k]) for k in tgt})
EOF
nohup python3 /tmp/cv_final2.py > /tmp/cvf2.txt 2>&1 &
echo "pid $!"; sleep 230; cat /tmp/cvf2.txtpid 2656
sleep 60 && cat /tmp/cvf2.txt && echo "running:" && ps aux|grep cv_final2|grep -v grep|wc -l
running: 1
sleep 120 && cat /tmp/cvf2.txt && echo "running:" && ps aux|grep cv_final2|grep -v grep|wc -l
running: 1
sleep 120 && cat /tmp/cvf2.txt && echo "running:" && ps aux|grep cv_final2|grep -v grep|wc -l
ENHANCED BLEND 10-fold: {'rmse': 2.211, 'mae': 1.614, 'p90': 3.12, 'p95': 3.378, 'maxc': 5.678, 'ncond': 82} frac<=2.35=0.74
pass: {'rmse': (2.211, '<=', 2.25, True), 'mae': (1.614, '<=', 1.55, False), 'p90': (3.12, '<=', 2.35, False), 'p95': (3.378, '<=', 4.75, True), 'maxc': (5.678, '<=', 5.6, False)}
running:
0command: str_replace
path: /app/train_and_predict.py
old_str: # Boosted trees (two complementary configs for diversity).
models.append((
HistGradientBoostingRegressor(
max_iter=800, learning_rate=0.04, max_leaf_nodes=31,
l2_regularization=1.0, min_samples_leaf=15, random_state=0,
),
1,
))
models.append((
HistGradientBoostingRegressor(
max_iter=1000, learning_rate=0.03, max_leaf_nodes=63,
l2_regularization=2.0, min_samples_leaf=20, random_state=1,
),
1,
))
# Smooth neural nets (graceful extrapolation of physical trends).
archs = [(128, 128, 64), (200, 100), (150, 150, 75), (256, 128, 64)]
new_str: # Boosted trees (bias correction; a single well-regularized config is used
# because deeper/larger boosting configs produced occasional large
# single-condition errors that threatened the max-condition-RMSE budget).
models.append((
HistGradientBoostingRegressor(
max_iter=800, learning_rate=0.04, max_leaf_nodes=31,
l2_regularization=1.0, min_samples_leaf=15, random_state=0,
),
2,
))
# Smooth neural nets (graceful extrapolation of physical trends).
archs = [(128, 128, 64), (200, 100), (150, 150, 75), (256, 128, 64)]The file /app/train_and_predict.py has been edited successfully.
cd /app && sed 's/\[10\]/[20]/' /tmp/cv_final2.py > /tmp/cv_final3.py && nohup python3 /tmp/cv_final3.py > /tmp/cvf3.txt 2>&1 & echo "pid $!"
pid 3014
cd /app && cat train_and_predict.py
#!/usr/bin/env python3
"""Airfoil Self-Noise surrogate.
Trains a physics-informed ensemble (ExtraTrees + HistGradientBoosting + MLP)
on the labeled public data and writes calibrated predictions for the
grader-staged evaluation features.
The task holds out *complete* aerodynamic condition groups
``(attack_angle, chord_length, free_stream_velocity)``, so the model is
designed and blended to generalize to unseen physical regimes rather than to
interpolate nearby rows. Feature engineering follows the Brooks-Pope-Marcolini
(BPM) self-noise scalings (Strouhal/Reynolds numbers, log amplitude terms),
which extrapolate more gracefully than raw coordinates. The ensemble averages a
low-bias tree model, a boosted tree model, and smooth neural nets to shrink the
per-condition error tail that the grader penalizes (p90/p95/max condition RMSE).
"""
from pathlib import Path
import warnings
import numpy as np
import pandas as pd
from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
warnings.filterwarnings("ignore")
DATA_DIR = Path("/app/data")
OUTPUT_DIR = Path("/app/output")
TARGET = "scaled_sound_pressure_level"
PREDICTION = "predicted_scaled_sound_pressure_level"
FEATURES = [
"frequency",
"attack_angle",
"chord_length",
"free_stream_velocity",
"suction_side_displacement_thickness",
]
def make_features(df: pd.DataFrame) -> pd.DataFrame:
"""Physics-informed feature map (BPM self-noise scalings + interactions)."""
f = np.asarray(df["frequency"], dtype=float)
a = np.asarray(df["attack_angle"], dtype=float)
c = np.asarray(df["chord_length"], dtype=float)
u = np.asarray(df["free_stream_velocity"], dtype=float)
d = np.asarray(df["suction_side_displacement_thickness"], dtype=float)
# Guard against non-positive values before taking logs.
eps = 1e-12
f = np.clip(f, eps, None)
c = np.clip(c, eps, None)
u = np.clip(u, eps, None)
d = np.clip(d, eps, None)
lf = np.log10(f)
ld = np.log10(d)
lc = np.log10(c)
lu = np.log10(u)
nu = 1.5e-5 # kinematic viscosity of air (m^2/s)
o = {}
# Primary (log) coordinates.
o["log_freq"] = lf
o["attack_angle"] = a
o["log_chord"] = lc
o["log_vel"] = lu
o["log_thick"] = ld
# Dimensionless BPM-style groups.
o["log_St_d"] = np.log10(f * d / u) # displacement-thickness Strouhal
o["log_St_c"] = np.log10(f * c / u) # chord Strouhal
o["log_Re_c"] = np.log10(c * u / nu) # chord Reynolds
o["log_Re_d"] = np.log10(d * u / nu) # thickness Reynolds
o["mach"] = u / 340.0
o["d_over_c"] = d / c
o["log_d_over_c"] = np.log10(d / c)
# Interactions (let smooth models capture coupled trends).
o["lf_angle"] = lf * a
o["lf_ld"] = lf * ld
o["lf_lc"] = lf * lc
o["lf_lu"] = lf * lu
o["a_ld"] = a * ld
o["a_lc"] = a * lc
o["a_lu"] = a * lu
o["lf2"] = lf * lf
o["angle2"] = a * a
o["ld2"] = ld * ld
o["sin_a"] = np.sin(np.radians(a))
o["raw_freq"] = f
o["raw_thick"] = d
out = pd.DataFrame(o, index=df.index)
return out.replace([np.inf, -np.inf], np.nan).fillna(0.0)
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))
data = pd.concat(parts, ignore_index=True)
# Keep only rows with a usable (finite) target.
data = data[np.isfinite(pd.to_numeric(data[TARGET], errors="coerce"))]
return data.reset_index(drop=True)
def load_evaluation_features() -> pd.DataFrame:
test_path = DATA_DIR / "test_features.csv"
if test_path.exists():
return pd.read_csv(test_path)
# Fallback for local runs without a staged test file.
validation = pd.read_csv(DATA_DIR / "
…[truncated]sleep 60 && cat /tmp/cvf3.txt && echo "running:" && ps aux|grep cv_final3|grep -v grep|wc -l
running: 1
sleep 200 && cat /tmp/cvf3.txt && echo "running:" && ps aux|grep cv_final3|grep -v grep|wc -l
running: 1
sleep 200 && cat /tmp/cvf3.txt && echo "running:" && ps aux|grep cv_final3|grep -v grep|wc -l
running: 1
sleep 180 && cat /tmp/cvf3.txt && echo "running:" && ps aux|grep cv_final3|grep -v grep|wc -l
running: 1
sleep 120 && cat /tmp/cvf3.txt && echo "running:" && ps aux|grep cv_final3|grep -v grep|wc -l
ENHANCED BLEND 20-fold: {'rmse': 2.124, 'mae': 1.557, 'p90': 3.092, 'p95': 3.346, 'maxc': 5.277, 'ncond': 82} frac<=2.35=0.77
pass: {'rmse': (2.124, '<=', 2.25, True), 'mae': (1.557, '<=', 1.55, False), 'p90': (3.092, '<=', 2.35, False), 'p95': (3.346, '<=', 4.75, True), 'maxc': (5.277, '<=', 5.6, True)}
running:
0cd /app && timeout 260 python3 -c "
import sys; sys.path.insert(0,'/tmp'); import warnings; warnings.filterwarnings('ignore')
from cv_harness import *
sys.path.insert(0,'/app'); import train_and_predict as T
import pandas as pd, numpy as np
from sklearn.ensemble import ExtraTreesRegressor
from sklearn.model_selection import GroupKFold
alld=pd.concat([pd.read_csv('/app/data/train.csv'),pd.read_csv('/app/data/validation.csv')],ignore_index=True)
y=alld[T.TARGET].to_numpy(float); cond=condition_key(alld); groups=cond.values
base=T.make_features(alld[T.FEATURES])
a=alld.attack_angle.values; lc=np.log10(alld.chord_length.values); d=alld.suction_side_displacement_thickness.values
stall=base.copy()
stall['stall12']=np.maximum(a-12.5,0); stall['stall17']=np.maximum(a-17,0)
stall['stall12_lc']=np.maximum(a-12.5,0)*lc; stall['stall_doc']=np.maximum(a-10,0)*(d/alld.chord_length.values)
stall['a3']=a**3
def run(name,X):
gkf=GroupKFold(10); p=np.zeros(len(y))
for tri,tei in gkf.split(X,y,groups):
acc=np.zeros(len(tei))
for s in (0,1):
acc+=ExtraTreesRegressor(n_estimators=800,max_features=0.6,n_jobs=-1,random_state=s).fit(X.iloc[tri],y[tri]).predict(X.iloc[tei])
p[tei]=acc/2
m=metrics(y,p,cond); cr=pd.DataFrame({'c':groups,'e':p-y}).groupby('c')['e'].apply(lambda e:np.sqrt(np.mean(e**2)))
print(f'{name:12s} rmse={m[\"rmse\"]:.3f} mae={m[\"mae\"]:.3f} p90={m[\"p90\"]:.2f} p95={m[\"p95\"]:.2f} max={m[\"maxc\"]:.2f} frac={ (cr<=2.35).mean():.2f}')
run('rich',base); run('rich+stall',stall)
"rich rmse=2.292 mae=1.664 p90=3.38 p95=3.98 max=5.51 frac=0.71 rich+stall rmse=2.302 mae=1.664 p90=3.38 p95=4.00 max=5.47 frac=0.72
command: str_replace
path: /app/train_and_predict.py
old_str: # Smooth neural nets (graceful extrapolation of physical trends).
archs = [(128, 128, 64), (200, 100), (150, 150, 75), (256, 128, 64)]
new_str: # Smooth neural nets (graceful extrapolation of physical trends).
archs = [(128, 128, 64), (200, 100), (150, 150, 75), (256, 128, 64),
(180, 120, 60)]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>/tmp/run_err.txt; echo "exit=$?"; echo "--- stderr (filtered) ---"; grep -vi "warn" /tmp/run_err.txt | head; echo "--- validate ---"; python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv'); tf=pd.read_csv('data/test_features.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level']
assert len(p)==len(tf)
assert p['sample_id'].astype(str).tolist()==tf['sample_id'].astype(str).tolist()
v=p['predicted_scaled_sound_pressure_level'].to_numpy()
assert np.isfinite(v).all() and np.ptp(v)>1
print('VALID: rows=%d range=%.2f..%.2f std=%.2f'%(len(p),v.min(),v.max(),v.std()))
"exit=0 --- stderr (filtered) --- --- validate --- VALID: rows=40 range=114.79..137.86 std=6.25 [stderr] real 0m41.903s user 0m46.890s sys 0m1.240s
cd /app && python3 -c "
import sys; sys.path.insert(0,'/app'); import warnings; warnings.filterwarnings('ignore')
import numpy as np, pandas as pd
import train_and_predict as T
# Build a 'hidden-like' eval set: many rows, shuffled column order, some NaN/inf
ev=pd.read_csv('data/train.csv').drop(columns=[T.TARGET]).copy()
ev=ev[['chord_length','frequency','suction_side_displacement_thickness','attack_angle','free_stream_velocity','sample_id']] # reordered
ev.loc[0,'frequency']=np.nan; ev.loc[1,'suction_side_displacement_thickness']=0.0; ev.loc[2,'attack_angle']=np.inf
Xtr=T.make_features(pd.read_csv('data/train.csv')[T.FEATURES]); ytr=pd.read_csv('data/train.csv')[T.TARGET].to_numpy(float)
Xev=T.make_features(ev[T.FEATURES])
assert np.isfinite(Xev.to_numpy()).all(), 'features not finite!'
print('feature matrix finite with NaN/inf/zero inputs: OK; shape',Xev.shape)
# quick single-model predict to ensure no crash end-to-end on odd eval
from sklearn.ensemble import ExtraTreesRegressor
m=ExtraTreesRegressor(n_estimators=50,random_state=0,n_jobs=-1).fit(Xtr,ytr)
pr=m.predict(Xev)
assert np.isfinite(pr).all()
print('predict on 956 reordered+corrupted rows: OK; range %.2f..%.2f'%(pr.min(),pr.max()))
print('input files untouched (no writes performed here).')
"feature matrix finite with NaN/inf/zero inputs: OK; shape (956, 25) predict on 956 reordered+corrupted rows: OK; range 104.94..140.29 input files untouched (no writes performed here).
cd /app && timeout 200 python3 -c "
import numpy as np, pandas as pd, itertools, sys; sys.path.insert(0,'/tmp')
from cv_harness import metrics
d=np.load('/tmp/oof_all.npz',allow_pickle=True)
y=d['y']; cond=d['cond']
use=['ET','ETnr','HGB','RF','MLP0','MLP1','MLP2']
P=[d[n] for n in use]
def sc(p): return metrics(y,p,cond)
best=None
# limit weights to 0..2 to bound search
for ws in itertools.product(range(3),repeat=len(use)):
s=sum(ws)
if s==0: continue
p=sum(w*P[i] for i,w in enumerate(ws))/s
m=sc(p)
if m['maxc']<=5.5 and m['mae']<=1.65 and m['rmse']<=2.35:
key=m['p90']
if best is None or key<best[0]:
best=(key,m,ws)
print('use order:',use)
print('best p90 blend (5-fold, constrained): weights',best[2])
print({k:round(v,3) for k,v in best[1].items()})
# also report my current-style blend equivalent on these oofs: ET+ETnr+HGB + MLPs
cur=(d['ET']+d['ETnr']+2*d['HGB']+d['MLP0']+d['MLP1']+d['MLP2'])/7
print('current-ish blend:',{k:round(v,3) for k,v in sc(cur).items()})
"use order: ['ET', 'ETnr', 'HGB', 'RF', 'MLP0', 'MLP1', 'MLP2']
best p90 blend (5-fold, constrained): weights (2, 1, 2, 0, 1, 2, 0)
{'rmse': 2.228, 'mae': 1.628, 'p90': 3.195, 'p95': 3.742, 'maxc': 4.73, 'ncond': 82}
current-ish blend: {'rmse': 2.253, 'mae': 1.65, 'p90': 3.354, 'p95': 3.547, 'maxc': 4.766, 'ncond': 82}cd /app && rm -f output/predictions.csv && python3 train_and_predict.py && python3 -c "
import pandas as pd, numpy as np
p=pd.read_csv('output/predictions.csv'); tf=pd.read_csv('data/test_features.csv')
assert list(p.columns)==['sample_id','predicted_scaled_sound_pressure_level'], 'columns'
assert len(p)==len(tf)==p['sample_id'].nunique(), 'row/id count'
assert p['sample_id'].astype(str).tolist()==tf['sample_id'].astype(str).tolist(), 'alignment'
v=p['predicted_scaled_sound_pressure_level'].to_numpy()
assert np.isfinite(v).all(), 'finite'
assert np.ptp(v)>1, 'non-constant'
print('FINAL DELIVERABLE VALID')
print(' rows:',len(p),'| unique ids:',p['sample_id'].nunique())
print(' pred range: %.2f .. %.2f (std %.2f)'%(v.min(),v.max(),v.std()))
print(p.head(3).to_string(index=False))
"FINAL DELIVERABLE VALID rows: 40 | unique ids: 40 pred range: 114.79 .. 137.86 (std 6.25) sample_id predicted_scaled_sound_pressure_level af_val_0000 116.535272 af_val_0001 125.178497 af_val_0002 132.852179
cd /app && echo "=== input files intact ===" && wc -l data/train.csv data/validation.csv data/test_features.csv && head -1 data/train.csv && echo "=== self-contained imports ===" && grep -n "^import\|^from" train_and_predict.py && echo "=== no forbidden path refs ===" && grep -nE "/tests|/grader|/mcp_server|reward|verifier" train_and_predict.py || echo "none found (good)" && echo "=== no writes outside /app/output ===" && grep -nE "to_csv|open\(|\.write" train_and_predict.py
=== input files intact ===
957 data/train.csv
202 data/validation.csv
41 data/test_features.csv
1200 total
sample_id,frequency,attack_angle,chord_length,free_stream_velocity,suction_side_displacement_thickness,scaled_sound_pressure_level
=== self-contained imports ===
18:from pathlib import Path
19:import warnings
21:import numpy as np
22:import pandas as pd
24:from sklearn.ensemble import ExtraTreesRegressor, HistGradientBoostingRegressor
25:from sklearn.neural_network import MLPRegressor
26:from sklearn.pipeline import make_pipeline
27:from sklearn.preprocessing import StandardScaler
=== no forbidden path refs ===
none found (good)
=== no writes outside /app/output ===
199: ).to_csv(OUTPUT_DIR / "predictions.csv", index=False)cd /app && sed -n '118,170p' train_and_predict.py
def build_models():
"""(estimator, integer blend weight) pairs.
The ensemble balances total weight between low-variance randomized trees
(ExtraTrees), bias-correcting boosted trees (HistGBR), and smooth neural
nets (MLP). Multiple seeds per family reduce variance, which tightens the
per-condition error tail (p90/p95) on held-out aerodynamic regimes.
SVR/kernel models are deliberately excluded: they gave occasional large
single-condition blow-ups that endanger the max-condition-RMSE budget.
"""
models = []
# Low-bias randomized trees (several seeds for variance reduction).
for seed in (0, 1, 2):
models.append((
ExtraTreesRegressor(
n_estimators=1200, max_features=0.6, min_samples_leaf=1,
n_jobs=-1, random_state=seed,
),
1,
))
# Boosted trees (bias correction; a single well-regularized config is used
# because deeper/larger boosting configs produced occasional large
# single-condition errors that threatened the max-condition-RMSE budget).
models.append((
HistGradientBoostingRegressor(
max_iter=800, learning_rate=0.04, max_leaf_nodes=31,
l2_regularization=1.0, min_samples_leaf=15, random_state=0,
),
2,
))
# Smooth neural nets (graceful extrapolation of physical trends).
archs = [(128, 128, 64), (200, 100), (150, 150, 75), (256, 128, 64),
(180, 120, 60)]
for seed, arch in enumerate(archs):
models.append((
make_pipeline(
StandardScaler(),
MLPRegressor(
hidden_layer_sizes=arch, activation="relu", alpha=1e-3,
max_iter=4000, random_state=seed,
),
),
1,
))
return models
def main() -> None:
labeled = load_labeled_data()
evaluation = load_evaluation_features()
X_train = make_features(labeled[FEATURES])No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary
# task: airfoil-self-noise
# attempt: 4 status: completed raw_score: 0.43
# reward rule: continuous: score 0.43 < 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).
----------------------------------------------------------------------
def build_models():
"""(estimator, integer blend weight) pairs.
The ensemble balances total weight between low-variance randomized trees
(ExtraTrees), bias-correcting boosted trees (HistGBR), and smooth neural
nets (MLP). Multiple seeds per family reduce variance, which tightens the
per-condition error tail (p90/p95) on held-out aerodynamic regimes.
SVR/kernel models are deliberately excluded: they gave occasional large
single-condition blow-ups that endanger the max-condition-RMSE budget.
"""
models = []
# Low-bias randomized trees (several seeds for variance reduction).
for seed in (0, 1, 2):
models.append((
ExtraTreesRegressor(
n_estimators=1200, max_features=0.6, min_samples_leaf=1,
n_jobs=-1, random_state=seed,
),
1,
))
# Boosted trees (bias correction; a single well-regularized config is used
# because deeper/larger boosting configs produced occasional large
# single-condition errors that threatened the max-condition-RMSE budget).
models.append((
HistGradientBoostingRegressor(
max_iter=800, learning_rate=0.04, max_leaf_nodes=31,
l2_regularization=1.0, min_samples_leaf=15, random_state=0,
),
2,
))
# Smooth neural nets (graceful extrapolation of physical trends).
archs = [(128, 128, 64), (200, 100), (150, 150, 75), (256, 128, 64),
(180, 120, 60)]
for seed, arch in enumerate(archs):
models.append((
make_pipeline(
StandardScaler(),
MLPRegressor(
hidden_layer_sizes=arch, activation="relu", alpha=1e-3,
max_iter=4000, random_state=seed,
),
),
1,
))
return models
def main() -> None:
labeled = load_labeled_data()
evaluation = load_evaluation_features()
X_train = make_features(labeled[FEATURES])Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_b1bbeed0328248b9. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_b1bbeed0328248b9 · verifier authoritative; classifier explanatory.