LeNet-5实战

一、LeNet-5介绍

LeNet-5用于手写数字和机器打印字符图片识别的神经网络。

LeNet-5 的网络结构图,它接受32 × 32大小的数字、字符图片,经过第一个卷积层得到[b, 28,28,6] 形状的张量,经过一个向下采样层,张量尺寸缩小到[b,14,14,6] ,经过第二个卷积层,得到[b,10,10,16] 形状的张量,同样经过下采样层,张量尺寸缩小到[b,5,5,16]],在进入全连接层之前,先将张量打成[b,400]的张量,送入输出节点数分别为 120、 84 的 2 个全连接层,得到 [b,84] 的张量,最后通过 Gaussian connections 层

基于 MNIST 手写数字图片数据集训练 LeNet-5 网络,并测试其最终准确度。将输入𝑿形状由32 × 32调整为28 × 28,然后将 2 个下采样层实现为最大池化层,Gaussian connections 层替换为输出维度为10的全连接层。

二、LeNet-5实现

1.数据集加载以及数据集的预处理

# 预处理
def preprocess(x, y):
    # x :[-1,1]
    x = 2 * tf.cast(x, dtype=tf.float32) / 255 - 1
    y = tf.cast(y, dtype=tf.int32)
    return x, y

# 加载数据集
(x, y), (x_text, y_text) = datasets.mnist.load_data()

# 创建batch
train_db = tf.data.Dataset.from_tensor_slices((x, y))
train_db = train_db.map(preprocess).shuffle(1000).batch(128)

test_db = tf.data.Dataset.from_tensor_slices((x, y))
test_db = test_db.map(preprocess).batch(128)

# 获取下一个batch
sample = next(iter(test_db))

mnist数据集从网上下载,并将它分为训练集核测试集。得到的训练集数据类型为Numpy,需要将他们转换为张量类型。为了防止出现梯度弥撒,将x从[0,255]标准化到[-1,1]之间。

 tf.data.Dataset.from_tensor_slices的作用将输入的张量或者Numpy类型的第一个维度看做样本的个数,沿前第一个维度将tensor(Numpy格式会自动转为tensor)切片,实现了输入张量的自动化切片,这里x的形状[60k, 28,28], 所以会切成60k张图片。然后将整个数据集分为128小样本的批处理,每一次循环处理一个batch

2.网络模型构建

 网络结构如下:

使用Sequential容器,生成Sequential类的一个实例

# 建立网络
network = Sequential([
    layers.Conv2D(6, kernel_size=3, strides=1),  # 第一个卷积层, 6个3x3卷积核
    layers.MaxPool2D(pool_size=2, strides=2),  # 高宽各减半的池化层
    layers.ReLU(),
    layers.Conv2D(16, kernel_size=3, strides=1),  # 第一个卷积层, 6个3x3卷积核
    layers.MaxPool2D(pool_size=2, strides=2),  # 高宽各减半的池化层
    layers.ReLU(),
    layers.Flatten(), # 打平层,方便全连接层处理
    layers.Dense(120, activation='relu'),
    layers.Dense(60, activation='relu'),
    layers.Dense(10)
])
# build 一次网络模型,给输入 X 的形状,其中 4 为随意给的 batchsz
network.build(input_shape=(4, 28, 28, 1))
# 统计网络信息
network.summary()

在卷积层与全连接层不能之间相连,全连接层需要传入的数据必须是一维的,所以需要使用Flatten将多维数据打平成一维数据。成员函数build, summary完成网络权值,偏置和输入维度的初始化与网络模型参数状况的输出

3.网络装配

在训练网络时,一般的流程是通过前向计算获得网络的输出值, 再通过损失函数计算网络误差,然后通过自动求导工具计算梯度并更新,同时间隔性地测试网络的性能。

所以,在完成网络模型的搭建后,需要指定网络使用的优化器对象、 损失函数类型, 评价指标等设定,这一步称为装配

