·前言
作为浅谈入门文,适合于新人科普阅读。本文介绍广告和推荐等场景下的一般模型的普遍做法,不涉及业务场景定制。会从数据和特征、模型结构、分布式训练等3个方面简述。
本文脉络
背景介绍
训练数据和特征
特征和模型结构
模型结构和分布式训练
后记
01
—
背景介绍
上图是一个典型的推荐场景流程,召回和排序阶段都有很多DL模型。在算力极速升级的当下,模型和数据都有了井喷式发展。虽然模型方向的研究更显繁荣,不过算力(GPU/TPU 等硬件研究)、分布式实现(Parameter Server、Allreduce 等)、以及完整的打包平台(Tensorflow、Paddle 等)这些方向也都在持续发展。数据、特征、模型、训练方案等,这些都是实现一个排序模型必不可少的因素,我们不妨笼统地称这些关键元素为『一套系统』。
PaddlePaddleRec 针对推荐场景提供的服务和算法
现如今几乎每个互联网公司都有这么一套自己的大规模分布式深度学习模型训练系统。虽然不同场景下这套'系统'不尽相同,但也基本可以归纳出一个通用的范式,即一个简化的套路。
首先是业务目标。绝大多数场景就是预估一个概率。CTR、CVR、CPM 等等,即使是拟合任务也可以都当做分类任务来看,都是估一个概率。下面我们以CTR 为例子,说明数据、特征、模型、训练这4个方面的套路。画了一个简易流程图,帮助大家对照理解。
从日志到模型训练的主要流程
【数据&特征】数据一般是展现点击日志,如「上海的用户A 于昨天9点在苹果手机里的虎扑上看到了3条内容点击了第2个」,数量大可以做些负采样处理。特征(上海、A、9点、苹果等等)也是来源于数据,比如用户特征、流量特征、内容特征等等,可以定义各种各样的特征体系。
【特征&模型】这么多特征怎么用呢?通常遇到的都是大规模离散特征,比如 id 类特征,即使是连续值特征也可以离散化。这些特征通过处理后,查一个表得到 embedding向量值再输入到模型的第一层。早期常用 LR 模型来使用这些特征,需要交叉特征就人工将原始特征做一些组合后再使用。现在深度学习模型逐渐取代了 LR 模型,对特征的使用上也有了很多研究。比如xdeepfm、wide&deep 等,都是模型结合特征的典型工作。
【模型&训练】数据量庞大、模型庞大(稀疏离散 embedding 也算作模型部分),就需要分布式来训练。不同的模型有着适合自己的分布式方案。数据并行、模型并行、同步更新异步更新,总之最后训练出一个模型,用于预测这个 CTR,同时还会更新特征的 embedding。
02
—
训练数据和特征
训练数据基本都是来自真实的线上展现日志,记录了系统每一条流量请求的全部链路信息,如用户、时间、OS 等。数据量级和流量的大小有关,小公司可能每天只有几十万的数据,大平台可以每天有一百亿条的数据。通常训练模型采用 online 的方式,即天级别或更高时效性的续跑模型。既然是续跑,当然会有一个 base 模型,这通常是用2周~3个月左右的时间窗数据来训练出来的。所以对应这个base 模型而言,可用样本可以达到千亿量级。
可用样本量级太大,我们不一定非得全部用上,所以通常会对负样本进行采样(大多数场景下,正样本很少)。负采样以及样本构造也是一个对模型有很大影响的环节,这里不再赘述。
特征除了来自于系统展现日志外,各大公司还会通过各种其他途径去获得更多的信息。常见的是对用户特征的挖掘,比如通过搜集用户手机安装的 app list 来刻画用户。
最终我们会把一条样本,抽出人工定义好的特征。比如上图中的location、time、app 等,通常也就一两百类特征。为啥一两百类的特征,我们却总是听说大规模特征?
实际是这里有一个定义 gap。举个例子,用户 userid 这一维特征,比如系统中用户有1亿个,那么每个 id 实际上也可以当做一个独立的特征对待。这样一算,特征规模就上去了。
虽然每一类特征可以细分为成千上万个 id 特征,但是模型似乎只是对这一类特征只学习出一个权重啊?无论 id 是哪个,到了模型层,都是用一个 w_userid 来计算的。
这里就要重新理解 embedding 的概念了。对于模型而言,id 查了embedding表后得到向量,输入进来进行计算,是对数据进行抽特征。如果类比到图像分类,抽取 rgb 特征来分类,这时候就会发现好像哪里有问题?图片分类时,特征是客观描述数据的,所以应该是不变的。但是推荐模型中,这个 embedding 表可是会随着模型训练一起更新的。
embedding 既然是随着模型训练一起更新的,那么上面说的『对数据进行抽特征』这句话就不完全正确了。对 embedding 词表更合理的定位是需要更新权重的稀疏模型,而不是客观不变的特征。因为同一个标题出现1次没点击和出现100次时没点击,显然对模型而言意义是不一样的。
这里用 title 再稍微细说下 embedding,通常我们会分词后,然后再对每一个词签名,作为这个词的新 id,当然 embedding 词表里存的也是这个签名而不是明文。title 这一维特征和 userid 不同,它是一个多值特征,分词后可以查到多个 embedding向量,这里可以将他们累加后再输入到模型。同时,如果想要用 title 的语义特征,那么也可以查表得到其语义信息 embedding 词表,比如经过 bert 模型的输出值,将这个值也拼接到模型上当特征。不过这个词表就不需要更新,因此训练时梯度不用回传。
03
—
特征和模型结构
wide&deep 结构模型很适合用来解释特征和模型的互动关系。前面介绍了大规模离散特征的 embedding 过程,如果出来的 embedding 直接送入一个 DNN 模型,那么学习出的就是所谓的 deep 模型,或者说 deep 部分。如果直接 concat 到 deep 模型顶层后再输出,那么这部分就是 wide 模型。wide 部分可以简单的当做是 LR 模型理解,具有线性模型的优点,可解释、具有较强记忆能力,即对离散的 id 敏感一些。deep 部分则由于有较多的非线性层学习到更多特征组合,会具有较强的泛化能力。
所以可以看出模型的设计是需要结合特征来看的。如果觉得某些特征或者特征组合很重要,尤其是一些物理意义明显的标量值,往往可以放到 wide 部分直接 concatenate 到顶层,这样作用更大。
当前的研究主要是追求充分利用底层特征和高阶特征以及特征的自动组合。如何避免人工设计,而又能充分挖掘特征的组合信息是当前的一个指导方向。很多工作都是基于这个出发点来开展的:
上图中都是具有代表性的工作,这些工作或串行、或并行,利用 FM 和 DNN 的组合来充分挖掘高阶组合特征,是目前研究的主流。打比赛的话,比如 xdeepfm 这种都是比较好用比较 solid 的 baseline,不过实际工作中,由于计算代价以及真实系统复杂性,一些复杂模型要么根本没法上线,要么上线了业务效果并不喜人,往往是一顿操作猛如虎,最后只涨0.5。
04
—
模型结构和分布式训练
上文部分都是偏算法理论和策略设计,真要落地,还缺最难的一环:高效的工程实现。在风口上飘了这么久,算法理论谁都能说几句,但搞个分布式框架来实现,大多数人只能沉默,能发光的都是大神比如李沐、世熹等。分布式平台这个话题太大,IO、负载、通信、容错、速度精度权衡等等太多要考虑。能力有限只能介绍下核心的计算流程,怎么从数据训练出模型。
分布式常用的2种模式有ParameterServer 和 AllReduce/RingAllReduce。随着开源框架的火热迭代,再者 GPU 显存也越来越大,AllReduce 模式研究的更多一点。毕竟大多数研究的还是 dense 模型,就算上百层的网络,参数也大不到哪去。所以很多训练都是数据并行,每个节点可以存储完整的模型参数。
但是像 CTR 这类用到大规模离散特征的,本质也是一个大规模离散模型,一般称为 sparse 模型。几百 G 的模型很常见,一个节点也不太可能放的下。这时候 parameter Server 模型更加适用一些(Paddle 的公开课里,有人问了 all reduce 面对节点存不下模型时怎么办,但是我没看到完整回答)。我个人理解是,一般大小的 dense 模型,ring allreduce模式有带宽优势。而超大的 sparse 模型,还是 Pserver 更能打一点。
上图是 downpour SGD 算法框架,也可以当做 Pserver 框架来讲。训练数据被切分成多份,输入到不同的节点(worker) 进行训练,模型的参数都存在 server 上。worker 和 server 之间通信来获得/提交数据。
server 上存储的参数格式是<key, value>对。每个 worker 读入一个 batch 数据后,会向 server 执行 pull 操作,获取当前计算需要的参数的最新的值。比如稀疏参数的获取,发现样本中,location 这一维特征有北京,上海,南京3个sign,那么则将这3个当做 key 去请求 server 获得当前的最新 embedding 向量。计算前向和梯度,也是需要 dense 模型参数的,所以也会 pull DNN 网络的参数。
每个worker 互相不干扰,各自 pull 参数,然后计算梯度后,再通过 push 将梯度值回传给 server。server 再汇总所有 worker 的梯度结果后一起更新最终参数。这里有2个问题,一个是有同步更新还是异步更新,虽然各有利弊,但一般都是采取异步。另一个问题是pull-计算-push 这个操作太频繁,通信有压力,拖慢计算。所以可以采取时效性不那么高的方法,就是不必每次都 pull 和 push,比如可以隔3次 pull 一下,隔5次 push 一下。经过多轮训练后,模型的参数就训练完了。
上面提到的是 pserver 的典型优点:异步通信计算,效率高;模型一致性和性能平衡。下面介绍下另一个设计特点:一致性环形哈希。
pserver 并不是只有一个master 来分发所有参数,而是存在一个 Server group,即多个 server 机器,每个机器只负责存一部分参数就行。这样就避免唯一 master 不停广播的通信代价问题。
前面说了,server 存的是<key,value>,每个 server 上这个 key 的范围就是通多一致性哈希来分配的。这样设计有个好处,就是加入一个新节点,或者丢失删除一个节点后,参数可以通过环形只在相邻的节点上调整即可,避免节点频繁大规模的参数变动。
小结下sparse 模型和 dense 模型可以选择各自合适分布式模式来提高训练效率。parameter Server 模式更适合广告推荐场景下大规模离散特征模型。
05
—
后记
记得14年在青岛参加 VALSE 研讨会,那年的 panel 话题是模型和数据哪个更重要。当时听着颜水成等一众大佬被迫选题辩论,自己也完全没考虑算力和落地。我现在更想说:工程实现更重要。
联系客服