在计算广告中,CTR是非常重要的一环。对于特征组合来说,业界通用的做法主要有两大类:FM系列和Tree系列。这里我们来介绍一下FM系列。
在传统的线性模型中,每个特征都是独立的,如果需要考虑特征与特征之间的相互作用,可能需要人工对特征进行交叉组合。非线性SVM可以对特征进行核变换,但是在特征高度稀疏的情况下,并不能很好的进行学习。现在有很多分解模型可以学习到特征之间的交互隐藏关系,基本上每个模型都只适用于特定的输入和场景。推荐系统是一个高度稀疏的数据场景,由此产生了FM系列算法。
本文主要涉及四种FM系列算法:FM,FFM,DeepFM,DeepFFM

因子分解机 (Factorization Machine,简称FM)

FM算法用于解决大规模稀疏数据下的特征组合问题。FM可以看做带特征交叉的LR。
考虑两阶多项式模型,也就是特征两两组合的问题,模型表达如下:
$$
\hat y(x) = w_0+\sum_{i=1}^n w_i x_i +\sum_{i=1}^n \sum_{j=i+1}^n w_{ij}x_i x_j
$$
其中$n$表示样本的特征数量,这里的特征是离散化后的特征。
然而,在数据稀疏性普遍存在的实际应用场景中,二次项参数的训练是很困难的。其原因是,每个参数 $w_ij$ 的训练需要大量 $x_i$ 和 $x_j$ 都非零的样本;由于样本数据本来就比较稀疏,满足“$x_i$ 和 $x_j$ 都非零”的样本将会非常少。训练样本的不足,很容易导致参数 $w_{ij}$ 不准确,最终将严重影响模型的性能。
为了解决二阶交叉项参数的学习问题,把多项式模型中二阶交叉项参数$w_{ij}$组成一个对称矩阵$W$(对角元素设为正实数),那么这个矩阵就可以分解。

将每个交叉项参数$w_{ij}$用隐向量的内积$⟨vi,vj⟩$表示,是FM模型的核心思想。

以二阶为例,FM模型表达式:
$$
\hat y(x) = w_0+\sum_{i=1}^n w_i x_i +\sum_{i=1}^n \sum_{j=i+1}^n ⟨vi,vj⟩ x_i x_j
$$
其中$v_i$表示第$i$特征的隐向量,$<\cdot,\cdot>$表示两个长度为$k$的向量的内积,计算公式为:
$$
⟨vi,vj⟩:=\sum_{f=1}^k v_{i,f} \cdot v_{j,f}
$$
参数因子化表示后,使得$x_h x_i$的参数与$x_i x_j$的参数不再相互独立。这样我们就可以在样本稀疏情况下相对合理的估计FM模型交叉项的参数。
FM模型的复杂度为$O(kn^2)$,但是通过下面的等价转换,可以将FM的二次项化简,其复杂度可优化到$O(kn)$。即:
$$
\sum_{i=1}^{n} \sum_{j=i+1}^{n} {\langle \mathbf{v}_i, \mathbf{v}_j \rangle} x_i x_j = \frac{1}{2} \sum_{f=1}^{k} {\left \lgroup \left(\sum_{i=1}^{n} v_{i,f} x_i \right)^2 - \sum_{i=1}^{n} v_{i,f}^2 x_i^2\right \rgroup} \qquad
$$
详细推导如下:
$$
\begin{aligned} & \sum_{i=1}^{n} \sum_{j=i+1}^{n} {\langle \mathbf{v}_i, \mathbf{v}_j \rangle} x_i x_j \qquad\qquad\qquad\qquad\qquad\qquad(1)\\ = & \frac{1}{2} \sum_{i=1}^{n} \sum_{j=1}^{n} {\langle \mathbf{v}_i, \mathbf{v}_j \rangle} x_i x_j - \frac{1}{2} \sum_{i=1}^{n} {\langle \mathbf{v}_i, \mathbf{v}_i \rangle} x_i x_i \qquad\qquad\;\;(2)\\ = & \frac{1}{2} \left(\sum_{i=1}^{n} \sum_{j=1}^{n} \sum_{f=1}^{k} v_{i,f} v_{j,f} x_i x_j - \sum_{i=1}^{n} \sum_{f=1}^{k} v_{i,f} v_{i,f} x_i x_i \right) \qquad\,(3) \\ = & \frac{1}{2} \sum_{f=1}^{k} {\left \lgroup \left(\sum_{i=1}^{n} v_{i,f} x_i \right) \cdot \left(\sum_{j=1}^{n} v_{j,f} x_j \right) - \sum_{i=1}^{n} v_{i,f}^2 x_i^2 \right \rgroup} \quad\;\;\,(4) \\ = & \frac{1}{2} \sum_{f=1}^{k} {\left \lgroup \left(\sum_{i=1}^{n} v_{i,f} x_i \right)^2 - \sum_{i=1}^{n} v_{i,f}^2 x_i^2\right \rgroup} \qquad\qquad\qquad\;\;(5) \end{aligned}
$$
解读第(1)步到第(2)步,这里用$A$表示系数矩阵$V$的上三角元素,$B$表示对角线上的交叉项系数。由于系数矩阵$V$是一个对称阵,所以下三角与上三角相等,有下式成立:
$$
A = \frac{1}{2} (2A+B) - \frac{1}{2} B. \quad \underline{ A=\sum_{i=1}^{n} \sum_{j=i+1}^{n} {\langle \mathbf{v}_i, \mathbf{v}_j \rangle} x_i x_j } ; \quad \underline{ B = \frac{1}{2} \sum_{i=1}^{n} {\langle \mathbf{v}_i, \mathbf{v}_i \rangle} x_i x_i } \quad
$$
如果用随机梯度下降(Stochastic Gradient Descent)法学习模型参数。那么,模型各个参数的梯度如下:
$$
\frac{\partial}{\partial \theta} y(\mathbf{x}) =
\begin{cases}
1, & \text{if}\; \theta\; \text{is}\; w_0 \text{(常数项)} \\
x_i & \text{if}\; \theta\; \text{is}\; w_i \text{(线性项)} \\
x_i \sum_{j=1}^{n} v_{j,f} x_j - v_{i,f} x_i^2, & \text{if}\; \theta\; \text{is}\; v_{i,f} \text{(交叉项)}
\end{cases}
$$

