Fork me on GitHub

因子分解机 libFM| DeepFM| Deep&wide| NFM

1. 因子分解机

MF的回顾

矩阵分解:

  • 将矩阵拆解为多个矩阵的乘积
  • 用户users和商品Items的评分矩阵是稀疏的,因为他不会对所有商品打分;把它拆分为2部分,K值是降维的维度,比如user1对10w个电影打分,10w降为K维(K一般取100左右);
  • k维降维变成稠密矩阵; 

矩阵分解方法:

  • EVD(特征值分解)
  • SVD(奇异值分解)

求解近似矩阵分解的最优化问题

  • ALS(交替最小二乘法):ALS-WR
  • SGD(随机梯度下降):FunkSVD, BiasSVD, SVD++

 

  • 奇异值分解可以对矩阵进行无损分解
  • 在实际中,我们可以抽取前K个特征,对矩阵进行降维
  • SVD在降维中有效,抽取不同的K值(10%的特征包含99%的信息)
  • 在评分预测中使用funkSVD,只根据实际评分误差进行目标最优化
  • 在funkSVD的基础上,加入用户/商品偏好 => BiasSVD
  • 在BiasSVD的基础上,考虑用户的隐式反馈 => SVD++
  • 以上的MF,我们都只考虑user和item特征,但实际上一个预测问题包含的特征维度可能很多

MF局限性

什么是预测问题:

  • 在机器学习中,可以把预测问题看成是估计一个函数  

           

  • n维实特征向量 映射到目标域T,对于训练集,

           

  • 回归问题:T为实数
  • 二分类问题:T为{0,1}
  • n的维度往往很大,不只是二维,即userID, itemID

 

对于电影评分系统,特征维度也不仅仅是userID, itemID,还有其他维度:

  • S={(A, TI, 2010-1, 5),  #用户和电影之间 时间维度也很重要
    • (A, NH, 2010-2, 3),
    • (A, SW, 2010-4, 1),
    • (B, SW, 2009-5, 4),
    • (B, ST, 2009-8, 5),
    • (C, TI, 2009-9, 1),
    • (C, SW, 2009-12, 5),
    • ...
    • }

U={Alice(A), Bob(B), Charlie(C), ...}

I={Titanic(TI), Notting Hill(NH), Star Wars(SW), Star Trek(ST),...}

用户在什么时间,对哪部电影,打了多少分

我们需要预测y',即某个用户,在某个时刻,对某个电影的打分

构建两两交互打分;

蓝色部分:当前评分用户信息

红色部分:当前被评分电影

黄色:当前评分用户,评分过的其他电影

绿色:评分时间(按照2009年1月开始的间隔月数)

棕色:用户评分过的上一部电影

FM算法

FM 就相当于是for 循环里的矩阵分解,两两之间组合(原来的矩阵分解只定义了2个特征);

线性回归:

  • 对于特征向量 

              

  • 目标预估函数

            

    • 认为变量之间是相互独立的,没有考虑变量之间的相互关系,比如性别+年龄的组合
  • 对于二阶表达式:

            

    • 二阶特征组合:考虑了两个特征变量之间的交互影响
    • 使用Wij进行二阶特征组合的参数估计存在问题,即如果观察样本中没有出现过该交互的特征分量,那么直接估计将为0

 

对于Wij的估计,转换成了矩阵分解MF问题

直接计算,复杂度为O(k*n^2) ;n是特征个数,k是特征的embedding size

 

FM与MF的区别 

  • FM矩阵将User和Item都进行了one-hot编码作为特征,使得特征维度非常巨大且稀疏
  • 矩阵分解MF(例子中只有2个维度)是FM的特例,即特征只有User ID 和Item ID的FM模型;MF只考虑了UserID, ItemID的特征,而实际我们需要考虑更多的特征,甚至是多个特征之间的组合;
  • 矩阵分解MF只适用于评分预测问题,进行简单的特征计算,无法利用其他特征,而实际问题可能是回归和分类问题 需要更通用的解决方式;
  • FM引入了更多辅助信息(Side information)作为特征,FM考虑了更多特征以及二阶特征组合,可以作为通用的回归和分类算法;
  • FM在计算二阶特征组合系数的时候,使用了MF
  • 特征提取是对现实世界的拟合方式

                      

FM中的特征交叉:

  • CTR(Click Through Rate)预估是个二分类问题
  • 线性模型无法学习到特征之间的相关性,比如“ Country=USA” +“Day=26/11/15” ,“ Country=China”+“Day=19/2/15”,对用户的Click有正面影响

 

