note


文章目录

  • note
  • 一、EGES图算法
  • 1.0 回顾GNN
  • 1.1 基本定义和数据预处理
  • 1.2 GES: GNN with side info
  • 1.3 EGES: enhanced版本
  • 二、Framework of EGES
  • 三、代码实现
  • 四、Experiments
  • 4.1 offline evaluation
  • 4.2 online A/B test
  • 五、系统部署和Operation
  • 六、离线评估
  • 七、EGES训练慢的解决办法
  • Reference


一、EGES图算法

1.0 回顾GNN

在商品推荐系统中普遍存在三大问题:可扩展性、稀疏性和冷启动问题。GNN类算法就能很好地解决推荐系统中的稀疏性问题,因为RS中有很多图结构数据,如二部图,序列图、社交关系图、知识语义图等。

回顾word2vec:基于节点的 ID 把每个节点映射成一个 embedding,这种方式的缺点在于只能处理出现频次大于一定阈值的节点,但是在例如淘宝的场景中,大量的商品(特别是新商品)被消费的次数很少,因此无法为这些商品生成 embedding。

python计算多分类的召回率 pytorch计算召回率_深度学习

  • 某宝在19年提出的EGES模型,是加入side information的graph embedding(如item的品牌、类别、价格、描述、海报等)方法,解决冷启动问题。核心任务在于基于用户行为计算所有项目之间的成对相似性。大致步骤为基于用户历史行为构造一个图,然后利用 Node2Vec 的方法来学习 Item 的 Embedding 向量。这样便可以根据向量的内积计算节点间的相似度来生成候选集。
  • 为了解决冷启动,阿里的GNN迭代了三次:BGE、GES 和 EGES。
  • 推荐系统中存在很多的图结构,如二部图,序列图,社交关系图,知识语义图等。GNN比random walk等算法效果更好。

python计算多分类的召回率 pytorch计算召回率_推荐算法_02

1.1 基本定义和数据预处理

利用滑动窗口选取用户历史行为序列,同时也有降噪处理,如点击少于1s的大概率为无意点击(需要剔除)、过度活跃用户(短时间购买几千件商品可能为刷的,需要剔除)等等。

python计算多分类的召回率 pytorch计算召回率_pytorch_03

根据用户行为构建加权图(如上图b中所示),然后通过我们最熟悉的deepwalk学习G图中每个节点的embedding。

random walk的转移概率定义如下:
python计算多分类的召回率 pytorch计算召回率_深度学习_04

其中几个定义:

  • python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_05,V表示节点(商品)集合,python计算多分类的召回率 pytorch计算召回率_pytorch_06表示加权有向边的集合,这里的边权,即从节点ij的边权表示所有用户,先消费商品item i再消费item j的频率
  • python计算多分类的召回率 pytorch计算召回率_推荐系统_07的邻接矩阵为M,并且python计算多分类的召回率 pytorch计算召回率_推荐系统_08为节点python计算多分类的召回率 pytorch计算召回率_深度学习_09到节点python计算多分类的召回率 pytorch计算召回率_深度学习_10的边权重
  • python计算多分类的召回率 pytorch计算召回率_深度学习_11表示python计算多分类的召回率 pytorch计算召回率_深度学习_12节点的所有邻居节点集合

通过random walk得到一系列的1.1(b)中的序列,再通过skip gram学习节点的embedding,训练目标是最大化我们在random walk得到序列中的节点共现概率,即优化问题为:python计算多分类的召回率 pytorch计算召回率_推荐算法_13
其中python计算多分类的召回率 pytorch计算召回率_深度学习_14为滑动窗口的大小,基于独立假设,有python计算多分类的召回率 pytorch计算召回率_推荐系统_15

利用负采样优化这里的损失函数(减少分类softmax的目标标签数),将问题转为:python计算多分类的召回率 pytorch计算召回率_pytorch_16
其中:

  • python计算多分类的召回率 pytorch计算召回率_深度学习_17python计算多分类的召回率 pytorch计算召回率_深度学习_12的负采样样本,经验上看,该负采样数值越大越好
  • python计算多分类的召回率 pytorch计算召回率_推荐系统_19是sigmoid函数python计算多分类的召回率 pytorch计算召回率_推荐系统_20