由于$\sum_{j=1}^{n} v_{j,f} x_j$只与$f$有关,在参数迭代过程中,只需要计算第一次所有$f$的$\sum_{j=1}^{n} v_{j,f} x_j$,就能够方便地得到所有$v_{i,f}$的梯度。显然,计算所有$f$的$\sum_{j=1}^{n} v_{j,f} x_j$的复杂度是$O(kn)$;已知$\sum_{j=1}^{n} v_{j,f} x_j$时,计算每个参数梯度的复杂度是$O(n)$;得到梯度后,更新每个参数的复杂度是 $O(1)$;模型参数一共有$nk+n+1$个。因此,FM参数训练的时间复杂度为$O(kn)$。
综上可知,FM算法可以在线性时间内完成模型训练,以及对新样本做出预测,所以说FM是一个非常高效的模型。

损失函数

  • 回归问题
    对于回归问题,损失函数可取为最小平方误差,即
    $$
    loss(\hat y,y) = (\hat y -y)^2
    $$

  • 二分类问题
    对于二分类问题(其中标签$y \in {+1,-1} $),损失函数可取为hinge loss函数 或 logit loss函数

  1. hinge loss函数
    $$
    loss(\hat y,y) = max { 0,1-y \hat y },
    $$
    当$y=+1$时,
    $$
    loss(\hat y,y) = max { 0,1- \hat y }=
    \begin{cases}
    0,\ \ \ \ \ \ \ \ \ \ \ \ \hat y \geq 1; \\
    1-\hat y,\ \ \ \ \ \hat y < 1;
    \end{cases}
    $$
    当$y=-1$时,
    $$
    loss(\hat y,y) = max { 0,1+ \hat y }=
    \begin{cases}
    0,\ \ \ \ \ \ \ \ \ \ \ \ \hat y \leq -1; \\
    1+\hat y,\ \ \ \ \ \hat y > -1;
    \end{cases}
    $$
    模型训练好后,就可以利用$\hat y(x)$的正负符号来预测$x$的分类了。

  2. logit loss函数
    $$
    loss(\hat y,y) = - \ln \sigma (\hat y y)
    $$
    其中$\sigma(x)=\frac{1}{1+e^{-x}}$为$sigmoid$函数,可见$\hat y$和$y$越接近,损失$loss(\hat y,y) $就越小。

此外,为了防止过拟合,我们通常会在优化目标函数中加入正则项(如L2正则)。

