DeepFM、DIN-推荐系统小结

【DeepFM、DIN-推荐系统小结】

一.DeepFM

在这里插入图片描述

​ 对于CTR问题,被证明的最有效的提升任务表现的策略是特征组合(Feature Interaction), 在CTR问题的探究历史上来看就是如何更好地学习特征组合,进而更加精确地描述数据的特点。可以说这是基础推荐模型到深度学习推荐模型遵循的一个主要的思想。而组合特征大牛们研究过组合二阶特征,三阶甚至更高阶,但是面临一个问题就是随着阶数的提升,复杂度就成几何倍的升高。这样即使模型的表现更好了,但是推荐系统在实时性的要求也不能满足了。所以很多模型的出现都是为了解决另外一个更加深入的问题:如何更高效的学习特征组合?

  • 简单的线性模型虽然简单,同样这样是它的不足,就是限制了模型的表达能力,随着数据的大且复杂,这种模型并不能充分挖掘数据中的隐含信息,且忽略了特征间的交互,如果想交互,需要复杂的特征工程。
  • FM模型考虑了特征的二阶交叉,但是这种交叉仅停留在了二阶层次,虽然说能够进行高阶,但是计算量和复杂性一下子随着阶数的增加一下子就上来了。所以二阶是最常见的情况,会忽略高阶特征交叉的信息
  • DNN,适合天然的高阶交叉信息的学习,但是低阶的交叉会忽略掉
  • 那么如果把上面这几种结构组合一下子,是不是效果会强大一些呢? 所以W&D模型在这个思路上进行了一个伟大的尝试,把简单的LR模型和DNN模型进行了组合, 使得模型既能够学习高阶组合特征,又能够学习低阶的特征模式,但是W&D的wide部分是用了LR模型, 这一块依然是需要一些经验性的特征工程的,且Wide部分和Deep部分需要两种不同的输入模式, 这个在具体实际应用中需要很强的业务经验。总体:1.wide&deep需要较为精巧的特征工程2.在output Units阶段直接将低阶和高阶特征进行组合,很容易让模型最终偏向学习到低阶或者高阶的特征

在这里插入图片描述

DeepFM是2017年哈工大和华为公司联合提出的一个模型,改动地方已经框出,论文作者提出:通过高阶和低阶交互特征一块来进行反向传播更新参数反而会使得模型表现更佳,当然,这个也依赖于共享Embedding输入的策略

所以与Wide&Deep不同的是,DeepFM中的Wide部分与Deep部分共享了输入特征,即Embedding向量

在这里插入图片描述

一些细节梳理

(1).特征工程改进

​ 一些特征工程比较容易理解,就比如上面提到的那两个, 这时候往往我们都能很容易的设计或者组合那样的特征。 然而,其他大部分特征交互都隐藏在数据中,难以先验识别(比如经典的关联规则 "尿布和啤酒 "就是从数据中挖掘出来的,而不是由专家发现的),只能由机器学习自动捕捉,即使是对于容易理解的交互,专家们似乎也不可能详尽地对它们进行建模,特别是当特征的数量很大的时候.

(2).与’‘前辈们’'的对比

在这里插入图片描述

