我用过的CTR预估模型

        之前也想过把实习和工作中曾经用过的模型总结一下,但是由于工作的原因一直抽不出时间,最近刚好有时间做一下总结。总体上来说只是记录个大概,因为有太多东西要写了,所以有兴趣的朋友可以在合适的时间,采用对话的形式针对每个细节讨论。主要包括以下几个模型:LR,GBDT,FM,FFM,DNN,还有一些自己对分布式系统的想法。

       第一次看到工业界使用LR模型,是在阿里实习的时候,当时用它来做展示广告的CTR预估,我们作为一个业务部门使用公司内部的分布式机器学习平台,当时还是用MPI实现的。后来在百度实习,用到了LR和DNN模型,LR也是用MPI实现的,有多种优化算法可选,SGD,LBFGS,OWLQN,对于做广告CTR预估的我们来说,选用OWLQN的最多,毕竟我们对模型的稀疏性有要求,OWLQN能对带有L1正则项的目标函数进行优化。DNN用的现在比较有名的paddle,当时还没开源但是内部已经开始用了。最后一份实习在360,当时主要是探索DNN在搜索广告上的效果,由于内部没有可用的分布式深度学习系统,在经过对比之后就选择了Petuum(http://www.petuum.com/),在这里感谢一下Petuum团队的朋友们,感谢他们非常热情的回复邮件帮我解答问题,希望我那蹩脚的英文也曾给他们带来过一些欢乐。。。 主要工作是根据360搜索广告已有数据构造特征,并针对petuum系统修改源代码做一些小改动,例如改变激活函数类型、参数初始化方式等。在360实习期间和纽约大学的一个小伙伴参加了kaggle上的一个展示广告CTR预估比赛,用到了FM模型,主要还是特征的处理,特别是连续值特征的离散化处理,成绩一般般第16名。

       毕业之后在第一家公司做风险控制,用到了GBDT和GBRT模型,因为是不同的业务需求,前者用来做分类,后者用来做回归。第二家公司,先是用GBDT做新闻推荐,后来使用ps-lite实现了用ftrl做优化算法的LR模型,又使用MPI实现集成了LR、FM、FFM三个模型的系统,同时提供了SGD和FTRL优化算法,本来要再提供OWLQN优化算法,但是要测性能,OWLQN的代码写完后就没来得及测。主要目的是打算做online learning,做出来之后做过一些性能测试实验,通过观察测试集AUC,FM在初期学习速率比LR快,例如LR在40次迭代之后达到0.7,FM在20次迭代之后就达到了,但是最终的结果FM并没有比LR高,两者的结果是差不多的,这和当时没有充分调参有关。

       总体上来说,LR是使用最广泛的模型,一般是结合onehot encoding之后的特征使用,我总结它有以下几个优点:1,模型简单,可以尽管拍脑袋加特征,2,容易debug,线上出了问题很容易就能根据模型和结果定位到是哪些特征出了问题,并且可以马上采取补救措施,例如人工修改模型权重,以达到增强或者减弱某些特征对结果的影响。3,有非常多的优化算法可选择,例如SGD,CD,LBFGS,OWLQN,FTRL等等,而且这些优化算法又有各种变体,可以尽管尝试,4,繁多的优化算法中,有些算法可以有效的产生稀疏模型并使得效果不损失,这对上线非常有利,也有利于减少线上平响时间。缺点:1,只是个线性模型,不是对所有的数据都能很有效,不同的业务其数据分布都不同,有些业务的数据可能还是需要非线性的模型来学习。

       GBDT也是一个被广泛使用的模型,一般是配合连续值特征使用,它的特点是:1,特征值在不断变化,knowledge存在于数据和模型中,因此,即使模型更新频率低一些,结果也暂时不会差到哪里去,从这个角度来讲,GBDT比较“稳定”。2,非线性模型,能学习复杂的数据分布。和LR相比,他不需要那么频繁的更新模型,而且基本上不存在线上特征miss的问题,如果是LR模型,可能一天之内某些ID特征就发生了很大的变化,导致线上出现大量的新ID特征无法命中。

       FM比LR、GBDT更晚一些,它的发明者是来自德国的一个大神也就是libFM的作者。与LR模型相比较,输入给FM模型的每个特征,除了学到一个对应的权重之外,还能得到一组权重,存在于vector之中,当需要把特征a和特征b进行组合时,就把a的那个vector和b的那个vector拿来做内积,这么做的好处:1,可以自动的进行特征组合,是的模型具有更强的学习能力。2,不但能做特征组合,vector的方式还解决了特征组合后维度过大的问题。关于FM模型的数学推导,可以找到那篇论文进行学习。现在FM模型也被各个公司广泛使用了。

       FFM是FM的增强版,它也能做特征组合,而且对于每个特征,它可以得到好几个vector,例如,对于特征a它可以有v1,v2两组权重,这两组权重分别用来和其它不同特征做组合,当特征a和特征b组合时用v1,当特征a和特征c组合时用v2. 这么做的好处是,即使是同一个特征,和其它不同特征组合时是有强弱之分的,这样使得模型更“个性化”,从而得到更好的效果。关于FFM模型的数学推导,也可以参考原始论文。目前在线上用FFM的并不多,因为线上预测时平响相对较大。

       DNN模型很多人都非常熟悉了,一般使用连续值特征,否则离线训练时间难以承受。优点是:非线性,模型表达能力强,只需简单的三层就能表示任意阶的函数。缺点是:1,离线训练时间较慢,不过目前好在有TensorFlow,Mxnet这样的分布式训练系统。2,线上预测时间较长,这也是阻碍DNN被广泛使用的原因之一。

       以上LR,GBDT,FM,FFM,DNN模型,都有相关的论文可以阅读学习,最好是找到关于理论推导方面的论文进行研究。

       从最开始的LR,GBDT到后来的FM,DNN,关于这些模型在广告和推荐问题上的应用的论文也越来越多,从简单到复杂,从单模型到模型融合,比如LR和GBDT,LR和DNN,FM和DNN各种各样的模型组合方式:stacking,bagging,boosting, 相信以后这方面的论文会越来越多。

       另外就是关于oneline learning,以上几个模型除了GBDT无法做online learning之外,其它几个模型理论上都是可以的,online learning的好处是可以更快地学到用户的行为,但这会造成它的模型不太稳定,整体上来看最终结果不一定比batch learning更好。因此,一般是采用一个离线的稳定模型+增量模型的方式,这又涉及到几个问题:1,模型的更新频率;2,增量模型中新特征参数的加入方式;2,增量模型中旧特征参数的加入方式。以上三个因素会严重影响online learning的效果。online learning一般是结合特定的优化算法例如SGD,FTRL来实现的,目的是为了使得模型更新频率更快,FTRL会让得到的模型更稀疏。

       关于优化算法,对于LR模型来说,使用不同的正则项就选择对应的优化算法,L2正则就选LBFGS或者SGD,不过SGD一般很少用,L1正则就选择OWLQN或者FTRL,OWLQN是用来做batch learning,FTRL是用来做online learning,现在FTRL已经被非常多的使用了。针对FM,FFM模型,目前最常用的方式也是FTRL,而且对于这两个模型,可以分别针对w和v采用不同的优化算法,例如DMLC的wormhole中对FM模型的优化就采用了两种不同优化算法。这取决于用户是否希望v更稀疏,如果希望得到dense的v就不对v加L1约束,如果希望得到sparse的v就对v加L1约束,这要看用户是打算如何使用FM产出的v向量,是否要和其它模型组合使用等。DNN模型常用的优化算法就是SGD了,也有不少是结合分布式框架做的一些SGD优化。还有些优化算法能在模型训练过程中自适应调整学习率,这一般是在参数较多时使用,有些reinforcement learning的意思了。一般在实际工作中去研究优化算法的并不是非常多,因为它是和分布式训练系统紧密结合的,想改变一个优化算法,需要改动分布式训练系统的代码,这一般很难做到,除非分布式系统的代码就是由自己管理。所以一般情况下业务部门都比较喜欢自己来实现分布式训练系统,方便自己根据业务需求灵活定制相应的模型和算法。

       在2013年年初的时候我就有自己动手实现分布式机器学习模型的想法,当时不知道用什么手段实现分布式,后来听说了MPI,就开始研究如何用MPI实现分布式LR。当时花了不少时间在MPI的用法上,现在想想MPI只是个通信库,即使不用MPI也可以用ZMQ,也可以用socket,其实重点应该是怎么设计算法的并行方式,怎样使得整个训练系统更快更准。就像现在各种开源或者自研的深度学习训练系统一样,用什么网络库不重要,重要的是怎么设计整个分布式系统。我分别仔细研读过TensorFlow和mxnet的代码,个人认为mxnet代码解耦合模块化做的很好,结构很清晰,这可能也和我最初先研究了ps-lite代码有关,我在这篇文章中对比过mxnet和TensorFlow的分布式框架:http://www.doesbetter.com/836/。以后会记录更多的关于这两个分布式深度学习框架的对比笔记。

       在实际业务中,分为召回、排序、策略这三个环节,算法工程师大部分时间都是在做特征做策略。特征工程方面,分为几个步骤,首先是特征准备,根据实际的业务特点和数据选择出足够多的候选集出来;其次是特征处理,一般情况下是对离散值最onehot encoding,对连续值做归一化或者等频、等值离散化等等;最后是特征选择,可以看单特征AUC,也可以加新特征到已有特征集合中,可以观察单日AUC变化,可以观察多日AUC衰减情况,也可以根据ROC曲线形状判断等等。特征工程是和业务和数据紧密结合的,关于这方面的工作,有很多方法可能要真正做业务的时候才能具体情况具体对待,所以在这里就不做深入拓展。策略是和业务结合最紧密的,是最直接影响业务指标的,所以有不少部分策略都是在指标出来之后做的应对方案。

       关于以上模型的实际应用,是无法通过笔记来充分表达的,所有的工作都体现在实验过程中。也欢迎各位感兴趣的同行可以针对一些细节进行深入讨论,刚好也帮我回忆一下那些实验过程。

               

基于MPI的分布式LR、FM、FFM模型

        MPI版本的分布式LR模型是最先实现的,我在读研一时就有这个打算,当时就想着用自己实现的分布式模型做CTR预估,直到去年才真正实现。将代码实现的一些细节简单记录一下,方便以后继续优化。代码地址在:https://github.com/xswang/Field-aware-Factorization-Machine-mpi

       程序启动后会根据参数启动对应的进程数,每个进程功能是等价的,都要负责计算,不过会指定某个节点做参数的allreduce, 在代码中我指定了rank 0。

       hosts文件中是集群IP地址。

       n2n_config.h是配置文件,用来设置是否online,FTRL的各种参数等等。

       main.cpp主要就是MPI rank设置,程序启动过程。

       param.h是参数的设置,基本不需要改动。

       predict.h是计算AUC的,是个分布式来算的。基于MPI实现的可分布式计算AUC的代码在这里:https://github.com/xswang/AUC-caculate-mpi , 就不再单独用一篇笔记解释代码,一般每人关注AUC的算法细节,只需要记得在算ROC曲线下面积时按纵横轴scale就行了,剩下的就很好理解。

       learner中提供了几种优化算法,主要是FtrlLearner,在这个算法实现中,通过一些方式可以在实现LR模型的同时,也实现了FM和FFM模型。OWLQN没在这里实现,可以看这里:https://github.com/xswang/logistic-regression-owlqn-mpi/blob/master/src/owlqn.h,不过这个并行的OWLQN并没有有来的及做正确性测试,所以它目前是不能用的。

       在ftrl_learner.h中update_w()函数和update_v()函数,其中update_w()函数就是标准的参数w的更新算法,update_v是针对FM和FFM模型的更新算法,在第98行,如果用户配置只是用LR模型,直接break。第102行,f是FFM模型中每个特征拥有的latnet factor vector个数,如果用户只是用FM模型,则将f设置为0,也就是每个特征只对应一个latent factor vector,这针对这一个vector更行,否则将更新特征对应的所有的latent factor vector.

       在ftrl_learner.cpp中,run()函数是入口,根据配置文件选择是哪种训练方式。无论哪种方式,都使用了线程池,多线程之间需要同步的地方通过加锁实现。allreduce_gradient()函数主要负责梯度的收集,allreduce_weight负责模型更新和发送。整个过程体现了星型拓扑结构:rank 0作为主节点,收集梯度并更新模型之后,将参数发送给其它节点。

       在代码实现时用到了openblas科学计算库已经一个修改过的线程池。

       在代码写完之后,我使用了三台机器对LR和FM模型进行了测试,根据测试集AUC变化情况,FM的ROC曲线在最开始更陡峭,但是最终和LR差不多,也就是FM能更快的学习,但是最终结果并不明显比LR好,这可能也和当时没有充分调参有关。不过至少说明这个分布式FM模型代码是可用的。FFM模型并没有测,主要是速度太慢。

       比较遗憾的是并没有对MPI版本的LR和ps-lite版本的LR进行对比过。

       目前Parameter Server框架比较流行,也许没人会再用MPI同步训练模型了。LR模型基本上都已经在各个公司上线,FM也有不少公司开始探索,基于ps-lite的FM和FFM模型是我的下一个计划。2014时用DNN做CTR预估还是个很神秘的事情,然而现在也被打破了神秘感,不少公司都在探索。不过目前DNN模型还没有一个很好的离散特征值输入的分布式训练系统,线下训练可能会比较慢,上线也比较困难。

       

分布式FTRL优化算法的实现

之前实现过Parameter Server框架下的分布式FTRL优化算法,用的是DMLC的ps-lite。在PS架构下,集群分为worker、server、scheduler三种线程。其中worker负责梯度的计算,server负责参数的更新和分布式存储,scheduler负责集群节点的管理和状态监控。将代码实现简单介绍如下:

        1.  在main.cpp中完成启三个线程的启动过程。

         2. 在worker.cpp中完成梯度的计算并向server 推送算得的梯度。其中,(1)oneline_learning_threadpool是做在线学习用的,数据流主要是从kafka中读取,所有数据只训练一遍;batch_learning_threadpool是做批量学习用的,数据主要是从磁盘或者HDFS中读取,训练数据可以反复训练多次。(2)calculate_batch_gradient_threadpool负责根据样本计算梯度,在计算过程中worker和server通过ps-lite提供的pull/push接口拉取模型参数和推送样本梯度值。针对梯度计算,过程如下:首先,读取一批训练数据,代码328行至339行得到该批次数据的所有特征ID:all_keys,340行至343行进行过滤得到唯一的特征ID,348行从server中拉取所需权重,且权重是按照特征ID值大小排序的。352行至363行,根据pull得到的特征权重,计算该批次中每条样本的w*x乘积,这里有个巧妙的方法可以在O(m)时间复杂度完成计算,最开始的做法是把pull回来的数据放到map中,这样需要m*O(logn)时间复杂度。经过试验对比,O(m)时间复杂度的算法速度快了很多。我曾经尝试过其它的hashmap来替代std::unordered_map,效果都没有明显提升。

        3. 在server.cpp中完成模型的更新,根据FTRL公式,server收到worker推送过来的梯度之后进行一系列的计算更新z和w,从而完成模型的更新。156行的Push函数是ps-lite会调用的回调函数,也是用户实现模型更新逻辑的地方。

        4. dump.cpp函数将二进制的模型文件转成明文。

        5. 在io文件夹中,io.h是所有io接口的声明。load_data_from_local.cc从磁盘读取数据,load_data_from_kafka.cc从kafka中读取流式数据。其中load_minibatch_hash_data_fread函数采用fread函数读取数据,比用std::cin读取一行之后再分割的方法,在速度上快了近8倍。

        代码地址在:https://github.com/xswang/Field-aware-Factorization-Machine-ps ,之前是在三台机器上测试过分布式版本的,目前修改成单机版,如果要改成多机版也非常方便。

TensorFlow的op placementce

关于op在device上的placement策略,目前还没有比较好的方法,TensorFlow目前也只是用了比较简单的策略,细节如下:

   1,用户明确指定了设备号,就尊重用户的意愿,例如:当代码中有with tf.device(“/job:local/task:0/gpu:0”):或者with tf.device(“/job:local/task:0/cpu:0”):

时,就把对应op放到用户明确指定的设备上去。

   2,除了source和sink的其它节点,优先分配设备等级较高的,其中GPU等级高于CPU,因此一般都会被分配到GPU:0。但是有以下三种例外:

2.1,generator节点,也就是0个输入且只有1个输出的节点,这样的节点会被放置到和输出节点相同的设备上。

2.2,元操作节点,例如reshape,该元操作节点放置在被操作的节点上。

2.3,明显需要在CPU上执行的op。

 

   以上就是TensorFlow的placement算法,从以上可以看出,如果想用机器上的多个GPU卡,需要用户明确指出,不然会默认用编号为0的GPU卡。

   在第二步中,在处理2.1和2.2两种情况时,用并查集算法找到整个计算图中的连通分量,节点之间的关联关系就是根据2.1和2.2得到的。找到连通分量之后,有两种处理方式,(1)已知连通分量中某个节点放到哪个设备,就将该连通分量中的所有节点都放置到该设备;(2)连通分量中所有节点都没有被指定设备,则选择设备级别最高的设备放置。

TensorFlow与mxnet分布式框架对比

Mxnet和TensorFlow是目前国内最流行的两个开源分布式深度学习训练系统, 两者在设计上有些不同.

mxnet是Parameter Server架构, server和worker是两个最主要的进程,另外还有个负责集群管理的scheduler进程。server负责分布式存储模型参数,worker负责计算,且worker之间不能直接通信,只能通过server互相影响,一般来说,mxnet常用来做数据并行,每个GPU设备上都训练完整的DL模型。TensorFlow主要由client,server,worker三种进程,它虽然也可以实现parameter server的功能,但它计算节点的通信方式并不是只能靠server来完成,TensorFlow的woker是可以互相通信的,可以根据op的依赖关系主动收发数据。

另一个不同点是关于op和device的对应,一般情况下,mxnet常被用来做数据并行,每个GPU设备上都包含了计算图中所有的op。而TensorFlow是可以由用户指定op的放置的,大部分情况下,一个GPU设备只负责某个或者某几个op的训练任务,因此,也催生了关于op placement算法的研究,Google最近发表了一篇使用LSTM模型进行op placement优化的论文,我也在知乎上回答过该论文的详细内容:https://www.zhihu.com/question/61210638/answer/188682910,那个抱着双手的老虎就是我。

这篇笔记主要对比mxnet和TensorFlow在分布式框架下的一些不同,主要包括:进程类型、如何在分布式环境下启集群、如何做初始化。

1.进程类型

mxnet tensorflow
worker client
server server
scheduler worker

2.网络通信库

Mxnet tensorflow
zmq grpc

3.进程间通信模式

Mxnet tensorflow
Worker与scheduler,控制信息 Client与server,控制信息
Server与scheduler,控制信息 Server与worker,控制信息
Worker与server,数据 Worker与worker,数据

4.通信模式

Mxnet tensorflow
消息(队列),PostOffice负责多机之间消息收发,每个机器节点上都有唯一的postoffice单例对象。 消息(std::map),Rendezvous负责多机之间消息收发。每个机器节点上都有唯一的Rendezvous对象。

5,分布式启动

Mxnet tensorflow
各节点执行相同脚本。

根据集群host配置在每个节点上分别启动worker,server,scheduler进程,并进行集群初始化例如worker和server向scheduler的注册等等。

此时集群各个节点之间通信已ready,必要的节点之间可以互相收发数据。

各节点执行相同脚本。

根据集群host配置在每个节点上分别启动client,server,worker进程。完成集群初始化例如GRPC的service启动等。

此时集群各个节点通信已ready,必要的节点之间可以互相收发数据。

  1. 初始化流程6.1 Mxnet:系统初始化是由ps-lite这个模块完成的。每个节点都有个postoffice对象,该对象在系统初始化时进行一系列操作:
    1.     在构造函数中,根据环境变量设置当前节点的
    2.     根据配置文件给worker,server,scheduler进程分配编号。
    3.     调用Van对象初始化各个节点之间的通信,包含以下几个步骤:
      • 获取role为scheduler的ip和端口地址,并建立本节点与scheduler的通信。
      • 启动一个receiveing线程用来接收remote节点的消息。
      • 发送一个message给scheduler报告自己的情况。
      • 启动一个线程用来向scheduler发送heartbeat。

    6.2 TensorFlow:

    1. 启动GRPC Server
    2. 在各个节点上分别启动一个WorkerService线程和一个MasterService线程。
  2. 消息处理

7.1 Mxnet

worker和server以及worker->scheduler、server->scheduler之间通过发送message进行通信。这些消息都会被postoffice这个单例对象负责处理。

在6.3.2中,zmq收到的消息会被放到customer的ThreadsafeQueue<Message>队列中,在customer的Receiving函数中,从消息队列中取出消息并交给recv_handle_处理。而recv_handle_函数就是KVWorker和KVServer的Process函数。

KVWorker::Process函数是worker进程的函数,只是在收到消息后执行回调,例如对pull到的数据进行计算。

KVServer::Process函数是server进程的函数,收到消息之后,调用request_handle_进行处理。request_handle_是一个函数指针,其指向的函数在用户程序中,由用户负责实现。

7.2 Tensorflow

Tensorflow中数据的发送和接收是由send Op和recv Op分别实现的, 发送的消息由Rendezvous类负责处理。

对于send Op, 首先给要发送的消息构造一个unique key,并且交给Rendezvous处理Rendezvous会根据key在自己的消息map中查找是否有对应的recv请求,如果有,就执行recv注册的回调函数;如果没有,就把<key,message>对存放到自己的消息map中。

对于recv Op, 首先给要接收的消息构造一个unique key,并交由Rendezvous处理,Rendezvous根据key在自己的消息map中查找是否已经存在所需的消息,如果已存在,就直接copy消息;如果不存在,就调用GRPC的接口向remote节点发起异步远程调用。

以上所有,都只是关于分布式框架部分的流程,不涉及深度学习模型分布式训练的业务需求,这些以后会记录下来。

DMLC和Petuum并行计算框架的搭建与使用

        DMLC的ps-lite以及Petuum的bosen两者都是目前比较流行的Parameter Server计算框架的实现,PS(Parameter Server)计算框架在并行机器学习系统中被广泛使用,之前并行机器学习领域比较流行的是基于MPI的All Reduce计算框架,AR(All Reduce)计算框架下,算法需要严格同步,节点之间需要等待; 而PS不需要严格同步,通过一些方法在一定的Error Bound条件下完成异步训练。
        由于是在实际中用到并行模型进行CTR预估训练,因此选择基于ps-lite的difacto以及基于bosen实现的mlr,前者是并行版本的factorization machine, 这个模型也是我在2014年参加Cretio在kaggle上举办的广告CTR预估竞赛时用过的,取得了17名的成绩,现在也被越来越多的公司在实际中使用;后者是并行版本的logistics regression模型,这个是最普遍被使用的模型。
     1 difacto
     编译dmlc-core,编译之前要将difacto_dmlc/dmlc-core/make/config.mk中关于使用HDFS的选项设置为:USE_HDFS = 1
      编译ps-lite, ps-lite依赖6个第三方库:gflags glog protobuf zmq lz4 cityhash。 由于所使用的机器不能连接外网,因此我先将这六个库先下载到ps-lite文件夹下,并且修改difacto_dmlc/ps-lite/make/deps.mk文件,避免在编译之前将已经下载好的第三方库给删除了。
      编译FM模型,进入文件夹:difacto_dmlc/src/difacto,执行命令:make即可,得到的可执行文件在build文件夹中。
    使用yarn提交job, run_yarn.sh内容为:
    ../../dmlc-core/tracker/dmlc_yarn.py –jobname dmlc_wxs –vcores 1 -mem 512 -n 2 -s 1 build/difacto.dmlc guide/dmlc.fm.conf –log_dir=log –sync_timeout 500 -alsologtostderr -v 10
     其中dmlc.fm.conf中内容为:
     train_data = “hdfs:///dmlc/data/agaricus.txt.train”
     val_data = “hdfs:///dmlc/data/agaricus.txt.test”
     model_out = “hdfs:///dmlc/data/out/”
     max_data_pass = 3
    在执行sh run_yarn.sh之前,记得将训练数据和测试数据放到dmlc.fm.conf中指定的路径下。
     过程中遇到过以下几个问题:

     (1)重新编译dmlc-core会遇到错误提示:
    In file included from src/io.cc:17:0: src/io/hdfs_filesys.h:10:18: 致命错误:hdfs.h:没有那个文件或目录 #include <hdfs.h>
  解决方法:设置HADOOP_HDFS_HOME的环境变量为:export HADOOP_HDFS_HOME=/home/worker/xiaoshu/hadoop/hadoop-2.7.1即可。
    2 mlr:
    第一次使用petuum是在2014年的6月份,当时在360搜索广告算法组实习做广告CTR预估。可以用三种方式使用petuum提供的算法:1,Local: 数据放在local机器上,单机训练;2,HDFS + ssh:将数据放到hdfs上,使用ssh提交job并行训练;3,HDFS + YARN,将数据放到hdfs上,使用yarn提交job并行训练。
     2.1  local模式:
        修改launch.py中train_file, test_file, output_file_prefix(输出路径),执行./launch.py
     2.2 HDFS+ssh模式:
        进入文件夹:petuum/bosen, 将defns.mk.template文件名修改为defns.mk,并将其中关于HDFS选项uncomment:  HAS_HDFS = -DHAS_HADOOP # Uncomment this line to enable hadoop。然后make 即可。
        随后进入:petuum/bosen/app/mlr文件夹,执行make命令编译mlr模型。
        进入petuum/bosen/app/mlr/script文件夹,修改launch.py中的几项配置:
   “train_file”: “hdfs://10.101.2.88:9000/user/worker/petuum/mlr/covtype.scale.train.small”
       “test_file”: “hdfs://10.101.2.88:9000/user/worker/petuum/mlr/covtype.scale.test.small”
        “output_file_prefix”: “hdfs://10.101.2.88:9000/user/worker/petuum/mlr/out”
     2.3 HDFS+YARN模式:
        需要用到launch_on_yarn.py和run_local.py这两个文件,只需要修改 run_local.py中的某些配置就可以:
   “train_file”: “hdfs://10.101.2.88:9000/user/worker/petuum/mlr/covtype.scale.train.small”
    “test_file”: “hdfs://10.101.2.88:9000/user/worker/petuum/mlr/covtype.scale.test.small”
    “output_file_prefix”: “hdfs://10.101.2.88:9000/user/worker/petuum/mlr/out”
        然后执行:./launch_on_yarn.py
       遇到的一些问题:
    (1)copy到新机器上直接运行./launch.py,会出现:
        /home/worker/xiaoshu/petuum_local/bosen/app/mlr/bin/mlr_main: error while loading shared libraries: libboost_thread.so.1.58.0: cannot open shared object file: No such file or directory
        重新make就行了。
    (2)执行./launch_on_local_with_hdfs.py
      重新在boson中make,成功;然后在mL中make,提示/usr/bin/ld: cannot find -lhdfs:
      进入/home/worker/xiaoshu/hadoop/hadoop-2.7.1/lib/native,里面有libhadoop.a libhadoop.so    libhadooputils.a  libhdfs.so libhadooppipes.a  libhadoop.so.1.0.0  libhdfs.a         libhdfs.so.0.0.0。
     执行:sudo cp * /usr/lib, 并执行sudo config生效
    (3)在执行./launch_on_local_with_hdfs.py,提示:Environment variable CLASSPATH not set! getJNIEnv: getGlobalJNIEnv failed F0810 04:20:17.638351 39658 hdfs.hpp:185] Cannot connect to HDFS. Host: 10.101.2.88, port: 9000
      执行:export CLASSPATH=`hadoop classpath –glob`:$CLASSPATH;然后即可成功执行./launch_on_local_with_hdfs.py

