From 04dc79d095936ce38145f392eecb9a25e66252a7 Mon Sep 17 00:00:00 2001
From: dayhaha <18800111918@163.com>
Date: Tue, 3 Jan 2017 13:21:56 +0800
Subject: [PATCH] initial complete for recognize_digits
---
.pre-commit-config.yaml | 17 +
.style.yapf | 3 +
.travis.yml | 24 +
README.md | 2 +-
recognize_digits/README.md | 582 +++++++++++++++++-
recognize_digits/data/get_mnist_data.sh | 35 ++
recognize_digits/image/Conv_layer.png | Bin 0 -> 56507 bytes
recognize_digits/image/MLP.png | Bin 0 -> 107651 bytes
recognize_digits/image/Max_pooling.png | Bin 0 -> 44061 bytes
recognize_digits/image/cnn.png | Bin 0 -> 44500 bytes
recognize_digits/image/mlp_train_log.png | Bin 0 -> 58618 bytes
.../image/mnist_example_image.png | Bin 0 -> 30626 bytes
recognize_digits/image/softmax_regression.png | Bin 0 -> 54755 bytes
recognize_digits/image/softmax_train_log.png | Bin 0 -> 59217 bytes
recognize_digits/src/cnn_mnist.py | 71 +++
recognize_digits/src/evaluate.py | 37 ++
recognize_digits/src/load_data.py | 53 ++
recognize_digits/src/mlp_mnist.py | 55 ++
recognize_digits/src/mnist_provider.py | 48 ++
recognize_digits/src/plot_error.py | 36 ++
recognize_digits/src/predict.py | 77 +++
recognize_digits/src/softmax_mnist.py | 50 ++
recognize_digits/src/train.sh | 34 +
travis/precommit.sh | 19 +
24 files changed, 1141 insertions(+), 2 deletions(-)
create mode 100644 .pre-commit-config.yaml
create mode 100644 .style.yapf
create mode 100644 .travis.yml
create mode 100755 recognize_digits/data/get_mnist_data.sh
create mode 100644 recognize_digits/image/Conv_layer.png
create mode 100644 recognize_digits/image/MLP.png
create mode 100644 recognize_digits/image/Max_pooling.png
create mode 100644 recognize_digits/image/cnn.png
create mode 100644 recognize_digits/image/mlp_train_log.png
create mode 100644 recognize_digits/image/mnist_example_image.png
create mode 100644 recognize_digits/image/softmax_regression.png
create mode 100644 recognize_digits/image/softmax_train_log.png
create mode 100644 recognize_digits/src/cnn_mnist.py
create mode 100755 recognize_digits/src/evaluate.py
create mode 100644 recognize_digits/src/load_data.py
create mode 100644 recognize_digits/src/mlp_mnist.py
create mode 100644 recognize_digits/src/mnist_provider.py
create mode 100644 recognize_digits/src/plot_error.py
create mode 100644 recognize_digits/src/predict.py
create mode 100644 recognize_digits/src/softmax_mnist.py
create mode 100755 recognize_digits/src/train.sh
create mode 100755 travis/precommit.sh
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..2edd051d
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,17 @@
+- repo: https://github.com/Lucas-C/pre-commit-hooks.git
+ sha: c25201a00e6b0514370501050cf2a8538ac12270
+ hooks:
+ - id: remove-crlf
+- repo: https://github.com/reyoung/mirrors-yapf.git
+ sha: v0.13.2
+ hooks:
+ - id: yapf
+ files: (.*\.(py|bzl)|BUILD|.*\.BUILD|WORKSPACE)$ # Bazel BUILD files follow Python syntax.
+- repo: https://github.com/pre-commit/pre-commit-hooks
+ sha: 7539d8bd1a00a3c1bfd34cdb606d3a6372e83469
+ hooks:
+ - id: check-added-large-files
+ - id: check-merge-conflict
+ - id: check-symlinks
+ - id: detect-private-key
+ - id: end-of-file-fixer
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 00000000..4741fb4f
--- /dev/null
+++ b/.style.yapf
@@ -0,0 +1,3 @@
+[style]
+based_on_style = pep8
+column_limit = 80
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..f5409798
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,24 @@
+language: cpp
+cache: ccache
+sudo: required
+dist: trusty
+os:
+ - linux
+env:
+ - JOB=PRE_COMMIT
+
+addons:
+ apt:
+ packages:
+ - git
+ - python
+ - python-pip
+ - python2.7-dev
+before_install:
+ - pip install virtualenv pre-commit
+script:
+ - travis/precommit.sh
+notifications:
+ email:
+ on_success: change
+ on_failure: always
diff --git a/README.md b/README.md
index edebfc2a..f6aeb277 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
# 深度学习入门
1. [新手入门](fit_a_line/README.md)
-1. [个性化推荐](recommender_system/README.md)
1. [识别数字](recognize_digits/README.md)
1. [图像分类](image_classification/README.md)
1. [词向量](word2vec/README.md)
1. [情感分析](understand_sentiment/README.md)
1. [文本序列标注](label_semantic_roles/README.md)
1. [机器翻译](machine_translation/README.md)
+1. [个性化推荐](recommender_system/README.md)
1. [图像自动生成](gan/README.md)
diff --git a/recognize_digits/README.md b/recognize_digits/README.md
index 566844d6..06047394 100644
--- a/recognize_digits/README.md
+++ b/recognize_digits/README.md
@@ -1 +1,581 @@
-TODO: Write about https://github.com/PaddlePaddle/Paddle/tree/develop/demo/mnist
+# 手写字符识别教程
+
+## 背景介绍
+当我们学习编程的时候,编写的第一个程序一般是实现打印"Hello World"。而就像编程语言有"Hello World",机器学习则
+有MNIST数据库。MNIST数据库是手写字符,属于机器学习中典型的图像分类问题,而由于其问题简单,数据集完备,因此
+常常被作为机器学习入门的教程。
+
+
+该数据库的提供者Yann LeCun,他早起在手写字符识别上做了很多研究,在研究过程中
+提出了卷积神经网络,大幅度的提高了识别能力,也因此成为了深度学习领域的奠基人之一。如今的深度学习领域,卷积
+神经网络占据了至关重要的地位,从最早Yann LeCun提出的简单LeNet,到如今ImageNet大赛上的优胜模型VGGNet,GoogLeNet,
+虽然在网络结构上要复杂很多,但是其核心还是与LeNet类似,只不过在网络的组织上面有了更多的技巧,如GoogLeNet的
+Inception模块,以及深度学习为了防止过拟合,还会加入正则,BatchNorm等等方法。
+
+
+MNIST数据库作为一个简单的计算机视觉数据集,包含一系列如下的手写数字:
+
+
+图1. MNIST图片示例
+
+其同时包含每张图片的标签,标签告诉我们图像中包含的数字是什么。例如,上面的图片中对应的标签为[3, 4, 7, 0, 4, 1, 1, 4, 3, 1]
+本教程作为入门教程,希望给初学者讲述机器学习和Paddle的入门知识。在该教程中,我们会训练一个模型,该模型会扫描给定的图片,然后给出对应的标签。
+
+
+我们意图并不是展示一个对于此类问题的完美的模型,而是会首先给出一个简单的模型,然后逐步深入的优化模型,让初学者感受复杂的模型是如何带来效
+果上提升的。
+
+
+## 模型概览
+### Softmax回归(Softmax Regression)
+
+####问题说明:
+给予MNIST数据,我们希望训练一个分类器f,对于每张给定的图片数据x,预测结果为f(x),应该尽可能准确的接近真实label。
+这是一个多分类问题,在神经网络中,多分类问题一般用softmax函数来作为输出,因此最简单的神经网络分类器就是一层输
+入数据层,加上一个输出softmax层,也就是softmax回归。
+
+####原理介绍:
+MNIST数据库的图片为28X28的二维图像,为了进行计算,我们一般将上将28X28的二维图像转化为n(n=784)维的向量,因此我们采用$x_i(i=0,1,2,...,n-1)$来表示输入的图片数据。同时,我们采用$y_i(i=0,1,2,..9)$来表示输出,其含义为:
+比如说,如果图片的数字标签为3,则$y_i$组成的向量为[0,0,0,1,0,0,0,0,0,0]。输入层的数据x传到softmax层,在激活操作之
+前,会乘以相应的权重w,并加上偏置变量b,具体如下:
+$$ net_i = \sum_j W_{i,j}x_j + b_i $$
+net是激活前的值(仅仅进行了矩阵乘法),然后再用softmax函数进行激活:
+其中softmax函数的定义如下:
+$$ softmax(x_i) = \frac{e^{x_i}}{\sum_j e^{x_j}} $$
+即:
+$$ y_i = \frac{e^{net_i}}{\sum_j e^{net_j}} $$
+神经网络的训练采用backpropagation的形式,其一般会定义一个损失函数(也称目标函数),训练的目的是为了减小目标函数的
+值。在分类问题中,我们一般采用交叉熵代价损失函数(cross entropy)。
+
+以下为softmax回归的网络图:
+
+
+图2. softmax回归网络结构图
+注:图中权重用黑线表示,偏置用红线表示,+1代表偏置参数的系数为1
+
+
+
+### 多层感知器(Multilayer Perceptron, MLP)
+#### 原理介绍:
+在softmax回归中,我们采用了最简单的两层神经网络,分别为输入的datalayer层和输出的softmax层,模型比较简单,意味着
+其拟合能力有限。因此,为了达到更好的识别效果,我们可以考虑在输入层和输出层中间加上若干个隐藏层。
+在该网络层中,我们有输入X($x_i(i=0,1,2,...,n-1)$),输出标签Y($y_i(i=0,1,2,..9)$),为了表示方便,以下我们都直接
+用向量计算来表示。经过第一层网络,我们可以得到:
+$$ H_1 = activation(W_1X + b_1) $$
+上面,activation代表激活函数,其常见的为sigmoid,tanh或ReLU等函数。
+经过第二层网络,可以得到:
+$$ H_2 = activation(W_2H_1 + b_2) $$
+最后,再经过输出层:
+$$ Y = softmax(W_3H_2 + b_3) $$
+得到的P即为最后的预测结果向量。
+
+
+图3. 多层感知器网络结构图
+注:图中权重用黑线表示,偏置用红线表示,+1代表偏置参数的系数为1
+
+
+#### 常见激活函数介绍
+- sigmoid激活函数:
+$$ f(x) = \frac{1}{1+e^{-x}} $$
+- tanh激活函数:
+$$ f(x) = tanh(x) = \frac{e^x-e^{-x}}{e^x+e^{-x}} $$
+实际上,tanh函数只是规模变化的sigmoid函数:
+$$ tanh(x) = 2sigmoid(2x) - 1 $$
+- ReLU激活函数:
+$$ f(x) = max(0, x) $$
+
+### 卷积神经网络(Convolutional Neural Network, CNN)
+#### 卷积层
+
+
+图4. 卷积层图片
+
+卷积层是卷积神经网络的核心基石。该层的参数由一组可学习的过滤器(也叫作卷积核)组成。在前向过程中,每个卷积核在
+输入层进行横向和纵向的扫描,与输入层对应扫描位置进行卷积,得到的结果加上偏置并用相应的激活函数进行激活,结果能够得到
+一个二维的activation map。每个特定的卷积核都能得到特定的activation map,如有的卷积核可能对识别边角,有的可能识别
+圆圈,那这些卷积核可能对于对应的特征响应要强。
+#### 池化层
+
+
+图5. 池化层图片
+
+卷积神经网络另外一个重要的概念就是池化层,这是非线性下采样的一种形式。有许多种非线性函数能够完成下采样的功能,
+而其中用的最多的就是最大采样。如上所示,其用特定大小不重叠的矩形框将输入层分成不同的区域,然后对于每一个矩形框
+内的数,取最大值来代表这个区域,从而得到输出层。池化层的主要作用是减少网络的参数,从而能够减小计算量,并且能够
+控制过拟合。通常情况下在卷积层的后面都会加上一个池化层。
+
+#### 网络结构LeNet
+
+
+图6. 卷积神经网络结构
+
+上面的网络是典型的LeNet结构,是卷积神经网络鼻祖Yann LeCun最早提出的模型。从图上我们可以看出,从输入的二维图像,
+经过两次卷积->采样,然后再经过全连接,最后的输出层是softmax分类层。
+
+
+## 数据准备
+
+### 数据下载
+
+我们首先下载 MNIST数据库,该数据库是手写字符识别常用的数据库。在 `demo/mnist` 目录中执行以下命令,进行下载:
+
+```bash
+./data/get_mnist_data.sh
+```
+
+### 数据说明
+1. 解压缩
+将下载下来的数据进行gzip解压,可以在文件夹 `demo/mnist/data/raw_data` 中找到以下文件:
+```
+t10k-images-idx3-ubyte 测试数据图片,10,000条数据
+t10k-labels-idx1-ubyte 测试数据标签,10,000条数据
+train-images-idx3-ubyte 训练数据图片,60,000条数据
+train-labels-idx1-ubyte 训练数据标签,60,000条数据
+ ```
+2. 数据格式
+MNIST的每条数据可以分为两部分:手写字符图片和对应的标签。图片是28X28的像素矩阵,而标签则对应着0~9的10个数字,我们随机选取训练集中的10张图片进行绘制,并给出选取图片对应的标签,绘制脚本为为 `./data/draw_data.py`。
+
+```python
+import numpy as np
+import matplotlib.pyplot as plt
+import random
+
+
+def read_data(path, filename):
+ imgf = path + filename + "-images-idx3-ubyte"
+ labelf = path + filename + "-labels-idx1-ubyte"
+ f = open(imgf, "rb")
+ l = open(labelf, "rb")
+
+ f.read(16)
+ l.read(8)
+
+ # Define number of samples for train/test
+ n = 60000 if "train" in filename else 10000
+
+ rows = 28
+ cols = 28
+
+ images = np.fromfile(
+ f, 'ubyte',
+ count=n * rows * cols).reshape(n, rows, cols).astype('float32')
+ labels = np.fromfile(l, 'ubyte', count=n).astype("int")
+
+ return images, labels
+
+
+if __name__ == "__main__":
+ train_images, train_labels = read_data("./raw_data/", "train")
+ test_images, test_labels = read_data("./raw_data/", "t10k")
+ label_list = []
+ for i in range(10):
+ index = random.randint(0, train_images.shape[0] - 1)
+ label_list.append(train_labels[index])
+ plt.subplot(1, 10, i + 1)
+ plt.imshow(train_images[index], cmap="Greys_r")
+ plt.axis('off')
+ print('label: %s' % (label_list, ))
+ plt.show()
+```
+
+
+
+
+
+```python
+label: [3, 4, 7, 0, 4, 1, 1, 4, 3, 1]
+```
+
+## 模型配置说明
+
+在 `./src/mnist_provider.py` 中,我们将数据用PyDataProvider2进行封装,然后在网络配置文件中进行datasource的配置:
+
+```python
+define_py_data_sources2(
+ train_list=data_dir + 'train.list',
+ test_list=data_dir + 'test.list',
+ module='mnist_provider',
+ obj='process')
+```
+以上封装完成了神经网络的数据层(data_layer)的工作,这样就可以通过调用数据层data_layer的方式来获取数据:
+
+```python
+# 此处 1 * 28 * 28,1代表1个channel,在图片处理中,彩色图片一般具有
+# RGB 3个channel,而MNIST数据只有1个channel,每个channel为28*28的像素矩阵。
+data_size = 1 * 28 * 28
+label_size = 10
+img = data_layer(name='pixel', size=data_size) # 获取图片
+label = data_layer(name='label', size=label_size) # 获取标签
+```
+
+
+
+###数据提供
+首先是用Paddle的PyDataProvider2来为模型导入数据,如 `src/mnist_provider.py` 中所示:
+
+```python
+# Define a py data provider
+@provider(
+ input_types={'pixel': dense_vector(28 * 28),
+ 'label': integer_value(10)},
+ cache=CacheType.CACHE_PASS_IN_MEM)
+def process(settings, filename): # settings is not used currently.
+ # following code not shown here
+```
+在上面代码中,process函数读取MNIST数据,然后用yield函数返回数据的生成器,然后用@provider对返回的数据进行包装
+(具体的过程见 `Paddle_root_path/python/paddle/trainer/PyDataProvider2.py`,从而方便以Paddle DataProvider对象的形式进行调用。
+
+### 配置
+以下为如何用Paddle实现Softmax回归模型:
+然后,进行网络的配置,如 `src/softmax_mnist.py` 中所示:
+
+```python
+####################Data Configuration ##################
+
+ if not is_predict:
+ data_dir = './data/'
+ define_py_data_sources2(
+ train_list=data_dir + 'train.list',
+ test_list=data_dir + 'test.list',
+ module='mnist_provider',
+ obj='process')
+```
+- 首先配置数据,如上给出在训练过程中,训练(train_list)和测试数据集(test_list)的位置,
+指定数据提供模块(module)函数,目标函数(obj)代表在module中提供数据的函数。
+
+```python
+######################Algorithm Configuration #############
+settings(
+ batch_size=128,
+ learning_rate=0.1 / 128.0,
+ learning_method=MomentumOptimizer(0.9),
+ regularization=L2Regularization(0.0005 * 128))
+```
+- 然后指定训练相关的参数,batch大小(batch_size)表示神经网络每次训练使用的数据为128条,训练速度(learning_rate)是迭代
+的速度,影响着网络的训练收敛速度有关系,训练方法(learning_method)代表训练过程在更新权重时采用动量优化器
+(MomentumOptimizer),其中参数0.9代表动量优化每次保持前一次速度的0.9倍,正则化(regularization)是防止网络过拟合的一
+种手段,此处采用L2正则化。
+
+### 网络结构
+#### Softmax回归
+
+```python
+#######################Network Configuration #############
+
+data_size = 1 * 28 * 28
+label_size = 10
+img = data_layer(name='pixel', size=data_size)
+
+# softmax regression is used
+predict = fc_layer(input=img, size=10, act=SoftmaxActivation())
+```
+- 最后就是指定网络的结构,定义好dataprovider之后,就可以通过data_layer调用来获取数据img,然后通过一层简单的softmax
+全连接层,得到预测的结果,然后指定训练的损失函数为分类损失(classification_cost),一般分类问题的损失函数为交叉熵
+损失函数(cross_entropy)。
+通过控制变量is_predict,该配置脚本也可以在预测时候使用,将is_predict置为True,则最后直接输出预测结果,而不会经过
+损失函数来进行训练过程。
+
+#### 多层感知器
+
+```python
+#######################Network Configuration #############
+
+# The first fully-connected layer
+hidden1 = fc_layer(input=img, size=128, act=ReluActivation())
+# The second fully-connected layer and the according activation function
+hidden2 = fc_layer(input=hidden1, size=64, act=ReluActivation())
+# The thrid fully-connected layer, note that the hidden size should be 10,
+# which is the number of unique digits
+predict = fc_layer(input=hidden2, size=10, act=SoftmaxActivation())
+```
+以上是一个简单的带有两个隐藏层的多层感知器,也就是全连接网络,两个隐藏层的激活函数均采用ReLU函数,最后的输出层用
+softmax激活函数。
+
+#### 卷积神经网络
+
+```python
+#######################Network Configuration #############
+
+# first conv layer
+conv_pool_1 = simple_img_conv_pool(
+ input=img,
+ filter_size=5,
+ num_filters=20,
+ num_channel=1,
+ pool_size=2,
+ pool_stride=2,
+ act=TanhActivation())
+# second conv layer
+conv_pool_2 = simple_img_conv_pool(
+ input=conv_pool_1,
+ filter_size=5,
+ num_filters=50,
+ num_channel=20,
+ pool_size=2,
+ pool_stride=2,
+ act=TanhActivation())
+# The first fully-connected layer
+fc1 = fc_layer(input=conv_pool_2, size=128, act=TanhActivation())
+# The softmax layer, note that the hidden size should be 10,
+# which is the number of unique digits
+predict = fc_layer(input=fc1, size=10, act=SoftmaxActivation())
+```
+
+
+```python
+if not is_predict:
+ lbl = data_layer(name="label", size=label_size)
+ inputs(img, lbl)
+ outputs(classification_cost(input=predict, label=lbl))
+else:
+ outputs(predict)
+```
+
+###训练过程
+最后,就可以通过配置训练脚本 `./src/train.sh` 来执行训练过程:
+
+```bash
+config=softmax_mnist.py
+output=./softmax_mnist_model
+log=softmax_train.log
+
+paddle train \
+--config=$config \
+--dot_period=10 \
+--log_period=100 \
+--test_all_data_in_one_period=1 \
+--use_gpu=0 \
+--trainer_count=1 \
+--num_passes=100 \
+--save_dir=$output \
+2>&1 | tee $log
+
+python -m paddle.utils.plotcurve -i $log > plot.png
+```
+参数意义分别为:
+- config: 网络配置的脚本
+- dot_period: 在每训练 `dot_period` 个批次后打印一个 `.`
+- log_period: 每隔多少batch打印一次日志
+- test_all_data_in_one_period: 每次测试是否用所有的数据
+- use_gpu: 是否使用GPU
+- trainer_count: 使用CPU或GPU的个数
+- num_passed: 训练进行的轮数(每次训练使用完所有数据为1轮)
+- save_dir: 模型存储的位置
+配置好参数之后,执行脚本 `./src/train.sh` 训练日志如下所示:
+
+## 训练模型
+### Softmax回归(Softmax Regression)
+####训练过程
+
+```
+I1218 18:12:41.720213 2464 Util.cpp:154] commandline: /usr/local/bin/../opt/paddle/bin/paddle_trainer --config=mlp_mnist.py --dot_period=10 --log_period=100 -- test_all_data_in_one_period=1 --use_gpu=0 --trainer_count=1 --num_passes=100 --save_dir=./mlp_mnist_model
+......
+```
+
+#### 训练误差图
+用脚本 `./src/plot_error.py` 可以画出训练过程中的误差变化曲线:
+```bash
+python src/plot_error.py softmax_train.log
+```
+```python
+import matplotlib.pyplot as plt
+import re
+import sys
+
+def plot_log(filename):
+ with open(filename, 'r') as f:
+ text = f.read()
+ pattern = re.compile(
+ 'Test.*? cost=([0-9]+\.[0-9]+).*?pass-([0-9]+)',
+ re.S)
+ results = re.findall(pattern, text)
+ cost, pass_ = zip(*results)
+ cost_float = map(float, cost)
+ pass_int = map(int, pass_)
+ plt.plot(pass_int, cost_float, 'bo', pass_, cost_float, 'k')
+ plt.ylabel('AvgCost')
+ plt.xlabel('epoch')
+ plt.show()
+
+
+if __name__ == '__main__':
+ plot_log('softmax_train.log')
+```
+
+
+图8. softmax回归训练误差图
+
+
+#### 评估过程
+用脚本 `./src/evaluate.py ` 可以选出最好的Pass训练出来的模型,可以执行以下命令:
+```bash
+./src/evaluate.py mlp_train.log
+```
+得到的结果类似如下:
+```text
+Best pass is 00047, error is 0.473053, which means predict get error as 0.343894
+The classification accuracy is 89.49%
+```
+
+从上面过程中可以看到,模型分类效果最好的时候是pass-00047,分类准确率为89.49%,而最终的pass-00099的准确率为85.39%。
+从图中也可以看出,准确率最好的时候并以不定是最后一个pass的模型。
+
+#### 预测过程
+用脚本 `./src/predict.py` 用训练好的模型进行预测
+```bash
+./src/predict.py -c softmax_mnist.py -d ../data/raw_data/ -m softmax_mnist_model/pass-00047
+```
+- -c 指定模型的结构
+- -d 指定需要预测的数据源,这里用测试数据集进行预测
+- -m 指定模型的参数,这里用之前训练效果最好的模型进行预测
+```python
+import os
+import sys
+from docopt import docopt
+import numpy as np
+
+from py_paddle import swig_paddle, DataProviderConverter
+from paddle.trainer.PyDataProvider2 import dense_vector
+from paddle.trainer.config_parser import parse_config
+
+from load_data import read_data
+
+class Prediction():
+ def __init__(self, train_conf, data_dir, model_dir):
+
+ conf = parse_config(
+ train_conf,
+ 'is_predict=1')
+ self.network = swig_paddle.GradientMachine.createFromConfigProto(
+ conf.model_config)
+ self.network.loadParameters(model_dir)
+
+ self.images, self.labels = read_data("./data/raw_data/", "train")
+
+ slots = [ dense_vector( 28 * 28 ) ]
+ self.converter = DataProviderConverter(slots)
+
+ def predict(self, index):
+ input = self.converter([[self.images[index].flatten().tolist() ]])
+ output = self.network.forwardTest(input)
+ prob = output[0]["value"]
+ predict = np.argsort(-prob)
+ print prob
+ print predict[0][0], self.labels[index]
+
+train_conf = 'softmax_mnist.py'
+data_dir = '../data/raw_data/'
+model_dir = 'softmax_mnist_model/pass-00047'
+predictor = Prediction(train_conf, data_dir, model_dir)
+while True:
+ index = int(raw_input("Input image_id [0~9999]: "))
+ predictor.predict(index)
+```
+根据提示,输入需要预测的图片的序号,则分类器能够给出预测的结果和实际的标签。
+```
+Input image_id [0~9999]: 3
+[[ 1.00000000e+00 1.60381094e-28 1.60381094e-28 1.60381094e-28
+ 1.60381094e-28 1.60381094e-28 1.60381094e-28 1.60381094e-28
+ 1.60381094e-28 1.60381094e-28]]
+0 0
+```
+上面,给出的向量是神经网络输出的十维向量,可以理解为分类为各个数字的概率,实际预测的类即为向量中
+值最大的一项。如上,代表该分类器接近100%的认为该图片上面的数字为0,而实际标签给出的类也确实如此。
+
+### 多层感知器(Multilayer Perceptron, MLP)
+####训练过程
+将配置脚本 `./src/train.sh` 一下部分进行修改:
+```bash
+config=mlp_mnist.py
+output=./mlp_mnist_model
+log=mlp_train.log
+```
+执行脚本即可进行训练
+```bash
+./train.sh
+```
+训练日志如下:
+```
+I1218 19:10:05.007380 3123 Util.cpp:154] commandline: /usr/local/bin/../opt/paddle/bin/paddle_trainer --config=mlp_mnist.py --dot_period=10 --log_period=100 -- test_all_data_in_one_period=1 --use_gpu=0 --trainer_count=1 --num_passes=100 --save_dir=./mlp_mnist_model
+......
+```
+#### 评估过程
+用脚本 `./src/evaluate.py ` 可以选出最好的Pass训练出来的模型,可以执行以下命令:
+```bash
+./src/evaluate.py mlp_train.log
+```
+得到的结果类似如下:
+```text
+Best pass is 00085, error is 0.164746, which means predict get error as 0.202945
+The classification accuracy is 94.95%
+```
+
+#### 训练误差图
+用脚本 `./src/plot_error.py` 可以画出训练过程中的误差变化曲线:
+```bash
+python src/plot_error.py mlp_train.log
+```
+
+
+图9. 多层感知器训练误差图
+
+
+从训练日志中我们可以看出,最终训练的准确率为94.95%。这是多层网络分类分类效果,可以
+看到其相对于softmax回归来说有了显著的分类效果的提升。但是,这还不是最好的分类效果,后面,我们可以看到卷积神经
+网络对于图像分类能够达到更为惊人的准确率。
+
+#### 预测过程
+用脚本 `./src/predict.py` 用训练好的模型进行预测
+```bash
+./src/predict.py -c mlp_mnist.py -d ./data/raw_data/ -m ./mlp_mnist_model/pass-00085
+```
+
+
+### 卷积神经网络(Convolutional Neural Network, CNN)
+####训练过程
+将配置脚本 `./src/train.sh` 一下部分进行修改:
+```bash
+config=cnn_mnist.py
+output=./cnn_mnist_model
+log=cnn_train.log
+```
+执行脚本即可进行训练
+```bash
+./train.sh
+```
+
+训练日志如下:
+```
+I1219 10:18:34.637485 4931 Util.cpp:154] commandline: /usr/local/bin/../opt/paddle/bin/paddle_trainer --config=cnn_mnist.py --dot_period=10 --log_period=100 -- test_all_data_in_one_period=1 --use_gpu=0 --trainer_count=1 --num_passes=100 --save_dir=./cnn_mnist_model
+......
+```
+#### 评估过程
+用脚本 `./src/evaluate.py ` 可以选出最好的Pass训练出来的模型,可以执行以下命令:
+```bash
+./src/evaluate.py cnn_train.log
+```
+得到的结果类似如下:
+```text
+Best pass is 00003, error is 0.0305203, which means predict get error as 0.087350
+The classification accuracy is 99.01%
+```
+
+从训练日志中我们可以看出,最终训练的准确率为99.01%。由此可以看到,对于图像问题而言,卷积神经网络能够
+比一般全连接网络达到更好的识别效果,而这与卷积层的性质是分不开的。同时,我们可以看到,卷积神经网络在
+pass-0003的时候就能达到好的效果,说明其训练收敛的速度很快。
+
+#### 预测过程
+用脚本 `./src/predict.py` 用训练好的模型进行预测
+```bash
+./src/predict.py -c cnn_mnist.py -d ./data/raw_data/ -m ./cnn_mnist_model/pass-00003
+```
+
+## 总结
+从上面的过程中,我们了解到了机器学习的基本知识,softmax回归、多层感知器和卷积神经网络都是最基础的机器学习模型,而复杂的神经网络
+都是从这些衍生出来的,因此了解了这些基本的东西之后,对于我们之后的学习大有裨益。在这过程中,我们也观察到当我们将模型从简单的
+Softmax回归到稍微复杂的卷积神经网络的时候,识别准确率有了大幅度的提升,而这与卷积层的加入是密切联系的,这启发我们在之后遇到新的
+模型的时候,要认识到新模型相对于其他模型来说效果提升的关键之处。此外,在体验用Paddle搭建模型识别MNIS图片的同时
+我们了解了Paddle模型搭建的过程,从dataprovider的编写,到网络层的构建,到最后的训练和预测,是用Paddle实现自定义模型的一个
+基本流程。对这个流程熟悉以后,我们可以用自己的数据,定义自己的网络模型,来完成自己的训练和预测任务。
+## 参考文献
+1. LeCun, Yann, et al. "Gradient-based learning applied to document recognition." Proceedings of the IEEE 86.11 (1998): 2278-2324.
diff --git a/recognize_digits/data/get_mnist_data.sh b/recognize_digits/data/get_mnist_data.sh
new file mode 100755
index 00000000..5a71d4f1
--- /dev/null
+++ b/recognize_digits/data/get_mnist_data.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env sh
+# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# This scripts downloads the mnist data and unzips it.
+set -e
+DIR="$( cd "$(dirname "$0")" ; pwd -P )"
+rm -rf "$DIR/raw_data"
+mkdir "$DIR/raw_data"
+cd "$DIR/raw_data"
+
+echo "Downloading..."
+
+for fname in train-images-idx3-ubyte train-labels-idx1-ubyte t10k-images-idx3-ubyte t10k-labels-idx1-ubyte
+do
+ if [ ! -e $fname ]; then
+ wget --no-check-certificate http://yann.lecun.com/exdb/mnist/${fname}.gz
+ gunzip ${fname}.gz
+ fi
+done
+
+cd $DIR
+rm -f *.list
+echo "./data/raw_data/train\n" > "$DIR/train.list"
+echo "./data/raw_data/t10k\n" > "$DIR/test.list"
diff --git a/recognize_digits/image/Conv_layer.png b/recognize_digits/image/Conv_layer.png
new file mode 100644
index 0000000000000000000000000000000000000000..750f2472921285f85ed74a02c50e1fc5acce84ad
GIT binary patch
literal 56507
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 }uZf4!_g;1nn+zd?(qdBI#yS|2PJg0H%$AM<
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%0D8FtpjAkipWU0wZe+A!cc
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
literal 0
HcmV?d00001
diff --git a/recognize_digits/image/MLP.png b/recognize_digits/image/MLP.png
new file mode 100644
index 0000000000000000000000000000000000000000..c3b34be48180c81f874e1636a2d00e5ef50e3fb1
GIT binary patch
literal 107651
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