【深度学习_TensorFlow】卷积神经网络(CNN)

news/2023/12/3 11:16:12/文章来源:https://blog.csdn.net/m0_73222051/article/details/133419831

写在前面

这篇文章的行文思路如下:

  1. 先根据视频了解卷积和卷积神经网络的整体框架

  2. 接着了解卷积神经网络构建过程中的一些重要操作,包括内积、填充、池化。

  3. 然后介绍卷积层如何实现。

  4. 最后用卷积神经网络的开山之作(LeNet-5)来进行上手练习。

一、初识卷积

最近学习信号与系统的时候,了解了卷积的相关概念,联想起此前学习opencv的时候也有卷积操作,到现在学到深度学习的时候又碰到了卷积神经网络,但是也一直搞不懂为什么要那样做,有什么意义,于是查阅相关博客视频,发现视频比博客直观的多,于是多分享几个制作精良的视频,帮你从数学定义、信号处理、图像处理、现实应用多维度了解卷积!

卷积在声乐中体现(了解即可)卷积究竟卷了啥?——17分钟了解什么是卷积
卷积在图像中应用【卷积神经网络】为什么卷积哪儿都能用?
卷积物理意义、卷积在矩阵运算【卷积】直观形象的实例,10分钟彻底搞懂
卷积神经网络解释【卷积神经网络】8分钟搞懂CNN,动画讲解喜闻乐见
卷积神经网络解释什么是卷积神经网络CNN?【知多少】
卷积神经网络【数之道 08】走进"卷积神经网络",了解图像识别背后的原理
神经网络可视化网站https://poloclub.github.io/cnn-explainer/

虽然这些视频讲解不尽相同,但是里面有大量的共同点,我们就只讨论卷积在神经网络中应用,有几点可以串联补充

1. 卷积优势

全连接的劣势就是卷积的优势。全连接层的参数量极大,具体可以体现在:假设一张图像输入的像素量为 28×28=784,我们将权值 w 和偏置 b 视为参数(每两个节点之间的参数w和b不一定相同),对于输入节点为 784,输出节点为 256 的第一层网络来说,总参数量就为784×256 + 256 = 200960(后面的256是b),这仅仅只是28×28的图像在第一层的参数量,更大的图像,更多的层数,就意味着更加恐怖的参数量!更难的特征提取!

2. 局部相关

为解决全连接层的缺陷,我们引入局部相关的概念,其实也挺容易理解的,对于一张图像,并非所有像素点都是重要的,就如手写数字识别中,手写数字才是关键,其他的背景部分都是次要的。因此关注的中心可以进一步缩小。将全连接层替换为卷积层来简化特征提取。具体的卷积操作如下图所示
在这里插入图片描述

局部相关操作是指使用卷积层中的卷积核,在输入图像上进行滑动,对每个位置进行卷积运算(卷积核与感受野内的图像矩阵进行内积运算,不了解可以搜索内积相关文章)。和信号的卷积运算先反褶、时移、叠加还是稍有些不同的。

3. 填充与池化

先讲讲为什么要进行填充,很明显上面的卷积计算后原来5×5 的图像矩阵,转换为3×3的特征矩阵,这样可能会丢失一些提取出来的特征,例如第一幅图像‘7’未进行填充,经过两个不同的卷积核后,‘7’ 的竖直特征被提取出,但是水平特征却丢失了,解决的办法就是,将原图像矩阵周围填充(如右图),这样水平特征和竖直特征都被提取出来了。(后文实现卷积网络层的方法无需进行手动填充,tensorflow自动帮你实现)

池化通常在卷积层之后,我们针对卷积过后的特征图中的小矩阵求最大值,就是最大池化;小矩阵中的数求平均值就是平均池化,从而减少模型参数,提高模型的计算效率和泛化能力,还可以在一定程度上防止过拟合,提高模型的鲁棒性
在这里插入图片描述

二、卷积神经网络

接下来,我们以一个经典的卷积模型 LeNet-5 来学习卷积神经网络。这个卷积模型是Yann LeCun 等人在1998年提出的,非常适合初学者学习,我们稍微修改这个模型,尝试使用 Tensorflow 复现出修改后的模型来,部分操作可能和下图模型不一致,但效果相差不大。
在这里插入图片描述

为了方便,我们仍然使用之前的 mnist 数据集来作为输入数据,图像的尺寸为 28×28,初始张量为[b, 28, 28, 1]:

 Layer (type)                Output Shape              Param #   
