导航菜单

西凤酒-卷积神经网络,仍是这样了解更简略!PyTorch深度学习入门

互联网开展至今,现已存储了海量的网络图片,可是这些图片被形象地称为互联网的“暗物质”,因为现在的核算机还难以分类或辨认这些非结构性的图片数据。在前期的图画辨认研讨中,运用人工提取的特征构成辨认作用欠安。卷积神经网络(Convolutional Neural Network, CNN)的呈现给图画辨认范畴带来了簇新的习尚,现在,CNN图画辨认技能的正确率现已能够到达人类水平。卷积神经网络的鼓起大大促进了深度学习研讨的开展。

下面咱们来研讨下卷积神经网络的基本原理,为接下来的实战打好根底。

仿生模型

20 世纪60 时代,神经科学家们研讨了猫的脑皮层中用于部分灵敏和方向选择的神经元,在这个进程中,他们发现猫的脑皮层所具有的共同网络结构能够有用地下降反应神经网络的复杂性,研讨成果显现:视觉体系的信息处理是分级的。

大脑分层处理的视觉原理如图1所示:首要,光信号进入瞳孔(视网膜),接着大脑皮层的初级视觉细胞(即V1 区)对信号进行开端处理,发现图画的边际和方向;然后进入下一层视觉细胞(即V2 区)进行笼统,发现物体的形状;终究在V4 区进一步笼统出物体的概念。

图1 人脑视觉处理机制

卷积神经网络模型的物体辨认仿照了人脑的视觉处理机制,选用分级提取特征的原理,每一级的特征均由网络学习提取,辨认作用优于人工选取特征的算法。例如在人脸辨认进程中,最底层特征基本上是各方向上的边际,越往上的神经层越能提取出人脸的部分特征(比方眼睛、嘴巴、鼻子等),最上层由不同的高档特征组合成人脸的图画。该模型最早在1998 年由Yann LeCun 提出并运用在手写字体辨认上(MINST),LeCun 提出的网络称为LeNet,其网络结构如图2所示,输入的手写字体图片通过两次卷积和池化,进入全衔接层后分类输出10 种成果。

图2 LeNet 结构示意图

LeNet 结构简略却完美地诠释了卷积神经网络的结构和其惊人的辨认成效,被称为深度神经网络的“果蝇”。囿于其时核算硬件的落后和网络图片数量的缺乏,卷积神经网络无法在分辨率较大的图片上展现它的巨大潜力,其时并没有引起广泛重视。在2012 年的ImageNet 核算机视觉辨认竞赛中,卷积神经网络AlexNet 以准确率抢先第二名10% 的明显距离获得了榜首名。从此,大批核算机视觉科学家纷繁加入了卷积神经网络的研讨傍边,深度学习的热潮开端鼓起。

卷积

在咱们前面所介绍的神经网络《这是我看过,最好懂的神经网络》中,输入层被描绘为一列神经元,而在卷积神经网络里,咱们把输入层看作二维的神经元。假定输入是像素巨细为2828 的图片①,则能够看作2828 的二维神经元,它的每一个节点对应图片在这个像素点的灰度值,如图3所示。

① PX(Picture Element)便是咱们常说的像素,它是构成印象的最小单位。像素是一个相对的单位,当图片尺度以像素为单位时,咱们需求指定其固定的分辨率,才能将图片尺度与实践中的实践尺度相转化,因而经常会省掉PX和像素,如本句表达为“像素巨细为2828 的图片”或“巨细为2828 的图片”。

图3 输入图片的二维展现

在传统的神经网络中,咱们会把输入层的节点与隐含层的一切节点相连。卷积神经网络中,选用“部分感知”的办法,即不再把输入层的每个节点都衔接到隐含层的每一个神经元节点上。如图4所示,咱们把相邻的55 部分区域内的输入节点衔接到了榜首个隐含层的一个节点上。这个相邻的区域,咱们称为部分感知域,能够把它看成是一个小窗口,咱们也称之为过滤器。

图4 输入层与隐含层的衔接

