FFM原理理解与应用

FFM原理理解与应用本文参考了美团团队的介绍文章。http://tech.meituan.com/deep-understanding-of-ffm-principles-and-practices.html以及http://blog.csdn.net/zc02051126/article/details/54614230FM和FFM模型是最近几年提出的模型,凭借其在数据量比较大并且特征稀疏的情况下,…

大家好,又见面了,我是你们的朋友全栈君。

本文参考了美团团队的介绍文章。
https://tech.meituan.com/deep_understanding_of_ffm_principles_and_practices.html
以及
http://blog.csdn.net/zc02051126/article/details/54614230

FM和FFM模型是最近几年提出的模型,凭借其在数据量比较大并且特征稀疏的情况下,仍然能够得到优秀的性能和效果的特性,屡次在各大公司举办的CTR预估比赛中获得不错的战绩。

FM原理

FM模型的提出旨在解决稀疏数据下的特征组合问题。假设一个广告分类问题,根据广告位信息和用户信息,预测用户是否点击广告。

Clicked? Country Day Ad_type
1 USA 26/11/15 Movie
0 China 1/7/14 Game
1 China 19/2/15 Game

三个特征都是类别型特征,经过one-hot编码

Clicked? Country=USA Country=China Day=26/11/15 Day=1/7/14 Day=19/2/15 Ad_type=Movie Ad_type=Game
1 1 0 1 0 0 1 0
1 0 1 0 1 0 0 1
1 0 1 0 0 1 0 1

通过观察可以发现,某些特征经过关联会更加有意义。比如Country=USA和Day=26/11/15(感恩节),Country=USA和Day=19/2/15(春节)这样的关联对用户的点击有着正向的影响。而一些其他的组合(Country=USA和Day=26/11/15)可能对用户点击并没有那么强烈的影响。因此引入两个特征的组合是非常有意义的。

多项式模型是包含特征组合最直观的模型,模型表达式如下

y(x)=w0+i=1nwixi+i=1nj=i+1nwijxixj y ( x ) = w 0 + ∑ i = 1 n w i x i + ∑ i = 1 n ∑ j = i + 1 n w i j x i x j

其中, n n 代表样本特征数量,
xi

x i
是第 i i 个特征的值,
w0,wi,wij

w 0 , w i , w i j
是模型参数。
从公式可以看出,组合特征的参数一共有 n(n1)2 n ( n − 1 ) 2 种,任意两个参数是独立的。
当我们想把这个模型运用到点击率预测时,会发现训练每个二次项系数都需要大量的 xi x i xj x j 都非0的样本。而由于经过了one-hot编码,数据变得很稀疏,导致训练样本的不足, wij w i j 会不准确,因此二次项参数的训练是变得困难,验证影响模型的性能。

那么如何解决这个问题呢?矩阵分解提供了思路。

Key idea:Set wi,j=<vi,vj> K e y   i d e a : S e t   w i , j =< v i , v j >





vi v i
是k维的向量

idea的来源是不同的组合特征之间相互不是独立的,他们之间的联系通过一个隐因子建立。

例如在协同过滤中,一个rating矩阵可以分解为user矩阵和item矩阵,每个user和item都可以用一个隐向量表示,下例中把一个user表示成一个二维向量,同时把每个item表示成一个二维向量,两个向量的点积就是矩阵中user对item的打分。

协同过滤

类似的,所有的二次项系数 wij w i j 可以组成一个对称阵W,那么这个矩阵就可以分解为 VTV V T V ,V的第j列就是第j维特征的隐向量。因此,FM的模型方程为

y(x)=w0+i=1nwixi+i=1nj=i+1n<vi,vj>xixj y ( x ) = w 0 + ∑ i = 1 n w i x i + ∑ i = 1 n ∑ j = i + 1 n < v i , v j > x i x j



其中,

vi v i
是第

i i
维特征的隐向量,<·,·>代表点积。隐向量的长度为


k(k<<n)

k ( k << n )

包含

k k
个描述特征的因子。

这样变换后,二次项的参数减少到



k n

个,远少于多项式模型的参数数量。而且参数之间有了联系,比如

xhxi x h x i


xixj x i x j
的系数分别是

<vh,vi> < v h , v i > <script type="math/tex" id="MathJax-Element-22"> </script> 和

