本篇主要记录一下一些相对高阶的梯度相关知识

梯度介绍

一.概念

梯度概念

导数 => 偏微分 => 梯度

前两者都是标量

梯度是所有的偏导组成的向量,反映了函数变化的趋势(方向和大小)

二.梯度求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()
    1. binary(二分类)
    2. multi-class(多分类)
    3. +softmax
    4. Leave it to Logistic Regression Part

二.MSE

mse定义
mse
与L2-norm相比,均方差不开根号,下面是梯度求导过程:
mse

# 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总结
gradient API

三.Softmax

soft version of max
softmax
将数值压缩到[0, 1],并且与sigmoid不同的是,和是1,因此比较适合多分类的情况。结果会把原来大的放的更大,小的缩的更小。比如这里原来2是1的两倍,经过softmax之后变成了3.5倍
下面是梯度求导过程:
softmax
softmax
softmax

# 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]),)