自动微分机制

公众号后台回复关键字: Pytorch ,获取项目github地址。

Pytorch是一个基于Python的机器学习库。它广泛应用于计算机视觉,自然语言处理等深度学习领域。是目前和TensorFlow分庭抗礼的深度学习框架,在学术圈颇受欢迎。

它主要提供了以下两种核心功能:

1,支持GPU加速的张量计算。

2,方便优化模型的自动微分机制。

Pytorch的主要优点:

  • 简洁易懂:Pytorch的API设计的相当简洁一致。基本上就是tensor, autograd, nn三级封装。学习起来非常容易。有一个这样的段子,说TensorFlow的设计哲学是 Make it complicated, Keras 的设计哲学是 Make it complicated and hide it, 而Pytorch的设计哲学是 Keep it simple and stupid.
  • 便于调试:Pytorch采用动态图,可以像普通Python代码一样进行调试。不同于TensorFlow, Pytorch的报错说明通常很容易看懂。有一个这样的段子,说你永远不可能从TensorFlow的报错说明中找到它出错的原因。
  • 强大高效:Pytorch提供了非常丰富的模型组件,可以快速实现想法。并且运行速度很快。目前大部分深度学习相关的Paper都是用Pytorch实现的。有些研究人员表示,从使用TensorFlow转换为使用Pytorch之后,他们的睡眠好多了,头发比以前浓密了,皮肤也比以前光滑了。

俗话说,万丈高楼平地起,Pytorch这座大厦也有它的地基。

Pytorch底层最核心的概念是张量,自动微分以及动态计算图。

本篇我们介绍自动微分。

神经网络通常依赖反向传播求梯度来更新网络参数,求梯度过程通常是一件非常复杂而容易出错的事情。

而深度学习框架可以帮助我们自动地完成这种求梯度运算。

Pytorch一般通过反向传播 backward 方法 实现这种求梯度计算。该方法求得的梯度将存在对应自变量张量的grad属性下。

除此之外,也能够调用torch.autograd.grad 函数来实现求梯度计算。

这就是Pytorch的自动微分机制。

一,利用backward方法求导数

backward 方法通常在一个标量张量上调用,该方法求得的梯度将存在对应自变量张量的grad属性下。

如果调用的张量非标量,则要传入一个和它同形状 的gradient参数张量。

相当于用该gradient参数张量与调用张量作向量点乘,得到的标量结果再反向传播。

1, 标量的反向传播


        
          
import numpy as np   
import torch   
  
# f(x) = a*x**2 + b*x + c的导数  
  
x = torch.tensor(0.0,requires_grad = True) # x需要被求导  
a = torch.tensor(1.0)  
b = torch.tensor(-2.0)  
c = torch.tensor(1.0)  
y = a*torch.pow(x,2) + b*x + c   
  
y.backward()  
dy_dx = x.grad  
print(dy_dx)  
  

      

        
          
tensor(-2.)  

      

2, 非标量的反向传播


        
          
import numpy as np   
import torch   
  
# f(x) = a*x**2 + b*x + c  
  
x = torch.tensor([[0.0,0.0],[1.0,2.0]],requires_grad = True) # x需要被求导  
a = torch.tensor(1.0)  
b = torch.tensor(-2.0)  
c = torch.tensor(1.0)  
y = a*torch.pow(x,2) + b*x + c   
  
gradient = torch.tensor([[1.0,1.0],[1.0,1.0]])  
  
print("x:\n",x)  
print("y:\n",y)  
y.backward(gradient = gradient)  
x_grad = x.grad  
print("x\_grad:\n",x_grad)  

      

        
          
x:  
 tensor([[0., 0.],  
        [1., 2.]], requires_grad=True)  
y:  
 tensor([[1., 1.],  
        [0., 1.]], grad_fn=<AddBackward0>)  
x_grad:  
 tensor([[-2., -2.],  
        [ 0.,  2.]])  

      

