项目地址:StephenBo-China/recommendation_system_sort_model

阿里天池数据集地址:数据集-阿里云天池

最近由于工作需要用tensorflow2.0复现了下阿里的兴趣模型,本文将先对论文进行描述,再介绍如何使用tensorflow2.0进行复现。建议阅读本文前先读一遍论文,本文的论文解析部分可作为辅助。

一、论文解析:

Base Model:


base model比较简单,就是将用户历史行为序列的各个商品的类别特征embedding之后,与非类别特征concat后进入pooling层(sum pooling与average pooling均可,具体可根据实际情况进行调整);pooling后与embedding后的用户画像特征及目标商品特征一起concat后进入最后的MLP得到最终的分类结果。

  1. Embedding Layer:

  • input: high dimensional binary vectors

  • ouput: low dimensional dence representations

对于第i个category特征来说,该特征有K个不同类别的值,我们希望将它embedding到D维,则:

739ca73c-d240-eb11-8da9-e4434bdf6706.svg

为第i个特征的词典, 759ca73c-d240-eb11-8da9-e4434bdf6706.svg 为第j个类别的embedding后的向量。具体的embedding的其他方法可阅读embedding相关的paper。

2. Pooling Layer:

由于MLP的输入层必须是固定长度的节点数,但是不同用户的历史行为序列长度不同,因而我们没有办法直接对行为序列特征进行concat,为了使得embedding后的行为序列长度相同,我们可以采用pooling操作让行为序列产出的特征长度相同,pooling可使用sum pooling或average pooling。sum对emebdding后的行为序列的各个item求和,average对embedding后的行为序列的各个item求均值得到。

779ca73c-d240-eb11-8da9-e4434bdf6706.svg

3. MLP:

输入为concat(用户画像特征,pooling(embedding(历史行为序列)),目标商品特征)的全连接神经网络,最终使用softmax得到最终分类结果。loss采用log loss。

Base Mode存在的问题:

  1. pooling直接处理用户历史行为序列会有信息丢失:

Base模型中直接对多个Embedding向量进行等权的sum-pooling,这种方法肯定会带来信息的丢失,而且相对重要的Embedding向量也无法完全突出自己所包含的信息。base model中使用average pooling或者sum pooling对emebdding后的历史行为序列进行处理,对不同用户的行为序列得到同长度的MLP输入。但是这样做对于行为序列内的所有内容都是一视同仁的,不利于用户兴趣的表达。比较简单的方法是将embedding后的向量直接展开,但是这样会增加embedding层的学习权重,容易导致过拟合(个人理解,这样做不但会增加训练权重,且依然不能表达用户的兴趣信息,但是论文中是这样说的)。

2. 如何让Base Model的pooling层产出能够表达出用户兴趣信息?

用Attention给pooling添加权重后求和,让模型更加关注有用的信息。因为在用户的历史行为序列中,展现item的数量要多于点击item,如果直接对行为序列等权sum pooling,展现序列贡献的信息要更多,但是点击item更加能体现用户的实际兴趣,因而加权后进行pooling可以提取出更多的兴趣信息。

DIN:


  1. DIN创新点:

(1) weighted-sum pooling:

a. weighted-sum pooling方法:

在历史行为序列进入pooling前,加入attention层,计算出行为序列中不同item的权重a_i后,再使用如下公式得到sum pooling的结果:

799ca73c-d240-eb11-8da9-e4434bdf6706.svg

其中, 7c9ca73c-d240-eb11-8da9-e4434bdf6706.svg 为embedding后的用户行为序列; 7e9ca73c-d240-eb11-8da9-e4434bdf6706.svg 是emebdding后目标商品的特征向量; 809ca73c-d240-eb11-8da9-e4434bdf6706.svg 为attention层产出结果。

本文attention不使用传统的attention方法,需单独维护一个全连接神经网络得到最终attention结果,attention神经网络的输入为:行为序列中的每个item组合上目标商品的特征concat后作为输入,输出为各个item的权重。即用全连接层得到行为序列各个item的attention权重后,直接使用attention的权重向量与emebdding后行为序列做矩阵乘法即可得到 829ca73c-d240-eb11-8da9-e4434bdf6706.svg 。

