zoukankan      html  css  js  c++  java
  • Deep Learning专栏--FFM+Recurrent Entity Network的端到端方案

    很久没有写总结了,这篇博客仅作为最近的一些尝试内容,记录一些心得。FFM的优势是可以处理高维稀疏样本的特征组合,已经在无数的CTR预估比赛和工业界中广泛应用,此外,其也可以与Deep Networks结合(如DeepFM等工作),很好地应用在数据规模足够大的工业场景中。Recurrent Entity Network是facebook AI在2017年的ICLR会议上发表的,文章提出了Recurrent Entity Network的模型用来对world state进行建模,根据模型的输入对记忆单元进行实时的更新,从而得到对world的一个即时的认识。该模型可以用于机器阅读理解、QA等领域。

    如果我们希望做一个关于豆瓣电影的QA机器人,我们有每个电影的剧情文本介绍和豆瓣下面的评论,也有每个电影的特征(如其导演、演员、获奖情况、拍摄年份等),那么利用FFM对电影特征进行Embedding向量化,再利用Deep Networks对电影剧情文本和评论文本进行向量化,两者融合起来,岂不是可以更好地回答提问者的问题?而Entity Networks通过memory机制存储文本上下文信息,能够更好地捕获前后信息,抓住实体关系,在QA领域会更加游刃有余。

    1. 从线性模型到FFM

    1.1 线性模型

    常见的线性模型,比如线性回归、逻辑回归等,它只考虑了每个特征对结果的单独影响,而没有考虑特征间的组合对结果的影响。

    对于一个有n维特征的模型,线性回归的形式如下:

    $$  f(x) = omega_0 + omega_1x_1+omega_2x_2+...+omega_nx_n =omega_0+sum_{i=1}^n{omega_ix_i} ag{1}  $$

    其中$(omega_0,omega_1...omega_n)$为模型参数,$(x_1,x_2...x_n)$为特征。
    从(1)式可以看出来,模型的最终计算结果是各个特征的独立计算结果,并没有考虑特征之间的相互关系。
    举个例子,我们“USA”与”Thanksgiving”,”China”与“Chinese new year”这样的组合特征是很有意义的,在这样的组合特征下,会对某些商品表现出更强的购买意愿,而单独考虑国家及节日都是没有意义的。

    1.2 二项式模型

    我们在(1)式的基础上,考虑任意2个特征分量之间的关系,得出以下模型: 

    $$ f(x)=omega_0+sum_{i=1}^nomega_ix_i+sum_{i=1}^{n-1}sum_{j=i+1}^nomega_{ij}x_ix_j ag{2} $$

    这个模型考虑了任意2个特征分量之间的关系,但并未考虑更高阶的关系。 
    模型涉及的参数数量为: 

    $$ 1+n+frac{n(n-1)}{2}=frac{1}{2}(n^2+n+2) ag{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}$不能得到足够的训练,从而不准确。

    1.3 FM算法

    1.3.1 FM基本原理

    为了解决上述由于数据稀疏引起的训练不足的问题,我们为每个特征维度$x_i$引入一个辅助向量:

    $$ V_i = (v_{i1},v_{i2},v_{i3},...,v_{ik})^Tin mathbb R^k, i=1,2,3,...,n ag{4} $$

    其中$k$为辅助变量的维度,依经验而定,一般而言,对于特征维度足够多的样本,$k<<n$。

    1.3.2 模型及目标函数

    目标是要求得以下交互矩阵W:

    $$ W=
    egin{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 imes n} ag{7}$$

    由于直接求解W不方便,因此我们引入隐变量V: 

    $$ V=
    egin{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 imes k}=egin{pmatrix}
    V_1^T\
    V_2^T\
    cdots \
    V_n^T\
    end{pmatrix} ag{8}$$

    $$ VV^T = W ag{9}$$

    如果我们先得到V,则可以得到W了。
    现在只剩下一个问题了,是否一个存在V,使得上述式(9)成立。
    理论研究表明:当k足够大时,对于任意对称正定的实矩阵$Win mathbb R^{n imes  n}$,均存在实矩阵$Vin mathbb R^{n imes  k}$,使得$W=VV^T$。
    理论分析中要求参数k足够的大,但在高度稀疏数据的场景中,由于 没有足够的样本,因此k通常取较小的值。事实上,对参数k的限制,在一定程度上可以提高模型的泛化能力。

    假设样本中有n个特征,每个特征对应的隐变量维度为k,则参数个数为1+n+nk。 
    正如上面所言,对于特征维度足够多的样本,$k<<n$。

    根据以上参数,列出FM的目标函数:

    $$ f(x) = omega_0+sum_{i=1}^nomega_ix_i+sum_{i=1}^{n-1}sum_{j=i+1}^n(V_i^TV_j)x_ix_j ag{6}  $$

    1.3.3 计算梯度

    FM有一个重要的性质:multilinearity。若记$Theta=(omega_0,omega_1,omega_2,...,omega_n,v_{11},v_{12},...,v_{nk})$表示FM模型的所有参数,则对于任意的$ heta in Theta$,存在与$ heta$无关的$g(x)$与$h(x)$,使得式(6)可以表示为:

    $$ f(x) = g(x) + heta h(x) ag{11} $$

    从式(11)中可以看出,如果我们得到了$g(x)$与$h(x)$,则对于参数$ heta$的梯度为$h(x)$。下面我们分情况讨论。

    A. 当$ heta=omega_0$时,式(6)可以表示为:

    $$ f(x) = color{blue}{sum_{i=1}^nomega_ix_i+sum_{i=1}^{n-1}sum_{j=i+1}^n(V_i^TV_j)x_ix_j} +omega_0 imes color{red}{1} ag{12} $$

    上述中的蓝色表示$g(x)$,红色表示$h(x)$。下同。

    从上述式子可以看出此时的梯度为1.

    B. 当$ heta=omega_l, l in (1,2,...,n)$时,

    $$ f(x) = color{blue}{omega_0+sum_{substack{i=1 \ i e l}}^nomega_ix_i+sum_{i=1}^{n-1}sum_{j=i+1}^n(V_i^TV_j)x_ix_j}+omega_l imes color{red}{x_l} ag{13} $$

    此时梯度为$x_l$。

    C. 当$ heta=v_{lm}$时,

    $$ f(x) =color{blue}{ omega_0+sum_{i=1}^nomega_ix_i+sum_{i=1}^{n-1}sum_{j=i+1}^n(sum_{substack{s=1 \ is e lm \js e lm}}^k v_{is}v_{js})x_ix_j }+v_{lm} imes color{red}{x_lsum_{substack{i=1\i e l }}^n v_{im}x_i} ag{14}$$

    此时梯度为$x_lsum_{i e l } v_{im}x_i$。

    综合上述结论,$f(x)$关于$ heta $的偏导数为:

    $$ frac{partial f(x)}{partial heta} =
    egin{cases}
    1, & heta=omega_0 \
    x_l, & heta=omega_l, l in (1,2,...,n) \
    x_lsum_{substack{i=1\i e l }}^n v_{im}x_i & heta=v_{lm}
    end{cases} ag{15}$$

    1.3.4 时间复杂度

    对于模型的计算时间复杂度,由(6)式,有:

    $$ egin{array}{l}
    sumlimits_{i = 1}^{n - 1} {sumlimits_{j = i + 1}^n {(V_i^T{V_j})} } {x_i}{x_j}
    \= frac{1}{2}left( {sumlimits_{i = 1}^n {sumlimits_{j = 1}^n {(V_i^T{V_j})} } {x_i}{x_j} - sumlimits_{i = 1}^n {(V_i^T{V_i})} {x_i}{x_i}} ight)
    \= frac{1}{2}left( {sumlimits_{i = 1}^n {sumlimits_{j = 1}^n {sumlimits_{l = 1}^k {{v_{il}}} } } {v_{jl}}{x_i}{x_j} - sumlimits_{i = 1}^n {sumlimits_{l = 1}^k {v_{il}^2} } x_i^2} ight)
    \= frac{1}{2}sumlimits_{l = 1}^k {left( {sumlimits_{i = 1}^n {({v_{il}}{x_i})} sumlimits_{j = 1}^n {({v_{jl}}{x_j})} - sumlimits_{i = 1}^n {v_{il}^2} x_i^2} ight)}
    \= frac{1}{2}sumlimits_{l = 1}^k {left( {{{left( {sumlimits_{i = 1}^n {({v_{il}}{x_i})} } ight)}^2} - sumlimits_{i = 1}^n {v_{il}^2} x_i^2} ight)}
    end{array} ag{10} $$

    上述式子中的$sumlimits_{i = 1}^n {({v_{il}}{x_i})}$只需要计算一次就好,因此,可以看出上述模型的复杂度为$O(kn)$。
    也就是说我们不要直接使用式(6)来计算预测结果,而应该使用式(10),这样的计算效率更高。

    对于训练时间复杂度,由式(15)可以得到,

    $$ x_lsum_{substack{i=1\i e l }}^n v_{im}x_i = x_lsum_{i=1}^n v_{im}x_i-v_{lm}x_l^2 ag{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_lsum_{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)$.

    1.4 FFM算法

    1.4.1 FFM基本原理

    在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各有一个隐变量。

    1.4.2 模型及目标函数

    根据上面的描述,可以得出FFM的模型为:

    $$ f(x) = omega_0+sum_{i=1}^nomega_ix_i+sum_{j1=1}^{n-1}sum_{j2=i+1}^n(V_{j1,f2}^TV_{j2,f1})x_{j1}x_{j2} ag{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} ag{18} $$

    根据逻辑回归的损失函数及分析,可以得出FFM的最优化问题为:

    $$ min frac{lambda}{2}||V||_2^2+sum_{i=1}^{m}log(1+exp(-y_iphi(V,x))) ag{19} $$

    上面加号的前面部分使用了L2范式,后面部分是逻辑回归的损失函数。m表示样本的数量,yiyi表示训练样本的真实值(如是否点击的-1/1),$phi(V,x)$表示使用当前的V代入式(18)计算得到的值。

    注意,以上的损失函数适用于样本分布为{-1,1}的情况。

    1.4.3 完整算法流程

    与FTRL一样,FFM也使用了累积梯度作为自适应学习率的一部分,即:

    $$ V_{j1,f2} = V_{j1,f2} - frac{eta}{sqrt{1+sum_t(g_{v_{j1,f2}}^t)^2}}g_{v_{j1,f2}} ag{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 ag{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 ag{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} ag{23} $$

    其中$kappa$为:

    $$ kappa = frac{{partial log (1 + exp( - {y_i}phi (V,x)))}}{{partial phi (V,x)}} = frac{{ - y}}{{1 + exp (yphi (V,x))}} ag{24} $$

    $g$与$V$都是$k$维的向量,在python中可以作为一个向量计算,在java/c++等需要通过一个循环进行计算。

    详细推导(23)式如下:
    (1)在SGD中,式(19)可以转化为:

    $$ min frac{lambda}{2}||V||_2^2+log(1+exp(-y_iphi(V,x))) ag{25} $$

    (2)上式对$V_{j1,f2}$ 求偏导,可得:

    $$ egin{array}{l}
    frac{{partial { m{{ }}frac{lambda }{2}||V||_2^2 + log (1 + exp( - {y_i}phi (V,x))){ m{} }}}}{{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 (yphi (V,x))}} cdot {V_{j2,f1}}
    end{array} ag{24} $$

    1.4.4 时间复杂度

    对于计算时间复杂度,由于式(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)$。

    1.4.5 模型优化

     特征归一化、样本归一化。

    2. Recurrent Entity Network原理介绍

     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} = sumlimits_i {{f_i}}  odot {e_i} $$

    输入是一个固定长度的向量,用来表示一个sentence。$f_i$是需要学习的multiplicative mask, 使用这个mask的目的在于加入位置信息。 $e_i$是单词的embedding表示。

    Dynamic memory为:

    $$ egin{array}{l}
    {g_j} leftarrow sigma left( {s_t^T{h_j} + s_t^T{w_j}} ight)\
    widetilde {{h_j}} leftarrow phi left( {U{h_j} + V{w_j} + W{s_t}} ight)\
    {h_j} leftarrow {h_j} + {g_j} odot widetilde {{h_j}}\
    {h_j} leftarrow frac{{{h_j}}}{{left| {{h_j}} ight|}}
    end{array} $$

    输出为:

    $$ egin{array}{l}
    {p_j} = softmax left( {{q^T}{h_j}} ight)\
    u = sumlimits_j {{p_j}} {h_j}\
    y = Rphi left( {q + Hu} ight)
    end{array} $$

    3. FFM + Recurrent Entity Network

     首先要将用于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

  • 相关阅读:
    Linux权限
    Linux用户和用户组操作
    input输入框美化
    Ajax原理一篇就够了
    CSS样式----浮动(图文详解)
    linx系统操作
    文件打包,压缩,解包,解压缩
    Linux学习笔记(一)
    ios 11导航栏替换返回按钮图片,隐藏文字
    swift开发笔记23 BirthDays
  • 原文地址:https://www.cnblogs.com/shixiangwan/p/10843690.html
Copyright © 2011-2022 走看看