Paddle网络结构中的层和模型

简 介: 这是 Paddle中的模型与层 的内容学习笔记。对于Paddle中的层的构造,操作进行了初步的测试与相关的学习。

关键词 LayerPaddle

模型与层
文章目录
在Paddle中定
义模型与层
模型与层
测试用例
子层接口
继承子层
增加子层
修改子层
层中的变量成员
参数变量添
加与修改
非参数变量的添加
Buffer变量的添加
执行层的功能
执行模式设置
执行函数
添加hook函数
保存模型参数
总 结

 

§01 型与层


  型是深度学习中的重要概念之一。模型的核心功能是将一组输入变量经过一系列计算,映射到另一组输出变量,该映射函数即代表一种深度学习算法。在Paddle框架中,模型包括以下两方面内容:

  • 一系列层的组合用于进行映射(前向执行)
  • 一些参数变量在训练过程中实时更新

  本文档中,你将学习如何定义与使用Paddle模型,并了解模型与层的关系。

1.1 在Paddle中定义模型与层

  在Paddle中,大多数模型由一系列层组成,层是模型的基础逻辑执行单元。层中持有两方面内容:

  • 一方面是计算所需的变量,以临时变量或参数的形式作为层的成员持有
  • 另一方面则持有一个或多个具体的Operator来完成相应的计算。

1.1.1 模型与层

  从零开始构建变量、Operator,从而组建层、模型是一个很复杂的过程,并且当中难以避免的会出现很多冗余代码,因此Paddle提供了基础数据类型 paddle.nn.Layer ,来方便你快速的实现自己的层和模型。模型和层都可以基于 paddle.nn.Layer 扩充实现,因此也可以说模型只是一种特殊的层。下面将演示如何利用 paddle.nn.Layer 建立自己的模型:

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()
        self.flatten = paddle.nn.Flatten()

    def forward(self, inputs):
        y = self.flatten(inputs)
        return y

  当前示例中,通过继承 paddle.nn.Layer 的方式构建了一个模型类型 Model ,模型中仅包含一个 paddle.nn.Flatten 层。模型执行时,输入变量inputs会被 paddle.nn.Flatten 层展平。

1.1.2 测试用例

x = paddle.to_tensor([[1,2,3],[4,5,6]])
print(x)

model = Model()
y = model(x)
print(y)
Tensor(shape=[2, 3], dtype=int64, place=CPUPlace, stop_gradient=True,
       [[1, 2, 3],
        [4, 5, 6]])
Tensor(shape=[2, 3], dtype=int64, place=CPUPlace, stop_gradient=True,
       [[1, 2, 3],
        [4, 5, 6]])

  这是什么鬼?结果怎么没有被层展平呢?

  将前面的代码修改:

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()

    def forward(self, inputs):
        y = self.Flatten(inputs)
        return y

    def Flatten(self, x):
        xx = x.numpy().flatten()
        return paddle.to_tensor(xx)

x = paddle.to_tensor([[1,2,3],[4,5,6]])
print(x)

model = Model()
y = model(x)
print(y)

  可以得到想要的结果了:

Tensor(shape=[2, 3], dtype=int64, place=CPUPlace, stop_gradient=True,
       [[1, 2, 3],
        [4, 5, 6]])
Tensor(shape=[6], dtype=int64, place=CPUPlace, stop_gradient=True,
       [1, 2, 3, 4, 5, 6])

1.2 子层接口

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

1.2.1 继承子层

  以上文创建的简单模型为例, 如果想要查看模型中定义的所有子层:

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()
        self.flatten = paddle.nn.Flatten()

    def forward(self, inputs):
        y = self.Flatten(inputs)
        return y
[Flatten()]
----------------------
('flatten', Flatten())

  以看到,通过调用 model.sublayers()接口,打印出了前述模型中持有的全部子层(这时模型中只有一个 paddle.nn.Flatten子层)。

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

  下面增加self中的一个层次,可以看到多出了更多的层。

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()
        self.flatten = paddle.nn.Flatten()
        self.f1   = paddle.nn.Flatten()

    def forward(self, inputs):
        y = self.Flatten(inputs)
        return y

