feat: 添加GPU检测与自动回退逻辑并移除冗余文档
- 在notebook中引入GPU检测逻辑,根据CUDA可用性自动选择XGBoost计算后端 - 更新XGBoost配置,使用动态变量替代硬编码的GPU参数 - 删除过时的需求分析文档,保持项目结构整洁 - 确保代码在不同硬件环境下均可正常运行
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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` 节的执行顺序推进,并优先先确认学号末位,再定制个性化改进方案。
|
||||
Binary file not shown.
Reference in New Issue
Block a user