PaddlePaddle实战 | CNN之Mnist手写字识别

P

阅读本文需大概了解CNN实现的基本原理,推荐资料如下:

  1. CNN笔记:通俗理解卷积神经网络 – CSDN博客
  2. 【深度学习系列】卷积神经网络CNN原理详解(一)–基本原理 – Charlotte77 – 博客园
  3. 哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili-深度学习 Deep Learning
  4. 吴恩达微专业 – 卷积神经网络 – 网易云课堂

网络结构参考经典的LeNet-5,结构如下:

PaddlePaddle实战 | CNN之Mnist手写字识别

卷积层–>采样层–>卷积层–>采样层–>全连接层

 

?show me the code

 

定义网络结构

第一卷积层:核大小为5,数量为20,通道为1

第一池化层:大小为2,步长为2

激活函数采用修正线性单元Relu

 

第二层参数类似如上

 

第三层全连层:神经元数量为10,激活函数为Softmax

# 定义卷曲网络结构
def convolutional_neural_network(self, img):

    # 第一卷积池化层
    conv_pool_1 = paddle.networks.simple_img_conv_pool(input=img,
                                                       filter_size=5,
                                                       num_filters=20,
                                                       num_channel=1,
                                                       pool_size=2,
                                                       pool_stride=2,
                                                       act=paddle.activation.Relu())

    # 第二卷积池化层
    conv_pool_2 = paddle.networks.simple_img_conv_pool(input=conv_pool_1,
                                                       filter_size=5,
                                                       num_filters=50,
                                                       num_channel=20,
                                                       pool_size=2,
                                                       pool_stride=2,
                                                       act=paddle.activation.Relu())

    # 全连接层
    predict = paddle.layer.fc(input=conv_pool_2,
                              size=10,
                              act=paddle.activation.Softmax())

    return predict

 

定义分类器

相当于一个盒子,把需要传入的images格式定义好,然后载入构建好的网络中

# 创建分类器
def classifier(self):
    images = paddle.layer.data(name='x', type=paddle.data_type.dense_vector(784))

    # 载入之前定义好的网络
    out = self.convolutional_neural_network(images)

    return out

 

创建训练器

把前面定义好的分类器载入进来,之后定义标签,创建损失函数,在定义训练模型之前,需要先创建训练相关的参数,后面paddlepaddle会根据此,保存参数,以便复用。

 

定义训练方法中,主要加入了正则化方法,防止训练过程中出现过拟合现象,具体原理需要看吴大大的课程,链接在这里:改善深层神经网络:超参数调试、正则化以及优化 – 网易云课堂

 

训练模型采用的是SGD(Stochastic gradient descent),即随机梯度下降优化算法,每次从样本集中选出部分进行训练,能够大大加快模型收敛效率。

# 创建训练器
def trainer(self):

    out = self.classifier()

    # 定义标签
    label = paddle.layer.data(name='label', type=paddle.data_type.integer_value(10))

    # 创建损失函数
    cost = paddle.layer.classification_cost(input=out, label=label)

    # 指定训练相关参数
    parameters = paddle.parameters.create(layers=cost)

    # 定义训练方法
    optimizer = paddle.optimizer.Momentum(learning_rate=0.01/128.0,
                                          momentum=0.9,
                                          regularization=paddle.optimizer.L2Regularization(rate=0.0005*128))

    # 定义训练模型
    trainer = paddle.trainer.SGD(cost=cost,
                                 parameters=parameters,
                                 update_equation=optimizer)

    return trainer

 

定义训练过程

这里不得不夸一下paddlepaddle封装的事件处理机制,支持在训练过程中打印你自己想要看到的信息,来判断模型的拟合程度。模型训练中的shuffle是paddlepaddle将数据打散的一种算法,具体可以查看这篇文章【深度学习系列】PaddlePaddle之数据预处理 – Charlotte77 – 博客园 的讲解

