{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":41880,"databundleVersionId":5677426,"sourceType":"competition"}],"dockerImageVersionId":30919,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pandas as pd\nimport numpy as np\nimport gc\nimport cupy as cp\nfrom cupyx.scipy.fft import fft\nfrom joblib import Parallel, delayed\nfrom sklearn.feature_selection import VarianceThreshold\nfrom cuml.ensemble import RandomForestClassifier\nimport joblib\nimport re\n# 内存优化 Оптимизация памяти\ndef reduce_memory_usage(df):\n    start_mem = df.memory_usage().sum() / 1024**2\n    for col in df.columns:\n        col_type = df[col].dtype.name\n        if col_type not in ['datetime64[ns]', 'category']:\n            if col_type != 'object':\n                c_min, c_max = df[col].min(), df[col].max()\n                if 'int' in col_type:\n                    for it in [np.int8, np.int16, np.int32, np.int64]:\n                        if c_min > np.iinfo(it).min and c_max < np.iinfo(it).max:\n                            df[col] = df[col].astype(it)\n                            break\n                else:\n                    if isinstance(df[col].values, cp.ndarray):  \n                        df[col] = df[col].astype(cp.float16)\n                    else:\n                        for ft in [np.float16, np.float32]:\n                            if c_min > np.finfo(ft).min and c_max < np.finfo(ft).max:\n                                df[col] = df[col].astype(ft)\n                                break\n            else:\n                df[col] = df[col].astype('category')\n    mem_usg = df.memory_usage().sum() / 1024**2 \n    print(f\"内存优化 Оптимизация памяти: {start_mem:.2f} MB -> {mem_usg:.2f} MB\")\n    return df\n\n# 数据加载（启用GPU加速） Загрузка данных (с использованием GPU)\ndef load_data():\n    DATA_ROOT_TDCSFOG = '/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/train/tdcsfog/'\n\n    all_dfs = []\n    for root, _, files in os.walk(DATA_ROOT_TDCSFOG):\n        for name in files:\n            df = pd.read_csv(os.path.join(root, name))\n            df['file'] = name.split('.')[0]\n            for col in ['AccV', 'AccML', 'AccAP']:\n                # 确保数据是浮点型 Убедиться, что данные имеют тип с плавающей точкой\n                df[col] = df[col].astype(np.float32)\n                df[col] = cp.asarray(df[col].values, dtype=cp.float16).get()\n            all_dfs.append(df)\n\n    tdcsfog = pd.concat(all_dfs, axis=0)\n    tdcsfog = reduce_memory_usage(tdcsfog)\n    return tdcsfog\n\n# 提取特征 Извлечение признаков\ndef extract_features(window):\n    \"\"\"GPU加速的特征提取 Извлечение признаков с ускорением GPU\"\"\"\n    features = {}\n    # 统计特征（GPU计算） Статистические признаки (вычисления на GPU)\n    for col in ['AccV', 'AccML', 'AccAP']:\n        gpu_data = cp.asarray(window[col].astype(np.float32))\n        features[f'{col}_mean'] = float(cp.mean(gpu_data).get())\n        features[f'{col}_std'] = float(cp.std(gpu_data).get())\n        features[f'{col}_max'] = float(cp.max(gpu_data).get())\n        features[f'{col}_min'] = float(cp.min(gpu_data).get())\n    # 差分特征 Разностные признаки\n    for col in ['AccV', 'AccML', 'AccAP']:\n        diff = cp.diff(cp.asarray(window[col].astype(np.float32))).get()\n        features[f'{col}_diff_mean'] = float(np.mean(diff))\n        features[f'{col}_diff_std'] = float(np.std(diff))\n    # 频域特征 Частотные признаки\n    for col in ['AccV', 'AccML', 'AccAP']:\n        n = len(window[col])\n        fft_vals = cp.abs(fft(cp.asarray(window[col].astype(np.float32))))[:n//2]\n        freqs = cp.linspace(0, 64, n//2).get()\n        mask = (freqs >= 0.5) & (freqs <= 3)\n        features[f'{col}_freq_energy'] = float(cp.sum(fft_vals[mask]).get())\n    # 修正取值问题 Исправление проблем с выбором значений\n    features.update({\n        'StartHesitation': int(window['StartHesitation'].iloc[-1]),  \n        'Turn': int(window['Turn'].iloc[-1]),  \n        'Walking': int(window['Walking'].iloc[-1]),  \n        'Time': float(window['Time'].iloc[-1]),  \n        'Subject': window['Subject'].iloc[-1]  \n    })\n    return features\n\n# 滑动窗口生成器 Генератор скользящего окна\ndef create_sliding_windows(df, window_size, step):\n    for start in range(0, len(df) - window_size + 1, step):\n        yield df.iloc[start:start + window_size]\n\n# 并行处理窗口 Параллельная обработка окон\ndef process_data_parallel(df, window_size=256, step=64):\n    \"\"\"使用 joblib 并行计算特征 Использование joblib для параллельного вычисления признаков\"\"\"\n    results = Parallel(n_jobs=-1)(\n        delayed(extract_features)(window) for window in create_sliding_windows(df, window_size, step)\n    )\n    return pd.DataFrame(results)\n\n# 确保列是浮点数类型 Обеспечение типа данных с плавающей точкой для столбцов\ndef ensure_float_columns(df, columns):\n    \"\"\"确保指定的列是浮点数类型 Обеспечение типа данных с плавающей точкой для указанных столбцов\"\"\"\n    for col in columns:\n        # 首先尝试直接转换 Сначала попробуем прямое преобразование\n        try:\n            df[col] = pd.to_numeric(df[col], errors='coerce').astype(np.float32)\n        except Exception as e:\n            print(f\"转换列 {col} 时出错: {e} Ошибка при преобразовании столбца {col}: {e}\")\n            # 如果失败，尝试清理非数值字符再转换 Если не удалось, попробуем очистить нечисловые символы и затем преобразовать\n            try:\n                df[col] = df[col].astype(str).str.replace(r'[^\\d\\.\\-]', '', regex=True)\n                df[col] = pd.to_numeric(df[col], errors='coerce').astype(np.float32)\n            except Exception as e2:\n                print(f\"尝试清理后转换列 {col} 时仍然出错: {e2} Ошибка после очистки при преобразовании столбца {col}: {e2}\")\n                # 最后尝试，将无法转换的值设为NaN В качестве последней попытки, установим непреобразуемые значения как NaN\n                df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0).astype(np.float32)\n    \n    return df\n\ndef main():\n    # 设置常量 Установка констант\n    WINDOW_SIZE = 256  \n    STEP = 64          \n    \n    print(\"开始加载数据... Начало загрузки данных...\")\n    # 加载数据 Загрузка данных\n    tdcsfog = load_data()\n    \n    # 读取元数据并合并 Чтение и объединение метаданных\n    tdcsfog_metadata = pd.read_csv(\"/kaggle/input/tlvmc-parkinsons-freezing-gait-prediction/tdcsfog_metadata.csv\")\n    tdcsfog_m = tdcsfog_metadata.merge(tdcsfog, how='inner', left_on='Id', right_on='file').drop('file', axis=1)\n    gc.collect()\n    \n    print(\"开始特征提取... Начало извлечения признаков...\")\n    # 执行数据处理 Выполнение обработки данных\n    processed_df = process_data_parallel(tdcsfog_m, WINDOW_SIZE, STEP)\n    print(processed_df.head())\n    \n    # 确保数值列是浮点数类型 Обеспечение типа данных с плавающей точкой для числовых столбцов\n    # 获取所有可能是数值的列(排除'Subject'列) Получение всех возможных числовых столбцов (исключая столбец 'Subject')\n    numeric_cols = [col for col in processed_df.columns if col != 'Subject']\n    processed_df = ensure_float_columns(processed_df, numeric_cols)\n    \n    # 检查数据类型 Проверка типов данных\n    print(\"初始处理后的数据类型: Типы данных после начальной обработки:\")\n    print(processed_df.dtypes)\n    \n    # 存储原始处理数据 Хранение обработанных данных\n    processed_df.to_csv(\"/kaggle/working/processed_features.csv\", index=False)\n    print(\"特征提取完成！Извлечение признаков завершено!\")\n    \n    # 特征选择（保留特征名称） Выбор признаков (сохранение имен признаков)\n    X = processed_df.drop(['Time', 'Subject', 'StartHesitation', 'Turn', 'Walking'], axis=1, errors='ignore')\n    y = processed_df[['StartHesitation', 'Turn', 'Walking']]\n    \n    print(\"开始特征选择... Начало выбора признаков...\")\n    # 过滤低方差特征 Фильтрация признаков с низкой дисперсией\n    selector = VarianceThreshold(threshold=0.01)\n    X_selected = selector.fit_transform(X)\n    \n    # 获取保留的特征信息 Получение информации о сохраненных признаках\n    selected_features = X.columns[selector.get_support()].tolist()\n    print(f\"保留特征数量 Количество сохраненных признаков: {len(selected_features)}\")\n    print(\"示例特征 Пример признаков:\", selected_features[:10])\n    \n    # 重建数据集 Перестроение набора данных\n    final_df = pd.DataFrame(X_selected, columns=selected_features)\n    final_df = pd.concat([final_df, processed_df[['Time', 'Subject', 'StartHesitation', 'Turn', 'Walking']]], axis=1)\n    \n    # 验证关键特征存在 Проверка наличия ключевых признаков\n    assert 'AccV_mean' in final_df.columns, \"统计特征丢失！Статистические признаки потеряны!\"\n    assert 'AccML_diff_std' in final_df.columns, \"差分特征丢失！Разностные признаки потеряны!\"\n    assert 'AccAP_freq_energy' in final_df.columns, \"频域特征丢失！Частотные признаки потеряны!\"\n    \n    print(\"开始数据集分割... Начало разделения набора данных...\")\n    # 数据集分割（按患者划分）Разделение набора данных (по пациентам)\n    subjects = final_df['Subject'].unique()\n    np.random.seed(42)\n    np.random.shuffle(subjects)\n    train_subjects = subjects[:int(0.8*len(subjects))]\n    val_subjects = subjects[int(0.8*len(subjects)):int(0.9*len(subjects))]\n    test_subjects = subjects[int(0.9*len(subjects)):]\n    \n    train_df = final_df[final_df['Subject'].isin(train_subjects)]\n    val_df = final_df[final_df['Subject'].isin(val_subjects)]\n    test_df = final_df[final_df['Subject'].isin(test_subjects)]\n    \n    # 输出分割后训练集、验证集和测试集的大小 Вывод размеров обучающего, валидационного и тестового наборов после разделения\n    print(f\"训练集大小 Размер обучающего набора: {len(train_df)}\")\n    print(f\"验证集大小 Размер валидационного набора: {len(val_df)}\")\n    print(f\"测试集大小 Размер тестового набора: {len(test_df)}\")\n    \n    print(\"转换数据类型... Преобразование типов данных...\")\n    # 再次确保所有特征列都是浮点型 Повторное обеспечение типа данных с плавающей точкой для всех столбцов признаков\n    numeric_feature_cols = [col for col in selected_features if col != 'Subject']\n    \n    # 使用to_numeric替代applymap，修复弃用警告 Использование to_numeric вместо applymap, исправление предупреждения о устаревшем методе\n    train_df.loc[:, numeric_feature_cols] = train_df[numeric_feature_cols].apply(pd.to_numeric, errors='coerce').astype(np.float32)\n    val_df.loc[:, numeric_feature_cols] = val_df[numeric_feature_cols].apply(pd.to_numeric, errors='coerce').astype(np.float32)\n    test_df.loc[:, numeric_feature_cols] = test_df[numeric_feature_cols].apply(pd.to_numeric, errors='coerce').astype(np.float32)\n    \n    # 确保目标变量是整数类型 Обеспечение целого типа для целевых переменных\n    train_df[['StartHesitation', 'Turn', 'Walking']] = train_df[['StartHesitation', 'Turn', 'Walking']].astype(np.int32)\n    val_df[['StartHesitation', 'Turn', 'Walking']] = val_df[['StartHesitation', 'Turn', 'Walking']].astype(np.int32)\n    test_df[['StartHesitation', 'Turn', 'Walking']] = test_df[['StartHesitation', 'Turn', 'Walking']].astype(np.int32)\n    \n    # 检查数据类型 Проверка типов данных\n    print(\"\\n转换后的特征数据类型示例： Примеры типов данных признаков после преобразования:\")\n    print(train_df[numeric_feature_cols].dtypes.head())\n    \n    # 检查是否有NaN值，并填充 Проверка на наличие значений NaN и их заполнение\n    print(f\"\\n检查NaN值情况: Проверка на наличие значений NaN:\")\n    print(f\"训练集NaN值数量: Количество значений NaN в обучающем наборе: {train_df[numeric_feature_cols].isna().sum().sum()}\")\n    print(f\"验证集NaN值数量: Количество значений NaN в валидационном наборе: {val_df[numeric_feature_cols].isna().sum().sum()}\")\n    print(f\"测试集NaN值数量: Количество значений NaN в тестовом наборе: {test_df[numeric_feature_cols].isna().sum().sum()}\")\n    \n    # 如果有NaN值，填充为0 Если есть значения NaN, заполнить их нулями\n    if train_df[numeric_feature_cols].isna().sum().sum() > 0:\n        print(\"填充NaN值... Заполнение значений NaN...\")\n        train_df[numeric_feature_cols] = train_df[numeric_feature_cols].fillna(0)\n        val_df[numeric_feature_cols] = val_df[numeric_feature_cols].fillna(0)\n        test_df[numeric_feature_cols] = test_df[numeric_feature_cols].fillna(0)\n    \n    print(\"保存处理后的数据... Сохранение обработанных данных...\")\n    # 保存结果（GPU兼容格式） Сохранение результатов (формат, совместимый с GPU)\n    joblib.dump({\n        'train': (train_df[selected_features], train_df[['StartHesitation', 'Turn', 'Walking']]),\n        'val': (val_df[selected_features], val_df[['StartHesitation', 'Turn', 'Walking']]),\n        'test': (test_df[selected_features], test_df[['StartHesitation', 'Turn', 'Walking']])\n    }, \"/kaggle/working/processed_data.pkl\")\n    \n    print(\"数据处理完成！Обработка данных завершена!\")\n    \n    # 重要：明确定义并保存训练、验证和测试集的X和y变量，以便后续使用 Важно: четко определить и сохранить переменные X и y для обучающего, валидационного и тестового наборов для последующего использования\n    # 这将解决后续运行XGBoost训练时的变量未定义问题 Это решит проблему неопределенных переменных при последующем запуске обучения XGBoost\n    train_X, train_y = train_df[selected_features], train_df[['StartHesitation', 'Turn', 'Walking']]\n    val_X, val_y = val_df[selected_features], val_df[['StartHesitation', 'Turn', 'Walking']]\n    test_X, test_y = test_df[selected_features], test_df[['StartHesitation', 'Turn', 'Walking']]\n    \n    # 单独保存这些变量，方便后续直接加载使用 Отдельно сохранить эти переменные для удобного последующего использования\n    joblib.dump({\n        'train_X': train_X,\n        'train_y': train_y,\n        'val_X': val_X,\n        'val_y': val_y,\n        'test_X': test_X,\n        'test_y': test_y,\n        'selected_features': selected_features\n    }, \"/kaggle/working/model_ready_data.pkl\")\n    \n    print(\"模型训练所需变量已保存，可以直接用于XGBoost训练！Переменные, необходимые для обучения модели, сохранены и готовы к использованию для обучения XGBoost!\")\n    \n    # 返回这些变量，以便可以在笔记本中直接使用 Возвращение этих переменных для прямого использования в ноутбуке\n    return train_X, train_y, val_X, val_y, test_X, test_y, selected_features\n\n# 执行主函数，并将返回值赋给全局变量 Выполнение основной функции и присвоение возвращаемых значений глобальным переменным\nif __name__ == \"__main__\":\n    train_X, train_y, val_X, val_y, test_X, test_y, selected_features = main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T16:53:54.801840Z","iopub.execute_input":"2025-04-27T16:53:54.802199Z","iopub.status.idle":"2025-04-27T17:00:44.834122Z","shell.execute_reply.started":"2025-04-27T16:53:54.802167Z","shell.execute_reply":"2025-04-27T17:00:44.833083Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Xgboost","metadata":{}},{"cell_type":"code","source":"import joblib\nimport pandas as pd\nimport numpy as np\nimport time\nimport os\n\n# 加载处理好的数据 Загрузка обработанных данных\ndef load_model_ready_data():\n    print(\"加载模型训练所需的数据... Загрузка данных, необходимых для обучения модели...\")\n    try:\n        # 尝试加载包含所有需要变量的数据文件 Попытка загрузить файл данных, содержащий все необходимые переменные\n        data = joblib.load(\"/kaggle/working/model_ready_data.pkl\")\n        train_X = data['train_X']\n        train_y = data['train_y'] \n        val_X = data['val_X']\n        val_y = data['val_y']\n        test_X = data['test_X']\n        test_y = data['test_y']\n        selected_features = data['selected_features']\n        \n        print(\"数据加载成功！Данные успешно загружены!\")\n        print(f\"训练集形状: Форма обучающего набора: {train_X.shape}, {train_y.shape}\")\n        print(f\"验证集形状: Форма валидационного набора: {val_X.shape}, {val_y.shape}\")\n        print(f\"测试集形状: Форма тестового набора: {test_X.shape}, {test_y.shape}\")\n        \n        return train_X, train_y, val_X, val_y, test_X, test_y, selected_features\n        \n    except (FileNotFoundError, KeyError) as e:\n        print(f\"警告: 无法加载model_ready_data.pkl: {e} Предупреждение: Невозможно загрузить model_ready_data.pkl: {e}\")\n        print(\"尝试加载并处理原始的processed_data.pkl... Попытка загрузить и обработать исходный processed_data.pkl...\")\n        \n        # 加载原始处理数据并重新提取需要的变量 Загрузка исходных обработанных данных и повторное извлечение необходимых переменных\n        try:\n            data = joblib.load(\"/kaggle/working/processed_data.pkl\")\n            \n            # 提取训练、验证和测试数据 Извлечение обучающих, валидационных и тестовых данных\n            train_X, train_y = data['train']\n            val_X, val_y = data['val']\n            test_X, test_y = data['test']\n            \n            # 获取特征名称 Получение имен признаков\n            selected_features = train_X.columns.tolist()\n            \n            print(\"从原始数据成功提取所需变量！Успешное извлечение необходимых переменных из исходных данных!\")\n            print(f\"训练集形状: Форма обучающего набора: {train_X.shape}, {train_y.shape}\")\n            print(f\"验证集形状: Форма валидационного набора: {val_X.shape}, {val_y.shape}\")\n            print(f\"测试集形状: Форма тестового набора: {test_X.shape}, {test_y.shape}\")\n            \n            return train_X, train_y, val_X, val_y, test_X, test_y, selected_features\n            \n        except Exception as e2:\n            print(f\"错误: 无法加载和处理数据: {e2} Ошибка: Невозможно загрузить и обработать данные: {e2}\")\n            print(\"请先运行数据预处理代码，确保所需数据文件已生成。Пожалуйста, сначала запустите код предварительной обработки данных, чтобы убедиться, что необходимые файлы данных были созданы.\")\n            raise\n\n# 用于运行XGBoost模型的示例函数 Пример функции для запуска модели XGBoost\ndef run_xgboost_example():\n    # 加载数据 Загрузка данных\n    train_X, train_y, val_X, val_y, test_X, test_y, selected_features = load_model_ready_data()\n    \n    print(\"\\n数据准备就绪，可以开始运行XGBoost模型... Данные готовы, можно начинать запуск модели XGBoost...\")\n    print(\"示例: 从第二个文件调用run_xgboost_training函数: Пример: вызов функции run_xgboost_training из второго файла:\")\n    print(\"\"\"\n    # 运行XGBoost模型训练 Запуск обучения модели XGBoost\n    model, evals_result, evaluation_results, scaler = run_xgboost_training(\n        train_X, train_y, val_X, val_y, test_X, test_y,\n        config={\n            'num_boost_round': 1000,\n            'early_stopping_rounds': 50,\n            'scale_features': True,\n            'handle_imbalance': True,\n            'use_gpu': True\n        }\n    )\n    \"\"\")\n    \n    # 返回加载好的数据，方便后续使用 Возврат загруженных данных для удобства последующего использования\n    return train_X, train_y, val_X, val_y, test_X, test_y, selected_features\n\n# 运行示例函数 Запуск примера функции\nif __name__ == \"__main__\":\n    train_X, train_y, val_X, val_y, test_X, test_y, selected_features = run_xgboost_example()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:00:44.835397Z","iopub.execute_input":"2025-04-27T17:00:44.835669Z","iopub.status.idle":"2025-04-27T17:00:44.855584Z","shell.execute_reply.started":"2025-04-27T17:00:44.835643Z","shell.execute_reply":"2025-04-27T17:00:44.854683Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport time\nimport json\nimport gc\nimport joblib\n\nimport xgboost as xgb\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import (\n    classification_report, f1_score, roc_auc_score, confusion_matrix, \n    roc_curve, auc, accuracy_score, precision_score, recall_score\n)\nfrom sklearn.utils.class_weight import compute_class_weight\n\n# ======= 常量定义 Определение констант =======\nOUTPUT_DIR = \"/kaggle/working\"\nXGBOOST_RESULTS_DIR = os.path.join(OUTPUT_DIR, \"xgboost_results\")\nos.makedirs(XGBOOST_RESULTS_DIR, exist_ok=True)\n\n# ======= 辅助函数 Вспомогательные функции =======\ndef preprocess_data_for_xgboost(train_X, train_y, val_X, val_y, test_X, test_y, scale_features=True):\n    \"\"\"预处理数据以适应XGBoost模型\n    Предварительная обработка данных для модели XGBoost\"\"\"\n    \n    # 确保数据为DataFrame或numpy数组 Убедитесь, что данные являются DataFrame или массивом numpy\n    if isinstance(train_X, pd.DataFrame):\n        train_X_df = train_X.copy()\n        feature_names = train_X.columns.tolist()\n    else:\n        train_X_df = pd.DataFrame(train_X)\n        feature_names = [f\"feature_{i}\" for i in range(train_X.shape[1])]\n        \n    if isinstance(val_X, pd.DataFrame):\n        val_X_df = val_X.copy()\n    else:\n        val_X_df = pd.DataFrame(val_X, columns=feature_names)\n        \n    if isinstance(test_X, pd.DataFrame):\n        test_X_df = test_X.copy()\n    else:\n        test_X_df = pd.DataFrame(test_X, columns=feature_names)\n    \n    # 特征缩放（可选） Масштабирование признаков (опционально)\n    scaler = None\n    if scale_features:\n        scaler = StandardScaler()\n        train_X_scaled = scaler.fit_transform(train_X_df)\n        val_X_scaled = scaler.transform(val_X_df)\n        test_X_scaled = scaler.transform(test_X_df)\n        \n        train_X_df = pd.DataFrame(train_X_scaled, columns=feature_names)\n        val_X_df = pd.DataFrame(val_X_scaled, columns=feature_names)\n        test_X_df = pd.DataFrame(test_X_scaled, columns=feature_names)\n    \n    # 处理标签 - 修复版本，正确处理全0向量 Обработка меток - исправленная версия, правильно обрабатывающая полностью нулевые векторы\n    def convert_labels(y):\n        if isinstance(y, pd.DataFrame) and y.shape[1] > 1:\n            # 检查每行是否为全0 Проверка, является ли каждая строка полностью нулевой\n            all_zeros = (y.sum(axis=1) == 0)\n            \n            # 为非零行使用argmax Использование argmax для ненулевых строк\n            y_indices = y.values.argmax(axis=1)\n            \n            # 为全0行分配特殊类别标签（0），并将其他类别向上移动 Назначение специальной метки класса (0) для полностью нулевых строк и сдвиг других классов вверх\n            y_indices = y_indices + 1  # 将所有非零类别向上移动1 Сдвиг всех ненулевых классов вверх на 1\n            y_indices[all_zeros] = 0   # 全0类别设为0 Установка полностью нулевого класса на 0\n            \n            return y_indices\n            \n        elif isinstance(y, np.ndarray) and y.ndim > 1 and y.shape[1] > 1:\n            # 检查每行是否为全0 Проверка, является ли каждая строка полностью нулевой\n            all_zeros = (np.sum(y, axis=1) == 0)\n            \n            # 为非零行使用argmax Использование argmax для ненулевых строк\n            y_indices = np.argmax(y, axis=1)\n            \n            # 为全0行分配特殊类别标签（0），并将其他类别向上移动 Назначение специальной метки класса (0) для полностью нулевых строк и сдвиг других классов вверх\n            y_indices = y_indices + 1  # 将所有非零类别向上移动1 Сдвиг всех ненулевых классов вверх на 1\n            y_indices[all_zeros] = 0   # 全0类别设为0 Установка полностью нулевого класса на 0\n            \n            return y_indices\n        else:\n            # 如果不是one-hot编码，直接返回 Если не в формате one-hot, просто вернуть\n            return y\n    \n    # 转换训练、验证和测试标签 Преобразование меток обучающего, валидационного и тестового наборов\n    train_y_indices = convert_labels(train_y)\n    val_y_indices = convert_labels(val_y)\n    test_y_indices = convert_labels(test_y)\n    \n    # 获取类别数量 Получение количества классов\n    num_classes = len(np.unique(train_y_indices))\n    \n    # 分析类别分布 Анализ распределения классов\n    class_counts = np.bincount(train_y_indices)\n    print(f\"类别分布 / Распределение классов: {dict(zip(range(len(class_counts)), class_counts))}\")\n    \n    # 创建XGBoost DMatrix数据集 Создание наборов данных XGBoost DMatrix\n    train_data = xgb.DMatrix(\n        train_X_df, \n        label=train_y_indices,\n        feature_names=feature_names\n    )\n    \n    val_data = xgb.DMatrix(\n        val_X_df, \n        label=val_y_indices,\n        feature_names=feature_names\n    )\n    \n    test_data = xgb.DMatrix(\n        test_X_df,\n        label=test_y_indices,\n        feature_names=feature_names\n    )\n    \n    return {\n        'train_data': train_data,\n        'val_data': val_data,\n        'test_data': test_data,\n        'test_X': test_X_df,\n        'test_y': test_y_indices,\n        'feature_names': feature_names,\n        'num_classes': num_classes,\n        'scaler': scaler\n    }\n\n\ndef compute_sample_weights(y):\n    \"\"\"计算样本权重以处理类别不平衡\n    Вычисление весов образцов для обработки несбалансированности классов\"\"\"\n    classes = np.unique(y)\n    class_weights = compute_class_weight('balanced', classes=classes, y=y)\n    sample_weights = np.ones_like(y, dtype=np.float64)\n    \n    for idx, cls in enumerate(classes):\n        sample_weights[y == cls] = class_weights[idx]\n    \n    return sample_weights\n\n\n\ndef calculate_metrics_xgboost(y_true, y_pred, y_scores):\n    \"\"\"计算各种评估指标\n    Расчет различных показателей оценки\"\"\"\n    \n    # 计算准确率 Расчет точности\n    accuracy = accuracy_score(y_true, y_pred)\n    \n    # 计算加权F1分数 Расчет взвешенного F1-показателя\n    f1_weighted = f1_score(y_true, y_pred, average='weighted')\n    \n    # 计算各类别F1分数 Расчет F1-показателя для каждого класса\n    f1_per_class = f1_score(y_true, y_pred, average=None)\n    \n    # 计算精确率和召回率 Расчет точности и полноты\n    precision_weighted = precision_score(y_true, y_pred, average='weighted')\n    recall_weighted = recall_score(y_true, y_pred, average='weighted')\n    \n    # 尝试计算ROC AUC Попытка расчета ROC AUC\n    try:\n        # 对于多分类问题，转换为one-hot编码 Для задач с несколькими классами преобразование в формат one-hot\n        n_classes = y_scores.shape[1]\n        y_true_onehot = np.zeros((len(y_true), n_classes))\n        for i in range(len(y_true)):\n            y_true_onehot[i, y_true[i]] = 1\n            \n        auc_score = roc_auc_score(y_true_onehot, y_scores, multi_class='ovr')\n    except Exception as e:\n        print(f\"计算ROC AUC时出错 / Ошибка при расчете ROC AUC: {e}\")\n        auc_score = None\n    \n    return {\n        'accuracy': accuracy,\n        'f1_weighted': f1_weighted,\n        'f1_per_class': f1_per_class,\n        'precision_weighted': precision_weighted,\n        'recall_weighted': recall_weighted,\n        'auc_score': auc_score\n    }\n\n# ======= XGBoost参数配置 Конфигурация параметров XGBoost =======\ndef get_default_xgboost_params(num_classes, use_gpu=True):\n    \"\"\"获取默认的XGBoost参数\n    Получение параметров XGBoost по умолчанию\"\"\"\n    \n    params = {\n        'objective': 'multi:softprob',\n        'eval_metric': 'mlogloss',\n        'num_class': num_classes,  # 这将正确设置为4个类别 Это правильно устанавливает 4 класса\n        'verbosity': 1,\n        'learning_rate': 0.05,\n        'max_depth': 6,\n        'min_child_weight': 3,\n        'subsample': 0.8,\n        'colsample_bytree': 0.8,\n        'colsample_bylevel': 0.8,\n        'gamma': 0.1,\n        'reg_alpha': 0.1,\n        'reg_lambda': 0.1,\n        'random_state': 42,\n        'tree_method': 'hist'\n    }\n    \n    # 如果使用GPU，添加GPU相关参数 Если используется GPU, добавляются параметры, связанные с GPU\n    if use_gpu:\n        params.update({\n            'tree_method': 'gpu_hist',\n            'gpu_id': 0,\n            'predictor': 'gpu_predictor'\n        })\n    \n    return params\n\n# ======= 训练函数 Функция обучения =======\ndef train_xgboost_model(\n    processed_data, \n    params=None, \n    num_boost_round=1000, \n    early_stopping_rounds=50,\n    handle_imbalance=True\n):\n    \"\"\"训练XGBoost模型\n    Обучение модели XGBoost\"\"\"\n    \n    train_data = processed_data['train_data']\n    val_data = processed_data['val_data']\n    num_classes = processed_data['num_classes']\n    \n    # 使用默认参数或自定义参数 Использование параметров по умолчанию или пользовательских параметров\n    if params is None:\n        params = get_default_xgboost_params(num_classes)\n    else:\n        # 确保必要的参数存在 Обеспечение наличия необходимых параметров\n        params['num_class'] = num_classes\n        if 'objective' not in params:\n            params['objective'] = 'multi:softprob'\n        if 'eval_metric' not in params:\n            params['eval_metric'] = 'mlogloss'\n    \n    print(\"XGBoost参数 / Параметры XGBoost:\")\n    print(json.dumps(params, indent=4))\n    \n    # 处理类别不平衡（如果需要） Обработка несбалансированности классов (если необходимо)\n    sample_weights = None\n    if handle_imbalance:\n        # 获取训练标签 Получение меток обучения\n        train_labels = train_data.get_label()\n        \n        # 计算类权重 Расчет весов классов\n        classes = np.unique(train_labels)\n        class_weights = compute_class_weight('balanced', classes=classes, y=train_labels)\n        class_weight_dict = {i: weight for i, weight in zip(classes, class_weights)}\n        \n        print(f\"类别权重 / Веса классов: {class_weight_dict}\")\n        \n        # 为每个样本分配权重 Назначение весов для каждого образца\n        # XGBoost support sample weights directly XGBoost напрямую поддерживает веса образцов\n        sample_weights = np.ones_like(train_labels, dtype=np.float32)\n        for idx, label in enumerate(train_labels):\n            sample_weights[idx] = class_weight_dict[label]\n        \n        # 设置样本权重 Установка весов образцов\n        train_data.set_weight(sample_weights)\n    \n    # 用于存储评估结果的字典 Словарь для хранения результатов оценки\n    evals_result = {}\n    \n    # 开始训练 Начало обучения\n    print(f\"\\n开始XGBoost训练 / Начало обучения XGBoost (max_rounds={num_boost_round}, early_stopping={early_stopping_rounds})...\")\n    start_time = time.time()\n    \n    # 设置评估数据集 Установка наборов данных для оценки\n    eval_list = [(train_data, 'train'), (val_data, 'validation')]\n    \n    model = xgb.train(\n        params,\n        train_data,\n        num_boost_round=num_boost_round,\n        evals=eval_list,\n        early_stopping_rounds=early_stopping_rounds,\n        verbose_eval=20,\n        evals_result=evals_result\n    )\n    \n    training_time = time.time() - start_time\n    print(f\"训练完成 / Обучение завершено! 用时 / Время: {training_time:.2f}秒\")\n    \n    # 获取最佳迭代次数 Получение лучшей итерации\n    best_iteration = model.best_iteration\n    print(f\"最佳迭代次数 / Лучшая итерация: {best_iteration}\")\n    \n    return model, evals_result\n\n# ======= 评估函数 Функция оценки =======\ndef evaluate_xgboost_model(model, test_data, test_y, processed_data=None):\n    \"\"\"评估XGBoost模型\n    Оценка модели XGBoost\"\"\"\n    \n    feature_names = None\n    if processed_data is not None:\n        feature_names = processed_data.get('feature_names')\n    \n    # 预测概率 Предсказание вероятностей\n    y_scores = model.predict(test_data)\n    \n    # 对于多分类问题，reshape概率输出 Для задач с несколькими классами изменение формы выходных вероятностей\n    num_classes = model.attr('num_class')\n    if num_classes is not None and int(num_classes) > 2:\n        num_classes = int(num_classes)\n        y_scores = y_scores.reshape(-1, num_classes)\n    \n    # 获取预测的类别 Получение предсказанных классов\n    y_pred = np.argmax(y_scores, axis=1)\n    \n    # 计算评估指标 Расчет показателей оценки\n    metrics = calculate_metrics_xgboost(test_y, y_pred, y_scores)\n    \n    # 打印详细指标 Вывод подробных показателей\n    print(f\"\\n===== 模型评估指标 / Показатели оценки модели =====\")\n    print(f\"准确率 / Точность: {metrics['accuracy']:.4f}\")\n    print(f\"加权F1分数 / Взвешенный F1-показатель: {metrics['f1_weighted']:.4f}\")\n    print(f\"加权精确率 / Взвешенная точность: {metrics['precision_weighted']:.4f}\")\n    print(f\"加权召回率 / Взвешенная полнота: {metrics['recall_weighted']:.4f}\")\n    print(f\"各类别F1分数 / F1-показатель для каждого класса: {metrics['f1_per_class']}\")\n    if metrics['auc_score']:\n        print(f\"ROC AUC: {metrics['auc_score']:.4f}\")\n    \n    # 计算混淆矩阵 Расчет матрицы ошибок\n    cm = confusion_matrix(test_y, y_pred)\n    \n    # 生成分类报告 - 更新为4个类别 Создание отчета о классификации - обновление для 4 классов\n    class_names = ['Нет категории', 'Начало нерешительности', 'Поворот', 'Ходьба']\n    report = classification_report(test_y, y_pred, target_names=class_names, output_dict=True)\n    \n    # 获取特征重要性 Получение важности признаков\n    importance_type = 'gain'\n    feature_importance = model.get_score(importance_type=importance_type)\n    feature_importance_df = pd.DataFrame({\n        'Feature': list(feature_importance.keys()),\n        'Importance': list(feature_importance.values())\n    }).sort_values(by='Importance', ascending=False)\n    \n    return {\n        'metrics': metrics,\n        'confusion_matrix': cm,\n        'classification_report': report,\n        'predictions': y_pred,\n        'probabilities': y_scores,\n        'feature_importance': feature_importance_df\n    }\n\n# ======= 可视化函数 Функция визуализации =======\ndef visualize_xgboost_results(model, evals_result, evaluation_results):\n    \"\"\"可视化XGBoost模型训练结果和性能\n    Визуализация результатов обучения и производительности модели XGBoost\"\"\"\n    \n    # 1. 绘制训练历史 Построение истории обучения\n    plt.figure(figsize=(15, 5))\n    \n    # 训练和验证损失曲线 Кривые потерь при обучении и валидации\n    plt.subplot(1, 3, 1)\n    epochs = len(evals_result['train']['mlogloss'])\n    x_axis = range(1, epochs + 1)\n    plt.plot(x_axis, evals_result['train']['mlogloss'], label='Потери при обучении')\n    plt.plot(x_axis, evals_result['validation']['mlogloss'], label='Потери при валидации')\n    plt.axvline(x=model.best_iteration, color='red', linestyle='--', label=f'Лучшая итерация: {model.best_iteration}')\n    plt.title('Потери модели')\n    plt.xlabel('Итерации')\n    plt.ylabel('Лог-потери')\n    plt.legend()\n    \n    # 特征重要性图 График важности признаков\n    plt.subplot(1, 3, 2)\n    feature_importance = evaluation_results['feature_importance']\n    top_features = feature_importance.head(15)\n    sns.barplot(x='Importance', y='Feature', data=top_features)\n    plt.title('Важность признаков (топ-15)')\n    plt.tight_layout()\n    \n    # 混淆矩阵 - 更新类别名称为4个类别 Матрица ошибок - обновление имен классов для 4 классов\n    plt.subplot(1, 3, 3)\n    cm = evaluation_results['confusion_matrix']\n    class_names = ['Нет категории', 'Начало нерешительности', 'Поворот', 'Ходьба']\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n                xticklabels=class_names, \n                yticklabels=class_names)\n    plt.xlabel('Предсказанная метка')\n    plt.ylabel('Истинная метка')\n    plt.title('Матрица ошибок')\n    \n    plt.tight_layout()\n    plt.savefig(os.path.join(XGBOOST_RESULTS_DIR, \"xgboost_training_results.png\"))\n    plt.show()\n    \n    # 2. ROC曲线 ROC-кривая\n    try:\n        y_scores = evaluation_results['probabilities']\n        y_true = evaluation_results['metrics']['true_labels'] if 'true_labels' in evaluation_results['metrics'] else None\n        \n        if y_true is None and 'predictions' in evaluation_results and 'probabilities' in evaluation_results:\n            y_pred = evaluation_results['predictions']\n            y_scores = evaluation_results['probabilities']\n            # 创建一个虚拟的y_true数组，基于测试集 Создание фиктивного массива y_true на основе тестового набора\n            y_true = np.zeros_like(y_pred)\n            # 这里我们假设评估结果字典中有test_y Здесь мы предполагаем, что в словаре результатов оценки есть test_y\n            if 'test_y' in evaluation_results:\n                y_true = evaluation_results['test_y']\n        \n        plt.figure(figsize=(10, 8))\n        \n        # 为每个类别绘制ROC曲线 Построение ROC-кривой для каждого класса\n        num_classes = y_scores.shape[1]\n        y_true_onehot = np.zeros((len(y_true), num_classes))\n        for i in range(len(y_true)):\n            y_true_onehot[i, y_true[i]] = 1\n            \n        for i in range(num_classes):\n            fpr, tpr, _ = roc_curve(y_true_onehot[:, i], y_scores[:, i])\n            roc_auc = auc(fpr, tpr)\n            plt.plot(fpr, tpr, lw=2, \n                    label=f'{class_names[i]} (AUC = {roc_auc:.4f})')\n        \n        # 绘制随机猜测的基线 Построение базовой линии для случайного угадывания\n        plt.plot([0, 1], [0, 1], 'k--', lw=2)\n        plt.xlim([0.0, 1.0])\n        plt.ylim([0.0, 1.05])\n        plt.xlabel('Доля ложноположительных результатов')\n        plt.ylabel('Доля истинно положительных результатов')\n        plt.title('ROC-кривая (для каждого класса)')\n        plt.legend(loc=\"lower right\")\n        plt.tight_layout()\n        plt.savefig(os.path.join(XGBOOST_RESULTS_DIR, \"xgboost_roc_curves.png\"))\n        plt.show()\n    except Exception as e:\n        print(f\"绘制ROC曲线时出错 / Ошибка при построении ROC-кривой: {e}\")\n    \n    # 3. 保存评估指标摘要 Сохранение сводки показателей оценки\n    metrics_summary = {\n        'accuracy': evaluation_results['metrics']['accuracy'],\n        'f1_weighted': evaluation_results['metrics']['f1_weighted'],\n        'precision_weighted': evaluation_results['metrics']['precision_weighted'],\n        'recall_weighted': evaluation_results['metrics']['recall_weighted'],\n        'auc_score': evaluation_results['metrics']['auc_score'],\n        'best_iteration': model.best_iteration\n    }\n    \n    with open(os.path.join(XGBOOST_RESULTS_DIR, \"xgboost_metrics_summary.json\"), \"w\") as f:\n        json.dump(metrics_summary, f, indent=4)\n    \n    # 4. 保存分类报告 Сохранение отчета о классификации\n    with open(os.path.join(XGBOOST_RESULTS_DIR, \"xgboost_classification_report.json\"), \"w\") as f:\n        json.dump(evaluation_results['classification_report'], f, indent=4)\n    \n    # 5. 保存特征重要性 Сохранение важности признаков\n    evaluation_results['feature_importance'].to_csv(\n        os.path.join(XGBOOST_RESULTS_DIR, \"xgboost_feature_importance.csv\"), \n        index=False\n    )\n    \n    print(\"\\nВсе результаты визуализации сохранены в:\", XGBOOST_RESULTS_DIR)\n\n# ======= 预测函数 Функция предсказания =======\ndef predict_with_xgboost_model(model, data, scaler=None):\n    \"\"\"使用训练好的XGBoost模型进行预测\n    Предсказание с использованием обученной модели XGBoost\"\"\"\n    \n    # 确保数据为DataFrame Убедитесь, что данные являются DataFrame\n    if not isinstance(data, pd.DataFrame):\n        data = pd.DataFrame(data)\n    \n    # 应用缩放器（如果提供） Применение масштабирования (если предоставлено)\n    if scaler is not None:\n        data = pd.DataFrame(scaler.transform(data), columns=data.columns)\n    \n    # 转换为DMatrix Преобразование в DMatrix\n    dtest = xgb.DMatrix(data)\n    \n    # 预测概率 Предсказание вероятностей\n    y_scores = model.predict(dtest)\n    \n    # 对于多分类问题，reshape概率输出 Для задач с несколькими классами изменение формы выходных вероятностей\n    num_classes = model.attr('num_class')\n    if num_classes is not None and int(num_classes) > 2:\n        num_classes = int(num_classes)\n        y_scores = y_scores.reshape(-1, num_classes)\n    \n    # 获取预测的类别 Получение предсказанных классов\n    y_pred = np.argmax(y_scores, axis=1)\n    \n    return {\n        'predictions': y_pred,\n        'probabilities': y_scores\n    }\n\n# ======= 交叉验证函数 Функция перекрестной проверки =======\ndef xgboost_cross_validation(processed_data, params=None, num_boost_round=1000, nfold=5, \n                            stratified=True, shuffle=True, early_stopping_rounds=50):\n    \"\"\"使用交叉验证训练XGBoost模型\n    Обучение модели XGBoost с использованием перекрестной проверки\"\"\"\n    \n    train_data = processed_data['train_data']\n    num_classes = processed_data['num_classes']\n    \n    # 使用默认参数或自定义参数 Использование параметров по умолчанию или пользовательских параметров\n    if params is None:\n        params = get_default_xgboost_params(num_classes)\n    else:\n        # 确保num_class参数正确 Обеспечение правильности параметра num_class\n        params['num_class'] = num_classes\n    \n    print(\"XGBoost交叉验证参数 / Параметры кросс-валидации XGBoost:\")\n    print(json.dumps(params, indent=4))\n    \n    # 执行交叉验证 Выполнение перекрестной проверки\n    print(f\"\\n开始{nfold}折交叉验证 / Начало {nfold}-кратной перекрестной проверки...\")\n    start_time = time.time()\n    \n    cv_results = xgb.cv(\n        params,\n        train_data,\n        num_boost_round=num_boost_round,\n        nfold=nfold,\n        stratified=stratified,\n        shuffle=shuffle,\n        early_stopping_rounds=early_stopping_rounds,\n        metrics='mlogloss',\n        verbose_eval=20,\n        seed=42\n    )\n    \n    cv_time = time.time() - start_time\n    print(f\"交叉验证完成 / Перекрестная проверка завершена! 用时 / Время: {cv_time:.2f}秒\")\n    \n    # 获取最佳迭代次数 Получение лучшей итерации\n    best_iteration = len(cv_results['test-mlogloss-mean'])\n    if early_stopping_rounds > 0:\n        # Find the iteration with minimum validation error Нахождение итерации с минимальной ошибкой валидации\n        best_score_idx = np.argmin(cv_results['test-mlogloss-mean'])\n        best_iteration = best_score_idx + 1\n        \n    print(f\"交叉验证最佳迭代次数 / Лучшая итерация при перекрестной проверке: {best_iteration}\")\n    print(f\"最佳验证分数 / Лучший показатель проверки: {cv_results['test-mlogloss-mean'][best_iteration-1]:.6f} ± {cv_results['test-mlogloss-std'][best_iteration-1]:.6f}\")\n    \n    # 使用找到的最佳迭代次数重新训练模型 Повторное обучение модели с использованием найденной лучшей итерации\n    print(f\"\\n使用最佳迭代次数重新训练最终模型 / Повторное обучение окончательной модели с использованием лучшей итерации...\")\n    \n    final_model = xgb.train(\n        params,\n        train_data,\n        num_boost_round=best_iteration\n    )\n    \n    return final_model, cv_results, best_iteration\n\n# ======= 主函数 Основная функция =======\ndef run_xgboost_training(train_X, train_y, val_X, val_y, test_X, test_y, config=None):\n    \"\"\"运行XGBoost模型训练与评估的完整流程\n    Запуск полного процесса обучения и оценки модели XGBoost\"\"\"\n    \n    print(\"=== Начало обучения и оценки XGBoost-модели для определения замораживания походки при болезни Паркинсона ===\")\n    start_time = time.time()\n    \n    # 默认配置 Конфигурация по умолчанию\n    default_config = {\n        'num_boost_round': 1000,\n        'early_stopping_rounds': 50,\n        'scale_features': True,\n        'handle_imbalance': True,\n        'use_gpu': True,\n        'use_cv': False,\n        'cv_folds': 5\n    }\n    \n    # 合并配置 Объединение конфигураций\n    if config:\n        config = {**default_config, **config}\n    else:\n        config = default_config\n    \n    # 自定义模型参数 Пользовательские параметры модели\n    model_params = config.get('model_params', None)\n    if model_params is None:\n        model_params = {}\n    \n    # 确保model_params中包含必要的多类参数 Обеспечение наличия необходимых многоклассовых параметров в model_params\n    if 'objective' not in model_params:\n        model_params['objective'] = 'multi:softprob'\n    if 'eval_metric' not in model_params:\n        model_params['eval_metric'] = 'mlogloss'\n    \n    print(\"模型配置 / Конфигурация модели:\")\n    print_config = {**config}\n    print_config['model_params'] = model_params\n    print(json.dumps(print_config, indent=4))\n    \n    try:\n        # 1. 数据预处理 Предварительная обработка данных\n        print(\"\\n执行数据预处理 / Выполнение предварительной обработки данных...\")\n        processed_data = preprocess_data_for_xgboost(\n            train_X, train_y, val_X, val_y, test_X, test_y,\n            scale_features=config['scale_features']\n        )\n        \n        # 2. 训练模型 Обучение модели\n        print(\"\\n开始模型训练 / Начало обучения модели...\")\n        \n        if config['use_cv']:\n            # 使用交叉验证 Использование перекрестной проверки\n            model, cv_results, best_iteration = xgboost_cross_validation(\n                processed_data,\n                params=model_params,\n                num_boost_round=config['num_boost_round'],\n                nfold=config['cv_folds'],\n                early_stopping_rounds=config['early_stopping_rounds']\n            )\n            \n            # 创建一个简单的评估历史记录 Создание простой истории оценки для совместимости с визуализацией\n            evals_result = {\n                'train': {\n                    'mlogloss': cv_results['train-mlogloss-mean'].tolist()\n                },\n                'validation': {\n                    'mlogloss': cv_results['test-mlogloss-mean'].tolist()\n                }\n            }\n        else:\n            # 正常训练 Обычное обучение\n            model, evals_result = train_xgboost_model(\n                processed_data,\n                params=model_params,\n                num_boost_round=config['num_boost_round'],\n                early_stopping_rounds=config['early_stopping_rounds'],\n                handle_imbalance=config['handle_imbalance']\n            )\n        \n        # 3. 评估模型 Оценка модели\n        print(\"\\n评估模型性能 / Оценка производительности модели...\")\n        evaluation_results = evaluate_xgboost_model(\n            model, \n            processed_data['test_data'], \n            processed_data['test_y'],\n            processed_data\n        )\n        \n        # 添加测试标签到评估结果 Добавление тестовых меток к результатам оценки\n        evaluation_results['test_y'] = processed_data['test_y']\n        \n        # 4. 可视化结果 Визуализация результатов\n        print(\"\\n生成可视化结果 / Создание результатов визуализации...\")\n        visualize_xgboost_results(model, evals_result, evaluation_results)\n        \n        # 5. 保存模型 Сохранение модели\n        model_path = os.path.join(XGBOOST_RESULTS_DIR, \"xgboost_model.json\")\n        model.save_model(model_path)\n        print(f\"模型已保存至 / Модель сохранена в: {model_path}\")\n        \n        # 6. 保存标准化器 Сохранение нормализатора\n        if processed_data['scaler'] is not None:\n            scaler_path = os.path.join(XGBOOST_RESULTS_DIR, \"xgboost_scaler.pkl\")\n            joblib.dump(processed_data['scaler'], scaler_path)\n            print(f\"特征标准化器已保存至 / Нормализатор признаков сохранен в: {scaler_path}\")\n        \n        # 7. 保存特征重要性 Сохранение важности признаков\n        feature_importance_path = os.path.join(XGBOOST_RESULTS_DIR, \"xgboost_feature_importance.csv\")\n        evaluation_results['feature_importance'].to_csv(feature_importance_path, index=False)\n        print(f\"特征重要性已保存至 / Важность признаков сохранена в: {feature_importance_path}\")\n        \n        # 计算总运行时间 Расчет общего времени выполнения\n        total_time = time.time() - start_time\n        hours, rem = divmod(total_time, 3600)\n        minutes, seconds = divmod(rem, 60)\n        print(f\"\\n执行完成！总时间 / Выполнение завершено! Общее время: {int(hours):02d}:{int(minutes):02d}:{seconds:05.2f}\")\n        \n        # 打印结果摘要 Вывод сводки результатов\n        print(\"\\n===== Сводка производительности модели =====\")\n        print(f\"Точность на тестовом наборе: {evaluation_results['metrics']['accuracy']:.4f}\")\n        print(f\"Взвешенный F1-показатель: {evaluation_results['metrics']['f1_weighted']:.4f}\")\n        if evaluation_results['metrics']['auc_score']:\n            print(f\"AUC-ROC: {evaluation_results['metrics']['auc_score']:.4f}\")\n        \n        return model, evals_result, evaluation_results, processed_data['scaler']\n        \n    except Exception as e:\n        print(f\"训练过程中出错 / Ошибка во время обучения: {e}\")\n        import traceback\n        traceback.print_exc()\n        return None, None, None, None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:00:44.857505Z","iopub.execute_input":"2025-04-27T17:00:44.857746Z","iopub.status.idle":"2025-04-27T17:00:45.364552Z","shell.execute_reply.started":"2025-04-27T17:00:44.857726Z","shell.execute_reply":"2025-04-27T17:00:45.363643Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    # 加载数据\n    # Load data\n    \n    # 运行XGBoost模型训练\n    # Run XGBoost model training\n    model, evals_result, evaluation_results, scaler = run_xgboost_training(\n        train_X, train_y, val_X, val_y, test_X, test_y,\n        config={\n            'num_boost_round': 1500,\n            'early_stopping_rounds': 50,\n            'handle_imbalance': True,\n            'use_gpu': True,\n            'model_params': {\n                'learning_rate': 0.005,\n                'max_depth': 6,\n                'min_child_weight': 3,\n                'subsample': 0.8,\n                'colsample_bytree': 0.8\n            }\n        }\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:00:45.365895Z","iopub.execute_input":"2025-04-27T17:00:45.366332Z","iopub.status.idle":"2025-04-27T17:01:36.887644Z","shell.execute_reply.started":"2025-04-27T17:00:45.366310Z","shell.execute_reply":"2025-04-27T17:01:36.886823Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# LightGBM","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport time\nimport json\nimport gc\nimport joblib\n\nimport lightgbm as lgb\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import (\n    classification_report, f1_score, roc_auc_score, confusion_matrix, \n    roc_curve, auc, accuracy_score, precision_score, recall_score\n)\nfrom sklearn.utils.class_weight import compute_class_weight\n\n# ======= 常量定义 Определение констант =======\nOUTPUT_DIR = \"/kaggle/working\"\nLGBM_RESULTS_DIR = os.path.join(OUTPUT_DIR, \"lgbm_results\")\nos.makedirs(LGBM_RESULTS_DIR, exist_ok=True)\n\n# ======= 辅助函数 Вспомогательные функции =======\ndef preprocess_data_for_lgbm(train_X, train_y, val_X, val_y, test_X, test_y, scale_features=True):\n    \"\"\"预处理数据以适应LightGBM模型\n    Предварительная обработка данных для модели LightGBM\"\"\"\n    \n    # 确保数据为DataFrame或numpy数组 Убедитесь, что данные являются DataFrame или массивом numpy\n    if isinstance(train_X, pd.DataFrame):\n        train_X_df = train_X.copy()\n        feature_names = train_X.columns.tolist()\n    else:\n        train_X_df = pd.DataFrame(train_X)\n        feature_names = [f\"feature_{i}\" for i in range(train_X.shape[1])]\n        \n    if isinstance(val_X, pd.DataFrame):\n        val_X_df = val_X.copy()\n    else:\n        val_X_df = pd.DataFrame(val_X, columns=feature_names)\n        \n    if isinstance(test_X, pd.DataFrame):\n        test_X_df = test_X.copy()\n    else:\n        test_X_df = pd.DataFrame(test_X, columns=feature_names)\n    \n    # 特征缩放（可选） Масштабирование признаков (опционально)\n    scaler = None\n    if scale_features:\n        scaler = StandardScaler()\n        train_X_scaled = scaler.fit_transform(train_X_df)\n        val_X_scaled = scaler.transform(val_X_df)\n        test_X_scaled = scaler.transform(test_X_df)\n        \n        train_X_df = pd.DataFrame(train_X_scaled, columns=feature_names)\n        val_X_df = pd.DataFrame(val_X_scaled, columns=feature_names)\n        test_X_df = pd.DataFrame(test_X_scaled, columns=feature_names)\n    \n    # 处理标签 - 修复版本，正确处理全0向量 Обработка меток - исправленная версия, правильно обрабатывающая полностью нулевые векторы\n    def convert_labels(y):\n        if isinstance(y, pd.DataFrame) and y.shape[1] > 1:\n            # 检查每行是否为全0 Проверка, является ли каждая строка полностью нулевой\n            all_zeros = (y.sum(axis=1) == 0)\n            \n            # 为非零行使用argmax Использование argmax для ненулевых строк\n            y_indices = y.values.argmax(axis=1)\n            \n            # 为全0行分配特殊类别标签（0），并将其他类别向上移动 Назначение специальной метки класса (0) для полностью нулевых строк и сдвиг других классов вверх\n            y_indices = y_indices + 1  # 将所有非零类别向上移动1 Сдвиг всех ненулевых классов вверх на 1\n            y_indices[all_zeros] = 0   # 全0类别设为0 Установка полностью нулевого класса на 0\n            \n            return y_indices\n            \n        elif isinstance(y, np.ndarray) and y.ndim > 1 and y.shape[1] > 1:\n            # 检查每行是否为全0 Проверка, является ли каждая строка полностью нулевой\n            all_zeros = (np.sum(y, axis=1) == 0)\n            \n            # 为非零行使用argmax Использование argmax для ненулевых строк\n            y_indices = np.argmax(y, axis=1)\n            \n            # 为全0行分配特殊类别标签（0），并将其他类别向上移动 Назначение специальной метки класса (0) для полностью нулевых строк и сдвиг других классов вверх\n            y_indices = y_indices + 1  # 将所有非零类别向上移动1 Сдвиг всех ненулевых классов вверх на 1\n            y_indices[all_zeros] = 0   # 全0类别设为0 Установка полностью нулевого класса на 0\n            \n            return y_indices\n        else:\n            # 如果不是one-hot编码，直接返回 Если не в формате one-hot, просто вернуть\n            return y\n    \n    # 转换训练、验证和测试标签 Преобразование меток обучающего, валидационного и тестового наборов\n    train_y_indices = convert_labels(train_y)\n    val_y_indices = convert_labels(val_y)\n    test_y_indices = convert_labels(test_y)\n    \n    # 获取类别数量 Получение количества классов\n    num_classes = len(np.unique(train_y_indices))\n    \n    # 分析类别分布 Анализ распределения классов\n    class_counts = np.bincount(train_y_indices)\n    print(f\"类别分布 / Распределение классов: {dict(zip(range(len(class_counts)), class_counts))}\")\n    \n    # 创建LightGBM数据集 Создание наборов данных LightGBM\n    train_data = lgb.Dataset(\n        train_X_df, \n        label=train_y_indices, \n        feature_name=feature_names\n    )\n    \n    val_data = lgb.Dataset(\n        val_X_df, \n        label=val_y_indices, \n        feature_name=feature_names,\n        reference=train_data\n    )\n    \n    return {\n        'train_data': train_data,\n        'val_data': val_data,\n        'test_X': test_X_df,\n        'test_y': test_y_indices,\n        'feature_names': feature_names,\n        'num_classes': num_classes,\n        'scaler': scaler\n    }\n\ndef compute_sample_weights(y):\n    \"\"\"计算样本权重以处理类别不平衡\n    Вычисление весов образцов для обработки несбалансированности классов\"\"\"\n    classes = np.unique(y)\n    class_weights = compute_class_weight('balanced', classes=classes, y=y)\n    sample_weights = np.ones_like(y, dtype=np.float64)\n    \n    for idx, cls in enumerate(classes):\n        sample_weights[y == cls] = class_weights[idx]\n    \n    return sample_weights\n\ndef calculate_metrics_lgbm(y_true, y_pred, y_scores):\n    \"\"\"计算各种评估指标\n    Расчет различных показателей оценки\"\"\"\n    \n    # 计算准确率 Расчет точности\n    accuracy = accuracy_score(y_true, y_pred)\n    \n    # 计算加权F1分数 Расчет взвешенного F1-показателя\n    f1_weighted = f1_score(y_true, y_pred, average='weighted')\n    \n    # 计算各类别F1分数 Расчет F1-показателя для каждого класса\n    f1_per_class = f1_score(y_true, y_pred, average=None)\n    \n    # 计算精确率和召回率 Расчет точности и полноты\n    precision_weighted = precision_score(y_true, y_pred, average='weighted')\n    recall_weighted = recall_score(y_true, y_pred, average='weighted')\n    \n    # 尝试计算ROC AUC Попытка расчета ROC AUC\n    try:\n        # 对于多分类问题，转换为one-hot编码 Для задач с несколькими классами преобразование в формат one-hot\n        n_classes = y_scores.shape[1]\n        y_true_onehot = np.zeros((len(y_true), n_classes))\n        for i in range(len(y_true)):\n            y_true_onehot[i, y_true[i]] = 1\n            \n        auc_score = roc_auc_score(y_true_onehot, y_scores, multi_class='ovr')\n    except Exception as e:\n        print(f\"计算ROC AUC时出错 / Ошибка при расчете ROC AUC: {e}\")\n        auc_score = None\n    \n    return {\n        'accuracy': accuracy,\n        'f1_weighted': f1_weighted,\n        'f1_per_class': f1_per_class,\n        'precision_weighted': precision_weighted,\n        'recall_weighted': recall_weighted,\n        'auc_score': auc_score\n    }\n\n# ======= LightGBM参数配置 Конфигурация параметров LightGBM =======\ndef get_default_lgbm_params(num_classes, use_gpu=True):\n    \"\"\"获取默认的LightGBM参数\n    Получение параметров LightGBM по умолчанию\"\"\"\n    \n    params = {\n        'objective': 'multiclass',\n        'metric': 'multi_logloss',\n        'num_class': num_classes,  # 这将正确设置为4个类别 Это правильно устанавливает 4 класса\n        'verbosity': -1,\n        'boosting_type': 'gbdt',\n        'learning_rate': 0.05,\n        'max_depth': 7,\n        'num_leaves': 31,\n        'feature_fraction': 0.8,\n        'bagging_fraction': 0.8,\n        'bagging_freq': 5,\n        'min_data_in_leaf': 50,\n        'lambda_l1': 0.1,\n        'lambda_l2': 0.1,\n        'seed': 42\n    }\n    \n    # 如果使用GPU，添加GPU相关参数 Если используется GPU, добавляются параметры, связанные с GPU\n    if use_gpu:\n        params.update({\n            'device': 'gpu',\n            'gpu_platform_id': 0,\n            'gpu_device_id': 0,\n            'max_bin': 63,\n            'gpu_use_dp': True\n        })\n    \n    return params\n\n# ======= 训练函数 Функция обучения =======\ndef train_lgbm_model(\n    processed_data, \n    params=None, \n    num_boost_round=1000, \n    early_stopping_rounds=50,\n    handle_imbalance=True\n):\n    \"\"\"训练LightGBM模型\n    Обучение модели LightGBM\"\"\"\n    \n    train_data = processed_data['train_data']\n    val_data = processed_data['val_data']\n    num_classes = processed_data['num_classes']\n    \n    # 使用默认参数或自定义参数 Использование параметров по умолчанию или пользовательских параметров\n    if params is None:\n        params = get_default_lgbm_params(num_classes)\n    else:\n        # 确保必要的参数存在 Обеспечение наличия необходимых параметров\n        params['num_class'] = num_classes\n        if 'objective' not in params:\n            params['objective'] = 'multiclass'\n        if 'metric' not in params:\n            params['metric'] = 'multi_logloss'\n    \n    print(\"LightGBM参数 / Параметры LightGBM:\")\n    print(json.dumps(params, indent=4))\n    \n    # 处理类别不平衡（如果需要） Обработка несбалансированности классов (если необходимо)\n    if handle_imbalance:\n        # 获取训练标签 Получение меток обучения\n        train_labels = train_data.get_label()\n        \n        # 计算类权重 Расчет весов классов\n        classes = np.unique(train_labels)\n        class_weights = compute_class_weight('balanced', classes=classes, y=train_labels)\n        class_weight_dict = {i: weight for i, weight in zip(classes, class_weights)}\n        \n        print(f\"类别权重 / Веса классов: {class_weight_dict}\")\n        \n        # 为每个样本分配权重 Назначение весов для каждого образца\n        # LightGBM不直接支持class_weight，而是使用样本权重 LightGBM не поддерживает class_weight напрямую, вместо этого используются веса образцов\n        sample_weights = np.ones_like(train_labels, dtype=np.float32)\n        for idx, label in enumerate(train_labels):\n            sample_weights[idx] = class_weight_dict[label]\n        \n        # 设置样本权重 Установка весов образцов\n        train_data.set_weight(sample_weights)\n    \n    # 用于存储评估结果的字典 Словарь для хранения результатов оценки\n    evals_result = {}\n    \n    # 训练回调 Обратные вызовы при обучении\n    callbacks = [\n        lgb.early_stopping(stopping_rounds=early_stopping_rounds, verbose=True),\n        lgb.log_evaluation(period=20),\n        lgb.record_evaluation(evals_result)\n    ]\n    \n    # 开始训练 Начало обучения\n    print(f\"\\n开始LightGBM训练 / Начало обучения LightGBM (max_rounds={num_boost_round}, early_stopping={early_stopping_rounds})...\")\n    start_time = time.time()\n    \n    model = lgb.train(\n        params,\n        train_data,\n        num_boost_round=num_boost_round,\n        valid_sets=[train_data, val_data],\n        valid_names=['train', 'validation'],\n        callbacks=callbacks\n    )\n    \n    training_time = time.time() - start_time\n    print(f\"训练完成 / Обучение завершено! 用时 / Время: {training_time:.2f}秒\")\n    \n    # 获取最佳迭代次数 Получение лучшей итерации\n    best_iteration = model.best_iteration\n    print(f\"最佳迭代次数 / Лучшая итерация: {best_iteration}\")\n    \n    return model, evals_result\n\n# ======= 评估函数 Функция оценки =======\ndef evaluate_lgbm_model(model, test_X, test_y, processed_data=None):\n    \"\"\"评估LightGBM模型\n    Оценка модели LightGBM\"\"\"\n    \n    feature_names = None\n    if processed_data is not None:\n        feature_names = processed_data.get('feature_names')\n    \n    # 预测概率 Предсказание вероятностей\n    y_scores = model.predict(test_X, num_iteration=model.best_iteration)\n    \n    # 获取预测的类别 Получение предсказанных классов\n    y_pred = np.argmax(y_scores, axis=1)\n    \n    # 计算评估指标 Расчет показателей оценки\n    metrics = calculate_metrics_lgbm(test_y, y_pred, y_scores)\n    \n    # 打印详细指标 Вывод подробных показателей\n    print(f\"\\n===== 模型评估指标 / Показатели оценки модели =====\")\n    print(f\"准确率 / Точность: {metrics['accuracy']:.4f}\")\n    print(f\"加权F1分数 / Взвешенный F1-показатель: {metrics['f1_weighted']:.4f}\")\n    print(f\"加权精确率 / Взвешенная точность: {metrics['precision_weighted']:.4f}\")\n    print(f\"加权召回率 / Взвешенная полнота: {metrics['recall_weighted']:.4f}\")\n    print(f\"各类别F1分数 / F1-показатель для каждого класса: {metrics['f1_per_class']}\")\n    if metrics['auc_score']:\n        print(f\"ROC AUC: {metrics['auc_score']:.4f}\")\n    \n    # 计算混淆矩阵 Расчет матрицы ошибок\n    cm = confusion_matrix(test_y, y_pred)\n    \n    # 生成分类报告 - 更新为4个类别 Создание отчета о классификации - обновление для 4 классов\n    class_names = ['Нет категории', 'Начало нерешительности', 'Поворот', 'Ходьба']\n    report = classification_report(test_y, y_pred, target_names=class_names, output_dict=True)\n    \n    # 获取特征重要性 Получение важности признаков\n    feature_importance = model.feature_importance(importance_type='gain')\n    feature_names = feature_names or [f\"feature_{i}\" for i in range(len(feature_importance))]\n    feature_importance_df = pd.DataFrame({\n        'Feature': feature_names,\n        'Importance': feature_importance\n    }).sort_values(by='Importance', ascending=False)\n    \n    return {\n        'metrics': metrics,\n        'confusion_matrix': cm,\n        'classification_report': report,\n        'predictions': y_pred,\n        'probabilities': y_scores,\n        'feature_importance': feature_importance_df\n    }\n\n# ======= 可视化函数 Функция визуализации =======\ndef visualize_lgbm_results(model, evals_result, evaluation_results):\n    \"\"\"可视化LightGBM模型训练结果和性能\n    Визуализация результатов обучения и производительности модели LightGBM\"\"\"\n    \n    # 1. 绘制训练历史 Построение истории обучения\n    plt.figure(figsize=(15, 5))\n    \n    # 训练和验证损失曲线 Кривые потерь при обучении и валидации\n    plt.subplot(1, 3, 1)\n    if evals_result and 'train' in evals_result and 'multi_logloss' in evals_result['train']:\n        epochs = len(evals_result['train']['multi_logloss'])\n        x_axis = range(1, epochs + 1)\n        plt.plot(x_axis, evals_result['train']['multi_logloss'], label='Потери при обучении')\n        plt.plot(x_axis, evals_result['validation']['multi_logloss'], label='Потери при валидации')\n        plt.axvline(x=model.best_iteration, color='red', linestyle='--', label=f'Лучшая итерация: {model.best_iteration}')\n    else:\n        # 如果没有评估历史，直接显示一条提示信息 Если нет истории оценки, просто отображается сообщение\n        plt.text(0.5, 0.5, 'Данные об истории обучения недоступны', \n                horizontalalignment='center', verticalalignment='center',\n                transform=plt.gca().transAxes)\n    \n    plt.title('Потери модели')\n    plt.xlabel('Итерации')\n    plt.ylabel('Лог-потери')\n    plt.legend()\n    \n    # 特征重要性图 График важности признаков\n    plt.subplot(1, 3, 2)\n    if 'feature_importance' in evaluation_results:\n        feature_importance = evaluation_results['feature_importance']\n        top_features = feature_importance.head(min(15, len(feature_importance)))\n        sns.barplot(x='Importance', y='Feature', data=top_features)\n        plt.title('Важность признаков (топ-15)')\n    else:\n        plt.text(0.5, 0.5, 'Данные о важности признаков недоступны', \n                horizontalalignment='center', verticalalignment='center',\n                transform=plt.gca().transAxes)\n    \n    # 混淆矩阵 - 更新类别名称为4个类别 Матрица ошибок - обновление имен классов для 4 классов\n    plt.subplot(1, 3, 3)\n    if 'confusion_matrix' in evaluation_results:\n        cm = evaluation_results['confusion_matrix']\n        class_names = ['Нет категории', 'Начало нерешительности', 'Поворот', 'Ходьба']\n        sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n                    xticklabels=class_names, \n                    yticklabels=class_names)\n        plt.xlabel('Предсказанная метка')\n        plt.ylabel('Истинная метка')\n        plt.title('Матрица ошибок')\n    else:\n        plt.text(0.5, 0.5, 'Данные о матрице ошибок недоступны', \n                horizontalalignment='center', verticalalignment='center',\n                transform=plt.gca().transAxes)\n    \n    plt.tight_layout()\n    plt.savefig(os.path.join(LGBM_RESULTS_DIR, \"lgbm_training_results.png\"))\n    plt.show()\n    \n    # 2. ROC曲线 ROC-кривая\n    try:\n        y_scores = evaluation_results['probabilities']\n        y_true = evaluation_results['metrics']['true_labels'] if 'true_labels' in evaluation_results['metrics'] else None\n        \n        if y_true is None and 'predictions' in evaluation_results and 'probabilities' in evaluation_results:\n            y_pred = evaluation_results['predictions']\n            y_scores = evaluation_results['probabilities']\n            # 创建一个虚拟的y_true数组，基于测试集 Создание фиктивного массива y_true на основе тестового набора\n            y_true = np.zeros_like(y_pred)\n            # 这里我们假设评估结果字典中有test_y Здесь мы предполагаем, что в словаре результатов оценки есть test_y\n            if 'test_y' in evaluation_results:\n                y_true = evaluation_results['test_y']\n        \n        plt.figure(figsize=(10, 8))\n        \n        # 为每个类别绘制ROC曲线 Построение ROC-кривой для каждого класса\n        num_classes = y_scores.shape[1]\n        y_true_onehot = np.zeros((len(y_true), num_classes))\n        for i in range(len(y_true)):\n            y_true_onehot[i, y_true[i]] = 1\n            \n        # 更新类别名称为4个类别 Обновление имен классов для 4 классов\n        class_names = ['Нет категории', 'Начало нерешительности', 'Поворот', 'Ходьба']\n        for i in range(num_classes):\n            fpr, tpr, _ = roc_curve(y_true_onehot[:, i], y_scores[:, i])\n            roc_auc = auc(fpr, tpr)\n            plt.plot(fpr, tpr, lw=2, \n                    label=f'{class_names[i]} (AUC = {roc_auc:.4f})')\n        \n        # 绘制随机猜测的基线 Построение базовой линии для случайного угадывания\n        plt.plot([0, 1], [0, 1], 'k--', lw=2)\n        plt.xlim([0.0, 1.0])\n        plt.ylim([0.0, 1.05])\n        plt.xlabel('Доля ложноположительных результатов')\n        plt.ylabel('Доля истинно положительных результатов')\n        plt.title('ROC-кривая (для каждого класса)')\n        plt.legend(loc=\"lower right\")\n        plt.tight_layout()\n        plt.savefig(os.path.join(LGBM_RESULTS_DIR, \"lgbm_roc_curves.png\"))\n        plt.show()\n    except Exception as e:\n        print(f\"绘制ROC曲线时出错 / Ошибка при построении ROC-кривой: {e}\")\n    \n    # 3. 保存评估指标摘要 Сохранение сводки показателей оценки\n    metrics_summary = {\n        'accuracy': evaluation_results['metrics']['accuracy'],\n        'f1_weighted': evaluation_results['metrics']['f1_weighted'],\n        'precision_weighted': evaluation_results['metrics']['precision_weighted'],\n        'recall_weighted': evaluation_results['metrics']['recall_weighted'],\n        'auc_score': evaluation_results['metrics']['auc_score'],\n        'best_iteration': model.best_iteration\n    }\n    \n    with open(os.path.join(LGBM_RESULTS_DIR, \"lgbm_metrics_summary.json\"), \"w\") as f:\n        json.dump(metrics_summary, f, indent=4)\n    \n    # 4. 保存分类报告 Сохранение отчета о классификации\n    with open(os.path.join(LGBM_RESULTS_DIR, \"lgbm_classification_report.json\"), \"w\") as f:\n        json.dump(evaluation_results['classification_report'], f, indent=4)\n    \n    # 5. 保存特征重要性 Сохранение важности признаков\n    evaluation_results['feature_importance'].to_csv(\n        os.path.join(LGBM_RESULTS_DIR, \"lgbm_feature_importance.csv\"), \n        index=False\n    )\n    \n    print(\"\\nВсе результаты визуализации сохранены в:\", LGBM_RESULTS_DIR)\n\n# ======= 预测函数 Функция предсказания =======\ndef predict_with_lgbm_model(model, data, scaler=None):\n    \"\"\"使用训练好的LightGBM模型进行预测\n    Предсказание с использованием обученной модели LightGBM\"\"\"\n    \n    # 确保数据为DataFrame Убедитесь, что данные являются DataFrame\n    if not isinstance(data, pd.DataFrame):\n        data = pd.DataFrame(data)\n    \n    # 应用缩放器（如果提供） Применение масштабирования (если предоставлено)\n    if scaler is not None:\n        data = pd.DataFrame(scaler.transform(data), columns=data.columns)\n    \n    # 预测概率 Предсказание вероятностей\n    y_scores = model.predict(data, num_iteration=model.best_iteration)\n    \n    # 获取预测的类别 Получение предсказанных классов\n    y_pred = np.argmax(y_scores, axis=1)\n    \n    return {\n        'predictions': y_pred,\n        'probabilities': y_scores\n    }\n\n# ======= 交叉验证函数 Функция перекрестной проверки =======\ndef lgbm_cross_validation(processed_data, params=None, num_boost_round=1000, nfold=5, \n                          stratified=True, shuffle=True, early_stopping_rounds=50):\n    \"\"\"使用交叉验证训练LightGBM模型\n    Обучение модели LightGBM с использованием перекрестной проверки\"\"\"\n    \n    train_data = processed_data['train_data']\n    num_classes = processed_data['num_classes']\n    \n    # 使用默认参数或自定义参数 Использование параметров по умолчанию или пользовательских параметров\n    if params is None:\n        params = get_default_lgbm_params(num_classes)\n    else:\n        # 确保num_class参数正确 Обеспечение правильности параметра num_class\n        params['num_class'] = num_classes\n    \n    print(\"LightGBM交叉验证参数 / Параметры кросс-валидации LightGBM:\")\n    print(json.dumps(params, indent=4))\n    \n    # 执行交叉验证 Выполнение перекрестной проверки\n    print(f\"\\n开始{nfold}折交叉验证 / Начало {nfold}-кратной перекрестной проверки...\")\n    start_time = time.time()\n    \n    cv_results = lgb.cv(\n        params,\n        train_data,\n        num_boost_round=num_boost_round,\n        nfold=nfold,\n        stratified=stratified,\n        shuffle=shuffle,\n        early_stopping_rounds=early_stopping_rounds,\n        metrics='multi_logloss',\n        verbose_eval=20,\n        seed=42\n    )\n    \n    cv_time = time.time() - start_time\n    print(f\"交叉验证完成 / Перекрестная проверка завершена! 用时 / Время: {cv_time:.2f}秒\")\n    \n    # 获取最佳迭代次数 Получение лучшей итерации\n    best_iteration = len(cv_results['multi_logloss-mean'])\n    if early_stopping_rounds > 0:\n        best_iteration = np.argmin(cv_results['valid multi_logloss-mean']) + 1\n        \n    print(f\"交叉验证最佳迭代次数 / Лучшая итерация при перекрестной проверке: {best_iteration}\")\n    print(f\"最佳验证分数 / Лучший показатель проверки: {cv_results['valid multi_logloss-mean'][best_iteration-1]:.6f} ± {cv_results['valid multi_logloss-stdv'][best_iteration-1]:.6f}\")\n    \n    # 使用找到的最佳迭代次数重新训练模型 Повторное обучение модели с использованием найденной лучшей итерации\n    print(f\"\\n使用最佳迭代次数重新训练最终模型 / Повторное обучение окончательной модели с использованием лучшей итерации...\")\n    \n    final_model = lgb.train(\n        params,\n        train_data,\n        num_boost_round=best_iteration\n    )\n    \n    return final_model, cv_results, best_iteration\n\n# ======= 主函数 Основная функция =======\ndef run_lgbm_training(train_X, train_y, val_X, val_y, test_X, test_y, config=None):\n    \"\"\"运行LightGBM模型训练与评估的完整流程\n    Запуск полного процесса обучения и оценки модели LightGBM\"\"\"\n    \n    print(\"=== Начало обучения и оценки LightGBM-модели для определения замораживания походки при болезни Паркинсона ===\")\n    start_time = time.time()\n    \n    # 默认配置 Конфигурация по умолчанию\n    default_config = {\n        'num_boost_round': 1000,\n        'early_stopping_rounds': 50,\n        'scale_features': True,\n        'handle_imbalance': True,\n        'use_gpu': True,\n        'use_cv': False,\n        'cv_folds': 5\n    }\n    \n    # 合并配置 Объединение конфигураций\n    if config:\n        config = {**default_config, **config}\n    else:\n        config = default_config\n    \n    # 自定义模型参数 Пользовательские параметры модели\n    model_params = config.get('model_params', None)\n    if model_params is None:\n        model_params = {}\n    \n    # 确保model_params中包含必要的多类参数 Обеспечение наличия необходимых многоклассовых параметров в model_params\n    if 'objective' not in model_params:\n        model_params['objective'] = 'multiclass'\n    if 'metric' not in model_params:\n        model_params['metric'] = 'multi_logloss'\n    \n    print(\"模型配置 / Конфигурация модели:\")\n    print_config = {**config}\n    print_config['model_params'] = model_params\n    print(json.dumps(print_config, indent=4))\n    \n    try:\n        # 1. 数据预处理 Предварительная обработка данных\n        print(\"\\n执行数据预处理 / Выполнение предварительной обработки данных...\")\n        processed_data = preprocess_data_for_lgbm(\n            train_X, train_y, val_X, val_y, test_X, test_y,\n            scale_features=config['scale_features']\n        )\n        \n        # 2. 训练模型 Обучение модели\n        print(\"\\n开始模型训练 / Начало обучения модели...\")\n        \n        if config['use_cv']:\n            # 使用交叉验证 Использование перекрестной проверки\n            model, cv_results, best_iteration = lgbm_cross_validation(\n                processed_data,\n                params=model_params,\n                num_boost_round=config['num_boost_round'],\n                nfold=config['cv_folds'],\n                early_stopping_rounds=config['early_stopping_rounds']\n            )\n            \n            # 创建一个简单的评估历史记录 Создание простой истории оценки\n            evals_result = {\n                'train': {\n                    'multi_logloss': cv_results['train multi_logloss-mean']\n                },\n                'validation': {\n                    'multi_logloss': cv_results['valid multi_logloss-mean']\n                }\n            }\n        else:\n            # 正常训练 Обычное обучение\n            model, evals_result = train_lgbm_model(\n                processed_data,\n                params=model_params,\n                num_boost_round=config['num_boost_round'],\n                early_stopping_rounds=config['early_stopping_rounds'],\n                handle_imbalance=config['handle_imbalance']\n            )\n        \n        # 3. 评估模型 Оценка модели\n        print(\"\\n评估模型性能 / Оценка производительности модели...\")\n        evaluation_results = evaluate_lgbm_model(\n            model, \n            processed_data['test_X'], \n            processed_data['test_y'],\n            processed_data\n        )\n        \n        # 添加测试标签到评估结果 Добавление тестовых меток к результатам оценки\n        evaluation_results['test_y'] = processed_data['test_y']\n        \n        # 4. 可视化结果 Визуализация результатов\n        print(\"\\n生成可视化结果 / Создание результатов визуализации...\")\n        visualize_lgbm_results(model, evals_result, evaluation_results)\n        \n        # 5. 保存模型 Сохранение модели\n        model_path = os.path.join(LGBM_RESULTS_DIR, \"lgbm_model.txt\")\n        model.save_model(model_path)\n        print(f\"模型已保存至 / Модель сохранена в: {model_path}\")\n        \n        # 6. 保存标准化器 Сохранение нормализатора\n        if processed_data['scaler'] is not None:\n            scaler_path = os.path.join(LGBM_RESULTS_DIR, \"lgbm_scaler.pkl\")\n            joblib.dump(processed_data['scaler'], scaler_path)\n            print(f\"特征标准化器已保存至 / Нормализатор признаков сохранен в: {scaler_path}\")\n        \n        # 7. 保存特征重要性 Сохранение важности признаков\n        feature_importance_path = os.path.join(LGBM_RESULTS_DIR, \"lgbm_feature_importance.csv\")\n        evaluation_results['feature_importance'].to_csv(feature_importance_path, index=False)\n        print(f\"特征重要性已保存至 / Важность признаков сохранена в: {feature_importance_path}\")\n        \n        # 计算总运行时间 Расчет общего времени выполнения\n        total_time = time.time() - start_time\n        hours, rem = divmod(total_time, 3600)\n        minutes, seconds = divmod(rem, 60)\n        print(f\"\\n执行完成！总时间 / Выполнение завершено! Общее время: {int(hours):02d}:{int(minutes):02d}:{seconds:05.2f}\")\n        \n        # 打印结果摘要 Вывод сводки результатов\n        print(\"\\n===== Сводка производительности модели =====\")\n        print(f\"Точность на тестовом наборе: {evaluation_results['metrics']['accuracy']:.4f}\")\n        print(f\"Взвешенный F1-показатель: {evaluation_results['metrics']['f1_weighted']:.4f}\")\n        if evaluation_results['metrics']['auc_score']:\n            print(f\"AUC-ROC: {evaluation_results['metrics']['auc_score']:.4f}\")\n        \n        return model, evals_result, evaluation_results, processed_data['scaler']\n        \n    except Exception as e:\n        print(f\"训练过程中出错 / Ошибка во время обучения: {e}\")\n        import traceback\n        traceback.print_exc()\n        return None, None, None, None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:01:36.888895Z","iopub.execute_input":"2025-04-27T17:01:36.889269Z","iopub.status.idle":"2025-04-27T17:01:37.964944Z","shell.execute_reply.started":"2025-04-27T17:01:36.889233Z","shell.execute_reply":"2025-04-27T17:01:37.964201Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    # 加载数据\n    # Load data\n    \n    # 运行LightGBM模型训练\n    # Run LightGBM model training\n    model, evals_result, evaluation_results, scaler = run_lgbm_training(\n        train_X, train_y, val_X, val_y, test_X, test_y,\n        config={\n            'num_boost_round': 1000,\n            'early_stopping_rounds': 50,\n            'handle_imbalance': True,\n            'use_gpu': True,\n            'model_params': {\n                'learning_rate': 0.05,\n                'max_depth': 7,\n                'num_leaves': 31,\n                'feature_fraction': 0.8\n            }\n        }\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:01:37.965789Z","iopub.execute_input":"2025-04-27T17:01:37.966070Z","iopub.status.idle":"2025-04-27T17:01:47.615426Z","shell.execute_reply.started":"2025-04-27T17:01:37.966048Z","shell.execute_reply":"2025-04-27T17:01:47.614553Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# CNN","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport time\nimport json\nimport gc\nimport joblib\nfrom collections import Counter\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.optim as optim\nfrom torch.utils.data import DataLoader, TensorDataset, WeightedRandomSampler\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau, CosineAnnealingLR, OneCycleLR\n\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.metrics import classification_report, f1_score, confusion_matrix, roc_curve, roc_auc_score, auc\nfrom sklearn.utils.class_weight import compute_class_weight\nfrom imblearn.over_sampling import SMOTE\n\n# ======= 常量定义 Определение констант =======\nOUTPUT_DIR = \"/kaggle/working\"\nRESULTS_DIR = os.path.join(OUTPUT_DIR, \"cnn_results\")\nos.makedirs(RESULTS_DIR, exist_ok=True)\n\n# 检查 GPU 是否可用 Проверка доступности GPU\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(f\"使用设备: Используемое устройство: {device}\")\n\n# ======= 增强的数据处理函数 Расширенные функции обработки данных =======\ndef create_class_weights(labels, beta=0.9999):\n    \"\"\"创建更强的类别权重以处理类别不平衡\n    Создание более сильных весов классов для обработки несбалансированности классов\"\"\"\n    # 获取每个类别的样本数量\n    class_counts = Counter(labels.numpy())\n    total_samples = len(labels)\n    n_classes = len(class_counts)\n    \n    # 使用更强的计算权重方法: effective number of samples with higher beta\n    weights = {}\n    \n    for class_id, count in class_counts.items():\n        # 计算有效样本数 (with stronger smoothing)\n        effective_num = 1.0 - beta ** count\n        weights[class_id] = (1.0 - beta) / effective_num\n    \n    # 进一步增强少数类的权重\n    max_weight = max(weights.values())\n    for class_id, count in class_counts.items():\n        # 给样本数占比小于5%的类别额外的权重提升\n        if count / total_samples < 0.05:\n            weights[class_id] *= 1.5\n    \n    # 归一化权重\n    weight_sum = sum(weights.values())\n    for class_id in weights:\n        weights[class_id] = weights[class_id] / weight_sum * n_classes\n    \n    print(f\"增强后的类别权重: {weights}\")\n    return weights\n\ndef targeted_augment_minority_classes(X, y):\n    \"\"\"有针对性地增强少数类样本，特别关注F1得分低的类别\n    Целенаправленная аугментация образцов миноритарных классов с особым вниманием к классам с низким F1-показателем\"\"\"\n    print(\"执行针对性少数类增强... Выполнение целенаправленной аугментации миноритарных классов...\")\n    \n    # 获取每个类别的样本数量 Получение количества образцов для каждого класса\n    unique_classes, class_counts = torch.unique(y, return_counts=True)\n    print(f\"原始类别分布 / Исходное распределение классов: {dict(zip(unique_classes.tolist(), class_counts.tolist()))}\")\n    \n    # 设置目标类别的增强系数(类别1和类别3需要额外增强)\n    augmentation_factor = {\n        0: 1.0,    # 主类别，不需要额外增强\n        1: 1.3,    # F1为0的类别，需要重点增强\n        2: 1.0,    # 表现尚可，常规增强\n        3: 1.3     # F1接近0的类别，需要重点增强\n    }\n    \n    # 确定多数类和需要增强的类 Определение мажоритарного класса и классов для аугментации\n    majority_class = unique_classes[torch.argmax(class_counts)]\n    majority_count = torch.max(class_counts).item()\n    \n    augmented_X = [X]\n    augmented_y = [y]\n    \n    # 对每个少数类进行增强 Аугментация каждого миноритарного класса\n    for cls in unique_classes:\n        if cls == majority_class:\n            continue\n            \n        # 获取当前类别的样本 Получение образцов для текущего класса\n        cls_indices = torch.where(y == cls)[0]\n        cls_X = X[cls_indices]\n        cls_y = y[cls_indices]\n        \n        # 计算需要生成的样本数 Расчет количества образцов для генерации\n        target_count = int(majority_count * augmentation_factor[cls.item()])\n        n_to_generate = target_count - len(cls_indices)\n        \n        if n_to_generate <= 0:\n            continue\n            \n        print(f\"为类别 {cls.item()} 生成 {n_to_generate} 个样本 / Генерация {n_to_generate} образцов для класса {cls.item()}\")\n        \n        # 使用多种增强技术 Using multiple augmentation techniques\n        new_samples_X = []\n        new_samples_y = []\n        \n        # 确定要生成的每种增强类型的样本数量\n        n_mixup = int(n_to_generate * 0.5)  # 混合增强\n        n_jitter = int(n_to_generate * 0.3)  # 抖动增强\n        n_time_warp = n_to_generate - n_mixup - n_jitter  # 时间扭曲\n        \n        # 1. 混合增强 (Mixup augmentation)\n        if len(cls_indices) >= 2 and n_mixup > 0:\n            for i in range(n_mixup):\n                # 随机选择两个样本进行混合\n                idx1, idx2 = torch.randperm(len(cls_X))[:2]\n                sample1 = cls_X[idx1].clone()\n                sample2 = cls_X[idx2].clone()\n                \n                # 随机混合系数\n                alpha = torch.rand(1).item() * 0.8 + 0.1  # 范围0.1-0.9，避免极端值\n                new_sample = alpha * sample1 + (1 - alpha) * sample2\n                \n                new_samples_X.append(new_sample.unsqueeze(0))\n                new_samples_y.append(cls_y[0].unsqueeze(0))\n        \n        # 2. 抖动增强 (Jitter augmentation)\n        if n_jitter > 0:\n            for i in range(n_jitter):\n                idx = torch.randint(0, len(cls_X), (1,)).item()\n                sample = cls_X[idx].clone()\n                \n                # 添加随机抖动噪声\n                noise_level = torch.rand(1).item() * 0.05 + 0.02  # 2%-7%的噪声水平\n                noise = torch.randn_like(sample) * noise_level * torch.std(sample)\n                sample += noise\n                \n                new_samples_X.append(sample.unsqueeze(0))\n                new_samples_y.append(cls_y[idx].unsqueeze(0))\n        \n        # 3. 时间扭曲增强 (Time warping augmentation)\n        if n_time_warp > 0:\n            for i in range(n_time_warp):\n                idx = torch.randint(0, len(cls_X), (1,)).item()\n                sample = cls_X[idx].clone()\n                \n                # 时间扭曲 - 分段缩放\n                segments = 4  # 分成4段\n                for seg in range(segments):\n                    start = seg * sample.shape[2] // segments\n                    end = (seg + 1) * sample.shape[2] // segments\n                    # 更强的扭曲范围\n                    scale = torch.FloatTensor(1).uniform_(0.7, 1.3).item()\n                    sample[:, :, start:end] *= scale\n                \n                new_samples_X.append(sample.unsqueeze(0))\n                new_samples_y.append(cls_y[idx].unsqueeze(0))\n        \n        # 合并新样本 Combine new samples\n        if new_samples_X:\n            augmented_X.extend(new_samples_X)\n            augmented_y.extend(new_samples_y)\n    \n    # 合并所有增强数据 Объединение всех аугментированных данных\n    augmented_X = torch.cat(augmented_X, dim=0)\n    augmented_y = torch.cat(augmented_y, dim=0)\n    \n    # 检查新的类别分布 Проверка нового распределения классов\n    unique_classes, new_class_counts = torch.unique(augmented_y, return_counts=True)\n    print(f\"增强后类别分布 / Распределение классов после аугментации: {dict(zip(unique_classes.tolist(), new_class_counts.tolist()))}\")\n    \n    # 随机打乱数据 Shuffle data\n    indices = torch.randperm(len(augmented_y))\n    augmented_X = augmented_X[indices]\n    augmented_y = augmented_y[indices]\n    \n    return augmented_X, augmented_y\n\ndef create_dataloaders(processed_data, batch_size=64, use_weighted_sampler=True, use_augmentation=False):\n    \"\"\"创建数据加载器 Создание загрузчиков данных\"\"\"\n    \n    train_X = processed_data['train_X']\n    train_y = processed_data['train_y']\n    val_X = processed_data['val_X']\n    val_y = processed_data['val_y']\n    test_X = processed_data['test_X']\n    test_y = processed_data['test_y']\n    \n    # 可选：数据增强 Опционально: аугментация данных\n    if use_augmentation:\n        train_X, train_y = targeted_augment_minority_classes(train_X, train_y)\n    \n    # 创建数据集 Создание наборов данных\n    train_dataset = TensorDataset(train_X, train_y)\n    val_dataset = TensorDataset(val_X, val_y)\n    test_dataset = TensorDataset(test_X, test_y)\n    \n    # 创建数据加载器 Создание загрузчиков данных\n    if use_weighted_sampler:\n        # 使用加权采样器处理类别不平衡 Использование взвешенного сэмплера для обработки несбалансированности классов\n        # 获取增强的类别权重\n        class_weights_dict = create_class_weights(train_y, beta=0.9999)\n        class_counts = torch.bincount(train_y)\n        class_weights = torch.FloatTensor([class_weights_dict[i] for i in range(len(class_counts))])\n        \n        # 对每个样本分配权重 - 使用更高的少数类权重\n        sample_weights = class_weights[train_y]\n        \n        train_sampler = WeightedRandomSampler(\n            weights=sample_weights,\n            num_samples=len(sample_weights),\n            replacement=True\n        )\n        train_loader = DataLoader(train_dataset, batch_size=batch_size, sampler=train_sampler, num_workers=0)\n    else:\n        train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=0)\n        \n    val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=0)\n    test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=0)\n    \n    return {\n        'train_loader': train_loader,\n        'val_loader': val_loader,\n        'test_loader': test_loader\n    }\n\n# ======= 增强的CNN模型 Улучшенная модель CNN =======\nclass ResidualBlock(nn.Module):\n    \"\"\"残差块，增强梯度流动和特征提取能力\n    Остаточный блок для улучшения потока градиентов и возможностей извлечения признаков\"\"\"\n    \n    def __init__(self, in_channels, out_channels, stride=1):\n        super(ResidualBlock, self).__init__()\n        \n        # 主路径 Main path\n        self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=(3, 1), stride=(stride, 1), padding=(1, 0))\n        self.bn1 = nn.BatchNorm2d(out_channels)\n        self.relu = nn.LeakyReLU(0.2)\n        self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=(3, 1), padding=(1, 0))\n        self.bn2 = nn.BatchNorm2d(out_channels)\n        \n        # 跳跃连接 Skip connection\n        self.shortcut = nn.Sequential()\n        if stride != 1 or in_channels != out_channels:\n            self.shortcut = nn.Sequential(\n                nn.Conv2d(in_channels, out_channels, kernel_size=(1, 1), stride=(stride, 1)),\n                nn.BatchNorm2d(out_channels)\n            )\n    \n    def forward(self, x):\n        residual = x\n        \n        out = self.conv1(x)\n        out = self.bn1(out)\n        out = self.relu(out)\n        \n        out = self.conv2(out)\n        out = self.bn2(out)\n        \n        out += self.shortcut(residual)  # 添加残差连接\n        out = self.relu(out)\n        \n        return out\n\nclass SimpleAttention(nn.Module):\n    \"\"\"简化的注意力机制，用于处理单通道输入\n    Упрощенный механизм внимания для обработки одноканального входа\"\"\"\n    \n    def __init__(self):\n        super(SimpleAttention, self).__init__()\n        \n        # 单通道直接使用简单注意力机制\n        self.conv = nn.Conv2d(1, 1, kernel_size=(3, 1), padding=(1, 0))\n        self.sigmoid = nn.Sigmoid()\n    \n    def forward(self, x):\n        # 生成注意力图\n        attention = self.sigmoid(self.conv(x))\n        return attention\n\nclass EnhancedAttention(nn.Module):\n    \"\"\"增强的注意力机制，更好地关注少数类特征\n    Улучшенный механизм внимания для лучшего фокуса на признаках миноритарных классов\"\"\"\n    \n    def __init__(self, channels):\n        super(EnhancedAttention, self).__init__()\n        \n        # 确保输出通道数至少为1\n        out_channels = max(1, channels // 4)\n        \n        # 使用多种卷积核大小来捕获不同尺度的特征\n        self.conv3 = nn.Conv2d(channels, out_channels, kernel_size=(3, 1), padding=(1, 0))\n        self.conv5 = nn.Conv2d(channels, out_channels, kernel_size=(5, 1), padding=(2, 0))\n        self.conv7 = nn.Conv2d(channels, out_channels, kernel_size=(7, 1), padding=(3, 0))\n        \n        # 合并多尺度特征并输出注意力图\n        self.conv_combine = nn.Conv2d(out_channels * 3, 1, kernel_size=(1, 1))\n        self.sigmoid = nn.Sigmoid()\n    \n    def forward(self, x):\n        # 多尺度特征提取\n        out3 = F.leaky_relu(self.conv3(x), 0.2)\n        out5 = F.leaky_relu(self.conv5(x), 0.2)\n        out7 = F.leaky_relu(self.conv7(x), 0.2)\n        \n        # 合并特征\n        combined = torch.cat([out3, out5, out7], dim=1)\n        \n        # 生成注意力图\n        attention = self.sigmoid(self.conv_combine(combined))\n        \n        return attention\n\nclass EnhancedCNNModel(nn.Module):\n    \"\"\"增强版CNN模型，添加残差连接和更强的注意力机制\n    Улучшенная модель CNN с добавлением остаточных соединений и более сильного механизма внимания\"\"\"\n    \n    def __init__(self, input_channels=1, n_features=18, num_classes=4, dropout_rate=0.5):\n        super(EnhancedCNNModel, self).__init__()\n        \n        # 定义通道数增长 Define channel growth\n        c1, c2, c3, c4 = 32, 64, 128, 256\n        \n        # 针对单通道输入使用简化的注意力机制\n        if input_channels == 1:\n            self.attention = SimpleAttention()\n        else:\n            # 多通道使用增强的注意力机制\n            self.attention = EnhancedAttention(input_channels)\n        \n        # 第一个残差块组 First residual block group\n        self.res_block1 = nn.Sequential(\n            ResidualBlock(input_channels, c1),\n            nn.MaxPool2d(kernel_size=(2, 1), stride=(2, 1)),\n            nn.Dropout(dropout_rate * 0.5)  # 较轻的dropout\n        )\n        \n        # 第二个残差块组 Second residual block group\n        self.res_block2 = nn.Sequential(\n            ResidualBlock(c1, c2),\n            ResidualBlock(c2, c2),\n            nn.MaxPool2d(kernel_size=(2, 1), stride=(2, 1)),\n            nn.Dropout(dropout_rate * 0.7)  # 中等dropout\n        )\n        \n        # 第三个残差块组 Third residual block group\n        self.res_block3 = nn.Sequential(\n            ResidualBlock(c2, c3),\n            ResidualBlock(c3, c3),\n            nn.MaxPool2d(kernel_size=(2, 1), stride=(2, 1)),\n            nn.Dropout(dropout_rate)  # 完整dropout\n        )\n        \n        # 计算展平后的特征数量 Calculate flattened feature size\n        self.flattened_size = self._calculate_flat_size(input_channels, n_features)\n        \n        # 全连接层 Fully connected layers\n        self.fc = nn.Sequential(\n            nn.Linear(self.flattened_size, c4),\n            nn.BatchNorm1d(c4),\n            nn.LeakyReLU(0.2),\n            nn.Dropout(dropout_rate),\n            \n            nn.Linear(c4, c4 // 2),\n            nn.BatchNorm1d(c4 // 2),\n            nn.LeakyReLU(0.2),\n            nn.Dropout(dropout_rate),\n            \n            nn.Linear(c4 // 2, num_classes)\n        )\n        \n        # 初始化权重 Initialize weights\n        self.apply(self._init_weights)\n    \n    def _init_weights(self, module):\n        \"\"\"初始化权重 Initialize weights\"\"\"\n        if isinstance(module, nn.Conv2d) or isinstance(module, nn.Linear):\n            nn.init.kaiming_normal_(module.weight, mode='fan_out', nonlinearity='leaky_relu')\n            if module.bias is not None:\n                nn.init.zeros_(module.bias)\n        elif isinstance(module, nn.BatchNorm2d) or isinstance(module, nn.BatchNorm1d):\n            nn.init.ones_(module.weight)\n            nn.init.zeros_(module.bias)\n    \n    def _calculate_flat_size(self, input_channels, n_features):\n        \"\"\"计算展平后的特征数量\n        Calculate flattened feature size\"\"\"\n        # 创建一个假的输入张量来计算展平后的维度\n        with torch.no_grad():\n            sample = torch.zeros(1, input_channels, n_features, 1)\n            attention = self.attention(sample)\n            sample = sample * attention\n            sample = self.res_block1(sample)\n            sample = self.res_block2(sample)\n            sample = self.res_block3(sample)\n            return sample.numel()\n    \n    def forward(self, x):\n        # 应用注意力机制 Apply attention mechanism\n        attention = self.attention(x)\n        x = x * attention\n        \n        # 应用残差块 Apply residual blocks\n        x = self.res_block1(x)\n        x = self.res_block2(x)\n        x = self.res_block3(x)\n        \n        # 展平 Flatten\n        x = x.view(x.size(0), -1)\n        \n        # 全连接层 Fully connected layers\n        x = self.fc(x)\n        \n        return x\n\n# ======= 损失函数 Функции потерь =======\nclass AdaptiveFocalLoss(nn.Module):\n    \"\"\"自适应的Focal Loss，对不同类别使用不同的gamma值\n    Адаптивный Focal Loss с разными значениями gamma для разных классов\"\"\"\n    \n    def __init__(self, class_gammas=None, class_weights=None, reduction='mean'):\n        super(AdaptiveFocalLoss, self).__init__()\n        self.reduction = reduction\n        \n        # 默认为每个类别设置不同的gamma值，针对性解决类别1和类别3的问题\n        self.class_gammas = class_gammas if class_gammas is not None else {\n            0: 1.0,  # 多数类使用较小的gamma\n            1: 3.0,  # 类别1(F1=0)使用更大的gamma\n            2: 1.5,  # 类别2表现较好，使用中等gamma\n            3: 3.0   # 类别3(F1接近0)使用更大的gamma\n        }\n        self.class_weights = class_weights\n    \n    def forward(self, inputs, targets):\n        # 计算交叉熵损失 Calculate cross entropy loss\n        ce_loss = F.cross_entropy(inputs, targets, reduction='none', \n                                 weight=self.class_weights.to(inputs.device) if self.class_weights is not None else None)\n        \n        # 获取预测概率 Get predicted probabilities\n        p_t = torch.exp(-ce_loss)\n        \n        # 根据目标类别应用不同的gamma值 Apply different gamma values based on target class\n        batch_gammas = torch.tensor([self.class_gammas[t.item()] for t in targets], device=targets.device)\n        \n        # 计算Focal Loss部分 Calculate Focal Loss component\n        focal_loss = (1 - p_t) ** batch_gammas.unsqueeze(0) * ce_loss\n        \n        # 减少 Reduction\n        if self.reduction == 'mean':\n            return focal_loss.mean()\n        elif self.reduction == 'sum':\n            return focal_loss.sum()\n        else:\n            return focal_loss\n\n# ======= 评估指标 Показатели оценки =======\ndef calculate_metrics(y_true, y_pred, y_scores, prefix=\"\"):\n    \"\"\"计算各种评估指标 Calculate various evaluation metrics\"\"\"\n    \n    # 计算准确率 Calculate accuracy\n    accuracy = (y_true == y_pred).float().mean().item()\n    \n    # 转为numpy处理 Convert to numpy for processing\n    y_true_np = y_true.cpu().numpy()\n    y_pred_np = y_pred.cpu().numpy()\n    y_scores_np = y_scores.cpu().numpy()\n    \n    # 计算F1分数 Calculate F1 score\n    weighted_f1 = f1_score(y_true_np, y_pred_np, average='weighted')\n    macro_f1 = f1_score(y_true_np, y_pred_np, average='macro')\n    \n    # 计算类别F1 Calculate class-wise F1\n    class_f1 = f1_score(y_true_np, y_pred_np, average=None)\n    \n    # 尝试计算ROC AUC Try to calculate ROC AUC\n    try:\n        # 获取类别数量 Get number of classes\n        num_classes = y_scores.shape[1]\n        \n        # 转为one-hot编码 Convert to one-hot encoding\n        y_true_onehot = np.zeros((len(y_true_np), num_classes))\n        for i in range(len(y_true_np)):\n            y_true_onehot[i, y_true_np[i]] = 1\n            \n        auc_score = roc_auc_score(y_true_onehot, y_scores_np, multi_class='ovr')\n        \n        # 计算每个类别的AUC Calculate AUC for each class\n        class_auc = []\n        for i in range(num_classes):\n            if np.any(y_true_onehot[:, i]) and np.any(~y_true_onehot[:, i]):  # 确保类别中有正负样本\n                class_auc.append(roc_auc_score(y_true_onehot[:, i], y_scores_np[:, i]))\n            else:\n                class_auc.append(float('nan'))\n                \n    except Exception as e:\n        print(f\"计算ROC AUC时出错 / Ошибка при расчете ROC AUC: {e}\")\n        auc_score = None\n        class_auc = None\n    \n    # 计算混淆矩阵 Calculate confusion matrix\n    cm = confusion_matrix(y_true_np, y_pred_np)\n    \n    result = {\n        f'{prefix}accuracy': accuracy,\n        f'{prefix}f1_weighted': weighted_f1,\n        f'{prefix}f1_macro': macro_f1,\n        f'{prefix}f1_per_class': class_f1,\n        f'{prefix}auc_score': auc_score,\n        f'{prefix}auc_per_class': class_auc,\n        f'{prefix}confusion_matrix': cm\n    }\n    \n    return result\n\n# ======= 训练函数 Функции обучения =======\ndef train_epoch(model, data_loader, criterion, optimizer, device, class_weights=None):\n    \"\"\"训练一个周期 Train one epoch\"\"\"\n    model.train()\n    epoch_loss = 0\n    correct = 0\n    total = 0\n    \n    # 类别特定的性能跟踪 Class-specific performance tracking\n    class_correct = {}\n    class_total = {}\n    \n    for inputs, labels in data_loader:\n        inputs, labels = inputs.to(device), labels.to(device)\n        \n        # 前向传播 Forward pass\n        outputs = model(inputs)\n        loss = criterion(outputs, labels)\n        \n        # 反向传播和优化 Backward pass and optimization\n        optimizer.zero_grad()\n        loss.backward()\n        \n        # 梯度裁剪以防止梯度爆炸 Gradient clipping to prevent gradient explosion\n        nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)\n        \n        optimizer.step()\n        \n        # 统计 Statistics\n        epoch_loss += loss.item()\n        _, predicted = torch.max(outputs.data, 1)\n        total += labels.size(0)\n        correct += (predicted == labels).sum().item()\n        \n        # 跟踪每个类别的性能 Track performance for each class\n        for c in range(4):  # 假设有4个类别\n            mask = (labels == c)\n            if mask.sum().item() > 0:\n                if c not in class_correct:\n                    class_correct[c] = 0\n                    class_total[c] = 0\n                class_correct[c] += ((predicted == labels) & mask).sum().item()\n                class_total[c] += mask.sum().item()\n    \n    # 计算平均损失和准确率 Calculate average loss and accuracy\n    epoch_loss = epoch_loss / len(data_loader)\n    accuracy = correct / total\n    \n    # 计算每个类别的准确率 Calculate accuracy for each class\n    class_accuracies = {}\n    for c in class_correct.keys():\n        if class_total[c] > 0:\n            class_accuracies[c] = class_correct[c] / class_total[c]\n        else:\n            class_accuracies[c] = 0.0\n    \n    return epoch_loss, accuracy, class_accuracies\n\ndef validate_epoch(model, data_loader, criterion, device):\n    \"\"\"验证一个周期 Validate one epoch\"\"\"\n    model.eval()\n    val_loss = 0\n    all_preds = []\n    all_scores = []\n    all_true = []\n    \n    with torch.no_grad():\n        for inputs, labels in data_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            \n            # 前向传播 Forward pass\n            outputs = model(inputs)\n            loss = criterion(outputs, labels)\n            \n            # 统计 Statistics\n            val_loss += loss.item()\n            _, predicted = torch.max(outputs.data, 1)\n            \n            # 收集预测和真实标签 Collect predictions and true labels\n            all_preds.append(predicted)\n            all_scores.append(F.softmax(outputs, dim=1))\n            all_true.append(labels)\n    \n    # 连接所有batch的结果 Concatenate results from all batches\n    all_preds = torch.cat(all_preds)\n    all_scores = torch.cat(all_scores)\n    all_true = torch.cat(all_true)\n    \n    # 计算验证指标 Calculate validation metrics\n    val_loss = val_loss / len(data_loader)\n    metrics = calculate_metrics(all_true, all_preds, all_scores)\n    \n    return val_loss, metrics, all_preds, all_scores, all_true\n\ndef train_cnn_model(model, data_loaders, criterion, optimizer, \n                   num_epochs=50, scheduler=None, \n                   early_stopping_patience=20, device=device,\n                   class_weights=None,\n                   model_path=os.path.join(RESULTS_DIR, \"best_cnn_model.pth\")):\n    \"\"\"训练CNN模型的完整流程 Complete CNN model training process\"\"\"\n    \n    train_loader = data_loaders['train_loader']\n    val_loader = data_loaders['val_loader']\n    \n    # 初始化训练历史记录 Initialize training history\n    history = {\n        'train_loss': [],\n        'val_loss': [],\n        'train_acc': [],\n        'val_acc': [],\n        'val_f1_weighted': [],\n        'val_f1_macro': [],\n        'val_f1_per_class': [],\n        'class_accuracies': [],\n        'lr': []\n    }\n    \n    # 早停设置 - 使用宏平均F1和更长的耐心 Early stopping settings - using macro F1 with longer patience\n    best_val_f1_macro = 0\n    patience_counter = 0\n    best_model_state = None\n    \n    # 训练循环 Training loop\n    start_time = time.time()\n    print(f\"开始训练，总共 {num_epochs} 个周期... Начало обучения, всего {num_epochs} эпох...\")\n    \n    for epoch in range(num_epochs):\n        epoch_start = time.time()\n        \n        # 训练阶段 Training phase\n        train_loss, train_acc, class_accs = train_epoch(model, train_loader, criterion, optimizer, device, class_weights)\n        \n        # 验证阶段 Validation phase\n        val_loss, val_metrics, val_preds, val_scores, val_true = validate_epoch(model, val_loader, criterion, device)\n        \n        # 当前学习率 Current learning rate\n        current_lr = optimizer.param_groups[0]['lr']\n        \n        # 保存历史记录 Save history\n        history['train_loss'].append(train_loss)\n        history['val_loss'].append(val_loss)\n        history['train_acc'].append(train_acc)\n        history['val_acc'].append(val_metrics['accuracy'])\n        history['val_f1_weighted'].append(val_metrics['f1_weighted'])\n        history['val_f1_macro'].append(val_metrics['f1_macro'])\n        history['val_f1_per_class'].append(val_metrics['f1_per_class'].tolist())\n        history['class_accuracies'].append(class_accs)\n        history['lr'].append(current_lr)\n        \n        # 更新学习率调度器 Update learning rate scheduler\n        if scheduler is not None:\n            if isinstance(scheduler, ReduceLROnPlateau):\n                scheduler.step(val_metrics['f1_macro'])  # 使用宏平均F1作为指标\n            else:\n                scheduler.step()\n        \n        # 输出进度 Output progress\n        epoch_end = time.time()\n        epoch_time = epoch_end - epoch_start\n        print(f\"Epoch [{epoch+1}/{num_epochs}], \"\n              f\"Time: {epoch_time:.2f}s, \"\n              f\"Train Loss: {train_loss:.4f}, \"\n              f\"Train Acc: {train_acc:.4f}, \"\n              f\"Val Loss: {val_loss:.4f}, \"\n              f\"Val Acc: {val_metrics['accuracy']:.4f}, \"\n              f\"Val F1 (weighted): {val_metrics['f1_weighted']:.4f}, \"\n              f\"Val F1 (macro): {val_metrics['f1_macro']:.4f}, \"\n              f\"LR: {current_lr:.6f}\")\n        \n        # 打印每个类别的F1分数和训练准确率 Print F1 scores and training accuracy for each class\n        print(f\"Val F1 per class: {[f'{f1:.4f}' for f1 in val_metrics['f1_per_class']]}\")\n        print(f\"Train Acc per class: {[f'{class_accs.get(c, 0):.4f}' for c in range(4)]}\")\n        \n        # 早停检查 - 使用宏平均F1 Early stopping check - using macro F1\n        if val_metrics['f1_macro'] > best_val_f1_macro:\n            best_val_f1_macro = val_metrics['f1_macro']\n            patience_counter = 0\n            best_model_state = model.state_dict().copy()\n            \n            # 保存最佳模型 Save best model\n            torch.save(model.state_dict(), model_path)\n            print(f\"Epoch {epoch+1}: 保存新的最佳模型，宏平均F1={val_metrics['f1_macro']:.4f} / Эпоха {epoch+1}: Сохранение новой лучшей модели, макро-F1={val_metrics['f1_macro']:.4f}\")\n        else:\n            patience_counter += 1\n            print(f\"Epoch {epoch+1}: 验证宏平均F1未提升，耐心计数={patience_counter}/{early_stopping_patience} / Эпоха {epoch+1}: Макро-F1 валидации не улучшился, счетчик терпения={patience_counter}/{early_stopping_patience}\")\n            \n        if patience_counter >= early_stopping_patience:\n            print(f\"早停触发：验证宏平均F1在{early_stopping_patience}个周期内未提升 / Ранний останов: Макро-F1 валидации не улучшался в течение {early_stopping_patience} эпох\")\n            break\n    \n    # 训练结束 End of training\n    total_time = time.time() - start_time\n    hours, rem = divmod(total_time, 3600)\n    minutes, seconds = divmod(rem, 60)\n    print(f\"训练完成！总时间 / Обучение завершено! Общее время: {int(hours):02d}:{int(minutes):02d}:{seconds:05.2f}\")\n    \n    # 加载最佳模型状态 Load best model state\n    if best_model_state is not None:\n        model.load_state_dict(best_model_state)\n        print(\"已加载最佳模型权重 / Загружены веса лучшей модели\")\n    \n    return model, history\n\n# ======= 评估函数 Функции оценки =======\ndef evaluate_cnn_model(model, data_loader, criterion, device=device):\n    \"\"\"评估CNN模型 Evaluate CNN model\"\"\"\n    \n    model.eval()\n    loss = 0.0\n    all_preds = []\n    all_scores = []\n    all_true = []\n    \n    with torch.no_grad():\n        for inputs, labels in data_loader:\n            inputs, labels = inputs.to(device), labels.to(device)\n            \n            # 前向传播 Forward pass\n            outputs = model(inputs)\n            batch_loss = criterion(outputs, labels)\n            \n            # 统计 Statistics\n            loss += batch_loss.item()\n            _, predicted = torch.max(outputs.data, 1)\n            \n            # 收集预测和真实标签 Collect predictions and true labels\n            all_preds.append(predicted)\n            all_scores.append(F.softmax(outputs, dim=1))\n            all_true.append(labels)\n    \n    # 连接所有batch的结果 Concatenate results from all batches\n    all_preds = torch.cat(all_preds)\n    all_scores = torch.cat(all_scores)\n    all_true = torch.cat(all_true)\n    \n    # 计算评估指标 Calculate evaluation metrics\n    metrics = calculate_metrics(all_true, all_preds, all_scores, prefix=\"test_\")\n    metrics['test_loss'] = loss / len(data_loader)\n    \n    # 打印详细指标 Print detailed metrics\n    print(f\"测试损失 / Тестовая потеря: {metrics['test_loss']:.4f}\")\n    print(f\"测试准确率 / Тестовая точность: {metrics['test_accuracy']:.4f}\")\n    print(f\"测试加权F1分数 / Тестовый взвешенный F1-показатель: {metrics['test_f1_weighted']:.4f}\")\n    print(f\"测试宏平均F1分数 / Тестовый макро-F1-показатель: {metrics['test_f1_macro']:.4f}\")\n    print(f\"各类别F1分数 / F1-показатель для каждого класса: {metrics['test_f1_per_class']}\")\n    if metrics['test_auc_score']:\n        print(f\"测试ROC AUC / Тестовый ROC AUC: {metrics['test_auc_score']:.4f}\")\n    \n    # 生成分类报告 Generate classification report\n    class_names = ['Нет категории', 'Начало нерешительности', 'Поворот', 'Ходьба']\n    report = classification_report(\n        all_true.cpu().numpy(), \n        all_preds.cpu().numpy(), \n        target_names=class_names, \n        output_dict=True\n    )\n    \n    return {\n        'metrics': metrics,\n        'classification_report': report,\n        'predictions': all_preds.cpu().numpy(),\n        'true_labels': all_true.cpu().numpy(),\n        'scores': all_scores.cpu().numpy()\n    }\n\n# ======= 可视化函数 Функции визуализации =======\ndef visualize_cnn_results(history, evaluation_results):\n    \"\"\"可视化CNN模型训练结果和性能\n    Визуализация результатов обучения и производительности модели CNN\"\"\"\n    \n    # 1. 绘制训练历史 Plot training history\n    plt.figure(figsize=(20, 15))\n    \n    # 损失曲线 Loss curve\n    plt.subplot(3, 2, 1)\n    plt.plot(history['train_loss'], label='Потери при обучении')\n    plt.plot(history['val_loss'], label='Потери при валидации')\n    plt.title('Потери модели')\n    plt.xlabel('Эпохи')\n    plt.ylabel('Потери')\n    plt.legend()\n    \n    # 准确率曲线 Accuracy curve\n    plt.subplot(3, 2, 2)\n    plt.plot(history['train_acc'], label='Точность при обучении')\n    plt.plot(history['val_acc'], label='Точность при валидации')\n    plt.title('Точность модели')\n    plt.xlabel('Эпохи')\n    plt.ylabel('Точность')\n    plt.legend()\n    \n    # F1分数曲线 F1 score curve\n    plt.subplot(3, 2, 3)\n    plt.plot(history['val_f1_weighted'], label='Взвешенный F1')\n    plt.plot(history['val_f1_macro'], label='Макро F1')\n    plt.title('F1-метрика валидации')\n    plt.xlabel('Эпохи')\n    plt.ylabel('F1-метрика')\n    plt.legend()\n    \n    # 各类别F1分数 F1 scores per class\n    plt.subplot(3, 2, 4)\n    for i in range(len(history['val_f1_per_class'][0])):\n        class_f1_history = [epoch_f1[i] for epoch_f1 in history['val_f1_per_class']]\n        plt.plot(class_f1_history, label=f'Класс {i}')\n    plt.title('F1-метрика для каждого класса')\n    plt.xlabel('Эпохи')\n    plt.ylabel('F1-метрика')\n    plt.legend()\n    \n    # 学习率 Learning rate\n    plt.subplot(3, 2, 5)\n    plt.plot(history['lr'])\n    plt.title('Скорость обучения')\n    plt.xlabel('Эпохи')\n    plt.ylabel('Скорость обучения')\n    plt.yscale('log')\n    \n    plt.tight_layout()\n    plt.savefig(os.path.join(RESULTS_DIR, \"cnn_training_history.png\"))\n    plt.close()\n    \n    # 2. 混淆矩阵 - 更新为4个类别 Confusion matrix - updated for 4 classes\n    cm = evaluation_results['metrics']['test_confusion_matrix']\n    \n    class_names = ['Нет категории', 'Начало нерешительности', 'Поворот', 'Ходьба']\n    plt.figure(figsize=(10, 8))\n    \n    # 计算归一化混淆矩阵 Calculate normalized confusion matrix\n    cm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n    cm_normalized = np.nan_to_num(cm_normalized)  # 处理除以0的情况 Handle division by zero\n    \n    sns.heatmap(cm_normalized, annot=cm, fmt='d', cmap='Blues', \n                xticklabels=class_names, \n                yticklabels=class_names)\n    plt.xlabel('Предсказанная метка')\n    plt.ylabel('Истинная метка')\n    plt.title('Матрица ошибок')\n    plt.tight_layout()\n    plt.savefig(os.path.join(RESULTS_DIR, \"cnn_confusion_matrix.png\"))\n    plt.close()\n    \n    # 3. ROC曲线（如果可能）- 更新为4个类别 ROC curve (if possible) - updated for 4 classes\n    try:\n        y_scores = evaluation_results['scores']\n        y_true = evaluation_results['true_labels']\n        \n        plt.figure(figsize=(10, 8))\n        \n        # 为每个类别绘制ROC曲线 Plot ROC curve for each class\n        num_classes = y_scores.shape[1]\n        y_true_onehot = np.zeros((len(y_true), num_classes))\n        for i in range(len(y_true)):\n            y_true_onehot[i, y_true[i]] = 1\n            \n        for i in range(num_classes):\n            if np.any(y_true_onehot[:, i]) and np.any(~y_true_onehot[:, i]):  # 确保有正负样本 Ensure there are positive and negative samples\n                fpr, tpr, _ = roc_curve(y_true_onehot[:, i], y_scores[:, i])\n                roc_auc = auc(fpr, tpr)\n                plt.plot(fpr, tpr, lw=2, \n                        label=f'{class_names[i]} (AUC = {roc_auc:.4f})')\n        \n        # 绘制随机猜测的基线 Plot baseline for random guessing\n        plt.plot([0, 1], [0, 1], 'k--', lw=2)\n        plt.xlim([0.0, 1.0])\n        plt.ylim([0.0, 1.05])\n        plt.xlabel('Доля ложноположительных результатов')\n        plt.ylabel('Доля истинно положительных результатов')\n        plt.title('ROC-кривая (для каждого класса)')\n        plt.legend(loc=\"lower right\")\n        plt.tight_layout()\n        plt.savefig(os.path.join(RESULTS_DIR, \"cnn_roc_curves.png\"))\n        plt.close()\n        \n        # 4. PR曲线 - 更合适评估不平衡数据集 PR curve - more suitable for imbalanced datasets\n        plt.figure(figsize=(10, 8))\n        \n        for i in range(num_classes):\n            if np.any(y_true_onehot[:, i]):  # 确保有正样本 Ensure there are positive samples\n                precision, recall, _ = precision_recall_curve(y_true_onehot[:, i], y_scores[:, i])\n                pr_auc = average_precision_score(y_true_onehot[:, i], y_scores[:, i])\n                plt.plot(recall, precision, lw=2, \n                        label=f'{class_names[i]} (AP = {pr_auc:.4f})')\n        \n        plt.xlim([0.0, 1.0])\n        plt.ylim([0.0, 1.05])\n        plt.xlabel('Полнота')\n        plt.ylabel('Точность')\n        plt.title('Кривая точность-полнота (для каждого класса)')\n        plt.legend(loc=\"lower left\")\n        plt.tight_layout()\n        plt.savefig(os.path.join(RESULTS_DIR, \"cnn_pr_curves.png\"))\n        plt.close()\n        \n    except Exception as e:\n        print(f\"绘制ROC/PR曲线时出错 / Ошибка при построении ROC/PR-кривой: {e}\")\n    \n    # 5. 保存评估指标摘要 Save evaluation metrics summary\n    metrics_summary = {k: v.tolist() if isinstance(v, np.ndarray) else v \n                      for k, v in evaluation_results['metrics'].items() \n                      if k != 'test_confusion_matrix'}\n    \n    # 添加混淆矩阵 Add confusion matrix\n    metrics_summary['test_confusion_matrix'] = evaluation_results['metrics']['test_confusion_matrix'].tolist()\n    \n    with open(os.path.join(RESULTS_DIR, \"cnn_metrics_summary.json\"), \"w\") as f:\n        json.dump(metrics_summary, f, indent=4)\n    \n    # 6. 保存分类报告 Save classification report\n    with open(os.path.join(RESULTS_DIR, \"cnn_classification_report.json\"), \"w\") as f:\n        json.dump(evaluation_results['classification_report'], f, indent=4)\n    \n    print(\"\\n所有可视化结果已保存至 / Все результаты визуализации сохранены в:\", RESULTS_DIR)\n\n# ======= 集成模型类 Класс ансамбля моделей =======\nclass ModelEnsemble:\n    \"\"\"模型集成类，组合多个模型的预测结果\n    Class for model ensemble, combining predictions from multiple models\"\"\"\n    \n    def __init__(self, models, scaler):\n        self.models = models\n        self.scaler = scaler\n        self.device = next(models[0].parameters()).device\n    \n    def predict(self, data):\n        \"\"\"使用集成模型进行预测 Make predictions using the ensemble model\"\"\"\n        # 预处理数据 Preprocess data\n        if isinstance(data, pd.DataFrame):\n            data = data.values\n        \n        data_scaled = self.scaler.transform(data)\n        data_tensor = torch.tensor(data_scaled, dtype=torch.float32)\n        data_adjusted = data_tensor.unsqueeze(1).unsqueeze(-1)\n        \n        # 创建DataLoader Create DataLoader\n        data_loader = DataLoader(data_adjusted, batch_size=64, shuffle=False)\n        \n        # 存储所有模型的预测结果 Store predictions from all models\n        all_probabilities = []\n        \n        # 获取每个模型的预测 Get predictions from each model\n        for model in self.models:\n            model.eval()\n            model_probs = []\n            \n            with torch.no_grad():\n                for batch in data_loader:\n                    batch = batch.to(self.device)\n                    outputs = model(batch)\n                    probs = F.softmax(outputs, dim=1)\n                    model_probs.append(probs.cpu().numpy())\n            \n            # 合并该模型的所有批次预测 Combine predictions from all batches for this model\n            model_probs = np.concatenate(model_probs)\n            all_probabilities.append(model_probs)\n        \n        # 平均所有模型的预测概率 Average prediction probabilities from all models\n        # 为了更好地处理少数类，使用更复杂的集成方法\n        # 少数类(类别1和3)有更大的权重\n        weights = np.array([1.0, 1.0, 1.0])  # 默认权重相等\n        \n        # 计算加权平均 Calculate weighted average\n        weighted_sum = np.zeros_like(all_probabilities[0])\n        for i, model_probs in enumerate(all_probabilities):\n            weighted_sum += model_probs * weights[i]\n        \n        avg_probabilities = weighted_sum / np.sum(weights)\n        \n        # 获取最终预测类别 Get final predicted classes\n        predictions = np.argmax(avg_probabilities, axis=1)\n        \n        return {\n            'predictions': predictions,\n            'probabilities': avg_probabilities\n        }\n\ndef preprocess_data_for_cnn(train_X, train_y, val_X, val_y, test_X, test_y, normalization='standard'):\n    \"\"\"预处理数据以适应CNN模型 Preprocess data for CNN model\"\"\"\n    \n    # 确保数据为numpy数组 Ensure data is numpy arrays\n    if isinstance(train_X, pd.DataFrame):\n        train_X = train_X.values\n    if isinstance(train_y, pd.DataFrame):\n        train_y = train_y.values\n    if isinstance(val_X, pd.DataFrame):\n        val_X = val_X.values\n    if isinstance(val_y, pd.DataFrame):\n        val_y = val_y.values\n    if isinstance(test_X, pd.DataFrame):\n        test_X = test_X.values\n    if isinstance(test_y, pd.DataFrame):\n        test_y = test_y.values\n    \n    # 数据标准化 Data normalization\n    if normalization == 'standard':\n        scaler = StandardScaler()\n    else:\n        scaler = MinMaxScaler(feature_range=(0, 1))\n    \n    train_X_scaled = scaler.fit_transform(train_X)\n    val_X_scaled = scaler.transform(val_X)\n    test_X_scaled = scaler.transform(test_X)\n    \n    # 转换为PyTorch张量 Convert to PyTorch tensors\n    train_X_tensor = torch.tensor(train_X_scaled, dtype=torch.float32)\n    val_X_tensor = torch.tensor(val_X_scaled, dtype=torch.float32)\n    test_X_tensor = torch.tensor(test_X_scaled, dtype=torch.float32)\n    \n    # 处理标签 Process labels\n    def convert_labels(y):\n        if y.shape[1] > 1:  # Multi-label\n            # 检查每行是否为全0 Check if each row is all zeros\n            all_zeros = (np.sum(y, axis=1) == 0)\n            \n            # 为非零行使用argmax Use argmax for non-zero rows\n            y_indices = np.argmax(y, axis=1)\n            \n            # 为全0行分配特殊类别标签（0），并将其他类别向上移动\n            # Assign special class label (0) for all-zero rows and shift other classes up\n            y_indices = y_indices + 1  # 将所有非零类别向上移动1 Shift all non-zero classes up by 1\n            y_indices[all_zeros] = 0   # 全0类别设为0 Set all-zero class to 0\n            \n            return y_indices\n        else:\n            # 如果不是one-hot编码，直接返回 If not one-hot encoded, return directly\n            return y.ravel()\n    \n    # 转换标签 Convert labels\n    train_y_indices = convert_labels(train_y)\n    val_y_indices = convert_labels(val_y)\n    test_y_indices = convert_labels(test_y)\n    \n    # 转换为PyTorch张量 Convert to PyTorch tensors\n    train_y_tensor = torch.tensor(train_y_indices, dtype=torch.long)\n    val_y_tensor = torch.tensor(val_y_indices, dtype=torch.long)\n    test_y_tensor = torch.tensor(test_y_indices, dtype=torch.long)\n    \n    # 分析类别分布 Analyze class distribution\n    unique_classes, class_counts = np.unique(train_y_indices, return_counts=True)\n    print(f\"类别分布 / Распределение классов: {dict(zip(unique_classes, class_counts))}\")\n    \n    # 调整CNN输入维度 Adjust CNN input dimensions\n    n_features = train_X_scaled.shape[1]\n    \n    # 调整为2D数据 Adjust to 2D data\n    train_X_adjusted = train_X_tensor.unsqueeze(1).unsqueeze(-1)\n    val_X_adjusted = val_X_tensor.unsqueeze(1).unsqueeze(-1)\n    test_X_adjusted = test_X_tensor.unsqueeze(1).unsqueeze(-1)\n    \n    print(f\"调整后 train_X 形状 / Форма train_X после настройки: {train_X_adjusted.shape}\")\n    print(f\"调整后 val_X 形状 / Форма val_X после настройки: {val_X_adjusted.shape}\")\n    print(f\"调整后 test_X 形状 / Форма test_X после настройки: {test_X_adjusted.shape}\")\n    \n    return {\n        'train_X': train_X_adjusted,\n        'train_y': train_y_tensor,\n        'val_X': val_X_adjusted,\n        'val_y': val_y_tensor,\n        'test_X': test_X_adjusted,\n        'test_y': test_y_tensor,\n        'scaler': scaler,\n        'n_features': n_features,\n        'num_classes': len(unique_classes)\n    }\n\n# ======= 主函数 Основная функция =======\ndef run_cnn_training(train_X, train_y, val_X, val_y, test_X, test_y, config=None):\n    \"\"\"运行CNN模型训练与评估的完整流程 Complete CNN model training and evaluation process\"\"\"\n    \n    print(\"=== 开始基于CNN的帕金森步态冻结检测模型训练与评估 / Начало обучения и оценки модели CNN для определения замораживания походки при болезни Паркинсона ===\")\n    start_time = time.time()\n    \n    # 默认配置 Default configuration\n    default_config = {\n        'batch_size': 32,               # 批量大小\n        'epochs': 100,                  # 训练周期数\n        'learning_rate': 0.0005,        # 学习率\n        'dropout_rate': 0.6,            # Dropout率 - 增加到0.6提高泛化能力\n        'weight_decay': 3e-5,           # L2正则化 - 增加正则化强度\n        'early_stopping_patience': 25,  # 早停耐心 - 增加耐心，给模型更多时间优化\n        'use_weighted_sampler': True,   # 使用加权采样器\n        'use_augmentation': True,       # 使用数据增强\n        'use_adaptive_focal_loss': True, # 使用自适应Focal Loss\n        'normalization': 'standard',    # 数据标准化方法\n        'use_ensemble': True,           # 使用模型集成\n        'num_ensemble_models': 3        # 集成模型数量\n    }\n    \n    # 合并配置 Merge configurations\n    if config:\n        config = {**default_config, **config}\n    else:\n        config = default_config\n    \n    print(\"模型配置 / Конфигурация модели:\")\n    print(json.dumps(config, indent=4))\n    \n    try:\n        # 1. 数据预处理 Data preprocessing\n        print(\"\\n执行数据预处理... / Выполнение предварительной обработки данных...\")\n        processed_data = preprocess_data_for_cnn(\n            train_X, train_y, val_X, val_y, test_X, test_y,\n            normalization=config['normalization']\n        )\n        \n        # 2. 创建数据加载器 Create data loaders\n        print(\"\\n创建数据加载器... / Создание загрузчиков данных...\")\n        data_loaders = create_dataloaders(\n            processed_data, \n            batch_size=config['batch_size'],\n            use_weighted_sampler=config['use_weighted_sampler'],\n            use_augmentation=config['use_augmentation']\n        )\n        \n        # 3. 如果使用集成，训练多个模型 Train multiple models if using ensemble\n        if config['use_ensemble']:\n            ensemble_models = []\n            ensemble_histories = []\n            \n            for i in range(config['num_ensemble_models']):\n                print(f\"\\n训练集成模型 {i+1}/{config['num_ensemble_models']} Обучение модели ансамбля {i+1}/{config['num_ensemble_models']}\")\n                \n                # 初始化模型 Initialize model\n                n_features = processed_data['n_features']\n                num_classes = processed_data['num_classes']\n                print(f\"特征数量 / Количество признаков: {n_features}, 类别数量 / Количество классов: {num_classes}\")\n                \n                model = EnhancedCNNModel(\n                    input_channels=1, \n                    n_features=n_features, \n                    num_classes=num_classes,\n                    dropout_rate=config['dropout_rate']\n                ).to(device)\n                \n                # 计算类别权重 Calculate class weights\n                class_counts = torch.bincount(processed_data['train_y'])\n                class_weights_dict = create_class_weights(processed_data['train_y'], beta=0.9999)\n                class_weights = torch.FloatTensor([class_weights_dict[i] for i in range(len(class_counts))])\n                \n                # 定义损失函数 Define loss function\n                if config['use_adaptive_focal_loss']:\n                    # 为不同类别设置不同的gamma值，针对性解决类别1和类别3的问题\n                    class_gammas = {\n                        0: 1.0,  # 多数类使用较小的gamma\n                        1: 3.0,  # 类别1(F1=0)使用更大的gamma\n                        2: 1.5,  # 类别2表现较好，使用中等gamma\n                        3: 3.0   # 类别3(F1接近0)使用更大的gamma\n                    }\n                    criterion = AdaptiveFocalLoss(\n                        class_gammas=class_gammas,\n                        class_weights=class_weights\n                    )\n                    print(\"使用自适应Focal Loss，为不同类别设置不同的gamma值 / Использование адаптивного Focal Loss с разными значениями gamma для разных классов\")\n                else:\n                    criterion = nn.CrossEntropyLoss(weight=class_weights.to(device))\n                    print(\"使用带类别权重的交叉熵损失 / Использование функции потерь перекрестной энтропии с весами классов\")\n                \n                # 定义优化器 Define optimizer\n                optimizer = optim.AdamW(\n                    model.parameters(), \n                    lr=config['learning_rate'],\n                    weight_decay=config['weight_decay'],\n                    betas=(0.9, 0.999)\n                )\n                \n                # 定义学习率调度器 Define learning rate scheduler\n                scheduler = OneCycleLR(\n                    optimizer,\n                    max_lr=config['learning_rate'],\n                    steps_per_epoch=len(data_loaders['train_loader']),\n                    epochs=config['epochs'],\n                    pct_start=0.3,\n                    anneal_strategy='cos'\n                )\n                \n                # 训练模型 Train model\n                model_path = os.path.join(RESULTS_DIR, f\"best_cnn_model_{i+1}.pth\")\n                model, history = train_cnn_model(\n                    model=model,\n                    data_loaders=data_loaders,\n                    criterion=criterion,\n                    optimizer=optimizer,\n                    num_epochs=config['epochs'],\n                    scheduler=scheduler,\n                    early_stopping_patience=config['early_stopping_patience'],\n                    device=device,\n                    class_weights=class_weights,\n                    model_path=model_path\n                )\n                \n                # 添加到集成 Add to ensemble\n                ensemble_models.append(model)\n                ensemble_histories.append(history)\n            \n            # 创建模型集成 Create model ensemble\n            ensemble = ModelEnsemble(ensemble_models, processed_data['scaler'])\n            \n            # 评估集成模型 Evaluate ensemble model\n            print(\"\\n评估集成模型性能... / Оценка производительности ансамбля моделей...\")\n            test_dataset = TensorDataset(processed_data['test_X'], processed_data['test_y'])\n            test_loader = DataLoader(test_dataset, batch_size=config['batch_size'], shuffle=False)\n            \n            # 获取集成预测 Get ensemble predictions\n            ensemble_preds = ensemble.predict(test_X)\n            predictions = ensemble_preds['predictions']\n            probabilities = ensemble_preds['probabilities']\n            \n            # 计算集成评估指标 Calculate ensemble evaluation metrics\n            test_true = processed_data['test_y'].cpu().numpy()\n            metrics = calculate_metrics(\n                torch.tensor(test_true), \n                torch.tensor(predictions), \n                torch.tensor(probabilities),\n                prefix=\"test_\"\n            )\n            \n            # 生成分类报告 Generate classification report\n            class_names = ['Нет категории', 'Начало нерешительности', 'Поворот', 'Ходьба']\n            report = classification_report(\n                test_true, \n                predictions, \n                target_names=class_names, \n                output_dict=True\n            )\n            \n            evaluation_results = {\n                'metrics': metrics,\n                'classification_report': report,\n                'predictions': predictions,\n                'true_labels': test_true,\n                'scores': probabilities\n            }\n            \n            # 可视化结果 Visualize results\n            print(\"\\n生成可视化结果... / Создание результатов визуализации...\")\n            # 使用第一个模型的历史记录进行可视化 Use the history of the first model for visualization\n            visualize_cnn_results(ensemble_histories[0], evaluation_results)\n            \n            # 保存模型 Save models\n            for i, model in enumerate(ensemble_models):\n                model_path = os.path.join(RESULTS_DIR, f\"final_cnn_model_{i+1}.pth\")\n                torch.save(model.state_dict(), model_path)\n                print(f\"集成模型 {i+1} 已保存至 / Модель ансамбля {i+1} сохранена в: {model_path}\")\n            \n            # 保存预处理器 Save preprocessor\n            scaler_path = os.path.join(RESULTS_DIR, \"cnn_feature_scaler.pkl\")\n            joblib.dump(processed_data['scaler'], scaler_path)\n            print(f\"特征标准化器已保存至 / Нормализатор признаков сохранен в: {scaler_path}\")\n            \n            # 保存集成配置 Save ensemble configuration\n            ensemble_config = {\n                'num_models': config['num_ensemble_models'],\n                'model_paths': [f\"final_cnn_model_{i+1}.pth\" for i in range(config['num_ensemble_models'])],\n                'scaler_path': \"cnn_feature_scaler.pkl\"\n            }\n            \n            with open(os.path.join(RESULTS_DIR, \"ensemble_config.json\"), \"w\") as f:\n                json.dump(ensemble_config, f, indent=4)\n            \n            # 计算总运行时间 Calculate total runtime\n            total_time = time.time() - start_time\n            hours, rem = divmod(total_time, 3600)\n            minutes, seconds = divmod(rem, 60)\n            print(f\"\\n执行完成！总时间 / Выполнение завершено! Общее время: {int(hours):02d}:{int(minutes):02d}:{seconds:05.2f}\")\n            \n            # 打印结果摘要 Print results summary\n            print(\"\\n===== 模型性能摘要 / Сводка производительности модели =====\")\n            print(f\"测试准确率 / Тестовая точность: {metrics['test_accuracy']:.4f}\")\n            print(f\"测试加权F1分数 / Тестовый взвешенный F1-показатель: {metrics['test_f1_weighted']:.4f}\")\n            print(f\"测试宏平均F1分数 / Тестовый макро-F1-показатель: {metrics['test_f1_macro']:.4f}\")\n            print(f\"各类别F1分数 / F1-показатель для каждого класса: {[f'{f1:.4f}' for f1 in metrics['test_f1_per_class']]}\")\n            if metrics['test_auc_score']:\n                print(f\"测试ROC AUC / Тестовый ROC AUC: {metrics['test_auc_score']:.4f}\")\n            \n            return ensemble, ensemble_histories, evaluation_results, processed_data['scaler']\n            \n        else:\n            # 非集成模式 - 训练单个模型 Non-ensemble mode - train a single model\n            \n            # 3. 初始化模型 Initialize model\n            print(\"\\n初始化CNN模型... / Инициализация модели CNN...\")\n            n_features = processed_data['n_features']\n            num_classes = processed_data['num_classes']\n            print(f\"特征数量 / Количество признаков: {n_features}, 类别数量 / Количество классов: {num_classes}\")\n            \n            model = EnhancedCNNModel(\n                input_channels=1, \n                n_features=n_features, \n                num_classes=num_classes,\n                dropout_rate=config['dropout_rate']\n            ).to(device)\n            \n            print(model)\n            \n            # 4. 定义损失函数、优化器和学习率调度器 Define loss function, optimizer and learning rate scheduler\n            \n            # 计算类别权重 Calculate class weights\n            class_counts = torch.bincount(processed_data['train_y'])\n            class_weights_dict = create_class_weights(processed_data['train_y'], beta=0.9999)\n            class_weights = torch.FloatTensor([class_weights_dict[i] for i in range(len(class_counts))])\n            \n            # 定义损失函数 Define loss function\n            if config['use_adaptive_focal_loss']:\n                # 为不同类别设置不同的gamma值，针对性解决类别1和类别3的问题\n                class_gammas = {\n                    0: 1.0,  # 多数类使用较小的gamma\n                    1: 3.0,  # 类别1(F1=0)使用更大的gamma\n                    2: 1.5,  # 类别2表现较好，使用中等gamma\n                    3: 3.0   # 类别3(F1接近0)使用更大的gamma\n                }\n                criterion = AdaptiveFocalLoss(\n                    class_gammas=class_gammas,\n                    class_weights=class_weights\n                )\n                print(\"使用自适应Focal Loss，为不同类别设置不同的gamma值 / Использование адаптивного Focal Loss с разными значениями gamma для разных классов\")\n            else:\n                criterion = nn.CrossEntropyLoss(weight=class_weights.to(device))\n                print(\"使用带类别权重的交叉熵损失 / Использование функции потерь перекрестной энтропии с весами классов\")\n            \n            optimizer = optim.AdamW(\n                model.parameters(), \n                lr=config['learning_rate'],\n                weight_decay=config['weight_decay'],\n                betas=(0.9, 0.999)\n            )\n            \n            scheduler = OneCycleLR(\n                optimizer,\n                max_lr=config['learning_rate'],\n                steps_per_epoch=len(data_loaders['train_loader']),\n                epochs=config['epochs'],\n                pct_start=0.3,\n                anneal_strategy='cos'\n            )\n            \n            # 5. 训练模型 Train model\n            print(\"\\n开始模型训练... / Начало обучения модели...\")\n            model, history = train_cnn_model(\n                model=model,\n                data_loaders=data_loaders,\n                criterion=criterion,\n                optimizer=optimizer,\n                num_epochs=config['epochs'],\n                scheduler=scheduler,\n                early_stopping_patience=config['early_stopping_patience'],\n                device=device,\n                class_weights=class_weights\n            )\n            \n            # 6. 评估模型 Evaluate model\n            print(\"\\n评估模型性能... / Оценка производительности модели...\")\n            evaluation_results = evaluate_cnn_model(\n                model=model,\n                data_loader=data_loaders['test_loader'],\n                criterion=criterion,\n                device=device\n            )\n            \n            # 7. 可视化结果 Visualize results\n            print(\"\\n生成可视化结果... / Создание результатов визуализации...\")\n            visualize_cnn_results(history, evaluation_results)\n            \n            # 8. 保存模型 Save model\n            final_model_path = os.path.join(RESULTS_DIR, \"final_cnn_model.pth\")\n            torch.save(model.state_dict(), final_model_path)\n            print(f\"最终模型已保存至 / Финальная модель сохранена в: {final_model_path}\")\n            \n            # 9. 保存预处理器 Save preprocessor\n            scaler_path = os.path.join(RESULTS_DIR, \"cnn_feature_scaler.pkl\")\n            joblib.dump(processed_data['scaler'], scaler_path)\n            print(f\"特征标准化器已保存至 / Нормализатор признаков сохранен в: {scaler_path}\")\n            \n            # 计算总运行时间 Calculate total runtime\n            total_time = time.time() - start_time\n            hours, rem = divmod(total_time, 3600)\n            minutes, seconds = divmod(rem, 60)\n            print(f\"\\n执行完成！总时间 / Выполнение завершено! Общее время: {int(hours):02d}:{int(minutes):02d}:{seconds:05.2f}\")\n            \n            # 打印结果摘要 Print results summary\n            print(\"\\n===== 模型性能摘要 / Сводка производительности модели =====\")\n            print(f\"测试准确率 / Тестовая точность: {evaluation_results['metrics']['test_accuracy']:.4f}\")\n            print(f\"测试加权F1分数 / Тестовый взвешенный F1-показатель: {evaluation_results['metrics']['test_f1_weighted']:.4f}\")\n            print(f\"测试宏平均F1分数 / Тестовый макро-F1-показатель: {evaluation_results['metrics']['test_f1_macro']:.4f}\")\n            print(f\"各类别F1分数 / F1-показатель для каждого класса: {[f'{f1:.4f}' for f1 in evaluation_results['metrics']['test_f1_per_class']]}\")\n            if evaluation_results['metrics']['test_auc_score']:\n                print(f\"测试ROC AUC / Тестовый ROC AUC: {evaluation_results['metrics']['test_auc_score']:.4f}\")\n            \n            return model, history, evaluation_results, processed_data['scaler']\n        \n    except Exception as e:\n        print(f\"训练过程中出错 / Ошибка во время обучения: {e}\")\n        import traceback\n        traceback.print_exc()\n        return None, None, None, None\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:01:47.616677Z","iopub.execute_input":"2025-04-27T17:01:47.616908Z","iopub.status.idle":"2025-04-27T17:01:51.031083Z","shell.execute_reply.started":"2025-04-27T17:01:47.616888Z","shell.execute_reply":"2025-04-27T17:01:51.030399Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    # 加载数据\n    # Load data\n    \n    # 运行CNN模型训练\n    # Run CNN model training\n    model, history, evaluation_results, scaler = run_cnn_training(\n        train_X, train_y, val_X, val_y, test_X, test_y,\n        config={\n            'batch_size': 32,\n            'epochs': 50,\n            'learning_rate': 0.001,\n            'use_focal_loss': True\n        }\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T17:01:51.032626Z","iopub.execute_input":"2025-04-27T17:01:51.033200Z","iopub.status.idle":"2025-04-27T20:54:39.382755Z","shell.execute_reply.started":"2025-04-27T17:01:51.033176Z","shell.execute_reply":"2025-04-27T20:54:39.381753Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.metrics import confusion_matrix\nimport os\n\n# Create results directory\nRESULTS_DIR = \"/kaggle/working/cnn_results_analysis\"\nos.makedirs(RESULTS_DIR, exist_ok=True)\n\n# Extract test results\ntest_accuracy = 0.6292\ntest_f1_weighted = 0.7399\ntest_f1_macro = 0.2118\ntest_f1_per_class = [0.7982, 0.0028, 0.0184, 0.0276]\nclass_names = ['No FOG', 'Start Hesitation', 'Turn', 'Walking']\n\n# Print model evaluation summary\nprint(\"\\n\" + \"=\"*50)\nprint(\"PARKINSON'S GAIT FREEZING DETECTION MODEL EVALUATION\")\nprint(\"=\"*50)\n\nprint(\"\\n1. Overall Performance Metrics\")\nprint(f\"   - Test Accuracy: {test_accuracy:.4f}\")\nprint(f\"   - Test Weighted F1: {test_f1_weighted:.4f}\")\nprint(f\"   - Test Macro F1: {test_f1_macro:.4f}\")\n\n# Evaluate class performance\nprint(\"\\n2. Class-wise Performance Metrics\")\nfor i, (name, f1) in enumerate(zip(class_names, test_f1_per_class)):\n    # Set performance level\n    if f1 > 0.7:\n        performance = \"Excellent\"\n    elif f1 > 0.5:\n        performance = \"Good\"\n    elif f1 > 0.3:\n        performance = \"Average\"\n    elif f1 > 0.1:\n        performance = \"Poor\"\n    else:\n        performance = \"Very Poor\"\n        \n    print(f\"   - Class {i} [{name}]: F1={f1:.4f} - {performance}\")\n\n# === Visualizations ===\n# 1. F1 Score Bar Chart\nplt.figure(figsize=(12, 6))\ncolors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728']\nbars = plt.bar(range(len(class_names)), test_f1_per_class, color=colors)\nplt.xticks(range(len(class_names)), class_names, rotation=30, ha='right')\nplt.ylim([0, 1.0])\nplt.xlabel('Class')\nplt.ylabel('F1 Score')\nplt.title('F1 Scores by Class')\nplt.grid(True, linestyle='--', alpha=0.7, axis='y')\n\n# Add F1 values on top of the bars\nfor i, bar in enumerate(bars):\n    height = bar.get_height()\n    plt.text(bar.get_x() + bar.get_width()/2, height + 0.02, \n             f'{test_f1_per_class[i]:.4f}', \n             ha='center', va='bottom', \n             color='black', fontweight='bold')\n\nplt.tight_layout()\nplt.savefig(os.path.join(RESULTS_DIR, \"class_f1_scores.png\"))\nplt.show()\n\n# 2. Estimated Confusion Matrix (based on F1 scores and class distribution)\nclass_distribution = {0: 60457, 1: 4359, 2: 21866, 3: 3149}\ntotal_samples = sum(class_distribution.values())\nclass_probs = [count/total_samples for count in class_distribution.values()]\n\n# Estimate confusion matrix\nn_classes = 4\ncm = np.zeros((n_classes, n_classes), dtype=int)\n\n# Class 0 (No FOG) - F1=0.7982, estimate high precision and recall\ntrue_positives_0 = int(class_distribution[0] * 0.85)  # Assume 85% accuracy\ncm[0, 0] = true_positives_0\nremaining_0 = class_distribution[0] - true_positives_0\ncm[0, 1] = int(remaining_0 * 0.5)  # Distribute errors\ncm[0, 2] = int(remaining_0 * 0.3)\ncm[0, 3] = remaining_0 - cm[0, 1] - cm[0, 2]\n\n# Class 1 (Start Hesitation) - F1=0.0028, estimate very low precision and recall\ntrue_positives_1 = int(class_distribution[1] * 0.01)  # Assume only 1% correctly identified\ncm[1, 1] = true_positives_1\ncm[1, 0] = int(class_distribution[1] * 0.9)  # Most misclassified as class 0\nremaining_1 = class_distribution[1] - true_positives_1 - cm[1, 0]\ncm[1, 2] = int(remaining_1 * 0.6)\ncm[1, 3] = remaining_1 - cm[1, 2]\n\n# Class 2 (Turn) - F1=0.0184, estimate low precision and recall\ntrue_positives_2 = int(class_distribution[2] * 0.02)  # Assume only 2% correctly identified\ncm[2, 2] = true_positives_2\ncm[2, 0] = int(class_distribution[2] * 0.85)  # Most misclassified as class 0\nremaining_2 = class_distribution[2] - true_positives_2 - cm[2, 0]\ncm[2, 1] = int(remaining_2 * 0.8)\ncm[2, 3] = remaining_2 - cm[2, 1]\n\n# Class 3 (Walking) - F1=0.0276, estimate low precision and recall \ntrue_positives_3 = int(class_distribution[3] * 0.03)  # Assume only 3% correctly identified\ncm[3, 3] = true_positives_3\ncm[3, 0] = int(class_distribution[3] * 0.8)  # Most misclassified as class 0\nremaining_3 = class_distribution[3] - true_positives_3 - cm[3, 0]\ncm[3, 1] = int(remaining_3 * 0.7)\ncm[3, 2] = remaining_3 - cm[3, 1]\n\n# Visualize confusion matrix\nplt.figure(figsize=(10, 8))\n# Calculate normalized confusion matrix\ncm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\ncm_normalized = np.nan_to_num(cm_normalized)  # Handle division by zero\n\n# Plot normalized confusion matrix with raw values\nsns.heatmap(cm_normalized, annot=cm, fmt='d', cmap='Blues', \n            xticklabels=class_names, \n            yticklabels=class_names)\nplt.xlabel('Predicted Label')\nplt.ylabel('True Label')\nplt.title('Estimated Confusion Matrix')\nplt.tight_layout()\nplt.savefig(os.path.join(RESULTS_DIR, \"estimated_confusion_matrix.png\"))\nplt.show()\n\n# 3. Overall performance chart\nmetrics = [test_accuracy, test_f1_weighted, test_f1_macro]\nmetric_names = ['Accuracy', 'Weighted F1', 'Macro F1']\n\nplt.figure(figsize=(10, 6))\nbars = plt.bar(range(len(metrics)), metrics, color=['#1f77b4', '#ff7f0e', '#2ca02c'])\nplt.xticks(range(len(metrics)), metric_names)\nplt.ylim([0, 1.0])\nplt.title('Overall Model Performance Metrics')\nplt.grid(True, linestyle='--', alpha=0.7, axis='y')\n\n# Add values on top of bars\nfor i, bar in enumerate(bars):\n    height = bar.get_height()\n    plt.text(bar.get_x() + bar.get_width()/2, height + 0.02, \n             f'{metrics[i]:.4f}', \n             ha='center', va='bottom', \n             color='black', fontweight='bold')\n\nplt.tight_layout()\nplt.savefig(os.path.join(RESULTS_DIR, \"overall_performance.png\"))\nplt.show()\n\n# === Model Analysis and Recommendations ===\nprint(\"\\n3. Model Performance Analysis\")\nprint(\"   - Model performs well on the majority class (No FOG), with F1 score near 0.80\")\nprint(\"   - Model performs extremely poorly on all minority classes, especially 'Start Hesitation' (F1=0.0028)\")\nprint(\"   - Low macro F1 score (0.2118) indicates insufficient minority class recognition\")\nprint(\"   - Relatively high weighted F1 score (0.7399) is primarily due to majority class performance\")\n\nprint(\"\\n4. Improvement Recommendations\")\nprint(\"   - Further increase sampling weights for minority classes\")\nprint(\"   - Design more targeted data augmentation strategies for minority classes\")\nprint(\"   - Adjust Focal Loss parameters with higher gamma values for minority classes\")\nprint(\"   - Consider a two-stage classification: first 'No FOG' vs 'FOG', then FOG type classification\")\nprint(\"   - Explore more advanced oversampling techniques like SMOTE or ADASYN\")\nprint(\"   - Try balanced evaluation metrics like Cohen's Kappa or G-Mean\")\nprint(\"   - Consider semi-supervised learning methods to leverage unlabeled data\")\n\nprint(\"\\n5. Training Parameter Analysis\")\nprint(\"   - Current batch size (32) may need adjustment, try smaller batches\")\nprint(\"   - Learning rate (0.001) could be lowered to 0.0005 or 0.0001\")\nprint(\"   - Current ensemble model count (3) may be insufficient, consider increasing to 5-10\")\nprint(\"   - Increase early stopping patience (25) to give model more time to learn minority classes\")\n\nprint(\"\\n\" + \"=\"*50)\n\n# Save results summary to file\nsummary = {\n    \"overall_metrics\": {\n        \"accuracy\": test_accuracy,\n        \"f1_weighted\": test_f1_weighted,\n        \"f1_macro\": test_f1_macro\n    },\n    \"class_f1_scores\": {class_names[i]: test_f1_per_class[i] for i in range(len(class_names))},\n    \"class_distribution\": class_distribution,\n    \"training_config\": {\n        \"batch_size\": 32,\n        \"epochs\": 50,\n        \"learning_rate\": 0.001,\n        \"dropout_rate\": 0.6,\n        \"weight_decay\": 3e-05,\n        \"early_stopping_patience\": 25,\n        \"use_weighted_sampler\": True,\n        \"use_augmentation\": True,\n        \"use_adaptive_focal_loss\": True,\n        \"normalization\": \"standard\",\n        \"use_ensemble\": True,\n        \"num_ensemble_models\": 3,\n        \"use_focal_loss\": True\n    }\n}\n\nimport json\nwith open(os.path.join(RESULTS_DIR, \"model_evaluation_summary.json\"), \"w\") as f:\n    json.dump(summary, f, indent=4)\n\nprint(f\"Analysis results and visualizations saved to: {RESULTS_DIR}\")\n\n# Create a simple performance report document\nwith open(os.path.join(RESULTS_DIR, \"performance_report.md\"), \"w\") as f:\n    f.write(\"# Parkinson's Gait Freezing Detection Model Performance Report\\n\\n\")\n    \n    f.write(\"## 1. Overall Performance Metrics\\n\\n\")\n    f.write(f\"- Test Accuracy: {test_accuracy:.4f}\\n\")\n    f.write(f\"- Test Weighted F1 Score: {test_f1_weighted:.4f}\\n\")\n    f.write(f\"- Test Macro F1 Score: {test_f1_macro:.4f}\\n\\n\")\n    \n    f.write(\"## 2. Class-wise Performance Metrics\\n\\n\")\n    for i, (name, f1) in enumerate(zip(class_names, test_f1_per_class)):\n        performance = \"\"\n        if f1 > 0.7: performance = \"Excellent\"\n        elif f1 > 0.5: performance = \"Good\"\n        elif f1 > 0.3: performance = \"Average\"\n        elif f1 > 0.1: performance = \"Poor\"\n        else: performance = \"Very Poor\"\n        \n        f.write(f\"- Class {i} [{name}]: F1={f1:.4f} - {performance}\\n\")\n    \n    f.write(\"\\n## 3. Model Performance Analysis\\n\\n\")\n    f.write(\"- Model performs well on the majority class (No FOG), with F1 score near 0.80\\n\")\n    f.write(\"- Model performs extremely poorly on all minority classes, especially 'Start Hesitation' (F1=0.0028)\\n\")\n    f.write(\"- Low macro F1 score (0.2118) indicates insufficient minority class recognition\\n\")\n    f.write(\"- Relatively high weighted F1 score (0.7399) is primarily due to majority class performance\\n\\n\")\n    \n    f.write(\"## 4. Improvement Recommendations\\n\\n\")\n    f.write(\"- Further increase sampling weights for minority classes\\n\")\n    f.write(\"- Design more targeted data augmentation strategies for minority classes\\n\")\n    f.write(\"- Adjust Focal Loss parameters with higher gamma values for minority classes\\n\")\n    f.write(\"- Consider a two-stage classification: first 'No FOG' vs 'FOG', then FOG type classification\\n\")\n    f.write(\"- Explore more advanced oversampling techniques like SMOTE or ADASYN\\n\")\n    f.write(\"- Try balanced evaluation metrics like Cohen's Kappa or G-Mean\\n\")\n    f.write(\"- Consider semi-supervised learning methods to leverage unlabeled data\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T20:54:39.384096Z","iopub.execute_input":"2025-04-27T20:54:39.384731Z","iopub.status.idle":"2025-04-27T20:54:40.387520Z","shell.execute_reply.started":"2025-04-27T20:54:39.384705Z","shell.execute_reply":"2025-04-27T20:54:40.386813Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# LSTM","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport tensorflow as tf\nfrom tensorflow.keras.models import Model, load_model\nfrom tensorflow.keras.layers import LSTM, Dense, Dropout, TimeDistributed, Input, Bidirectional\nfrom tensorflow.keras.layers import Conv1D, Concatenate, BatchNormalization, LayerNormalization\nfrom tensorflow.keras.layers import GlobalAveragePooling1D, GlobalMaxPooling1D, Multiply, Add\nfrom tensorflow.keras.layers import Reshape, Permute, Activation, Lambda\nfrom tensorflow.keras.optimizers import Adam\nfrom sklearn.preprocessing import RobustScaler\nfrom sklearn.metrics import classification_report, f1_score, roc_auc_score, confusion_matrix\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau\nimport joblib\nimport time\nimport json\nimport gc\n\n# =====================================================================\n# Constants and Configuration / 常量和配置\n# =====================================================================\nLSTM_WINDOW_SIZE = 128\nOUTPUT_DIR = \"/kaggle/working\"\nRESULTS_DIR = os.path.join(OUTPUT_DIR, \"results\")\nos.makedirs(RESULTS_DIR, exist_ok=True)\n\n# Class names in English to avoid font rendering issues\n# 使用英文类名以避免字体渲染问题\nCLASS_NAMES = ['Normal', 'Start Hesitation', 'Turning', 'Walking']\n\n# =====================================================================\n# Helper Functions / 辅助函数\n# =====================================================================\ndef reshape_for_lstm(X, window_size=LSTM_WINDOW_SIZE):\n    \"\"\"\n    Reshape data for LSTM input format (samples, timesteps, features)\n    将数据重塑为LSTM需要的格式 (样本数, 时间步长, 特征数)\n    \"\"\"\n    num_samples = X.shape[0] // window_size\n    if num_samples == 0:\n        raise ValueError(f\"Number of samples ({X.shape[0]}) less than window size ({window_size})\")\n    return X[:num_samples * window_size].reshape(num_samples, window_size, -1)\n\ndef process_labels_for_4_classes(y):\n    \"\"\"\n    Process labels to correctly identify 4 classes (including all-zero vector as a separate class)\n    处理标签以正确识别4个类别（包括全零向量作为单独类别）\n    \"\"\"\n    print(\"Processing labels for 4 classes (including zero class)...\")\n    \n    if len(y.shape) < 2 or y.shape[1] < 3:\n        raise ValueError(f\"Label shape {y.shape} is not compatible with multi-class format\")\n    \n    if y.shape[1] == 4:\n        print(\"Labels already in 4-class format, no conversion needed\")\n        return y\n    \n    all_zeros = np.all(y == 0, axis=1)\n    new_y = np.zeros((y.shape[0], 4))\n    new_y[all_zeros, 0] = 1\n    \n    for i in range(y.shape[1]):\n        mask = ~all_zeros & (y[:, i] == 1)\n        new_y[mask, i+1] = 1\n    \n    class_counts = np.sum(new_y, axis=0)\n    print(f\"Class distribution after processing: {dict(enumerate(class_counts))}\")\n    \n    return new_y\n\n# =====================================================================\n# TensorFlow Compatible Metrics / TensorFlow兼容指标\n# =====================================================================\ndef f1_metric(y_true, y_pred):\n    \"\"\"\n    TensorFlow compatible F1 score metric function\n    TensorFlow兼容的F1分数度量函数\n    \"\"\"\n    # 获取预测的类别（argmax）\n    y_pred_classes = tf.argmax(y_pred, axis=-1)\n    y_true_classes = tf.argmax(y_true, axis=-1)\n    \n    # 转换为浮点型\n    y_pred_classes = tf.cast(y_pred_classes, tf.float32)\n    y_true_classes = tf.cast(y_true_classes, tf.float32)\n    \n    # 计算True Positives (TP)\n    tp = tf.reduce_sum(tf.cast(tf.equal(y_pred_classes, y_true_classes), tf.float32))\n    \n    # 计算各类指标\n    predicted_positives = tf.reduce_sum(tf.ones_like(y_pred_classes))\n    actual_positives = tf.reduce_sum(tf.ones_like(y_true_classes))\n    \n    # 添加平滑因子避免除零错误\n    smooth = tf.constant(1e-7, dtype=tf.float32)\n    \n    # 计算precision和recall\n    precision = (tp + smooth) / (predicted_positives + smooth)\n    recall = (tp + smooth) / (actual_positives + smooth)\n    \n    # 计算F1分数\n    f1 = 2 * precision * recall / (precision + recall + smooth)\n    \n    return f1\n\n# =====================================================================\n# Fixed Advanced Attention Mechanisms / 修复的高级注意力机制\n# =====================================================================\ndef squeeze_excite_block(input_tensor, ratio=16):\n    \"\"\"\n    Squeeze and Excitation block to enhance channel dependencies\n    Squeeze and Excitation块，增强特征通道间的依赖关系\n    \"\"\"\n    init = input_tensor\n    channel_axis = -1\n    filters = init.shape[channel_axis]\n    se_shape = (1, filters)\n\n    se = GlobalAveragePooling1D()(init)\n    se = Reshape(se_shape)(se)\n    se = Dense(filters // ratio, activation='relu', use_bias=False)(se)\n    se = Dense(filters, activation='sigmoid', use_bias=False)(se)\n\n    x = Multiply()([init, se])\n    return x\n\ndef temporal_attention(input_tensor):\n    \"\"\"\n    Temporal attention mechanism\n    时间维度注意力机制\n    \"\"\"\n    x = Conv1D(1, kernel_size=1, padding='same')(input_tensor)\n    \n    # 使用Lambda层包装tf.nn.softmax\n    x = Lambda(lambda x: tf.nn.softmax(x, axis=1))(x)\n    \n    return Multiply()([input_tensor, x])\n\ndef feature_attention(input_tensor):\n    \"\"\"\n    Feature dimension attention mechanism\n    特征维度注意力机制\n    \"\"\"\n    # 使用Lambda包装reduce_mean\n    x = Lambda(lambda x: tf.reduce_mean(x, axis=1, keepdims=True))(input_tensor)\n    x = Dense(input_tensor.shape[-1], activation='sigmoid')(x)\n    return Multiply()([input_tensor, x])\n\ndef dual_attention(input_tensor):\n    \"\"\"\n    Dual attention: focus on both temporal and feature dimensions\n    双重注意力：同时关注时间和特征维度\n    \"\"\"\n    temp_att = temporal_attention(input_tensor)\n    feat_att = feature_attention(input_tensor)\n    return Add()([temp_att, feat_att])\n\n# =====================================================================\n# Improved LSTM Blocks / 改进的LSTM块\n# =====================================================================\ndef residual_lstm_block(x, units, dropout_rate=0.2):\n    \"\"\"\n    Residual LSTM block to maintain information flow and mitigate vanishing gradients\n    残差LSTM块，保持信息流并缓解梯度消失\n    \"\"\"\n    # Save input for later residual connection\n    original = x\n    \n    # Bidirectional LSTM\n    lstm = Bidirectional(LSTM(units, return_sequences=True, \n                              recurrent_dropout=0.1))(x)\n    lstm = BatchNormalization()(lstm)\n    lstm = Dropout(dropout_rate)(lstm)\n    \n    # Ensure shape matching, project if necessary\n    if original.shape[-1] != lstm.shape[-1]:\n        original = Conv1D(lstm.shape[-1], 1, padding='same')(original)\n    \n    # Residual connection\n    x = Add()([lstm, original])\n    x = LayerNormalization()(x)\n    \n    return x\n\ndef multi_scale_conv_block(x, filters_list=[32, 64, 128]):\n    \"\"\"\n    Multi-scale convolution block to capture features at different temporal ranges\n    多尺度卷积块，捕获不同时间范围的特征\n    \"\"\"\n    conv_outputs = []\n    \n    # Different sized convolution layers\n    for filters in filters_list:\n        # 1x1 convolution for dimensionality reduction\n        conv1 = Conv1D(filters, 1, padding='same', activation='relu')(x)\n        \n        # Different scale convolutions\n        conv3 = Conv1D(filters, 3, padding='same', activation='relu')(conv1)\n        conv5 = Conv1D(filters, 5, padding='same', activation='relu')(conv1)\n        conv7 = Conv1D(filters, 7, padding='same', activation='relu')(conv1)\n        \n        # Concatenate features from different scales\n        multi_scale = Concatenate()([conv3, conv5, conv7])\n        multi_scale = Dropout(0.1)(multi_scale)\n        \n        conv_outputs.append(multi_scale)\n    \n    # Concatenate outputs from all scales\n    if len(conv_outputs) > 1:\n        return Concatenate()(conv_outputs)\n    else:\n        return conv_outputs[0]\n\n# =====================================================================\n# Special Regularization Techniques / 特殊正则化技术\n# =====================================================================\ndef gradient_centralization(optimizer):\n    \"\"\"\n    Gradient centralization to improve training stability\n    梯度中心化，提高训练稳定性\n    \"\"\"\n    old_apply_gradients = optimizer.apply_gradients\n    \n    def apply_centralization(grads_and_vars, **kwargs):\n        grads_and_vars_new = []\n        for grad, var in grads_and_vars:\n            if grad is not None and 'kernel' in var.name:\n                # Convert TF operation to use tf.keras.backend instead of direct tf\n                if len(var.shape) == 4:  # Conv layers\n                    axes = [0, 1, 2]\n                else:  # Dense layers\n                    axes = [0]\n                # Use tf.keras.backend operations instead of direct tensorflow ops\n                mean = tf.keras.backend.mean(grad, axis=axes, keepdims=True)\n                grad = grad - mean\n            grads_and_vars_new.append((grad, var))\n        return old_apply_gradients(grads_and_vars_new, **kwargs)\n    \n    optimizer.apply_gradients = apply_centralization\n    return optimizer\n\ndef focal_loss_with_dynamic_weights(class_weights, gamma=2.0):\n    \"\"\"\n    Focal loss with dynamic class weights - enhanced to better handle class imbalance\n    带有动态类别权重的Focal Loss - 增强以更好地处理类别不平衡\n    \"\"\"\n    # 转换类别权重为Keras常量张量\n    class_weights_tensor = tf.constant(\n        [class_weights.get(i, 1.0) for i in range(len(class_weights))],\n        dtype=tf.float32\n    )\n    \n    def loss(y_true, y_pred):\n        # 转换为float32\n        y_true = tf.cast(y_true, tf.float32)\n        y_pred = tf.cast(y_pred, tf.float32)\n        \n        # 确保预测值在有效范围内\n        epsilon = tf.keras.backend.epsilon()\n        y_pred = tf.clip_by_value(y_pred, epsilon, 1.0 - epsilon)\n        \n        # 简化focal weight计算 - 针对每个类别\n        pt = tf.reduce_sum(y_true * y_pred, axis=-1, keepdims=True)\n        focal_weight = tf.pow(1.0 - pt, gamma)\n        \n        # 计算交叉熵损失\n        cross_entropy = -y_true * tf.math.log(y_pred)\n        \n        # 应用focal weighting\n        weighted_loss = cross_entropy * focal_weight\n        \n        # 应用类别权重 - 扩展维度以匹配batch和时间步维度\n        class_weights_expanded = tf.reshape(class_weights_tensor, [1, 1, -1])\n        weighted_loss = weighted_loss * class_weights_expanded\n        \n        # 计算平均损失\n        return tf.reduce_mean(weighted_loss)\n    \n    return loss\n\n# =====================================================================\n# Enhanced Data Augmentation / 增强的数据增强\n# =====================================================================\ndef targeted_smote_augmentation(X, y, minority_classes=[1, 3], neighbor_count=5, multiplier=1.0):\n    \"\"\"\n    Targeted SMOTE data augmentation focusing on minority classes\n    针对性的SMOTE数据增强，专注于少数类\n    \"\"\"\n    print(f\"Performing targeted SMOTE augmentation for classes {minority_classes}...\")\n    \n    # Ensure data is numpy arrays\n    if isinstance(X, pd.DataFrame):\n        X = X.to_numpy()\n    if isinstance(y, pd.DataFrame):\n        y = y.to_numpy()\n    \n    # Process labels\n    y_processed = process_labels_for_4_classes(y)\n    \n    # Get class for each sample\n    if len(y_processed.shape) == 2:\n        y_classes = np.argmax(y_processed, axis=1)\n    else:\n        y_classes = y_processed  # Assume already class indices\n    \n    augmented_X = list(X)\n    augmented_y = list(y_processed)\n    \n    # Augment each minority class\n    for cls in minority_classes:\n        # Get indices of current class samples\n        cls_indices = np.where(y_classes == cls)[0]\n        \n        if len(cls_indices) < 2:\n            print(f\"Warning: Class {cls} has too few samples for SMOTE\")\n            continue\n        \n        # Determine how many new samples to generate\n        n_to_generate = int(len(cls_indices) * multiplier)\n        print(f\"Generating {n_to_generate} samples for class {cls}\")\n        \n        for _ in range(n_to_generate):\n            # Randomly select a sample\n            idx = np.random.choice(cls_indices)\n            sample_X = X[idx]\n            sample_y = y_processed[idx]\n            \n            # Find K nearest neighbors\n            if len(cls_indices) <= neighbor_count:\n                neighbor_indices = cls_indices\n            else:\n                # Simple random neighbor selection\n                neighbor_indices = np.random.choice(\n                    cls_indices, \n                    size=min(neighbor_count, len(cls_indices)), \n                    replace=False\n                )\n            \n            # Randomly select one neighbor\n            neighbor_idx = np.random.choice(neighbor_indices)\n            neighbor_X = X[neighbor_idx]\n            \n            # Interpolate between sample and neighbor\n            alpha = np.random.random()\n            interpolated_X = sample_X * alpha + neighbor_X * (1 - alpha)\n            \n            # Add slight perturbation\n            noise_level = np.random.uniform(0.02, 0.05)\n            noise = np.random.normal(0, noise_level, interpolated_X.shape)\n            interpolated_X += noise\n            \n            # Add to augmented dataset\n            augmented_X.append(interpolated_X)\n            augmented_y.append(sample_y)\n    \n    # Convert to numpy arrays\n    augmented_X = np.array(augmented_X)\n    augmented_y = np.array(augmented_y)\n    \n    return augmented_X, augmented_y\n\ndef data_augmentation_pipeline(X, y, minority_classes=[1, 3]):\n    \"\"\"\n    Comprehensive data augmentation pipeline combining multiple techniques\n    综合数据增强管道，结合多种技术\n    \"\"\"\n    # 1. Targeted SMOTE augmentation\n    X_aug, y_aug = targeted_smote_augmentation(\n        X, y, \n        minority_classes=minority_classes, \n        multiplier=5.0  # Generate 5x samples for minority classes\n    )\n    \n    # 2. Oversample majority classes (classes 0 and 2) to maintain balance\n    if isinstance(y, pd.DataFrame):\n        y = y.to_numpy()\n    \n    # Process labels\n    y_processed = process_labels_for_4_classes(y)\n    if len(y_processed.shape) == 2:\n        y_classes = np.argmax(y_processed, axis=1)\n    else:\n        y_classes = y_processed\n    \n    # Count samples per class\n    class_counts = np.bincount(y_classes)\n    print(f\"Original class distribution: {dict(enumerate(class_counts))}\")\n    \n    # Find class with most samples after augmentation\n    aug_classes = np.argmax(y_aug, axis=1) if len(y_aug.shape) > 1 else y_aug\n    aug_class_counts = np.bincount(aug_classes)\n    target_count = np.max(aug_class_counts)\n    \n    # Oversample other classes\n    for cls in range(len(aug_class_counts)):\n        if cls in minority_classes:\n            continue  # Minority classes already augmented via SMOTE\n            \n        cls_indices = np.where(aug_classes == cls)[0]\n        n_to_generate = target_count - len(cls_indices)\n        \n        if n_to_generate <= 0:\n            continue\n            \n        print(f\"Oversampling class {cls}, generating {n_to_generate} samples\")\n        \n        # Random oversampling\n        for _ in range(n_to_generate):\n            idx = np.random.choice(cls_indices)\n            X_aug = np.vstack([X_aug, X_aug[idx:idx+1]])\n            y_aug = np.vstack([y_aug, y_aug[idx:idx+1]])\n    \n    # Final class distribution\n    final_classes = np.argmax(y_aug, axis=1) if len(y_aug.shape) > 1 else y_aug\n    final_class_counts = np.bincount(final_classes)\n    print(f\"Class distribution after augmentation: {dict(enumerate(final_class_counts))}\")\n    \n    return X_aug, y_aug\n\n# =====================================================================\n# Advanced LSTM Model with Fixed Attention / 修复注意力机制的高级LSTM模型\n# =====================================================================\ndef build_advanced_lstm_model(input_shape, num_classes):\n    \"\"\"\n    Build advanced LSTM model with fixed architecture for stability\n    构建稳定的高级LSTM模型架构\n    \"\"\"\n    inputs = Input(shape=input_shape, name=\"input_layer\")\n    \n    # 1. Feature extraction with Conv1D layers \n    conv1 = Conv1D(64, 3, padding='same', activation='relu')(inputs)\n    conv2 = Conv1D(64, 5, padding='same', activation='relu')(inputs)\n    x = Concatenate()([conv1, conv2])\n    x = BatchNormalization()(x)\n    x = Dropout(0.2)(x)\n    \n    # 2. First LSTM layer\n    x = Bidirectional(LSTM(128, return_sequences=True, recurrent_dropout=0.1))(x)\n    x = BatchNormalization()(x)\n    x = Dropout(0.3)(x)\n    \n    # 3. Second LSTM layer\n    x = Bidirectional(LSTM(64, return_sequences=True, recurrent_dropout=0.1))(x)\n    x = BatchNormalization()(x)\n    x = Dropout(0.3)(x)\n    \n    # 4. Attention mechanism - 修复实现\n    attention = Dense(1, activation='tanh')(x)\n    attention = Lambda(lambda x: tf.nn.softmax(x, axis=1))(attention)\n    x = Multiply()([x, attention])\n    \n    # 5. Dense layers\n    x = TimeDistributed(Dense(64, activation='relu'))(x)\n    x = LayerNormalization()(x)\n    x = Dropout(0.2)(x)\n    \n    # 6. Output layer\n    outputs = TimeDistributed(Dense(num_classes, activation='softmax'))(x)\n    \n    # Build model\n    model = Model(inputs, outputs)\n    \n    return model\n\n# =====================================================================\n# Fixed Training Function / 修复的训练函数\n# =====================================================================\ndef train_advanced_lstm_model(train_X, train_y, val_X, val_y, test_X, test_y):\n    \"\"\"\n    Train advanced LSTM model applying all optimization techniques\n    训练高级LSTM模型，应用所有优化技术\n    \"\"\"\n    # Ensure data is numpy arrays\n    train_X = np.array(train_X)\n    val_X = np.array(val_X)\n    test_X = np.array(test_X)\n    train_y = np.array(train_y)\n    val_y = np.array(val_y)\n    test_y = np.array(test_y)\n    \n    # Process labels for 4 classes\n    train_y = process_labels_for_4_classes(train_y)\n    val_y = process_labels_for_4_classes(val_y)\n    test_y = process_labels_for_4_classes(test_y)\n    \n    # Use RobustScaler for feature normalization\n    scaler = RobustScaler()\n    train_X_scaled = scaler.fit_transform(train_X)\n    val_X_scaled = scaler.transform(val_X)\n    test_X_scaled = scaler.transform(test_X)\n    \n    # Reshape data to LSTM input format\n    train_X_reshaped = reshape_for_lstm(train_X_scaled)\n    val_X_reshaped = reshape_for_lstm(val_X_scaled)\n    test_X_reshaped = reshape_for_lstm(test_X_scaled)\n    \n    # Reshape labels similarly\n    train_y_reshaped = train_y[:train_X_reshaped.shape[0] * LSTM_WINDOW_SIZE].reshape(\n        train_X_reshaped.shape[0], LSTM_WINDOW_SIZE, -1)\n    val_y_reshaped = val_y[:val_X_reshaped.shape[0] * LSTM_WINDOW_SIZE].reshape(\n        val_X_reshaped.shape[0], LSTM_WINDOW_SIZE, -1)\n    test_y_reshaped = test_y[:test_X_reshaped.shape[0] * LSTM_WINDOW_SIZE].reshape(\n        test_X_reshaped.shape[0], LSTM_WINDOW_SIZE, -1)\n    \n    print(f\"Training data shape: {train_X_reshaped.shape}, Label shape: {train_y_reshaped.shape}\")\n    print(f\"Validation data shape: {val_X_reshaped.shape}, Label shape: {val_y_reshaped.shape}\")\n    print(f\"Test data shape: {test_X_reshaped.shape}, Label shape: {test_y_reshaped.shape}\")\n    \n    # Calculate class weights\n    # Key: heavily boost minority class weights, especially classes 1 and 3\n    if len(train_y_reshaped.shape) == 3:\n        # Use labels from the last time step to calculate weights\n        flat_y = np.argmax(train_y_reshaped[:, -1, :], axis=1)\n        classes = np.unique(flat_y)\n        \n        # Calculate class counts\n        class_counts = np.bincount(flat_y)\n        total_samples = np.sum(class_counts)\n        \n        # Base weight formula\n        n_classes = len(classes)\n        class_weights = {i: (total_samples / (class_counts[i] * n_classes)) for i in range(n_classes)}\n        \n        # Specially boost minority class weights\n        if 1 in class_weights:  # StartHesitation\n            class_weights[1] *= 5.0\n        if 3 in class_weights:  # Walking\n            class_weights[3] *= 3.0\n    else:\n        print(\"Warning: Incorrect label shape, using average weights\")\n        num_classes = train_y_reshaped.shape[-1]\n        class_weights = {i: 1.0 for i in range(num_classes)}\n    \n    print(\"Using class weights:\", class_weights)\n    \n    # Get input shape and number of classes\n    input_shape = (train_X_reshaped.shape[1], train_X_reshaped.shape[2])\n    num_classes = train_y_reshaped.shape[2]\n    print(f\"Number of classes: {num_classes}\")\n    \n    # Build advanced model with fixed attention mechanisms\n    model = build_advanced_lstm_model(input_shape, num_classes)\n    \n    # Use custom focal loss with dynamic weights\n    focal_loss = focal_loss_with_dynamic_weights(class_weights, gamma=3.0)\n    \n    # Optimizer\n    optimizer = tf.keras.optimizers.Adam(\n        learning_rate=5e-4,\n        beta_1=0.9,\n        beta_2=0.999,\n        epsilon=1e-7\n    )\n    \n    # Apply gradient centralization\n    optimizer = gradient_centralization(optimizer)\n    \n    # 修复: 使用自定义的f1_metric而不是scikit-learn的f1_score\n    model.compile(\n        optimizer=optimizer,\n        loss=focal_loss,\n        metrics=['accuracy', f1_metric]\n    )\n    \n    # Show model summary\n    model.summary()\n    \n    # 修复: 确保回调函数中使用的度量名称与编译时一致\n    callbacks = [\n        # Aggressive learning rate scheduling\n        ReduceLROnPlateau(\n            monitor='val_loss',\n            factor=0.65,\n            patience=4,\n            min_lr=1e-6,\n            verbose=1\n        ),\n        # Early stopping (using validation F1 score)\n        EarlyStopping(\n            monitor='val_f1_metric',  # 修复: 度量名称需匹配\n            patience=12,\n            mode='max',\n            restore_best_weights=True,\n            verbose=1\n        ),\n        # Model checkpoint\n        ModelCheckpoint(\n            filepath=os.path.join(OUTPUT_DIR, \"best_model.keras\"),\n            monitor='val_f1_metric',  # 修复: 度量名称需匹配\n            mode='max',\n            save_best_only=True,\n            verbose=1\n        )\n    ]\n    \n    # Train model - 已移除class_weight参数以修复错误\n    print(\"Training with enhanced LSTM model and optimized strategy...\")\n    history = model.fit(\n        train_X_reshaped, train_y_reshaped,\n        validation_data=(val_X_reshaped, val_y_reshaped),\n        epochs=120,  # Increase epochs to let early stopping take effect\n        batch_size=12,  # Smaller batch size helps learn subtle patterns\n        callbacks=callbacks,\n        verbose=1\n        # 移除class_weight参数 - 已在loss函数中处理\n    )\n    \n    # 评估模型 (使用标准方法，不再直接使用f1_score)\n    print(\"\\nEvaluating model on test set...\")\n    test_loss, test_accuracy, test_f1 = model.evaluate(test_X_reshaped, test_y_reshaped, verbose=1)\n    print(f\"Test loss: {test_loss}\")\n    print(f\"Test accuracy: {test_accuracy}\")\n    print(f\"Test F1-Score: {test_f1}\")\n    \n    # 详细评估 - 将预测转换为NumPy数组后再使用scikit-learn\n    y_pred = model.predict(test_X_reshaped)\n    y_pred_flat = np.argmax(y_pred, axis=2).flatten()\n    y_true_flat = np.argmax(test_y_reshaped, axis=2).flatten()\n    \n    # 使用scikit-learn计算详细指标\n    f1 = f1_score(y_true_flat, y_pred_flat, average='weighted')\n    print(f\"Weighted F1-score (sklearn): {f1}\")\n    \n    # 为每个类别计算F1分数\n    class_f1 = f1_score(y_true_flat, y_pred_flat, average=None)\n    for i, score in enumerate(class_f1):\n        print(f\"Class {i} ({CLASS_NAMES[i]}) F1-score: {score}\")\n    \n    # 尝试计算AUC-ROC\n    try:\n        y_pred_proba = y_pred.reshape(-1, num_classes)\n        y_true_onehot = test_y_reshaped.reshape(-1, num_classes)\n        auc_score = roc_auc_score(y_true_onehot, y_pred_proba, multi_class='ovr')\n        print(f\"AUC-ROC: {auc_score}\")\n    except Exception as e:\n        print(f\"Error calculating AUC-ROC: {e}\")\n        auc_score = None\n    \n    return model, history, {\n        \"test_X_reshaped\": test_X_reshaped, \n        \"test_y_reshaped\": test_y_reshaped,\n        \"y_pred\": y_pred,\n        \"scaler\": scaler,\n        \"num_classes\": num_classes,\n        \"class_weights\": class_weights,\n        \"metrics\": {\n            \"test_loss\": test_loss,\n            \"test_accuracy\": test_accuracy,\n            \"f1_score\": f1,\n            \"class_f1\": class_f1,\n            \"auc_score\": auc_score\n        }\n    }\n\n# =====================================================================\n# Visualization Function / 可视化函数\n# =====================================================================\ndef visualize_results(model, history, test_data):\n    \"\"\"\n    Visualize model training results and performance\n    可视化模型训练结果和性能\n    \"\"\"\n    # Unpack test data\n    test_X_reshaped = test_data[\"test_X_reshaped\"]\n    test_y_reshaped = test_data[\"test_y_reshaped\"]\n    y_pred = test_data[\"y_pred\"]\n    num_classes = test_data[\"num_classes\"]\n    metrics = test_data.get(\"metrics\", {})\n    \n    # 1. Plot training history\n    plt.figure(figsize=(16, 6))\n    \n    # Loss curve\n    plt.subplot(1, 3, 1)\n    plt.plot(history.history['loss'], label='Training Loss')\n    plt.plot(history.history['val_loss'], label='Validation Loss')\n    plt.title('Model Loss')\n    plt.xlabel('Epoch')\n    plt.ylabel('Loss')\n    plt.legend()\n    \n    # Accuracy curve\n    plt.subplot(1, 3, 2)\n    plt.plot(history.history['accuracy'], label='Training Accuracy')\n    plt.plot(history.history['val_accuracy'], label='Validation Accuracy')\n    plt.title('Model Accuracy')\n    plt.xlabel('Epoch')\n    plt.ylabel('Accuracy')\n    plt.legend()\n    \n    # F1-score curve - corrected metric name\n    plt.subplot(1, 3, 3)\n    plt.plot(history.history['f1_metric'], label='Training F1')\n    plt.plot(history.history['val_f1_metric'], label='Validation F1')\n    plt.title('Model F1 Score')\n    plt.xlabel('Epoch')\n    plt.ylabel('F1 Score')\n    plt.legend()\n    \n    plt.tight_layout()\n    plt.savefig(os.path.join(RESULTS_DIR, \"training_history.png\"))\n    plt.show()\n    \n    # 2. Confusion matrix\n    y_pred_flat = np.argmax(y_pred, axis=2).flatten()\n    y_true_flat = np.argmax(test_y_reshaped, axis=2).flatten()\n    \n    cm = confusion_matrix(y_true_flat, y_pred_flat)\n    \n    plt.figure(figsize=(10, 8))\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n                xticklabels=CLASS_NAMES, \n                yticklabels=CLASS_NAMES)\n    plt.xlabel('Predicted Label')\n    plt.ylabel('True Label')\n    plt.title('Confusion Matrix')\n    plt.tight_layout()\n    plt.savefig(os.path.join(RESULTS_DIR, \"confusion_matrix.png\"))\n    plt.show()\n    \n    # 3. Class F1 score bar chart\n    class_f1 = metrics.get(\"class_f1\", [0, 0, 0, 0])\n    \n    plt.figure(figsize=(10, 6))\n    bars = plt.bar(CLASS_NAMES, class_f1, color='skyblue')\n    plt.title('F1 Score by Class')\n    plt.xlabel('Class')\n    plt.ylabel('F1 Score')\n    \n    # Show exact values on bars\n    for bar in bars:\n        height = bar.get_height()\n        plt.text(bar.get_x() + bar.get_width()/2., height + 0.02,\n                f'{height:.3f}', ha='center', va='bottom')\n    \n    plt.tight_layout()\n    plt.savefig(os.path.join(RESULTS_DIR, \"class_f1_scores.png\"))\n    plt.show()\n    \n    # 4. Classification report\n    report = classification_report(y_true_flat, y_pred_flat, \n                                  target_names=CLASS_NAMES, \n                                  output_dict=True)\n    \n    # Save as JSON\n    with open(os.path.join(RESULTS_DIR, \"classification_report.json\"), \"w\") as f:\n        json.dump(report, f, indent=4)\n    \n    # Print classification report\n    print(\"\\nClassification Report:\")\n    print(classification_report(y_true_flat, y_pred_flat, target_names=CLASS_NAMES))\n    \n    # 5. Prediction confidence distribution by class\n    plt.figure(figsize=(14, 10))\n    \n    for i in range(num_classes):\n        plt.subplot(2, 2, i+1)\n        \n        # Get samples truly belonging to this class\n        mask = y_true_flat == i\n        if np.sum(mask) > 0:\n            # Get prediction probabilities for these samples\n            probs = y_pred.reshape(-1, num_classes)[mask, i]\n            \n            # Plot histogram\n            sns.histplot(probs, bins=20, kde=True)\n            plt.title(f'Prediction Confidence for \"{CLASS_NAMES[i]}\"')\n            plt.xlabel('Prediction Confidence')\n            plt.ylabel('Sample Count')\n            plt.axvline(x=0.5, color='r', linestyle='--')\n        else:\n            plt.text(0.5, 0.5, f'No samples for class \"{CLASS_NAMES[i]}\"',\n                    horizontalalignment='center', verticalalignment='center')\n    \n    plt.tight_layout()\n    plt.savefig(os.path.join(RESULTS_DIR, \"confidence_distributions.png\"))\n    plt.show()\n    \n    return\n\n# =====================================================================\n# Main Function / 主函数\n# =====================================================================\ndef run_improved_model_training(train_X, train_y, val_X, val_y, test_X, test_y):\n    \"\"\"\n    Run improved model training and evaluation\n    运行改进的模型训练与评估\n    \"\"\"\n    print(\"=== Starting training and evaluation of improved LSTM model for Parkinson's gait freezing detection ===\")\n    start_time = time.time()\n    \n    try:\n        # 1. Special augmentation for minority classes\n        print(\"\\nPerforming special data augmentation for minority classes...\")\n        augmented_train_X, augmented_train_y = data_augmentation_pipeline(\n            train_X, train_y, \n            minority_classes=[1, 3]  # Focus on \"Start Hesitation\" and \"Walking\" classes\n        )\n        \n        # 2. Train advanced model\n        print(\"\\nTraining improved model...\")\n        model, history, test_data = train_advanced_lstm_model(\n            augmented_train_X, augmented_train_y, val_X, val_y, test_X, test_y\n        )\n        \n        # 3. Save model\n        model_path = os.path.join(OUTPUT_DIR, \"final_improved_lstm_model.keras\")\n        model.save(model_path)\n        print(f\"Model saved to: {model_path}\")\n        \n        # 4. Save feature scaler\n        scaler_path = os.path.join(OUTPUT_DIR, \"feature_scaler.pkl\")\n        joblib.dump(test_data[\"scaler\"], scaler_path)\n        print(f\"Feature scaler saved to: {scaler_path}\")\n        \n        # 5. Visualize results\n        print(\"\\nGenerating visualization results...\")\n        visualize_results(model, history, test_data)\n        \n        # Calculate total runtime\n        elapsed_time = time.time() - start_time\n        hours, rem = divmod(elapsed_time, 3600)\n        minutes, seconds = divmod(rem, 60)\n        print(f\"\\nExecution completed! Total time: {int(hours):02d}:{int(minutes):02d}:{seconds:05.2f}\")\n        \n        # Print summary results\n        metrics = test_data.get(\"metrics\", {})\n        print(\"\\n===== Model Performance Summary =====\")\n        print(f\"Test Accuracy: {metrics.get('test_accuracy', 'N/A')}\")\n        print(f\"Weighted F1-score: {metrics.get('f1_score', 'N/A')}\")\n        \n        # Print F1 score for each class\n        class_f1 = metrics.get(\"class_f1\", [])\n        if len(class_f1) > 0:\n            print(\"\\nF1-score by class:\")\n            for i, score in enumerate(class_f1):\n                print(f\"  {CLASS_NAMES[i]}: {score:.4f}\")\n        \n        if metrics.get('auc_score'):\n            print(f\"AUC-ROC: {metrics.get('auc_score')}\")\n        \n        return model, history, test_data\n        \n    except Exception as e:\n        print(f\"Error during training: {e}\")\n        import traceback\n        traceback.print_exc()\n        return None, None, None\n\n# =====================================================================\n# Prediction Function / 预测函数\n# =====================================================================\ndef predict_with_improved_model(model, scaler, new_data, window_size=LSTM_WINDOW_SIZE):\n    \"\"\"\n    Use the improved LSTM model for prediction\n    使用改进的LSTM模型进行预测\n    \"\"\"\n    # Ensure data is numpy array\n    if isinstance(new_data, pd.DataFrame):\n        new_data = new_data.values\n    \n    # Normalize features\n    new_data_scaled = scaler.transform(new_data)\n    \n    # Reshape to LSTM format\n    new_data_reshaped = reshape_for_lstm(new_data_scaled, window_size)\n    \n    # Make predictions\n    predictions = model.predict(new_data_reshaped)\n    \n    # Get class indices\n    predicted_classes = np.argmax(predictions, axis=2)\n    \n    # Get prediction confidence\n    prediction_confidence = np.max(predictions, axis=2)\n    \n    # Analyze predictions by class\n    class_predictions = {}\n    \n    for i, name in enumerate(CLASS_NAMES):\n        class_mask = predicted_classes == i\n        count = np.sum(class_mask)\n        avg_confidence = np.mean(prediction_confidence[class_mask]) if count > 0 else 0\n        \n        class_predictions[name] = {\n            'count': int(count),\n            'percentage': float(count / predicted_classes.size * 100),\n            'avg_confidence': float(avg_confidence)\n        }\n    \n    return {\n        \"raw_predictions\": predictions,\n        \"predicted_classes\": predicted_classes,\n        \"confidence\": prediction_confidence,\n        \"class_predictions\": class_predictions\n    }\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T20:54:40.388536Z","iopub.execute_input":"2025-04-27T20:54:40.388804Z","iopub.status.idle":"2025-04-27T20:54:51.598895Z","shell.execute_reply.started":"2025-04-27T20:54:40.388783Z","shell.execute_reply":"2025-04-27T20:54:51.598199Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"if __name__ == \"__main__\":\n    # Load data / 加载数据\n    print(\"Loading data...\")\n    data = joblib.load(\"/kaggle/working/processed_data.pkl\")\n    train_X, train_y = data['train']\n    val_X, val_y = data['val']\n    test_X, test_y = data['test']\n    \n    # Run improved model training / 运行改进的模型训练\n    print(\"Starting model training...\")\n    model, history, test_data = run_improved_model_training(\n        train_X, train_y, val_X, val_y, test_X, test_y\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-27T20:54:51.599693Z","iopub.execute_input":"2025-04-27T20:54:51.600481Z","iopub.status.idle":"2025-04-27T21:07:14.580199Z","shell.execute_reply.started":"2025-04-27T20:54:51.600453Z","shell.execute_reply":"2025-04-27T21:07:14.579409Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}