【Torch-RecHub学习】DSSM双塔模型与其两个方向的改进

1.DSSM思想简述

2013年CMKI,由微软团队提出,应用于文本相似度匹配场景

原理:把搜索词query和文档doc映射到语义空间(Embedding空间)中,最大化query和doc之间的余弦相似度,训练得到隐语义模型(两者的Embedding),获取语句的低维语义向量表示sentence_embedding,用于预测两句话的语义相似度

论文下载:dl.acm.org/doi/pdf/10.…

dssm.png

  1. 经过第一层word Hashing控制DNN输入层的大小
  2. 然后经过多层的DNN网络,把高维的query向量和doc向量转为语义空间中的低维稠密向量
  3. 计算向量之间的余弦相似度表示向量之间的相似性分数
  4. 最后送入softmax中

2.把双塔思想应用到推荐模型中

2.1 朴素DSSM双塔

朴素dssm双塔.png

把文档doc看作item侧,把搜索词query看作user侧,在user侧放入用户相关特征(用户基本信息、统计属性、行为过的item序列、context上下文特征等),在item侧放入item相关特征(item基本信息、属性信息等)。中间的DNN模型则使用经典的MLP。

训练过程中,one-hot特征经过Feature_Embedding层转为低维稠密的Embedding向量,经过几层的MLP隐层,两个塔分别输出user_Embedding和item_embedding,两者做内积或cosine相似度计算(cosine=emb做归一化后点积)。使user和正例的item+在embedding空间中更接近(cos接近1),与负例的item-更远(cos接近-1)。

2.2 朴素DSSM的优点和缺点

优点:

  1. 部署时user和item分离
  2. item塔不依赖user信息,可以离线更新,item_emb可以周期性、批量、离线生成
  3. user塔不依赖item信息,user_emb只需要设如果能成一次

缺点:

  1. 训练时,user和item分离
  2. 没有信息输入到user和item之间的特征交叉
  3. user侧和item侧只有一次交叉机会,压缩过的embedding损失了细节信息
  4. 为了快速的线上服务,最后的预测层只是简单的点积和相似度计算

改建方向: 如何使得最终的emb中包含更多有用的信息,用于和对侧塔进行交叉

2.3 加入SENet的双塔

如何改进:“堵”

在两个塔的Embedding层上面,各自加入SENet结构,动态学习特征的重要性,给重要特征给予大权重,屏蔽或弱化噪声,使塔内信道变宽,保证重要信息无损通过。

SENet_dssm.png

为什么加入SENet?

突出对高层user_emb和item_emb的特征交叉中起重要作用的特征,有利于表达两侧的特征交叉,避免单侧无效特征经过DNN双塔非线性融合时带来的噪声。

2.4 多通道的双塔

如何改建:“疏”

信息沿着适合自己的塔,向上流动浓缩避免相互干扰,每个小塔聚合成最后的emb与对侧的final_emb做点积或相似度计算。

LGC-ACF.png 华南理工大学,IEEE ACCESS 2021,《Light Graph Convolutional Collaborative Filtering with Multi-aspect Information》

图示以movielens数据集为例,对于每个除了user_id和item_id外的每个特征建立一个二部数据图和与其对应的tower,从中得到对应的user_emb和item_emb,送入LGC(何向南团队提出的在Matching阶段的GCN模型)进行卷积学习和融合得到最后的final_emb,最后做点积预测输出

3.朴素DSSM的实现

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    # 这里加入的温度系数是近几年被证明有用的trick
        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

猜你喜欢

转载自juejin.im/post/7112264644089085965