动态与静态计算图 – PyTorch和TensorFlow
TensorFlow和Pytorch是最近最流行的两个深度学习库。这两个库都在主流深度学习领域发展了各自的利基,都有优秀的文档和教程,最重要的是,它们背后有一个旺盛的、支持性的社区。
TensorFlow中的静态计算图和Pytorch中的动态计算图之间的区别
虽然这两个库都采用了有向无环图(或DAG)来表示他们的机器学习和深度学习模型,但他们让数据和计算在图中流动的方式仍然有很大的区别。这两个库之间的微妙区别是,Tensorflow(v < 2.0)允许静态图计算,而Pytorch允许动态图计算。本文将通过代码实例,以直观的方式介绍这些差异。本文假设你对计算图有一定的了解,并对TensorFlow和Pytorch模块有基本了解。对于这些概念的快速复习,建议读者阅读以下文章。
- 深度学习中的计算图
- 开始使用PyTorch
- TensorFlow简介
Tensorflow中的静态计算图
节点和边的属性。 节点代表直接应用于通过边流入和流出的数据的操作。对于上述方程组,我们在TensorFlow中实现它时可以记住以下几点。
- 由于输入作为图的边,我们可以使用tf.Placeholder()对象,它可以接受任何所需数据类型的输入。
- 为了计算输出 “c”,我们定义了一个简单的乘法运算,并启动一个tensorflow会话,通过session.run()方法中的feed_dict属性传入所需的输入值,以计算输出和梯度。
现在让我们在TensorFlow中实现上述计算,观察操作是如何发生的。
# Importing tensorflow version 1
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
# Initializing placeholder variables of
# the graph
a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
# Defining the operation
c = tf.multiply(a, b)
# Instantiating a tensorflow session
with tf.Session() as sess:
# Computing the output of the graph by giving
# respective input values
out = sess.run(, feed_dict={a: [15.0], b: [20.0]})[0][0]
# Computing the output gradient of the output with
# respect to the input 'a'
derivative_out_a = sess.run(tf.gradients(c, a), feed_dict={
a: [15.0], b: [20.0]})[0][0]
# Computing the output gradient of the output with
# respect to the input 'b'
derivative_out_b = sess.run(tf.gradients(c, b), feed_dict={
a: [15.0], b: [20.0]})[0][0]
# Displaying the outputs
print(f'c = {out}')
print(f'Derivative of c with respect to a = {derivative_out_a}')
print(f'Derivative of c with respect to b = {derivative_out_b}')
输出:
c = 300.0
Derivative of c with respect to a = 20.0
Derivative of c with respect to b = 15.0
我们可以看到,输出结果与我们在介绍部分的计算结果正确匹配,从而表明成功完成。从代码中可以看出静态结构,因为我们可以看到,一旦在一个会话中,我们不能定义新的操作(或节点),但我们肯定可以使用sess.run()方法中的feed_dict属性来改变输入变量。
优势:
- 由于图是静态的,它在结构和资源分配方面提供了许多优化的可能性。
- 由于结构固定,计算的速度比动态图略快。
劣势:
- 对可变维度输入的扩展性很差。例如,一个CNN(卷积神经网络)架构有一个静态的计算图,在28×28的图像上训练,如果没有大量的预处理模板代码,在不同尺寸的图像(如100×100)上就不会有好的表现。
- 调试性差。这些都是很难调试的,主要是因为用户无法接触到信息流是如何发生的。 erg:假设用户创建了一个畸形的静态图,用户无法直接跟踪这个错误,直到TensorFlow会话在计算反向传播和前向传播时发现一个错误。当模型巨大时,这就成为一个主要问题,因为它既浪费了用户的时间又浪费了计算资源。
Pytorch中的动态计算图
节点和边的属性。 节点代表数据(以张量的形式),边代表应用于输入数据的操作。
对于导论中给出的方程,我们在Pytorch中实现它时,可以记住以下事项。
- 由于Pytorch中的所有东西都是动态创建的,我们不需要任何占位符,可以在飞行中定义我们的输入和操作。
- 在定义输入和计算输出’c’之后,我们调用backward()方法,计算相对于通过.grad指定符访问的两个输入的相应偏导。
现在让我们看看一个代码例子来验证我们的发现。
# Importing torch
import torch
# Initializing input tensors
a = torch.tensor(15.0, requires_grad=True)
b = torch.tensor(20.0, requires_grad=True)
# Computing the output
c = a * b
# Computing the gradients
c.backward()
# Collecting the output gradient of the
# output with respect to the input 'a'
derivative_out_a = a.grad
# Collecting the output gradient of the
# output with respect to the input 'b'
derivative_out_b = b.grad
# Displaying the outputs
print(f'c = {c}')
print(f'Derivative of c with respect to a = {derivative_out_a}')
print(f'Derivative of c with respect to b = {derivative_out_b}')
输出:
c = 300.0
Derivative of c with respect to a = 20.0
Derivative of c with respect to b = 15.0
我们可以看到,输出结果与我们在介绍部分的计算结果正确匹配,从而表明成功完成。从代码中可以看出动态结构。我们可以看到,所有的输入和输出都只能在运行时访问和改变,这与Tensorflow使用的方法完全不同。
优势:
- 对不同维度输入的可扩展性。对不同维度的输入有很好的扩展性,因为新的预处理层可以动态地加入到网络本身。
- 调试容易。这些都是非常容易调试的,也是很多人从Tensorflow转向Pytorch的原因之一。由于节点是在任何信息流经它们之前动态创建的,因此错误变得非常容易发现,因为用户完全控制了训练过程中使用的变量。
劣势:
- 允许图形优化的空间很小,因为每个训练实例/批次都需要创建一个新的图形。
结论
本文阐明了Tensorflow和Pytorch的建模结构的区别。文章还通过代码实例列出了两种方法的一些优缺点。这些库的开发背后各自的组织在随后的迭代中不断改进,但读者现在可以在为他们的下一个项目选择最佳框架之前做出更明智的决定。