model = Model()
print(model.sublayers())

print("----------------------")

for item in model.named_sublayers():
    print(item)
[Flatten(), Flatten()]
----------------------
('flatten', Flatten())
('f1', Flatten())

1.2.2 增加子层

  接下来如果想要进一步添加一个子层,则可以调用 add_sublayer() 接口:

fc = paddle.nn.Linear(10, 3)
model.add_sublayer("fc", fc)
print(model.sublayers())
[Flatten(), Linear(in_features=10, out_features=3, dtype=float32)]

  可以看到 model.add_sublayer() 向模型中添加了一个 paddle.nn.Linear 子层,这样模型中总共有 paddle.nn.Flattenpaddle.nn.Linear 两个子层了。

1.2.3 修改子层

  通过上述方法可以往模型中添加成千上万个子层,当模型中子层数量较多时,如何高效地对所有子层进行统一修改呢?

  Paddle 提供了 apply() 接口。通过这个接口,可以自定义一个函数,然后将该函数批量作用在所有子层上:

def function(layer):
    print(layer)

model.apply(function)
Flatten()
Linear(in_features=10, out_features=3, dtype=float32)
Model(
  (flatten): Flatten()
  (fc): Linear(in_features=10, out_features=3, dtype=float32)
)

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

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

sublayer_iter = model.children()
for sublayer in sublayer_iter:
    print(sublayer)
Flatten()
Linear(in_features=10, out_features=3, dtype=float32)

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

1.3 层中的变量成员

1.3.1 参数变量添加与修改

  有的时候希望向网络中添加一个参数作为输入。比如在使用图像风格转换模型时,会使用参数作为输入图像,在训练过程中不断更新该图像参数,最终拿到风格转换后的图像。

  这时可以通过 create_parameter()add_parameter() 组合,来创建并记录参数:

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()
        img = self.create_parameter([1,3,256,256])
        self.add_parameter("img", img)
        self.flatten = paddle.nn.Flatten()

    def forward(self):
        y = self.flatten(self.img)
        return y

  上述例子创建并向模型中添加了一个名字为"img"的参数。随后可以直接通过调用model.img来访问该参数。

  对于已经添加的参数,可以通过 parameters() 或者 named_parameters() 来访问

model = Model()
model.parameters()
print('--------------------------------------------')
for item in model.named_parameters():
    print(item)
[Parameter containing:
Tensor(shape=[1, 3, 256, 256], dtype=float32, place=CPUPlace, stop_gradient=False,
       [[[[-0.00323536,  0.00417978,  0.00387184, ..., -0.00263438,
            0.00336105, -0.00079275],
          [-0.00398997,  0.00305213,  0.00338405, ...,  0.00321609,
            0.00385862,  0.00383085],
          [ 0.00456822,  0.00335924, -0.00396630, ..., -0.00260351,
            0.00388722,  0.00292703],
          ...,
          ...,
          [-0.00302772, -0.00052290, -0.00259735, ...,  0.00325148,
            0.00051726,  0.00464376],
          [ 0.00238924, -0.00105374,  0.00219904, ..., -0.00279356,
           -0.00214116, -0.00319181],
          [ 0.00180969,  0.00476100,  0.00380237, ...,  0.00249749,
            0.00374650,  0.00050141]]]])]
--------------------------------------------
('img', Parameter containing:
Tensor(shape=[1, 3, 256, 256], dtype=float32, place=CPUPlace, stop_gradient=False,
       [[[[-0.00323536,  0.00417978,  0.00387184, ..., -0.00263438,
            0.00336105, -0.00079275],
          [-0.00398997,  0.00305213,  0.00338405, ...,  0.00321609,
            0.00385862,  0.00383085],
          [ 0.00456822,  0.00335924, -0.00396630, ..., -0.00260351,
            0.00388722,  0.00292703],
          ...,
          ...,
          [-0.00302772, -0.00052290, -0.00259735, ...,  0.00325148,
            0.00051726,  0.00464376],
          [ 0.00238924, -0.00105374,  0.00219904, ..., -0.00279356,
           -0.00214116, -0.00319181],
          [ 0.00180969,  0.00476100,  0.00380237, ...,  0.00249749,
            0.00374650,  0.00050141]]]]))

  可以看到,model.parameters() 将模型中所有参数以数组的方式返回。

  在实际的模型训练过程中,当调用反向图执行方法后,Paddle会计算出模型中每个参数的梯度并将其保存在相应的参数对象中。如果已经对该参数进行了梯度更新,或者出于一些原因不希望该梯度累加到下一轮训练,则可以调用 clear_gradients() 来清除这些梯度值。