FM的学习算法:

  • ALS,交替最小二乘法
  • SGD,随机梯度下降法

 

    • 可以提前计算(与i无关),因此梯度的计算复杂度为O(1),参数更新的计算复杂度为O(k*n)
  • MCMC,马尔科夫链蒙特卡罗法

 

D-阶FM算法: 

           

  • 因为计算量大,一般FM采用2阶特征组合的方式 
  • 实际上高阶/非线性的特征组合适合采用深度模型
  • Wij = <Vi, Vj>是FM的核心思想,使得稀疏数据下学习不充分的问题也能得到充分解决        => 可提供的非零样本大大增加

 

FM算法的应用场景:

  • 回归问题,y'(x)直接作为预测值,损失函数可采用least square error

 

  • 二分类问题,FM的输出还需要经过 sigmoid 函数变换,也就是将y'(x)转化为二分类标签,即0,1:

                

              y(i)是真实样本的类别标记,正样本为1,负样本为0,m为样本总数

   损失函数可采用logistic loss

               

libFM

libFM(FM软件)

  • 下载地址:https://github.com/srendle/libfm
  • 使用文档:http://www.libfm.org/libfm-1.42.manual.pdf
  • FM论文作者Steffen Rendle提供的工具(2010年)
  • 在KDD CUP 2012,以及Music Hackathon中都取得不错的成绩
  • 不仅适用于推荐系统,还可以用于机器学习(分类问题)
  • 实现三种优化算法:SGD,ALS,MCMC
  • 支持2种输入数据格式:文本格式(推荐)和二进制格式

 

libFM数据格式

  • 每一行都包含一个训练数据(x,y),首先规定y的值,然后是x的非零值。
  • 对于二分类问题,y>0的类型被认为是正分类,y<=0被认为是负分类。

                        

                         3个样本,7个维度,y值 ;进行数据格式转换,y放前边,把有的值放进去即可; 

使用libFM自带的libsvm格式转换

  • triple_format_to_libfm.pl (perl文件)
  • -target 目标变量
  • -delete_column 不需要的变量
  • perl triple_format_to_libfm.pl -in ratings.dat -target 2 -delete_column 3 -separator "::"
  • 自动将.dat文件 => .libfm文件

 

使用libFM训练FM模型

  • -train 指定训练集,libfm格式或者二进制文件
  • -test 指定测试集,libfm格式或者二进制文件
  • -task,说明任务类型classification还是regression
  • dim,维度  指定k0,k1,k2,
  • -iter,迭代次数,默认100
  • -method,优化方式,可以使用SGD, SGDA, ALS, MCMC,默认为MCMC
  • -out,指定输出文件
  • libFM -task r -train ratings.dat.libfm -test ratings.dat.libfm -dim '1,1,8' -out out.txt

使用libFM进行分类

  • Titanic数据集,train.csv和test.csv
  • Step1,对train.csv和test.csv进行处理
    • 去掉列名,针对test.csv增加虚拟target列(值设置为1)
  • Step2,将train.csv, test.csv转换成libfm格式
    • perl triple_format_to_libfm.pl -in ./titanic/train.csv -target 1 -delete_column 0 -separator ","
    • perl triple_format_to_libfm.pl -in ./titanic/test.csv -target 1 -delete_column 0 -separator ","
  • Titanic数据集,train.csv和test.csv
  • Step3,使用libfm进行训练,输出结果文件 titanic_out.txt
    • libFM -task c -train ./titanic/train.csv.libfm -test ./titanic/test.csv.libfm -dim '1,1,8' -out titanic_out.txt

 

FFM算法

FM无法区分出来是哪个特征列 field; 

          

 

  • Field-aware Factorization Machines for CTR Prediction
  • https://www.csie.ntu.edu.tw/~cjlin/papers/ffm.pdf
  • 通过引入field的概念,FFM把相同性质的特征归于同一个field,比如“Day=26/11/15”、“Day=1/7/14”、“Day=19/2/15”这三个特征代表日期,放到同一个field中
  • 当“Day=26/11/15”与Country特征,Ad_type特征进行特征组合时,使用不同的隐向量(Field-aware),这是因为Country特征和Ad_type特征,本身的field不同

 

对于FM算法:

          

  • 每个特征有唯一的一个隐向量表示,这个隐向量被用来学习与其他任何特征之间的影响。
  • w(ESPN)用来学习与Nike的隐性影响w(ESPN)*w(Nike),同时也用来学习与Male的影响w(ESPN)*w(Male)。但是Nike和Male属于不同的领域,它们的隐性影响(latent effects)应该是不一样的。
  • 对于FFM算法:

             

             

  • 每个特征会有几个不同的隐向量,fj 是第 j 个特征所属的field

                  

 

