欢迎访问某某网站首页!
广告位

百万发-百万发平台-百万发娱乐平台

广告位
当前位置:主页 > 前沿趋势 > 互联网 >

基于PaddlePaddle的点击率的深度学习方法尝试

时间:2018-04-17 09:22 点击: 作者:
[导读]前面在团队内部分享点击率相关的一些文章时,输出了一篇常见计算广告点击率预估算法...

  前言

  前面在团队内部分享点击率相关的一些文章时,输出了一篇常见计算广告点击率预估算法总结,看了一些广告点击率的文章,从最经典的Logistic Regression到Factorization Machined,FFM,FNN,PNN到今年的DeepFM,还有文章里面没有讲的gbdt+lr这类,一直想找时间实践下,正好这次在学习paddle的时候在它的models目录下看到了DeepFM的实现,因为之前对DeepFM有过比较详细的描述,这里稍微复习一下:

  DeepFM更有意思的地方是WDL和FM结合了,其实就是把PNN和WDL结合了,PNN即将FM用神经网络的方式构造了一遍,作为wide的补充,原始的Wide and Deep,Wide的部分只是LR,构造线性关系,Deep部分建模更高阶的关系,所以在Wide and Deep中还需要做一些特征的东西,如Cross Column的工作,而我们知道FM是可以建模二阶关系达到Cross column的效果,DeepFM就是把FM和NN结合,无需再对特征做诸如Cross Column的工作了,这个是我感觉最吸引人的地方,其实FM的部分感觉就是PNN的一次描述,这里只描述下结构图,PNN的部分前面都描述, FM部分:

基于PaddlePaddle的点击率的深度学习方法尝试

  Deep部分:

基于PaddlePaddle的点击率的深度学习方法尝试

  DeepFM相对于FNN、PNN,能够利用其Deep部分建模更高阶信息(二阶以上),而相对于Wide and Deep能够减少特征工程的部分工作,wide部分类似FM建模一、二阶特征间关系, 算是NN和FM的一个更完美的结合方向,另外不同的是如下图,DeepFM的wide和deep部分共享embedding向量空间,wide和deep均可以更新embedding部分,虽说wide部分纯是PNN的工作,但感觉还是蛮有意思的。

基于PaddlePaddle的点击率的深度学习方法尝试

  本文相关代码部分都是来自于paddlepaddle/model, 我这里走一遍流程,学习下,另外想要了解算法原理的可以仔细再看看上面的文章,今天我们来paddlepaddle上做下实验,来从代码程度学习下DeepFM怎么实现的:

  数据集说明

  criteo Display Advertising Challenge,数据主要来criteolab一周的业务数据,用来预测用户在访问页面时,是否会点击某广告。

  wget --no-check-certificate https://s3-eu-west-1.amazonaws.com/criteo-labs/dac.tar.gz

  tar zxf dac.tar.gz

  rm -f dac.tar.gz

  mkdir raw

  mv ./*.txt raw/

  数据有点大, 大概4.26G,慢慢等吧,数据下载完成之后,解压出train.csv,test.csv,其中训练集45840617条样本数,测试集45840617条样本,数据量还是蛮大的。 数据主要有三部分组成:

  label: 广告是否被点击;

  连续性特征: 1-13,为各维度下的统计信息,连续性特征;

  离散型特征:一些被脱敏处理的类目特征

  Overview

  整个项目主要由几个部分组成:

基于PaddlePaddle的点击率的深度学习方法尝试

  数据处理

  这里数据处理主要包括两个部分:

  连续值特征值处理:

  滤除统计次数95%以上的数据,这样可以滤除大部分异值数据,这里的处理方式和以前我在1号店做相关工作时一致,代码里面已经做了这部分工作,直接给出了这部分的特征阈值;

  归一化处理,这里andnew ng的课程有张图很明显,表明不同的特征的值域范围,会使得模型寻优走『之』字形,这样会增加收敛的计算和时间;

  离散特征值处理:

  one-hot: 对应特征值映射到指定维度的只有一个值为1的稀疏变量;

  embedding: 对应特征值映射到指定的特征维度上;

  具体我们来研究下代码:

  class ContinuousFeatureGenerator:

  """

  Normalize the integer features to [0, 1] by min-max normalization

  """

  def __init__(self, num_feature):

  self.num_feature = num_feature

  self.min = [sys.maxint] * num_feature

  self.max = [-sys.maxint] * num_feature

  def build(self, datafile, continous_features):

  with open(datafile, 'r') as f:

  for line in f:

  features = line.rstrip('\n').split('\t')

  for i in range(0, self.num_feature):

  val = features[continous_features[i]]

  if val != '':

  val = int(val)

  if val > continous_clip[i]:

  val = continous_clip[i]

  self.min[i] = min(self.min[i], val)

  self.max[i] = max(self.max[i], val)

  def gen(self, idx, val):

  if val == '':

  return 0.0

  val = float(val)

  return (val - self.min[idx]) / (self.max[idx] - self.min[idx])

  连续特征是在1-13的位置,读取文件,如果值大于对应维度的特征值的95%阈值,则该特征值置为该阈值,并计算特征维度的最大、最小值,在gen时归一化处理。

  class CategoryDictGenerator:

  """

  Generate dictionary for each of the categorical features

  """

  def __init__(self, num_feature):

  self.dicts = []

  self.num_feature = num_feature

  for i in range(0, num_feature):

  self.dicts.append(collections.defaultdict(int))

  def build(self, datafile, categorial_features, cutoff=0):

  with open(datafile, 'r') as f:

  for line in f:

  features = line.rstrip('\n').split('\t')

  for i in range(0, self.num_feature):

  if features[categorial_features[i]] != '':

  self.dicts[i][features[categorial_features[i]]] += 1

  for i in range(0, self.num_feature):

  self.dicts[i] = filter(lambda x: x[1] >= cutoff,

  self.dicts[i].items())

  self.dicts[i] = sorted(self.dicts[i], key=lambda x: (-x[1], x[0]))

  vocabs, _ = list(zip(*self.dicts[i]))

  self.dicts[i] = dict(zip(vocabs, range(1, len(vocabs) + 1)))

  self.dicts[i][''] = 0

  def gen(self, idx, key):

  if key not in self.dicts[idx]:

  res = self.dicts[idx]['']

  else:

  res = self.dicts[idx][key]

  return res

  def dicts_sizes(self):

  return map(len, self.dicts)