飞桨PaddlePaddle-百度架构师手把手带你零基础实践深度学习——21日学习总结

飞桨PaddlePaddle-百度架构师手把手带你零基础实践深度学习——21日学习总结

写在前面的话

纯新手小白,第一次接触深度学习方面的应用。感谢飞桨提供的这次学习机会。之前有学习到深度学习的理论方面的知识,但是到实践,总觉得无从下手,一筹莫展。刚好被朋友推荐一起学习该门课程,感觉受益匪浅。一个好的入门老师可以带来无限的可能。感谢平台提供的机会,感谢几位老师的教导和经验分享。
PS:本人自己更喜欢纸质的笔记,所以可能会上传大量图片。

第一章 Python基础

python作为一种“神奇的”编程语言,有着丰富的【办法】和【函数】,它更像是一种工具,简单快捷(尽管我深爱C++)。我习惯把它的基础语言分为以下五种,即:print语句,列表(含元组)字典,循环判断,函数&类, 文件操作。

print语句

python 的 print 语句简单粗暴,就是print。

输出目标 print语句
输出字符串 print(“字符串”)/print(‘字符串’)
输出变量 print(变量名)

字符串与字符串之间,可以用逗号(,)或者加号(+)连接,变量名之间只能用加号连接。
在这里插入图片描述
与print相关的一些函数以及方法还有

列表list & 字典dic

列表和元组相似,区别在于元组中的数据不能更改而列表可以。它们二者的表现区别在于前者(列表)用方括号"[]“表示,而元组用圆括号”()"表示。
在这里插入图片描述
列表的相关函数有:
在这里插入图片描述
字典是由键值对组成的。
在这里插入图片描述
在这里插入图片描述
字典还可以与列表,字典进行嵌套。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

循环 & 判断

python的循环和判断语句都是用缩进来表示的。
循环主要是for循环和while循环。
for 循环
在这里插入图片描述
在这里插入图片描述
while 循环
在这里插入图片描述
if 判断语句
在这里插入图片描述

函数 & 类class

函数
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

文件操作

从文件中读取数据
在这里插入图片描述
写入文件
在这里插入图片描述
存储数据
在这里插入图片描述

以上是本人个人整理的python基础语句。

在了解了python基础知识后,就是关于深度学习的学习了。

第二章 深度学习的基础知识

深度学习的简单介绍

近些年人工智能、机器学习和深度学习的概念十分火热,但很多从业者却很难说清它们之间的关系,外行人更是说到人工智能就只是一种“哇!很厉害哦。”的感叹。那么,这三者到底是什么关系?
人工智能、机器学习和深度学习覆盖的技术范畴是逐层递减的。人工智能是最宽泛的概念。机器学习是当前比较有效的一种实现人工智能的方式。深度学习是机器学习算法中最热门的一个分支,近些年取得了显著的进展,并替代了大多数传统机器学习算法。三者的关系如 图所示,即:人工智能 > 机器学习 > 深度学习。
在这里插入图片描述

人工智能

如字面含义,人工智能是研发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。由于这个定义只阐述了目标,而没有限定方法,因此实现人工智能存在的诸多方法和分支,导致其变成一个“大杂烩”式的学科。

机器学习的实现

机器学习的实现可以分成两步:训练和预测,类似于我们熟悉的归纳和演绎:

归纳: 从具体案例中抽象一般规律,机器学习中的“训练”亦是如此。从一定数量的样本(已知模型输入X和模型输出Y)中,学习输出Y与输入X的关系(可以想象成是某种表达式)。
演绎: 从一般规律推导出具体案例的结果,机器学习中的“预测”亦是如此。基于训练得到的Y与X之间的关系,如出现新的输入X,计算出输出Y。通常情况下,如果通过模型计算的输出和真实场景的输出一致,则说明模型是有效的。

机器学习的方法论

