【问题标题】:Understanding device allocation, parallelism(tf.while_loop) and tf.function in tensorflow了解 tensorflow 中的设备分配、并行性(tf.while_loop)和 tf.function
【发布时间】:2019-04-10 15:10:32
【问题描述】:

我正在尝试理解 tensorflow 中 GPU 上的并行性,因为我需要将其应用于更丑陋的图表。

import tensorflow as tf
from datetime import datetime

with tf.device('/device:GPU:0'):
    var = tf.Variable(tf.ones([100000], dtype=tf.dtypes.float32), dtype=tf.dtypes.float32)

@tf.function
def foo():
    return tf.while_loop(c, b, [i], parallel_iterations=1000)      #tweak

@tf.function
def b(i):
    var.assign(tf.tensor_scatter_nd_update(var, tf.reshape(i, [-1,1]), tf.constant([0], dtype=tf.dtypes.float32)))
    return tf.add(i,1)

with tf.device('/device:GPU:0'):
    i = tf.constant(0)
    c = lambda i: tf.less(i,100000)

start = datetime.today()
with tf.device('/device:GPU:0'):
    foo()
print(datetime.today()-start)

在上面的代码中,var 是一个长度为 100000 的张量,其元素更新如上图所示。当我将 parallel_iterations 值从 10、100、1000、10000 更改时。即使明确提到了 parallel_iterations 变量,也几乎没有任何时间差(全部为 9.8 秒)。

我希望这些在 GPU 上并行发生。如何实现?

【问题讨论】:

标签: python gpgpu tensorflow2.0 eager-execution


【解决方案1】:

首先,请注意您的 tensor_scatter_nd_update 只是增加一个索引,因此您只能测量循环本身的开销。

我修改了您的代码以使用更大的批处理大小。在 GPU 下的 Colab 中运行,我需要 batch=10000 来隐藏循环延迟。低于该值的任何内容都会衡量(或支付)延迟开销。

另外,问题是,var.assign(tensor_scatter_nd_update(...)) 是否真的阻止了tensor_scatter_nd_update 制作的额外副本?使用批量大小表明我们确实没有为额外的副本付费,因此似乎很好地防止了额外的副本。

然而,事实证明,在这种情况下,显然,tensorflow 只是认为迭代是相互依赖的,因此如果你增加循环迭代,它不会有任何区别(至少在我的测试中)。有关 TF 功能的进一步讨论,请参阅此内容:https://github.com/tensorflow/tensorflow/issues/1984

只有当它们是独立的(操作)时,它才会并行执行。

顺便说一句,任意分散运算在 GPU 上的效率不会很高,但如果 TF 认为它们是独立的,您仍然可能(应该)能够并行执行多个运算。

import tensorflow as tf
from datetime import datetime

size = 1000000
index_count = size
batch = 10000
iterations = 10

with tf.device('/device:GPU:0'):
    var = tf.Variable(tf.ones([size], dtype=tf.dtypes.float32), dtype=tf.dtypes.float32)
    indexes = tf.Variable(tf.range(index_count, dtype=tf.dtypes.int32), dtype=tf.dtypes.int32)
    var2 = tf.Variable(tf.range([index_count], dtype=tf.dtypes.float32), dtype=tf.dtypes.float32)

@tf.function
def foo():
    return tf.while_loop(c, b, [i], parallel_iterations = iterations)      #tweak

@tf.function
def b(i):
    var.assign(tf.tensor_scatter_nd_update(var, tf.reshape(indexes, [-1,1])[i:i+batch], var2[i:i+batch]))
    return tf.add(i, batch)

with tf.device('/device:GPU:0'):
    i = tf.constant(0)
    c = lambda i: tf.less(i,index_count)

start = datetime.today()
with tf.device('/device:GPU:0'):
    foo()
print(datetime.today()-start)

【讨论】:

    【解决方案2】:

    一种技术是使用分发策略和范围:

    strategy = tf.distribute.MirroredStrategy()
    
    with strategy.scope():
      inputs = tf.keras.layers.Input(shape=(1,))
      predictions = tf.keras.layers.Dense(1)(inputs)
      model = tf.keras.models.Model(inputs=inputs, outputs=predictions)
      model.compile(loss='mse',
                    optimizer=tf.train.GradientDescentOptimizer(learning_rate=0.2))
    

    另一种选择是在每台设备上复制操作:

    # Replicate your computation on multiple GPUs
    c = []
    for d in ['/device:GPU:2', '/device:GPU:3']:
      with tf.device(d):
        a = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[2, 3])
        b = tf.constant([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], shape=[3, 2])
        c.append(tf.matmul(a, b))
    with tf.device('/cpu:0'):
      sum = tf.add_n(c)
    
    
    

    详情请见this guide

    【讨论】:

      猜你喜欢
      • 2021-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-10
      • 1970-01-01
      • 1970-01-01
      • 2021-12-01
      • 2019-10-11
      相关资源
      最近更新 更多