利用Tensorflow2.0实现卷积神经网络CNN
前面几节课我们给大家介绍的都是全连接神经网络,但全连接神经网络有个明显的缺点,那就是当网络层数较多时(尤其是在图像识别任务中),它每层的参数数量容易变得很大,不好控制。所以本节课老shi准备给大家介绍另外一种非常重要的网络结构——卷积神经网络。卷积神经网络(Convolutional Neural Network, CNN)近几年在深度学习中的应用非常广泛,特别是在图像识别、语音识别以及本文处理方面。可以说,卷积神经网络是深度学习中最重要的神经网络之一,例如图像识别中非常有名的LeNet、AlexNet、 ResNet、VGGNet、InceptionNet等网络结构都是在卷积神经网络基础上得来的。
全连接神经网络 VS 卷积神经网络
全连接神经网络之所以不太适合图像识别任务,主要有以下几个方面的问题:
1.参数数量太多
假设一个输入图片由1000*1000像素组成,那么输入层就有1000*1000=100万节点。假设第一个隐藏层有100个节点,那么仅这一层就有(1000*1000+1)*100=1亿个参数,这个参数量确实非常大!如果我们的网络层数再多一些,那么参数数量就会更多,因此它的扩展性较差。
2.没有利用像素之间的位置信息
对于图像识别任务来说,每个像素和其周围像素的联系是比较紧密的,和离得很远的像素之间的联系可能就很小了。如果一个神经元和上一层所有神经元相连,那么就相当于对于一个像素来说,把图像的所有像素都等同看待,这不符合前面的假设。当我们完成每个连接权重的学习之后,最终可能会发现,有大量的权重,它们的值都是很小的(也就是这些连接其实无关紧要)。努力学习大量并不重要的权重,这样的学习必将是非常低效的。
3.网络层数限制
我们知道网络层数越多其表达能力越强,但是通过梯度下降法来训练深度全连接神经网络很困难的,因为全连接神经网络的梯度很难传递超过3层(梯度会消失)。因此,我们不可能得到一个很深的全连接神经网络,也就限制了它的能力。
那么,卷积神经网络又是怎么解决以上这些问题的呢?主要有四个思路:
1.局部连接
这是最容易想到的,每个神经元不再像全连接网络那样和上一层的所有神经元连接,而是只和一小部分神经元连接,这样参数量就大大减少了。
2.权值共享
每一组连接可以共享同一个权重,而不是每个连接有一个不同的权重,这样也大大减少了很多参数。
3.下采样
卷积神经网络可以通过池化层来进行下采样减少每层的样本数,同样也可以进一步减少参数量,同时还可以提高模型的鲁棒性。
4.充分利用位置信息
卷积核通过不断的卷积操作充分利用每个像素点之间的位置信息,更适用于图像识别任务中。
总之,对于图像识别任务来说,卷积神经网路通过尽可能保留重要参数,去掉大量不重要的参数,来达到更好的学习效果。
什么是卷积神经网络
首先,我们先获得一个感性的认识,以下是卷积神经网络的示意图。
卷积神经网络由若干卷积层(Convolution Layer)、池化层(Pooling Layer)和全连接层(Fully Connected Network)组成。它的常用架构模式为:
INPUT -> [[CONV]*N -> POOL?]*M -> [FC]*K
也就是输入数据INPUT,经过N个卷积层叠加,然后(可选)叠加一个池化层,重复这个结构M次,最后叠加K个全连接层。
注:每个卷积层后不一定需要连接池化层,看实际情况而定。
卷积神经网络各层计算
1、卷积层
我们用一个简单的例子来讲述如何计算卷积,假设有一个5*5的图像,使用3*3的卷积核(filter)进行卷积,最后想得到一个3*3的Feature Map,如下图所示:
为了描述清楚卷积计算过程,我们首先对图像的每个像素进行编号,用X(i,j)表示图像image的第i行第j列元素;对卷积核filter的每个权重进行编号,用W(m,n)表示第m行第n列权重,用Wb表示卷积核的偏置项(上图中每个卷积核filter的偏置项bias都是0);对Feature Map的每个元素进行编号,用a(i,j)表示Feature Map的第i行第j列元素;用f表示激活函数(这个例子选择relu函数作为激活函数)。然后,使用下列公式计算卷积:
例如,对于Feature Map左上角元素来说,其卷积计算方法为:
=relu(1+0+1+0+1+0+0+0+1+0)
=relu(4)
=4
下面的动图显示了整个Feature Map的计算过程:
以上是卷积步幅等于1的情况,当卷积步幅设置为2时,Feature Map就变成了2*2了。这说明Feature Map的大小是和原来图像大小和步幅有关系。它们满足以下关系:
在上面的两个公式中,W2是卷积后Feature Map的宽度,W1是卷积前图像的宽度,F是卷积核filter的宽度,P是原始图像周围填充0的数量(也就是在原始图像周围补几圈0,如果P是1,那么就是补1圈0,以此类推),S是步幅;H2是卷积后Feature Map的高度,H1是卷积前原始图像的高度。
注:卷积神经网络中的卷积和数学中的卷积是有区别的,卷积神经网络中的卷积严格来说应该叫互相关操作,只是通常我们都习惯叫卷积操作而已。
2、池化层
池化层主要的作用是下采样,通过去掉Feature Map中不重要的样本,进一步减少参数数量。池化的方法很多,最常用的是Max Pooling。Max Pooling实际上就是在n*n的样本中取最大值,作为采样后的样本值。下图是2*2卷积核,步长为2的池化操作:
除了Max Pooling之外,常用的还有Mean Pooling(取均值),对于深度为D的Feature Map,各层独立做池化操作,因此池化后的深度仍为D(池化深度不变,样本数减少)。
3、全连接层
全连接神经网络前面已经详细介绍过了,这里不再赘述。
最后,我们一起来看一个卷积神经网络CNN实现人脸分类的实例。有兴趣的同学可以自己动手试一下。ok,本节课到此。下节课给大家介绍深度学习中常用的激活函数,敬请期待!
#coding:utf8
import numpy as np
from sklearn import datasets
from matplotlib import pyplot as plt
from sklearn.model_selection import train_test_split
import tensorflow.keras as keras
import warnings
warnings.filterwarnings("ignore")
# 导入数据
faces_data = datasets.fetch_olivetti_faces()
# 显示原始图片
i = 0
plt.figure(figsize=(20, 20))
for img in faces_data.images:
#总共400张图片,每个人10个头像,共40个人
plt.subplot(20, 20, i+1)
plt.imshow(img, cmap="gray")
plt.xticks([])
plt.yticks([])
plt.xlabel(faces_data.target[i])
i = i + 1
plt.show()
X = faces_data.images
y = faces_data.target
# 400张图片,每张图片64x64,灰色图片通道数为1
X = X.reshape(400, 64, 64, 1)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
model = keras.Sequential()
# 第一层卷积,卷积核数为128,卷积核3x3,激活函数使用relu,输入是每张原始图片64x64*1
model.add(keras.layers.Conv2D(128, kernel_size=3, activation='relu', input_shape=(64, 64, 1)))
# 第一池化层
model.add(keras.layers.MaxPool2D((2, 2), strides=2))
# 第二层卷积,卷积核数为64,卷积核3x3,激活函数使用relu
model.add(keras.layers.Conv2D(64, kernel_size=3, activation='relu'))
# 第二池化层
model.add(keras.layers.MaxPool2D((2, 2), strides=2))
#把多维数组压缩成一维,里面的操作可以简单理解为reshape,方便后面全连接层使用
model.add(keras.layers.Flatten())
#对应cnn的全连接层,40个人对应40种分类,激活函数使用softmax,进行分类
model.add(keras.layers.Dense(40, activation='softmax'))
model.compile(optimizer='adam',loss='sparse_categorical_crossentropy', metrics=['accuracy'])
# 进行训练和预测
model.fit(X_train, y_train, epochs=10)
y_predict = model.predict(X_test)
# 打印实际标签与预测结果
print(y_test[0], np.argmax(y_predict[0]))
- sl从程序集中读取xaml文件
- WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[实现篇]
- WCF技术剖析之二十六:如何导出WCF服务的元数据(Metadata)[扩展篇]
- 如何解决打开VS2010后没有UI界面的问题
- RadRails1.0降临——增加Profiler、CallGraph Analyzer和Rails Shell等新特性
- Rails存储库从SVN转向Git
- 报告称10后已变成出境游“老司机”屌丝80后:我还没出过国
- 担心人工智能取代你的工作?听听微软科学家怎么说!
- Radiant: 基于Ruby on Rails的内容管理系统
- “大数据”如何追回1.3亿元税款?
- Enterprise Library深入解析与灵活应用(5):创建一个简易版的批处理执行器,认识Enterprise Library典型的配置方式和对象创建方式
- BTC.com时讯-IBM等老牌大企业因区块链技术获得新活力
- Kit 3D 更新
- Enterprise Library深入解析与灵活应用(5):创建一个简易版的批处理执行器,认识Enterprise Library典型的配置方式和对象创建方式
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 使用OpenCV进行模糊检测(拉普拉斯算子)
- 利用opencv对图像进行长曝光
- 总说手机没有“好壁纸”,Python一次性抓取500张“美女”图片,够不够用!
- KEDA发布2.0(Beta)|来一个伸缩测试
- pandas入门:Series、DataFrame、Index基本操作都有了!
- 这几个用 Pyecharts 做出来的交互图表,领导说叼爆了!
- 手把手教你用 Python 搞定网页爬虫!
- 为并发而生的 ConcurrentHashMap,基于 Java8 分析
- 使用Kustomize定制Helm Chart
- 踩坑了,JDK8 中 HashMap 依然会产生死循环问题!
- 使用shell-operator实现Operator
- 教你用Python 操作 PDF 的几种方法
- 如何在K8s上设置生产级的EFK?(上)
- IntelliJ IDEA 构建 Maven 多模块工程项目
- GDP越高就越幸福吗?用Python分析《世界幸福指数报告》后我们发现…