深度学习 精选笔记(13.1)卷积神经网络-LeNet模型

学习参考:

  • 动手学深度学习2.0
  • Deep-Learning-with-TensorFlow-book
  • pytorchlightning

①如有冒犯、请联系侵删。
②已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。
③非常推荐上面(学习参考)的前两个教程,在网上是开源免费的,写的很棒,不管是开始学还是复习巩固都很不错的。

深度学习回顾,专栏内容来源多个书籍笔记、在线笔记、以及自己的感想、想法,佛系更新。争取内容全面而不失重点。完结时间到了也会一直更新下去,已写完的笔记文章会不定时一直修订修改(删、改、增),以达到集多方教程的精华于一文的目的。所有文章涉及的教程都会写在开头、一起学习一起进步。

1.LeNet模型结构

LeNet(LeNet-5)由两个部分组成:

  • 卷积编码器:由两个卷积层组成;
  • 全连接层密集块:由三个全连接层组成。

结构如下:
在这里插入图片描述

每个卷积块中的基本单元是一个卷积层、一个sigmoid激活函数和平均汇聚层。请注意,虽然ReLU和最大汇聚层更有效,但它们在20世纪90年代还没有出现(所以该模型就没有)。

每个卷积层使用 5×5 卷积核和一个sigmoid激活函数。些层将输入映射到多个二维特征输出,通常同时增加通道的数量。第一卷积层有6个输出通道,而第二个卷积层有16个输出通道。每个 2×2池操作(步幅2)通过空间下采样将维数减少4倍。卷积的输出形状由批量大小、通道数、高度、宽度决定。
在这里插入图片描述

为了将卷积块的输出传递给稠密块(全连接那部分),必须在小批量中展平每个样本。换言之,将这个四维输入转换成全连接层所期望的二维输入。这里的二维表示的第一个维度索引小批量中的样本,第二个维度给出每个样本的平面向量表示。LeNet的稠密块有三个全连接层,分别有120、84和10个输出。因为在执行分类任务,所以输出层的10维对应于最后输出结果的数量。

请注意,在整个卷积块中,与上一层相比,每一层特征的高度和宽度都减小了。 第一个卷积层使用2个像素的填充,来补偿 5×5
卷积核导致的特征减少。 相反,第二个卷积层没有填充,因此高度和宽度都减少了4个像素。 随着层叠的上升,通道的数量从输入时的1个,增加到第一个卷积层之后的6个,再到第二个卷积层之后的16个。 同时,每个汇聚层的高度和宽度都减半。最后,每个全连接层减少维数,最终输出一个维数与结果分类数相匹配的输出。

模型结构实现如下:(对原始模型做了一点小改动,去掉了最后一层的高斯激活。除此之外,这个网络与最初的LeNet-5一致。)

import tensorflow as tf
from d2l import tensorflow as d2l


def net():
    return tf.keras.models.Sequential([
        tf.keras.layers.Conv2D(filters=6, kernel_size=5, activation='sigmoid',
                               padding='same'),
        tf.keras.layers.AvgPool2D(pool_size=2, strides=2),
        tf.keras.layers.Conv2D(filters=16, kernel_size=5,
                               activation='sigmoid'),
        tf.keras.layers.AvgPool2D(pool_size=2, strides=2),
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(120, activation='sigmoid'),
        tf.keras.layers.Dense(84, activation='sigmoid'),
        tf.keras.layers.Dense(10)])

将一个大小为 28×28 的单通道(黑白)图像通过LeNet。通过在每一层打印输出的形状,可以检查模型,以确保其操作与期望的结构 一致。

X = tf.random.uniform((1, 28, 28, 1))
for layer in net().layers:
    X = layer(X)
    print(layer.__class__.__name__, 'output shape: \t', X.shape)

如下,输出维度和层次变化与结构一致,说明模型没有搭建出错。

Conv2D output shape: 	 (1, 28, 28, 6)
AveragePooling2D output shape: 	 (1, 14, 14, 6)
Conv2D output shape: 	 (1, 10, 10, 16)
AveragePooling2D output shape: 	 (1, 5, 5, 16)
Flatten output shape: 	 (1, 400)
Dense output shape: 	 (1, 120)
Dense output shape: 	 (1, 84)
Dense output shape: 	 (1, 10)

2.LeNet5在Fashion-MNIST数据集上的表现

虽然卷积神经网络的参数较少,但与深度的多层感知机相比,它们的计算成本仍然很高,因为每个参数都参与更多的乘法。

batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)

通过使用GPU,可以用它加快训练。为了使用GPU,还需要一点小改动,在进行正向和反向传播之前,需要将每一小批量数据移动到我们指定的设备(例如GPU)上。将实现多层神经网络,因此将主要使用高级API。 以下训练函数假定从高级API创建的模型作为输入,并进行相应的优化。

  • 使用在 Xavier随机初始化模型参数。
  • 使用交叉熵损失函数和小批量随机梯度下降。
