美文网首页
【深度学习】PaddlePaddle计算机视觉项目:手写数字识别

【深度学习】PaddlePaddle计算机视觉项目:手写数字识别

作者: 居家龙龙 | 来源:发表于2019-07-31 11:50 被阅读0次

    百度上有很多可以实践的项目,PaddlePaddle有很多官方模型,每个模型均可fork后运行。最近运行了手写数字识别项目,该项目是计算机视觉入门项目,简单易学,且每个步骤后都有注释,运行代码的页面也非常简洁。在此记录一下,部分内容摘自百度的手写数字识别项目。

    本实践使用多层感知器训练(DNN)模型,用于预测手写数字图片。

    image

    实践总体步骤和过程

    实践总体过程和步骤如下图

    image

    首先导入必要的包

    numpy---------->python第三方库,用于进行科学计算

    PIL------------> Python Image Library,python第三方图像处理库

    matplotlib----->python的绘图库 pyplot:matplotlib的绘图框架

    os------------->提供了丰富的方法来处理文件和目录

    In[1]

    #导入需要的包
    import numpy as np
    import paddle as paddle
    import paddle.fluid as fluid
    from PIL import Image
    import matplotlib.pyplot as plt
    import os 
    

    Step1 准备数据

    (1)数据集介绍

    MNIST数据集包含60000个训练集和10000测试数据集。分为图片和标签,图片是28*28的像素矩阵,标签为0~9共10个数字。

    image

    (2)train_reader和test_reader

    paddle.dataset.mnist.train()和test()分别用于获取mnist训练集和测试集

    paddle.reader.shuffle()表示每次缓存BUF_SIZE个数据项,并进行打乱

    paddle.batch()表示每BATCH_SIZE组成一个batch

    (3)打印出数据。用print(sampledata)。PaddlePaddle接口提供的数据已经经过了归一化、居中等处理。

    In[56]

    BUF_SIZE=512
    BATCH_SIZE=128
    #用于训练的数据提供器,每次从缓存中随机读取批次大小的数据
    train_reader = paddle.batch(
        paddle.reader.shuffle(paddle.dataset.mnist.train(),
                              buf_size=BUF_SIZE),
        batch_size=BATCH_SIZE)
    #用于训练的数据提供器,每次从缓存中随机读取批次大小的数据
    test_reader = paddle.batch(
        paddle.reader.shuffle(paddle.dataset.mnist.test(),
                              buf_size=BUF_SIZE),
        batch_size=BATCH_SIZE)
    
    #用于打印,查看mnist数据
    train_data=paddle.dataset.mnist.train();
    sampledata=next(train_data())
    print(sampledata)
    

    Step2.网络配置

    以下的代码判断就是定义一个简单的多层感知器,一共有三层,两个大小为100的隐层和一个大小为10的输出层,因为MNIST数据集是手写0到9的灰度图像,类别有10个,所以最后的输出大小是10。最后输出层的激活函数是Softmax,所以最后的输出层相当于一个分类器。加上一个输入层的话,多层感知器的结构是:输入层-->>隐层-->>隐层-->>输出层。

    image

    In[57]

    # 定义多层感知器 
    def multilayer_perceptron(input): 
        # 第一个全连接层,激活函数为ReLU 
        hidden1 = fluid.layers.fc(input=input, size=100, act='relu') 
        # 第二个全连接层,激活函数为ReLU 
        hidden2 = fluid.layers.fc(input=hidden1, size=100, act='relu') 
        # 以softmax为激活函数的全连接输出层,输出层的大小必须为数字的个数10 
        prediction = fluid.layers.fc(input=hidden2, size=10, act='softmax') 
        return prediction 
    

    (2)定义数据层

    输入的是图像数据。图像是 28 * 28 的灰度图,所以输入的形状是[1, 28, 28],如果图像是32*32的彩色图,那么输入的形状是[3. 32, 32],因为灰度图只有一个通道,而彩色图有RGB三个通道。

    In[58]

    # 输入的原始图像数据,大小为1*28*28
    image = fluid.layers.data(name='image', shape=[1, 28, 28], dtype='float32')#单通道,28*28像素值
    # 标签,名称为label,对应输入图片的类别标签
    label = fluid.layers.data(name='label', shape=[1], dtype='int64')          #图片标签
    

    (3)获取分类器

    In[12]

    # 获取分类器
    predict = multilayer_perceptron(image)
    

    (4)定义损失函数和准确率

    这次使用的是交叉熵损失函数,该函数在分类任务上比较常用。

    定义了一个损失函数之后,还有对它求平均值,训练程序必须返回平均损失作为第一个返回值,因为它会被后面反向传播算法所用到。。

    同时我们还可以定义一个准确率函数,这个可以在我们训练的时候输出分类的准确率。

    In[14]

    #使用交叉熵损失函数,描述真实样本标签和预测概率之间的差值
    cost = fluid.layers.cross_entropy(input=predict, label=label)  
    # 使用类交叉熵函数计算predict和label之间的损失函数
    avg_cost = fluid.layers.mean(cost)
    # 计算分类准确率
    acc = fluid.layers.accuracy(input=predict, label=label)
    

    (5)定义优化函数

    这次我们使用的是Adam优化方法,同时指定学习率为0.001

    In[16]

     #使用Adam算法进行优化, learning_rate 是学习率(它的大小与网络的训练收敛速度有关系)
    optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.001)  
    opts = optimizer.minimize(avg_cost)
    

    在上述模型配置完毕后,得到两个fluid.Program:fluid.default_startup_program() 与fluid.default_main_program() 配置完毕了。

    参数初始化操作会被写入fluid.default_startup_program()

    fluid.default_main_program()用于获取默认或全局main program(主程序)。该主程序用于训练和测试模型。fluid.layers 中的所有layer函数可以向 default_main_program 中添加算子和变量。default_main_program 是fluid的许多编程接口(API)的Program参数的缺省值。例如,当用户program没有传入的时候, Executor.run() 会默认执行 default_main_program 。

    Step3.模型训练 and Step4.模型评估

    (1)创建训练的Executor

    首先定义运算场所 fluid.CPUPlace()和 fluid.CUDAPlace(0)分别表示运算场所为CPU和GPU

    Executor:接收传入的program,通过run()方法运行program。

    In[18]

    # 定义使用CPU还是GPU,使用CPU时use_cuda = False,使用GPU时use_cuda = True
    use_cuda = False
    place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
    # 获取测试程序
    test_program = fluid.default_main_program().clone(for_test=True)
    exe = fluid.Executor(place)
    exe.run(fluid.default_startup_program())
    

    (2)告知网络传入的数据分为两部分,第一部分是image值,第二部分是label值

    DataFeeder负责将数据提供器(train_reader,test_reader)返回的数据转成一种特殊的数据结构,使其可以输入到Executor中。

    In[20]

    
    feeder = fluid.DataFeeder(place=place, feed_list=[image, label])
    

    (3)展示模型训练曲线

    In[22]

    all_train_iter=0
    all_train_iters=[]
    all_train_costs=[]
    all_train_accs=[]
    
    def draw_train_process(title,iters,costs,accs,label_cost,lable_acc):
        plt.title(title, fontsize=24)
        plt.xlabel("iter", fontsize=20)
        plt.ylabel("cost/acc", fontsize=20)
        plt.plot(iters, costs,color='red',label=label_cost) 
        plt.plot(iters, accs,color='green',label=lable_acc) 
        plt.legend()
        plt.grid()
        plt.show()
    

    (4)训练并保存模型

    训练需要有一个训练程序和一些必要参数,并构建了一个获取训练过程中测试误差的函数。必要参数有executor,program,reader,feeder,fetch_list。

    executor表示之前创建的执行器

    program表示执行器所执行的program,是之前创建的program,如果该项参数没有给定的话则默认使用defalut_main_program

    reader表示读取到的数据

    feeder表示前向输入的变量

    fetch_list表示用户想得到的变量

    In[24]

    EPOCH_NUM=2
    model_save_dir = "/home/aistudio/work/hand.inference.model"
    for pass_id in range(EPOCH_NUM):
        # 进行训练
        for batch_id, data in enumerate(train_reader()):                         #遍历train_reader
            train_cost, train_acc = exe.run(program=fluid.default_main_program(),#运行主程序
                                            feed=feeder.feed(data),              #给模型喂入数据
                                            fetch_list=[avg_cost, acc])          #fetch 误差、准确率  
    
            all_train_iter=all_train_iter+BATCH_SIZE
            all_train_iters.append(all_train_iter)
    
            all_train_costs.append(train_cost[0])
            all_train_accs.append(train_acc[0])
    
            # 每200个batch打印一次信息  误差、准确率
            if batch_id % 200 == 0:
                print('Pass:%d, Batch:%d, Cost:%0.5f, Accuracy:%0.5f' %
                      (pass_id, batch_id, train_cost[0], train_acc[0]))
    
        # 进行测试
        test_accs = []
        test_costs = []
        #每训练一轮 进行一次测试
        for batch_id, data in enumerate(test_reader()):                         #遍历test_reader
            test_cost, test_acc = exe.run(program=test_program, #执行训练程序
                                          feed=feeder.feed(data),               #喂入数据
                                          fetch_list=[avg_cost, acc])           #fetch 误差、准确率
            test_accs.append(test_acc[0])                                       #每个batch的准确率
            test_costs.append(test_cost[0])                                     #每个batch的误差
    
        # 求测试结果的平均值
        test_cost = (sum(test_costs) / len(test_costs))                         #每轮的平均误差
        test_acc = (sum(test_accs) / len(test_accs))                            #每轮的平均准确率
        print('Test:%d, Cost:%0.5f, Accuracy:%0.5f' % (pass_id, test_cost, test_acc))
    
        #保存模型
        # 如果保存路径不存在就创建
    if not os.path.exists(model_save_dir):
        os.makedirs(model_save_dir)
    print ('save models to %s' % (model_save_dir))
    fluid.io.save_inference_model(model_save_dir,   #保存推理model的路径
                                      ['image'],    #推理(inference)需要 feed 的数据
                                      [predict],    #保存推理(inference)结果的 Variables
                                      exe)             #executor 保存 inference model
    
    print('训练模型保存完成!')
    draw_train_process("training",all_train_iters,all_train_costs,all_train_accs,"trainning cost","trainning acc")
    

    Step5.模型预测

    (1)图片预处理

    在预测之前,要对图像进行预处理。

    首先进行灰度化,然后压缩图像大小为28*28,接着将图像转换成一维向量,最后再对一维向量进行归一化处理。

    In[26]

    def load_image(file):
        im = Image.open(file).convert('L')                        #将RGB转化为灰度图像,L代表灰度图像,像素值在0~255之间
        im = im.resize((28, 28), Image.ANTIALIAS)                 #resize image with high-quality 图像大小为28*28
        im = np.array(im).reshape(1, 1, 28, 28).astype(np.float32)#返回新形状的数组,把它变成一个 numpy 数组以匹配数据馈送格式。
        # print(im)
        im = im / 255.0 * 2.0 - 1.0                               #归一化到【-1~1】之间
        return im
    
    

    (2)使用Matplotlib工具显示这张图像。

    In[28]

    infer_path='/home/aistudio/data/data1910/infer_3.png'
    img = Image.open(infer_path)
    plt.imshow(img)   #根据数组绘制图像
    plt.show()        #显示图像
    

    (3)创建预测用的Executer

    In[30]

    infer_exe = fluid.Executor(place)
    inference_scope = fluid.core.Scope()
    

    (4)开始预测

    通过fluid.io.load_inference_model,预测器会从params_dirname中读取已经训练好的模型,来对从未遇见过的数据进行预测。

    In[32]

    # 加载数据并开始预测
    with fluid.scope_guard(inference_scope):
        #获取训练好的模型
        #从指定目录中加载 推理model(inference model)
        [inference_program,                                            #推理Program
         feed_target_names,                                            #是一个str列表,它包含需要在推理 Program 中提供数据的变量的名称。 
         fetch_targets] = fluid.io.load_inference_model(model_save_dir,#fetch_targets:是一个 Variable 列表,从中我们可以得到推断结果。model_save_dir:模型保存的路径
                                                        infer_exe)     #infer_exe: 运行 inference model的 executor
        img = load_image(infer_path)
    
        results = infer_exe.run(program=inference_program,               #运行推测程序
                       feed={feed_target_names[0]: img},           #喂入要预测的img
                       fetch_list=fetch_targets)                   #得到推测结果,  
        # 获取概率最大的label
        lab = np.argsort(results)                                  #argsort函数返回的是result数组值从小到大的索引值
        #print(lab)
        print("该图片的预测结果的label为: %d" % lab[0][0][-1])     #-1代表读取数组中倒数第一列  
    

    相关文章

      网友评论

          本文标题:【深度学习】PaddlePaddle计算机视觉项目:手写数字识别

          本文链接:https://www.haomeiwen.com/subject/baworctx.html