阅读本文需大概了解CNN实现的基本原理,推荐资料如下:
- CNN笔记:通俗理解卷积神经网络 – CSDN博客
- 【深度学习系列】卷积神经网络CNN原理详解(一)–基本原理 – Charlotte77 – 博客园
- 哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili-深度学习 Deep Learning
- 吴恩达微专业 – 卷积神经网络 – 网易云课堂
网络结构参考经典的LeNet-5,结构如下:

卷积层–>采样层–>卷积层–>采样层–>全连接层
?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%,至此我们的模型就已经训练好了,这个时候,你会发现文件夹中已经出现了我们训练好的模型参数了

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

调用预测函数,从上面的输出,我们知道,最好的训练结果是最后一次,因此我们这里传入第九次参数对应的编号,并开始预测。
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. 尽可能深度纵向发展
虽然由于大数据的概念普及,才使得机器学习爆炸式增长。但工业级的成熟工具,已经让机器学习门槛变得相当低。因此,貌似你稍微懂点编程,了解大学的数学知识,好像就可以上手机器学习的模型,但了解不等于理解,更有甚者可能发展为误解。我现在的认知是,即使市面上有大量可用的库,但天花板依然在深厚的数学和编码素养。所以,没事多补点数学和动手撸代码,提神又醒脑。不温馨提示:晚上千万不要学数学或撸代码!!!!
相关课程资料:
深度学习工程师微专业 – 一线人工智能大师吴恩达亲研-网易云课堂 – 网易云课堂【中英双语】机器学习(Machine Learning)- 吴恩达(Andrew Ng)