实现多变量线性回归梯度下降时出现inf/nan值的问题排查
问题描述
我手动实现多变量线性回归的梯度下降算法,运行代码后输出全为inf值,调小学习率也无法解决。对数据做标准化处理后,输出又变成nan值,但使用sklearn的LinearRegression无论是否标准化都能正常运行,求排查错误原因。
原始实现代码
# Starting values w = np.ones(3) # 特征数量为3 b = float(0) def gradient_descent(): global w global b learning_rate = 0.0001 for i in range(x_train.shape[0]): prediction = np.dot(x_train[i], w) + b error = x_train[i] - prediction for j in range(w.shape[0]): w[j] = w[j] - (error * x_train[i][j] * learning_rate) b = b - (error * learning_rate) def train(): for i in range(10_000): gradient_descent() print(i, ':', w, b) train()
输出示例
0 : [inf inf inf] inf 1 : [inf inf inf] inf ....
数据说明
特征为total_rooms、population、households(x_train形状(17000,3)),目标变量是bedrooms(y_train形状(17000,1)),样本示例:
total_rooms,population,households,bedrooms(target) 5612.0,1015.0,472.0,1283.0 7650.0,1129.0,463.0,1901.0 720.0,333.0,117.0,174.0 1501.0,515.0,226.0,337.0 1454.0,624.0,262.0,326.0
标准化处理代码
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() train_data = scaler.fit_transform(train_data) x_train = train_data[:, :3] y_train = train_data[:, -1]
错误原因分析
核心错误:误差计算逻辑完全错误
线性回归梯度下降中,误差应为预测值与真实目标值的差值,即error = prediction - y_train[i](或y_train[i] - prediction,仅影响梯度更新方向),但你写成了error = x_train[i] - prediction——用特征向量和预测值做差,完全违背了损失函数的逻辑。
这个错误会导致参数更新时使用完全错误的梯度方向,加上原始数据特征值量级较大(比如total_rooms达数千),参数会在迭代中迅速膨胀到无穷大(inf);标准化后,错误的误差计算会引发数值不稳定,最终变成nan。额外问题:迭代次数冗余
当前实现是随机梯度下降(SGD),但外层又套了10000次全样本循环,相当于做了10000轮SGD,迭代次数过多会加剧数值不稳定。
修正方案
1. 修复误差计算逻辑
将误差计算改为目标值与预测值的差,同时确保梯度更新符号正确:
# 修正误差计算 error = prediction - y_train[i] # 或者用 y_train[i] - prediction,此时梯度更新需改为 += (因为梯度方向反转)
2. 标准化优化(推荐保留)
标准化能均衡不同量级特征对参数更新的影响,建议仅对特征做标准化,目标变量可按需处理:
# 仅标准化特征x_train scaler = StandardScaler() x_train = scaler.fit_transform(x_train) # 若需对y_train标准化,可单独处理,最后用逆变换还原结果
3. 优化梯度下降实现(可选)
改成批量梯度下降(计算所有样本的梯度均值再更新),提升稳定性:
def gradient_descent(): global w, b learning_rate = 0.01 m = x_train.shape[0] dw = np.zeros_like(w) db = 0.0 # 计算所有样本的梯度总和 for i in range(m): prediction = np.dot(x_train[i], w) + b error = prediction - y_train[i] dw += error * x_train[i] db += error # 取梯度均值,避免样本数量影响更新幅度 dw /= m db /= m # 更新参数 w -= learning_rate * dw b -= learning_rate * db
验证结果
修正后,无论是否标准化数据,梯度下降的参数都会逐步收敛到合理值,结果与sklearn的LinearRegression输出接近,不会再出现inf或nan。
内容的提问来源于stack exchange,提问作者elfarouk kamal