总结:

  1. FM降低了交叉项参数学习不充分的影响
    one-hot编码后的样本数据非常稀疏,组合特征更是如此。为了解决交叉项参数学习不充分、导致模型有偏或不稳定的问题。作者借鉴矩阵分解的思路:每一维特征用$k$维的隐向量表示,交叉项的参数$w_{ij}$用对应特征隐向量的内积表示,即$⟨v_i,v_j$(也可以理解为平滑技术)。这样参数学习由之前学习交叉项参数$w_{ij}$的过程,转变为学习$n$个单特征对应$k$维隐向量的过程。
    很明显,单特征参数($k$维隐向量${v_i}$)的学习要比交叉项参数$wij$学习得更充分。

  2. FM提升了模型预估能力
    由于FM学习的参数就是单特征的隐向量,那么交叉项的预估结果可以用隐向量内积得到。这样,即便训练集中没有出现交叉项的样本,FM模型仍然可以用来预估,提升了预估能力。

  3. FM提升了参数学习效率
    这个显而易见,参数个数由$(n^2+n+1)$变为$(nk+n+1)$个,模型训练复杂度也由$O(mn^2)$)变为$O(mnk)$。$m$为训练样本数。对于训练样本和特征数而言,都是线性复杂度。
    此外,就FM模型本身而言,它是在多项式模型基础上对参数的计算做了调整,因此也有人把FM模型称为多项式的广义线性模型,也是恰如其分的。

FM模型对稀疏数据有更好的学习能力,通过交互项可以学习特征之间的关联关系,并且保证了学习效率和预估能力。

FM vs SVM

SVM和FM的主要区别在于:

  • SVM的二元特征交叉参数是独立的,而FM的二元特征交叉参数是两个k维的向量$v_i$、$v_j$,交叉参数就不是独立的,而是相互影响的。
  • FM可以在原始形式下进行优化学习,而基于kernel的非线性SVM通常需要在对偶形式下进行
  • FM的模型预测是与训练样本独立,而SVM则与部分训练样本有关,即支持向量

FM vs LR

Fm学习的是特征的隐向量,没有出现的特征也可以通过隐向量内积得到,打破了特征之间的独立性。LR学习的是组合特征的权重,没有出现的组合特征,权重无法学习。简单理解就是数据太稀疏了,$x_i*x_j$的样本不一定存在,LR就无法学习$w_{ij}$。

场感知分解机(Field-aware Factorization Machine ,简称FFM)

在CTR预估中,通常会遇到one-hot类型的变量,会导致数据特征的稀疏。未解决这个问题,FFM在FM的基础上进一步改进,在模型中引入类别的概念,即field。将同一个field的特征单独进行one-hot,因此在FFM中,每一维特征都会针对其他特征的每个field,分别学习一个隐变量,该隐变量不仅与特征相关,也与field相关。假设样本的n个特征属于f个field,那么FFM的二次项有nf个隐向量。而在FM模型中,每一维特征的隐向量只有一个。FM可以看做FFM的特例,把所有特征都归属到一个field的FFM模型。通过引入field的概念,FFM把相同性质的特征归于同一个field。
同一个categorical特征可以包括用户属性信息(年龄、性别、职业、收入、地域等),用户行为信息(兴趣、偏好、时间等),上下文信息(位置、内容等)以及其它信息(天气、交通等)。

其模型表达式为:
$$
\hat{y}(\mathbf{x}) := w_0 + \sum_{i=1}^{n} w_i x_i + \sum_{i=1}^{n} \sum_{j=i+1}^{n} \langle \mathbf{v}_{i,\,f_j}, \mathbf{v}_{j,\,f_i} \rangle x_i x_j \qquad
$$
其中,$fj$是第$j$个特征所属的field。如果隐向量的长度为$k$,那么FFM的二交叉项参数就有n$fk$个,远多于FM模型的$nk$个。此外,由于隐向量与field相关,FFM的交叉项并不能够像FM那样做化简,其预测复杂度为$O(kn^2)$。

FFM应用

在DSP或者推荐场景中,FFM主要用来评估站内的CTR和CVR,即一个用户对一个商品的潜在点击率和点击后的转化率。
CTR和CVR预估模型都是在线下训练,然后线上预测。两个模型采用的特征大同小异,主要分三类:

  • 用户相关的特征
    年龄、性别、职业、兴趣、品类偏好、浏览/购买品类等基本信息,以及用户近期点击量/购买量/消费额等统计信息

  • 商品相关的特征
    商品所属品类、销量、价格、评分、历史CTR/CVR等信息

  • 用户-商品匹配特征
    浏览/购买品类匹配、浏览/购买商家匹配、兴趣偏好匹配等

为了使用FFM方法,所有的特征必须转换成“field_id:feat_id:value”格式,field_id代表特征所属field的编号,feat_id是特征编号,value是特征的值。数值型的特征比较容易处理,只需分配单独的field编号,如用户评论得分、商品的历史CTR/CVR等。categorical特征需要经过One-Hot编码成数值型,编码产生的所有特征同属于一个field,而特征的值只能是0或1,如用户的性别、年龄段,商品的品类id等。除此之外,还有第三类特征,如用户浏览/购买品类,有多个品类id且用一个数值衡量用户浏览或购买每个品类商品的数量。这类特征按照categorical特征处理,不同的只是特征的值不是0或1,而是代表用户浏览或购买数量的数值。按前述方法得到field_id之后,再对转换后特征顺序编号,得到feat_id,特征的值也可以按照之前的方法获得。