model = Model()
out = model()
out.backward()
model.clear_gradients()

1.3.2 非参数变量的添加

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

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()
        self.saved_tensor = self.create_tensor(name="saved_tensor0")
        self.flatten = paddle.nn.Flatten()
        self.fc = paddle.nn.Linear(10, 100)

    def forward(self, input):
        y = self.flatten(input)
        # Save intermediate tensor
        paddle.assign(y, self.saved_tensor)
        y = self.fc(y)
        return y
model = Model()
print(model.sublayers())
[Flatten()]

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

1.3.3 Buffer变量的添加

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

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()
        saved_tensor = self.create_tensor(name="saved_tensor0")
        self.register_buffer("saved_tensor", saved_tensor, persistable=True)
        self.flatten = paddle.nn.Flatten()
        self.fc = paddle.nn.Linear(10, 100)

    def forward(self, input):
        y = self.flatten(input)
        # Save intermediate tensor
        paddle.assign(y, self.saved_tensor)
        y = self.fc(y)
        return y

  这样在动态图转静态图时saved_tensor就会被记录到静态图中。

  对于模型中已经注册的Buffer,可以通过 buffers() 或者 named_buffers() 进行访问:

model = Model()

print(model.buffers())

for item in model.named_buffers():
    print(item)
[Tensor(Not initialized)]
('saved_tensor', Tensor(Not initialized))

  可以看到 model.buffers() 以数组形式返回了模型中注册的所有Buffer

1.4 执行层的功能

  经过一系列对模型的配置,假如已经准备好了一个Paddle模型如下:

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()
        self.flatten = paddle.nn.Flatten()

    def forward(self, inputs):
        y = self.flatten(inputs)
        return y

  想要执行该模型,首先需要对执行模式进行设置

1.4.1 执行模式设置

  模型的执行模式有两种,如果需要训练的话调用 train() ,如果只进行前向执行则调用 eval()

x = paddle.randn([10, 1], 'float32')
model = Model()
model.eval()  # set model to eval mode
out = model(x)
model.train()  # set model to train mode
out = model(x)

  这里将模型的执行模式先后设置为evaltrain。两种执行模式是互斥的,新的执行模式设置会覆盖原有的设置。

1.4.2 执行函数

  模式设置完成后可以直接调用执行函数。可以直接调用forward()方法进行前向执行,也可以调用 __call__() ,从而执行在 forward() 当中定义的前向计算逻辑。

class Model(paddle.nn.Layer):

    def __init__(self):
        super(Model, self).__init__()
        self.flatten = paddle.nn.Flatten()

    def forward(self, inputs):
        y = self.Flatten(inputs)
        return y

    def Flatten(self, inputs):
        return paddle.to_tensor(inputs.numpy().flatten())
model = Model()
x = paddle.randn([10, 1], 'float32')
out = model(x)
print(out)
Tensor(shape=[10], dtype=float32, place=CPUPlace, stop_gradient=True,
       [-0.26968753, -2.34697795,  0.87075204,  1.20670414,  2.26653862,
         0.25821996,  0.70133287,  1.44512081,  0.96671742,  0.96629554])

  这里直接调用 __call__() 方法调用模型的前向执行逻辑。

1.4.3 添加hook函数

  有时希望某些变量在进入层前首先进行一些预处理,这个功能可以通过注册hook来实现。hook是一个作用于变量的自定义函数,在模型执行时调用。对于注册在层上的hook函数,可以分为pre_hookpost_hook两种。pre_hook可以对层的输入变量进行处理,用函数的返回值作为新的变量参与层的计算。post_hook则可以对层的输出变量进行处理,将层的输出进行进一步处理后,用函数的返回值作为层计算的输出。

  通过 register_forward_post_hook() 接口,我们可以注册一个post_hook