课程从“牛顿第二定律”入手,介绍机器学习的思考过程,以及在过程中如何确定模型参数,模型三个关键部分(假设、评价、优化)该如何应用。其中:

  • 模型假设:世界上的可能关系千千万,漫无目标的试探YX之间的关系显然是十分低效的。因此假设空间先圈定了一个模型能够表达的关系可能,如蓝色圆圈所示。机器还会进一步在假设圈定的圆圈内寻找最优的YX关系,即确定参数W。
  • 评价函数:寻找最优之前,我们需要先定义什么是最优,即评价一个Y~X关系的好坏的指标。通常衡量该关系是否能很好的拟合现有观测样本,将拟合的误差最小作为优化目标。
  • 优化算法:设置了评价指标后,就可以在假设圈定的范围内,将使得评价指标最优(损失函数最小/最拟合已有观测样本)的Y~X关系找出来,这个寻找的方法即为优化算法。最笨的优化算法即按照参数的可能,穷举每一个可能取值来计算损失函数,保留使得损失函数最小的参数作为最终结果。
    在牛顿第二定律的学习过程中,以上三个部分分别是这样进行的:
  1. 假设:通过观察加速度a和作用力F的观测数据,假设aaa和FFF是线性关系,即a=w∗F。
  2. 评价:对已知观测数据上的拟合效果好,即w∗F计算的结果,要和观测的a尽量接近。
  3. 优化:在参数w的所有可能取值中,发现w=1/m可使得评价最好(最拟合观测样本)。

总而言之:机器执行学习的框架体现了其学习的本质是“参数估计”(Learning is parameter estimation)。

深度学习

机器学习算法理论在上个世纪90年代发展成熟,在许多领域都取得了成功应用。但平静的日子只延续到2010年左右,随着大数据的涌现和计算机算力提升,深度学习模型异军突起,极大改变了机器学习的应用格局。今天,多数机器学习任务都可以使用深度学习模型解决,尤其在在语音、计算机视觉和自然语言处理等领域,深度学习模型的效果比传统机器学习算法有显著提升。
相比传统的机器学习算法,深度学习在理论结构上与其基本是一致的,即:模型假设、评价函数和优化算法,其根本差别在于假设的复杂度。

神经网络的基本概念

人工神经网络包括多个神经网络层,如卷积层、全连接层、LSTM等,每一层又包括很多神经元,超过三层的非线性神经网络都可以被称为深度神经网络。通俗的讲,深度学习的模型可以视为是输入到输出的映射函数,足够深的神经网络理论上可以拟合任何复杂的函数。因此神经网络非常适合学习样本数据的内在规律和表示层次,对文字、图像和语音任务有很好的适用性。因为这几个领域的任务是人工智能的基础模块,所以深度学习被称为实现人工智能的基础也就不足为奇了。
神经网络结构如下所示。
在这里插入图片描述
以上就是深度学习的一些基本理论知识。
理论知识晦涩难懂,需要反复研读,但是融入实践中就很容易理解了。
就比如一个牙牙学语的孩子,他对整个世界是陌生未知的。我们需要教导他“认识世界”。妈妈拿着“苹果”的识字卡,告诉宝宝,这是苹果。宝宝就会记住这个东西的基本特征,并记住“苹果”的发音。等再一次看到“苹果”的时候,正确的指出来。
深度学习也是一样,我们要告诉它,这个东西是“苹果”,它提取“苹果”的基本特征,并记住“苹果”的标签(label)。等下一次再看到“苹果”的时候正确的反馈出来。所以最困难的在于“苹果”的基本特征是什么。

程序结构“八股文”

深度学习的程序明显没有理论知识那么晦涩难懂。甚至有固定的“模板”,只要改变相应的内容就可以实现一个新的网络的搭建。(感谢前人种的树,使得后人可以乘凉:))
所有平台的程序结构大框架是基本一致,如图所示。
在这里插入图片描述
飞桨paddle具体的网络内容如下:
在这里插入图片描述

对于不同的网络模型,只需要横向改动其中相关内容即可。

第一个实践——mnist

课程以最基本的手写数字为例,向我们介绍如何使用“模板”来实现深度学习。

STEP 0

在数据处理前,首先要加载飞桨与手写数字识别模型相关的类库,实现方法如下。

#加载飞桨和相关类库
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Linear
import numpy as np
import os
from PIL import Image

STEP 1 数据处理

飞桨提供了多个封装好的数据集API,涵盖计算机视觉、自然语言处理、推荐系统等多个领域,帮助读者快速完成深度学习任务。如在手写数字识别任务中,通过paddle.dataset.mnist可以直接获取处理好的MNIST训练集、测试集。
如果已下载将会直接引用,若未下载将会先下载再直接引用。

通过*paddle.dataset.mnist.train()*函数设置数据读取器,batch_size设置为8,即一个批次有8张图片和8个标签,代码如下所示。

# 如果~/.cache/paddle/dataset/mnist/目录下没有MNIST数据,API会自动将MINST数据下载到该文件夹下
# 设置数据读取器,读取MNIST数据训练集
trainset = paddle.dataset.mnist.train()
# 包装数据读取器,每次读取的数据数量设置为batch_size=8
train_reader = paddle.batch(trainset, batch_size=8)

paddle.batch函数将MNIST数据集拆分成多个批次,通过如下代码读取第一个批次的数据内容,观察打印结果。

# 以迭代的形式读取数据
for batch_id, data in enumerate(train_reader()):
    # 获得图像数据,并转为float32类型的数组
    img_data = np.array([x[0] for x in data]).astype('float32')
    # 获得图像标签数据,并转为float32类型的数组
    label_data = np.array([x[1] for x in data]).astype('float32')
    # 打印数据形状
    print("图像数据形状和对应数据为:", img_data.shape, img_data[0])
    print("图像标签形状和对应数据为:", label_data.shape, label_data[0])
    break

在工业实践中,我们面临的任务和数据环境千差万别,通常需要自己编写适合当前任务的数据处理程序,一般涉及如下五个环节:

  • 读入数据
  • 划分数据集
  • 生成批次数据
  • 训练样本集乱序
  • 校验数据有效性
读入数据 & 划分数据集

在实际应用中,保存到本地的数据存储格式多种多样,我们需要将它大致拆分为60%的训练集,20%的测试集,20%的验证集。
其中:
train_set(训练集):包含50000条手写数字图片和对应的标签,用于确定模型参数。
val_set(验证集):包含10000条手写数字图片和对应的标签,用于调节模型超参数(如多个网络结构、正则化权重的最优选择)。
test_set(测试集):包含10000条手写数字图片和对应的标签,用于估计应用效果(没有在模型中应用过的数据,更贴近模型在真实场景应用的效果)。
train_set包含两个元素的列表:train_images、train_labels。

train_images:[5000, 784]的二维列表,包含5000张图片。每张图片用一个长度为784的向量表示,内容是28*28尺寸的像素灰度值(黑白图片)。
train_labels:[5000, ]的列表,表示这些图片对应的分类标签,即0-9之间的一个数字。
具体操作如下:

# 声明数据集文件位置
datafile = './work/mnist.json.gz'
print('loading mnist dataset from {} ......'.format(datafile))
# 加载json数据文件
data = json.load(gzip.open(datafile))
print('mnist dataset load done')
# 读取到的数据区分训练集,验证集,测试集
train_set, val_set, eval_set = data

# 数据集相关参数,图片高度IMG_ROWS, 图片宽度IMG_COLS
IMG_ROWS = 28
IMG_COLS = 28

# 打印数据信息
imgs, labels = train_set[0], train_set[1]
print("训练数据集数量: ", len(imgs))

# 观察验证集数量
imgs, labels = val_set[0], val_set[1]
print("验证数据集数量: ", len(imgs))

# 观察测试集数量
imgs, labels = val= eval_set[0], eval_set[1]
print("测试数据集数量: ", len(imgs))
训练样本乱序、生成批次数据

训练样本乱序: 先将样本按顺序进行编号,建立ID集合index_list。然后将index_list乱序,最后按乱序后的顺序读取数据。
说明:

通过大量实验发现,模型对最后出现的数据印象更加深刻。训练数据导入后,越接近模型训练结束,最后几个批次数据对模型参数的影响越大。为了避免模型记忆影响训练效果,需要进行样本乱序操作。

