{"metadata":{"kernelspec":{"display_name":"kgl_py311","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.11.9"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":102335,"databundleVersionId":12518947,"sourceType":"competition"}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"0c85e0b1","cell_type":"code","source":"# general library\nimport datetime\nimport numpy as np\nimport os\nimport pandas as pd\nimport polars as pl\nimport random\nfrom tqdm.notebook import tqdm\n\n# sklearn\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import StratifiedKFold, StratifiedShuffleSplit\nfrom sklearn.metrics import f1_score, classification_report\n\n# ML argorithm\nimport lightgbm as lgb\n\n# torch\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, Dataset, random_split\n\n# Matplotlib\nimport matplotlib.pyplot as plt\n%matplotlib inline\n#import japanize_matplotlib\n\n# warnings\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# Import evaluation API\nimport kaggle_evaluation.cmi_inference_server","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.044816Z","iopub.execute_input":"2025-06-24T01:51:10.045681Z","iopub.status.idle":"2025-06-24T01:51:10.053119Z","shell.execute_reply.started":"2025-06-24T01:51:10.045646Z","shell.execute_reply":"2025-06-24T01:51:10.052249Z"}},"outputs":[],"execution_count":null},{"id":"0e4a8b9c","cell_type":"code","source":"start = datetime.datetime.now()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.054441Z","iopub.execute_input":"2025-06-24T01:51:10.054734Z","iopub.status.idle":"2025-06-24T01:51:10.070975Z","shell.execute_reply.started":"2025-06-24T01:51:10.054719Z","shell.execute_reply":"2025-06-24T01:51:10.070252Z"}},"outputs":[],"execution_count":null},{"id":"ef2ce37b","cell_type":"markdown","source":"## My funcition","metadata":{}},{"id":"04edd0fe","cell_type":"code","source":"def show_df(df, n_head=3):\n    print(df.shape)\n    display(df.head(n_head))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.071753Z","iopub.execute_input":"2025-06-24T01:51:10.072002Z","iopub.status.idle":"2025-06-24T01:51:10.086654Z","shell.execute_reply.started":"2025-06-24T01:51:10.071980Z","shell.execute_reply":"2025-06-24T01:51:10.086018Z"}},"outputs":[],"execution_count":null},{"id":"73a9ff0a","cell_type":"markdown","source":"# Constant","metadata":{}},{"id":"fd1fda93","cell_type":"code","source":"# Target\nBFRB_GESTURE = [\n\t'Above ear - pull hair', 'Forehead - pull hairline', 'Forehead - scratch', 'Eyebrow - pull hair', \n\t'Eyelash - pull hair', 'Neck - pinch skin', 'Neck - scratch', 'Cheek - pinch skin',\n]\nNON_BFRB_GESTURE = [\n\t'Drink from bottle/cup', 'Glasses on/off', 'Pull air toward your face', 'Pinch knee/leg skin', 'Scratch knee/leg skin', \n\t'Write name on leg', 'Text on phone', 'Feel around in tray and pull out an object', 'Write name in air', 'Wave hello', \n]\nGESTURE = BFRB_GESTURE + NON_BFRB_GESTURE\nprint(len(BFRB_GESTURE), len(NON_BFRB_GESTURE), len(GESTURE))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.087457Z","iopub.execute_input":"2025-06-24T01:51:10.087707Z","iopub.status.idle":"2025-06-24T01:51:10.102500Z","shell.execute_reply.started":"2025-06-24T01:51:10.087685Z","shell.execute_reply":"2025-06-24T01:51:10.101858Z"}},"outputs":[],"execution_count":null},{"id":"3e08a015","cell_type":"code","source":"# Feature\nFEATURES_ACC = ['acc_x', 'acc_y', 'acc_z']\nFEATURES = FEATURES_ACC\nprint(len(FEATURES_ACC), len(FEATURES))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.104151Z","iopub.execute_input":"2025-06-24T01:51:10.104338Z","iopub.status.idle":"2025-06-24T01:51:10.121708Z","shell.execute_reply.started":"2025-06-24T01:51:10.104324Z","shell.execute_reply":"2025-06-24T01:51:10.121074Z"}},"outputs":[],"execution_count":null},{"id":"a7e1bef3","cell_type":"code","source":"# data path\n#PATH = '../ignore_dir/input/cmi-detect-behavior-with-sensor-data' \nPATH = '/kaggle/input/cmi-detect-behavior-with-sensor-data' # Kaggle用","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.122561Z","iopub.execute_input":"2025-06-24T01:51:10.122777Z","iopub.status.idle":"2025-06-24T01:51:10.137825Z","shell.execute_reply.started":"2025-06-24T01:51:10.122762Z","shell.execute_reply":"2025-06-24T01:51:10.137259Z"}},"outputs":[],"execution_count":null},{"id":"e21ee9e9","cell_type":"code","source":"VAL_SIZE = 0.25 # 訓練データと検証データを分割するときの割合\nBATCH_SIZE = 32 # 64 # バッチサイズ\nLR = 0.0001 # 学習率\nN_HIDDEN = 32\nNUM_EPOCHS = 30 #80 # エポック数\n\nMAX_SEQ_LEN = 128 # シーケンス長","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.138547Z","iopub.execute_input":"2025-06-24T01:51:10.138770Z","iopub.status.idle":"2025-06-24T01:51:10.152765Z","shell.execute_reply.started":"2025-06-24T01:51:10.138755Z","shell.execute_reply":"2025-06-24T01:51:10.152206Z"}},"outputs":[],"execution_count":null},{"id":"38dc29e8","cell_type":"code","source":"# Check device\ndevice = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\nprint(device)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.153448Z","iopub.execute_input":"2025-06-24T01:51:10.153677Z","iopub.status.idle":"2025-06-24T01:51:10.167920Z","shell.execute_reply.started":"2025-06-24T01:51:10.153654Z","shell.execute_reply":"2025-06-24T01:51:10.167244Z"}},"outputs":[],"execution_count":null},{"id":"365a9717","cell_type":"markdown","source":"# Seed","metadata":{}},{"id":"bec2f96f","cell_type":"code","source":"SEED = 123\ndef seed_everything(seed=42):\n    random.seed(seed)\n    #np.random.seed(seed)\n    np.random.RandomState(seed)\n    # os.environ['PYTHONHASHSEED'] = str(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.Generator().manual_seed(seed)\n    torch.backends.cudnn.daterministic = True\n    torch.use_deterministic_algorithms = True\n    # torch.backends.cudnn.benchmark = False\nseed_everything(SEED)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.168643Z","iopub.execute_input":"2025-06-24T01:51:10.168836Z","iopub.status.idle":"2025-06-24T01:51:10.186196Z","shell.execute_reply.started":"2025-06-24T01:51:10.168814Z","shell.execute_reply":"2025-06-24T01:51:10.185653Z"}},"outputs":[],"execution_count":null},{"id":"d26421ad","cell_type":"markdown","source":"## Load data","metadata":{}},{"id":"f3a91491","cell_type":"code","source":"train_df = pd.read_csv(f\"{PATH}/train.csv\")\ntest_df = pd.read_csv(f\"{PATH}/test.csv\")\ntrain_demographics_df = pd.read_csv(f\"{PATH}/train_demographics.csv\")\ntest_demographics_df = pd.read_csv(f\"{PATH}/test_demographics.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:10.186965Z","iopub.execute_input":"2025-06-24T01:51:10.187178Z","iopub.status.idle":"2025-06-24T01:51:29.880940Z","shell.execute_reply.started":"2025-06-24T01:51:10.187153Z","shell.execute_reply":"2025-06-24T01:51:29.880402Z"}},"outputs":[],"execution_count":null},{"id":"6070e793","cell_type":"code","source":"show_df(train_df)\nshow_df(test_df)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:29.881597Z","iopub.execute_input":"2025-06-24T01:51:29.881790Z","iopub.status.idle":"2025-06-24T01:51:29.912394Z","shell.execute_reply.started":"2025-06-24T01:51:29.881774Z","shell.execute_reply":"2025-06-24T01:51:29.911818Z"}},"outputs":[],"execution_count":null},{"id":"3cc5eb2e","cell_type":"code","source":"#train_df[:1000].to_csv('../ignore_dir/preprocess/train_df_1000.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:29.913205Z","iopub.execute_input":"2025-06-24T01:51:29.913502Z","iopub.status.idle":"2025-06-24T01:51:29.917007Z","shell.execute_reply.started":"2025-06-24T01:51:29.913478Z","shell.execute_reply":"2025-06-24T01:51:29.916413Z"}},"outputs":[],"execution_count":null},{"id":"6573b762","cell_type":"markdown","source":"# Preprocess","metadata":{}},{"id":"c7810f2f","cell_type":"code","source":"def filling(df):\n\t\"\"\"\n\tfill NA\n\t\"\"\"\n\treturn df.ffill().bfill().fillna(0)\n\ndef gesture2numeric(df, gesture):\n\t\"\"\"\n\tConvert gesture labels to numeric values.\n\n\tParameters:\n\t\tdf (pd.DataFrame): DataFrame containing gesture labels.\n\t\tgesture (list): List of gesture labels.\n\n\tReturns:\n\t\tpd.DataFrame: DataFrame with an additional column for numeric gesture labels.\n\t\tdict: Dictionary mapping gesture labels to numeric values.\t  \n\t\"\"\"\n\tdic_gesture = {gesture: idx for idx, gesture in enumerate(GESTURE)}\n\tdf['gesture_numeric'] = df['gesture'].map(dic_gesture)\n\n\treturn df, dic_gesture\n\ndef data_shaping(df, y_boolean):\n\t\"\"\"\n\tデータの整形を行う関数:\n\t\tsubjectとsequence_idでグループ化\n\t\t各グループの特徴量FEATURESを抽出し、numpy配列に変換（X_ret）\n\t\t各グループのgesture_numericとgestureを抽出\n\t\tgesture_numericが一意でない場合は-1を設定し、gestureを'error'とする(y_ret, ystr_ret)\n\t\t各グループのsubjectとsequence_idをキーとしてmeta_retに格納\n\n\tParameters:\n\t\tdf (pd.DataFrame): 入力データフレーム\n\n\tReturns:\n\t\tX_ret (list): 各グループの特徴量を格納したリスト\n\t\t\tリストの要素はnumpy配列(n_feature(in_channel), sequence_length)\n\t\tret (list): 各グループのgesture_numericを格納したリスト\n\t\t\tグループ内に複数のgesture_numが存在する場合は-1を設定\n\t\tystr_ret (list)各グループのgestureを格納したリスト\n\t\t\tグループ内に複数のgestureが存在する場合は'error'を設定\n\t\tmeta_ret (list): 各グループのsubjectとsequence_idを格納したリスト\n\n\t\"\"\"\n\tX_ret = []\n\ty_ret = []\n\tystr_ret = []\n\tmeta_ret =[]\n\n\tdf_gb = df.groupby(['subject', 'sequence_id'])\n\tfor keys, data in tqdm(df_gb):\n\t\tdata_ft = np.array(data[FEATURES].values, dtype=np.float32).reshape(len(FEATURES), -1) # reshape to (n_features, sequence length)\n\t\tX_ret.append(data_ft)\n\t\tmeta_ret.append(keys)\n\t\t\n\t\tif y_boolean:\n\t\t\tgesture_num = data['gesture_numeric']\n\t\t\tgesture_str = data['gesture']\n\t\t\tif gesture_num.nunique() == 1:\n\t\t\t\ttarget_num = gesture_num.iloc[0]\n\t\t\t\ttarget_str = gesture_str.iloc[0]\n\t\t\telse:\n\t\t\t\ttarget_num = -1\n\t\t\t\ttarget_str = 'error'\n\t\t\ty_ret.append(target_num)\n\t\t\tystr_ret.append(target_str)\n\t\t\n\treturn X_ret, y_ret, ystr_ret, meta_ret\n\n\ndef trim_sequence(X):\n\t\"\"\"\n\tシーケンス長を揃える関数\n\n\tParameters:\n\t\tX(list): 説明変数のリスト\n\t \t\t各要素はシーケンス長が可変のnumpy配列\n\n\tReturns:\n\t\tX_ret (list): 各要素のシーケンス長を揃えたnumpy配列のリスト\n\t\"\"\"\n\tX_ret = []\n\tfor x in tqdm(X):\n\t\t# シーケンス長がMAX_SEQ_LEN以下の場合はパディングを行い、MAX_SEQ_LENを超える場合は切り捨て\n\t\tif x.shape[1] <= MAX_SEQ_LEN:\n\t\t\tpad = np.zeros((len(FEATURES), MAX_SEQ_LEN - x.shape[1]), dtype=np.float32)\n\t\t\tx_padded = np.concatenate([x, pad], axis=1)\n\t\t\tif x_padded.shape[1] != MAX_SEQ_LEN:\n\t\t\t\tprint(f\"Warning: うまくパディングできてないかも→{x_padded.shape[1]}\")\n\t\telse:\n\t\t\tx_padded = x[:, :MAX_SEQ_LEN]\n\t\tX_ret.append(x_padded)\n\treturn X_ret","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:29.917687Z","iopub.execute_input":"2025-06-24T01:51:29.917877Z","iopub.status.idle":"2025-06-24T01:51:29.931227Z","shell.execute_reply.started":"2025-06-24T01:51:29.917862Z","shell.execute_reply":"2025-06-24T01:51:29.930523Z"}},"outputs":[],"execution_count":null},{"id":"79bcf1fb","cell_type":"code","source":"# 欠損値補完\ntrain_df = filling(train_df)\n# ラベルエンコーディング\ntrain_df, dic_gesture = gesture2numeric(train_df, GESTURE) # 訓練のみ\n# データ整形\nX_train, y_train, ystr_train, meta_train = data_shaping(train_df, y_boolean=True)\nprint(f\"X_train: {len(X_train)}, y_train: {len(y_train)}, ystr_train: {len(ystr_train)}, meta_tr: {len(meta_train)}\")\n# シーケンス長を揃える\nX_train_trim = trim_sequence(X_train)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:29.933482Z","iopub.execute_input":"2025-06-24T01:51:29.933675Z","iopub.status.idle":"2025-06-24T01:51:48.275254Z","shell.execute_reply.started":"2025-06-24T01:51:29.933661Z","shell.execute_reply":"2025-06-24T01:51:48.274538Z"}},"outputs":[],"execution_count":null},{"id":"43f5d317","cell_type":"markdown","source":"# Check data","metadata":{}},{"id":"b18a19de","cell_type":"code","source":"# シーケンス長について確認\ndef check_sequence_length(lis_X):\n\tmin, max = lis_X[0].shape[1], lis_X[0].shape[1]\n\tfor record in tqdm(lis_X):\n\t\tif record.shape[1] < min: min = record.shape[1]\n\t\tif record.shape[1] > max: max = record.shape[1]\n\tprint(f\"sequence length >>> min:{min}, max:{max}\")\ncheck_sequence_length(X_train)\ncheck_sequence_length(X_train_trim)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:48.276084Z","iopub.execute_input":"2025-06-24T01:51:48.276311Z","iopub.status.idle":"2025-06-24T01:51:48.309882Z","shell.execute_reply.started":"2025-06-24T01:51:48.276296Z","shell.execute_reply":"2025-06-24T01:51:48.309201Z"}},"outputs":[],"execution_count":null},{"id":"5f732e95","cell_type":"code","source":"# ターゲットの頻度分布を可視化\ncount_BFRB = []\ncount_NON_BFRB = []\ncount_error = []\nn_sample = 0\nfor g in GESTURE+['error']:\n\tc = ystr_train.count(g)\n\tn_sample += c\n\tif g in BFRB_GESTURE:\n\t\tcount_BFRB.append(c)\n\t\tcount_NON_BFRB.append(0)\n\t\tcount_error.append(0)\n\telif g in NON_BFRB_GESTURE:\n\t\tcount_BFRB.append(0)\n\t\tcount_NON_BFRB.append(c)\n\t\tcount_error.append(0)\n\telse:\n\t\tcount_BFRB.append(0)\n\t\tcount_NON_BFRB.append(0)\n\t\tcount_error.append(c)\nplt.figure()\nplt.bar(GESTURE+['error'], count_BFRB, label='BFRB')\nplt.bar(GESTURE+['error'], count_NON_BFRB, label='Non-BFRB', bottom=count_BFRB)\nplt.bar(GESTURE+['error'], count_error, label='error', bottom=np.array(count_BFRB)+np.array(count_NON_BFRB))\nplt.xticks(rotation=90)\nplt.title(f\"Gesture's frequency (samples:{n_sample})\")\nplt.ylim(0, 800)\nplt.ylabel(\"frequency\")\nplt.yticks(np.arange(0, 801, 200))\nplt.grid(axis='y')\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:48.310723Z","iopub.execute_input":"2025-06-24T01:51:48.310982Z","iopub.status.idle":"2025-06-24T01:51:48.583845Z","shell.execute_reply.started":"2025-06-24T01:51:48.310961Z","shell.execute_reply":"2025-06-24T01:51:48.583210Z"}},"outputs":[],"execution_count":null},{"id":"9e5ac663","cell_type":"code","source":"# BFRBとNON-BFRBの割合を確認\nsum_BFRB = sum(count_BFRB)\nsum_NON_BFRB = sum(count_NON_BFRB)\nsum_error = sum(count_error)\n\nplt.figure()\nplt.pie([sum_BFRB, sum_NON_BFRB, sum_error], startangle=90, counterclock=False,\n        labels=['BFRB', 'NON-BFRB', 'error'], labeldistance=None,\n        autopct=\"%.1f%%\", pctdistance=0.5, )\nplt.legend(loc='upper right')\nplt.title(f'Percentage of gesture (samples:{sum_BFRB + sum_NON_BFRB})')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:48.584715Z","iopub.execute_input":"2025-06-24T01:51:48.584919Z","iopub.status.idle":"2025-06-24T01:51:48.681669Z","shell.execute_reply.started":"2025-06-24T01:51:48.584904Z","shell.execute_reply":"2025-06-24T01:51:48.680972Z"}},"outputs":[],"execution_count":null},{"id":"0bbeedf1","cell_type":"markdown","source":"# Split tr/val\n- subject(被験者)毎に分割する\n- そうするとデータ数にばらつきが生じるが（subjectごとにデータ数が違うため）、いったんそこは目をつぶる","metadata":{}},{"id":"89623919","cell_type":"code","source":"# subject列のユニーク要素とその数を取得\nunique_subject = train_df['subject'].unique().tolist()\nnunique_subject = train_df['subject'].nunique()\n\n# random.sampleを使って、VAL_SIZEの割合でval_subjectを選ぶ。残りをtr_subjectとする。\nval_subject = random.sample(unique_subject, int(nunique_subject*VAL_SIZE))\ntr_subject = [s for s in unique_subject if s not in val_subject]\nprint(f\"tr subject length: {len(tr_subject)} ex.{tr_subject[:3]} ... {tr_subject[-3:]}\")\nprint(f\"val subject length: {len(val_subject)} ex. {val_subject[:3]} ... {val_subject[-3:]}\")\n\n# インデックスを取得\nidx_tr  = [i for i in range(len(meta_train)) if meta_train[i][0] in tr_subject]\nidx_val = [i for i in range(len(meta_train)) if meta_train[i][0] in val_subject]\nprint(len(idx_tr), len(idx_val))\n\n# インデックスを使ってデータを抽出する関数\ndef extract(lis, idx):\n\treturn [lis[i] for i in range(len(lis)) if i in idx]\n\nX_tr = extract(X_train_trim, idx_tr)\nX_val = extract(X_train_trim, idx_val)\ny_tr = extract(y_train, idx_tr)\ny_val = extract(y_train, idx_val)\nystr_tr = extract(ystr_train, idx_tr)\nystr_val = extract(ystr_train, idx_val)\nmeta_tr = extract(meta_train, idx_tr)\nmeta_val = extract(meta_train, idx_val)\nprint(len(X_tr), len(y_tr), len(ystr_tr), len(meta_tr))\nprint(len(X_val), len(y_val), len(ystr_val), len(meta_val))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:48.682417Z","iopub.execute_input":"2025-06-24T01:51:48.682623Z","iopub.status.idle":"2025-06-24T01:51:50.589947Z","shell.execute_reply.started":"2025-06-24T01:51:48.682608Z","shell.execute_reply":"2025-06-24T01:51:50.589191Z"}},"outputs":[],"execution_count":null},{"id":"0ff66834","cell_type":"markdown","source":"# Data loader","metadata":{}},{"id":"ab71d370","cell_type":"code","source":"# データセットクラスを定義\nclass TimeSeriesDataset(Dataset):\n    def __init__(self, data, labels):\n        self.data = [torch.tensor(d, dtype=torch.float32) for d in data]\n        self.labels = torch.tensor(labels, dtype=torch.long)\n\n    def __len__(self):\n        return len(self.labels)\n\n    def __getitem__(self, idx):\n        return self.data[idx], self.labels[idx]\n    \n# データセット作成\ntr_dataset = TimeSeriesDataset(X_tr, y_tr)\nprint(len(tr_dataset))\n\nval_dataset = TimeSeriesDataset(X_val, y_val)\nprint(len(val_dataset))\n\n# データローダーを作成\ntr_loader = DataLoader(tr_dataset, batch_size=BATCH_SIZE, shuffle=True)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:50.590767Z","iopub.execute_input":"2025-06-24T01:51:50.591007Z","iopub.status.idle":"2025-06-24T01:51:50.666504Z","shell.execute_reply.started":"2025-06-24T01:51:50.590990Z","shell.execute_reply":"2025-06-24T01:51:50.665785Z"}},"outputs":[],"execution_count":null},{"id":"6db93766","cell_type":"code","source":"for inputs, labels in val_loader:\n\tbreak\nprint(type(inputs))\nprint(len(inputs))\nprint(inputs)\nprint(inputs[0].shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:50.667872Z","iopub.execute_input":"2025-06-24T01:51:50.668119Z","iopub.status.idle":"2025-06-24T01:51:50.674996Z","shell.execute_reply.started":"2025-06-24T01:51:50.668103Z","shell.execute_reply":"2025-06-24T01:51:50.674433Z"}},"outputs":[],"execution_count":null},{"id":"9d87358b","cell_type":"markdown","source":"# Get parameters in this task","metadata":{}},{"id":"481410ba","cell_type":"code","source":"in_channel = len(FEATURES) # 入力チャネル数\nsequence_length = MAX_SEQ_LEN # シーケンス長\nn_output = len(GESTURE) # 出力クラス数\nprint(f'in_channel: {in_channel}, sequence_length: {sequence_length}, n_output: {n_output}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:50.675658Z","iopub.execute_input":"2025-06-24T01:51:50.675871Z","iopub.status.idle":"2025-06-24T01:51:50.688509Z","shell.execute_reply.started":"2025-06-24T01:51:50.675847Z","shell.execute_reply":"2025-06-24T01:51:50.687818Z"}},"outputs":[],"execution_count":null},{"id":"9270d3a2","cell_type":"markdown","source":"# Define model","metadata":{}},{"id":"b3b41058","cell_type":"code","source":"class CNN(nn.Module):\n    def __init__(self, in_channels, sequence_length, n_hidden, n_output):\n        super().__init__()\n        self.conv1 = nn.Conv1d(in_channels=in_channels, out_channels=16, kernel_size=3, stride=1, padding=1)\n        self.conv2 = nn.Conv1d(in_channels=16, out_channels=32, kernel_size=3, stride=1, padding=1)\n        self.relu = nn.ReLU(inplace=True)\n        self.maxpool = nn.MaxPool1d(kernel_size=2, stride=2)\n        self.flatten = nn.Flatten()\n        self.l1 = nn.Linear(32*(sequence_length//2), n_hidden)\n        self.l2 = nn.Linear(n_hidden, n_output)\n        \n        self.features = nn.Sequential(\n            self.conv1,\n            self.relu,\n            self.conv2,\n            self.relu,\n            self.maxpool\n        )\n        \n        self.classifier = nn.Sequential(\n            self.l1,\n            self.relu,\n            self.l2\n        )\n\n    def forward(self, x):\n        x1 = self.features(x)\n        x2 = self.flatten(x1)\n        x3 = self.classifier(x2)\n        return x3 ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:50.689233Z","iopub.execute_input":"2025-06-24T01:51:50.689455Z","iopub.status.idle":"2025-06-24T01:51:50.703266Z","shell.execute_reply.started":"2025-06-24T01:51:50.689432Z","shell.execute_reply":"2025-06-24T01:51:50.702703Z"}},"outputs":[],"execution_count":null},{"id":"92793354","cell_type":"code","source":"# インスタンス作成\nnet = CNN(in_channel, sequence_length, N_HIDDEN, n_output).to(device)\n\n# 損失関数\ncriterion = nn.CrossEntropyLoss() # 交差エントロピー誤差\n\n# 最適化関数と学習率\noptimizer = optim.Adam(net.parameters(), lr=LR)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:50.703957Z","iopub.execute_input":"2025-06-24T01:51:50.704186Z","iopub.status.idle":"2025-06-24T01:51:50.725111Z","shell.execute_reply.started":"2025-06-24T01:51:50.704166Z","shell.execute_reply":"2025-06-24T01:51:50.724627Z"}},"outputs":[],"execution_count":null},{"id":"572adb47","cell_type":"markdown","source":"# Train","metadata":{}},{"id":"ed8b21d5","cell_type":"code","source":"history = np.zeros((0, 7)) # (繰り返し数、訓練損失、訓練精度、訓練F1スコア、検証損失、検証精度、検証F1スコア)\n\nfor epoch in tqdm(range(NUM_EPOCHS)):\n\t# ===== 訓練モード =====\n\tnet.train()\n\ttr_running_loss = 0.0 # 累積損失を格納\n\ttr_preds = []\n\ttr_labels = []\n\tfor inputs, labels in tr_loader: #tqdm(tr_loader):\n\t\t# データをデバイスに転送\n\t\tinputs = inputs.to(device)\n\t\tlabels = labels.to(device)\n\n\t\t# 勾配を初期化\n\t\toptimizer.zero_grad()\n\n\t\t# 順伝播\n\t\toutputs = net(inputs)\n\n\t\t# 損失計算\n\t\tloss = criterion(outputs, labels)\n\n\t\t# 逆伝播\n\t\tloss.backward()\n\n\t\t# パラメータ更新\n\t\toptimizer.step()\n\n\t\t# ロスの蓄積\n\t\ttr_running_loss += loss.item()\n\t\t_, predicted = torch.max(outputs, 1) # 予測値を取得\n\t\ttr_preds.extend(predicted.cpu().numpy())\n\t\ttr_labels.extend(labels.cpu().numpy())\n\n\n\t# ===== 検証モード =====\n\tnet.eval()\n\tval_running_loss = 0.0\n\tval_preds = []\n\tval_labels = []\n\twith torch.no_grad():\n\t\tfor inputs, labels in val_loader: #tqdm(val_loader): \n\t\t\t# データをデバイスに結合\n\t\t\tinputs = inputs.to(device)\n\t\t\tlabels = labels.to(device)\n\n\t\t\t# 順伝播\n\t\t\toutputs = net(inputs)\n\n\t\t\t# 損失計算\n\t\t\tloss = criterion(outputs, labels)\n\n\t\t\t# 予測値を計算\n\t\t\tval_running_loss += loss.item()\n\t\t\t_, predicted = torch.max(outputs, 1)\n\t\t\tval_preds.extend(predicted.cpu().numpy())\n\t\t\tval_labels.extend(labels.cpu().numpy())\n\n\t# 損失や評価指標の計算\n\ttr_loss = tr_running_loss / len(tr_loader)\n\tval_loss = val_running_loss / len(val_loader)\n\ttr_accuracy  = (np.array(tr_preds) == np.array(tr_labels)).mean()\n\tval_accuracy = (np.array(val_preds) == np.array(val_labels)).mean()\n\ttr_f1_macro  = f1_score(tr_labels, tr_preds, average='macro')\n\tval_f1_macro = f1_score(val_labels, val_preds, average='macro')\n\tprint(f'Epoch:{epoch+1}/{NUM_EPOCHS}')\n\tprint(f'tr_loss :{tr_loss:.6f}, tr_acc :{tr_accuracy:.6f}, tr_f1_macro :{tr_f1_macro:.6f}')\n\tprint(f'val_loss:{val_loss:.6f}, val_acc:{val_accuracy:.6f}, val_f1_macro:{val_f1_macro:.6f}')\n\n\t# historyに保存\n\thistory = np.vstack([history, [epoch+1, tr_loss, tr_accuracy, tr_f1_macro, val_loss, val_accuracy, val_f1_macro]])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:51:50.725965Z","iopub.execute_input":"2025-06-24T01:51:50.726155Z","iopub.status.idle":"2025-06-24T01:52:04.908658Z","shell.execute_reply.started":"2025-06-24T01:51:50.726141Z","shell.execute_reply":"2025-06-24T01:52:04.907780Z"}},"outputs":[],"execution_count":null},{"id":"8c8aef7f","cell_type":"markdown","source":"# Result","metadata":{}},{"id":"ebfd6137","cell_type":"code","source":"def show_learning_curve(history, ylabel, n_col_tr, n_col_val):\n\t\"\"\"\n\t学習曲線を描画する関数\n\n\tParameters\n\t------------------\n\thistory: np.array: (繰り返し数, 訓練損失, 訓練精度, 訓練F1, 検証損失, 検証精度, 検証F1)\n\t\t訓練結果が格納されたnumpy配列\n\tylabel: str\n\t\ty軸のラベル（タイトルにも使われる）\n\tn_col_train: int\n\t\t訓練データの列番号\n\tn_col_val: int\n\t\t検証データの列番号\n\n\tReturn\n\t------------------\n\tNone\n\n\tDisplay\n\t------------------\n\t学習曲線を表示\n\t\"\"\"\n\tticks_interval = 10 #num_epochs // 10\n\tnum_epochs = len(history)\n\n\tplt.figure(figsize=(8, 8))\n\tplt.plot(history[:, 0], history[:, n_col_tr], label='Train')\n\tplt.plot(history[:, 0], history[:, n_col_val], label='Validation')\n\tplt.xlabel('Epochs')\n\tplt.ylabel(ylabel)\n\tplt.title(f'Learning curve ({ylabel})')\n\tplt.legend()\n\tplt.grid()\n\tplt.show()\n\n\n# 検証損失と精度の確認\nprint(f'Val:init  - loss:{history[ 0,4]:.5f}, acc:{history[ 0,5]:.5f}, F1:{history[ 0,6]:.5f}') \nprint(f'Val:final - loss:{history[-1,4]:.5f}, acc:{history[-1,5]:.5f}, F1:{history[-1,6]:.5f}')\n\n# 学習曲線の表示\nshow_learning_curve(history, 'loss', 1, 4) # 損失\nshow_learning_curve(history, 'acc', 2, 5) # 精度\nshow_learning_curve(history, 'F1_macro', 3, 6) # F1_macro\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:52:04.909543Z","iopub.execute_input":"2025-06-24T01:52:04.909784Z","iopub.status.idle":"2025-06-24T01:52:05.430107Z","shell.execute_reply.started":"2025-06-24T01:52:04.909761Z","shell.execute_reply":"2025-06-24T01:52:05.429414Z"}},"outputs":[],"execution_count":null},{"id":"693518bf","cell_type":"markdown","source":"# Evaluation API","metadata":{}},{"id":"88e28d97","cell_type":"code","source":"def predict(sequence: pl.DataFrame, demographics: pl.DataFrame) -> str:\n\ttry:\n\t\t# pandasに変換\n\t\tdata = sequence.to_pandas()\n\n\t\t# データの前処理\n\t\tdata = filling(data)\n\t\tX_subm, _, _, meta_subm = data_shaping(data, y_boolean=False)\n\t\tX_subm_trim = trim_sequence(X_subm)\n\n\t\t# データローダーの作成\n\t\tsubm_dataset = TimeSeriesDataset(X_subm_trim, [0]*len(X_subm_trim))  # ラベルは不要なのでダミーを使用\n\t\tsubm_loader = DataLoader(subm_dataset, batch_size=BATCH_SIZE, shuffle=False)\n\n\t\t# 予測\n\t\tnet.eval()\n\t\twith torch.no_grad():\n\t\t\tfor inputs, _ in subm_loader:\n\t\t\t\tinputs = inputs.to(device)\n\t\t\t\toutputs = net(inputs)\n\t\t\t\t_, predicted = torch.max(outputs, 1)\n\t\t\t\tpredicted = predicted.cpu().numpy()\n\t\t\tpredicted_gesture = GESTURE[predicted[0]]\n\t\t\t\t\n\texcept:\n\t\tpredicted_gesture = 'Text on phone'\n\n\tprint(predicted_gesture)\n\treturn predicted_gesture\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:52:05.430811Z","iopub.execute_input":"2025-06-24T01:52:05.431016Z","iopub.status.idle":"2025-06-24T01:52:05.436593Z","shell.execute_reply.started":"2025-06-24T01:52:05.430996Z","shell.execute_reply":"2025-06-24T01:52:05.435864Z"}},"outputs":[],"execution_count":null},{"id":"83d9de12","cell_type":"code","source":"# Launch inference server\ninference_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            f'{PATH}/test.csv',\n            f'{PATH}/test_demographics.csv',\n\t\t\t# '/kaggle/input/cmi-detect-behavior-with-sensor-data/test.csv',\n\t\t\t# '/kaggle/input/cmi-detect-behavior-with-sensor-data/test_demographics.csv',\n        )\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:52:05.437268Z","iopub.execute_input":"2025-06-24T01:52:05.437461Z","iopub.status.idle":"2025-06-24T01:52:06.471798Z","shell.execute_reply.started":"2025-06-24T01:52:05.437446Z","shell.execute_reply":"2025-06-24T01:52:06.471224Z"}},"outputs":[],"execution_count":null},{"id":"51fe60e4","cell_type":"code","source":"end = datetime.datetime.now()\nprint(f\"Start time: \\t{start.strftime('%Y-%m-%d %H:%M:%S')}\")\nprint(f\"End time: \\t{end.strftime('%Y-%m-%d %H:%M:%S')}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-24T01:52:06.472510Z","iopub.execute_input":"2025-06-24T01:52:06.472801Z","iopub.status.idle":"2025-06-24T01:52:06.477402Z","shell.execute_reply.started":"2025-06-24T01:52:06.472779Z","shell.execute_reply":"2025-06-24T01:52:06.476726Z"}},"outputs":[],"execution_count":null},{"id":"5a0e3bfb","cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}