[CTR模型] DIEN(Deep Interest Evolution Network)模型解读与Deepctr实现

背景

DIEN(Deep Interest Evolution Network)是DIN的改进版,先回顾一下DIN模型:

在这里插入图片描述

DIN模型的核心思想是:在用户兴趣多样性的推荐背景下,利用用户的历史行为数据,通过Attention机制来捕捉被推荐物品的相对兴趣。DIN模型没有考虑到用户的兴趣的动态变化,如何在CTR中捕捉到用户兴趣的发展变化,就是DIEN要解决的问题。与DIN相似的是:DIEN也是用Attention机制来捕捉与候选广告相关的兴趣发展路径。

基本原理

DIEN致力于捕捉用户的兴趣和模型兴趣的演变过程。如下图所示,DIEN由几个部分组成:首先,通过嵌入层对各类特征进行变换。其次,DIEN采用两个步骤来捕获兴趣的演化:interest extractor layer根据行为序列提取兴趣序列;兴趣进化层描述了与目标物品相关的兴趣进化过程。然后将User Profile, Ad and Context的最终兴趣表示和嵌入向量连接起来。输入MLP进行最终的预测。

在这里插入图片描述

以下是模型的详细说明:

1. 模型的输入

包括四个部分,User Profile包括用户性别,年龄等;User Behacior是用户的历史行为序列,如用户的访问商品id列表;Ad是指被推荐物品的id,商店id等;Context是指设备类型id和时间等。

2. Interest Extractor Layer

DIEN采用GRU捕捉用户兴趣的演变过程,GRU的输入是按其发生时间排序的行为。GRU克服了RNN的消失梯度问题,比LSTM 更快。

GRU的公式如下:
u t = σ ( W u i t + U u h t − 1 + b u ) , r t = σ ( W r i t + U r h t − 1 + b r ) , h ~ t = t a n h ( W h i t + r t ∘ U h h t − 1 + b h ) , h t = ( 1 − u t ) ∘ h t − 1 + u t ∘ h ~ t , u_t = \sigma(W^ui_t+U^uh_{t-1}+b^u), \\ r_t = \sigma(W^ri_t+U^rh_{t-1}+b^r), \\ \tilde{h}_t = tan h(W^hi_t+r_t \circ U^h h_{t-1} + b^h), \\ h_t = (1-u_t) \circ h_{t-1} + u_t \circ \tilde{h}_t, ut=σ(Wuit+Uuht1+bu),rt=σ(Writ+Urht1+br),h~t=tanh(Whit+rtUhht1+bh),ht=(1ut)ht1+uth~t,
其中, i t = e b [ t ] \mathbf i_t = \mathbf e_b[t] it=eb[t]指用户的第 t t t个行为序列, h t \mathbf h_t ht是用户的第 t t t个隐层的输出。

为了让模型更好的学习历史状态的输出 h t h_t ht,DIEN增加了辅助loss,用 b t + 1 \mathbf b_{t+1} bt+1来监督学学习兴趣层的状态 h t \mathbf h_t ht,即用当前行为的下一个行为作为正实例,对除了该正例以外的样本进行负采样作为负样本,进行训练。loss函数如下:
L a u x = − 1 N ( ∑ i = 1 N ∑ t log ⁡ σ ( h t i , e b i [ t + 1 ] ) + log ⁡ ( 1 − σ ( h t i , e ^ b i [ t + 1 ] ) ) ) \begin{aligned} L_{a u x}=-& \frac{1}{N}\left(\sum_{i=1}^{N} \sum_{t} \log \sigma\left(\mathbf{h}_{t}^{i}, \mathbf{e}_{b}^{i}[t+1]\right)\right.\\ &\left.+\log \left(1-\sigma\left(\mathbf{h}_{t}^{i}, \hat{\mathbf{e}}_{b}^{i}[t+1]\right)\right)\right) \end{aligned} Laux=N1(i=1Ntlogσ(hti,ebi[t+1])+log(1σ(hti,e^bi[t+1])))
其中 h t i h_t^i hti 表示第 t 个隐含状态, e b i e_b^i ebi 表示有真实点击行为的正样本, e ^ b i \hat e_b^i e^bi 表示没有点击行为的负样本,其中 σ \sigma σ 表示将向量内积的结果输入到 sigmoid 函数:
σ ( a , b ) = 1 1 + e x p ( − a ⋅ b ) \sigma(a, b) = \frac{1}{1+exp^{(-a \cdot b)}} σ(a,b)=1+exp(ab)1
而全局的损失函数则变为:
L = L t a r g e t + α L a u x L = L_{target} + \alpha L_{aux} L=Ltarget+αLaux
其中, α 是平衡最终损失与兴趣表示的超参数。

