召回算法训练

DSSM---YoutobeDNN

DSSM--双塔模型

DSSM 是由微软研究院于 CIKM 在 2013 年提出的一篇工作,该模型主要用来解决 NLP 领域语义相似度任务,利用深度神经网络将文本表示为低维度的向量,用来提升搜索场景下文档和 query 匹配的问题。

因为模型分为 Query 和 Documents 两部分,在推荐场景中也可对应 user 和 item 部分,被人形象地成为 “双塔”。DSSM 是经典的双塔模型。

 

71c667b24f7e371fc2d8d42e778bb2c7.png

其中,原始输入 x 是初始文本,使用词袋模型或 TF-IDF 模型来表示。毫无疑问,x 的维度会很大,因为要囊括所有可能的词汇。

为了降维以降低模型复杂度和计算量,论文提出了 Word hashing 方法,也就是图中的第一层。该方法实际上是将基于 Word 的表征转换为基于 letter n-grams 的表征,比如将单词 "good" 转换为 letter trigrams: #go, goo, ood, od#。对英文来说,word 几乎是无限的,可能不断增长,但是 letter n-grams 是有限的,能够很好地降维、并且对那些 unseen words 或者拼写错误的单词有很好的应对能力。存在的缺点是,有可能两个不同的单词会有相同的 letter n-grams 分布。但是实验证明,这种缺点带来的负面影响可以忽略不计,如下表所示。

 

3ed57b71cfe138d87089c9a5fde7abaf.png

这种方法也可以用在中文场景,将基于词汇(word)的文本模型转换为基于字(char)的文本模型。

另外值得强调的是,DSSM 模型优化过程中的损失函数设置和负样本生成。

模型的训练数据来自 clickthrough 日志,它由查询列表及其用户点击过的文档组成。我们假设一个查询与随后用户点击过的文档相关,至少部分相关。模型使用有监督的方法来训练,最大化给定查询下点击文档的条件可能性。

给定查询下,候选文档被点击的概率如下所示:

 

cc47c97332c3a2b647cb9ed2a7459e2d.png

其中,γ 是 softmax 函数中的平滑因子,该因子是在我们的实验中根据经验在 held-out 数据集上设置的。

我们希望在训练模型后,真实被点击的文档得到的分数高,未被点击的文档得到的分数低。因此,我们需要正负样本来帮助训练。理想情况下,未被点击的文档都应该算作负样本,但这在计算上是不现实的。实际的做法是随机选择指定数量的未被点击文档作为负样本(论文中设定为4)。模型的训练目标(最小化)如下所示:

 

22eee52f517927cc2636ab13d4e3d21f.png

class DSSM(torch.nn.Module):
    """Deep Structured Semantic Model
​
    Args:
        user_features (list[Feature Class]): training by the user tower module.
        item_features (list[Feature Class]): training by the item tower module.
        temperature (float): temperature factor for similarity score, default to 1.0.
        user_params (dict): the params of the User Tower module, keys include:`{"dims":list, "activation":str, "dropout":float, "output_layer":bool`}.
        item_params (dict): the params of the Item Tower module, keys include:`{"dims":list, "activation":str, "dropout":float, "output_layer":bool`}.
    """
​
    def __init__(self, user_features, item_features, user_params, item_params, temperature=1.0):
        super().__init__()
        self.user_features = user_features
        self.item_features = item_features
        self.temperature = temperature
        self.user_dims = sum([fea.embed_dim for fea in user_features])
        self.item_dims = sum([fea.embed_dim for fea in item_features])
​
        self.embedding = EmbeddingLayer(user_features + item_features)
        self.user_mlp = MLP(self.user_dims, output_layer=False, **user_params)
        self.item_mlp = MLP(self.item_dims, output_layer=False, **item_params)
        self.mode = None
​
    def forward(self, x):
        user_embedding = self.user_tower(x)
        item_embedding = self.item_tower(x)
        if self.mode == "user":
            return user_embedding
        if self.mode == "item":
            return item_embedding
​
        # calculate cosine score
        y = torch.mul(user_embedding, item_embedding).sum(dim=1)
        # y = y / self.temperature
        return torch.sigmoid(y)
​
    def user_tower(self, x):
        if self.mode == "item":
            return None
        input_user = self.embedding(x, self.user_features, squeeze_dim=True)  #[batch_size, num_features*deep_dims]
        user_embedding = self.user_mlp(input_user)  #[batch_size, user_params["dims"][-1]]
        user_embedding = F.normalize(user_embedding, p=2, dim=1)  # L2 normalize
        return user_embedding
​
    def item_tower(self, x):
        if self.mode == "user":
            return None
        input_item = self.embedding(x, self.item_features, squeeze_dim=True)  #[batch_size, num_features*embed_dim]
        item_embedding = self.item_mlp(input_item)  #[batch_size, item_params["dims"][-1]]
        item_embedding = F.normalize(item_embedding, p=2, dim=1)
        return item_embedding

 

YoutobeDNN--双塔模型

