基于PyTorch的深度学习--创建卷积神经网络-面向对象的神经网络

本篇文章是翻译:https://deeplizard.com网站中的关于Pytorch学习的文章,供学习使用。
原文地址为:https://deeplizard.com/learn/video/k4jY9L8H89U

使用PyTorch创建神经网络

欢迎回到基于Pytorch的神经网络系列课程。在这一篇文章中,我们将开始创建我们的第一个卷积神经网络(CNN)。废发不多说,现在开始吧。

概述:

在之前的文章中或之前的概述中我们可以了解到,我们准备了数据,现在我们准备创建我们的模型。

  • 准备数据。
  • 创建模型。
  • 训练数据。
  • 分析模型结果。
    当说到模型时,我们指的是网络模型。我们希望我们的网络最终做的是形成一个模型或者近似函数功能的结构,其作用是将图像的输入映射到正确的输出类别。

先决条件

使用PtTorch创建一个神经网络,我们扩展torch.nn.Module类。意思是说我们需要在Python中利用一些面向对象编程的方法。
我们将对面向对象进行快速的复习,以涵盖使用pytorch神经网络所需的详细信息,但是,如果你需要发现更多的信息,请查看python文档
创建卷积神经网络,我们需要对卷积神经网络和组成部分有一个大致的了解。这个深度学习基础系类课程是一个很好的前导课程。如果你还没有深度学习基础的话,强烈建议你先去进行学习。如果你知识想速成卷积神经网络,可以以下特定文章。

  • Convolutional Neural Networks (CNNs) explained
  • Visualizing Convolutional Filters from a CNN
  • Zero Padding in Convolutional Neural Networks explained
  • Max Pooling in Convolutional Neural Networks explained
  • Learnable Parameters in a Convolutional Neural Network (CNN) explained
    让我们来快速复习一下面向对象编程。

面向对象编程

当我们写程序或者编写软件时,一般包含两个关键的东西–代码和数据。在面向对象编程思想中,我们围绕对象定位程序设计和结构。
对象使用类来进行定义。一个类定义一个对象的规范,该规范指定该类的每个对象应具有的数据和代码。
当我们创建一个类的对象时,我们称作这个对象为该类的实例,一个类的所有实例都有两个核心部分:

  • 方法(Methods(code))
  • 属性(Attributes(data))
    方法代表代码,而属性代表数据,所以方法和属性都是由类定义的。
    在给定的程序中,存在许多对象,也就是指定类的实例可以同时存在,所有的实例有许多可用属性和相同的可用方法。从这个角度来说它们是统一的。
    同一个类中的不同对象存在一些差异,即对象中的每一个属性所包含的值可能不同。每个对象有它自己的属性值。这些值决定了对象的内在状态。每个对象的代码和数据的意思就是被封装在对象里的方法和属性。
    让我们为蜥蜴创建一个简单的类来验证类的封装。
class Lizard: #class declaration
    def __init__(self, name): #class constructor (code)
        self.name = name #attribute (data)

    def set_name(self, name): #method declaration (code)
        self.name = name #method implementation (code)

第一行定义了类并且指定了类名-Lizard
第二行定义了一个特殊的函数称为类的构造函数,当一个新的实例被创建时,类的构造函数将被调用。其中存在两个参数即selfname
self参数使我们能够创建一个存储或封装在对象内的属性值。当我们调用构造函数或其他函数时,我们不用传递self参数,Python会自动为我们执行此操作。
调用方可以任意传递任何其他参数的参数值,这些传入方法的值可以在计算中使用,也可以稍后使用self保存和访问。
在完成构造函数之后,我们可以创建许多指定的方法,比如下面代码中表示的允许调用者改变存储在self中的名字(name)的值。我们所需要做的是调用函数并传入一个名字的新值,如下所示:

> lizard = Lizard('deep')
> print(lizard.name)
deep

> lizard.set_name('lizard')
> print(lizard.name)
lizard

我们通过一个指定类名和传递构造函数参数的类创建一个实例对象。这个构造函数将会收到参数,并且构造函数将会运行,保存传入的名字。
从面向对象的角度来看,其重要部分是属性和方法被组织并包含在对象中。
现在,我们来看看面向对象编程如何与PyTorch相适应的。

PyTorch的torch.nn包

使用PyTorch创建神经网络,我们使用PyTorch的神经网络库中的torch.nn数据包,典型的导入方式如下所示:

import torch.nn as nn

我们可以使用nn别名来访问神经网络包。所以从现在开始,我们提到nn,就是在说torch.nn。PyTorch的神经网络库包含所有我们需要的神经网络典型组件。
建立神经网络所需要的主要组件是层(layer),所以,正如我们所期待的那样,PyTorch神经网络库中包含可以帮助我们构建层的类。

PyTorch的nn.Module类

