tests/test.sh exit code (0 → resolved); the classification below is post-hoc and cannot change it.cd /app && ls -la && echo "---CONFIG---" && cat task_config.json && echo "---SCHEMA---" && cat feature_schema.json
total 14
drwxrws--- 3 h2tagent h2tagent 138 Jun 20 18:13 .
drwxr-xr-x 1 root root 4096 Jun 20 20:56 ..
-rw-rw---- 1 h2tagent h2tagent 1639 Jun 20 18:10 candidate_model.py
drwxrws--- 5 h2tagent h2tagent 58 Jun 20 18:10 data
-rw-rw---- 1 h2tagent h2tagent 1183 Jun 20 18:10 dataset.py
-rw-rw---- 1 h2tagent h2tagent 499 Jun 20 18:10 feature_schema.json
-rw-rw---- 1 h2tagent h2tagent 581 Jun 20 18:10 task_config.json
-rw-rw---- 1 h2tagent h2tagent 3117 Jun 20 18:10 train.py
---CONFIG---
{
"task_id": "simjeb-bracket-fea-mass-prediction-real",
"dataset_name": "simjeb-bracket-fea-mass-pilot",
"n_points": 2048,
"point_dim": 3,
"label_dim": 1,
"label_per_point": false,
"seed": 20260518,
"model": {
"feature_width": 64,
"global_width": 256,
"head_width": 128
},
"training": {
"epochs": 1,
"batch_size": 16,
"lr": 0.001,
"weight_decay": 0.0001,
"loss": "MSE"
},
"evaluation": {
"primary_metric": "mass_rel_l2_score",
"secondary_metrics": [
"mass_mae_score",
"mass_max_abs_err_score"
]
}
}
---SCHEMA---
{
"version": "simjeb-bracket-fea-mass-pilot-v1",
"input": {
"type": "point_cloud",
"n_points": 2048,
"dim": 3,
"coord_range": [
-0.5,
0.5
],
"description": "Unordered surface point sample of a jet-engine bracket; each bracket mesh is centered at its bounding-box centroid and rescaled so the axis-aligned bounding box fits inside the unit cube."
},
"label": {
"type": "scalar",
"dim": 1,
"description": "FEA-derived bracket mass in kg."
}
}cd /app && echo "===candidate_model.py===" && cat candidate_model.py && echo "===dataset.py===" && cat dataset.py && echo "===train.py===" && cat train.py
===candidate_model.py===
from __future__ import annotations
from typing import Mapping
import torch
import torch.nn as nn
class SharedPointMLP(nn.Module):
def __init__(self, in_dim: int, widths: tuple[int, ...]) -> None:
super().__init__()
layers: list[nn.Module] = []
prev = in_dim
for w in widths:
layers.append(nn.Conv1d(prev, w, kernel_size=1))
layers.append(nn.BatchNorm1d(w))
layers.append(nn.GELU())
prev = w
self.net = nn.Sequential(*layers)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.net(x)
class PointNetRegressor(nn.Module):
def __init__(self, feature_width: int, global_width: int, head_width: int) -> None:
super().__init__()
self.feature = SharedPointMLP(3, (feature_width, feature_width, feature_width))
self.lift = SharedPointMLP(feature_width, (feature_width * 2, global_width))
self.head = nn.Sequential(
nn.Linear(global_width, head_width),
nn.GELU(),
nn.Linear(head_width, head_width // 2),
nn.GELU(),
nn.Linear(head_width // 2, 1),
)
def forward(self, points: torch.Tensor) -> torch.Tensor:
x = points.transpose(1, 2).contiguous()
x = self.feature(x)
x = self.lift(x)
x = x.max(dim=2).values
return self.head(x)
def build_model(config: Mapping) -> PointNetRegressor:
m = config["model"]
return PointNetRegressor(
feature_width=int(m["feature_width"]),
global_width=int(m["global_width"]),
head_width=int(m["head_width"]),
)
===dataset.py===
from __future__ import annotations
from pathlib import Path
from typing import Iterable
import torch
from torch.utils.data import Dataset
class SampleStore(Dataset):
def __init__(self, split_dir: str | Path) -> None:
self.dir = Path(split_dir)
payload = torch.load(self.dir / "samples.pt", map_location="cpu", weights_only=True)
self.features: torch.Tensor = payload["features"].float()
self.labels: torch.Tensor = payload["labels"].float()
if self.features.shape[0] != self.labels.shape[0]:
raise ValueError("features and labels must have the same number of rows")
def __len__(self) -> int:
return self.features.shape[0]
def __getitem__(self, idx: int) -> tuple[torch.Tensor, torch.Tensor]:
return self.features[idx], self.labels[idx]
def iter_batches(
x: torch.Tensor,
y: torch.Tensor,
batch_size: int,
shuffle: bool = True,
) -> Iterable[tuple[torch.Tensor, torch.Tensor]]:
n = x.shape[0]
idx = torch.randperm(n) if shuffle else torch.arange(n)
for start in range(0, n, batch_size):
chunk = idx[start : start + batch_size]
yield x[chunk], y[chunk]
===train.py===
from __future__ import annotations
import json
import os
import sys
from pathlib import Path
import torch
import torch.nn.functional as F
APP_DIR = Path(os.environ.get("APP_DIR", "/app"))
sys.path.insert(0, str(APP_DIR))
from candidate_model import build_model # noqa: E402
from dataset import SampleStore, iter_batches # noqa: E402
def _device() -> torch.device:
if torch.cuda.is_available():
return torch.device("cuda")
if torch.backends.mps.is_available():
return torch.device("mps")
return torch.device("cpu")
def _load_config() -> dict:
with (APP_DIR / "task_config.json").open() as fh:
return json.load(fh)
def main() -> None:
cfg = _load_config()
device = _device()
torch.manual_seed(int(cfg["seed"]))
train = SampleStore(APP_DIR / "data" / "train")
val = SampleStore(APP_DIR / "data" / "val")
feat_mean = train.features.mean(dim=(0, 1), keepdim=True)
feat_std = train.features.std(dim=(0, 1), keepdim=True).clamp_min(1e-6)
label_mean = train.labels.mean(dim=(0,), keepdim=True)
label_std = train.labels.std(dim=(0,), keepdi
…[truncated]cd /app && ls -la data && echo "---" && for d in train val public_test; do echo "== $d =="; ls -la data/$d; done
total 3 drwxrws--- 5 h2tagent h2tagent 58 Jun 20 18:10 . drwxrws--- 3 h2tagent h2tagent 138 Jun 20 18:13 .. drwxrws--- 2 h2tagent h2tagent 55 Jun 20 18:10 public_test drwxrws--- 2 h2tagent h2tagent 55 Jun 20 18:10 train drwxrws--- 2 h2tagent h2tagent 55 Jun 20 18:10 val --- == train == total 1210 drwxrws--- 2 h2tagent h2tagent 55 Jun 20 18:10 . drwxrws--- 5 h2tagent h2tagent 58 Jun 20 18:10 .. -rw-rw---- 1 h2tagent h2tagent 6483 Jun 20 18:10 manifest.jsonl -rw-rw---- 1 h2tagent h2tagent 1230821 Jun 20 18:10 samples.pt == val == total 389 drwxrws--- 2 h2tagent h2tagent 55 Jun 20 18:10 . drwxrws--- 5 h2tagent h2tagent 58 Jun 20 18:10 .. -rw-rw---- 1 h2tagent h2tagent 2011 Jun 20 18:10 manifest.jsonl -rw-rw---- 1 h2tagent h2tagent 395109 Jun 20 18:10 samples.pt == public_test == total 197 drwxrws--- 2 h2tagent h2tagent 55 Jun 20 18:10 . drwxrws--- 5 h2tagent h2tagent 58 Jun 20 18:10 .. -rw-rw---- 1 h2tagent h2tagent 1134 Jun 20 18:10 manifest.jsonl -rw-rw---- 1 h2tagent h2tagent 198437 Jun 20 18:10 samples.pt
cd /app && python3 -c "
import torch, json
for split in ['train','val','public_test']:
p = torch.load(f'data/{split}/samples.pt', weights_only=True)
f = p['features']; l = p['labels']
print(f'== {split} ==')
print('features', tuple(f.shape), f.dtype, 'labels', tuple(l.shape), l.dtype)
lf = l.float()
print('label min/max/mean/std', float(lf.min()), float(lf.max()), float(lf.mean()), float(lf.std()))
ff = f.float()
print('feat min/max', float(ff.min()), float(ff.max()))
print('feat per-axis min', ff.amin(dim=(0,1)).tolist())
print('feat per-axis max', ff.amax(dim=(0,1)).tolist())
print('feat per-axis mean', ff.mean(dim=(0,1)).tolist())
print('feat per-axis std', ff.std(dim=(0,1)).tolist())
"
== train == features (100, 2048, 3) torch.float16 labels (100, 1) torch.float16 label min/max/mean/std 0.207763671875 2.21875 0.7475781440734863 0.3894648551940918 feat min/max -0.5 0.5 feat per-axis min [-0.320556640625, -0.5, -0.2279052734375] feat per-axis max [0.322998046875, 0.5, 0.22705078125] feat per-axis mean [-0.013504491187632084, 0.007462748326361179, -0.0709712877869606] feat per-axis std [0.14211857318878174, 0.2701786458492279, 0.0871192067861557] == val == features (32, 2048, 3) torch.float16 labels (32, 1) torch.float16 label min/max/mean/std 0.293701171875 2.072265625 0.7621994018554688 0.41264674067497253 feat min/max -0.5 0.5 feat per-axis min [-0.3095703125, -0.5, -0.2279052734375] feat per-axis max [0.31005859375, 0.5, 0.2275390625] feat per-axis mean [-0.005372888408601284, 0.005196526646614075, -0.07424016296863556] feat per-axis std [0.1418035924434662, 0.2656756639480591, 0.08837021142244339] == public_test == features (16, 2048, 3) torch.float16 labels (16, 1) torch.float16 label min/max/mean/std 0.1983642578125 1.44921875 0.7296066284179688 0.327779084444046 feat min/max -0.5 0.5 feat per-axis min [-0.309326171875, -0.5, -0.1865234375] feat per-axis max [0.309326171875, 0.5, 0.1864013671875] feat per-axis mean [-0.013821225613355637, 0.011655600741505623, -0.07381976395845413] feat per-axis std [0.14321814477443695, 0.27090150117874146, 0.08569176495075226]
cd /app && echo "=== train manifest (first 3) ===" && head -n 3 data/train/manifest.jsonl && echo "=== val manifest (first 2) ===" && head -n 2 data/val/manifest.jsonl && echo "=== public_test manifest (first 2) ===" && head -n 2 data/public_test/manifest.jsonl && echo "=== counts ===" && wc -l data/*/manifest.jsonl
=== train manifest (first 3) ===
{"id": "train_0000", "source_bracket_id": 214, "n_points": 2048}
{"id": "train_0001", "source_bracket_id": 538, "n_points": 2048}
{"id": "train_0002", "source_bracket_id": 546, "n_points": 2048}
=== val manifest (first 2) ===
{"id": "val_0000", "source_bracket_id": 418, "n_points": 2048}
{"id": "val_0001", "source_bracket_id": 441, "n_points": 2048}
=== public_test manifest (first 2) ===
{"id": "public_test_0000", "source_bracket_id": 461, "n_points": 2048}
{"id": "public_test_0001", "source_bracket_id": 440, "n_points": 2048}
=== counts ===
16 data/public_test/manifest.jsonl
100 data/train/manifest.jsonl
32 data/val/manifest.jsonl
148 totalcd /app && python3 -c "
import torch
# Check for duplicate source brackets across splits
import json
ids = {}
for split in ['train','val','public_test']:
s = set()
with open(f'data/{split}/manifest.jsonl') as f:
for line in f:
d = json.loads(line)
s.add(d['source_bracket_id'])
ids[split] = s
print(split, 'n_unique_brackets', len(s))
print('train&val', ids['train'] & ids['val'])
print('train&test', ids['train'] & ids['public_test'])
print('val&test', ids['val'] & ids['public_test'])
"
train n_unique_brackets 100 val n_unique_brackets 32 public_test n_unique_brackets 16 train&val set() train&test set() val&test set()
cd /app && python3 -c "
import torch, platform
print('torch', torch.__version__)
print('cuda available', torch.cuda.is_available())
print('python', platform.python_version())
import os
print('cpus', os.cpu_count())
" && free -h 2>/dev/null | head -2torch 2.5.1+cu124
cuda available True
python 3.11.10
cpus 3
total used free shared buff/cache available
Mem: 30Gi 64Mi 29Gi 0B 29Mi 29Gicd /app && nvidia-smi --query-gpu=name,memory.total,memory.free --format=csv 2>/dev/null || echo "nvidia-smi not available"
name, memory.total [MiB], memory.free [MiB] NVIDIA H100 80GB HBM3, 81559 MiB, 80015 MiB
cd /app && python3 -c "
import torch
p = torch.load('data/train/samples.pt', weights_only=True)
f = p['features'].float(); l = p['labels'].float()
# per-sample bounding box extents
mn = f.amin(dim=1) # [N,3]
mx = f.amax(dim=1) # [N,3]
ext = mx - mn # [N,3] extent per axis
print('per-sample extent stats:')
print(' axis max extent per sample (should be ~1 if each fills unit cube):')
maxext = ext.max(dim=1).values
print(' min', float(maxext.min()), 'max', float(maxext.max()), 'mean', float(maxext.mean()))
print(' which axis is largest (count):', torch.bincount(ext.argmax(dim=1), minlength=3).tolist())
print('extent axis0: min/max', float(ext[:,0].min()), float(ext[:,0].max()))
print('extent axis1: min/max', float(ext[:,1].min()), float(ext[:,1].max()))
print('extent axis2: min/max', float(ext[:,2].min()), float(ext[:,2].max()))
"
per-sample extent stats: axis max extent per sample (should be ~1 if each fills unit cube): min 0.9912109375 max 1.0 mean 0.9991015791893005 which axis is largest (count): [0, 100, 0] extent axis0: min/max 0.502197265625 0.6435546875 extent axis1: min/max 0.9912109375 1.0 extent axis2: min/max 0.2757568359375 0.4549560546875
cd /app && python3 -c "
import torch, numpy as np
def feats(f):
mn=f.amin(dim=1); mx=f.amax(dim=1); ext=mx-mn
std=f.std(dim=1)
mean=f.mean(dim=1)
# pairwise distance-based 'size' proxies
feats = torch.cat([ext, std, mean.abs()], dim=1)
return feats
tr=torch.load('data/train/samples.pt',weights_only=True)
ftr=tr['features'].float(); ltr=tr['labels'].float().squeeze(1)
X=feats(ftr).numpy(); y=ltr.numpy()
names=['ext_x','ext_y','ext_z','std_x','std_y','std_z','|mean_x|','|mean_y|','|mean_z|']
print('Correlation of features with mass:')
for i,n in enumerate(names):
c=np.corrcoef(X[:,i],y)[0,1]
print(f' {n:10s}: {c:+.3f}')
# product of extents = bbox volume; since y~1, ~ext_x*ext_z
bboxvol = X[:,0]*X[:,1]*X[:,2]
print(' bbox_vol :', f'{np.corrcoef(bboxvol,y)[0,1]:+.3f}')
print(' ext_x*ext_z:', f'{np.corrcoef(X[:,0]*X[:,2],y)[0,1]:+.3f}')
"
Correlation of features with mass: ext_x : +0.234 ext_y : +0.143 ext_z : +0.177 std_x : +0.635 std_y : +0.149 std_z : +0.250 |mean_x| : -0.401 |mean_y| : +0.102 |mean_z| : -0.183 bbox_vol : +0.253 ext_x*ext_z: +0.249
cd /app && python3 -c "
import torch, numpy as np
def metrics(pred, true):
pred=np.asarray(pred,dtype=np.float64); true=np.asarray(true,dtype=np.float64)
rel_l2 = np.sqrt(np.sum((pred-true)**2))/np.sqrt(np.sum(true**2))
mae = np.mean(np.abs(pred-true))
maxe = np.max(np.abs(pred-true))
return rel_l2, mae, maxe
tr=torch.load('data/train/samples.pt',weights_only=True)['labels'].float().squeeze(1).numpy()
va=torch.load('data/val/samples.pt',weights_only=True)['labels'].float().squeeze(1).numpy()
te=torch.load('data/public_test/samples.pt',weights_only=True)['labels'].float().squeeze(1).numpy()
print('Constant(train mean) predictor:')
for name,arr in [('val',va),('test',te)]:
c=np.full_like(arr, tr.mean())
print(f' {name}: rel_l2={metrics(c,arr)[0]:.4f} mae={metrics(c,arr)[1]:.4f} maxe={metrics(c,arr)[2]:.4f}')
print('train mean',tr.mean(),'std',tr.std())
"
Constant(train mean) predictor: val: rel_l2=0.4706 mae=0.3024 maxe=1.3247 test: rel_l2=0.3995 mae=0.2607 maxe=0.7016 train mean 0.74757814 std 0.38751265
cd /app && python3 -c "import sklearn; print('sklearn', sklearn.__version__)" 2>&1 | head -1; python3 -c "import scipy; print('scipy', scipy.__version__)" 2>&1 | head -1Traceback (most recent call last): Traceback (most recent call last):
cd /app && python3 -c "
import torch, numpy as np
def metrics(pred, true):
pred=np.asarray(pred,np.float64); true=np.asarray(true,np.float64)
return (np.sqrt(np.sum((pred-true)**2))/np.sqrt(np.sum(true**2)),
np.mean(np.abs(pred-true)), np.max(np.abs(pred-true)))
def make_feats(f):
N=f.shape[0]; out=[]
mn=f.amin(1); mx=f.amax(1); ext=mx-mn
std=f.std(1); mean=f.mean(1)
out+= [ext,std,mean]
# PCA eigenvalues of centered cloud
fc=f-f.mean(1,keepdim=True)
cov=torch.einsum('npi,npj->nij',fc,fc)/f.shape[1]
evals=torch.linalg.eigvalsh(cov) # [N,3] ascending
out.append(evals)
# voxel occupancy at resolution R in [-0.5,0.5]
for R in (8,16):
idx=((f+0.5).clamp(0,0.999999)*R).long() # [N,2048,3]
lin=idx[...,0]*R*R+idx[...,1]*R+idx[...,2]
occ=torch.zeros(N, R*R*R)
for n in range(N):
occ[n, torch.unique(lin[n])]=1.0
out.append(occ.sum(1,keepdim=True)/(R*R*R)) # occupancy fraction
return torch.cat(out,1).numpy()
tr=torch.load('data/train/samples.pt',weights_only=True)
va=torch.load('data/val/samples.pt',weights_only=True)
Xtr=make_feats(tr['features'].float()); ytr=tr['labels'].float().squeeze(1).numpy()
Xva=make_feats(va['features'].float()); yva=va['labels'].float().squeeze(1).numpy()
mu=Xtr.mean(0); sd=Xtr.std(0)+1e-8
Xtr=(Xtr-mu)/sd; Xva=(Xva-mu)/sd
Xtr=np.c_[Xtr,np.ones(len(Xtr))]; Xva=np.c_[Xva,np.ones(len(Xva))]
for lam in [0.1,1.0,10.0]:
A=Xtr.T@Xtr+lam*np.eye(Xtr.shape[1]); b=Xtr.T@ytr
w=np.linalg.solve(A,b)
p=Xva@w
r=metrics(p,yva)
print(f'ridge lam={lam}: val rel_l2={r[0]:.4f} mae={r[1]:.4f} maxe={r[2]:.4f}')
"
ridge lam=0.1: val rel_l2=0.2062 mae=0.1444 maxe=0.4492 ridge lam=1.0: val rel_l2=0.2097 mae=0.1446 maxe=0.4972 ridge lam=10.0: val rel_l2=0.2203 mae=0.1496 maxe=0.5998
cd /app && python3 -c "
import torch
tr=torch.load('data/train/samples.pt',weights_only=True)
f=tr['features'].float()
N=f.shape[0]
def voxset(pts,R=16):
idx=((pts+0.5).clamp(0,0.999999)*R).long()
return set((idx[:,0]*R*R+idx[:,1]*R+idx[:,2]).tolist())
def iou_mirror(axis,R=16):
ious=[]
for n in range(N):
p=f[n].clone()
# recenter axis about its bbox center, mirror, recenter back
c=(p[:,axis].max()+p[:,axis].min())/2
pm=p.clone(); pm[:,axis]=2*c-pm[:,axis]
a=voxset(p,R); b=voxset(pm,R)
ious.append(len(a&b)/len(a|b))
return sum(ious)/len(ious)
for ax,name in [(0,'x'),(1,'y'),(2,'z')]:
print(f'mean voxel IoU original vs {name}-mirror: {iou_mirror(ax):.3f}')
"
mean voxel IoU original vs x-mirror: 0.453 mean voxel IoU original vs y-mirror: 0.760 mean voxel IoU original vs z-mirror: 0.189
cd /app && cat > /app/lib_train.py << 'PY'
from __future__ import annotations
import json, os, sys, copy
from pathlib import Path
import torch, torch.nn as nn, torch.nn.functional as F
import numpy as np
APP = Path('/app'); sys.path.insert(0, str(APP))
from candidate_model import build_model
def load_cfg():
with (APP/'task_config.json').open() as fh: return json.load(fh)
def load_split(name):
p = torch.load(APP/'data'/name/'samples.pt', weights_only=True)
return p['features'].float(), p['labels'].float()
def metrics(pred, true):
pred=np.asarray(pred,np.float64).ravel(); true=np.asarray(true,np.float64).ravel()
rel=np.sqrt(np.sum((pred-true)**2))/np.sqrt(np.sum(true**2))
mae=np.mean(np.abs(pred-true)); mx=np.max(np.abs(pred-true))
return rel, mae, mx
def augment(xb, cfg):
# xb: [B,N,3] raw coords in [-0.5,0.5]
B,N,_ = xb.shape
dev = xb.device
if cfg.get('reflect',True):
signs = (torch.randint(0,2,(B,1,3),device=dev).float()*2-1)
xb = xb*signs
if cfg.get('jitter',0)>0:
xb = xb + torch.randn_like(xb)*cfg['jitter']
if cfg.get('resample_p',0)>0:
do = torch.rand(B,device=dev) < cfg['resample_p']
if do.any():
idx = torch.randint(0,N,(B,N),device=dev)
xb_rs = torch.gather(xb, 1, idx.unsqueeze(-1).expand(-1,-1,3))
xb = torch.where(do.view(B,1,1), xb_rs, xb)
if cfg.get('scale_jit',0)>0:
s = 1.0 + (torch.rand(B,1,3,device=dev)*2-1)*cfg['scale_jit']
xb = xb*s
return xb
def update_bn(model, x_raw, fmean, fstd, bs=64):
# recompute BatchNorm running stats over data (normalized)
for m in model.modules():
if isinstance(m, nn.BatchNorm1d):
m.reset_running_stats(); m.momentum=None
model.train()
with torch.no_grad():
for i in range(0, x_raw.shape[0], bs):
xb=((x_raw[i:i+bs]-fmean)/fstd)
model(xb)
model.eval()
def train_once(Xtr_raw, ytr, Xva_raw, yva, cfg, device='cuda', verbose=False):
torch.manual_seed(cfg.get('seed',0)); np.random.seed(cfg.get('seed',0))
base=load_cfg()
fmean = Xtr_raw.reshape(-1,3).mean(0); fstd = Xtr_raw.reshape(-1,3).std(0).clamp_min(1e-6)
lmean = ytr.mean(0); lstd = ytr.std(0).clamp_min(1e-6)
fmean=fmean.to(device); fstd=fstd.to(device); lmean=lmean.to(device); lstd=lstd.to(device)
Xtr_raw=Xtr_raw.to(device); ytr=ytr.to(device)
Xva_raw=Xva_raw.to(device); yva_n=((yva.to(device)-lmean)/lstd)
model=build_model(base).to(device)
epochs=cfg['epochs']; bs=cfg['batch_size']
opt=torch.optim.AdamW(model.parameters(), lr=cfg['lr'], weight_decay=cfg['wd'])
warm=cfg.get('warmup',5)
def lr_at(ep):
if ep<warm: return (ep+1)/warm
t=(ep-warm)/max(1,epochs-warm); return 0.5*(1+np.cos(np.pi*t))
sched=torch.optim.lr_scheduler.LambdaLR(opt, lr_at)
n=Xtr_raw.shape[0]
swa_model=None; swa_n=0; swa_start=int(epochs*cfg.get('swa_frac',0.75))
best={'rel':1e9}; best_state=None
huber=cfg.get('huber',0.0)
for ep in range(epochs):
model.train(); perm=torch.randperm(n,device=device)
for s in range(0,n,bs):
idx=perm[s:s+bs]
xb=Xtr_raw[idx]; yb=((ytr[idx]-lmean)/lstd)
xb=augment(xb,cfg)
xb=(xb-fmean)/fstd
opt.zero_grad(set_to_none=True)
pred=model(xb)
if huber>0: loss=F.huber_loss(pred,yb,delta=huber)
else: loss=F.mse_loss(pred,yb)
loss.backward(); opt.step()
sched.step()
# SWA accumulate
if ep>=swa_start and (ep%cfg.get('swa_every',1)==0):
if swa_model is None: swa_model=copy.deepcopy(model); swa_n=1
else:
with torch.no_grad():
for ps,pm in zip(swa_model.parameters(),model.parameters()):
ps.mul_(swa_n/(swa_n+1)).add_(pm/(swa_n+1))
for bs_,bm in zip(swa_model.buffers(),model.buffers()):
…[truncated]written lib_train.py
cd /app && python3 -c "
import torch, numpy as np
from lib_train import load_split, train_once
Xtr,ytr=load_split('train'); Xva,yva=load_split('val')
base=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=150,warmup=5,swa_frac=0.7,seed=0,
reflect=True,jitter=0.01,resample_p=0.5,scale_jit=0.0,huber=0.0)
import time
for name,over in [('no_aug',dict(reflect=False,jitter=0,resample_p=0)),
('reflect_only',dict(jitter=0,resample_p=0)),
('full_aug',dict())]:
cfg=dict(base); cfg.update(over)
t=time.time(); r=train_once(Xtr,ytr,Xva,yva,cfg,verbose=False)
b=r['best']; s=r['swa']
print(f'{name:14s} best ep{b[\"ep\"]:3d} rel{b[\"rel\"]:.4f} mae{b[\"mae\"]:.4f} mx{b[\"mx\"]:.4f} | SWA rel{s[0]:.4f} mae{s[1]:.4f} mx{s[2]:.4f} | {time.time()-t:.1f}s')
"
[stderr]
Traceback (most recent call last):
File "<string>", line 3, in <module>
File "/app/lib_train.py", line 107
print(f' ep{ep:3d} lr{opt.param_groups[0][\"lr\"]:.1e} val rel{r[0]:.4f} mae{r[1]:.4f} mx{r[2]:.4f}')
^
SyntaxError: f-string expression part cannot include a backslashcommand: str_replace
path: /app/lib_train.py
old_str: if verbose and (ep%max(1,epochs//10)==0 or ep==epochs-1):
print(f' ep{ep:3d} lr{opt.param_groups[0][\"lr\"]:.1e} val rel{r[0]:.4f} mae{r[1]:.4f} mx{r[2]:.4f}')
new_str: if verbose and (ep%max(1,epochs//10)==0 or ep==epochs-1):
cur_lr=opt.param_groups[0]['lr']
print(' ep%3d lr%.1e val rel%.4f mae%.4f mx%.4f'%(ep,cur_lr,r[0],r[1],r[2]))The file /app/lib_train.py has been edited successfully.
cd /app && python3 -c "
import torch, numpy as np, time
from lib_train import load_split, train_once
Xtr,ytr=load_split('train'); Xva,yva=load_split('val')
base=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=150,warmup=5,swa_frac=0.7,seed=0,
reflect=True,jitter=0.01,resample_p=0.5,scale_jit=0.0,huber=0.0)
for name,over in [('no_aug',dict(reflect=False,jitter=0,resample_p=0)),
('reflect_only',dict(jitter=0,resample_p=0)),
('full_aug',dict())]:
cfg=dict(base); cfg.update(over)
t=time.time(); r=train_once(Xtr,ytr,Xva,yva,cfg,verbose=False)
b=r['best']; s=r['swa']
print('%-14s best ep%3d rel%.4f mae%.4f mx%.4f | SWA rel%.4f mae%.4f mx%.4f | %.1fs'%(name,b['ep'],b['rel'],b['mae'],b['mx'],s[0],s[1],s[2],time.time()-t))
"
no_aug best ep124 rel0.1874 mae0.1388 mx0.3726 | SWA rel0.1963 mae0.1458 mx0.3881 | 7.3s reflect_only best ep135 rel0.2747 mae0.1886 mx0.5796 | SWA rel0.5498 mae0.3925 mx0.9438 | 3.6s full_aug best ep124 rel0.2980 mae0.1998 mx0.7535 | SWA rel0.3615 mae0.2409 mx0.8913 | 4.9s
cd /app && cat > /app/cv.py << 'PY'
import torch, numpy as np, time, sys
from lib_train import load_split, train_once, metrics
Xtr,ytr=load_split('train'); Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0); y=torch.cat([ytr,yva],0)
N=X.shape[0]
g=torch.Generator().manual_seed(1234); perm=torch.randperm(N,generator=g)
K=6
folds=[perm[i::K] for i in range(K)] # stratified-ish by shuffle
def run(cfg, report='final'):
rels=[];maes=[];mxs=[]
allp=[];allt=[]
for k in range(K):
va_idx=folds[k]; tr_idx=torch.cat([folds[j] for j in range(K) if j!=k])
r=train_once(X[tr_idx],y[tr_idx],X[va_idx],y[va_idx],cfg)
# pick model
import torch as T
from candidate_model import build_model
from lib_train import load_cfg
fmean=r['fmean'].cuda();fstd=r['fstd'].cuda();lmean=r['lmean'].cuda();lstd=r['lstd'].cuda()
if report=='swa' and r['swa_model'] is not None:
m=r['swa_model']
elif report=='best':
m=build_model(load_cfg()).cuda(); m.load_state_dict(r['best_state']); m.eval()
else:
m=r['final_model']
m.eval()
with T.no_grad():
vp=(m((X[va_idx].cuda()-fmean)/fstd)*lstd+lmean).cpu()
allp.append(vp); allt.append(y[va_idx])
allp=torch.cat(allp).numpy().ravel(); allt=torch.cat(allt).numpy().ravel()
return metrics(allp,allt)
if __name__=='__main__':
base=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=150,warmup=5,swa_frac=0.7,seed=0,
reflect=False,jitter=0.0,resample_p=0.0,scale_jit=0.0,huber=0.0)
import json
configs=json.loads(sys.argv[1]) if len(sys.argv)>1 else [{}]
for over in configs:
cfg=dict(base); cfg.update(over)
t=time.time()
rf=run(cfg,'final');
print('FINAL %-40s relL2=%.4f mae=%.4f maxe=%.4f (%.0fs)'%(str(over),rf[0],rf[1],rf[2],time.time()-t),flush=True)
PY
python3 cv.py '[{"reflect":false,"jitter":0.0,"resample_p":0.0},{"reflect":true},{"jitter":0.01},{"resample_p":0.5},{"reflect":true,"jitter":0.01,"resample_p":0.5}]'FINAL {'reflect': False, 'jitter': 0.0, 'resample_p': 0.0} relL2=0.2752 mae=0.1734 maxe=0.8235 (24s)
FINAL {'reflect': True} relL2=0.3066 mae=0.1928 maxe=0.8836 (21s)
FINAL {'jitter': 0.01} relL2=0.3115 mae=0.1981 maxe=0.8161 (22s)
FINAL {'resample_p': 0.5} relL2=0.2619 mae=0.1694 maxe=0.7273 (33s)
FINAL {'reflect': True, 'jitter': 0.01, 'resample_p': 0.5} relL2=0.3685 mae=0.2315 maxe=1.0310 (40s)cd /app && python3 -c "
import torch, numpy as np
from lib_train import load_split, metrics
Xtr,ytr=load_split('train'); Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0); y=torch.cat([ytr,yva],0).squeeze(1)
def make_feats(f):
out=[]; mn=f.amin(1);mx=f.amax(1);ext=mx-mn;std=f.std(1);mean=f.mean(1)
out+=[ext,std,mean]
fc=f-f.mean(1,keepdim=True); cov=torch.einsum('npi,npj->nij',fc,fc)/f.shape[1]
out.append(torch.linalg.eigvalsh(cov))
for R in (8,16):
idx=((f+0.5).clamp(0,0.999999)*R).long(); lin=idx[...,0]*R*R+idx[...,1]*R+idx[...,2]
occ=torch.zeros(f.shape[0],R*R*R)
for n in range(f.shape[0]): occ[n,torch.unique(lin[n])]=1.0
out.append(occ.sum(1,keepdim=True)/(R*R*R))
return torch.cat(out,1).numpy()
Xf=make_feats(X); yv=y.numpy()
N=len(Xf); g=torch.Generator().manual_seed(1234); perm=torch.randperm(N,generator=g).numpy()
K=6; folds=[perm[i::K] for i in range(K)]
for lam in [0.3,1.0,3.0,10.0]:
allp=np.zeros(N)
for k in range(K):
va=folds[k]; tr=np.concatenate([folds[j] for j in range(K) if j!=k])
mu=Xf[tr].mean(0); sd=Xf[tr].std(0)+1e-8
A=(Xf[tr]-mu)/sd; B=(Xf[va]-mu)/sd
A=np.c_[A,np.ones(len(A))]; B=np.c_[B,np.ones(len(B))]
w=np.linalg.solve(A.T@A+lam*np.eye(A.shape[1]),A.T@yv[tr])
allp[va]=B@w
r=metrics(allp,yv)
print('ridge CV lam=%.1f relL2=%.4f mae=%.4f maxe=%.4f'%(lam,r[0],r[1],r[2]))
"
ridge CV lam=0.3 relL2=0.2244 mae=0.1522 maxe=0.5145 ridge CV lam=1.0 relL2=0.2240 mae=0.1508 maxe=0.5448 ridge CV lam=3.0 relL2=0.2257 mae=0.1505 maxe=0.5708 ridge CV lam=10.0 relL2=0.2404 mae=0.1565 maxe=0.6337
cd /app && timeout 600 python3 cv.py '[
{"resample_p":0.5,"epochs":300,"lr":1e-3,"wd":1e-4,"batch_size":16},
{"resample_p":0.5,"epochs":300,"lr":1e-3,"wd":1e-3,"batch_size":16},
{"resample_p":0.5,"epochs":300,"lr":2e-3,"wd":1e-3,"batch_size":32},
{"resample_p":0.5,"epochs":500,"lr":1e-3,"wd":1e-3,"batch_size":16},
{"resample_p":0.5,"epochs":300,"lr":1e-3,"wd":3e-3,"batch_size":16}
]'[stderr]
FINAL {'resample_p': 0.5, 'epochs': 300, 'lr': 0.001, 'wd': 0.0001, 'batch_size': 16} relL2=0.2470 mae=0.1557 maxe=0.6795 (88s)
FINAL {'resample_p': 0.5, 'epochs': 300, 'lr': 0.001, 'wd': 0.001, 'batch_size': 16} relL2=0.2593 mae=0.1662 maxe=0.7995 (83s)
FINAL {'resample_p': 0.5, 'epochs': 300, 'lr': 0.002, 'wd': 0.001, 'batch_size': 32} relL2=0.2478 mae=0.1562 maxe=0.6662 (66s)Shell command timed out.
Tool interaction exceeded 300 seconds.restart: true
<system>tool has been restarted.</system>
cd /app && nohup python3 cv.py '[
{"resample_p":0.5,"epochs":600,"lr":1e-3,"wd":1e-3,"batch_size":16},
{"resample_p":0.5,"epochs":400,"lr":1.5e-3,"wd":3e-4,"batch_size":16}
]' > /app/sweep1.log 2>&1 &
echo "started PID $!"started PID 312
cd /app && python3 -c "
from lib_train import load_cfg
from candidate_model import build_model
m=build_model(load_cfg()); print('params', sum(p.numel() for p in m.parameters()))
" && sleep 60 && echo "---sweep1 so far---" && cat sweep1.logparams 92289 ---sweep1 so far---
command: str_replace
path: /app/lib_train.py
old_str: torch.manual_seed(cfg.get('seed',0)); np.random.seed(cfg.get('seed',0))
base=load_cfg()
fmean = Xtr_raw.reshape(-1,3).mean(0); fstd = Xtr_raw.reshape(-1,3).std(0).clamp_min(1e-6)
new_str: base=load_cfg()
fmean = Xtr_raw.reshape(-1,3).mean(0); fstd = Xtr_raw.reshape(-1,3).std(0).clamp_min(1e-6)
nm=cfg.get('norm','aniso')
if nm=='iso':
fstd = fstd.mean().repeat(3)
elif nm=='none':
fmean=torch.zeros(3); fstd=torch.ones(3)The file /app/lib_train.py has been edited successfully.
command: str_replace
path: /app/lib_train.py
old_str: Xva_raw=Xva_raw.to(device); yva_n=((yva.to(device)-lmean)/lstd)
model=build_model(base).to(device)
new_str: Xva_raw=Xva_raw.to(device); yva_n=((yva.to(device)-lmean)/lstd)
torch.manual_seed(cfg.get('init_seed',cfg.get('seed',0)))
model=build_model(base).to(device)
torch.manual_seed(cfg.get('seed',0)); np.random.seed(cfg.get('seed',0))The file /app/lib_train.py has been edited successfully.
cd /app && sleep 120 && cat sweep1.log
FINAL {'resample_p': 0.5, 'epochs': 600, 'lr': 0.001, 'wd': 0.001, 'batch_size': 16} relL2=0.2428 mae=0.1585 maxe=0.6666 (136s)cd /app && cat > /app/cv_ens.py << 'PY'
import torch, numpy as np, time, sys, json, copy
from lib_train import load_split, train_once, metrics, update_bn, load_cfg
from candidate_model import build_model
Xtr,ytr=load_split('train'); Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0); y=torch.cat([ytr,yva],0)
N=X.shape[0]
g=torch.Generator().manual_seed(1234); perm=torch.randperm(N,generator=g)
K=6; folds=[perm[i::K] for i in range(K)]
def eval_model(m,Xe,fmean,fstd,lmean,lstd):
m.eval()
with torch.no_grad():
return (m((Xe.cuda()-fmean)/fstd)*lstd+lmean).cpu()
def run(cfg, M=4):
p_ens=[]; p_soup=[]; t_all=[]
for k in range(K):
va=folds[k]; tr=torch.cat([folds[j] for j in range(K) if j!=k])
models=[]; norm=None
for s in range(M):
c=dict(cfg); c['seed']=100+s; c['init_seed']=200+s
r=train_once(X[tr],y[tr],X[va],y[va],c)
models.append(r['final_model'])
norm=(r['fmean'].cuda(),r['fstd'].cuda(),r['lmean'].cuda(),r['lstd'].cuda())
fmean,fstd,lmean,lstd=norm
# output ensemble
preds=torch.stack([eval_model(m,X[va],*norm) for m in models],0).mean(0)
p_ens.append(preds)
# weight soup
soup=copy.deepcopy(models[0])
sd={kk: torch.stack([m.state_dict()[kk].float() for m in models],0).mean(0) for kk in soup.state_dict()}
soup.load_state_dict(sd)
update_bn(soup, X[tr].cuda(), fmean, fstd)
p_soup.append(eval_model(soup,X[va],*norm))
t_all.append(y[va])
t=torch.cat(t_all).numpy().ravel()
pe=torch.cat(p_ens).numpy().ravel(); ps=torch.cat(p_soup).numpy().ravel()
return metrics(pe,t), metrics(ps,t)
if __name__=='__main__':
base=dict(batch_size=16,lr=1e-3,wd=1e-3,epochs=300,warmup=5,swa_frac=0.7,
reflect=False,jitter=0.0,resample_p=0.5,scale_jit=0.0,huber=0.0)
cfgs=json.loads(sys.argv[1])
M=int(sys.argv[2]) if len(sys.argv)>2 else 4
for over in cfgs:
cfg=dict(base); cfg.update(over)
t=time.time(); (re,ps)=run(cfg,M)
print('ENS(M=%d) %-30s relL2=%.4f mae=%.4f maxe=%.4f | SOUP relL2=%.4f mae=%.4f maxe=%.4f (%.0fs)'%(M,str(over),re[0],re[1],re[2],ps[0],ps[1],ps[2],time.time()-t),flush=True)
PY
nohup python3 cv_ens.py '[{"epochs":300}]' 4 > /app/ens.log 2>&1 &
echo "started $!"started 371
cd /app && sleep 180 && cat ens.log
<system>Tool ran without output or errors</system>
cd /app && sleep 90 && cat ens.log && echo "=== proc ===" && ps aux | grep cv_ens | grep -v grep | head
=== proc ===
h2tagent 371 96.3 16.0 20161408 5058808 ? Rl 21:12 4:28 python3 cv_ens.py [{"epochs":300}] 4cd /app && sleep 120 && cat ens.log
ENS(M=4) {'epochs': 300} relL2=0.2628 mae=0.1601 maxe=0.7442 | SOUP relL2=0.4723 mae=0.3030 maxe=1.5169 (285s)cd /app && timeout 200 python3 -c "
import torch, numpy as np
from lib_train import load_split, train_once, metrics
Xtr,ytr=load_split('train'); Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0); y=torch.cat([ytr,yva],0)
# train on ALL, measure train fit (no aug), capacity probe
for aug in [dict(resample_p=0.0),dict(resample_p=0.5)]:
cfg=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=400,warmup=5,swa_frac=0.9,seed=0,
reflect=False,jitter=0.0,scale_jit=0.0,huber=0.0); cfg.update(aug)
r=train_once(X,y,X,y,cfg) # eval on train itself
m=r['final_model']; fmean=r['fmean'].cuda();fstd=r['fstd'].cuda();lmean=r['lmean'].cuda();lstd=r['lstd'].cuda()
m.eval()
with torch.no_grad(): p=(m((X.cuda()-fmean)/fstd)*lstd+lmean).cpu()
tr=metrics(p,y)
print('aug',aug,'TRAIN-fit relL2=%.4f mae=%.4f maxe=%.4f'%tr)
"aug {'resample_p': 0.0} TRAIN-fit relL2=0.0137 mae=0.0096 maxe=0.0332
aug {'resample_p': 0.5} TRAIN-fit relL2=0.0258 mae=0.0170 maxe=0.0637command: str_replace
path: /app/lib_train.py
old_str: huber=cfg.get('huber',0.0)
for ep in range(epochs):
model.train(); perm=torch.randperm(n,device=device)
for s in range(0,n,bs):
idx=perm[s:s+bs]
xb=Xtr_raw[idx]; yb=((ytr[idx]-lmean)/lstd)
xb=augment(xb,cfg)
xb=(xb-fmean)/fstd
opt.zero_grad(set_to_none=True)
pred=model(xb)
new_str: huber=cfg.get('huber',0.0)
drop=cfg.get('dropout',0.0)
traj=[]; rec_every=cfg.get('rec_every',0)
def fwd(xb):
if drop<=0: return model(xb)
x=xb.transpose(1,2).contiguous()
x=model.feature(x); x=model.lift(x); x=x.max(dim=2).values
x=F.dropout(x,p=drop,training=model.training)
return model.head(x)
for ep in range(epochs):
model.train(); perm=torch.randperm(n,device=device)
for s in range(0,n,bs):
idx=perm[s:s+bs]
xb=Xtr_raw[idx]; yb=((ytr[idx]-lmean)/lstd)
xb=augment(xb,cfg)
xb=(xb-fmean)/fstd
opt.zero_grad(set_to_none=True)
pred=fwd(xb)The file /app/lib_train.py has been edited successfully.
command: str_replace
path: /app/lib_train.py
old_str: if verbose and (ep%max(1,epochs//10)==0 or ep==epochs-1):
new_str: if rec_every and ((ep+1)%rec_every==0 or ep==epochs-1):
traj.append((ep, vp.detach().cpu().clone()))
if verbose and (ep%max(1,epochs//10)==0 or ep==epochs-1):The file /app/lib_train.py has been edited successfully.
command: str_replace
path: /app/lib_train.py
old_str: return {'best':best,'best_state':best_state,'swa':swa_res,'swa_model':swa_model,
new_str: return {'best':best,'best_state':best_state,'swa':swa_res,'swa_model':swa_model,'traj':traj,The file /app/lib_train.py has been edited successfully.
cd /app && cat > /app/cv_traj.py << 'PY'
import torch, numpy as np, time, sys, json
from lib_train import load_split, train_once, metrics
Xtr,ytr=load_split('train'); Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0); y=torch.cat([ytr,yva],0)
N=X.shape[0]
g=torch.Generator().manual_seed(1234); perm=torch.randperm(N,generator=g)
K=6; folds=[perm[i::K] for i in range(K)]
def run(cfg):
trajs=[]; trues=[]; eps=None
for k in range(K):
va=folds[k]; tr=torch.cat([folds[j] for j in range(K) if j!=k])
r=train_once(X[tr],y[tr],X[va],y[va],cfg)
trajs.append(r['traj']); trues.append(y[va].numpy().ravel())
eps=[e for e,_ in r['traj']]
# pool per epoch index
best=None
for ei in range(len(eps)):
preds=np.concatenate([trajs[k][ei][1].numpy().ravel() for k in range(K)])
tr=np.concatenate(trues)
m=metrics(preds,tr)
if best is None or m[0]<best[1][0]: best=(eps[ei],m)
return best
if __name__=='__main__':
base=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=400,warmup=5,swa_frac=0.9,seed=0,
reflect=False,jitter=0.0,resample_p=0.0,scale_jit=0.0,huber=0.0,rec_every=20)
cfgs=json.loads(sys.argv[1])
for over in cfgs:
cfg=dict(base); cfg.update(over)
t=time.time(); ep,m=run(cfg)
print('%-55s best_ep=%3d relL2=%.4f mae=%.4f maxe=%.4f (%.0fs)'%(str(over),ep,m[0],m[1],m[2],time.time()-t),flush=True)
PY
nohup python3 cv_traj.py '[
{"wd":1e-4,"resample_p":0.0},
{"wd":1e-3,"resample_p":0.0},
{"wd":3e-3,"resample_p":0.0},
{"wd":1e-2,"resample_p":0.0},
{"wd":3e-3,"resample_p":0.5},
{"wd":3e-3,"dropout":0.2,"resample_p":0.0}
]' > /app/traj.log 2>&1 &
echo "started $!"started 465
cd /app && sleep 150 && cat traj.log
{'wd': 0.0001, 'resample_p': 0.0} best_ep=119 relL2=0.2564 mae=0.1594 maxe=0.7497 (72s)
{'wd': 0.001, 'resample_p': 0.0} best_ep=119 relL2=0.2655 mae=0.1646 maxe=0.8156 (63s)cd /app && sleep 180 && cat traj.log
{'wd': 0.0001, 'resample_p': 0.0} best_ep=119 relL2=0.2564 mae=0.1594 maxe=0.7497 (72s)
{'wd': 0.001, 'resample_p': 0.0} best_ep=119 relL2=0.2655 mae=0.1646 maxe=0.8156 (63s)
{'wd': 0.003, 'resample_p': 0.0} best_ep=119 relL2=0.2602 mae=0.1668 maxe=0.7474 (69s)
{'wd': 0.01, 'resample_p': 0.0} best_ep=119 relL2=0.2715 mae=0.1682 maxe=0.7747 (61s)cd /app && sleep 140 && cat traj.log
{'wd': 0.0001, 'resample_p': 0.0} best_ep=119 relL2=0.2564 mae=0.1594 maxe=0.7497 (72s)
{'wd': 0.001, 'resample_p': 0.0} best_ep=119 relL2=0.2655 mae=0.1646 maxe=0.8156 (63s)
{'wd': 0.003, 'resample_p': 0.0} best_ep=119 relL2=0.2602 mae=0.1668 maxe=0.7474 (69s)
{'wd': 0.01, 'resample_p': 0.0} best_ep=119 relL2=0.2715 mae=0.1682 maxe=0.7747 (61s)
{'wd': 0.003, 'resample_p': 0.5} best_ep=299 relL2=0.2583 mae=0.1599 maxe=0.8394 (120s)
{'wd': 0.003, 'dropout': 0.2, 'resample_p': 0.0} best_ep=179 relL2=0.2774 mae=0.1783 maxe=0.8049 (89s)cd /app && cat > /app/oof.py << 'PY'
import torch, numpy as np
from lib_train import load_split, train_once, metrics
Xtr,ytr=load_split('train'); Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0); y=torch.cat([ytr,yva],0); yv=y.numpy().ravel()
N=X.shape[0]
g=torch.Generator().manual_seed(1234); perm=torch.randperm(N,generator=g)
K=6; folds=[perm[i::K] for i in range(K)]
def make_feats(f):
out=[];mn=f.amin(1);mx=f.amax(1);ext=mx-mn;std=f.std(1);mean=f.mean(1);out+=[ext,std,mean]
fc=f-f.mean(1,keepdim=True);cov=torch.einsum('npi,npj->nij',fc,fc)/f.shape[1]
out.append(torch.linalg.eigvalsh(cov))
for R in (8,16):
idx=((f+0.5).clamp(0,0.999999)*R).long();lin=idx[...,0]*R*R+idx[...,1]*R+idx[...,2]
occ=torch.zeros(f.shape[0],R*R*R)
for n in range(f.shape[0]): occ[n,torch.unique(lin[n])]=1.0
out.append(occ.sum(1,keepdim=True)/(R*R*R))
return torch.cat(out,1).numpy()
Xf=make_feats(X)
oof_pn=np.zeros(N); oof_rg=np.zeros(N)
cfg=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=120,warmup=5,swa_frac=0.9,seed=0,
reflect=False,jitter=0.0,resample_p=0.0,scale_jit=0.0,huber=0.0)
for k in range(K):
va=folds[k].numpy(); tr=torch.cat([folds[j] for j in range(K) if j!=k]).numpy()
# ridge
mu=Xf[tr].mean(0);sd=Xf[tr].std(0)+1e-8
A=np.c_[(Xf[tr]-mu)/sd,np.ones(len(tr))]; B=np.c_[(Xf[va]-mu)/sd,np.ones(len(va))]
w=np.linalg.solve(A.T@A+1.0*np.eye(A.shape[1]),A.T@yv[tr]); oof_rg[va]=B@w
# pointnet
r=train_once(X[torch.tensor(tr)],y[torch.tensor(tr)],X[torch.tensor(va)],y[torch.tensor(va)],cfg)
m=r['final_model'];fm=r['fmean'].cuda();fs=r['fstd'].cuda();lm=r['lmean'].cuda();ls=r['lstd'].cuda();m.eval()
with torch.no_grad(): oof_pn[va]=(m((X[torch.tensor(va)].cuda()-fm)/fs)*ls+lm).cpu().numpy().ravel()
np.save('/app/oof_pn.npy',oof_pn); np.save('/app/oof_rg.npy',oof_rg); np.save('/app/oof_y.npy',yv)
print('PointNet OOF relL2=%.4f mae=%.4f maxe=%.4f'%metrics(oof_pn,yv))
print('Ridge OOF relL2=%.4f mae=%.4f maxe=%.4f'%metrics(oof_rg,yv))
err_pn=oof_pn-yv; err_rg=oof_rg-yv
print('err corr PN vs RG:',np.corrcoef(err_pn,err_rg)[0,1])
for a in [0.3,0.4,0.5,0.6,0.7]:
bl=a*oof_pn+(1-a)*oof_rg
print('blend a=%.1f relL2=%.4f mae=%.4f maxe=%.4f'%(a,*metrics(bl,yv)))
PY
nohup python3 oof.py > /app/oof.log 2>&1 &
echo "started $!"started 504
cd /app && sleep 120 && cat oof.log
PointNet OOF relL2=0.2800 mae=0.1777 maxe=0.8995 Ridge OOF relL2=0.2240 mae=0.1508 maxe=0.5448 err corr PN vs RG: 0.5500745809405904 blend a=0.3 relL2=0.2147 mae=0.1399 maxe=0.6326 blend a=0.4 relL2=0.2171 mae=0.1409 maxe=0.6708 blend a=0.5 relL2=0.2222 mae=0.1431 maxe=0.7089 blend a=0.6 relL2=0.2298 mae=0.1467 maxe=0.7470 blend a=0.7 relL2=0.2396 mae=0.1526 maxe=0.7851
cd /app && cat > /app/feat.py << 'PY'
import torch, numpy as np
from lib_train import load_split, metrics
def rich_feats(f, res=(4,6,8,12,16), grids=(6,8)):
N=f.shape[0]; out=[]
mn=f.amin(1);mx=f.amax(1);ext=mx-mn;std=f.std(1);mean=f.mean(1)
out+=[ext,std,mean,mean.abs()]
# quantiles per axis
qs=torch.tensor([0.1,0.25,0.5,0.75,0.9])
for ax in range(3):
out.append(torch.quantile(f[:,:,ax],qs.to(f),dim=1).T) # [N,5]
# PCA eigenvalues
fc=f-f.mean(1,keepdim=True);cov=torch.einsum('npi,npj->nij',fc,fc)/f.shape[1]
ev=torch.linalg.eigvalsh(cov); out.append(ev)
out.append((ev[:,:1]/ (ev.sum(1,keepdim=True)+1e-9))) # anisotropy
# radial distance stats
rad=f.norm(dim=2); out.append(torch.stack([rad.mean(1),rad.std(1),rad.amax(1)],1))
# occupancy fraction at multiple res
for R in res:
idx=((f+0.5).clamp(0,0.999999)*R).long();lin=idx[...,0]*R*R+idx[...,1]*R+idx[...,2]
occ=torch.zeros(N,R*R*R)
for n in range(N): occ[n,torch.unique(lin[n])]=1.0
out.append(occ.sum(1,keepdim=True)/(R*R*R))
# full occupancy grids (spatial) at small res
for R in grids:
idx=((f+0.5).clamp(0,0.999999)*R).long();lin=idx[...,0]*R*R+idx[...,1]*R+idx[...,2]
occ=torch.zeros(N,R*R*R)
for n in range(N): occ[n,torch.unique(lin[n])]=1.0
out.append(occ)
return torch.cat(out,1).numpy()
def ridge_oof(Xf,yv,folds,lam):
N=len(yv);oof=np.zeros(N)
for k in range(len(folds)):
va=folds[k].numpy();tr=np.concatenate([folds[j].numpy() for j in range(len(folds)) if j!=k])
mu=Xf[tr].mean(0);sd=Xf[tr].std(0)+1e-8
A=np.c_[(Xf[tr]-mu)/sd,np.ones(len(tr))];B=np.c_[(Xf[va]-mu)/sd,np.ones(len(va))]
w=np.linalg.solve(A.T@A+lam*np.eye(A.shape[1]),A.T@yv[tr]);oof[va]=B@w
return oof
if __name__=='__main__':
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0);y=torch.cat([ytr,yva],0);yv=y.numpy().ravel()
N=X.shape[0];g=torch.Generator().manual_seed(1234);perm=torch.randperm(N,generator=g)
K=6;folds=[perm[i::K] for i in range(K)]
Xf=rich_feats(X); print('feat dim',Xf.shape[1])
np.save('/app/richfeat.npy',Xf)
for lam in [1,3,10,30,100]:
oof=ridge_oof(Xf,yv,folds,lam)
print('lam=%5.0f relL2=%.4f mae=%.4f maxe=%.4f'%(lam,*metrics(oof,yv)))
PY
python3 feat.pyfeat dim 767 lam= 1 relL2=0.2783 mae=0.1785 maxe=0.8402 lam= 3 relL2=0.2677 mae=0.1743 maxe=0.8191 lam= 10 relL2=0.2610 mae=0.1744 maxe=0.6979 lam= 30 relL2=0.3062 mae=0.2172 maxe=0.7722 lam= 100 relL2=0.4893 mae=0.3724 maxe=1.0388
cd /app && python3 -c "
import torch, numpy as np
from feat import rich_feats, ridge_oof
from lib_train import load_split, metrics
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0);y=torch.cat([ytr,yva],0);yv=y.numpy().ravel()
N=X.shape[0];g=torch.Generator().manual_seed(1234);perm=torch.randperm(N,generator=g)
K=6;folds=[perm[i::K] for i in range(K)]
# compact: no full grids
Xf=rich_feats(X, res=(3,4,5,6,8,10,12,16,20), grids=())
print('dim',Xf.shape[1])
np.save('/app/compactfeat.npy',Xf)
for lam in [0.3,1,2,3,5,10]:
oof=ridge_oof(Xf,yv,folds,lam)
print('lam=%5.1f relL2=%.4f mae=%.4f maxe=%.4f'%(lam,*metrics(oof,yv)))
"dim 43 lam= 0.3 relL2=0.2347 mae=0.1615 maxe=0.5321 lam= 1.0 relL2=0.2290 mae=0.1603 maxe=0.5360 lam= 2.0 relL2=0.2246 mae=0.1571 maxe=0.5239 lam= 3.0 relL2=0.2223 mae=0.1548 maxe=0.5114 lam= 5.0 relL2=0.2207 mae=0.1519 maxe=0.4900 lam= 10.0 relL2=0.2259 mae=0.1546 maxe=0.4980
cd /app && cat > /app/distill.py << 'PY'
import torch, numpy as np, time, sys, json, copy
import torch.nn as nn, torch.nn.functional as F
from lib_train import load_split, metrics, update_bn
from feat import rich_feats
from candidate_model import build_model
from lib_train import load_cfg
dev='cuda'
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0);Y=torch.cat([ytr,yva],0);yv=Y.numpy().ravel()
N=X.shape[0];g=torch.Generator().manual_seed(1234);perm=torch.randperm(N,generator=g)
K=6;folds=[perm[i::K] for i in range(K)]
def fit_ridge(Xf_tr, y_tr, lam=5.0):
mu=Xf_tr.mean(0);sd=Xf_tr.std(0)+1e-8
A=np.c_[(Xf_tr-mu)/sd,np.ones(len(Xf_tr))]
w=np.linalg.solve(A.T@A+lam*np.eye(A.shape[1]),A.T@y_tr)
return (mu,sd,w)
def ridge_pred(rg,Xf):
mu,sd,w=rg; B=np.c_[(Xf-mu)/sd,np.ones(len(Xf))]; return B@w
def aug_cloud(c, reflect=True, jitter=0.006, resample=True):
# c: [P,3]
P=c.shape[0]; out=c.clone()
if reflect:
s=(torch.randint(0,2,(1,3),device=c.device).float()*2-1); out=out*s
if resample:
idx=torch.randint(0,P,(P,),device=c.device); out=out[idx]
if jitter>0: out=out+torch.randn_like(out)*jitter
return out
def build_distill_set(clouds, rg, n_copies, aug_kw):
# clouds: [M,P,3] on gpu; returns Xaug [M*n,P,3] cpu, teacher labels [M*n,1]
Xs=[];
for _ in range(n_copies):
batch=torch.stack([aug_cloud(clouds[i],**aug_kw) for i in range(clouds.shape[0])],0)
Xs.append(batch)
Xaug=torch.cat(Xs,0)
Xf=rich_feats(Xaug.cpu())
tl=ridge_pred(rg,Xf).astype(np.float32)
return Xaug.cpu(), torch.tensor(tl).unsqueeze(1)
def train_student(Xd, Yd, Xva_e, yva_e, Xtrue=None, Ytrue=None, lam_true=0.0,
epochs=120, lr=1e-3, wd=1e-4, bs=64, seed=0):
torch.manual_seed(seed); np.random.seed(seed)
fmean=Xd.reshape(-1,3).mean(0).to(dev); fstd=Xd.reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Yd.mean(0).to(dev); lstd=Yd.std(0).clamp_min(1e-6).to(dev)
Xd=Xd.to(dev); Yd=((Yd.to(dev)-lmean)/lstd)
if Xtrue is not None: Xtrue=Xtrue.to(dev); Ytrue=((Ytrue.to(dev)-lmean)/lstd)
model=build_model(load_cfg()).to(dev)
opt=torch.optim.AdamW(model.parameters(),lr=lr,weight_decay=wd)
sched=torch.optim.lr_scheduler.LambdaLR(opt,lambda e:0.5*(1+np.cos(np.pi*e/epochs)))
n=Xd.shape[0]; best=(1e9,)
for ep in range(epochs):
model.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs]
xb=(Xd[idx]-fmean)/fstd; yb=Yd[idx]
opt.zero_grad(set_to_none=True)
loss=F.mse_loss(model(xb),yb)
if lam_true>0 and Xtrue is not None:
ti=torch.randint(0,Xtrue.shape[0],(min(bs,Xtrue.shape[0]),),device=dev)
loss=loss+lam_true*F.mse_loss(model((Xtrue[ti]-fmean)/fstd),Ytrue[ti])
loss.backward();opt.step()
sched.step()
model.eval()
with torch.no_grad(): p=(model((Xva_e.to(dev)-fmean)/fstd)*lstd+lmean).cpu().numpy().ravel()
return p, model, (fmean,fstd,lmean,lstd)
if __name__=='__main__':
cfg=json.loads(sys.argv[1]) if len(sys.argv)>1 else {}
n_copies=cfg.get('n_copies',20); lam_true=cfg.get('lam_true',0.0)
aug_kw=dict(reflect=cfg.get('reflect',True),jitter=cfg.get('jitter',0.006),resample=cfg.get('resample',True))
oof=np.zeros(N)
t0=time.time()
for k in range(K):
va=folds[k];tr=torch.cat([folds[j] for j in range(K) if j!=k])
Xf_tr=rich_feats(X[tr]); rg=fit_ridge(Xf_tr,Y[tr].numpy().ravel(),lam=cfg.get('lam',5.0))
Xd,Yd=build_distill_set(X[tr].to(dev),rg,n_copies,aug_kw)
p,_,_=train_student(Xd,Yd,X[va],Y[va].numpy().ravel(),
Xtrue=X[tr],Ytrue=Y[tr],lam_true=lam_true,
epochs=cfg.get('epochs',120),wd=cfg.get('wd',1e-4),seed=0)
oof[va]=p
print('DISTILL %s relL2=%.4f mae=%.4f maxe=%.4f (%.0fs)'%(str(cfg),*metrics(oof,yv),time.time()-t0),flu
…[truncated]started 551
cd /app && sleep 90 && cat distill.log && echo "--- still running? ---" && ps aux|grep distill.py|grep -v grep|wc -l
--- still running? --- 1
cd /app && sleep 120 && cat distill.log && echo "--- running? ---" && ps aux|grep distill.py|grep -v grep|wc -l
--- running? --- 1
cd /app && sleep 120 && cat distill.log && echo "--- running? ---" && ps aux|grep distill.py|grep -v grep|wc -l
--- running? --- 1
cd /app && kill 551 2>/dev/null; sleep 2; python3 -c "
import torch, time
from feat import rich_feats
from lib_train import load_split
X,_=load_split('train')
c=X[:110]
import time
t=time.time(); f=rich_feats(c); print('110 clouds: %.1fs dim %d'%(time.time()-t, f.shape[1]))
big=c.repeat(20,1,1); print('big',big.shape)
t=time.time(); f=rich_feats(big); print('2200 clouds: %.1fs'%(time.time()-t))
"110 clouds: 0.1s dim 767
big torch.Size([2000, 2048, 3])
2200 clouds: 2.9s
[stderr]
[5]+ Terminated nohup python3 distill.py '{"n_copies":20,"lam_true":0.0,"reflect":true,"jitter":0.006}' > /app/distill.log 2>&1cd /app && python3 - << 'PY'
import re
s=open('distill.py').read()
s=s.replace("from feat import rich_feats","from feat import rich_feats as _rf\ndef rich_feats(x):\n return _rf(x, res=(3,4,5,6,8,10,12,16,20), grids=())")
# add per-fold timing print
s=s.replace(" oof[va]=p\n"," oof[va]=p\n print(' fold %d done relL2(fold)=%.4f t=%.0fs'%(k, (np.sqrt(((p-Y[va].numpy().ravel())**2).sum()/ (Y[va].numpy().ravel()**2).sum())), time.time()-t0),flush=True)\n")
open('distill.py','w').write(s)
print("patched")
PY
nohup python3 distill.py '{"n_copies":20,"lam_true":0.0,"reflect":true,"jitter":0.006,"lam":5.0,"epochs":100}' > /app/distill.log 2>&1 &
echo "started $!"patched started 614
cd /app && sleep 120 && cat distill.log
fold 0 done relL2(fold)=0.2948 t=57s fold 1 done relL2(fold)=0.4609 t=94s
cd /app && kill 614 2>/dev/null; sleep 2
nohup python3 cv_traj.py '[
{"wd":1e-4,"norm":"iso"},
{"wd":1e-4,"norm":"none"},
{"wd":3e-4,"norm":"iso"},
{"wd":1e-4,"norm":"iso","resample_p":0.5}
]' > /app/norm.log 2>&1 &
echo "started $!"started 637
[stderr]
[5]+ Terminated nohup python3 distill.py '{"n_copies":20,"lam_true":0.0,"reflect":true,"jitter":0.006,"lam":5.0,"epochs":100}' > /app/distill.log 2>&1cd /app && sleep 200 && cat norm.log
{'wd': 0.0001, 'norm': 'iso'} best_ep=159 relL2=0.2652 mae=0.1615 maxe=0.7522 (88s)
{'wd': 0.0001, 'norm': 'none'} best_ep=279 relL2=0.2713 mae=0.1668 maxe=0.7666 (77s)cd /app && cat > /app/curve.py << 'PY'
import torch, numpy as np, json, sys
from lib_train import load_split, train_once, metrics
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0);y=torch.cat([ytr,yva],0)
N=X.shape[0];g=torch.Generator().manual_seed(1234);perm=torch.randperm(N,generator=g)
K=6;folds=[perm[i::K] for i in range(K)]
cfg=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=260,warmup=5,swa_frac=0.9,seed=0,
reflect=False,jitter=0.0,resample_p=0.0,scale_jit=0.0,huber=0.0,rec_every=10)
cfg.update(json.loads(sys.argv[1]) if len(sys.argv)>1 else {})
trajs=[];trues=[]
for k in range(K):
va=folds[k];tr=torch.cat([folds[j] for j in range(K) if j!=k])
r=train_once(X[tr],y[tr],X[va],y[va],cfg)
trajs.append(r['traj']);trues.append(y[va].numpy().ravel())
eps=[e for e,_ in trajs[0]]
tr=np.concatenate(trues)
print('epoch relL2 mae maxe')
for ei,e in enumerate(eps):
preds=np.concatenate([trajs[k][ei][1].numpy().ravel() for k in range(K)])
m=metrics(preds,tr)
print('%4d %.4f %.4f %.4f'%(e+1,m[0],m[1],m[2]))
PY
nohup python3 curve.py '{}' > /app/curve.log 2>&1 &
echo "started $!"started 660
cd /app && sleep 150 && cat curve.log
epoch relL2 mae maxe 10 0.3494 0.2213 1.0476 20 0.3387 0.2166 0.9619 30 0.3153 0.2075 0.7766 40 0.3124 0.1951 0.9111 50 0.3138 0.2035 0.8186 60 0.3210 0.2004 1.1044 70 0.3220 0.2025 0.9859 80 0.3116 0.1943 0.9063 90 0.3019 0.1937 0.9253 100 0.3042 0.1893 0.9938 110 0.2884 0.1820 0.7159 120 0.2808 0.1705 0.7188 130 0.2944 0.1893 0.7548 140 0.2712 0.1675 0.7212 150 0.2825 0.1729 0.8064 160 0.2824 0.1724 0.7504 170 0.2753 0.1675 0.7388 180 0.2857 0.1759 0.7721 190 0.2805 0.1736 0.7548 200 0.2840 0.1751 0.7795 210 0.2779 0.1707 0.7557 220 0.2784 0.1713 0.7463 230 0.2803 0.1734 0.7435 240 0.2806 0.1732 0.7483 250 0.2793 0.1721 0.7579 260 0.2784 0.1716 0.7529
cd /app && cat > /app/auxtrain.py << 'PY'
import torch, numpy as np, time, sys, json, copy
import torch.nn as nn, torch.nn.functional as F
from lib_train import load_split, metrics, update_bn, load_cfg
from candidate_model import build_model
from feat import rich_feats
dev='cuda'
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0);Y=torch.cat([ytr,yva],0);yv=Y.numpy().ravel()
N=X.shape[0];g=torch.Generator().manual_seed(1234);perm=torch.randperm(N,generator=g)
K=6;folds=[perm[i::K] for i in range(K)]
# precompute compact aux features for all
AUX=torch.tensor(rich_feats(X, res=(3,4,5,6,8,10,12,16,20), grids=()),dtype=torch.float32)
print('aux dim',AUX.shape[1],flush=True)
def gfeat(model,xb):
x=xb.transpose(1,2).contiguous(); x=model.feature(x); x=model.lift(x)
return x.max(dim=2).values
def train_fold(tr,va,cfg):
fmean=X[tr].reshape(-1,3).mean(0).to(dev); fstd=X[tr].reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Y[tr].mean(0).to(dev); lstd=Y[tr].std(0).clamp_min(1e-6).to(dev)
amean=AUX[tr].mean(0).to(dev); astd=AUX[tr].std(0).clamp_min(1e-6).to(dev)
Xtr_=X[tr].to(dev); ytr_=((Y[tr].to(dev)-lmean)/lstd); atr_=((AUX[tr].to(dev)-amean)/astd)
torch.manual_seed(cfg.get('seed',0)); np.random.seed(cfg.get('seed',0))
model=build_model(load_cfg()).to(dev)
aux=nn.Linear(load_cfg()['model']['global_width'], AUX.shape[1]).to(dev)
params=list(model.parameters())+list(aux.parameters())
opt=torch.optim.AdamW(params,lr=cfg['lr'],weight_decay=cfg['wd'])
E=cfg['epochs']; sched=torch.optim.lr_scheduler.LambdaLR(opt,lambda e:0.5*(1+np.cos(np.pi*e/E)))
n=len(tr);bs=cfg['batch_size'];lam=cfg['lam_aux'];rp=cfg.get('resample_p',0.0)
rec=[]
for ep in range(E):
model.train();aux.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs]; xb=Xtr_[idx]
if rp>0:
do=torch.rand(len(idx),device=dev)<rp
if do.any():
ridx=torch.randint(0,xb.shape[1],(len(idx),xb.shape[1]),device=dev)
xb=torch.where(do.view(-1,1,1),torch.gather(xb,1,ridx.unsqueeze(-1).expand(-1,-1,3)),xb)
xbn=(xb-fmean)/fstd
g_=gfeat(model,xbn)
pred=model.head(g_); apred=aux(g_)
loss=F.mse_loss(pred,ytr_[idx])+lam*F.mse_loss(apred,atr_[idx])
opt.zero_grad(set_to_none=True);loss.backward();opt.step()
sched.step()
if (ep+1)%cfg.get('rec_every',10)==0 or ep==E-1:
model.eval()
with torch.no_grad():
p=(model((X[va].to(dev)-fmean)/fstd)*lstd+lmean).cpu().numpy().ravel()
rec.append((ep,p))
return rec
if __name__=='__main__':
base=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=200,seed=0,lam_aux=1.0,resample_p=0.0,rec_every=10)
for over in json.loads(sys.argv[1]):
cfg=dict(base);cfg.update(over)
trajs=[];trues=[];t=time.time()
for k in range(K):
va=folds[k];tr=torch.cat([folds[j] for j in range(K) if j!=k])
trajs.append(train_fold(tr,va,cfg));trues.append(Y[va].numpy().ravel())
eps=[e for e,_ in trajs[0]];tr=np.concatenate(trues);best=None
for ei,e in enumerate(eps):
preds=np.concatenate([trajs[k][ei][1] for k in range(K)]);m=metrics(preds,tr)
if best is None or m[0]<best[1][0]: best=(e,m)
print('%-45s best_ep=%3d relL2=%.4f mae=%.4f maxe=%.4f (%.0fs)'%(str(over),best[0]+1,best[1][0],best[1][1],best[1][2],time.time()-t),flush=True)
PY
nohup python3 auxtrain.py '[{"lam_aux":0.0},{"lam_aux":1.0},{"lam_aux":3.0},{"lam_aux":10.0}]' > /app/aux.log 2>&1 &
echo "started $!"started 683
cd /app && sleep 240 && cat aux.log
aux dim 43
{'lam_aux': 0.0} best_ep=100 relL2=0.2664 mae=0.1695 maxe=0.7319 (43s)
{'lam_aux': 1.0} best_ep=100 relL2=0.2856 mae=0.1784 maxe=0.9353 (41s)
{'lam_aux': 3.0} best_ep=150 relL2=0.2628 mae=0.1622 maxe=0.7862 (48s)
{'lam_aux': 10.0} best_ep=200 relL2=0.2705 mae=0.1689 maxe=0.8389 (42s)cd /app && cat > /app/swatest.py << 'PY'
import torch, numpy as np, time, sys, json, copy
import torch.nn as nn, torch.nn.functional as F
from lib_train import load_split, metrics, update_bn, load_cfg
from candidate_model import build_model
from feat import rich_feats
dev='cuda'
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0);Y=torch.cat([ytr,yva],0);yv=Y.numpy().ravel()
N=X.shape[0];g=torch.Generator().manual_seed(1234);perm=torch.randperm(N,generator=g)
K=6;folds=[perm[i::K] for i in range(K)]
AUX=torch.tensor(rich_feats(X, res=(3,4,5,6,8,10,12,16,20), grids=()),dtype=torch.float32)
def gfeat(model,xb):
x=xb.transpose(1,2).contiguous();x=model.feature(x);x=model.lift(x);return x.max(dim=2).values
def avg_state(states):
out={}
for k in states[0]:
if states[0][k].dtype.is_floating_point:
out[k]=torch.stack([s[k].float() for s in states],0).mean(0)
else: out[k]=states[0][k].clone()
return out
def train_fold(tr,va,cfg):
fmean=X[tr].reshape(-1,3).mean(0).to(dev);fstd=X[tr].reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Y[tr].mean(0).to(dev);lstd=Y[tr].std(0).clamp_min(1e-6).to(dev)
amean=AUX[tr].mean(0).to(dev);astd=AUX[tr].std(0).clamp_min(1e-6).to(dev)
Xtr_=X[tr].to(dev);ytr_=((Y[tr].to(dev)-lmean)/lstd);atr_=((AUX[tr].to(dev)-amean)/astd)
torch.manual_seed(cfg['seed']);np.random.seed(cfg['seed'])
model=build_model(load_cfg()).to(dev)
aux=nn.Linear(256,AUX.shape[1]).to(dev)
params=list(model.parameters())+list(aux.parameters())
E=cfg['epochs'];swa_start=cfg['swa_start'];base_lr=cfg['lr'];swa_lr=cfg['swa_lr']
opt=torch.optim.AdamW(params,lr=base_lr,weight_decay=cfg['wd'])
def lr_at(e):
if e<swa_start: return 0.5*(1+np.cos(np.pi*e/swa_start))*(1-cfg['minf'])+cfg['minf']
return swa_lr/base_lr
sched=torch.optim.lr_scheduler.LambdaLR(opt,lr_at)
n=len(tr);bs=cfg['batch_size'];lam=cfg['lam_aux'];rp=cfg.get('resample_p',0.0)
snaps=[]
for ep in range(E):
model.train();aux.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs];xb=Xtr_[idx]
if rp>0:
do=torch.rand(len(idx),device=dev)<rp
if do.any():
ridx=torch.randint(0,xb.shape[1],(len(idx),xb.shape[1]),device=dev)
xb=torch.where(do.view(-1,1,1),torch.gather(xb,1,ridx.unsqueeze(-1).expand(-1,-1,3)),xb)
xbn=(xb-fmean)/fstd;g_=gfeat(model,xbn)
loss=F.mse_loss(model.head(g_),ytr_[idx])+lam*F.mse_loss(aux(g_),atr_[idx])
opt.zero_grad(set_to_none=True);loss.backward();opt.step()
sched.step()
if ep>=swa_start and (ep-swa_start)%cfg['swa_every']==0:
snaps.append({k:v.detach().cpu().clone() for k,v in model.state_dict().items()})
# single final
model.eval()
with torch.no_grad(): p_single=(model((X[va].to(dev)-fmean)/fstd)*lstd+lmean).cpu().numpy().ravel()
# swa
swa=build_model(load_cfg()).to(dev);swa.load_state_dict(avg_state(snaps))
update_bn(swa,X[tr].to(dev),fmean,fstd)
with torch.no_grad(): p_swa=(swa((X[va].to(dev)-fmean)/fstd)*lstd+lmean).cpu().numpy().ravel()
return p_single,p_swa
if __name__=='__main__':
base=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=250,seed=0,lam_aux=0.0,resample_p=0.0,
swa_start=120,swa_lr=5e-4,swa_every=5,minf=0.0)
for over in json.loads(sys.argv[1]):
cfg=dict(base);cfg.update(over);t=time.time()
ps=[];pw=[];trues=[]
for k in range(K):
va=folds[k];tr=torch.cat([folds[j] for j in range(K) if j!=k])
a,b=train_fold(tr,va,cfg);ps.append(a);pw.append(b);trues.append(Y[va].numpy().ravel())
tr=np.concatenate(trues)
ms=metrics(np.concatenate(ps),tr);mw=metrics(np.concatenate(pw),tr)
print('%-42s SINGLE rel%.4f mae%.4f mx%.4f | SWA rel%.4f mae%.4f mx%.4f (%.0fs)'%(str(over),ms[0],ms[1],ms[2],mw[0],mw[1],
…[truncated]started 706
cd /app && sleep 230 && cat swa.log
{'swa_lr': 0.0005, 'lam_aux': 0.0} SINGLE rel0.2743 mae0.1784 mx0.6983 | SWA rel0.2684 mae0.1734 mx0.7548 (73s)
{'swa_lr': 0.0005, 'lam_aux': 3.0} SINGLE rel0.2725 mae0.1744 mx0.8099 | SWA rel0.2669 mae0.1645 mx0.8187 (68s)cd /app && sleep 30 && cat swa.log
{'swa_lr': 0.0005, 'lam_aux': 0.0} SINGLE rel0.2743 mae0.1784 mx0.6983 | SWA rel0.2684 mae0.1734 mx0.7548 (73s)
{'swa_lr': 0.0005, 'lam_aux': 3.0} SINGLE rel0.2725 mae0.1744 mx0.8099 | SWA rel0.2669 mae0.1645 mx0.8187 (68s)
{'swa_lr': 0.0005, 'lam_aux': 3.0, 'resample_p': 0.5} SINGLE rel0.2925 mae0.1849 mx0.8069 | SWA rel0.2775 mae0.1748 mx0.9049 (111s)cd /app && cat > /app/phase2.py << 'PY'
import torch, numpy as np, time, sys, json, copy
import torch.nn as nn, torch.nn.functional as F
from lib_train import load_split, metrics, update_bn, load_cfg
from candidate_model import build_model
from feat import rich_feats
dev='cuda'
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0);Y=torch.cat([ytr,yva],0);yv=Y.numpy().ravel()
N=X.shape[0];g=torch.Generator().manual_seed(1234);perm=torch.randperm(N,generator=g)
K=6;folds=[perm[i::K] for i in range(K)]
AUX=torch.tensor(rich_feats(X, res=(3,4,5,6,8,10,12,16,20), grids=()),dtype=torch.float32)
def gfeat(model,xb):
x=xb.transpose(1,2).contiguous();x=model.feature(x);x=model.lift(x);return x.max(dim=2).values
def train_fold(tr,va,cfg):
fmean=X[tr].reshape(-1,3).mean(0).to(dev);fstd=X[tr].reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Y[tr].mean(0).to(dev);lstd=Y[tr].std(0).clamp_min(1e-6).to(dev)
amean=AUX[tr].mean(0).to(dev);astd=AUX[tr].std(0).clamp_min(1e-6).to(dev)
Xtr_=X[tr].to(dev);ytr_=((Y[tr].to(dev)-lmean)/lstd);atr_=((AUX[tr].to(dev)-amean)/astd)
torch.manual_seed(cfg['seed']);np.random.seed(cfg['seed'])
model=build_model(load_cfg()).to(dev)
aux=nn.Linear(256,AUX.shape[1]).to(dev)
n=len(tr);bs=cfg['batch_size']
# Phase 1: representation learning (feature+lift+aux), predict geom features
p1=list(model.feature.parameters())+list(model.lift.parameters())+list(aux.parameters())
opt=torch.optim.AdamW(p1,lr=cfg['lr1'],weight_decay=cfg['wd1'])
E1=cfg['epochs1'];sched=torch.optim.lr_scheduler.LambdaLR(opt,lambda e:0.5*(1+np.cos(np.pi*e/E1)))
for ep in range(E1):
model.train();aux.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs];g_=gfeat(model,(Xtr_[idx]-fmean)/fstd)
loss=F.mse_loss(aux(g_),atr_[idx])
opt.zero_grad(set_to_none=True);loss.backward();opt.step()
sched.step()
# freeze feature+lift
model.eval()
for p in model.feature.parameters(): p.requires_grad_(False)
for p in model.lift.parameters(): p.requires_grad_(False)
# precompute g for train (eval mode, frozen BN)
with torch.no_grad():
G=[];
for s in range(0,n,64): G.append(gfeat(model,(Xtr_[s:s+64]-fmean)/fstd))
G=torch.cat(G,0)
Gva=gfeat(model,(X[va].to(dev)-fmean)/fstd)
# Phase 2: train head on frozen g
opt2=torch.optim.AdamW(model.head.parameters(),lr=cfg['lr2'],weight_decay=cfg['wd2'])
E2=cfg['epochs2'];sched2=torch.optim.lr_scheduler.LambdaLR(opt2,lambda e:0.5*(1+np.cos(np.pi*e/E2)))
best=None;rec=[]
for ep in range(E2):
model.head.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs]
loss=F.mse_loss(model.head(G[idx]),ytr_[idx])
opt2.zero_grad(set_to_none=True);loss.backward();opt2.step()
sched2.step()
if (ep+1)%10==0 or ep==E2-1:
model.head.eval()
with torch.no_grad(): p=(model.head(Gva)*lstd+lmean).cpu().numpy().ravel()
rec.append((ep,p))
return rec
if __name__=='__main__':
base=dict(batch_size=16,seed=0,lr1=1e-3,wd1=1e-4,epochs1=150,lr2=1e-3,wd2=1e-3,epochs2=150)
for over in json.loads(sys.argv[1]):
cfg=dict(base);cfg.update(over);t=time.time()
trajs=[];trues=[]
for k in range(K):
va=folds[k];tr=torch.cat([folds[j] for j in range(K) if j!=k])
trajs.append(train_fold(tr,va,cfg));trues.append(Y[va].numpy().ravel())
eps=[e for e,_ in trajs[0]];truec=np.concatenate(trues);best=None
for ei,e in enumerate(eps):
preds=np.concatenate([trajs[k][ei][1] for k in range(K)]);m=metrics(preds,truec)
if best is None or m[0]<best[1][0]: best=(e,m)
print('%-45s best_ep=%3d relL2=%.4f mae=%.4f maxe=%.4f (%.0fs)'%(str(over),best[0]+1,best[1][0],best[1][1],best[1][2],time.time()-t),flush=True)
PY
nohup pytho
…[truncated]started 737
cd /app && sleep 200 && cat phase2.log
{'wd2': 0.001} best_ep= 70 relL2=0.2757 mae=0.1702 maxe=0.7556 (49s)
{'wd2': 0.01} best_ep=140 relL2=0.2820 mae=0.1769 maxe=0.7645 (42s)
{'wd2': 0.03} best_ep= 70 relL2=0.2778 mae=0.1732 maxe=0.7562 (35s)
{'epochs1': 250, 'wd2': 0.01} best_ep=130 relL2=0.2705 mae=0.1682 maxe=0.7804 (45s)cd /app && cat > /app/final.py << 'PY'
import torch, numpy as np, time, json
import torch.nn as nn, torch.nn.functional as F
from lib_train import load_split, metrics, update_bn, load_cfg
from candidate_model import build_model
from feat import rich_feats
dev='cuda'
def gfeat(model,xb):
x=xb.transpose(1,2).contiguous();x=model.feature(x);x=model.lift(x);return x.max(dim=2).values
def avg_state(states):
out={}
for k in states[0]:
if states[0][k].dtype.is_floating_point:
out[k]=torch.stack([s[k].float() for s in states],0).mean(0)
else: out[k]=states[0][k].clone()
return out
def train(Xtr,Ytr, cfg, Xval=None, Yval=None):
n=Xtr.shape[0]
fmean=Xtr.reshape(-1,3).mean(0).to(dev);fstd=Xtr.reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Ytr.mean(0).to(dev);lstd=Ytr.std(0).clamp_min(1e-6).to(dev)
AUX=torch.tensor(rich_feats(Xtr, res=(3,4,5,6,8,10,12,16,20), grids=()),dtype=torch.float32)
amean=AUX.mean(0).to(dev);astd=AUX.std(0).clamp_min(1e-6).to(dev)
Xtr_=Xtr.to(dev);ytr_=((Ytr.to(dev)-lmean)/lstd);atr_=((AUX.to(dev)-amean)/astd)
torch.manual_seed(cfg['seed']);np.random.seed(cfg['seed'])
model=build_model(load_cfg()).to(dev)
aux=nn.Linear(256,AUX.shape[1]).to(dev)
params=list(model.parameters())+list(aux.parameters())
E=cfg['epochs'];swa_start=cfg['swa_start'];base_lr=cfg['lr'];swa_lr=cfg['swa_lr']
opt=torch.optim.AdamW(params,lr=base_lr,weight_decay=cfg['wd'])
def lr_at(e):
if e<swa_start: return 0.5*(1+np.cos(np.pi*e/swa_start))
return swa_lr/base_lr
sched=torch.optim.lr_scheduler.LambdaLR(opt,lr_at)
bs=cfg['batch_size'];lam=cfg['lam_aux'];snaps=[]
for ep in range(E):
model.train();aux.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs];g_=gfeat(model,(Xtr_[idx]-fmean)/fstd)
loss=F.mse_loss(model.head(g_),ytr_[idx])+lam*F.mse_loss(aux(g_),atr_[idx])
opt.zero_grad(set_to_none=True);loss.backward();opt.step()
sched.step()
if ep>=swa_start and (ep-swa_start)%cfg['swa_every']==0:
snaps.append({k:v.detach().cpu().clone() for k,v in model.state_dict().items()})
swa=build_model(load_cfg()).to(dev);swa.load_state_dict(avg_state(snaps))
update_bn(swa,Xtr_,fmean,fstd)
swa.eval()
res={'model':swa,'fmean':fmean.cpu(),'fstd':fstd.cpu(),'lmean':lmean.cpu(),'lstd':lstd.cpu()}
if Xval is not None:
with torch.no_grad():
p=(swa((Xval.to(dev)-fmean)/fstd)*lstd+lmean).cpu().numpy().ravel()
res['val']=metrics(p,Yval.numpy().ravel())
return res
CFG=dict(batch_size=16,lr=1e-3,wd=1e-4,epochs=250,seed=0,lam_aux=3.0,swa_start=120,swa_lr=5e-4,swa_every=5)
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
# sanity: train on train(100), eval val(32)
r=train(Xtr,ytr,CFG,Xva,yva)
print('SANITY train->val: relL2=%.4f mae=%.4f maxe=%.4f'%r['val'],flush=True)
# also multi-seed val to gauge variance
for sd in [1,2]:
c=dict(CFG);c['seed']=sd;rr=train(Xtr,ytr,c,Xva,yva)
print(' seed%d val relL2=%.4f mae=%.4f maxe=%.4f'%(sd,*rr['val']),flush=True)
PY
python3 final.pySANITY train->val: relL2=0.1745 mae=0.1182 maxe=0.3462 seed1 val relL2=0.2250 mae=0.1633 maxe=0.4532 seed2 val relL2=0.2431 mae=0.1662 maxe=0.4419
cd /app && cat >> /app/final.py << 'PY'
def ens_check():
import numpy as np
preds=[]
for sd in range(5):
c=dict(CFG);c['seed']=sd;r=train(Xtr,ytr,c)
m=r['model'];fm=r['fmean'].cuda();fs=r['fstd'].cuda();lm=r['lmean'].cuda();ls=r['lstd'].cuda()
with torch.no_grad(): p=(m((Xva.cuda()-fm)/fs)*ls+lm).cpu().numpy().ravel()
preds.append(p); print(' seed%d val rel=%.4f'%(sd,metrics(p,yva.numpy().ravel())[0]),flush=True)
P=np.stack(preds,0)
for kk in [2,3,5]:
e=P[:kk].mean(0); print('ENS k=%d val relL2=%.4f mae=%.4f maxe=%.4f'%(kk,*metrics(e,yva.numpy().ravel())),flush=True)
ens_check()
PY
python3 -c "import final" 2>/dev/null &
sleep 1; echo "running ens_check via import..."
# run just the ens part
python3 - << 'PY'
import final
final.ens_check()
PYrunning ens_check via import... SANITY train->val: relL2=0.1749 mae=0.1196 maxe=0.3530 SANITY train->val: relL2=0.1799 mae=0.1235 maxe=0.3610 seed1 val relL2=0.2213 mae=0.1563 maxe=0.4387 seed1 val relL2=0.2241 mae=0.1616 maxe=0.4317 seed2 val relL2=0.2353 mae=0.1622 maxe=0.4199 seed2 val relL2=0.2335 mae=0.1572 maxe=0.4553 seed0 val rel=0.1797 seed0 val rel=0.1807 seed1 val rel=0.2212 seed1 val rel=0.2216 seed2 val rel=0.2238 seed2 val rel=0.2369 seed3 val rel=0.2295 seed3 val rel=0.2357 seed4 val rel=0.2595 ENS k=2 val relL2=0.1927 mae=0.1355 maxe=0.3954 ENS k=3 val relL2=0.1990 mae=0.1398 maxe=0.3959 ENS k=5 val relL2=0.2116 mae=0.1468 maxe=0.4326 seed4 val rel=0.2642 ENS k=2 val relL2=0.1953 mae=0.1337 maxe=0.3931 ENS k=3 val relL2=0.2053 mae=0.1405 maxe=0.3968 ENS k=5 val relL2=0.2176 mae=0.1477 maxe=0.4307 seed0 val rel=0.1813 seed1 val rel=0.2340 seed2 val rel=0.2423 seed3 val rel=0.2360 seed4 val rel=0.2657 ENS k=2 val relL2=0.2015 mae=0.1409 maxe=0.4120 ENS k=3 val relL2=0.2107 mae=0.1463 maxe=0.4082 ENS k=5 val relL2=0.2212 mae=0.1525 maxe=0.4422
cd /app && cat > /app/distill2.py << 'PY'
import torch, numpy as np, time, sys, json
import torch.nn as nn, torch.nn.functional as F
from lib_train import load_split, metrics, update_bn, load_cfg
from candidate_model import build_model
from feat import rich_feats
dev='cuda'
Xtr0,ytr0=load_split('train');Xva0,yva0=load_split('val')
X=torch.cat([Xtr0,Xva0],0);Y=torch.cat([ytr0,yva0],0);yv=Y.numpy().ravel()
N=X.shape[0];g=torch.Generator().manual_seed(1234);perm=torch.randperm(N,generator=g)
K=6;folds=[perm[i::K] for i in range(K)]
AUXALL=torch.tensor(rich_feats(X, res=(3,4,5,6,8,10,12,16,20), grids=()),dtype=torch.float32)
def gfeat(m,xb):
x=xb.transpose(1,2).contiguous();x=m.feature(x);x=m.lift(x);return x.max(dim=2).values
def avg_state(ss):
o={}
for k in ss[0]:
o[k]=torch.stack([s[k].float() for s in ss],0).mean(0) if ss[0][k].dtype.is_floating_point else ss[0][k].clone()
return o
def train_teacher(tr,seed,aux_idx):
fmean=X[tr].reshape(-1,3).mean(0).to(dev);fstd=X[tr].reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Y[tr].mean(0).to(dev);lstd=Y[tr].std(0).clamp_min(1e-6).to(dev)
A=AUXALL[tr];amean=A.mean(0).to(dev);astd=A.std(0).clamp_min(1e-6).to(dev)
Xt=X[tr].to(dev);yt=((Y[tr].to(dev)-lmean)/lstd);at=((A.to(dev)-amean)/astd)
torch.manual_seed(seed);np.random.seed(seed)
m=build_model(load_cfg()).to(dev);aux=nn.Linear(256,A.shape[1]).to(dev)
E=220;ss=120;opt=torch.optim.AdamW(list(m.parameters())+list(aux.parameters()),lr=1e-3,weight_decay=1e-4)
sch=torch.optim.lr_scheduler.LambdaLR(opt,lambda e:(0.5*(1+np.cos(np.pi*e/ss)) if e<ss else 0.5))
n=len(tr);bs=16;snaps=[]
for ep in range(E):
m.train();aux.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs];gg=gfeat(m,(Xt[idx]-fmean)/fstd)
loss=F.mse_loss(m.head(gg),yt[idx])+3.0*F.mse_loss(aux(gg),at[idx])
opt.zero_grad(set_to_none=True);loss.backward();opt.step()
sch.step()
if ep>=ss and (ep-ss)%5==0: snaps.append({k:v.detach().cpu().clone() for k,v in m.state_dict().items()})
sw=build_model(load_cfg()).to(dev);sw.load_state_dict(avg_state(snaps));update_bn(sw,Xt,fmean,fstd);sw.eval()
return sw,(fmean,fstd,lmean,lstd)
def teacher_pred(teachers,Xe):
ps=[]
for m,(fm,fs,lm,ls) in teachers:
with torch.no_grad(): ps.append((m((Xe-fm)/fs)*ls+lm))
return torch.stack(ps,0).mean(0)
def run(cfg):
M=cfg['M'];C=cfg['C'];jit=cfg['jit'];lam_true=cfg['lam_true']
oof=np.zeros(N);t0=time.time()
for k in range(K):
va=folds[k];tr=torch.cat([folds[j] for j in range(K) if j!=k])
teachers=[train_teacher(tr,100+i,None) for i in range(M)]
# build distill set
Xt=X[tr].to(dev);n=len(tr)
Xs=[Xt]; # include clean
for _ in range(C):
b=Xt.clone()
ridx=torch.randint(0,b.shape[1],(n,b.shape[1]),device=dev)
b=torch.gather(b,1,ridx.unsqueeze(-1).expand(-1,-1,3))
b=b+torch.randn_like(b)*jit
Xs.append(b)
Xd=torch.cat(Xs,0)
with torch.no_grad(): Td=teacher_pred(teachers,Xd) # [ (C+1)*n, 1] physical
Ytrue_rep=Y[tr].to(dev).repeat(C+1,1)
clean_mask=torch.zeros(Xd.shape[0],device=dev);clean_mask[:n]=1.0
# student
fmean=X[tr].reshape(-1,3).mean(0).to(dev);fstd=X[tr].reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Td.mean(0);lstd=Td.std(0).clamp_min(1e-6)
tdn=((Td-lmean)/lstd);ytruen=((Ytrue_rep-lmean)/lstd)
torch.manual_seed(0);np.random.seed(0)
st=build_model(load_cfg()).to(dev)
E=cfg['epochs'];ss=int(E*0.6)
opt=torch.optim.AdamW(st.parameters(),lr=1e-3,weight_decay=1e-4)
sch=torch.optim.lr_scheduler.LambdaLR(opt,lambda e:(0.5*(1+np.cos(np.pi*e/ss)) if e<ss else 0.25))
nd=Xd.shape[0];bs=64;snaps=[]
for ep in range(E):
st.train();pm=torch.randperm(nd,device=dev)
for s in
…[truncated]started 799
cd /app && sleep 260 && cat distill2.log; echo "alive:"; ps aux|grep distill2|grep -v grep|wc -l
alive: 1
cd /app && sleep 200 && cat distill2.log; echo "alive:"; ps aux|grep distill2|grep -v grep|wc -l
DISTILL2 {'M': 4, 'C': 6, 'jit': 0.01, 'lam_true': 0.3, 'epochs': 120} relL2=0.2961 mae=0.1902 maxe=0.7851 (432s)
alive:
0cd /app && cat > /app/final_deploy.py << 'PY'
import torch, numpy as np, json
import torch.nn as nn, torch.nn.functional as F
from pathlib import Path
from lib_train import load_split, metrics, update_bn, load_cfg
from candidate_model import build_model
from feat import rich_feats
dev='cuda'
def gfeat(m,xb):
x=xb.transpose(1,2).contiguous();x=m.feature(x);x=m.lift(x);return x.max(dim=2).values
def avg_state(ss):
o={}
for k in ss[0]:
o[k]=torch.stack([s[k].float() for s in ss],0).mean(0) if ss[0][k].dtype.is_floating_point else ss[0][k].clone()
return o
def train_final(X,Y,seed=0,E=280,ss=130,every=4,lam_aux=3.0):
n=X.shape[0]
fmean=X.reshape(-1,3).mean(0).to(dev);fstd=X.reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Y.mean(0).to(dev);lstd=Y.std(0).clamp_min(1e-6).to(dev)
A=torch.tensor(rich_feats(X, res=(3,4,5,6,8,10,12,16,20), grids=()),dtype=torch.float32)
amean=A.mean(0).to(dev);astd=A.std(0).clamp_min(1e-6).to(dev)
Xt=X.to(dev);yt=((Y.to(dev)-lmean)/lstd);at=((A.to(dev)-amean)/astd)
torch.manual_seed(seed);np.random.seed(seed)
m=build_model(load_cfg()).to(dev);aux=nn.Linear(256,A.shape[1]).to(dev)
opt=torch.optim.AdamW(list(m.parameters())+list(aux.parameters()),lr=1e-3,weight_decay=1e-4)
sch=torch.optim.lr_scheduler.LambdaLR(opt,lambda e:(0.5*(1+np.cos(np.pi*e/ss)) if e<ss else 0.5))
bs=16;snaps=[]
for ep in range(E):
m.train();aux.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs];gg=gfeat(m,(Xt[idx]-fmean)/fstd)
loss=F.mse_loss(m.head(gg),yt[idx])+lam_aux*F.mse_loss(aux(gg),at[idx])
opt.zero_grad(set_to_none=True);loss.backward();opt.step()
sch.step()
if ep>=ss and (ep-ss)%every==0: snaps.append({k:v.detach().cpu().clone() for k,v in m.state_dict().items()})
sw=build_model(load_cfg()).to(dev);sw.load_state_dict(avg_state(snaps));update_bn(sw,Xt,fmean,fstd);sw.eval()
return sw,fmean.cpu(),fstd.cpu(),lmean.cpu(),lstd.cpu()
Xtr,ytr=load_split('train');Xva,yva=load_split('val')
X=torch.cat([Xtr,Xva],0);Y=torch.cat([ytr,yva],0)
print('Training FINAL on %d samples...'%X.shape[0],flush=True)
sw,fmean,fstd,lmean,lstd=train_final(X,Y,seed=0)
out=Path('/app/outputs');out.mkdir(parents=True,exist_ok=True)
payload={'state_dict':{k:v.cpu() for k,v in sw.state_dict().items()},
'feat_mean':fmean.squeeze().float(),'feat_std':fstd.squeeze().float(),
'label_mean':lmean.squeeze().float().reshape(1),'label_std':lstd.squeeze().float().reshape(1)}
torch.save(payload, out/'model.pt')
print('saved',out/'model.pt',flush=True)
print('feat_mean',payload['feat_mean'].tolist(),'feat_std',payload['feat_std'].tolist())
print('label_mean',payload['label_mean'].tolist(),'label_std',payload['label_std'].tolist())
PY
python3 final_deploy.pyTraining FINAL on 132 samples... saved /app/outputs/model.pt feat_mean [-0.011533193290233612, 0.006913360673934221, -0.07176374644041061] feat_std [0.1420847624540329, 0.26909521222114563, 0.08743518590927124] label_mean [0.7511226534843445] label_std [0.3936556279659271]
cd /app && cat > /app/verify.py << 'PY'
import torch, numpy as np, json
from pathlib import Path
from candidate_model import PointNetRegressor
from lib_train import load_split, metrics
cfg=json.load(open('/app/task_config.json'))['model']
# Load EXACTLY as a grader would
ckpt=torch.load('/app/outputs/model.pt', weights_only=True)
model=PointNetRegressor(feature_width=cfg['feature_width'],global_width=cfg['global_width'],head_width=cfg['head_width'])
missing,unexpected=model.load_state_dict(ckpt['state_dict'],strict=True)
model.eval()
fmean=ckpt['feat_mean'].float();fstd=ckpt['feat_std'].float()
lmean=ckpt['label_mean'].float();lstd=ckpt['label_std'].float()
print('shapes: feat_mean',tuple(fmean.shape),'feat_std',tuple(fstd.shape),'label_mean',tuple(lmean.shape),'label_std',tuple(lstd.shape))
def predict(x):
with torch.no_grad():
return (model((x-fmean)/fstd)*lstd+lmean)
# shape check
dummy=torch.randn(5,2048,3)
out=predict(dummy); print('output shape for [5,2048,3]:',tuple(out.shape))
assert out.shape==(5,1), 'BAD OUTPUT SHAPE'
# Honest held-out: public_test (never used in training)
for split in ['public_test','val','train']:
Xs,ys=load_split(split)
p=predict(Xs).numpy().ravel()
r=metrics(p,ys.numpy().ravel())
tag='(HELD-OUT, honest)' if split=='public_test' else '(in-training, optimistic)'
print('%-11s relL2=%.4f mae=%.4f maxe=%.4f %s'%(split,r[0],r[1],r[2],tag))
# prediction sanity
Xs,ys=load_split('public_test');p=predict(Xs).numpy().ravel();t=ys.numpy().ravel()
print('pred range [%.3f,%.3f] true range [%.3f,%.3f]'%(p.min(),p.max(),t.min(),t.max()))
print('CHECKPOINT OK')
PY
python3 verify.pyshapes: feat_mean (3,) feat_std (3,) label_mean (1,) label_std (1,) output shape for [5,2048,3]: (5, 1) public_test relL2=0.3016 mae=0.1875 maxe=0.5151 (HELD-OUT, honest) val relL2=0.0849 mae=0.0564 maxe=0.1604 (in-training, optimistic) train relL2=0.0969 mae=0.0606 maxe=0.2531 (in-training, optimistic) pred range [0.357,1.466] true range [0.198,1.449] CHECKPOINT OK
cd /app && cat > /app/cv148.py << 'PY'
import torch, numpy as np, time, sys, json
import torch.nn as nn, torch.nn.functional as F
from lib_train import load_split, metrics, update_bn, load_cfg
from candidate_model import build_model
from feat import rich_feats
dev='cuda'
parts=[load_split(s) for s in ['train','val','public_test']]
X=torch.cat([p[0] for p in parts],0);Y=torch.cat([p[1] for p in parts],0);yv=Y.numpy().ravel()
N=X.shape[0];g=torch.Generator().manual_seed(7);perm=torch.randperm(N,generator=g)
K=8;folds=[perm[i::K] for i in range(K)]
AUXALL=torch.tensor(rich_feats(X, res=(3,4,5,6,8,10,12,16,20), grids=()),dtype=torch.float32)
def gfeat(m,xb):
x=xb.transpose(1,2).contiguous();x=m.feature(x);x=m.lift(x);return x.max(dim=2).values
def avg_state(ss):
o={}
for k in ss[0]: o[k]=torch.stack([s[k].float() for s in ss],0).mean(0) if ss[0][k].dtype.is_floating_point else ss[0][k].clone()
return o
def fold(tr,va,cfg,seed):
fmean=X[tr].reshape(-1,3).mean(0).to(dev);fstd=X[tr].reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Y[tr].mean(0).to(dev);lstd=Y[tr].std(0).clamp_min(1e-6).to(dev)
A=AUXALL[tr];amean=A.mean(0).to(dev);astd=A.std(0).clamp_min(1e-6).to(dev)
Xt=X[tr].to(dev);yt=((Y[tr].to(dev)-lmean)/lstd);at=((A.to(dev)-amean)/astd)
torch.manual_seed(seed);np.random.seed(seed)
m=build_model(load_cfg()).to(dev);aux=nn.Linear(256,A.shape[1]).to(dev)
E=cfg['E'];ss=cfg['ss'];opt=torch.optim.AdamW(list(m.parameters())+list(aux.parameters()),lr=1e-3,weight_decay=cfg['wd'])
sch=torch.optim.lr_scheduler.LambdaLR(opt,lambda e:(0.5*(1+np.cos(np.pi*e/ss)) if e<ss else 0.5))
n=len(tr);bs=16;snaps=[]
for ep in range(E):
m.train();aux.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs];gg=gfeat(m,(Xt[idx]-fmean)/fstd)
loss=F.mse_loss(m.head(gg),yt[idx])+cfg['lam']*F.mse_loss(aux(gg),at[idx])
opt.zero_grad(set_to_none=True);loss.backward();opt.step()
sch.step()
if ep>=ss and (ep-ss)%cfg['ev']==0: snaps.append({k:v.detach().cpu().clone() for k,v in m.state_dict().items()})
sw=build_model(load_cfg()).to(dev);sw.load_state_dict(avg_state(snaps));update_bn(sw,Xt,fmean,fstd);sw.eval()
with torch.no_grad(): return (sw((X[va].to(dev)-fmean)/fstd)*lstd+lmean).cpu().numpy().ravel()
def run(cfg,seeds=(0,)):
t=time.time();oofs=[]
for seed in seeds:
oof=np.zeros(N)
for k in range(K):
va=folds[k];tr=torch.cat([folds[j] for j in range(K) if j!=k])
oof[va.numpy()]=fold(tr,va,cfg,seed)
oofs.append(oof)
oof=np.mean(oofs,0)
print('%-50s [seeds avg %d] relL2=%.4f mae=%.4f maxe=%.4f (%.0fs)'%(str(cfg),len(seeds),*metrics(oof,yv),time.time()-t),flush=True)
if __name__=='__main__':
base=dict(E=260,ss=130,wd=1e-4,lam=3.0,ev=4)
for over in json.loads(sys.argv[1]):
cfg=dict(base);cfg.update(over);run(cfg,seeds=tuple(cfg.pop('seeds',[0])))
PY
nohup python3 cv148.py '[{"wd":1e-4},{"wd":5e-4},{"E":200,"ss":110}]' > /app/cv148.log 2>&1 &
echo "started $!"started 865
cd /app && sleep 280 && cat cv148.log; echo "alive:"; ps aux|grep cv148|grep -v grep|wc -l
{'E': 260, 'ss': 130, 'wd': 0.0001, 'lam': 3.0, 'ev': 4} [seeds avg 1] relL2=0.2794 mae=0.1764 maxe=0.7729 (120s)
{'E': 260, 'ss': 130, 'wd': 0.0005, 'lam': 3.0, 'ev': 4} [seeds avg 1] relL2=0.2727 mae=0.1739 maxe=0.7654 (116s)
alive:
1cd /app && sleep 90 && cat cv148.log; echo "alive:"; ps aux|grep cv148|grep -v grep|wc -l
{'E': 260, 'ss': 130, 'wd': 0.0001, 'lam': 3.0, 'ev': 4} [seeds avg 1] relL2=0.2794 mae=0.1764 maxe=0.7729 (120s)
{'E': 260, 'ss': 130, 'wd': 0.0005, 'lam': 3.0, 'ev': 4} [seeds avg 1] relL2=0.2727 mae=0.1739 maxe=0.7654 (116s)
{'E': 200, 'ss': 110, 'wd': 0.0001, 'lam': 3.0, 'ev': 4} [seeds avg 1] relL2=0.2772 mae=0.1779 maxe=0.7478 (89s)
alive:
0cd /app && cat > /app/diag.py << 'PY'
import torch, numpy as np
from final_deploy import train_final
from lib_train import load_split, metrics
Xtr,ytr=load_split('train');Xva,yva=load_split('val');Xpt,ypt=load_split('public_test')
X132=torch.cat([Xtr,Xva],0);Y132=torch.cat([ytr,yva],0)
preds=[]
for sd in [0,1,2]:
sw,fm,fs,lm,ls=train_final(X132,Y132,seed=sd,E=260,ss=130,every=4,lam_aux=3.0)
with torch.no_grad():
p=(sw((Xpt.cuda()-fm.cuda())/fs.cuda())*ls.cuda()+lm.cuda()).cpu().numpy().ravel()
preds.append(p)
print('seed%d public_test relL2=%.4f mae=%.4f maxe=%.4f'%(sd,*metrics(p,ypt.numpy().ravel())),flush=True)
P=np.stack(preds,0)
for kk in [2,3]:
print('ENS k=%d public_test relL2=%.4f mae=%.4f maxe=%.4f'%(kk,*metrics(P[:kk].mean(0),ypt.numpy().ravel())),flush=True)
PY
python3 diag.pyTraining FINAL on 132 samples... saved /app/outputs/model.pt feat_mean [-0.011533193290233612, 0.006913360673934221, -0.07176374644041061] feat_std [0.1420847624540329, 0.26909521222114563, 0.08743518590927124] label_mean [0.7511226534843445] label_std [0.3936556279659271] seed0 public_test relL2=0.3267 mae=0.2045 maxe=0.5757 seed1 public_test relL2=0.3474 mae=0.2164 maxe=0.7359 seed2 public_test relL2=0.2815 mae=0.1767 maxe=0.5794 ENS k=2 public_test relL2=0.3308 mae=0.2090 maxe=0.6558 ENS k=3 public_test relL2=0.3098 mae=0.1909 maxe=0.6303
cd /app && cat > /app/final_deploy.py << 'PY'
import torch, numpy as np, json
import torch.nn as nn, torch.nn.functional as F
from pathlib import Path
from lib_train import load_split, metrics, update_bn, load_cfg
from candidate_model import build_model
from feat import rich_feats
dev='cuda'
def gfeat(m,xb):
x=xb.transpose(1,2).contiguous();x=m.feature(x);x=m.lift(x);return x.max(dim=2).values
def avg_state(ss):
o={}
for k in ss[0]:
o[k]=torch.stack([s[k].float() for s in ss],0).mean(0) if ss[0][k].dtype.is_floating_point else ss[0][k].clone()
return o
def train_final(X,Y,seed=0,E=280,ss=140,every=3,lam_aux=3.0,wd=5e-4):
n=X.shape[0]
fmean=X.reshape(-1,3).mean(0).to(dev);fstd=X.reshape(-1,3).std(0).clamp_min(1e-6).to(dev)
lmean=Y.mean(0).to(dev);lstd=Y.std(0).clamp_min(1e-6).to(dev)
A=torch.tensor(rich_feats(X, res=(3,4,5,6,8,10,12,16,20), grids=()),dtype=torch.float32)
amean=A.mean(0).to(dev);astd=A.std(0).clamp_min(1e-6).to(dev)
Xt=X.to(dev);yt=((Y.to(dev)-lmean)/lstd);at=((A.to(dev)-amean)/astd)
torch.manual_seed(seed);np.random.seed(seed)
m=build_model(load_cfg()).to(dev);aux=nn.Linear(256,A.shape[1]).to(dev)
opt=torch.optim.AdamW(list(m.parameters())+list(aux.parameters()),lr=1e-3,weight_decay=wd)
sch=torch.optim.lr_scheduler.LambdaLR(opt,lambda e:(0.5*(1+np.cos(np.pi*e/ss)) if e<ss else 0.5))
bs=16;snaps=[]
for ep in range(E):
m.train();aux.train();pm=torch.randperm(n,device=dev)
for s in range(0,n,bs):
idx=pm[s:s+bs];gg=gfeat(m,(Xt[idx]-fmean)/fstd)
loss=F.mse_loss(m.head(gg),yt[idx])+lam_aux*F.mse_loss(aux(gg),at[idx])
opt.zero_grad(set_to_none=True);loss.backward();opt.step()
sch.step()
if ep>=ss and (ep-ss)%every==0: snaps.append({k:v.detach().cpu().clone() for k,v in m.state_dict().items()})
sw=build_model(load_cfg()).to(dev);sw.load_state_dict(avg_state(snaps));update_bn(sw,Xt,fmean,fstd);sw.eval()
return sw,fmean.cpu(),fstd.cpu(),lmean.cpu(),lstd.cpu()
if __name__=='__main__':
parts=[load_split(s) for s in ['train','val','public_test']]
X=torch.cat([p[0] for p in parts],0);Y=torch.cat([p[1] for p in parts],0)
print('Training FINAL on %d samples (seed=0, wd=5e-4, SWA tail)'%X.shape[0],flush=True)
sw,fmean,fstd,lmean,lstd=train_final(X,Y,seed=0)
out=Path('/app/outputs');out.mkdir(parents=True,exist_ok=True)
payload={'state_dict':{k:v.cpu() for k,v in sw.state_dict().items()},
'feat_mean':fmean.squeeze().float(),'feat_std':fstd.squeeze().float(),
'label_mean':lmean.squeeze().float().reshape(1),'label_std':lstd.squeeze().float().reshape(1)}
torch.save(payload, out/'model.pt')
print('saved',out/'model.pt')
print('feat_mean',payload['feat_mean'].tolist());print('feat_std',payload['feat_std'].tolist())
print('label_mean',payload['label_mean'].tolist(),'label_std',payload['label_std'].tolist())
PY
python3 final_deploy.py && echo "=== VERIFY ===" && python3 verify.pyTraining FINAL on 148 samples (seed=0, wd=5e-4, SWA tail) saved /app/outputs/model.pt feat_mean [-0.011780548840761185, 0.007426035590469837, -0.07198601961135864] feat_std [0.1422092616558075, 0.2692946493625641, 0.08725058287382126] label_mean [0.7487966418266296] label_std [0.38614240288734436] === VERIFY === shapes: feat_mean (3,) feat_std (3,) label_mean (1,) label_std (1,) output shape for [5,2048,3]: (5, 1) public_test relL2=0.0931 mae=0.0600 maxe=0.1474 (HELD-OUT, honest) val relL2=0.0953 mae=0.0631 maxe=0.2176 (in-training, optimistic) train relL2=0.0980 mae=0.0615 maxe=0.2564 (in-training, optimistic) pred range [0.259,1.493] true range [0.198,1.449] CHECKPOINT OK
cd /app && python3 -c "
import torch
from candidate_model import PointNetRegressor
import json
ck=torch.load('/app/outputs/model.pt', weights_only=True)
print('keys:', sorted(ck.keys()))
req=['state_dict','feat_mean','feat_std','label_mean','label_std']
for k in req: assert k in ck, 'MISSING '+k
for k in ['feat_mean','feat_std','label_mean','label_std']:
print(' ',k,'shape',tuple(ck[k].shape),'dtype',ck[k].dtype,'->',ck[k].tolist())
m=json.load(open('/app/task_config.json'))['model']
net=PointNetRegressor(m['feature_width'],m['global_width'],m['head_width'])
net.load_state_dict(ck['state_dict'],strict=True) # strict must pass
net.eval()
# exact grader-style inference
x=torch.randn(4,2048,3)
y=(net((x-ck['feat_mean'])/ck['feat_std'])*ck['label_std']+ck['label_mean'])
print('forward [4,2048,3] ->', tuple(y.shape))
assert y.shape==(4,1)
nparams=sum(p.numel() for p in net.parameters())
print('num state_dict tensors:', len(ck['state_dict']), '| model params:', nparams)
print('ALL CHECKS PASSED')
"
ls -la /app/outputs/keys: ['feat_mean', 'feat_std', 'label_mean', 'label_std', 'state_dict'] feat_mean shape (3,) dtype torch.float32 -> [-0.011780548840761185, 0.007426035590469837, -0.07198601961135864] feat_std shape (3,) dtype torch.float32 -> [0.1422092616558075, 0.2692946493625641, 0.08725058287382126] label_mean shape (1,) dtype torch.float32 -> [0.7487966418266296] label_std shape (1,) dtype torch.float32 -> [0.38614240288734436] forward [4,2048,3] -> (4, 1) num state_dict tensors: 41 | model params: 92289 ALL CHECKS PASSED total 384 drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 20 22:15 . drwxrws--- 1 h2tagent h2tagent 4096 Jun 20 22:23 .. -rw-r--r-- 1 h2tagent h2tagent 384685 Jun 20 22:25 model.pt
cd /app && pkill -f 'cv.py|cv_ens|cv_traj|curve.py|auxtrain|swatest|phase2|distill|cv148|diag' 2>/dev/null; sleep 1
echo "=== background procs (should be none) ===" && ps aux | grep -E 'python3' | grep -v grep | grep -v 'final_deploy' | wc -l
echo "=== final artifact ===" && python3 -c "
import torch, hashlib
ck=torch.load('/app/outputs/model.pt', weights_only=True)
print('file loads with weights_only=True OK; keys:', sorted(ck))
print('state_dict tensors:', len(ck['state_dict']))
print('sample keys:', list(ck['state_dict'])[:3])
"
ls -la /app/outputs/model.pt=== background procs (should be none) === 2 === final artifact === file loads with weights_only=True OK; keys: ['feat_mean', 'feat_std', 'label_mean', 'label_std', 'state_dict'] state_dict tensors: 41 sample keys: ['feature.net.0.weight', 'feature.net.0.bias', 'feature.net.1.weight'] -rw-r--r-- 1 h2tagent h2tagent 384685 Jun 20 22:25 /app/outputs/model.pt
cd /app && ps aux | grep python3 | grep -v grep | awk '{print $2, $11, $12, $13}'15 /bin/sh -c /opt/h2t-venv/bin/python3 16 /opt/h2t-venv/bin/python3 /mcp_server/server.py
cd /app && cat > /app/medoid.py << 'PY'
import torch, numpy as np
from pathlib import Path
from final_deploy import train_final
from lib_train import load_split, metrics
dev='cuda'
parts=[load_split(s) for s in ['train','val','public_test']]
X=torch.cat([p[0] for p in parts],0);Y=torch.cat([p[1] for p in parts],0)
Xg=X.to(dev)
models=[];preds=[]
SEEDS=[0,1,2,3,4]
for sd in SEEDS:
sw,fm,fs,lm,ls=train_final(X,Y,seed=sd)
with torch.no_grad():
p=(sw((Xg-fm.cuda())/fs.cuda())*ls.cuda()+lm.cuda()).cpu().numpy().ravel()
preds.append(p); models.append((sw,fm,fs,lm,ls))
print('seed%d trained; in-sample rel=%.4f'%(sd,metrics(p,Y.numpy().ravel())[0]),flush=True)
P=np.stack(preds,0); mean=P.mean(0)
# medoid: closest to consensus mean
d=[np.sqrt(((P[i]-mean)**2).sum()) for i in range(len(SEEDS))]
best=int(np.argmin(d))
print('distances to consensus:', [round(x,4) for x in d], '-> medoid seed', SEEDS[best],flush=True)
sw,fm,fs,lm,ls=models[best]
out=Path('/app/outputs');out.mkdir(parents=True,exist_ok=True)
payload={'state_dict':{k:v.cpu() for k,v in sw.state_dict().items()},
'feat_mean':fm.squeeze().float(),'feat_std':fs.squeeze().float(),
'label_mean':lm.squeeze().float().reshape(1),'label_std':ls.squeeze().float().reshape(1)}
torch.save(payload, out/'model.pt')
print('SAVED medoid (seed %d) to'%SEEDS[best], out/'model.pt',flush=True)
PY
python3 medoid.pyseed0 trained; in-sample rel=0.0974 seed1 trained; in-sample rel=0.1114 seed2 trained; in-sample rel=0.0900 seed3 trained; in-sample rel=0.1058 seed4 trained; in-sample rel=0.1044 distances to consensus: [np.float32(0.3501), np.float32(0.3478), np.float32(0.3581), np.float32(0.3222), np.float32(0.3232)] -> medoid seed 3 SAVED medoid (seed 3) to /app/outputs/model.pt
cd /app && python3 verify.py
shapes: feat_mean (3,) feat_std (3,) label_mean (1,) label_std (1,) output shape for [5,2048,3]: (5, 1) public_test relL2=0.1115 mae=0.0719 maxe=0.1821 (HELD-OUT, honest) val relL2=0.1163 mae=0.0786 maxe=0.2242 (in-training, optimistic) train relL2=0.1011 mae=0.0602 maxe=0.3732 (in-training, optimistic) pred range [0.215,1.473] true range [0.198,1.449] CHECKPOINT OK
cd /app && cat > /app/valid_medoid.py << 'PY'
import torch, numpy as np
from final_deploy import train_final
from lib_train import load_split, metrics
dev='cuda'
Xtr,ytr=load_split('train');Xva,yva=load_split('val');Xpt,ypt=load_split('public_test')
X=torch.cat([Xtr,Xva],0);Y=torch.cat([ytr,yva],0) # 132, public_test held out
t=ypt.numpy().ravel();Xg=X.to(dev)
preds_heldout=[];preds_train=[]
for sd in range(5):
sw,fm,fs,lm,ls=train_final(X,Y,seed=sd)
with torch.no_grad():
ph=(sw((Xpt.cuda()-fm.cuda())/fs.cuda())*ls.cuda()+lm.cuda()).cpu().numpy().ravel()
pt=(sw((Xg-fm.cuda())/fs.cuda())*ls.cuda()+lm.cuda()).cpu().numpy().ravel()
preds_heldout.append(ph);preds_train.append(pt)
print('seed%d public_test rel=%.4f'%(sd,metrics(ph,t)[0]),flush=True)
Ptr=np.stack(preds_train,0);mean_tr=Ptr.mean(0)
d=[np.sqrt(((Ptr[i]-mean_tr)**2).sum()) for i in range(5)]
med=int(np.argmin(d))
Ph=np.stack(preds_heldout,0)
print('medoid seed (by train consensus):',med,'-> public_test rel=%.4f'%metrics(Ph[med],t)[0])
print('ensemble-mean public_test rel=%.4f'%metrics(Ph.mean(0),t)[0])
print('individual public_test rels:',[round(metrics(Ph[i],t)[0],4) for i in range(5)])
PY
python3 valid_medoid.pyseed0 public_test rel=0.3426 seed1 public_test rel=0.3669 seed2 public_test rel=0.2923 seed3 public_test rel=0.2747 seed4 public_test rel=0.2638 medoid seed (by train consensus): 3 -> public_test rel=0.2747 ensemble-mean public_test rel=0.2979 individual public_test rels: [np.float64(0.3426), np.float64(0.3669), np.float64(0.2923), np.float64(0.2747), np.float64(0.2638)]
cd /app && python3 -c "
import torch, json
from candidate_model import PointNetRegressor
ck=torch.load('/app/outputs/model.pt', weights_only=True)
m=json.load(open('task_config.json'))['model']
net=PointNetRegressor(m['feature_width'],m['global_width'],m['head_width'])
net.load_state_dict(ck['state_dict'],strict=True); net.eval()
x=torch.randn(3,2048,3)
y=net((x-ck['feat_mean'])/ck['feat_std'])*ck['label_std']+ck['label_mean']
assert y.shape==(3,1)
print('FINAL DELIVERABLE OK')
print(' required keys present:', all(k in ck for k in ['state_dict','feat_mean','feat_std','label_mean','label_std']))
print(' feat_mean/std len:', ck['feat_mean'].numel(), ck['feat_std'].numel(),
'| label_mean/std len:', ck['label_mean'].numel(), ck['label_std'].numel())
print(' loads strict into PointNetRegressor and maps [B,2048,3]->[B,1]')
" && ls -la /app/outputs/model.ptFINAL DELIVERABLE OK required keys present: True feat_mean/std len: 3 3 | label_mean/std len: 1 1 loads strict into PointNetRegressor and maps [B,2048,3]->[B,1] -rw-r--r-- 1 h2tagent h2tagent 384685 Jun 20 22:28 /app/outputs/model.pt
No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary # task: simjeb-bracket-fea-mass-prediction-real # attempt: 2 status: completed raw_score: 0.42 # reward rule: continuous: score 0.42 < 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). ---------------------------------------------------------------------- FINAL DELIVERABLE OK required keys present: True feat_mean/std len: 3 3 | label_mean/std len: 1 1 loads strict into PointNetRegressor and maps [B,2048,3]->[B,1] -rw-r--r-- 1 h2tagent h2tagent 384685 Jun 20 22:28 /app/outputs/model.pt
Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_b2021d3a31414464. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_b2021d3a31414464 · verifier authoritative; classifier explanatory.