(3).工业经验

  1. MLP这端神经网络的层数, 工业上的经验值不超过3层,一般用两层即可。

  2. MLP这端隐藏神经元的个数,工业上的经验值,一般128就差不多,最多不超过500

  3. embedding的维度一般不要超过50维, 经验值10-50

    模型复现

    使用Torch-RecHub实现DeepFm

    Torch-RecHub中不仅给出了现成的DeepFm,而且还可以分模块地自己组装DeepFM

    from torch_rechub.basic.layers import FM, MLP, LR, EmbeddingLayer
    
    class MyDeepFM(torch.nn.Module):
      # Deep和FM为两部分,分别处理不同的特征,因此传入的参数要有两种特征,由此我们得到参数deep_features,fm_features
      # 此外神经网络类的模型中,基本组成原件为MLP多层感知机,多层感知机的参数也需要传进来,即为mlp_params
      def __init__(self, deep_features, fm_features, mlp_params):
        super().__init__()
        self.deep_features = deep_features
        self.fm_features = fm_features
        self.deep_dims = sum([fea.embed_dim for fea in deep_features])
        self.fm_dims = sum([fea.embed_dim for fea in fm_features])
        # LR建模一阶特征交互
        self.linear = LR(self.fm_dims)
        # FM建模二阶特征交互
        self.fm = FM(reduce_sum=True)
        # 对特征做嵌入表征
        self.embedding = EmbeddingLayer(deep_features + fm_features)
        self.mlp = MLP(self.deep_dims, **mlp_params)
    
      def forward(self, x):
        input_deep = self.embedding(x, self.deep_features, squeeze_dim=True)  #[batch_size, deep_dims]
        input_fm = self.embedding(x, self.fm_features, squeeze_dim=False)  #[batch_size, num_fields, embed_dim]
    
        y_linear = self.linear(input_fm.flatten(start_dim=1))
        y_fm = self.fm(input_fm)
        y_deep = self.mlp(input_deep)  #[batch_size, 1]
        # 最终的预测值为一阶特征交互,二阶特征交互,以及深层模型的组合
        y = y_linear + y_fm + y_deep
        # 利用sigmoid来将预测得分规整到0,1区间内
        return torch.sigmoid(y.squeeze(1))
    

    二、DIN-Deep Interest Network

    ​ DIN是2018年阿里巴巴提出来的模型, 该模型基于业务的观察,从实际应用的角度进行改进,相比于之前很多“学术风”的深度模型, 该模型更加具有业务气息。之前的那些深度学习模型,是没法很好的去表达出用户这广泛多样的兴趣的,如果想表达的准确些, 那么就得加大隐向量的维度,让每个特征的信息更加丰富, 那这样带来的问题就是计算量上去了,毕竟真实情景尤其是电商广告推荐的场景,特征维度的规模是非常大的。 并且根据上面的例子, 也并不是用户所有的历史行为特征都会对某个商品广告点击预测起到作用。所以对于当前某个商品广告的点击预测任务,没必要考虑之前所有的用户历史行为。

    ​ DIN的动机就是在业务的角度,我们应该自适应的去捕捉用户的兴趣变化,这样才能较为准确的实施广告推荐;而放到模型的角度, 我们应该考虑到用户的历史行为商品与当前商品广告的一个关联性

    在这里插入图片描述

    ​ 上面的图表示basemodel, 用户的历史行为特征和当前的候选广告特征在全都拼起来给神经网络之前,是一点交互的过程都没有, 而拼起来之后给神经网络,虽然是有了交互了,但是原来的一些信息,比如,每个历史商品的信息会丢失了一部分,因为这个与当前候选广告商品交互的是池化后的历史特征embedding, 这个embedding是综合了所有的历史商品信息, 这个通过我们前面的分析,对于预测当前广告点击率,并不是所有历史商品都有用,综合所有的商品信息反而会增加一些噪声性的信息,可以联想上面举得那个键盘鼠标的例子,如果加上了各种洗面奶,衣服啥的反而会起到反作用。其次就是这样综合起来,已经没法再看出到底用户历史行为中的哪个商品与当前商品比较相关,也就是丢失了历史行为中各个商品对当前预测的重要性程度。最后一点就是如果所有用户浏览过的历史行为商品,最后都通过embedding和pooling转换成了固定长度的embedding,这样会限制模型学习用户的多样化兴趣。

    ​ 那么改进这个问题的思路有哪些呢?

    ​ 第一个就是加大embedding的维度,增加之前各个商品的表达能力,这样即使综合起来,embedding的表达能力也会加强, 能够蕴涵用户的兴趣信息,但是这个在大规模的真实推荐场景计算量超级大,不可取。 另外一个思路就是在当前候选广告和用户的历史行为之间引入注意力的机制,这样在预测当前广告是否点击的时候,让模型更关注于与当前广告相关的那些用户历史产品,也就是说与当前商品更加相关的历史行为更能促进用户的点击行为

    ​ 第二点是DIN通过给定一个候选广告,然后去注意与该广告相关的局部兴趣的表示来模拟此过程。 DIN不会通过使用同一向量来表达所有用户的不同兴趣,而是通过考虑历史行为的相关性来自适应地计算用户兴趣的表示向量(对于给的的广告)。 该表示向量随不同广告而变化。

    在这里插入图片描述

    ​ 对比basemodel,DIN只不过是在这个的基础上加了一个注意力机制来学习用户兴趣与当前候选广告间的关联程度, 用论文里面的话是,引入了一个新的local activation unit, 这个东西用在了用户历史行为特征上面, 能够根据用户历史行为特征和当前广告的相关性给用户历史行为特征embedding进行加权。这个相关性相当于每个历史商品的权重,把这个权重与原来的历史行为embedding相乘求和就得到了用户的兴趣表示 v U ( A )
    这个东西的计算公式如下:
    在这里插入图片描述

    这里的*{ v A , e 1 , e 2 , … , e H }* 是用户U的历史行为特征embedding, vA表示的是候选广告A的embedding向量, a ( e j , v A ) = w j 表示的权重或者历史行为商品与当前广告A 的相关性程度。a ( ⋅ ) 表示的上面那个前馈神经网络,也就是那个所谓的注意力机制, 当然,看图里的话,输入除了历史行为向量和候选广告向量外,还加了一个它俩的积操作(对应位置元素乘),作者说这里是有利于模型相关性建模的显性知识,有的还加上差的,就类似于PNN里面的(concat(A, B, A-B, A*B)。

    这里有一点需要特别注意,就是这里的权重加和不是1, 准确的说这里不是权重, 而是直接算的相关性的那种分数作为了权重,也就是平时的那种scores(softmax之前的那个值),这个是为了保留用户的兴趣强度。

    一些问题:

    • 为什么增加叉乘作为输入呢?因为两个embedding的叉乘是显示地反映了两者之间的相关性,加入后有助于更好学习weight。
    • 为什么选叉乘而非其他形式呢?其实论文的初版使用的是两个embedding的差,发表的最新版才转为使用叉乘,相信也都是经过了一系列的尝试和实验对比。
    • 为什么使用简单的MLP实现AU呢?同样是尝试出来的,作者也尝试过 LSTM 结构实现AU,效果并不理想。文中给出的一个possible解释是,文本是在语法严格约束下的有序序列,而用户历史行为序列可能包含了多个同时存在的用户兴趣点,用户会在这些兴趣点之间“随意切换”,这使得这个序列并不是那么严格的“有序”,产生了一些噪声。

    两个训练技术(之后再补充)

    1. Mini-batch Aware Regularization
    2.Data Adaptive Activation Function

    DIN的数据处理(待办)

    Torch-RecHub实现(待复习)

    # DIN网络搭建
    def DIN(feature_columns, behavior_feature_list, behavior_seq_feature_list):
        """
        这里搭建DIN网络,有了上面的各个模块,这里直接拼起来
        :param feature_columns: A list. 里面的每个元素是namedtuple(元组的一种扩展类型,同时支持序号和属性名访问组件)类型,表示的是数据的特征封装版
        :param behavior_feature_list: A list. 用户的候选行为列表
        :param behavior_seq_feature_list: A list. 用户的历史行为列表
        """
        # 构建Input层并将Input层转成列表作为模型的输入
        input_layer_dict = build_input_layers(feature_columns)
        input_layers = list(input_layer_dict.values())
      
        # 筛选出特征中的sparse和Dense特征, 后面要单独处理
        sparse_feature_columns = list(filter(lambda x: isinstance(x, SparseFeat), feature_columns))
        dense_feature_columns = list(filter(lambda x: isinstance(x, DenseFeat), feature_columns))
      
        # 获取Dense Input
        dnn_dense_input = []
        for fc in dense_feature_columns:
            dnn_dense_input.append(input_layer_dict[fc.name])
      
        # 将所有的dense特征拼接
        dnn_dense_input = concat_input_list(dnn_dense_input)   # (None, dense_fea_nums)
      
        # 构建embedding字典
        embedding_layer_dict = build_embedding_layers(feature_columns, input_layer_dict)
    
        # 离散的这些特特征embedding之后,然后拼接,然后直接作为全连接层Dense的输入,所以需要进行Flatten
        dnn_sparse_embed_input = concat_embedding_list(sparse_feature_columns, input_layer_dict, embedding_layer_dict, flatten=True)
      
        # 将所有的sparse特征embedding特征拼接
        dnn_sparse_input = concat_input_list(dnn_sparse_embed_input)   # (None, sparse_fea_nums*embed_dim)
      
        # 获取当前行为特征的embedding, 这里有可能有多个行为产生了行为列表,所以需要列表将其放在一起
        query_embed_list = embedding_lookup(behavior_feature_list, input_layer_dict, embedding_layer_dict)
      
        # 获取历史行为的embedding, 这里有可能有多个行为产生了行为列表,所以需要列表将其放在一起
        keys_embed_list = embedding_lookup(behavior_seq_feature_list, input_layer_dict, embedding_layer_dict)
        # 使用注意力机制将历史行为的序列池化,得到用户的兴趣
        dnn_seq_input_list = []
        for i in range(len(keys_embed_list)):
            seq_embed = AttentionPoolingLayer()([query_embed_list[i], keys_embed_list[i]])  # (None, embed_dim)
            dnn_seq_input_list.append(seq_embed)
      
        # 将多个行为序列的embedding进行拼接
        dnn_seq_input = concat_input_list(dnn_seq_input_list)  # (None, hist_len*embed_dim)
      
        # 将dense特征,sparse特征, 即通过注意力机制加权的序列特征拼接起来
        dnn_input = Concatenate(axis=1)([dnn_dense_input, dnn_sparse_input, dnn_seq_input]) # (None, dense_fea_num+sparse_fea_nums*embed_dim+hist_len*embed_dim)
      
        # 获取最终的DNN的预测值
        dnn_logits = get_dnn_logits(dnn_input, activation='prelu')
      
        model = Model(inputs=input_layers, outputs=dnn_logits)
      
        return model
    
    参考资料[ღ( ´・ᴗ・` )比心]:

    AI上推荐 之 FNN、DeepFM、NFM
    推荐系统精排模型:DIN与DeepFM
    FunRec-datawhale

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值