• 推荐算法之召回模型:DSSM、YoutubeDNN


    1. DSSM

    1.1 DSSM模型原理

    DSSM(Deep Structured Semantic Model),由微软研究院提出,利用深度神经网络将文本表示为低维度的向量,应用于文本相似度匹配场景下的一个算法。不仅局限于文本,在其他可以计算相似性计算的场景,例如推荐系统中。根据用户搜索行为中query(文本搜索)和doc(要匹配的文本)的日志数据,使用深度学习网络将query和doc映射到相同维度的语义空间中,即query侧特征的embedding和doc侧特征的embedding,从而得到语句的低维语义向量表达sentence embedding,用于预测两句话的语义相似度。

    1.2 DSSM结构

    在这里插入图片描述
    模型结构:user侧塔和item侧塔分别经过各自的DNN得到embedding,再计算两者之间的相似度
    特点:

    1. user和item两侧最终得到的embedding维度需要保持一致
    2. 对物料库中所有item计算相似度时,使用负采样进行近似计算
    3. 在海量的候选数据进行召回的场景下,速度很快
      **缺点:**双塔的结构无法考虑两侧特征之间的交互信息,在一定程度上牺牲掉模型的部分精准性。

    1.3 正负样本构建

    正样本:以内容推荐为例,选“用户点击”的item为正样本。最多考虑一下用户停留时长,将“用户误点击”排除在外
    负样本user与item不匹配的样本,为负样本。

    • 全局随机采样: 从全局候选item里面随机抽取一定数量作为召回模型的负样本,但可能会导致长尾现象。
    • 全局随机采样+热门打压:对一些热门item进行适当的采样,减少热门对搜索的影响,提高模型对相似item的区分能力。
    • Hard Negative增强样本:选取一部分匹配度适中的item,增加模型在训练时的难度
    • Batch内随机选择:利用其他样本的正样本在batch内随机采样作为自己的负样本

    1.4 DSSM的代码

    class DSSM(torch.nn.Module):    
        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
    
            # 计算余弦相似度
            y = torch.mul(user_embedding, item_embedding).sum(dim=1)
            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)
            # user DNN
            user_embedding = self.user_mlp(input_user)
            user_embedding = F.normalize(user_embedding, p=2, dim=1)
            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)
            # item DNN
            item_embedding = self.item_mlp(input_item)
            item_embedding = F.normalize(item_embedding, p=2, dim=1)
            return item_embedding
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    2. YoutubeDNN

    2.1 YoutubeDNN模型原理

    YoutubeDNN是Youtube用于做视频推荐的落地模型,可谓推荐系统中的经典,其大体思路为召回阶段使用多个简单模型筛除大量相关度较低的样本,排序阶段使用较为复杂的模型获取精准的推荐结果。

    2.2 YoutubeDNN结构

    在这里插入图片描述
    在这里插入图片描述

    召回部分: 主要的输入是用户的点击历史数据,输出是与该用户相关的一个候选视频集合;
    精排部分: 主要方法是特征工程, 模型设计和训练方法;
    线下评估:采用一些常用的评估指标,通过A/B实验观察用户真实行为;

    2.2.1 YoutubeDNN召回模型

    在这里插入图片描述

    1. 输入层是用户观看视频序列的embedding mean pooling、搜索词的embedding mean pooling、地理位置embedding、用户特征;
    2. 输入层给到三层激活函数位ReLU的全连接层,然后得到用户向量;
    3. 最后,经过softmax层,得到每个视频的观看概率。

    2.2.2 训练数据选取

    • 采样方式:负采样(类似于skip-gram的采样)
    • 样本来源:来自于全部的YouTube用户观看记录,包含用户从其他渠道观看的视频

    注意:

    1. 训练数据中对于每个用户选取相同的样本数,保证用户样本在损失函数中的权重;
    2. 避免让模型知道不该知道的信息,即信息泄露

    2.2.3 Example Age特征

    • what:Example Age为 视频年龄的特征,即视频的发布时间
    • 背景:由于用户对新视频的观看特点,导致视频的播放预测值期望不准确。
    • 作用:捕捉视频的生命周期,让模型学习到用户对新颖内容的bias,消除热度偏见。
    • 操作:在线上预测时,将example age全部设为0或一个小的负值,不依赖于各个视频的上传时间。
    • 好处:将example age设置为常数值,在计算用户向量时只需要一次;对不同的视频,对应的example age所在范围一致,只依赖训练数据选取的时间跨度,便于归一化操作。

    2.3 YoutubeDNN代码

    import torch
    import torch.nn.functional as F
    from torch_rechub.basic.layers import MLP, EmbeddingLayer
    from tqdm import tqdm
    
    class YoutubeDNN(torch.nn.Module):
        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
    
            # 计算相似度
            y = torch.mul(user_embedding, item_embedding).sum(dim=2)
            y = y / self.temperature
            return y
    
        def user_tower(self, x):
            # 用于inference_embedding阶段
            if self.mode == "item":
                return None
            input_user = self.embedding(x, self.user_features, squeeze_dim=True)
            user_embedding = self.user_mlp(input_user).unsqueeze(1)
            user_embedding = F.normalize(user_embedding, p=2, dim=2)
            if self.mode == "user":
                return user_embedding.squeeze(1)
            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)
            pos_embedding = F.normalize(pos_embedding, p=2, dim=2)
            if self.mode == "item":
                return pos_embedding.squeeze(1)
            neg_embeddings = self.embedding(x, self.neg_item_feature, squeeze_dim=False).squeeze(1)
            neg_embeddings = F.normalize(neg_embeddings, p=2, dim=2)
            return torch.cat((pos_embedding, neg_embeddings), dim=1)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    3. 总结

    1. DSSM为双塔模型,user与item分别经过的DNN得到embedding,再计算两者之间的相似度。训练样本,正样本为正确的搜索目标,负样本为全局采样+热门打击所得到的负样本。
    2. YoutubeDNN在双塔模型基础上进行了改进,召回阶段使用多个简单模型筛除大量相关度较低的样本,排序阶段使用较为复杂的模型获取精准的推荐结果。

    参考

    https://www.zhihu.com/question/426543628
    https://blog.csdn.net/a321123b/article/details/122291013
    https://zhuanlan.zhihu.com/p/405907646
    https://relph1119.github.io/my-team-learning/#/pytorch_rechub_learning38/task03

  • 相关阅读:
    NLP模型笔记2022-32:Sentence-BERT句子语义相似计算与相似句子聚类
    栈简介、手写顺序栈、手写链栈和栈的应用
    .gitignore不生效的解决方案
    Vue3 第二十四篇:集成vue.draggable
    上传本地文件到github
    SpringBoot笔记:SpringBoot集成MybatisPlus、H2纯内存数据库实战
    算法训练---Day1
    git创建新分支将项目挂载到新分支操作
    深度学习:pytorch nn.Embedding详解
    记录--CSS 滚动驱动动画 scroll()
  • 原文地址:https://blog.csdn.net/A496608119/article/details/125401017