Hadoop 集群安装过程

        最近研究并行机器学习并将在工作中用来训练CTR模型,因此申请了3台机器(ReadHeat系统),准备把目前比较流行的Parameter server并行计算框架搭建起来,由于会用到HDFS和YARN,因此先安装Hadoop集群,其中一台机器作为master,其余两台机器作为slave。。
    这里初步记录一下搭建Hadoop环境的过程,也欢迎大家提出意见和问题进行交流。
一,环境设置
    1,安装Java,执行命令:yum install java,然后通过whereis java命令可以看到idk所在的路径。
    对三台机器分别进行环境变量的设置:
    有两种方式:
        对所有用户生效:sudo vi /etc/profile,添加
        export JAVA_HOME=/usr/local/jdk1.7/
        export PATH=$JAVA_HOME/bin:$PATH
        然后:  source /etc/profile
        只对当前用户有效:sudo vi ~/.bashrc,添加:
        export JAVA_HOME=/usr/local/jdk1.7/
        export PATH=$JAVA_HOME/bin:$PATH
        然后source ~/.bashrc
    2,修改hosts文件(在三台机器上都进行下面的操作)
        sudo vi /etc/hosts
        添加(或者修改):
        10.101.2.88 master
        10.101.2.89 slave1
        10.101.2.90 slave2
        IP和hostname之间空格分隔。
      3,修改hostname文件(在三台机器上都进行下面的操作)
        sudo vi /etc/hostname
        在master主机上此文件中内容修改为:master
        在slave1主机上此文件中内容修改为:slave1
         在slave2主机上此文件中内容修改为:slave2
      4,ssh免密码登录:
        说明:master必须能免密码登录slave1, slave2, 并且要能免密码登录本机。
        ssh-keygen
        ssh-copy-id worker@10.101.2.89(slave1)
        ssh-copy-id worker@10.101.2.90(slave2)
        cat ~/.ssh/id_rsa.pub>>~/.ssh/authorized_keys(如果authorized_keys中已经有master的rsa,一定要先删除)