=================================================================conv2d (Conv2D)             (None, 26, 26, 6)         60        max_pooling2d (MaxPooling2D  (None, 13, 13, 6)        0         )                                                               conv2d_1 (Conv2D)           (None, 11, 11, 16)        880       max_pooling2d_1 (MaxPooling  (None, 5, 5, 16)         0         2D)                                                             flatten (Flatten)           (None, 400)               0         dense (Dense)               (None, 120)               48120     dense_1 (Dense)             (None, 84)                10164     dense_2 (Dense)             (None, 10)                850       =================================================================
  1. 经过一个卷积层(convolution),得到[b, 26, 26, 6]形状的张量,由于代码没有填充,故张量尺寸会缩减,具体的计算公式如下:其中(b, h’, w’, c)的张量,卷积核的数量c,卷积核的大小k,步长s, p h p_h ph表示上下填充行数(只考虑两端相等), p w p_w pw表示左右填充列数(只考虑相等情况),在这里

h ′ = [ h + 2 ⋅ p h − k s ] + 1 h'=\left[\frac{h+2\cdot p_h-k}{s}\right]+1 h=[sh+2phk]+1

h ′ = [ h + 2 ⋅ p w − k s ] + 1 h'=\left[\frac{h+2\cdot p_w-k}{s}\right]+1 h=[sh+2pwk]+1

在第一层卷积,就可以使用上面的公式来计算,括号里面向下取整

h ′ = [ 28 + 2 ∗ 0 − 3 1 ] + 1 = ⌊ 25 ⌋ + 1 = 26 w ′ = [ 28 + 2 ∗ 0 − 3 1 ] + 1 = ⌊ 25 ⌋ + 1 = 26 \begin{aligned}h^{\prime} & =\left[\frac{28+2*0-3}{1}\right]+1=\lfloor25\rfloor+1=26\\ & \\ w^{\prime} & =\left[\frac{28+2*0-3}{1}\right]+1=\lfloor25\rfloor+1=26\end{aligned} hw=[128+203]+1=25+1=26=[128+203]+1=25+1=26


  1. 经过一个向下采样层,张量尺寸缩小为[b, 13, 13, 6],这里我们使用池化层来实现;

  2. 经过第二个卷积层,得到[b, 11, 11, 16]的张量;

  3. 经过第二个向下采样层,张量尺寸缩小为[b, 5, 5, 16],这里我们使用池化层实现;

  4. 将张量打平为[b, 400],之后进入三个节点为 120、84、10 的全连接层将结果矩阵输出


三、卷积实现

由于使用卷积神经网络进行手写数字识别和之前的使用全连接层实现的手写数字识别的代码极为相似,只需将网络层的地方修改即可,所以就不再介绍其它部分的代码,不了解的可以查阅**这篇文章 ,**在tensorflow中卷积的实现主要有下面两种方式,自定义方式函数的方式和卷积层的方式

1. 自定义权值

tf.nn.conv2d(input, kernel, strides=1, padding=[[0, 0], [0, 0], [0, 0], [0, 0]])

input:输入数据图像,例如[100, 28, 28, 3]表示 100 张 28×28的三通道图像。

kernel:卷积核,例如[3, 3, 3, 4] 表示4个 3×3 的三通道的卷积核

strides:表示步长

padding:表示填充,填充的具体数量由tensorflow自动计算并完成

input = tf.random.normal([100, 28, 28, 3])
w = tf.random.normal([3, 3, 4, 3])out = tf.nn.conv2d(x, w, strides=1, padding=[[0, 0], [0, 0], [0, 0], [0, 0]])

2. 卷积层的实现

还有更为常见的层方式实现:

# strides=1, padding='same',可得到输入输出同大小的卷积层,具体填充操作自动完成
layers = layers.Conv2D(4, kernel_size=3, strides=1, padding='SAME')

四、leNet-5实战

这套代码和之前使用全连接层实现mnist手写数字识别的代码框架是一致的,只是修改了网络层的部分。不了解的可以查看往期专栏文章。

