浅析梯度迭代算法
|Word count:1.7k|Reading time:5min|Post View:
浅析梯度迭代算法
梯度迭代类算法已成为目前各种领域的主流算法。各种现实中的问题分解抽象成机器可以处理的形式之后,基本都可归类为图像、自然语言处理、决策、时序、强化学习这几种类型,而当今解决这些问题的顶尖算法中,梯度迭代(梯度上升或梯度下降)都占据主流地位,比如决策类问题的比赛中,梯度下降决策树
GBDT
类算法是完全的主流,使用深度学习网络处理图片自然语言问题更毋庸置疑。
那么,梯度迭代算法究竟是什么?简单地说,就是代入数据,预测结果,如果结果偏大就调小参数,结果偏小就调大参数。举一个简单的例子,分为三个小问题:
问题一
假设父亲的智商影响儿子的智商,设父亲的智商为x,儿子的智商为y,y=wx,训练一个参数
w 学习二者之间的关系。目前有多个父子智商数据对,其中第一个数据:父亲智商
x=100,儿子智商 w=110,将 w 初值设为 w=1.0;学习率设为
0.00001,计算平均误差。
如下程序用于学习 w。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import torch import matplotlib.pyplot as plt %matplotlib inline x = torch.tensor([100.]) # 只做训练不求梯度 y = torch.tensor([110.]) w = torch.tensor([1.], requires_grad=True) # 模拟网络参数 lr = 0.00001 # 学习率 arr = [] for i in range(10): pred = x * w loss = (pred-y)*(pred-y) # 平方误差 loss.backward() # 计算梯度 print("real", y.item(), "pred", pred.item(), "loss", loss.item()) print("w.data", w.data.item(), "w.grad", w.grad.item()) w.data = w.data - lr * w.grad # 按学习率调参 w.grad.zero_() # 梯度清 0,否则梯度会不断累加 arr.append(loss.item()) plt.plot(arr)
|
程序中使用了 torch 的基本数据结构 Tensor
及其自动计算梯度的功能,模型、优化器、误差函数全部写代码实现。其误差等于实际的
y 值减预测值的平方,在第一次迭代中计算结果如下:
求误差函数对参数 w 的偏导数,用以调节 w,具体使用链式法则:
然后使用梯度修改参数 w,每次修改一个很小的步幅,即学习率。
每次 w 都变好一点,经过多次迭代,参数 w
逐渐逼近其真实值,误差也逐渐下降,如下图所示:
此时再代入值 x=100,即可得到正确的预测
y=110。上述是最简单的情况,为简化操作只训练了一个实例,如果有 10000
个实例代入训练,反复迭代 20
次,最终将通过微调的方法得到最为合理的参数w。
使用 Pytorch
提供的线性层、误差函数和优化器,功能与上面的程序一致,代码更加简单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import torch x = torch.tensor([100.]) # 只做训练不求梯度 y = torch.tensor([110.]) lr = 0.00001 # 学习率 model = torch.nn.Linear(1, 1, bias=False) model.weight.data.fill_(1.0) # 初始化参数 optimizer = torch.optim.SGD(model.parameters(), lr=lr, momentum=0) # 优化器 lossfunc = torch.nn.MSELoss() # 平方根损失 for i in range(10): pred = model(x) loss = lossfunc(pred, y) loss.backward() # 计算梯度 optimizer.step() optimizer.zero_grad()
|
问题二
如果孩子的智商由父母双方决定,那么每一实例的x将提供父母双方的智商值
x1,x2,学到的参数 w 也是两个:y=x1w1+x2w2,每次调节参数时误差函数分别对
w1,w2 求偏导。
(码字不易,转载请注明出处:谢彦的技术博客)
问题三
如果孩子的智商由父亲决定,父亲的智商由奶奶决定,此时网络的输入是奶奶的智商
x,输出是孩子的智商
y,父亲的智商成为中间变量。因此有:y=xw1w2,其中 w1
是奶奶对父亲的影响,w2
是父亲对儿子的影响。(示例仅用于描述多层网络,实际上这样的双层网络与单层网络效果无异
w3=w2*w1)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import torch x = torch.tensor([100.]) # 只做训练不求梯度 y = torch.tensor([110.]) lr = 0.00001 # 学习率 model1 = torch.nn.Linear(1, 1, bias=False) model2 = torch.nn.Linear(1, 1, bias=False) params = [{'params':model1.parameters()}, {'params':model2.parameters()}] # 同时优化两组参数 optimizer = torch.optim.SGD(params, lr=lr, momentum=0) # 优化器 lossfunc = torch.nn.MSELoss() # 平方根损失 for i in range(10): pred = model2(model1(x)) loss = lossfunc(pred, y) loss.backward() # 计算梯度 optimizer.step() print("real", y.item(), "pred", pred.item(), "loss", loss.item()) print("w1.data", model1.weight.data.item(), "w1.grad", model1.weight.grad.item()) print("w2.data", model2.weight.data.item(), "w2.grad", model2.weight.grad.item()) optimizer.zero_grad()
|
(码字不易,转载请注明出处:谢彦的技术博客)
###梯度爆炸和梯度消失
梯度爆炸和梯度消失指的是梯度太陡或者梯度太平,引发的调参问题,该问题由连乘引发,假设一个
n 层网络,每一次都有参数 w,b,试想比较简单的情况
b=0,于是有如下的前向传播:
当所有的 W 都大于 1,且网络非常深时,最终的 Y
将非常大甚至越界,反之,当所有 W 都小于 1,且网络非常深时,最终的 Y
将趋于 0。如果网络的后几层的输入和输出都是
0,则梯度也必然是“平”的,导致无法正常调参。
一般用下列方法缓解梯度爆炸和消失问题:
- 用归一化方法处理模型输入 x。
- 控制模型初值参数w。
- 使用较小的学习率。
- 对模型中的数据流做一些限制。
- 使用残差网络,在前向传播过程中累加 X 值,使得 W 小于 1 时,Y
也不再趋于 0,从而解决梯度消失问题。
当模型参数无法如设想中的调整时,除上述方法还可以尝试:
- 冻结一些层,调整另一些层。
- 不能收敛时,或波动太大时,可考虑缩小学习率。
- 跟踪 backward 之后参数梯度的均值和方差,查看有无异常。
- 如果手动实现模型,需要验证梯度计算是否正确(计算双边误差)。
- 调试时需去掉 Dropout,以保持稳定。
- 注意正则化项对代价函数的影响。
- W 和 B 一般在训练过程中逐渐变大,有些问题可能只在 W,B
较大时才出现。
- 使用 BatchNormal 时,mini-batch size 不能太小。