# 训练过程
def training(self):

    # 获取训练器
    trainer = self.trainer()

    lists = []

    # 定义训练事件
    def event_handler(enevt):

        if isinstance(enevt, paddle.event.EndIteration):
            if enevt.batch_id % 100 == 0:
                print 'Pass %d, Batch %d, Cost %f, %s' % (enevt.pass_id, enevt.batch_id, enevt.cost, enevt.metrics)

        if isinstance(enevt, paddle.event.EndPass):
            with open('%s/models/params_pass_%d.tar' % (os.getcwd(), enevt.pass_id), 'w') as f:
                trainer.save_parameter_to_tar(f)

            result = trainer.test(reader=paddle.batch(paddle.dataset.mnist.test(), batch_size=128))

            print 'Test with Pass %d, Cost %f, %s\n' % (enevt.pass_id, result.cost, result.metrics)

            lists.append((enevt.pass_id, result.cost, result.metrics['classification_error_evaluator']))

    trainer.train(
        reader=paddle.batch(
            paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size=10000),
            batch_size=128),
        event_handler=event_handler,
        num_passes=10)

    # 寻找最小误差的那次训练
    best = sorted(lists, key=lambda x: float(x[1]))[0]
    print 'Best Pass is %s, testing Avgcost is %s' % (best[0], best[1])
    print 'The classification accuracy is %.2f%%' % (100 - float(best[2]) * 100)

 

参数提取器

对训练好的参数进行提取

# 参数提取器
def get_parameters(self, n=0):
    with open('%s/models/params_pass_%d.tar' % (os.getcwd(), n), 'r') as f:
        parameters = paddle.parameters.Parameters.from_tar(f)
    return parameters

 

图像处理器

将图像进行灰度,大小重置,矩阵化平铺和数值正则化处理,得到一个形如(784, )形式的矩阵

# 图像处理器
def img_handler(self, file=None):

    def load_img(file_path=None):
        img = Image.open(file_path).convert('L')
        img = img.resize((28, 28), Image.ANTIALIAS)
        img = np.array(img).astype(np.float32).flatten()
        img = img / 255.0
        return img

    result = []
    result.append((load_img(file),))
    return result

 

图像预测器

将之前训练过程中得到的参数传入,并预测结果

# 预测器
def predicter(self, out_layer, parameter, input_data):

    predict = paddle.infer(output_layer=out_layer,
                           parameters=parameter,
                           input=input_data)

    lab = np.argsort(-predict)
    print 'The Label img is: %d' % lab[0][0]

 

好,咱们调用下执行的函数,并看下执行的结果

Pass 0, Batch 0, Cost 3.579303, {'classification_error_evaluator': 0.8828125}
Pass 0, Batch 100, Cost 0.131309, {'classification_error_evaluator': 0.046875}
Pass 0, Batch 200, Cost 0.110048, {'classification_error_evaluator': 0.0234375}
Pass 0, Batch 300, Cost 0.071385, {'classification_error_evaluator': 0.0234375}
Pass 0, Batch 400, Cost 0.096440, {'classification_error_evaluator': 0.0390625}
Test with Pass 0, Cost 0.064678, {'classification_error_evaluator': 0.019300000742077827}

- - - - - - 中间的部分太长,就pass掉了哦- - - - - -

Pass 9, Batch 0, Cost 0.008112, {'classification_error_evaluator': 0.0}
Pass 9, Batch 100, Cost 0.007286, {'classification_error_evaluator': 0.0}
Pass 9, Batch 200, Cost 0.011037, {'classification_error_evaluator': 0.0}
Pass 9, Batch 300, Cost 0.027968, {'classification_error_evaluator': 0.015625}
Pass 9, Batch 400, Cost 0.017957, {'classification_error_evaluator': 0.0078125}
Test with Pass 9, Cost 0.027690, {'classification_error_evaluator': 0.009399999864399433}

Best Pass is 9, testing Avgcost is 0.0276895618367
The classification accuracy is 99.06%

