兴趣点推荐代码_推荐系统模型阿里用户兴趣模型(附完整代码)
项目地址: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得到最终的分类结果。
Embedding Layer:
input: high dimensional binary vectors
ouput: low dimensional dence representations
对于第i个category特征来说,该特征有K个不同类别的值,我们希望将它embedding到D维,则:
为第i个特征的词典, 为第j个类别的embedding后的向量。具体的embedding的其他方法可阅读embedding相关的paper。
2. Pooling Layer:
由于MLP的输入层必须是固定长度的节点数,但是不同用户的历史行为序列长度不同,因而我们没有办法直接对行为序列特征进行concat,为了使得embedding后的行为序列长度相同,我们可以采用pooling操作让行为序列产出的特征长度相同,pooling可使用sum pooling或average pooling。sum对emebdding后的行为序列的各个item求和,average对embedding后的行为序列的各个item求均值得到。
3. MLP:
输入为concat(用户画像特征,pooling(embedding(历史行为序列)),目标商品特征)的全连接神经网络,最终使用softmax得到最终分类结果。loss采用log loss。
Base Mode存在的问题:
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:
DIN创新点:
(1) weighted-sum pooling:
a. weighted-sum pooling方法:
在历史行为序列进入pooling前,加入attention层,计算出行为序列中不同item的权重a_i后,再使用如下公式得到sum pooling的结果:
其中, 为embedding后的用户行为序列; 是emebdding后目标商品的特征向量; 为attention层产出结果。
本文attention不使用传统的attention方法,需单独维护一个全连接神经网络得到最终attention结果,attention神经网络的输入为:行为序列中的每个item组合上目标商品的特征concat后作为输入,输出为各个item的权重。即用全连接层得到行为序列各个item的attention权重后,直接使用attention的权重向量与emebdding后行为序列做矩阵乘法即可得到 。
attention的神经网络结构如下图所示:
b. weighted-sum pooling为什么不用传统attenion方法:
传统方法使用softmax获得attention产出的权重被遗弃,因为传统方法使得attention的权重求和为1,这样求得的权重并不是用户兴趣的分布估计,采用神经网络最终使用sigmoid或其他激活函数得到序列attention权重分布,从而获得对用户兴趣的权重估计更适用于用户兴趣的表示。比如一个用户历史行为序列中90%都是衣服,10%是电子产品。如果目标商品是t恤和手机的话,传统attention方法会使得t恤的 高于手机,因为用户的行为序列中大部分都是衣服,但是用户购买手机与否与他购买衣服与否是没有直接关联的,因而不能用传统的方法直接求softmax,softmax使得行为序列中的各个item都有了关联从而得出了商品权重。实际使用时可尝试最后激活函数使用softmax与sigmoid分别看效果后进行选择。
2. Dice激活函数:
可也看到,每一个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
三、实验:
本文使用阿里天池数据集进行实验:
数据集介绍:
用户行为日志:
本数据集涵盖了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条)
模型评估:
评估报告
2. ROC曲线及AUC:
兴趣点推荐代码_推荐系统模型阿里用户兴趣模型(附完整代码)相关推荐
- java登录注册抽奖完整代码_JAVA实现用户抽奖功能(附完整代码)
需求分析 1)实现三个基本功能:登录.注册.抽奖. 2)登录:用户输入账号密码进行登录,输入账号后会匹配已注册的用户,若输入用户不存在则退出,密码有三次输入机会,登录成功后主界面会显示已登录用户的账号 ...
- OpenCV演示代码以查找图像中的轮廓(附完整代码)
OpenCV演示代码以查找图像中的轮廓 OpenCV演示代码以查找图像中的轮廓 OpenCV演示代码以查找图像中的轮廓 #include "opencv2/imgcodecs.hpp&quo ...
- bp神经网络预测python代码_机器学习之多层神经网络(附Python代码和数据)
1 引言 多层神经网络,Multiple-layers Perceptron (MLP),又被称为多层感知机,是机器学习中深度学习的典型算法.关于多层神经网络的算法原理,我们在Stata和R实现的文章 ...
- 距离矢量路由算法的java代码_八大排序算法比较(附Java代码)
冒泡排序 /*** 冒泡排序 比较好理解* 两两相比 较大的放后面* 时间复杂度O(n^2)*//*** 改进前的冒泡排序算法进行100,000数据排序运行时间为:3829ms* 优化后的冒泡排序算法 ...
- PCL提取3D点云模型特征(3.0 FPFH快速点特征直方图)附完整代码
一.概述 上一篇博客解释了PFH是什么以及如何利用PFH来提取点云的特征,那么讲了PFH(PCL提取3D点云模型特征(2.0 PFH点特征直方图 )附完整代码)之后肯定是要接着说FPFH的.本来想着把 ...
- c++ 三次多项式拟合_线性回归进阶版,多项式线性回归讲解与实现(附完整代码)...
每天给小编五分钟,小编用自己的代码,带你轻松学习深度学习!本文将会带你做完一个深度学习进阶版的线性回归---多项式线性回归,带你进一步掌握线性回归这一深度学习经典模型,然后在此基础上,小编将在下篇文章 ...
- Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一)
Three.js实例详解___旋转的精灵女孩(附完整代码和资源)(一) 本文目录: 一.[旋转的精灵女孩]案例运行效果 二.Three.js简介 三.Three.js代码正常运行显示条件 (1)不载入 ...
- 对“科大讯飞2021丨广告点击率预估挑战赛 Top1方案(附完整代码)_Jack_Yang的博客-CSDN博客”的补充。
这篇文章的初衷是针对科大讯飞2021丨广告点击率预估挑战赛 Top1方案(附完整代码)_Jack_Yang的博客-CSDN博客进行补充. 博客的信息量很少,对任务背景的介绍也不太对,说实话令人费解.我 ...
- 吴恩达机器学习python实现(6):SVM支持向量机(文末附完整代码)
所有的数据来源:链接:https://pan.baidu.com/s/1vTaw1n77xPPfKk23KEKARA 提取码:5gl2 1 Support Vector Machines 1.1 Pr ...
最新文章
- linux 驱动 内核模式,Linux内核模块和驱动的编写
- 树莓派获4500万美元融资,估值已达5亿美元,去年创下了710万台销量纪录
- 用计算机探索规律反思,《用计算器探索规律》教学反思
- SQL语句中的AND和OR执行顺序问题
- 【转载】ABAP自定义长文本的处理
- 使用VMware VSphere WebService SDK进行开发 (四)——获取集群(Cluster, ComputeResource)的相关信息
- spi 协议驱动设计
- Javascript 调试技巧
- 关于分布式集群的几个问题
- 蓝牙GFSK基带调制解调
- 21-win10下ElasticSearch.6.1.0安装SQL插件
- DL_C1_week4-1(Build Deep Neural Network)
- printf()输出格式大全(附 - 示例代码)
- java 获取上周开始时间和结束时间,上上周开始和上上周结束时间
- 【Linux 内核】实时调度类 ④ ( 实时运行队列 rt_rq 源码分析 | 实时运行队列 rt_rq 结构体字段分析 | active、rt_nr_running、curr、next 字段 )
- 佛说五百年的回眸才换来今生的擦肩而过
- 微信公众平台编辑器可收藏、使用模版了
- 产品设计--七大定律
- JPA Spring Data JPA详解
- TM1621数码管驱动
热门文章
- 基于TF-IDF编码进行文本聚类分析:文档成对相似性计算、层次聚类(链接矩阵、树形图dendrogram绘制、聚类标签)
- Python编码实现冒泡排序
- TPOT: 自动化的sklearn
- ggsave的图片图例不显示中文解决办法
- postman 接口测试工具介绍
- 非线性求解器Casadi使用简介
- java6特性_Java6的新特性
- java中保存图片到本地_java保存网络图片到本地
- tf.keras.losses.SparseCategoricalCrossentropy() 稀疏交叉熵 损失函数 示例
- javascript 判断 前端 是 pc端 还是 移动端