肥胖风险多分类预测项目
一个完整的机器学习教学项目,用于预测个人的肥胖风险等级
目录
快速入门
1. 环境准备
# 创建虚拟环境(推荐)
conda create -n obesity python=3.9
conda activate obesity
# 安装依赖
pip install -r requirements.txt
2. 准备数据
从 Kaggle竞赛页面 下载数据,放到 data/ 目录:
data/
├── train.csv
├── test.csv
└── sample_submission.csv
3. 运行项目
# 完整流程
python run.py
# 只进行数据探索
python run.py --explore-only
# 快速训练(跳过交叉验证)
python run.py --skip-cv
# 保存模型
python run.py --save-model
4. 查看结果
- 提交文件:
submissions/submission_*.csv - 日志文件:
logs/training.log - 可视化图表:
logs/*.png
项目概述
竞赛目标
使用各种因素预测个人的肥胖风险等级(与心血管疾病相关)
评估指标
准确率 (Accuracy) = 正确预测的样本数 / 总样本数
目标类别 (7个)
| 类别 | 中文含义 |
|---|---|
| Insufficient_Weight | 体重不足 |
| Normal_Weight | 正常体重 |
| Overweight_Level_I | 超重I级 |
| Overweight_Level_II | 超重II级 |
| Obesity_Type_I | 肥胖I型 |
| Obesity_Type_II | 肥胖II型 |
| Obesity_Type_III | 肥胖III型 |
数据特点
- 训练集: 约20,000条记录
- 测试集: 约10,000条记录
- 特征数: 16个原始特征
- 目标类别: 7个肥胖等级
项目结构
obesity_risk_project/
├── data/ # 数据目录
│ ├── train.csv # 训练数据
│ ├── test.csv # 测试数据
│ └── sample_submission.csv # 提交样例
│
├── src/ # 源代码目录
│ ├── config.py # 配置文件
│ ├── data_loader.py # 数据加载模块
│ ├── feature_engineering.py # 特征工程模块
│ ├── model_trainer.py # 模型训练模块
│ ├── validation.py # 验证评估模块
│ ├── predictor.py # 预测提交模块
│ └── main.py # 主程序入口
│
├── models/ # 保存的模型
├── submissions/ # 提交文件
├── logs/ # 日志和图表
├── notebooks/ # Jupyter notebooks
├── tests/ # 测试代码
│
├── run.py # 项目启动脚本
├── requirements.txt # 依赖包
└── README.md # 项目说明
核心模块详解
1. config.py - 配置文件
作用: 集中管理所有项目参数
# 路径配置
BASE_DIR # 项目根目录
DATA_DIR # 数据目录
TRAIN_PATH # 训练数据路径
TEST_PATH # 测试数据路径
# 输出目录
MODELS_DIR # 模型保存目录
SUBMISSIONS_DIR # 提交文件目录
LOGS_DIR # 日志目录
# 数据配置
TARGET_COL # 目标列名 'NObeyesdad'
ID_COL # ID列名 'id'
RANDOM_STATE # 随机种子 42
2. data_loader.py - 数据加载模块
核心类: DataLoader
from data_loader import DataLoader
loader = DataLoader('data/train.csv', 'data/test.csv', 'NObeyesdad')
train_df, test_df = loader.load_data()
# 数据探索
loader.get_basic_info() # 获取基本信息
loader.check_data_quality() # 检查数据质量
loader.plot_target_distribution() # 绘制目标分布
3. feature_engineering.py - 特征工程模块
核心类: FeatureEngineer
from feature_engineering import FeatureEngineer
engineer = FeatureEngineer()
X_train, X_test, y_train = engineer.prepare_features(
train_df, test_df, 'NObeyesdad', 'id'
)
创建的新特征:
| 特征名 | 计算方式 | 说明 |
|---|---|---|
| BMI | Weight / Height² | 身体质量指数 |
| BMI_Category | 根据BMI分组 | 体重分类 |
| Age_Group | 根据年龄分组 | 年龄段 |
| Lifestyle_Score | (FAF×2 + (3-TUE) + CH2O) / 3 | 生活方式评分 |
| Diet_Score | 综合饮食相关特征 | 饮食习惯评分 |
| Age_BMI | Age × BMI | 年龄与BMI交互 |
| Weight_Activity | Weight × FAF | 体重与运动交互 |
4. model_trainer.py - 模型训练模块
核心类: ModelTrainer
from model_trainer import ModelTrainer
# 创建训练器
trainer = ModelTrainer(
model_params={'n_estimators': 100, 'max_depth': 6},
use_early_stopping=True,
early_stopping_rounds=50
)
# 训练模型
trainer.train(X_train, y_train, X_val, y_val)
# 预测
predictions = trainer.predict(X_test)
# 获取特征重要性
importance = trainer.get_feature_importance()
5. validation.py - 验证评估模块
核心函数: train_val_split, cross_validate
from validation import train_val_split, cross_validate
# 划分数据集
X_train, X_val, y_train, y_val = train_val_split(X, y, test_size=0.2)
# 交叉验证
scores = cross_validate(X, y, model_params, n_splits=5)
6. predictor.py - 预测提交模块
核心类: Predictor
from predictor import Predictor
predictor = Predictor(trainer, label_encoder)
submission = predictor.predict_and_submit(X_test, test_ids, 'submissions')
机器学习流程详解
阶段1: 数据探索 (EDA)
# 1. 加载数据
train_df = pd.read_csv('train.csv')
# 2. 查看基本信息
print(train_df.shape) # (行数, 列数)
print(train_df.info()) # 数据类型
print(train_df.describe()) # 统计描述
# 3. 检查数据质量
print(train_df.isnull().sum()) # 缺失值
print(train_df.duplicated().sum()) # 重复值
# 4. 探索目标变量
print(train_df['NObeyesdad'].value_counts())
阶段2: 数据划分
原始数据
│
├──► 训练集 (70-80%) ──► 用于训练模型参数
│
├──► 验证集 (10-15%) ──► 用于调参和早停
│
└──► 测试集 (10-15%) ──► 用于最终评估
from sklearn.model_selection import train_test_split
X_train, X_val, y_train, y_val = train_test_split(
X, y,
test_size=0.2, # 20%作为验证集
random_state=42, # 保证可重复
stratify=y # 保持类别比例
)
阶段3: 特征工程
# 1. BMI (身体质量指数)
df['BMI'] = df['Weight'] / (df['Height'] ** 2)
# 2. 生活方式评分
df['Lifestyle_Score'] = (
df['FAF'] * 2 + # 体育活动频率
(3 - df['TUE']) + # 技术使用时间
df['CH2O'] # 饮水量
) / 3
# 3. 交互特征
df['Age_BMI'] = df['Age'] * df['BMI']
df['Weight_Activity'] = df['Weight'] * df['FAF']
# 4. 特征编码
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
df['Gender_encoded'] = le.fit_transform(df['Gender'])
# 5. 特征缩放
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df[numeric_cols] = scaler.fit_transform(df[numeric_cols])
阶段4: 模型训练
import xgboost as xgb
# 创建模型
model = xgb.XGBClassifier(
n_estimators=1000,
learning_rate=0.05,
max_depth=6,
objective='multi:softprob',
random_state=42
)
# 带早停的训练
model.fit(
X_train, y_train,
eval_set=[(X_val, y_val)],
early_stopping_rounds=50,
verbose=False
)
早停原理:
验证集准确率
│
1.0 ┤ ╭────── 过拟合开始
│ ╭──╯
0.9 ┤ ╭──╯
│ ╭──╯
0.8 ┤ ╭──╯
│ ╭──╯
0.7 ┤ ╭──╯
│╭─╯
0.6 ┤╯
└────┬────┬────┬────┬────┬────┬────┬────► 迭代次数
100 200 300 400 500 600 700
↑
最佳迭代点(早停)
阶段5: 模型评估
from sklearn.metrics import accuracy_score, classification_report
# 预测
y_pred = model.predict(X_val)
# 准确率
accuracy = accuracy_score(y_val, y_pred)
print(f"准确率: {accuracy:.4f}")
# 详细报告
print(classification_report(y_val, y_pred))
阶段6: 生成提交
# 1. 预测测试集
predictions = model.predict(X_test)
# 2. 创建提交文件
submission = pd.DataFrame({
'id': test_df['id'],
'NObeyesdad': predictions
})
# 3. 保存
submission.to_csv('submission.csv', index=False)
数据字典
| 特征名 | 说明 | 类型 |
|---|---|---|
| Gender | 性别 | 分类 |
| Age | 年龄 | 数值 |
| Height | 身高(米) | 数值 |
| Weight | 体重(公斤) | 数值 |
| family_history_with_overweight | 家族肥胖史 | 分类 |
| FAVC | 频繁摄入高热量食物 | 分类 |
| FCVC | 蔬菜摄入频率 | 数值 |
| NCP | 主餐数量 | 数值 |
| CAEC | 餐间进食 | 分类 |
| SMOKE | 是否吸烟 | 分类 |
| CH2O | 每日饮水量 | 数值 |
| SCC | 卡路里监测 | 分类 |
| FAF | 体育活动频率 | 数值 |
| TUE | 使用技术设备时间 | 数值 |
| CALC | 饮酒 | 分类 |
| MTRANS | 交通方式 | 分类 |
常见问题
Q1: 运行时报错 "ModuleNotFoundError"
pip install -r requirements.txt
Q2: 如何提高分数?
- 特征工程: 创建更多有意义的特征
- 超参数调优: 使用Optuna进行自动调参
- 模型集成: 组合多个模型的预测
- 交叉验证: 使用更多折数
Q3: 为什么验证集分数比训练集低很多?
原因: 过拟合 - 模型记住了训练数据
解决:
- 使用正则化
- 减少模型复杂度
- 使用早停机制
Q4: 内存不足怎么办?
- 减少交叉验证折数:
--n-folds 3 - 减少特征数量
- 使用更小的batch_size
进阶技巧
技巧1: 超参数调优
import optuna
def objective(trial):
params = {
'n_estimators': trial.suggest_int('n_estimators', 100, 2000),
'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.3),
'max_depth': trial.suggest_int('max_depth', 3, 10),
}
model = xgb.XGBClassifier(**params)
scores = cross_val_score(model, X_train, y_train, cv=5)
return scores.mean()
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)
print(f"最佳参数: {study.best_params}")
技巧2: 模型集成
# 训练多个模型
models = {
'xgb': xgb.XGBClassifier(**xgb_params),
'lgb': lgb.LGBMClassifier(**lgb_params),
}
# 加权平均预测
predictions = {}
for name, model in models.items():
model.fit(X_train, y_train)
predictions[name] = model.predict_proba(X_test)
ensemble_pred = 0.5 * predictions['xgb'] + 0.5 * predictions['lgb']
final_pred = np.argmax(ensemble_pred, axis=1)
技巧3: 特征重要性分析
import matplotlib.pyplot as plt
import seaborn as sns
importance = model.feature_importances_
feature_importance = pd.DataFrame({
'feature': X_train.columns,
'importance': importance
}).sort_values('importance', ascending=False)
plt.figure(figsize=(10, 8))
sns.barplot(x='importance', y='feature', data=feature_importance.head(20))
plt.title('Top 20 Feature Importance')
plt.show()
参考资料
许可证
MIT License