别再只调.fit()了!用sklearn的LinearRegression做波士顿房价预测,这5个实战细节新手必看

当你第一次用LinearRegression跑通波士顿房价预测时,那种成就感就像拿到了机器学习的"Hello World"证书。但很快你会发现,同样的代码第二次运行结果可能完全不同,测试集上的分数忽高忽低,甚至某次预测结果完全偏离实际——这不是你的错,而是sklearn留给初学者的"甜蜜陷阱"。

1. 数据分割的随机性陷阱

train_test_splitrandom_state参数看似微不足道,实则掌控着模型命运的钥匙。我们做个实验:

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split

boston = load_boston()
X, y = boston.data, boston.target

# 对比不同random_state下的测试集分数
for seed in [42, 123, 999]:
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=seed)
    model = LinearRegression().fit(X_train, y_train)
    print(f"random_state={seed}时测试集R²分数: {model.score(X_test, y_test):.4f}")

运行三次可能得到这样的结果:

  • random_state=42 → 0.6624
  • random_state=123 → 0.5893
  • random_state=999 → 0.7216

这揭示了一个关键事实:单次分割的评估结果具有偶然性。解决方案有两种:

  1. 固定random_state:确保实验可复现,适合教学演示
  2. 交叉验证:更可靠的评估方式(后文详解)

注意:波士顿数据集在较新版本sklearn中已被移除,可用fetch_california_housing()替代

2. 评估指标的选择艺术

.score()默认返回R²值,但这个"万能指标"可能掩盖真实问题。看这个对比表格:

指标 计算公式 特点 适用场景
1 - (残差平方和/总平方和) 无量纲,易解释 初步模型快速评估
MAE mean(|y_true - y_pred|) 与原始单位一致 需要直观误差值时
MSE mean((y_true - y_pred)²) 惩罚大误差 关注极端错误的情况
RMSE sqrt(MSE) 与原始单位一致 需要放大的误差信号

实战中建议至少计算两种指标:

from sklearn.metrics import mean_absolute_error, mean_squared_error

y_pred = model.predict(X_test)
print(f"MAE: {mean_absolute_error(y_test, y_pred):.2f}万美元")
print(f"RMSE: {np.sqrt(mean_squared_error(y_test, y_pred)):.2f}万美元")

当R²很高但MAE也很大时,说明模型可能系统性高估/低估某些价格区间。

3. 可视化诊断:超越数字的洞察

数字指标是冰冷的,可视化才能发现模型真正的行为模式。试试这个进阶版预测对比图:

plt.figure(figsize=(10,6))
sorted_idx = np.argsort(y_test)  # 按真实值排序
plt.plot(y_test[sorted_idx], 'o-', label='真实价格', alpha=0.7)
plt.plot(y_pred[sorted_idx], 'x--', label='预测价格', alpha=0.7)
plt.fill_between(range(len(y_test)), 
                 y_pred[sorted_idx]-5, 
                 y_pred[sorted_idx]+5,
                 alpha=0.1, color='green')  # 显示±5万美元误差带
plt.legend()
plt.title("预测值与真实值对比(按真实值排序)")
plt.xlabel("样本编号(按真实价格排序)")
plt.ylabel("房价(万美元)")

这种可视化能清晰显示:

  • 模型在哪些价格区间表现更好
  • 是否存在系统性偏差(如总是高估低价房)
  • 误差范围是否均匀分布

4. 特征工程的隐形战场

原始特征直接喂给模型就像用生米煮饭——能吃,但不好吃。关键处理步骤:

  1. 特征缩放(虽然线性回归不需要,但为后续正则化准备):

    from sklearn.preprocessing import StandardScaler
    scaler = StandardScaler().fit(X_train)
    X_train_scaled = scaler.transform(X_train)
    X_test_scaled = scaler.transform(X_test)
    
  2. 特征相关性检测

    import seaborn as sns
    df = pd.DataFrame(X_train, columns=boston.feature_names)
    df['PRICE'] = y_train
    sns.heatmap(df.corr().round(2), annot=True, cmap='coolwarm')
    
  3. 处理多重共线性

    • 计算方差膨胀因子(VIF):
      from statsmodels.stats.outliers_influence import variance_inflation_factor
      vif = [variance_inflation_factor(X_train, i) 
             for i in range(X_train.shape[1])]
      pd.DataFrame({'feature': boston.feature_names, 'VIF': vif})
      
    • VIF>10的特征需要考虑删除或合并

5. 从线性回归到正则化之路

当发现以下现象时,就该考虑进阶方案了:

  • 不同数据分割结果差异很大
  • 系数值异常大(如超过1e5)
  • 添加/删除一个特征导致其他系数剧烈变化

正则化方法对比

类型 惩罚项 特点 sklearn实现类
标准线性回归 基础方法 LinearRegression
岭回归 L2范数 缩小所有系数 Ridge
Lasso L1范数 产生稀疏系数 Lasso
ElasticNet L1+L2 权衡两种正则化 ElasticNet

过渡到岭回归的示例:

from sklearn.linear_model import RidgeCV

# 自动寻找最佳alpha值
ridge = RidgeCV(alphas=[0.1, 1.0, 10.0], 
                scoring='neg_mean_squared_error')
ridge.fit(X_train_scaled, y_train)

print(f"最佳alpha值: {ridge.alpha_}")
print(f"测试集分数: {ridge.score(X_test_scaled, y_test):.3f}")

记住:没有"最好"的模型,只有最适合当前业务场景的解决方案。当预测波士顿房价时,最终可能发现简单调整数据分割策略、精心选择评估指标,比直接换复杂模型更能提升实战效果。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