李宏毅机器学习HW1实战:从1.52到1.06的RMSE优化全解析
当第一次看到李宏毅教授机器学习课程中HW1的作业要求时,许多同学都会被"预测新冠阳性率"这个现实问题所吸引。但真正开始动手实践后,从数据清洗到模型调优的每个环节都可能成为拦路虎。本文将带你完整复现一个RMSE从初始1.52优化至1.06的实战案例,揭示那些课程视频中未提及的实战细节。
1. 作业背景与数据初探
HW1的任务是基于美国各州的新冠相关数据,构建回归模型预测未来第三天的阳性率。数据集包含93个特征,其中前40个是州名的one-hot编码,后53个是各类统计指标。评估指标RMSE(均方根误差)直接反映了预测值与真实值的偏离程度。
初次接触数据时,有几个关键点需要特别注意:
- 数据分布:阳性率在不同州、不同时间段差异显著
- 特征尺度:数值型特征的量纲差异巨大(如检测人数与温度值)
- 时序特性:虽然作业未明确要求时序模型,但数据本身具有时间连续性
import pandas as pd train_df = pd.read_csv('covid.train.csv') print(train_df.describe())输出显示部分特征的数值范围:
| 特征 | 均值 | 标准差 | 最小值 | 最大值 |
|---|---|---|---|---|
| tested_positive.1 | 5.21 | 4.38 | 0.1 | 25.8 |
| population | 6.5e6 | 7.1e6 | 5.8e5 | 3.9e7 |
| temperature | 62.4 | 10.2 | 28.3 | 87.5 |
2. 基础模型搭建
课程提供的baseline采用了一个简单的三层神经网络:
- 输入层(93维)
- 隐藏层(256个神经元,ReLU激活)
- 输出层(1维)
这个初始模型在测试集上的RMSE约为1.52。虽然结构简单,但已经包含了深度学习的核心组件:
import torch.nn as nn class COVID19Predictor(nn.Module): def __init__(self, input_dim): super().__init__() self.layers = nn.Sequential( nn.Linear(input_dim, 256), nn.ReLU(), nn.Linear(256, 1) ) def forward(self, x): return self.layers(x).squeeze(1)训练过程中有几个易错点需要特别注意:
- 数据标准化:必须在训练集上计算均值和方差,然后应用到验证/测试集
- 早停机制:当验证集loss连续5个epoch不下降时终止训练
- 随机种子:固定随机种子确保实验可复现
3. 特征工程优化
从1.52到1.28的第一阶段提升主要来自特征选择。通过分析发现:
- 州特征:某些州的阳性率明显高于其他地区
- 时序特征:前两天的阳性率与第三天高度相关
- 冗余特征:部分统计指标间存在强相关性
采用两种特征选择方法:
皮尔逊相关系数筛选(适用于连续变量):
corr = train_df.corr()['tested_positive.2'] selected_features = corr[abs(corr) > 0.3].index.tolist()方差膨胀因子(VIF)(检测多重共线性):
from statsmodels.stats.outliers_influence import variance_inflation_factor vif = pd.DataFrame() vif["features"] = train_df.columns[40:-1] vif["VIF"] = [variance_inflation_factor(train_df.values[:,40:-1], i) for i in range(train_df.shape[1]-41)] low_vif_features = vif[vif["VIF"] < 5]["features"]最终保留的特征包括:
- 15个州标识
- 前两天的阳性率
- 人口密度
- 检测数量
- 7个关键医疗指标
4. 模型架构调优
从1.28到1.06的提升主要来自模型结构调整和训练技巧:
网络结构改进:
class ImprovedModel(nn.Module): def __init__(self, input_dim): super().__init__() self.net = nn.Sequential( nn.Linear(input_dim, 128), nn.BatchNorm1d(128), nn.ReLU(), nn.Dropout(0.3), nn.Linear(128, 64), nn.BatchNorm1d(64), nn.ReLU(), nn.Linear(64, 1) ) def forward(self, x): return self.net(x).squeeze(1)关键改进点:
- 加入BatchNorm加速收敛
- 添加Dropout防止过拟合(设置为0.3)
- 缩减网络规模以适应精简后的特征
训练策略优化:
- 使用AdamW优化器(学习率3e-4)
- 采用余弦退火学习率调度
- 增加梯度裁剪(max_norm=1.0)
optimizer = torch.optim.AdamW(model.parameters(), lr=3e-4, weight_decay=1e-5) scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)5. 结果分析与经验总结
经过多轮优化,各阶段效果对比如下:
| 优化阶段 | 验证集RMSE | 测试集RMSE | 相对提升 |
|---|---|---|---|
| 原始baseline | 1.61 | 1.52 | - |
| 特征选择后 | 1.35 | 1.28 | 15.8% |
| 模型结构调整后 | 1.12 | 1.06 | 17.2% |
几个值得注意的发现:
- 更复杂的模型不一定表现更好,三层的网络反而优于五层的设计
- 特征工程带来的提升比模型结构调整更显著
- 早停机制有效防止了过拟合,最优epoch通常在80-120之间
最终提交的预测结果在测试集上达到了1.06的RMSE,这意味着平均预测误差约为真实值的±1.06%。这个案例生动展示了从课程理论到实践落地的完整过程——理解数据特性比盲目应用复杂模型更重要,适当的特征选择和模型正则化往往能带来意想不到的效果提升。