二, 安装Hadoop
    1, 下载hadoop-2.7.2, 解压到master机器的任意一个文件夹中
        cd hadoop-2.7.2
        mkdir hdfs;
        mkdir hdfs/name;
        mkdir hdfs/data;
        mkdir tmp
    2, 修改配置文件:
        2.1 ,进入hadoop-2.7.1/etc/hadoop文件夹,core-site配置文件如下:
    <configuration>
    <property>
         <name>hadoop.tmp.dir</name>
         <value>/home/worker/xiaoshu/hadoop/hadoop-2.7.1/tmp</value>
     </property>
     <property>
         <name>fs.defaultFS</name>
         <value>hdfs://master:9000</value>
     </property>
     <property>
        <name>fs.hdfs.impl</name>
        <value>org.apache.hadoop.hdfs.DistributedFileSystem</value>
        <description>The FileSystem for hdfs: uris.</description>
     </property>
</configuration>
    2.2,hdfs-site.xml配置内容如下:
<configuration>
    <property>
            <name>dfs.namenode.name.dir</name>
            <value>/home/worker/xiaoshu/hadoop/hadoop-2.7.1/hdfs/name</value>
    </property>
    <property>
            <name>dfs.datanode.data.dir</name>
            <value>/home/worker/xiaoshu/hadoop/hadoop-2.7.1/hdfs/data</value>
    </property>
    <property>
            <name>dfs.replication</name>
            <value>2</value>
    </property>
    <property>
        <name>dfs.permissions</name>
        <value>false</value>
    </property>
</configuration>
    2.3,yarn-site.xml配置内容如下:
<configuration>
<property>
<name>yarn.nodemanager.aux-services</name>
<value>mapreduce_shuffle</value>
</property>
<property>
<name>yarn.nodemanager.aux-services.mapreduce.shuffle.class</name>
<value>org.apache.hadoop.mapred.shuffleHandler</value>
</property>
<property>
<name>yarn.nodemanager.vmem-pmem-ratio</name>
<value>100</value>
</property>
<property>
<name>yarn.resourcemanager.address</name>
<value>master:8032</value>
</property>
<property>
<name>yarn.resourcemanager.scheduler.address</name>
<value>master:8030</value>
</property>
<property>
        <name>yarn.resourcemanager.resource-tracker.address</name>
                <value>master:8031</value>
                    </property>
<property>
        <name>yarn.resourcemanager.admin.address</name>
                <value>master:8033</value>
                    </property>
<property>
        <name>yarn.resourcemanager.webapp.address</name>
                <value>master:8088</value>
                    </property>
</configuration>
    2.4,mapred-site.xml配置内容如下:
<configuration>
    <property>
    <name>mapreduce.framework.name</name>
    <value>yarn</value>
    </property>
</configuration>
    配置完毕之后,将hadoop-2.7.2文件夹分别copy到slave1和slave2机器上,文件夹所在路径最好路径相同。
    3, 格式化namenode:
    只在master机器上执行:
    (格式化之前:要在hadoop-2.7.1/重新创建hdfs文件夹)
    ssh master ‘/home/worker/xiaoshu/hadoop/hadoop-2.7.1/bin/hdfs namenode -format -clusterId cluster1’
    4,启动namenode,datanode和resourceManager、nodeManager
    4.1 启动namenode:
    在master上执行:
    ssh master ‘/home/worker/xiaoshu/hadoop/hadoop-2.7.1/sbin/hadoop-daemon.sh start namenode’
    4.2 启动slave1上的datanode:
    在master上执行:
    ssh slave1 ‘/home/worker/xiaoshu/hadoop/hadoop-2.7.1/sbin/hadoop-daemon.sh start datanode’
    4.3 启动slave2上的datanode:
    ssh slave2 ‘/home/worker/xiaoshu/hadoop/hadoop-2.7.1/sbin/hadoop-daemon.sh start datanode’
    4.4 启动YARN:
    在master上执行:
    ssh master ‘/home/worker/xiaoshu/hadoop/hadoop-2.7.1/sbin/start-yarn.sh’
    最后,在master上输入jps,看到:
    5863 ResourceManager
    5657 NameNode
    5545 Jps
    在两个slave上输入jps看到:
    37136 NodeManager
    37009 DataNode
    86469 Jps
    出现以上结果表明hadoop集群已经安装成功。
    master节点上有NameNode,slave节点上有DataNode说明HDFS已经正常启动;master节点上有ResourceManager,slave节点上有NodeManager,说明YARN已经正常启动。