正如我们所知的那样,深度神经网络是由许多的层构成的,这是使神经网络更深的原因。神经网络的每一个层包含两个组成部分:

  • A transformation(code)
  • A collection of weights(data)
    正如生活中的一些事物一样,这一事实使图层成为使用面向对象编程中的对象。
    事实上,在PyTorch的案例中,在nn包中,有一个名为Module的类,它是神经网络模块(包括层)的基类。
    即在PyTorch中的所有层都是扩展了nn.Module类,并且继承nn.Module类中的所有PyTorch的内置功能。在面向对象编程中,此概念被称为继承。
    甚至神经网络都扩展了nn.Module类。这是有道理的,因为神经网络本身可以看作是一个很大的层(如果需要,可以让它随着时间的流逝而沉没)。
    PyTorch中的神经网络和层扩展了nn.Module类,这意味着当我们在使用PyTorch创建新的网络层的时候必须扩展nn.Module

nn.Module的forward()函数

当我们向我们的网络中出入张量时,这个张量向前传递经过每一个层的转换最终到达输出层。张量通过网络向前流动的过程称为前向传递。

张量输入通过网络转发

张量穿过每一个层的时候便会被这个层所转换。所有单个层的组成部分都在进行网络前向传递的转换过程中发挥了一定的作用。
整体转换的目标是将输入转换或映射到正确的预测数据类别,并且在训练过程中,层的权重的更新方式会调整映射以输出更接近正确的预测。
这一切意味着,每个PyTorch的nn.Module都有一个forward()函数,并且当我们建立层和网络时,我们必须提供一个forward()函数的实现。这个forward函数是实际的转换。

PyTorch的nn.functional包

当我们实现我们nn.Module子类中的forward()函数的时候,我们通常使用nn.functional包中的函数。这个包提供给我们许多可用于构建图层的操作。实际上,许多nn.Module层类的许多操作都是使用nn.functional的方法实现的。
nn.functional包中包含nn.Module子类执行它们的forward()函数的方法。最后,我们以一个例子来查看PyTorch中nn.Conv2d的卷积层类的源代码。

使用PyTorch创建一个神经网络

我们现在可以使用PyTorch进行神经网络轮廓的创建,遵循下面的步骤:
精简版:

  1. 扩展nn.Module基类。
  2. 定义层的属性。
  3. 执行forward()方法。

详细版:

  1. 创建一个扩展了nn.Module基类的神经网络类。
  2. 在类的构造函数中使用来自torch.nn的欲构建图层来定义网络层的属性。
  3. 使用网络层属性以及nn.functionalAPI的操作来定义网络的前向传递。

扩展PyTorch的nn.Module类

以之前的Lizard类为例,创建一个简单的类来表示一个神经网络。

class Network:
    def __init__(self):
        self.layer = None

    def forward(self, t):
        t = self.layer(t)
        return t

这个简单的神经网络在构造函数中有一个单一的虚拟层,并且这个虚拟层实现了forward函数。
这个forward()函数中接收一个张量 t,然后使用虚拟层对其进行转换。在这个张量被转换后,返回一个新的张量。
这是一个好的开始,但是这个类并没有扩展nn.Module类,让我们的Network类扩展nn.Module。我们必须做两件事:

  1. 在第一行指定nn.Module类。
  2. 在构造函数的第三行插入一个父类构造函数的回调。
    如下所示:
class Network(nn.Module): # line 1
    def __init__(self):
        super().__init__() # line 3
        self.layer = None

    def forward(self, t):
        t = self.layer(t)
        return t

这个改变将我们简单的神经网络变成了PyTorch神经网络,其原因是我们扩展了Pytorch的nn.Module基类。
现在我们有了一个Network类,它具有PyTorch的nn.Module类的所有功能。

将网络层定义为类属性

现在,我们的Network类有一个单一的虚拟层作为属性。让我们使用PyTorch的nn库欲构建的真实层来代替它。我们创建一个CNN网络,所以我们将会使用线性层和卷积层这两个类型的层。

class Network(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=12, kernel_size=5)

        self.fc1 = nn.Linear(in_features=12 * 4 * 4, out_features=120)
        self.fc2 = nn.Linear(in_features=120, out_features=60)
        self.out = nn.Linear(in_features=60, out_features=10)

    def forward(self, t):
        # implement the forward pass
        return t

现在,我们有一个叫做Network的类,这个类扩展了nn.Module类。在我们的Network类中,我们有五个被定义为属性的层。两个卷积层,self.conv1self.conv2,三个线性层self.fc1self.fc2self.out
因为线性层又叫全连接层(fully connected layers),所以我们使用缩写fc来表示线性层。线性层有时也被叫做dense层。所以线性层、dense和全连接层指的是一个类型的层。PyTorch使用linear这个词表示,所以类名叫做nn.linear
我们使用out表示最后一层,因为最后一层为输出层。

最后

我们现在应该对使用torch.nn库构建神经网络有了一定的了解。在下一章中将介绍两个不同类型的参数并且知道如何选择它们。下次见。

你可能感兴趣的:(Pytorch,pytorch,深度学习)