paddle深度学习基础之数据处理以及优化

前言

在这一系列基础课中,我将总结从数据处理到模型调参方方面面的基础知识的讲解。一方面作为自己学习的总结,另一方面给大家分享自己的一些想法和见解。这系列课程将以手写数字识别为基本例子,在此基础上,将这个例子由简到复杂一步一步的改进。

系列课程框架

img

数据介绍

尽管paddle框架中已经封装好了mnist手写数据集的接口,但是在我们实际项目中,一般需要我们自己区收集数据。这里我提供好了一个mnist数据,数据结构如下:

图片

data包含三个元素的列表:train_setval_settest_set

  • train_set(训练集):包含50000条手写数字图片和对应的标签,用于确定模型参数。
  • val_set(验证集):包含10000条手写数字图片和对应的标签,用于调节模型超参数(如多个网络结构、正则化权重的最优选择)。
  • test_set(测试集):包含10000条手写数字图片和对应的标签,用于估计应用效果(没有在模型中应用过的数据,更贴近模型在真实场景应用的效果)。

train_set包含两个元素的列表:train_imagestrain_labels

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

数据处理环节

  1. 读入数据
  2. 划分数据集
  3. 生成批次数据
  4. 训练样本乱序
  5. 校验数据有效性

一、读入数据

读入数据有许多方法,比如使用open()打开文件读取内容、使用numpy.fromfile()方法、pandas.read_csv()方法 都可以。由于此次数据是以json文件存储的,所以我们使用json.load()方法.

``

import gzip #解压缩包,python自带的包
import json #加载json文件使用的
import numpy as np

mnistdata = gzip.open('data/mnist.json.gz')#解压缩文件
data = json.load(mnistdata)#加载json文件

此时data是一个数组,咱们转化为numpy类型,并查看一下他的形状

``

data = np.array(data)
print(data.shape)

[out] : (3, 2)

二、划分数据集

结果输出的形状与咱们上面介绍的data数据结构一致,三行数组分别是训练集、验证集、测试集。两列分别是 图片数据、标签数据。接下来通过多重赋值的方式,获取这三个数据集

#分别获取训练集、验证集和测试集数据
train_data,val_data,test_data = data

以训练集为例,获取其图片和标签数据

img = train_data[0] #图片数据
label = train_data[1] #标签数据

三、生成批次数据

这里需要明白两个概念,就是python中的迭代器和生成器。不清楚的先去补课

我们在使用深度学习框架向模型传入数据时,经常会简单BATCH_SIZI 这个参数。这个就是指定一批次数据的个数。这么做的最大好处就是节约内存。可能我们平常使用的数据量比较小,没太感觉。当我们使用几千万条数据进行训练时,如果一股脑的将数据一下子读取到内存中,那占据的空间就非常的大。所以我们采取批量处理的方式

#设置图片大小
IMG_ROWS=28
IMG_COLS=28
def data_genergator(batch_size=20):
        listdata=[]
        listlabel=[]
        for i in range(len(img)):
            #转化数据结构
            imgdata = np.reshape(img[i],[1,IMG_ROWS,IMG_COLS]).astype('float32')
            labeldata = np.reshape(label[i],[1]).astype('float32')
            listdata.append(imgdata)
            listlabel.append(labeldata)
            if(len(listdata)%batch_size==0):
                yield np.array(listdata),np.array(listlabel)
                listlabel=[]
                listdata=[]
        if(len(listdata)>0):
            yield np.array(listdata),np.array(listlabel)

    return data_genergator

步骤讲解

  1. 循环遍历数据,将图片和标签数据进行相应的格式转换
  2. 将数据分别添加到列表中
  3. 我们设置的BATCH_SIZE(batch_size)的值为20,当列表的长度达到20时,我们使用一个yield 这个生成器函数 将列表返回
  4. 清空列表
  5. 将剩下的值返回

注:比如一共由121条数据,batch_size=20,那么,一共会产生7批次数据(121/20=6、121 % 20=1),最后一批次为1个数据.

四、样本乱序

