从零实现线性回归Theta参数趋于无穷的解决方法咨询
解决梯度下降中参数趋于无穷/NaN的问题
你遇到的参数发散到无穷、出现NaN的情况,在自定义梯度下降实现中很常见,尤其是处理多项式特征时,主要原因集中在特征未缩放、正则化实现有误以及学习率适配性这几个方面。下面一步步帮你解决:
1. 必须先做特征缩放(最关键)
多项式特征会让不同特征的数值范围变得极其悬殊——比如波士顿数据里rm大概在4-9之间,它的平方就是16-81;而lstat在1-37之间,平方是1-1369,交叉项rm*lstat也会有几十到几百的范围。这种数值范围的巨大差异会导致梯度更新时,不同参数的步长失衡,最终引发参数爆炸。
解决方法:
在生成多项式特征后,对所有特征做标准化(StandardScaler)或者归一化(MinMaxScaler),注意要用训练集的统计量去缩放测试集,避免数据泄露。
修改你的测试脚本:
from sklearn.preprocessing import PolynomialFeatures, StandardScaler # ... 之前的代码 ... poly_features = PolynomialFeatures(degree=2) X_train_poly = poly_features.fit_transform(X_train) X_test_poly = poly_features.transform(X_test) # 这里不要用fit_transform,避免测试集重新拟合 # 特征标准化 scaler = StandardScaler() X_train_poly_scaled = scaler.fit_transform(X_train_poly) X_test_poly_scaled = scaler.transform(X_test_poly) # 用缩放后的特征训练模型 mlr_poly = MultivariateLinearRegression(0.001, 'Gradient Descent', 5000, None) # 可以适当调大学习率,因为缩放后梯度更稳定 mlr_poly.train(X_train_poly_scaled, Y_train)
2. 修复正则化的实现错误
你的正则化代码里,把偏置项(也就是theta的最后一个元素,对应你添加的全1列)也加入了正则化,这是错误的——偏置项不需要正则化,它只是调整预测值的整体偏移,不会导致过拟合。
修改_trainGradientDescent中的正则化部分:
if self._reg_rate != None: # 复制theta,避免修改原数组 theta_without_bias = self._theta.copy() theta_without_bias[-1] = 0 # 偏置项不参与正则化 reg_factor = (1 - (self._learning_rate * self._reg_rate) / self._training_sample) self._theta = np.subtract(reg_factor * theta_without_bias, gradient) else: self._theta = np.subtract(self._theta, gradient)
另外,sklearn的Ridge默认会对特征做标准化(如果你没设normalize=False的话),而且它正确排除了偏置项的正则化,这也是它不会发散的原因。
3. 调整学习率和迭代次数
- 特征缩放后,梯度的量级会更稳定,你可以尝试把学习率从
0.0001调到0.001甚至0.01,加快收敛速度。 - 迭代次数400可能太少,不足以让梯度下降收敛,建议调到5000以上,同时可以监控损失值的变化,当损失不再下降时提前停止(早停机制)。
4. 监控损失值的变化
你注释掉了损失计算的代码,建议恢复它,这样可以直观看到损失是否在下降,还是在震荡/上升:
# 在_trainGradientDescent的循环里恢复损失计算 hypothesis = X.dot(self._theta) difference = np.subtract(hypothesis, Y) # 计算MSE损失 cost_val = np.sum(difference ** 2) / (2 * self._training_sample) # 除以2是为了求导后简化,不影响趋势 self._cost_vals.append(cost_val)
然后可以用matplotlib画出损失曲线,判断模型是否收敛:
plt.plot(range(len(mlr_poly._cost_vals)), mlr_poly._cost_vals) plt.xlabel('Iterations') plt.ylabel('MSE Loss') plt.show()
按照以上步骤修改后,你的模型应该就能稳定收敛,不会出现参数无穷大的问题了。
内容的提问来源于stack exchange,提问作者H. Can
相关产品推荐
相关产品推荐