class TrainCallback(tf.keras.callbacks.Callback):  #@save
    """一个以可视化的训练进展的回调"""
    def __init__(self, net, train_iter, test_iter, num_epochs, device_name):
        self.timer = d2l.Timer()
        self.animator = d2l.Animator(
            xlabel='epoch', xlim=[1, num_epochs], legend=[
                'train loss', 'train acc', 'test acc'])
        self.net = net
        self.train_iter = train_iter
        self.test_iter = test_iter
        self.num_epochs = num_epochs
        self.device_name = device_name

    def on_epoch_begin(self, epoch, logs=None):
        self.timer.start()

    def on_epoch_end(self, epoch, logs):
        self.timer.stop()
        test_acc = self.net.evaluate(
            self.test_iter, verbose=0, return_dict=True)['accuracy']
        metrics = (logs['loss'], logs['accuracy'], test_acc)
        self.animator.add(epoch + 1, metrics)
        if epoch == self.num_epochs - 1:
            batch_size = next(iter(self.train_iter))[0].shape[0]
            num_examples = batch_size * tf.data.experimental.cardinality(
                self.train_iter).numpy()
            print(f'loss {metrics[0]:.3f}, train acc {metrics[1]:.3f}, '
                  f'test acc {metrics[2]:.3f}')
            print(f'{num_examples / self.timer.avg():.1f} examples/sec on '
                  f'{str(self.device_name)}')

#@save
def train_ch6(net_fn, train_iter, test_iter, num_epochs, lr, device):
    """用GPU训练模型(在第六章定义)"""
    device_name = device._device_name
    strategy = tf.distribute.OneDeviceStrategy(device_name)
    with strategy.scope():
        optimizer = tf.keras.optimizers.SGD(learning_rate=lr)
        loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
        net = net_fn()
        net.compile(optimizer=optimizer, loss=loss, metrics=['accuracy'])
    callback = TrainCallback(net, train_iter, test_iter, num_epochs,
                             device_name)
    net.fit(train_iter, epochs=num_epochs, verbose=0, callbacks=[callback])
    return net

训练和评估LeNet-5模型:

lr, num_epochs = 0.9, 10
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

在这里插入图片描述

3. TensorFlow实现LeNet

基于 MNIST 手写数字图片数据集训练 LeNet-5 网络,并测试其最终准确度。

在 LeNet-5 的基础上进行了少许调整,使得它更容易在现代深度学习框架上实 现。首先我们将输入𝒀形状由32 × 32调整为28 × 28,然后将 2 个下采样层实现为最大池化层(降低特征图的高、宽,后续会介绍),最后利用全连接层替换掉 Gaussian connections层。

import tensorflow as tf
# 获取GPU列表
gpus = tf.config.experimental.list_physical_devices('GPU')
if gpus:
    try:
        # 设置GPU为增长式占用
        for gpu in gpus:
            tf.config.experimental.set_memory_growth(gpu, True) 
    except RuntimeError as e:
        # 打印异常
        print(e)

# LeNet-5模型

from tensorflow.keras import Sequential,layers,losses
network = Sequential([ # 网络容器
    layers.Conv2D(6,kernel_size=3,strides=1), # 第一个卷积层, 6 个 3x3 卷积核
    layers.MaxPooling2D(pool_size=2,strides=2), # 高宽各减半的池化层
    layers.ReLU(), # 激活函数
    layers.Conv2D(16,kernel_size=3,strides=1), # 第二个卷积层, 16 个 3x3 卷积核
    layers.MaxPooling2D(pool_size=2,strides=2), # 高宽各减半的池化层
    layers.ReLU(), # 激活函数
    layers.Flatten(), # 打平层,方便全连接层处理
    layers.Dense(120, activation='relu'), # 全连接层,120 个节点
    layers.Dense(84, activation='relu'), # 全连接层,84 节点
    layers.Dense(10) # 全连接层,10 个节点
])
# build 一次网络模型,给输入 X 的形状,其中 4 为随意给的 batchsize
network.build(input_shape=(4, 28, 28, 1))
# 统计网络信息
network.summary()
from tensorflow.keras import datasets

# 准备数据
(x_train, y_train), (x_test, y_test) = datasets.mnist.load_data() # 加载 MNIST 数据集
x_train = 2*tf.convert_to_tensor(x_train, dtype=tf.float32)/255.-1 # 转换为浮点张量,并缩放到-1~1
y_train = tf.convert_to_tensor(y_train, dtype=tf.int32) # 转换为整形张量
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))  # 构建数据集对象
train_dataset = train_dataset.batch(64).repeat(10)  # 设置批量训练的batch为32,要将训练集重复训练10遍

