本文翻譯自: 《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)
複製代碼
但若是利用了廣播機制,這種操做就能夠更有效地完成。舉個例子,由於咱們知道的事實,因此咱們能夠分別進行線性操做,並使用廣播進行隱式鏈接:網絡
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