attention的神经网络结构如下图所示:

b. weighted-sum pooling为什么不用传统attenion方法:

传统方法使用softmax获得attention产出的权重被遗弃,因为传统方法使得attention的权重求和为1,这样求得的权重并不是用户兴趣的分布估计,采用神经网络最终使用sigmoid或其他激活函数得到序列attention权重分布,从而获得对用户兴趣的权重估计更适用于用户兴趣的表示。比如一个用户历史行为序列中90%都是衣服,10%是电子产品。如果目标商品是t恤和手机的话,传统attention方法会使得t恤的 899ca73c-d240-eb11-8da9-e4434bdf6706.svg 高于手机,因为用户的行为序列中大部分都是衣服,但是用户购买手机与否与他购买衣服与否是没有直接关联的,因而不能用传统的方法直接求softmax,softmax使得行为序列中的各个item都有了关联从而得出了商品权重。实际使用时可尝试最后激活函数使用softmax与sigmoid分别看效果后进行选择。

2. Dice激活函数:

8b9ca73c-d240-eb11-8da9-e4434bdf6706.svg

8e9ca73c-d240-eb11-8da9-e4434bdf6706.svg

919ca73c-d240-eb11-8da9-e4434bdf6706.svg

可也看到,每一个yi对应了一个概率值pi。pi的计算主要分为两步:将yi进行标准化和进行sigmoid变换。Dice激活函数与batch normalisation一样都是用来解决Internal Covariate Shift问题。

3. Mini-batch Aware Regularization:

商品id等需要embedding的特征,有些特征非常稀疏,导致embedding的训练参数过多,很容易造成过拟合。用户对于商品的数据符合长尾定律,也就是说有些id只出现了几次,而以下部分id会出现很多次,这样训练过程中就加入了更多噪声,使得模型更容易过拟合。因而需要正则化的方法来防止过拟合,但是由于DIN的大部分训练权重都是由embedding贡献的,直接加入L1正则或者L2正则的话会提高模型训练的复杂度(对于一个mini-batch来说,在没有L2正则时,梯度下降只需要更新embedding中的非0参数,但是加入L2正则由于要计算L2-norm,则需要对所有的参数进行计算)。因而论文提出了mini-batch regularization的方法,让模型自适应各个embedding feature的正则化强度。

最终推导出的权重更新公式如下:


由于Mini-batch Aware Regularization是生效在embedding层的权重更新中,且只有在需要embedding的特征非常稀疏时,才需要该正则化方法,因而本文直接使用tensorflow自带的embedding层,只要输入特征不是非常稀疏,不用该方法不会影响到模型最终效果。

二、实现:

模型自定义层:

1.weighted-sum pooling:

Attention层输入为行为序列与target item组合后的特征,由于一个batch下的行为序列长度不同,因而给长度不足最大长度的补1padding后,输入到全连接神经网络,最后通过sigmoid激活函数得到a(i)的值,在用得到的权重向量与embedding后的行为序列矩阵做矩阵乘法即得到了权重乘以各个item后求和pooling的结果。

class attention(tf.keras.layers.Layer):    def __init__(self, keys_dim):        super(attention, self).__init__()        self.keys_dim = keys_dim        self.fc = tf.keras.Sequential()        self.fc.add(layers.BatchNormalization())        self.fc.add(layers.Dense(100, activation="sigmoid"))         self.fc.add(layers.ReLU())        self.fc.add(layers.Dense(50, activation="sigmoid"))        self.fc.add(layers.ReLU())        self.fc.add(layers.Dense(1, activation=None))

    def call(self, queries, keys, keys_length):        #Attention        queries = tf.tile(tf.expand_dims(queries, 1), [1, tf.shape(keys)[1], 1])        din_all = tf.concat([queries, keys, queries-keys, queries*keys], axis=-1)        outputs = tf.transpose(self.fc(din_all), [0,2,1])        key_masks = tf.sequence_mask(keys_length, max(keys_length), dtype=tf.bool)        key_masks = tf.expand_dims(key_masks, 1)        paddings = tf.ones_like(outputs) * (-2 ** 32 + 1)        outputs = tf.where(key_masks, outputs, paddings)        outputs = outputs / (self.keys_dim ** 0.5)        #outputs = tf.keras.activations.softmax(outputs, -1)        outputs = tf.keras.activations.sigmoid(outputs)

        #Sum Pooling        outputs = tf.squeeze(tf.matmul(outputs, keys))        print("outputs:" + str(outputs.numpy().shape))        return outputs