3, 非标量的反向传播可以用标量的反向传播实现


        
          
import numpy as np   
import torch   
  
# f(x) = a*x**2 + b*x + c  
  
x = torch.tensor([[0.0,0.0],[1.0,2.0]],requires_grad = True) # x需要被求导  
a = torch.tensor(1.0)  
b = torch.tensor(-2.0)  
c = torch.tensor(1.0)  
y = a*torch.pow(x,2) + b*x + c   
  
gradient = torch.tensor([[1.0,1.0],[1.0,1.0]])  
z = torch.sum(y*gradient)  
  
print("x:",x)  
print("y:",y)  
z.backward()  
x_grad = x.grad  
print("x\_grad:\n",x_grad)  

      

        
          
x: tensor([[0., 0.],  
        [1., 2.]], requires_grad=True)  
y: tensor([[1., 1.],  
        [0., 1.]], grad_fn=<AddBackward0>)  
x_grad:  
 tensor([[-2., -2.],  
        [ 0.,  2.]])  

      

二,利用autograd.grad方法求导数


        
          
import numpy as np   
import torch   
  
# f(x) = a*x**2 + b*x + c的导数  
  
x = torch.tensor(0.0,requires_grad = True) # x需要被求导  
a = torch.tensor(1.0)  
b = torch.tensor(-2.0)  
c = torch.tensor(1.0)  
y = a*torch.pow(x,2) + b*x + c  
  
  
# create\_graph 设置为 True 将允许创建更高阶的导数   
dy_dx = torch.autograd.grad(y,x,create_graph=True)[0]  
print(dy_dx.data)  
  
# 求二阶导数  
dy2_dx2 = torch.autograd.grad(dy_dx,x)[0]   
  
print(dy2_dx2.data)  
  
  

      

        
          
tensor(-2.)  
tensor(2.)  

      

        
          
import numpy as np   
import torch   
  
x1 = torch.tensor(1.0,requires_grad = True) # x需要被求导  
x2 = torch.tensor(2.0,requires_grad = True)  
  
y1 = x1*x2  
y2 = x1+x2  
  
  
# 允许同时对多个自变量求导数  
(dy1_dx1,dy1_dx2) = torch.autograd.grad(outputs=y1,inputs = [x1,x2],retain_graph = True)  
print(dy1_dx1,dy1_dx2)  
  
# 如果有多个因变量,相当于把多个因变量的梯度结果求和  
(dy12_dx1,dy12_dx2) = torch.autograd.grad(outputs=[y1,y2],inputs = [x1,x2])  
print(dy12_dx1,dy12_dx2)  
  
  

      

        
          
tensor(2.) tensor(1.)  
tensor(3.) tensor(2.)  

      

三,利用自动微分和优化器求最小值


        
          
import numpy as np   
import torch   
  
# f(x) = a*x**2 + b*x + c的最小值  
  
x = torch.tensor(0.0,requires_grad = True) # x需要被求导  
a = torch.tensor(1.0)  
b = torch.tensor(-2.0)  
c = torch.tensor(1.0)  
  
optimizer = torch.optim.SGD(params=[x],lr = 0.01)  
  
  
def f(x):  
    result = a*torch.pow(x,2) + b*x + c   
    return(result)  
  
for i in range(500):  
    optimizer.zero_grad()  
    y = f(x)  
    y.backward()  
    optimizer.step()  
     
      
print("y=",f(x).data,";","x=",x.data)  
  

      

        
          
y= tensor(0.) ; x= tensor(1.0000)  

      

如果本书对你有所帮助,想鼓励一下作者,记得给本项目加一颗星星star⭐️,并分享给你的朋友们喔😊!

如果对本书内容理解上有需要进一步和作者交流的地方,可以在公众号后台回复关键字: 加群 ,加入读者交流群和大家讨论。

公众号后台回复关键字:pytorch,获取项目github地址。

picture.image picture.image

picture.image

0
0
0
0
评论
未登录
暂无评论