热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

百度深度学习手写数字识别之网络结构

概述前几节我们尝试使用与房价预测相同的简单神经网络解决手写数字

概述

前几节我们尝试使用与房价预测相同的简单神经网络解决手写数字识别问题,但是效果并不理想。原因是手写数字识别的输入是28 × 28的像素值,输出是0-9的数字标签,而线性回归模型无法捕捉二维图像数据中蕴含的复杂信息,如 图1 所示。无论是牛顿第二定律任务,还是房价预测任务,输入特征和输出预测值之间的关系均可以使用“直线”刻画(使用线性方程来表达)。但手写数字识别任务的输入像素和输出数字标签之间的关系显然不是线性的,甚至这个关系复杂到我们靠人脑难以直观理解的程度。


图1:数字识别任务的输入和输入不是线性关系

因此,我们需要尝试使用其他更复杂、更强大的网络来构建手写数字识别任务,观察一下训练效果,即将“横纵式”教学法从横向展开,如 图2 所示。本节主要介绍两种常见的网络结构:经典的多层全连接神经网络和卷积神经网络。


图2:“横纵式”教学法 — 网络结构优化

数据处理

在介绍网络结构前,需要先进行数据处理,代码与上一节保持一致。

#数据处理部分之前的代码,保持不变
import os
import random
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
import gzip
import json
# 定义数据集读取器
def load_data(mode='train'):
# 数据文件
datafile = './work/mnist.json.gz'
print('loading mnist dataset from {} ......'.format(datafile))
data = json.load(gzip.open(datafile))
train_set, val_set, eval_set = data
# 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS
IMG_ROWS = 28
IMG_COLS = 28
if mode == 'train':
imgs = train_set[0]
labels = train_set[1]
elif mode == 'valid':
imgs = val_set[0]
labels = val_set[1]
elif mode == 'eval':
imgs = eval_set[0]
labels = eval_set[1]
imgs_length = len(imgs)
assert len(imgs) == len(labels), \
"length of train_imgs({}) should be the same as train_labels({})".format(
len(imgs), len(labels))
index_list = list(range(imgs_length))
# 读入数据时用到的batchsize
BATCHSIZE = 100
# 定义数据生成器
def data_generator():
if mode == 'train':
random.shuffle(index_list)
imgs_list = []
labels_list = []
for i in index_list:
img = np.reshape(imgs[i], [1, IMG_ROWS, IMG_COLS]).astype('float32')
label = np.reshape(labels[i], [1]).astype('float32')
imgs_list.append(img)
labels_list.append(label)
if len(imgs_list) == BATCHSIZE:
yield np.array(imgs_list), np.array(labels_list)
imgs_list = []
labels_list = []
# 如果剩余数据的数目小于BATCHSIZE,
# 则剩余数据一起构成一个大小为len(imgs_list)的mini-batch
if len(imgs_list) > 0:
yield np.array(imgs_list), np.array(labels_list)
return data_generator

2020-03-26 15:24:28,868-INFO: font search path ['/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/ttf', '/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/afm', '/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/mpl-data/fonts/pdfcorefonts']
2020-03-26 15:24:29,250-INFO: generated new fontManager
经典的全连接神经网络

经典的全连接神经网络来包含四层网络:两个隐含层,输入层和输出层,将手写数字识别任务通过全连接神经网络表示,如 图3 所示。


图3:手写数字识别任务的全连接神经网络结构

  • 输入层:将数据输入给神经网络。在该任务中,输入层的尺度为28×28的像素值。
  • 隐含层:增加网络深度和复杂度,隐含层的节点数是可以调整的,节点数越多,神经网络表示能力越强,参数量也会增加。在该任务中,中间的两个隐含层为10×10的结构,通常隐含层会比输入层的尺寸小,以便对关键信息做抽象,激活函数使用常见的sigmoid函数。
  • 输出层:输出网络计算结果,输出层的节点数是固定的。如果是回归问题,节点数量为需要回归的数字数量。如果是分类问题,则是分类标签的数量。在该任务中,模型的输出是回归一个数字,输出层的尺寸为1。

说明:

隐含层引入非线性激活函数sigmoid是为了增加神经网络的非线性能力。