2.Dice激活函数:

class dice(tf.keras.layers.Layer):    def __init__(self, feat_dim):        super(dice, self).__init__()        self.feat_dim = feat_dim        self.alphas= tf.Variable(tf.zeros([feat_dim]), dtype=tf.float32)        self.beta  = tf.Variable(tf.zeros([feat_dim]), dtype=tf.float32)

        self.bn = tf.keras.layers.BatchNormalization(center=False, scale=False)

    def call(self, _x, axis=-1, epsilon=0.000000001):

        reduction_axes = list(range(len(_x.get_shape())))        del reduction_axes[axis]        broadcast_shape = [1] * len(_x.get_shape())        broadcast_shape[axis] = self.feat_dim

        mean = tf.reduce_mean(_x, axis=reduction_axes)        brodcast_mean = tf.reshape(mean, broadcast_shape)        std = tf.reduce_mean(tf.square(_x - brodcast_mean) + epsilon, axis=reduction_axes)        std = tf.sqrt(std)        brodcast_std = tf.reshape(std, broadcast_shape)

        x_normed = self.bn(_x)        x_p = tf.keras.activations.sigmoid(self.beta * x_normed)

        return self.alphas * (1.0 - x_p) * _x + x_p * _x

整体实现:

除了weighted-sum pooling为din的自定义层,其余均为tensorflow包含层,直接通过论文中模型结构构造整个模型即可:


class DIN(tf.keras.Model):    def __init__(self, embedding_count_dict, embedding_dim_dict, embedding_features_list, user_behavior_features, activation="PReLU"):        super(DIN, self).__init__(embedding_count_dict, embedding_dim_dict, embedding_features_list, user_behavior_features, activation)        #Init Embedding Layer        self.embedding_dim_dict = embedding_dim_dict        self.embedding_count_dict = embedding_count_dict        self.embedding_layers = dict()        for feature in embedding_features_list:            self.embedding_layers[feature] = layers.Embedding(embedding_count_dict[feature], embedding_dim_dict[feature])        #DIN Attention+Sum pooling        self.hist_at = attention(utils.get_input_dim(embedding_dim_dict, user_behavior_features))        #Init Fully Connection Layer        self.fc = tf.keras.Sequential()        self.fc.add(layers.BatchNormalization())        self.fc.add(layers.Dense(200, activation="relu"))         if activation == "Dice":            self.fc.add(Dice())        elif activation == "dice":            self.fc.add(dice(200))        elif activation == "PReLU":            self.fc.add(layers.PReLU(alpha_initializer='zeros', weights=None))        self.fc.add(layers.Dense(80, activation="relu"))        if activation == "Dice":            self.fc.add(Dice())         elif activation == "dice":            self.fc.add(dice(80))        elif activation == "PReLU":            self.fc.add(layers.PReLU(alpha_initializer='zeros', weights=None))        self.fc.add(layers.Dense(2, activation=None))

    def get_emb_din(self, user_profile_dict, user_profile_list, hist_behavior_dict, target_item_dict, user_behavior_list):        user_profile_feature_embedding = dict()        for feature in user_profile_list:            data = user_profile_dict[feature]            embedding_layer = self.embedding_layers[feature]            user_profile_feature_embedding[feature] = embedding_layer(data)

        target_item_feature_embedding = dict()        for feature in user_behavior_list:            data = target_item_dict[feature]            embedding_layer = self.embedding_layers[feature]            target_item_feature_embedding[feature] = embedding_layer(data)

        hist_behavior_embedding = dict()        for feature in user_behavior_list:            data = hist_behavior_dict[feature]            embedding_layer = self.embedding_layers[feature]            hist_behavior_embedding[feature] = embedding_layer(data)

        return utils.concat_features(user_profile_feature_embedding), utils.concat_features(target_item_feature_embedding), utils.concat_features(hist_behavior_embedding)

    def call(self, user_profile_dict, user_profile_list, hist_behavior_dict, target_item_dict, user_behavior_list, length):        #Embedding Layer        user_profile_embedding, target_item_embedding, hist_behavior_emebedding = self.get_emb_din(user_profile_dict, user_profile_list, hist_behavior_dict, target_item_dict, user_behavior_list)        hist_attn_emb = self.hist_at(target_item_embedding, hist_behavior_emebedding, length)        join_emb = tf.concat([user_profile_embedding, target_item_embedding, hist_attn_emb], -1)        logit = tf.squeeze(self.fc(join_emb))        output = tf.keras.activations.softmax(logit)        return output, logit