1.2 GES: GNN with side info

python计算多分类的召回率 pytorch计算召回率_推荐系统_21


GBE模型存在严重的冷启动问题,在BGE基础上提出GES,即为图中每个节点增加side information。定义矩阵 python计算多分类的召回率 pytorch计算召回率_推荐算法_22 表示物品及其side information的embedding向量表示, python计算多分类的召回率 pytorch计算召回率_推荐算法_23 表示物品 python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_24 自身的向 量表示, python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_25 表示物品 python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_24 第s 类side information的向量表示。为了合并item v的所有side info向量,做一层平均池化(average-pooling):python计算多分类的召回率 pytorch计算召回率_推荐算法_27

实际上就是word2vec的多个embedding的版本,我们传统的word2vec的结构中单个input 对应一个词,而这里,单个input对应一个向量,以item2vec为例,单个input就是一个item,而这里单个input包括了[item ,item的价格,item的商品类型,item所在的shop…]并且为每一个属性都建立了一个input embedding,然后这些input embeddings做一个mean pooling就重新变成了一个常规的单个item的input embedding的形式,原论文中对price category shop这些的embedding的size取的大小和item embedding相同,这样才可以直接mean pooling,否则维度不同只能concat了

1.3 EGES: enhanced版本

  • 定义一个矩阵 python计算多分类的召回率 pytorch计算召回率_pytorch_28 表示的是图中所有节点的数量, python计算多分类的召回率 pytorch计算召回率_推荐系统_29
  • python计算多分类的召回率 pytorch计算召回率_推荐算法_30 表示第i个item的第j类side information的权重;
  • python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_31
  • python计算多分类的召回率 pytorch计算召回率_pytorch_32 表示物品 python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_33 的第 python计算多分类的召回率 pytorch计算召回率_推荐系统_34

融入side info的加权层定义:
python计算多分类的召回率 pytorch计算召回率_推荐算法_35
其中上面有个trick,使用python计算多分类的召回率 pytorch计算召回率_深度学习_36代替python计算多分类的召回率 pytorch计算召回率_pytorch_37确保每个side info值是大于0的,分母python计算多分类的召回率 pytorch计算召回率_深度学习_38是对每个side information进行归一化标准处理。

EGES的目标函数:python计算多分类的召回率 pytorch计算召回率_pytorch_39
其中:

  • python计算多分类的召回率 pytorch计算召回率_深度学习_40:表示在训练集中,节点v的上下文节点u的embedding,即item u的output vector(在softmax层中的embedding)
  • python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_41 看成是 python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_33
  • label python计算多分类的召回率 pytorch计算召回率_推荐算法_43 表示 python计算多分类的召回率 pytorch计算召回率_推荐算法_44 是否 是 python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_33

二、Framework of EGES

为了得到采样序列,先随机选择一个起始节点,然后使用alias method进行采样确定下一个节点。
alias method:加权随机采样. 胡诚. https://guyuecanhui.github.io/2020/12/05/weighted-sample/

node2vec 算法 [9] 还提出了一种有偏的随机游走策略,通过引入两个超参数来决定游走是偏向于向远离初始点的方向探索,还是围绕初始点附近进行探索,因此更加灵活,但是初始化的效率比较低。

python计算多分类的召回率 pytorch计算召回率_pytorch_46


