Pytorch快速入门

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

在这里插入图片描述
在这里插入图片描述

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
        
    

在这里插入图片描述

使用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_()

在这里插入图片描述

十分钟搭建神经网络

如果想搭建一个网络,首先要定义一个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()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值