{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"},{"sourceId":12139340,"sourceType":"datasetVersion","datasetId":7645099},{"sourceId":12293285,"sourceType":"datasetVersion","datasetId":7748073},{"sourceId":12411879,"sourceType":"datasetVersion","datasetId":7827797},{"sourceId":242954653,"sourceType":"kernelVersion"},{"sourceId":246893721,"sourceType":"kernelVersion"}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"<h1 style=\"color: #6cb4e4;  text-align: center;  padding: 0.25em;  border-top: solid 2.5px #6cb4e4;  border-bottom: solid 2.5px #6cb4e4;  background: -webkit-repeating-linear-gradient(-45deg, #f0f8ff, #f0f8ff 3px,#e9f4ff 3px, #e9f4ff 7px);  background: repeating-linear-gradient(-45deg, #f0f8ff, #f0f8ff 3px,#e9f4ff 3px, #e9f4ff 7px);height:45px;\">\n<b>\nOnlyInfKernel\n</b></h1> ","metadata":{}},{"cell_type":"markdown","source":"### **ℹ️INFO**\n* First, we would like to thank our participants for sharing their excellent baselines.\n    * [Two‑Branch Human‑Activity‑Recognition Pipeline (IMU + Thermopile/TOF  + SE‑CNN + BiLSTM + Attentio](https://www.kaggle.com/code/vonmainstein/imu-tof)\n\n### **ℹ️IMU+THM/TOF Great Related works(Published order)**\n* Thanks for sharing your implementation of the new feature.\n    * LB.75 [CMI25 | IMU+THM/TOF |TF BiLSTM+GRU+Attention|LB.75](https://www.kaggle.com/code/hideyukizushi/cmi25-imu-thm-tof-tf-bilstm-gru-attention-lb-75)\n    * LB.76 [IMU Signal Processing Optimization](https://www.kaggle.com/code/rktqwe/lb-0-76-imu-thm-tof-tf-bilstm-gru-attention)\n    * LB.77 [Gravity Component Removal from Accelerometer Data](https://www.kaggle.com/code/rktqwe/lb-0-77-linear-accel-tf-bilstm-gru-attention)\n    * LB.78 [New Feature: Angular Velocity from Quaternion Derivatives](https://www.kaggle.com/code/nksusth/lb-0-78-quaternions-tf-bilstm-gru-attention)\n    * LB.80 [IMU × TOF/THM Fusion with Physics‑FE & MixUp|LB0.8](https://www.kaggle.com/code/pepushi/imu-tof-thm-fusion-with-physics-fe-mixup-lb0-8)\n\n---\n\n### **ℹ️[LB.80 2025/06/28]MyUpdate**\n* In previous notebooks that used \"IMU+TOF\" as features, the validation strategy was TTS, which raised concerns about its robustness.\nFor this reason, we will release an implementation that simply blends five models and a model with a high ValidationScore.\n\n```\npredictions = []\nfor model in models:\n    idx = int(model.predict(pad_input, verbose=0).argmax(1)[0])\n    predictions.append(idx)\n\nidx = max(set(predictions), key=predictions.count)\nreturn str(gesture_classes[idx])\n```\n\n* The validationScore for the local training  model included in this notebook is\n    * [ModelWeight](https://www.kaggle.com/datasets/hideyukizushi/20250627-cmi-b-102-b-105)\n\n```\n0.891134700273056\n0.8912659261884439\n0.8914825129445727\n0.8915471835009202\n0.8922128108549205\n```\n\n### **ℹ️[LB.82 2025/07/09]MyUpdate**\n* blend use SGKF split model(The model is attached in a notebook and is publicly available.)\n    * [ModelWeight](https://www.kaggle.com/datasets/hideyukizushi/cmi-d-111)\n\n```\n# OOF(CMI Metrics)\n0.8120966859982701\n```","metadata":{}},{"cell_type":"markdown","source":"<h1 style=\"color: #6cb4e4;  text-align: center;  padding: 0.25em;  border-top: solid 2.5px #6cb4e4;  border-bottom: solid 2.5px #6cb4e4;  background: -webkit-repeating-linear-gradient(-45deg, #f0f8ff, #f0f8ff 3px,#e9f4ff 3px, #e9f4ff 7px);  background: repeating-linear-gradient(-45deg, #f0f8ff, #f0f8ff 3px,#e9f4ff 3px, #e9f4ff 7px);height:45px;\">\n<b>\nModel Detail\n</b></h1> ","metadata":{}},{"cell_type":"code","source":"from PIL import Image\nim1 = Image.open('/kaggle/input/cmi25-imu-thmtof-tf-bilstm-gru-attentionlb-xx/CMI-Model.png')\ndef get_concat_h_multi_resize(im_list, resample=Image.BICUBIC):\n    min_height = min(im.height for im in im_list)\n    im_list_resize = [im.resize((int(im.width * min_height / im.height), min_height),resample=resample)\n                      for im in im_list]\n    total_width = sum(im.width for im in im_list_resize)\n    dst = Image.new('RGB', (total_width, min_height))\n    pos_x = 0\n    for im in im_list_resize:\n        dst.paste(im, (pos_x, 0))\n        pos_x += im.width\n    return dst\nget_concat_h_multi_resize([im1])","metadata":{"trusted":true,"_kg_hide-input":true,"execution":{"iopub.status.busy":"2025-07-08T16:26:59.404132Z","iopub.execute_input":"2025-07-08T16:26:59.404817Z","iopub.status.idle":"2025-07-08T16:27:02.987290Z","shell.execute_reply.started":"2025-07-08T16:26:59.404776Z","shell.execute_reply":"2025-07-08T16:27:02.986127Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<h1 style=\"color: #6cb4e4;  text-align: center;  padding: 0.25em;  border-top: solid 2.5px #6cb4e4;  border-bottom: solid 2.5px #6cb4e4;  background: -webkit-repeating-linear-gradient(-45deg, #f0f8ff, #f0f8ff 3px,#e9f4ff 3px, #e9f4ff 7px);  background: repeating-linear-gradient(-45deg, #f0f8ff, #f0f8ff 3px,#e9f4ff 3px, #e9f4ff 7px);height:45px;\">\n<b>\nInfPipeline\n</b></h1> ","metadata":{}},{"cell_type":"markdown","source":"# 》》》**Importing the necessary Libraries**","metadata":{}},{"cell_type":"code","source":"import os, json, joblib, numpy as np, pandas as pd\nfrom pathlib import Path\nimport warnings \nwarnings.filterwarnings(\"ignore\")\n\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nfrom sklearn.utils.class_weight import compute_class_weight\n\nfrom tensorflow.keras.utils import Sequence, to_categorical, pad_sequences\nfrom tensorflow.keras.models import Model, load_model\nfrom tensorflow.keras.layers import (\n    Input, Conv1D, BatchNormalization, Activation, add, MaxPooling1D, Dropout,\n    Bidirectional, LSTM, GlobalAveragePooling1D, Dense, Multiply, Reshape,\n    Lambda, Concatenate, GRU, GaussianNoise\n)\nfrom tensorflow.keras.regularizers import l2\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import EarlyStopping\nfrom tensorflow.keras import backend as K\nimport tensorflow as tf\nimport polars as pl\nfrom sklearn.model_selection import StratifiedGroupKFold\nfrom scipy.spatial.transform import Rotation as R\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:02.989016Z","iopub.execute_input":"2025-07-08T16:27:02.989388Z","iopub.status.idle":"2025-07-08T16:27:16.473224Z","shell.execute_reply.started":"2025-07-08T16:27:02.989357Z","shell.execute_reply":"2025-07-08T16:27:16.472651Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 》》》**Fix Seed**","metadata":{}},{"cell_type":"code","source":"import random\ndef seed_everything(seed):\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    random.seed(seed)\n    np.random.seed(seed)\n    tf.random.set_seed(seed)\n    tf.experimental.numpy.random.seed(seed)\n    os.environ['TF_CUDNN_DETERMINISTIC'] = '1'\n    os.environ['TF_DETERMINISTIC_OPS'] = '1'\nseed_everything(seed=42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:16.473870Z","iopub.execute_input":"2025-07-08T16:27:16.474275Z","iopub.status.idle":"2025-07-08T16:27:16.479283Z","shell.execute_reply.started":"2025-07-08T16:27:16.474258Z","shell.execute_reply":"2025-07-08T16:27:16.478122Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 》》》**Configuration**","metadata":{}},{"cell_type":"code","source":"# (Competition metric will only be imported when TRAINing)\nTRAIN = False                     # ← set to True when you want to train\nRAW_DIR = Path(\"/kaggle/input/cmi-detect-behavior-with-sensor-data\")\nPRETRAINED_DIR = Path(\"/kaggle/input/cmi-d-111\")\nEXPORT_DIR = Path(\"./\")                                    # artefacts will be saved here\nBATCH_SIZE = 64\nPAD_PERCENTILE = 95\nLR_INIT = 5e-4\nWD = 3e-3\nMIXUP_ALPHA = 0.4\nEPOCHS = 160\nPATIENCE = 40\n\n\nprint(\"▶ imports ready · tensorflow\", tf.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:16.481549Z","iopub.execute_input":"2025-07-08T16:27:16.481959Z","iopub.status.idle":"2025-07-08T16:27:16.514173Z","shell.execute_reply.started":"2025-07-08T16:27:16.481939Z","shell.execute_reply":"2025-07-08T16:27:16.513553Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 》》》**Utility Functions**","metadata":{}},{"cell_type":"code","source":"#Tensor Manipulations\ndef time_sum(x):\n    return K.sum(x, axis=1)\n\ndef squeeze_last_axis(x):\n    return tf.squeeze(x, axis=-1)\n\ndef expand_last_axis(x):\n    return tf.expand_dims(x, axis=-1)\n\ndef se_block(x, reduction=8):\n    ch = x.shape[-1]\n    se = GlobalAveragePooling1D()(x)\n    se = Dense(ch // reduction, activation='relu')(se)\n    se = Dense(ch, activation='sigmoid')(se)\n    se = Reshape((1, ch))(se)\n    return Multiply()([x, se])\n\n# Residual CNN Block with SE\ndef residual_se_cnn_block(x, filters, kernel_size, pool_size=2, drop=0.3, wd=1e-4):\n    shortcut = x\n    for _ in range(2):\n        x = Conv1D(filters, kernel_size, padding='same', use_bias=False,\n                   kernel_regularizer=l2(wd))(x)\n        x = BatchNormalization()(x)\n        x = Activation('relu')(x)\n    x = se_block(x)\n    if shortcut.shape[-1] != filters:\n        shortcut = Conv1D(filters, 1, padding='same', use_bias=False,\n                          kernel_regularizer=l2(wd))(shortcut)\n        shortcut = BatchNormalization()(shortcut)\n    x = add([x, shortcut])\n    x = Activation('relu')(x)\n    x = MaxPooling1D(pool_size)(x)\n    x = Dropout(drop)(x)\n    return x\n\ndef attention_layer(inputs):\n    score = Dense(1, activation='tanh')(inputs)\n    score = Lambda(squeeze_last_axis)(score)\n    weights = Activation('softmax')(score)\n    weights = Lambda(expand_last_axis)(weights)\n    context = Multiply()([inputs, weights])\n    context = Lambda(time_sum)(context)\n    return context","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:16.514896Z","iopub.execute_input":"2025-07-08T16:27:16.515142Z","iopub.status.idle":"2025-07-08T16:27:16.533548Z","shell.execute_reply.started":"2025-07-08T16:27:16.515125Z","shell.execute_reply":"2025-07-08T16:27:16.532958Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 》》》**Data Helpers**","metadata":{}},{"cell_type":"code","source":"# Normalizes and cleans the time series sequence. \n\ndef preprocess_sequence(df_seq: pd.DataFrame, feature_cols: list[str], scaler: StandardScaler):\n    mat = df_seq[feature_cols].ffill().bfill().fillna(0).values\n    return scaler.transform(mat).astype('float32')\n\n# MixUp the data argumentation in order to regularize the neural network. \n\nclass MixupGenerator(Sequence):\n    def __init__(self, X, y, batch_size, alpha=0.2):\n        self.X, self.y = X, y\n        self.batch = batch_size\n        self.alpha = alpha\n        self.indices = np.arange(len(X))\n    def __len__(self):\n        return int(np.ceil(len(self.X) / self.batch))\n    def __getitem__(self, i):\n        idx = self.indices[i*self.batch:(i+1)*self.batch]\n        Xb, yb = self.X[idx], self.y[idx]\n        lam = np.random.beta(self.alpha, self.alpha)\n        perm = np.random.permutation(len(Xb))\n        X_mix = lam * Xb + (1-lam) * Xb[perm]\n        y_mix = lam * yb + (1-lam) * yb[perm]\n        return X_mix, y_mix\n    def on_epoch_end(self):\n        np.random.shuffle(self.indices)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:16.534293Z","iopub.execute_input":"2025-07-08T16:27:16.534526Z","iopub.status.idle":"2025-07-08T16:27:16.549465Z","shell.execute_reply.started":"2025-07-08T16:27:16.534500Z","shell.execute_reply":"2025-07-08T16:27:16.548791Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def remove_gravity_from_acc(acc_data, rot_data):\n\n    if isinstance(acc_data, pd.DataFrame):\n        acc_values = acc_data[['acc_x', 'acc_y', 'acc_z']].values\n    else:\n        acc_values = acc_data\n\n    if isinstance(rot_data, pd.DataFrame):\n        quat_values = rot_data[['rot_x', 'rot_y', 'rot_z', 'rot_w']].values\n    else:\n        quat_values = rot_data\n\n    num_samples = acc_values.shape[0]\n    linear_accel = np.zeros_like(acc_values)\n    \n    gravity_world = np.array([0, 0, 9.81])\n\n    for i in range(num_samples):\n        if np.all(np.isnan(quat_values[i])) or np.all(np.isclose(quat_values[i], 0)):\n            linear_accel[i, :] = acc_values[i, :] \n            continue\n\n        try:\n            rotation = R.from_quat(quat_values[i])\n            gravity_sensor_frame = rotation.apply(gravity_world, inverse=True)\n            linear_accel[i, :] = acc_values[i, :] - gravity_sensor_frame\n        except ValueError:\n             linear_accel[i, :] = acc_values[i, :]\n             \n    return linear_accel\n\ndef calculate_angular_velocity_from_quat(rot_data, time_delta=1/200): # Assuming 200Hz sampling rate\n    if isinstance(rot_data, pd.DataFrame):\n        quat_values = rot_data[['rot_x', 'rot_y', 'rot_z', 'rot_w']].values\n    else:\n        quat_values = rot_data\n\n    num_samples = quat_values.shape[0]\n    angular_vel = np.zeros((num_samples, 3))\n\n    for i in range(num_samples - 1):\n        q_t = quat_values[i]\n        q_t_plus_dt = quat_values[i+1]\n\n        if np.all(np.isnan(q_t)) or np.all(np.isclose(q_t, 0)) or \\\n           np.all(np.isnan(q_t_plus_dt)) or np.all(np.isclose(q_t_plus_dt, 0)):\n            continue\n\n        try:\n            rot_t = R.from_quat(q_t)\n            rot_t_plus_dt = R.from_quat(q_t_plus_dt)\n\n            # Calculate the relative rotation\n            delta_rot = rot_t.inv() * rot_t_plus_dt\n            \n            # Convert delta rotation to angular velocity vector\n            # The rotation vector (Euler axis * angle) scaled by 1/dt\n            # is a good approximation for small delta_rot\n            angular_vel[i, :] = delta_rot.as_rotvec() / time_delta\n        except ValueError:\n            # If quaternion is invalid, angular velocity remains zero\n            pass\n            \n    return angular_vel","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:16.550171Z","iopub.execute_input":"2025-07-08T16:27:16.550376Z","iopub.status.idle":"2025-07-08T16:27:16.569025Z","shell.execute_reply.started":"2025-07-08T16:27:16.550361Z","shell.execute_reply":"2025-07-08T16:27:16.568313Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def calculate_angular_distance(rot_data):\n    if isinstance(rot_data, pd.DataFrame):\n        quat_values = rot_data[['rot_x', 'rot_y', 'rot_z', 'rot_w']].values\n    else:\n        quat_values = rot_data\n\n    num_samples = quat_values.shape[0]\n    angular_dist = np.zeros(num_samples)\n\n    for i in range(num_samples - 1):\n        q1 = quat_values[i]\n        q2 = quat_values[i+1]\n\n        if np.all(np.isnan(q1)) or np.all(np.isclose(q1, 0)) or \\\n           np.all(np.isnan(q2)) or np.all(np.isclose(q2, 0)):\n            angular_dist[i] = 0 # Или np.nan, в зависимости от желаемого поведения\n            continue\n        try:\n            # Преобразование кватернионов в объекты Rotation\n            r1 = R.from_quat(q1)\n            r2 = R.from_quat(q2)\n\n            # Вычисление углового расстояния: 2 * arccos(|real(p * q*)|)\n            # где p* - сопряженный кватернион q\n            # В scipy.spatial.transform.Rotation, r1.inv() * r2 дает относительное вращение.\n            # Угол этого относительного вращения - это и есть угловое расстояние.\n            relative_rotation = r1.inv() * r2\n            \n            # Угол rotation vector соответствует угловому расстоянию\n            # Норма rotation vector - это угол в радианах\n            angle = np.linalg.norm(relative_rotation.as_rotvec())\n            angular_dist[i] = angle\n        except ValueError:\n            angular_dist[i] = 0 # В случае недействительных кватернионов\n            pass\n            \n    return angular_dist","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:16.569728Z","iopub.execute_input":"2025-07-08T16:27:16.569957Z","iopub.status.idle":"2025-07-08T16:27:16.586146Z","shell.execute_reply.started":"2025-07-08T16:27:16.569938Z","shell.execute_reply":"2025-07-08T16:27:16.585456Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 》》》**Model Definition - Two Branch Architecture**","metadata":{}},{"cell_type":"code","source":"def build_two_branch_model(pad_len, imu_dim, tof_dim, n_classes, wd=1e-4):\n    inp = Input(shape=(pad_len, imu_dim+tof_dim))\n    imu = Lambda(lambda t: t[:, :, :imu_dim])(inp)\n    tof = Lambda(lambda t: t[:, :, imu_dim:])(inp)\n\n    # IMU deep branch\n    x1 = residual_se_cnn_block(imu, 64, 3, drop=0.1, wd=wd)\n    x1 = residual_se_cnn_block(x1, 128, 5, drop=0.1, wd=wd)\n\n    # TOF/Thermal lighter branch\n    x2 = Conv1D(64, 3, padding='same', use_bias=False, kernel_regularizer=l2(wd))(tof)\n    x2 = BatchNormalization()(x2); x2 = Activation('relu')(x2)\n    x2 = MaxPooling1D(2)(x2); x2 = Dropout(0.2)(x2)\n    x2 = Conv1D(128, 3, padding='same', use_bias=False, kernel_regularizer=l2(wd))(x2)\n    x2 = BatchNormalization()(x2); x2 = Activation('relu')(x2)\n    x2 = MaxPooling1D(2)(x2); x2 = Dropout(0.2)(x2)\n\n    merged = Concatenate()([x1, x2])\n\n    xa = Bidirectional(LSTM(128, return_sequences=True, kernel_regularizer=l2(wd)))(merged)\n    xb = Bidirectional(GRU(128, return_sequences=True, kernel_regularizer=l2(wd)))(merged)\n    xc = GaussianNoise(0.09)(merged)\n    xc = Dense(16, activation='elu')(xc)\n    \n    x = Concatenate()([xa, xb, xc])\n    x = Dropout(0.4)(x)\n    x = attention_layer(x)\n\n    for units, drop in [(256, 0.5), (128, 0.3)]:\n        x = Dense(units, use_bias=False, kernel_regularizer=l2(wd))(x)\n        x = BatchNormalization()(x); x = Activation('relu')(x)\n        x = Dropout(drop)(x)\n\n    out = Dense(n_classes, activation='softmax', kernel_regularizer=l2(wd))(x)\n    return Model(inp, out)\n\ntmp_model = build_two_branch_model(127,7,325,18)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:16.586889Z","iopub.execute_input":"2025-07-08T16:27:16.587077Z","iopub.status.idle":"2025-07-08T16:27:19.100024Z","shell.execute_reply.started":"2025-07-08T16:27:16.587063Z","shell.execute_reply":"2025-07-08T16:27:19.099238Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"<h1 style=\"color: #6cb4e4;  text-align: center;  padding: 0.25em;  border-top: solid 2.5px #6cb4e4;  border-bottom: solid 2.5px #6cb4e4;  background: -webkit-repeating-linear-gradient(-45deg, #f0f8ff, #f0f8ff 3px,#e9f4ff 3px, #e9f4ff 7px);  background: repeating-linear-gradient(-45deg, #f0f8ff, #f0f8ff 3px,#e9f4ff 3px, #e9f4ff 7px);height:45px;\">\n<b>\nBlending Model\n</b></h1> ","metadata":{}},{"cell_type":"markdown","source":"# 》》》**LoadBlendingModels**","metadata":{}},{"cell_type":"code","source":"print(\"▶ INFERENCE MODE – loading artefacts from\", PRETRAINED_DIR)\nfinal_feature_cols = np.load(PRETRAINED_DIR / \"feature_cols.npy\", allow_pickle=True).tolist()\npad_len        = int(np.load(PRETRAINED_DIR / \"sequence_maxlen.npy\"))\nscaler         = joblib.load(PRETRAINED_DIR / \"scaler.pkl\")\ngesture_classes = np.load(PRETRAINED_DIR / \"gesture_classes.npy\", allow_pickle=True)\n\n\ncustom_objs = {\n    'time_sum': time_sum, 'squeeze_last_axis': squeeze_last_axis, 'expand_last_axis': expand_last_axis,\n    'se_block': se_block, 'residual_se_cnn_block': residual_se_cnn_block, 'attention_layer': attention_layer,\n}\n\n# ----------------------------------------------------------------- #\n# Load any Models\n# * is 2 Train Model Load\n# ----------------------------------------------------------------- #\n\nmodels = []\nprint(f\"  Loading models for ensemble inference...\")\nfor fold in range(10):\n    MODEL_DIR = \"/kaggle/input/cmi-d-111\"\n    \n    model_path = f\"{MODEL_DIR}/D-111_{fold}.h5\"\n    print(\">>>LoadModel>>>\",model_path)\n    model = load_model(model_path, compile=False, custom_objects=custom_objs)\n    models.append(model)\nprint(\"-\"*50)\n\nfor fold in range(10):\n    MODEL_DIR = \"/kaggle/input/cmi-d-111\"\n    \n    model_path = f\"{MODEL_DIR}/v0629_{fold}.h5\"\n    print(\">>>LoadModel>>>\",model_path)\n    model = load_model(model_path, compile=False, custom_objects=custom_objs)\n    models.append(model)\nprint(\"-\"*50)\nprint(f\"[INFO]NumUseModels:{len(models)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:19.102056Z","iopub.execute_input":"2025-07-08T16:27:19.102280Z","iopub.status.idle":"2025-07-08T16:27:29.457321Z","shell.execute_reply.started":"2025-07-08T16:27:19.102258Z","shell.execute_reply":"2025-07-08T16:27:29.456677Z"},"scrolled":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 》》》**Predict**","metadata":{}},{"cell_type":"code","source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    df_seq = sequence.to_pandas()\n    linear_accel = remove_gravity_from_acc(df_seq, df_seq)\n    df_seq['linear_acc_x'], df_seq['linear_acc_y'], df_seq['linear_acc_z'] = linear_accel[:, 0], linear_accel[:, 1], linear_accel[:, 2]\n    df_seq['linear_acc_mag'] = np.sqrt(df_seq['linear_acc_x']**2 + df_seq['linear_acc_y']**2 + df_seq['linear_acc_z']**2)\n    df_seq['linear_acc_mag_jerk'] = df_seq['linear_acc_mag'].diff().fillna(0)\n    angular_vel = calculate_angular_velocity_from_quat(df_seq)\n    df_seq['angular_vel_x'], df_seq['angular_vel_y'], df_seq['angular_vel_z'] = angular_vel[:, 0], angular_vel[:, 1], angular_vel[:, 2]\n    df_seq['angular_distance'] = calculate_angular_distance(df_seq)\n    \n    for i in range(1, 6):\n        pixel_cols = [f\"tof_{i}_v{p}\" for p in range(64)]; tof_data = df_seq[pixel_cols].replace(-1, np.nan)\n        df_seq[f'tof_{i}_mean'], df_seq[f'tof_{i}_std'], df_seq[f'tof_{i}_min'], df_seq[f'tof_{i}_max'] = tof_data.mean(axis=1), tof_data.std(axis=1), tof_data.min(axis=1), tof_data.max(axis=1)\n        \n    mat_unscaled = df_seq[final_feature_cols].ffill().bfill().fillna(0).values.astype('float32')\n    mat_scaled = scaler.transform(mat_unscaled)\n    pad_input = pad_sequences([mat_scaled], maxlen=pad_len, padding='post', truncating='post', dtype='float32')\n    \n    all_preds = [model.predict(pad_input, verbose=0)[0] for model in models] # 主出力のみ取得\n    avg_pred = np.mean(all_preds, axis=0)\n    return str(gesture_classes[avg_pred.argmax()])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:29.458002Z","iopub.execute_input":"2025-07-08T16:27:29.458269Z","iopub.status.idle":"2025-07-08T16:27:29.465954Z","shell.execute_reply.started":"2025-07-08T16:27:29.458245Z","shell.execute_reply":"2025-07-08T16:27:29.465111Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 》》》**Submit Inference server**","metadata":{}},{"cell_type":"code","source":"import kaggle_evaluation.cmi_inference_server\ninference_server = kaggle_evaluation.cmi_inference_server.CMIInferenceServer(predict)\n\nif os.getenv('KAGGLE_IS_COMPETITION_RERUN'):\n    inference_server.serve()\nelse:\n    inference_server.run_local_gateway(\n        data_paths=(\n            '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv',\n            '/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv',\n        )\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-08T16:27:29.466592Z","iopub.execute_input":"2025-07-08T16:27:29.466830Z","iopub.status.idle":"2025-07-08T16:27:53.094881Z","shell.execute_reply.started":"2025-07-08T16:27:29.466806Z","shell.execute_reply":"2025-07-08T16:27:53.094265Z"},"_kg_hide-output":true},"outputs":[],"execution_count":null}]}