{"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":12236446,"sourceType":"datasetVersion","datasetId":7709947},{"sourceId":12843075,"sourceType":"datasetVersion","datasetId":8122850}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\n\nimport polars as pl\n\nimport kaggle_evaluation.cmi_inference_server\n\nimport pandas as pd\nimport numpy as np\nfrom pathlib import Path\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import LabelEncoder\n\nimport torch\nfrom torch import nn\nfrom torch.utils.data import Dataset, DataLoader","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-05-29T23:31:28.762062Z","iopub.execute_input":"2025-05-29T23:31:28.762435Z","iopub.status.idle":"2025-05-29T23:31:30.304963Z","shell.execute_reply.started":"2025-05-29T23:31:28.762409Z","shell.execute_reply":"2025-05-29T23:31:30.303684Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# データ読み込みと前処理 ######################\nDATA_DIR = Path(\"/kaggle/input/cmi-detect-behavior-with-sensor-data\")\n# DATA_DIR = Path(\"./drive/MyDrive/Colab Notebooks/cmi_data/\")\n\ndf = pd.read_csv(DATA_DIR / \"train.csv\")\n\n# ラベルのエンコード（ジェスチャーの種類を番号に振り分けて新しい列を追加）\nle = LabelEncoder()\ndf['gesture_encoded'] = le.fit_transform(df['gesture'])\n\n# 対応表を確認する\nlabel_mapping = dict(zip(le.classes_, le.transform(le.classes_)))\nprint(label_mapping)\n\n# 使用する特徴量（IMUのみ）\nfeature_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z', 'rot_w']\n\n# シーケンスごとにデータとラベルをリスト化\nsequence_data = []\nlabels = []\n\n# groupby は同じsequence_idごとにグループ分けする。\nfor seq_id, group in df.groupby('sequence_id'):\n    features = group[feature_cols].fillna(0).values #　欠損値があれば0で補完。valuesでnumpy配列に変換\n    label = group['gesture_encoded'].iloc[0] #　ラベルを一つ取得する。先頭の一つをとれば後は全部一緒\n    sequence_data.append(features) \n    labels.append(label)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# シーケンスをパディング（長さを揃える）\nfrom torch.nn.utils.rnn import pad_sequence\n\nmax_len = 100  # 長さ制限（同じに揃える）\n\n# 最大300ステップにトリム。float32 型の PyTorch テンソルに変換 したリストを作ります。\nseq_tensors = [torch.tensor(seq[:max_len], dtype=torch.float32) for seq in sequence_data] \n\n# 上で作ったテンソルのリストをまとめて1つの3次元テンソル（[バッチ数, 時系列長, 特徴量数]）にします。\nseq_tensors_padded = pad_sequence(seq_tensors, batch_first=True)\n# pad_sequence(..., batch_first=True) を使うと、それらを最長シーケンスの長さに揃えるように、末尾に0を足して補完してくれる。\n\n# 各シーケンスに対応する ラベルのリストを整数型テンソルに変換します（クロスエントロピー損失などで使えるように）。\nlabels_tensor = torch.tensor(labels, dtype=torch.long)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Dataset & DataLoader 作成\nclass SensorDataset(Dataset):\n    def __init__(self, sequences, labels):\n        self.sequences = sequences\n        self.labels = labels\n\n    def __len__(self):\n        return len(self.sequences)\n\n    def __getitem__(self, idx):\n        return self.sequences[idx], self.labels[idx]\n        # このメソッドの出力がモデルに入る。このクラス内でデータ処理を行ってもよい。\n\n# train/test split\n# ランダムでTrainデータとTestデータを分ける\nX_train, X_val, y_train, y_val = train_test_split(\n    seq_tensors_padded, labels_tensor, test_size=0.2, random_state=42\n)\n\ntrain_dataset = SensorDataset(X_train, y_train)\nval_dataset = SensorDataset(X_val, y_val)\n\n# データをモデルに渡しやすい形にする。\n# 学習時に32件ずつまとめて取り出す（=バッチ学習）\n# shuffle=True：毎エポックごとにデータの順番をランダムにシャッフル。\ntrain_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=32)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # シンプルなLSTMモデル　#########################\n# # これをもう一回実行するとモデルがリセットされる。\n\n# # input_size 1ステップあたりの特徴量\n# # hidden_size LSTMセルの隠れ状態のサイズ\n# # num_classes 分類するジェスチャーの種類\n# # self.lstm：時系列を処理するLSTM層\n# # self.fc：LSTMの出力を分類に変換する全結合層\n\n# class LSTMClassifier(nn.Module):\n#     def __init__(self, input_size, hidden_size, num_classes):\n#         super().__init__()\n#         self.lstm = nn.LSTM(input_size, hidden_size, batch_first=True)\n#         self.fc = nn.Linear(hidden_size, num_classes)\n#     # 順伝播の処理\n#     def forward(self, x):\n#         output, _ = self.lstm(x)\n#         # 最後のタイムステップの出力を使用\n#         last_output = output[:, -1, :]\n#         return self.fc(last_output)\n\n\n# 1d-CNN\nclass CNN1DClassifier(nn.Module):\n    def __init__(self, num_features, num_classes):\n        super().__init__()\n        self.conv_layers = nn.Sequential(\n            nn.Conv1d(num_features, 64, kernel_size=5, padding=2),\n            nn.ReLU(),\n            nn.Conv1d(64, 128, kernel_size=5, padding=2),\n            nn.ReLU(),\n            nn.AdaptiveMaxPool1d(1)  # 時系列長を1に圧縮\n        )\n        self.fc_layers = nn.Sequential(\n            nn.Linear(128, 64),\n            nn.ReLU(),\n            nn.Dropout(0.5),  \n            nn.Linear(64, num_classes)\n        )\n\n    def forward(self, x):\n        # 入力は [B, seq_len, feat_dim]\n        x = x.permute(0, 2, 1)   # [B, feat_dim, seq_len]\n        x = self.conv_layers(x)  # [B, 128, 1]\n        x = x.squeeze(-1)        # [B, 128]\n        x = self.fc_layers(x)    # [B, num_classes]\n        return x\n\nmodel = CNN1DClassifier(num_features=len(feature_cols), num_classes=len(le.classes_))\n\n# # インスタンス化\n# input_size = len(feature_cols)\n# hidden_size = 64\n# num_classes = len(le.classes_)\n# model = LSTMClassifier(input_size, hidden_size, num_classes)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The evaluation API requires that you set up a server which will respond to inference requests. We have already defined the server; you just need write the predict function. When we evaluate your submission on the hidden test set the client defined in the gateway will run in a different container with direct access to the hidden test set and hand off the one sequence at a time.\n\nYour code will always have access to the published copies of the files.","metadata":{"execution":{"iopub.status.busy":"2025-05-29T22:29:18.955791Z","iopub.execute_input":"2025-05-29T22:29:18.956193Z","iopub.status.idle":"2025-05-29T22:29:18.965179Z","shell.execute_reply.started":"2025-05-29T22:29:18.956169Z","shell.execute_reply":"2025-05-29T22:29:18.963797Z"}}},{"cell_type":"code","source":"# def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n#     # Replace this function with your inference code.\n#     # You can return either a Pandas or Polars dataframe, though Polars is recommended.\n#     # Each prediction (except the very first) must be returned within 30 minutes of the batch features being provided.\n    \n#     outputs = model(x_batch)\n#     return 'Text on phone'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-29T23:31:30.306452Z","iopub.execute_input":"2025-05-29T23:31:30.306956Z","iopub.status.idle":"2025-05-29T23:31:30.312445Z","shell.execute_reply.started":"2025-05-29T23:31:30.306918Z","shell.execute_reply":"2025-05-29T23:31:30.311257Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class SensorDatasetTest(Dataset):\n    def __init__(self, sequences):\n        self.sequences = sequences\n\n    def __len__(self):\n        return len(self.sequences)\n\n    def __getitem__(self, idx):\n        return self.sequences[idx]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from torch.nn.utils.rnn import pad_sequence\n\nlabel_mapping = dict(zip(le.classes_, le.transform(le.classes_)))\nprint(label_mapping)\ninverse_dict = {i: label for i, label in enumerate(le.classes_)}","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n    \n    feature_cols = ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z', 'rot_w']\n    sequence_pd = sequence.to_pandas()\n    features = sequence_pd[feature_cols].fillna(0).values #　欠損値があれば0で補完。valuesでnumpy配列に変換\n\n    max_len = 100  # 長さ制限（同じに揃える）\n    seq_tensors =torch.tensor(features[:max_len], dtype=torch.float32)\n    seq_tensors_padded = pad_sequence([seq_tensors], batch_first=True)\n    \n    # 各シーケンスに対応する ラベルのリストを整数型テンソルに変換します（クロスエントロピー損失などで使えるように）。\n    labels_tensor = torch.tensor(labels, dtype=torch.long)\n\n\n    dataset = SensorDatasetTest(seq_tensors_padded)\n    dataloader = DataLoader(dataset, batch_size=1, shuffle=False)\n\n\n    input_size = len(feature_cols)\n    hidden_size = 64\n    num_classes = len(le.classes_)\n    # model = LSTMClassifier(input_size, hidden_size, num_classes)\n    model = CNN1DClassifier(input_size, num_classes)\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") \n    model.load_state_dict(torch.load(\"/kaggle/input/second/submit_cmi (1).pth\"))\n    model.to(device)\n    \n    \n\n    model.eval()  # 評価モードに設定\n    \n    val_probs_list = []\n    val_label_list = []\n    with torch.no_grad():  # 検証時は勾配計算を行わない\n        for batch in dataloader:\n            images = batch\n            val_images = images.to(device)\n            \n            # 順伝播\n            val_outputs = model(val_images)\n            probs = torch.softmax(val_outputs[0], dim=0)\n            pred_label = torch.argmax(probs)\n            pred_label = pred_label.cpu()\n        \n    return inverse_dict[pred_label.item()]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"inference_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},"outputs":[],"execution_count":null}]}