FM算法:

  • 每个特征只有一个隐向量
  • FM是FFM的特例 

FFM算法

  • 每个特征有多个隐向量
  • 使用哪个,取决于和哪个向量进行点乘

FM的特点是 可以有2个特征项x1,x2,WESPN和WNIKE 可以做交叉学习,把之前没有学到的学出来;

FFM也要两两组合,学到的参数量更大了,它带有了方向看是跟谁去做交叉组合; 

               

 

FFM算法:

  • 隐向量的长度为 k,FFM的二次参数有 nfk 个,多于FM模型的 nk 个
  • 由于隐向量与field相关,FFM二次项并不能够化简,计算复杂度是 O(k*n^2),FFM的k值一般远小于FM的k值; 特征格式:field_id:feat_id:value
  • field_id代表field编号,feat_id代表特征编号,value是特征值。
  • 如果特征为数值型,只需分配单独的field编号,比如评分,item的历史CTR/CVR等。
  • 如果特征为分类(categorical)特征,需要经过One-Hot编码成数值型,编码产生的所有特征同属于一个field。特征值是0或1,比如性别、商品的品类id等

                   

 FFM的输入格式

有几个field就是原数特征有几列,4个field,5个特征feature; 

libFFM

  • https://github.com/ycjuan/libffm

xlearn

  • https://xlearn-doc-cn.readthedocs.io/en/latest/index.html
  • 提供Python接口
  • 支持LR,FM,FFM算法等
  • 运行效率高,比libfm, libffm快

 

criteo_ctr数据集

  • 展示广告CTR预估比赛
  • 欧洲大型重定向广告公司Criteo的互联网广告数据集(4000万训练样本,500万测试样本)
  • 原始数据:https://labs.criteo.com/2013/12/download-terabyte-click-logs/ 
  • small_train.txt 和 small_test.txt文件
    • (FFM数据格式,200条记录)

 

import xlearn as xl
# 创建FFM模型
ffm_model = xl.create_ffm()
# 设置训练集和测试集
ffm_model.setTrain("./small_train.txt")
ffm_model.setValidate("./small_test.txt")
# 设置参数,任务为二分类,学习率0.2,正则项lambda: 0.002,评估指标 accuracy
param = {'task':'binary', 'lr':0.2, 'lambda':0.002, 'metric':'acc'}
# FFM训练,并输出模型
ffm_model.fit(param, './model.out')
# 设置测试集,将输出结果转换为0-1
ffm_model.setTest("./small_test.txt")
ffm_model.setSigmoid()
ffm_model.predict("./model.out", "./output.txt")


xlearn工具输入数据格式:

LR ,FM 算法: CSV 或者 libsvm

FFM 算法:libffm 格式

DeepFM算法

DeepFM算法:

  • DeepFM: A Factorization-Machine based Neural Network for CTR Prediction,2017
  • https://arxiv.org/abs/1703.04247
  • FM可以做特征组合,但是计算量大 (kn^d),一般只考虑2阶特征组合
  • 如何既考虑低阶(1阶+2阶),又能考虑到高阶特征 => DeepFM=FM+DNN
  • 设计了一种end-to-end的模型结构 => 无须特征工程
  • 在各种benchmark和工程中效果好 
    • Criteo点击率预测, 4500万用户点击记录,90%样本用于训练,10%用于测试
    • Company*游戏中心,10亿记录,连续7天用户点击记录用于训练,之后1天用于测试

DeepFM = FM + DNN:

  • 提取低阶(low order)特征 => 因子分解机FM
    • 既可以做1阶特征建模,也可以做2阶特征建模
  • 提取高阶(high order)特征 => 神经网络DNN
  • end-to-end,共享特征输入
    • 对于特征i,wi是1阶特征的权重,
    • Vi表示该特征与其他特征的交互影响,输入到FM模型中可以获得特征的2阶特征表示,输入到DNN模型得到高阶特征。

    下图 左侧为FM、右侧为DNN,原始数据Sparse Features 稀疏项 做one hot编码,通过Embedding进行稠密化 (如果维度很大喂给DNN 会维度爆炸 影响收敛);

  左侧做低阶机器学习,右侧做深度学习,同一份数据做两个模型的训练,两路大军yFM和 yDNN 汇合; 

         

                

FM模型

  • 1阶特征建模
  • 通过隐向量点乘 => 得到2阶特征组合

                  

                            

