{"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":"none","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"},{"sourceId":244221416,"sourceType":"kernelVersion"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"🛠 Traininig Notebook: [OIKAN ~ Interpretable ML for CMI 2025 - Training](https://www.kaggle.com/code/armanzhalgasbayev/oikan-interpretable-ml-for-cmi-2025-training)","metadata":{}},{"cell_type":"code","source":"# === Libraries ===\nimport os\nimport json\nimport numpy as np\nimport pandas as pd\nimport polars as pl\nimport joblib\nimport matplotlib.pyplot as plt\nimport kaggle_evaluation.cmi_inference_server","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:36:15.931406Z","iopub.execute_input":"2025-06-07T12:36:15.931707Z","iopub.status.idle":"2025-06-07T12:36:19.361653Z","shell.execute_reply.started":"2025-06-07T12:36:15.931681Z","shell.execute_reply":"2025-06-07T12:36:19.360663Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:37:22.171436Z","iopub.execute_input":"2025-06-07T12:37:22.171859Z","iopub.status.idle":"2025-06-07T12:37:22.176782Z","shell.execute_reply.started":"2025-06-07T12:37:22.171838Z","shell.execute_reply":"2025-06-07T12:37:22.175852Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Load selected features and model artifacts ===\n\nwith open('/kaggle/input/oikan-interpretable-ml-for-cmi-2025-training/model_artifacts/used_features.txt') as f:\n    text = f.read()\n    selected_feature_names = text.split()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:37:23.026371Z","iopub.execute_input":"2025-06-07T12:37:23.027152Z","iopub.status.idle":"2025-06-07T12:37:23.037074Z","shell.execute_reply.started":"2025-06-07T12:37:23.027111Z","shell.execute_reply":"2025-06-07T12:37:23.035901Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(selected_feature_names[:5])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:37:23.760362Z","iopub.execute_input":"2025-06-07T12:37:23.760853Z","iopub.status.idle":"2025-06-07T12:37:23.766311Z","shell.execute_reply.started":"2025-06-07T12:37:23.760826Z","shell.execute_reply":"2025-06-07T12:37:23.765530Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with open('/kaggle/input/oikan-interpretable-ml-for-cmi-2025-training/model_artifacts/oikan_model.json') as f:\n    oikan_model = json.load(f)\n\nprint(oikan_model['basis_functions'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:37:24.604732Z","iopub.execute_input":"2025-06-07T12:37:24.605033Z","iopub.status.idle":"2025-06-07T12:37:24.613322Z","shell.execute_reply.started":"2025-06-07T12:37:24.605013Z","shell.execute_reply":"2025-06-07T12:37:24.612352Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"le = joblib.load('/kaggle/input/oikan-interpretable-ml-for-cmi-2025-training/model_artifacts/label_encoder.pkl')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:37:36.644010Z","iopub.execute_input":"2025-06-07T12:37:36.644677Z","iopub.status.idle":"2025-06-07T12:37:36.651161Z","shell.execute_reply.started":"2025-06-07T12:37:36.644649Z","shell.execute_reply":"2025-06-07T12:37:36.650103Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_dict = {index: value for index, value in enumerate(le.classes_)}\nprint(labels_dict)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:37:38.410161Z","iopub.execute_input":"2025-06-07T12:37:38.411242Z","iopub.status.idle":"2025-06-07T12:37:38.416875Z","shell.execute_reply.started":"2025-06-07T12:37:38.411207Z","shell.execute_reply":"2025-06-07T12:37:38.415959Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === OIKAN core logic ===\ndef softmax(X):\n    X = np.asarray(X)\n    X -= np.max(X, axis=1, keepdims=True)\n    e_X = np.exp(X)\n    return e_X / np.sum(e_X, axis=1, keepdims=True)\n\ndef evaluate_basis_functions(X, basis_functions, n_features):\n    X_transformed = np.zeros((X.shape[0], len(basis_functions)))\n    for i, func in enumerate(basis_functions):\n        if func == '1':\n            X_transformed[:, i] = 1\n        elif func.startswith('log1p_x'):\n            idx = int(func.split('_')[1][1:])\n            X_transformed[:, i] = np.log1p(np.abs(X[:, idx]))\n        elif func.startswith('exp_x'):\n            idx = int(func.split('_')[1][1:])\n            X_transformed[:, i] = np.exp(np.clip(X[:, idx], -10, 10))\n        elif func.startswith('sin_x'):\n            idx = int(func.split('_')[1][1:])\n            X_transformed[:, i] = np.sin(X[:, idx])\n        elif '^' in func:\n            var, power = func.split('^')\n            idx = int(var[1:])\n            X_transformed[:, i] = X[:, idx] ** int(power)\n        elif ' ' in func:\n            vars_ = func.split(' ')\n            result = np.ones(X.shape[0])\n            for var in vars_:\n                idx = int(var[1:])\n                result *= X[:, idx]\n            X_transformed[:, i] = result\n        else:\n            idx = int(func[1:])\n            X_transformed[:, i] = X[:, idx]\n    return X_transformed","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:37:40.860694Z","iopub.execute_input":"2025-06-07T12:37:40.860995Z","iopub.status.idle":"2025-06-07T12:37:40.872428Z","shell.execute_reply.started":"2025-06-07T12:37:40.860974Z","shell.execute_reply":"2025-06-07T12:37:40.871499Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def oikan_predict(X, symbolic_model):\n    X = np.asarray(X)\n    X_transformed = evaluate_basis_functions(X, symbolic_model['basis_functions'], symbolic_model['n_features'])\n    logits = np.dot(X_transformed, np.array(symbolic_model['coefficients_list']).T)\n    probabilities = softmax(logits)\n    return np.argmax(probabilities, axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:37:44.569574Z","iopub.execute_input":"2025-06-07T12:37:44.569888Z","iopub.status.idle":"2025-06-07T12:37:44.575468Z","shell.execute_reply.started":"2025-06-07T12:37:44.569869Z","shell.execute_reply":"2025-06-07T12:37:44.574463Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Sensor columns ===\nacc_cols = [f'acc_{axis}' for axis in ['x', 'y', 'z']]\nrot_cols = [f'rot_{axis}' for axis in ['w', 'x', 'y', 'z']]\nthm_cols = [f'thm_{i}' for i in range(1, 6)]\ntof_cols = [f'tof_{i}_v{j}' for i in range(1, 6) for j in range(64)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:38:06.211526Z","iopub.execute_input":"2025-06-07T12:38:06.211839Z","iopub.status.idle":"2025-06-07T12:38:06.218353Z","shell.execute_reply.started":"2025-06-07T12:38:06.211820Z","shell.execute_reply":"2025-06-07T12:38:06.217479Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Summary statistics generation ===\ndef seq_to_row(sequence, demographics):\n    df = pd.merge(sequence, demographics, on=\"subject\", how=\"left\")\n\n    cols = ['sequence_id'] + acc_cols + rot_cols + thm_cols + tof_cols\n    summary = df[cols].groupby('sequence_id').agg(['mean', 'std', 'min', 'max', 'median'])\n    summary.columns = ['_'.join(col).strip() for col in summary.columns.values]\n    summary = summary.reset_index()\n\n    remaining = df.drop_duplicates('sequence_id')\n    df = pd.merge(remaining, summary, on=\"sequence_id\", how=\"left\")\n    return df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:38:48.916019Z","iopub.execute_input":"2025-06-07T12:38:48.916426Z","iopub.status.idle":"2025-06-07T12:38:48.923747Z","shell.execute_reply.started":"2025-06-07T12:38:48.916392Z","shell.execute_reply":"2025-06-07T12:38:48.922547Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Prediction Function ===\ndef predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    seq_df = sequence.to_pandas()\n    dem_df = demographics.to_pandas()\n\n    X = seq_to_row(seq_df, dem_df)\n\n    # Add missing features as NaN\n    for feat in selected_feature_names:\n        if feat not in X.columns:\n            X[feat] = np.nan\n\n    X = X[selected_feature_names]\n\n    numeric_cols = X.select_dtypes(include=\"number\").columns.tolist()\n    cat_cols = [col for col in selected_feature_names if col not in numeric_cols]\n\n    X_num = X.fillna(X.median(numeric_only=True))\n    means = X_num.mean()\n    stds = X_num.std(ddof=0)\n    X_num = (X_num - means) / stds\n\n    X_cat = X[cat_cols].to_numpy(dtype=object) if cat_cols else np.empty((X.shape[0], 0))\n    X_proc = np.hstack([X_num, X_cat])\n\n    y_pred_encoded = oikan_predict(X_proc, oikan_model)\n    y_pred = str(le.inverse_transform([y_pred_encoded[0]])[0])\n    # print(y_pred)\n    \n    return y_pred","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:39:11.524102Z","iopub.execute_input":"2025-06-07T12:39:11.524452Z","iopub.status.idle":"2025-06-07T12:39:11.532266Z","shell.execute_reply.started":"2025-06-07T12:39:11.524430Z","shell.execute_reply":"2025-06-07T12:39:11.531214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Inference Server Hook ===\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-06-07T12:39:12.736366Z","iopub.execute_input":"2025-06-07T12:39:12.737257Z","iopub.status.idle":"2025-06-07T12:39:14.477895Z","shell.execute_reply.started":"2025-06-07T12:39:12.737227Z","shell.execute_reply":"2025-06-07T12:39:14.477073Z"}},"outputs":[],"execution_count":null}]}