手动实现梯度下降踩坑:死磕10000次迭代真的合理吗?
刚把吴恩达 ML 专项课程里的线性回归部分啃完,在跑 Lab 的时候被一个细节给整懵了。代码里直接写死了一个
下一篇
我的产品没人用:关于“信息孤岛”的一次实操复盘 →
iterations = 10000 的循环,这种硬编码的迭代次数在实际实操中真的没问题吗?直觉告诉我,与其盲目跑一万次,不如在每次迭代后检查一下 Cost Function(代价函数)的数值变化。如果两次迭代之间的差值已经小到某个阈值(比如 1e-6),直接 break 掉不就省事了?死跑一万次,万一在第 500 次就收敛了,剩下的 9500 次完全是在浪费计算资源。
而且我盯着这段手动实现的逻辑看了半天,总觉得效率低得离谱。虽然我知道现在大家直接用 scikit-learn 这种成熟的库,但我想在不依赖框架的情况下,真正按照底层逻辑把梯度下降写正确。
最让我困惑的是,这段代码根本没有对最小值(Minima)进行校验。如果学习率(Learning Rate)设得稍微大一点,很容易直接在最小值附近跳来跳去,甚至直接发散,但循环依然会强行跑完 10000 次。
为了验证我的想法,我试着把死循环改成了带阈值判断的逻辑,对比了一下效果。
这里是我尝试修改后的逻辑片段:
# 原始写法是 for i in range(iterations):
# 我尝试改成这样来监控收敛情况
prev_cost = float('inf')
learning_rate = 0.01
epsilon = 1e-6 # 设定一个极小的阈值
for i in range(10000):
# 计算梯度并更新参数
grad = (1/m) * np.dot(X.T, (np.dot(X, theta) - y))
theta = theta - learning_rate * grad
# 计算当前的代价函数
cost = (1/(2*m)) * np.sum(np.square(np.dot(X, theta) - y))
# 核心改进:检查代价函数的改变量
if abs(prev_cost - cost) < epsilon:
print(f"Converged at iteration {i}")
break
prev_cost = cost实测下来,在某些数据集上,原本需要跑 10000 次的流程,在第 1200 次左右就触发了 break,响应速度明显快了很多。
但我现在又遇到了一个新的坑:如果 epsilon 设得太大了,模型还没到真正的全局最小值就提前停止了;如果设得太小,又回到了死跑一万次的老路。而且如果学习率没调好,prev_cost - cost 可能会出现正负波动,导致 abs() 判定失效。
我想请教下,在手动实现这种大模型底层优化算法时,除了监控 Cost 变化,还有什么更优雅的退出机制?或者说,在这种简单的线性回归实操中,死磕固定迭代次数是不是一种为了教学方便而牺牲的“简陋”写法?
全部回复 (2)
阿
阿Sam的日常
高级
10小时前
而且学习率要是设高了,跑一万次只会让梯度直接炸掉,根本收敛不了。
0
产