举例来说,如果一个神经网络采用线性变换,有四个输入x 1 x_1x1~x 4 x_4x4,一个输出y yy。假设第一层的变换是z 1 = x 1 − x 2 z_1=x_1-x_2z1=x1x2z 2 = x 3 + x 4 z_2=x_3+x_4z2=x3+x4,第二层的变换是y = z 1 + z 2 y=z_1+z_2y=z1+z2,则将两层的变换展开后得到y = x 1 − x 2 + x 3 + x 4 y=x_1-x_2+x_3+x_4y=x1x2+x3+x4。也就是说,无论中间累积了多少层线性变换,原始输入和最终输出之间依然是线性关系。


Sigmoid是早期神经网络模型中常见的非线性变换函数,通过如下代码,绘制出Sigmoid的函数曲线。

def sigmoid(x):
# 直接返回sigmoid函数
return 1. / (1. + np.exp(-x))

# param:起点,终点,间距
x = np.arange(-8, 8, 0.2)
y = sigmoid(x)
plt.plot(x, y)
plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qiNrO8x6-1586597881005)(output_3_0.png)]

针对手写数字识别的任务,网络层的设计如下:

  • 输入层的尺度为28×28,但批次计算的时候会统一加1个维度(大小为bitchsize)。
  • 中间的两个隐含层为10×10的结构,激活函数使用常见的sigmoid函数。
  • 与房价预测模型一样,模型的输出是回归一个数字,输出层的尺寸设置成1。

下述代码为经典全连接神经网络的实现。完成网络结构定义后,即可训练神经网络。

# 多层全连接神经网络实现
class MNIST(fluid.dygraph.Layer):
def __init__(self, name_scope):
super(MNIST, self).__init__(name_scope)
# 定义两层全连接隐含层,输出维度是10,激活函数为sigmoid
self.fc1 = Linear(input_dim=784, output_dim=10, act='sigmoid') # 隐含层节点为10,可根据任务调整
self.fc2 = Linear(input_dim=10, output_dim=10, act='sigmoid')
# 定义一层全连接输出层,输出维度是1,不使用激活函数
self.fc3 = Linear(input_dim=10, output_dim=1, act=None)

# 定义网络的前向计算
def forward(self, inputs, label=None):
inputs = fluid.layers.reshape(inputs, [inputs.shape[0], 784])
outputs1 = self.fc1(inputs)
outputs2 = self.fc2(outputs1)
outputs_final = self.fc3(outputs2)
return outputs_final

#网络结构部分之后的代码,保持不变
with fluid.dygraph.guard():
model = MNIST("mnist")
model.train()
#调用加载数据的函数,获得MNIST训练数据集
train_loader = load_data('train')
# 使用SGD优化器,learning_rate设置为0.01
optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
# 训练5轮
EPOCH_NUM = 5
for epoch_id in range(EPOCH_NUM):
for batch_id, data in enumerate(train_loader()):
#准备数据
image_data, label_data = data
image = fluid.dygraph.to_variable(image_data)
label = fluid.dygraph.to_variable(label_data)

#前向计算的过程
predict = model(image)

#计算损失,取一个批次样本损失的平均值
loss = fluid.layers.square_error_cost(predict, label)
avg_loss = fluid.layers.mean(loss)

#每训练了200批次的数据,打印下当前Loss的情况
if batch_id % 200 == 0:
print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))

#后向传播,更新参数的过程
avg_loss.backward()
optimizer.minimize(avg_loss)
model.clear_gradients()
#保存模型参数
fluid.save_dygraph(model.state_dict(), 'mnist')

loading mnist dataset from ./work/mnist.json.gz ......
epoch: 0, batch: 0, loss is: [27.740425]
epoch: 0, batch: 200, loss is: [5.4588423]
epoch: 0, batch: 400, loss is: [3.9063952]
epoch: 1, batch: 0, loss is: [3.8620145]
epoch: 1, batch: 200, loss is: [4.6423216]
epoch: 1, batch: 400, loss is: [3.9099925]
epoch: 2, batch: 0, loss is: [3.3493927]
epoch: 2, batch: 200, loss is: [2.8054562]
epoch: 2, batch: 400, loss is: [2.8475616]
epoch: 3, batch: 0, loss is: [3.1059093]
epoch: 3, batch: 200, loss is: [2.8764062]
epoch: 3, batch: 400, loss is: [2.248354]
epoch: 4, batch: 0, loss is: [2.3325133]
epoch: 4, batch: 200, loss is: [2.9140906]
epoch: 4, batch: 400, loss is: [1.6771106]
卷积神经网络

