动态图

作者: PaddlePaddle
日期: 2021.05
摘要: 从飞桨框架2.0版本开始,飞桨默认为开启了动态图开发模式。在这种模式下,每次执行一个运算,可以立即得到结果(而不是事先定义好网络结构,然后再执行)。在动态图模式下,你可以更加方便的组织代码,更容易的调试程序,本示例教程将向你介绍飞桨的动态图的使用。

一、环境配置

本教程基于Paddle 2.1 编写,如果你的环境不是本版本,请先参考官网安装 Paddle 2.1 。

  1. import paddle
  2. import paddle.nn.functional as F
  3. import numpy as np
  4. print(paddle.__version__)
  1. 2.1.0

二、基本用法

在动态图模式下,你可以直接运行一个飞桨提供的API,它会立刻返回结果到python。不再需要首先创建一个计算图,然后再给定数据去运行。

  1. a = paddle.randn([4, 2])
  2. b = paddle.arange(1, 3, dtype='float32')
  3. print(a)
  4. print(b)
  5. c = a + b
  6. print(c)
  7. d = paddle.matmul(a, b)
  8. print(d)
  1. Tensor(shape=[4, 2], dtype=float32, place=CPUPlace, stop_gradient=True,
  2. [[ 1.37919605, -0.62310869],
  3. [ 1.67646325, -1.12426722],
  4. [ 0.29535872, 0.15387590],
  5. [-0.55509675, -1.91841769]])
  6. Tensor(shape=[2], dtype=float32, place=CPUPlace, stop_gradient=True,
  7. [1., 2.])
  8. Tensor(shape=[4, 2], dtype=float32, place=CPUPlace, stop_gradient=True,
  9. [[2.37919617, 1.37689137],
  10. [2.67646313, 0.87573278],
  11. [1.29535866, 2.15387583],
  12. [0.44490325, 0.08158231]])
  13. Tensor(shape=[4], dtype=float32, place=CPUPlace, stop_gradient=True,
  14. [ 0.13297868, -0.57207119, 0.60311055, -4.39193201])

三、使用python的控制流

动态图模式下,你可以使用python的条件判断和循环,这类控制语句来执行神经网络的计算。(不再需要cond, loop这类OP)

  1. a = paddle.to_tensor(np.array([1, 2, 3]))
  2. b = paddle.to_tensor(np.array([4, 5, 6]))
  3. for i in range(10):
  4. r = paddle.rand([1,])
  5. if r > 0.5:
  6. c = paddle.pow(a, i) + b
  7. print("{} +> {}".format(i, c.numpy()))
  8. else:
  9. c = paddle.pow(a, i) - b
  10. print("{} -> {}".format(i, c.numpy()))
  1. 0 +> [5 6 7]
  2. 1 -> [-3 -3 -3]
  3. 2 +> [ 5 9 15]
  4. 3 -> [-3 3 21]
  5. 4 +> [ 5 21 87]
  6. 5 +> [ 5 37 249]
  7. 6 +> [ 5 69 735]
  8. 7 -> [ -3 123 2181]
  9. 8 +> [ 5 261 6567]
  10. 9 +> [ 5 517 19689]

四、构建更加灵活的网络:控制流

  • 使用动态图可以用来创建更加灵活的网络,比如根据控制流选择不同的分支网络,和方便的构建权重共享的网络。接下来来看一个具体的例子,在这个例子中,第二个线性变换只有0.5的可能性会运行。

  • 在sequence to sequence with attention的机器翻译的示例中,你会看到更实际的使用动态图构建RNN类的网络带来的灵活性。

  1. class MyModel(paddle.nn.Layer):
  2. def __init__(self, input_size, hidden_size):
  3. super(MyModel, self).__init__()
  4. self.linear1 = paddle.nn.Linear(input_size, hidden_size)
  5. self.linear2 = paddle.nn.Linear(hidden_size, hidden_size)
  6. self.linear3 = paddle.nn.Linear(hidden_size, 1)
  7. def forward(self, inputs):
  8. x = self.linear1(inputs)
  9. x = F.relu(x)
  10. if paddle.rand([1,]) > 0.5:
  11. x = self.linear2(x)
  12. x = F.relu(x)
  13. x = self.linear3(x)
  14. return x
  1. total_data, batch_size, input_size, hidden_size = 1000, 64, 128, 256
  2. x_data = np.random.randn(total_data, input_size).astype(np.float32)
  3. y_data = np.random.randn(total_data, 1).astype(np.float32)
  4. model = MyModel(input_size, hidden_size)
  5. loss_fn = paddle.nn.MSELoss(reduction='mean')
  6. optimizer = paddle.optimizer.SGD(learning_rate=0.01,
  7. parameters=model.parameters())
  8. for t in range(200 * (total_data // batch_size)):
  9. idx = np.random.choice(total_data, batch_size, replace=False)
  10. x = paddle.to_tensor(x_data[idx,:])
  11. y = paddle.to_tensor(y_data[idx,:])
  12. y_pred = model(x)
  13. loss = loss_fn(y_pred, y)
  14. if t % 200 == 0:
  15. print(t, loss.numpy())
  16. loss.backward()
  17. optimizer.step()
  18. optimizer.clear_grad()
  1. 0 [1.1265092]
  2. 200 [0.8528375]
  3. 400 [0.4673411]
  4. 600 [0.25207424]
  5. 800 [0.20187281]
  6. 1000 [0.0523094]
  7. 1200 [0.02333442]
  8. 1400 [0.01016075]
  9. 1600 [0.00662685]
  10. 1800 [0.02008343]
  11. 2000 [0.00313827]
  12. 2200 [0.00228308]
  13. 2400 [0.0010894]
  14. 2600 [0.00323017]
  15. 2800 [0.00210726]

五、构建更加灵活的网络:共享权重

  • 使用动态图还可以更加方便的创建共享权重的网络,下面的示例展示了一个共享了权重的简单的AutoEncoder。

  • 你也可以参考图像搜索的示例看到共享参数权重的更实际的使用。

  1. inputs = paddle.rand((256, 64))
  2. linear = paddle.nn.Linear(64, 8, bias_attr=False)
  3. loss_fn = paddle.nn.MSELoss()
  4. optimizer = paddle.optimizer.Adam(0.01, parameters=linear.parameters())
  5. for i in range(10):
  6. hidden = linear(inputs)
  7. # weight from input to hidden is shared with the linear mapping from hidden to output
  8. outputs = paddle.matmul(hidden, linear.weight, transpose_y=True)
  9. loss = loss_fn(outputs, inputs)
  10. loss.backward()
  11. print("step: {}, loss: {}".format(i, loss.numpy()))
  12. optimizer.step()
  13. optimizer.clear_grad()
  1. step: 0, loss: [0.34925056]
  2. step: 1, loss: [0.30813003]
  3. step: 2, loss: [0.2843244]
  4. step: 3, loss: [0.26361164]
  5. step: 4, loss: [0.23956387]
  6. step: 5, loss: [0.21207763]
  7. step: 6, loss: [0.18401164]
  8. step: 7, loss: [0.1586062]
  9. step: 8, loss: [0.13708147]
  10. step: 9, loss: [0.11902912]

The End

可以看到使用动态图带来了更灵活易用的方式来组网和训练。你也可以在【使用注意力机制的LSTM的机器翻译】和【图片检索】两个示例中看到更完整的动态图的实际应用的灵活和便利。