今天分享一篇谷歌2016年发表的推荐论文《Wide & Deep Learning for Recommender Systems》。
1. 摘要
Wide & Deep 设计了一种融合浅层(wide)模型和深层(deep)模型进行联合训练的框架。
Wide部分:通过线性模型 + 特征交叉,使模型具有“记忆能力(Memorization)”,通常非常有效、可解释较强。但其”泛化能力(Generalization)“需要更多的人工特征工程。
Deep部分:只需要少量的特征工程,深度神经网络(DNN)通过对稀疏特征进行学习,可以较好地推广到训练样本中未出现过的特征组合,使模型具有“泛化能力”。但当user-item交互矩阵稀疏且高阶时,例如特定偏好的用户和小众产品,容易出现过拟合,导致推荐的item相关性差。
2. 引言
在这篇论文中,主要围绕模型的两部分能力进行探讨:Memorization与Generalization。
Memorization:模型能够学习并利用(exploiting)历史数据中高频共现的特征或item的能力。
Memorization更注重相关性(relevant),可以简单理解为发现“直接的”、“暴力的”、“显然的”关联规则的能力。常见的协同过滤、逻辑回归模型都是具有较强的“记忆能力”,原始数据往往可以直接影响推荐结果。比如文章中,Google Wide&Deep期望在wide部分发现这样的规则:
用户安装了应用A,此时曝光应用B,用户安装的B概率大。
Generalization:模型能够利用相关性的传递性去探索(exploring)历史数据中从未出现过的特征组合。
Generalization更注重多样性(diversity)。对于深度学习模型,通过对特征的自动组合,挖掘数据中的潜在模式,Google Wide&Deep期望在deep部分发现这样的规则:
用户安装了video应用,此时曝光应用music,用户安装的music应用的概率大。
模型训练采用的是联合训练(joint training),线性模型、DNN同时进行参数更新。
3. Wide & Deep
3.1 wide component
wide 部分是线性模型:
y
=
w
T
x
+
b
y=\mathbf{w}^{T}\mathbf{x}+b
y=wTx+b
其中
x
=
[
x
1
,
x
2
,
.
.
.
,
x
d
]
\mathbf{x} = [x_{1}, x_{2}, ..., x_{d}]
x=[x1,x2,...,xd]是
d
d
d维特征。
w
\mathbf{w}
w为形参,
d
d
d为偏置。
特征 x \mathbf{x} x由原生输入特征和变换特征(特征工程)构成。
文中提到过的特征变换为交叉乘积变换(cross-product transformation):
ϕ
k
(
x
)
=
∏
i
=
1
d
x
i
c
k
i
\phi _{k}(\mathbf{x})=\prod_{i=1}^{d}x_{i}^{c_{ki}}
ϕk(x)=i=1∏dxicki
其中,
c
k
i
∈
{
0
,
1
}
c_{ki}\in \left \{ 0,1 \right \}
cki∈{0,1},
ϕ
k
(
x
)
\phi _{k}(\mathbf{x})
ϕk(x)为
k
k
k维特征转化函数向量,当第
i
i
i 个特征是第
k
k
k 个变换特征的一部分时,该值为1,否则为0。
举例来说,对于二值特征,一个特征交叉函数为 ”AND(gender=female, language=en)“,当样本中"gender=female"与 ”language=en“ 同时存在时,该特征交叉函数值为1,其他特征值对应的 c k i = 0 c_{ki}=0 cki=0,即可忽略。这种特征组合可以为模型引入非线性。
3.2 deep component
deep部分是Embedding+MLP前馈神经网络。高维离散特征会首先被转为低维稠密向量,通常被称为Embedding vectors,Embedding 被随机初始化,并根据最终的loss来反向训练更新。
隐藏层为全连接网络:
a
(
l
+
1
)
=
f
(
W
(
l
)
a
(
l
)
+
b
(
l
)
)
a^{(l+1)} = f\left ( W^{(l)}a^{(l)} + b^{(l)}\right )
a(l+1)=f(W(l)a(l)+b(l))
其中,
a
(
l
)
a^{(l)}
a(l)、
b
(
l
)
b^{(l)}
b(l)、
W
(
l
)
W^{(l)}
W(l)、
f
f
f分别为第
l
l
l层的输入、偏置项、参数项与激活函数。
3.3 Joint Training
Wide & Deep 将Wide部分和Deep部分的输出进行加权求和(weighted sum)作为最后的输出。
模型预测的公式为:
P
(
Y
=
1
∣
x
)
=
σ
(
w
w
i
d
e
T
[
x
,
ϕ
(
x
)
]
+
w
d
e
e
p
T
a
(
l
f
)
+
b
)
P\left ( Y =1 \right|\mathbf{x} )=\sigma (\mathbf{w}_{wide}^{T}\left [ \mathbf{x},\phi (\mathbf{x}) \right ]+\mathbf{w}_{deep}^{T}a^{\left (l_{f} \right )} + b)
P(Y=1∣x)=σ(wwideT[x,ϕ(x)]+wdeepTa(lf)+b)
其中
Y
Y
Y 为 label,
σ
(
⋅
)
\sigma(·)
σ(⋅)是sigmoid,
ϕ
k
(
x
)
\phi _{k}(\mathbf{x})
ϕk(x)为
k
k
k维特征转化函数向量。
b
b
b为偏置项。
w
w
i
d
e
\mathbf{w}_{wide}
wwide、
w
w
i
d
e
\mathbf{w}_{wide}
wwide分别是wide部分和deep部分的权重。
值得注意的是文中wide部分和deep部分的优化器不相同。Wide部分采用基于L1正则的FTRL优化算法,Deep部分采用AdaGrad的优化算法。
4. 系统实现
4.1 Data Generation
User Data和App Impression Data共同组成了Training Data。样本的label要根据实际的业务需求来定,如果是点击率预估,点了就是1,不点就是0;如果下载率预估,那么下载了就是1,不下载就是0。
本阶段负责对数据进行预处理,供给到后续模型训练阶段。其中包括用户数据收集、样本构造。
- 对于类别特征,首先过滤掉低频特征,然后构造映射表,将类别字段映射为编号,即token化。
- 对于连续特征可以将连续特征归一化,并根据其累计分布函数,进行离散化。
4.2 Model Training
针对Google paly场景,本文构建了如下结构的Wide&Deep模型。
在Wide侧,作者仅使用了用户历史安装记录与app曝光记录(impression apps)作为输入。
在Deep侧,连续特征预处理后直接送入全连接层,对于类别特征首先输入到Embedding层,然后与连续特征向量拼接到全连接层。
从头训练模型,计算成本很高, 并且会延迟从数据到达到提供更新模型的时间。(梳理一下原因,因为模型收敛需要时间) 原文是:
However, retraining from scratch every time is computationally expensive and delays the time from data arrival to serving an updated model. To tackle this challenge, we implemented a warm-starting system which initializes a new model with the embeddings and the linear model weights from the previous model.
为解决上述问题,热启:从当前模型中获取embedding 和线性模型的 weights(也就是初始化的时候不是随机初始化)
4.3 Model Serving
在实际推荐场景,并不会对全量的样本进行预测。而是针对召回阶段返回的一小部分样本进行打分预测,同时还会采用多线程并行预测,严格控制线上服务时延。
Q & A
1. 要使用带L1正则化项的FTRL作为wide部分的优化方法,而使用AdaGrad作为deep部分的优化方法?
- FTRL with L1 稀释性
FTRL是一个稀疏性很好,精度又不错的随机梯度下降方法。由于是随机梯度下降,当然可以做到来一个样本就训练一次,进而实现模型的在线更新。曾是线性模型在线训练的主要方法。
相比L2正则,L1正则更易产生稀疏性。因此,FTRL with L1非常注重模型的稀疏性。这也就是问题的答案,W&D采用L1 FTRL是想让Wide部分变得更加稀疏。
再白话一点就是,L1 FTRL会让Wide部分的大部分权重都为0,我们准备特征的时候就不用准备那么多0权重的特征了,这大大压缩了模型权重,也压缩了特征向量的维度。
由此,引发了第二个问题:
为什么稀疏性这么关键?
稀疏性是考虑了泛化能力,在当前业务场景中,Wide部分特征选取了User Installed App 和 Impression App,这是两个id类特征,两个id类特征向量进行组合,在维度爆炸的同时,会让原本已经非常稀疏的multihot特征向量,变得更加稀疏。正因如此,wide部分的权重数量其实是海量的。为了不把数量如此之巨的权重都搬到线上进行model serving,采用FTRL过滤掉哪些稀疏特征无疑是非常好的工程经验。
那么,为什么Deep部分不特别考虑稀疏性的问题?
大家注意观察可以发现Deep部分的输入,要么是Age,#App Installs这些数值类特征,要么是已经降维并稠密化的Embedding向量,工程师们不会也不敢把过度稀疏的特征向量直接输入到Deep网络中。所以Deep部分不存在严重的特征稀疏问题,自然可以使用精度更好,更适用于深度学习训练的AdaGrad去训练。
2. 在文章4.1 节中,为什么要将 连续特征离散化?
Continuous real-valued features are normalized to [0, 1] by map- ping a feature value x to its cumulative distribution function P (X ≤ x), divided into nq quantiles. The normalized value
is ( i − 1 ) / ( n q − 1 ) (i-1)/(n_{q}-1) (i−1)/(nq−1) for values in the i-th quantiles.
- 离散特征的增加和减少都很容易,易于模型的快速迭代
- 稀疏向量内积乘法运算速度快,计算结果方便存储,容易扩展
- 离散化后的特征对异常数据有很强的鲁棒性:比如一个特征是年龄>30是1,否则0。如果特征没有离散化,一个异常数据“年龄300岁”会给模型造成很大的干扰
- 逻辑回归属于广义线性模型,表达能力受限;单变量离散化为N个后,每个变量有单独的权重,相当于为模型引入了非线性,能够提升模型表达能力,加大拟合
- 离散化后可以进行特征交叉,由M+N个变量变为M*N个变量,进一步引入非线性,提升表达能力
- 特征离散化后,模型会更稳定,比如如果对用户年龄离散化,20-30作为一个区间,不会因为一个用户年龄长了一岁就变成一个完全不同的人。当然处于区间相邻处的样本会刚好相反,所以怎么划分区间是门学问
- 特征离散化以后,起到了简化了逻辑回归模型的作用,降低了模型过拟合的风险。
3. MLP(Multi-layer Perceptron)为什么会有对特征进行高阶交叉的作用?MLP中每个神经元不仅仅是对特征进行加权求和吗?
-
感知机的结构是一个基于加法操作的结构,如下图所示:
-
两个特征从来没有直接交叉过。即使到了下一层,不同特征之间,也不会直接相乘,而是继续以加权和的方式叠加起来。那既然这样,为什么还说MLP具备特征交叉的能力呢?
-
就是因为激活函数的存在,为MLP提供了非线性能力。特征加权求和的结果,通过sigmoid,tanh这类激活函数之后,与其他神经元的输出进行进一步的混合,再通过下一层神经元的激活函数增加非线性,经过层层神经网络处理后,使MLP具备了特征交叉的能力,甚至在层数非常多之后,具备了拟合任意函数的能力。多层神经网络逼近任意函数都已经是理论证明过的工作了(Multilayer feedforward networks are universal approximators)。
-
需要注意的是:MLP这种特征交叉的能力是比较弱的,MLP不是天生为了进行特征交叉设计的。因此有许多特征交叉的改进算法,最常见的例如FM、PNN等。
4. DNN可以进行高阶特征交互,为什么Wide&Deep和DeepFM等模型仍然需要显式构造Wide部分?或者说wide部分是否是必要的?
- 大部分用户的行为还是具有显然的,直接的,规律可循,具有”记忆能力“的wide部分必不可少。
- w&d可能更多的是给出一个简单通用的lr到deep的迁移框架。wide和deep分开完全可以,特别是对时效性要求高的时间段或场景,deep的效率跟不上,可以deep部分做batch update保证准确性和充足表达能力,wide部分做online learning保证实效性。