【译】Effective TensorFlow Chapter4——TensorFlow中的Broadcast机制的优缺点

本文翻译自: 《Broadcasting the good and the ugly》, 若有侵权请联系删除,仅限于学术交流,请勿商用。若有谬误,请联系指出。git

TensorFlow 支持广播元素操做。 一般,当你想作加法或乘法的运算时,你须要确保操做数的形状(shape)是匹配的,例如:你不能将一个形状为[3, 2]的张量和一个形状为[3,4]的张量相加。可是,这里有一个特殊状况,那就是当你的其中一个操做数是一个具备单独维度(singular dimension)的张量的时候,TF会隐式地在它的单独维度方向填满(tile),以确保和另外一个操做数的形状相匹配。因此,对一个[3,2]的张量和一个[3,1]的张量相加在TF中是合法的。github

import tensorflow as tf

a = tf.constant([[1., 2.], [3., 4.]])
b = tf.constant([[1.], [2.]])
# c = a + tf.tile(b, [1, 2])
c = a + b
复制代码

广播机制容许咱们在隐式状况下进行填充(tile),这种操做可使得咱们的代码更加简洁,而且更有效率地利用内存,由于咱们不须要储存填充操做的结果。一个能够表现这个优点的应用场景就是在结合具备不一样长度的特征向量的时候。为了拼接具备不一样长度的特征向量,咱们通常都先填充输入向量,拼接这个结果真后进行以后的一系列非线性操做等。这是各类神经网络架构的常见模式::bash

a = tf.random_uniform([5, 3, 5])
b = tf.random_uniform([5, 1, 6])

# concat a and b and apply nonlinearity
tiled_b = tf.tile(b, [1, 3, 1])
c = tf.concat([a, tiled_b], 2)
d = tf.layers.dense(c, 10, activation=tf.nn.relu)
复制代码

但若是利用了广播机制,这种操做就能够更有效地完成。举个例子,由于咱们知道f(m(x+y))=f(mx+my)的事实,因此咱们能够分别进行线性操做,并使用广播进行隐式链接:网络

pa = tf.layers.dense(a, 10, activation=None)
pb = tf.layers.dense(b, 10, activation=None)
d = tf.nn.relu(pa + pb)
复制代码

事实上,这段代码是通用的,只要张量之间可以进行广播,就能够应用于任意形状的张量:架构

def merge(a, b, units, activation=tf.nn.relu):
    pa = tf.layers.dense(a, units, activation=None)
    pb = tf.layers.dense(b, units, activation=None)
    c = pa + pb
    if activation is not None:
        c = activation(c)
    return c
复制代码

一个更通用的函数在app

目前为止,咱们讨论了广播机制的优势,可是一样的广播机制也有其缺点,隐式假设几乎老是使得调试变得更加困难,考虑下面的例子:dom

a = tf.constant([[1.], [2.]])
b = tf.constant([1., 2.])
c = tf.reduce_sum(a + b)
复制代码

你猜这个结果是多少?若是你说是6,那么你就错了,答案应该是12。这是由于当两个张量的不匹配的时候,TensorFlow将会在元素操做以前自动展开较低的张量的第一维,因此这个加法的结果将会变为[[2, 3], [3, 4]],对全部参数进行约化后获得12。函数

解决这种麻烦的方法就是尽量地显示使用。咱们在须要reduce某些张量的时候,显式地指定维度,而后寻找这个bug就会变得简单:ui

a = tf.constant([[1.], [2.]])
b = tf.constant([1., 2.])
c = tf.reduce_sum(a + b, 0)
复制代码

这样,c的值就是[5, 7],咱们就容易猜到其出错的缘由。一个更通用的法则就是:在进行reduce操做和使用tf.squeeze时咱们必须指定维度。spa

相关文章
相关标签/搜索