赞
踩
梯度 : 一个向量,导数+变化最快的方向
机器学习:
收集数据x, 构建模型f,通过f(x, w) = Ypredict
判断模型质量的方法,计算loss
l
o
s
s
=
(
Y
p
r
e
d
i
c
t
−
Y
t
r
u
e
)
2
(
回
归
损
失
)
l
o
s
s
=
Y
t
r
u
e
⋅
l
o
g
(
Y
p
r
e
d
i
c
t
)
(
分
类
损
失
)
loss = (Y_{predict} - Y_{true})^2 \quad (回归损失)\\ loss = Y_{true}·log(Y_{predict}) \quad (分类损失)
loss=(Ypredict−Ytrue)2(回归损失)loss=Ytrue⋅log(Ypredict)(分类损失)
通过学习参数w,尽可能降低模型的loss,那我们应该如何调整w呢?
对w0点进行求导,求得梯度为:
Δ
w
=
f
(
w
+
x
)
−
f
(
w
)
x
(
x
→
0
)
\Delta w = \frac {f(w+x) - f(w)}{x} \quad (x \rightarrow 0)
Δw=xf(w+x)−f(w)(x→0)
更新w
w = w − α Δ w w = w - \alpha \Delta w w=w−αΔw
当 Δ w \Delta w Δw>0, 意味着w将增大,反之w将减小。
总结:梯度就是函数参数的变化趋势,若只有一个变量时,就是导数
通过计算图,我们可以轻易通过多个变量计算最后的结果 J(a, b, c),这也称之为向前计算。
其中,通过如图对各部分的导数的计算,我们可以轻易地获得任意变量之间的偏导
如:
d
J
d
b
=
3
×
1
×
c
d
J
d
c
=
3
×
1
×
b
\frac {dJ} {db} = 3\times 1\times c \quad \frac {dJ}{dc} = 3 \times 1\times b
dbdJ=3×1×cdcdJ=3×1×b
import torch import matplotlib.pyplot as plt #1. y = 4x + 0.3 x = torch.rand([200, 1]) y = x * 3 + 0.8 learning_rate = 0.01;#设置学习率,即公式中的α #2. 通过模型计算y_predict w = torch.rand([1, 1], requires_grad=True) #require_grad为要求系统计算梯度,也即是纪录该计算图,才可以后面使用backward()进行反向传播 b = torch.tensor(0, requires_grad=True, dtype=torch.float32) y_predict = y; plt.figure(figsize=(20, 8)) #通过plt画图 for i in range(1000): #循环1000次训练 y_predict = torch.mm(x, w) + b loss = (y - y_predict).pow(2).mean() if w.grad is not None : #若梯度不空,则必须先对其置零操作,否则无法进行下一步的反向传播 w.grad.zero_() if b.grad is not None : b.grad.zero_() loss.backward(); w.data = w.data - learning_rate*w.grad; b.data = b.data - learning_rate*b.grad; print("第" , i ,"次 循环: w ,b = ", w, " ", b) if i == 0: ##画出第一条线,形状为点线,颜色为蓝色 plt.plot(x.numpy().reshape(-1), y_predict.detach().numpy().reshape(-1), 'b-.') elif i % 100 == 0:#当为100的整数,画一条形状为虚线,颜色为黑色的线 plt.plot(x.numpy().reshape(-1), y_predict.detach().numpy().reshape(-1), 'k--') print("结束时: w ,b = ", w, " ", b) # print("结束时: y_predict = ", y_predict) print("结束时: loss = ", loss) y_predict = torch.mm(x, w) + b plt.scatter(x.numpy().reshape(-1), y.numpy().reshape(-1))#画点,通过点描绘真实y的线 plt.plot(x.numpy().reshape(-1), y_predict.detach().numpy().reshape(-1), "r-") #画出最后一条预测线,颜色为红色,线为实线 plt.show() ##画线
循环1000次时:
第 999 次 循环: w ,b = tensor([[2.5627]], requires_grad=True) tensor(1.0495, requires_grad=True)
结束时: w ,b = tensor([[2.5627]], requires_grad=True) tensor(1.0495, requires_grad=True) //跟原来的3x+0.8还是有很大差距的
结束时: loss = tensor(0.0149, grad_fn=<MeanBackward0>)
其中: 最下面的蓝色点线,为第一次的预测y线;黑色线为第1次到999次的线,蓝色点线为真实值y线,红色线为最后一次的预测线;我们可以看到,随着训练数量的增加,预测y线越来越接近真实值y线
循环5000次时:
第 4999 次 循环: w ,b = tensor([[2.9958]], requires_grad=True) tensor(0.8021, requires_grad=True)
结束时: w ,b = tensor([[2.9958]], requires_grad=True) tensor(0.8021, requires_grad=True) //跟上面的1000次作对比,可以看到这次的参数已经很接近真实值了
结束时: loss = tensor(1.3419e-06, grad_fn=<MeanBackward0>)
改变学习率为0.1, 循环1000次
第 999 次 循环: w ,b = tensor([[3.0000]], requires_grad=True) tensor(0.8000, requires_grad=True)
结束时: w ,b = tensor([[3.0000]], requires_grad=True) tensor(0.8000, requires_grad=True) //居然直接找到了精确值,实际上在第826次就找到了
结束时: loss = tensor(4.9771e-12, grad_fn=<MeanBackward0>)
可以看到,通过增大学习率,使得模型更快地建立起来,只是在其他模型有可能会导致获取精确的模型。
import torch import torch.nn as nn import torch.optim as optim import matplotlib.pyplot as plt x = torch.rand([200, 1]) y = x*3 + 0.8 # 1.定义模型,优化器类实例化,loss实例化 class Ln(nn.Module): def __init__(self): super(Ln, self).__init__() self.linear = nn.Linear(1, 2) #分别为输入和输出的特征数,在本例子中为列数,它会自动为你申请参数 def forward(self, x): out = self.linear(x) return out model = Ln() y_pre = 0 optimizer = optim.SGD(model.parameters(), lr=0.01) lossFun = nn.MSELoss() ##初始化回归损失函数 plt.figure(figsize=(20, 8)) param = 0 print("长度为 : ", len(list(model.parameters()))) for i in range(1000): y_pre = model(x) #计算y预测值 loss = lossFun(y, y_pre) #计算损失 optimizer.zero_grad() #更新梯度为0 loss.backward() #反向传播计算梯度 optimizer.step() #通过梯度更新各个参数 param = list(model.parameters()) print("第" , i ,"次 循环: w ,b = ", param[0], " ", param[1]) if i == 0: plt.plot(x.numpy().reshape(-1), y_pre.detach().numpy().reshape(-1), 'b-.') elif i % 100 == 0: plt.plot(x.numpy().reshape(-1), y_pre.detach().numpy().reshape(-1), 'k--') print("结束时 w ,b = ", param[0], " ", param[1]) print("损失为 : " , lossFun(y, y_pre)) plt.scatter(x.numpy().reshape(-1), y.numpy().reshape(-1)) plt.plot(x.numpy().reshape(-1), y_pre.detach().numpy().reshape(-1), "r-") plt.show()
每次迭代都将所有样本放入,这样每次迭代都顾及所有样本,做的是全局优化。
缺点: 速度慢,需要考虑所有样本
从样本随机抽出一组,训练后更新一次,然后再抽取一组再更新一次,在样本量很大的情况下,可能不用训练完就可以获得损失值较小的模型了。
缺点:随机性强,但由于单个样本的训练可能带来很多噪声,往往会出现在开始训练时收敛的很快,训练一段时间之后变得很慢。
torch的api为:
torch.optim.SGD()
从样本抽取一小批进行训练,而不是一组,平均了速度和效果。
小批量SGD虽然速度快,但是在最优点时难以精确,而是在最优点附近徘徊。
其次,另一缺点是小批量SGD需要我们挑选一个合适的学习率,当我们采用较小的学习率,会导致训练时收敛太慢;当我们使用较大的,就会导致训练时难以达到最优点。
基于梯度的移动指数加权平均,对网络的梯度进行平滑处理,让梯度的摆动幅度变得更小,更好地进入最优点
g
=
0.8
g
+
0.2
p
r
e
g
w
为
上
一
次
的
梯
度
w
=
w
−
α
g
α
为
学
习
率
g = 0.8g + 0.2preg \quad w为上一次的梯度 \\ w = w - \alpha g \quad \alpha为学习率
g=0.8g+0.2pregw为上一次的梯度w=w−αgα为学习率
w变化例子:(w为公式中的g,即为梯度)
AdaGrad可以让梯度自适应学习,让梯度从大变小。
v
=
v
+
p
r
e
V
2
w
=
w
−
α
(
v
+
∂
)
p
r
e
V
∂
为
小
常
数
,
为
了
数
值
稳
定
通
常
设
置
为
1
0
−
7
v = v + preV^2 \\ w = w - \frac \alpha {(v + \partial )} preV \quad \partial为小常数,为了数值稳定通常设置为10^{-7}
v=v+preV2w=w−(v+∂)αpreV∂为小常数,为了数值稳定通常设置为10−7
由公式我们不难看出梯度(prev)会受到v的受到影响,而v会随着次数增多逐渐变大,梯度会随之逐渐下降,从而实现梯度的自适应。
进一步优化函数在更新函数摆动幅度过大的问题,让步长越来越小。
对参数的梯度使用了平方加权平均数。
将Momentum算法和RMSP算法结合的一种算法,防止梯度的摆幅过大,也能增快收敛速度。
torch的api为:
torch.optim.Adam()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。