【KGAT】Knowledge Graph Attention Network for Recommendation

news2024/12/25 9:01:59

note

  • 其实不结合KG,何向南团队之前也直接使用GCN做了NGCF和LightGCN。
  • KGAT结合KG和GAT,首先是CKG嵌入表示层使用TransR模型获得实体和关系的embedding;然后在attention表示传播层,使用attention求出每个邻居节点的贡献权重,需要把实体节点 h h h自身的嵌入表示 e h e_h eh和它基于邻域的嵌入表示 e N h \boldsymbol{e}_{\mathcal{N}_h} eNh融合起来,得到节点 h h h的新表示 e h ( 1 ) \boldsymbol{e}_h^{(1)} eh(1),这里的融合方法也有三种;最后的预测评估层就是用户向量和物品向量的点积结果,和真实label进行交叉熵损失函数计算,优化权重。

文章目录

  • note
  • 零、论文
  • 一、论文动机
    • 1.1 motivation
    • 1.2 图采样
    • 1.3 回顾以前的GAT网络
  • 二、KGAT模型
    • 2.1 CKG嵌入表示层
    • 2.2 注意力感知的表示传播层
    • 2.3 预测评估层
  • 三、代码
  • 四、实验结果
  • Reference

零、论文

在这里插入图片描述

一、论文动机

1.1 motivation

传统的有监督学习方法中,例如因子分解机,在抽取出样本的属性特征后,把每个样本视作一个独立的事件来预测,而忽视了样本之间内在的关系。知识图谱可以把样本之间通过属性关联起来,使得样本之间不再独立预测。KGAT基于三个基准数据实验,优于Neural FM和RippleNet等。

KGAT把用户-物品的交互二分图和知识图谱融合在一起——协同知识图CKG,即将图谱关系、用户user和item的二部交互图融合到一个图空间,以融合CF信息和KG信息,KKG也能发现更高阶的关系信息。在协同知识图 G \mathcal{G} G中:

  • 节点包含实体、用户和物品;
  • 关系包含原来知识图谱的关系外加一个反应用户-物品的交互关系。

在这里插入图片描述

1.2 图采样

获得邻居的节点embedding和关系embedding,使用get_neighbors函数,通过物品id得到其邻居及连接他们的关系的id,然后通过邻居和关系id得到邻居实体与关系的embedding的方法,而中间的adj_entityadj_Relation分别是图采样后得到的指定度数的邻接列表。

 # 得到邻居的节点embedding和关系embedding
 def get_neighbors( self, items ):
     e_ids = [self.adj_entity[ item ] for item in items ]
     r_ids = [ self.adj_relation[ item ] for item in items ]
     e_ids = torch.LongTensor( e_ids )
     r_ids = torch.LongTensor( r_ids )
     neighbor_entities_embs = self.entity_embs( e_ids )
     neighbor_relations_embs = self.relation_embs( r_ids )
     return neighbor_entities_embs, neighbor_relations_embs

adj_entityadj_Relation即代码中的dataloader4KGNN.construct_adj函数返回值(通过kg邻接列表得到实体邻接列表和关系邻接列表),后面在消息传递时需要通过该邻接矩阵,获得当前节点的邻居节点embedding和关系embedding。

# 根据kg邻接列表,得到实体邻接列表和关系邻接列表
def construct_adj( neighbor_sample_size, kg_indexes, entity_num ):
    print('生成实体邻接列表和关系邻接列表')
    adj_entity = np.zeros([ entity_num, neighbor_sample_size ], dtype = np.int64 )
    adj_relation = np.zeros([ entity_num, neighbor_sample_size ], dtype = np.int64 )
    for entity in range( entity_num ):
        neighbors = kg_indexes[ str( entity ) ]
        n_neighbors = len( neighbors )
        if n_neighbors >= neighbor_sample_size:
            sampled_indices = np.random.choice( list( range( n_neighbors ) ),
                                                size = neighbor_sample_size, replace = False )
        else:
            sampled_indices = np.random.choice( list( range( n_neighbors ) ),
                                                size = neighbor_sample_size, replace = True )
        adj_entity[ entity ] = np.array( [ neighbors[i][0] for i in sampled_indices ] )
        adj_relation[ entity]  = np.array( [ neighbors[i][1] for i in sampled_indices ] )
    return adj_entity, adj_relation

1.3 回顾以前的GAT网络

这里的GAT我们为了简单起见使用dgl里面的dglnn.GATConv层,这里我们做的节点分类的任务。图注意神经网络(GAT)来源于论文 Graph Attention Networks。其数学定义为,
x i ′ = α i , i Θ x i + ∑ j ∈ N ( i ) α i , j Θ x j , \mathbf{x}^{\prime}_i = \alpha_{i,i}\mathbf{\Theta}\mathbf{x}_{i} + \sum_{j \in \mathcal{N}(i)} \alpha_{i,j}\mathbf{\Theta}\mathbf{x}_{j}, xi=αi,iΘxi+jN(i)αi,jΘxj,
GAT和所有的attention mechanism一样,GAT的计算也分为两步走:
(1)计算注意力系数(attention coefficient):(下图来自《GRAPH ATTENTION NETWORKS》)。其中注意力系数 α i , j \alpha_{i,j} αi,j的计算方法为,
α i , j = exp ⁡ ( L e a k y R e L U ( a ⊤ [ Θ x i   ∥   Θ x j ] ) ) ∑ k ∈ N ( i ) ∪ { i } exp ⁡ ( L e a k y R e L U ( a ⊤ [ Θ x i   ∥   Θ x k ] ) ) . \alpha_{i,j} = \frac{ \exp\left(\mathrm{LeakyReLU}\left(\mathbf{a}^{\top} [\mathbf{\Theta}\mathbf{x}_i \, \Vert \, \mathbf{\Theta}\mathbf{x}_j] \right)\right)} {\sum_{k \in \mathcal{N}(i) \cup \{ i \}} \exp\left(\mathrm{LeakyReLU}\left(\mathbf{a}^{\top} [\mathbf{\Theta}\mathbf{x}_i \, \Vert \, \mathbf{\Theta}\mathbf{x}_k] \right)\right)}. αi,j=kN(i){i}exp(LeakyReLU(a[ΘxiΘxk]))exp(LeakyReLU(a[ΘxiΘxj])).
在这里插入图片描述

(2)加权求和(aggregate):根据(1)的系数,把特征加权求和(aggregate)

class GAT(nn.Module):
    def __init__(self,in_size, hid_size, out_size, heads):
        super().__init__()
        self.gat_layers = nn.ModuleList()
        # two-layer GAT(attention)
        self.gat_layers.append(dglnn.GATConv(in_size, hid_size, heads[0], feat_drop=0.6, attn_drop=0.6, activation=F.elu))
        # GATConv: in_feat, out_feat, num_head(multi-head)
        self.gat_layers.append(dglnn.GATConv(hid_size*heads[0], out_size, heads[1], feat_drop=0.6, attn_drop=0.6, activation=None))
        
    def forward(self, g, inputs):
        h = inputs
        for i, layer in enumerate(self.gat_layers):
            h = layer(g, h)
            if i == 1:  # last layer 
                h = h.mean(1)
            else:       # other layer(s)
                h = h.flatten(1)
        return h

使用交叉熵损失函数,在dgl中我们使用train_mask表示出在训练集的节点,如果对应位置的train_mask为false则表示不会该节点不会出现在训练集。

二、KGAT模型

在这里插入图片描述

2.1 CKG嵌入表示层

该层为了得到知识图结构的实体和关系的嵌入表示,使用TransR模型,TransR模型使得三元组(h, r, t)在关系r的投影平面上有平移关系: g ( h , r 1 , t ) = ∥ W r e h + e r − W r e t ∥ 2 2 g\left(h, r_1, t\right)=\left\|\boldsymbol{W}_r \boldsymbol{e}_h+\boldsymbol{e}_{\mathrm{r}}-\boldsymbol{W}_r \boldsymbol{e}_t\right\|_2^2 g(h,r1,t)=Wreh+erWret22其中:

  • W r ∈ R k × d \boldsymbol{W}_r \in \mathbb{R}^{k \times d} WrRk×d 是关系 r r r 的变换矩阵,将实体从d维实体空间投影到k维关系空间中;
  • g ( h , r , t ) g(h, r, t) g(h,r,t) 分数越小, 表示三元组 ( h , r , t ) (h, r, t) (h,r,t) 成立的概率越大,该三元组越可信。

使用pairwise ranking loss作为训练损失函数:
L K G = ∑ ⟨ h , r , t , t ′ ⟩ ∈ T − log ⁡ σ ( g ( h , r , t ′ ) − g ( h , r , t ) ) \mathcal{L}_{\mathrm{KG}}=\sum_{\left\langle h, r, t, t^{\prime}\right\rangle \in \mathcal{T}}-\log \sigma\left(g\left(h, r, t^{\prime}\right)-g(h, r, t)\right) LKG=h,r,t,tTlogσ(g(h,r,t)g(h,r,t))
其中:

  • T = { ( h , r , t , t ′ ) ∣ ( h , r , t ) ∈ G , ( h , r , t ′ ) ∉ G } \mathcal{T}=\left\{\left(h, r, t, t^{\prime}\right) \mid(h, r, t) \in \mathcal{G},\left(h, r, t^{\prime}\right) \notin \mathcal{G}\right\} T={(h,r,t,t)(h,r,t)G,(h,r,t)/G} ;
  • ( h , r , t ′ ) \left(h, r, t^{\prime}\right) (h,r,t) 是负例三元组, 可以通过把正常的三元组中的尾实体替换掉得来;
  • σ \sigma σ 是 Sigmoid 函数。

2.2 注意力感知的表示传播层

通过层迭代的形式吸收图上高阶的邻域信息,同时通过GAT把重要信息保存,忽略噪声信息。

先考虑一层传播的操作过程:给定一个头节点 h h h, 令 N h = \mathcal{N}_h= Nh= { ( h , r , t ) ∣ ( h , r , t ) ∈ G } \{(h, r, t) \mid(h, r, t) \in \mathcal{G}\} {(h,r,t)(h,r,t)G} ,表示以它起始的所有三元组的集合,hrt分别为头实体向量、关系向量、尾实体向量。那么节点 h h h 在图上的一阶邻域向量表示:
e N h = ∑ ( h , r , t ) ∈ N h π ( h , r , t ) e t \boldsymbol{e}_{\mathcal{N}_h}=\sum_{(h, r, t) \in N_h} \pi(h, r, t) \boldsymbol{e}_t eNh=(h,r,t)Nhπ(h,r,t)et
其中:

  • π ( h , r , t ) \pi(h, r, t) π(h,r,t) 反映了三元组对 h h h 的一阶邻域表示的重要程度, 也控制了有多少程度的信息从尾节点 t t t传播过来。
  • π ( h , r , t ) \pi(h, r, t) π(h,r,t)的计算方法: π ^ ( h , r , t ) = ( W r e t ) ⊤ tanh ⁡ ( W r e h + e r ) π ( h , r , t ) = exp ⁡ ( π ^ ( h , r , t ) ) ∑ ( h , r ′ , t ′ ) ∈ N h exp ⁡ ( π ^ ( h , r ′ , t ′ ) ) \begin{aligned} & \hat{\pi}(h, r, t)=\left(W_{\mathrm{r}} e_t\right)^{\top} \tanh \left(W_r e_h+e_r\right) \\ & \pi(h, r, t)=\frac{\exp (\hat{\pi}(h, r, t))}{\sum_{\left(h, r^{\prime}, t^{\prime}\right) \in N_h} \exp \left(\hat{\pi}\left(h, r^{\prime}, t^{\prime}\right)\right)} \end{aligned} π^(h,r,t)=(Wret)tanh(Wreh+er)π(h,r,t)=(h,r,t)Nhexp(π^(h,r,t))exp(π^(h,r,t))

对应的一层传播代码:

# GAT消息传递
def GATMessagePass( self, h_embs, r_embs, t_embs ):
    '''
    :param h_embs: 头实体向量[ batch_size, e_dim ]
    :param r_embs: 关系向量[ batch_size, n_neibours, r_dim ]
    :param t_embs: 尾实体向量[ batch_size, n_neibours, e_dim ]
    '''
    # # 将h张量广播,维度扩散为 [ batch_size, n_neibours, e_dim ]
    h_broadcast_embs = torch.cat( [ torch.unsqueeze( h_embs, 1 ) for _ in range( t_embs.shape[ 1 ] ) ], dim = 1 )
    # [ batch_size, n_neibours, r_dim ]
    tr_embs = self.Wr( t_embs )
    # [ batch_size, n_neibours, r_dim ]
    hr_embs = self.Wr( h_broadcast_embs )
    # [ batch_size, n_neibours, r_dim ]
    hr_embs= torch.tanh( hr_embs + r_embs)
    # [ batch_size, n_neibours, 1 ]
    atten = torch.sum( hr_embs * tr_embs,dim = -1 ,keepdim=True)
    atten = torch.softmax( atten, dim = -1 )
    # [ batch_size, n_neibours, e_dim ]
    t_embs = t_embs * atten
    # [ batch_size, e_dim ]
    return  torch.sum( t_embs, dim = 1 )

最后:需要把实体节点 h h h自身的嵌入表示 e h e_h eh和它基于邻域的嵌入表示 e N h \boldsymbol{e}_{\mathcal{N}_h} eNh融合起来,得到节点 h h h的新表示 e h ( 1 ) \boldsymbol{e}_h^{(1)} eh(1)。融合的方式有三种选择:

  • GCN聚合方法:将2个向量相加,然后经过一层非线性变换层: f C C N = LeakyReLU ⁡ ( W ( e h + ϵ N h ) ) f_{\mathrm{CCN}}=\operatorname{LeakyReLU}\left(W\left(e_{\mathrm{h}}+\epsilon_{\mathcal{N}_{\mathrm{h}}}\right)\right) fCCN=LeakyReLU(W(eh+ϵNh))
  • GraphSage聚合方法:将拼接2个向量,然后经过一层非线性变换层: f GriphtSage  =  LeakyReLU  ( W ( e h ∥ e N h ) ) f_{\text {GriphtSage }}=\text { LeakyReLU }\left(\boldsymbol{W}\left(e_h \| e_{\mathcal{N}_h}\right)\right) fGriphtSage = LeakyReLU (W(eheNh))
  • 二重交互聚合方法:考虑向量的两种交互方式——向量相加和向量的按位点积操作 ⊙ \odot ,再经过一层非线性变换层: f Bi-Interiction  = LeakyReLU ⁡ ( W 1 ( e h + e N h ) ) + LeakyReLU ⁡ ( W 2 ( e h ⊙ e N h ) ) f_{\text {Bi-Interiction }}=\operatorname{LeakyReLU}\left(W_1\left(e_h+e_{\mathcal{N}_h}\right)\right)+\operatorname{LeakyReLU}\left(W_2\left(e_h \odot e_{N_h}\right)\right) fBi-Interiction =LeakyReLU(W1(eh+eNh))+LeakyReLU(W2(eheNh))

以上是注意力感知的表示传播层操作;若要考虑更高阶的信息,可以重复堆叠多次: e h ( l ) = f ( e l t { l − 1 ) , e N k ( l − 1 ) ) e_h^{(l)}=f\left(e_{l_t}^{\{l-1)}, e_{N_k}^{(l-1)}\right) eh(l)=f(elt{l1),eNk(l1))

上面的三种融合(聚合)方法对应代码:

# 消息聚合
def aggregate( self, h_embs, Nh_embs, agg_method = 'Bi-Interaction' ):
    '''
    :param h_embs: 原始的头实体向量 [ batch_size, e_dim ]
    :param Nh_embs: 消息传递后头实体位置的向量 [ batch_size, e_dim ]
    :param agg_method: 聚合方式,总共有三种,分别是'Bi-Interaction','concat','sum'
    '''
    if agg_method == 'Bi-Interaction':
        return self.leakyRelu( self.W1( h_embs + Nh_embs ) )\
               + self.leakyRelu( self.W2( h_embs * Nh_embs ) )
    elif agg_method == 'concat':
        return self.leakyRelu( self.W_concat( torch.cat([ h_embs,Nh_embs ], dim = -1 ) ) )
    else: #sum
        return self.leakyRelu( self.W1( h_embs + Nh_embs ) )

2.3 预测评估层

该层需要把用户和物品在各层得到的向量拼接起来得到最终的表示: e u ∗ = e u ( 0 ) ∥ ⋯ ∥ e u ( L ) , e i ∗ = e i ( 0 ) ∥ ⋯ ∥ e ( L ) e_u^*=e_u^{(0)}\|\cdots\| e_u^{(L)}, e_i^*=e_i^{(0)}\|\cdots\| e^{(L)} eu=eu(0)eu(L),ei=ei(0)e(L)
用户对物品的偏好程度预测为两个向量的点积: y ^ u i = e u ∗ ⊤ e i ∗ \hat{y}_{u i}=\boldsymbol{e}_u^{* \top} \boldsymbol{e}_i^* y^ui=euei
推荐预测的损失函数也是成对优化误差: L C = ∑ ( u , i j ) ∈ O − log ⁡ σ ( y ^ u i − y ^ u j ) \mathcal{L}_{\mathrm{C}}=\sum_{(u, i j) \in \mathcal{O}}-\log \sigma\left(\hat{y}_{u i}-\hat{y}_{u j}\right) LC=(u,ij)Ologσ(y^uiy^uj)
其中:

  • O = { ( u , i , j ) ∣ ( u , i ) ∈ R + , ( u , j ) ∈ R − } O=\left\{(u, i, j) \mid(u, i) \in \mathcal{R}^{+},(u, j) \in \mathcal{R}^{-}\right\} O={(u,i,j)(u,i)R+,(u,j)R}表示训练集;
  • R + \mathcal{R}^{+} R+表示正样本;
  • R − \mathcal{R}^{-} R表示负样本;
  • KGAT的联合训练损失函数:
    L K G A T = L K G + L C F + λ ∥ Θ ∥ 2 2 \mathcal{L}_{\mathrm{KGAT}}=\mathcal{L}_{\mathrm{KG}}+\mathcal{L}_{\mathrm{CF}}+\lambda\|\Theta\|_2^2 LKGAT=LKG+LCF+λ∥Θ22
    其中: Θ \Theta Θ表示模型的参数集合。

三、代码

class KGAT( nn.Module ):

    def __init__( self, n_users, n_entitys, n_relations, e_dim, r_dim,
                  adj_entity, adj_relation ,agg_method = 'Bi-Interaction'):
        super( KGAT, self ).__init__( )

        self.user_embs = nn.Embedding( n_users, e_dim, max_norm = 1 )
        self.entity_embs = nn.Embedding( n_entitys, e_dim, max_norm = 1 )
        self.relation_embs = nn.Embedding( n_relations, r_dim, max_norm = 1 )

        self.adj_entity = adj_entity  # 节点的邻接列表
        self.adj_relation = adj_relation  # 关系的邻接列表

        self.agg_method = agg_method # 聚合方法

        # 初始化计算注意力时的关系变换线性层
        self.Wr = nn.Linear( e_dim, r_dim )

        # 初始化最终聚合时所用的激活函数
        self.leakyRelu = nn.LeakyReLU( negative_slope = 0.2 )

        # 初始化各种聚合时所用的线性层
        if agg_method == 'concat':
            self.W_concat = nn.Linear( e_dim * 2, e_dim )
        else:
            self.W1 = nn.Linear( e_dim, e_dim )
            if agg_method == 'Bi-Interaction':
                self.W2 = nn.Linear( e_dim, e_dim )

    # 得到邻居的节点embedding和关系embedding
    def get_neighbors( self, items ):
        e_ids = [self.adj_entity[ item ] for item in items ]
        r_ids = [ self.adj_relation[ item ] for item in items ]
        e_ids = torch.LongTensor( e_ids )
        r_ids = torch.LongTensor( r_ids )
        neighbor_entities_embs = self.entity_embs( e_ids )
        neighbor_relations_embs = self.relation_embs( r_ids )
        return neighbor_entities_embs, neighbor_relations_embs

    # GAT消息传递
    def GATMessagePass( self, h_embs, r_embs, t_embs ):
        '''
        :param h_embs: 头实体向量[ batch_size, e_dim ]
        :param r_embs: 关系向量[ batch_size, n_neibours, r_dim ]
        :param t_embs: 为实体向量[ batch_size, n_neibours, e_dim ]
        '''
        # # 将h张量广播,维度扩散为 [ batch_size, n_neibours, e_dim ]
        h_broadcast_embs = torch.cat( [ torch.unsqueeze( h_embs, 1 ) for _ in range( t_embs.shape[ 1 ] ) ], dim = 1 )
        # [ batch_size, n_neibours, r_dim ]
        tr_embs = self.Wr( t_embs )
        # [ batch_size, n_neibours, r_dim ]
        hr_embs = self.Wr( h_broadcast_embs )
        # [ batch_size, n_neibours, r_dim ]
        hr_embs= torch.tanh( hr_embs + r_embs)
        # [ batch_size, n_neibours, 1 ]
        atten = torch.sum( hr_embs * tr_embs,dim = -1 ,keepdim=True)
        atten = torch.softmax( atten, dim = -1 )
        # [ batch_size, n_neibours, e_dim ]
        t_embs = t_embs * atten
        # [ batch_size, e_dim ]
        return  torch.sum( t_embs, dim = 1 )

    # 消息聚合
    def aggregate( self, h_embs, Nh_embs, agg_method = 'Bi-Interaction' ):
        '''
        :param h_embs: 原始的头实体向量 [ batch_size, e_dim ]
        :param Nh_embs: 消息传递后头实体位置的向量 [ batch_size, e_dim ]
        :param agg_method: 聚合方式,总共有三种,分别是'Bi-Interaction','concat','sum'
        '''
        if agg_method == 'Bi-Interaction':
            return self.leakyRelu( self.W1( h_embs + Nh_embs ) )\
                   + self.leakyRelu( self.W2( h_embs * Nh_embs ) )
        elif agg_method == 'concat':
            return self.leakyRelu( self.W_concat( torch.cat([ h_embs,Nh_embs ], dim = -1 ) ) )
        else: #sum
            return self.leakyRelu( self.W1( h_embs + Nh_embs ) )

    def forward( self, u, i ):
        # # [ batch_size, n_neibours, e_dim ] and # [ batch_size, n_neibours, r_dim ]
        # 得到邻居的节点embedding和关系embedding
        t_embs, r_embs = self.get_neighbors( i )
        # # [ batch_size, e_dim ]
        h_embs = self.entity_embs( i )
        # # [ batch_size, e_dim ]
        Nh_embs = self.GATMessagePass( h_embs, r_embs, t_embs )
        # # [ batch_size, e_dim ]
        item_embs = self.aggregate( h_embs, Nh_embs, self.agg_method )
        # # [ batch_size, e_dim ]
        user_embs = self.user_embs( u )
        # # [ batch_size ]
        logits = torch.sigmoid( torch.sum( user_embs * item_embs, dim = 1 ) )
        return logits

四、实验结果

在三个基准数据集:Amazon-book、Last-FM和Yelp2018数据集上实验,KGAT与SL (FM和NFM)、基于正则化(CFKG和CKE)、基于路径(MCRec和RippleNet)和基于图形神经网络(GC-MC)的方法进行了比较:
在这里插入图片描述

在这里插入图片描述
为了探索聚合器的影响,作者考虑了使用不同设置的KGAT-1变体,即之前提到的GCN、GraphSage和Bi-Interaction,实验结果如下表所示。可以看出,Bi-Interaction的性能最好:
在这里插入图片描述

Reference

[1] 推荐系统前沿与实践. 李东胜等
[2] 自然语言处理cs224n-2021–Lecture15: 知识图谱
[3] 东南大学《知识图谱》研究生课程课件
[4] 2022年中国知识图谱行业研究报告
[5] 浙江大学慕课:知识图谱导论.陈华钧老师
[6] https://conceptnet.io/
[7] KG paper:https://github.com/km1994/nlp_paper_study_kg
[8] 北大gStore - a graph based RDF triple store
[9] Natural Language Processing Demystified
[10] https://github.com/datawhalechina/team-learning-nlp/tree/master/KnowledgeGraph_Basic
[11] 新一代知识图谱关键技术综述. 东南大学 王萌
[12] cs224w(图机器学习)2021冬季课程学习笔记12 Knowledge Graph Embeddings
[13] 关系抽取和事件抽取代码案例:https://github.com/taishan1994/taishan1994 (西西嘛呦)
[14] 年末巨制:知识图谱嵌入方法研究总结
[15] “知识图谱+”系列:知识图谱+图神经网络
[16] 【知识图谱】斯坦福 CS520公开课(双语字幕)
[17] https://github.com/LIANGKE23/Awesome-Knowledge-Graph-Reasoning
[18] 再谈图谱表示:图网络表示GE与知识图谱表示KGE的原理对比与实操效果分析
[19] WSDM’23 | 工业界搜推广nlp论文整理
[20] https://github.com/LunaBlack/KGAT-pytorch
[21] 推荐系统之深度召回模型综述(PART III)
[22] 深度融合 | 当推荐系统遇上知识图谱(二)
[23] KGAT_基于知识图谱+图注意力网络的推荐系统(KG+GAT)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/111562.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

35岁有儿有女,为什么我开始自学编程?

零基础编程入门越来越容易 这么讲并不夸张:无论你初学哪门编程语言,第一行代码几乎都是打印出 Hello world ! print(Hello world!) print(Hello python!) 遥想当年,花上一两天折腾完各种安装配置调试环境,写下第一句“面世代码…

该怎么选择副业,三条建议形成自己的副业思维

受经济环境的影响,许多年轻人觉得原来稳定的工作不那么稳定,看着周围的朋友因为企业破产和失业,生活变得没有信心,也想找到自己的副业,在紧急情况下赚更多的钱。所以,年轻人在选择副业时也面临着很多困惑&a…

Java --- JUC的CompletableFuture的使用

目录 一、Future接口 二、Future接口的功能 三、FutureTask 四、CompletableFuture背景及使用 4.1、CompletionStage 4.2、CompletableFuture 4.3、四个静态方法 4.4、减少阻塞和轮询 4.5、使用CompletableFuture完成电商大比价 五、CompletableFuture常用API 5.1、获…

【华为OD机试真题 C++】TLV解析 【2022 Q4 | 100分】

■ 题目描述 TLV编码是按[Tag Length Value]格式进行编码的,一段码流中的信元用Tag标识,Tag在码流中唯一不重复,Length表示信元Value的长度,Value表示信元的值。 码流以某信元的Tag开头,Tag固定占一个字节&#xff0…

机器学习 | 逻辑回归

一.基本原理 面对一个分类问题,建立代价函数,通过优化方法迭代求解出最优的模型参数,然后测试验证我们这个求解的模型的好坏。逻辑回归是一种分类方法,主要用于二分类问题,应用于研究某些事件发生的概率 二.优缺点 …

day28【代码随想录】回溯之组合、组合总和|||、电话号码的字母组合

文章目录前言一、组合(力扣77)剪枝优化二、组合总和 III(力扣216)剪枝优化三、电话号码的字母组合(力扣17)总结前言 1、组合 2、组合总和||| 3、电话号码的字母组合 一、组合(力扣77&#xff0…

第1章 计算机组成原理概述

文章目录前言1.0 课程简介1.0.1 课程的地位1.0.2 课程学习思路1.0.3 课程组成1.1 计算机系统简介1.1.1 计算机组成1.计算机的类型2.计算机的组成3.软件组成1.1.2 计算机系统的层次结构1.物理层方面2.程序员角度1.1.3 计算机体系结构与计算机组成1.2 计算机的基本组成1.2.1 冯诺…

esp8266测试1.44英寸TFT屏(驱动7735)的demo

参考这教程: 使用esp8266点亮福利屏型号st7735的1.44的TFT屏 管脚连接: 我的用的TFT1.44寸ST7735,与NodeMCU针脚接线成功连接 VCC——3V GND——G LED——3V CLK——D5 SDI——D7 RS——D6 RST——D4 CS——D8 这里给出常用的屏幕管脚定义 以及esp8266…

女生也能学编程:行政女生转行学编程获13000元薪资

“女生不能学编程” “女生学编程找不到工作” “企业根本不会招女生” …… 这样类似的说法,让非常多的女生放弃了学编程,但达妹今天要明确的说,这种说法是 错误的! 只要你愿意改变,有梦想,想追求更好的…

想要快速准备好性能数据?方法这不就来了!

[内部资源] 想拿年薪30W的软件测试人员,这份资料必须领取~ Python自动化测试全栈性能测试全栈,挑战年薪40W 性能测试的一般流程 收集性能需求——>编写性能脚本——>执行性能测试——>分析测试报告——>系统性能调优。 在收集性能需求后…

Spring IOC\AOP\事务\注解

DAY1 一、引言 1.1 原生web开发中存在哪些问题? 传统Web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。 部分Java EE API较为复杂,使用效率低(例如:JDBC开发步骤&…

17. 【gRPC系列学习】http2 各类型帧的含义

本节介绍http2有哪些类型的帧以及各帧的主要作用,是rfc7540规范标准定义,文末有参考链接,为后续介绍gRPC帧处理做技术储备。 1. 帧结构 帧长度3个字节 24 bit帧类型1个字节,含义如下:FrameData FrameType = 0x0FrameHeaders FrameType = 0x1FramePriority …

MySQL#4(JDBC常用API详解)

目录 一.简介 1.概念 2.本质 3.优点 4.步骤 二.API详解 1.DriverManager(驱动管理类) 2.Connection 3.Statement 4.ResultSet 5.PreparedStatement 一.简介 1.概念 JDBC就是使用Java语言操作关系型数据库的一套API(Java DataBase Connectivity)Java 数据库连接 2.本…

年货节微信活动有哪些_分享微信小程序商城开发好处

新年临近,又是百姓们囤年货的日子。各行业的微商商城或者线下实体店的商家们,趁此机会别,做一波优惠促销活动,今年的业绩就靠它来个完美的收尾啦! 1.类型:转盘拆福袋等抽奖活动 点击对应抽奖按钮&#xff0…

Doo Prime 提供高达 1000 倍杠杆,助您撬动无限机遇

2022 年 11 月 19 日,Doo Prime 正式将全部账户类型的可选杠杆从 1:500 上调至 1:1000 倍,提供更灵活的杠杆选择,让全球客户有机会以更少的资金撬动更高的潜在利润,进一步拓展投资机遇。 *备注:杠杆调整详情请参阅下文…

Sentinel系列——概述与安装1-1

Sentinel系列——概述与安装1-1概述服务雪崩解决方法基本概念资源规则Sentinel 是如何工作的安装Sentinel下载地址启动修改sentinel启动参数设置启动端口设置用户名密码概述 随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言…

面试官问 Redis 的数据结构的时候怎么答能加分?

一提到 Redis,我们的脑子里马上就会出现一个词:“快。”但是你有没有想过,Redis 的快,到底是快在哪里呢?实际上,这里有一个重要的表现:它接收到一个键值对操作后,能以微秒级别的速度…

【代码随想录】鱼与钰遇雨数据结构与算法刷题笔记

代码随想录 https://www.programmercarl.com/ 编程素养 代码风格 C代码风格参考google C style Guide 我使用的是驼峰命名法&#xff0c;其中空格留白的规则如下例&#xff1a; class Solution { public:void moveZeroes(vector<int>& nums) {int slowIndex 0…

化妆品商城小程序制作步骤_分享化妆品商城小程序开发好处

第一&#xff0c;用套餐做团购活动&#xff0c;主打节日套餐 随着疫情的反反复复&#xff0c;越来越多的用户都减少出门&#xff0c;那么就会减少到线下门店选购商品的机会&#xff0c;那么有一款化妆品商城小程序就可以直接在手机上下单&#xff0c;非常方便&#xff0c;那么我…

清华百度升级AIR白皮书2.0:发布车路协同重大突破

12月23日&#xff0c;全球首份车路协同自动驾驶技术创新白皮书《面向自动驾驶的车路协同关键技术与展望》升级发布2.0版(以下称“白皮书2.0”)&#xff0c;并向全行业公开征集意见。 该白皮书是全球车路协同技术研究领域最权威的报告之一&#xff0c;由张亚勤院士牵头&#xff…