对于模型来说,越是靠后的数据,对模型的影响最大。所以,在我们进行每一轮训练的时候,需要对数据进行打乱顺序,这样对于会提高我们模型的精准度和泛化能力。

下面介绍一种乱序方法:


list = [i for i in range(len(img))]#增加
np.random.shuffle(list)#增加
IMG_ROWS=28
IMG_COLS=28
def data_genergator(batch_size=20):
        listdata=[]
        listlabel=[]
        for i in list:#修改
            #转化数据结构
            imgdata = np.reshape(img[i],[1,IMG_ROWS,IMG_COLS]).astype('float32')
            labeldata = np.reshape(label[i],[1]).astype('float32')
            listdata.append(imgdata)
            listlabel.append(labeldata)
            if(len(listdata)%batch_size==0):
                yield np.array(listdata),np.array(listlabel)
                listlabel=[]
                listdata=[]
        if(len(listdata)>0):
            yield np.array(listdata),np.array(listlabel)

    return data_genergator

这里我们增加了两处代码,修改了一处。

首先我们获取数据长度,并生成列表;然后我们通过np.random.shuffle()方法打乱顺序;紧接着在遍历时使用打乱的顺序获取数据。

五、检验数据的有效性

什么叫数据的有效性?如果你的图片数据个数与你的标签数据个数不一致,那么就无法正常的训练下去。所以,我们需要提前检查这种问题。

    #验证数据有效性
    assert len(img)==len(label),'the lenth of img must be the  same as the length of label'

assert 函数是python自带的一个函数,也叫断言函数。功能与if语句判断差不多。逗号前面是判断条件,逗号后面是条件不成立时触发的异常,等价于:

if not len(img)==len(label):
	raise Exception('the lenth of img must be the  same as the length of label')

异步数据读取

即便我们使用了迭代器获取数据,节约了内存,但是还是存在一个问题:在我们进行训练前,要等待数据传入。他们之间是同步的关系,挺浪费时间的。

图片

接下来,我们使用paddle自带的异步数据读取的方法,改造我们现有的代码,只需要添加三处,修改一处:

place = fluid.CPUPlace()

# 设置读取的数据是放在CPU还是GPU上。

data_loader = fluid.io.DataLoader.from_generator(capacity=5, return_list=True) 

# 创建一个DataLoader对象用于加载Python生成器产生的数据。数据会由Python线程预先读取,并异步送入一个队列中。

data_loader.set_batch_generator(train_loader, place) 

# 用创建的DataLoader对象设置一个数据生成器set_batch_generator,输入的参数是一个Python数据生成器train_loader和服务器资源类型place(标明CPU还是GPU)

添加后的代码:

class MNIST(fluid.dygraph.Layer):
    def __init__(self):
        super(MNIST, self).__init__()
        self.linear = Linear(input_dim=28*28,output_dim=1,act=None)
    def forward(self, inputs):
        inputs = fluid.layers.reshape(inputs,(-1,28*28))
        outputs = self.linear(inputs)
        return outputs
with fluid.dygraph.guard():
    model = MNIST()
    model.train()
    train_loader = data_loader()
    optimaizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001,parameter_list=Linear.parameters(model))
    place = fluid.CPUPlace()#添加
    data_loader = fluid.io.DataLoader.from_generator(capacity=20, return_list=True)#添加
    data_loader.set_batch_generator(train_loader, places=place)#添加
    EPOCH_NUM = 10
    model_save_path="model/mnist-model/dygraph-mnist"
    for epoch_id in range(EPOCH_NUM):
        for batch_id,data in enumerate(data_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)
            if batch_id !=0 and batch_id %1000 ==0:
                print("epoch:{},batch:{},loss is:{}".format(epoch_id,batch_id,avg_loss.numpy()))
            avg_loss.backward()
            optimaizer.minimize(avg_loss)
            model.clear_gradients()
    print("保存模型")
    fluid.save_dygraph(model.state_dict(),model_save_path)

总结

上面我们介绍了深度学习处理过程,这些处理都是以后我们经常会遇到的。我又在上面的基础上稍加修改,完整的代码如下:

import paddle
import numpy as np
import matplotlib.pyplot as plt
import gzip
import json
import paddle.fluid as fluid
from paddle.fluid.dygraph.nn import Linear
from PIL import Image
import os
'''
此项目主要是在数据层面上操作具体流程
数据处理->封装函数->读文件->预处理->异步
'''

#解压缩
mnistdata = gzip.open('data/mnist.json.gz')
#通过json导入  因为此数据使用json形式保存的,所以需要json导入 也可以使用pandas 数据导入方式
data = json.load(mnistdata)

#分别获取训练集、验证集和测试集数据
train_data,val_data,test_data = data
#设置数据大小
IMG_ROWS=28
IMG_COLS=28
##数据乱序,生成批次数据
def data_loader(dataname='train',batch_size=20):
    #乱序处理方法1
    if(dataname=='train'):
        img = train_data[0]
        label = train_data[1]
    elif(dataname=='test'):
        img = test_data[0]
        label = test_data[1]
    elif(dataname=='val'):
        img = val_data[0]
        label = val_data[1]
    else:
        raise Exception("data only can be one of ['train','test','val']")
    #验证数据有效性
    assert len(img)==len(label),'the lenth of img must be the  same as the length of label'
    list = []
    datasize = len(img)
    list = [i for i in range(datasize)]
    #打乱数据
    np.random.shuffle(list)
    #获取数据,定义一个数据生成器
    def data_genergator():
        listdata=[]
        listlabel=[]
        for i in list:
            #转化数据结构
            imgdata = np.reshape(img[i],[1,IMG_ROWS,IMG_COLS]).astype('float32')
            labeldata = np.reshape(label[i],[1]).astype('float32')
            listdata.append(imgdata)
            listlabel.append(labeldata)
            if(len(listdata)%batch_size==0):
                yield np.array(listdata),np.array(listlabel)
                listlabel=[]
                listdata=[]
        if(len(listdata)>0):
            yield np.array(listdata),np.array(listlabel)

    return data_genergator
#定义类
class MNIST(fluid.dygraph.Layer):
    def __init__(self):
        super(MNIST, self).__init__()
        self.linear = Linear(input_dim=28*28,output_dim=1,act=None)
    def forward(self, inputs):
        inputs = fluid.layers.reshape(inputs,(-1,28*28))
        outputs = self.linear(inputs)
        return outputs
#训练
with fluid.dygraph.guard():
    model = MNIST()
    model.train()
    train_loader = data_loader()
    optimaizer = fluid.optimizer.SGDOptimizer(learning_rate=0.001,parameter_list=Linear.parameters(model))
    place = fluid.CPUPlace()
    data_loader = fluid.io.DataLoader.from_generator(capacity=5, return_list=True)
    data_loader.set_batch_generator(train_loader, places=place)
    EPOCH_NUM = 10
    model_save_path="model/mnist-model/dygraph-mnist"
    for epoch_id in range(EPOCH_NUM):
        for batch_id,data in enumerate(data_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)
            if batch_id !=0 and batch_id %1000 ==0:
                print("epoch:{},batch:{},loss is:{}".format(epoch_id,batch_id,avg_loss.numpy()))
            avg_loss.backward()
            optimaizer.minimize(avg_loss)
            model.clear_gradients()
    print("保存模型")
    fluid.save_dygraph(model.state_dict(),model_save_path)
#预测
with fluid.dygraph.guard():
    model = MNIST()
    train_loader = data_loader()
    traindata = next(train_loader())
    imgdata,label = traindata
    imgdata  = np.array(imgdata[0]).reshape(28,28)
    label = label[0]
    print(label)
    param_file_path = "model/mnist-model/dygraph-mnist"
    para_dict,opt_dict = fluid.load_dygraph(param_file_path)#返回值一个是参数,另一个是优化器
    model.eval()#启动测试模型
    result = model(fluid.dygraph.to_variable(imgdata))
    print("本次预测的数字是:{},实际数据是:{}".format(result.numpy().astype('int32'),label))


发布了87 篇原创文章 · 获赞 76 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/lzx159951/article/details/105254828
今日推荐