本文轉載自:https://blog.csdn.net/LoseInVain/article/details/78763303
TensorFlow支持廣播機制(Broadcast),可以廣播元素間操作(elementwise operations)。正常情況下,當你想要進行一些操作如加法,乘法時,你需要確保操作數的形狀是相匹配的,如:你不能將一個具有形狀[3, 2]的張量和一個具有[3,4]形狀的張量相加。但是,這里有一個特殊情況,那就是當你的其中一個操作數是一個具有單獨維度(singular dimension)的張量的時候,TF會隱式地在它的單獨維度方向填滿(tile),以確保和另一個操作數的形狀相匹配。所以,對一個[3,2]的張量和一個[3,1]的張量相加在TF中是合法的。(譯者:這個機制繼承自numpy的廣播功能。其中所謂的單獨維度就是一個維度為1,或者那個維度缺失,具體可參考numpy broadcast)。
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
- 1
- 2
- 3
- 4
- 5
- 6
廣播機制允許我們在隱式情況下進行填充(tile),而這可以使得我們的代碼更加簡潔,並且更有效率地利用內存,因為我們不需要另外儲存填充操作的結果。一個可以表現這個優勢的應用場景就是在結合具有不同長度的特征向量的時候。為了拼接具有不同長度的特征向量,我們一般都先填充輸入向量,拼接這個結果然后進行之后的一系列非線性操作等。這是一大類神經網絡架構的共同套路(common pattern)
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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
但是這個可以通過廣播機制更有效地完成。我們利用事實f(m(x+y))=f(mx+my)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)
- 1
- 2
- 3
事實上,這個代碼足夠通用,並且可以在具有抽象形狀(arbitrary shape)的張量間應用:
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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
一個更為通用函數形式如上所述:
目前為止,我們討論了廣播機制的優點,但是同樣的廣播機制也有其缺點,隱式假設幾乎總是使得調試變得更加困難,考慮下面的例子:
a = tf.constant([[1.], [2.]]) b = tf.constant([1., 2.]) c = tf.reduce_sum(a + b)
- 1
- 2
- 3
你猜這個結果是多少?如果你說是6,那么你就錯了,答案應該是12.這是因為當兩個張量的階數不匹配的時候,在進行元素間操作之前,TF將會自動地在更低階數的張量的第一個維度開始擴展,所以這個加法的結果將會變為[[2, 3], [3, 4]],所以這個reduce的結果是12.
(譯者:答案詳解如下,第一個張量的shape為[2, 1],第二個張量的shape為[2,]。因為從較低階數張量的第一個維度開始擴展,所以應該將第二個張量擴展為shape=[2,2],也就是值為[[1,2], [1,2]]。第一個張量將會變成shape=[2,2],其值為[[1, 1], [2, 2]]。)
解決這種麻煩的方法就是盡可能地顯示使用。我們在需要reduce某些張量的時候,顯式地指定維度,然后尋找這個bug就會變得簡單:
a = tf.constant([[1.], [2.]]) b = tf.constant([1., 2.]) c = tf.reduce_sum(a + b, 0)
- 1
- 2
- 3
這樣,c的值就是[5, 7],我們就容易猜到其出錯的原因。一個更通用的法則就是總是在reduce操作和在使用tf.squeeze
中指定維度。