引入辅助loss的作用如下:

(1)有助于GRU更好的学习到用户的兴趣演变过程。

(2)有助于降低长序列建模中梯度反向传播的难度

(3)辅助损失函数还有助于嵌入层学习更多的语义信息,得到更好的嵌入矩阵。

3. Interest Evolving Layer

兴趣是多样性的,兴趣可能会相互影响,但是每一种兴趣都有自己的演变过程,例如书籍和衣服的进化过程几乎是单独的。DIEN只关注与目标物品相关的进化过程。

在Interest Extractor Layer层,在辅助loss的帮助下,DIEN得到了比较符合用户兴趣的兴趣序列的表达,在Interest Evolving Layer层,通过对兴趣演化特征的分析,将注意力机制的局部激活能力和GRU的序列学习能力结合起来对兴趣演化进行建模。在此层中,GRU的输入有两个部分 i t ′ 和 a t i'_t和a_t itat i t , i^{,}_t it,是Interest Extractor Layer中的GRU的隐藏状态 h t h_t ht a t a_t at是Attention层的计算结果,计算公式如下:
a t = e x p ( h t W e a ) ∑ j = 1 T e x p ( h j W e a ) a_t = \frac{exp(h_tWe_a)}{\sum_{j=1}^Texp(h_jWe_a)} at=j=1Texp(hjWea)exp(htWea)
式子中的 α t \alpha_t αt 表示相似度系数(注意力得分), e a e_a ea 表示候选商品的 embedding 向量。注意分值可以反映广告 e a e_a ea与输入 h t h_t ht之间的关系,相关性强导致注意分值大。

GRU 得到这两个输入后,如何进行计算,论文给出了三种办法:

(1)GRU with attentional input(AIGRU)

这种方法直接将第二个 GRU 的输入与相似度系数相乘,即:
i t ′ = h t × a t i'_t = h_t \times a_t it=ht×at
但是这种方法效果不好,因为即使用系数 0 来表示候选商品与该兴趣完全无关,这个隐含状态仍然会影响到之后的兴趣演变过程的学习。

(2)Attention based GRU(AGRU)

在问答领域,AGRU 可以有效的提取 query 中的关键信息。具体的,AGRU 利用相似度系数来代替 GRU 的 update gate,通过相似度系数来改变隐藏层的输出:
h t ′ = ( 1 − a t ) × h t − 1 ′ + a t × h ~ t ′ h'_t = (1-a_t)\times h'_{t-1}+a_t \times \tilde{h}_t' ht=(1at)×ht1+at×h~t
虽然 AGRU 可以控制隐藏层的输出,但是它使用了一个标量来代替原来公式中的向量,忽视了不同维度之间的差异。

(3)GRU with attention update gate(AUGRU)

DIEN 的论文中结合了上面的两种方法,提出了 AUGRU:
u ~ t ′ = a t × u t ′ h t ′ = ( 1 − u ~ t ′ ) ∘ h t − 1 ′ + u ~ t ′ ∘ h ~ t ′ \tilde{u}_t' = a_t \times u_t' \\ h_t' = (1-\tilde{u}_t') \circ h_{t-1}' + \tilde{u}_t' \circ \tilde{h}_t' u~t=at×utht=(1u~t)ht1+u~th~t
文中将连续 50 天点击的广告作为样本,用户点击目标商品之前 14 天的点击行为作为历史行为序列。其中前 49 天的样本作为训练集,第 50 天的样本作为测试集。实验表明,AUGRU 效果最佳。

Deepctr实现

例子是在 tensorflow2 的环境中使用了 deepctr 实现的 DIN 模型,deepctr 安装方式如下:

pip install deepctr[gpu]

使用包括两个部分

(1)处理数据