# import tensorflow as tf
# from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics, regularizers
# from tensorflow.keras.callbacks import EarlyStopping
# # 处理每一张图像
# def preprocess(x, y):
#
#     x = tf.cast(x, dtype=tf.float32) / 255.
#     y = tf.one_hot(y, depth=10)
#
#     return x, y
#
# # 数据集读取
# (x, y), (x_test, y_test) = datasets.mnist.load_data()
#
# # 交叉验证切分,防止过拟合的有效手段
# idx = tf.range(60000)  # 按顺序生成索引0~59999
# idx = tf.random.shuffle(idx)  # 将索引打乱
# x_train, y_train = tf.gather(x, idx[:50000]), tf.gather(y, idx[:50000])
# x_val, y_val = tf.gather(x, idx[-10000:]), tf.gather(y, idx[-10000:])
#
# # 训练集
# train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train))
# train_db = train_db.map(preprocess).shuffle(10000).batch(128).repeat(5)  # 这里粗暴的重复数据增加数据集,repeat改小训练时间缩短
#
# # 验证集
# val_db = tf.data.Dataset.from_tensor_slices((x_val, y_val))
# val_db = val_db.map(preprocess).shuffle(10000).batch(128)
#
# # 测试集
# test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test))
# test_db = test_db.map(preprocess).shuffle(10000).batch(128)
#
# # 定义早停法回调函数
# early_stopping = EarlyStopping(monitor='val_loss',  # 监视验证集loss
#                                patience=3,  # 当验证集loss在2个epoch内都没有改善则停止训练
#                                mode='min',  # 监测loss时一般设置为min,监测准确值时一般设置为max
#                                verbose=1,  # 检测值改善时打印一条信息
#                                restore_best_weights=True  # 将权重恢复到最好的一个epoch
#                                )
#
# # 网络构建
# # 正则化:在模型损失函数中加入权重参数的L2范数作为惩罚项,力度由0.001控制。
# network = Sequential([
#                       layers.Reshape(target_shape=(28, 28, 1), input_shape=(28, 28)),  # 这里指定了层,就不需要在process里进行reshape了,也不需要built构建参数了
#                       layers.Conv2D(6, kernel_size=3, strides=1),  # 6个3×3的卷积核,步长设置为1
#                       layers.MaxPooling2D(pool_size=(2, 2), strides=2),  # 高宽减半的池化层
#                       layers.Conv2D(16, kernel_size=3, strides=1),  # 第二个卷积层,16个3×3的卷积核
#                       layers.MaxPooling2D(pool_size=(2, 2), strides=2),  # 高宽减半的池化层
#                       layers.Flatten(),  # 打平,方便连接全连接层
#                       layers.Dense(120, kernel_regularizer=regularizers.l2(0.001), activation='relu'),
#                       layers.Dense(84, kernel_regularizer=regularizers.l2(0.001), activation='relu'),
#                       layers.Dense(10)
#                     ])
#
# # 参数构建
# # network.build(input_shape=(None, 28, 28, 1))  # 由于第一层已经指定好大小了,所以这里不构建也可以
# # 模型展示
# network.summary()
# # 模型装配
# network.compile(optimizer=optimizers.Adam(learning_rate=0.01),
#                 loss=tf.losses.CategoricalCrossentropy(from_logits=True),
#                 metrics=['accuracy'])
# # 模型训练,这里控制训练次数
# network.fit(train_db, epochs=50,
#             validation_data=val_db, validation_steps=10,
#             callbacks=[early_stopping])
# # 模型评估
# print('模型评估:')
# network.evaluate(test_db)# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~# 这两段代码框架一致,只是在不同位置进行了数据处理,注意辨别
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, regularizers
from tensorflow.keras.callbacks import EarlyStopping# 处理每一张图像
def preprocess(x, y):x = tf.cast(x, dtype=tf.float32) / 255.x = tf.reshape(x, [28, 28, 1])y = tf.cast(y, dtype=tf.int32)y = tf.one_hot(y, depth=10)return x, y# 数据集读取
(x, y), (x_test, y_test) = datasets.mnist.load_data()# 交叉验证切分,防止过拟合的有效手段
idx = tf.range(60000)  # 按顺序生成索引0~59999
idx = tf.random.shuffle(idx)  # 将索引打乱
x_train, y_train = tf.gather(x, idx[:50000]), tf.gather(y, idx[:50000])
x_val, y_val = tf.gather(x, idx[-10000:]), tf.gather(y, idx[-10000:])# 训练集
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train))
train_db = train_db.map(preprocess).shuffle(10000).batch(128).repeat(10)# 验证集
val_db = tf.data.Dataset.from_tensor_slices((x_val, y_val))
val_db = val_db.map(preprocess).shuffle(10000).batch(128)# 测试集
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test))
test_db = test_db.map(preprocess).shuffle(10000).batch(128)# 定义早停法回调函数
early_stopping = EarlyStopping(monitor='val_loss',  # 监视验证集losspatience=3,  # 当验证集loss在2个epoch内都没有改善则停止训练mode='min',  # 监测loss时一般设置为min,监测准确值时一般设置为maxverbose=1,  # 检测值改善时打印一条信息restore_best_weights=True  # 将权重恢复到最好的一个epoch)network = Sequential([layers.Conv2D(6, kernel_size=3, strides=1),  # 6个3×3的卷积核,步长设置为1layers.MaxPooling2D(pool_size=(2, 2), strides=2),  # 高宽减半的池化层layers.Conv2D(16, kernel_size=3, strides=1),  # 第二个卷积层,16个3×3的卷积核layers.MaxPooling2D(pool_size=(2, 2), strides=2),  # 高宽减半的池化层layers.Flatten(),  # 打平,方便连接全连接层layers.Dense(120, kernel_regularizer=regularizers.l2(0.001), activation='relu'),layers.Dense(84, kernel_regularizer=regularizers.l2(0.001), activation='relu'),layers.Dense(10)])# 参数构建
network.build(input_shape=(None, 28, 28, 1))
# 模型展示
network.summary()# 模型装配
network.compile(optimizer=optimizers.Adam(learning_rate=0.01),loss=tf.losses.CategoricalCrossentropy(from_logits=True),metrics=['accuracy'])
# 模型训练,这里控制训练次数
network.fit(train_db, epochs=50,validation_data=val_db, validation_steps=10,callbacks=[early_stopping])
# 模型评估
print('模型评估:')
network.evaluate(test_db)

写在最后

👍🏻点赞,你的认可是我创作的动力!
⭐收藏,你的青睐是我努力的方向!
✏️评论,你的意见是我进步的财富!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ldbm.cn/p/138496.html

如若内容造成侵权/违法违规/事实不符,请联系编程新知网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视频异常检测:Human Kinematics-inspired Skeleton-based Video Anomaly Detection

论文作者:Jian Xiao,Tianyuan Liu,Genlin Ji 作者单位:Nanjing Normal University;The Hong Kong Polytechnic University 论文链接:http://arxiv.org/abs/2309.15662v1 内容简介: 1)方向:视频异常检测…

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 试题 D: 合并数列

[蓝桥杯 2023 国 B] 合并数列 【问题描述】 小明发现有很多方案可以把一个很大的正整数拆成若干正整数的和。他采取了其中两种方案,分别将他们列为两个数组 { a 1 , a 2 , ⋯ a n } \{a_1, a_2, \cdots a_n\} {a1​,a2​,⋯an​} 和 { b 1 , b 2 , ⋯ b m } \{b…

强化学习到底是什么?它是怎么运维的

https://mp.weixin.qq.com/s/LL3HfU2iNlmSqaTX_3J7fQ 强化学习是一种行为学习模型,由算法提供数据分析反馈,引导用户逐步获取最佳结果。 来源丨Towards Data Science 作者丨Jair Ribeiro 编译丨科技行者 强化学习属于机器学习中的一个子集,它使代理能够理解在特定环境中…

【STL巨头】set、map、multiset、multimap的介绍及使用

set、map、multiset、multimap的介绍及使用 一、关联式容器二、键值对键值对概念定义 三、setset的介绍set的使用set的模板参数列表set的构造set的迭代器set的容量emptysize set的修改操作insertfind && erasecountlower_bound 和 upper_bound Multiset的用法 四、mapm…

vue前端项目中添加独立的静态资源

如果想要在vue项目中放一些独立的静态资源,比如html文件或者用于下载的业务模板或其他文件等,需要在vue打包的时候指定一下静态资源的位置和打包后的目标位置。 使用的是 copy-webpack-plugin 插件,如果没有安装则需要先安装一下,…

ChatGPT必应联网功能正式上线

今日凌晨发现,ChatGPT又支持必应联网了!虽然有人使用过newbing这个阉割版的联网GPT4,但官方版本确实更加便捷好用啊! 尽管 ChatGPT 此前已经展现出了其他人工智能模型无可比拟的智能,但由于其训练数据的限制&#xff…

excel筛选后求和

需要对excel先筛选,后对“完成数量”进行求和。初始表格如下: 一、选中表内任意单元格,按ctrlshiftL,开启筛选 二、根据“部门”筛选,比如选择“一班” 筛选完毕后,选中上图单元格,然后按alt后&…

使用U3D、pico开发VR(二)——添加手柄摇杆控制移动

一、将unity 与visual studio 相关联 1.Edit->Preference->External tool 选择相应的版本 二、手柄遥控人物转向和人物移动 1.添加Locomotion System组件 选择XR Origin; 2.添加Continuous Move Provider(Action-based)组件 1>…

volatile关键字以及使用场景

在多线程环境下,如果编程不当,可能会出现程序运行结果混乱的问题。 出现这个原因主要是,JMM 中主内存和线程工作内存的数据不一致,以及多个线程执行时无序,共同导致的结果。 同时也提到引入synchronized同步锁&#x…

八、3d场景的区域光墙

在遇到区域展示的时候我们就能看到炫酷的区域选中效果,那么代码是怎么编辑的呢,今天咱们就好好说说,下面看实现效果。 思路: 首先,光墙肯定有多个,那么必须要创建一个新的js文件来作为他的原型对象。这个光…

抖音开放平台第三方代小程序开发,一整套流程

大家好,我是小悟 抖音小程序第三方平台开发着力于解决抖音生态体系内的小程序管理问题,一套模板,随处部署。能尽可能地减少服务商的开发成本,服务商只用开发一套小程序代码作为模板就可以快速批量的孵化出大量的商家小程序。 第…

NLP中token总结

Token 可以被理解为文本中的最小单位。在英文中,一个 token 可以是一个单词,也可以是一个标点符号。在中文中,通常以字或词作为 token。ChatGPT 将输入文本拆分成一个个 token,使模型能够对其进行处理和理解 在自然语言处理&#…

从零开始学习 Java:简单易懂的入门指南之IO字节流(三十)

IO流之字节流 1. IO概述1.1 什么是IO1.2 IO的分类1.3 IO的流向说明图解1.4 顶级父类们 2. 字节流2.1 一切皆为字节2.2 字节输出流【OutputStream】2.3 FileOutputStream类构造方法写出字节数据数据追加续写写出换行 2.4 字节输入流【InputStream】2.5 FileInputStream类构造方法…

leetCode 122.买卖股票的最佳时机 II 动态规划 + 状态转移 + 状态压缩

122. 买卖股票的最佳时机 II - 力扣(LeetCode) 给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。 在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&…

Halcon 从基础到精通-01- 基本概念

1 HALCON Architecture 【图一】 HALCON的架构如上,其主要的部分,就是图像处理库。 2 HALCON的基本架构 2.1 Operators HALCON库功能的使用都是通过【operators】操作符来实现的。绝大多数的操作符由多种方法构成,具体可以参考给出的下面…

Bug:elementUI样式不起作用、Vue引入组件报错not found等(Vue+ElementUI问题汇总)

前端问题合集:VueElementUI 1. Vue引用Element-UI时,组件无效果解决方案 前提: 已经安装好elementUI依赖 //安装依赖 npm install element-ui //main.js中导入依赖并在全局中使用 import ElementUI from element-ui Vue.use(ElementUI)如果此…

windows下python开发环境的搭建 python入门系列 【环境搭建篇】

在正式学习Python之前要先搭建Python开发环境。由于Python是跨平台的,所以可以在多个操作系统上进行编程 一、python的下载安装与配置 1、Python解释器 1. 要进行Python开发,首先需要Python解释器,这里说的安装Python就是安装Python解释器…

LeetCode每日一题:2136. 全部开花的最早一天(2023.9.30 C++)

目录 2136. 全部开花的最早一天 题目描述: 实现代码与解析: 贪心 原理思路: 2136. 全部开花的最早一天 题目描述: 你有 n 枚花的种子。每枚种子必须先种下,才能开始生长、开花。播种需要时间,种子的生…

Mybatis 二级缓存(使用Ehcache作为二级缓存)

上一篇我们介绍了mybatis中二级缓存的使用,本篇我们在此基础上介绍Mybatis中如何使用Ehcache作为二级缓存。 如果您对mybatis中二级缓存的使用不太了解,建议您先进行了解后再阅读本篇,可以参考: Mybatis 二级缓存https://blog.c…

JS前端树形Tree数据结构使用

前端开发中会经常用到树形结构数据,如多级菜单、商品的多级分类等。数据库的设计和存储都是扁平结构,就会用到各种Tree树结构的转换操作,本文就尝试全面总结一下。 如下示例数据,关键字段id为唯一标识,pid为父级id&am…