别再只调.fit()了!用sklearn的LinearRegression做波士顿房价预测,这5个实战细节新手必看
·
别再只调.fit()了!用sklearn的LinearRegression做波士顿房价预测,这5个实战细节新手必看
当你第一次用LinearRegression跑通波士顿房价预测时,那种成就感就像拿到了机器学习的"Hello World"证书。但很快你会发现,同样的代码第二次运行结果可能完全不同,测试集上的分数忽高忽低,甚至某次预测结果完全偏离实际——这不是你的错,而是sklearn留给初学者的"甜蜜陷阱"。
1. 数据分割的随机性陷阱
train_test_split的random_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
这揭示了一个关键事实:单次分割的评估结果具有偶然性。解决方案有两种:
- 固定random_state:确保实验可复现,适合教学演示
- 交叉验证:更可靠的评估方式(后文详解)
注意:波士顿数据集在较新版本sklearn中已被移除,可用
fetch_california_housing()替代
2. 评估指标的选择艺术
.score()默认返回R²值,但这个"万能指标"可能掩盖真实问题。看这个对比表格:
| 指标 | 计算公式 | 特点 | 适用场景 |
|---|---|---|---|
| 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. 特征工程的隐形战场
原始特征直接喂给模型就像用生米煮饭——能吃,但不好吃。关键处理步骤:
-
特征缩放(虽然线性回归不需要,但为后续正则化准备):
from sklearn.preprocessing import StandardScaler scaler = StandardScaler().fit(X_train) X_train_scaled = scaler.transform(X_train) X_test_scaled = scaler.transform(X_test) -
特征相关性检测:
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') -
处理多重共线性:
- 计算方差膨胀因子(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的特征需要考虑删除或合并
- 计算方差膨胀因子(VIF):
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}")
记住:没有"最好"的模型,只有最适合当前业务场景的解决方案。当预测波士顿房价时,最终可能发现简单调整数据分割策略、精心选择评估指标,比直接换复杂模型更能提升实战效果。
更多推荐

所有评论(0)