{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"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-08-12T05:46:48.390382Z","iopub.execute_input":"2025-08-12T05:46:48.390715Z","iopub.status.idle":"2025-08-12T05:46:49.270624Z","shell.execute_reply.started":"2025-08-12T05:46:48.390689Z","shell.execute_reply":"2025-08-12T05:46:49.269732Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_filePath = '/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv'\ntrain_demographics_filePath = '/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv'\ntest_filePath = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv'\ntest_demographics_filePath = '/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:46:49.271553Z","iopub.execute_input":"2025-08-12T05:46:49.272035Z","iopub.status.idle":"2025-08-12T05:46:49.276402Z","shell.execute_reply.started":"2025-08-12T05:46:49.272005Z","shell.execute_reply":"2025-08-12T05:46:49.275567Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(train_filePath)\ntrain_dem_df = pd.read_csv(train_demographics_filePath)\ntest_df = pd.read_csv(test_filePath)\ntest_dem_df = pd.read_csv(test_demographics_filePath)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:46:49.278279Z","iopub.execute_input":"2025-08-12T05:46:49.278485Z","iopub.status.idle":"2025-08-12T05:47:24.511115Z","shell.execute_reply.started":"2025-08-12T05:46:49.278469Z","shell.execute_reply":"2025-08-12T05:47:24.510113Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:24.511984Z","iopub.execute_input":"2025-08-12T05:47:24.512232Z","iopub.status.idle":"2025-08-12T05:47:24.556002Z","shell.execute_reply.started":"2025-08-12T05:47:24.512203Z","shell.execute_reply":"2025-08-12T05:47:24.555275Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.columns[train_df.isnull().any()].tolist()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:24.556790Z","iopub.execute_input":"2025-08-12T05:47:24.557013Z","iopub.status.idle":"2025-08-12T05:47:25.254960Z","shell.execute_reply.started":"2025-08-12T05:47:24.556995Z","shell.execute_reply":"2025-08-12T05:47:25.254127Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df['sequence_type'].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.255938Z","iopub.execute_input":"2025-08-12T05:47:25.256635Z","iopub.status.idle":"2025-08-12T05:47:25.309707Z","shell.execute_reply.started":"2025-08-12T05:47:25.256603Z","shell.execute_reply":"2025-08-12T05:47:25.308831Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"feature_columns = list(train_df.columns)\ndel feature_columns[16:]\ndel feature_columns[0:9]\n\nfeature_columns[0]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.310668Z","iopub.execute_input":"2025-08-12T05:47:25.310998Z","iopub.status.idle":"2025-08-12T05:47:25.324137Z","shell.execute_reply.started":"2025-08-12T05:47:25.310973Z","shell.execute_reply":"2025-08-12T05:47:25.323404Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMU_df = train_df[feature_columns]\nIMU_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.324913Z","iopub.execute_input":"2025-08-12T05:47:25.325746Z","iopub.status.idle":"2025-08-12T05:47:25.355514Z","shell.execute_reply.started":"2025-08-12T05:47:25.325721Z","shell.execute_reply":"2025-08-12T05:47:25.354746Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMU_df.isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.358060Z","iopub.execute_input":"2025-08-12T05:47:25.358349Z","iopub.status.idle":"2025-08-12T05:47:25.378362Z","shell.execute_reply.started":"2025-08-12T05:47:25.358329Z","shell.execute_reply":"2025-08-12T05:47:25.377715Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMU_df[IMU_df.isnull().any(axis=1)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.379049Z","iopub.execute_input":"2025-08-12T05:47:25.379292Z","iopub.status.idle":"2025-08-12T05:47:25.404110Z","shell.execute_reply.started":"2025-08-12T05:47:25.379266Z","shell.execute_reply":"2025-08-12T05:47:25.403360Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"null_rotations = train_df[train_df[['rot_w', 'rot_x', 'rot_y', 'rot_z']].isnull().any(axis=1)]\n\n# 위에서 찾은 행들의 'sequence_id' 열에서 중복되지 않는 값들만 추출합니다.\nunique_sequence_ids = null_rotations['sequence_id'].unique()\n\n# NumPy 배열을 파이썬 리스트로 변환하여 출력합니다.\nunique_sequence_ids_list = unique_sequence_ids.tolist()\nprint(unique_sequence_ids_list)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.404888Z","iopub.execute_input":"2025-08-12T05:47:25.405107Z","iopub.status.idle":"2025-08-12T05:47:25.427421Z","shell.execute_reply.started":"2025-08-12T05:47:25.405090Z","shell.execute_reply":"2025-08-12T05:47:25.426750Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# rot가 null인 seq_id\nnull_rot = null_rotations.groupby('sequence_id').first()[['orientation']]\nnull_seq_len = null_rotations.groupby('sequence_id').last()[['sequence_counter']]\nnull_rot = pd.merge(null_rot, null_seq_len, on='sequence_id', how='right')\nnull_rot","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.428241Z","iopub.execute_input":"2025-08-12T05:47:25.428524Z","iopub.status.idle":"2025-08-12T05:47:25.465669Z","shell.execute_reply.started":"2025-08-12T05:47:25.428499Z","shell.execute_reply":"2025-08-12T05:47:25.464968Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df[['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']].isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.466471Z","iopub.execute_input":"2025-08-12T05:47:25.466680Z","iopub.status.idle":"2025-08-12T05:47:25.493383Z","shell.execute_reply.started":"2025-08-12T05:47:25.466663Z","shell.execute_reply":"2025-08-12T05:47:25.492560Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def input_rot(seq_id, last_seq_id, seq_len, seq_c):\n    # 원본(last_seq_id) 데이터와 대상(seq_id) 데이터를 미리 필터링\n    source_rows = train_df[train_df['sequence_id'] == last_seq_id].sort_values('sequence_counter')\n    target_rows_to_fill = train_df[train_df['sequence_id'] == seq_id].sort_values('sequence_counter')\n\n    # 복사할 데이터의 인덱스를 찾습니다. (source_df의 0부터 seq_len만큼)\n    source_indices = source_rows.head(seq_len).index\n\n    target_indices = target_rows_to_fill.head(seq_len).index\n    # 길이가 같지 않은 경우 오류를 방지합니다.\n    while len(source_indices) != len(target_indices):\n        \n        print(f\"오류: 복사할 행의 수({len(source_indices)})와 채워넣을 행의 수({len(target_indices)})가 다릅니다.\")\n        return\n    train_df.loc[target_indices, ['rot_w', 'rot_x', 'rot_y', 'rot_z']] = \\\n        train_df.loc[source_indices, ['rot_w', 'rot_x', 'rot_y', 'rot_z']].values\n    \n    print(f\"'{seq_id}'에 '{last_seq_id}'의 rot 값을 {len(source_indices)}개 복사했습니다.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.494329Z","iopub.execute_input":"2025-08-12T05:47:25.494669Z","iopub.status.idle":"2025-08-12T05:47:25.500383Z","shell.execute_reply.started":"2025-08-12T05:47:25.494643Z","shell.execute_reply":"2025-08-12T05:47:25.499566Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for row in null_rot.itertuples():\n    seq_id, orientation, seq_len = row\n    filtered_df = train_df[(train_df['orientation'] == orientation)]\n\n    max_counter = 0\n    # last seq_counter\n    last_counter_df = filtered_df.groupby('sequence_id')['sequence_counter'].max()\n\n    for col in last_counter_df.items():\n        last_seq_id, seq_c = col\n        if seq_id == last_seq_id:\n            continue\n        elif seq_c == seq_len:\n            input_rot(seq_id, last_seq_id, seq_len + 1, seq_c)\n            max_counter = -1\n            break\n        elif (seq_c > max_counter) & (seq_c < seq_len + 1):\n            max_counter = seq_c\n            temp_id = last_seq_id\n            \n    if max_counter != -1:\n        print(max_counter)\n        input_rot(seq_id, temp_id, seq_len + 1, max_counter)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:25.501218Z","iopub.execute_input":"2025-08-12T05:47:25.501425Z","iopub.status.idle":"2025-08-12T05:47:42.419204Z","shell.execute_reply.started":"2025-08-12T05:47:25.501407Z","shell.execute_reply":"2025-08-12T05:47:42.418360Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"채워 넣을 행의 수가 더 많은 경우, ","metadata":{}},{"cell_type":"code","source":"train_df[['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']].isnull().sum()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:42.420103Z","iopub.execute_input":"2025-08-12T05:47:42.420322Z","iopub.status.idle":"2025-08-12T05:47:42.447137Z","shell.execute_reply.started":"2025-08-12T05:47:42.420304Z","shell.execute_reply":"2025-08-12T05:47:42.446330Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMU_df = train_df[feature_columns]\nIMU_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:42.448031Z","iopub.execute_input":"2025-08-12T05:47:42.448310Z","iopub.status.idle":"2025-08-12T05:47:42.467124Z","shell.execute_reply.started":"2025-08-12T05:47:42.448286Z","shell.execute_reply":"2025-08-12T05:47:42.466300Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df[train_df[['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']].isnull().any(axis=1)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:42.468014Z","iopub.execute_input":"2025-08-12T05:47:42.468540Z","iopub.status.idle":"2025-08-12T05:47:42.497216Z","shell.execute_reply.started":"2025-08-12T05:47:42.468511Z","shell.execute_reply":"2025-08-12T05:47:42.496547Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df[train_df['sequence_id'] == 'SEQ_001160'][['acc_x', 'acc_y', 'acc_z', 'rot_w', 'rot_x', 'rot_y', 'rot_z']]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:42.618147Z","iopub.execute_input":"2025-08-12T05:47:42.618411Z","iopub.status.idle":"2025-08-12T05:47:42.685740Z","shell.execute_reply.started":"2025-08-12T05:47:42.618386Z","shell.execute_reply":"2025-08-12T05:47:42.684857Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"grouped_train_df = train_df.groupby('sequence_id')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:42.686592Z","iopub.execute_input":"2025-08-12T05:47:42.686833Z","iopub.status.idle":"2025-08-12T05:47:42.691007Z","shell.execute_reply.started":"2025-08-12T05:47:42.686814Z","shell.execute_reply":"2025-08-12T05:47:42.690208Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\n\n# 비어있는 리스트를 준비합니다. 이 리스트에 각 시퀀스의 텐서를 담을 것입니다.\nsequences_as_tensors = []\n\n# grouped는 (sequence_id, DataFrame) 형태의 튜플을 반환합니다.\nfor sequence_id, group_df in grouped_train_df:\n    # 1. 특성 컬럼만 선택합니다.\n    features_df = group_df[feature_columns]\n\n    # 2. DataFrame을 NumPy 배열로 변환합니다.\n    features_numpy = features_df.values\n\n    # 3. NumPy 배열을 PyTorch 텐서로 변환합니다.\n    # .float()는 데이터 타입을 실수형으로 맞춰줍니다. 딥러닝에서 주로 사용됩니다.\n    sequence_tensor = torch.from_numpy(features_numpy).float()\n    \n    # 4. 변환된 텐서를 리스트에 추가합니다.\n    sequences_as_tensors.append(sequence_tensor)\n\n# 이제 sequences_as_tensors 리스트가 예시의 sequences 리스트와 동일한 역할을 합니다.\nprint(f\"총 시퀀스 개수: {len(sequences_as_tensors)}\")\nprint(f\"첫 번째 시퀀스 텐서의 크기: {sequences_as_tensors[0].shape}\")\nprint(f\"두 번째 시퀀스 텐서의 크기: {sequences_as_tensors[1].shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:42.691912Z","iopub.execute_input":"2025-08-12T05:47:42.692185Z","iopub.status.idle":"2025-08-12T05:47:50.905301Z","shell.execute_reply.started":"2025-08-12T05:47:42.692160Z","shell.execute_reply":"2025-08-12T05:47:50.904248Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def pad_to_max_len(sequences, max_len, padding_value=0):\n    padded_tensors = []\n    for seq in sequences:\n        # 현재 시퀀스 길이\n        current_len = seq.size(0)\n        \n        # 패딩할 길이 계산\n        padding_len = max_len - current_len\n        \n        # 패딩 텐서 생성\n        # torch.zeros를 사용해 패딩할 텐서를 만들고, padding_value로 채움\n        # seq.size(1)은 컬럼 수(특성 수)\n        padding_tensor = torch.full((padding_len, seq.size(1)), padding_value, dtype=seq.dtype)\n        \n        # 원래 시퀀스와 패딩 텐서를 합침\n        padded_seq = torch.cat([seq, padding_tensor], dim=0)\n        padded_tensors.append(padded_seq)\n        \n    # 패딩된 텐서들을 하나의 배치 텐서로 묶음\n    return torch.stack(padded_tensors, dim=0)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:50.906297Z","iopub.execute_input":"2025-08-12T05:47:50.907294Z","iopub.status.idle":"2025-08-12T05:47:50.912243Z","shell.execute_reply.started":"2025-08-12T05:47:50.907269Z","shell.execute_reply":"2025-08-12T05:47:50.911451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 이전에 작성했던 pad_to_max_len 함수가 있다고 가정합니다.\n# from your_utils import pad_to_max_len\n\n# 모든 시퀀스를 길이 700으로 패딩\npadded_X = pad_to_max_len(sequences_as_tensors, max_len=700, padding_value=0)\n\nprint(f\"패딩 완료된 텐서의 크기: {padded_X.shape}\")\n# 결과: torch.Size([8151, 700, 7])\n# (총 시퀀스 개수, 시퀀스 길이, 특성 수)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:50.913079Z","iopub.execute_input":"2025-08-12T05:47:50.913365Z","iopub.status.idle":"2025-08-12T05:47:51.168582Z","shell.execute_reply.started":"2025-08-12T05:47:50.913343Z","shell.execute_reply":"2025-08-12T05:47:51.167905Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"target_column = 'sequence_type'\n\n# y 텐서 생성 함수\ndef create_y_tensor(df, target_col):\n    # 'sequence_id'별로 그룹화\n    grouped = df.groupby('sequence_id')\n    \n    y_list = []\n    \n    # 1. 타겟 레이블을 숫자로 인코딩 (이진 분류)\n    # 'Target' -> 1, 'Non-target' -> 0으로 매핑\n    target_to_id = {'Non-Target': 0, 'Target': 1}\n    print(f\"타겟 인코딩 맵: {target_to_id}\")\n\n    # 2. 각 시퀀스(그룹)의 타겟 레이블 추출\n    for _, group_df in grouped:\n        # 시퀀스 내 모든 행의 타겟 레이블이 동일하다고 가정하고 첫 번째 값을 사용\n        sequence_type = group_df.iloc[0][target_col]\n        y_label = target_to_id[sequence_type]\n        y_list.append(y_label)\n\n    # 파이토치 텐서로 변환\n    # 'long' 타입은 분류 레이블에 사용됨\n    return torch.tensor(y_list, dtype=torch.long)\n\n\n# y 텐서 생성 및 확인\ny_tensor = create_y_tensor(train_df, target_column)\nprint(f\"y 텐서의 크기: {y_tensor.shape}\")\nprint(f\"y 텐서: {y_tensor}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:51.169459Z","iopub.execute_input":"2025-08-12T05:47:51.169749Z","iopub.status.idle":"2025-08-12T05:47:53.667709Z","shell.execute_reply.started":"2025-08-12T05:47:51.169723Z","shell.execute_reply":"2025-08-12T05:47:53.666957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch.nn as nn\n\nclass LSTMClassifier(nn.Module):\n    def __init__(self, input_size, hidden_size, num_layers, num_classes):\n        super(LSTMClassifier, self).__init__()\n        self.hidden_size = hidden_size\n        self.num_layers = num_layers\n        \n        # LSTM 레이어 정의\n        # batch_first=True는 입력 텐서의 형태가 (배치 크기, 시퀀스 길이, 특성 수)임을 의미합니다.\n        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True)\n        \n        # fully connected 레이어 정의\n        self.fc = nn.Linear(hidden_size, num_classes)\n    \n    def forward(self, x):\n        # h0와 c0는 초기 hidden state와 cell state입니다.\n        h0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)\n        c0 = torch.zeros(self.num_layers, x.size(0), self.hidden_size).to(x.device)\n        \n        # LSTM 레이어를 통과시킵니다.\n        # out: 모든 time_step의 output, _ : 최종 hidden state와 cell state\n        out, _ = self.lstm(x, (h0, c0))  \n        \n        # 마지막 time_step의 output만 사용합니다.\n        # out[:, -1, :]은 (배치 크기, hidden_size) 형태가 됩니다.\n        out = self.fc(out[:, -1, :])\n        return out","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:53.670815Z","iopub.execute_input":"2025-08-12T05:47:53.671108Z","iopub.status.idle":"2025-08-12T05:47:53.677991Z","shell.execute_reply.started":"2025-08-12T05:47:53.671083Z","shell.execute_reply":"2025-08-12T05:47:53.677097Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 모델 초기화\n# input_size = 332 (패딩된 텐서의 마지막 차원 크기)\ninput_size = 332\nhidden_size = 128  # 원하는 은닉 상태 크기로 설정\nnum_layers = 2     # 원하는 레이어 수로 설정\nnum_classes = 2   # 11가지 동작 분류\n\nmodel = LSTMClassifier(input_size, hidden_size, num_layers, num_classes)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:53.678763Z","iopub.execute_input":"2025-08-12T05:47:53.678999Z","iopub.status.idle":"2025-08-12T05:47:53.704024Z","shell.execute_reply.started":"2025-08-12T05:47:53.678980Z","shell.execute_reply":"2025-08-12T05:47:53.703160Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"from torch.utils.data import TensorDataset, DataLoader\nfrom sklearn.metrics import accuracy_score\n\ndataset = TensorDataset(padded_X, y_tensor)\nbatch_size = 64\ndataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True)\n\n# 손실함수 정의\ncriterion = nn.CrossEntropyLoss()\n\n# 최적화 도구 정의 (Adam이 일반적으로 좋은 성능을 보여줌)\noptimizer = torch.optim.Adam(model.parameters(), lr=0.0001)\n\nnum_epochs = 5\n\nfor epoch in range(num_epochs):\n    model.train()  # 모델을 훈련 모드로 설정\n    \n    total_loss = 0\n    y_true_list = []\n    y_pred_list = []\n    \n    for inputs, labels in dataloader:\n        optimizer.zero_grad()\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n        \n        loss.backward()\n        optimizer.step()\n        \n        total_loss += loss.item()\n        \n        # 예측값 계산\n        _, predicted = torch.max(outputs.data, 1)\n        y_true_list.extend(labels.cpu().numpy())\n        y_pred_list.extend(predicted.cpu().numpy())\n\n    # 한 epoch 종료 후 손실 및 정확도 출력\n    avg_loss = total_loss / len(dataloader)\n    accuracy = accuracy_score(y_true_list, y_pred_list)\n    print(f'Epoch [{epoch+1}/{num_epochs}], Avg Loss: {avg_loss:.4f}, Accuracy: {accuracy:.4f}')","metadata":{}},{"cell_type":"markdown","source":"# 예시 코드\n\n\n# inputs: 센서 데이터 텐서 (X)\n# labels: 정답 제스처 텐서 (y)\n\nfor inputs, labels in dataloader:\n    optimizer.zero_grad()\n    outputs = model(inputs) # 모델이 inputs를 보고 예측 (X)\n    loss = criterion(outputs, labels) # 모델의 예측값과 실제 정답을 비교하여 손실 계산 (y)\n    loss.backward()\n    optimizer.step()","metadata":{}},{"cell_type":"code","source":"# sequence_counter 개수의 \ngrouped_train_df[['sequence_counter']].max().describe()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:53.704956Z","iopub.execute_input":"2025-08-12T05:47:53.705195Z","iopub.status.idle":"2025-08-12T05:47:53.722651Z","shell.execute_reply.started":"2025-08-12T05:47:53.705177Z","shell.execute_reply":"2025-08-12T05:47:53.721935Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"seq_len = grouped_train_df[['sequence_counter']].max() + 1\nseq_len","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:53.723396Z","iopub.execute_input":"2025-08-12T05:47:53.723620Z","iopub.status.idle":"2025-08-12T05:47:53.735946Z","shell.execute_reply.started":"2025-08-12T05:47:53.723601Z","shell.execute_reply":"2025-08-12T05:47:53.735102Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 95%\nseq_len.quantile(0.95)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T05:47:53.736782Z","iopub.execute_input":"2025-08-12T05:47:53.737096Z","iopub.status.idle":"2025-08-12T05:47:53.743900Z","shell.execute_reply.started":"2025-08-12T05:47:53.737069Z","shell.execute_reply":"2025-08-12T05:47:53.743205Z"}},"outputs":[],"execution_count":null}]}