虽然使用经典的神经网络可以提升一定的准确率,但对于计算机视觉问题,效果最好的模型仍然是卷积神经网络。卷积神经网络针对视觉问题的特点进行了网络结构优化,更适合处理视觉问题。

卷积神经网络由多个卷积层和池化层组成,如 图4 所示。卷积层负责对输入进行扫描以生成更抽象的特征表示,池化层对这些特征表示进行过滤,保留最关键的特征信息。


图4:在处理计算机视觉任务中大放异彩的卷积神经网络


说明:

本节只介绍手写数字识别在卷积神经网络的实现以及它带来的效果提升。读者可以将卷积神经网络先简单的理解成是一种比经典的全连接神经网络更强大的模型即可,更详细的原理和实现在接下来的第四章-计算机视觉-卷积神经网络基础中讲述。


两层卷积和池化的神经网络实现如下代码所示。

# 多层卷积神经网络实现
class MNIST(fluid.dygraph.Layer):
def __init__(self, name_scope):
super(MNIST, self).__init__(name_scope)

# 定义卷积层,输出特征通道num_filters设置为20,卷积核的大小filter_size为5,卷积步长stride=1,padding=2
# 激活函数使用relu
self.conv1 = Conv2D(num_channels=1, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
# 定义池化层,池化核pool_size=2,池化步长为2,选择最大池化方式
self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
# 定义卷积层,输出特征通道num_filters设置为20,卷积核的大小filter_size为5,卷积步长stride=1,padding=2
self.conv2 = Conv2D(num_channels=20, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
# 定义池化层,池化核pool_size=2,池化步长为2,选择最大池化方式
self.pool2 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
# 定义一层全连接层,输出维度是1,不使用激活函数
self.fc = Linear(input_dim=980, output_dim=1, act=None)

# 定义网络前向计算过程,卷积后紧接着使用池化层,最后使用全连接层计算最终输出
def forward(self, inputs):
x = self.conv1(inputs)
x = self.pool1(x)
x = self.conv2(x)
x = self.pool2(x)
x = fluid.layers.reshape(x, [x.shape[0], -1])
x = self.fc(x)
return x

训练定义好的卷积神经网络,代码如下所示。

#网络结构部分之后的代码,保持不变
with fluid.dygraph.guard():
model = MNIST("mnist")
model.train()
#调用加载数据的函数
train_loader = load_data('train')
optimizer = fluid.optimizer.SGDOptimizer(learning_rate=0.01, parameter_list=model.parameters())
EPOCH_NUM = 5
for epoch_id in range(EPOCH_NUM):
for batch_id, data in enumerate(train_loader()):
#准备数据
image_data, label_data = data
image = fluid.dygraph.to_variable(image_data)
label = fluid.dygraph.to_variable(label_data)

#前向计算的过程
predict = model(image)

#计算损失,取一个批次样本损失的平均值
loss = fluid.layers.square_error_cost(predict, label)
avg_loss = fluid.layers.mean(loss)

#每训练了100批次的数据,打印下当前Loss的情况
if batch_id % 200 == 0:
print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy()))

#后向传播,更新参数的过程
avg_loss.backward()
optimizer.minimize(avg_loss)
model.clear_gradients()
#保存模型参数
fluid.save_dygraph(model.state_dict(), 'mnist')

loading mnist dataset from ./work/mnist.json.gz ......
epoch: 0, batch: 0, loss is: [31.675833]
epoch: 0, batch: 200, loss is: [9.248349]
epoch: 0, batch: 400, loss is: [3.2532346]
epoch: 1, batch: 0, loss is: [2.5735705]
epoch: 1, batch: 200, loss is: [2.7086043]
epoch: 1, batch: 400, loss is: [2.351327]
epoch: 2, batch: 0, loss is: [2.2003784]
epoch: 2, batch: 200, loss is: [2.53069]
epoch: 2, batch: 400, loss is: [2.154322]
epoch: 3, batch: 0, loss is: [1.8227897]
epoch: 3, batch: 200, loss is: [1.8546791]
epoch: 3, batch: 400, loss is: [2.3879793]
epoch: 4, batch: 0, loss is: [2.6370738]
epoch: 4, batch: 200, loss is: [1.6437341]
epoch: 4, batch: 400, loss is: [1.6468849]

