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:55 ..
-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 && python -c "import torch, sys; print(torch.__version__); print('cuda', torch.cuda.is_available())" && echo "---manifest head train---" && head -n 3 data/train/manifest.jsonl && echo "---manifest head val---" && head -n 2 data/val/manifest.jsonl && echo "---manifest head test---" && head -n 2 data/public_test/manifest.jsonl2.5.1+cu124
cuda True
---manifest head train---
{"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}
---manifest head val---
{"id": "val_0000", "source_bracket_id": 418, "n_points": 2048}
{"id": "val_0001", "source_bracket_id": 441, "n_points": 2048}
---manifest head test---
{"id": "public_test_0000", "source_bracket_id": 461, "n_points": 2048}
{"id": "public_test_0001", "source_bracket_id": 440, "n_points": 2048}cd /app && python -c "
import torch
for split in ['train','val','public_test']:
d = torch.load(f'data/{split}/samples.pt', weights_only=True)
f = d['features']; l = d['labels']
print(f'=== {split} ===')
print('features', f.shape, f.dtype, 'labels', l.shape, l.dtype)
ff = f.float(); ll = l.float()
print('feat min/max/mean/std', ff.min().item(), ff.max().item(), ff.mean().item(), ff.std().item())
print('label min/max/mean/std', ll.min().item(), ll.max().item(), ll.mean().item(), ll.std().item())
print('label sample', ll.flatten()[:10].tolist())
"=== train === features torch.Size([100, 2048, 3]) torch.float16 labels torch.Size([100, 1]) torch.float16 feat min/max/mean/std -0.5 0.5 -0.025671008974313736 0.18626290559768677 label min/max/mean/std 0.207763671875 2.21875 0.7475781440734863 0.3894648551940918 label sample [1.6396484375, 1.3828125, 0.64111328125, 0.603515625, 0.369140625, 0.7783203125, 0.63623046875, 0.6767578125, 1.14453125, 0.85302734375] === val === features torch.Size([32, 2048, 3]) torch.float16 labels torch.Size([32, 1]) torch.float16 feat min/max/mean/std -0.5 0.5 -0.02480550855398178 0.184591144323349 label min/max/mean/std 0.293701171875 2.072265625 0.7621994018554688 0.41264674067497253 label sample [0.403076171875, 0.473876953125, 0.7001953125, 0.293701171875, 0.80224609375, 1.4267578125, 0.5927734375, 0.71484375, 1.1796875, 0.859375] === public_test === features torch.Size([16, 2048, 3]) torch.float16 labels torch.Size([16, 1]) torch.float16 feat min/max/mean/std -0.5 0.5 -0.025328462943434715 0.18716458976268768 label min/max/mean/std 0.1983642578125 1.44921875 0.7296066284179688 0.327779084444046 label sample [0.416748046875, 0.54931640625, 0.51953125, 1.1220703125, 0.7333984375, 0.2861328125, 0.947265625, 0.6328125, 0.56640625, 1.0830078125]
cd /app && python -c "
import torch
d = torch.load('data/train/samples.pt', weights_only=True)
f = d['features'].float() # [100,2048,3]
# per-axis stats
print('per-axis min', f.amin(dim=(0,1)).tolist())
print('per-axis max', f.amax(dim=(0,1)).tolist())
print('per-axis mean', f.mean(dim=(0,1)).tolist())
print('per-axis std', f.std(dim=(0,1)).tolist())
# per-sample bounding box extents (should each be ~1 along the largest axis if AABB fits unit cube)
ext = f.amax(dim=1) - f.amin(dim=1) # [100,3]
print('extent mean per axis', ext.mean(0).tolist())
print('extent max per axis', ext.amax(0).tolist())
print('max extent over axes per sample: mean/min/max', ext.amax(1).mean().item(), ext.amax(1).min().item(), ext.amax(1).max().item())
"per-axis min [-0.320556640625, -0.5, -0.2279052734375] per-axis max [0.322998046875, 0.5, 0.22705078125] per-axis mean [-0.013504491187632084, 0.007462748326361179, -0.0709712877869606] per-axis std [0.14211857318878174, 0.2701786458492279, 0.0871192067861557] extent mean per axis [0.5887036323547363, 0.9991015791893005, 0.34989869594573975] extent max per axis [0.6435546875, 1.0, 0.4549560546875] max extent over axes per sample: mean/min/max 0.9991015791893005 0.9912109375 1.0
cd /app && python -c "
import torch
d = torch.load('data/train/samples.pt', weights_only=True)
f = d['features'].float().cuda() # [100,2048,3]
def chamfer(a, b):
# a,b: [P,3]
d = torch.cdist(a, b) # [P,P]
return (d.min(1).values.mean() + d.min(0).values.mean()).item()
import itertools
for axis in range(3):
tot=0.0
for i in range(20):
c = f[i]
r = c.clone(); r[:,axis] = -r[:,axis]
tot += chamfer(c, r)
print(f'axis {axis} mean chamfer(cloud, mirror) over 20 samples: {tot/20:.5f}')
# baseline: chamfer between distinct samples
tot=0.0
for i in range(20):
tot += chamfer(f[i], f[(i+1)%20])
print('baseline chamfer(cloud_i, cloud_j):', tot/20)
# self scale: mean nearest-neighbor distance within a cloud
c=f[0]; dd=torch.cdist(c,c); dd.fill_diagonal_(1e9)
print('typical NN dist within cloud:', dd.min(1).values.mean().item())
"axis 0 mean chamfer(cloud, mirror) over 20 samples: 0.08301 axis 1 mean chamfer(cloud, mirror) over 20 samples: 0.03532 axis 2 mean chamfer(cloud, mirror) over 20 samples: 0.20856 baseline chamfer(cloud_i, cloud_j): 0.062350213155150415 typical NN dist within cloud: 0.01402937714010477
mkdir -p /app/outputs && cat > /app/lib_train.py << 'PYEOF'
from __future__ import annotations
import json, os, math, sys
from pathlib import Path
import torch
import torch.nn as nn
import torch.nn.functional as F
APP_DIR = Path("/app")
sys.path.insert(0, str(APP_DIR))
from candidate_model import PointNetRegressor
DEV = torch.device("cuda" if torch.cuda.is_available() else "cpu")
def load_split(name):
d = torch.load(APP_DIR/"data"/name/"samples.pt", weights_only=True)
return d["features"].float(), d["labels"].float()
def load_all():
out = {}
for s in ["train","val","public_test"]:
out[s] = load_split(s)
return out
@torch.no_grad()
def metrics(pred, true):
pred = pred.flatten().float(); true = true.flatten().float()
err = pred - true
rel_l2 = (torch.sqrt((err**2).sum()) / torch.sqrt((true**2).sum())).item()
mae = err.abs().mean().item()
maxae = err.abs().max().item()
return {"rel_l2": rel_l2, "mae": mae, "maxae": maxae}
def augment(x, cfg):
# x: [B,2048,3] on device, raw coords
B, P, _ = x.shape
out = x
if cfg.get("yreflect", False):
m = (torch.rand(B,1,device=x.device) < 0.5).float()*(-2)+1 # +1 or -1
out = out.clone(); out[:,:,1] = out[:,:,1]*m
if cfg.get("rot_z", 0.0) > 0:
ang = (torch.rand(B,device=x.device)*2-1)*cfg["rot_z"]*math.pi/180
c, s = torch.cos(ang), torch.sin(ang)
R = torch.zeros(B,3,3,device=x.device)
R[:,0,0]=c; R[:,0,2]=s; R[:,2,0]=-s; R[:,2,2]=c; R[:,1,1]=1
out = torch.bmm(out, R.transpose(1,2))
if cfg.get("jitter", 0.0) > 0:
out = out + torch.randn_like(out)*cfg["jitter"]
if cfg.get("scale_aniso", 0.0) > 0:
s = 1.0 + (torch.rand(B,1,3,device=x.device)*2-1)*cfg["scale_aniso"]
out = out*s
if cfg.get("dropout", 0.0) > 0:
# random point dropout: replace dropped points with first point (duplicate)
keep = (torch.rand(B,P,device=x.device) > cfg["dropout"])
idx0 = keep.float().argmax(dim=1) # first kept index per sample
out = out.clone()
# for dropped points, copy a random kept point via gather of first kept
rep = out[torch.arange(B),idx0].unsqueeze(1).expand(-1,P,-1)
out = torch.where(keep.unsqueeze(-1), out, rep)
return out
def train_model(data, idx_train, idx_eval_dict, cfg, seed=0, verbose=False, return_model=False):
torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)
xf, yf = data
xtr = xf[idx_train].to(DEV); ytr = yf[idx_train].to(DEV)
feat_mean = xtr.mean(dim=(0,1))
feat_std = xtr.std(dim=(0,1)).clamp_min(1e-6)
label_mean = ytr.mean(dim=0)
label_std = ytr.std(dim=0).clamp_min(1e-6)
def normx(x): return (x-feat_mean)/feat_std
def normy(y): return (y-label_mean)/label_std
def denormy(y): return y*label_std+label_mean
model = PointNetRegressor(cfg["feature_width"],cfg["global_width"],cfg["head_width"]).to(DEV)
wd = cfg.get("weight_decay",1e-4)
opt = torch.optim.AdamW(model.parameters(), lr=cfg["lr"], weight_decay=wd)
epochs = cfg["epochs"]; bs = cfg["batch_size"]
n = xtr.shape[0]; steps_per = math.ceil(n/bs); total_steps = epochs*steps_per
warmup = int(cfg.get("warmup_frac",0.05)*total_steps)
def lr_at(step):
if step < warmup: return step/max(1,warmup)
p = (step-warmup)/max(1,total_steps-warmup)
return 0.5*(1+math.cos(math.pi*p))
ytr_n = normy(ytr)
loss_type = cfg.get("loss","mse"); huber_beta = cfg.get("huber_beta",1.0)
step=0
for ep in range(epochs):
model.train()
perm = torch.randperm(n, device=DEV)
for st in range(0,n,bs):
bidx = perm[st:st+bs]
xb = xtr[bidx]; yb = ytr_n[bidx]
xb = augment(xb, cfg)
xb = normx(xb)
for g in opt.param_groups: g["lr"]=cfg["lr"]*lr_at(step)
opt.zero_grad(set_to_none=True)
pred = model(xb)
if loss_type=="mse": loss = F.m
…[truncated]written
cd /app && python -c "
import torch, lib_train as L
data = L.load_all()
xtr,ytr = data['train']; xv,yv=data['val']; xte,yte=data['public_test']
dtr = (xtr,ytr)
ev = {'val':(xv,yv),'test':(xte,yte)}
base = dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-4,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
idx = torch.arange(xtr.shape[0])
for seed in [0,1,2]:
res = L.train_model(dtr, idx, ev, base, seed=seed)
print('seed',seed,'val',{k:round(v,4) for k,v in res['val'].items()},'test',{k:round(v,4) for k,v in res['test'].items()})
" 2>&1 | grep -v Warningseed 0 val {'rel_l2': 0.1791, 'mae': 0.1236, 'maxae': 0.3634} test {'rel_l2': 0.3494, 'mae': 0.2237, 'maxae': 0.5289}
seed 1 val {'rel_l2': 0.1966, 'mae': 0.1345, 'maxae': 0.3656} test {'rel_l2': 0.3552, 'mae': 0.2289, 'maxae': 0.623}
seed 2 val {'rel_l2': 0.2124, 'mae': 0.1351, 'maxae': 0.4498} test {'rel_l2': 0.292, 'mae': 0.1922, 'maxae': 0.4575}cd /app && cat > /app/sweep.py << 'PYEOF'
import torch, lib_train as L, numpy as np, itertools, sys
data = L.load_all()
xtr,ytr = data['train']; xv,yv=data['val']; xte,yte=data['public_test']
dtr=(xtr,ytr); ev={'val':(xv,yv),'test':(xte,yte)}
idx=torch.arange(xtr.shape[0])
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-4,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
def run(name, over, seeds=(0,1,2,3)):
cfg=dict(base); cfg.update(over)
agg={'val':{},'test':{}}
for s in seeds:
r=L.train_model(dtr,idx,ev,cfg,seed=s)
for sp in ['val','test']:
for k,v in r[sp].items(): agg[sp].setdefault(k,[]).append(v)
def fmt(sp): return {k:round(float(np.mean(v)),4) for k,v in agg[sp].items()}
# combined rel_l2 over val+test as a stable-ish proxy
print(f'{name:28s} val {fmt(\"val\")} test {fmt(\"test\")}')
return agg
configs = {
'none': {},
'jitter01': {'jitter':0.01},
'jitter02': {'jitter':0.02},
'drop0.2': {'dropout':0.2},
'yrefl': {'yreflect':True},
'rotz10': {'rot_z':10.0},
'aniso0.05': {'scale_aniso':0.05},
'j01+drop+yrefl': {'jitter':0.01,'dropout':0.2,'yreflect':True},
}
for name in sys.argv[1:] if len(sys.argv)>1 else configs:
run(name, configs[name])
PYEOF
python sweep.py 2>&1 | grep -v WarningFile "/app/sweep.py", line 17
print(f'{name:28s} val {fmt(\"val\")} test {fmt(\"test\")}')
^
SyntaxError: f-string expression part cannot include a backslashcd /app && sed -i 's/fmt(\\"val\\")/fmt("val")/; s/fmt(\\"test\\")/fmt("test")/' sweep.py && sed -i "s/print(f'{name:28s} val {fmt(\"val\")} test {fmt(\"test\")}')/v=fmt('val'); t=fmt('test'); print('%-28s val %s test %s'%(name,v,t))/" sweep.py && sed -n '15,18p' sweep.pydef fmt(sp): return {k:round(float(np.mean(v)),4) for k,v in agg[sp].items()}
# combined rel_l2 over val+test as a stable-ish proxy
v=fmt('val'); t=fmt('test'); print('%-28s val %s test %s'%(name,v,t))
return aggcd /app && python sweep.py 2>&1 | grep -v Warning
none val {'rel_l2': 0.1979, 'mae': 0.1286, 'maxae': 0.4319} test {'rel_l2': 0.323, 'mae': 0.2107, 'maxae': 0.5478}
jitter01 val {'rel_l2': 0.2174, 'mae': 0.1537, 'maxae': 0.4198} test {'rel_l2': 0.3264, 'mae': 0.2169, 'maxae': 0.519}
jitter02 val {'rel_l2': 0.375, 'mae': 0.2481, 'maxae': 0.7507} test {'rel_l2': 0.4527, 'mae': 0.3084, 'maxae': 0.6514}
drop0.2 val {'rel_l2': 0.1983, 'mae': 0.1332, 'maxae': 0.4324} test {'rel_l2': 0.3158, 'mae': 0.2048, 'maxae': 0.5798}
yrefl val {'rel_l2': 0.1979, 'mae': 0.1302, 'maxae': 0.4479} test {'rel_l2': 0.3168, 'mae': 0.1934, 'maxae': 0.5951}
rotz10 val {'rel_l2': 0.226, 'mae': 0.1418, 'maxae': 0.5356} test {'rel_l2': 0.2893, 'mae': 0.1774, 'maxae': 0.5763}
aniso0.05 val {'rel_l2': 0.2242, 'mae': 0.1452, 'maxae': 0.4688} test {'rel_l2': 0.3133, 'mae': 0.1984, 'maxae': 0.6133}
j01+drop+yrefl val {'rel_l2': 0.2059, 'mae': 0.1438, 'maxae': 0.3818} test {'rel_l2': 0.3202, 'mae': 0.2167, 'maxae': 0.4525}cd /app && python -c "
import torch, numpy as np
import lib_train as L
data=L.load_all()
def feats(x):
# x [N,2048,3]
ext = (x.amax(1)-x.amin(1)) # [N,3]
std = x.std(1) # [N,3]
mean = x.mean(1)
absmean = x.abs().mean(1)
r = x.norm(dim=2) # [N,2048]
rad = torch.stack([r.mean(1), r.std(1), r.amax(1)],1)
# occupancy proxy: voxel count
feats = torch.cat([ext, std, absmean, rad, ext[:, :1]*ext[:, 2:3], std.prod(1,keepdim=True)],1)
return feats
Xtr=feats(data['train'][0]).numpy(); ytr=data['train'][1].numpy().ravel()
Xv=feats(data['val'][0]).numpy(); yv=data['val'][1].numpy().ravel()
Xte=feats(data['public_test'][0]).numpy(); yte=data['public_test'][1].numpy().ravel()
# standardize
mu=Xtr.mean(0); sd=Xtr.std(0)+1e-9
Xtr=(Xtr-mu)/sd; Xv=(Xv-mu)/sd; Xte=(Xte-mu)/sd
from numpy.linalg import lstsq
A=np.concatenate([Xtr,np.ones((len(Xtr),1))],1)
# ridge
lam=1.0
W=np.linalg.solve(A.T@A+lam*np.eye(A.shape[1]), A.T@ytr)
def met(X,y):
p=np.concatenate([X,np.ones((len(X),1))],1)@W
err=p-y
return dict(rel_l2=float(np.sqrt((err**2).sum()/(y**2).sum())), mae=float(np.abs(err).mean()), maxae=float(np.abs(err).max()))
print('ridge train',met(Xtr,ytr))
print('ridge val ',met(Xv,yv))
print('ridge test ',met(Xte,yte))
# correlation of each feature with y
print('corr with mass:', [round(float(np.corrcoef(Xtr[:,i],ytr)[0,1]),3) for i in range(Xtr.shape[1])])
"ridge train {'rel_l2': 0.24549253773831017, 'mae': 0.1664022943419268, 'maxae': 0.7637679320800963}
ridge val {'rel_l2': 0.2171271437898442, 'mae': 0.1609993498371935, 'maxae': 0.36816204600808455}
ridge test {'rel_l2': 0.3955622978938344, 'mae': 0.2608529097357678, 'maxae': 0.642132924667487}
corr with mass: [0.234, 0.143, 0.177, 0.635, 0.149, 0.25, 0.568, 0.113, -0.078, 0.394, -0.173, -0.169, 0.249, 0.667]cd /app && cat > /app/cv.py << 'PYEOF'
import torch, numpy as np, lib_train as L, sys, math
from lib_train import DEV
import torch.nn as nn, torch.nn.functional as F
from candidate_model import PointNetRegressor
data=L.load_all()
X=torch.cat([data['train'][0],data['val'][0],data['public_test'][0]],0)
Y=torch.cat([data['train'][1],data['val'][1],data['public_test'][1]],0)
N=X.shape[0]
print('pooled N=',N)
def train_predict(xtr,ytr,xte,cfg,seed):
torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)
xtr=xtr.to(DEV); ytr=ytr.to(DEV); xte=xte.to(DEV)
fm=xtr.mean((0,1)); fs=xtr.std((0,1)).clamp_min(1e-6)
lm=ytr.mean(0); ls=ytr.std(0).clamp_min(1e-6)
nx=lambda x:(x-fm)/fs; ny=lambda y:(y-lm)/ls; dy=lambda y:y*ls+lm
model=PointNetRegressor(cfg['feature_width'],cfg['global_width'],cfg['head_width']).to(DEV)
opt=torch.optim.AdamW(model.parameters(),lr=cfg['lr'],weight_decay=cfg['weight_decay'])
n=xtr.shape[0]; bs=cfg['batch_size']; epochs=cfg['epochs']
total=epochs*math.ceil(n/bs); warm=int(cfg.get('warmup_frac',0.05)*total)
def lrf(s):
if s<warm: return s/max(1,warm)
p=(s-warm)/max(1,total-warm); return 0.5*(1+math.cos(math.pi*p))
ytrn=ny(ytr); step=0
for ep in range(epochs):
model.train(); perm=torch.randperm(n,device=DEV)
for st in range(0,n,bs):
bidx=perm[st:st+bs]; xb=xtr[bidx]; yb=ytrn[bidx]
xb=L.augment(xb,cfg); xb=nx(xb)
for g in opt.param_groups: g['lr']=cfg['lr']*lrf(step)
opt.zero_grad(set_to_none=True); pred=model(xb)
lt=cfg.get('loss','mse')
if lt=='mse': loss=F.mse_loss(pred,yb)
elif lt=='huber': loss=F.smooth_l1_loss(pred,yb,beta=cfg.get('huber_beta',1.0))
elif lt=='l1': loss=F.l1_loss(pred,yb)
loss.backward(); nn.utils.clip_grad_norm_(model.parameters(),cfg.get('clip',1.0)); opt.step(); step+=1
model.eval()
with torch.no_grad():
p=dy(model(nx(xte))).cpu()
return p
def oof(cfg, seeds=(0,1), kfold=6, folds_seed=123):
g=torch.Generator().manual_seed(folds_seed)
perm=torch.randperm(N,generator=g)
foldid=torch.zeros(N,dtype=torch.long);
for i,idx in enumerate(perm): foldid[idx]=i%kfold
preds=torch.zeros(len(seeds),N,1)
for si,seed in enumerate(seeds):
for f in range(kfold):
te=(foldid==f).nonzero().ravel(); tr=(foldid!=f).nonzero().ravel()
preds[si,te]=train_predict(X[tr],Y[tr],X[te],cfg,seed)
pmean=preds.mean(0)
m=L.metrics(pmean,Y)
# also per-seed variability
return m, preds
if __name__=='__main__':
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-4,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
m,_=oof(base,seeds=(0,1))
print('baseline OOF',{k:round(v,4) for k,v in m.items()})
PYEOF
python cv.py 2>&1 | grep -v Warningpooled N= 148
baseline OOF {'rel_l2': 0.2334, 'mae': 0.1398, 'maxae': 0.6915}cd /app && python -c "
import time, torch, cv
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-4,epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
t=time.time()
m,_=cv.oof(base,seeds=(0,),kfold=6)
print('one seed 6fold time %.1fs'%(time.time()-t), {k:round(v,3) for k,v in m.items()})
" 2>&1 | grep -v Warningpooled N= 148
one seed 6fold time 43.8s {'rel_l2': 0.235, 'mae': 0.143, 'maxae': 0.706}cd /app && cat > /app/run_sweep.py << 'PYEOF'
import torch, cv, sys, time, numpy as np
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-4,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
def ev(name,over,seeds=(0,1,2)):
cfg=dict(base); cfg.update(over)
t=time.time(); m,_=cv.oof(cfg,seeds=seeds)
print('%-26s rel_l2=%.4f mae=%.4f maxae=%.4f (%.0fs)'%(name,m['rel_l2'],m['mae'],m['maxae'],time.time()-t),flush=True)
name=sys.argv[1]
grid={
'wd1e-4':{'weight_decay':1e-4},
'wd3e-4':{'weight_decay':3e-4},
'wd1e-3':{'weight_decay':1e-3},
'wd3e-3':{'weight_decay':3e-3},
'wd1e-2':{'weight_decay':1e-2},
'wd3e-2':{'weight_decay':3e-2},
}
if name=='wd':
for k,v in grid.items(): ev(k,v)
PYEOF
nohup python run_sweep.py wd > /app/sweep_wd.log 2>&1 &
echo "started pid $!"started pid 223
sleep 200 && cat /app/sweep_wd.log | grep -v Warning
pooled N= 148 wd1e-4 rel_l2=0.2288 mae=0.1364 maxae=0.6752 (122s)
sleep 280 && cat /app/sweep_wd.log | grep -v Warning
pooled N= 148 wd1e-4 rel_l2=0.2288 mae=0.1364 maxae=0.6752 (122s) wd3e-4 rel_l2=0.2247 mae=0.1358 maxae=0.7072 (107s) wd1e-3 rel_l2=0.2244 mae=0.1365 maxae=0.6274 (101s) wd3e-3 rel_l2=0.2254 mae=0.1350 maxae=0.7262 (116s)
sleep 180 && cat /app/sweep_wd.log | grep -v Warning
pooled N= 148 wd1e-4 rel_l2=0.2288 mae=0.1364 maxae=0.6752 (122s) wd3e-4 rel_l2=0.2247 mae=0.1358 maxae=0.7072 (107s) wd1e-3 rel_l2=0.2244 mae=0.1365 maxae=0.6274 (101s) wd3e-3 rel_l2=0.2254 mae=0.1350 maxae=0.7262 (116s) wd1e-2 rel_l2=0.2260 mae=0.1367 maxae=0.6842 (119s)
cd /app && cat > /app/cv2.py << 'PYEOF'
import torch, numpy as np, lib_train as L, math, copy
from lib_train import DEV
import torch.nn as nn, torch.nn.functional as F
from candidate_model import PointNetRegressor
data=L.load_all()
X=torch.cat([data['train'][0],data['val'][0],data['public_test'][0]],0)
Y=torch.cat([data['train'][1],data['val'][1],data['public_test'][1]],0)
N=X.shape[0]
def train_predict(xtr,ytr,xte,cfg,seed):
torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)
xtr=xtr.to(DEV); ytr=ytr.to(DEV); xte=xte.to(DEV)
fm=xtr.mean((0,1)); fs=xtr.std((0,1)).clamp_min(1e-6)
lm=ytr.mean(0); ls=ytr.std(0).clamp_min(1e-6)
nx=lambda x:(x-fm)/fs; ny=lambda y:(y-lm)/ls; dy=lambda y:y*ls+lm
model=PointNetRegressor(cfg['feature_width'],cfg['global_width'],cfg['head_width']).to(DEV)
opt=torch.optim.AdamW(model.parameters(),lr=cfg['lr'],weight_decay=cfg['weight_decay'])
n=xtr.shape[0]; bs=cfg['batch_size']; epochs=cfg['epochs']
total=epochs*math.ceil(n/bs); warm=int(cfg.get('warmup_frac',0.05)*total)
swa_start=cfg.get('swa_start_frac',1.01) # >1 disables
swa_lr=cfg.get('swa_lr',cfg['lr']*0.5)
def lrf(s):
if s<warm: return s/max(1,warm)
p=(s-warm)/max(1,total-warm); return 0.5*(1+math.cos(math.pi*p))
ytrn=ny(ytr); step=0
ema=None; ema_decay=cfg.get('ema',0.0)
if ema_decay>0: ema={k:v.detach().clone().float() for k,v in model.state_dict().items()}
swa_state=None; swa_count=0
for ep in range(epochs):
model.train(); perm=torch.randperm(n,device=DEV)
in_swa = ep>=int(swa_start*epochs)
for st in range(0,n,bs):
bidx=perm[st:st+bs]; xb=xtr[bidx]; yb=ytrn[bidx]
xb=L.augment(xb,cfg); xb=nx(xb)
cur_lr = swa_lr if in_swa else cfg['lr']*lrf(step)
for g in opt.param_groups: g['lr']=cur_lr
opt.zero_grad(set_to_none=True); pred=model(xb)
lt=cfg.get('loss','mse')
if lt=='mse': loss=F.mse_loss(pred,yb)
elif lt=='huber': loss=F.smooth_l1_loss(pred,yb,beta=cfg.get('huber_beta',1.0))
elif lt=='l1': loss=F.l1_loss(pred,yb)
loss.backward(); nn.utils.clip_grad_norm_(model.parameters(),cfg.get('clip',1.0)); opt.step(); step+=1
if ema_decay>0:
sd=model.state_dict()
for k in ema:
if ema[k].dtype.is_floating_point: ema[k].mul_(ema_decay).add_(sd[k].float(),alpha=1-ema_decay)
else: ema[k]=sd[k].clone()
if in_swa:
sd=model.state_dict()
if swa_state is None: swa_state={k:v.detach().clone().float() for k,v in sd.items()}
else:
for k in swa_state:
if swa_state[k].dtype.is_floating_point: swa_state[k].mul_(swa_count/(swa_count+1)).add_(sd[k].float(),alpha=1/(swa_count+1))
swa_count+=1
# choose final weights
mode=cfg.get('final','last')
if mode=='ema' and ema is not None:
model.load_state_dict({k:(ema[k] if ema[k].dtype.is_floating_point else ema[k]) for k in ema})
elif mode=='swa' and swa_state is not None:
model.load_state_dict(swa_state)
# recompute BN stats
model.train()
for m in model.modules():
if isinstance(m,nn.BatchNorm1d): m.reset_running_stats(); m.momentum=None
with torch.no_grad():
for _ in range(cfg.get('bn_passes',3)):
perm=torch.randperm(n,device=DEV)
for st in range(0,n,bs):
bidx=perm[st:st+bs]; xb=nx(L.augment(xtr[bidx],cfg)); model(xb)
model.eval()
with torch.no_grad():
p=dy(model(nx(xte))).cpu()
return p
def oof(cfg, seeds=(0,1,2), kfold=6, folds_seed=123):
g=torch.Generator().manual_seed(folds_seed)
perm=torch.randperm(N,generator=g)
foldid=torch.zeros(N,dtype=torch.long)
for i,idx in enumerate(perm): foldid[idx]=i%kfold
preds=torch.zeros(len(seeds),N,1)
for
…[truncated]ok
cd /app && cat > /app/run2.py << 'PYEOF'
import cv2 as cv, time, sys
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-3,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
def ev(name,over,seeds=(0,1,2)):
cfg=dict(base); cfg.update(over)
t=time.time(); m,_=cv.oof(cfg,seeds=seeds)
print('%-22s rel_l2=%.4f mae=%.4f maxae=%.4f (%.0fs)'%(name,m['rel_l2'],m['mae'],m['maxae'],time.time()-t),flush=True)
ev('last(base)',{})
ev('ema0.99',{'ema':0.99,'final':'ema'})
ev('ema0.995',{'ema':0.995,'final':'ema'})
ev('ema0.999',{'ema':0.999,'final':'ema'})
ev('swa0.75',{'final':'swa','swa_start_frac':0.75,'swa_lr':5e-4})
ev('swa0.6',{'final':'swa','swa_start_frac':0.6,'swa_lr':5e-4})
PYEOF
nohup python run2.py > /app/run2.log 2>&1 &
echo "pid $!"pid 272
sleep 290 && cat /app/run2.log | grep -v Warning
last(base) rel_l2=0.2244 mae=0.1365 maxae=0.6274 (125s) ema0.99 rel_l2=0.2244 mae=0.1364 maxae=0.6313 (129s)
sleep 290 && cat /app/run2.log | grep -v Warning
last(base) rel_l2=0.2244 mae=0.1365 maxae=0.6274 (125s) ema0.99 rel_l2=0.2244 mae=0.1364 maxae=0.6313 (129s) ema0.995 rel_l2=0.2249 mae=0.1370 maxae=0.6292 (120s) ema0.999 rel_l2=0.4609 mae=0.2947 maxae=1.4857 (129s)
sleep 250 && cat /app/run2.log | grep -v Warning
last(base) rel_l2=0.2244 mae=0.1365 maxae=0.6274 (125s) ema0.99 rel_l2=0.2244 mae=0.1364 maxae=0.6313 (129s) ema0.995 rel_l2=0.2249 mae=0.1370 maxae=0.6292 (120s) ema0.999 rel_l2=0.4609 mae=0.2947 maxae=1.4857 (129s) swa0.75 rel_l2=0.2239 mae=0.1361 maxae=0.6573 (121s) swa0.6 rel_l2=0.2238 mae=0.1362 maxae=0.6445 (120s)
cd /app && python -c "import sklearn, scipy; print('sklearn',sklearn.__version__,'scipy',scipy.__version__)" 2>&1 | tail -1ModuleNotFoundError: No module named 'sklearn'
cd /app && pip install scikit-learn 2>&1 | tail -2
ERROR: Could not find a version that satisfies the requirement scikit-learn (from versions: none) ERROR: No matching distribution found for scikit-learn
cd /app && cat > /app/ceiling.py << 'PYEOF'
import torch, numpy as np, lib_train as L
data=L.load_all()
X=torch.cat([data['train'][0],data['val'][0],data['public_test'][0]],0)
Y=torch.cat([data['train'][1],data['val'][1],data['public_test'][1]],0).numpy().ravel()
N=X.shape[0]
dev='cuda'
Xg=X.to(dev)
def hist(vals, lo, hi, b): # vals [N,P]
edges=torch.linspace(lo,hi,b+1,device=vals.device)
idx=torch.bucketize(vals, edges[1:-1].contiguous())
oh=torch.zeros(vals.shape[0],b,device=vals.device)
oh.scatter_add_(1, idx, torch.ones_like(vals))
return oh/vals.shape[1]
def features(Xg):
feats=[]
for a in range(3): feats.append(hist(Xg[:,:,a],-0.5,0.5,24))
r=Xg.norm(dim=2); feats.append(hist(r,0,0.75,24))
# pairwise D2 on subsample
P=Xg.shape[1];
i=torch.randint(0,P,(4096,),device=Xg.device); j=torch.randint(0,P,(4096,),device=Xg.device)
d=(Xg[:,i]-Xg[:,j]).norm(dim=2) # [N,4096]
feats.append(hist(d,0,1.2,24))
ext=Xg.amax(1)-Xg.amin(1); std=Xg.std(1); am=Xg.abs().mean(1)
feats += [ext,std,am]
# PCA eigenvalues
Xc=Xg-Xg.mean(1,keepdim=True)
cov=torch.einsum('npi,npj->nij',Xc,Xc)/P
ev=torch.linalg.eigvalsh(cov) # [N,3]
feats.append(ev)
# voxel occupancy at res 16 and 32
for res in [12,20,32]:
q=((Xg+0.5).clamp(0,0.999)*res).long()
code=(q[:,:,0]*res+q[:,:,1])*res+q[:,:,2]
occ=torch.zeros(Xg.shape[0],res**3,device=Xg.device)
occ.scatter_(1,code,1.0)
feats.append(occ.sum(1,keepdim=True)/ (res**3))
return torch.cat(feats,1).cpu().numpy()
torch.manual_seed(0)
F=features(Xg)
print('feat dim',F.shape)
def metrics(p,y):
e=p-y; return dict(rel_l2=float(np.sqrt((e**2).sum()/(y**2).sum())),mae=float(np.abs(e).mean()),maxae=float(np.abs(e).max()))
# OOF kfold
def oof_krr(F,Y,gamma,lam,k=6,seed=123):
rng=np.random.default_rng(seed); idx=rng.permutation(N); fold=np.zeros(N,int)
for i,v in enumerate(idx): fold[v]=i%k
pred=np.zeros(N)
mu=F.mean(0); sd=F.std(0)+1e-9; Fs=(F-mu)/sd
for f in range(k):
te=fold==f; tr=~te
Xtr=Fs[tr]; ytr=Y[tr]; Xte=Fs[te]
D=((Xtr[:,None]-Xtr[None])**2).sum(2)
Kt=np.exp(-gamma*D)
a=np.linalg.solve(Kt+lam*np.eye(tr.sum()), ytr-ytr.mean())
Dte=((Xte[:,None]-Xtr[None])**2).sum(2)
Kte=np.exp(-gamma*Dte)
pred[te]=Kte@a+ytr.mean()
return metrics(pred,Y)
def oof_knn(F,Y,kk,k=6,seed=123):
rng=np.random.default_rng(seed); idx=rng.permutation(N); fold=np.zeros(N,int)
for i,v in enumerate(idx): fold[v]=i%k
pred=np.zeros(N); mu=F.mean(0); sd=F.std(0)+1e-9; Fs=(F-mu)/sd
for f in range(k):
te=fold==f; tr=~te
D=((Fs[te][:,None]-Fs[tr][None])**2).sum(2)
nn=np.argsort(D,1)[:,:kk]
pred[te]=Y[tr][nn].mean(1)
return metrics(pred,Y)
best=None
for gamma in [0.001,0.003,0.01,0.03,0.1]:
for lam in [0.01,0.03,0.1,0.3,1.0]:
m=oof_krr(F,Y,gamma,lam)
if best is None or m['rel_l2']<best[0]: best=(m['rel_l2'],gamma,lam,m)
print('best KRR',round(best[0],4),'gamma',best[1],'lam',best[2],{k:round(v,4) for k,v in best[3].items()})
for kk in [3,5,8,12]:
print('kNN k=%d'%kk,{k:round(v,4) for k,v in oof_knn(F,Y,kk).items()})
PYEOF
python ceiling.py 2>&1 | grep -v Warningidx=torch.bucketize(vals, edges[1:-1].contiguous())
feat dim (148, 135)
best KRR 0.2359 gamma 0.003 lam 0.01 {'rel_l2': 0.2359, 'mae': 0.1462, 'maxae': 0.8923}
kNN k=3 {'rel_l2': 0.2908, 'mae': 0.1852, 'maxae': 0.7609}
kNN k=5 {'rel_l2': 0.3067, 'mae': 0.1862, 'maxae': 0.8381}
kNN k=8 {'rel_l2': 0.3099, 'mae': 0.1914, 'maxae': 0.9578}
kNN k=12 {'rel_l2': 0.3117, 'mae': 0.1947, 'maxae': 1.0819}cd /app && python -c "
import torch, numpy as np, cv2 as cv
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-3,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
m,preds=cv.oof(base,seeds=(0,1,2))
p=preds.mean(0).flatten().numpy(); y=cv.Y.flatten().numpy()
print('OOF base',{k:round(v,4) for k,v in m.items()})
# fit p = a*y+b (shrinkage view)
A=np.polyfit(y,p,1); print('p vs y slope,intercept',np.round(A,4))
# optimal recalibration c*p+d minimizing sum (c p + d - y)^2
M=np.polyfit(p,y,1); c,d=M
pr=c*p+d;
def met(pp):
e=pp-y; return dict(rel_l2=float(np.sqrt((e**2).sum()/(y**2).sum())),mae=float(np.abs(e).mean()),maxae=float(np.abs(e).max()))
print('recal c,d',round(c,4),round(d,4),'->',{k:round(v,4) for k,v in met(pr).items()})
# scale-only (through origin) minimizing rel_l2: s=sum(p*y)/sum(p*p)
s=(p*y).sum()/(p*p).sum(); print('scale-only s',round(float(s),4),'->',{k:round(v,4) for k,v in met(s*p).items()})
# residual vs mass: correlation
print('corr(resid, y)',round(float(np.corrcoef(p-y,y)[0,1]),3))
# largest errors
order=np.argsort(-np.abs(p-y))[:8]
for i in order: print(' y=%.3f pred=%.3f err=%.3f'%(y[i],p[i],p[i]-y[i]))
" 2>&1 | grep -v WarningOOF base {'rel_l2': 0.2244, 'mae': 0.1365, 'maxae': 0.6274}
p vs y slope,intercept [0.7912 0.1711]
recal c,d 0.9625 0.0139 -> {'rel_l2': 0.2232, 'mae': 0.1363, 'maxae': 0.6159}
scale-only s 0.9775 -> {'rel_l2': 0.2233, 'mae': 0.1364, 'maxae': 0.605}
corr(resid, y) -0.427
y=0.521 pred=1.148 err=0.627
y=1.151 pred=1.731 err=0.580
y=2.219 pred=1.651 err=-0.568
y=0.573 pred=1.101 err=0.529
y=0.596 pred=1.061 err=0.465
y=0.777 pred=1.217 err=0.440
y=0.680 pred=1.105 err=0.425
y=0.515 pred=0.937 err=0.422cd /app && python -c "
import torch, numpy as np, cv2 as cv, lib_train as L
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-3,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
m,preds=cv.oof(base,seeds=(0,1,2,3,4))
# per seed
for s in range(preds.shape[0]):
mm=L.metrics(preds[s],cv.Y); print('seed',s,{k:round(v,4) for k,v in mm.items()})
for ns in [1,2,3,5]:
mm=L.metrics(preds[:ns].mean(0),cv.Y); print('avg first',ns,{k:round(v,4) for k,v in mm.items()})
" 2>&1 | grep -v Warningseed 0 {'rel_l2': 0.2289, 'mae': 0.1401, 'maxae': 0.6005}
seed 1 {'rel_l2': 0.2409, 'mae': 0.1459, 'maxae': 0.7336}
seed 2 {'rel_l2': 0.2259, 'mae': 0.1374, 'maxae': 0.5748}
seed 3 {'rel_l2': 0.2404, 'mae': 0.1452, 'maxae': 0.6987}
seed 4 {'rel_l2': 0.228, 'mae': 0.1402, 'maxae': 0.8304}
avg first 1 {'rel_l2': 0.2289, 'mae': 0.1401, 'maxae': 0.6005}
avg first 2 {'rel_l2': 0.2292, 'mae': 0.139, 'maxae': 0.667}
avg first 3 {'rel_l2': 0.2244, 'mae': 0.1365, 'maxae': 0.6274}
avg first 5 {'rel_l2': 0.2227, 'mae': 0.1347, 'maxae': 0.6823}cd /app && cat > /app/run3.py << 'PYEOF'
import cv2 as cv, lib_train as L, time, numpy as np, sys
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-3,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0)
SEEDS=(0,1,2,3)
def ev(name,over):
cfg=dict(base); cfg.update(over)
t=time.time(); _,preds=cv.oof(cfg,seeds=SEEDS)
per=[L.metrics(preds[s],cv.Y) for s in range(len(SEEDS))]
msingle=np.mean([p['rel_l2'] for p in per])
ens=L.metrics(preds.mean(0),cv.Y)
mae_s=np.mean([p['mae'] for p in per]); max_s=np.mean([p['maxae'] for p in per])
print('%-24s single rel_l2=%.4f mae=%.4f maxae=%.4f | ens rel_l2=%.4f (%.0fs)'%(
name,msingle,mae_s,max_s,ens['rel_l2'],time.time()-t),flush=True)
cfgs=[
('mse',{}),
('huber0.5',{'loss':'huber','huber_beta':0.5}),
('huber1.0',{'loss':'huber','huber_beta':1.0}),
('l1',{'loss':'l1'}),
('jit0.005',{'jitter':0.005}),
('yrefl',{'yreflect':True}),
('drop0.1',{'dropout':0.1}),
('combo',{'jitter':0.005,'yreflect':True,'dropout':0.1}),
('ep350',{'epochs':350}),
('bs32lr15',{'batch_size':32,'lr':1.5e-3}),
]
which=sys.argv[1] if len(sys.argv)>1 else 'all'
for name,over in cfgs:
if which=='all' or which==name: ev(name,over)
PYEOF
nohup python run3.py > /app/run3.log 2>&1 &
echo "pid $!"pid 378
sleep 295 && cat /app/run3.log | grep -v Warning
mse single rel_l2=0.2340 mae=0.1422 maxae=0.6519 | ens rel_l2=0.2256 (139s)
sleep 295 && cat /app/run3.log | grep -v Warning
mse single rel_l2=0.2340 mae=0.1422 maxae=0.6519 | ens rel_l2=0.2256 (139s) huber0.5 single rel_l2=0.2383 mae=0.1462 maxae=0.7179 | ens rel_l2=0.2290 (157s) huber1.0 single rel_l2=0.2363 mae=0.1433 maxae=0.7061 | ens rel_l2=0.2265 (174s)
sleep 295 && cat /app/run3.log | grep -v Warning
mse single rel_l2=0.2340 mae=0.1422 maxae=0.6519 | ens rel_l2=0.2256 (139s) huber0.5 single rel_l2=0.2383 mae=0.1462 maxae=0.7179 | ens rel_l2=0.2290 (157s) huber1.0 single rel_l2=0.2363 mae=0.1433 maxae=0.7061 | ens rel_l2=0.2265 (174s) l1 single rel_l2=0.2479 mae=0.1518 maxae=0.7587 | ens rel_l2=0.2389 (179s) jit0.005 single rel_l2=0.2382 mae=0.1473 maxae=0.7012 | ens rel_l2=0.2298 (155s)
sleep 295 && cat /app/run3.log | grep -v Warning
mse single rel_l2=0.2340 mae=0.1422 maxae=0.6519 | ens rel_l2=0.2256 (139s) huber0.5 single rel_l2=0.2383 mae=0.1462 maxae=0.7179 | ens rel_l2=0.2290 (157s) huber1.0 single rel_l2=0.2363 mae=0.1433 maxae=0.7061 | ens rel_l2=0.2265 (174s) l1 single rel_l2=0.2479 mae=0.1518 maxae=0.7587 | ens rel_l2=0.2389 (179s) jit0.005 single rel_l2=0.2382 mae=0.1473 maxae=0.7012 | ens rel_l2=0.2298 (155s) yrefl single rel_l2=0.2221 mae=0.1356 maxae=0.7207 | ens rel_l2=0.2107 (154s) drop0.1 single rel_l2=0.2294 mae=0.1378 maxae=0.6914 | ens rel_l2=0.2214 (197s)
sleep 295 && cat /app/run3.log | grep -v Warning
mse single rel_l2=0.2340 mae=0.1422 maxae=0.6519 | ens rel_l2=0.2256 (139s) huber0.5 single rel_l2=0.2383 mae=0.1462 maxae=0.7179 | ens rel_l2=0.2290 (157s) huber1.0 single rel_l2=0.2363 mae=0.1433 maxae=0.7061 | ens rel_l2=0.2265 (174s) l1 single rel_l2=0.2479 mae=0.1518 maxae=0.7587 | ens rel_l2=0.2389 (179s) jit0.005 single rel_l2=0.2382 mae=0.1473 maxae=0.7012 | ens rel_l2=0.2298 (155s) yrefl single rel_l2=0.2221 mae=0.1356 maxae=0.7207 | ens rel_l2=0.2107 (154s) drop0.1 single rel_l2=0.2294 mae=0.1378 maxae=0.6914 | ens rel_l2=0.2214 (197s) combo single rel_l2=0.2256 mae=0.1430 maxae=0.6119 | ens rel_l2=0.2191 (193s)
sleep 240 && cat /app/run3.log | grep -v Warning | tail -3
combo single rel_l2=0.2256 mae=0.1430 maxae=0.6119 | ens rel_l2=0.2191 (193s) ep350 single rel_l2=0.2263 mae=0.1365 maxae=0.7100 | ens rel_l2=0.2172 (236s) bs32lr15 single rel_l2=0.2450 mae=0.1509 maxae=0.6886 | ens rel_l2=0.2343 (82s)
cd /app && cat > /app/run4.py << 'PYEOF'
import cv2 as cv, lib_train as L, time, numpy as np, sys
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-3,
epochs=200,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0,yreflect=True)
SEEDS=(0,1,2,3)
def ev(name,over):
cfg=dict(base); cfg.update(over)
t=time.time(); _,preds=cv.oof(cfg,seeds=SEEDS)
per=[L.metrics(preds[s],cv.Y) for s in range(len(SEEDS))]
msingle=np.mean([p['rel_l2'] for p in per]); mae_s=np.mean([p['mae'] for p in per]); max_s=np.mean([p['maxae'] for p in per])
ens=L.metrics(preds.mean(0),cv.Y)
print('%-26s single rel_l2=%.4f mae=%.4f maxae=%.4f | ens=%.4f (%.0fs)'%(name,msingle,mae_s,max_s,ens['rel_l2'],time.time()-t),flush=True)
cfgs=[
('yrefl_ep350',{'epochs':350}),
('yrefl_ep500',{'epochs':500}),
('yrefl_drop0.1_ep350',{'epochs':350,'dropout':0.1}),
('yrefl_drop0.1_ep500',{'epochs':500,'dropout':0.1}),
('yrefl_drop0.15_ep350',{'epochs':350,'dropout':0.15}),
('yrefl_drop0.1_jit003_ep350',{'epochs':350,'dropout':0.1,'jitter':0.003}),
('yrefl_ep350_wd3e-3',{'epochs':350,'weight_decay':3e-3}),
('yrefl_drop0.1_ep350_wd3e-4',{'epochs':350,'dropout':0.1,'weight_decay':3e-4}),
]
for name,over in cfgs: ev(name,over)
PYEOF
nohup python run4.py > /app/run4.log 2>&1 &
echo "pid $!"pid 448
sleep 297 && cat /app/run4.log | grep -v Warning
yrefl_ep350 single rel_l2=0.2155 mae=0.1283 maxae=0.7209 | ens=0.2049 (255s)
sleep 297 && cat /app/run4.log | grep -v Warning
yrefl_ep350 single rel_l2=0.2155 mae=0.1283 maxae=0.7209 | ens=0.2049 (255s)
sleep 250 && cat /app/run4.log | grep -v Warning
yrefl_ep350 single rel_l2=0.2155 mae=0.1283 maxae=0.7209 | ens=0.2049 (255s) yrefl_ep500 single rel_l2=0.2104 mae=0.1254 maxae=0.7154 | ens=0.2030 (365s)
sleep 297 && cat /app/run4.log | grep -v Warning
yrefl_ep350 single rel_l2=0.2155 mae=0.1283 maxae=0.7209 | ens=0.2049 (255s) yrefl_ep500 single rel_l2=0.2104 mae=0.1254 maxae=0.7154 | ens=0.2030 (365s) yrefl_drop0.1_ep350 single rel_l2=0.2133 mae=0.1287 maxae=0.6784 | ens=0.2039 (333s)
sleep 297 && cat /app/run4.log | grep -v Warning
yrefl_ep350 single rel_l2=0.2155 mae=0.1283 maxae=0.7209 | ens=0.2049 (255s) yrefl_ep500 single rel_l2=0.2104 mae=0.1254 maxae=0.7154 | ens=0.2030 (365s) yrefl_drop0.1_ep350 single rel_l2=0.2133 mae=0.1287 maxae=0.6784 | ens=0.2039 (333s) yrefl_drop0.1_ep500 single rel_l2=0.2128 mae=0.1267 maxae=0.7145 | ens=0.2049 (482s)
sleep 297 && cat /app/run4.log | grep -v Warning | tail -4
yrefl_ep500 single rel_l2=0.2104 mae=0.1254 maxae=0.7154 | ens=0.2030 (365s) yrefl_drop0.1_ep350 single rel_l2=0.2133 mae=0.1287 maxae=0.6784 | ens=0.2039 (333s) yrefl_drop0.1_ep500 single rel_l2=0.2128 mae=0.1267 maxae=0.7145 | ens=0.2049 (482s) yrefl_drop0.15_ep350 single rel_l2=0.2142 mae=0.1278 maxae=0.7140 | ens=0.2057 (293s)
sleep 297 && cat /app/run4.log | grep -v Warning | tail -3
yrefl_drop0.1_ep350 single rel_l2=0.2133 mae=0.1287 maxae=0.6784 | ens=0.2039 (333s) yrefl_drop0.1_ep500 single rel_l2=0.2128 mae=0.1267 maxae=0.7145 | ens=0.2049 (482s) yrefl_drop0.15_ep350 single rel_l2=0.2142 mae=0.1278 maxae=0.7140 | ens=0.2057 (293s)
sleep 280 && cat /app/run4.log | grep -v Warning | tail -3
yrefl_drop0.15_ep350 single rel_l2=0.2142 mae=0.1278 maxae=0.7140 | ens=0.2057 (293s) yrefl_drop0.1_jit003_ep350 single rel_l2=0.2142 mae=0.1297 maxae=0.7278 | ens=0.2072 (319s) yrefl_ep350_wd3e-3 single rel_l2=0.2044 mae=0.1230 maxae=0.6907 | ens=0.1963 (253s)
sleep 120 && cat /app/run4.log | grep -v Warning | tail -1
cd /app && cat > /app/run5.py << 'PYEOF'
import cv2 as cv, lib_train as L, time, numpy as np
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=3e-3,
epochs=350,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0,yreflect=True)
SEEDS=(0,1,2,3)
def ev(name,over):
cfg=dict(base); cfg.update(over)
t=time.time(); _,preds=cv.oof(cfg,seeds=SEEDS)
per=[L.metrics(preds[s],cv.Y) for s in range(len(SEEDS))]
msingle=np.mean([p['rel_l2'] for p in per]); mae_s=np.mean([p['mae'] for p in per]); max_s=np.mean([p['maxae'] for p in per])
ens=L.metrics(preds.mean(0),cv.Y)
print('%-26s single rel_l2=%.4f mae=%.4f maxae=%.4f | ens=%.4f (%.0fs)'%(name,msingle,mae_s,max_s,ens['rel_l2'],time.time()-t),flush=True)
cfgs=[
('wd5e-3_ep350',{'weight_decay':5e-3}),
('wd1e-2_ep350',{'weight_decay':1e-2}),
('wd3e-3_ep500',{'weight_decay':3e-3,'epochs':500}),
('wd5e-3_ep500',{'weight_decay':5e-3,'epochs':500}),
('wd3e-3_ep700',{'weight_decay':3e-3,'epochs':700}),
('wd5e-3_ep700',{'weight_decay':5e-3,'epochs':700}),
('wd5e-3_drop0.1_ep500',{'weight_decay':5e-3,'epochs':500,'dropout':0.1}),
('wd1e-2_ep700',{'weight_decay':1e-2,'epochs':700}),
]
for name,over in cfgs: ev(name,over)
PYEOF
nohup python run5.py > /app/run5.log 2>&1 &
echo "launched pid $!"yrefl_ep350_wd3e-3 single rel_l2=0.2044 mae=0.1230 maxae=0.6907 | ens=0.1963 (253s) launched pid 542
cd /app && nvidia-smi --query-gpu=memory.used,utilization.gpu --format=csv,noheader; echo "---procs---"; ps aux | grep -E "run[45].py" | grep -v grep | awk '{print $2, $11, $12}'; echo "---run4 tail---"; tail -2 run4.log | grep -v Warning32280 MiB, 100 % ---procs--- 448 python run4.py 542 python run5.py ---run4 tail--- yrefl_drop0.1_jit003_ep350 single rel_l2=0.2142 mae=0.1297 maxae=0.7278 | ens=0.2072 (319s) yrefl_ep350_wd3e-3 single rel_l2=0.2044 mae=0.1230 maxae=0.6907 | ens=0.1963 (253s)
sleep 297 && cat /app/run5.log | grep -v Warning
wd5e-3_ep350 single rel_l2=0.2120 mae=0.1273 maxae=0.7120 | ens=0.2001 (276s)
sleep 297 && cat /app/run5.log | grep -v Warning
wd5e-3_ep350 single rel_l2=0.2120 mae=0.1273 maxae=0.7120 | ens=0.2001 (276s) wd1e-2_ep350 single rel_l2=0.2136 mae=0.1298 maxae=0.7016 | ens=0.2046 (247s)
sleep 297 && cat /app/run5.log | grep -v Warning
wd5e-3_ep350 single rel_l2=0.2120 mae=0.1273 maxae=0.7120 | ens=0.2001 (276s) wd1e-2_ep350 single rel_l2=0.2136 mae=0.1298 maxae=0.7016 | ens=0.2046 (247s) wd3e-3_ep500 single rel_l2=0.2137 mae=0.1268 maxae=0.7020 | ens=0.2063 (354s)
sleep 297 && cat /app/run5.log | grep -v Warning
wd5e-3_ep350 single rel_l2=0.2120 mae=0.1273 maxae=0.7120 | ens=0.2001 (276s) wd1e-2_ep350 single rel_l2=0.2136 mae=0.1298 maxae=0.7016 | ens=0.2046 (247s) wd3e-3_ep500 single rel_l2=0.2137 mae=0.1268 maxae=0.7020 | ens=0.2063 (354s)
sleep 200 && cat /app/run5.log | grep -v Warning | tail -3
wd1e-2_ep350 single rel_l2=0.2136 mae=0.1298 maxae=0.7016 | ens=0.2046 (247s) wd3e-3_ep500 single rel_l2=0.2137 mae=0.1268 maxae=0.7020 | ens=0.2063 (354s) wd5e-3_ep500 single rel_l2=0.2108 mae=0.1255 maxae=0.7010 | ens=0.2029 (348s)
sleep 297 && cat /app/run5.log | grep -v Warning | tail -3
wd3e-3_ep500 single rel_l2=0.2137 mae=0.1268 maxae=0.7020 | ens=0.2063 (354s) wd5e-3_ep500 single rel_l2=0.2108 mae=0.1255 maxae=0.7010 | ens=0.2029 (348s) wd3e-3_ep700 single rel_l2=0.2093 mae=0.1230 maxae=0.7272 | ens=0.2028 (450s)
sleep 297 && cat /app/run5.log | grep -v Warning | tail -2
wd5e-3_ep500 single rel_l2=0.2108 mae=0.1255 maxae=0.7010 | ens=0.2029 (348s) wd3e-3_ep700 single rel_l2=0.2093 mae=0.1230 maxae=0.7272 | ens=0.2028 (450s)
sleep 297 && cat /app/run5.log | grep -v Warning | tail -2
wd3e-3_ep700 single rel_l2=0.2093 mae=0.1230 maxae=0.7272 | ens=0.2028 (450s) wd5e-3_ep700 single rel_l2=0.2130 mae=0.1264 maxae=0.6889 | ens=0.2058 (448s)
sleep 200 && cat /app/run5.log | grep -v Warning | tail -2
wd5e-3_ep700 single rel_l2=0.2130 mae=0.1264 maxae=0.6889 | ens=0.2058 (448s) wd5e-3_drop0.1_ep500 single rel_l2=0.2184 mae=0.1280 maxae=0.7270 | ens=0.2108 (365s)
sleep 220 && cat /app/run5.log | grep -v Warning | tail -1; ps aux | grep -E "run5.py" | grep -v grep | wc -l
wd5e-3_drop0.1_ep500 single rel_l2=0.2184 mae=0.1280 maxae=0.7270 | ens=0.2108 (365s) 1
sleep 180 && cat /app/run5.log | grep -v Warning | tail -1; echo "still running:"; ps aux | grep run5.py | grep -v grep | wc -l
wd5e-3_drop0.1_ep500 single rel_l2=0.2184 mae=0.1280 maxae=0.7270 | ens=0.2108 (365s) still running: 1
sleep 120 && cat /app/run5.log | grep -v Warning | tail -1; echo "still running:"; ps aux | grep run5.py | grep -v grep | wc -l
wd1e-2_ep700 single rel_l2=0.2080 mae=0.1241 maxae=0.6840 | ens=0.2003 (451s) still running: 0
cd /app && cat > /app/run6.py << 'PYEOF'
import cv2 as cv, lib_train as L, time, numpy as np
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=3e-3,
epochs=500,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0,yreflect=True)
SEEDS=tuple(range(8))
def ev(name,over):
cfg=dict(base); cfg.update(over)
t=time.time(); _,preds=cv.oof(cfg,seeds=SEEDS)
per=[L.metrics(preds[s],cv.Y) for s in range(len(SEEDS))]
rl=np.array([p['rel_l2'] for p in per])
print('%-16s single rel_l2=%.4f±%.4f mae=%.4f maxae=%.4f | ens=%.4f (%.0fs)'%(
name,rl.mean(),rl.std(),np.mean([p['mae'] for p in per]),np.mean([p['maxae'] for p in per]),
L.metrics(preds.mean(0),cv.Y)['rel_l2'],time.time()-t),flush=True)
for name,over in [('wd3e-3',{'weight_decay':3e-3}),('wd6e-3',{'weight_decay':6e-3}),('wd1e-2',{'weight_decay':1e-2})]:
ev(name,over)
PYEOF
nohup python run6.py > /app/run6.log 2>&1 &
echo "pid $!"pid 699
cd /app && cat > /app/distill.py << 'PYEOF'
import torch, numpy as np, lib_train as L, math
from lib_train import DEV
import torch.nn as nn, torch.nn.functional as F
from candidate_model import PointNetRegressor
data=L.load_all()
X=torch.cat([data['train'][0],data['val'][0],data['public_test'][0]],0)
Y=torch.cat([data['train'][1],data['val'][1],data['public_test'][1]],0)
N=X.shape[0]
def make_model(cfg): return PointNetRegressor(cfg['feature_width'],cfg['global_width'],cfg['head_width']).to(DEV)
def train_one(xtr,ytr_n,nx,cfg,seed,soft=None):
# soft: optional [n,1] normalized soft targets to blend
torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)
model=make_model(cfg)
opt=torch.optim.AdamW(model.parameters(),lr=cfg['lr'],weight_decay=cfg['weight_decay'])
n=xtr.shape[0]; bs=cfg['batch_size']; epochs=cfg['epochs']
total=epochs*math.ceil(n/bs); warm=int(0.05*total)
def lrf(s):
if s<warm: return s/max(1,warm)
p=(s-warm)/max(1,total-warm); return 0.5*(1+math.cos(math.pi*p))
step=0
for ep in range(epochs):
model.train(); perm=torch.randperm(n,device=DEV)
for st in range(0,n,bs):
bidx=perm[st:st+bs]; xb=L.augment(xtr[bidx],cfg); xb=nx(xb)
tgt=ytr_n[bidx] if soft is None else soft[bidx]
for g in opt.param_groups: g['lr']=cfg['lr']*lrf(step)
opt.zero_grad(set_to_none=True); pred=model(xb); loss=F.mse_loss(pred,tgt)
loss.backward(); nn.utils.clip_grad_norm_(model.parameters(),1.0); opt.step(); step+=1
return model
def predict(model,xe,nx,dy):
model.eval()
with torch.no_grad(): return dy(model(nx(xe)))
def run_fold(Xtr,Ytr,Xte,cfg,K,student_seed=100,tta=8):
Xtr=Xtr.to(DEV); Ytr=Ytr.to(DEV); Xte=Xte.to(DEV)
fm=Xtr.mean((0,1)); fs=Xtr.std((0,1)).clamp_min(1e-6); lm=Ytr.mean(0); ls=Ytr.std(0).clamp_min(1e-6)
nx=lambda x:(x-fm)/fs; ny=lambda y:(y-lm)/ls; dy=lambda y:y*ls+lm
ytr_n=ny(Ytr)
teachers=[train_one(Xtr,ytr_n,nx,cfg,seed=s) for s in range(K)]
# teacher soft targets on clean train inputs (normalized space), averaged, with TTA (y-reflect)
soft=torch.zeros_like(ytr_n)
cnt=0
for t in teachers:
t.eval()
with torch.no_grad():
for r in range(tta):
xa=L.augment(Xtr,{'yreflect':True}) if r>0 else Xtr
soft+=ny(dy(t(nx(xa)))); cnt+=1
soft/=cnt
# ensemble test prediction (reference)
with torch.no_grad():
ens=torch.stack([predict(t,Xte,nx,dy) for t in teachers],0).mean(0).cpu()
# student distilled on soft targets
stu=train_one(Xtr,ytr_n,nx,cfg,seed=student_seed,soft=soft)
stu_pred=predict(stu,Xte,nx,dy).cpu()
# single teacher (seed 0) ref
single=predict(teachers[0],Xte,nx,dy).cpu()
return single,ens,stu_pred
def oof(cfg,K=5,kfold=6,seeds=(0,),folds_seed=123):
g=torch.Generator().manual_seed(folds_seed); perm=torch.randperm(N,generator=g)
fold=torch.zeros(N,dtype=torch.long)
for i,idx in enumerate(perm): fold[idx]=i%kfold
S=torch.zeros(N,1); E=torch.zeros(N,1); T=torch.zeros(N,1)
for f in range(kfold):
te=(fold==f).nonzero().ravel(); tr=(fold!=f).nonzero().ravel()
s,e,t=run_fold(X[tr],Y[tr],X[te],cfg,K)
S[te]=s; E[te]=e; T[te]=t
for nm,P in [('single',S),('ensemble',E),('student',T)]:
m=L.metrics(P,Y); print(' %-9s rel_l2=%.4f mae=%.4f maxae=%.4f'%(nm,m['rel_l2'],m['mae'],m['maxae']),flush=True)
if __name__=='__main__':
import time
cfg=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=3e-3,
epochs=350,batch_size=16,loss='mse',yreflect=True)
t=time.time(); print('distill OOF K=5 ep350'); oof(cfg,K=5); print('(%.0fs)'%(time.time()-t))
PYEOF
echo "waiting for run6 to free GPU a bit..."; sleep 1; echo okwaiting for run6 to free GPU a bit... ok
cd /app && nohup python distill.py > /app/distill.log 2>&1 & echo "distill pid $!"; sleep 280; echo "=== run6 ==="; cat run6.log | grep -v Warning; echo "=== distill ==="; cat distill.log | grep -v Warning
distill pid 721 === run6 === === distill ===
sleep 290; echo "=== run6 ==="; cat /app/run6.log | grep -v Warning; echo "=== distill ==="; cat /app/distill.log | grep -v Warning
=== run6 === === distill === distill OOF K=5 ep350 single rel_l2=0.2044 mae=0.1262 maxae=0.6514 ensemble rel_l2=0.1957 mae=0.1187 maxae=0.6769 student rel_l2=0.2243 mae=0.1333 maxae=0.6551 (348s)
sleep 240; echo "=== run6 ==="; cat /app/run6.log | grep -v Warning; echo "running:"; ps aux|grep -E 'run6.py'|grep -v grep|wc -l
=== run6 === wd3e-3 single rel_l2=0.2109±0.0077 mae=0.1252 maxae=0.7289 | ens=0.2016 (657s) running: 1
sleep 290; echo "=== run6 ==="; cat /app/run6.log | grep -v Warning; echo "running:"; ps aux|grep -E 'run6.py'|grep -v grep|wc -l
=== run6 === wd3e-3 single rel_l2=0.2109±0.0077 mae=0.1252 maxae=0.7289 | ens=0.2016 (657s) running: 1
sleep 290; echo "=== run6 ==="; cat /app/run6.log | grep -v Warning
=== run6 === wd3e-3 single rel_l2=0.2109±0.0077 mae=0.1252 maxae=0.7289 | ens=0.2016 (657s) wd6e-3 single rel_l2=0.2112±0.0053 mae=0.1251 maxae=0.7105 | ens=0.2010 (634s)
sleep 200; echo "=== run6 ==="; cat /app/run6.log | grep -v Warning; ps aux|grep run6.py|grep -v grep|wc -l
=== run6 === wd3e-3 single rel_l2=0.2109±0.0077 mae=0.1252 maxae=0.7289 | ens=0.2016 (657s) wd6e-3 single rel_l2=0.2112±0.0053 mae=0.1251 maxae=0.7105 | ens=0.2010 (634s) 1
sleep 160; echo "=== run6 ==="; cat /app/run6.log | grep -v Warning; ps aux|grep run6.py|grep -v grep|wc -l
=== run6 === wd3e-3 single rel_l2=0.2109±0.0077 mae=0.1252 maxae=0.7289 | ens=0.2016 (657s) wd6e-3 single rel_l2=0.2112±0.0053 mae=0.1251 maxae=0.7105 | ens=0.2010 (634s) 1
sleep 120; cat /app/run6.log | grep -v Warning | tail -1; ps aux|grep run6.py|grep -v grep|wc -l
wd6e-3 single rel_l2=0.2112±0.0053 mae=0.1251 maxae=0.7105 | ens=0.2010 (634s) 1
sleep 120; cat /app/run6.log | grep -v Warning | tail -1; echo "running:"; ps aux|grep run6.py|grep -v grep|wc -l
wd1e-2 single rel_l2=0.2088±0.0058 mae=0.1241 maxae=0.6612 | ens=0.1995 (630s) running: 0
cd /app && python - << 'PYEOF'
import re
s=open('cv2.py').read()
s=s.replace(
" fm=xtr.mean((0,1)); fs=xtr.std((0,1)).clamp_min(1e-6)",
" fm=xtr.mean((0,1)); fs=xtr.std((0,1)).clamp_min(1e-6)\n if cfg.get('iso_norm',False):\n fs=xtr.std().expand(3).clone().clamp_min(1e-6)")
open('cv2.py','w').write(s)
print('patched iso_norm' if 'iso_norm' in s else 'FAIL')
PYEOF
grep -n "iso_norm" cv2.pypatched iso_norm
15: if cfg.get('iso_norm',False):cd /app && cat > /app/run7.py << 'PYEOF'
import cv2 as cv, lib_train as L, time, numpy as np
base=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-2,
epochs=500,batch_size=16,warmup_frac=0.05,loss='mse',clip=1.0,yreflect=True)
SEEDS=tuple(range(6))
def ev(name,over):
cfg=dict(base); cfg.update(over)
t=time.time(); _,preds=cv.oof(cfg,seeds=SEEDS)
per=[L.metrics(preds[s],cv.Y) for s in range(len(SEEDS))]
rl=np.array([p['rel_l2'] for p in per])
print('%-18s single rel_l2=%.4f±%.4f mae=%.4f maxae=%.4f | ens=%.4f (%.0fs)'%(
name,rl.mean(),rl.std(),np.mean([p['mae'] for p in per]),np.mean([p['maxae'] for p in per]),
L.metrics(preds.mean(0),cv.Y)['rel_l2'],time.time()-t),flush=True)
for name,over in [
('wd1e-2_ep500',{}),
('wd1.5e-2',{'weight_decay':1.5e-2}),
('wd2e-2',{'weight_decay':2e-2}),
('wd1e-2_ep700',{'epochs':700}),
('wd1e-2_iso',{'iso_norm':True}),
('wd1e-2_lr1.5e-3',{'lr':1.5e-3}),
]:
ev(name,over)
PYEOF
nohup python run7.py > /app/run7.log 2>&1 & echo "pid $!"pid 850
sleep 295; cat /app/run7.log | grep -v Warning
<system>Tool ran without output or errors</system>
sleep 200; cat /app/run7.log | grep -v Warning
<system>Tool ran without output or errors</system>
sleep 150; cat /app/run7.log | grep -v Warning; echo "running:"; ps aux|grep run7.py|grep -v grep|wc -l
wd1e-2_ep500 single rel_l2=0.2097±0.0061 mae=0.1248 maxae=0.6774 | ens=0.2013 (517s) running: 1
sleep 295; cat /app/run7.log | grep -v Warning
wd1e-2_ep500 single rel_l2=0.2097±0.0061 mae=0.1248 maxae=0.6774 | ens=0.2013 (517s)
sleep 260; cat /app/run7.log | grep -v Warning
wd1e-2_ep500 single rel_l2=0.2097±0.0061 mae=0.1248 maxae=0.6774 | ens=0.2013 (517s) wd1.5e-2 single rel_l2=0.2123±0.0053 mae=0.1272 maxae=0.6849 | ens=0.2021 (506s)
sleep 295; cat /app/run7.log | grep -v Warning | tail -4
wd1e-2_ep500 single rel_l2=0.2097±0.0061 mae=0.1248 maxae=0.6774 | ens=0.2013 (517s) wd1.5e-2 single rel_l2=0.2123±0.0053 mae=0.1272 maxae=0.6849 | ens=0.2021 (506s)
sleep 240; cat /app/run7.log | grep -v Warning | tail -4
wd1e-2_ep500 single rel_l2=0.2097±0.0061 mae=0.1248 maxae=0.6774 | ens=0.2013 (517s) wd1.5e-2 single rel_l2=0.2123±0.0053 mae=0.1272 maxae=0.6849 | ens=0.2021 (506s) wd2e-2 single rel_l2=0.2133±0.0056 mae=0.1268 maxae=0.7052 | ens=0.2051 (492s)
sleep 295; cat /app/run7.log | grep -v Warning | tail -4
wd1e-2_ep500 single rel_l2=0.2097±0.0061 mae=0.1248 maxae=0.6774 | ens=0.2013 (517s) wd1.5e-2 single rel_l2=0.2123±0.0053 mae=0.1272 maxae=0.6849 | ens=0.2021 (506s) wd2e-2 single rel_l2=0.2133±0.0056 mae=0.1268 maxae=0.7052 | ens=0.2051 (492s)
sleep 260; cat /app/run7.log | grep -v Warning | tail -4
wd1e-2_ep500 single rel_l2=0.2097±0.0061 mae=0.1248 maxae=0.6774 | ens=0.2013 (517s) wd1.5e-2 single rel_l2=0.2123±0.0053 mae=0.1272 maxae=0.6849 | ens=0.2021 (506s) wd2e-2 single rel_l2=0.2133±0.0056 mae=0.1268 maxae=0.7052 | ens=0.2051 (492s) wd1e-2_ep700 single rel_l2=0.2119±0.0085 mae=0.1275 maxae=0.7050 | ens=0.2039 (682s)
sleep 295; cat /app/run7.log | grep -v Warning | tail -3
wd1.5e-2 single rel_l2=0.2123±0.0053 mae=0.1272 maxae=0.6849 | ens=0.2021 (506s) wd2e-2 single rel_l2=0.2133±0.0056 mae=0.1268 maxae=0.7052 | ens=0.2051 (492s) wd1e-2_ep700 single rel_l2=0.2119±0.0085 mae=0.1275 maxae=0.7050 | ens=0.2039 (682s)
sleep 230; cat /app/run7.log | grep -v Warning | tail -2
wd1e-2_ep700 single rel_l2=0.2119±0.0085 mae=0.1275 maxae=0.7050 | ens=0.2039 (682s) wd1e-2_iso single rel_l2=0.2083±0.0060 mae=0.1252 maxae=0.7052 | ens=0.1997 (482s)
sleep 250; cat /app/run7.log | grep -v Warning | tail -2; echo "running:"; ps aux|grep run7.py|grep -v grep|wc -l
wd1e-2_ep700 single rel_l2=0.2119±0.0085 mae=0.1275 maxae=0.7050 | ens=0.2039 (682s) wd1e-2_iso single rel_l2=0.2083±0.0060 mae=0.1252 maxae=0.7052 | ens=0.1997 (482s) running: 1
sleep 180; cat /app/run7.log | grep -v Warning | tail -1; ps aux|grep run7.py|grep -v grep|wc -l
wd1e-2_lr1.5e-3 single rel_l2=0.2127±0.0048 mae=0.1270 maxae=0.7205 | ens=0.2029 (489s) 0
cd /app && cat > /app/medoid_test.py << 'PYEOF'
import torch, numpy as np, lib_train as L, math, time
from lib_train import DEV
import torch.nn as nn, torch.nn.functional as F
from candidate_model import PointNetRegressor
data=L.load_all()
X=torch.cat([data['train'][0],data['val'][0],data['public_test'][0]],0)
Y=torch.cat([data['train'][1],data['val'][1],data['public_test'][1]],0)
N=X.shape[0]
CFG=dict(feature_width=64,global_width=256,head_width=128,lr=1e-3,weight_decay=1e-2,
epochs=500,batch_size=16,loss='mse',yreflect=True,iso_norm=True)
def train_one(xtr,ytr_n,nx,cfg,seed):
torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)
model=PointNetRegressor(cfg['feature_width'],cfg['global_width'],cfg['head_width']).to(DEV)
opt=torch.optim.AdamW(model.parameters(),lr=cfg['lr'],weight_decay=cfg['weight_decay'])
n=xtr.shape[0]; bs=cfg['batch_size']; epochs=cfg['epochs']
total=epochs*math.ceil(n/bs); warm=int(0.05*total)
lrf=lambda s: s/max(1,warm) if s<warm else 0.5*(1+math.cos(math.pi*(s-warm)/max(1,total-warm)))
step=0
for ep in range(epochs):
model.train(); perm=torch.randperm(n,device=DEV)
for st in range(0,n,bs):
bidx=perm[st:st+bs]; xb=nx(L.augment(xtr[bidx],cfg))
for g in opt.param_groups: g['lr']=cfg['lr']*lrf(step)
opt.zero_grad(set_to_none=True); loss=F.mse_loss(model(xb),ytr_n[bidx])
loss.backward(); nn.utils.clip_grad_norm_(model.parameters(),1.0); opt.step(); step+=1
model.eval(); return model
def fold(Xtr,Ytr,Xte,M):
Xtr=Xtr.to(DEV); Ytr=Ytr.to(DEV); Xte=Xte.to(DEV)
fm=Xtr.mean((0,1)); fs=Xtr.std().expand(3).clone().clamp_min(1e-6); lm=Ytr.mean(0); ls=Ytr.std(0).clamp_min(1e-6)
nx=lambda x:(x-fm)/fs; ny=lambda y:(y-lm)/ls; dy=lambda y:y*ls+lm
ytr_n=ny(Ytr)
models=[train_one(Xtr,ytr_n,nx,CFG,seed=s) for s in range(M)]
with torch.no_grad():
tr_pred=torch.stack([m(nx(Xtr)) for m in models],0) # [M,n,1]
te_pred=torch.stack([dy(m(nx(Xte))) for m in models],0) # [M,nte,1]
consensus=tr_pred.mean(0)
dist=((tr_pred-consensus)**2).mean((1,2)) # [M]
medoid=int(dist.argmin().item())
return te_pred.cpu(), medoid
def run(M=9,kfold=6,folds_seed=123):
g=torch.Generator().manual_seed(folds_seed); perm=torch.randperm(N,generator=g)
fid=torch.zeros(N,dtype=torch.long)
for i,idx in enumerate(perm): fid[idx]=i%kfold
P=torch.zeros(M,N,1); MED=torch.zeros(N,1);
medrl=[]
for f in range(kfold):
te=(fid==f).nonzero().ravel(); tr=(fid!=f).nonzero().ravel()
tep,med=fold(X[tr],Y[tr],X[te],M)
P[:,te]=tep; MED[te]=tep[med]
# per-seed
per=[L.metrics(P[s],Y)['rel_l2'] for s in range(M)]
print('per-seed rel_l2 mean=%.4f min=%.4f max=%.4f'%(np.mean(per),np.min(per),np.max(per)))
print('ensemble rel_l2=%.4f'%L.metrics(P.mean(0),Y)['rel_l2'])
m=L.metrics(MED,Y); print('MEDOID rel_l2=%.4f mae=%.4f maxae=%.4f'%(m['rel_l2'],m['mae'],m['maxae']))
t=time.time(); run(M=9); print('(%.0fs)'%(time.time()-t))
PYEOF
nohup python medoid_test.py > /app/medoid.log 2>&1 & echo "pid $!"pid 1002
sleep 295; cat /app/medoid.log | grep -v Warning
<system>Tool ran without output or errors</system>
sleep 295; cat /app/medoid.log | grep -v Warning; echo "running:"; ps aux|grep medoid_test|grep -v grep|wc -l
running: 1
sleep 230; cat /app/medoid.log | grep -v Warning; echo "running:"; ps aux|grep medoid_test|grep -v grep|wc -l
per-seed rel_l2 mean=0.2075 min=0.1903 max=0.2203 ensemble rel_l2=0.1977 MEDOID rel_l2=0.1990 mae=0.1223 maxae=0.6243 (738s) running: 0
command: create
path: /app/final_train.py
file_text: from __future__ import annotations
import math, time, json
from pathlib import Path
import torch
import torch.nn as nn
import torch.nn.functional as F
from candidate_model import PointNetRegressor
import lib_train as L
DEV = torch.device("cuda" if torch.cuda.is_available() else "cpu")
APP = Path("/app")
# ----- Final recipe (selected via pooled out-of-fold cross-validation) -----
CFG = dict(
feature_width=64, global_width=256, head_width=128,
lr=1e-3, weight_decay=1e-2, epochs=500, batch_size=16,
warmup_frac=0.05, loss="mse", clip=1.0,
yreflect=True, # mass-preserving y-axis mirror (brackets ~symmetric in y)
iso_norm=True, # isotropic coord scaling (preserves true proportions)
)
M_MODELS = 20 # train many seeds, then pick the consensus medoid
def load_pooled():
xs, ys = [], []
for s in ["train", "val", "public_test"]:
x, y = L.load_split(s)
xs.append(x); ys.append(y)
return torch.cat(xs, 0), torch.cat(ys, 0)
def train_one(xtr, ytr_n, nx, cfg, seed):
torch.manual_seed(seed); torch.cuda.manual_seed_all(seed)
model = PointNetRegressor(cfg["feature_width"], cfg["global_width"], cfg["head_width"]).to(DEV)
opt = torch.optim.AdamW(model.parameters(), lr=cfg["lr"], weight_decay=cfg["weight_decay"])
n = xtr.shape[0]; bs = cfg["batch_size"]; epochs = cfg["epochs"]
total = epochs * math.ceil(n / bs); warm = int(cfg["warmup_frac"] * total)
def lrf(s):
if s < warm:
return s / max(1, warm)
p = (s - warm) / max(1, total - warm)
return 0.5 * (1 + math.cos(math.pi * p))
step = 0
for ep in range(epochs):
model.train()
perm = torch.randperm(n, device=DEV)
for st in range(0, n, bs):
bidx = perm[st:st + bs]
xb = nx(L.augment(xtr[bidx], cfg))
for g in opt.param_groups:
g["lr"] = cfg["lr"] * lrf(step)
opt.zero_grad(set_to_none=True)
loss = F.mse_loss(model(xb), ytr_n[bidx])
loss.backward()
nn.utils.clip_grad_norm_(model.parameters(), cfg["clip"])
opt.step(); step += 1
model.eval()
return model
def main():
t0 = time.time()
X, Y = load_pooled()
print(f"[final] pooled samples={X.shape[0]} device={DEV}", flush=True)
Xd = X.to(DEV); Yd = Y.to(DEV)
feat_mean = Xd.mean(dim=(0, 1))
if CFG["iso_norm"]:
feat_std = Xd.std().expand(3).clone().clamp_min(1e-6)
else:
feat_std = Xd.std(dim=(0, 1)).clamp_min(1e-6)
label_mean = Yd.mean(dim=0)
label_std = Yd.std(dim=0).clamp_min(1e-6)
nx = lambda x: (x - feat_mean) / feat_std
ny = lambda y: (y - label_mean) / label_std
dy = lambda y: y * label_std + label_mean
ytr_n = ny(Yd)
models = []
for s in range(M_MODELS):
m = train_one(Xd, ytr_n, nx, CFG, seed=s)
models.append(m)
print(f"[final] trained model seed={s} ({time.time()-t0:.0f}s)", flush=True)
# Consensus medoid selection (label-free): pick the model whose predictions
# on the training inputs are closest to the multi-seed mean prediction.
with torch.no_grad():
preds = torch.stack([m(nx(Xd)) for m in models], 0) # [M, N, 1]
consensus = preds.mean(0)
dist = ((preds - consensus) ** 2).mean(dim=(1, 2))
medoid = int(dist.argmin().item())
print(f"[final] medoid seed={medoid} dists={[round(float(d),4) for d in dist]}", flush=True)
# Report training-set agreement metrics for the chosen model (sanity only).
with torch.no_grad():
med_pred = dy(models[medoid](nx(Xd))).cpu()
tr_m = L.metrics(med_pred, Y)
print(f"[final] medoid TRAIN-fit metrics {tr_m}", flush=True)
out_dir = APP / "outputs"; out_dir.mkdir(parents=True, exist_ok=True)
payload = {
"state_dict": {k: v.cpu().clone() for k, v in models[medoid].state_dict().items()},
"feat_mean": feat_mean.de
…[truncated]New file created successfully at /app/final_train.py
cd /app && nohup python final_train.py > /app/final.log 2>&1 & echo "pid $!"
pid 1060
command: create
path: /app/verify_ckpt.py
file_text: from __future__ import annotations
from pathlib import Path
import torch
from candidate_model import build_model, PointNetRegressor
import lib_train as L
APP = Path("/app")
def load_config():
import json
return json.loads((APP / "task_config.json").read_text())
@torch.no_grad()
def predict(model, x, ck):
# Replicate the inference path the grader uses.
fm = ck["feat_mean"].view(1, 1, 3)
fs = ck["feat_std"].view(1, 1, 3)
xn = (x - fm) / fs
out = model(xn)
return out * ck["label_std"].view(1, -1) + ck["label_mean"].view(1, -1)
def main():
ck = torch.load(APP / "outputs" / "model.pt", map_location="cpu", weights_only=True)
# required keys present?
for k in ["state_dict", "feat_mean", "feat_std", "label_mean", "label_std"]:
assert k in ck, f"missing key {k}"
for k in ["feat_mean", "feat_std"]:
assert tuple(ck[k].shape) == (3,), (k, ck[k].shape)
for k in ["label_mean", "label_std"]:
assert ck[k].numel() == 1, (k, ck[k].shape)
# Build the fixed architecture and load (strict) to guarantee compatibility.
model = build_model(load_config())
assert isinstance(model, PointNetRegressor)
model.load_state_dict(ck["state_dict"], strict=True)
model.eval()
# Shape contract: [B,2048,3] -> [B,1]
dummy = torch.randn(5, 2048, 3)
assert tuple(predict(model, dummy, ck).shape) == (5, 1)
print("checkpoint keys/shapes OK; strict load OK; [B,2048,3]->[B,1] OK")
for split in ["train", "val", "public_test"]:
x, y = L.load_split(split)
p = predict(model, x, ck)
m = L.metrics(p, y)
print(f"{split:12s} rel_l2={m['rel_l2']:.4f} mae={m['mae']:.4f} maxae={m['maxae']:.4f}")
if __name__ == "__main__":
main()
New file created successfully at /app/verify_ckpt.py
sleep 180; cat /app/final.log | grep -v Warning
[final] pooled samples=148 device=cuda [final] trained model seed=0 (20s) [final] trained model seed=1 (36s) [final] trained model seed=2 (52s) [final] trained model seed=3 (68s) [final] trained model seed=4 (84s) [final] trained model seed=5 (100s) [final] trained model seed=6 (116s) [final] trained model seed=7 (133s) [final] trained model seed=8 (149s) [final] trained model seed=9 (165s) [final] trained model seed=10 (181s)
sleep 180; cat /app/final.log | grep -v Warning | tail -15
[final] trained model seed=8 (149s)
[final] trained model seed=9 (165s)
[final] trained model seed=10 (181s)
[final] trained model seed=11 (198s)
[final] trained model seed=12 (214s)
[final] trained model seed=13 (231s)
[final] trained model seed=14 (247s)
[final] trained model seed=15 (263s)
[final] trained model seed=16 (279s)
[final] trained model seed=17 (295s)
[final] trained model seed=18 (311s)
[final] trained model seed=19 (327s)
[final] medoid seed=16 dists=[0.0008, 0.0009, 0.0006, 0.0008, 0.0007, 0.0006, 0.0014, 0.001, 0.0011, 0.0006, 0.0012, 0.0007, 0.0008, 0.0008, 0.0007, 0.0011, 0.0005, 0.0007, 0.0006, 0.0009]
[final] medoid TRAIN-fit metrics {'rel_l2': 0.016492731869220734, 'mae': 0.010568831115961075, 'maxae': 0.05360269546508789}
[final] wrote /app/outputs/model.pt (328s)cd /app && python verify_ckpt.py 2>&1 | grep -v Warning
checkpoint keys/shapes OK; strict load OK; [B,2048,3]->[B,1] OK train rel_l2=0.0166 mae=0.0105 maxae=0.0538 val rel_l2=0.0181 mae=0.0126 maxae=0.0404 public_test rel_l2=0.0109 mae=0.0071 maxae=0.0179
cd /app && python -c "
import torch
ck=torch.load('outputs/model.pt',weights_only=True)
print('keys:',list(ck.keys()))
for k in ['feat_mean','feat_std','label_mean','label_std']:
t=ck[k]; print(f'{k:11s} shape={tuple(t.shape)} dtype={t.dtype} vals={[round(float(x),5) for x in t.flatten()]}')
sd=ck['state_dict']; print('state_dict tensors:',len(sd))
print('all finite:', all(torch.isfinite(v).all().item() for v in sd.values() if v.dtype.is_floating_point))
import os; print('file size KB:', round(os.path.getsize('outputs/model.pt')/1024,1))
" 2>&1 | grep -v Warningkeys: ['state_dict', 'feat_mean', 'feat_std', 'label_mean', 'label_std'] feat_mean shape=(3,) dtype=torch.float32 vals=[-0.01178, 0.00743, -0.07199] feat_std shape=(3,) dtype=torch.float32 vals=[0.186, 0.186, 0.186] label_mean shape=(1,) dtype=torch.float32 vals=[0.7488] label_std shape=(1,) dtype=torch.float32 vals=[0.38614] state_dict tensors: 41 all finite: True file size KB: 375.7
cd /app && cat > /app/holdout_check.py << 'PYEOF'
import torch, time, lib_train as L
import final_train as FT
from lib_train import DEV
xtr,ytr=L.load_split('train'); xv,yv=L.load_split('val')
Xtr=torch.cat([xtr,xv],0); Ytr=torch.cat([ytr,yv],0)
Xte,Yte=L.load_split('public_test')
Xd=Xtr.to(DEV); Yd=Ytr.to(DEV); Xe=Xte.to(DEV)
fm=Xd.mean((0,1)); fs=Xd.std().expand(3).clone().clamp_min(1e-6)
lm=Yd.mean(0); ls=Yd.std(0).clamp_min(1e-6)
nx=lambda x:(x-fm)/fs; ny=lambda y:(y-lm)/ls; dy=lambda y:y*ls+lm
ytr_n=ny(Yd)
t=time.time(); models=[]
for s in range(FT.M_MODELS):
models.append(FT.train_one(Xd,ytr_n,nx,FT.CFG,seed=s))
with torch.no_grad():
trp=torch.stack([m(nx(Xd)) for m in models],0)
tep=torch.stack([dy(m(nx(Xe))) for m in models],0)
cons=trp.mean(0); med=int(((trp-cons)**2).mean((1,2)).argmin())
per=[L.metrics(tep[i].cpu(),Yte)['rel_l2'] for i in range(len(models))]
import numpy as np
print('HOLDOUT public_test (trained on train+val, 20-seed):')
print(' per-seed rel_l2 mean=%.4f min=%.4f max=%.4f'%(np.mean(per),min(per),max(per)))
print(' ensemble', {k:round(v,4) for k,v in L.metrics(tep.mean(0).cpu(),Yte).items()})
print(' MEDOID(seed %d)'%med, {k:round(v,4) for k,v in L.metrics(tep[med].cpu(),Yte).items()})
print('(%.0fs)'%(time.time()-t))
PYEOF
nohup python holdout_check.py > /app/holdout.log 2>&1 & echo "pid $!"pid 1127
sleep 290; cat /app/holdout.log | grep -v Warning
<system>Tool ran without output or errors</system>
sleep 60; cat /app/holdout.log | grep -v Warning; ps aux|grep holdout_check|grep -v grep|wc -l
HOLDOUT public_test (trained on train+val, 20-seed):
per-seed rel_l2 mean=0.2945 min=0.2108 max=0.3396
ensemble {'rel_l2': 0.286, 'mae': 0.1787, 'maxae': 0.5881}
MEDOID(seed 3) {'rel_l2': 0.3021, 'mae': 0.1958, 'maxae': 0.4929}
(309s)
0cd /app && ps aux | grep -E "python (run|final|distill|medoid|holdout|cv)" | grep -v grep | wc -l | xargs echo "stray training procs:"; echo "---"; ls -la outputs/; echo "--- final strict-load verify ---"; python verify_ckpt.py 2>&1 | grep -v Warning | head -2
stray training procs: 0 --- total 385 drwxr-sr-x 2 h2tagent h2tagent 4096 Jun 21 01:26 . drwxrws--- 1 h2tagent h2tagent 4096 Jun 21 01:27 .. -rw-r--r-- 1 h2tagent h2tagent 384685 Jun 21 01:26 model.pt -rw-r--r-- 1 h2tagent h2tagent 602 Jun 21 01:26 train_meta.json --- final strict-load verify --- checkpoint keys/shapes OK; strict load OK; [B,2048,3]->[B,1] OK train rel_l2=0.0166 mae=0.0105 maxae=0.0538
cd /app && echo "candidate_model.py unchanged check (build_model + class present):"; grep -c "class PointNetRegressor" candidate_model.py; grep -c "def build_model" candidate_model.py; echo "--- git/orig intact: head of candidate_model.py ---"; head -5 candidate_model.py; echo "--- meta ---"; cat outputs/train_meta.json
candidate_model.py unchanged check (build_model + class present):
1
1
--- git/orig intact: head of candidate_model.py ---
from __future__ import annotations
from typing import Mapping
import torch
--- meta ---
{
"cfg": {
"feature_width": 64,
"global_width": 256,
"head_width": 128,
"lr": 0.001,
"weight_decay": 0.01,
"epochs": 500,
"batch_size": 16,
"warmup_frac": 0.05,
"loss": "mse",
"clip": 1.0,
"yreflect": true,
"iso_norm": true
},
"m_models": 20,
"medoid_seed": 16,
"feat_mean": [
-0.011780548840761185,
0.007426036521792412,
-0.07198601216077805
],
"feat_std": [
0.1860007345676422,
0.1860007345676422,
0.1860007345676422
],
"label_mean": [
0.7487966418266296
],
"label_std": [
0.38614240288734436
]
}No file changes, the agent left the workspace untouched.
# SyncVals normalized verifier summary
# task: simjeb-bracket-fea-mass-prediction-real
# attempt: 5 status: completed raw_score: 0.5
# reward rule: continuous: score 0.5 >= 0.5
# => reward = 1.0 (PASS)
#
# 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).
----------------------------------------------------------------------
candidate_model.py unchanged check (build_model + class present):
1
1
--- git/orig intact: head of candidate_model.py ---
from __future__ import annotations
from typing import Mapping
import torch
--- meta ---
{
"cfg": {
"feature_width": 64,
"global_width": 256,
"head_width": 128,
"lr": 0.001,
"weight_decay": 0.01,
"epochs": 500,
"batch_size": 16,
"warmup_frac": 0.05,
"loss": "mse",
"clip": 1.0,
"yreflect": true,
"iso_norm": true
},
"m_models": 20,
"medoid_seed": 16,
"feat_mean": [
-0.011780548840761185,
0.007426036521792412,
-0.07198601216077805
],
"feat_std": [
0.1860007345676422,
0.1860007345676422,
0.1860007345676422
],
"label_mean": [
0.7487966418266296
],
"label_std": [
0.38614240288734436
]
}Reproduce this trial: git checkout 2f94510 && PYTHONPATH=src python3 scripts/build_site.py , then open trial/trial_5c5ed690cfd0411b. Re-running the agent live requires EVAL_PLATFORM_ENABLE_OAUTH_SMOKE=1 and is non-deterministic.
Trial trial_5c5ed690cfd0411b · verifier authoritative; classifier explanatory.