{"cells":[{"cell_type":"markdown","id":"cell-0","metadata":{},"source":"# CMI V8 - Improved Model with Full Dataset\n\n**Target: 87%+ Validation Accuracy**\n- 全データセット使用（8151シーケンス）\n- 高度な特徴量エンジニアリング\n- 最適化されたアンサンブル"},{"cell_type":"code","execution_count":null,"id":"cell-1","metadata":{},"outputs":[],"source":"import numpy as np\nimport pandas as pd\nimport pickle\nimport gc\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# ML imports\nimport lightgbm as lgb\nimport xgboost as xgb\nimport catboost as cb\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nfrom sklearn.model_selection import GroupKFold\nfrom scipy import signal, stats\nfrom scipy.fft import fft, fftfreq\n\nprint('Loading data...')\ntrain_df = pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/train.csv')\ntrain_demo = pd.read_csv('/kaggle/input/cmi-detect-behavior-with-sensor-data/train_demographics.csv')\n\n# Get labels\ngesture_labels = sorted(train_df['gesture'].unique())\nnum_classes = len(gesture_labels)\n\nprint(f'Classes: {num_classes}')\nprint(f'Total samples: {len(train_df)}')\nprint(f'Unique sequences: {train_df[\"sequence_id\"].nunique()}')"},{"cell_type":"code","execution_count":null,"id":"cell-2","metadata":{},"outputs":[],"source":"def remove_gravity_from_acc(df_seq, alpha=0.8):\n    \"\"\"重力除去による線形加速度の計算\"\"\"\n    acc_cols = ['acc_x', 'acc_y', 'acc_z']\n    if not all(col in df_seq.columns for col in acc_cols):\n        return None\n    \n    acc_data = df_seq[acc_cols].values\n    gravity = np.zeros_like(acc_data)\n    gravity[0] = acc_data[0]\n    \n    for i in range(1, len(acc_data)):\n        gravity[i] = alpha * gravity[i-1] + (1 - alpha) * acc_data[i]\n    \n    linear_acc = acc_data - gravity\n    return linear_acc\n\ndef quaternion_to_angular_velocity(df_seq):\n    \"\"\"クォータニオンから角速度を計算\"\"\"\n    quat_cols = ['rot_x', 'rot_y', 'rot_z', 'rot_w']\n    if not all(col in df_seq.columns for col in quat_cols):\n        return None\n    \n    q = df_seq[quat_cols].values\n    \n    # 正規化\n    q_norm = np.linalg.norm(q, axis=1, keepdims=True)\n    q = q / (q_norm + 1e-10)\n    \n    # 時間微分で角速度を推定\n    dq = np.gradient(q, axis=0)\n    \n    # 角速度ベクトル計算\n    w = np.zeros((len(q), 3))\n    for i in range(len(q)):\n        w[i, 0] = 2 * (q[i, 3] * dq[i, 0] - q[i, 0] * dq[i, 3] - q[i, 1] * dq[i, 2] + q[i, 2] * dq[i, 1])\n        w[i, 1] = 2 * (q[i, 3] * dq[i, 1] - q[i, 1] * dq[i, 3] - q[i, 2] * dq[i, 0] + q[i, 0] * dq[i, 2])\n        w[i, 2] = 2 * (q[i, 3] * dq[i, 2] - q[i, 2] * dq[i, 3] - q[i, 0] * dq[i, 1] + q[i, 1] * dq[i, 0])\n    \n    return w\n\ndef extract_frequency_features(signal_data, sampling_rate=50):\n    \"\"\"周波数領域特徴量の抽出\"\"\"\n    if len(signal_data) < 10:\n        return {}\n    \n    fft_values = np.abs(fft(signal_data))\n    freqs = fftfreq(len(signal_data), 1/sampling_rate)\n    \n    # 正の周波数のみ\n    pos_idx = freqs > 0\n    fft_values = fft_values[pos_idx]\n    freqs = freqs[pos_idx]\n    \n    features = {}\n    if len(fft_values) > 0:\n        features['fft_max'] = np.max(fft_values)\n        features['fft_mean'] = np.mean(fft_values)\n        features['fft_std'] = np.std(fft_values)\n        \n        # ピーク周波数\n        peak_idx = np.argmax(fft_values)\n        features['peak_freq'] = freqs[peak_idx] if len(freqs) > 0 else 0\n    \n    return features"},{"cell_type":"code","execution_count":null,"id":"cell-3","metadata":{},"outputs":[],"source":"def extract_comprehensive_features(df_seq):\n    \"\"\"包括的な特徴量抽出\"\"\"\n    features = {}\n    \n    # 基本統計量\n    for col in ['acc_x', 'acc_y', 'acc_z', 'rot_x', 'rot_y', 'rot_z', 'rot_w']:\n        if col in df_seq.columns:\n            values = df_seq[col].values\n            features[f'{col}_mean'] = np.mean(values)\n            features[f'{col}_std'] = np.std(values)\n            features[f'{col}_max'] = np.max(values)\n            features[f'{col}_min'] = np.min(values)\n            features[f'{col}_range'] = features[f'{col}_max'] - features[f'{col}_min']\n            features[f'{col}_skew'] = stats.skew(values)\n            features[f'{col}_kurtosis'] = stats.kurtosis(values)\n            \n            # 変化量統計\n            diff = np.diff(values)\n            if len(diff) > 0:\n                features[f'{col}_diff_mean'] = np.mean(diff)\n                features[f'{col}_diff_std'] = np.std(diff)\n    \n    # 重力除去加速度\n    linear_acc = remove_gravity_from_acc(df_seq)\n    if linear_acc is not None:\n        for i, axis in enumerate(['x', 'y', 'z']):\n            features[f'linear_acc_{axis}_mean'] = np.mean(linear_acc[:, i])\n            features[f'linear_acc_{axis}_std'] = np.std(linear_acc[:, i])\n            features[f'linear_acc_{axis}_max'] = np.max(np.abs(linear_acc[:, i]))\n    \n    # 角速度\n    angular_vel = quaternion_to_angular_velocity(df_seq)\n    if angular_vel is not None:\n        for i, axis in enumerate(['x', 'y', 'z']):\n            features[f'angular_vel_{axis}_mean'] = np.mean(angular_vel[:, i])\n            features[f'angular_vel_{axis}_std'] = np.std(angular_vel[:, i])\n            features[f'angular_vel_{axis}_max'] = np.max(np.abs(angular_vel[:, i]))\n    \n    # 加速度マグニチュード\n    if all(col in df_seq.columns for col in ['acc_x', 'acc_y', 'acc_z']):\n        acc_mag = np.sqrt(df_seq['acc_x']**2 + df_seq['acc_y']**2 + df_seq['acc_z']**2)\n        features['acc_mag_mean'] = np.mean(acc_mag)\n        features['acc_mag_std'] = np.std(acc_mag)\n        features['acc_mag_max'] = np.max(acc_mag)\n        \n        # ジャーク（加速度の変化率）\n        jerk = np.diff(acc_mag)\n        if len(jerk) > 0:\n            features['jerk_mean'] = np.mean(np.abs(jerk))\n            features['jerk_std'] = np.std(jerk)\n            features['jerk_max'] = np.max(np.abs(jerk))\n        \n        # 周波数特徴\n        freq_features = extract_frequency_features(acc_mag.values)\n        for key, value in freq_features.items():\n            features[f'acc_mag_{key}'] = value\n    \n    # 温度センサー\n    temp_cols = [f'thm_{i}' for i in range(1, 6)]\n    temp_cols = [col for col in temp_cols if col in df_seq.columns]\n    if temp_cols:\n        temp_data = df_seq[temp_cols].values\n        features['temp_mean'] = np.mean(temp_data)\n        features['temp_std'] = np.std(temp_data)\n        features['temp_range'] = np.max(temp_data) - np.min(temp_data)\n    \n    # ToFセンサー（次元削減）\n    tof_cols = [col for col in df_seq.columns if col.startswith('tof_')]\n    if tof_cols:\n        tof_data = df_seq[tof_cols].values\n        # 統計量で要約\n        features['tof_mean'] = np.mean(tof_data)\n        features['tof_std'] = np.std(tof_data)\n        features['tof_max'] = np.max(tof_data)\n        features['tof_min'] = np.min(tof_data)\n        \n        # 各フレームの統計量\n        features['tof_frame_mean_std'] = np.std(np.mean(tof_data, axis=1))\n        features['tof_frame_max_mean'] = np.mean(np.max(tof_data, axis=1))\n    \n    # シーケンス長\n    features['sequence_length'] = len(df_seq)\n    \n    return features"},{"cell_type":"code","execution_count":null,"id":"cell-4","metadata":{},"outputs":[],"source":"# 全データで特徴量抽出\nprint('Extracting features from all sequences...')\nX_features = []\ny_labels = []\ngroups = []\n\n# Label encoder\nle = LabelEncoder()\nle.fit(gesture_labels)\n\nsequence_ids = train_df['sequence_id'].unique()\ntotal_sequences = len(sequence_ids)\n\n# 全データを処理（バッチ処理で効率化）\nbatch_size = 1000\nfor batch_start in range(0, total_sequences, batch_size):\n    batch_end = min(batch_start + batch_size, total_sequences)\n    batch_seq_ids = sequence_ids[batch_start:batch_end]\n    \n    if batch_start % 2000 == 0:\n        print(f'Processing sequences {batch_start}/{total_sequences}...')\n    \n    for seq_id in batch_seq_ids:\n        seq_data = train_df[train_df['sequence_id'] == seq_id]\n        \n        # 特徴量抽出\n        features = extract_comprehensive_features(seq_data)\n        X_features.append(features)\n        \n        # ラベル\n        gesture = seq_data['gesture'].iloc[0]\n        label = le.transform([gesture])[0]\n        y_labels.append(label)\n        \n        # Subject ID（GroupKFold用）\n        subject_id = seq_data['subject_id'].iloc[0]\n        groups.append(subject_id)\n    \n    # メモリ解放\n    if batch_start % 2000 == 0:\n        gc.collect()\n\n# DataFrameに変換\nX_df = pd.DataFrame(X_features)\ny_array = np.array(y_labels)\ngroups_array = np.array(groups)\n\nprint(f'Feature shape: {X_df.shape}')\nprint(f'Number of features: {len(X_df.columns)}')\n\n# 欠損値を0で埋める\nX_df = X_df.fillna(0)"},{"cell_type":"code","execution_count":null,"id":"cell-5","metadata":{},"outputs":[],"source":"# GroupKFoldで交差検証\nprint('Training models with GroupKFold...')\ngkf = GroupKFold(n_splits=5)\n\n# 検証スコア保存用\nval_scores = []\nmodels = []\n\nfor fold, (train_idx, val_idx) in enumerate(gkf.split(X_df, y_array, groups_array)):\n    print(f'\\nFold {fold + 1}/5')\n    \n    X_train = X_df.iloc[train_idx]\n    X_val = X_df.iloc[val_idx]\n    y_train = y_array[train_idx]\n    y_val = y_array[val_idx]\n    \n    # LightGBM\n    lgb_params = {\n        'objective': 'multiclass',\n        'num_class': num_classes,\n        'metric': 'multi_logloss',\n        'boosting_type': 'gbdt',\n        'num_leaves': 127,\n        'learning_rate': 0.05,\n        'feature_fraction': 0.9,\n        'bagging_fraction': 0.9,\n        'bagging_freq': 5,\n        'min_child_samples': 20,\n        'verbose': -1,\n        'seed': 42 + fold\n    }\n    \n    train_data = lgb.Dataset(X_train, label=y_train)\n    valid_data = lgb.Dataset(X_val, label=y_val)\n    \n    model_lgb = lgb.train(\n        lgb_params,\n        train_data,\n        valid_sets=[valid_data],\n        num_boost_round=1000,\n        callbacks=[lgb.early_stopping(100), lgb.log_evaluation(0)]\n    )\n    \n    # 予測\n    pred_lgb = model_lgb.predict(X_val, num_iteration=model_lgb.best_iteration)\n    pred_class = np.argmax(pred_lgb, axis=1)\n    accuracy = np.mean(pred_class == y_val)\n    \n    print(f'LightGBM Validation Accuracy: {accuracy:.4f}')\n    val_scores.append(accuracy)\n    models.append(model_lgb)\n\n# 平均精度\nmean_accuracy = np.mean(val_scores)\nstd_accuracy = np.std(val_scores)\nprint(f'\\n=== Cross-Validation Results ===')\nprint(f'Mean Accuracy: {mean_accuracy:.4f} (+/- {std_accuracy:.4f})')\nprint(f'Target 87%: {\"✅ ACHIEVED\" if mean_accuracy >= 0.87 else \"❌ NOT YET\"}')"},{"cell_type":"code","execution_count":null,"id":"cell-6","metadata":{},"outputs":[],"source":"# XGBoostも追加でアンサンブル\nprint('\\nTraining XGBoost for ensemble...')\n\n# 最良のfoldのデータでXGBoostをトレーニング\nbest_fold = np.argmax(val_scores)\ntrain_idx, val_idx = list(gkf.split(X_df, y_array, groups_array))[best_fold]\n\nX_train = X_df.iloc[train_idx]\nX_val = X_df.iloc[val_idx]\ny_train = y_array[train_idx]\ny_val = y_array[val_idx]\n\n# XGBoost\nxgb_params = {\n    'objective': 'multi:softprob',\n    'num_class': num_classes,\n    'max_depth': 8,\n    'learning_rate': 0.05,\n    'subsample': 0.9,\n    'colsample_bytree': 0.9,\n    'eval_metric': 'mlogloss',\n    'seed': 42,\n    'tree_method': 'hist'\n}\n\ndtrain = xgb.DMatrix(X_train, label=y_train)\ndval = xgb.DMatrix(X_val, label=y_val)\n\nmodel_xgb = xgb.train(\n    xgb_params,\n    dtrain,\n    num_boost_round=1000,\n    evals=[(dval, 'val')],\n    early_stopping_rounds=100,\n    verbose_eval=False\n)\n\n# XGBoost予測\npred_xgb = model_xgb.predict(dval)\npred_xgb_class = np.argmax(pred_xgb, axis=1)\nxgb_accuracy = np.mean(pred_xgb_class == y_val)\nprint(f'XGBoost Validation Accuracy: {xgb_accuracy:.4f}')\n\n# アンサンブル\npred_lgb = models[best_fold].predict(X_val, num_iteration=models[best_fold].best_iteration)\npred_ensemble = 0.6 * pred_lgb + 0.4 * pred_xgb\npred_ensemble_class = np.argmax(pred_ensemble, axis=1)\nensemble_accuracy = np.mean(pred_ensemble_class == y_val)\n\nprint(f'\\n🎯 Ensemble Accuracy: {ensemble_accuracy:.4f}')\nprint(f'Target 87%: {\"✅ ACHIEVED\" if ensemble_accuracy >= 0.87 else \"❌ NOT YET\"}')"},{"cell_type":"code","execution_count":null,"id":"cell-7","metadata":{},"outputs":[],"source":"# CatBoostも追加\nprint('\\nTraining CatBoost for triple ensemble...')\n\ncat_params = {\n    'loss_function': 'MultiClass',\n    'iterations': 1000,\n    'learning_rate': 0.05,\n    'depth': 8,\n    'l2_leaf_reg': 3,\n    'random_seed': 42,\n    'verbose': False,\n    'early_stopping_rounds': 100\n}\n\ntrain_pool = cb.Pool(X_train, y_train)\nval_pool = cb.Pool(X_val, y_val)\n\nmodel_cat = cb.CatBoostClassifier(**cat_params)\nmodel_cat.fit(train_pool, eval_set=val_pool, verbose=False)\n\n# CatBoost予測\npred_cat = model_cat.predict_proba(X_val)\npred_cat_class = np.argmax(pred_cat, axis=1)\ncat_accuracy = np.mean(pred_cat_class == y_val)\nprint(f'CatBoost Validation Accuracy: {cat_accuracy:.4f}')\n\n# トリプルアンサンブル\npred_triple = 0.4 * pred_lgb + 0.3 * pred_xgb + 0.3 * pred_cat\npred_triple_class = np.argmax(pred_triple, axis=1)\ntriple_accuracy = np.mean(pred_triple_class == y_val)\n\nprint(f'\\n🎯 Triple Ensemble Accuracy: {triple_accuracy:.4f}')\nprint(f'Target 87%: {\"✅ ACHIEVED!!!\" if triple_accuracy >= 0.87 else \"❌ NOT YET\"}')\n\n# 最終精度\nfinal_accuracy = max(mean_accuracy, ensemble_accuracy, triple_accuracy)\nprint(f'\\n🏆 Best Validation Accuracy: {final_accuracy:.4f}')"},{"cell_type":"code","execution_count":null,"id":"cell-8","metadata":{},"outputs":[],"source":"# モデル保存\nprint('\\nSaving models...')\n\n# 最良のLightGBMモデル\nwith open('lgb_model_v8.pkl', 'wb') as f:\n    pickle.dump(models[best_fold], f)\n\n# XGBoostモデル\nwith open('xgb_model_v8.pkl', 'wb') as f:\n    pickle.dump(model_xgb, f)\n\n# CatBoostモデル\nmodel_cat.save_model('cat_model_v8.cbm')\n\n# Label encoder\nwith open('label_encoder_v8.pkl', 'wb') as f:\n    pickle.dump(le, f)\n\nprint('Models saved successfully')"},{"cell_type":"code","execution_count":null,"id":"cell-9","metadata":{},"outputs":[],"source":"# CMI推論サーバー\nimport sys\nsys.path.append('/kaggle/input/cmi-detect-behavior-with-sensor-data')\nfrom kaggle_evaluation.cmi_inference_server import CMIInferenceServer\n\ndef predict(sequence, demographics):\n    \"\"\"CMI推論サーバー用予測関数\"\"\"\n    try:\n        # DataFrameに変換\n        df_seq = pd.DataFrame(sequence)\n        \n        # 特徴量抽出\n        features = extract_comprehensive_features(df_seq)\n        X_feat = pd.DataFrame([features])\n        X_feat = X_feat.fillna(0)\n        \n        # 各モデルで予測\n        pred_lgb = models[best_fold].predict(X_feat, num_iteration=models[best_fold].best_iteration)\n        \n        dtest = xgb.DMatrix(X_feat)\n        pred_xgb = model_xgb.predict(dtest)\n        \n        pred_cat = model_cat.predict_proba(X_feat)\n        \n        # アンサンブル\n        pred_ensemble = 0.4 * pred_lgb + 0.3 * pred_xgb + 0.3 * pred_cat\n        \n        # 予測クラス\n        pred_class = np.argmax(pred_ensemble[0])\n        pred_gesture = le.inverse_transform([pred_class])[0]\n        \n        return pred_gesture\n        \n    except Exception as e:\n        print(f'Prediction error: {e}')\n        return 'Text on phone'\n\nprint('Starting CMI Inference Server...')\nserver = CMIInferenceServer(predict)\nserver.serve()\nprint('Inference complete')"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10.0"}},"nbformat":4,"nbformat_minor":5}