3
3
4
4
本教程源代码目录在[ book/fit_a_line] ( https://github.com/PaddlePaddle/book/tree/develop/01.fit_a_line ) , 初次使用请您参考[ Book文档使用说明] ( https://github.com/PaddlePaddle/book/blob/develop/README.cn.md#运行这本书 ) 。
5
5
6
+ ### 说明:
7
+ 1.硬件环境要求:
8
+ 本文可支持在CPU、GPU下运行
9
+ 2 . Docker镜像支持的CUDA/cuDNN版本:
10
+ 如果使用了Docker运行Book,请注意:这里所提供的默认镜像的GPU环境为 CUDA 8/cuDNN 5,对于NVIDIA Tesla V100等要求CUDA 9的 GPU,使用该镜像可能会运行失败。
11
+ 3 . 文档和脚本中代码的一致性问题:
12
+ 请注意:为使本文更加易读易用,我们拆分、调整了train.py的代码并放入本文。本文中代码与train.py的运行结果一致,可直接运行[ train.py] ( https://github.com/PaddlePaddle/book/blob/develop/01.fit_a_line/train.py ) 进行验证。
13
+
6
14
## 背景介绍
7
15
给定一个大小为$n$的数据集 ${\{ y_ {i}, x_ {i1}, ..., x_ {id}\} }_ {i=1}^{n}$,其中$x_ {i1}, \ldots, x_ {id}$是第$i$个样本$d$个属性上的取值,$y_i$是该样本待预测的目标。线性回归模型假设目标$y_i$可以被属性间的线性组合描述,即
8
16
9
- $$ y_i = \omega_1x_{i1} + \omega_2x_{i2} + \ldots + \omega_dx_{id} + b, i=1,\ldots,n $$
17
+
18
+ <p align =" center " >
19
+ <img src = "https://github.com/PaddlePaddle/book/blob/develop/01.fit_a_line/image/formula_fit_a_line_1.png?raw=true" width=550><br/>
20
+ </p >
10
21
11
22
例如,在我们将要建模的房价预测问题里,$x_ {ij}$是描述房子$i$的各种属性(比如房间的个数、周围学校和医院的个数、交通状况等),而 $y_i$是房屋的价格。
12
23
@@ -25,21 +36,27 @@ $$y_i = \omega_1x_{i1} + \omega_2x_{i2} + \ldots + \omega_dx_{id} + b, i=1,\ldo
25
36
26
37
在波士顿房价数据集中,和房屋相关的值共有14个:前13个用来描述房屋相关的各种信息,即模型中的 $x_i$;最后一个值为我们要预测的该类房屋价格的中位数,即模型中的 $y_i$。因此,我们的模型就可以表示成:
27
38
28
- $$ \hat{Y} = \omega_1X_{1} + \omega_2X_{2} + \ldots + \omega_{13}X_{13} + b $$
39
+ <p align =" center " >
40
+ <img src = "https://github.com/PaddlePaddle/book/blob/develop/01.fit_a_line/image/formula_fit_a_line_2.png?raw=true" width=350><br/>
41
+ </p >
29
42
30
43
$\hat{Y}$ 表示模型的预测结果,用来和真实值$Y$区分。模型要学习的参数即:$\omega_1, \ldots, \omega_ {13}, b$。
31
44
32
45
建立模型后,我们需要给模型一个优化目标,使得学到的参数能够让预测值$\hat{Y}$尽可能地接近真实值$Y$。这里我们引入损失函数([ Loss Function] ( https://en.wikipedia.org/wiki/Loss_function ) ,或Cost Function)这个概念。 输入任意一个数据样本的目标值$y_ {i}$和模型给出的预测值$\hat{y_ {i}}$,损失函数输出一个非负的实值。这个实值通常用来反映模型误差的大小。
33
46
34
47
对于线性回归模型来讲,最常见的损失函数就是均方误差(Mean Squared Error, [ MSE] ( https://en.wikipedia.org/wiki/Mean_squared_error ) )了,它的形式是:
35
48
36
- $$ MSE=\frac{1}{n}\sum_{i=1}^{n}{(\hat{Y_i}-Y_i)}^2 $$
49
+ <p align =" center " >
50
+ <img src = "https://github.com/PaddlePaddle/book/blob/develop/01.fit_a_line/image/formula_fit_a_line_3.png?raw=true" width=200><br/>
51
+ </p >
37
52
38
53
即对于一个大小为$n$的测试集,$MSE$是$n$个数据预测结果误差平方的均值。
39
54
40
55
对损失函数进行优化所采用的方法一般为梯度下降法。梯度下降法是一种一阶最优化算法。如果$f(x)$在点$x_n$有定义且可微,则认为$f(x)$在点$x_n$沿着梯度的负方向$-▽f(x_n)$下降的是最快的。反复调节$x$,使得$f(x)$接近最小值或者极小值,调节的方式为:
41
56
42
- $$ x_n+1=x_n-λ▽f(x), n≧0 $$
57
+ <p align =" center " >
58
+ <img src = "https://github.com/PaddlePaddle/book/blob/develop/01.fit_a_line/image/formula_fit_a_line_4.png?raw=true" width=250><br/>
59
+ </p >
43
60
44
61
其中λ代表学习率。这种调节的方法称为梯度下降法。
45
62
@@ -101,25 +118,25 @@ $$x_n+1=x_n-λ▽f(x), n≧0$$
101
118
102
119
## 训练
103
120
104
- ` fit_a_line/trainer .py ` 演示了训练的整体过程。
121
+ ` fit_a_line/train .py ` 演示了训练的整体过程。
105
122
106
123
### 配置数据提供器(Datafeeder)
107
124
首先我们引入必要的库:
108
125
``` python
126
+ from __future__ import print_function
109
127
import paddle
110
128
import paddle.fluid as fluid
111
129
import numpy
112
130
import math
113
131
import sys
114
- from __future__ import print_function
115
132
```
116
133
117
134
我们通过uci_housing模块引入了数据集合[ UCI Housing Data Set] ( http://paddlemodels.bj.bcebos.com/uci_housing/housing.data )
118
135
119
136
其中,在uci_housing模块中封装了:
120
137
121
138
1 . 数据下载的过程。下载数据保存在~ /.cache/paddle/dataset/uci_housing/housing.data。
122
- 2 . [ 数据预处理 ] ( #数据预处理 ) 的过程 。
139
+ 2 . 数据预处理的过程 。
123
140
124
141
接下来我们定义了用于训练的数据提供器。提供器每次读入一个大小为` BATCH_SIZE ` 的数据批次。如果用户希望加一些随机性,它可以同时定义一个批次大小和一个缓存大小。这样的话,每次数据提供器会从缓存中随机读取批次大小那么多的数据。
125
142
@@ -163,14 +180,18 @@ train_data = data[:offset]
163
180
164
181
test_data = data[ offset:]
165
182
183
+ def reader(data):
184
+ for d in train_data:
185
+ yield d[ :1] , d[ -1:]
186
+
166
187
train_reader = paddle.batch(
167
188
paddle.reader.shuffle(
168
- train_data, buf_size=500),
189
+ reader( train_data) , buf_size=500),
169
190
batch_size=BATCH_SIZE)
170
191
171
192
test_reader = paddle.batch(
172
193
paddle.reader.shuffle(
173
- test_data, buf_size=500),
194
+ reader( test_data) , buf_size=500),
174
195
batch_size=BATCH_SIZE)
175
196
176
197
### 配置训练程序
@@ -196,13 +217,14 @@ avg_loss = fluid.layers.mean(cost) # 对方差求均值,得到平均损失
196
217
在下面的 ` SGD optimizer ` ,` learning_rate ` 是学习率,与网络的训练收敛速度有关系。
197
218
198
219
``` python
199
- sgd_optimizer = fluid.optimizer.SGD(learning_rate = 0.001 )
200
- sgd_optimizer.minimize(avg_loss)
201
-
202
220
# 克隆main_program得到test_program
203
221
# 有些operator在训练和测试之间的操作是不同的,例如batch_norm,使用参数for_test来区分该程序是用来训练还是用来测试
204
222
# 该api不会删除任何操作符,请在backward和optimization之前使用
205
223
test_program = main_program.clone(for_test = True )
224
+
225
+ sgd_optimizer = fluid.optimizer.SGD(learning_rate = 0.001 )
226
+ sgd_optimizer.minimize(avg_loss)
227
+
206
228
```
207
229
208
230
### 定义运算场所
@@ -220,7 +242,7 @@ exe = fluid.Executor(place)
220
242
[ fluid.executor] ( http://www.paddlepaddle.org/documentation/docs/zh/develop/api_cn/fluid_cn.html#permalink-15-executor )
221
243
222
244
### 创建训练过程
223
- 训练需要有一个训练程序和一些必要参数,并构建了一个获取训练过程中测试误差的函数。必要参数有executor,program,reader,feeder,fetch_list,executor表示之前创建的执行器,program表示执行器所执行的program,是之前创建的program,如果该项参数没有给定的话则默认使用defalut_main_program ,reader表示读取到的数据,feeder表示前向输入的变量,fetch_list表示用户想得到的变量或者命名的结果。
245
+ 训练需要有一个训练程序和一些必要参数,并构建了一个获取训练过程中测试误差的函数。必要参数有executor,program,reader,feeder,fetch_list,executor表示之前创建的执行器,program表示执行器所执行的program,是之前创建的program,如果该项参数没有给定的话则默认使用default_main_program ,reader表示读取到的数据,feeder表示前向输入的变量,fetch_list表示用户想得到的变量或者命名的结果。
224
246
225
247
``` python
226
248
num_epochs = 100
@@ -236,24 +258,6 @@ def train_test(executor, program, reader, feeder, fetch_list):
236
258
count += 1 # 累加测试集中的样本数量
237
259
return [x_d / count for x_d in accumulated] # 计算平均损失
238
260
239
- ```
240
- 可以直接输出损失值来观察` 训练进程 ` :
241
-
242
- ``` python
243
- train_prompt = " train cost"
244
- test_prompt = " test cost"
245
- print (" %s ', out %f " % (train_prompt, out))
246
- print (" %s ', out %f " % (test_prompt, out))
247
-
248
- ```
249
-
250
- 除此之外,还可以通过画图,来展现` 训练进程 ` :
251
-
252
- ``` python
253
- from paddle.utils.plot import ploter
254
-
255
- plot_prompt = ploter(train_prompt, test_prompt)
256
-
257
261
```
258
262
259
263
### 训练主循环
@@ -264,8 +268,11 @@ plot_prompt = ploter(train_prompt, test_prompt)
264
268
% matplotlib inline
265
269
params_dirname = " fit_a_line.inference.model"
266
270
feeder = fluid.DataFeeder(place = place, feed_list = [x, y])
267
- naive_exe = fluid.Executor(place)
268
- naive_exe.run(startup_program)
271
+ exe.run(startup_program)
272
+ train_prompt = " train cost"
273
+ test_prompt = " test cost"
274
+ from paddle.utils.plot import Ploter
275
+ plot_prompt = Ploter(train_prompt, test_prompt)
269
276
step = 0
270
277
271
278
exe_test = fluid.Executor(place)
@@ -280,17 +287,21 @@ for pass_id in range(num_epochs):
280
287
avg_loss_value, = exe.run(main_program,
281
288
feed = feeder.feed(data_train),
282
289
fetch_list = [avg_loss])
283
- if step % 10 == 0 : # 每10个批次记录一下训练损失
290
+ if step % 10 == 0 : # 每10个批次记录并输出一下训练损失
284
291
plot_prompt.append(train_prompt, step, avg_loss_value[0 ])
285
292
plot_prompt.plot()
286
- if step % 100 == 0 : # 每100批次记录一下测试损失
293
+ print (" %s , Step %d , Cost %f " %
294
+ (train_prompt, step, avg_loss_value[0 ]))
295
+ if step % 100 == 0 : # 每100批次记录并输出一下测试损失
287
296
test_metics = train_test(executor = exe_test,
288
297
program = test_program,
289
298
reader = test_reader,
290
299
fetch_list = [avg_loss.name],
291
300
feeder = feeder)
292
301
plot_prompt.append(test_prompt, step, test_metics[0 ])
293
302
plot_prompt.plot()
303
+ print (" %s , Step %d , Cost %f " %
304
+ (test_prompt, step, test_metics[0 ]))
294
305
if test_metics[0 ] < 10.0 : # 如果准确率达到要求,则停止训练
295
306
break
296
307
@@ -316,6 +327,24 @@ inference_scope = fluid.core.Scope()
316
327
```
317
328
318
329
### 预测
330
+
331
+ 保存图片
332
+ ``` python
333
+ def save_result (points1 , points2 ):
334
+ import matplotlib
335
+ matplotlib.use(' Agg' )
336
+ import matplotlib.pyplot as plt
337
+ x1 = [idx for idx in range (len (points1))]
338
+ y1 = points1
339
+ y2 = points2
340
+ l1 = plt.plot(x1, y1, ' r--' , label = ' predictions' )
341
+ l2 = plt.plot(x1, y2, ' g--' , label = ' GT' )
342
+ plt.plot(x1, y1, ' ro-' , x1, y2, ' g+-' )
343
+ plt.title(' predictions VS GT' )
344
+ plt.legend()
345
+ plt.savefig(' ./image/prediction_gt.png' )
346
+ ```
347
+
319
348
通过fluid.io.load_inference_model,预测器会从` params_dirname ` 中读取已经训练好的模型,来对从未遇见过的数据进行预测。
320
349
321
350
``` python
@@ -337,37 +366,19 @@ with fluid.scope_guard(inference_scope):
337
366
results = infer_exe.run(inference_program,
338
367
feed = {feed_target_names[0 ]: numpy.array(infer_feat)},
339
368
fetch_list = fetch_targets) # 进行预测
340
- ```
341
-
342
- 保存图片
343
- ``` python
344
- def save_result (points1 , points2 ):
345
- import matplotlib
346
- matplotlib.use(' Agg' )
347
- import matplotlib.pyplot as plt
348
- x1 = [idx for idx in range (len (points1))]
349
- y1 = points1
350
- y2 = points2
351
- l1 = plt.plot(x1, y1, ' r--' , label = ' predictions' )
352
- l2 = plt.plot(x1, y2, ' g--' , label = ' GT' )
353
- plt.plot(x1, y1, ' ro-' , x1, y2, ' g+-' )
354
- plt.title(' predictions VS GT' )
355
- plt.legend()
356
- plt.savefig(' ./image/prediction_gt.png' )
357
- ```
358
-
359
- 打印预测结果和标签并可视化结果
360
- ``` python
361
- print (" infer results: (House Price)" )
362
- for idx, val in enumerate (results[0 ]):
363
- print (" %d : %.2f " % (idx, val)) # 打印预测结果
369
+ # 打印预测结果和标签并可视化结果
370
+ print (" infer results: (House Price)" )
371
+ for idx, val in enumerate (results[0 ]):
372
+ print (" %d : %.2f " % (idx, val)) # 打印预测结果
364
373
365
- print (" \n ground truth:" )
366
- for idx, val in enumerate (infer_label):
367
- print (" %d : %.2f " % (idx, val)) # 打印标签值
374
+ print (" \n ground truth:" )
375
+ for idx, val in enumerate (infer_label):
376
+ print (" %d : %.2f " % (idx, val)) # 打印标签值
368
377
369
- save_result(results[0 ], infer_label) # 保存图片
378
+ save_result(results[0 ], infer_label) # 保存图片
370
379
```
380
+ 由于每次都是随机选择一个minibatch的数据作为当前迭代的训练数据,所以每次得到的预测结果会有所不同。
381
+
371
382
372
383
## 总结
373
384
在这章里,我们借助波士顿房价这一数据集,介绍了线性回归模型的基本概念,以及如何使用PaddlePaddle实现训练和测试的过程。很多的模型和技巧都是从简单的线性回归模型演化而来,因此弄清楚线性模型的原理和局限非常重要。
@@ -380,4 +391,4 @@ save_result(results[0], infer_label) # 保存图片
380
391
4 . Bishop C M. Pattern recognition[ J] . Machine Learning, 2006, 128.
381
392
382
393
<br />
383
- <a rel =" license " href =" http://creativecommons.org/licenses/by-sa/4.0/ " ><img alt =" 知识共享许可协议 " style =" border-width :0 " src =" https://i.creativecommons.org/l/by-sa/4.0/88x31. png " /></a ><br /><span xmlns:dct =" http://purl.org/dc/terms/ " href =" http://purl.org/dc/dcmitype/Text " property =" dct:title " rel =" dct:type " >本教程</span > 由 <a xmlns:cc =" http://creativecommons.org/ns# " href =" http://book .paddlepaddle.org " property =" cc:attributionName " rel =" cc:attributionURL " >PaddlePaddle</a > 创作,采用 <a rel =" license " href =" http://creativecommons.org/licenses/by-sa/4.0/ " >知识共享 署名-相同方式共享 4.0 国际 许可协议</a >进行许可。
394
+ <a rel =" license " href =" http://creativecommons.org/licenses/by-sa/4.0/ " ><img alt =" 知识共享许可协议 " style =" border-width :0 " src =" https://paddlepaddleimage.cdn.bcebos.com/bookimage/camo. png " /></a ><br /><span xmlns:dct =" http://purl.org/dc/terms/ " href =" http://purl.org/dc/dcmitype/Text " property =" dct:title " rel =" dct:type " >本教程</span > 由 <a xmlns:cc =" http://creativecommons.org/ns# " href =" http://www .paddlepaddle.org " property =" cc:attributionName " rel =" cc:attributionURL " >PaddlePaddle</a > 创作,采用 <a rel =" license " href =" http://creativecommons.org/licenses/by-sa/4.0/ " >知识共享 署名-相同方式共享 4.0 国际 许可协议</a >进行许可。
0 commit comments