梯度下降法(Gradient descent,简称GD)是一阶最优化算法,计算过程就是沿梯度下降的方向求解极小值,公式如下所示,其中 μ \mu μ表示学习率, g t g^t gt表示梯度, t t t表示第t次迭代。通过多次迭代计算上面公式可以求得极小值点(非最小值)。
x t + 1 = x t − μ ⋅ g t x^{t+1}=x^t-\mu·g^t xt+1=xt−μ⋅gt
在torch中内置许多算法,其中torch.optim.SGD
函数就是随机梯度下降法。其参数包括:
params
是需要进行优化的参数,lr就是学习率。在使用SDG()
需要用到他的两个方法:zero_grad()
和step()
以及参数的梯度计算:backward()
SDG(params, lr).zero_grad()
,这个方法的作用是将params
所包含的张量的梯度置0,因为每次使用backward()
计算得到的梯度都会累加到原来的梯度上。
SDG(params, lr).step()
,这个方法是执行 x t + 1 = x t − μ ⋅ g t x^{t+1}=x^t-\mu·g^t xt+1=xt−μ⋅gt这个公式,也就是更新params
包含的参数的值。深入分析源码的可以考:pytorch中optimizer.zero_grad(), loss.backward(), optimizer.step()的作用及原理
随机梯度下降算法是梯度下降算法中的一种,当训练数据只有一条的时候SGD和GD是一样的。具体可参考:三种梯度下降法对比(Batch gradient descent、Mini-batch gradient descent 和 stochastic gradient descent)
import torch
import matplotlib.pyplot as plt
x = torch.tensor(3., requires_grad=True)
optimizer = torch.optim.SGD([x], lr=0.15)
# 进行十次迭代
x_record, y_record = [], [] # 记录每次更新后的x和y的值
for i in range(10):
y = x**2 # 每次x都会更新,所以y也需要同时更新
x_record.append(x.item()) # x的类型是tensor,调用item()是将其转化成python基本数据类型。
y_record.append(y.item())
optimizer.zero_grad() # 每次计算梯度前都要将原来的梯度置0
y.backward() # 计算梯度
optimizer.step() # 更新x值。
# 画坐标图
a = torch.linspace(-3., 3., 1000) # 从-3到3生成1000的等距的点
b = a**2
plt.figure(figsize=(6, 4)) # 创建一张长为6,宽为4的画布
plt.plot(a, b, c='b') # 画出y=x^2的曲线
plt.scatter(x_record, y_record, c='r', alpha=0.5) # 画出x下降过程中经过的点
plt.show()
动量梯度下降法是动量梯度下降法的一种改进,是将物理学中动量概念引入梯度下降法。公式如下:
x t + 1 = x t + m t + 1 x^{t+1}=x^t+m^{t+1} xt+1=xt+mt+1 其中 m t m^t mt表示第t次迭代是的动量,关于m的公式如下(初始动量 m 0 = 0 m^0=0 m0=0):
m t + 1 = λ ⋅ m t − μ ⋅ g t m^{t+1}=\lambda·m^t-\mu·g^t mt+1=λ⋅mt−μ⋅gt
有时候通过梯度下降法得到的值不是最优解,有可能是局部最优解,如下图一所示。引入动量的概念时,求最小值就像一个球从高处落下,落到局部最低点时会继续向前探索,有可能得到更小的值,如下图二所示。
设置
torch.optim.SGD()
中的momentum
参数就引入了动量系数(公式中的 λ \lambda λ)。
import torch
# 未引入动量
x1 = torch.tensor([-10.], requires_grad=True)
optimizer = torch.optim.SGD([x1], lr=0.01)
x1_list, y1_list = [], []
for i in range(10):
y1 = 16 * x1.pow(2) - x1.pow(3)
x1_list.append(x1.item())
y1_list.append(y1.item())
optimizer.zero_grad()
y1.backward()
optimizer.step()
# 引入动量
x2 = torch.tensor([-10.], requires_grad=True)
optimizer = torch.optim.SGD([x2], lr=0.01, momentum=0.76)
x2_list, y2_list = [], []
for i in range(10):
y2 = 16 * x2.pow(2) - x2.pow(3)
x2_list.append(x2.item())
y2_list.append(y2.item())
optimizer.zero_grad()
y2.backward()
optimizer.step()
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = torch.linspace(-10., 20., 1000)
y = 16 * x.pow(2) - x.pow(3)
plt.figure(figsize=(12, 4))
# 图一
plt.subplot(1, 2, 1)
plt.grid(True, linestyle='--', alpha=0.5)
plt.plot(x, y, c='b')
plt.scatter(x1_list, y1_list, c='r', alpha=0.5)
plt.title('图一', y=-0.2)
# 图二
plt.subplot(1, 2, 2)
plt.grid(True, linestyle='--', alpha=0.5)
plt.plot(x, y, c='b')
plt.scatter(x2_list, y2_list, c='r', alpha=0.5)
plt.title('图二', y=-0.2)
plt.show()
pytorch中optimizer.zero_grad(), loss.backward(), optimizer.step()的作用及原理
三种梯度下降法对比(Batch gradient descent、Mini-batch gradient descent 和 stochastic gradient descent)