def forward_post_hook(layer, input, output):
    return 2*output

x = paddle.ones([10, 1], 'float32')
model = Model()
forward_post_hook_handle = model.flatten.register_forward_post_hook(forward_post_hook)
out = model(x)
print(out)
Tensor(shape=[10, 1], dtype=float32, place=CPUPlace, stop_gradient=True,
       [[2.],
        [2.],
        [2.],
        [2.],
        [2.],
        [2.],
        [2.],
        [2.],
        [2.],
        [2.]])

1.5 保存模型参数

  如果想要保存模型中参数而不存储模型本身,则可以首先调用 state_dict() 接口将模型中的参数以及永久变量存储到一个Python字典中,随后保存该字典。

model = Model()
state_dict = model.state_dict()
paddle.save( state_dict, "./paddle_dy.pdparams")
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/framework/io.py:729: UserWarning: The input state dict is empty, no need to save.
  warnings.warn("The input state dict is empty, no need to save.")

  运行了第二遍的时候,就没有这个提示。

 

  结 ※


  Paddle中的模型与层 的内容学习笔记。对于Paddle中的层的构造,操作进行了初步的测试与相关的学习。


■ 相关文献链接:

好的,以下是在PyCharm搭建PaddlePaddle神经网络模型的步骤: 1. 安装PaddlePaddle 首先需要安装PaddlePaddle,可以通过pip命令进行安装。在PyCharm的Terminal输入以下命令即可: ``` pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple ``` 2. 创建PyCharm项目 打开PyCharm,创建一个新项目。选择Python解释器为刚刚安装的PaddlePaddle所使用的Python解释器。 3. 导入PaddlePaddle 在PyCharm的代码编辑区域,导入PaddlePaddle库: ```python import paddle ``` 4. 构建神经网络模型 在PyCharm的代码编辑区域,构建神经网络模型。可以使用PaddlePaddle的API来构建模型。 ```python import paddle.nn.functional as F class MyModel(paddle.nn.Layer): def __init__(self): super(MyModel, self).__init__() self.fc1 = paddle.nn.Linear(784, 512) self.fc2 = paddle.nn.Linear(512, 10) def forward(self, x): x = F.relu(self.fc1(x)) x = self.fc2(x) return x ``` 在这个例子,我们创建了一个名为MyModel的类,继承自paddle.nn.Layer。该类包含两个线性(fc1和fc2),以及一个前向方法(forward)。 5. 训练模型 在PyCharm的代码编辑区域,训练神经网络模型。可以使用PaddlePaddle的API来训练模型。 ```python model = MyModel() # 定义损失函数和优化器 loss_fn = paddle.nn.CrossEntropyLoss() optimizer = paddle.optimizer.Adam(parameters=model.parameters()) # 进行训练 for epoch in range(10): for batch_id, data in enumerate(train_loader()): x_data = data[0] y_data = data[1] logits = model(x_data) loss = loss_fn(logits, y_data) loss.backward() optimizer.step() optimizer.clear_grad() if batch_id % 100 == 0: print("epoch: {}, batch_id: {}, loss is: {}".format(epoch, batch_id, loss.numpy())) ``` 在这个例子,我们首先创建了一个MyModel的实例,然后定义了损失函数和优化器。接着,我们进行了10个epoch的训练。在每个epoch,我们遍历了训练集的所有batch,并使用反向传播算法更新模型参数。 6. 预测 在PyCharm的代码编辑区域,使用神经网络模型进行预测。可以使用PaddlePaddle的API来进行预测。 ```python # 进行预测 model.eval() accs = [] for batch_id, data in enumerate(val_loader()): x_data = data[0] y_data = data[1] logits = model(x_data) pred = paddle.argmax(logits, axis=-1) acc = paddle.metric.accuracy(pred, y_data) accs.append(acc.numpy()) print("accuracy is: {}".format(np.mean(accs))) ``` 在这个例子,我们使用了验证集进行了预测,并计算了模型的准确率。 以上就是在PyCharm搭建PaddlePaddle神经网络模型的基本步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卓晴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值