# 创建损失函数类
criteon = losses.CategoricalCrossentropy(from_logits=True)

# w = w - lr * grad
# 学习率的设置,更新参数
optimizers = optimizers.Adam(learning_rate=1e-3)

优化器主要使用apply_gradients方法传入变量和对应梯度从而来对给定变量进行迭代

4.计算梯度,代价函数并更新参数

            with tf.GradientTape() as tape:
                # 插入通道维度 =>[b,28,28,1]
                x = tf.expand_dims(x, axis=3)
                # 向前计算,获得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)
            # 自动跟新参数
            optimizers.apply_gradients(zip(grads, network.trainable_variables))

在使用自动求导功能计算梯度,需要将向前计算过程放置在tf.GradientTape()环境中, 利用GradientTape对象的gradient()方法自动求解参数的梯度, 并利用optimizers对象更新参数

由于卷积层需要输出数据的通道数量,卷积核的深度必须和输入图像的深度一样,因此需要增加第3个维度

5.测试

# 测试集,记录预测正确的数量,总样本数量
        total_correct, total = 0, 0
        for x, y in test_db:
            # 插入通道维数
            x = tf.expand_dims(x, axis=3)
            # 前向计算,获得 10 类别的预测分布, [b, 784] => [b, 10]
            out = network(x)
            # 将输出结果归一化处理,得到和为1的概率
            prob = tf.nn.softmax(out, axis=1)  # [0,1]
            # 找到对应维度最大值的索引位置
            pred = tf.argmax(prob, axis=1)
            pred = tf.cast(pred, dtype=tf.int32)
            # pred:[b]
            # y: [b]
            # correct: [b], True(1): equal; False(0): not equal
            correct = tf.equal(pred, y)
            correct = tf.reduce_sum(tf.cast(correct, dtype=tf.int32))
            # 预测对的数量
            total_correct += int(correct)
            # 统计预测样本总数
            total += x.shape[0]
    
        # acc
        print('acc:', total_correct / total)

在测试阶段,由于不需要记录梯度信息,代码一般不需要写在 with tf.GradientTape() as tape 环境中。前向计算得到的输出经过 softmax 函数后,代表了网络预测当前图片输入𝒙属于类别𝑖的概率𝑃(𝒙标签是𝑖|𝑥), 𝑖 ∈ 9 。通过 argmax 函数选取概率最大的元素所在的索引,作为当前𝒙的预测类别,与真实标注𝑦比较,通过计算比较结果中间 True 的数量并求和来统计预测正确的样本的个数,最后除以总样本的个数,得出网络的测试准确度

网络训练了2个epoch的准确率可以达到0.98,多训练几次可能还会高一些。

三、完整程序

# -*- codeing = utf-8 -*-
# @Time : 11:07
# @Author:Paranipd
# @File : LeNet-5-test.py
# @Software:PyCharm

import os
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets, layers, optimizers, Sequential, losses  # 数据集, 网络层, 分类器, 容器

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'  # 去掉不必要的报错

# 预处理
def preprocess(x, y):
    # x :[-1,1]
    x = 2 * tf.cast(x, dtype=tf.float32) / 255 - 1
    y = tf.cast(y, dtype=tf.int32)
    return x, y

# 加载数据集
(x, y), (x_text, y_text) = datasets.mnist.load_data()

# 创建batch
train_db = tf.data.Dataset.from_tensor_slices((x, y))
train_db = train_db.map(preprocess).shuffle(1000).batch(128)

test_db = tf.data.Dataset.from_tensor_slices((x, y))
test_db = test_db.map(preprocess).batch(128)

# 获取下一个batch
sample = next(iter(test_db))


