[阿里DIN] 從模型源碼梳理TensorFlow的乘法相關概念

[阿里DIN] 從模型源碼梳理TensorFlow的乘法相關概念

0x00 摘要

本文基於阿里推薦 DIN 和 DIEN 代碼,梳理了下深度學習一些概念,以及TensorFlow中的相關實現。php

由於篇幅所限,因此以前的總體代碼講解中,不少細節沒有深刻,因此本文會就這些細節進行探討,旨在幫助小夥伴們詳細瞭解每一的步驟以及爲何要這樣作。html

本文涉及概念有:矩陣乘積,多維矩陣相乘,tile,張量廣播等。python

0x01 矩陣乘積

這裏只介紹通常矩陣乘積和哈達瑪積,由於DIN和DIEN有使用到。數組

1.1 matmul product(通常矩陣乘積)

m x p矩陣A與p x n矩陣B,那麼稱 m x n 矩陣C爲矩陣A與矩陣B的通常乘積,記做C = AB ,其中矩陣C元素[cij]爲矩陣A、B對應兩兩元素乘積之和,網絡

1.2 Hadamard product(哈達瑪積)

m x n 矩陣A = [aij]與矩陣 B = [bij]的Hadamard積,記爲A * B 。新矩陣元素定義爲矩陣A、B對應元素的乘積 (A * B)ij = aij.bij架構

1.3 tf.matmul

此函數是:將矩陣a乘以矩陣b,生成a * b。就是向量乘法,即線性代數中的矩陣之間相乘的運算。函數

格式: tf.matmul(a, b, transpose_a=False, transpose_b=False, adjoint_a=False, adjoint_b=False, a_is_sparse=False, b_is_sparse=False, name=None)學習

主要參數:ui

  • a: 一個類型爲 float16, float32, float64, int32, complex64, complex128 且張量秩 > 1 的張量。
  • b: 一個類型跟張量a相同的張量。

注意:spa

  • 輸入必須是矩陣(或者是張量秩 >2的張量,表示成批的矩陣),而且其在轉置以後有相匹配的矩陣尺寸。
  • 兩個矩陣必須都是一樣的類型,支持的類型以下:float16, float32, float64, int32, complex64, complex128。

1.4 tf.multiply

此函數是:兩個矩陣中對應元素各自相乘,即逐元素操做。逐元素操做是指把x中的每個元素與y中的每個元素逐個地進行運算。就是哈達瑪積

格式: tf.multiply(x, y, name=None)

參數:

  • x: 一個類型爲:half, float32, float64, uint8, int8, uint16, int16, int32, int64, complex64, complex128的張量;
  • y: 一個類型跟張量x相同的張量;
  • 返回值: x * y element-wise;

注意:

  • multiply這個函數實現的是元素級別的相乘,也就是兩個相乘的數元素各自相乘,而不是矩陣乘法,注意和tf.matmul區別。
  • 兩個相乘的數必須有相同的數據類型,否則就會報錯。

1.5 重載

TensorFlow會進行操做符重載,具體是:

元素乘法:tf.multiply(),能夠用*運算符代替,

向量乘法:tf.matmul(),能夠用@運算符代替。向量乘法採用的乘法是線性代數中的矩陣之間相乘的運算。

1.6 DIN使用

在DIN使用以下:

# 7. 獲得了正確的權重 scores 以及用戶歷史行爲序列 facts, 再進行矩陣相乘獲得用戶的興趣表徵
# Weighted sum,
if mode == 'SUM':
    # scores 的大小爲 [B, 1, T], 表示每條歷史行爲的權重,
    # facts 爲歷史行爲序列, 大小爲 [B, T, H];
    # 二者用矩陣乘法作, 獲得的結果 output 就是 [B, 1, H]
    # B * 1 * H 三維矩陣相乘,相乘發生在後兩維,即 B * (( 1 * T ) * ( T * H ))
    # 這裏的output是attention計算出來的權重,即論文公式(3)裏的w,
    output = tf.matmul(scores, facts)  # [B, 1, H]
    # output = tf.reshape(output, [-1, tf.shape(facts)[-1]])
else:
    # 從 [B, 1, H] 變化成 Batch * Time
    scores = tf.reshape(scores, [-1, tf.shape(facts)[1]]) 
    # 先把scores在最後增長一維,而後進行哈達碼積,[B, T, H] x [B, T, 1] =  [B, T, H]
    output = facts * tf.expand_dims(scores, -1) # 重載了,就是multiply,哈達瑪積
    output = tf.reshape(output, tf.shape(facts)) # Batch * Time * Hidden Size
return outputpy

0x02 多維矩陣相乘

2.1 TensorFlow實現

矩陣乘法本質上只能是兩個二維的matrix進行叉乘,那麼兩個三維甚至四維的矩陣相乘是怎麼作到的呢?