2014年总结

最近还是在继续做DNN,针对的问题还是广告CTR预估。DNN model理论方面的资料实在太少,看到的更多是一些pre-trianing这些的,而且大部分都是做图像,文本,语音的。所以干脆只关注如何求解,也就是如何优化DNN,调研了一些SGD方面的paper,重点看了Tong zhang的paper,不过我觉得针对非凸优化,那些方法不一定可行,先继续研究研究再说吧。
中间参加了一次面试,有些总结:1,我之前从不关注feature的,觉得那没啥意思没什么技术含量,经过面试才发现,对方还是很关注feature engineering的,特征的人工选择,如何选择,选了哪些作为feature等等。这个以后要注意,多玩数据,研究研究feature。2,对于model,我最开始学习机器学习时是很看重这个的,但是把李航那本统计学习方法的看了几遍,公式也推了一遍。以及后来的prml(这本书可真不好懂,最近在和几个小伙伴一起读第二遍)和ESL,也都看了。后来发现实际工业界用的也就那几种而已,完全没有必要个个都搞的很深(但是最好要都熟悉或者了解),而且我有时候觉得搞模型没啥意思啊,来个新数据,加个圈,就提出了一个新model,对我个人来说,不是很感兴趣。但是面试时会被问到不同的model的优缺点,为什么好为什么差,之前没仔细考虑过,以后要考虑清楚为什么DNN要比LR或者GBDT效果更好。估计要找不少paper,又会有很多时间是在做无用功,我心理上已经做好了准备。3,我之前看的很多的优化算法也有问到,owlqn这个我当时靠它把优化算法给串了一遍,结果面试时有些忘记说了,还是因为没有经常用到吧。以后要经常总结,多回顾。其实我觉得优化算法才是工业界可能做的最多的事情,model不会轻易改,也不好改,太难了;但是优化算法是可以尝试实验的,有数据有计算资源,肯定可以玩玩。
最近也看了google的FTRL,也说说自己的想法吧:一连串的paper读下来,发现牛人也是要站在巨人的肩膀上的,没有什么是突兀出现的,这也给我了一些启发,我一直想做好非凸优化这件事,那我就需要把相关paper串起来找灵感,而不是拍脑袋!另外,google的这篇paper也验证了工业界易算法不易做model的情况。
可能要关注的如下:
首先,LR,GBDT,DNN这三者在广告ctr预估上的优越性对比,模型的优缺点,为什么好为什么坏,这个是重点要掌握的,切记;
其次,FTRL,sgd,L-BFGS,owlqn,cdn,conjugate gradient descent,重点关注SGD以及各种变形。
最后,模型上的LR,GBDT,dnn,navie bayesian,maximum entropy,knn,SVM,LDA以及阿里的MLR,百度的DANOVA,实在太多了,只重点关注几个,比如LDA,现在我几乎不用了,但是也怕面试会再被问到。
以后打算重点关注large scale machine learning,这个信息量很大,绝对不是只扯model.加油!

