{"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":242561727,"sourceType":"kernelVersion"}],"dockerImageVersionId":31041,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# OIKAN training notebook (without NN data augmentation)\n\n🍴 Notebook baseline forked from [CMI 2025 - Training (XGBoost Baseline)](https://www.kaggle.com/code/coreymichaud/cmi-2025-inference-xgboost-baseline) & INFERENCE from [CMI 2025 - Inference (XGBoost Baseline)](https://www.kaggle.com/code/coreymichaud/cmi-2025-inference-xgboost-baseline) by [Corey Michaud](https://www.kaggle.com/coreymichaud)\n\n📤 Submission Notebook: [OIKAN ~ CMI 2025 Submission](https://www.kaggle.com/code/armanzhalgasbayev/oikan-cmi-2025-submission)\n\n📜 About OIKAN library: https://github.com/silvermete0r/oikan","metadata":{}},{"cell_type":"code","source":"!pip install -qU oikan","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:11:58.558674Z","iopub.execute_input":"2025-06-07T12:11:58.559232Z","iopub.status.idle":"2025-06-07T12:12:01.748901Z","shell.execute_reply.started":"2025-06-07T12:11:58.559194Z","shell.execute_reply":"2025-06-07T12:12:01.748080Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip freeze | grep oikan","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:12:01.750194Z","iopub.execute_input":"2025-06-07T12:12:01.750523Z","iopub.status.idle":"2025-06-07T12:12:03.328800Z","shell.execute_reply.started":"2025-06-07T12:12:01.750493Z","shell.execute_reply":"2025-06-07T12:12:03.328034Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Imports ===\nimport os\nimport pandas as pd\nimport numpy as np\nimport joblib\nimport matplotlib.pyplot as plt\n\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import accuracy_score, classification_report\nfrom sklearn.feature_selection import SelectKBest, f_classif\n\nfrom metric import score, CompetitionMetric  # Given competition metric\nfrom oikan import OIKANClassifier","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:19:18.520773Z","iopub.execute_input":"2025-06-07T12:19:18.521047Z","iopub.status.idle":"2025-06-07T12:19:18.552678Z","shell.execute_reply.started":"2025-06-07T12:19:18.521030Z","shell.execute_reply":"2025-06-07T12:19:18.552142Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:12:10.320678Z","iopub.execute_input":"2025-06-07T12:12:10.321536Z","iopub.status.idle":"2025-06-07T12:12:10.324879Z","shell.execute_reply.started":"2025-06-07T12:12:10.321511Z","shell.execute_reply":"2025-06-07T12:12:10.324137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === CONFIG ===\nTRAINING = 1\nSAVING = 1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:12:10.764625Z","iopub.execute_input":"2025-06-07T12:12:10.764911Z","iopub.status.idle":"2025-06-07T12:12:10.768822Z","shell.execute_reply.started":"2025-06-07T12:12:10.764890Z","shell.execute_reply":"2025-06-07T12:12:10.768066Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Data Loading ===\ntrain = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv\")\ntrain_demographics = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv\")\n\ntrain_merged = pd.merge(train, train_demographics, on=\"subject\", how=\"left\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:12:12.196125Z","iopub.execute_input":"2025-06-07T12:12:12.196858Z","iopub.status.idle":"2025-06-07T12:12:33.851237Z","shell.execute_reply.started":"2025-06-07T12:12:12.196829Z","shell.execute_reply":"2025-06-07T12:12:33.850408Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_merged.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:12:33.852819Z","iopub.execute_input":"2025-06-07T12:12:33.853014Z","iopub.status.idle":"2025-06-07T12:12:33.874326Z","shell.execute_reply.started":"2025-06-07T12:12:33.852999Z","shell.execute_reply":"2025-06-07T12:12:33.873731Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_merged.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:12:33.875282Z","iopub.execute_input":"2025-06-07T12:12:33.875553Z","iopub.status.idle":"2025-06-07T12:12:33.892101Z","shell.execute_reply.started":"2025-06-07T12:12:33.875526Z","shell.execute_reply":"2025-06-07T12:12:33.891150Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Feature Engineering","metadata":{}},{"cell_type":"code","source":"%%time\n\n# === Feature Engineering ===\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)]\n\nnumerical_cols = [\"age\", \"height_cm\", \"shoulder_to_wrist_cm\", \"elbow_to_wrist_cm\"] + acc_cols + rot_cols + thm_cols + tof_cols\ncategorical_cols = [\"adult_child\", \"sex\", \"handedness\"]\n\nsummary_cols = ['sequence_id'] + acc_cols + rot_cols + thm_cols + tof_cols\nsummary = train_merged[summary_cols].groupby('sequence_id').agg(['mean', 'std', 'min', 'max', 'median'])\n\nsummary.columns = ['_'.join(col).strip() for col in summary.columns.values]\nsummary = summary.reset_index()\n\nremaining = train_merged.drop_duplicates('sequence_id')\ntrain_df = pd.merge(remaining, summary, on=\"sequence_id\", how=\"left\")\n\nexclude_cols = set(categorical_cols) | {\"sequence_id\"}\nnumerical_cols = [\n    col for col in train_df.select_dtypes(include=[\"number\"]).columns\n    if col not in exclude_cols and col != \"gesture\"\n]\nX_full = train_df[numerical_cols]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:15:38.368978Z","iopub.execute_input":"2025-06-07T12:15:38.369508Z","iopub.status.idle":"2025-06-07T12:15:47.106686Z","shell.execute_reply.started":"2025-06-07T12:15:38.369486Z","shell.execute_reply":"2025-06-07T12:15:47.106010Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Label encode target\nle = LabelEncoder()\ny = le.fit_transform(train_df[\"gesture\"])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:15:49.821970Z","iopub.execute_input":"2025-06-07T12:15:49.822245Z","iopub.status.idle":"2025-06-07T12:15:49.828526Z","shell.execute_reply.started":"2025-06-07T12:15:49.822223Z","shell.execute_reply":"2025-06-07T12:15:49.827740Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Mapping info:\nlabels_dict = {index: value for index, value in enumerate(le.classes_)}\nprint(labels_dict)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:15:51.553345Z","iopub.execute_input":"2025-06-07T12:15:51.553668Z","iopub.status.idle":"2025-06-07T12:15:51.558242Z","shell.execute_reply.started":"2025-06-07T12:15:51.553646Z","shell.execute_reply":"2025-06-07T12:15:51.557460Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visualize Class Distribution\ngesture_counts = train_df['gesture'].value_counts(normalize=True)\nplt.figure(figsize=(8, 8))\nplt.pie(gesture_counts, labels=gesture_counts.index, autopct='%1.1f%%', startangle=90)\nplt.title(\"Gesture Class Distribution\")\nplt.axis('equal')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:15:53.026929Z","iopub.execute_input":"2025-06-07T12:15:53.027527Z","iopub.status.idle":"2025-06-07T12:15:53.329836Z","shell.execute_reply.started":"2025-06-07T12:15:53.027506Z","shell.execute_reply":"2025-06-07T12:15:53.328912Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Fill missing values with median (numeric only)\nX = X_full.fillna(X_full.median(numeric_only=True))\n\n# Standardize (Z-score normalization)\nmeans = X.mean()\nstds = X.std(ddof=0)\nX_scaled = (X - means) / stds\n\nX_scaled = X_scaled.fillna(0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:15:56.516613Z","iopub.execute_input":"2025-06-07T12:15:56.516900Z","iopub.status.idle":"2025-06-07T12:15:57.872918Z","shell.execute_reply.started":"2025-06-07T12:15:56.516880Z","shell.execute_reply":"2025-06-07T12:15:57.872327Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"selector = SelectKBest(f_classif, k=300)  # try different k values\nX_selected = selector.fit_transform(X_scaled, y)\nselected_features = X.columns[selector.get_support()]\nprint(selected_features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:20:19.918624Z","iopub.execute_input":"2025-06-07T12:20:19.918906Z","iopub.status.idle":"2025-06-07T12:20:20.355439Z","shell.execute_reply.started":"2025-06-07T12:20:19.918886Z","shell.execute_reply":"2025-06-07T12:20:20.354659Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = X_scaled[selected_features]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:20:46.311489Z","iopub.execute_input":"2025-06-07T12:20:46.311988Z","iopub.status.idle":"2025-06-07T12:20:46.332680Z","shell.execute_reply.started":"2025-06-07T12:20:46.311964Z","shell.execute_reply":"2025-06-07T12:20:46.331958Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# OIKAN Model Training","metadata":{}},{"cell_type":"code","source":"# === OIKAN Training ===\noikan_model = OIKANClassifier(\n    augmentation_factor=1,\n    alpha=0.2,\n    top_k=30,\n    verbose=True,\n    random_state=42\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:22:41.134033Z","iopub.execute_input":"2025-06-07T12:22:41.134650Z","iopub.status.idle":"2025-06-07T12:22:41.139316Z","shell.execute_reply.started":"2025-06-07T12:22:41.134626Z","shell.execute_reply":"2025-06-07T12:22:41.138729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%time\n\nif TRAINING:\n    oikan_model.fit(X, y)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:22:41.375794Z","iopub.execute_input":"2025-06-07T12:22:41.376488Z","iopub.status.idle":"2025-06-07T12:25:45.619098Z","shell.execute_reply.started":"2025-06-07T12:22:41.376464Z","shell.execute_reply":"2025-06-07T12:25:45.618413Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# === Display symbolic formula and importances ===\nprint(\"\\n=== OIKAN Symbolic Formula ===\")\nformulas = oikan_model.get_formula()\nfor formula in formulas:\n    print(formula)\n\nprint(\"\\n=== OIKAN Feature Importances ===\")\nimportances = oikan_model.feature_importances()\nfeature_names = selected_features\n\n# Top 20 plot\nplt.figure(figsize=(10, 6))\nsorted_idx = np.argsort(importances)[::-1][:20]\nplt.barh([feature_names[i] for i in sorted_idx][::-1], importances[sorted_idx][::-1])\nplt.xlabel(\"Importance\")\nplt.title(\"Top 20 OIKAN Feature Importances\")\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:28:49.170861Z","iopub.execute_input":"2025-06-07T12:28:49.171536Z","iopub.status.idle":"2025-06-07T12:28:49.448082Z","shell.execute_reply.started":"2025-06-07T12:28:49.171508Z","shell.execute_reply":"2025-06-07T12:28:49.447287Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Saving Pipeline and Label Encoder","metadata":{}},{"cell_type":"code","source":"# === Save Artifacts ===\nif SAVING:\n    os.makedirs(\"model_artifacts\", exist_ok=True)\n\n    # Save selected feature names for inference\n    with open(\"model_artifacts/used_features.txt\", \"w\") as f:\n        f.write(\"\\n\".join(selected_features))\n\n    # Save label encoder\n    joblib.dump(le, \"model_artifacts/label_encoder.pkl\")\n\n    # Save trained OIKAN model\n    oikan_model.save(\"model_artifacts/oikan_model.json\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-07T12:28:56.414019Z","iopub.execute_input":"2025-06-07T12:28:56.414800Z","iopub.status.idle":"2025-06-07T12:28:56.421136Z","shell.execute_reply.started":"2025-06-07T12:28:56.414773Z","shell.execute_reply":"2025-06-07T12:28:56.420524Z"}},"outputs":[],"execution_count":null}]}