YouTube 是全球最大的视频分享和观看平台,服务超过十亿用户,也存在着很严重的信息过载问题。因此,YouTube 的推荐系统是非常重要的。目前,YouTube 视频推荐系统主要面临以下三个挑战:

  • Scale(规模): 视频数量非常庞大,大规模数据下需要分布式学习算法以及高效的线上服务系统(此时,很多在小规模数据中工作良好的推荐算法不再适用)。

  • Freshness(新鲜度): YouTube上的视频一直在动态变化,每秒钟都有很多用户去上传新视频。用户一般都比较喜欢看比较新的视频,而不管是不是真和用户相关(这个感觉和新闻比较类似)。

  • Noise(噪声): 由于数据的稀疏和不可见的其他原因,数据里面的噪声非常之多,很难获取用户对观看过的视频的满意度或隐式反馈。

 

e77759f59030957bef6516e8dc9412ab.png

召回(Candidate generation)

论文把推荐问题转换为一个多分类问题:根据用户 U 和上下文 C,预测在时刻 t 观看视频 i 的概率。

 

8ac8677f5a821454d3bed392cc839e6e.png

式子中,u 指的是用户 U 和上下文 C 的 Embedding,v 指的是候选视频项目的 Embedding。深度学习的任务是学习一个函数(参数估计),怎么根据用户的历史记录和上下文生成当前的 Embedding。

另外,YouTube 不使用点赞等用户显式反馈作为用户满意度的信号,而是使用用户是否观看完视频的隐式反馈作为用户满意度信号。因为后者有更多的可用数据(很多视频用户感兴趣,看完了,但因为一些原因不愿点赞),前者的数据稀疏性较为严重。

召回阶段的模型架构如下所示(可以理解为一个 DNN):

 

febf7b80749feaa85a571488a4301901.png

上图中,搜索历史(search history)指用户在搜索中输入的 Query。Query 将被分词为 unigrames 和 bigrams,并且每个词汇都会转变为 Embedding 进行处理(和 watch history 类似)。用户的基本信息,比如年龄、地区、性别等,可使用binary或者嵌入的方式表征,得到的表征与其他的拼接在一起。

“example age” 是和场景比较相关的特征,也是作者的经验传授。 我们知道,视频有明显的生命周期,例如刚上传的视频比之后更受欢迎,也就是用户往往喜欢看最新的东西,而不管它是不是和用户相关,所以视频的流行度随着时间的分布是高度非稳态变化的(下面图中的绿色曲线)。

 

15b5b7e9c78f25820c5957a2d715215c.png

"example age" 定义为 tmax−t 其中 tmax 是训练数据中所有样本的时间最大值,而 t 为当前样本的时间。线上预测时, 直接把example age全部设为0或一个小的负值,这样就不依赖于各个视频的上传时间了。

精排(Ranking)

 

e2585fcab1de5007d411e41b8989370c.png

class YoutubeDNN(torch.nn.Module):
    """The match model mentioned in `Deep Neural Networks for YouTube Recommendations` paper.
    It's a DSSM match model trained by global softmax loss on list-wise samples.
    Note in origin paper, it's without item dnn tower and train item embedding directly.
​
    Args:
        user_features (list[Feature Class]): training by the user tower module.
        item_features (list[Feature Class]): training by the embedding table, it's the item id feature.
        neg_item_feature (list[Feature Class]): training by the embedding table, it's the negative items id feature.
        user_params (dict): the params of the User Tower module, keys include:`{"dims":list, "activation":str, "dropout":float, "output_layer":bool`}.
        temperature (float): temperature factor for similarity score, default to 1.0.
    """
​
    def __init__(self, user_features, item_features, neg_item_feature, user_params, temperature=1.0):
        super().__init__()
        self.user_features = user_features
        self.item_features = item_features
        self.neg_item_feature = neg_item_feature
        self.temperature = temperature
        self.user_dims = sum([fea.embed_dim for fea in user_features])
        self.embedding = EmbeddingLayer(user_features + item_features)
        self.user_mlp = MLP(self.user_dims, output_layer=False, **user_params)
        self.mode = None
​
    def forward(self, x):
        user_embedding = self.user_tower(x)
        item_embedding = self.item_tower(x)
        if self.mode == "user":
            return user_embedding
        if self.mode == "item":
            return item_embedding
​
        # calculate cosine score
        y = torch.mul(user_embedding, item_embedding).sum(dim=2)
        y = y / self.temperature
        return y
​
    def user_tower(self, x):
        if self.mode == "item":
            return None
        input_user = self.embedding(x, self.user_features, squeeze_dim=True)  #[batch_size, num_features*deep_dims]
        user_embedding = self.user_mlp(input_user).unsqueeze(1)  #[batch_size, 1, embed_dim]
        user_embedding = F.normalize(user_embedding, p=2, dim=2)
        if self.mode == "user":
            return user_embedding.squeeze(1)  #inference embedding mode -> [batch_size, embed_dim]
        return user_embedding
​
    def item_tower(self, x):
        if self.mode == "user":
            return None
        pos_embedding = self.embedding(x, self.item_features, squeeze_dim=False)  #[batch_size, 1, embed_dim]
        pos_embedding = F.normalize(pos_embedding, p=2, dim=2)
        if self.mode == "item":  #inference embedding mode
            return pos_embedding.squeeze(1)  #[batch_size, embed_dim]
        neg_embeddings = self.embedding(x, self.neg_item_feature,
                                        squeeze_dim=False).squeeze(1)  #[batch_size, n_neg_items, embed_dim]
        neg_embeddings = F.normalize(neg_embeddings, p=2, dim=2)
        return torch.cat((pos_embedding, neg_embeddings), dim=1)  #[batch_size, 1+n_neg_items, embed_dim]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值