1. 项目概述:COVID-19病例预测的机器学习实践
这个项目源自2021年春季机器学习课程的第一个作业,核心目标是利用历史数据构建预测模型,对COVID-19确诊病例数进行预测。作为公共卫生领域的经典时序预测问题,它不仅考验学生对基础机器学习流程的掌握程度,更涉及真实世界数据处理的完整链条。
我在完成这个作业时发现,单纯套用教材案例的方法很难取得好成绩——疫情数据具有明显的时空异质性、政策干预突变等特点,需要特别关注特征工程的设计和模型选择。下面将详细拆解这个项目的技术实现路径,包含我调整了三次才稳定的特征构造方案,以及最终在测试集上排名前10%的模型配置。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据理解与预处理关键步骤
2.1 原始数据特征解析
提供的训练数据集包含以下关键字段:
- 日期字段(date):从2020-01-22开始的每日记录
- 地理标识(country/province):国家/地区级数据
- 确诊病例数(confirmed):需要预测的目标变量
- 基础特征(tests, positive_rate等):部分地区的检测量数据
数据立即显现出三个典型问题:
- 地域覆盖不完整(约30%地区缺少检测数据)
- 时间序列存在断裂(节假日无数据上报)
- 数值尺度差异巨大(如湖北省与其他省份的确诊数差量级)
2.2 缺失值处理方案对比
我测试了三种缺失值填充策略的效果:
- 前向填充(ffill):在时间序列上表现最稳定
- 线性插值:对检测数据这类平缓变化特征有效
- 零值填充:会引入噪声,最终仅用于极少数特征
具体实现时,对检测阳性率这类敏感指标,采用分省份的滚动窗口均值填充更为可靠。以下是关键代码片段:
python复制def fill_missing_values(df):
# 对检测相关特征按省份分组处理
test_cols = ['tests', 'positive_rate']
for col in test_cols:
df[col] = df.groupby('province')[col].transform(
lambda x: x.rolling(7, min_periods=1).mean()
)
# 确诊数使用时间前向填充
df['confirmed'] = df.groupby('province')['confirmed'].ffill()
return df
2.3 关键特征工程设计
经过多次迭代,最终保留的特征构造方法包括:
- 时间特征:星期几、当月第几天、季度等周期性编码
- 统计特征:过去7/14/21天的移动平均值、标准差
- 差异特征:与前一日的变化量、变化率
- 地域特征:省份级别的one-hot编码
特别注意:不要直接使用原始确诊数的绝对值作为特征,这会导致严重的数据泄漏(data leakage)。所有统计特征必须严格使用滞后值(lagged values)。
3. 模型选型与调优过程
3.1 基线模型性能对比
首先在50%训练集上评估了四种基础模型:
| 模型类型 | RMSE | 训练时间 | 内存占用 |
|---|---|---|---|
| 线性回归 | 482 | 12s | 1.2GB |
| 随机森林 | 387 | 3min | 4.5GB |
| XGBoost | 356 | 2min | 3.8GB |
| LSTM | 413 | 25min | 8.2GB |
虽然LSTM在理论上是时序问题的首选,但在数据量有限的情况下,树模型反而展现出更好的性价比。最终选择XGBoost作为基础架构。
3.2 XGBoost参数调优
通过贝叶斯优化确定了关键参数组合:
python复制params = {
'n_estimators': 320,
'max_depth': 6,
'learning_rate': 0.05,
'subsample': 0.8,
'colsample_bytree': 0.7,
'gamma': 0.1,
'objective': 'reg:squarederror',
'eval_metric': 'rmse'
}
特别需要注意的是:
- 设置
early_stopping_rounds=50防止过拟合 - 使用
gamma参数控制树结构的复杂度 - 采用时间序列交叉验证(TimeSeriesSplit)
3.3 集成策略改进
单个模型的预测存在波动,最终采用三层堆叠(stacking)方案:
- 第一层:XGBoost、LightGBM、CatBoost
- 第二层:ElasticNet回归
- 元模型:简单加权平均
这种结构在测试集上将RMSE从356降低到328,关键是在节假日等特殊日期表现得更加稳定。
4. 实战中的经验与教训
4.1 数据泄漏的典型陷阱
初期尝试使用滑动窗口特征时,曾犯过一个致命错误——在计算滚动统计量时包含了当前日期的值。这会导致模型在预测时"偷看"未来数据。正确的做法应该是:
python复制# 错误示范(包含当前行)
df['rolling_mean'] = df['confirmed'].rolling(7).mean()
# 正确做法(使用shift避免泄漏)
df['rolling_mean'] = df['confirmed'].shift(1).rolling(7).mean()
4.2 地域差异的处理技巧
不同地区的疫情发展曲线差异极大。通过分析发现:
- 对疫情初期地区(如武汉),需要加强突变检测
- 对后期爆发地区,更关注与周边区域的关联性
解决方案是采用分省份的标准化:
python复制from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
df['confirmed_norm'] = df.groupby('province')['confirmed'].transform(
lambda x: scaler.fit_transform(x.values.reshape(-1,1)).ravel()
)
4.3 评估指标的优化方向
作业要求使用RMSE作为评估指标,但实际应用中还需要关注:
- 对爆发期预测的准确率(peak error)
- 趋势方向的判断正确率(trend accuracy)
- 政策敏感度(如封城后的预测调整速度)
我在代码中额外添加了这些指标的监控,发现它们与最终成绩有强相关性。特别是在测试集包含新一波疫情时,能快速调整的模型得分明显更高。
5. 完整实现流程
5.1 环境配置建议
推荐使用Python 3.8+环境,主要依赖库版本:
code复制numpy==1.20.3
pandas==1.2.4
scikit-learn==0.24.2
xgboost==1.4.2
为避免环境冲突,建议使用conda创建虚拟环境:
bash复制conda create -n ml2021 python=3.8
conda activate ml2021
pip install -r requirements.txt
5.2 核心训练流程
完整训练脚本的主要逻辑:
python复制def main():
# 数据加载与预处理
raw_df = load_data('train.csv')
clean_df = preprocess(raw_df)
# 特征工程
feature_df = build_features(clean_df)
# 数据集划分
X_train, X_val, y_train, y_val = train_test_split(
feature_df.drop('confirmed', axis=1),
feature_df['confirmed'],
test_size=0.2,
shuffle=False # 保持时间顺序
)
# 模型训练
model = XGBRegressor(**params)
model.fit(
X_train, y_train,
eval_set=[(X_val, y_val)],
early_stopping_rounds=50,
verbose=10
)
# 预测输出
test_df = process_test_data('test.csv')
predictions = model.predict(test_df)
save_results(predictions)
5.3 结果提交技巧
作业评分系统有几个隐藏规则需要注意:
- 提交文件的列名必须完全匹配要求
- 预测值需为整数(round处理)
- 缺失值会被自动填充为0
最优提交策略是:
python复制submission = pd.DataFrame({
'id': test_ids,
'confirmed': predictions.round().astype(int)
})
submission.fillna(0).to_csv('submission.csv', index=False)
6. 延伸改进方向
虽然这个作业已经结束,但基于后续的实践经验,还有几个值得尝试的优化点:
-
引入外部特征:
- 天气数据(温度、湿度与病毒传播的关系)
- 人口流动数据(百度迁徙指数等)
- 政策强度指数(封城等级量化)
-
混合模型架构:
- 对趋势项使用Prophet等专业时序模型
- 对残差项使用XGBoost捕捉非线性关系
- 用神经网络融合各子模型输出
-
不确定性量化:
- 采用分位数回归预测区间
- 通过集成模型计算预测方差
- 蒙特卡洛模拟极端情况
这个项目让我深刻体会到,好的预测模型不仅需要算法技巧,更需要深入理解问题领域的特性。疫情预测中那些突然的转折点,往往来自非数据因素(如政策调整),这提醒我们在做机器学习项目时,保持对业务背景的敏感度同样重要。
