好久没有写总结了,这篇博客仅做为最近的一些尝试内容,记录一些心得。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