{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.13.15","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":53666}],"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:16:27.445647Z","iopub.execute_input":"2026-10-19T04:16:27.446116Z","iopub.status.idle":"2026-10-19T04:16:29.472167Z","shell.execute_reply.started":"2026-10-19T04:16:27.446094Z","shell.execute_reply":"2026-10-19T04:16:29.47114Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    print(dirname, \"->\", filenames[:6])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:16:45.142178Z","iopub.execute_input":"2026-10-19T04:16:45.142878Z","iopub.status.idle":"2026-10-19T04:16:45.149975Z","shell.execute_reply.started":"2026-10-19T04:16:45.14285Z","shell.execute_reply":"2026-10-19T04:16:45.149221Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os, time, random, gc, glob\nimport numpy as np, pandas as pd, scipy, sklearn, torch, torch.nn as nn\nfrom scipy.signal import find_peaks\nfrom scipy.ndimage import gaussian_filter1d\n\nprint(\"torch\", torch.__version__, \"| numpy\", np.__version__, \"| pandas\", pd.__version__,\n      \"| scipy\", scipy.__version__, \"| sklearn\", sklearn.__version__)\n\ndef seed_everything(s=42):\n    random.seed(s); os.environ[\"PYTHONHASHSEED\"] = str(s)\n    np.random.seed(s); torch.manual_seed(s); torch.cuda.manual_seed_all(s)\n    torch.backends.cudnn.deterministic = True\nseed_everything(42)\n\nDEV = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nhits = glob.glob(\"/kaggle/input/**/train_events.csv\", recursive=True)\nassert hits, \"Competition data not attached: use Add Input and accept the rules\"\nDATA = os.path.dirname(hits[0])\nprint(\"DATA =\", DATA, \"| device:\", DEV)\n\nDS, SIGMA, WIN, PEAK_DIST = 12, 12, 2880, 24\nEVENTS = [\"onset\", \"wakeup\"]\n\nSUBMIT_CFG = \"BiLSTM_large\"   # v1: \"baseline_LSTM\" | v2: \"BiLSTM\" | v3: \"BiLSTM_large\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:16:49.36334Z","iopub.execute_input":"2026-10-19T04:16:49.363955Z","iopub.status.idle":"2026-10-19T04:16:57.106786Z","shell.execute_reply.started":"2026-10-19T04:16:49.363927Z","shell.execute_reply":"2026-10-19T04:16:57.106148Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"events = pd.read_csv(f\"{DATA}/train_events.csv\").dropna(subset=[\"step\"])\nevents[\"step\"] = events[\"step\"].astype(int)\nprint(events.head()); print(events.event.value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:17:01.516378Z","iopub.execute_input":"2026-10-19T04:17:01.517173Z","iopub.status.idle":"2026-10-19T04:17:01.59452Z","shell.execute_reply.started":"2026-10-19T04:17:01.517142Z","shell.execute_reply":"2026-10-19T04:17:01.59386Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_parquet(f\"{DATA}/train_series.parquet\",\n                           columns=[\"series_id\", \"timestamp\", \"anglez\", \"enmo\"])\nprint(train_df.shape, \"| series:\", train_df.series_id.nunique())\nprint(train_df[[\"anglez\", \"enmo\"]].describe())\nprint(\"Events per series:\\n\", events.groupby(\"series_id\").size().describe())\nimport matplotlib.pyplot as plt\nsid0 = events.series_id.iloc[0]\ng0 = train_df[train_df.series_id == sid0].iloc[:17280]\nfig, ax = plt.subplots(2, 1, figsize=(14, 5), sharex=True)\nax[0].plot(g0.anglez.values); ax[0].set_ylabel(\"anglez\")\nax[1].plot(g0.enmo.values);   ax[1].set_ylabel(\"enmo\")\nfor _, r in events[(events.series_id == sid0) & (events.step < 17280)].iterrows():\n    for a in ax: a.axvline(r.step, color=\"g\" if r.event == \"onset\" else \"r\")\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:17:04.908117Z","iopub.execute_input":"2026-10-19T04:17:04.908796Z","iopub.status.idle":"2026-10-19T04:18:09.148218Z","shell.execute_reply.started":"2026-10-19T04:17:04.908755Z","shell.execute_reply":"2026-10-19T04:18:09.147572Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ev_raw = pd.read_csv(f\"{DATA}/train_events.csv\")\nprint(\"event rows:\", len(ev_raw), \"| missing step:\", ev_raw.step.isna().sum(),\n      \"| duplicate event rows:\", ev_raw.duplicated().sum())\n\ntst = pd.read_parquet(f\"{DATA}/test_series.parquet\")\nprint(\"test rows:\", len(tst), \"| test series:\", tst.series_id.nunique())\n\nts = pd.read_parquet(f\"{DATA}/train_series.parquet\", columns=[\"series_id\", \"step\"])\nkey = ts.series_id.astype(\"category\").cat.codes.astype(\"int64\") * 10**7 + ts.step.astype(\"int64\")\nprint(\"duplicate (series_id, step) rows in train:\", int(key.duplicated().sum()))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:18:24.315953Z","iopub.execute_input":"2026-10-19T04:18:24.316234Z","iopub.status.idle":"2026-10-19T04:18:57.376665Z","shell.execute_reply.started":"2026-10-19T04:18:24.316213Z","shell.execute_reply":"2026-10-19T04:18:57.375988Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_features(anglez, enmo, start_min):\n    n = len(anglez); k = int(np.ceil(n / DS)); pad = k * DS - n\n    if pad:\n        anglez = np.pad(anglez, (0, pad), mode=\"edge\"); enmo = np.pad(enmo, (0, pad), mode=\"edge\")\n    adiff = np.abs(np.diff(anglez, prepend=anglez[0]))\n    a, e, d = anglez.reshape(k, DS), enmo.reshape(k, DS), adiff.reshape(k, DS)\n    tod = (start_min + np.arange(k)) % 1440\n    ang = 2 * np.pi * tod / 1440\n    f = np.stack([a.mean(1) / 45, a.std(1) / 45,\n                  np.log1p(d.mean(1)),\n                  np.log1p(e.mean(1) * 100), np.log1p(e.max(1) * 100),\n                  np.sin(ang), np.cos(ang)], axis=1)\n    return f.astype(np.float32)\n\ndef start_minute(ts):\n    return int(ts[11:13]) * 60 + int(ts[14:16])\n\ndef build_targets(k, ev):\n    y = np.zeros((k, 2), np.float32)\n    for c, name in enumerate(EVENTS):\n        imp = np.zeros(k, np.float32)\n        for s in ev[ev.event == name].step.values:\n            imp[min(s // DS, k - 1)] = 1\n        y[:, c] = np.clip(gaussian_filter1d(imp, SIGMA) * SIGMA * np.sqrt(2 * np.pi), 0, 1)\n    return y","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:19:01.169403Z","iopub.execute_input":"2026-10-19T04:19:01.169839Z","iopub.status.idle":"2026-10-19T04:19:01.178848Z","shell.execute_reply.started":"2026-10-19T04:19:01.169814Z","shell.execute_reply":"2026-10-19T04:19:01.178Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ev_groups = {s: g for s, g in events.groupby(\"series_id\")}\n\nif \"train_df\" not in globals():                       # NEW: agar Cell 3 nahi chala to khud load karo\n    train_df = pd.read_parquet(f\"{DATA}/train_series.parquet\",\n                               columns=[\"series_id\", \"timestamp\", \"anglez\", \"enmo\"])\n\nX, Y = {}, {}\nfor sid, g in train_df.groupby(\"series_id\", sort=False):\n    if sid not in ev_groups: continue\n    f = build_features(g.anglez.values.astype(np.float32), g.enmo.values.astype(np.float32),\n                       start_minute(g.timestamp.iloc[0]))\n    X[sid] = f; Y[sid] = build_targets(len(f), ev_groups[sid])\ndel train_df; gc.collect()\n\nids = sorted(X.keys()); rng = np.random.RandomState(42); rng.shuffle(ids)\nn_val = int(0.2 * len(ids)); val_ids, train_ids = ids[:n_val], ids[n_val:]\nval_gt = events[events.series_id.isin(val_ids)][[\"series_id\", \"event\", \"step\"]]\nNF = X[ids[0]].shape[1]\nprint(len(train_ids), \"train series |\", len(val_ids), \"val series |\", NF, \"features\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:19:05.492236Z","iopub.execute_input":"2026-10-19T04:19:05.493067Z","iopub.status.idle":"2026-10-19T04:19:27.585625Z","shell.execute_reply.started":"2026-10-19T04:19:05.493037Z","shell.execute_reply":"2026-10-19T04:19:27.585009Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_batch(sids, bs):\n    xb = np.zeros((bs, WIN, NF), np.float32); yb = np.zeros((bs, WIN, 2), np.float32)\n    for i in range(bs):\n        s = random.choice(sids); T = len(X[s]); st = random.randint(0, max(0, T - WIN))\n        xs, ys = X[s][st:st + WIN], Y[s][st:st + WIN]\n        xb[i, :len(xs)] = xs; yb[i, :len(ys)] = ys\n    xb += np.random.normal(0, 0.02, xb.shape).astype(np.float32)\n    return torch.from_numpy(xb).to(DEV), torch.from_numpy(yb).to(DEV)\n\nclass SleepNet(nn.Module):\n    def __init__(s, nin, hid=64, layers=2, bidir=True, drop=0.2):\n        super().__init__()\n        s.conv = nn.Sequential(nn.Conv1d(nin, hid, 5, padding=2), nn.GELU())\n        s.lstm = nn.LSTM(hid, hid, layers, batch_first=True, bidirectional=bidir,\n                         dropout=drop if layers > 1 else 0)\n        s.head = nn.Sequential(nn.Dropout(drop), nn.Linear(hid * (2 if bidir else 1), 2))\n    def forward(s, x):\n        h = s.conv(x.transpose(1, 2)).transpose(1, 2).contiguous()\n        h, _ = s.lstm(h)\n        return s.head(h)\n\n@torch.no_grad()\ndef predict_probs(model, x, bs=16):\n    model.eval()\n    T = len(x)\n    if T < WIN:\n        xp = np.zeros((WIN, x.shape[1]), np.float32); xp[:T] = x; x = xp\n    Tn = len(x); stride = WIN // 2\n    starts = list(range(0, Tn - WIN + 1, stride))\n    if starts[-1] + WIN < Tn: starts.append(Tn - WIN)\n    w = (np.hanning(WIN) + 1e-3).astype(np.float32)[:, None]\n    acc = np.zeros((Tn, 2), np.float32); cnt = np.zeros((Tn, 1), np.float32)\n    for i in range(0, len(starts), bs):\n        sb = starts[i:i + bs]\n        xb = torch.from_numpy(np.stack([x[s:s + WIN] for s in sb])).to(DEV)\n        out = torch.sigmoid(model(xb)).cpu().numpy()\n        for s, o in zip(sb, out):\n            acc[s:s + WIN] += o * w; cnt[s:s + WIN] += w\n    return (acc / cnt)[:T]\n\ndef extract_events(sid, prob, n_steps=None):\n    rows = []\n    for c, name in enumerate(EVENTS):\n        p = gaussian_filter1d(prob[:, c], 2)\n        pk, _ = find_peaks(p, height=0.005, distance=PEAK_DIST)\n        for i in pk:\n            st = i * DS + DS // 2\n            if n_steps: st = min(st, n_steps - 1)\n            rows.append((sid, name, int(st), float(p[i])))\n    return rows\n\nTOLS = [12, 36, 60, 90, 120, 150, 180, 240, 300, 360, 480, 720]\ndef event_ap(gt, pred):\n    aps = []\n    for ev in EVENTS:\n        g, p = gt[gt.event == ev], pred[pred.event == ev]\n        n_gt = len(g); gts = {s: d.step.values for s, d in g.groupby(\"series_id\")}\n        for tol in TOLS:\n            scores, hits = [], []\n            for s, d in p.groupby(\"series_id\"):\n                d = d.sort_values(\"score\", ascending=False)\n                gv = gts.get(s, np.array([])); used = np.zeros(len(gv), bool)\n                for st, sc in zip(d.step.values, d.score.values):\n                    ok = False\n                    if len(gv):\n                        dist = np.abs(gv - st).astype(float); dist[used] = np.inf\n                        j = dist.argmin()\n                        if dist[j] < tol: used[j] = True; ok = True\n                    scores.append(sc); hits.append(ok)\n            if not scores: aps.append(0.0); continue\n            h = np.array(hits)[np.argsort(-np.array(scores), kind=\"stable\")]\n            tp = np.cumsum(h); prec = tp / np.arange(1, len(h) + 1); rec = tp / n_gt\n            aps.append(float(np.sum(np.diff(rec, prepend=0) * prec)))\n    return float(np.mean(aps))\n\ndef evaluate(model):\n    rows = []\n    for sid in val_ids:\n        rows += extract_events(sid, predict_probs(model, X[sid]))\n    pred = pd.DataFrame(rows, columns=[\"series_id\", \"event\", \"step\", \"score\"])\n    return event_ap(val_gt, pred)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:19:31.482444Z","iopub.execute_input":"2026-10-19T04:19:31.483089Z","iopub.status.idle":"2026-10-19T04:19:31.501659Z","shell.execute_reply.started":"2026-10-19T04:19:31.48306Z","shell.execute_reply":"2026-10-19T04:19:31.500903Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"CONFIGS = {\n  \"baseline_LSTM\":  dict(hid=32,  layers=1, bidir=False, drop=0.1, lr=2e-3, epochs=15, bs=32, spe=100),\n  \"BiLSTM\":         dict(hid=64,  layers=2, bidir=True,  drop=0.2, lr=2e-3, epochs=25, bs=32, spe=100),\n  \"BiLSTM_large\":   dict(hid=128, layers=2, bidir=True,  drop=0.3, lr=1e-3, epochs=30, bs=32, spe=100),\n}\nassert SUBMIT_CFG in CONFIGS, \"SUBMIT_CFG galat likha hai\"\n\ndef train_model(name, cfg):\n    seed_everything(42)\n    model = SleepNet(NF, cfg[\"hid\"], cfg[\"layers\"], cfg[\"bidir\"], cfg[\"drop\"]).to(DEV)\n    print(\"Trainable parameters:\", sum(p.numel() for p in model.parameters() if p.requires_grad))\n    opt = torch.optim.AdamW(model.parameters(), lr=cfg[\"lr\"], weight_decay=1e-2)\n    sched = torch.optim.lr_scheduler.OneCycleLR(opt, max_lr=cfg[\"lr\"],\n                                                total_steps=cfg[\"epochs\"] * cfg[\"spe\"])\n    loss_fn = nn.BCEWithLogitsLoss(); best_ap, best_state = -1, None\n    hist = {\"loss\": [], \"val_ap\": []}\n    for ep in range(cfg[\"epochs\"]):\n        model.train(); tot = 0\n        for _ in range(cfg[\"spe\"]):\n            xb, yb = get_batch(train_ids, cfg[\"bs\"])\n            loss = loss_fn(model(xb), yb)\n            opt.zero_grad(); loss.backward()\n            nn.utils.clip_grad_norm_(model.parameters(), 1.0)\n            opt.step(); sched.step(); tot += loss.item()\n        ap = evaluate(model)\n        hist[\"loss\"].append(tot / cfg[\"spe\"]); hist[\"val_ap\"].append(ap)\n        if ap > best_ap:\n            best_ap = ap; best_state = {k: v.clone() for k, v in model.state_dict().items()}\n        print(f\"[{name}] epoch {ep+1:02d} loss {tot/cfg['spe']:.4f} val_AP {ap:.4f}\")\n    model.load_state_dict(best_state)\n    return model, best_ap, hist\n\ncfg = CONFIGS[SUBMIT_CFG]\nt0 = time.time()\nmodel, best_ap, hist = train_model(SUBMIT_CFG, cfg)\ntrain_min = (time.time() - t0) / 60\nprint(f\"\\nMODEL: {SUBMIT_CFG} | best val_AP: {best_ap:.4f} | TRAINING TIME: {train_min:.1f} min (limit: 120)\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:19:37.384358Z","iopub.execute_input":"2026-10-19T04:19:37.385047Z","iopub.status.idle":"2026-10-19T04:32:53.97961Z","shell.execute_reply.started":"2026-10-19T04:19:37.385018Z","shell.execute_reply":"2026-10-19T04:32:53.978758Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nfig, ax = plt.subplots(1, 2, figsize=(11, 3.5))\nax[0].plot(hist[\"loss\"]);   ax[0].set_title(f\"{SUBMIT_CFG}: training loss\"); ax[0].set_xlabel(\"epoch\")\nax[1].plot(hist[\"val_ap\"]); ax[1].set_title(f\"{SUBMIT_CFG}: validation AP\"); ax[1].set_xlabel(\"epoch\")\nplt.tight_layout(); plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:34:07.961525Z","iopub.execute_input":"2026-10-19T04:34:07.962184Z","iopub.status.idle":"2026-10-19T04:34:08.202115Z","shell.execute_reply.started":"2026-10-19T04:34:07.962156Z","shell.execute_reply":"2026-10-19T04:34:08.20158Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Submitting:\", SUBMIT_CFG)\n\ntest_df = pd.read_parquet(f\"{DATA}/test_series.parquet\")\nrows = []\nfor sid, g in test_df.groupby(\"series_id\", sort=False):\n    f = build_features(g.anglez.values.astype(np.float32), g.enmo.values.astype(np.float32),\n                       start_minute(g.timestamp.iloc[0]))\n    rows += extract_events(sid, predict_probs(model, f), n_steps=len(g))   # NEW: model use hota hai\n\nsub = pd.DataFrame(rows, columns=[\"series_id\", \"event\", \"step\", \"score\"])\nif sub.empty:\n    sub = pd.DataFrame([(test_df.series_id.iloc[0], \"onset\", 0, 0.0)], columns=sub.columns)\nsub.insert(0, \"row_id\", range(len(sub)))\nsub.to_csv(\"submission.csv\", index=False)\nprint(sub.shape); display(sub.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:37:26.532173Z","iopub.execute_input":"2026-10-19T04:37:26.532576Z","iopub.status.idle":"2026-10-19T04:37:28.578331Z","shell.execute_reply.started":"2026-10-19T04:37:26.532548Z","shell.execute_reply":"2026-10-19T04:37:28.57766Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nsid = val_ids[0]\nprob = predict_probs(model, X[sid])\nfig, ax = plt.subplots(2, 1, figsize=(14, 5), sharex=True)\nfor c, name in enumerate(EVENTS):\n    ax[c].plot(prob[:, c], label=\"predicted\")\n    ax[c].plot(Y[sid][:, c], label=\"true (Gaussian)\", alpha=.6)\n    ax[c].set_title(f\"{name} - {sid}\"); ax[c].legend()\nplt.tight_layout(); plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:37:32.150961Z","iopub.execute_input":"2026-10-19T04:37:32.151358Z","iopub.status.idle":"2026-10-19T04:37:32.961223Z","shell.execute_reply.started":"2026-10-19T04:37:32.151334Z","shell.execute_reply":"2026-10-19T04:37:32.960558Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results_df = pd.DataFrame([\n  dict(model=\"M0 baseline_LSTM\", architecture=\"LSTM x1, hid=32\",   hyperparams=\"lr=2e-3, drop=0.1, epochs=15, bs=32\", val_AP=0.3888, kaggle_private_score=0.323),\n  dict(model=\"M1 BiLSTM\",        architecture=\"BiLSTM x2, hid=64\",  hyperparams=\"lr=2e-3, drop=0.2, epochs=25, bs=32\", val_AP=None, kaggle_private_score=None),\n  dict(model=\"M2 BiLSTM_large\",  architecture=\"BiLSTM x2, hid=128\", hyperparams=\"lr=1e-3, drop=0.3, epochs=30, bs=32\", val_AP=round(best_ap, 4), kaggle_private_score=None),\n])\ndisplay(results_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-19T04:37:40.632211Z","iopub.execute_input":"2026-10-19T04:37:40.633001Z","iopub.status.idle":"2026-10-19T04:37:40.643187Z","shell.execute_reply.started":"2026-10-19T04:37:40.632975Z","shell.execute_reply":"2026-10-19T04:37:40.64245Z"}},"outputs":[],"execution_count":null}]}