zoukankan      html  css  js  c++  java
  • 深度学习框架PyTorch一书的学习-第一/二章

    参考https://github.com/chenyuntc/pytorch-book/tree/v1.0

    希望大家直接到上面的网址去查看代码,下面是本人的笔记

    pytorch的设计遵循tensor-> variable(autograd)-> nn.Module三个由低到高的抽象层次,分别代表高维数组(张量)、自动求导(变量)和神经网络(层/模块)。这三个抽象之间联系紧密,可以同时进行修改和操作

    在IPython和Jupyter notebook两个工具中使用了Jupyter notebook

    更多安装内容可见:深度学习PyTorch环境安装——mac

    1.tensor

    Tensor是pytorch中的重要数据结构,可认为是一个高维数组。它可以是一个数(标量)、一维数组(向量)、二维数组(矩阵)或更高维的数组

    其简单的用法可见pytorch学习-WHAT IS PYTORCH

    1)导入包

    from __future__ import print_function
    import torch as t

    2)仅分配空间,不初始化

    #构建5*3矩阵,只是分配了空间,未初始化
    x = t.Tensor(5,3)
    x

    返回:

    tensor([[ 0.0000e+00,  2.0000e+00, -1.0696e+12],
            [-1.5849e+29,  2.4158e-12,  1.1625e+33],
            [ 8.9605e-01,  1.1632e+33,  5.6003e-02],
            [ 7.0374e+22,  5.7453e-44,  0.0000e+00],
            [ 0.0000e+00,  2.0000e+00, -9.4044e+11]])

    3)初始化

    #使用[0,1]均匀分布随机初始化二维数组
    x = t.rand(5, 3)
    x

    返回:

    tensor([[0.0136, 0.1579, 0.7545],
            [0.0127, 0.3194, 0.0126],
            [0.7604, 0.0289, 0.4808],
            [0.3374, 0.8478, 0.4047],
            [0.2441, 0.4211, 0.5480]])

    4)获得size

    print(x.size())
    #两种写法,得到二维数组的行列数
    #支持该写法的原因是因为torch.Size是tuple对象的子类,因此其支持tuple的所有操作,如索引等
    x.size()[0], x.size(1)

    返回:

    torch.Size([5, 3])
    (5, 3)

    5)加法的几种写法

    y = t.rand(5,3)
    y

    返回:

    tensor([[0.6840, 0.5716, 0.3848],
            [0.8492, 0.5449, 0.3555],
            [0.4052, 0.5689, 0.1948],
            [0.2308, 0.6252, 0.8855],
            [0.0487, 0.2880, 0.9232]])

    1>第一种

    x + y

    返回:

    tensor([[0.6975, 0.7295, 1.1394],
            [0.8619, 0.8643, 0.3681],
            [1.1656, 0.5978, 0.6756],
            [0.5681, 1.4729, 1.2902],
            [0.2928, 0.7091, 1.4712]])

    2>第二种

    t.add(x,y)

    结果相同

    3》第三种:指定加法结果的输出目标为result

    result = t.Tensor(5,3) #预分配空间存储结果
    t.add(x, y, out=result)
    result

    结果相同

    4》第四种:改变y的值

    y.add_(x)
    y

    y变为上面的加法结果

    函数名后面带下划线_的函数会修改tensor本身

    2.自动微分

    深度学习算法的本质是通过反向函数求导数,pytorch的Autograd模块实现了此功能。在Tensor上的所有操作,Autograd都能够为他们自动提供微分,避免手动计算的复杂过程

    其中Autograd.Variable是Autograd的核心类。Tensor被封装成Variable后,可以调用它的.backward实现反向传播,自动计算所有梯度

     

    • data : 保存Variable所包含的Tensor
    • grad : 保存data对应的梯度,grad也是一个Variable(即也有datagradgrad_fn三个值),而不是Tensor,它和data的形状一样
    • grad_fn : 指向一个Function对象,这个Function用来反向传播计算输入的梯度

    1)导入包

    from __future__ import print_function 
    import torch as t

    from
    torch.autograd import Variable

    2)新建

    #使用Tensor新建一个Variable
    x = Variable(t.ones(2, 2),requires_grad = True)
    x

    返回:

    tensor([[1., 1.],
            [1., 1.]], requires_grad=True)

    此时查看该值的grad和grad_fn是没有返回值的,因为没有进行任何操作

    x.grad_fn
    x.grad

    3)进行求和操作,查看梯度

    y = x.sum()
    y

    返回:

    tensor(4., grad_fn=<SumBackward0>)

    这时候可查看:

    y.grad_fn

    返回:

    <SumBackward0 at 0x122782978>

    可知y是变量Variable x进行sum操作求来的,但是这个时候y.grad是没有返回值的,因为没有使用y进行别的操作

    这个时候的x.grad也是没有值的,虽然使用x进行了sum操作,但是还没有对y反向传播来计算梯度

    y.backward()#反向传播,计算梯度

    然后再查看:

    #因为y = x.sum() = (x[0][0] + x[0][1] + x[1][0] + x[1][1])
    #每个值的梯度都为1
    x.grad

    返回:

    tensor([[1., 1.],
            [1., 1.]])

    但是需要注意的一点就是grad在反向求和的过程中是累加的,所以反向传播前要把梯度清零

    比如再运行一遍反向传播:

    y.backward()
    x.grad

    可见返回:

    tensor([[2., 2.],
            [2., 2.]])

    清零操作:

    x.grad.data.zero_()#清零

    再运行即可:

    y.backward()
    x.grad

    返回:

    tensor([[1., 1.],
            [1., 1.]])

    在这里简单解释一下梯度的计算,如图示:

    下面我们使用代码实现一下看看是不是这样:

    x = Variable(t.ones(2,2), requires_grad = True)
    y = x +1
    z = y*y
    k = z.mean()

    然后进行反向传播,获得x的梯度:

    k.backward()
    x.grad

    返回果然是:

    tensor([[1., 1.],
            [1., 1.]])

    4)Variable和Tensor有近乎一致的接口,在实际中可以无缝切换

    x = Variable(t.ones(4,5))
    y = t.cos(x)#传入的是Variable
    x_tensor_cos = t.cos(x.data)#传入的是Tensor
    print(y)
    x_tensor_cos

    返回:

    tensor([[0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
            [0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
            [0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
            [0.5403, 0.5403, 0.5403, 0.5403, 0.5403]])
    tensor([[0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
            [0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
            [0.5403, 0.5403, 0.5403, 0.5403, 0.5403],
            [0.5403, 0.5403, 0.5403, 0.5403, 0.5403]])

    可见这样子得到的值并没有什么区别,返回的都是Tensor,而不是Variable

    3.神经网络

    torch.nn是专门为神经网络设计的模块化接口

    nn构建于Autograd之上,可用来定义和运行神经网络。nn.Module是nn中最重要的类,可以把它看作一个网络的封装,包含网络各层定义和forward方法,调用forward(input)可返回前向传播的结果

    举例最早的卷积神经网络LeNet(判断图片中的字符),来看看如何使用nn.Module实现

    1)定义网络

    继承nn.Module,并实现它的forward方法,把网络中具有可学习参数的层放在构造函数__init__中。

    如果某一层(如ReLu)不具有可学习的参数,那么可放在构造函数中,也可以不放

    import torch.nn as nn
    import torch.nn.functional as F
    
    class Net(nn.Module):
        def __init__(self):
            #nn.Module子类的函数必须在构造函数中执行父类的构造函数
            #下式等价于nn.Module.__init__(self)
            super(Net, self).__init__() 
            #卷积层1,参数1表示输入图片为单通道,即灰度图像,为1*32*32,6表示输出通道数,即过滤器的通道数,5表示过滤器为5*5
            #所以下一层得到的值为6*28*28,作为输入
            self.conv1 = nn.Conv2d(1, 6, 5)
            #卷积层2
            self.conv2 = nn.Conv2d(6, 16, 5)
            #全连接层,y = Wx + b
            self.fc1 = nn.Linear(16*5*5, 120)
            self.fc2 = nn.Linear(120, 84)        
            self.fc3 = nn.Linear(84, 10) #最后的输出是10,是因为判断的数值分成了10类
        
        def forward(self, x):
            #先conv1卷积->再relu激活->再max_pool池化
            x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) #(2, 2)相当于池化将高度和宽度缩减一半,得到结果为6*14*14
            x = F.max_pool2d(F.relu(self.conv2(x)), 2) #激活后为16*10*10,池化后16*5*5,因为大小是正方形,所以第二个参数可以只有一个值,效果和上面的(2,2)是相同的
            
            #reshape,将图形扁平化,使其由16*5*5的三维立体变成1*(16*5*5)的二维矩阵
            x = x.view(x.size()[0], -1)
            x = F.relu(self.fc1(x))
            x = F.relu(self.fc2(x))
            x = self.fc3(x)
            return x
        
    net = Net()
    print(net)
            

    返回:

    Net(
      (conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
      (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
      (fc1): Linear(in_features=400, out_features=120, bias=True)
      (fc2): Linear(in_features=120, out_features=84, bias=True)
      (fc3): Linear(in_features=84, out_features=10, bias=True)
    )

    只要在nn.Module的子类中定义了forward函数,backward函数就会被自动实现(利用Autograd)

    在forward函数中可以使用任何Variable支持的函数

    上面定义的网络的可学习参数可通过net.Parameters()返回,net,named_parameters()可同时返回可学习的参数及名称

    params = list(net.parameters())
    print(len(params)) #返回10

    如果想要查看参数的名字等信息,可见:

    for name, parameter in net.named_parameters():
        print(name, ':', parameter.size())

    返回:

    conv1.weight : torch.Size([6, 1, 5, 5])
    conv1.bias : torch.Size([6])
    conv2.weight : torch.Size([16, 6, 5, 5])
    conv2.bias : torch.Size([16])
    fc1.weight : torch.Size([120, 400])
    fc1.bias : torch.Size([120])
    fc2.weight : torch.Size([84, 120])
    fc2.bias : torch.Size([84])
    fc3.weight : torch.Size([10, 84])
    fc3.bias : torch.Size([10])

    forward函数的输入和输出都是Variable,只有Variable才具有自动求导功能,Tensor是没有的。所以在输入时,需要把Tensor封装成Variable

    这里nn.Conv2d的输入必须是四维的,即样本数*通道数*高*宽

    input = Variable(t.randn(1, 1, 32, 32)) #即输入是一张灰度的32*32大小的图片
    output = net(input)
    output.size()

    返回:

    torch.Size([1, 10])
    net.zero_grad() #所有参数的梯度清零
    output.backward(Variable(t.ones(1, 10))) #反向传播

    这里的backward()中为什么需要传入参数Variable(t.ones(1, 10))呢?没有传入就会报错:

    RuntimeError: grad can be implicitly created only for scalar outputs

    原因可见:pytorch的backward

    这时候就可以查看参数的梯度了:

    params = list(net.parameters())
    print(params[0].grad)

    返回:

    tensor([[[[ 1.0658e-01, -3.3146e-02,  3.7709e-03, -3.0634e-02,  1.1791e-02],
              [ 3.1975e-02,  4.4254e-02, -1.1230e-02,  8.9412e-02,  7.1382e-02],
              [-5.6357e-02,  9.1797e-05,  1.8966e-02, -4.4844e-03,  5.7771e-02],
              [-1.2467e-03, -6.6853e-02, -6.9389e-02,  1.6258e-02,  7.9231e-02],
              [-3.1562e-02,  2.0557e-02,  2.1569e-02,  8.2486e-02, -1.1457e-01]]],
    
    
            [[[-4.3394e-02, -4.5307e-03,  2.7980e-02,  1.8965e-03,  1.0381e-01],
              [-2.9320e-02,  2.8249e-02, -3.9046e-02,  1.8078e-02, -4.6308e-02],
              [-3.2397e-03, -1.8470e-02, -6.8865e-03,  5.4334e-03, -6.9593e-02],
              [-3.7685e-02, -1.2786e-01, -5.6480e-03, -5.7637e-02, -2.2518e-02],
              [ 6.1855e-03,  2.5202e-02,  3.0603e-02,  2.8058e-02, -6.9573e-02]]],
    
    
            [[[-9.2159e-02,  8.4124e-02, -7.6889e-02, -4.0251e-03, -2.3461e-02],
              [ 1.3258e-01, -4.1542e-02,  1.5377e-02, -2.0014e-02, -1.3080e-02],
              [-1.8869e-02,  7.1884e-02, -5.8952e-02,  2.0899e-02,  1.5558e-02],
              [ 2.7937e-02,  7.8165e-02, -4.8216e-02, -1.6983e-02,  4.5583e-02],
              [-7.2376e-02,  5.4218e-03,  7.5949e-02, -6.5286e-02,  3.4859e-03]]],
    
    
            [[[ 9.8840e-02, -9.1584e-02,  4.7770e-02,  6.2509e-03,  8.5981e-03],
              [ 4.2380e-02,  1.8999e-02,  2.4990e-02,  8.1884e-03, -3.5326e-02],
              [-1.2791e-02,  9.0136e-02,  8.5160e-06,  2.5163e-02,  7.3440e-03],
              [-8.7427e-02,  4.0755e-02, -1.1036e-02,  5.3797e-02,  7.8865e-03],
              [-3.1478e-02,  6.5078e-02,  3.5277e-02, -3.8831e-02, -8.0675e-02]]],
    
    
            [[[-7.8461e-03,  5.7134e-02,  5.0355e-02, -1.2885e-02, -1.3467e-02],
              [-1.3372e-02, -1.9114e-02, -9.7893e-02, -8.6965e-03,  1.4722e-02],
              [ 1.9918e-02,  2.6398e-02,  1.5144e-02,  4.7445e-02,  6.1139e-03],
              [-4.0234e-02, -3.6375e-02,  5.7755e-02, -1.3112e-02, -6.8822e-02],
              [-2.2237e-02, -3.2318e-02,  1.3830e-03, -1.1355e-02, -2.0060e-02]]],
    
    
            [[[-9.9372e-02, -6.5939e-02,  5.9860e-02,  1.4672e-02, -3.6069e-02],
              [ 3.5409e-02, -8.1433e-02,  3.1477e-03, -2.8791e-02,  5.8678e-02],
              [ 3.1001e-02,  5.9960e-02, -7.9541e-02, -1.0646e-01,  1.2691e-02],
              [ 2.8374e-02, -1.0153e-01,  4.3227e-02,  7.5082e-02, -1.0772e-02],
              [-4.9453e-02, -9.9464e-03, -4.2608e-02, -4.2708e-02,  5.5077e-02]]]])

    ⚠️torch.nn只支持mini_batches,不支持一次只输入一个样本,即一次必须是一个batch

    如果只想输入一个样本,则使用input.unsqueeze(0)将batch_size设为1

    4.损失函数

    nn实现了神经网络中大多数的损失函数,例如nn.MSELoss用来计算均方误差,nn.CrossEntropyLoss用来计算交叉熵损失

    output = net(input)
    print(output)
    target = Variable(t.arange(0, 10))
    print(target) criterion
    = nn.MSELoss() loss = criterion(output, target) loss

    运行时遇到下面的错误:

    RuntimeError: Expected object of scalar type Float but got scalar type Long for argument #2 'target'

    解决办法就是转换target类别为float即可:

    output = net(input)
    print(output)
    target = Variable(t.arange(0, 10)).float() #更改
    print(target) criterion
    = nn.MSELoss() loss = criterion(output, target) loss

    返回:

    tensor([[-0.0552, -0.0790,  0.0176, -0.1695, -0.1261, -0.0572,  0.0072, -0.1686,
              0.0739, -0.0895]], grad_fn=<AddmmBackward>)
    tensor([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
    tensor(29.0489, grad_fn=<MseLossBackward>)

    如果对该loss进行反向传播溯源,即使用grad_fn属性

    首先从之前的网络可知其整个计算图为:

    input -> conv2d -> relu -> maxpool2d -> conv2d -> relu -> maxpool2d
          -> view -> linear -> relu -> linear -> relu -> linear
          -> MSELoss
          -> loss

    当我们使用loss.backward()时,该图会动态生成并自动微分,也会自动计算途中参数(parameter)的导数

    net.zero_grad() #把net中所有可学习的参数的梯度清零
    print("反向传播前conv1.bias的梯度:")
    print(net.conv1.bias.grad)
    loss.backward()
    print("反向传播后conv1.bias的梯度:")
    print(net.conv1.bias.grad)

    返回:

    反向传播前conv1.bias的梯度:
    tensor([0., 0., 0., 0., 0., 0.])
    反向传播后conv1.bias的梯度:
    tensor([-0.0503,  0.0551,  0.0052,  0.0081,  0.0084,  0.0665])

    5.优化器

    torch.optim中实现了深度学习中绝大多数的优化方法,例如RMSProp,Adam,SGD等

    import torch.optim as optim
    #新建一个优化器,指定要调整的参数和学习率
    optimizer = optim.SGD(net.parameters(), lr = 0.01)
    
    #在训练的过程中
    #先将梯度清零(和net.zero_grad()效果一样)
    optimizer.zero_grad()
    
    #计算损失
    output = net(input)
    loss = criterion(output, target)
    
    #反向传播
    loss.backward()
    
    #更新参数
    optimizer.step()

    6.数据加载和预处理

    torchvision实现了常用的图像数据加载功能,例如Imagenet,CIFAR10,MNIST等,以及常用的数据转换操作,这极大方便了数据加载

    小试牛刀:CIFAR-10分类

    可见pytorch例子学习——TRAINING A CLASSIFIER

  • 相关阅读:
    CPPFormatLibary提升效率的优化原理
    Unity4、Unity5移动平台多线程渲染在部分安卓手机上会造成闪退
    Hello World!
    Mac/IOS/linux获取当前时间包含微秒毫秒的代码
    插入图块闪烁问题的原因
    选择实体时的选项
    dataGridView 单元格添加combox checkbox
    交互拾取点时 右键取消 禁止出现点无效
    曲线上到指定点最近的点
    移除实体应用程序名(xdata)
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/10609591.html
Copyright © 2011-2022 走看看