pytorch学习笔记-高阶篇(梯度相关)
本篇主要记录一下一些相对高阶的梯度相关知识
梯度介绍
一.概念
导数 => 偏微分 => 梯度
前两者都是标量
梯度是所有的偏导组成的向量,反映了函数变化的趋势(方向和大小)
二.梯度求loss的问题
- 局部极小值
- saddle point鞍点(x 取得局部极小值的同时y取得几部极大值)
三.影响优化性能的因素
- initialization status 初始状态
- learning rate 学习率
- momentun 动量(逃离局部极小值)
常见函数梯度
激活函数及其梯度
一.激活函数
第一个输出是输入的加权求和的过程,第二个则不是简单的线性过程,而是一个阈值函数,为解决上述的阶梯函数在0处不可导,提出了下面的sigmod函数,作了一个平滑处理
1. sigmod函数
下面是sigmoid函数的求导过程
一般在想要把数据范围压缩到[0, 1]内可以用sigmoid函数。例如prob(概率),RGB(颜色等);sigmoid函数存在的缺点是在两端函数的导数会接近于0,此时loss会出现长时间保持不便的现象
a = torch.linspace(-100, 100, 10)
a
'''
Out[4]:
tensor([-100.0000, -77.7778, -55.5556, -33.3333, -11.1111, 11.1111,
33.3333, 55.5556, 77.7778, 100.0000])
'''
torch.sigmoid(a)
'''
Out[5]:
tensor([0.0000e+00, 1.6655e-34, 7.4564e-25, 3.3382e-15, 1.4945e-05, 9.9999e-01,
1.0000e+00, 1.0000e+00, 1.0000e+00, 1.0000e+00])
'''
2. Tanh函数(在RNN-循环神经网络中经常使用)
下面是Tanh函数的求导过程
# 2. Tanh
a = torch.linspace(-1, 1, 10)
a
'''
Out[7]:
tensor([-1.0000, -0.7778, -0.5556, -0.3333, -0.1111, 0.1111, 0.3333, 0.5556,
0.7778, 1.0000])
'''
torch.tanh(a)
'''
Out[8]:
tensor([-0.7616, -0.6514, -0.5047, -0.3215, -0.1107, 0.1107, 0.3215, 0.5047,
0.6514, 0.7616])
'''
相比之前sigmoid函数,可以很明显地看到结果的后几个开始有区别,不全是1
3. ReLU函数(Rectified Linear Unit非常常见)
下面是ReLU函数的求导过程
# 3. ReLU
from torch.nn import functional as F
a = torch.linspace(-1, 1, 10)
a
'''
Out[5]:
tensor([-1.0000, -0.7778, -0.5556, -0.3333, -0.1111, 0.1111, 0.3333, 0.5556,
0.7778, 1.0000])
'''
torch.relu(a)
'''
Out[6]:
tensor([0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1111, 0.3333, 0.5556, 0.7778,
1.0000])
'''
F.relu(a)
'''
Out[7]:
tensor([0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.1111, 0.3333, 0.5556, 0.7778,
1.0000])
'''
Loss及其梯度
一.两种典型loss
- Mean Square Error(MSE均方差)
- Cross Entropy Loss()
- binary(二分类)
- multi-class(多分类)
- +softmax
- Leave it to Logistic Regression Part
二.MSE
mse定义
与L2-norm相比,均方差不开根号,下面是梯度求导过程:
# pytorch 自动求导
# 方法1:autofrad.grad
# pred = x*w + b
# x = 1, w = 2, b = 0
x = torch.ones(1)
w = torch.full([1], 2)
# 即(1-2)^2
mse = F.mse_loss(torch.ones(1), w*x)
# Out[14]: tensor(1.)
# 参数意义【pred, [w1, w2, w3...]】
# 因为w没有设置需要求导信息,直接求导会报如下错误
torch.autograd.grad(mse, [w])
# element 0 of tensors does not require grad and does not have a grad_fn
w = w.float()
w.requires_grad_()
# Out[21]: tensor([2.], requires_grad=True)
# 此时仍然会报错,pytorch是动态更新图
torch.autograd.grad(mse, [w])
# RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
# 需要重新生成图
mse = F.mse_loss(torch.ones(1), w*x)
torch.autograd.grad(mse, [w])
# Out[24]: (tensor([2.]),)
# 方法2:loss.backward
x = torch.ones(1)
w = torch.full([1], 2)
w = w.float()
w.requires_grad_()
mse = F.mse_loss(torch.ones(1), x*w)
# .backward(),会把梯度信息直接加在tensor的成员grad中
mse.backward()
w.grad
# Out[7]: tensor([2.])
gradient API总结
三.Softmax
soft version of max
将数值压缩到[0, 1],并且与sigmoid不同的是,和是1,因此比较适合多分类的情况。结果会把原来大的放的更大,小的缩的更小。比如这里原来2是1的两倍,经过softmax之后变成了3.5倍
下面是梯度求导过程:
# softmax
# 将数值压缩到[0, 1],并且与sigmoid不同的是,和是1
a = torch.rand(3)
a = a.float()
a.requires_grad_()
# Out[8]: tensor([0.4036, 0.0773, 0.3948], requires_grad=True)
# 如果有多个维度,比如[batch_size, feature],那肯定是在feature维度作softmax
p = F.softmax(a, dim=0)
# 不能直接传p,此处的参数必须是一个长度为1,dim=1的scalar
torch.autograd.grad(p[1], [a], retain_graph=True)
# 这里的结果的意义分别是 grad(p[1],a[0]),grad(p[1],a[1]),grad(p[1],a[2])
# 可以看到,与softmax计算梯度的通用公式一致
# 当下标一致时,所得是一个正数,否则为负
# Out[15]: (tensor([-0.0980, 0.1952, -0.0972]),)
torch.autograd.grad(p[2], [a])
# Out[16]: (tensor([-0.1347, -0.0972, 0.2319]),)
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 不听话的兔子君!