实践注意项

  • 样本归一化:FFM默认是进行样本数据的归一化,即 为真;若此参数设置为假,很容易造成数据inf溢出,进而引起梯度计算的nan错误。因此,样本层面的数据是推荐进行归一化的。
  • 特征归一化:CTR/CVR模型采用了多种类型的源特征,包括数值型和categorical类型等。但是,categorical类编码后的特征取值只有0或1,较大的数值型特征会造成样本归一化后categorical类生成特征的值非常小,没有区分性。例如,一条用户-商品记录,用户为“男”性,商品的销量是5000个(假设其它特征的值为零),那么归一化后特征“sex=male”(性别为男)的值略小于0.0002,而“volume”(销量)的值近似为1。特征“sex=male”在这个样本中的作用几乎可以忽略不计,这是相当不合理的。因此,将源数值型特征的值归一化到 是非常必要的。
  • 省略零值特征:从FFM模型的表达式可以看出,零值特征对模型完全没有贡献。包含零值特征的一次项和组合项均为零,对于训练模型参数或者目标值预估是没有作用的。因此,可以省去零值特征,提高FFM模型训练和预测的速度,这也是稀疏样本采用FFM的显著优势。

实现FM & FFM的最流行的python库有:LibFM、LibFFM、xlearn和tffm。其中,xLearn是一款高性能,易于使用且可扩展的机器学习软件包,包括FM和FFM模型,可用于大规模解决机器学习问题。xlearn比libfm和libffm库快得多,并为模型测试和调优提供了更好的功能。

DeepFM

FM通过对于每一位特征的隐变量内积来提取特征组合,最后的结果也不错,虽然理论上FM可以对高阶特征组合进行建模,但实际上因为计算复杂度原因,一般都只用到了二阶特征组合。对于高阶特征组合来说,我们很自然想到多层神经网络DNN。
DeepFM目的是同时学习低阶和高阶的特征交叉,主要由FM和DNN两部分组成,底部共享同样的输入。模型可以表示为:
$$
\hat{y} = sigmoid(y_{FM}+y_{DNN})
$$

DeepFM

  1. FM部分
    数学表达为:
    $$
    y_{FM} = w_0+\sum_{i=1}^n w_i x_i +\sum_{i=1}^n \sum_{j=i+1}^n ⟨vi,vj⟩ x_i x_j
    $$
    FM模型可以用神经网络进行表示,模型输入$x=[x_{field1},x_{field2},⋯,x_{fieldm}]$,这是一个$d$维的向量,其中$x_{fieldi}$即为第$i$个$field$的特征表示,如果是类别,则为one-hot编码后的向量,连续值则为它本身。然后对每个field分别进行embedding
    DeepFM FM部分
    值得注意的是,即使各个field的维度是不一样的,但是它们embedding后长度均为k。
    接着FM层即为embedding后结果的内积和一次项的和,最后一层sigmoid后再输出结果。
  1. 深度部分
    是一个前馈神经网络,与图像或语音类的输入不同,CTR的输入一般是极其稀疏的,因此需要重新设计网络结构。在第一层隐藏层之前,引入一个嵌入层来完成输入向量压缩到低位稠密向量:

嵌入层的输出为$a(0)=[e1,e2,…,em]$,其中$e_i$是嵌入的第i个filed,m是field的个数,前向过程将嵌入层的输出输入到隐藏层为
$$
a(l+1)=σ(W^{(l)}a^{(l)}+b^{(l)})
$$
其中$l$是层数,$σ$是激活函数,$W(l)$是模型的权重,$b(l)$是$l$层的偏置
因此,DNN得预测模型表达为:
$$
y_{DNN} = W^{|H|+1} \cdot a^{|H|} + b^{|H|+1}
$$
$|H|$为隐藏层层数

有两个有趣的特性:
1) 尽管不同field的输入长度不同,但是embedding之后向量的长度均为k
2) 在FM中得到的隐变量$V_{ik}$现在作为嵌入层网络的权重

Embedding层的隐式向量在(残差反向传播)训练时可以同时接受到深度部分和FM部分的信息,从而使Embedding层的信息表达更加准确而最终提升推荐效果。
需要指出的是,FM部分与深度部分共享相同的embedding带来了两个好处:
1.从原始数据中同时学习到了低维与高维特征
2.不再需要特征工程。而Wide&Deep Model需要

DeepFFM

类似于FFM对于FM模型来说,划分了field,对于不同的field内积时采用对应的隐向量。同样可以把DeepFM进行进化为DeepFFM,即将每一个field embedding为m个维度为k的隐向量(m为field的个数)