答案是:兩個多維矩陣相乘時,假如分別是a 和 b,若是a和b的dimention大於2,實際上進行的會是batch_mat_mul,此時進行叉乘的是batch中的每個切片(slice)。

  • a和b除了最後兩個維度能夠不一致,其餘維度要相同;
  • a和b最後兩維的維度要符合矩陣乘法的要求(好比a的(3,4)能和b的(4,6)進行矩陣乘法);

好比

  • a的維度是(2,2,3);
  • b的維度是(2,3,2);

第一維 2 相同, 最後兩維 知足矩陣乘法要求,一個是(i,j),另外一個必須是(j,k)。

相乘後,除後兩維以外的維度不變,後兩維變成(i,k),如(…,i,j)*(…,j,k)= (…,i,k),對應本例相乘結果是 (2,2,2)。

2.2 DIN使用

DIN中使用能夠參見上節代碼,裏面都是高維矩陣相乘。

0x03 tile

某些狀況下,矩陣相乘中會隱含包括tile操做,因此要預先講解。

3.1 tile函數

Tensorflow中tile是用來複制tensor的指定維度。具體看下面的代碼:

import tensorflow as tf
a = tf.constant([[1, 2], [3, 4], [5, 6]], dtype=tf.float32)
a1 = tf.tile(a, [2, 2])
with tf.Session() as sess:
    print(sess.run(a1))

結果就是:

[[ 1.  2.  1.  2.]
 [ 3.  4.  3.  4.]
 [ 5.  6.  5.  6.]
 [ 1.  2.  1.  2.]
 [ 3.  4.  3.  4.]
 [ 5.  6.  5.  6.]]

由於

a1 = tf.tile(a, [2, 2]) 表示把a的第一個維度複製兩次,第二個維度複製2次。

3.2 DIN使用

在DIN中,能夠經過運行時變量看到tile的做用,可見 query 擴展成 queries,就是按照 tf.shape(facts)[1] 的數值來擴展。

queries = tf.tile(query, [1, tf.shape(facts)[1]])

facts = {Tensor} Tensor("rnn_1/gru1/transpose:0", shape=(?, ?, 36), dtype=float32)
query = {Tensor} Tensor("Attention_layer_1/add:0", shape=(?, 36), dtype=float32)
queries = {Tensor} Tensor("Attention_layer_1/Tile:0", shape=(?, ?), dtype=float32)

queries = tf.reshape(queries, tf.shape(facts))

queries = {Tensor} Tensor("Attention_layer_1/Reshape:0", shape=(?, ?, 36), dtype=float32)

tf.shape(facts)[1] 的數值是 4,query 的shape是[128 36]。

