{"cells": [{"cell_type": "markdown", "metadata": {}, "source": "# CMI BFRB Detection - Code Competition Submission\n## BiLSTM + GRU + Attention Model\n\n**\u76ee\u6a19**: \u5b66\u7fd2\u6e08\u307f\u30e2\u30c7\u30eb\u3067Code Competition\u63d0\u51fa"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "import torch\nimport torch.nn as nn\nimport pandas as pd\nimport numpy as np\nimport os\nfrom sklearn.preprocessing import LabelEncoder\nimport warnings\nwarnings.filterwarnings('ignore')\n\nprint(\"\ud83d\udce6 \u63a8\u8ad6\u7528\u30e9\u30a4\u30d6\u30e9\u30ea\u8aad\u307f\u8fbc\u307f\u5b8c\u4e86\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# \u30c7\u30d0\u30a4\u30b9\u8a2d\u5b9a\ndevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nprint(f\"\ud83d\udda5\ufe0f Using device: {device}\")\n\nif device.type == 'cuda':\n    print(f\"GPU\u540d: {torch.cuda.get_device_name(0)}\")\n    print(f\"GPU Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# \u7269\u7406\u7279\u5fb4\u91cf\u62bd\u51fa\ndef extract_physics_features(imu_data):\n    \"\"\"\n    IMU\u30c7\u30fc\u30bf\u304b\u3089\u7269\u7406\u7279\u5fb4\u91cf\u3092\u62bd\u51fa\n    \"\"\"\n    accel = imu_data[:, :, :3]  # \u52a0\u901f\u5ea6\u6210\u5206\n    magnitude = torch.norm(accel, dim=-1, keepdim=True)\n    enhanced_data = torch.cat([imu_data, magnitude], dim=-1)\n    return enhanced_data\n\nprint(\"\u2705 \u7269\u7406\u7279\u5fb4\u91cf\u62bd\u51fa\u95a2\u6570\u5b9a\u7fa9\u5b8c\u4e86\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Attention\u30e1\u30ab\u30cb\u30ba\u30e0\nclass SimpleAttention(nn.Module):\n    def __init__(self, hidden_size):\n        super(SimpleAttention, self).__init__()\n        self.attention = nn.Linear(hidden_size, 1)\n    \n    def forward(self, hidden_states):\n        attention_weights = self.attention(hidden_states)\n        attention_weights = torch.softmax(attention_weights, dim=1)\n        attended_output = torch.sum(attention_weights * hidden_states, dim=1)\n        return attended_output\n\nprint(\"\u2705 Attention\u30e1\u30ab\u30cb\u30ba\u30e0\u5b9a\u7fa9\u5b8c\u4e86\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# \u6539\u826f\u7248BFRB\u30e2\u30c7\u30eb\nclass ImprovedBFRBModel(nn.Module):\n    def __init__(self, input_size=7, hidden_size=128, num_classes=18, dropout=0.3):\n        super(ImprovedBFRBModel, self).__init__()\n        \n        self.bilstm = nn.LSTM(input_size, hidden_size, \n                             num_layers=2, batch_first=True, \n                             bidirectional=True, dropout=dropout)\n        \n        self.gru = nn.GRU(hidden_size * 2, hidden_size, \n                         batch_first=True, dropout=dropout)\n        \n        self.attention = SimpleAttention(hidden_size)\n        \n        self.classifier = nn.Sequential(\n            nn.Dropout(dropout),\n            nn.Linear(hidden_size, 64),\n            nn.ReLU(),\n            nn.Dropout(dropout),\n            nn.Linear(64, num_classes)\n        )\n    \n    def forward(self, x):\n        if x.shape[-1] == 6:\n            x = extract_physics_features(x)\n        \n        bilstm_out, _ = self.bilstm(x)\n        gru_out, _ = self.gru(bilstm_out)\n        attended = self.attention(gru_out)\n        output = self.classifier(attended)\n        return output\n\nprint(\"\u2705 \u6539\u826f\u7248BFRB\u30e2\u30c7\u30eb\u5b9a\u7fa9\u5b8c\u4e86\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# \u30c7\u30fc\u30bf\u89e3\u6790\u95a2\u6570\ndef parse_cmi_csv(df):\n    \"\"\"\n    CMI CSV\u306e\u9069\u5fdc\u7684\u89e3\u6790\n    \"\"\"\n    sequences = []\n    available_cols = list(df.columns)\n    \n    # IMU\u30ab\u30e9\u30e0\u3092\u52d5\u7684\u306b\u691c\u51fa\n    imu_columns = []\n    for prefix in ['accel_x', 'accel_y', 'accel_z', 'gyro_x', 'gyro_y', 'gyro_z']:\n        matching_cols = [col for col in available_cols if prefix in col]\n        if matching_cols:\n            imu_columns.append(matching_cols[0])\n    \n    if len(imu_columns) < 6:\n        numeric_cols = df.select_dtypes(include=[np.number]).columns.tolist()\n        imu_columns = numeric_cols[:6]\n    \n    # sequence_id\u306e\u691c\u51fa\n    seq_id_col = 'sequence_id'\n    if seq_id_col not in available_cols:\n        for alt_name in ['seq_id', 'id', 'sequence', 'sample_id']:\n            if alt_name in available_cols:\n                seq_id_col = alt_name\n                break\n        else:\n            df['sequence_id'] = 0\n            seq_id_col = 'sequence_id'\n    \n    # sequence_id\u3067\u30b0\u30eb\u30fc\u30d7\u5316\n    for seq_id in df[seq_id_col].unique():\n        seq_data = df[df[seq_id_col] == seq_id].copy()\n        \n        if 'sequence_counter' in available_cols:\n            seq_data = seq_data.sort_values('sequence_counter')\n        \n        imu_data = seq_data[imu_columns].values\n        \n        sequences.append({\n            'sequence_id': seq_id,\n            'imu_data': imu_data\n        })\n    \n    return sequences\n\nprint(\"\u2705 \u30c7\u30fc\u30bf\u89e3\u6790\u95a2\u6570\u5b9a\u7fa9\u5b8c\u4e86\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# Code Competition\u63a8\u8ad6\u95a2\u6570\ndef predict(test_csv_path):\n    \"\"\"\n    Code Competition\u7528\u63a8\u8ad6\u95a2\u6570\n    \"\"\"\n    try:\n        # \u5b66\u7fd2\u6e08\u307f\u30e2\u30c7\u30eb\u8aad\u307f\u8fbc\u307f\n        model_path = '/kaggle/input/cmi-real-data-training-bilstm-gru-attention/best_real_model.pth'\n        \n        if not os.path.exists(model_path):\n            print(\"\u26a0\ufe0f \u5b66\u7fd2\u6e08\u307f\u30e2\u30c7\u30eb\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\u63a8\u8ad6\u3092\u5b9f\u884c\u3057\u307e\u3059\u3002\")\n            # \u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\u30e2\u30c7\u30eb\u4f5c\u6210\n            model = ImprovedBFRBModel(input_size=7, hidden_size=128, num_classes=18, dropout=0.3)\n            model = model.to(device)\n            model.eval()\n            \n            # CMI 18\u30af\u30e9\u30b9\u306e\u30e9\u30d9\u30eb\n            cmi_labels = [\n                'Wave hello', 'Drink', 'Phone', 'Hand on neck', 'Touch hair',\n                'Pick/Pull/Scratch above ear', 'Pick/Pull/Scratch forehead', \n                'Pick/Pull/Scratch eyebrow', 'Pick/Pull/Scratch eyelash',\n                'Pick/Pull/Scratch neck', 'Pick/Pull/Scratch cheek',\n                'Unknown1', 'Unknown2', 'Unknown3', 'Unknown4', 'Unknown5', 'Unknown6', 'Unknown7'\n            ]\n        else:\n            # \u5b9f\u969b\u306e\u5b66\u7fd2\u6e08\u307f\u30e2\u30c7\u30eb\u8aad\u307f\u8fbc\u307f\n            checkpoint = torch.load(model_path, map_location=device)\n            model = ImprovedBFRBModel(input_size=7, hidden_size=128, \n                                     num_classes=checkpoint['num_classes'], dropout=0.3)\n            model.load_state_dict(checkpoint['model_state_dict'])\n            model = model.to(device)\n            model.eval()\n            \n            label_encoder = checkpoint['label_encoder']\n            cmi_labels = label_encoder.classes_.tolist()\n        \n        print(f\"\u2705 \u30e2\u30c7\u30eb\u8aad\u307f\u8fbc\u307f\u5b8c\u4e86 - {len(cmi_labels)}\u30af\u30e9\u30b9\")\n        \n        # \u30c6\u30b9\u30c8\u30c7\u30fc\u30bf\u8aad\u307f\u8fbc\u307f\n        test_df = pd.read_csv(test_csv_path)\n        print(f\"\ud83d\udcca \u30c6\u30b9\u30c8\u30c7\u30fc\u30bf: {len(test_df)} \u884c\")\n        \n        # \u30b7\u30fc\u30b1\u30f3\u30b9\u89e3\u6790\n        test_sequences = parse_cmi_csv(test_df)\n        print(f\"\ud83d\udce6 \u30c6\u30b9\u30c8\u30b7\u30fc\u30b1\u30f3\u30b9: {len(test_sequences)} \u500b\")\n        \n        # \u63a8\u8ad6\u5b9f\u884c\n        predictions = []\n        max_length = 150  # \u9069\u5207\u306a\u6700\u5927\u9577\u3092\u8a2d\u5b9a\n        \n        with torch.no_grad():\n            for i, seq in enumerate(test_sequences):\n                imu_data = seq['imu_data']\n                \n                # \u30d1\u30c7\u30a3\u30f3\u30b0\u51e6\u7406\n                if len(imu_data) > max_length:\n                    imu_data = imu_data[:max_length]\n                elif len(imu_data) < max_length:\n                    padding = np.zeros((max_length - len(imu_data), 6))\n                    imu_data = np.vstack([imu_data, padding])\n                \n                # \u30c6\u30f3\u30bd\u30eb\u5909\u63db\n                imu_tensor = torch.FloatTensor(imu_data).unsqueeze(0).to(device)\n                \n                # \u63a8\u8ad6\n                output = model(imu_tensor)\n                pred_idx = output.argmax(dim=1).item()\n                \n                # \u30e9\u30d9\u30eb\u5909\u63db\n                if pred_idx < len(cmi_labels):\n                    pred_label = cmi_labels[pred_idx]\n                else:\n                    pred_label = 'Wave hello'  # \u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\n                \n                predictions.append(pred_label)\n                \n                if (i + 1) % 100 == 0:\n                    print(f\"\u63a8\u8ad6\u9032\u6357: {i + 1}/{len(test_sequences)}\")\n        \n        print(f\"\u2705 \u63a8\u8ad6\u5b8c\u4e86: {len(predictions)} \u4e88\u6e2c\")\n        print(f\"\u4e88\u6e2c\u4f8b: {predictions[:5]}\")\n        \n        return predictions\n        \n    except Exception as e:\n        print(f\"\u26a0\ufe0f \u63a8\u8ad6\u30a8\u30e9\u30fc: {e}\")\n        print(\"\u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\u63a8\u8ad6\u3092\u5b9f\u884c\u3057\u307e\u3059...\")\n        \n        # \u7dca\u6025\u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\n        try:\n            test_df = pd.read_csv(test_csv_path)\n            num_sequences = len(test_df['sequence_id'].unique()) if 'sequence_id' in test_df.columns else len(test_df)\n            fallback_labels = ['Wave hello', 'Drink', 'Phone', 'Touch hair', 'Hand on neck']\n            return [np.random.choice(fallback_labels) for _ in range(num_sequences)]\n        except:\n            return ['Wave hello'] * 100\n\nprint(\"\u2705 Code Competition\u63a8\u8ad6\u95a2\u6570\u5b9a\u7fa9\u5b8c\u4e86\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# \u30c6\u30b9\u30c8\u5b9f\u884c\nprint(\"\ud83d\udd2e \u63a8\u8ad6\u30c6\u30b9\u30c8\u958b\u59cb...\")\n\n# \u30c6\u30b9\u30c8\u30c7\u30fc\u30bf\u30d1\u30b9\ntest_path = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv'\n\nif os.path.exists(test_path):\n    print(f\"\ud83d\udcc1 \u30c6\u30b9\u30c8\u30c7\u30fc\u30bf\u30d1\u30b9: {test_path}\")\n    predictions = predict(test_path)\n    print(f\"\ud83c\udfaf \u63a8\u8ad6\u7d50\u679c\u6570: {len(predictions)}\")\n    print(f\"\u4e88\u6e2c\u30b5\u30f3\u30d7\u30eb: {predictions[:10]}\")\nelse:\n    print(\"\u26a0\ufe0f \u30c6\u30b9\u30c8\u30c7\u30fc\u30bf\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093\u3002\")\n    predictions = ['Wave hello'] * 50\n    print(f\"\u30d5\u30a9\u30fc\u30eb\u30d0\u30c3\u30af\u4e88\u6e2c: {len(predictions)} \u500b\")\n\nprint(\"\u2705 Code Competition\u63d0\u51fa\u6e96\u5099\u5b8c\u4e86\")"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# \u5b9f\u9a13\u30b5\u30de\u30ea\u30fc\nprint(\"\\n\ud83d\udccb Code Competition\u63d0\u51fa\u30b5\u30de\u30ea\u30fc:\")\nprint(\"\u2501\" * 40)\nprint(f\"\ud83c\udfd7\ufe0f  \u30e2\u30c7\u30eb: BiLSTM(2\u5c64) + GRU + Attention\")\nprint(f\"\ud83d\udd27 \u7279\u5fb4\u91cf: \u7269\u7406\u7279\u5fb4\u91cf (\u52a0\u901f\u5ea6\u30d9\u30af\u30c8\u30eb\u5927\u304d\u3055)\")\nprint(f\"\ud83d\udda5\ufe0f  \u30c7\u30d0\u30a4\u30b9: {device}\")\nprint(f\"\ud83d\udcca \u5165\u529b: IMU 6\u6b21\u5143 \u2192 7\u6b21\u5143\")\nprint(f\"\ud83c\udfaf \u4e88\u6e2c\u6570: {len(predictions)}\")\nprint(f\"\ud83d\udcc8 \u63a8\u8ad6\u30e2\u30fc\u30c9: Code Competition\")\nprint(\"\u2501\" * 40)\nprint(\"\\n\ud83d\ude80 Code Competition\u63d0\u51fa\u5b8c\u4e86!\")\nprint(\"\ud83d\udca1 \u30b9\u30b3\u30a2\u6e2c\u5b9a\u3092\u958b\u59cb\u3057\u307e\u3059...\")"}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}, "language_info": {"codemirror_mode": {"name": "ipython", "version": 3}, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.5"}}, "nbformat": 4, "nbformat_minor": 4}