欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Pytorch快速入门

程序员文章站 2022-04-18 20:09:34
...

Pytorch快速入门

通过实例学习Pytorch

Pytorch主要提供了两个功能:

  • 使一个类似于numpy的N维张量直接在GPU上运行。
  • 对建立和训练神经网络做自动微分

numpy搭建神经网络

在介绍pytorch之前,我们首先通过numpy来实现一个神经网络。

Numpy提供一个n维数组对象以及许多用于操作这些数组的函数。Numpy是一个用于科学计算的通用框架。它不知道任何关于计算图形 、深度学习梯度的知识。然而,我们可以很容易地使用numpy来建立一个两层网络适应随机数据,通过使用numpy操作的网络手动实现前向传播和反向传播。

import numpy as np

# N是batch_size,D_in是输入维度
# H是隐藏维度,D_out 是输出维度
N,D_in,H,D_out = 64,1000,100,10

#产生一个随机的输入和输出数据(服从标准高斯分布)
x = np.random.randn(N,D_in)
y = np.random.randn(N,D_out)

#随机初始化权重参数
w1 = np.random.randn(D_in,H)
w2 = np.random.randn(H,D_out)

learning_rate = 1e-6
for t in range(500):  
    #前向传播
    h = x.dot(w1) #矩阵乘积
    h_relu = np.maximum(h,0) #h和0比较,选择最大值
    y_pred = h_relu.dot(w2)  
    
    #计算和打印损失
    loss = np.square(y_pred - y).sum()   #计算(y-pred-y)的平方和
    print(t,loss)
    
    #计算w1和w2相对于损失的梯度
    grad_y_pred = 2.0 * (y_pred - y)
    grad_w2 = h_relu.T.dot(grad_y_pred)
    grad_h_relu = grad_y_pred.dot(w2.T)
    grad_h = grad_h_relu.copy()
    grad_h[h<0] = 0
    grad_w1 = x.T.dot(grad_h)
    
    #更新参数
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2

Pytorch快速入门
Pytorch快速入门

tensor搭建神经网络

Numpy是一个伟大的框架,但它不能利用gpu来加速它的数值计算。对于现代的深度神经网络,gpu通常提供50倍或更高的速度,因此numpy对于现代的深度学习来说是不够的。

在这里我们介绍最基本的PyTorch概念:tensor。一个PyTorch的tensor在概念上与一个numpy数组是相同的:一个tensor是一个n维数组,PyTorch提供了许多作用于这些tensor的函数。在幕后,tensor不仅可以可以跟踪计算图形和梯度,而且作为科学计算的通用工具tensor也很有用。

与numpy不同的是,PyTorch tensor(张量)可以利用gpu来加速它们的数值计算。要在GPU上运行tensor,只需将其转换为新的数据类型。

接下来我们用tensor来重新搭建上面的网络

import torch

dtype = torch.float
device = torch.device("cuda:0") #在gpu上运行
# device = torch.device("cpu")  #在cpu上运行

# N是batch_size,D_in是输入维度
# H是隐藏维度,D_out 是输出维度
N,D_in,H,D_out = 64,1000,100,10

#产生一个随机的输入和输出数据(服从标准高斯分布)   
#rand(size,out=None)是从0-1均匀分布
#randn(sizes,out = None)返回一个张量,服从标准正态分布,均值为0,方差为1,即高斯白噪声
x = torch.randn(N,D_in,device=device,dtype=dtype)
y = torch.randn(N,D_out,device=device,dtype=dtype)

#随机初始化权重参数
w1 = torch.randn(D_in,H,device=device,dtype=dtype)
w2 = torch.randn(H,D_out,device=device,dtype=dtype)

learning_rate = 1e-6
for t in range(500):
    # 前向传播:计算预测的y
    h = x.mm(w1) #矩阵乘法,要求两个tensor维度满足矩阵乘法的要求 3*4 4*2
    h_relu = h.clamp(min=0) #截断,<0赋值为0
    y_pred = h_relu.mm(w2)
    
    # 计算并打印损失
    loss = (y_pred - y).pow(2).sum().item()
    if t%100 == 99:
        print(t,loss)
    
    #使用反向传播计算w1和w2相对于损失的梯度
    grad_y_pred = 2.0*(y_pred-y)
    grad_w2 = h_relu.t().mm(grad_y_pred)
    grad_h_relu = grad_y_pred.mm(w2.t())
    grad_h = grad_h_relu.clone()
    grad_h[h < 0] = 0
    grad_w1 = x.t().mm(grad_h)

    # 使用梯度下降更新参数
    w1 -= learning_rate * grad_w1
    w2 -= learning_rate * grad_w2
        
    

Pytorch快速入门

使用autograd

在上面的例子中,我们必须手动实现神经网络的向前和向后传播。对于一个小型的两层网络来说,手动实现向后传递并不是一件大事,但是对于大型复杂的网络来说,可能很快就会变得非常麻烦。

幸运的是,我们可以使用自动微分来自动计算神经网络中的反向路径。PyTorch中的autograd包就提供了这种功能。当使用autograd时,网络的前向传播将定义一个计算图;图中的节点就是张量,边就是从输入张量产生输出张量的函数。然后通过这个图进行反向传播,就可以轻松地计算梯度。

这听起来很复杂,但在实践中却很简单。每个张量表示计算图中的一个节点。如果x是一个张量,它的requires_grad=True那么x.grad是另一个张量,它保持x对某个标量的梯度。

这里我们使用PyTorch张量和autograd来实现我们的双层网络,不再需要手动实现通过网络的向后传播:

import torch
dtype = torch.float
device = torch.device("cuda:0")

