Skip to content

Commit bd36593

Browse files
authored
Update layer_and_model_cn.md (#5326)
modify
1 parent c89f5ce commit bd36593

File tree

1 file changed

+33
-33
lines changed

1 file changed

+33
-33
lines changed

docs/guides/advanced/layer_and_model_cn.md

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
在具体操作之前,让我们先了解与之相关的基本概念。
88

9-
# 一、概念介绍
9+
## 一、概念介绍
1010

1111
**1. 模型**
1212

@@ -35,9 +35,9 @@ paddle.nn.Layer 是飞桨定义的一个非常重要的类,是飞桨所有神
3535
3636
以下内容假定你已经完成了飞桨的安装以及熟悉了一些基本的飞桨操作。
3737

38-
# 二、数据处理
38+
## 二、数据处理
3939

40-
## 2.1 加载 Mnist 数据集
40+
### 2.1 加载 Mnist 数据集
4141

4242
相信根据前面的内容,你已经知道如何使用 paddle.Dataset 和 paddle.DataLoader 处理想要的数据了,如果你还有问题可以参考[数据读取](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/guides/beginner/data_load_cn.html)文档,这里采用前面讲到的方法使用 Mnist 数据集。
4343

@@ -55,7 +55,7 @@ train_dataset = paddle.vision.datasets.MNIST(mode='train', transform=transform)
5555

5656

5757

58-
## 2.2 对数据集进行预处理
58+
### 2.2 对数据集进行预处理
5959

6060
为演示方便先从这个训练集中取出一条数据,简单测试下后面搭建的网络,同时为了方便训练对该数据进行形状的变换。
6161

@@ -69,11 +69,11 @@ print("x_data's shape is:", x_data.shape)
6969
x_data's shape is: [1, 784]
7070
```
7171

72-
# 三、搭建一个完整的深度学习网络
72+
## 三、搭建一个完整的深度学习网络
7373

7474
接下来仅利用飞桨最基本的 Tensor 功能快速完成一个深度学习网络的搭建。
7575

76-
## 3.1 参数初始化
76+
### 3.1 参数初始化
7777

7878
首先, 需要通过 paddle.randn 函数或者 paddle.zeros 函数来创建随机数填充或者全零填充的一个参数(Weight)(模型训练中会被更新的部分),和一个偏置项(Bias)。
7979

@@ -88,7 +88,7 @@ bias.stop_gradient=False
8888

8989
参数初始化完成后,就可以开始准备神经网络了。
9090

91-
## 3.2 准备网络结构
91+
### 3.2 准备网络结构
9292

9393
网络结构是深度学习模型关键要素之一,相当于模型的假设空间,即实现模型从输入到输出的映射过程(前向计算)。
9494

@@ -102,7 +102,7 @@ def model(x):
102102
return log_softmax(paddle.matmul(x, weights) + bias)
103103
```
104104

105-
## 3.3 前向计算
105+
### 3.3 前向计算
106106

107107
通常训练都是针对一个 batch 进行的,可以从之前的数据中按照 batch_size=64 取出一部分进行一轮的前向执行。由于本轮利用随机初始化的参数进行前向计算,那么计算的结果也就和一个随机的网络差不多。
108108

@@ -129,7 +129,7 @@ y[0]: Tensor(shape=[10], dtype=float32, place=Place(gpu:0), stop_gradient=False,
129129
y.shape: [64, 10]
130130
```
131131

132-
## 3.4 反向传播
132+
### 3.4 反向传播
133133

134134
这里我们会发现,y 的信息中包含一项 StopGradient=False。这意味着我们可以通过 y 来进行 BP(反向传播),同时可以定义自己的损失函数。以一个简单的 nll_loss 来演示,写法上如同写一段简单的 Python 代码。
135135

@@ -146,7 +146,7 @@ loss: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=False,
146146
[2.85819387])
147147
```
148148

149-
## 3.5 计算 ACC 观察模型收敛情况
149+
### 3.5 计算 ACC 观察模型收敛情况
150150

151151
同样,也可以实现一个简单的计算 accuracy 的方法来验证模型收敛情况。
152152

@@ -161,7 +161,7 @@ accuracy: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=Tru
161161
[0.09375000])
162162
```
163163

164-
## 3.6 使用自动微分功能计算网络的梯度并更新参数
164+
### 3.6 使用自动微分功能计算网络的梯度并更新参数
165165

166166
接下来我们将利用飞桨的自动微分功能计算网络的梯度,并且利用该梯度和参数完成一轮参数更新(需要注意的是,在更新参数的阶段我们并不希望进行微分的逻辑,只需要使用 paddle.no_grad 禁用相关功能即可)。
167167

@@ -218,11 +218,11 @@ bias after optimize: Tensor(shape=[10], dtype=float32, place=Place(cpu), stop_g
218218

219219

220220

221-
# 四、使用 paddle.nn.Layer 构建深度学习网络
221+
## 四、使用 paddle.nn.Layer 构建深度学习网络
222222

223223
paddle.nn.Layer 是飞桨定义的一个类,它代表所有可以用层表示的网络结构。对本文前面这个例子来说,我们需要构建线性网络的参数 weight,bias,以及矩阵乘法,加法,log_softmax 也可以看成是一个层。换句话说 ,我们可以把任意的网络结构看成是一个层,层是网络结构的一个封装。
224224

225-
## 4.1 使用 Layer 改造线性层
225+
### 4.1 使用 Layer 改造线性层
226226

227227
首先,可以定义自己的线性层:
228228

@@ -241,7 +241,7 @@ class MyLayer(paddle.nn.Layer):
241241

242242
那么通过继承 paddle.nn.Layer 构建层有什么好处呢?
243243

244-
### 4.1.1 子类调用父类的构造函数
244+
#### 4.1.1 子类调用父类的构造函数
245245

246246
首先,我们会发现,在这个继承的子类当中需要去调用一下父类的构造函数:
247247

@@ -250,7 +250,7 @@ class MyLayer(paddle.nn.Layer):
250250
super(MyLayer, self).__init__()
251251
```
252252

253-
### 4.1.2 完成一系列的初始化
253+
#### 4.1.2 完成一系列的初始化
254254

255255
这个时候飞桨会完成一系列初始化操作,其目的是为了记录所有定义在该层的状态,包括参数,call_back, 子层等信息。
256256

@@ -261,7 +261,7 @@ class MyLayer(paddle.nn.Layer):
261261
self.bias = self.create_parameter([10], is_bias=True, default_initializer=paddle.nn.initializer.Constant(value=0.0))
262262
```
263263

264-
## 4.2 访问并自动记录参数的更新过程
264+
### 4.2 访问并自动记录参数的更新过程
265265

266266
这里我们调用的 create_parameter 函数就来自于 paddle.nn.Layer 类,这个函数帮助我们简单的创建并初始化参数。最简单的我们仅仅传入希望的参数形状即可(如 weight),这时候 create_parameter 会通过默认的方式初始化参数(默认是参数而不是 bias,使用 UniformRandom 来初始化参数,详情可以参考 create_parameter);或者可以通过诸多参数来定义你自己希望的初始化参数的方式(如 bias),可以限定其初始化方式是全零的常数项(更多初始化方式可以参考 paddle.nn.initializer)。
267267

@@ -291,11 +291,11 @@ Tensor(shape=[10], dtype=float32, place=Place(gpu:0), stop_gradient=False,
291291
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
292292
```
293293

294-
## 4.3 执行已定义的层
294+
### 4.3 执行已定义的层
295295

296296
下面可以看看如何使用我们定义好的层。
297297

298-
### 4.3.1 进入训练阶段并执行
298+
#### 4.3.1 进入训练阶段并执行
299299

300300
首先, 我们通过构造函数构造了一个层,并且设置其执行模式为 train(训练)模式(通常你并不需要显式调用,因为默认是训练模式,这里仅仅为了演示),这样做是因为如 Dropout,BatchNorm 等计算,在训练和评估阶段的行为往往有区别,因此飞桨提供了方便的接口对整层设置该属性,如果层包含相关操作,可以通过这个设置改变他们在不同阶段的行为。
301301

@@ -315,7 +315,7 @@ y[0] Tensor(shape=[10], dtype=float32, place=Place(gpu:0), stop_gradient=False,
315315
-5.03897095, -1.63698268, -0.70400816, -6.44660282, -2.51351619])
316316
```
317317

318-
### 4.3.2 计算 loss
318+
#### 4.3.2 计算 loss
319319

320320
同样调用 paddle.nn.functional.nll_loss 来计算 nll_loss。
321321

@@ -326,7 +326,7 @@ loss = loss_func(y, y_standard)
326326
print("loss: ", loss)
327327
```
328328