# 建立网络
network = Sequential([
    layers.Conv2D(6, kernel_size=3, strides=1),  # 第一个卷积层, 6个3x3卷积核
    layers.MaxPool2D(pool_size=2, strides=2),  # 高宽各减半的池化层
    layers.ReLU(),
    layers.Conv2D(16, kernel_size=3, strides=1),  # 第一个卷积层, 6个3x3卷积核
    layers.MaxPool2D(pool_size=2, strides=2),  # 高宽各减半的池化层
    layers.ReLU(),
    layers.Flatten(), # 打平层,方便全连接层处理
    layers.Dense(120, activation='relu'),
    layers.Dense(60, activation='relu'),
    layers.Dense(10)
])
# build 一次网络模型,给输入 X 的形状,其中 4 为随意给的 batchsz
network.build(input_shape=(4, 28, 28, 1))
# 统计网络信息
network.summary()

# 创建损失函数类
criteon = losses.CategoricalCrossentropy(from_logits=True)

# w = w - lr * grad
# 学习率的设置,更新参数
optimizers = optimizers.Adam(learning_rate=1e-3)

def main():
    # Step4.loop
    for epoch in range(50):
        for step, (x, y) in enumerate(train_db):
            with tf.GradientTape() as tape:
                # 插入通道维度 =>[b,28,28,1]
                x = tf.expand_dims(x, axis=3)
                # 向前计算,获得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)
            # 自动跟新参数
            optimizers.apply_gradients(zip(grads, network.trainable_variables))

            if step % 100 == 0:
                print(epoch, step, 'loss', float(loss))


        # 测试集,记录预测正确的数量,总样本数量
        total_correct, total = 0, 0
        for x, y in test_db:
            # 插入通道维数
            x = tf.expand_dims(x, axis=3)
            # 前向计算,获得 10 类别的预测分布, [b, 784] => [b, 10]
            out = network(x)
            # 将输出结果归一化处理,得到和为1的概率
            prob = tf.nn.softmax(out, axis=1)  # [0,1]
            # 找到对应维度最大值的索引位置
            pred = tf.argmax(prob, axis=1)
            pred = tf.cast(pred, dtype=tf.int32)
            # pred:[b]
            # y: [b]
            # correct: [b], True(1): equal; False(0): not equal
            correct = tf.equal(pred, y)
            correct = tf.reduce_sum(tf.cast(correct, dtype=tf.int32))
            # 预测对的数量
            total_correct += int(correct)
            # 统计预测样本总数
            total += x.shape[0]

        # acc
        print('acc:', total_correct / total)

if __name__ == "__main__":
    main()

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
LeNet-5是一个经典的卷积神经网络模型,最初由Yann LeCun提出,用于手写数字识别。Lenet-5的结构相对简单,但在计算机视觉领域的早期任务中具有较好的性能。 在TensorFlow 2中实现Lenet-5可以分为几个步骤。首先,我们需要导入所需的库,包括tensorflow、numpy和matplotlib等。接下来,我们可以定义Lenet-5模型的架构。 Lenet-5的架构包括两个卷积层、两个池化层和三个全连接层。我们可以使用tf.keras.Sequential()来定义模型,并逐层添加卷积层、池化层和全连接层。在每个卷积层后面,我们可以使用ReLU激活函数对输出进行非线性处理。 在定义完模型架构后,我们可以编译模型,指定优化器、损失函数和评价指标等。一般来说,我们可以选择Adam优化器,交叉熵损失函数,并将准确率作为评价指标。 然后,我们需要准备数据。Lenet-5最初是用于手写数字识别,因此我们可以使用MNIST数据集来进行训练和测试。我们可以使用tf.keras.datasets模块中的mnist.load_data()函数加载MNIST数据集,并将其分为训练集和测试集。 接下来,我们可以对数据进行预处理,包括将像素值缩放到0到1之间,并将标签数据转换为One-Hot编码。 最后,我们可以使用.fit()函数对模型进行训练,并使用测试集评估模型性能。我们可以指定训练的epoch数和批量大小,并在训练过程中观察训练集和验证集上的准确率和损失值。 这样,我们就能够在TensorFlow 2中实现并使用Lenet-5模型进行手写数字识别。当然,我们还可以尝试调整模型参数和网络结构,以进一步提高模型的性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super.Bear

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值