附上之前关于deep learning的想法:

我的疑问:为什么大家都在讲deep learning?deep learning是什么?不就是多层的神经网络吗?多层神经网络不是早就有了吗?怎么突然大家像是找到了宝藏一样疯狂?不讲deep leaning你都不好意思和别人说你做机器学习的……
神经网络早就有了,多层神经网络也肯定早就出现过,但是在模型训练时 浅层还好,比如2层的(不算输入层,即只有输入层,单隐藏层,输出层的结构),使用BP方法训练,可以有不错的效果,可是,层数若是增加几层,比如弄个7,8层的隐藏层,使用BP方法训练时就会出现一种叫“梯度弥散”的问题,也就是说,在远离输出层的隐藏层训练其权值时,模型输出值与真实值之间的误差已经不能有效的指导权值的更新(这是BP算法自身性质造成的)。所以,多层神经网络效果不太好,因为没法很好的训练。
后来,针对这种深层网络,有人提出了新的训练算法,抛弃了BP方法。新的方法不再把整个网络中的权值作为一个整体同时训练,而是提出greedy的训练方法逐层训练:首先,只针对输入层与第一隐藏层进行训练,可以用auto-encode,rbm等等算法;然后,固定输入层,使用第一隐藏层与第二隐藏层进行训练,算法同上。依此类推…每一层的学习都是无监督的self-learning(自学习)也就是自己学习自己,至于能学出个什么东西来,我也不知道…应该是和数据和选定的算法有关吧。最后,在输出层与最后一层隐藏层训练时输入label做有监督学习,并根据训练误差做反向fine-tune(微调),具体怎么微调的,我也不知道。。。
我的个人观点:deep learning相对于神经网络的改进主要在以下几个方面:(1)由之前的作为整体训练改进为逐层贪心训练;(2)由之前的基于优化的思路(比如BP中的梯度下降求解)改进为即可有优化思路(比如auto-encode等)也可以有概率推理思路(比如RBM等)。
deep leaning是否有效的关键:(1)greedy训练过程中所选定的算法,比如,对某一个问题,auto-encode好还是RBM好?(2)据说deep leaning不适合解决离散信号(离散值)问题,而适合解决连续信号(离散值)问题。
另外,deep learning不一定单指deep神经网络,deep的思想可以用在其它模型中,而且貌似还挺有用。

思考两个问题:

(1)为什么要降维?降维的好处在哪里?降维前与降维后效果有什么差别?

(2)需要如何处理广告数据的feature?找出与图像的“同构”?还是把图像与广告数据都映射到一个新的“空间”,再找其中的关系?

2013年总结

大概从去年9月份(研一开学)开始接触机器学习。到目前为止也有一年多,最近身心无力,有一种严重的挫败感。

最开始的时候关注搜索引擎,大概在去年8,9月份吧,最初是从北京理工大学的一篇论文开始的,那篇论文貌似是发表在2010年的KDD,主要讲的是根据标签密度抽取网页内容的方法。也是偶然从一个QQ群里看到有人讨论这篇论文,然后就拿来读了读,用C++写了网页爬取的程序,爬了一些新浪博客的内容,又用C++简单实现了一下论文的方法,先调用一个xml库将网页解析成DOM tree,然后获得tag之间的内容,密度值设置30左右时效果最好。后来就没有继续在做,主要是觉得太机械,不够智能。现在挺怀念那段时间的,虽然什么都不懂什么都不会,但是有很多时间可以尝试去做自己感兴趣的事情。而且那时候心态一直很好,什么问题都敢问,即使被人嘲笑了也觉得无所谓,就觉得自己很差被别人嘲笑也没什么,还有很多时间去学习,去做自己喜欢的事情。

九月份研究生开学,最初的一个月我一直处于恍惚之中,不是精神恍惚,是有些不敢相信现实:在社会上晃悠了将近2年的我,怎么现在又坐在教室里了?呵呵,现在想起来自己挺搞笑的。最初的一个月,几乎没怎么睡觉,每天都很亢奋,晚上睡的很晚,早上起得很早,从网上查机器学习的基本模型算法,计划一个个先学习一遍。花了将近一个月学习决策树,主要时间花在了看懂源码上了。现在觉得不该那么学习,方法不对,特别是要读懂上千行的代码,感觉有些不值得。

后来,爬微博上的内容,从网上搜集了一些资料,模拟登录新浪微博,以浏览网页方式爬取微博内容,用python写的,不过大部分代码都是从网上找的,根据微博ID一个一个爬,从文件中读取ID,发送命令,获取返回字符串,保存到硬盘里。微博ID是从梁斌博士那里copy的,那天上午我跑到清华,但梁博士说不在学校,然后我就在清华东门附近晃悠了一中午,午饭吃了肠粉,真不好吃,扔了。下午找到梁博士,copy,走人,在此向梁斌博士表示感谢。。话说我看到一个牌子上写搜狗与清华联合实验室什么的,感慨他们的条件真不错。记得临走时梁斌和我说:这些数据你拿着,预祝你在科研上能取得进步!唉,很可惜,自己并没有在科研上有什么进展。由于实验室没有服务器,我的小破笔记本又太慢,所以每个ID只爬取45条微博,我算了一下,如果只用笔记本的话,要爬到第二年8月份..于是我到处找服务器用,但是没找到,后来找到2个同学借用了实验室台式机。爬回来的数据占用空间太大,又用C++写程序提取内容,本来打算用之前实现的北理工论文方法的,但是效果不太好,微博本来就140字,一不小心一条微博就被丢掉了,所以就基于标签匹配的方式提取内容,主要是有转发的情况不好处理,不过,调调程序,总算搞定了。大概持续了将近一个月吧,也不知道爬回来的数据怎么用,就没继续做了。现在想想,那个时候还处于原始阶段,也就只能弄个爬虫基于规则写个程序什么的,太简单太原始,太傻太天真。

中间去过中科院信工所面试实习生,主要是想趁导师不在出去实习学点儿东西,也是想尽快做“真正的机器学习”,面试时问了我关于爬虫的事,貌似还有一些基本的机器学习算法的问题,一切顺利,但是问到实习时间时出问题了,时间达不到要求,然后就没去成。记得我还提了一个关于分类的想法,大概意思是:特征处理成向量形式,从前向后扫描时记录1所在的位置,对于每条样本,计算一个sum += exp(index_i),然后再根据每条样本计算得到的那个sum数值分类…,不过都是瞎想的,不可行。面试时信工所老师的意思是让我去做爬虫方面的事情,后来虽然没去,不过我细细想了一下:为啥是让我做爬虫?因为我当时的能力貌似只能做爬虫…然后我就想,不能这样,我得做些更有技术含量的事情。

