{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"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","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:35.854668Z","iopub.execute_input":"2025-05-31T10:39:35.855411Z","iopub.status.idle":"2025-05-31T10:39:35.863656Z","shell.execute_reply.started":"2025-05-31T10:39:35.855387Z","shell.execute_reply":"2025-05-31T10:39:35.862897Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Library","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nfrom sklearn.metrics import f1_score\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import LSTM, Dense, Input, Concatenate, Masking, Dropout\nfrom tensorflow.keras.optimizers import Adam\nimport tensorflow as tf\nimport os\nimport warnings\nimport matplotlib.pyplot as plt\nimport plotly.express as px\nimport seaborn as sns","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:35.864992Z","iopub.execute_input":"2025-05-31T10:39:35.865225Z","iopub.status.idle":"2025-05-31T10:39:35.880098Z","shell.execute_reply.started":"2025-05-31T10:39:35.865202Z","shell.execute_reply":"2025-05-31T10:39:35.879523Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"np.random.seed(42)\ntf.random.set_seed(42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:35.880735Z","iopub.execute_input":"2025-05-31T10:39:35.880923Z","iopub.status.idle":"2025-05-31T10:39:35.893455Z","shell.execute_reply.started":"2025-05-31T10:39:35.880909Z","shell.execute_reply":"2025-05-31T10:39:35.892541Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Suppress CUDA warnings\nos.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'  \nwarnings.filterwarnings('ignore', category=UserWarning)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:35.895038Z","iopub.execute_input":"2025-05-31T10:39:35.895511Z","iopub.status.idle":"2025-05-31T10:39:35.906878Z","shell.execute_reply.started":"2025-05-31T10:39:35.895494Z","shell.execute_reply":"2025-05-31T10:39:35.906115Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Load Data ","metadata":{}},{"cell_type":"code","source":"\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\")\ntest = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv\")\ntest_demographics = pd.read_csv(\"/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:35.907730Z","iopub.execute_input":"2025-05-31T10:39:35.907992Z","iopub.status.idle":"2025-05-31T10:39:52.743020Z","shell.execute_reply.started":"2025-05-31T10:39:35.907970Z","shell.execute_reply":"2025-05-31T10:39:52.742115Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:52.743947Z","iopub.execute_input":"2025-05-31T10:39:52.744202Z","iopub.status.idle":"2025-05-31T10:39:52.773184Z","shell.execute_reply.started":"2025-05-31T10:39:52.744175Z","shell.execute_reply":"2025-05-31T10:39:52.772410Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.describe()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:52.775271Z","iopub.execute_input":"2025-05-31T10:39:52.775504Z","iopub.status.idle":"2025-05-31T10:39:59.697364Z","shell.execute_reply.started":"2025-05-31T10:39:52.775488Z","shell.execute_reply":"2025-05-31T10:39:59.696672Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:59.698067Z","iopub.execute_input":"2025-05-31T10:39:59.698241Z","iopub.status.idle":"2025-05-31T10:39:59.713429Z","shell.execute_reply.started":"2025-05-31T10:39:59.698228Z","shell.execute_reply":"2025-05-31T10:39:59.712748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:39:59.714117Z","iopub.execute_input":"2025-05-31T10:39:59.714406Z","iopub.status.idle":"2025-05-31T10:40:00.508126Z","shell.execute_reply.started":"2025-05-31T10:39:59.714386Z","shell.execute_reply":"2025-05-31T10:40:00.507407Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Plot data ","metadata":{}},{"cell_type":"code","source":"# Pie chart of sequence_type distribution\nsample_seq_id = train['sequence_id'].iloc[0]\nsample_seq = train[train['sequence_id'] == sample_seq_id]\nsequence_type_counts = train.groupby('sequence_id')['sequence_type'].first().value_counts()\nplt.figure(figsize=(8, 8))\nplt.pie(\n    sequence_type_counts,\n    labels=sequence_type_counts.index,\n    autopct='%1.1f%%',\n    colors=['#ff9999', '#66b3ff'],\n    startangle=90\n)\nplt.title('Distribution of Sequence Types (Target vs Non-Target)')\nplt.savefig('/kaggle/working/sequence_type_pie.png')\nplt.show()\nplt.close()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:40:00.509002Z","iopub.execute_input":"2025-05-31T10:40:00.509264Z","iopub.status.idle":"2025-05-31T10:40:00.761371Z","shell.execute_reply.started":"2025-05-31T10:40:00.509244Z","shell.execute_reply":"2025-05-31T10:40:00.760648Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Heatmap of IMU feature correlations for one sequence\nheatmap_seq = train[train['sequence_id'] == sample_seq_id]\nimu_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\nimu_data = heatmap_seq[imu_cols].fillna(0)\nacc_magnitude = np.sqrt((heatmap_seq[['acc_x', 'acc_y', 'acc_z']] ** 2).sum(axis=1))\nvelocity = np.cumsum(acc_magnitude) / len(acc_magnitude)\njerk = np.diff(acc_magnitude, prepend=acc_magnitude.iloc[0])\nimu_df = pd.DataFrame(imu_data, columns=imu_cols)\nimu_df['acc_magnitude'] = acc_magnitude\nimu_df['velocity'] = velocity\nimu_df['jerk'] = jerk\ncorrelation_matrix = imu_df.corr()\nplt.figure(figsize=(10, 8))\nsns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt='.2f', vmin=-1, vmax=1)\nplt.title(f'Correlation Heatmap of IMU Features for Sequence {sample_seq_id}')\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:40:00.762159Z","iopub.execute_input":"2025-05-31T10:40:00.762474Z","iopub.status.idle":"2025-05-31T10:40:01.352109Z","shell.execute_reply.started":"2025-05-31T10:40:00.762450Z","shell.execute_reply":"2025-05-31T10:40:01.351302Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"selected_seq_id = train['sequence_id'].unique()[4]  # Get 5th sequence (index 4)\nseq_subset = train[train['sequence_id'] == selected_seq_id]\nplot_3d = px.scatter_3d(\n    seq_subset,\n    x='acc_x',\n    y='acc_y',\n    z='acc_z',\n    title=f'3D Acceleration Axes for Sequence {selected_seq_id}',\n    color='sequence_type',\n    size_max=8,\n    width=1000,\n    height=800,\n    opacity=0.9,\n    template='plotly_dark'\n)\nplot_3d.update_layout(\n    font=dict(size=8),\n    legend=dict(font=dict(size=16))\n)\nplot_3d.write_html('/kaggle/working/accel_3d_plot.html')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:40:01.352831Z","iopub.execute_input":"2025-05-31T10:40:01.353043Z","iopub.status.idle":"2025-05-31T10:40:01.482640Z","shell.execute_reply.started":"2025-05-31T10:40:01.353028Z","shell.execute_reply":"2025-05-31T10:40:01.481784Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig = px.scatter_3d(\n    train,\n    x=\"acc_x\",\n    y=\"acc_y\",\n    z=\"acc_z\",\n    color=\"sequence_type\",\n    size_max=8,\n    opacity=0.9,\n    title=\"Linear acceleration along three axes\",\n    width=1000,\n    height=800,\n    template=\"plotly_dark\"\n)\n\n# TWEAK FONT SIZES\nfig.update_layout(\n    font_size=8,\n    legend_font_size=16\n)\n\nfig.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:40:01.483501Z","iopub.execute_input":"2025-05-31T10:40:01.483808Z","iopub.status.idle":"2025-05-31T10:40:02.107738Z","shell.execute_reply.started":"2025-05-31T10:40:01.483782Z","shell.execute_reply":"2025-05-31T10:40:02.106426Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Time-series plot for IMU data\nplt.figure(figsize=(10, 6))\nplt.plot(sample_seq['sequence_counter'], sample_seq['acc_x'], label='acc_x')\nplt.plot(sample_seq['sequence_counter'], sample_seq['acc_y'], label='acc_y')\nplt.plot(sample_seq['sequence_counter'], sample_seq['acc_z'], label='acc_z')\n\nplt.title(f'IMU Acceleration for Sequence {sample_seq_id}')\nplt.xlabel('Sequence Counter')\nplt.ylabel('Acceleration (m/s²)')\nplt.legend()\n\n# Display the plot in the notebook\nplt.show()\n\n# Save the plot\nplt.savefig('/kaggle/working/imu_plot.png')\nplt.close()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:40:02.111804Z","iopub.execute_input":"2025-05-31T10:40:02.112059Z","iopub.status.idle":"2025-05-31T10:40:02.355192Z","shell.execute_reply.started":"2025-05-31T10:40:02.112041Z","shell.execute_reply":"2025-05-31T10:40:02.354321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Prepare labels\nbinary_le = LabelEncoder()\nbinary_labels = binary_le.fit_transform(train['sequence_type'])\ngesture_map = {\n    'Above ear - Pull hair': 0,\n    'Forehead - Pull hairline': 1,\n    'Forehead - Scratch': 2,\n    'Eyebrow - Pull hair': 3,\n    'Eyelash - Pull hair': 4,\n    'Neck - Pinch skin': 5,\n    'Neck - Scratch': 6,\n    'Cheek - Pinch skin': 7\n}\nmulticlass_labels = train['gesture'].apply(lambda x: gesture_map.get(x, 8)).values  ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:40:02.356243Z","iopub.execute_input":"2025-05-31T10:40:02.356499Z","iopub.status.idle":"2025-05-31T10:40:02.643501Z","shell.execute_reply.started":"2025-05-31T10:40:02.356481Z","shell.execute_reply":"2025-05-31T10:40:02.642700Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Preprocess Sensor Data","metadata":{}},{"cell_type":"code","source":"# Preprocess demographics\nscaler = StandardScaler()\ndemo_cols = ['age', 'height_cm', 'shoulder_to_wrist_cm', 'elbow_to_wrist_cm']\ntrain_demo_data = scaler.fit_transform(train_demographics[demo_cols])\ntrain_demo_cat = train_demographics[['adult_child', 'sex', 'handedness']].values\ntrain_demo_processed = np.concatenate([train_demo_data, train_demo_cat], axis=1)\ntest_demo_data = scaler.transform(test_demographics[demo_cols])\ntest_demo_cat = test_demographics[['adult_child', 'sex', 'handedness']].values\ntest_demo_processed = np.concatenate([test_demo_data, test_demo_cat], axis=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:40:02.644336Z","iopub.execute_input":"2025-05-31T10:40:02.644561Z","iopub.status.idle":"2025-05-31T10:40:02.656790Z","shell.execute_reply.started":"2025-05-31T10:40:02.644546Z","shell.execute_reply":"2025-05-31T10:40:02.656166Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Preprocess training sequences\ntrain_sequences = []\ntrain_demo_final = []\ntrain_binary_final = []\ntrain_multiclass_final = []\n\nfor seq_id in train['sequence_id'].unique():\n    seq_data = train[train['sequence_id'] == seq_id]\n    is_imu_only = 'thm_1' not in seq_data.columns or seq_data['thm_1'].isna().all()\n    \n    # IMU features\n    imu_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\n    imu_data = seq_data[imu_cols].fillna(0).values\n    acc_magnitude = np.sqrt((seq_data[['acc_x', 'acc_y', 'acc_z']] ** 2).sum(axis=1))\n    velocity = np.cumsum(acc_magnitude) / len(acc_magnitude)\n    jerk = np.diff(acc_magnitude, prepend=acc_magnitude.iloc[0])  # Fix: Use .iloc[0]\n    imu_data = np.column_stack([imu_data, acc_magnitude, velocity, jerk])\n    \n    # Thermopile and ToF\n    if is_imu_only:\n        thermo_data = np.zeros((len(seq_data), 10))  # 5 raw + 5 differences\n        tof_data = np.zeros((len(seq_data), 15))  # 5 mean + 5 max + 5 gradient\n    else:\n        thermo_cols = [f'thm_{i}' for i in range(1, 6)]\n        thermo_data = seq_data[thermo_cols].fillna(0).values\n        thermo_diff = np.diff(thermo_data, axis=0, prepend=thermo_data[0:1])\n        thermo_data = np.concatenate([thermo_data, thermo_diff], axis=1)\n        \n        tof_cols = [f'tof_{i}_v{j}' for i in range(1, 6) for j in range(64)]\n        tof_data = seq_data[tof_cols].replace(-1, 0).fillna(0).values\n        tof_reshaped = tof_data.reshape(-1, 5, 8, 8)\n        tof_mean = tof_reshaped.mean(axis=(2, 3))\n        tof_max = tof_reshaped.max(axis=(2, 3))\n        tof_gradient = np.abs(tof_reshaped[:, :, :-1, :-1] - tof_reshaped[:, :, 1:, 1:]).mean(axis=(2, 3))\n        tof_data = np.concatenate([tof_mean, tof_max, tof_gradient], axis=1)\n    \n    sensor_data = np.concatenate([imu_data, thermo_data, tof_data], axis=1)\n    train_sequences.append(sensor_data)\n    \n    subject = seq_data['subject'].iloc[0]\n    demo_idx = train_demographics[train_demographics['subject'] == subject].index[0]\n    train_demo_final.append(train_demo_processed[demo_idx])\n    \n    binary_label = binary_le.transform([seq_data['sequence_type'].iloc[0]])[0]\n    gesture = seq_data['gesture'].iloc[0]\n    multiclass_label = gesture_map.get(gesture, 8)\n    train_binary_final.append(binary_label)\n    train_multiclass_final.append(multiclass_label)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:40:02.657742Z","iopub.execute_input":"2025-05-31T10:40:02.658443Z","iopub.status.idle":"2025-05-31T10:45:58.020961Z","shell.execute_reply.started":"2025-05-31T10:40:02.658423Z","shell.execute_reply":"2025-05-31T10:45:58.020291Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Pad training sequences\nmax_len = max(len(seq) for seq in train_sequences)\ntrain_sequences_padded = np.array([np.pad(seq, ((0, max_len - len(seq)), (0, 0)), mode='constant') for seq in train_sequences])\ntrain_demo_final = np.array(train_demo_final)\ntrain_binary_final = np.array(train_binary_final)\ntrain_multiclass_final = np.array(train_multiclass_final)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:45:58.021750Z","iopub.execute_input":"2025-05-31T10:45:58.021977Z","iopub.status.idle":"2025-05-31T10:45:59.530628Z","shell.execute_reply.started":"2025-05-31T10:45:58.021960Z","shell.execute_reply":"2025-05-31T10:45:59.530029Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Preprocess test sequences\ntest_sequences = []\ntest_demo_final = []","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:45:59.531349Z","iopub.execute_input":"2025-05-31T10:45:59.531617Z","iopub.status.idle":"2025-05-31T10:45:59.535415Z","shell.execute_reply.started":"2025-05-31T10:45:59.531595Z","shell.execute_reply":"2025-05-31T10:45:59.534669Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for seq_id in test['sequence_id'].unique():\n    seq_data = test[test['sequence_id'] == seq_id]\n    is_imu_only = 'thm_1' not in seq_data.columns or seq_data['thm_1'].isna().all()\n    \n    # IMU features\n    imu_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']\n    imu_data = seq_data[imu_cols].fillna(0).values\n    acc_magnitude = np.sqrt((seq_data[['acc_x', 'acc_y', 'acc_z']] ** 2).sum(axis=1))\n    velocity = np.cumsum(acc_magnitude) / len(acc_magnitude)\n    jerk = np.diff(acc_magnitude, prepend=acc_magnitude.iloc[0])  # Fix: Use .iloc[0]\n    imu_data = np.column_stack([imu_data, acc_magnitude, velocity, jerk])\n    \n    # Thermopile and ToF\n    if is_imu_only:\n        thermo_data = np.zeros((len(seq_data), 10))\n        tof_data = np.zeros((len(seq_data), 15))\n    else:\n        thermo_cols = [f'thm_{i}' for i in range(1, 6)]\n        thermo_data = seq_data[thermo_cols].fillna(0).values\n        thermo_diff = np.diff(thermo_data, axis=0, prepend=thermo_data[0:1])\n        thermo_data = np.concatenate([thermo_data, thermo_diff], axis=1)\n        \n        tof_cols = [f'tof_{i}_v{j}' for i in range(1, 6) for j in range(64)]\n        tof_data = seq_data[tof_cols].replace(-1, 0).fillna(0).values\n        tof_reshaped = tof_data.reshape(-1, 5, 8, 8)\n        tof_mean = tof_reshaped.mean(axis=(2, 3))\n        tof_max = tof_reshaped.max(axis=(2, 3))\n        tof_gradient = np.abs(tof_reshaped[:, :, :-1, :-1] - tof_reshaped[:, :, 1:, 1:]).mean(axis=(2, 3))\n        tof_data = np.concatenate([tof_mean, tof_max, tof_gradient], axis=1)\n    \n    sensor_data = np.concatenate([imu_data, thermo_data, tof_data], axis=1)\n    test_sequences.append(sensor_data)\n    \n    subject = seq_data['subject'].iloc[0]\n    demo_idx = test_demographics[test_demographics['subject'] == subject].index[0]\n    test_demo_final.append(test_demo_processed[demo_idx])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:45:59.536147Z","iopub.execute_input":"2025-05-31T10:45:59.536394Z","iopub.status.idle":"2025-05-31T10:45:59.565476Z","shell.execute_reply.started":"2025-05-31T10:45:59.536370Z","shell.execute_reply":"2025-05-31T10:45:59.564828Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Pad test sequences\ntest_sequences_padded = np.array([np.pad(seq, ((0, max_len - len(seq)), (0, 0)), mode='constant') for seq in test_sequences])\ntest_demo_final = np.array(test_demo_final)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:45:59.566380Z","iopub.execute_input":"2025-05-31T10:45:59.566665Z","iopub.status.idle":"2025-05-31T10:45:59.575445Z","shell.execute_reply.started":"2025-05-31T10:45:59.566636Z","shell.execute_reply":"2025-05-31T10:45:59.574807Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model","metadata":{}},{"cell_type":"code","source":"sensor_input = Input(shape=(train_sequences_padded.shape[1], train_sequences_padded.shape[2]), name='sensor_input')\nmasked = Masking(mask_value=0.0)(sensor_input)\nlstm1 = LSTM(128, return_sequences=True)(masked)\nlstm2 = LSTM(64)(lstm1)\ndropout1 = Dropout(0.3)(lstm2)\ndemo_input = Input(shape=(train_demo_final.shape[1],), name='demo_input')\ncombined = Concatenate()([dropout1, demo_input])\ndense = Dense(64, activation='relu')(combined)\ndropout2 = Dropout(0.3)(dense)\nbinary_output = Dense(1, activation='sigmoid', name='binary_output')(dropout2)\nmulticlass_output = Dense(9, activation='softmax', name='multiclass_output')(dropout2)\nmodel = Model(inputs=[sensor_input, demo_input], outputs=[binary_output, multiclass_output])\nmodel.compile(\n    optimizer=Adam(learning_rate=0.001),\n    loss={'binary_output': 'binary_crossentropy', 'multiclass_output': 'sparse_categorical_crossentropy'},\n    loss_weights={'binary_output': 0.5, 'multiclass_output': 0.5},\n    metrics={'binary_output': 'accuracy', 'multiclass_output': 'accuracy'}\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:45:59.576179Z","iopub.execute_input":"2025-05-31T10:45:59.576485Z","iopub.status.idle":"2025-05-31T10:46:03.285989Z","shell.execute_reply.started":"2025-05-31T10:45:59.576468Z","shell.execute_reply":"2025-05-31T10:46:03.285385Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Train model\nmodel.fit(\n    [train_sequences_padded, train_demo_final],\n    [train_binary_final, train_multiclass_final],\n    epochs=20,\n    batch_size=16,\n    validation_split=0.2,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:46:03.286809Z","iopub.execute_input":"2025-05-31T10:46:03.287091Z","iopub.status.idle":"2025-05-31T10:48:47.070110Z","shell.execute_reply.started":"2025-05-31T10:46:03.287068Z","shell.execute_reply":"2025-05-31T10:48:47.069232Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Generate predictions\npredictions = []\ninverse_gesture_map = {v: k for k, v in gesture_map.items()}\ninverse_gesture_map[8] = 'non_target'\n\nfor i, seq_id in enumerate(test['sequence_id'].unique()):\n    seq_data = test_sequences_padded[i:i+1]\n    demo_data = test_demo_final[i:i+1]\n    binary_pred, multiclass_pred = model.predict([seq_data, demo_data], verbose=0)\n    gesture_idx = np.argmax(multiclass_pred, axis=1)[0]\n    gesture = inverse_gesture_map[gesture_idx]\n    predictions.append({'sequence_id': seq_id, 'gesture': gesture})","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:48:47.072802Z","iopub.execute_input":"2025-05-31T10:48:47.073031Z","iopub.status.idle":"2025-05-31T10:48:47.566311Z","shell.execute_reply.started":"2025-05-31T10:48:47.073014Z","shell.execute_reply":"2025-05-31T10:48:47.565624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Save submission\nsubmission = pd.DataFrame(predictions)\nsubmission.to_parquet('/kaggle/working/submission.parquet', index=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-31T10:48:47.583346Z","iopub.execute_input":"2025-05-31T10:48:47.583677Z","iopub.status.idle":"2025-05-31T10:48:47.737870Z","shell.execute_reply.started":"2025-05-31T10:48:47.583647Z","shell.execute_reply":"2025-05-31T10:48:47.737028Z"}},"outputs":[],"execution_count":null}]}