好久沒有寫總結了,這篇博客僅做爲最近的一些嘗試內容,記錄一些心得。FFM的優點是能夠處理高維稀疏樣本的特徵組合,已經在無數的CTR預估比賽和工業界中普遍應用,此外,其也能夠與Deep Networks結合(如DeepFM等工做),很好地應用在數據規模足夠大的工業場景中。Recurrent Entity Network是facebook AI在2017年的ICLR會議上發表的,文章提出了Recurrent Entity Network的模型用來對world state進行建模,根據模型的輸入對記憶單元進行實時的更新,從而獲得對world的一個即時的認識。該模型能夠用於機器閱讀理解、QA等領域。java
若是咱們但願作一個關於豆瓣電影的QA機器人,咱們有每一個電影的劇情文本介紹和豆瓣下面的評論,也有每一個電影的特徵(如其導演、演員、獲獎狀況、拍攝年份等),那麼利用FFM對電影特徵進行Embedding向量化,再利用Deep Networks對電影劇情文本和評論文本進行向量化,二者融合起來,豈不是能夠更好地回答提問者的問題?而Entity Networks經過memory機制存儲文本上下文信息,可以更好地捕獲先後信息,抓住實體關係,在QA領域會更加遊刃有餘。python
常見的線性模型,好比線性迴歸、邏輯迴歸等,它只考慮了每一個特徵對結果的單獨影響,而沒有考慮特徵間的組合對結果的影響。c++
對於一個有n維特徵的模型,線性迴歸的形式以下:算法
$$ f(x) = \omega_0 + \omega_1x_1+\omega_2x_2+...+\omega_nx_n =\omega_0+\sum_{i=1}^n{\omega_ix_i} \tag{1} $$架構
其中$(\omega_0,\omega_1...\omega_n)$爲模型參數,$(x_1,x_2...x_n)$爲特徵。
從(1)式能夠看出來,模型的最終計算結果是各個特徵的獨立計算結果,並無考慮特徵之間的相互關係。
舉個例子,咱們「USA」與」Thanksgiving」,」China」與「Chinese new year」這樣的組合特徵是頗有意義的,在這樣的組合特徵下,會對某些商品表現出更強的購買意願,而單獨考慮國家及節日都是沒有意義的。app
咱們在(1)式的基礎上,考慮任意2個特徵份量之間的關係,得出如下模型: dom
$$ f(x)=\omega_0+\sum_{i=1}^n\omega_ix_i+\sum_{i=1}^{n-1}\sum_{j=i+1}^n\omega_{ij}x_ix_j \tag{2} $$ide
這個模型考慮了任意2個特徵份量之間的關係,但並未考慮更高階的關係。
模型涉及的參數數量爲: 函數
$$ 1+n+\frac{n(n-1)}{2}=\frac{1}{2}(n^2+n+2) \tag{3} $$學習
對於參數$\omega_i$的訓練,只要這個樣本中對應的$x_i$不爲0,則能夠完成一次訓練。
但對於參數$\omega_{ij}$的訓練,須要這個樣本中的$x_i$和$x_j$同時不爲0,才能夠完成一次訓練。
在數據稀疏的實際應用場景中,二次項$\omega_{ij}$的訓練是很是困難的。由於每一個$\omega_{ij}$都須要大量$x_i$和$x_j$都不爲0的樣本。但在數據稀疏性比較明顯的樣本中,$x_i$和$x_j$都不爲0的樣本會很是稀少,這會致使$\omega_{ij}$不能獲得足夠的訓練,從而不許確。
爲了解決上述因爲數據稀疏引發的訓練不足的問題,咱們爲每一個特徵維度$x_i$引入一個輔助向量:
$$ V_i = (v_{i1},v_{i2},v_{i3},...,v_{ik})^T\in \mathbb R^k, i=1,2,3,...,n \tag{4} $$
其中$k$爲輔助變量的維度,依經驗而定,通常而言,對於特徵維度足夠多的樣本,$k<<n$。
目標是要求得如下交互矩陣W:
$$ W=
\begin{pmatrix}
\omega_{11} & \omega_{12}& ... &\omega_{1n} \\
\omega_{21} & \omega_{22}& ... &\omega_{2n} \\
\vdots &\vdots &\ddots &\vdots\\
\omega_{n1} & \omega_{n2}& ... &\omega_{nn} \\
\end{pmatrix}_{n\times n}\tag{7}$$
因爲直接求解W不方便,所以咱們引入隱變量V:
$$ V=
\begin{pmatrix}
v_{11} & v_{12}& ... &v_{1k} \\
v_{21} & v_{22}& ... &v_{2k} \\
\vdots &\vdots &\ddots &\vdots\\
v_{n1} & v_{n2}& ... &v_{nk} \\
\end{pmatrix}_{n\times k}=\begin{pmatrix}
V_1^T\\
V_2^T\\
\cdots \\
V_n^T\\
\end{pmatrix}\tag{8}$$
令
$$ VV^T = W\tag{9}$$
若是咱們先獲得V,則能夠獲得W了。
如今只剩下一個問題了,是否一個存在V,使得上述式(9)成立。
理論研究代表:當k足夠大時,對於任意對稱正定的實矩陣$W\in \mathbb R^{n \times n}$,均存在實矩陣$V\in \mathbb R^{n \times k}$,使得$W=VV^T$。
理論分析中要求參數k足夠的大,但在高度稀疏數據的場景中,因爲 沒有足夠的樣本,所以k一般取較小的值。事實上,對參數k的限制,在必定程度上能夠提升模型的泛化能力。
假設樣本中有n個特徵,每一個特徵對應的隱變量維度爲k,則參數個數爲1+n+nk。
正如上面所言,對於特徵維度足夠多的樣本,$k<<n$。
根據以上參數,列出FM的目標函數:
$$ f(x) = \omega_0+\sum_{i=1}^n\omega_ix_i+\sum_{i=1}^{n-1}\sum_{j=i+1}^n(V_i^TV_j)x_ix_j \tag{6} $$
FM有一個重要的性質:multilinearity。若記$\Theta=(\omega_0,\omega_1,\omega_2,...,\omega_n,v_{11},v_{12},...,v_{nk})$表示FM模型的全部參數,則對於任意的$\theta \in \Theta$,存在與$\theta$無關的$g(x)$與$h(x)$,使得式(6)能夠表示爲:
$$ f(x) = g(x) + \theta h(x) \tag{11} $$
從式(11)中能夠看出,若是咱們獲得了$g(x)$與$h(x)$,則對於參數$\theta$的梯度爲$h(x)$。下面咱們分狀況討論。
A. 當$\theta=\omega_0$時,式(6)能夠表示爲:
$$ f(x) = \color{blue}{\sum_{i=1}^n\omega_ix_i+\sum_{i=1}^{n-1}\sum_{j=i+1}^n(V_i^TV_j)x_ix_j} +\omega_0 \times \color{red}{1}\tag{12} $$
上述中的藍色表示$g(x)$,紅色表示$h(x)$。下同。
從上述式子能夠看出此時的梯度爲1.
B. 當$\theta=\omega_l, l \in (1,2,...,n)$時,
$$ f(x) = \color{blue}{\omega_0+\sum_{\substack{i=1 \\ i \ne l}}^n\omega_ix_i+\sum_{i=1}^{n-1}\sum_{j=i+1}^n(V_i^TV_j)x_ix_j}+\omega_l \times \color{red}{x_l} \tag{13} $$
此時梯度爲$x_l$。
C. 當$\theta=v_{lm}$時,
$$ f(x) =\color{blue}{ \omega_0+\sum_{i=1}^n\omega_ix_i+\sum_{i=1}^{n-1}\sum_{j=i+1}^n(\sum_{\substack{s=1 \\ is \ne lm \\js \ne lm}}^k v_{is}v_{js})x_ix_j }+v_{lm}\times \color{red}{x_l\sum_{\substack{i=1\\i \ne l }}^n v_{im}x_i} \tag{14}$$
此時梯度爲$x_l\sum_{i \ne l } v_{im}x_i$。
綜合上述結論,$f(x)$關於$\theta $的偏導數爲:
$$ \frac{\partial f(x)}{\partial \theta} =
\begin{cases}
1, & \theta=\omega_0 \\
x_l, & \theta=\omega_l, l \in (1,2,...,n) \\
x_l\sum_{\substack{i=1\\i \ne l }}^n v_{im}x_i & \theta=v_{lm}
\end{cases} \tag{15}$$
對於模型的計算時間複雜度,由(6)式,有:
$$ \begin{array}{l}
\sum\limits_{i = 1}^{n - 1} {\sum\limits_{j = i + 1}^n {(V_i^T{V_j})} } {x_i}{x_j}
\\= \frac{1}{2}\left( {\sum\limits_{i = 1}^n {\sum\limits_{j = 1}^n {(V_i^T{V_j})} } {x_i}{x_j} - \sum\limits_{i = 1}^n {(V_i^T{V_i})} {x_i}{x_i}} \right)
\\= \frac{1}{2}\left( {\sum\limits_{i = 1}^n {\sum\limits_{j = 1}^n {\sum\limits_{l = 1}^k {{v_{il}}} } } {v_{jl}}{x_i}{x_j} - \sum\limits_{i = 1}^n {\sum\limits_{l = 1}^k {v_{il}^2} } x_i^2} \right)
\\= \frac{1}{2}\sum\limits_{l = 1}^k {\left( {\sum\limits_{i = 1}^n {({v_{il}}{x_i})} \sum\limits_{j = 1}^n {({v_{jl}}{x_j})} - \sum\limits_{i = 1}^n {v_{il}^2} x_i^2} \right)}
\\= \frac{1}{2}\sum\limits_{l = 1}^k {\left( {{{\left( {\sum\limits_{i = 1}^n {({v_{il}}{x_i})} } \right)}^2} - \sum\limits_{i = 1}^n {v_{il}^2} x_i^2} \right)}
\end{array} \tag{10} $$
上述式子中的$\sum\limits_{i = 1}^n {({v_{il}}{x_i})}$只須要計算一次就好,所以,能夠看出上述模型的複雜度爲$O(kn)$。
也就是說咱們不要直接使用式(6)來計算預測結果,而應該使用式(10),這樣的計算效率更高。
對於訓練時間複雜度,由式(15)能夠獲得,
$$ x_l\sum_{\substack{i=1\\i \ne l }}^n v_{im}x_i = x_l\sum_{i=1}^n v_{im}x_i-v_{lm}x_l^2 \tag{16} $$
對於上式中的前半部分$\sum_{i=1}^n v_{im}x_i$,對於每一個樣本只須要計算一次,因此時間複雜度爲$O(n)$,對於k個隱變量的維度分別計算一次,則複雜度爲$O(kn)$。其它項的時間複雜度都小於這一項,所以,模型訓練的時間複雜度爲$O(kn)$。
具體地解釋,
(1)咱們首先計算$\sum_{i=1}^n v_{im}x_i$,時間複雜度爲n,這個值對於全部特徵對應的隱變量的某一個維度是相同的。咱們設這值爲C。
(2)計算每個特徵對應的$x_l\sum_{i=1}^n v_{im}x_i-v_{lm}x_l^2 =Cx_l-v_{lm}x_l^2$,因爲總共有n個特徵,所以時間複雜度爲n,至此,總的時間複雜度爲n+n。
(3)上述只是計算了隱變量的其中一個維度,咱們總共有k個維度,所以總的時間複雜度爲$k(n+n)=O(kn)k(n+n)=O(kn)$.
在FM模型中,每個特徵會對應一個隱變量,但在FFM模型中,認爲應該將特徵分爲多個field,每一個特徵對應每一個field分別有一個隱變量。
舉個例子,咱們的樣本有3種類型的字段:publisher, advertiser, gender,分別能夠表明媒體,廣告主或者是具體的商品,性別。其中publisher有5種數據,advertiser有10種數據,gender有男女2種,通過one-hot編碼之後,每一個樣本有17個特徵,其中只有3個特徵非空。
若是使用FM模型,則17個特徵,每一個特徵對應一個隱變量。
若是使用FFM模型,則17個特徵,每一個特徵對應3個隱變量,即每一個類型對應一個隱變量,具體而言,就是對應publisher, advertiser, gender三個field各有一個隱變量。
根據上面的描述,能夠得出FFM的模型爲:
$$ f(x) = \omega_0+\sum_{i=1}^n\omega_ix_i+\sum_{j1=1}^{n-1}\sum_{j2=i+1}^n(V_{j1,f2}^TV_{j2,f1})x_{j1}x_{j2} \tag{17}$$
其中$j1, j2$表示特徵的索引。咱們假設$j1$特徵屬於$f1$這個field,$j2$特徵屬於$f2$這個field,則$V_{j1,f2}$表示$j1$這個特徵對應$f2$($j2$所屬的field)的隱變量,同時$V_{j2,f1}$表示$j2$這個特徵對應$f1$($j1$所屬的field)的隱變量。
事實上,在大多數狀況下,FFM模型只保留了二次項,即:
$$ \phi(V,x) = \sum_{j1=1}^{n-1}\sum_{j2=i+1}^n(V_{j1,f2}^TV_{j2,f1})x_{j1}x_{j2} \tag{18} $$
根據邏輯迴歸的損失函數及分析,能夠得出FFM的最優化問題爲:
$$ \min \frac{\lambda}{2}||V||_2^2+\sum_{i=1}^{m}\log(1+exp(-y_i\phi(V,x))) \tag{19} $$
上面加號的前面部分使用了L2範式,後面部分是邏輯迴歸的損失函數。m表示樣本的數量,yiyi表示訓練樣本的真實值(如是否點擊的-1/1),$\phi(V,x)$表示使用當前的V代入式(18)計算獲得的值。
注意,以上的損失函數適用於樣本分佈爲{-1,1}的狀況。
與FTRL同樣,FFM也使用了累積梯度做爲自適應學習率的一部分,即:
$$ V_{j1,f2} = V_{j1,f2} - \frac{\eta}{\sqrt{1+\sum_t(g_{v_{j1,f2}}^t)^2}}g_{v_{j1,f2}} \tag{20} $$
其中$g_{v_{j1,f2}}$表示對於$V_{v_{j1,f2}}$這個變量的梯度向量,由於$V_{v_{j1,f2}}$是一個向量,所以$g_{v_{j1,f2}}$也是一個向量,尺寸爲隱變量的維度大小,即k。
而$\sum_t(g_{v_{j1,f2}}^t)^2$表示從第一個樣本到當前樣本一直以來的累積梯度平方和。
$$ (V_{j1,f2})_d=(V_{j1,f2})_{d-1}-\frac{\eta}{\sqrt{(G_{j1,f2})_d}} \cdot (g_{j1,f2})_d\\
(V_{j2,f1})_d=(V_{j2,f1})_{d-1}-\frac{\eta}{\sqrt{(G_{j2,f1})_d}} \cdot (g_{j2,f1})_d \tag{21} $$
其中$G$爲累積梯度平方和:
$$ (G_{j1,f2})_d=(G_{j1,f2})_{d-1}+(g_{j1,f2})_d^2 \\
(G_{j2,f1})_d=(G_{j2,f1})_{d-1}+(g_{j2,f1})_d^2 \tag{22} $$
$g$爲梯度,好比$g_{ji,f2}$爲$j1$這個特徵對應$f2$這個field的梯度向量:
$$ g_{ji,f2}=\lambda \cdot V_{ji,f2} + \kappa \cdot V_{j2,f1}\\
g_{j2,f1}=\lambda \cdot V_{j2,f1} + \kappa \cdot V_{j1,f2} \tag{23} $$
其中$\kappa$爲:
$$ \kappa = \frac{{\partial \log (1 + exp( - {y_i}\phi (V,x)))}}{{\partial \phi (V,x)}} = \frac{{ - y}}{{1 + \exp (y\phi (V,x))}} \tag{24} $$
$g$與$V$都是$k$維的向量,在python中能夠做爲一個向量計算,在java/c++等須要經過一個循環進行計算。
詳細推導(23)式以下:
(1)在SGD中,式(19)能夠轉化爲:
$$ \min \frac{\lambda}{2}||V||_2^2+\log(1+exp(-y_i\phi(V,x))) \tag{25} $$
(2)上式對$V_{j1,f2}$ 求偏導,可得:
$$ \begin{array}{l}
\frac{{\partial {\rm{\{ }}\frac{\lambda }{2}||V||_2^2 + \log (1 + exp( - {y_i}\phi (V,x))){\rm{\} }}}}{{\partial {V_{j1,f2}}}}\\
= \lambda \cdot {V_{j1,f2}} + \frac{{\partial \log (1 + exp( - {y_i}\phi (V,x)))}}{{\partial {V_{j1,f2}}}}\\
= \lambda \cdot {V_{j1,f2}} + \frac{{\partial \log (1 + exp( - {y_i}\phi (V,x)))}}{{\partial \phi }} \cdot \frac{{\partial \phi }}{{{V_{j1,f2}}}}\\
= \lambda \cdot {V_{j1,f2}} + \frac{{ - y}}{{1 + \exp (y\phi (V,x))}} \cdot {V_{j2,f1}}
\end{array} \tag{24} $$
對於計算時間複雜度,因爲式(18)沒法作相似於式(10)的簡化,所以FFM的計算時間複雜度爲$O(kn^2)$。
對於訓練時間複雜度,因爲訓練時,須要先根據式(18)計算$\phi$,複雜度爲$O(kn^2)$,計算獲得$\phi$後,還須要按照式(22)計算1次,按照式(21)計算$2k$次,按照式(23)計算$2k$次,按照式(24)計算$2k$次,也就是說,總的訓練時間複雜度爲:
$$O(kn^2) + 1 + 2k + 2k + 2k = O(kn^2) $$
所以,訓練時間複雜度爲$O(kn^2)$。
特徵歸一化、樣本歸一化。
Recurrent Entity Network簡稱EntNet,最初在論文TRACKING THE WORLD STATE WITH RECURRENT ENTITY NETWORKS中提出,文中給出了lua+torch的代碼地址。做者包括Facebook AI研究院的Mikael Henaff, Jason Weston(MemNN和MemN2N的做者)以及Yann LeCun.
Entity Network模型共分爲Input Encoder、Dynamic Memory和Output Model三個部分。以下圖的架構圖所示:
EntNet使用了動態長期記憶,所以能夠用在語言理解任務,QA等;
各個memory cell是獨立的,所以EntNet能夠看作是一系列共享權值的gated RNN。
輸入爲:
$$ {s_t} = \sum\limits_i {{f_i}} \odot {e_i} $$
輸入是一個固定長度的向量,用來表示一個sentence。$f_i$是須要學習的multiplicative mask, 使用這個mask的目的在於加入位置信息。 $e_i$是單詞的embedding表示。
Dynamic memory爲:
$$ \begin{array}{l}
{g_j} \leftarrow \sigma \left( {s_t^T{h_j} + s_t^T{w_j}} \right)\\
\widetilde {{h_j}} \leftarrow \phi \left( {U{h_j} + V{w_j} + W{s_t}} \right)\\
{h_j} \leftarrow {h_j} + {g_j} \odot \widetilde {{h_j}}\\
{h_j} \leftarrow \frac{{{h_j}}}{{\left\| {{h_j}} \right\|}}
\end{array} $$
輸出爲:
$$ \begin{array}{l}
{p_j} = soft\max \left( {{q^T}{h_j}} \right)\\
u = \sum\limits_j {{p_j}} {h_j}\\
y = R\phi \left( {q + Hu} \right)
\end{array} $$
首先要將用於FFM的特徵作處理,生成"feature index"和"feature value",用於FFM作特徵交叉。
feature_index是把全部特徵進行了標序,feature1,feature2......featurem,分別對應0,1,2,3,...m,可是,請注意分類變量須要拆分!
就是說若是有性別:男|女|未知,三個選項。須要構造feature男,feature女,feature未知三個變量,而連續變量就不須要這樣。
feature_value就是特徵的值,連續變量按真實值填寫,分類變量所有填寫1。
FFM模塊代碼實現以下(Tensorflow):
def ffm_module(self): # initial weights self.weights = dict() # feature_size * K self.weights["feature_embeddings"] = tf.Variable( tf.random_normal([self.feature_size, self.embedding_size], 0.0, 0.01), name="feature_embeddings") # feature_size * 1 self.weights["feature_bias"] = tf.Variable( tf.random_uniform([self.feature_size, 1], 0.0, 1.0), name="feature_bias") input_size = self.field_size * self.embedding_size glorot = np.sqrt(2.0 / (input_size + 1)) self.weights["use_fm_concat_weights"] = tf.Variable( np.random.normal(loc=0, scale=glorot, size=(self.field_size+self.embedding_size, self.num_classes)), dtype=np.float32) # model self.embeddings = tf.nn.embedding_lookup(self.weights["feature_embeddings"], self.feat_index) # None * F * K feat_value = tf.reshape(self.feat_value, shape=[-1, self.field_size, 1]) self.embeddings = tf.multiply(self.embeddings, feat_value) # ---------- first order term ---------- self.y_first_order = tf.nn.embedding_lookup(self.weights["feature_bias"], self.feat_index) # None * F * 1 self.y_first_order = tf.reduce_sum(tf.multiply(self.y_first_order, feat_value), 2) # None * F self.y_first_order = tf.nn.dropout(self.y_first_order, self.dropout_keep_fm[0]) # None * F # ---------- second order term --------------- # sum_square part self.summed_features_emb = tf.reduce_sum(self.embeddings, 1) # None * K self.summed_features_emb_square = tf.square(self.summed_features_emb) # None * K # square_sum part self.squared_features_emb = tf.square(self.embeddings) self.squared_sum_features_emb = tf.reduce_sum(self.squared_features_emb, 1) # None * K # second order self.y_second_order = 0.5 * tf.subtract(self.summed_features_emb_square, self.squared_sum_features_emb) # None * K self.y_second_order = tf.nn.dropout(self.y_second_order, self.dropout_keep_fm[1]) # None * K self.ffm_y = tf.concat([self.y_first_order, self.y_second_order], axis=1) self.ffm_y = tf.reshape(self.ffm_y, shape=[self.batch_size, self.field_size+self.embedding_size]) self.ffm_y = tf.nn.dropout(self.activation(self.ffm_y), self.dropout_keep_fm[2]) self.ffm_y = tf.matmul(self.ffm_y, self.weights["use_fm_concat_weights"])
EntityNet的核心代碼:
def rnn_story(self): """ run rnn for story to get last hidden state input is: story: [batch_size,story_length,embed_size] :return: last hidden state. [batch_size,embed_size] """ # 1.split input to get lists. input_split=tf.split(self.story_embedding,self.story_length,axis=1) #a list.length is:story_length.each element is:[batch_size,1,embed_size] input_list=[tf.squeeze(x,axis=1) for x in input_split] #a list.length is:story_length.each element is:[batch_size,embed_size] # 2.init keys(w_all) and values(h_all) of memory h_all=tf.get_variable("hidden_states",shape=[self.block_size,self.dimension],initializer=self.initializer)# [block_size,hidden_size] w_all=tf.get_variable("keys", shape=[self.block_size,self.dimension],initializer=self.initializer)# [block_size,hidden_size] # 3.expand keys and values to prepare operation of rnn w_all_expand=tf.tile(tf.expand_dims(w_all,axis=0),[self.batch_size,1,1]) #[batch_size,block_size,hidden_size] h_all_expand=tf.tile(tf.expand_dims(h_all,axis=0),[self.batch_size,1,1]) #[batch_size,block_size,hidden_size] # 4. run rnn using input with cell. for i,input in enumerate(input_list): h_all_expand=self.cell(input,h_all_expand,w_all_expand,i) #w_all:[batch_size,block_size,hidden_size]; h_all:[batch_size,block_size,hidden_size] return h_all_expand #[batch_size,block_size,hidden_size] def cell(self,s_t,h_all,w_all,i): """ parallel implementation of single time step for compute of input with memory :param s_t: [batch_size,hidden_size].vector representation of current input(is a sentence).notice:hidden_size=embedding_size :param w_all: [batch_size,block_size,hidden_size] :param h_all: [batch_size,block_size,hidden_size] :return: new hidden state: [batch_size,block_size,hidden_size] """ # 1.gate s_t_expand=tf.expand_dims(s_t, axis=1) #[batch_size,1,hidden_size] g=tf.nn.sigmoid(tf.multiply(s_t_expand,h_all)+tf.multiply(s_t_expand,w_all))#shape:[batch_size,block_size,hidden_size] # 2.candidate hidden state #below' shape:[batch_size*block_size,hidden_size] h_candidate_part1=tf.matmul(tf.reshape(h_all,shape=(-1,self.dimension)), self.U) + tf.matmul(tf.reshape(w_all,shape=(-1,self.dimension)), self.V)+self.h_bias # print("======>h_candidate_part1:",h_candidate_part1) #(160, 100) h_candidate_part1=tf.reshape(h_candidate_part1,shape=(self.batch_size,self.block_size,self.dimension)) #[batch_size,block_size,hidden_size] h_candidate_part2=tf.expand_dims(tf.matmul(s_t,self.W)+self.h2_bias,axis=1) #shape:[batch_size,1,hidden_size] h_candidate=self.activation(h_candidate_part1+h_candidate_part2,scope="h_candidate"+str(i)) #shape:[batch_size,block_size,hidden_size] # 3.update hidden state h_all=h_all+tf.multiply(g,h_candidate) #shape:[batch_size,block_size,hidden_size] # 4.normalized hidden state h_all=tf.nn.l2_normalize(h_all,-1) #shape:[batch_size,block_size,hidden_size] return h_all #shape:[batch_size,block_size,hidden_size]
將二者結合起來:
def output_module(self): """ 1.use attention mechanism between query and hidden states, to get weighted sum of hidden state. 2.non-linearity of query and hidden state to get label. input: query_embedding:[batch_size,embed_size], hidden state:[batch_size,block_size,hidden_size] of memory :return:y: predicted label.[] """ # 1.use attention mechanism between query and hidden states, to get weighted sum of hidden state. # 1.1 get possibility distribution (of similiarity) p = tf.nn.softmax(tf.multiply(tf.expand_dims(self.query_embedding, axis=1), self.hidden_state)) #shape:[batch_size,block_size,hidden_size]<---query_embedding_expand:[batch_size,1,hidden_size]; hidden_state:[batch_size,block_size,hidden_size] # 1.2 get weighted sum of hidden state u = tf.reduce_sum(tf.multiply(p, self.hidden_state), axis=1) # shape:[batch_size,hidden_size]<----------([batch_size,block_size,hidden_size],[batch_size,block_size,hidden_size]) # 2.non-linearity of query and hidden state to get label H_u_matmul = tf.matmul(u, self.H) + self.h_u_bias # shape:[batch_size,hidden_size]<----([batch_size,hidden_size],[hidden_size,hidden_size]) activation = self.activation(self.query_embedding + H_u_matmul, scope="query_add_hidden") #shape:[batch_size,hidden_size] activation = tf.nn.dropout(activation, keep_prob=self.dropout_keep_prob) #shape:[batch_size,hidden_size] y = tf.matmul(activation, self.R) + self.y_bias # shape:[batch_size,vocab_size]<-----([batch_size,hidden_size],[hidden_size,vocab_size]) # FFM layer if self.use_fm: self.ffm_module() y = tf.add(y, self.ffm_y) return y # shape:[batch_size,vocab_size]
詳細demo參考:playground_ffm_entitynet.zip。
參考文章:
1. https://www.jianshu.com/p/71d819005fed
2. https://blog.csdn.net/Irving_zhang/article/details/79204426
3. https://blog.csdn.net/jediael_lu/article/details/77772565