回到学校后继续学习机器学习算法,李航的《统计学习方法》我是从前往后看的,特别是SVM那一章,不容易看懂,我就推公式,也泡图书馆查资料…推着推着,发现自己已经不是在学习机器学习的事情了,已经完全陷入了数学的包围圈,记得当时在图书馆读一本叫做least square什么什么的书,突然我又迷茫了:这样是不是不太好啊,这成了学数学了啊。我不讨厌数学,但是我更喜欢应用数学…于是,在20天左右之后,我又不知道该怎么办了。我焦虑,失眠,甚至有些失望,我每天呆在寝室学习,晚睡早起,目的就是想多学些东西,把我自大一以来浪费的时间,浪费的知识不会来,可是现在呢,感觉不到自己的进步…

快期末考试了,我继续东一下西一下的学习机器学习,看优化算法方面的书,把本科时的数值分析也重新翻了出来。后来我觉得可能是因为我没有实际的应用场景,所以不知道具体该学什么,所以我决定找个应用方向,当时是从自然语言处理,推荐系统,数据挖掘三个方向选的,思来想去,选择了推荐系统。然后开始学习LDA,读blei的那篇经典论文,和北航一个博士师兄一起推导论文中的公式,看源代码,关于变分推理那部分的代码,真是难,还有梯度求导,花了好几个星期,期间还要参看其它论文,总之,又是一段时间的亢奋,记得那时候我经常每晚快12点了才从北航骑车回学校,冻得要死。后来寒假放假,室友都走完了,我还是每天早起晚归,还好北邮离北航不算太远。

年后,导师从国外回来了,要派我去公司实习,我是真不想去,想继续跟着北航博士师兄做事情,后来不得不去,我抱着试一试的态度过去一看,不到20人的公司,老板说,我们的技术是“国内领先的”,听到这句话我已经明白了…然后就是找导师谈我的想法,说我已经做了很多努力,想在机器学习这条路上走下去,花了不少时间和精力,导师同意了,所以我必须回实验室。记得是今年(2013年)的3月22号,我搬到了实验室,我说打算做推荐系统,导师就说让我要找到新的问题,要多看论文,可是我不知道怎么去找到新的问题,导师告诉我说,就是多看论文,你做的东西我也不懂,全靠你自己了。然后我就到处搜集论文,到处查资料,先行综述看起,看了一些论文,做了一些PPT,然后,问题还没有找到,我也很着急,可是也没办法, 我问导师:关于如何找到新问题,如何读论文,有没有一套方法论的东西?导师说没有。那好吧,我实在没办法了,继续读论文吧。然后发现online learning应该是个不错的方向,所以就查资料,关于online learning的一些算法,以及reinforcement learning的资料,还找到了VW的源代码去看,可是,我又陷入了一个误区,看什么源代码啊,要发现问题思考问题啊,唉,时间就是这么浪费的…

5月份的时候,我觉得推荐系统不好找到新问题,我觉得做个比较新的方向吧,应该容易找到新的问题,然后我就关注计算广告学,看刘鹏老师的视频,查相关资料,读相关论文。期间参加阿里实习生面试,通过了,6月底了才让去入职,话说效率好低。去了公司,亢奋,我每天6点就起床,先坐公交再坐地铁,7点10分左右能到公司,比别人早去2个小时。早班地铁上人少,我就拿起论文看。每天这样,一直到实习结束。实习的内容就是读读论文,讲讲论文,大部分都在读吧,一共讲了3次,然后就是一些小项目小程序,接触到了hadoop, 写了hadoop job, 对展示广告有了深入的了解,也做了一些实验。最大的收获是改掉了之前拿起论文就开始推导公式的坏毛病,都不看论文是解决什么问题的,这和之前的经历有关,之前做推荐方面的事情的时候,由于是跟着别人学习,所以自己just do, 没有问过why。也就是没有独自思考问题的能力。实习期间向刘鹏老师请教过,刘老师说E&E问题还有的搞,但是比较难,可是,我正是那种明知山有虎偏向虎山行的人,我一听比较难做,又亢奋了,就搞E&E了!所以整个暑假除了ctr预估方面论文,就是E&E的论文,快开学的时候,开始coding,打算把baseline实现了。开学后我把想法和导师一说,导师说只是个比例问题,还是不要搞了,所以就没继续做了。

回到实验室,重新开始关于ctr预估方面的学习,暑假的东西真是白做了,好痛心。又是一顿狂搜集资料,可惜的是这方面资料比较少。后来我一想,先实现baseline吧,就是线性模型logistic regression。找来kddcup 2012 track 2的数据自习研究,给自己的笔记本添了内存条,达到16G的土豪配置。然后学习MPI,Redis,处理数据,用C++编程…5900万的维度,1100万的样本,并行logistic regression,SGD的并行,读了AUC的资料,用C++写了个计算AUC的程序,一算,AUC最好才0.55,我还不如扔了算了……

过去的一年,我失败的一年,每天很忙碌,但是过的很乱,我现在很受伤,很受打击,最让我觉得毫无意义的是收集资料的时间,有时候想解决一个问题,论文就在那里,可是我却好不容易才找到,这些不是只靠输入关键词就能找到的论文,是最浪费我的生命的!我很羡慕那些有人指导有人带领的同学,他们的师兄师姐随手指定一些论文,就可以安心去读了。而我却还要一边读一边判断是否是我需要的,虽然有些是可以通过introduction或者conclusion直接判断的,但是有些是开始觉得是我想要的,然后看着看着就发现不是了,所以就丢掉继续找下一篇…就这样,TM的我的时间就这样没了!!!我到现在也没能仔仔细细认认真真读过超过20篇论文。时间总是在忧郁不觉中慢慢流逝,到头来什么也没得到。我有不少书,凸优化,PRML,MLAPP,每一本都是读一些,或者跳跃着读,从来没有系统的完整读过一本。

到现在我突然觉得自己一无所获,一无所有,搞了一年的机器学习,感觉还不如别人天天没事儿刷刷题。但是我不后悔自己的选择,只是,接下来的一年要改变学习策略了。从现在起,有一年的实习时间,如果能找到合适的实习机会,就继续把全部精力放到机器学习这件事,继续做自己喜欢的研究性学习。如果不能找到合适的实习机会,那就把时间放在安心读书上,首先要做一个合格的码农。

未来的计划一是花时间继续刷题,而是认真读书PRML,MLAPP,convx optimization(感觉不如numerical optimization好啊),matrix analysis…去TM的论文,再也不会像以前那样花大量的时间就为了找到一篇论文!受够了!!!

百度计算广告学沙龙笔记

夏粉的分享:

        首先解释计算广告学,追求的是,在一定环境下用户与广告的最佳匹配,这里的一定环境下,包括用户的query,当前浏览的网页,也有一些用户个人信息,但是比较少,这是用户的隐私数据,不好拿到。

        点击率预估的问题:

                1表示用户点击,0表示没有点击或者用户根本没看到,这个和推荐系统中用户评分差不多啊,没评分的可能表示用户根本不感兴趣或者用户根本没看到。关键的问题是:特征维度高,数据量大(每天可能上亿次的请求)。

        数据预处理的问题,首先是展示于点击日志的拼接,我之前以为是在一起的。包括两方面:日志->数据,这是行上的处理,比如抽样,去除缺失数据什么的;日志->特征,这是矩阵中列的处理。

        数据/特征规模的问题:百亿广告(行),千亿/百亿级的特征(列),一个问题是类别不平衡(点击的少,未点击或者用户根本没看到的多),噪声大(恶意点击等等)。

        特征复杂度高:特征之间高度非线性,举个栗子:不同性别不同年龄段的人关注的广告不同,人工组合方式把特征的非线性表示出来,例如:什么样的人在什么时间应该展示出什么样的广告。话说我觉得应该是response与特征之间的高度非线性问题吧?不是特征之间的非线性,response可能依赖于非线性的某些特征,而这些特征根需要人工或者机器组合得到。