其中:

  • python计算多分类的召回率 pytorch计算召回率_深度学习_47
  • python计算多分类的召回率 pytorch计算召回率_推荐系统_48
  • python计算多分类的召回率 pytorch计算召回率_推荐算法_49
  • python计算多分类的召回率 pytorch计算召回率_python计算多分类的召回率_50
  • python计算多分类的召回率 pytorch计算召回率_pytorch_51 : Skip-Gram算法窗口大小(Skip-Gram window size python计算多分类的召回率 pytorch计算召回率_推荐系统_52
  • python计算多分类的召回率 pytorch计算召回率_推荐系统_53
  • python计算多分类的召回率 pytorch计算召回率_推荐系统_54

其中加权skip-gram的伪代码如下,目标是最大化所有序列中节点共现的概率。

python计算多分类的召回率 pytorch计算召回率_推荐算法_55

三、代码实现

import torch as th

class EGES(th.nn.Module):
    def __init__(self, dim, num_nodes, num_brands, num_shops, num_cates):
        super(EGES, self).__init__()
        self.dim = dim
        # embeddings for nodes
        base_embeds = th.nn.Embedding(num_nodes, dim)
        brand_embeds = th.nn.Embedding(num_brands, dim)
        shop_embeds = th.nn.Embedding(num_shops, dim)
        cate_embeds = th.nn.Embedding(num_cates, dim)
        # concat four embedding
        self.embeds = [base_embeds, brand_embeds, shop_embeds, cate_embeds]
        # weights for each node's side information
        self.side_info_weights = th.nn.Embedding(num_nodes, 4)

    #
    def forward(self, srcs, dsts):
        # srcs: sku_id, brand_id, shop_id, cate_id
        srcs = self.query_node_embed(srcs)
        dsts = self.query_node_embed(dsts)
        
        return srcs, dsts
    
    def query_node_embed(self, nodes):
        """
            @nodes: tensor of shape (batch_size, num_side_info)
        """
        batch_size = nodes.shape[0]
        # query side info weights, (batch_size, 4)
        side_info_weights = th.exp(self.side_info_weights(nodes[:, 0]))
        # merge all embeddings
        side_info_weighted_embeds_sum = []
        side_info_weights_sum = []
        # four embeddings
        for i in range(4):
            # weights for i-th side info, (batch_size, ) -> (batch_size, 1)
            i_th_side_info_weights = side_info_weights[:, i].view((batch_size, 1))
            # batch of i-th side info embedding * its weight, (batch_size, dim)
            side_info_weighted_embeds_sum.append(i_th_side_info_weights * self.embeds[i](nodes[:, i]))
            side_info_weights_sum.append(i_th_side_info_weights)
        # stack: (batch_size, 4, dim), sum: (batch_size, dim)
        side_info_weighted_embeds_sum = th.sum(th.stack(side_info_weighted_embeds_sum, axis=1), axis=1)
        # stack: (batch_size, 4), sum: (batch_size, )
        side_info_weights_sum = th.sum(th.stack(side_info_weights_sum, axis=1), axis=1)
        # (batch_size, dim)
        H = side_info_weighted_embeds_sum / side_info_weights_sum

        return H       

    def loss(self, srcs, dsts, labels):
        dots = th.sigmoid(th.sum(srcs * dsts, axis=1))
        dots = th.clamp(dots, min=1e-7, max=1 - 1e-7)

        return th.mean(- (labels * th.log(dots) + (1 - labels) * th.log(1 - dots)))

四、Experiments

4.1 offline evaluation

python计算多分类的召回率 pytorch计算召回率_深度学习_56

4.2 online A/B test

python计算多分类的召回率 pytorch计算召回率_推荐算法_57

五、系统部署和Operation

python计算多分类的召回率 pytorch计算召回率_深度学习_58

六、离线评估

Embedding 的离线评估是很困难的。原文提出一种连接预测的方式,也就是把图中随机选一些有向边作为正例,随机构造一些不存在的有向边作为负例来测试模型。除了这种方式,还可以通过选择一些典型的 case 来将各维度的 embedding 进行可视化,或者召回相似列表,看看是否满足主观预期,但是这种方式可以用来理解和优化,不适合用于评估。

七、EGES训练慢的解决办法

  • 第一条是我们可以把商品 Embedding 进行预训练,再跟其他 side information 特征一起输入 EGES,不用直接在 EGES 中加 Embedding 层进行 End2End 训练。
  • 第二条是我们可以把商品进行聚类后再输入 EGES 网络,比如非常类似的商品,可以用一个商品聚类 id 替代,当作一个商品来处理。事实上,这种方法往往可以大幅减少商品数量的量级,AirBnb 就曾经非常成功地应用了该方法,用一些特征的组合来代替一类商品或用户,不仅大幅加快训练速度,而且推荐效果也没有受到影响。