[
[0.0200167075 -0.00225125789 -9.32959301e-05 0.0160047226 0.0463943668 -0.00113779912 -0.00141796377 -0.000895748846 0.0205967128 0.0120106135 0.0233127 -0.000518312503 0.0179327205 0.00611556 0.0276019834 0.0250585414 0.0206870511 0.0126676112 -0.00169671408 -0.0029286067 -0.00291765784 0.00653835898 0.0137697691 0.0447938591 0.006571854 0.0171166249 0.0594488233 0.0111965612 0.0217649955 -0.000470559491 0.0169355199 0.0325907469 0.0242765 -0.00169698952 0.0238724295 0.0290065929]

[0.0174195394 -0.00232273433 -0.000350985356 0.0126237422 0.0450226218 -0.00097405276 -0.00162016717 -0.000970863 0.0230836142 0.0101783276 0.0212102327 -0.000583510089 0.0152175426 0.00769237662 0.0285565071 0.0254475642 0.0209889729 0.0134746656 -0.00162631273 -0.00267679896 -0.00319493 0.00920876209 0.0141795734 0.0454878397 0.0029891273 0.0177330635 0.0595819876 0.011406675 0.0246347431 -0.000576826278 0.0158954468 0.0311567299 0.024484111 -0.00184945751 0.0230423771 0.0260604471]

[0.0178403854 -0.00220142 -0.000242564696 0.0132796057 0.0460800715 -0.000954665651 -0.00147331599 -0.000593276578 0.0236354619 0.0102384314 0.0232978407 -0.000677037227 0.0149542987 0.0083344169 0.026211584 0.0257896669 0.0201499276 0.0104032271 -0.00147544965 -0.00248164777 -0.00298029534 0.00669088727 0.0161470883 0.046244178 0.00351092312 0.0186183155 0.0588327497 0.00999171101 0.0243503805 -0.000576853694 0.0162444208 0.0293106604 0.0244945567 -0.0017665698 0.022099141 0.0269105248]

...

queries的shape是 [128 144],內容以下:

[
[0.0200167075 -0.00225125789 -9.32959301e-05 0.0160047226 0.0463943668 -0.00113779912 -0.00141796377 -0.000895748846 0.0205967128 0.0120106135 0.0233127 -0.000518312503 0.0179327205 0.00611556 0.0276019834 0.0250585414 0.0206870511 0.0126676112 -0.00169671408 -0.0029286067 -0.00291765784 0.00653835898 0.0137697691 0.0447938591 0.006571854 0.0171166249 0.0594488233 0.0111965612 0.0217649955 -0.000470559491 0.0169355199 0.0325907469 0.0242765 -0.00169698952 0.0238724295 0.0290065929 0.0200167075 -0.00225125789 -9.32959301e-05 0.0160047226 ...
....

0x04 張量廣播

廣播(broadcasting)指的是不一樣形狀的張量之間的算數運算的執行方式。

4.1 目的

廣播的目的是將兩個不一樣形狀的張量 變成兩個形狀相同的張量:

TensorFlow支持廣播機制(Broadcast),能夠廣播元素間操做(elementwise operations)

正常狀況下,當你想要進行一些操做如加法,乘法時,你須要確保操做數的形狀是相匹配的,如:你不能將一個具備形狀[3, 2]的張量和一個具備[3,4]形狀的張量相加。

可是,這裏有一個特殊狀況,那就是當你的其中一個操做數是一個具備單獨維度(singular dimension)的張量的時候,TF會隱式地在它的單獨維度方向填滿(tile),以確保和另外一個操做數的形狀相匹配。因此,對一個[3,2]的張量和一個[3,1]的張量相加在TF中是合法的。(這個機制繼承自numpy的廣播功能。其中所謂的單獨維度就是一個維度爲1,或者那個維度缺失)

4.2 機制

廣播的機制是:

  • 先對小的張量添加軸(使其ndim與較大的張量相同);
  • 再把較小的張量沿着新軸重複(使其shape與較大的相同);

廣播的的限制條件爲:

  • 兩個張量的 trailing dimension(從後往前算起的維度)的軸長相等;
  • 或 其中一個的長度爲1;

即,若是兩個數組的後緣維度(從末尾開始算起的維度) 的 軸長度相符其中一方的長度爲1,則認爲它們是廣播兼容的。廣播會在缺失維度和(或)軸長度爲1的維度上進行。

廣播機制容許咱們在隱式狀況下進行填充(tile),而這可使得咱們的代碼更加簡潔,而且更有效率地利用內存,由於咱們不須要另外儲存填充操做的結果。一個能夠表現這個優點的應用場景就是在結合具備不一樣長度的特徵向量的時候。爲了拼接具備不一樣長度的特徵向量,咱們通常都先填充輸入向量,拼接這個結果真後進行以後的一系列非線性操做等。這是一大類神經網絡架構的共同套路(common pattern)。

下面給出幾個例子。

4.3 例1

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

輸出是

[[2. 3.]
 [5. 6.]]

4.4 例2

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

#c輸出12

給出分析以下:

你猜這個結果是多少?若是你說是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]]。)

4.5 DIN使用

在DIN使用以下:

# Weighted sum,
if mode == 'SUM':
   ...
else:
    # facts 爲歷史行爲序列, 大小爲 [B, T, H];
    # scores 從 [B, 1, H] 變化成 Batch * Time
    scores = tf.reshape(scores, [-1, tf.shape(facts)[1]]) 
    # 而後把scores在最後增長一維,而後進行哈達碼積,[B, T, H] x [B, T, 1] =  [B, T, H]
    # 這裏就進行了張量廣播,由於 廣播會在缺失維度和(或)軸長度爲1的維度上進行,自動進行tile操做
    output = facts * tf.expand_dims(scores, -1) # 重載了,就是multiply,哈達瑪積

0xFF 參考

tf.matmul() 和tf.multiply() 的區別

卷積神經網絡(CNN)入門講解關注專欄

全鏈接層的做用是什麼?

對全鏈接層(fully connected layer)的通俗理解

爲何用ReLU?

RNN LSTM 最後還須要一層普通全連接層?

斯坦福cs231n學習筆記(9)------神經網絡訓練細節(Batch Normalization)

完全理解 tf.reduce_sum()

關於numy中np.expand_dims方法的理解?

辨析matmul product(通常矩陣乘積),hadamard product(哈達瑪積)、kronecker product(克羅內克積)

[tensorflow] 多維矩陣的乘法

Tensorflow 的reduce_sum()函數究竟是什麼意思

Batch Normalization導讀——張俊林

理解Batch Normalization中Batch所表明具體含義的知識基礎

快速掌握TensorFlow中張量運算的廣播機制

tensorflow的廣播機制

張量(tensor)的廣播

相關文章
相關標籤/搜索