时效性高:点击率随着时间变化,也就是随着时间人的兴趣会变化。

话说核心还是人啊,有些情况下即使输入的query和广告毫不相关,用户也可能以很大的概率点击广告。当然,大部分情况下,query还是能反映出用户的一些意图的,从而推断用户的后续行为。

        新广告或者流量的上下线。

        数据训练频繁:包括模型更新,比如从LR->DeepLearning.另一个是策略调整(不明觉厉)。因此需要快速,要做的是1获取主要信息,2去噪,所以选择对点击概率分布足够多的样本。

        数据处理,去除一些可见/不完整样本,或则机器学习的方法进行样本采样。google的做法:某个query对应的广告一个都没被点击过,则以概率p去掉该query,但是在训练时对那些以P为概率删除的样本乘以一个权重r.目的是为了补偿它。具体见google的论文<>

        要用尽可能少的特征表示模型的数据,具体方法包括特征选择与特征删减。模型要小,满足速度上的需求。

        主要包括大量id类特征,少量连续型特征。注意是大量id类,少量连续型。id类特征使用one-hot encoding的编码方式。

        然后是一个loss function,和之前见到不同的是在非正则化项前加的C,等值线随着C的增大扩张,直到与约束(正则化项)相遇,最常见的是L1范数正则化。其实和之前的都一样。

        特征删减:虽然特征那么高维,其实模型真正能用上的特征是比较少的。可以在训练前判断哪些特征的权重可能为0,比如可以根据点击的情况,也就是真实的反馈判断哪些特征权值为0,可以用一个公式衡量特征的分数,例如百度的Fea-G算法(screening test?),新的特征以概率P加入;bloomfilter + 次数超过n次的才加入;google的做法。

        人工特征工程:构造高阶组合特征,描述特征间非线性关系(我还是觉得应该是response所需要的非线性特征之间的关系。ps:到底是特征间非线性关系,还是response与非线性特征之间的关系(线性or非线性)?),如下方法:1,人工经验,耗时,也容易达到上界;2,枚举,太慢了;这两种都无法泛化(泛化能力很弱)。

         下面,高大上的Deep Learning登场:

             DL能自动学习feature,在语音和图像上的情况是:特征少,数据量大。比如28*28的图片可以有几千万张。但是在计算广告学领域,特征的特点是大规模稀疏特征,1,目前尚无专门针对大规模稀疏特征的深度特征学习算法,2,主要是样本相对特征不充分,样本量相对特征量来说比较少。

            针对第一个问题,需要降维的方法,针对第二个问题,需要把样本相对多的找出来(比如PV比较多的feature,要充分学习一个feature,需要包含该feature的样本充足)。

不过,网盟团队研发了能直接应用于大规模sparse特征的深度特征学习算法DANNOVA。逐层贪婪算法(这个信息比较有用):单特征->二阶组合->高阶组合。PPT上有个图。另外的问题:训练数据要少,模型需要稀疏才行,这样速度才会快。

            优化算法:google保留前N次的梯度,这个貌似是针对L-BFGS说的?每条样本要多训练几次,一次学不完样本中的所有信息(这个我没明白,一次不久行了吗?)。SOA算法,貌似是模型优化算法?

            对于L-BFGS算法来说,每一次的梯度之差,步长之差都要保留,为了根据割线定理计算hession矩阵?,由于是个向量,而且维度上千亿,所以对内存是个很大的挑战,而且,当特征矩阵的谱很大时(特征值变化范围较大),收敛速度慢;启动慢,每次重新训练时都要先得到历史上m次的梯度之差,当初始点离真实的解很近时不能很快收敛到真实解。百度自己的shooting算法,对hession矩阵分块,使得特征值差不多的靠的更近。

    陈雨强的分享:

        首先说广告中的长尾分布,长尾query,百度每天有约20%的query是历史记录中从没有出现过的。长尾问题,是针对query的长尾。不是广告的长尾(展示广告中是广告长尾?)理解点击:1,广告并不是用户所需要的信息(这个容易理解)2,不只是<ad-query>语义。做搜索还行,做广告就不行,对搜索广告来说,语义不是最关键的。

        数据比较复杂,则模型也要复杂;数据与模型要线性的?(模型与非线性特征之间的线性?)。常用的方法:大量特征+线性模型;小量特征+非线性模型。

    各大公司的方法,来自于论文以及各种传说:

        1,小规模feature + 非线性模型:MS:浅层ANN;yahoo!:GBDT;淘宝:局部线性LR(从盖坤的PPT上可以看到);facebook:二层结构,low hierarchies + high hierarchies

        2, 大规模特征+线性模型:百度,特征精细描述用户行为,比如query是否有空格等等。

        已经证实LR对ctr预估问题有效。通过设计特征提升准确度,feature engineering.

        L-BFGS:特征向量上千亿维,一条历史记录的梯度都要占用很大内存。可以使用坐标下降法,随机挑选n列(虽然是随机,但是要尽量使得能遍历全部)update。

        优化数据流:所用时间少。

        LR:人工加特征,组合特征。但是,人工的方式目前已经没法做了,如何改进?

高大上Deep Learning:

        1,学习高层特征,2,逐层贪心算法学习(again)。

        DL所遇到的问题:1,特征维度太高,2,模型不能太大,因为要数十毫秒内给出结果(只在线上的时候)。所以要对特征降维:各种降维算法(主要针对id类的,embedding function),连续值型的没必要了。

        对于大规模feature,使用cpu/LBFGS即可;小规模feature(容量合适的情况下),可以使用GPU()。

        另一个问题:E&E问题,这个就是机制的问题了。baseline算法比如LR可以接触到更多的badcase,从而很好的训练模型,DL接触的机会小,难以说明是不是改进的算法不行。

        大家提的一些问题:

         1,特征降维:根据特征的层级性。2,数据分布不一致:迁移学习,原数据->目标数据,通过学习数据量比较多的原数据的特征,加入到目标数据中,另外,定义元数据与目标数据的相似度。3,要实用更通用的特征,泛化能力强。4,分布式,SGD不适合分布式,mini-batch SGD,一般是100左右的batch。异步SGD。

总结:

    核心还是人。

    网搜和商搜用DL的方式不同:网搜直接针对高维稀疏特征,输入是单特征;商搜是先降维(有监督、无监督方法),针对categorical variable降维。方法还是逐层训练不是直接BP。

    另外夏粉提到的数据压缩Fea-G,不知道是指的哪些方法。

    DL省去了人工组合特征,让机器自己找,有监督的方法会不会更好些?特征可以是高阶,但是特征和模型要线性。

    以前考虑广告数据和图像语音数据的不同,图像,语音数据具有结构性,可以抽象出直观的high level feature,所以DL取得的效果比较好。现在觉得,那并不是主要原因吧,关键应该还是非线性模型对线性低阶特征的拟合。

    至少知道了我之前的想法中哪些是合理的,哪些是无解的。没办法,没人带就自己一点点儿琢磨吧…

专注于机器学习