比较经典全连接神经网络和卷积神经网络的损失变化,可以发现卷积神经网络的损失值下降更快,且最终的损失值更小。


推荐阅读
  • 在机器学习领域,深入探讨了概率论与数理统计的基础知识,特别是这些理论在数据挖掘中的应用。文章重点分析了偏差(Bias)与方差(Variance)之间的平衡问题,强调了方差反映了不同训练模型之间的差异,例如在K折交叉验证中,不同模型之间的性能差异显著。此外,还讨论了如何通过优化模型选择和参数调整来有效控制这一平衡,以提高模型的泛化能力。 ... [详细]
  • 本文详细介绍了在CentOS 6.5 64位系统上使用阿里云ECS服务器搭建LAMP环境的具体步骤。首先,通过PuTTY工具实现远程连接至服务器。接着,检查当前系统的磁盘空间使用情况,确保有足够的空间进行后续操作,可使用 `df` 命令进行查看。此外,文章还涵盖了安装和配置Apache、MySQL和PHP的相关步骤,以及常见问题的解决方法,帮助用户顺利完成LAMP环境的搭建。 ... [详细]
  • 【图像分类实战】利用DenseNet在PyTorch中实现秃头识别
    本文详细介绍了如何使用DenseNet模型在PyTorch框架下实现秃头识别。首先,文章概述了项目所需的库和全局参数设置。接着,对图像进行预处理并读取数据集。随后,构建并配置DenseNet模型,设置训练和验证流程。最后,通过测试阶段验证模型性能,并提供了完整的代码实现。本文不仅涵盖了技术细节,还提供了实用的操作指南,适合初学者和有经验的研究人员参考。 ... [详细]
  • 大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式
    大类|电阻器_使用Requests、Etree、BeautifulSoup、Pandas和Path库进行数据抓取与处理 | 将指定区域内容保存为HTML和Excel格式 ... [详细]
  • Python 数据可视化实战指南
    本文详细介绍如何使用 Python 进行数据可视化,涵盖从环境搭建到具体实例的全过程。 ... [详细]
  • Python 序列图分割与可视化编程入门教程
    本文介绍了如何使用 Python 进行序列图的快速分割与可视化。通过一个实际案例,详细展示了从需求分析到代码实现的全过程。具体包括如何读取序列图数据、应用分割算法以及利用可视化库生成直观的图表,帮助非编程背景的用户也能轻松上手。 ... [详细]
  • 能够感知你情绪状态的智能机器人即将问世 | 科技前沿观察
    本周科技前沿报道了多项重要进展,包括美国多所高校在机器人技术和自动驾驶领域的最新研究成果,以及硅谷大型企业在智能硬件和深度学习技术上的突破性进展。特别值得一提的是,一款能够感知用户情绪状态的智能机器人即将问世,为未来的人机交互带来了全新的可能性。 ... [详细]
  • 探讨Redis的最佳应用场景
    本文将深入探讨Redis在不同场景下的最佳应用,包括其优势和适用范围。 ... [详细]
  • 深入解析HTML5字符集属性:charset与defaultCharset
    本文将详细介绍HTML5中新增的字符集属性charset和defaultCharset,帮助开发者更好地理解和应用这些属性,以确保网页在不同环境下的正确显示。 ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 如何在Java中使用DButils类
    这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
  • 开发日志:高效图片压缩与上传技术解析 ... [详细]
  • 在《Cocos2d-x学习笔记:基础概念解析与内存管理机制深入探讨》中,详细介绍了Cocos2d-x的基础概念,并深入分析了其内存管理机制。特别是针对Boost库引入的智能指针管理方法进行了详细的讲解,例如在处理鱼的运动过程中,可以通过编写自定义函数来动态计算角度变化,利用CallFunc回调机制实现高效的游戏逻辑控制。此外,文章还探讨了如何通过智能指针优化资源管理和避免内存泄漏,为开发者提供了实用的编程技巧和最佳实践。 ... [详细]
  • Python编程中条件控制下的循环终止机制详解 ... [详细]
  • 如何优化MySQL数据库性能以提升查询效率和系统稳定性 ... [详细]
author-avatar
tuiqiu
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有