三、实验:

本文使用阿里天池数据集进行实验:

数据集介绍:

  1. 用户行为日志:

本数据集涵盖了raw_sample中全部用户22天内的购物行为(共七亿条记录)。字段说明如下:

(1) user:脱敏过的用户ID;

(2) time_stamp:时间戳;

(3) btag:行为类型, 包括以下四种:浏览、加入购物车、喜欢、购买。其中浏览为负例(展现未点击),其他均为正例(点击)。

2. 用户画像特征:

(1) cms_segid:微群ID;

(2) cms_group_id:cms_group_id;

(3) final_gender_code:性别 1:男,2:女;

(4) age_level:年龄层次;

(5) pvalue_level:消费档次,1:低档,2:中档,3:高档;

(6) shopping_level:购物深度,1:浅层用户,2:中度用户,3:深度用户

(7) occupation:是否大学生 ,1:是,0:否

(8) new_user_class_level:城市层级

3. 目标商品:

(1) user_id:脱敏过的用户ID;

(2) adgroup_id:脱敏过的广告单元ID;

(3) time_stamp:时间戳;

(4) pid:资源位;

(5) noclk:为1代表没有点击;为0代表点击;

(6) clk:为0代表没有点击;为1代表点击;

4. 内容特征:

(1) adgroup_id:脱敏过的广告ID;

(2) cate_id:脱敏过的商品类目ID;

(3) campaign_id:脱敏过的广告计划ID;

(4) customer_id:脱敏过的广告主ID;

(5) brand:脱敏过的品牌ID;

(6) price: 宝贝的价格

5. 已处理好的实验数据集:

备注:实际训练样本共23249296条,测试样本共3308665条【用前面7天的做训练样本(20170506-20170512),用第8天的做测试样本(20170513)】。为快速评估复现模型,仅取训练样本中10000条样本进行训练,测试样本中1000条进行测试。

训练loss:

(epoch=3,batch=100,训练数据集10000条,测试数据集1000条)


模型评估:

  1. 评估报告

2. ROC曲线及AUC:


兴趣点推荐代码_推荐系统模型阿里用户兴趣模型(附完整代码)相关推荐

  1. java登录注册抽奖完整代码_JAVA实现用户抽奖功能(附完整代码)

    需求分析 1)实现三个基本功能:登录.注册.抽奖. 2)登录:用户输入账号密码进行登录,输入账号后会匹配已注册的用户,若输入用户不存在则退出,密码有三次输入机会,登录成功后主界面会显示已登录用户的账号 ...

  2. OpenCV演示代码以查找图像中的轮廓(附完整代码)

    OpenCV演示代码以查找图像中的轮廓 OpenCV演示代码以查找图像中的轮廓 OpenCV演示代码以查找图像中的轮廓 #include "opencv2/imgcodecs.hpp&quo ...

  3. bp神经网络预测python代码_机器学习之多层神经网络(附Python代码和数据)

    1 引言 多层神经网络,Multiple-layers Perceptron (MLP),又被称为多层感知机,是机器学习中深度学习的典型算法.关于多层神经网络的算法原理,我们在Stata和R实现的文章 ...

  4. 距离矢量路由算法的java代码_八大排序算法比较(附Java代码)

    冒泡排序 /*** 冒泡排序 比较好理解* 两两相比 较大的放后面* 时间复杂度O(n^2)*//*** 改进前的冒泡排序算法进行100,000数据排序运行时间为:3829ms* 优化后的冒泡排序算法 ...

  5. PCL提取3D点云模型特征(3.0 FPFH快速点特征直方图)附完整代码

    一.概述 上一篇博客解释了PFH是什么以及如何利用PFH来提取点云的特征,那么讲了PFH(PCL提取3D点云模型特征(2.0 PFH点特征直方图 )附完整代码)之后肯定是要接着说FPFH的.本来想着把 ...

  6. c++ 三次多项式拟合_线性回归进阶版,多项式线性回归讲解与实现(附完整代码)...

    每天给小编五分钟,小编用自己的代码,带你轻松学习深度学习!本文将会带你做完一个深度学习进阶版的线性回归---多项式线性回归,带你进一步掌握线性回归这一深度学习经典模型,然后在此基础上,小编将在下篇文章 ...

  7. Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一)

    Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一) 本文目录: 一.[旋转的精灵女孩]案例运行效果 二.Three.js简介 三.Three.js代码正常运行显示条件 (1)不载入 ...

  8. 对“科大讯飞2021丨广告点击率预估挑战赛 Top1方案(附完整代码)_Jack_Yang的博客-CSDN博客”的补充。

    这篇文章的初衷是针对科大讯飞2021丨广告点击率预估挑战赛 Top1方案(附完整代码)_Jack_Yang的博客-CSDN博客进行补充. 博客的信息量很少,对任务背景的介绍也不太对,说实话令人费解.我 ...

  9. 吴恩达机器学习python实现(6):SVM支持向量机(文末附完整代码)

    所有的数据来源:链接:https://pan.baidu.com/s/1vTaw1n77xPPfKk23KEKARA 提取码:5gl2 1 Support Vector Machines 1.1 Pr ...

最新文章

  1. linux 驱动 内核模式,Linux内核模块和驱动的编写
  2. 树莓派获4500万美元融资,估值已达5亿美元,去年创下了710万台销量纪录
  3. 用计算机探索规律反思,《用计算器探索规律》教学反思
  4. SQL语句中的AND和OR执行顺序问题
  5. 【转载】ABAP自定义长文本的处理
  6. 使用VMware VSphere WebService SDK进行开发 (四)——获取集群(Cluster, ComputeResource)的相关信息
  7. spi 协议驱动设计
  8. Javascript 调试技巧
  9. 关于分布式集群的几个问题
  10. 蓝牙GFSK基带调制解调
  11. 21-win10下ElasticSearch.6.1.0安装SQL插件
  12. DL_C1_week4-1(Build Deep Neural Network)
  13. printf()输出格式大全(附 - 示例代码)
  14. java 获取上周开始时间和结束时间,上上周开始和上上周结束时间
  15. 【Linux 内核】实时调度类 ④ ( 实时运行队列 rt_rq 源码分析 | 实时运行队列 rt_rq 结构体字段分析 | active、rt_nr_running、curr、next 字段 )
  16. 佛说五百年的回眸才换来今生的擦肩而过
  17. 微信公众平台编辑器可收藏、使用模版了
  18. 产品设计--七大定律
  19. JPA Spring Data JPA详解
  20. TM1621数码管驱动

热门文章

  1. 基于TF-IDF编码进行文本聚类分析:文档成对相似性计算、层次聚类(链接矩阵、树形图dendrogram绘制、聚类标签)
  2. Python编码实现冒泡排序
  3. TPOT: 自动化的sklearn
  4. ggsave的图片图例不显示中文解决办法
  5. postman 接口测试工具介绍
  6. 非线性求解器Casadi使用简介
  7. java6特性_Java6的新特性
  8. java中保存图片到本地_java保存网络图片到本地
  9. tf.keras.losses.SparseCategoricalCrossentropy() 稀疏交叉熵 损失函数 示例
  10. javascript 判断 前端 是 pc端 还是 移动端