1.numpy中的廣播
廣播(broadcast)是numpy中經常使用的一個技能點,他能夠對不同形狀的數組進行各種方式的計算。
舉個簡單的例子:
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = a + b
此時c的結果爲[5, 7, 9]。一般情況下要進行a+b的操作,需要a.shape=b.shape。比如上面的例子中,a、b的shape均爲(3,),(3,)表示一維數組,數組中有3個元素。
很多情況下,維度不同的兩個數組也能進行類似的操作,這裏頭的原理就是會自動出發廣播機制。
a = np.array([[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[3, 3, 3]])
b = np.array([1, 2, 3])
print(a.shape)
print(b.shape)
print(a + b)
結果爲
(4, 3)
(3,)
[[1 2 3]
[2 3 4]
[3 4 5]
[4 5 6]]
[[1 2 3]
[2 3 4]
[3 4 5]
[4 5 6]]
a是一個4*3的二維數組,b是一個(3,)的一維數組。相當於將b在行的維度上擴充四倍進行運算。
此時a+b
等價於a + np.tile(b, [4, 1])
如果是a + np.tile(b, [5, 1])
,此時代碼會報錯,提示維度不匹配。
ValueError: operands could not be broadcast together with shapes (4,3) (5,3)
2.tensorflow中的廣播
tensorflow中的廣播機制與numpy類似。
同樣看一個例子
def broadcast():
a = tf.constant([[1, 2], [3, 4]])
b = tf.constant([[1], [2]])
c = a + b
d = a + tf.tile(b, [1, 2])
with tf.Session() as sess:
cc, dd = sess.run([c, d])
print(cc)
print(dd)
輸出的結果爲
[[2 3]
[5 6]]
[[2 3]
[5 6]]
可見此時a + b
與a + tf.tile(b, [1, 2])
的效果是一樣的。
3.廣播的優點
廣播機制允許我們在隱式情況下進行填充(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)
但是這個可以通過廣播機制更有效地完成。我們利用事實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)