如图5 所示,咱们能够在整个输入图画上滑动这个过滤器。关于每一个过滤器,都有一个隐含层的神经元与之对应。将过滤器向右滑动一个单位后对应一个隐含层的神经元节点。以此类推,咱们构建出了榜首个隐含层。在这个比如中,输入是2828,而且运用55 的过滤器,那么榜首个隐含层的巨细为2424。因为过滤器的小窗口只能向右和向下移动23 像素,再往下或许往右移动就会移出图画的鸿沟。在这个图中,过滤器运用的滑动单位为1 像素,实践上,咱们也能够让滑动单位不止1 像素,这个滑动值的英文叫stride,咱们也称它为步长。在卷积神经网络中,这种隐含层被称为“特征图”。

图5 过滤器的移动

在进行卷积操作之前,需求界说一个过滤器,其间的每一格都有一个权重值。卷积的进程是将每个格子中的权重值与图片对应的像素值相乘并累加,所得到的值便是特征图中的一个像素值。如图6 所示,咱们对过滤器和卷积进行一个比较详细而详细的阐明。假定咱们输入的是55 的图片,过滤器能够看作一个小窗口,这个小窗口巨细是33,里边包含了9 个权重值,咱们将9 个权重值别离与输入的一部分像素值相乘后进行累加,这个进程被称为“卷积”。图中小窗口掩盖的输入区域卷积成果是隐含层的灰色部分,成果为2。隐含层的成果便是咱们通过卷积生成的特征图。

图6 过滤器的卷积进程

此外,咱们发现,即使步长为1,通过卷积之后的特征图尺度也会缩小。因为过滤器在移动到边际的时分就完毕了,中心的像素点比边际的像素点参加核算的次数要多。因而越是边际的点,对输出的影响就越小,咱们就有或许丢掉边际信息。为了处理这个问题,咱们进行填充,英文叫padding,即在图片外围弥补一些像素点,并将这些像素点的值初始化为0。

为什么卷积

在传统全衔接的神经网络中,假定要对一张图片进行分类,衔接办法如图7所示。咱们把一张巨细为100100 的图片的每个像素点都衔接到每一个隐含层的节点上,假定隐含层的节点数为10 000,那么衔接的权重总数则为10的8次方个。当图片像素更大,隐含层的节点数目更多时,则需求愈加巨大的权重数目。

图7 全衔接神经网络的权重数目

在卷积神经网络中,咱们不再需求如此巨大的权重数目。如图8 所示,在运用1010 的过滤器对100100 的原图进行卷积时,该过滤器在不断滑动的进程中对应生成一张特征图,即一个过滤器(100 个权重值)可对应一张特征图。假定咱们有100 张特征图,则总共只需求10的4次方个权重值。

如此一来,在一个西凤酒-卷积神经网络,仍是这样了解更简略!PyTorch深度学习入门隐含层的状况下,卷积神经网络的权重数目能够减小至全衔接神经网络权重数意图一万分之一,大大削减核算量,进步核算功率。

图8 卷积神经网络的权重数目

在实践练习的进程中,榜首层的每一个过滤器的权重值会不断地被更新优化,终究构成如图3-40所示的成果,每个过滤器的可视化纹路办法基本上反映了各个方向上的边际特征。图9 为咱们展现了24 个过滤器的纹路办法,这24 种边际能够描绘出咱们的原图。

图9 过滤器的可视化

池化

池化的意图是下降数据的维度,进程很简略,实践上便是下采样。详细进程如图10 所示,假定特征图的尺度是88,池化的窗口为44,则对特征图依照每44 进行一次采样,生成一个池化特征值。这样一来,88 的特征图能够生成一个22 的池化特征图。

图10 池化进程

在实践运用中,生成池化特征的办法一般有两种:最大值池化(Max-Pooling)与均匀值池化(Mean-Pooling)。其间,最大值池化的办法是将特征图中池化窗口范围内的最大值作为池化成果的特征值,进程如图11所示;均匀值池化的办法是将特征图中池化窗口范围内的一切值进行均匀后作为池化的特征值。

图11 最大池化

实战:用PyTorch 构建卷积神经网络

下面,咱们测验运用PyTorch 构建卷积神经网络。这儿以LeNet 网络为例,运用MINST 手写字体库进行练习,完结一个手写体的主动辨认器。

1. LeNet——深度学习界的“果蝇”

LeNet是1998年由Yann LeCun提出的一种卷积神经网络,其时现已被美国大多数银行用于辨认支票上的手写数字。详细资料能够阅览LeNet-5官网http://yann.lecun.com/exdb/lenet/,图12是LeNet进行手写体辨认时,输入图片、各层特征图的可视化办法及其终究的辨认成果。

图12 LeNet 进行手写体辨认

LeNet-5 的结构如图2所示:

其网络结构比较简略,假定不包含输入,它总共有7 层,输入图画的巨细为2828。通过上面的学习,咱们能够分辨出C1 层为6 张特征图,由6 个巨细为55 的卷积核过滤生成,特征图的尺度为2424。S2 是池化层,选用最大池化法,池化窗口巨细为22。因而,6 张2424 的特征图池化后会生成6 张1212 的池化特征图。C3 层是卷积层,总共有西凤酒-卷积神经网络,仍是这样了解更简略!PyTorch深度学习入门16 个过滤器,生成16 张88 巨细的特征图,卷积核巨细为55。这儿,S2 与C3 的衔接组合办法并不是固定的,C3层的每一张特征图能够衔接S2 层中的悉数或许部分特征图。一般状况下,为了更好地下降总衔接数,并不运用悉数特征图的全衔接办法。S4 是池化层,它是由16 张88 的特征图最大池化生成的16 张44的特征图,其池化核巨细为22。F5 是全衔接层,总共有120 个神经元节点,每个节点与S4 层的16张池化特征图进行衔接。因而,F5 层与S4 层是全衔接。F6 层有84 个神经元节点,与F5 进行全衔接。终究一层为输出层,把输出的神经元节点数设为10,代表0 到9 的分值。

2. 预备数据集

MNIST 是一个手写数字数据库(官网地址:http://yann.lecun.com/exdb/mnist/)。如图13 所示,该数据库有60 000 张练习样本和10 000 张测验样本,每张图的像素尺度为2828。其间train-imagesidx3-ubyte.gz 为练习样本集,train-labels-idx1-ubyte.gz 为练习样本的标签集,t10k-images-idx3-ubyte.gz为测验样本集,t10k-labels-idx1-ubyte.gz 为测验样本的标签集。这些图片文件均被保存为二进制格局。

图13 MNIST 官网

咱们现在需求用到上述这些数据集。走运的是,PyTorch 为咱们编写了快速下载并加载MNIST 数据集的办法。为了便利图画的运用开发,PyTorch 团队为咱们专门编写了处理图画的东西包:torchvision。

torchvision里边包含了图画的预处理、加载等办法,还包含了数种通过预练习的经典卷积神经网络模型。

下面用一个比如给咱们介绍。首要咱们从torchvision库中导入datasets和transforms,datasets是加载图画数据的办法,transforms是图画数据预处理的办法:

from torchvision import datasets, transforms

然后咱们运用transforms.Compose()函数设置预处理的办法:

transform = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])

这儿可顺次填写需求进行数据预处理的办法。这个比如中,咱们只用了两种办法,其间transforms.ToTensor()是将数据转化为Tensor方针,transforms.Normalize()是将数据进行归一化处理。

接下来咱们运用datasets.MNIST()函数别离下载练习数据集和测验数据集:

trainset = datasets.MNIST('data', train=True, download=True, transform=transform)
testset = datasets.MNIST('data', train=False, download=True, transform=transform)

data.MNIST的榜首个参数指定了数据集下载并存储的方针文件夹;train=True表明加载练习数据集,train=False表明加载测验数据集;这儿咱们令download=True,代表运用这个函数协助咱们主动下载MNIST数据集;transform的设置代表咱们运用方才界说的数据预处理办法。

接下来,运转上述的代码,程序就会主动下载MNIST数据集:

Downloading http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-images-idx3-ubyte.gz
Downloading http://yann.lecun.com/exdb/mnist/t10k-labels-idx1-ubyte.gz
Processing...
Done!

显现以上信息后,如图14所示,咱们会发现代码地点目录下新增了一个data文件夹,里边包含了processed和raw文件夹,其间raw文件夹里边包含了MNIST数据集的练习和测验的数据与标签。

图14 主动下载MNIST数据集

3. 构建LeNet

预备好数据之后,咱们能够开端构建LeNet模型。咱们知道,初始化函数需求先运转父类的初始化函数。先界说C1卷积层,PyTorch中的nn.Conv2d()函数为咱们简化了卷积层的构建。在C1层的界说中,榜首个参数为1,代表输入1张灰度图;第二个参数为6,代表输出6张特征图;第三个参数是一个元组 (5, 5),也能够简化成5,代表巨细为55的卷积核过滤器。然后界说C3卷积层,C3输入6张特征图,输出16张特征图,过滤器巨细仍为55。接着界说全衔接层,其间fc1是由池化层S4中的一切特征点(共1644个)全衔接到120个点,fc2是由120个点全衔接到84个点,fc3是由84个点全衔接到输出层中的10个输出节点。

界说完初始函数之后,咱们开端界说foward()函数。c1卷积之后,运用relu()函数增加了网络的非线性拟合才能,接着运用F.max_pool2d()函数对c1的特征图进行池化,池化核巨细为22,也能够简化参数为2。通过两轮卷积和池化之后,运用view函数将x的形状转化成1维的向量,其间咱们自界说了num_flat_features()函数来核算x的特征点的总数。在自界说的num_flat_features()函数中,因为PyTorch只承受批数据输入的办法(即一起输入好几张图片进行处理),所以咱们在通过view()函数之前的x是4个维度的。假定咱们批量输入4张图片,则x.size()的成果为(4,16,4,4)。咱们运用x.size()[1:]回来x的第二维今后的形状,即(16,4,4)。因而,依照num_flat_features()函数回来的数值为1644,即256,随后进行全衔接fc1、fc2和fc3:

class LeNet(nn.Module):
def __init__(self):
super(LeNet,self).__init__()
self.c1 = nn.Conv2d(1,6,(5,5))
self.c3 = nn.Conv2d(6,16,5)
self.fc1 = nn.Linear(16*4*4,120)
self.fc2 = nn.Linear(120,84)
self.fc3 = nn.Linear(84,10)
def forward(self,x):
x = F.max_pool2d(F.relu(self.c1(x)),2)
x = F.max_pool2d(F.relu(self.c3(x)),2)
x = x.view(-1,self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x

def num_flat_features(self,x):
size = x.size()[1:]
num_features = 1
for s in size:
num_features *= s
return num_features

接下来咱们初始化LeNet,并界说丢失函数为穿插熵函数,优化器为随机梯度下降:

CUDA = torch.cuda.is_available()
if CUDA:
lenet = LeNet().cuda()
else:
lenet = LeNet()
criterion=nn.CrossEntropyLoss()
optimizer = optim.SGD(lenet.parameters(),lr=0.001,momentum=0.9)

随后,用PyTorch的数据加载东西DataLoader来加载练习数据:

trainloader = torch.utils.data.DataLoader(trainset,batch_size=4, shuffle=True, 
num_workers=2)

上面DataLoader()的参数中,batch_size表明一次性加载的数据量,shuffle=True表明遍历不同批次的数据时打乱次序,num_workers=2表明运用两个子进程加载数据。

4. 练习

现在开端练习LeNet,咱们将彻底遍历练习数据2次。为了便利调查练习进程中丢失值loss的改变状况,界说变量running_loss。一开端,咱们将running_loss设为0.0,随后对输入每一个练习数据后的loss值进行累加,每练习1000次打印一次loss均值,并清零。enumerate(trainloader,0)表明从第0项开端对trainloader中的数据进行枚举,回来的i是序号,data是咱们需求的数据,其间包含了练习数据和标签。随后咱们进行前向传达和反向传达。代码如下:

def train(model,criterion,optimizer,epochs=1):
for epoch in range(epochs):
running_loss = 0.0
for i, data in enumerate(trainloader,0):
inputs,labels = data
if CUDA:
inputs,labels = inputs.cuda(),labels.cuda()
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs,labels)
loss.backward()
optimizer.step()

running_loss += loss.item()
if i%1000==999:
print('[Epoch:%d, Batch:%5d] Loss: %.3f' % (epoch+1, i+1,
running_loss / 1000))
running_loss = 0.0

print('Finished Training')

train(lenet,criterion,optimizer,epochs=2)

运转成果如下:

[Epoch:1, Batch: 1000] loss: 1.336
[Epoch:1, Batch: 2000] loss: 0.293
[Epoch:1, Batch: 3000] loss: 0.213
[Epoch:1, Batch: 4000] loss: 0.157
[Epoch:1, Batch: 5000] loss: 0.144
[Epoch:1, Batch: 6000] loss: 0.120
[Epoch:1, Batch: 7000] loss: 0.109
[Epoch:1, Batch: 8000] loss: 0.106
[Epoch:1, Batch: 9000] loss: 0.117
[Epoch:1, Batch:10000] loss: 0.098
[Epoch:1, Batch:11000] loss: 0.072
[Epoch:1, Batch:12000] loss: 0.095
[Epoch:1, Batch:13000] loss: 0.084
[Epoch:1, Batch:14000] loss: 0.073
[Epoch:1, Batch:15000] loss: 0.079
[Epoch:2, Batch: 1000] loss: 0.065
[Epoch:2, Batch: 2000] loss: 0.065
[Epoch:2, Batch: 3000] loss: 0.072
[Epoch:2, Batch: 4000] loss: 0.070
[Epoch:2, Batch: 5000] loss: 0.055
[Epoch:2, Batch: 6000] loss: 0.047
[Epoch:2, Batch: 7000] loss: 0.058
[Epoch:2, Batch: 8000] loss: 0.051
[Epoch:2, Batch: 9000] loss: 0.066
[Epoch:2, Batch:10000] loss: 0.047
[Epoch:2, Batch:11000] loss: 0.066
[Epoch:2, Batch:12000] loss: 0.059
[Epoch:2, Batch:13000] loss: 0.054
[Epoch:2, Batch:14000] loss: 0.058
[Epoch:2, Batch:15000] loss: 0.062
Finished Training

从成果中能够发现,咱们的练习是有用的,丢失值loss的均匀值从1.336逐步优化并下降为0.062。

5. 存储与加载

咱们练习完神经网络之后,需求存储练习好的参数,以便利今后运用。存储的办法有两种。

(1) 存储和加载模型

存储:

torch.save(lenet, 'model.pkl')

加载:

lenet = torch.load('model.pkl')

运用torch.save()函数直接传入整个网络模型lenet,并设置存储的途径。这种办法尽管简练,但在神经网络比较复杂的时分,会占用较大的存储空间。

(2) 存储和加载模型参数

存储:

torch.save(lenet.state_dict(),'model.pkl')

加载:

lenet.load_state_dict(torch.load('model.pkl'))

这种办法直接保存模型参数,节省了空间,可是它不存储模型的结构,所以在加载时需求先结构好模型结构。

在这个比如中,咱们倾向于运用第二种办法,运用os包的exists()办法查看是否存在模型参数文件。结构load_param()和save_param()函数便利随时调用:

def load_param(model,path):
if os.path.exists(path):
model.load_state_dict(torch.load(path))
def save_param(model,path):
torch.save(model.state_dict(),path)

6. 测验

神经网络模型通过长期的练习之后,能在练习集的数据上体现得很好,并不一定代表它在练习集以外的数据上相同体现优异。为了愈加客观地衡量神经网络模型的辨认率,咱们一般需求别的一批数据进行测验。为此,设置train=False预备测验集,并设置testloader对测验集进行加载:

testset = datasets.MNIST('data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2)

下面咱们对练习完结的神经网络增加测验模块:

def test(testloader,model):
correct = 0
total = 0
for data in testloader:
images, labels = data
if CUDA:
images = images.cuda()
labels = labels.cuda()
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum()
print('Accuracy on the test set: %d %%' % (100 * correct / total))

咱们创立一个test()函数,传入testloader和model方针,让testloader中的测验数据通过神经网络,得到outputs,再运用torch.max(outputs.data,1)找出每一组10个输出值中最大的那个值,并将该值地点的序号保存在predicted变量中。total用于累计labels的总数,correct用于累计正确的成果总数:

load_param(lenet,'model.pkl')
train(lenet,criterion,optimizer,epochs=2)
save_param(lenet,'model.pkl')
test(testloader,lenet)

能够在练习前加载之前练习完结的参数,练习后对参数进行保存,接着进行测验,测验成果如下:

Accuracy on the test set: 98 %

fastai库

PyTorch的生态圈非常丰富,AllenNLP、ELP、fastai、Glow、GPyTorch、Horovod、ParlAI、Pyro、TensorLy等库都是根据PyTorch开发的AI东西,能够协助咱们愈加快速地开发AI产品。fastai库(https://www.fast.ai)致力于运用当时最先进的实践办法去简化传统的神经网络练习办法,进步练习速度。fastai能够协助程序员愈加高效地练习神经网络,用它来完结MNIST手写字体辨认只需求短短7行代码。假定体系装置的PyTorch版别在1.0以上,那么能够直接装置并运用fastai 1.0版别。

1. 装置

运用conda指令进行装置:

conda install -c fastai fastai

咱们也能够运用pip指令进行装置:

pip install fastai

2. 代码实践

导入fastai中的vision包:

from fastai.vision import *

运用untar_data()函数,传入参数URLs.MNIST_SAMPLE,下载并解压MNIST数据包:

path = untar_data(URLs.MNIST_SAMPLE)

创立一个DataBunch:

data = ImageDataBunch.from_folder(path)

初始化一个Learner,传入数据data,设置模型为resnet18。调用fit()函数开端练习,fit()函数的参数为练习次数,这儿仅练习1次:

learner = create_cnn(data, models.resnet18, metrics=accuracy)
learner.fit(1)

练习和测验完结后,打印如下:

epoch train loss valid loss accuracy
0 0.078617 0.041789 0.984789

除了图片辨认之外,fastai还供给了自然言语处理、协同过滤以及结构数据处理等运用,因为篇幅有限,本文不再详尽解说。

——

本文咱们介绍了卷积神经网络的基本原理,用当下很盛行的深度学习结构PyTorch构建了一个卷积神经网络。终究,咱们还了解了fastai,能够用短短几行代码构建深度卷积网络,完结了辨认手科学上网什么意思写字体的小试验。

——本文节选自《PyTorch深度学习入门》

这是文章阅览量10万+的作者倾力打造的一份超简略西凤酒-卷积神经网络,仍是这样了解更简略!PyTorch深度学习入门PyTorch入门教程。关于想要了解深度学习的人的榜首选择。

它更适合小白的思路与解说办法:从硬件选择、体系配置开端,图文并茂,手把手教你建立神经网络。

书中从怎么选择硬件到神经网络的开端建立,再到完结图片辨认、文本翻译、强化学习、生成对立网络等多个现在最盛行的深度学习运用。书中根据现在盛行的PyTorch结构,运用Python言语完结了各种深度学习的运用程序,让理论和实践紧密结合。

榜首部分 根底篇

第1章 预备工作

第2章 Tensor根底

第3章 深度学习根底

第二部分 实战篇

第4章 搬迁学习

第5章 序列转序列模型

第6章 生成对立网络

第7章 深度强化学习

第8章 风格搬迁

第三部分 高档篇

第9章 PyTorch扩展

第10章 PyTorch模型搬迁

第11章 PyTorch可视化

第12章 PyTorch的并行核算

二维码