def get_xy_fd(use_neg=False, hash_flag=False):
     # 初始化虚拟数据

    # 行为序列名称,一般包括item_id和item对应的cate_id
    behavior_feature_list = ["item_id", "cate_id"]
    
    # user_id
    uid = np.array([0, 1, 2])
    
    # user 性别特征
    ugender = np.array([0, 1, 0])
    
    # 被推荐的物品的id及其所属类别
    iid = np.array([1, 2, 3])  # 0 is mask value
    cate_id = np.array([1, 2, 2])  # 0 is mask value
    
    # user 的评分特征
    score = np.array([0.1, 0.2, 0.3])
    
    # 用户的历史行为序列,假设长度为4,长度不足4的用0填充
    hist_iid = np.array([[1, 2, 3, 0], [1, 2, 3, 0], [1, 2, 0, 0]])
    hist_cate_id = np.array([[1, 2, 2, 0], [1, 2, 2, 0], [1, 2, 0, 0]])
    
    # 用户历史行为序列长度
    behavior_length = np.array([3, 3, 2])
    
    # 对特征进行嵌入

    # SparseFeat(name, vocabulary_size, embedding_dim,use_hash)
    # 当特征维度过多时,设置use_hash=True进行压缩
    # eg 将3个user分别嵌入到10维
    feature_columns = [SparseFeat('user', 3, embedding_dim=10, use_hash=hash_flag),
                       SparseFeat('gender', 2, embedding_dim=4, use_hash=hash_flag),
                       SparseFeat('item_id', 3 + 1, embedding_dim=8, use_hash=hash_flag),
                       SparseFeat('cate_id', 2 + 1, embedding_dim=4, use_hash=hash_flag),
                       DenseFeat('pay_score', 1)]
    
    # 处理历史序列数据
    # VarLenSparseFeat(sparsefeat, maxlen, combiner, length_name, weight_name,weight_norm)
    # vocabulary_size = 原始的行为种类个数+1,对于长度不足4的部分会用0来填充,因此 vocabulary_size 应该在原来的基础上
    feature_columns += [
            VarLenSparseFeat(SparseFeat('hist_item_id', vocabulary_size=3 + 1, embedding_dim=8, embedding_name='item_id'),
                             maxlen=4, length_name="seq_length"),
            VarLenSparseFeat(SparseFeat('hist_cate_id', 2 + 1, embedding_dim=4, embedding_name='cate_id'), maxlen=4,
                             length_name="seq_length")]

   
    # 构造特征字典
    feature_dict = {'user': uid, 'gender': ugender, 'item_id': iid, 'cate_id': cate_id,
                    'hist_item_id': hist_iid, 'hist_cate_id': hist_cate_id,
                    'pay_score': score, "seq_length": behavior_length}

    # 负采样:neg_hist_item_id 包含了每一个用户的负采样序列,
    # [1, 2, 3, 0]表示第一个人的负采样序列,[1, 2, 3, 0]时第二个人的负采样序列
    if use_neg:
        feature_dict['neg_hist_item_id'] = np.array([[1, 2, 3, 0], [1, 2, 3, 0], [1, 2, 0, 0]])
        feature_dict['neg_hist_cate_id'] = np.array([[1, 2, 2, 0], [1, 2, 2, 0], [1, 2, 0, 0]])
        feature_columns += [
            VarLenSparseFeat(SparseFeat('neg_hist_item_id', vocabulary_size=3 + 1, embedding_dim=8, embedding_name='item_id'),
                             maxlen=4, length_name="seq_length"),
            VarLenSparseFeat(SparseFeat('neg_hist_cate_id', 2 + 1, embedding_dim=4, embedding_name='cate_id'),
                             maxlen=4, length_name="seq_length")]
    
    # 构造输入(x,y)
    x = {name: feature_dict[name] for name in get_feature_names(feature_columns)}
    y = np.array([1, 0, 1])
    return x, y, feature_columns, behavior_feature_list


(2)训练模型


if __name__ == "__main__":
    if tf.__version__ >= '2.0.0':
        tf.compat.v1.disable_eager_execution()
    USE_NEG = True
    x, y, feature_columns, behavior_feature_list = get_xy_fd(use_neg=USE_NEG)
    
    # dnn_hidden_units,dnn的层数,以及每一层的输出个数
    model = DIEN(feature_columns, behavior_feature_list,
                 dnn_hidden_units=[4, 4, 4], dnn_dropout=0.6, gru_type="AUGRU", use_negsampling=USE_NEG)

    model.compile('adam', 'binary_crossentropy',
                  metrics=['binary_crossentropy'])
    history = model.fit(x, y, verbose=1, epochs=10, validation_split=0.5)

参考:

CTR深度学习模型之 DIEN(Deep Interest Evolution Network) 的理解与示例

详解阿里之Deep Interest Evolution Network(AAAI 2019)

Deep Interest and Evolution Network for CTR

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值