N,D_in,H,D_out = 64,1000,100,10

x = torch.randn(N,D_in,device=device,dtype=dtype)
y = torch.randn(N,D_out,device=device,dtype=dtype)

learning_rate = 1e-6

w1 = torch.randn(D_in,H,device=device,dtype=dtype,requires_grad=True)
w2 = torch.randn(H,D_out,device=device,dtype=dtype,requires_grad=True)

for t in range(500):
    #前向传播
    y_pred = x.mm(w1).clamp(min=0).mm(w2)
    
    #计算loss
    loss = (y_pred-y).pow(2).sum()
    if t%100 == 99:
        print(t,loss.item())
    
    loss.backward()
    
    with torch.no_grad():
        w1 -= learning_rate*w1.grad
        w2 -= learning_rate*w2.grad
        
        #在权重更新之后手动归零梯度
        w1.grad.zero_()
        w2.grad.zero_()

Pytorch快速入门

十分钟搭建神经网络

如果想搭建一个网络,首先要定义一个Class,继承nn.Module

import torch.nn as nn
class Net(nn.Module):

在Class里面主要写两个函数,一个是初始化__init__函数,一个是forward函数用于前向传播。

def __init__(self):
        super().__init()
        self.conv1 = nn.Conv2d(1,6,5)
        self.conv2 = nn.Conv2d(6,15,5)
    
def forward(self,x):
        x = F.max_pool2d(F.relu(self.conv1(x)),2)
        x = F.max_pool2d(F.relu(self.conv2(x)),2)
        return x

__init__里面就是卷积层的定义了,首先要super()一下,给父类nn.Module初始化一下,比如第一层卷积叫做conv1,把它定义成输入1通道,输出6通道,卷积核为5*5的一个卷继承。conv2是输入通道为6通道,输出通道为15通道,卷积核为5*5的一个卷积层。

forward函数里面是真正执行数据流动的,比如上面的代码,输入的x先经过conv1,然后再经过relu函数,最后再做一个maxpooling。

**注意:**前面一层的卷积层输出通道数必须和下一层的输入通道数一致,不然会报错!

但是Net只是一个类不能够传参数,那怎么办呢?定义一个Net的实例就好啦

net = Net()

这样我们就可以往里面传x了~

假设我们现在已经有一个输入input

output = net(input)

!!看看之前的定义

def __init__(self):
    ******

def forward(self,x):
    ******

有点奇怪,常规的python一般是向class里面传入一个数据,因为在class的定义里面,应该是把x作为形参传入__init__函数里面的,而在上面的定义里面,x作为形参传入forward函数。

其实这并不矛盾,因为定义net的时候,net=Net(),并没有往里面传参数。传入网络的时候,会自动认为这个x是喂给forward函数的~~

定义好一个神经网络之后呢就可以开始训练啦!涉及到传入参数,算误差,反向传播和更新权重balabla~~~

传入方式就相当于一次正向传播,把一路上各层的输入x都算出来了。but!鬼都知道这个神经网络的output肯定很糟糕,为什么?因为根本就没训练啊想让网络自学成才吗?!所以我们就要耐心地给它做一下指导,不断减少神经网络的output和groundtruth之间的差距~~这个差距是我们自己决定的,例如选一个损失函数。

我们知道损失函数loss是基本不会达到0的,但是可以让他达到最小值哇,所以要损失函数能够按照梯度进行下降。切记在训练中,只有输入是我们能够决定的,神经网络只能够决定每一层卷积层的权重。例如我们定了一个y=wx+b,x是我们决定的,w和b是神经网络能够改变的值,神经网络要做的就是调整w和b使得最后的y接近我们想要的y。

如果loss对于卷积层里面的参数w的偏导都接近0了,那不是意味达到了一个极小值吗?而在loss计算方式已经给定的情况下,loss对于w的偏导数的减小只能够通过更新卷积层参数w来实现。

所以我们通过以下方式来实现对w的更新

[1] 先算loss对输入x的偏导

[2] 对于[1]的结果我们要再乘一个步长(这样就相当于得到一个对参数w的修改量)

[3] 用w-掉这个修改量,完成w的一次更新

大规模神经网络的参数w更新过程十分的繁琐,我们是不可能手动实现的,所以要借助pytorch和torch.nn

loss = nn.MSELoss()
loss_net = loss(target,output)  #target是groundtruth,ouput是网络预测结果

算出loss之后就是反向传播啦

loss_net.backward()

but上面我们只是做到了第[1]步,那接下来两步咋搞啊?

定义一个优化器(例如SGD或者Adam):

from torch import optim
optimizer = optim.SGD(net.parameters(),lr = 0.001,momentum = 0.9)

同样地,优化器也是一个类,所以要先实例化,创建一个实例optimizer,传入net的参数parameters,以及learning rate!!

每次迭代之前,先对梯度进行清零。

optimizer.zero_gard()

然后再loss_net.backward()反向传播之后,更新参数:

optimizer.step()

以上就是神经网络搭建的全部过程啦,十分滴简单

总结一下有哪些步骤吧:

  1. 定义一个网络,写Net的class,声明网络实例net = Net()

  2. 定义优化器optimizer,optimizer = optim.SGD(net.parameters (),lr = xxxx)

  3. 定义损失函数(自己写或者调用pytorch已经封装好的)

  4. 定义完之后开启一次次的循环:

    • 梯度清零:optimizer.zero_grad()
    • 传入input:output = net(input)
    • 算损失:loss_net = loss(target,output)
    • 误差反向传播:loss.backward()
    • 更新参数:optimizer.step()
相关标签: 深度学习入门