生成批次数据: 先设置合理的batch_size,再将数据转变成符合模型输入要求的np.array格式返回。同时,在返回数据时将Python生成器设置为yield模式,以减少内存占用。
在执行如上两个操作之前,需要先将数据处理代码封装成load_data函数,方便后续调用。load_data有三种模型:train、valid、eval,分为对应返回的数据是训练集、验证集、测试集。

具体操作如下:

imgs, labels = train_set[0], train_set[1]
print("训练数据集数量: ", len(imgs))
#获得数据集长度
imgs_length = len(imgs)
#定义数据集每个数据的序号,根据序号读取数据
index_list = list(range(imgs_length))
#读入数据时用到的批次大小
BATCHSIZE = 100

#随机打乱训练数据的索引序号
random.shuffle(index_list)

#定义数据生成器,返回批次数据
def data_generator():

    imgs_list = []
    labels_list = []
    for i in index_list:
        # 将数据处理成期望的格式,比如类型为float32,shape为[1, 28, 28]
        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:
            # 获得一个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
训练数据集数量:  50000
In [4]
#声明数据读取函数,从训练集中读取数据
train_loader = data_generator
#以迭代的形式读取数据
for batch_id, data in enumerate(train_loader()):
    image_data, label_data = data
    if batch_id == 0:
        # 打印数据shape和类型
        print("打印第一个batch数据的维度:")
        print("图像维度: {}, 标签维度: {}".format(image_data.shape, label_data.shape))
    break
校验数据有效性

在实际应用中,原始数据可能存在标注不准确、数据杂乱或格式不统一等情况。因此在完成数据处理流程后,还需要进行数据校验,一般有两种方式:
机器校验:加入一些校验和清理数据的操作。
人工校验:先打印数据输出结果,观察是否是设置的格式;再从训练的结果验证数据处理和读取的有效性。
机器校验
如下代码所示,如果数据集中的图片数量和标签数量不等,说明数据逻辑存在问题,可使用assert语句校验图像数量和标签数据是否一致。

imgs_length = len(imgs)
assert len(imgs) == len(labels), \
          "length of train_imgs({}) should be the same as train_labels({})".format(len(imgs), len(label))

人工校验
人工校验是指打印数据输出结果,观察是否是预期的格式。实现数据处理和加载函数后,我们可以调用它读取一次数据,观察数据的shape和类型是否与函数中设置的一致。

#声明数据读取函数,从训练集中读取数据
train_loader = data_generator
#以迭代的形式读取数据
for batch_id, data in enumerate(train_loader()):
    image_data, label_data = data
    if batch_id == 0:
        # 打印数据shape和类型
        print("打印第一个batch数据的维度,以及数据的类型:")
        print("图像维度: {}, 标签维度: {}, 图像数据类型: {}, 标签数据类型: {}".format(image_data.shape, label_data.shape, type(image_data), type(label_data)))
    break

STEP 2 模型设计

1)网络结构
全连接神经网络

经典的全连接神经网络来包含四层网络:输入层、两个隐含层和输出层将手写数字识别任务通过全连接神经网络表示:
在这里插入图片描述
输入层:将数据输入给神经网络。在该任务中,输入层的尺度为28×28的像素值。
隐含层:增加网络深度和复杂度,隐含层的节点数是可以调整的,节点数越多,神经网络表示能力越强,参数量也会增加。在该任务中,中间的两个隐含层为10×10的结构,通常隐含层会比输入层的尺寸小,以便对关键信息做抽象,激活函数使用常见的sigmoid函数。
输出层:输出网络计算结果,输出层的节点数是固定的。如果是回归问题,节点数量为需要回归的数字数量;如果是分类问题,则是分类标签的数量。在该任务中,模型的输出是回归一个数字,输出层的尺寸为1。
下述代码为经典全连接神经网络的实现。

# 多层全连接神经网络实现
class MNIST(fluid.dygraph.Layer):
    def __init__(self):
        super(MNIST, self).__init__()
        # 定义两层全连接隐含层,输出维度是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
