0. 前言
古人说: “ 温故而知新, 可以为师矣.” 刚好自己也有点时间, 就把几个月前敲过的代码又敲了一遍.
1. nan? inf?
这是一个非常基础的一元线性回归代码, 照着西瓜书公式就可以直接敲出来的那种. 重新在网上找了一个数据集, 如图所示:
虽然说分布有点散, 但是总归还是能找到一条线来拟合的嘛.
然后运行, 不对劲了. 结果给我输出这个:
theta1 is: -1.1121132929727208e+169
theta0 is: -2.1858247925415553e+167
loss is: inf
再看图像...
这误差已经大得离谱, 已经是无穷大了.
接着, 我把这数据用另外一个代码跑了一下, 输出loss.
loss: 2782.553917241607
loss: 1.5311494984548842e+279
loss: inf
loss: nan
loss: nan
loss: nan
loss: nan
loss: nan
loss: nan
loss: nan
nan : Not a number
并且还出现了 RuntimeWarning 的警告, 为啥损失会这么大, 突然我想到一个词: "梯度爆炸"
3. 梯度爆炸
什么是梯度爆炸?
梯度的衰减是有连续乘法导致的, 如果在连续乘法中出现一个非常大的值, 最后计算出的梯度就会很大, 就想当优化到断崖处时, 会获得一个很大的梯度值, 如果以这个梯度值进行更新, 那么这次迭代的步长就很大, 可能会一下子飞出了合理的区域.
意思就是说, 咱们的 theta 在迭代时, 一直在变大. 看看公式,
损失函数: \( J(w_0, \dots, w_n) \)
梯度下降: \( w_j = w_j - \alpha * \frac{\partial}{\partial w_j} J \)
写成矩阵形式: \(\mathbf{w} = [ w_0,\dots, w_n ]^{\mathrm{T}} = \mathbf{w} - \alpha * \frac 1m * (\mathbf{X}^{\mathrm{T}} ( \mathbf{X} \mathbf{w} - \mathbf{Y}) ) \)
根据上面的解释, 影响的因素可能包含
1) 学习率 \( \alpha \)
2) 初始权重 \( \mathbf{w} \)
3) \( \mathbf{X} , \mathbf{Y} \)
此处, 初始权重都设为 0.
3.1 学习率
Test 1: \(\alpha = 0.01 , \rm{numiter} = 100. \) \(\rm{numiter}\) indicates the number of iterations.
失败.
Test 2: \(\alpha = 0.001 , \rm{numiter} = 100. \)
失败.
Test 3: \(\alpha = 0.0001 , \rm{numiter} = 100. \)
根据上图来看, 好像是下降成功了.
theta1 is: 1.4788027175308358
theta0 is: 0.03507497059234175
loss is : 112.64709271657506
根据上图来看, 确实拟合出来了一条直线.
之后我又做了一系列的测试, 当学习率 \( \alpha \leq 0.0001 \)时, 调整迭代次数, 总归能够拟合出来一条直线. 误差大约为112.
结论: 学习率\( \alpha \) 会影响梯度的下降, \(\mathbf{w}\)的更新.
3.2 数据
数据的值太大会影响梯度下降吗?假设会的话, 那我就把它缩小. 下面就介绍两种常用的特征缩放方式.
1. Min-Max Normalization(min-max标准化)
$$x^*= \frac{x - min(x)} {max(x) - min(x)}$$
其实质是将数据映射到 [0, 1] 之间.
2. Standarddization(z-score标准化)
$$x^* = \frac{x - \mu} {\sigma}$$
其中 \( \mu \)为均值, \( \sigma \)为标准差, 实质是使数据符合标准正态分布.
我们采用第二种方法,
def z_score(x):
"""
feature scaling
:param x: the feature need to scale
:return: the feature after standardization
"""
sum1 = 0
sum2 = 0
n = len(x)
# calculate mu
for i in range(n):
sum1 += x[i]
mu = sum1 / n
# calculate sigma
for i in range(n):
sum2 += (x[i] - mu) ** 2
sigma = (sum2 / n) ** 0.5
x = (x - mu) / sigma
return x
处理之后, 我们再做测试.
Test 4: \(\alpha = 0.01 , \rm{numiter} = 100. \)
theta1 is: 11.123553842945782
theta0 is: 63.088960451274154
cost is : 210.15286292756554
与Test 1相比, 它拟合出来了一条直线, 并且误差为210左右. 误差应该还可以减少, 增加迭代次数看看.
Test 5: \(\alpha = 0.01 , \rm{numiter} = 500. \)
theta1 is: 12.823781751169484
theta0 is: 72.73206667205297
cost is : 110.25739302507955
误差减小到了110.25 左右. 之后我又做了一系列测试, 最终误差维持在为110.25左右.
结论: 数据值的大小会影响线性回归中梯度的下降.
4. 特征缩放
关于特征缩放, 吴恩达教授是这样讲的:
未进行归一化:
它会是这样一个狭长的函数. 在这样的损失函数上运行梯度下降法, 必须使用一个非常小的学习率. 并且如果初始位置设在这, 梯度下降法可能需要多次迭代.
相反地, 特征缩放后, 它会是一个更圆的球形轮廓, 那么无论从哪个位置开始, 梯度下降法都能够更直接地找到最小值, 并且可以使用较大步长.
5. 小结
特征缩放优点:
(1)缩放后加快了梯度下降求最优解的速度.
(2)缩放有可能提高精度(归一化是让不同维度之间的特征在数值上有一定的比较性).
若出现梯度爆炸现象, 首先去看迭代式子!
你可能会觉得奇怪, 做个简单的线性回归怎么还会扯上梯度爆炸. 你若不信, 可以用sklearn.datasets 中的波士顿数据去做个线性回归, 前提是不对数据进行处理, 试一试能不能直接拟合出一个预测函数.
当然, 若文章所述有误, 也欢迎批评指正!
参考: