0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何在Python中从头构建CNN?

Dbwd_Imgtec 来源:YXQ 2019-08-08 11:07 次阅读

这是一篇关于CNN(卷积神经网络)的简单指南,本文将介绍CNN如何工作,以及如何在Python中从头开始构建一个CNN。

在过去的几年中,有很多关于卷积神经网络(CNN)的讨论,尤其是因为它们已经彻底改变了计算机视觉领域。在这篇文章中,我们将基于神经网络的基本背景知识,探索CNN是什么,理解它们是如何工作的,并使用Python中的numpy从头开始构建一个真正的卷积神经网络。

本文假设读者有一定的神经网络的基本知识。如果你想要了解一些关于神经网络的知识,你可以读一下我的关于对神经网络的介绍。

1、动机

CNN的经典用例是执行图像分类,例如查看宠物的图像并确定它是猫还是狗。这是一项看似非常简单的任务,你可能会有这样的疑惑:为什么不使用普通的神经网络呢?不得不说这是一个好问题。

原因1:图像很大

目前用于计算机视觉问题的图像通常为224x224甚至更大。想象一下,构建一个神经网络来处理224x224彩色图像:包括图像中的3个颜色通道(RGB),即224x224x3=150528个输入权重!这种网络中的典型隐藏层可能有1024个节点,因此我们必须仅为第一层训练150528x1024=15亿个权重。想象一下拥有15亿个权重的神经网络,是不是太大了?这几乎是不可能完成训练的。

最重要的是其实我们并不需要那么多的权重,相反我们仅知道像素点其邻居的点才是最有用的。因为图像中的物体是由小的局部特征组成的,如圆形虹膜或一张纸的方角。对于第一个隐藏层中的每个节点来说,查看每个像素似乎是很浪费的!

原因2:位置可变

如果你训练了一个网络来检测狗,那么无论图像出现在哪张照片中,你都希望它能够检测到狗。想象一下,训练一个在某个狗图像上运行良好的网络,然后为它提供相同图像的略微移位版本,此时的网络会有完全不同的反应!

那么CNN是如何帮助我们解决这些问题的呢?不要着急我们很快就会看到CNN如何帮助我们缓解这些问题!

2、数据集

在这篇文章中,我们将解决计算机视觉的“Hello,World!”:MNIST手写数字分类问题。很简单:给定图像,将其分类为数字。

来自MNIST数据集的样本图像

MNIST数据集中的每个图像都是28x28,其中包含了一个居中的灰度数字。说实话,一个正常的神经网络实际上可以很好地解决这个问题。你可以将每个图像视为28x28=784维矢量,将其输入到784-dim图层,堆叠一些隐藏图层,最后输出10个节点的输出图层,每个数字1个。

这样做可以完成任务,因为MNIST数据集包含的都是些居中的小图像,因此我们不会遇到上述的大小或移位问题。但是,请记住,大多数现实世界的图像分类问题并不容易。

那么就让我们进入CNN吧!

3、卷积

什么是卷积神经网络?

它们基本上是使用卷积层的神经网络,即Conv层,它们基于卷积的数学运算。Conv图层由一组过滤器组成,你可以将其视为2d数字矩阵。这是一个示例3x3过滤器:

一个3x3过滤器

我们可以通过将滤波器与输入图像进行卷积来产生输出图像。

这包括:

在某个位置覆盖图像顶部的过滤器;

在过滤器中的值与图像中的相应值之间执行逐元素乘法;

所有元素(element-wise products)求和,此和是输出图像中目标像素的输出值。

重复所有位置。

注释:实际上我们(以及许多CNN实现)在技术上使用互相关(Cross-correlation)而不是卷积,但它们几乎完全相同。

这4步描述有点抽象,所以让我们举个例子吧,考虑这个微小的4x4灰度图像和这个3x3过滤器:

4x4图像(左)和3x3滤镜(右)

图像中的数字表示像素强度,其中0是黑色,255是白色。我们将对输入图像和过滤器进行卷积以生成2x2输出图像:

2x2输出图像

首先,让我们将过滤器叠加在图片的左上角:

第1步:将过滤器(右)叠加在图像上方(左)

接下来,我们在重叠图像值和过滤器值之间执行逐元素乘法。以下是结果,从左上角开始向右,然后向下:

第2步:执行逐元素乘法。

接下来,我们总结所有结果。这很容易:62-33=29。

最后,我们将结果放在输出图像的目标像素中。由于我们的过滤器覆盖在输入图像的左上角,因此我们的目标像素是输出图像的左上角像素:

我们做同样的步骤来生成输出图像的其余部分:

3.1 这有用吗?

用过滤器卷积图像有什么作用?我们可以先使用我们一直使用的示例3x3滤波器,这通常被称为垂直索贝尔滤波器:

垂直索贝尔滤波器

以下是垂直Sobel滤波器的示例:

垂直Sobel滤波器卷积的图像

同样,还有一个水平Sobel滤波器:

水平Sobel滤波器

水平Sobel滤波器卷积的图像

到底发生了什么?Sobel滤波器是边缘检测器。垂直Sobel滤波器检测的是垂直边缘,水平Sobel滤波器的是检测水平边缘。现在可以轻松解释输出图像:输出图像中的亮像素(具有高值的像素)表示原始图像中存在强边缘。

你能看出为什么边缘检测图像可能比原始图像更有用吗?回想一下我们的MNIST手写数字分类问题。在MNIST上训练的CNN可以寻找数字1,例如,通过使用边缘检测滤波器并检查图像中心附近的两个突出的垂直边缘。通常,卷积有助于我们查找特定的本地化图像特征。

3.2 填充(Padding)

还记得先用3x3过滤器对4x4的输入图像进行卷积,以产生2x2输出图像吗?通常,我们希望输出图像的大小与输入图像的大小相同。为此,我们在图像周围添加零,以便我们可以在更多位置叠加过滤器。3x3滤镜需要1个像素的填充:

4x4输入与3x3滤波器卷积,使用填充以产生4x4输出

这称为“相同”填充,因为输入和输出具有相同的尺寸。不使用任何填充,这是我们一直在做的,有时也被称为“有效”填充。

3.3 Conv图层

既然我们知道图像卷积是如何工作的以及为什么用它,那让我们看看它是如何在CNN中实际使用的。如前所述,CNN包括使用一组过滤器将输入图像转换为输出图像的conv layer。conv层的主要参数是它具有的过滤器数量。

对于我们的MNIST CNN,我们将使用一个带有8个过滤器的小conv layer作为我们网络中的初始层。这意味着它会将28x28输入图像转换为26x26x8输出向量:

提醒:输出为26x26x8而不是28x28x8,因为我们使用有效填充,这会将输入的宽度和高度减少2。

conv layer中的4个过滤器中的每一个都会产生26x26输出,因此堆叠在一起它们构成26x26x8的向量。

3.4 执行卷积

是时候将我们学到的东西放到代码中了!我们将实现一个conv layer的前馈部分,该部分负责处理带有输入图像的过滤器,以产生输出向量。为简单起见,我们假设过滤器是3x3(其实5x5和7x7过滤器也很常见)。

让我们开始实现一个conv layer类:

importnumpyasnpclassConv3x3:#AConvolutionlayerusing3x3filters.def__init__(self,num_filters):self.num_filters=num_filters#filtersisa3darraywithdimensions(num_filters,3,3)#Wedivideby9toreducethevarianceofourinitialvaluesself.filters=np.random.randn(num_filters,3,3)/9

该类只有一个参数:过滤器的数量。在构造函数中,我们存储过滤器的数量并使用NumPy的randn()方法初始化随机过滤器数组。

注意:在初始化期间初始值很重要,如果初始值太大或太小,则训练网络将无效。

要了解更多信息,请阅读Xavier Initialization:https://www.quora.com/What-is-an-intuitive-explanation-of-the-Xavier-Initialization-for-Deep-Neural-Networks

接下来,实际卷积:

classConv3x3:#...defiterate_regions(self,image):'''Generatesallpossible3x3imageregionsusingvalidpadding.-imageisa2dnumpyarray'''h,w=image.shapeforiinrange(h-2):forjinrange(w-2):im_region=image[i:(i+3),j:(j+3)]yieldim_region,i,jdefforward(self,input):'''Performsaforwardpassoftheconvlayerusingthegiveninput.Returnsa3dnumpyarraywithdimensions(h,w,num_filters).-inputisa2dnumpyarray'''h,w=input.shapeoutput=np.zeros((h-2,w-2,self.num_filters))forim_region,i,jinself.iterate_regions(input):output[i,j]=np.sum(im_region*self.filters,axis=(1,2))returnoutput

iterate_regions()是一个辅助生成器方法,为我们生成所有有效的3x3图像区域,这对于稍后实现此类的后向部分非常有用。

上面是实际执行卷积的代码行。让我们分解一下:

im_region:一个包含相关图像区域的3x3阵列。

self.filters:一个3d数组。

im_region*self.filtersself.filters:我们使用numpy的广播(broadcasting)功能以元素方式乘以两个数组,结果是具有相同尺寸的3d数组。

axis=(1,2)num_filters:我们使用np.sum()上一步的结果,产生一个长度为1d的数组,其中每个元素包含相应过滤器的卷积结果。

我们将结果分配给output[i,j],其中包含输出中像素的卷积结果(i,j)。

对输出中的每个像素执行上面的序列,直到我们获得我们想要的结果!让我们的代码进行测试运行:

importmnistfromconvimportConv3x3#ThemnistpackagehandlestheMNISTdatasetforus!#Learnmoreathttps://github.com/datapythonista/mnisttrain_images=mnist.train_images()train_labels=mnist.train_labels()conv=Conv3x3(8)output=conv.forward(train_images[0])print(output.shape)#(26,26,8)

注意:为简单起见,在我们的Conv3x3实现中,我们假设输入是一个2d numpy数组,因为这是我们的MNIST图像的存储方式。这对我们有用,我们将它用作网络中的第一层,但大多数CNN都有更多的Conv层。如果我们要构建一个需要Conv3x3多次使用的更大的网络,我们必须使输入成为3d numpy数组。

4、池化(Pooling)

图像中的相邻像素倾向于具有相似的值,因此conv layer通常也会为输出中的相邻像素产生类似的值。但是conv layer中输出的大部分信息都是冗余的,例如,如果我们使用边缘检测过滤器并在某个位置找到强边缘,那么我们也可能会在距离原始像素1个像素偏移的位置找到相对较强的边缘。但是,我们可能并没有找到任何新的东西。

池化层解决了这个问题。他们所做的就是减少通过猜测在输入中产生的汇总值。该池通常是通过简单的操作完成max,min或average等这些操作。以下是池化大小为2的Max Pooling图层的示例:

4x4图像上的最大池(池大小为2)以产生2x2输出

为了执行最大池化,我们以2x2块(因为池大小=2)遍历输入图像,并将最大值放入相应像素的输出图像中。

对于我们的MNIST CNN,我们将在初始conv layer之后放置一个池大小为2的Max Pooling层,这样池化层就会将26x26x8输入转换为13x13x8输出:

4.1执行池化

我们将使用MaxPool2与上一节中的conv类相同的方法实现一个类:

importnumpyasnpclassMaxPool2:#AMaxPoolinglayerusingapoolsizeof2.defiterate_regions(self,image):'''Generatesnon-overlapping2x2imageregionstopoolover.-imageisa2dnumpyarray'''h,w,_=image.shapenew_h=h//2new_w=w//2foriinrange(new_h):forjinrange(new_w):im_region=image[(i*2):(i*2+2),(j*2):(j*2+2)]yieldim_region,i,jdefforward(self,input):'''Performsaforwardpassofthemaxpoollayerusingthegiveninput.Returnsa3dnumpyarraywithdimensions(h/2,w/2,num_filters).-inputisa3dnumpyarraywithdimensions(h,w,num_filters)'''h,w,num_filters=input.shapeoutput=np.zeros((h//2,w//2,num_filters))forim_region,i,jinself.iterate_regions(input):output[i,j]=np.amax(im_region,axis=(0,1))returnoutput

此类与我们之前实现的Conv3x3类工作方式类似。特别注意的是为了找到给定图像区域的最大值,我们使用np.amax()。我们设置是axis=(0,1),因为我们只希望在前两个维度(高度和宽度)上进行最大化,而不是第三个维度,num_filters。

我们来试试吧!

importmnistfromconvimportConv3x3frommaxpoolimportMaxPool2#ThemnistpackagehandlestheMNISTdatasetforus!#Learnmoreathttps://github.com/datapythonista/mnisttrain_images=mnist.train_images()train_labels=mnist.train_labels()conv=Conv3x3(8)pool=MaxPool2()output=conv.forward(train_images[0])output=pool.forward(output)print(output.shape)#(13,13,8)

MNIST CNN马上要完成了!

5、Softmax

为了完成我们的CNN,我们需要让它能够实际进行预测。我们将通过使用标准最终层来实现多类分类问题:Softmax层,一个使用softmax激活函数的标准全连接(密集)层。

提示:完全连接层将每个节点连接到前一层的每个输出。我们在之前的神经网络的介绍中使用了完全连接的图层。

Softmax将任意实际值转换为概率。如果你对其背后的数学有兴趣,自己可以简单了解下,因为它很简单。

5.1用法

我们将使用一个带有10个节点的softmax层,每个节点都代表一个数字,softmax层是我们CNN的最后一层。图层中的每个节点都将连接到输入,在使用softmax变换之后,概率最高的节点表示的数字将成为CNN的输出!

5.2交叉熵损失函数

你可能会有这样的疑问,为什么还要将输出转换为概率呢?最高输出值是否总是具有最高概率?如果你有这样的疑问,那说明你的感觉很对。我们实际上不需要使用softmax来预测数字,因为我们可以选择网络输出最高的数字!

softmax真正做的是帮助我们量化我们对预测的确定程度,这在训练和评估我们的CNN时非常有用。更具体地说,它可以帮我们确定每个预测的正确程度。

5.3实施Softmax

让我们实现一个Softmax图层类:

importnumpyasnpclassSoftmax:#Astandardfully-connectedlayerwithsoftmaxactivation.def__init__(self,input_len,nodes):#Wedividebyinput_lentoreducethevarianceofourinitialvaluesself.weights=np.random.randn(input_len,nodes)/input_lenself.biases=np.zeros(nodes)defforward(self,input):'''Performsaforwardpassofthesoftmaxlayerusingthegiveninput.Returnsa1dnumpyarraycontainingtherespectiveprobabilityvalues.-inputcanbeanyarraywithanydimensions.'''input=input.flatten()input_len,nodes=self.weights.shapetotals=np.dot(input,self.weights)+self.biasesexp=np.exp(totals)returnexp/np.sum(exp,axis=0)

我们现在已经完成了CNN的整个编码工作!把它放在一起:

importmnistimportnumpyasnpfromconvimportConv3x3frommaxpoolimportMaxPool2fromsoftmaximportSoftmax#Weonlyusethefirst1ktestingexamples(outof10ktotal)#intheinterestoftime.Feelfreetochangethisifyouwant.test_images=mnist.test_images()[:1000]test_labels=mnist.test_labels()[:1000]conv=Conv3x3(8)#28x28x1->26x26x8pool=MaxPool2()#26x26x8->13x13x8softmax=Softmax(13*13*8,10)#13x13x8->10defforward(image,label):'''CompletesaforwardpassoftheCNNandcalculatestheaccuracyandcross-entropyloss.-imageisa2dnumpyarray-labelisadigit'''#Wetransformtheimagefrom[0,255]to[-0.5,0.5]tomakeiteasier#toworkwith.Thisisstandardpractice.out=conv.forward((image/255)-0.5)out=pool.forward(out)out=softmax.forward(out)#Calculatecross-entropylossandaccuracy.np.log()isthenaturallog.loss=-np.log(out[label])acc=1ifnp.argmax(out)==labelelse0returnout,loss,accprint('MNISTCNNinitialized!')loss=0num_correct=0fori,(im,label)inenumerate(zip(test_images,test_labels)):#Doaforwardpass._,l,acc=forward(im,label)loss+=lnum_correct+=acc#Printstatsevery100steps.ifi%100==99:print('[Step%d]Past100steps:AverageLoss%.3f|Accuracy:%d%%'%(i+1,loss/100,num_correct))loss=0num_correct=0

执行cnn.py,我们可以得到:

MNISTCNNinitialized![Step100]Past100steps:AverageLoss2.302|Accuracy:11%[Step200]Past100steps:AverageLoss2.302|Accuracy:8%[Step300]Past100steps:AverageLoss2.302|Accuracy:3%[Step400]Past100steps:AverageLoss2.302|Accuracy:12%

想亲自尝试或修改这些代码?在浏览器中运行此CNN,你也可以在Github上找到它。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • python
    +关注

    关注

    55

    文章

    4777

    浏览量

    84407
  • 卷积神经网络

    关注

    4

    文章

    366

    浏览量

    11838

原文标题:手把手带你走进卷积神经网络!

文章出处:【微信号:Imgtec,微信公众号:Imagination Tech】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    如何使用Python构建LSTM神经网络模型

    构建一个LSTM(长短期记忆)神经网络模型是一个涉及多个步骤的过程。以下是使用Python和Keras库构建LSTM模型的指南。 1. 安装必要的库 首先,确保你已经安装了Python
    的头像 发表于 11-13 10:10 104次阅读

    使用Python构建高效的HTTP代理服务器

    构建一个高效的HTTP代理服务器在Python涉及多个方面,包括性能优化、并发处理、协议支持(HTTP/HTTPS)、错误处理以及日志记录等。
    的头像 发表于 10-23 07:41 95次阅读

    何在Python开发人工智能

    Python开发人工智能(AI)是一个广泛而深入的主题,它涵盖了从基础的数据处理到复杂的机器学习、深度学习以及自然语言处理等多个领域。
    的头像 发表于 07-15 15:01 1799次阅读

    何在PyTorch实现LeNet-5网络

    等人提出,主要用于手写数字识别任务(如MNIST数据集)。下面,我将详细阐述如何在PyTorch从头开始实现LeNet-5网络,包括网络架构设计、参数初始化、前向传播、损失函数选择、优化器配置以及训练流程等方面。
    的头像 发表于 07-11 10:58 678次阅读

    何在TensorFlow构建并训练CNN模型

    在TensorFlow构建并训练一个卷积神经网络(CNN)模型是一个涉及多个步骤的过程,包括数据预处理、模型设计、编译、训练以及评估。下面,我将详细阐述这些步骤,并附上一个完整的代码示例。
    的头像 发表于 07-04 11:47 715次阅读

    如何利用CNN实现图像识别

    卷积神经网络(CNN)是深度学习领域中一种特别适用于图像识别任务的神经网络结构。它通过模拟人类视觉系统的处理方式,利用卷积、池化等操作,自动提取图像的特征,进而实现高效的图像识别。本文将从CNN的基本原理、
    的头像 发表于 07-03 16:16 1043次阅读

    NLP模型RNN与CNN的选择

    在自然语言处理(NLP)领域,循环神经网络(RNN)与卷积神经网络(CNN)是两种极为重要且广泛应用的网络结构。它们各自具有独特的优势,适用于处理不同类型的NLP任务。本文旨在深入探讨RNN与CNN
    的头像 发表于 07-03 15:59 400次阅读

    何在不同应用场景下构建音频测试环境

    在之前的文章,我们已经详细介绍了基础音频参数和AP525的软硬件配置。本文将延续这一主题,以泰凌TLSR9518A EVB作为测试设备(DUT),向大家展示如何在不同应用场景下构建音频测试环境。
    的头像 发表于 07-03 15:00 436次阅读
    如<b class='flag-5'>何在</b>不同应用场景下<b class='flag-5'>构建</b>音频测试环境

    深度神经网络模型cnn的基本概念、结构及原理

    ,其核心是构建具有多层结构的神经网络模型,以实现对复杂数据的高效表示和处理。在众多深度学习模型,卷积神经网络(CNN)因其在图像识别等领域的卓越性能而备受关注。CNN通过引入卷积层和
    的头像 发表于 07-02 10:11 9494次阅读

    基于Python和深度学习的CNN原理详解

    卷积神经网络 (CNN) 由各种类型的层组成,这些层协同工作以从输入数据中学习分层表示。每个层在整体架构中都发挥着独特的作用。
    的头像 发表于 04-06 05:51 1932次阅读
    基于<b class='flag-5'>Python</b>和深度学习的<b class='flag-5'>CNN</b>原理详解

    如何使用linux下gdb来调试python程序

    如何使用linux下gdb来调试python程序  在Linux下,可以使用GDB(GNU调试器)来调试Python程序。GDB是一个强大的调试工具,可以帮助开发者诊断和修复程序的错误。在本文
    的头像 发表于 01-31 10:41 2401次阅读

    pythontext的用法

    Python的text是一个常见的数据类型,它用于存储和处理文本数据。在Python,文本常常被表示为字符串(string),字符串是一系列Unicode字符的有序序列。 在
    的头像 发表于 11-23 15:46 3319次阅读

    pythonnumber代表什么

    Python,number(数字)是一种内置的数据类型,用于表示数值。Python提供了几种不同的number类型,分别是整数(int)、浮点数(float)、复数(complex)和布尔值
    的头像 发表于 11-22 09:50 1871次阅读

    pythontuple的用法

    Python的元组(tuple)是一种不可变的有序集合。与列表(list)类似,元组可以存储任意类型的数据,但是元组一旦创建就不能被修改。在Python,元组是使用圆括号进行表示,
    的头像 发表于 11-21 16:27 905次阅读

    使用Python卷积神经网络(CNN)进行图像识别的基本步骤

    Python 卷积神经网络(CNN)在图像识别领域具有广泛的应用。通过使用卷积神经网络,我们可以让计算机从图像中学习特征,从而实现对图像的分类、识别和分析等任务。以下是使用 Python 卷积神经网络进行图像识别的基本步骤。
    的头像 发表于 11-20 11:20 5494次阅读