x_test = 2*tf.convert_to_tensor(x_test, dtype=tf.float32)/255.-1 # 转换为浮点张量,并缩放到-1~1
y_test = tf.convert_to_tensor(y_test, dtype=tf.int32) # 转换为整形张量
test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))  # 构建数据集对象
test_dataset = test_dataset.batch(64)  # 

# 导入误差计算,优化器模块
from tensorflow.keras import losses, optimizers,metrics

# 创建损失函数的类,在实际计算时直接调用类实例即可
criteon = losses.CategoricalCrossentropy(from_logits=True)
acc_meter = metrics.Accuracy()  # 创建准确度测量器
optimizer = optimizers.SGD(lr=0.01)  # 声明采用批量随机梯度下降方法,学习率=0.01

for step, (x, y) in enumerate(train_dataset):  # 一次输入batch组数据进行训练
    # 构建梯度记录环境
    with tf.GradientTape() as tape:
        x = tf.expand_dims(x,axis=3)#添加一个维度[64,28,28,1]
        # 前向计算,获得 10 类别的概率分布,[b, 784] => [b, 10]
        out = network(x)
        # 真实标签 one-hot 编码,[b] => [b, 10]
        y_onehot = tf.one_hot(y, depth=10)
        # 计算交叉熵损失函数,标量
        loss = criteon(y_onehot, out)
        # 自动计算梯度
        grads = tape.gradient(loss, network.trainable_variables)
        # 自动更新参数
        optimizer.apply_gradients(zip(grads, network.trainable_variables))
        acc_meter.update_state(tf.argmax(out, axis=1), y)  # 比较预测值与标签,并计算精确度
    if step % 200 == 0:  # 每200个step,打印一次结果
        print('Step', step, ': Loss is: ', float(loss), ' Accuracy: ', acc_meter.result().numpy())
        acc_meter.reset_states()  # 每一个step后准确度清零
"""
测试集评估
"""

# 记录预测正确的数量,总样本数量
correct, total = 0,0
for step, (x, y) in enumerate(test_dataset): # 遍历所有测试集样本
    # 插入通道维度,=>[b,28,28,1]
    x = tf.expand_dims(x,axis=3)
    # 前向计算,获得 10 类别的预测分布,[b, 784] => [b, 10]
    out = network(x)
#     # 真实的流程时先经过 softmax,再 argmax
#     # 但是由于 softmax 不改变元素的大小相对关系,故省去
    pred = tf.argmax(out, axis=-1)
    y = tf.cast(y, tf.int64)
#     # 统计预测正确数量
    correct += float(tf.reduce_sum(tf.cast(tf.equal(pred, y),tf.float32)))
#     # 统计预测样本总数
    total += x.shape[0]
# 计算准确率
print('test acc:', correct/total)

在数据集上面循环训练 30 个 Epoch 后,网络的训练准确度达到了 98.1%,测试准确度 也达到了 97.7%。对于非常简单的手写数字图片识别任务,古老的 LeNet-5 网络已经可以 取得很好的效果,但是稍复杂一点的任务,比如彩色动物图片识别,LeNet-5 性能就会急 剧下降。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SAR ADC教程系列5——FFT频谱泄露以及相干采样

频谱泄露的出现以及如何规避? 为什么要相干采样? 1.分析ADC输出信号的频谱工具:DFT(Discrete Fourier Transform) 重点:DFT相邻频谱频率间隔为fs/N 如何规避频谱泄露? 对于DFT,它对于接收到的信…

Qt文件读写

做一个简单的文件读写,我们把一个结构体内的数据写入到二进制文件中,并重新读取解析。代码结构如下: 项目名称随便起就好了。main.cpp是主函数;DataHandler实现文件的写与读,还要模拟过程;Definition.h放置…

机器学习-绪论

机器学习致力于研究如何通过计算的手段、利用经验来改善系统自身的性能。在计算机系统中,“经验”通常以“数据”的形式存在,因此,机器学习所研究的主要内容,是关于在计算机上从数据中产生“模型”的算法,即“学习算法…

NPM 仓库的超集 JSR 来了!

引言 今天在 Deno 博客中看到了一篇文章,介绍了一个叫 JSR 的包管理注册中心,简单尝试了一下觉得还不错,本文将结合原文章和个人体验对 JSR 进行一个详细的介绍。 在现如今的前端开发中,包管理注册中心 (如 npmjs.com) 扮演着至…

Git——本地使用详解

目录 Git1、开始版本控制1.1、初始化Repository1.2、使目录脱离Git控制 2、把文件交给Git管控2.1、创建文件后交给Git2.2、git add之后再次修改文件2.3、git add "--all"与"."参数区别2.4、把暂存区的内容提交到存储库里存档 3、工作区、暂存区与存储库3.1…

Epuck2机器人固件更新及IP查询

