在PC端使用paddlepaddle跑通手写数字识别(CV+Python)

paddlepaddle是近期很火的开源深度学习平台,我们可以在其官网上使用该平台,平台上提供了一个深度学习的案例:手写数字识别

今天我们来试试,在本地电脑跑通这个程序,首先安装paddlepaddle,具体安装方法请查看官网:
https://www.paddlepaddle.org.cn/install/quick

成功安装以后是这样的:
在这里插入图片描述
官网上对这段代码的解释很清楚了,这里我主要讲讲我做的一些改进:

import numpy as np
import paddle as paddle
import paddle.fluid as fluid
from PIL import Image
import matplotlib.pyplot as plt
import os

导入必备的资源库,这块不需要改变

  • numpy:python第三方库,用于进行科学计算
  • PIL:Python Image Library,python第三方图像处理库
  • PIL:Python Image Library,python第三方图像处理库
  • matplotlib:python的绘图库 pyplot:matplotlib的绘图框架
  • os:提供了丰富的方法来处理文件和目录

改进的地方就在于,我把程序代码写成了一个类:

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

简单讲讲这里的数据集,如果没有数据集,该程序中,paddlepaddle会自行下载mnist数据集,就像这样:
在这里插入图片描述
当然,您也可以自行下载:
http://yann.lecun.com/exdb/mnist/
在这里插入图片描述
自行下载的数据集存放到这个目录里:

/home/username/.cache/paddle/dataset/mnist

如下图所示:
在这里插入图片描述
记得解压数据集!!记得解压数据集!!记得解压数据集!!(重要的事情说三遍!!!)

我们可以打印一下数据集:
在这里插入图片描述
这是一部分,太大了,放不完。

接下来,是几个函数:

# 定义多层感知器
    def multilayer_perceptron(self, 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

如果准备数据是第一步,那第二步就是配置网络

#绘制训练过程图像
    def draw_train_process(self, 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()

在这里插入图片描述
可以看到,误差在一直减小,准确率也在趋近于1

#加载图片
    def load_image(self, 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

数据集是不能直接拿来训练的,我们需要做一些简单的处理

接下来是主函数,代码量比较庞大,我分开来讲解,以下是代码结构:

def main():
    paddle_number = PADDLE_NUMBER()
    pass
    
if __name__ == '__main__':
    main()

以下代码属于主函数部分:

# 输入的原始图像数据,大小为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')  # 图片标签
    # 获取分类器
    predict = paddle_number.multilayer_perceptron(image)
    # 使用交叉熵损失函数,描述真实样本标签和预测概率之间的差值
    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)
    # 获取测试程序
    test_program = fluid.default_main_program().clone(for_test=True)
    # 使用Adam算法进行优化, learning_rate 是学习率(它的大小与网络的训练收敛速度有关系)
    optimizer = fluid.optimizer.AdamOptimizer(learning_rate=0.001)
    opts = optimizer.minimize(avg_cost)
    # 定义使用CPU还是GPU,使用CPU时use_cuda = False,使用GPU时use_cuda = True
    use_cuda = False
    place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
    exe = fluid.Executor(place)
    exe.run(fluid.default_startup_program())
    feeder = fluid.DataFeeder(place=place, feed_list=[image, label])

在这里 ,因为我的电脑没有独立显卡,所以我选择使用CPU,注释里也写了,如果使用GPU,请把 use_cuda = False 改成 use_cuda = True

	all_train_iter = 0
    all_train_iters = []
    all_train_costs = []
    all_train_accs = []

这四行代码是为draw_train_process函数准备的,定义一下画模型训练曲线需要用的容器。

接下来是第三步,开始进行模型训练:

	EPOCH_NUM = 2
    model_save_dir = "‪hand.inference.model"
    for pass_id in range(EPOCH_NUM):
        # 进行训练
        for batch_id, data in enumerate(paddle_number.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 + paddle_number.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(paddle_number.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('训练模型保存完成!')
    paddle_number.draw_train_process("training", all_train_iters, all_train_costs, all_train_accs, "trainning cost", "trainning acc")

在训练的过程对模型进行评估,这是第四步,接下来就是模型预测了:

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

在这里插入图片描述
记得把这张图片放在程序的相同文件夹下:
在这里插入图片描述
当然,我们可以自己写一个数字放进程序里运行,看看能否识别:
在这里插入图片描述
比如这个数字6,识别结果为:
在这里插入图片描述
说明这个模型还是非常不错的,当然,大家可以继续更换测试的图像,试着换一换其他的数字,你也许就会发现,机器可能对6和9傻傻分不清,除此之外,在识别验证码时,Z和2,I和1,O和0都是机器很难去正确识别的。

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

    # 加载数据并开始预测
    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 = paddle_number.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代表读取数组中倒数第一列

以上就是第五步模型预测的代码

至此,你应该就能在本地电脑跑通这个程序了,总的来说,这段代码还是有一定难度的,如果读不懂也没关系,大家可以通过上传自己的手写数字进行学习,学习这个模型是怎么工作的。

另外,再多插一嘴,自己上传的图片,图片路径要写对,有时直接把图片地址复制过来会报错,这时可以在引号前加一个r:

infer_path = r'C:\Users\Administrator\Desktop\paddledetection\自己的手写数字6.jpg'

在这里插入图片描述
如果想要获取完整代码,大家可以关注以下公众号,并回复:“手写数字”
在这里插入图片描述
里面还有更多python趣味程序在等着大家,欢迎大家一起交流学习!

发布了32 篇原创文章 · 获赞 63 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/zbp_12138/article/details/104144563