从执行来看,第九次训练的效果最好,准确率达到:99.06%,至此我们的模型就已经训练好了,这个时候,你会发现文件夹中已经出现了我们训练好的模型参数了

PaddlePaddle实战 | CNN之Mnist手写字识别

接下来,我们打开PS,随便建立一个正方形的黑底图片,并写出手写字,大概这样, 数字7我用的另外一个笔刷,所以看起来会有些许不同。

PaddlePaddle实战 | CNN之Mnist手写字识别

 

调用预测函数,从上面的输出,我们知道,最好的训练结果是最后一次,因此我们这里传入第九次参数对应的编号,并开始预测。

MnistReg = MnistRecognizer()

# 预测图片
img = MnistReg.img_handler('./images/infer_5.png')
# 选择训练好的模型参数
parameters = MnistReg.get_parameters(9)

MnistReg.predicter(MnistReg.classifier(), parameters, img)

 

看下预测结果,见证奇迹的时刻吧:

I0303 14:42:16.236407 2824999872 Util.cpp:166] commandline:  --use_gpu=False --trainer_count=1 
[INFO 2018-03-03 14:42:16,435 layers.py:2689] output for __conv_pool_0___conv: c = 20, h = 24, w = 24, size = 11520
[INFO 2018-03-03 14:42:16,435 layers.py:2829] output for __conv_pool_0___pool: c = 20, h = 12, w = 12, size = 2880
[INFO 2018-03-03 14:42:16,436 layers.py:2689] output for __conv_pool_1___conv: c = 50, h = 8, w = 8, size = 3200
[INFO 2018-03-03 14:42:16,436 layers.py:2829] output for __conv_pool_1___pool: c = 50, h = 4, w = 4, size = 800
The Label img is: 5

 

完整代码请移步至github:PaddlePaddle手写字识别

欢迎关注和star~~~

 

至此,就撸完了机器学习的Hello World,算是阶段性战果吧,总结一下学习中的关键点和注意事项:

 

1. 选对教程可以事半功倍

因为机器学习中包含大量的数学知识,若在入门阶段没有选对教程,很容易被忽悠得云里雾里,造成从入门到生无可恋。这里建议从吴恩达爸爸的课程开始看起,从course的评价几乎全五星便可窥见一二

 

2. 勿对数学公式耿耿于怀

只要是数学定理前面有人名的,都是那些非人类花了大笔心血的结晶,所以不要妄想花了几个小时就能吃透。看不懂,就放一放,先用一用,看看效果。把模型先跑通,记着它的作用,并标记自己对这一块不懂。并在之后每天花一点时间补足这一块的知识,无外乎线代、概率论和微积分,花多点时间磨即可

 

3. 对自己有一个合理评价

一旦决定要入坑,就不要轻言放弃,这是骨气。但要时常总结,对自己的学习过程进行复盘,就像梯度下降法(GD)一样,一步步达到最优状态。但可能受困于眼界,会时常停留在局部最优中无法走出来,这个时候,可以找一些大牛的文章看下,看看别人研究的领域和业界最新文章,能够有效督促自己走出局部最优区(亦即舒适区)

 

4. 尽可能深度纵向发展

虽然由于大数据的概念普及,才使得机器学习爆炸式增长。但工业级的成熟工具,已经让机器学习门槛变得相当低。因此,貌似你稍微懂点编程,了解大学的数学知识,好像就可以上手机器学习的模型,但了解不等于理解,更有甚者可能发展为误解。我现在的认知是,即使市面上有大量可用的库,但天花板依然在深厚的数学和编码素养。所以,没事多补点数学和动手撸代码,提神又醒脑。不温馨提示:晚上千万不要学数学或撸代码!!!!

 

相关课程资料:

深度学习工程师微专业 – 一线人工智能大师吴恩达亲研-网易云课堂 – 网易云课堂mooc.study.163.com【中英双语】机器学习(Machine Learning)- 吴恩达(Andrew Ng)www.bilibili.com

About the author

张小鸡

日拱一卒,功不唐捐

Add comment

By 张小鸡