不同训练测试拆分比例下LinearRegression预测差异大的原因及解决
LinearRegression预测结果随训练测试拆分比例异常的原因及解决方法
问题场景
你在使用LinearRegression训练线性模型时,发现了一个奇怪的现象:当设置test_size=0.2时,测试集的预测结果里出现了极端大的异常值(比如那个4.17353113e+07),但训练集的预测值都很正常;而把测试集比例调到0.4之后,训练和测试的预测结果就都恢复正常了。
初始拆分(test_size=0.2)的代码与输出
x_train,x_test,y_train,true_p=train_test_split(train,y,random_state=121,test_size=0.2,shuffle=True) lreg=LinearRegression() lreg.fit(x_train,y_train) train_pred=lreg.predict(x_train) test_pred=lreg.predict(x_test)
- 训练集预测结果:
array([12.37512481, 11.67234874, 11.82821202, ..., 12.61139596, 12.13886881, 12.42435563]) - 测试集预测结果(存在异常值):
array([ 1.21885520e+01, 1.13462088e+01, 1.14144208e+01, 1.22832932e+01, 1.29980626e+01, 1.17641183e+01, 1.20982465e+01, 1.15846156e+01, 1.17403904e+01, 4.17353113e+07, 1.27941840e+01, 1.21739628e+01, ..., 1.22022858e+01, 1.15779229e+01, 1.24931376e+01, 1.26387188e+01, 1.18341585e+01, 1.18411881e+01, 1.21475986e+01, 1.25104774e+01])
调整拆分比例(test_size=0.4)的代码与输出
x_train,x_test,y_train,true_p=train_test_split(train,y,random_state=121,test_size=0.4,shuffle=True) lreg=LinearRegression() lreg.fit(x_train,y_train) train_pred=lreg.predict(x_train) test_pred=lreg.predict(x_test)
- 训练集预测结果:
array([11.95505983, 12.66847164, 11.81978843, 12.82992812, 12.44707462, 11.78809995, 11.92753084, 12.6082893 , 12.22644843, 11.93325658, 12.2449481 ,..., 11.69256008, 11.67984786, 12.54313682, 12.30652695]) - 测试集预测结果(无异常值):
array([12.22133867, 11.18863973, 11.46923967, 12.26340761, 12.99240451, 11.77865948, 12.04321231, 11.44137667, 11.71213919, 11.44206212, ..., 12.15412777, 12.39184805, 10.96310233, 12.06243916, 12.11383494, 12.28327695, 11.19989021, 12.61439939, 12.22474378])
原因分析
咱们直接点说,这个问题的核心是你的数据里存在离群点(outlier),而拆分比例的变化刚好改变了这个离群点的分配位置:
- 当
test_size=0.2时,这个极端样本被分到了测试集里。线性回归是用最小二乘法拟合的,对离群点特别敏感——训练模型时根本没见过这种极端数据,所以用它预测测试集里的这个离群点时,就会输出极端大的值。 - 当
test_size=0.4时,这个离群点被分到了训练集里。此时模型会被这个点“拉偏”,但测试集里没有极端样本,所以预测结果看起来都正常。
你设置了random_state=121,这让拆分结果固定了,所以两种比例下的差异完全是这个离群点的位置导致的。
解决方法(针对test_size=0.2的场景)
1. 检测并处理离群点
首先得找到这个离群点:
- 你可以用箱线图查看特征和目标变量的分布,找出超出四分位间距(IQR)3倍以上的样本;
- 或者计算每个特征的Z分数,标记Z分数绝对值大于3的样本。
处理方式分两种:
- 如果是数据录入错误导致的离群点,直接删掉就行;
- 如果是真实存在的极端样本,推荐用稳健回归模型,比如
RANSACRegressor或者HuberRegressor,它们对离群点的鲁棒性比普通线性回归强很多。
示例代码(用RANSAC):
from sklearn.linear_model import RANSACRegressor # 拆分数据保持test_size=0.2 x_train,x_test,y_train,true_p=train_test_split(train,y,random_state=121,test_size=0.2,shuffle=True) # RANSAC会自动筛选出符合模型的内点,忽略离群点 ransac_reg = RANSACRegressor(LinearRegression(), random_state=121) ransac_reg.fit(x_train, y_train) # 重新预测 train_pred = ransac_reg.predict(x_train) test_pred = ransac_reg.predict(x_test)
2. 对特征做标准化处理
线性回归对特征的尺度很敏感,如果某个特征的数值范围特别大,也可能导致预测异常。用StandardScaler把所有特征缩放到均值为0、方差为1的范围,能缓解这个问题:
from sklearn.preprocessing import StandardScaler # 先标准化所有特征 scaler = StandardScaler() train_scaled = scaler.fit_transform(train) # 再拆分数据 x_train,x_test,y_train,true_p=train_test_split(train_scaled,y,random_state=121,test_size=0.2,shuffle=True) lreg=LinearRegression() lreg.fit(x_train,y_train) train_pred=lreg.predict(x_train) test_pred=lreg.predict(x_test)
3. 用交叉验证替代单次拆分
单次拆分很容易因为样本分配的偶然性出现问题,用交叉验证能更全面地评估模型性能,也能避免这种“刚好抽到离群点到测试集”的情况:
from sklearn.model_selection import cross_val_predict from sklearn.preprocessing import StandardScaler # 先标准化特征 scaler = StandardScaler() train_scaled = scaler.fit_transform(train) lreg = LinearRegression() # 用5折交叉验证得到所有样本的预测结果 cv_predictions = cross_val_predict(lreg, train_scaled, y, cv=5)
内容的提问来源于stack exchange,提问作者Aneesh Krishna
相关产品推荐
相关产品推荐

