diff --git a/强化学习个人课程作业报告/notebooks/insurance_premium_risk.ipynb b/强化学习个人课程作业报告/notebooks/insurance_premium_risk.ipynb index b8dfc35..cd30a74 100644 --- a/强化学习个人课程作业报告/notebooks/insurance_premium_risk.ipynb +++ b/强化学习个人课程作业报告/notebooks/insurance_premium_risk.ipynb @@ -44,9 +44,7 @@ "id": "a12f069a", "metadata": {}, "outputs": [], - "source": [ - "import warnings\nwarnings.filterwarnings('ignore')\nimport os\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.metrics import accuracy_score, f1_score, classification_report, confusion_matrix, ConfusionMatrixDisplay\nfrom sklearn.model_selection import cross_val_score\nfrom sklearn.preprocessing import StandardScaler, LabelEncoder\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.ensemble import RandomForestClassifier, VotingClassifier\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.compose import ColumnTransformer\nfrom sklearn.preprocessing import OneHotEncoder\nfrom sklearn.impute import SimpleImputer\nfrom sklearn.cluster import KMeans\nfrom sklearn.mixture import GaussianMixture\nfrom sklearn.metrics import silhouette_score\nfrom sklearn.decomposition import PCA\nimport xgboost as xgb\nimport optuna\noptuna.logging.set_verbosity(optuna.logging.WARNING)\n\nRANDOM_STATE = 42\nnp.random.seed(RANDOM_STATE)\nplt.rcParams['figure.figsize'] = (10, 6)\nplt.rcParams['font.size'] = 12\nsns.set_style('whitegrid')\nprint('All libraries imported successfully!')" - ] + "source": "import xgboost as xgb\nimport optuna\noptuna.logging.set_verbosity(optuna.logging.WARNING)\n\n# GPU Fallback: 自动检测CUDA可用性,无GPU时自动切换到CPU\ntry:\n import subprocess\n result = subprocess.run(['nvidia-smi'], capture_output=True, text=True)\n USE_GPU = result.returncode == 0\nexcept:\n USE_GPU = False\n\nXGB_TREE_METHOD = 'gpu_hist' if USE_GPU else 'hist'\nXGB_DEVICE = 'cuda' if USE_GPU else 'cpu'\nprint(f'XGBoost compute method: {\"GPU (CUDA)\" if USE_GPU else \"CPU\"}')\n\nRANDOM_STATE = 42\nnp.random.seed(RANDOM_STATE)\nplt.rcParams['figure.figsize'] = (10, 6)\nplt.rcParams['font.size'] = 12\nsns.set_style('whitegrid')\nprint('All libraries imported successfully!')" }, { "cell_type": "code", @@ -227,9 +225,7 @@ "id": "30cd02ce", "metadata": {}, "outputs": [], - "source": [ - "import time\n\nprint('Training Random Forest...')\nstart = time.time()\nrf_pipeline = Pipeline(steps=[\n ('preprocessor', preprocessor),\n ('classifier', RandomForestClassifier(n_estimators=200, class_weight='balanced', random_state=RANDOM_STATE, n_jobs=-1))\n])\nrf_pipeline.fit(X_train, y_train_enc)\nrf_time = time.time() - start\n\nrf_results = evaluate_model(rf_pipeline, X_train, y_train_enc, X_val, y_val_enc, le_target, 'RandomForest')\nrf_results['train_time'] = rf_time\n\nprint('Training XGBoost...')\nstart = time.time()\nxgb_pipeline = Pipeline(steps=[\n ('preprocessor', preprocessor),\n ('classifier', xgb.XGBClassifier(n_estimators=200, learning_rate=0.1, max_depth=6,\n objective='multi:softmax', num_class=3,\n tree_method='gpu_hist', device='cuda', random_state=RANDOM_STATE, verbosity=0))\n])\nxgb_pipeline.fit(X_train, y_train_enc)\nxgb_time = time.time() - start\n\nxgb_results = evaluate_model(xgb_pipeline, X_train, y_train_enc, X_val, y_val_enc, le_target, 'XGBoost')\nxgb_results['train_time'] = xgb_time\n\nprint(f'RF time: {rf_time:.2f}s | XGB time: {xgb_time:.2f}s')" - ] + "source": "print('Training Random Forest...')\nstart = time.time()\nrf_pipeline = Pipeline(steps=[\n ('preprocessor', preprocessor),\n ('classifier', RandomForestClassifier(n_estimators=200, class_weight='balanced', random_state=RANDOM_STATE, n_jobs=-1))\n])\nrf_pipeline.fit(X_train, y_train_enc)\nrf_time = time.time() - start\n\nrf_results = evaluate_model(rf_pipeline, X_train, y_train_enc, X_val, y_val_enc, le_target, 'RandomForest')\nrf_results['train_time'] = rf_time\n\nprint('Training XGBoost...')\nstart = time.time()\nxgb_pipeline = Pipeline(steps=[\n ('preprocessor', preprocessor),\n ('classifier', xgb.XGBClassifier(n_estimators=200, learning_rate=0.1, max_depth=6,\n objective='multi:softmax', num_class=3,\n tree_method=XGB_TREE_METHOD, device=XGB_DEVICE,\n random_state=RANDOM_STATE, verbosity=0))\n])\nxgb_pipeline.fit(X_train, y_train_enc)\nxgb_time = time.time() - start\n\nxgb_results = evaluate_model(xgb_pipeline, X_train, y_train_enc, X_val, y_val_enc, le_target, 'XGBoost')\nxgb_results['train_time'] = xgb_time\n\nprint(f'RF time: {rf_time:.2f}s | XGB time: {xgb_time:.2f}s')" }, { "cell_type": "code", @@ -293,9 +289,7 @@ "id": "e6361576", "metadata": {}, "outputs": [], - "source": [ - "def objective(trial):\n params = {\n 'n_estimators': trial.suggest_int('n_estimators', 100, 500),\n 'max_depth': trial.suggest_int('max_depth', 3, 10),\n 'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3, log=True),\n 'min_child_weight': trial.suggest_int('min_child_weight', 1, 10),\n 'subsample': trial.suggest_float('subsample', 0.5, 1.0),\n 'colsample_bytree': trial.suggest_float('colsample_bytree', 0.5, 1.0),\n 'gamma': trial.suggest_float('gamma', 0, 5),\n 'reg_alpha': trial.suggest_float('reg_alpha', 1e-4, 10.0, log=True),\n 'reg_lambda': trial.suggest_float('reg_lambda', 1e-4, 10.0, log=True),\n 'objective': 'multi:softmax',\n 'num_class': 3,\n 'random_state': RANDOM_STATE,\n 'tree_method': 'gpu_hist', 'device': 'cuda',\n 'verbosity': 0\n }\n pipeline = Pipeline(steps=[\n ('preprocessor', preprocessor),\n ('classifier', xgb.XGBClassifier(**params))\n ])\n pipeline.fit(X_train, y_train_enc)\n y_pred = pipeline.predict(X_val)\n score = f1_score(y_val_enc, y_pred, average='macro')\n return score\n\nprint('Starting Optuna hyperparameter optimisation (30 trials)...')\nstudy = optuna.create_study(direction='maximize', sampler=optuna.samplers.TPESampler(seed=RANDOM_STATE))\nstudy.optimize(objective, n_trials=30, show_progress_bar=False)\n\nprint(f'Best trial: {study.best_trial.number} | Best macro-F1: {study.best_value:.4f}')" - ] + "source": "def objective(trial):\n params = {\n 'n_estimators': trial.suggest_int('n_estimators', 100, 500),\n 'max_depth': trial.suggest_int('max_depth', 3, 10),\n 'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3, log=True),\n 'min_child_weight': trial.suggest_int('min_child_weight', 1, 10),\n 'subsample': trial.suggest_float('subsample', 0.5, 1.0),\n 'colsample_bytree': trial.suggest_float('colsample_bytree', 0.5, 1.0),\n 'gamma': trial.suggest_float('gamma', 0, 5),\n 'reg_alpha': trial.suggest_float('reg_alpha', 1e-4, 10.0, log=True),\n 'reg_lambda': trial.suggest_float('reg_lambda', 1e-4, 10.0, log=True),\n 'objective': 'multi:softmax',\n 'num_class': 3,\n 'random_state': RANDOM_STATE,\n 'tree_method': XGB_TREE_METHOD,\n 'device': XGB_DEVICE,\n 'verbosity': 0\n }\n pipeline = Pipeline(steps=[\n ('preprocessor', preprocessor),\n ('classifier', xgb.XGBClassifier(**params))\n ])\n pipeline.fit(X_train, y_train_enc)\n y_pred = pipeline.predict(X_val)\n score = f1_score(y_val_enc, y_pred, average='macro')\n return score\n\nprint('Starting Optuna hyperparameter optimisation (30 trials)...')\nstudy = optuna.create_study(direction='maximize', sampler=optuna.samplers.TPESampler(seed=RANDOM_STATE))\nstudy.optimize(objective, n_trials=30, show_progress_bar=False)\n\nprint(f'Best trial: {study.best_trial.number} | Best macro-F1: {study.best_value:.4f}')" }, { "cell_type": "code", @@ -313,9 +307,7 @@ "id": "640263ea", "metadata": {}, "outputs": [], - "source": [ - "best_xgb_params = {\n **study.best_params,\n 'objective': 'multi:softmax',\n 'num_class': 3,\n 'random_state': RANDOM_STATE,\n 'tree_method': 'gpu_hist', 'device': 'cuda',\n 'verbosity': 0\n}\n\nprint('Training tuned XGBoost...')\nimport time\nstart = time.time()\ntuned_xgb_pipeline = Pipeline(steps=[\n ('preprocessor', preprocessor),\n ('classifier', xgb.XGBClassifier(**best_xgb_params))\n])\ntuned_xgb_pipeline.fit(X_train, y_train_enc)\ntuned_time = time.time() - start\n\ntuned_results = evaluate_model(tuned_xgb_pipeline, X_train, y_train_enc, X_val, y_val_enc, le_target, 'XGBoost_Tuned')\ntuned_results['train_time'] = tuned_time\n\nprint('\\n=== TUNED XGBOOST RESULTS ===')\nfor k, v in tuned_results.items():\n if k != 'model':\n print(f'{k}: {v:.4f}')\n\nprint(f'\\nTuning improvement (macro-F1): +{tuned_results[\"val_f1_macro\"] - xgb_results[\"val_f1_macro\"]:.4f}')" - ] + "source": "best_xgb_params = {\n **study.best_params,\n 'objective': 'multi:softmax',\n 'num_class': 3,\n 'random_state': RANDOM_STATE,\n 'tree_method': XGB_TREE_METHOD,\n 'device': XGB_DEVICE,\n 'verbosity': 0\n}\n\nprint('Training tuned XGBoost...')\nimport time\nstart = time.time()\ntuned_xgb_pipeline = Pipeline(steps=[\n ('preprocessor', preprocessor),\n ('classifier', xgb.XGBClassifier(**best_xgb_params))\n])\ntuned_xgb_pipeline.fit(X_train, y_train_enc)\ntuned_time = time.time() - start\n\ntuned_results = evaluate_model(tuned_xgb_pipeline, X_train, y_train_enc, X_val, y_val_enc, le_target, 'XGBoost_Tuned')\ntuned_results['train_time'] = tuned_time\n\nprint('\\n=== TUNED XGBOOST RESULTS ===')\nfor k, v in tuned_results.items():\n if k != 'model':\n print(f'{k}: {v:.4f}')\n\nprint(f'\\nTuning improvement (macro-F1): +{tuned_results[\"val_f1_macro\"] - xgb_results[\"val_f1_macro\"]:.4f}')" }, { "cell_type": "code", diff --git a/强化学习个人课程作业报告/机器学习个人课程作业_需求分析与实现方案.md b/强化学习个人课程作业报告/机器学习个人课程作业_需求分析与实现方案.md deleted file mode 100644 index 2680fb0..0000000 --- a/强化学习个人课程作业报告/机器学习个人课程作业_需求分析与实现方案.md +++ /dev/null @@ -1,942 +0,0 @@ -# 机器学习个人课程作业需求分析与实现方案 - -## 1. 文档目的 - -本文档基于以下材料整理: - -- `外教课/原文要求.txt` -- `外教课/课程作业实现方案分析.md` -- `外教课/课程作业整合及任务拆解与时间规划清单.md` -- `资料/DTS304TC_Assessment1_(word)_2026(1).pdf` -- `资料/dataset_final(1).zip` 中的数据文件结构 - -目标是输出一份面向实际执行的详细需求分析和实现方案,用于指导 `Jupyter Notebook + Theory and Reflection PDF + hidden-test CSV + 补充代码` 的完整完成过程。 - ---- - -## 2. 原始任务核心要求 - -### 2.1 作业目标 - -需要围绕一个虚构但接近真实场景的健康保险数据集,建立并改进一个多分类模型,用于预测申请人的保费风险等级: - -- `Low` -- `Standard` -- `High` - -该任务不是单纯追求 leaderboard 排名,而是要求展示完整、规范、可复现的机器学习工作流,包括: - -- 数据清理与预处理 -- 数据泄露特征识别与删除 -- 基线模型建立 -- `Random Forest` 与一种 `Boosting` 模型的公平对比 -- 至少一个主模型的高级超参数优化 -- 按学号末位完成一个必做改进类别,并额外完成至少一个可选改进类别 -- `K-Means` 与 `GMM` 的无监督探索 -- 基于验证集证据选择最终模型 -- 生成规定格式的 hidden-test 预测结果 CSV - -### 2.2 提交物 - -必须提交以下文件: - -- 一个 `Jupyter Notebook`,格式为 `.ipynb` -- 一个 `Coursework Answer Sheet / Theory and Reflection PDF` -- 一个 hidden-test 预测结果 `CSV` -- 如使用 notebook 外部的辅助脚本,也必须一并提交 - -### 2.3 成绩结构 - -根据 PDF 原文,整份作业分值结构如下: - -- `Question 1: Notebook-Based Coding Exercise`:`60` 分 -- `Theory and Reflection PDF`:`30` 分 -- `Coding Quality / Answer Sheet Quality / Submission Guidelines`:`10` 分 - -这意味着不能只重模型分数,文档质量、实验组织、结果解释和提交格式同样直接影响成绩。 - ---- - -## 3. 从原始 PDF 提炼出的硬性约束 - -以下内容属于正式要求中的关键硬约束,必须优先满足。 - -### 3.1 评价指标约束 - -- 主指标是 `macro-F1` -- `accuracy` 只是辅助指标 -- 所有重要模型对比、调参结果、改进结果都应同时报告: - - `macro-F1` - - `accuracy` - -原因是类别不平衡明显,不能只用准确率判断模型优劣。 - -### 3.2 数据泄露约束 - -PDF 明确指出: - -- 数据中存在 `1` 个泄露特征 -- 必须先识别并移除,再进入后续分析 -- 如果没有删除,后续多个部分都会被视为无效或严重失真 - -这说明“找出泄露特征”不是建议项,而是作业关键检查点。 - -### 3.3 模型比较约束 - -Notebook 中必须完成: - -- 一个 baseline pipeline -- 一个 `Random Forest` -- 一个 `Boosting` 模型 - -并且需要满足“受控比较”原则: - -- 使用同一套预处理流程 -- 使用同一份训练/验证划分 -- 使用同一评价指标 - -否则比较结论不成立。 - -### 3.4 高级调参约束 - -至少一个主模型需要使用真正的高级调参方法,例如: - -- `Optuna/TPE` -- 贝叶斯优化 -- `Hyperopt` -- `Ray Tune` - -PDF 明确说明: - -- 单独使用 `RandomizedSearchCV` 通常不足以进入高分档 - -因此建议主方案使用 `Optuna`。 - -### 3.5 个性化改进约束 - -必须完成: - -- 根据学号末位对应的 `1` 个必做类别 -- 额外至少 `1` 个自选类别 - -推荐但非强制: - -- 再额外完成第 `2` 个可选类别,以增强区分度 - -### 3.6 无监督探索约束 - -必须完成紧凑版的: - -- `K-Means` -- `Gaussian Mixture Model (GMM)` - -重点不是聚类分数超过监督学习模型,而是体现: - -- 对无监督方法机制的理解 -- 对结果的谨慎解释 -- 对 `hard assignment` 和 `soft assignment` 区别的认识 - -### 3.7 hidden-test 导出约束 - -最终 CSV 必须: - -- 文件名为 `test_result_[your_student_id].csv` -- 第一列必须是 `applicant_id` -- 第二列必须是 `customer_key` -- 第三列必须是 `premium_risk` -- 预测标签只能使用: - - `Standard` - - `High` - - `Low` - -PDF 明确说明: - -- 命名或格式错误会在该部分自动扣 `4` 分 -- 不允许在 hidden test 上调参 -- 不允许声称 hidden test 性能 - -### 3.8 PDF 写作约束 - -`Theory and Reflection PDF` 必须: - -- 不超过 `4` 页 -- 不超过 `1200` 词 -- 不得简单重复 notebook 内容 -- 每一个理论问题都必须引用 notebook 中至少一个表、图或指标 - -超过页数或字数限制会固定扣 `5` 分。 - -### 3.9 AI 使用约束 - -PDF 原文给出了比普通课程更严格的 AI 使用边界: - -- 不允许直接用 ChatGPT 生成作业答案 -- AI 只能作为有限支持工具: - - 代码理解 - - 调试 - - 语法润色 -- AI 不能替代: - - 方法设计 - - 消融逻辑 - - 定性分析 - - 反思写作 - -因此最终提交物必须明显体现“你自己做了实验和分析”。 - ---- - -## 4. 数据集层面的已知信息 - -基于对 `dataset_final(1).zip` 的结构和文件头信息检查,目前已确认: - -### 4.1 数据文件 - -压缩包内包含: - -- `dataset_final/train.csv` -- `dataset_final/val.csv` -- `dataset_final/test_features.csv` - -### 4.2 数据规模 - -- `train.csv`:`74375 x 33` -- `val.csv`:`13125 x 33` -- `test_features.csv`:`12500 x 32` - -说明: - -- 训练集和验证集包含标签列 `premium_risk` -- hidden-test 文件不包含标签 - -### 4.3 字段结构 - -训练集列名包括: - -- 标识类字段:`applicant_id`, `customer_key`, `applicant_ref_code` -- 时间/类别字段:`application_month`, `employment_sector`, `prior_debt_products`, `debt_portfolio_quality`, `account_tenure`, `minimum_payment_only`, `spending_profile` -- 数值特征:收入、负债、额度变化、查询次数、逾期情况、投资金额、余额等 -- 明显可疑字段:`bureau_risk_index` -- 噪声字段:`noise_feature_1` 到 `noise_feature_5` -- 标签:`premium_risk` - -### 4.4 类别分布 - -训练集标签分布: - -- `Standard`: `39686` -- `High`: `21586` -- `Low`: `13103` - -结论: - -- 数据明显不平衡 -- 使用 `macro-F1` 作为主指标完全合理 -- 在个性化改进中,`Category C` 类的重采样、类权重、阈值逻辑会很自然 - -### 4.5 缺失值概览 - -当前观察到缺失值较多的字段包括: - -- `net_monthly_income_gbp` -- `avg_payment_delay_days` -- `monthly_investment_gbp` -- `prior_debt_products` -- `account_tenure` -- `late_payment_count` -- `credit_limit_change_pct` -- `credit_inquiry_count` -- `end_month_balance_gbp` - -这说明预处理部分不能仅做“简单删行”,更适合使用 pipeline 化的缺失值处理方案。 - ---- - -## 5. 对任务本质的理解 - -这份作业本质上考查的不是“谁把模型调得最高”,而是以下四个能力: - -- 是否能建立规范的机器学习实验流程 -- 是否能识别不合理特征并避免数据泄露 -- 是否能做公平比较、合理调参和证据驱动分析 -- 是否能把理论概念和自己的实验结果一一对应起来 - -因此,高分解法必须同时满足: - -- 模型结果合理 -- 实验过程规范 -- 分析论证充分 -- notebook 和 PDF 严格互相对应 - ---- - -## 6. Notebook 需求拆解 - -下面按原始评分结构,对 notebook 部分做可执行拆解。 - -### 6.1 A 部分:清洗、预处理与基线建模 - -必须完成的内容: - -- 读取 `train.csv`、`val.csv`、`test_features.csv` -- 明确 `X_train / y_train / X_val / y_val / X_test` -- 识别并删除泄露特征 -- 处理脏值、缺失值、类别变量 -- 建立一个基线 pipeline -- 报告 baseline 的: - - `accuracy` - - `macro-F1` - - confusion matrix -- 确保 train、val、test 使用完全一致的预处理规则 - -建议实现: - -- 使用 `ColumnTransformer + Pipeline` -- 数值特征: - - `SimpleImputer(strategy='median')` -- 类别特征: - - `SimpleImputer(strategy='most_frequent')` - - `OneHotEncoder(handle_unknown='ignore')` -- baseline 模型: - - `LogisticRegression` - - 或 `HistGradientBoosting` 前的简单基线 - -更稳妥的推荐是: - -- 用 `LogisticRegression(class_weight='balanced')` 作为 baseline - -原因: - -- 简单 -- 可解释 -- 适合作为后续树模型的比较起点 - -### 6.2 泄露特征识别策略 - -由于 PDF 强调必须自行识别,不建议在正式 notebook 中直接“拍脑袋认定”某列是泄露。 - -建议采用下面的证据链: - -1. 先从业务语义初筛高风险列 - 候选优先检查: - - `bureau_risk_index` - - 各类明显后验统计或接近标签定义的字段 - -2. 做单变量或极简模型筛查 - 例如: - - 单列训练一个简单模型 - - 比较每列单独带来的验证集 `macro-F1` - -3. 检查“异常高”的预测能力 - 若某列单独就能异常接近目标标签,则高度怀疑为 leakage - -4. 删除该特征后重新建立基线 - 在 notebook 中明确说明: - - 删除前风险 - - 删除后原因 - - 为什么后续分析必须基于删除后的数据 - -注意: - -- 从字段命名看,`bureau_risk_index` 是最值得优先怀疑的候选 -- 但正式提交中最好写成“通过字段语义 + 验证结果发现其构成泄露或近似泄露” - -### 6.3 B 部分:随机森林与 boosting 的受控比较 - -必须完成: - -- 保持同一预处理 -- 比较: - - `RandomForestClassifier` - - 一个 boosting 模型 - -推荐 boosting 模型优先级: - -1. `XGBoost` -2. `LightGBM` -3. `HistGradientBoostingClassifier` - -推荐理由: - -- PDF 明确点名推荐 `XGBoost` -- 后续调参空间更丰富 -- 更容易做高质量的超参数优化 - -本节输出建议至少包含: - -- 模型对比表: - - accuracy - - macro-F1 - - 训练时间 -- 每个模型的 confusion matrix -- 分类报告或按类别 F1 -- 简短解释 bagging 与 boosting 在本数据集上的差异 - -重点: - -- 不是证明谁永远更强 -- 而是说明在当前数据集上,哪种偏差-方差特性更适合 - -### 6.4 C 部分:高级超参数优化 - -必须完成: - -- 至少选择一个主模型 -- 使用高级优化方法进行调参 -- 目标函数对准验证集 `macro-F1` - -推荐主模型: - -- `XGBoost` - -推荐优化器: - -- `Optuna` 的 `TPESampler` - -推荐搜索空间示例: - -- `n_estimators` -- `max_depth` -- `learning_rate` -- `min_child_weight` -- `subsample` -- `colsample_bytree` -- `gamma` -- `reg_alpha` -- `reg_lambda` - -建议输出: - -- 最优参数表 -- 前若干 trial 的结果摘要 -- 调参前后性能对比表 -- 关键超参数的重要性解释 - -注意: - -- 需要简要说明为什么搜索空间这样设 -- 需要说明“原本预期哪些参数最关键” -- 需要说明“调参结果是否符合预期” - -这部分内容将直接为 PDF 中第 2 题服务。 - -### 6.5 D 部分:个性化改进 - -这是 notebook 中占比最高、最容易拉开差距的部分。 - -#### 学号末位对应关系 - -- `0-1` -> `Category A`:数据质量与缺失机制 -- `2-3` -> `Category B`:特征表示与特征工程 -- `4-5` -> `Category C`:类别不平衡与目标设计 -- `6-7` -> `Category D`:鲁棒性、校准或集成 -- `8-9` -> `Category E`:公平性、诊断或可解释性 - -#### 每类推荐落地方式 - -`Category A` 可选实现: - -- `IterativeImputer` -- 更细粒度缺失指示器 -- 异常值裁剪或 winsorization -- 脏值统一清洗 - -`Category B` 可选实现: - -- 特征交叉 -- 类别合并 -- 目标无监督编码之外的安全表示 -- log 变换、比例特征、债务收入比等衍生变量 - -`Category C` 可选实现: - -- `class_weight` -- `SMOTE` 或其他重采样 -- focal-like 思想的替代实现 -- 基于验证集的阈值调整 - -`Category D` 可选实现: - -- 概率校准 -- soft voting -- stacking -- bootstrap 稳定性测试 - -`Category E` 可选实现: - -- `SHAP` -- permutation importance -- 分组公平性检查 -- 错误样本分析 -- 高风险误判模式总结 - -#### 强烈建议的写法 - -无论学号末位落在哪类,都建议再加一个容易出效果的 optional 类别: - -- 若主模型是树模型,优先补: - - `Category E` 可解释性 - - 或 `Category D` 集成/校准 - -这是因为: - -- PDF 明确欢迎“具体洞见” -- 可解释性和误差分析非常利于写 reflection -- 这些内容能让 PDF 更有证据,不容易空泛 - -#### 个性化改进部分必须有的证据 - -- 紧凑版 ablation table -- 改进前后 accuracy / macro-F1 对比 -- 必要时增加 class-wise F1 -- 简短解释: - - 做了什么 - - 为什么这样做 - - 结果是否提升 - - 若未提升,为什么仍然有价值 - -### 6.6 E 部分:K-Means 与 GMM 探索 - -这一部分应控制篇幅,不宜做成主线。 - -建议流程: - -1. 从清洗后的特征中取适合聚类的数值空间 -2. 必要时先做缩放 -3. 可以考虑降维到: - - PCA 2D 用于可视化 -4. 对 `k=2~8` 分别跑: - - `KMeans` - - `GaussianMixture` - -建议输出: - -- `K-Means`: - - inertia / SSE 曲线 - - cluster size - - silhouette score(可选加强) -- `GMM`: - - BIC / AIC 或 log-likelihood 趋势 - - component size - - posterior probability / responsibility 统计 - -最后做一个对比表或图,回答: - -- 为什么 `K-Means` 是 hard assignment -- 为什么 `GMM` 是 soft assignment -- 当前数据是否存在模糊边界 -- `GMM` 是否额外揭示了不确定性或重叠结构 - -### 6.7 F 部分:最终模型与 hidden-test 导出 - -必须满足的原则: - -- 只能依据验证集结果选最终模型 -- 不允许根据 hidden-test 结果回头调参 - -建议流程: - -1. 锁定最终 pipeline -2. 用 `train + val` 合并后重新训练 -3. 对 `test_features.csv` 预测 -4. 生成严格符合格式的 CSV - -推荐导出逻辑: - -- 从 `test_features.csv` 保留: - - `applicant_id` - - `customer_key` -- 新增一列: - - `premium_risk` - -最终列顺序必须是: - -1. `applicant_id` -2. `customer_key` -3. `premium_risk` - ---- - -## 7. 推荐的整体实现路线 - -下面给出一条适合拿分且可操作的实现主线。 - -### 7.1 技术栈建议 - -- Python -- `pandas` -- `numpy` -- `scikit-learn` -- `xgboost` -- `optuna` -- `matplotlib` -- `seaborn` -- `shap`(如选择可解释性方向) -- `imbalanced-learn`(如选择重采样方向) - -### 7.2 推荐项目结构 - -```text -coursework_ml/ -├─ notebook/ -│ └─ insurance_premium_risk.ipynb -├─ src/ -│ ├─ data_utils.py -│ ├─ features.py -│ ├─ metrics.py -│ ├─ tuning.py -│ └─ export.py -├─ outputs/ -│ ├─ figures/ -│ ├─ tables/ -│ └─ predictions/ -├─ report/ -│ └─ theory_and_reflection.pdf -└─ README.md -``` - -如果想降低复杂度,也可以只保留: - -- 一个主 notebook -- 一到两个辅助 `.py` 脚本 - -关键是要保证复现性,而不是强行复杂化。 - -### 7.3 Notebook 章节建议 - -建议 notebook 按下面顺序组织: - -1. Introduction and Setup -2. Data Loading -3. Data Cleaning and Leakage Check -4. Baseline Pipeline -5. Controlled Comparison: Random Forest vs Boosting -6. Advanced Hyperparameter Optimisation -7. Personalised Improvement Work -8. K-Means and GMM Exploration -9. Final Model Selection -10. Hidden-Test Export -11. Conclusion - -优点: - -- 与评分结构高度对齐 -- PDF 写作时可以直接反向索引表格和图片 - ---- - -## 8. 推荐的模型方案 - -### 8.1 baseline - -推荐: - -- `LogisticRegression` - -作用: - -- 给出最低可比较基线 -- 验证预处理链是否稳定 -- 提供线性模型与树模型的风格对照 - -### 8.2 bagging 方案 - -推荐: - -- `RandomForestClassifier` - -关注点: - -- 对类别变量经 one-hot 后的适应性 -- 是否更稳但不够激进 -- 是否在少数类上表现一般 - -### 8.3 boosting 方案 - -推荐首选: - -- `XGBoost` - -备选: - -- `LightGBM` - -若环境依赖受限可退而求其次: - -- `HistGradientBoostingClassifier` - -### 8.4 最终模型的推荐候选 - -最可能的优胜路线通常是: - -- 删除泄露特征后 -- 用稳定的预处理 pipeline -- 以 `XGBoost` 作为主模型 -- 叠加一个与你学号类别匹配的必做改进 -- 再叠加一个可解释性或校准类的 optional 改进 - -一个较稳的最终候选组合是: - -- 主模型:调参后的 `XGBoost` -- 必做改进:按学号末位选择 -- 可选改进:`SHAP + error analysis` 或 `probability calibration` - ---- - -## 9. PDF 的写作映射方案 - -为了避免 PDF 和 notebook 脱节,建议从 notebook 设计开始就准备好下面这些证据位。 - -### 9.1 Bagging vs Boosting - -PDF 要回答: - -- bagging 和 boosting 的定义与性质 -- 两个模型的验证结果 -- 辅助分析 -- 与本数据集的结合解释 - -Notebook 需要提前准备: - -- `RF vs XGB` 对比表 -- confusion matrix -- class-wise F1 -- 调参前后稳定性或训练/验证表现 - -### 9.2 Hyperparameter Optimisation - -PDF 要回答: - -- 优化器为什么合理 -- 搜索空间为什么合理 -- 哪些参数最重要 -- 调参结果是否符合预期 - -Notebook 需要提前准备: - -- Optuna study 结果表 -- 最优参数 -- 调参前后指标变化 -- 若可能,参数重要性图 - -### 9.3 K-Means vs GMM - -PDF 要回答: - -- hard vs soft assignment -- 两者假设差异 -- 当前数据上的观察结论 - -Notebook 需要提前准备: - -- 一张聚类比较图 -- 一张指标对比表 -- 一段关于重叠和不确定性的解释 - -### 9.4 Personalised Reflection - -PDF 要回答: - -- 必做类别做了什么 -- 可选类别做了什么 -- 遇到的问题 -- 做出的努力 -- 学到了什么 - -Notebook 需要提前准备: - -- ablation table -- before/after 结果对比 -- 若有失败实验,也保留最关键的一两个作为证据 - -### 9.5 AI-use Declaration - -PDF 中建议采用诚实、克制、可核验的表述,例如: - -- 使用 AI 协助理解报错、检查代码逻辑、润色语言 -- 所有方法设计、实验执行、结果核验和结论撰写均由本人完成 -- 所有表格、图和结论均以 notebook 实验结果为依据 - -注意: - -- 不能写成“AI 帮我完成了模型设计” -- 不能出现与 notebook 证据对不上的泛化陈述 - ---- - -## 10. 风险点分析 - -### 10.1 最大风险:没有先删除泄露特征 - -后果: - -- 分数看似很高 -- 但整个分析会被视为失真 -- 影响 baseline、比较、调参和最终模型选择 - -### 10.2 常见风险:比较不公平 - -表现: - -- baseline 和后续模型使用了不同预处理 -- 一个模型用 train,一个模型用 train+val -- 某些模型调参后和默认模型直接横比 - -后果: - -- 结论缺乏可信度 - -### 10.3 常见风险:只报 accuracy - -由于类别不平衡: - -- 只看 accuracy 会掩盖少数类问题 -- 很容易丢掉对 `Low` 或 `High` 类的分析深度 - -### 10.4 常见风险:个性化改进做成“试很多但没有逻辑” - -PDF 原文明确更看重: - -- meaningful diagnostic -- concrete insight - -而不是: - -- 一大堆零散实验截图 - -### 10.5 常见风险:PDF 空泛 - -如果 PDF 只是教材知识复述,而没有引用 notebook 的具体图表或指标,会被明显扣分。 - -### 10.6 常见风险:CSV 格式错误 - -尤其要避免: - -- 文件名错误 -- 列顺序错误 -- 标签拼写错误 -- 导出时丢掉 `applicant_id` 或 `customer_key` - ---- - -## 11. 建议的执行顺序 - -建议按以下顺序推进,效率最高: - -1. 先读取并检查 train / val / test 的列结构 -2. 找出并删除泄露特征 -3. 建立统一预处理 pipeline -4. 跑 baseline -5. 跑 `Random Forest vs XGBoost` 初始比较 -6. 选一个主模型做 `Optuna` -7. 根据学号末位完成必做改进 -8. 增加一个 optional 改进 -9. 做 `K-Means + GMM` 紧凑探索 -10. 选最终模型并重训 -11. 导出 `test_result_[student_id].csv` -12. 最后再写 PDF - -原因: - -- PDF 的每个回答都依赖 notebook 证据 -- 先写 PDF 容易出现空泛和证据脱节 - ---- - -## 12. 建议的时间分配 - -如果按一份完整高质量作业来做,建议分配如下: - -- 数据清洗与泄露识别:`15%` -- baseline 与模型比较:`20%` -- 高级调参:`20%` -- 个性化改进:`25%` -- K-Means / GMM:`10%` -- 最终导出与提交检查:`5%` -- PDF 写作:`5%` - -如果时间紧,最不能压缩的部分是: - -- 泄露识别 -- 受控比较 -- 个性化改进 -- PDF 与 notebook 的证据对应 - ---- - -## 13. 最推荐的落地方案 - -在当前要求下,一套相对稳妥、兼顾分数与实现成本的方案如下: - -### 13.1 Notebook 主线 - -- 删除泄露特征 -- 建立 `ColumnTransformer + Pipeline` -- baseline 使用 `LogisticRegression` -- 受控比较使用: - - `RandomForestClassifier` - - `XGBoost` -- 用 `Optuna` 调 `XGBoost` -- 个性化改进做: - - 学号对应的必做类别 - - 再补一个 `Category E` 或 `Category D` -- 无监督探索做: - - `KMeans` - - `GaussianMixture` -- 最终模型大概率采用: - - 调参后的 `XGBoost` 或其增强版本 - -### 13.2 PDF 主线 - -围绕五个 compulsory prompt 分别写,每一题都强制绑定 notebook 中的至少一个: - -- 表 -- 图 -- 指标结果 - -写法上坚持: - -- 先简短理论 -- 再接本次实验数据 -- 再给出数据集相关解释 - -### 13.3 提交前检查清单 - -- notebook 能从头运行到尾 -- 图表和表格在 notebook 中可见 -- PDF 中提到的数值和 notebook 完全一致 -- hidden-test CSV 命名正确 -- hidden-test CSV 列顺序正确 -- 标签名称拼写正确 -- 所有附加脚本一并提交 - ---- - -## 14. 当前仍待确认的信息 - -以下信息目前仍需你确认,才可以把“最终实现方案”从通用版收束为定制版: - -- 你的 `学号末位` -- 你是否打算使用 `XGBoost` -- 你是否希望 optional 改进优先走: - - 可解释性方向 - - 集成/校准方向 - - 类别不平衡方向 - -其中最关键的是: - -- `学号末位` - -因为它直接决定 `Category A/B/C/D/E` 中哪一类是你的必做项。 - ---- - -## 15. 结论 - -这份机器学习作业的最优策略不是“盲目追求最高分模型”,而是: - -- 先保证实验规范 -- 再确保比较公平 -- 再通过高级调参和个性化改进拉开差距 -- 最后让 PDF 和 notebook 形成严格的证据闭环 - -如果后续要真正动手实现,建议直接按照本文档第 `11` 节的执行顺序推进,并优先先确认学号末位,再定制个性化改进方案。 diff --git a/强化学习个人项目报告/docs/25_26_DTS307TC_Courseworks_2(1).pdf b/强化学习个人项目报告/docs/25_26_DTS307TC_Courseworks_2(1).pdf new file mode 100644 index 0000000..8d338f6 Binary files /dev/null and b/强化学习个人项目报告/docs/25_26_DTS307TC_Courseworks_2(1).pdf differ