使用Numpy实现线性回归时梯度下降出现损失与梯度爆炸问题排查
为什么你的梯度下降会出现损失和参数爆炸?
这问题我太熟了——你踩了梯度下降里最常见的坑:特征尺度过大+学习率不匹配,两者叠加直接把参数和损失推到爆炸的地步。咱们一步步拆解:
核心问题分析
1. 特征尺度过于庞大
你的X1和X2都是1到10000的整数,数值范围特别大。梯度计算的时候,比如dL_dw1 = (-1/n) * np.sum((y - y_pred) * X1),X1的大数值会直接放大梯度的量级:
- 初始迭代时,w1=1、w2=1,y_pred和真实y的差就是随机误差e(0~0.5),
(y-y_pred)*X1的平均量级大概是0.25*5000=1250,除以样本数后梯度值仍有1250左右。 - 这么大的梯度乘以0.01的学习率,直接让w1从1跳到13.7,参数一步就偏离了正确方向,下一次迭代的y_pred和y的差距指数级扩大,梯度自然跟着爆炸。
2. 学习率与特征尺度不匹配
alpha=0.01对于常规归一化后的特征来说可能合适,但面对1~10000的原始特征,这个学习率太大了。每一步参数更新幅度过大,直接越过损失函数的极小值点,甚至往损失越来越大的方向狂奔。
解决方案
第一步:对特征做归一化/标准化
把X1和X2的尺度压缩到相近范围(比如0~1或者均值0、方差1),这是解决梯度爆炸最关键的一步。你可以用工具或手动实现:
用StandardScaler标准化(推荐)
import numpy as np import random from sklearn.preprocessing import StandardScaler # 生成原始数据 X1 = np.random.randint(1, 10000, 5000) X2 = np.random.randint(1, 10000, 5000) e = np.array([random.uniform(0, 0.5) for i in range(5000)]) y = X1 + X2 + e # 合并特征并标准化 X = np.column_stack((X1, X2)) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) X1_scaled = X_scaled[:, 0] X2_scaled = X_scaled[:, 1]
手动归一化到0~1
X1_norm = (X1 - X1.min()) / (X1.max() - X1.min()) X2_norm = (X2 - X2.min()) / (X2.max() - X2.min())
第二步:调整学习率并重新训练
特征归一化后,梯度量级会变得可控,这时用0.01的学习率就没问题了。修改后的梯度下降代码:
w1, w2, b = 0, 0, 0 # 初始值设为0更合理,不影响收敛 n = X1_scaled.shape[0] alpha = 0.01 for i in range(50): # 多迭代几次观察收敛情况 y_pred = w1 * X1_scaled + w2 * X2_scaled + b L = np.sum(np.square(y - y_pred))/(2 * n) dL_dw1 = (-1/n) * np.sum((y - y_pred) * X1_scaled) dL_dw2 = (-1/n) * np.sum((y - y_pred) * X2_scaled) dL_db = (-1/n) * np.sum((y - y_pred)) w1 = w1 - alpha * dL_dw1 w2 = w2 - alpha * dL_dw2 b = b - alpha * dL_db print(f"Iteration {i+1}: Loss = {L:.4f}, w1 = {w1:.4f}, w2 = {w2:.4f}, b = {b:.4f}")
运行这段代码你会看到损失稳步下降,参数逐渐收敛到合理范围(真实模型是w1=1、w2=1,标准化后的参数需要结合scaler反推,但趋势肯定是对的)。
另外提个小细节:你初始设置w1=1、w2=1其实已经很接近真实值了,但因为特征尺度问题,第一次迭代反而把参数带偏了,这也能看出来特征尺度对梯度下降的影响有多大。
内容的提问来源于stack exchange,提问作者dufrmbgr
相关产品推荐
相关产品推荐

