从深度学习项目的高角度或鸟瞰的角度来看,我们准备了数据,现在,我们准备构建模型。
准备数据
建立模型
训练模型
分析模型的结果
说模型,是指我们的网络。“模型”
和“网络”
一词含义相同。 我们希望网络最终要做的是对将图像输入映射到正确的输出类别的函数进行建模或近似。
为了在PyTorch中构建神经网络,我们扩展了torch.nn.Module
PyTorch类。这意味着我们需要在Python中利用一些面向对象的编程(object oriented programming: OOP)。
我们将在这篇文章中对OOP
进行快速回顾,以介绍使用PyTorch神经网络所需的详细信息,但是如果您发现需要更多的信息,Python文档将在此处提供概述教程。
要构建卷积神经网络,我们需要对CNN的工作原理以及用于构建CNN的组件有一个大致的了解。这个深度学习基础知识系列是该系列的一个很好的先决条件,因此,我强烈建议您(如果尚未)涵盖该系列。如果您只想使用CNN速成班,可以查看以下特定文章:
现在让我们快速进行面向对象的编程回顾。
当我们编写程序或构建软件时,有两个关键部分,即代码
和数据
。通过面向对象的编程,我们可以围绕对象来定向程序设计和结构。
使用类在代码中定义对象。一个类定义对象的规范或规范,该规范或规范指定该类的每个对象应具有的数据和代码。
当创建类的对象时,我们将该对象称为类的实例,并且给定类的所有实例都具有两个核心组件:
方法(代码)
属性(数据)
方法代表代码,而属性代表数据,因此方法和属性由类
定义。
在给定程序中,许多对象
(即给定类的实例)可以同时存在,并且所有实例将具有相同的可用属性
和相同的可用方法。从这个角度来看,它们是统一的。
相同类的对象
之间的差异是对象中每个属性所包含的值。每个对象都有其自己的属性值
。这些值确定对象的内部状态。据说每个对象的代码和数据都封装在对象内。
让我们构建一个简单的蜥蜴类
,以演示类如何封装数据和代码:
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
。
第二行定义了一个称为类构造函数的特殊方法。创建类的新实例时,将调用类构造函数。作为参数,我们具有self
和name
。
self参数使我们能够创建存储或封装在对象内的属性值。当我们调用此构造函数或任何其他方法时,我们不会传递self
参数。 Python会自动为我们执行此操作。
调用者可以任意传递任何其他参数的参数值,这些传入方法的值可以在计算中使用,也可以在以后使用self保存和访问。
构造函数完成后,我们可以在此处创建任意数量的专用方法,例如这种方法,它允许调用者更改存储在self
中的名称值。我们在这里要做的就是调用方法并为该名称传递一个新值。让我们看看实际情况。
> lizard = Lizard('deep')
> print(lizard.name)
deep
> lizard.set_name('lizard')
> print(lizard.name)
lizard
我们通过指定类名并传递构造函数参数来创建该类的对象实例。构造函数将接收这些参数,并且构造函数代码将运行以保存传递的名称。
然后,我们可以访问名称并进行打印,还可以调用set_name()
方法来更改名称。一个程序中可以存在多个这些Lizard
实例,每个实例将包含自己的数据。
从面向对象的角度来看,关于此设置的重要部分是属性和方法被组织并包含在对象中。
现在让我们切换一下档,看看面向对象的编程如何与PyTorch配合。
要在PyTorch中构建神经网络,我们使用torch.nn
包,这是PyTorch的神经网络(nn)
库。 我们通常像这样导入包:
import torch.nn as nn
这使我们可以使用nn
别名访问神经网络包。 因此,从现在开始,如果我们说nn
,则表示torch.nn
。 PyTorch的神经网络库包含构建神经网络所需的所有典型组件。
我们需要构建神经网络的主要组件是一个层,因此,正如我们可能期望的那样,PyTorch的神经网络库包含有助于我们构建层的类。
众所周知,深度神经网络是使用多层构建的。这就是使网络更深的原因。神经网络的每一层都有两个主要组成部分:
转换(代码)
权重集合(数据)
就像生活中的许多事物一样,这一事实使图层成为使用OOP
表示为对象的理想对象。 OOP
是面向对象编程的缩写。
实际上,PyTorch就是这种情况。在nn
包中,有一个名为Module
的类,它是所有神经网络模块(包括层)的基类。
这意味着PyTorch中的所有层都扩展了nn.Module
类,并继承了nn.Module
类中所有PyTorch的内置功能。在OOP
中,此概念称为继承。
甚至神经网络都扩展了nn.Module
类。这是有道理的,因为神经网络本身可以看作是一个很大的层次(如果需要,可以让它随着时间的流逝而沉没)。
PyTorch中的神经网络和图层扩展了nn.Module
类。这意味着在PyTorch中构建新层或神经网络时,必须扩展nn.Module
类。
nn.Modules
Have A forward()
Method当我们将张量作为输入传递到我们的网络时,张量会通过各层变换向前流动,直到张量到达输出层为止。张量通过网络向前流动的过程称为前向通过。
张量输入通过网络转发。
每一层都有自己的变换(代码),并且张量向前穿过每一层。所有单个层前向通过的组成定义了网络的整体前向变换。
整体转换的目标是将输入转换或映射到正确的预测输出类,并且在训练过程中,以使映射调整以使输出更接近的方式更新图层权重(数据)正确的预测。
这一切意味着,每个PyTorch nn.Module
都有一个forward()
方法,因此,当我们构建层和网络时,我们必须提供forward()
方法的实现。正向方法是实际的转换。
nn.functional
Package当我们实现nn.Module子类的forward()
方法时,通常将使用nn.functional
包中的函数。 该软件包为我们提供了许多可用于构建图层的神经网络操作。 实际上,许多nn.Module
层类都使用nn.functional
函数来执行其操作。
nn.functional
软件包包含nn.Module
子类用于实现其forward()
函数的方法。 稍后,通过查看nn.Conv2d
卷积层类的PyTorch源代码,我们看到了一个示例。
现在,我们有足够的信息来提供在PyTorch中构建神经网络的概述。 步骤如下:
简洁版本:
扩展nn.Module基类。
将图层定义为类属性。
实现forward()方法。
更详细的版本:
创建一个扩展nn.Module基类的神经网络类。
在类构造函数中,使用torch.nn中的预构建图层将网络的图层定义为类属性。
使用网络的图层属性以及nn.functional API的操作来定义网络的前向传递。
nn.Module
Class就像我们在蜥蜴类示例中所做的一样,让我们创建一个简单的类来表示神经网络。
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类。
在构造函数内部的第3行上插入对超类构造函数的调用。
这给我们:
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
的Python类,该类扩展了PyTorch的nn.Module
类。在Network类
内部,我们有五个定义为属性的层。我们有两个卷积层,self.conv1
和self.conv2
,以及三个线性层,self.fc1,self.fc2,self.out
。
我们在fc1
和fc2
中使用了缩写fc
,因为线性层也称为全连接层
。他们还有一个我们可能会听到的第三个名字,有时叫做密集。因此,线性,密集和完全连接是指同一类型的层的所有方法。 PyTorch使用线性这个词,因此使用nn.Linear
类名。
我们将名称out
用作最后一个线性层
,因为网络中的最后一层是输出层。
现在,我们应该对如何使用torch.nn
库开始在PyTorch中构建神经网络有一个好主意。 在下一篇文章中,我们将研究图层参数的不同类型,并了解如何选择它们。 下一个见。