In [4]
#网络结构部分之后的代码,保持不变
with fluid.dygraph.guard():
    model = 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')
卷积神经网络

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

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

# 多层卷积神经网络实现
class MNIST(fluid.dygraph.Layer):
     def __init__(self):
         super(MNIST, self).__init__()
         
         # 定义卷积层,输出特征通道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
训练定义好的卷积神经网络,如下所示。

In [7]
#网络结构部分之后的代码,保持不变
with fluid.dygraph.guard():
    model = 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)
            
            #每训练了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')

与经典全连接神经网络相比,卷积神经网络的损失值下降更快,且最终的损失值更小。

2)损失函数

损失函数是模型优化的目标,用于在众多的参数取值中,识别最理想的取值。损失函数的计算在训练过程的代码中,每一轮模型训练的过程都相同,分如下三步:

  1. 先根据输入数据正向计算预测输出。

  2. 再根据预测值和真实值计算损失。

  3. 最后根据损失反向传播梯度并更新参数。

Softmax函数

对于分类函数而言,如果模型能输出10个标签的概率,对应真实标签的概率输出尽可能接近100%,而其他标签的概率输出尽可能接近0%,且所有输出概率之和为1。这是一种更合理的假设!与此对应,真实的标签值可以转变成一个10维度的one-hot向量,在对应数字的位置上为1,其余位置为0,比如标签“6”可以转变成[0,0,0,0,0,0,1,0,0,0]。

为了实现上述思路,需要引入Softmax函数,它可以将原始输出转变成对应标签的概率,公式如下,其中C是标签类别个数。
在这里插入图片描述

从公式的形式可见,每个输出的范围均在0~1之间,且所有输出之和等于1,这是变换后可被解释成概率的基本前提。对应到代码上,我们需要在网络定义部分修改输出层:self.fc = Linear(input_dim=10, output_dim=1, act=‘softmax’),即是对全连接层的输出加一个softmax运算。

下图是一个三个标签的分类模型(三分类)使用的softmax输出层,从中可见原始输出的三个数字3、1、-3,经过softmax层后转变成加和为1的三个概率值0.88、0.12、0。
在这里插入图片描述

交叉熵

在模型输出为分类标签的概率时,直接以标签和概率做比较也不够合理,人们更习惯使用交叉熵误差作为分类问题的损失衡量。
交叉熵损失函数的设计是基于最大似然思想。

交叉熵的代码实现

在手写数字识别任务中,仅改动三行代码,就可以将在现有模型的损失函数替换成交叉熵(cross_entropy)。

在读取数据部分,将标签的类型设置成int,体现它是一个标签而不是实数值(飞桨默认将标签处理成“int64”)。
网络定义部分,将输出层改成“输出十个标签的概率”的模式。
训练过程部分,将损失函数从均方误差换成交叉熵。
数据处理部分,需要修改标签变量Label的格式,代码如下所示。

  • 从:label = np.reshape(labels[i], [1]).astype(‘float32’)
  • 到:label = np.reshape(labels[i], [1]).astype(‘int64’)
    具体代码如下:
#修改标签数据的格式,从float32到int64
import os
import random
import paddle
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Conv2D, Pool2D, Linear
import numpy as np
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('int64')
            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
在网络定义部分,需要修改输出层结构,代码如下所示。

从:self.fc = Linear(input_dim=980, output_dim=1, act=None)
到:self.fc = Linear(input_dim=980, output_dim=10, act='softmax')

# 定义模型结构
class MNIST(fluid.dygraph.Layer):
     def __init__(self):
         super(MNIST, self).__init__()
         
         # 定义一个卷积层,使用relu激活函数
         self.conv1 = Conv2D(num_channels=1, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
         # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
         self.pool1 = Pool2D(pool_size=2, pool_stride=2, pool_type='max')
         # 定义一个卷积层,使用relu激活函数
         self.conv2 = Conv2D(num_channels=20, num_filters=20, filter_size=5, stride=1, padding=2, act='relu')
         # 定义一个池化层,池化核为2,步长为2,使用最大池化方式
         self.pool2 = Pool2D(pool_size=2, pool_stride
  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值