<vi,vj> < v i , v j > <script type="math/tex" id="MathJax-Element-23"> </script> 他们之间有共同项

vi v i
,这样所有包含

xi x i
的非零组合特征的样本都可以用来学习隐向量

vi v i
,很大程度上避免了数据稀疏造成的影响。

直观上看,FM的复杂度是

O(kn2) O ( k n 2 )
。通过下面式子化简,复杂度可以优化到

O(kn) O ( k n )



i=1nj=i+1n<vi,vj>xixj=12f=1k((i=1nvi,fxi)2i=1nv2i,fx2i) ∑ i = 1 n ∑ j = i + 1 n < v i , v j > x i x j = 1 2 ∑ f = 1 k ( ( ∑ i = 1 n v i , f x i ) 2 − ∑ i = 1 n v i , f 2 x i 2 )



推导过程如下


推导过程

利用SGD(Stochastic Gradient Descent)训练模型,梯度如下


θy(x)=1,xi,xinj=1vj,fxjvi,fx2i,if θ is w0if θ is wiif θ is vi,f ∂ ∂ θ y ( x ) = { 1 , if  θ  is  w 0 x i , if  θ  is  w i x i ∑ j = 1 n v j , f x j − v i , f x i 2 , if  θ  is  v i , f



其中,

vj,f v j , f
是隐向量

vj v j
的第

f f
个元素,由于



j = 1 n v j , f x j

只与

f f
有关,在每次迭代时只需计算一下所有



f



nj=1vj,fxj ∑ j = 1 n v j , f x j
就能够方便的得到所有

vj,f v j , f
的梯度,复杂度为

O(kn) O ( k n )
;已知

nj=1vj,fxj ∑ j = 1 n v j , f x j
时,计算每个参数梯度的复杂度是

O(1) O ( 1 )
;得到梯度后,更新每个参数的复杂度是

O(1) O ( 1 )
;模型参数一共有

nk+n+1 n k + n + 1
个。因此,FM参数训练的复杂度也是

O(kn) O ( k n )
。综上,FM可以在线性时间训练和预测,是一个非常高效的模型。

FFM原理

ffm引入了field的概念。不同field之间的隐因子变得不同。用 <vi,f(j),vj,f(i)> < v i , f ( j ) , v j , f ( i ) > <script type="math/tex" id="MathJax-Element-45"> </script> 代替 <vi,vj> < v i , v j > <script type="math/tex" id="MathJax-Element-46"> </script>,其中 f(i) f ( i ) 是特征 i i 所属的field。
简单来说,同一个类别型特征经过one-hot编码后的都可以放到同一个field中。
为什么要引入field这个概念呢?我的理解是FM模型的二次项参数是一个个相互独立的数,但是特征之间在根本上还是有原始类别的,FM模型并没有体现出这一点。现在把之前一个个的数变成了一个个的向量
vector
这样特征之间就有了域的概念了。
假设样本的n个特征属于f个field,那么FFM的二次项有nf个隐向量。而在FM中,每一维特征的隐向量只有一个。FM可以看做是只有一个field的FFM模型。FFM的模型方程如下



y ( x ) = w 0 + i = 1 n w i x i + i = 1 n j = i + 1 n < v i , f j , v j , f i > x i x j



其中,

fj f j
是第

j j
个特征所属的field。如果隐向量的长度为



k

, 那么FFM的二次参数有

nfk n f k
个,远多于FM模型的

nk n k
个。此外,由于隐向量与field相关,FFM的二次项不能化简,其预测的复杂度是

O(kn2) O ( k n 2 )


FFM的编码格式如下

数据

User Movie Genre Price
YuChin 3Idiots Comedy,Drama $9,99

FFM格式
ffm格式
转换成LIBFFM格式为
1:1:1 2:2:2 3:3:1 3:4:1 4:5:9.99
FFM的组合有10项,如下图所示
ffm

LIBFFM

台湾大学开发的libffm网址 http://www.csie.ntu.edu.tw/~cjlin/libffm/
这个版本的ffm省略了常数项和一次项。模型方程如下

ϕ(w,x)=j1,j2C2<wj1,f2,wj2,f1>xj1xj2 ϕ ( w , x ) = ∑ j 1 , j 2 ∈ C 2 < w j 1 , f 2 , w j 2 , f 1 > x j 1 x j 2



其中

C2 C 2
是非零特征的二元组合,

j1 j 1
是特征。属于field

f1 f 1
,

wj1,f2 w j 1 , f 2
是特征

j1 j 1
对field

f2 f 2
的隐向量。此FFM模型采用Logistic loss作为损失函数,L2惩罚项,因此只能用于二元分类


minwi=1Llog(1+exp{
yiϕ(w,xi)})+λ2||w||2
min w ∑ i = 1 L l o g ( 1 + e x p { − y i ϕ ( w , x i ) } ) + λ 2 | | w | | 2



算法采用SGD优化过程,在寻优时,采用了一些小技巧

第一,梯度分布计算。采用SGD训练FFM模型时,只采用单个样本的损失函数来计算模型参数的梯度。


=err+reg=log(1+exp({
yiϕ(w,xi)})+λ2||w||2
L = L e r r + L r e g = l o g ( 1 + e x p ( { − y i ϕ ( w , x i ) } ) + λ 2 | | w | | 2




w=errϕ.ϕw+regw ∂ L ∂ w = ∂ L e r r ∂ ϕ . ∂ ϕ ∂ w + ∂ L r e g ∂ w



上面公式表明,

errϕ ∂ L e r r ∂ ϕ
与具体模型参数无关,因此每次更新模型时,只需计算一次,之后每次调用值即可。

第二,自适应学习率。此版本的FFM实现没有采用常用的指数递减的学习率更新策略,而是利用

nfk n f k
个浮点数的临时空间,自适应地更新学习率。学习率是参考AdaGrad算法计算的,按如下更新


wj1,f2=wj1,f2η1+t(gtwj1,f2)2.gwj1,f2 w j 1 , f 2 ′ = w j 1 , f 2 − η 1 + ∑ t ( g w j 1 , f 2 t ) 2 . g w j 1 , f 2



其中,

wj1,f2 w j 1 , f 2
是特征

j1 j 1
对field

f2 f 2
隐向量的一个元素,

gwj1,f2 g w j 1 , f 2
是损失函数对参数

wj1,f2 w j 1 , f 2
的梯度;

gtwj1,f2 g w j 1 , f 2 t
是第t次迭代的梯度;

η η
是初始学习速率。可以看出,随着迭代的进行,每个参数的历史梯度会慢慢累加,导致每个参数的学习率逐渐减小。另外,每个参数的学习率更新速度是不同的,与其历史梯度有关,根据AdaGrad的特点,对于样本比较稀疏的特征,学习率高于样本比较密集的特征,因此每个参数既可以比较快速达到最优,也不会导致验证误差出现很大的震荡。

第三,OpenMP多核并行计算。OpenMP是用于共享内存并行系统的多处理器程序设计的编译方案,便于移植和多核扩展。FFM的源码采用了OpenMP的API,对参数训练过程SGD进行了多线程扩展,支持多线程编译。因此,OpenMP技术极大地提高了FFM的训练效率和多核CPU的利用率。在训练模型时,输入的训练参数ns_threads指定了线程数量,一般设定为CPU的核心数,便于完全利用CPU资源。

第四,SSE3指令并行编程。

除了上面的技巧之外,FFM的实现中还有很多调优技巧需要探索。例如,代码是按field和特征的编号申请参数空间的,如果选取了非连续或过大的编号,就会造成大量的内存浪费;在每个样本中加入值为1的新特征,相当于引入了因子化的一次项,避免了缺少一次项带来的模型偏差等。

使用LIBFFM的时候,注意不同的版本参数有些许不同,在最新的一些版本中没有-on-disk命令,模型默认使用硬盘训练。

源码分析

http://blog.csdn.net/zc02051126/article/details/54614230 介绍的很详细

应用

这次腾讯比赛中我们所构造的特征有原始类别型特征和一些统计量特征。
FFM更适用于类别特征,将类别特征onehot编码后输入到FFM中效果更好。
基于GBDT的树模型更适合数值型特征。
当同时拥有这两类特征时,将这两个模型组合起来效果会更好。如下如所示
FFM

代码戳
https://github.com/leisurehippo/txbs/blob/master/FFM_train.py

FM和树模型都能自动学习特征交叉组合,树模型适合连续中低度稀疏数据,容易学到高阶组合;FM适合高度稀疏数据。
树模型和简单二阶多项式模型中存在共同问题:不能学习到数据中不存在的模式,但FM可以。这是FM在高度稀疏数据建模优势的关键。
神经网络处理高维稀疏离散特征的一种手段:词向量。FM可以看做一种embedding的方法。word2vec词出现在上下文的概率和两个词向量内积正相关;FM用户和商品的偏好与用户向量和商品向量内积正相关。
FM和embedding都是通过将高维稀疏的离散特征映射为低维向量来增强其特征的泛化能力的。

deep&wide模型

将离散特征做低维嵌入,和连续特征一起放到神经网络里学习高度非线性的特征交叉。wide是将类别特征和数值特征一起学习,deep是一个深度网络,其实就是一些简单的relu和dense。
有点像上面操作的对偶形式?上面最终是用FFM学习类别特征,用xgb将数值特征提取类别特征。这个是用深度网络学习连续数值特征。用embedding的方式将离散高维特征提取低维表示。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/131849.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 深度学习笔记(三):激活函数和损失函数

    深度学习笔记(三):激活函数和损失函数这一部分来探讨下激活函数和损失函数。在之前的logistic和神经网络中,激活函数是sigmoid,损失函数是平方函数。但是这并不是固定的。事实上,这两部分都有很多其他不错的选项,下面来一一讨论3.激活函数和损失函数3.1激活函数关于激活函数,首先要搞清楚的问题是,激活函数是什么,有什么用?不用激活函数可不可以?答案是不可以。激活函数的主要作用是提供网络的非线性建模能力。如果没有激活函数,那么

    2022年7月14日
    16
  • mysql数据库优化大全

    mysql数据库优化大全

    2021年11月7日
    50
  • BIRCH详解_Bilabial

    BIRCH详解_BilabialBIRCH(BalancedIterativeReducingandClusteringusingHierarchies)详解第三十次写博客,本人数学基础不是太好,如果有幸能得到读者指正,感激不尽,希望能借此机会向大家学习。这一篇作为可伸缩聚类(ScalableClustering)算法的第三篇,主要是对BIRCH(BalancedIterativeReducingand…

    2025年6月16日
    0
  • s一般怎么称呼自己的m_一般要怎么选合适自己的中频熔炼炉呢?

    s一般怎么称呼自己的m_一般要怎么选合适自己的中频熔炼炉呢?中频熔炼炉全称“中频感应式熔炼炉”,又名中频熔金机,在金属熔炼领域有着广泛的应用,特别是对于首饰铸造加工行业,起着至关重要的地位。市面上的中频熔炼炉那么多要怎么去选择呢?要如何去选择一款安全可靠的设备支持我们的企业的生产不掉链子呢?那就点从下面几个因素开始考虑了。基本我们在挑选设备功率的时候,需要考虑五个因素,1、要根据日常的生产需要去选择相对产品的性能。例如要看加热的体积和相应面积;加热体积大…

    2022年6月23日
    42
  • 从入门到真香!java核心技术卷一pdf「建议收藏」

    从入门到真香!java核心技术卷一pdf「建议收藏」拼多多(三面)面试前面完蚂蚁后,早就听闻拼多多这个独角兽,决定也去面一把。首先我在脉脉找了一个拼多多的HR,加了微信聊了下,发了简历便开始我的拼多多面试之旅。这里要非常感谢拼多多HR小姐姐,从面试内推到offer确认一直都在帮我,人真的很nice。拼多多:一面为啥蚂蚁只待了三个月?没转正?Java中的HashMap、TreeMap解释下?TreeMap查询写入的时间复杂度多少?HashMap多线程有什么问题?CAS和synchronize有什么区别?都用synchronize不行么?如

    2022年7月7日
    35
  • Java学习:assert(断言)的使用——测试程序和AssertionError错误事件

    Java学习:assert(断言)的使用——测试程序和AssertionError错误事件assert是在J2SE1.4中引入的新特性,assertion就是在代码中包括的布尔型状态,程序员认为这个状态是true。一般来说assert在开发的时候是检查程序的安全性的,在发布的时候通常都不使用assert。在1.4中添加了assert关键字

    2025年9月17日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号