文章目录 前言一、下载固件更新软件包:二、查询机器人在局域网下的IP 前言 前面进行了多机器人编队仿真包括集中式和分布式,最近打算在实物机器人上跑一跑之前的编队算法。但由于Epuck2机器人长时间没使用,故对其进行固件的更新,…

软考高级:软件架构本质和作用概念和例题

作者:明明如月学长, CSDN 博客专家,大厂高级 Java 工程师,《性能优化方法论》作者、《解锁大厂思维:剖析《阿里巴巴Java开发手册》》、《再学经典:《Effective Java》独家解析》专栏作者。 热门文章推荐&am…

【PyTorch】进阶学习:一文详细介绍 torch.save() 的应用场景、实战代码示例

【PyTorch】进阶学习:一文详细介绍 torch.save() 的应用场景、实战代码示例 🌈 个人主页:高斯小哥 🔥 高质量专栏:Matplotlib之旅:零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程…

Vue组件封装方案对比——v-if方式与内置component方式

近期在准备搭建一个通用组件库,而公司现有的各个系统也已有自己的组件库只是没抽离出来,但是目前有两套不同的组件封装方案,所以对于方案的选择比较困惑,于是对两种方式进行了对比,结合网上找到的一些开源组件库进行分…

wireshark解析https数据包

Debian11环境: 在linux环境下抓取访问某个https的网址时抓取的数据包都是加密的,导致无法跟踪到数据包流,现在尝试将抓取的https包进行解密。 1、解密https数据包需要设置SSLKEYLOGFILE变量,推荐写入配置文件中。 echo "exp…

Mysql的行级锁

MySQL 中锁定粒度最小的一种锁,是 针对索引字段加的锁 ,只针对当前操作的行记录进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。行级锁和存…

Ps:文字工具

工具箱里的文字工具组中包含了四种工具: 横排文字工具 Horizontal Type Tool 直排文字工具 Vertical Type Tool 横排文字蒙版工具 Horizontal Type Mask Tool 直排文字蒙版工具 Vertical Type Mask Tool 快捷键:T 横排文字蒙版工具和直排文字蒙版工具…

C++第六弹---类与对象(三)

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】 目录 1、类的6个默认成员函数 2、构造函数 2.1、概念 2.2、特性 3、析构函数 3.1、概念 3.2、特性 3.3、调用顺序 总结 1、类的6个默认成员函数…

力扣hot100:33. 搜索旋转排序数组(二分的理解)

33.搜索旋转排序数组 ​ 这是一个非常有趣的问题,如果不要求使用O(logn)应该没人会想到吧。。 方法一: 极致的分类讨论。旋转排序数组,无非就是右边的增区间的数小于左边的增区间的数,然后依次排序。因此我们只需要分三类讨论即可…

【测试开发学习历程】MySQL数据类型 + MySQL表创建与操作

前言: 半夜梦到自己没有写今天的博客,结果惊醒起来看一看。 得,真的没写。QWQ 可谓垂死病中惊坐起了。 看看发博的时间6:16,而不是什么整点的,就知道我4点就起来了,不是定时发布&#xff01…

知识积累(五):Transformer 家族的学习笔记

文章目录 1. RNN1.1 缺点 2. Transformer2.1 组成2.2 Encoder2.2.1 Input Embedding(嵌入层)2.2.2 位置编码2.2.3 多头注意力2.2.4 Add & Norm 2.3 Decoder2.3.1 概览2.3.2 Masked multi-head attention 2.4 Transformer 模型的训练和推理2.4.1 训练…

C语言学习过程总结(16)——指针(4)

一、数组名的理解 我们直接使用%p打印出地址来看看&arr【0】 和 arr的不同: int main() {int arr[10] { 1,2,3,4,5,6,7,8,9,10 };printf("&arr[0] %p\n", &arr[0]);printf("arr %p\n", arr);} 、 很容易看出来两者的输出…

ES模块化

Node.js默认并不支持ES模块化,如果需要使用可以采用两种方式。方式一,直接将所有的js文件修改为mjs扩展名。方式二,修改package.json中type属性为module。 导出 默认导出 // 向外部导出内容 export let a 10 export const b "孙悟空…

数据分析 | NumPy

NumPy,全称是 Numerical Python,它是目前 Python 数值计算中最重要的基础模块。NumPy 是针对多维数组的一个科学计算模块,这个模块封装了很多数组类型的常用操作。 使用numpy来创建数组 import numpy as npdata np.array([1, 2, 3]) print…

Unity中UGUI中的PSD导入工具的原理和作用

先说一下PSD导入工具的作用,比如在和美术同事合作开发一个背包UI业务系统时,美术做好效果图后,程序在UGUI中制作好界面,美术说这个图差了2像素,那个图位置不对差了1像素,另外一个图大小不对等等一系列零碎的…