Deep模型 

  • 原始特征向量维度非常大,高度稀疏,为了更好的发挥DNN模型学习高阶特征的能力,设计子网络结构(从输入层=>嵌入层),将原始的稀疏表示特征映射为稠密的特征向量。
    • Input Layer (one hot编码 如1w维)=> Embedding Layer (Embedding site 比如为100,)
  • 不同field特征长度不同,但是子网络输出的向量具有相同维度k
  • 利用FM模型的隐特征向量V作为网络权重初始化来获得子网络输出向量

             

         从稀疏到稠密,是一个嵌入的过程,

                          

什么是Embedding:

  • 一种降维方式,将不同特征转换为维度相同的向量
  • 在推荐系统中,对于离线变量我们需要转换成one-hot => 维度非常高,可以将其转换为embedding向量
  • 原来每个Field i维度很高,都统一降成k维embedding向量

                  

  • 方法:接入全连接层,对于每个Field只有一个位置为1,其余为0,因此得到的embedding就是图中连接的红线,对于Field 1来说就是 V11, V12, V13,...V1K
  • FM模型和Deep模型中的子网络权重共享,也就是对于同一个特征,向量Vi是相同的

                                  

DeepFM中的模块:

  • Sparse Features,输入多个稀疏特征
  • Dense Embeddings
    • 对每个稀疏特征做embedding,学习到他们的embedding向量(维度相等,均为k),因为需要将这些embedding向量送到FM层做内积。同时embedding进行了降维,更好发挥Deep Layer的高阶特征学习能力
  • FM Layer
    • 一阶特征:原始特征相加
    • 二阶特征:原始特征embedding后的embedding向量两两内积
  • Deep Layer,将每个embedding向量做级联,然后做多层的全连接,学习更深的特征
  • Output Units,将FM层输出与Deep层输出进行级联,接一个dense层,作为最终输出结果

                    

激活函数

  • relu和tanh比sigmoid更适合
  • relu对于所有深度模型来说更适合

Dropout

  • 设置为0.6-0.9之间最适合

 

 

神经元个数

  • 每层的神经元个数越多,未必越好
  • 设置为200-400个比较适合

隐藏层层数

  • 层数越多,起初效果明显,之后效果不明显,甚至更差,容易过拟合
  • 3层比较适合 

 

 

执行时间

  • 不论是CPU还是GPU,DeepFM的执行时间都是最短的

网络形状

  • 固定(200-200-200),增长(100-200-300), 下降(300-200-100), 菱形(150-300-150).
  • 采用固定的形状效果最好

 

Wide & Deep算法 

Wide & Deep模型

  • 论文:Wide & Deep Learning for Recommender Systems,2016 https://arxiv.org/abs/1606.07792
  • 推荐系统的挑战是 memorization与generalization
  • memorization,记忆能力(wide是LR),学习items或者features之间的相关频率,在历史数据中探索相关性的可行性
  • generalization,泛化(推理)能力(deep),基于相关性的传递,去探索一些在过去没有出现过的特征组合
  • 结合线性模型的记忆能力和DNN模型的泛化能力,在训练过程中同时优化两个模型的参数

 

Wide推荐

  • 系统通过获得用户的购物日志数据,包括用户点击哪些商品,购买过哪些商品,然后通过OneHot编码转换为离散特征
  • 好处是可解释性强,不足在于特征组合需要人为操作

Deep推荐

  • 通过深度学习出一些向量,这些向量是隐性特征,往往没有可解释性的

两个模型融合的方法

  • ensemble:两个模型分别对全量数据进行预测,然后根据权重组合最终的预测结果
  • joint training:wide和deep的特征合一,构成一个模型进行预测

Wide推荐

  • 采用Linear Regression,特征组合需要人来设计一个有d个特征的样本 X = [x1, x2, x3,...xd ]
  • 模型的参数 W = [w1, w2, w3,...wd
  • 线性回归 y =WT X + b  
  • 实际中往往需要交叉特征 

                   

  • 最终Wide模型

                

wide模型是线性模型,输入特征可以是连续特征,也可以是稀疏的离散特征。离散特征通过交叉可以组成更高维的离散特征。 

            

 

Deep推荐  

Deep模型使用的特征:连续特征,Embedding后的离散特征,

使用前馈网络模型,特征首先转换为低维稠密向量,作为第一个隐藏层的输入,解决维度爆炸问题

根据最终的loss反向训练更新。向量进行随机初始化,隐藏层的激活函数通常使用ReLU 

                 

Wide模型使用的特征:Cross Product Transformation生成的组合特征,但无法学习到训练集中没有出现的组合特征 

                 

  • 代表X的Cross Product Transformation
  • 代表Wide模型的权重向量
  • 代表Deep模型的权重向量
  • 代表最终的神经网络数结果,b为偏差

Wide join Deep

           

 

Google在2016年基于TensorFlow发布的用于分类和回归的模型,并应用到了 Google Play 的应用推荐中 ;

Wide & Deep模型不论在线下还是线上相比于单独的wide模型和单独的Deep模型,效果都有明显提升。 

 

                

 

NFM算法 

FNN, Wide & Deep, DeepFM都是在DNN部分,对embedding之后的特征进行concatenate,没有充分进行特征交叉计算。

NFM算法是对embedding直接采用对位相乘(element-wise)后相加起来作为交叉特征,然后通过DNN直接将特征压缩,最后concatenate linear部分和deep部分的特征。

两种FM和DNN的结合方式:

  • DeepFM, 并行结构,FM和DNN分开计算 
  • NFM,串行架构,将FM的结果作为DNN的输入

           

 

NFM模型

  • 对于输入X的预测公式: 

                   

  • Embedding层:全连接层,将稀疏输入映射到一个密集向量,得到  Vx = {x1v1, x2v2, ... xnv
  • BI层: 池化操作,将一系列的Embedding向量转换为一个向量 

                  

  • 隐藏层:神经网络的全连接层
  • 预测层:将隐藏层输出到n*1的全连接层, Vx = {x1v1, x2v2, ... xnv
    • 得到预测结果

           

NFM模型

FM 通过隐向量完成特征组合工作,同时解决了稀疏的问题。但是 FM 对于 non-linear 和 higher-order 特征交叉能力不足,因此可以使用FM和DNN来弥补这个不足 => NFM

BI层,将每个特征embedding进行两两做元素积, BI层的输出是一个 k维向量(隐向量的大小),BI层负责了二阶特征组合

可以将FM看成是NFM模型 Hidden Layer层数为0的一种特殊情况

            

DeepCTR工具

  • https://github.com/shenweichen/DeepCTR
  • 实现了多种CTR深度模型
  • 与Tensorflow 1.4和2.0兼容

            

 

数据集:MovieLens_Sample

包括了多个特征:user_id, movie_id, rating, timestamp, title, genres, gender, age, occupation, zip

使用DeepFM、DeepCTR中的WDL、DeepCTR中的NFM,计算RMSE值

data = pd.read_csv("movielens_sample.txt")
sparse_features = ["movie_id", "user_id", "gender", "age", "occupation", "zip"]
target = ['rating']
……
train, test = train_test_split(data, test_size=0.2)
train_model_input = {name:train[name].values for name in feature_names}
test_model_input = {name:test[name].values for name in feature_names}
# 使用DeepFM进行训练
model = DeepFM(linear_feature_columns, dnn_feature_columns, task='regression')
model.compile("adam", "mse", metrics=['mse'], )
history = model.fit(train_model_input, train[target].values,batch_size=256, epochs=1, verbose=True, validation_split=0.2, )
# 使用DeepFM进行预测
pred_ans = model.predict(test_model_input, batch_size=256)


# 使用WDL进行训练
model = WDL(linear_feature_columns, dnn_feature_columns, task='regression')
model.compile("adam", "mse", metrics=['mse'], )
history = model.fit(train_model_input, train[target].values,batch_size=256, epochs=10, verbose=True, validation_split=0.2, )
# 使用WDL进行预测
pred_ans = model.predict(test_model_input, batch_size=256)


# 使用NFM进行训练
model = NFM(linear_feature_columns, dnn_feature_columns, task='regression')
model.compile("adam", "mse", metrics=['mse'], )
history = model.fit(train_model_input, train[target].values,batch_size=256, epochs=10, verbose=True, validation_split=0.2, )
# 使用NFM进行预测
pred_ans = model.predict(test_model_input, batch_size=256)

 Summary

FM算法的作用:

  • 泛化能力强,解决大规模稀疏数据下的特征组合问题,不仅是UserID, ItemID特征
  • MF是FM的特例,使用了特征embedding(User,Item)。FM使用了更多Side Information作为特征,同时在进行二阶特征组合权重预估的时候,使用到了MF
  • 计算复杂度,可以在线性时间对样本做出预测,通过公式变换将计算复杂度降到O(k*n)

特征组合的作用:

  • 更好的模拟真实世界中的影响因素
  • DeepFM采用了FM+DNN的方式,在低阶和高阶特征组合上更接近真实世界,因此效果也更好
  • 很多特征,在高阶情况下,人很难理解,但是机器可以发现规律(使用DNN模型)

 

 

 

 

 
 
 
 
 
 
 
 
posted @ 2023-07-07 08:23  kris12  阅读(101)  评论(0编辑  收藏  举报
levels of contents