Skip to content

Commit 6860a0d

Browse files
authored
上传独立修改的版本
1 parent aff2868 commit 6860a0d

File tree

3 files changed

+1008
-0
lines changed

3 files changed

+1008
-0
lines changed

lesson10/train_with_paddle_bn.py

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
#!/usr/bin/env python
2+
# -*- coding:utf-8 -*-
3+
################################################################################
4+
#
5+
# Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved
6+
#
7+
################################################################################
8+
"""
9+
Authors: xiake(kedou1993@163.com)
10+
Date: 2017/11/29
11+
12+
使用paddle框架实现逻辑数字识别案例,关键步骤如下:
13+
1.定义分类器网络结构
14+
2.初始化
15+
3.配置网络结构
16+
4.定义成本函数cost
17+
5.定义优化器optimizer
18+
6.定义事件处理函数
19+
7.进行训练
20+
8.利用训练好的模型进行预测
21+
"""
22+
23+
import matplotlib
24+
matplotlib.use('Agg')
25+
import os
26+
from PIL import Image
27+
import numpy as np
28+
import paddle.v2 as paddle
29+
from paddle.v2.plot import Ploter
30+
31+
with_gpu = os.getenv('WITH_GPU', '0') != '0'
32+
33+
step = 0
34+
35+
# 绘图相关标注
36+
train_title_cost = "Train cost"
37+
test_title_cost = "Test cost"
38+
39+
train_title_error = "Train error rate"
40+
test_title_error = "Test error rate"
41+
42+
def softmax_regression(img):
43+
"""
44+
定义softmax分类器:
45+
只通过一层简单的以softmax为激活函数的全连接层,可以得到分类的结果
46+
Args:
47+
img -- 输入的原始图像数据
48+
Return:
49+
predict -- 分类的结果
50+
"""
51+
predict = paddle.layer.fc(
52+
input=img, size=10, act=paddle.activation.Softmax())
53+
return predict
54+
55+
56+
def multilayer_perceptron(img):
57+
"""
58+
定义多层感知机分类器:
59+
含有两个隐藏层(即全连接层)的多层感知器
60+
其中两个隐藏层的激活函数均采用ReLU,输出层的激活函数用Softmax
61+
Args:
62+
img -- 输入的原始图像数据
63+
Return:
64+
predict -- 分类的结果
65+
"""
66+
# 第一个全连接层
67+
hidden1 = paddle.layer.fc(input=img, size=128, act=paddle.activation.Relu())
68+
# 第二个全连接层
69+
hidden2 = paddle.layer.fc(
70+
input=hidden1, size=64, act=paddle.activation.Relu())
71+
# 第三个全连接层,需要注意输出尺寸为10,,对应0-9这10个数字
72+
predict = paddle.layer.fc(
73+
input=hidden2, size=10, act=paddle.activation.Softmax())
74+
return predict
75+
76+
77+
def convolutional_neural_network(img):
78+
"""
79+
定义卷积神经网络分类器:
80+
输入的二维图像,经过两个卷积-池化层,使用以softmax为激活函数的全连接层作为输出层
81+
Args:
82+
img -- 输入的原始图像数据
83+
Return:
84+
predict -- 分类的结果
85+
"""
86+
"""
87+
与第六章代码不同之处:
88+
在两个卷积-池化层之后都加入了batch normalization层norm1和norm2
89+
"""
90+
# 第一个卷积-池化层
91+
conv_pool_1 = paddle.networks.simple_img_conv_pool(
92+
input=img,
93+
filter_size=5,
94+
num_filters=20,
95+
num_channel=1,
96+
pool_size=2,
97+
pool_stride=2,
98+
act=paddle.activation.Relu())
99+
100+
norm1 = paddle.layer.batch_norm(input=conv_pool_1, act=paddle.activation.Relu())
101+
102+
# 第二个卷积-池化层
103+
conv_pool_2 = paddle.networks.simple_img_conv_pool(
104+
input=conv_pool_1,
105+
filter_size=5,
106+
num_filters=50,
107+
num_channel=20,
108+
pool_size=2,
109+
pool_stride=2,
110+
act=paddle.activation.Relu())
111+
112+
norm2 = paddle.layer.batch_norm(input=conv_pool_2, act=paddle.activation.Relu())
113+
114+
# 全连接层
115+
predict = paddle.layer.fc(
116+
input=norm2, size=10, act=paddle.activation.Softmax())
117+
return predict
118+
119+
120+
def netconfig():
121+
"""
122+
配置网络结构
123+
Args:
124+
Return:
125+
images -- 输入层
126+
label -- 标签数据
127+
predict -- 输出层
128+
cost -- 损失函数
129+
parameters -- 模型参数
130+
optimizer -- 优化器
131+
"""
132+
133+
"""
134+
输入层:
135+
paddle.layer.data表示数据层,
136+
name=’pixel’:名称为pixel,对应输入图片特征
137+
type=paddle.data_type.dense_vector(784):数据类型为784维(输入图片的尺寸为28*28)稠密向量
138+
"""
139+
images = paddle.layer.data(
140+
name='pixel', type=paddle.data_type.dense_vector(784))
141+
142+
"""
143+
数据层:
144+
paddle.layer.data表示数据层,
145+
name=’label’:名称为label,对应输入图片的类别标签
146+
type=paddle.data_type.dense_vector(10):数据类型为10维(对应0-9这10个数字)稠密向量
147+
"""
148+
label = paddle.layer.data(
149+
name='label', type=paddle.data_type.integer_value(10))
150+
151+
"""
152+
选择分类器:
153+
在此之前已经定义了3种不同的分类器,在下面的代码中,
154+
我们可以通过保留某种方法的调用语句、注释掉其余两种,以选择特定的分类器
155+
"""
156+
# predict = softmax_regression(images)
157+
# predict = multilayer_perceptron(images)
158+
predict = convolutional_neural_network(images)
159+
160+
# 定义成本函数,addle.layer.classification_cost()函数内部采用的是交叉熵损失函数
161+
cost = paddle.layer.classification_cost(input=predict, label=label)
162+
163+
# 利用cost创建参数parameters
164+
parameters = paddle.parameters.create(cost)
165+
166+
# 创建优化器optimizer,下面列举了2种常用的优化器,不同类型优化器选一即可
167+
# 创建Momentum优化器,并设置学习率(learning_rate)、动量(momentum)和正则化项(regularization)
168+
"""
169+
与第六章代码不同之处:
170+
学习率learning_rate和动量momentum设置的数值不同,
171+
一方面,可以通过单纯修改某个参数值而不引入其他改变,对比第六章实验结果来验证该参数的影响;
172+
另一方面,可以通过设置learning_rate=0.1 / 128.0,momentum=0.95,以使得模型的基础表现相对第六章中下降,如收敛程度或者速度下降
173+
而进一步加入新的模块或者设置后(如加入dropout),模型表现得到提升,从而验证新加入的模块或者设置的有效性;
174+
"""
175+
optimizer = paddle.optimizer.Momentum(
176+
learning_rate=0.1 / 128.0,
177+
momentum=0.95,
178+
regularization=paddle.optimizer.L2Regularization(rate=0.0005 * 128))
179+
180+
# 创建Adam优化器,并设置参数beta1、beta2、epsilon
181+
# optimizer = paddle.optimizer.Adam(beta1=0.9, beta2=0.99, epsilon=1e-06)
182+
183+
config_data = [images, label, predict, cost, parameters, optimizer]
184+
185+
return config_data
186+
187+
188+
def plot_init():
189+
"""
190+
绘图初始化函数:
191+
初始化绘图相关变量
192+
Args:
193+
Return:
194+
cost_ploter -- 用于绘制cost曲线的变量
195+
error_ploter -- 用于绘制error_rate曲线的变量
196+
"""
197+
# 绘制cost曲线所做的初始化设置
198+
cost_ploter = Ploter(train_title_cost, test_title_cost)
199+
200+
# 绘制error_rate曲线所做的初始化设置
201+
error_ploter = Ploter(train_title_error, test_title_error)
202+
203+
ploter = [cost_ploter, error_ploter]
204+
205+
return ploter
206+
207+
208+
def load_image(file):
209+
"""
210+
定义读取输入图片的函数:
211+
读取指定路径下的图片,将其处理成分类网络输入数据对应形式的数据,如数据维度等
212+
Args:
213+
file -- 输入图片的文件路径
214+
Return:
215+
im -- 分类网络输入数据对应形式的数据
216+
"""
217+
im = Image.open(file).convert('L')
218+
im = im.resize((28, 28), Image.ANTIALIAS)
219+
im = np.array(im).astype(np.float32).flatten()
220+
im = im / 255.0
221+
return im
222+
223+
224+
def infer(predict, parameters, file):
225+
"""
226+
定义判断输入图片类别的函数:
227+
读取并处理指定路径下的图片,然后调用训练得到的模型进行类别预测
228+
Args:
229+
predict -- 输出层
230+
parameters -- 模型参数
231+
file -- 输入图片的文件路径
232+
Return:
233+
"""
234+
# 读取并预处理要预测的图片
235+
test_data = []
236+
cur_dir = os.path.dirname(os.path.realpath(__file__))
237+
test_data.append((load_image(cur_dir + file), ))
238+
239+
# 利用训练好的分类模型,对输入的图片类别进行预测
240+
probs = paddle.infer(
241+
output_layer=predict, parameters=parameters, input=test_data)
242+
lab = np.argsort(-probs)
243+
print "Label of image/infer_3.png is: %d" % lab[0][0]
244+
245+
246+
247+
def main():
248+
"""
249+
主函数:
250+
定义神经网络结构,训练模型并打印学习曲线、预测测试数据类别
251+
Args:
252+
Return:
253+
"""
254+
# 初始化,设置是否使用gpu,trainer数量
255+
paddle.init(use_gpu=with_gpu, trainer_count=1)
256+
257+
# 定义神经网络结构
258+
images, label, predict, cost, parameters, optimizer = netconfig()
259+
260+
# 构造trainer,配置三个参数cost、parameters、update_equation,它们分别表示成本函数、参数和更新公式
261+
trainer = paddle.trainer.SGD(
262+
cost=cost, parameters=parameters, update_equation=optimizer)
263+
264+
# 初始化绘图变量
265+
cost_ploter, error_ploter = plot_init()
266+
267+
# lists用于存储训练的中间结果,包括cost和error_rate信息,初始化为空
268+
lists = []
269+
270+
def event_handler_plot(event):
271+
"""
272+
定义event_handler_plot事件处理函数:
273+
事件处理器,可以根据训练过程的信息做相应操作:包括绘图和输出训练结果信息
274+
Args:
275+
event -- 事件对象,包含event.pass_id, event.batch_id, event.cost等信息
276+
Return:
277+
"""
278+
global step
279+
if isinstance(event, paddle.event.EndIteration):
280+
# 每训练100次(即100个batch),添加一个绘图点
281+
if step % 100 == 0:
282+
cost_ploter.append(train_title_cost, step, event.cost)
283+
# 绘制cost图像,保存图像为‘train_test_cost.png’
284+
cost_ploter.plot('./train_test_cost')
285+
error_ploter.append(
286+
train_title_error, step, event.metrics['classification_error_evaluator'])
287+
# 绘制error_rate图像,保存图像为‘train_test_error_rate.png’
288+
error_ploter.plot('./train_test_error_rate')
289+
step += 1
290+
# 每训练100个batch,输出一次训练结果信息
291+
if event.batch_id % 100 == 0:
292+
print "Pass %d, Batch %d, Cost %f, %s" % (
293+
event.pass_id, event.batch_id, event.cost, event.metrics)
294+
if isinstance(event, paddle.event.EndPass):
295+
# 保存参数至文件
296+
with open('params_pass_%d.tar' % event.pass_id, 'w') as f:
297+
trainer.save_parameter_to_tar(f)
298+
# 利用测试数据进行测试
299+
result = trainer.test(reader=paddle.batch(
300+
paddle.dataset.mnist.test(), batch_size=128))
301+
print "Test with Pass %d, Cost %f, %s\n" % (
302+
event.pass_id, result.cost, result.metrics)
303+
# 添加测试数据的cost和error_rate绘图数据
304+
cost_ploter.append(test_title_cost, step, result.cost)
305+
error_ploter.append(
306+
test_title_error, step, result.metrics['classification_error_evaluator'])
307+
# 存储测试数据的cost和error_rate数据
308+
lists.append((
309+
event.pass_id, result.cost, result.metrics['classification_error_evaluator']))
310+
311+
"""
312+
训练模型:
313+
paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size=8192):
314+
表示trainer从paddle.dataset.mnist.train()这个reader中读取了buf_size=8192大小的数据并打乱顺序
315+
paddle.batch(reader(), batch_size=128):
316+
表示从打乱的数据中再取出batch_size=128大小的数据进行一次迭代训练
317+
event_handler:事件处理函数,可以自定义event_handler,根据事件信息做相应的操作,
318+
下方代码中选择的是event_handler_plot函数
319+
num_passes:定义训练的迭代次数
320+
"""
321+
trainer.train(
322+
reader=paddle.batch(
323+
paddle.reader.shuffle(paddle.dataset.mnist.train(), buf_size=8192),
324+
batch_size=128),
325+
event_handler=event_handler_plot,
326+
num_passes=10)
327+
328+
# 在多次迭代中,找到在测试数据上表现最好的一组参数,并输出相应信息
329+
best = sorted(lists, key=lambda list: float(list[1]))[0]
330+
print 'Best pass is %s, testing Avgcost is %s' % (best[0], best[1])
331+
print 'The classification accuracy is %.2f%%' % (100 - float(best[2]) * 100)
332+
333+
# 预测输入图片的类型
334+
infer(predict, parameters, '/image/infer_3.png')
335+
336+
337+
338+
339+
if __name__ == '__main__':
340+
main()

0 commit comments

Comments
 (0)