LexHo's picture
a3v21_s42: frozen weights (S-fast + recording FiLM), predict.py, submission.csv (official 0.26306)
1b0d082 verified
Raw History Blame Contribute Delete
4.12 kB
#!/usr/bin/env python3
"""Load the challenge splits into memory as (N, 200, 6) float32 windows.
Ported from sample_code/ts-tcmpio-unified-temporal-resnet.ipynb (cells 1-2, 11),
with the Kaggle-mounted paths replaced by the local data root. The whole train
split is 82k windows = 0.39 GB, so everything is held in RAM (and later on the
GPU) rather than streamed.
"""
from __future__ import annotations
import os
from pathlib import Path
import numpy as np
import pandas as pd
WINDOW_SIZE = 200
PLATFORM_TO_ID = {"car": 0, "dog": 1, "drone": 2, "human": 3}
PLATFORMS = tuple(PLATFORM_TO_ID)
# Kaggle mounts the competition at /kaggle/input/<slug>; set TARTANIMU_DATA there
DATA_ROOT = Path(os.environ.get("TARTANIMU_DATA", "data/tartan-imu-challenge-iros2026"))
CACHE_SCHEMA = 2 # 2: DenseWindows carries the gravity-direction stream
def _read(path: Path) -> pd.DataFrame:
return pd.read_csv(path).dropna(axis=1, how="all")
def load_index(data_root: Path = DATA_ROOT) -> dict:
idx = data_root / "index"
train = _read(idx / "train_windows.csv").merge(_read(idx / "train_targets.csv"), on="window_id")
val = _read(idx / "val_windows.csv").merge(_read(idx / "val_targets.csv"), on="window_id")
test = _read(idx / "test_windows.csv")
train["platform_id"] = train["platform"].map(PLATFORM_TO_ID)
val["platform_id"] = val["platform"].map(PLATFORM_TO_ID)
paths = {split: {p.stem: str(p) for p in (data_root / split).rglob("*.npz")}
for split in ("train", "val", "test")}
out = {}
for split, frame in (("train", train), ("val", val), ("test", test)):
frame = frame.reset_index(drop=True)
frame["traj_id"] = frame["traj_id"].astype(str).str.replace(".npz", "", regex=False)
frame["file_path"] = frame["traj_id"].map(paths[split])
if frame["file_path"].isna().any():
raise SystemExit(f"{split}: {frame['file_path'].isna().sum()} windows have no .npz")
out[split] = frame
return out
def cache_path(frame: pd.DataFrame, kind: str) -> Path | None:
"""Where to memoise a decoded split, or None when caching is off.
Keyed by the frame's own contents, so no call site has to name its split and
a mismatched cache cannot be picked up. Set TARTANIMU_CACHE to a writable,
node-local directory: on Kaggle the competition mounts over the network with
no page cache, and every run re-decoding 395 .npz files dominates the job.
"""
root = os.environ.get("TARTANIMU_CACHE")
if not root:
return None
Path(root).mkdir(parents=True, exist_ok=True)
# CACHE_SCHEMA must change whenever the cached arrays gain or lose a field,
# or a stale cache is silently reused with the wrong contents
key = (f"{kind}_v{CACHE_SCHEMA}_{len(frame)}"
f"_{frame['window_id'].iloc[0]}_{frame['window_id'].iloc[-1]}")
return Path(root) / f"{key}.npy"
def build_windows(frame: pd.DataFrame) -> np.ndarray:
"""(len(frame), 200, 6) float32, row i matching frame.iloc[i]."""
cache = cache_path(frame, "win")
if cache is not None and cache.exists():
return np.load(cache, mmap_mode="r")
X = np.empty((len(frame), WINDOW_SIZE, 6), dtype=np.float32)
for file_path, rows in frame.groupby("file_path", sort=False):
with np.load(file_path) as d:
imu = d["imu"]
starts = rows["win_idx"].to_numpy() * WINDOW_SIZE
X[rows.index.to_numpy()] = np.stack([imu[s:s + WINDOW_SIZE] for s in starts])
if cache is not None:
np.save(cache, X)
return X
def sample_weights(train: pd.DataFrame) -> np.ndarray:
"""Notebook's WeightedRandomSampler weights.
1 / (windows in this trajectory * trajectories in this platform) makes every
platform equally likely and every trajectory within a platform equally
likely -- which is exactly how the competition macro-averages.
"""
traj_windows = train.groupby("traj_id")["window_id"].transform("size")
plat_trajs = train.groupby("platform")["traj_id"].transform("nunique")
return (1.0 / (traj_windows * plat_trajs)).to_numpy(np.float64)