329-
### 4.3.3 构建 SGD 优化器、参数传递及计算
329+
#### 4.3.3 构建 SGD 优化器、参数传递及计算
330330

331331
与此同时,可以利用飞桨提供的 API 完成之前的操作。
332332

@@ -347,7 +347,7 @@ loss: Tensor(shape=[1], dtype=float32, place=Place(gpu:0), stop_gradient=False,
347347

348348
这样,我们就利用 paddle.nn.Layer 完成了网络的改造。可以发现,paddle.nn.Layer 对大部分的网络场景提供了简单的网络状态控制和网络信息处理的方法。
349349

350-
## 4.4 使用 paddle.nn.Linear 改造预定义的层
350+
### 4.4 使用 paddle.nn.Linear 改造预定义的层
351351

352352
此外,飞桨基于 paddle.nn.Layer 构建了一系列层,这些层都可以通过简单的方式被复用在我们自定义网络中,上述例子中的 MyLayer 可以用飞桨定义的 paddle.nn.Linear 来改造。
353353

@@ -365,19 +365,19 @@ class MyLayer(paddle.nn.Layer):
365365

366366
可以看到,利用线性层替换了之前的矩阵乘法和加法(而这也正是线性层的定义)。只需要定义好自己的隐层大小,以及参数的初始化方式,就可以利用 paddle.nn.Linear 建立我们的线性层,此方式可节省自定义参数和运算的成本。
367367

368-
## 4.5 总结
368+
### 4.5 总结
369369

370370
至此,我们完成了如何用飞桨层的概念和 paddle.nn.Layer 来完成一个简单的训练任务。可点击此[链接](https://aistudio.baidu.com/aistudio/projectdetail/4508657?contributionType=1)获取完整代码。
371371

372372
paddle.nn.Layer 的功能远不止于此,利用 paddle.nn.Layer 还可以进行子层访问、层的成员变量操作、模型存储等操作,具体操作接下来会逐一介绍。
373373

374374

375375

376-
# 五、利用 paddle.nn.Layer 进行子层的访问
376+
## 五、利用 paddle.nn.Layer 进行子层的访问
377377

378378
本节继续基于前面的手写数字识别任务,介绍如何使用 paddle.nn.layer 进行子层的访问。
379379

380-
## 5.1 查看模型的所有层
380+
### 5.1 查看模型的所有层
381381

382382
如果想要访问或修改一个模型中定义的层,则可以调用**SubLayer**相关的接口。
383383

@@ -400,7 +400,7 @@ for item in mylayer.named_sublayers():
400400

401401
而遍历 mylayer`.named_sublayers()` 时,每一轮循环会拿到一组 ( 子层名称('flatten'),子层对象(paddle.nn.Flatten) )的元组。
402402

403-
## 5.2 向模型添加一个子层
403+
### 5.2 向模型添加一个子层
404404

405405
接下来如果想要进一步添加一个子层,则可以调用 `add_sublayer()` 接口。例如可以通过这个接口在前面做好的线性网络中再加入一个子层。
406406

@@ -414,7 +414,7 @@ print(my_layer.sublayers())
414414

415415
可以看到 my_layer.add_sublayer() 向模型中添加了一个 10*3 的 paddle.nn.Linear 子层,这样模型中总共有两个 paddle.nn.Linear 的子层。
416416

417-
## 5.3 自定义函数并批量作用在所有子层
417+
### 5.3 自定义函数并批量作用在所有子层
418418

419419
通过上述方法可以在模型中添加成千上万个子层。当模型中子层数量较多时,如何高效地对所有子层进行统一修改呢?Paddle 提供了 apply() 接口。通过这个接口,可以自定义一个函数,然后将该函数批量作用在所有子层上。
420420

@@ -433,7 +433,7 @@ MyLayer(
433433

434434
当前例子,定义了一个以 layer 作为参数的函数 function,用来打印传入的 layer 信息。通过调用 model.apply() 接口,将 function 作用在模型的所有子层中,输出信息打印 model 中所有子层的信息。
435435

436-
## 5.4 循环访问所有子层
436+
### 5.4 循环访问所有子层
437437

438438
另外一个批量访问子层的接口是 children() 或者 named_children() 。这两个接口通过 Iterator 的方式访问每个子层。
439439

@@ -450,9 +450,9 @@ Linear(in_features=10, out_features=3, dtype=float32)
450450

451451
可以看到,遍历 model.children() 时,每一轮循环都可以按照子层注册顺序拿到对应 paddle.nn.Layer 的对象。
452452

453-
# 六、修改 paddle.nn.Layer 层的成员变量
453+
## 六、修改 paddle.nn.Layer 层的成员变量
454454

455-
## 6.1 批量添加参数变量
455+
### 6.1 批量添加参数变量
456456

457457
和我们在前面演示的一样,你可以通过 create_parameter 来为当前层加入参数,这对于只有几个参数的层是比较简单的。但是,当我们需要很多参数的时候就比较麻烦了,尤其是希望使用一些 container 来处理这些参数,这时候就需要使用 add_parameter,让层感知需要增加的参数。
458458

@@ -470,7 +470,7 @@ for name, item in my_layer.named_parameters():
470470
print(name)
471471
```
472472

473-
## 6.2 添加临时中间变量
473+
### 6.2 添加临时中间变量
474474

475475
刚刚的 Minst 的例子中,仅仅使用参数 weight,bias。参数变量往往需要参与梯度更新,但很多情况下只是需要一个临时变量甚至一个常量。比如在模型执行过程中想将一个中间变量保存下来,这时需要调用 create_tensor() 接口。
476476

@@ -493,7 +493,7 @@ class Model(paddle.nn.Layer):
493493

494494
这里调用 `self.create_tensor()` 创造一个临时变量,并将其记录在模型的 `self.saved_tensor` 中。在模型执行时,调用 `paddle.assign` 用该临时变量记录变量**y**的数值。
495495

496-
## 6.3 添加 Buffer 变量完成动转静
496+
### 6.3 添加 Buffer 变量完成动转静
497497

498498
Buffer 的概念仅仅影响动态图向静态图的转换过程。在上一节中创建了一个临时变量用来临时存储中间变量的值。但这个临时变量在动态图向静态图转换的过程中并不会被记录在静态的计算图当中。如果希望该变量成为静态图的一部分,就需要进一步调用 register_buffers() 接口。
499499

@@ -528,7 +528,7 @@ for item in model.named_buffers():
528528
('saved_tensor', Tensor(Not initialized))
529529
```
530530

531-
# 七、存储模型的参数
531+
## 七、存储模型的参数
532532

533533
参考前面的操作完成 Layer 自定义和修改之后,可以参考以下操作进行保存。
534534

@@ -554,6 +554,6 @@ model.set_state_dict(state_dict)
554554

555555

556556

557-
# 八、总结
557+
## 八、总结
558558

559559
至此,本文介绍了如何使用 paddle.nn.Layer 来辅助您构造深度学习网络模型,并展示了如何使用 paddle.nn.Layer 进行层的查看、修改。还可以根据自己的需要进一步探索 Layer 的更多用法。此外,如果在使用 paddle.nn.Layer 的过程中遇到任何问题及建议,欢迎在飞桨 Github 中进行提问和反馈。

0 commit comments

Comments
 (0)