基于域的分解机(FFM)理论介绍及libFFM源码解析

来源:互联网 发布:深圳淘宝开店培训 编辑:程序博客网 时间:2024/06/01 08:21

符号说明:

  • x
  • y
  • i(xi,yi)便x=xii

1 基于域的分解机模型(FFM)

1.1 线性模型

(w,x)=wTx=w0+jC1wjxj(1)

C1x

1.2 二次多项式模型

(w,x)=wTx=w0+j1,j2C2wj1,j2xj1xj2(2)

C2x

1.3 分解机模型

(w,x)=wTx=w0+jC1wjxj+j1,j2C2wj1,wj2xj1xj2(3)

wwiikk

1.4 域分解机模型

(w,x)=wTx=w0+jC1wjxj+j1,j2C2wj1,fj2,wj2,fj1xj1xj2(4)

因为线性项

w0+jC1wjxj
在训练过程中非常容易计算,所以可以先将其忽略,只需要研究
j1,j2C2wj1,fj2,wj2,fj1xj1xj2
,将其写成另一种形式

i=1nj=i+1nwi,fj,wj,fixixj(5)

1.5 基于FFM的逻辑回归模型

1.5.1 -1,1损失的逻辑回归模型

minwp=1L(log(1+exp(yp(w,xp)))+λ2w2)(6)

其中

(w,xp)=i=1nj=i+1nwi,fp,j,wj,fp,ixp,ixp,j(7)

L(xp,yp)=log(1+exp(yp(w,xp)))+λ2w2(8)

其为每个样本的损失函数。

L(xp,yp)w

L(xp,yp)w=ypexp(yp(w,xp))1+exp(yp(w,xp))(w,xp)w+λw(9)

在式(8)的导数计算公式中,

ypexp(yp(w,xp))1+exp(yp(w,xp))
是标量,对于每个样本只要按照式(7)计算即可,第二项
λw
用惩罚参数乘以对应的参数即可,比较麻烦的就是
(w,xp)w
的计算。

1.5.2 (w,xp)的求导过程$

为了便于理解首先对

wi,fp,j,wj,fp,ixp,ixp,j
中的
wi,fp,j
wj,fp,i
分别的偏导数如下

wi,fp,j,wj,fp,ixp,ixp,jwi,fp,j=wj,fp,ixp,ixp,j(10)

wi,fp,j,wj,fp,ixp,ixp,jwj,fp,i=wi,fp,jxp,ixp,j(11)

(w,xp)wi,fp,jwj,fp,i

(w,xp)wi,fp,j=i=1nj=i+1nwj,fp,ixp,ixp,j(12)

(w,xp)wj,fp,i=i=1nj=i+1nwi,fp,jxp,ixp,j(13)

训练模型时需要注意的问题:在式(12)和式(13)中会存在
wj,fp,i(xt,ixt,j+xq,ixq,j+)
在训练时不需要合并这些项,只要把这些项当成更新参数
wj,fp,i
的多个样本即可,这在编程实现中将非常有用。

假设有如下的例子,五个特征,两个域


这里写图片描述
图1

7p1

(w,x)=i=15j=i+15wi,fj,wj,fixixj

=w1,f1,w2,f1x1x2+w1,f1,w3,f1x1x3+w1,f2,w4,f1x1x4+w1,f2,w5,f1x1x5

=w2,f1,w3,f1x2x3+w2,f2,w4,f1x2x4+w2,f2,w5,f1x2x5

=w3,f2,w4,f1x3x4+w3,f2,w5,f1x3x5

=w4,f2,w5,f1x4x5

i=1,fj=1i=3,fj=2

(w,x)w1,f1=w2,f1x1x2+w3,f1x1x3

(w,x)w3,f2=w4,f1x3x4+w5,f1x3x5

在模型学习时,需要迭代公式

w=w+ηg
η,g
分别为学习率和梯度向量,则在计算
w3,f2
时有两种方式:

方式1:

w3,f2=w3,f2+η(w4,f1x3x4+w5,f1x3x5)

方式2:

w3,f2=w3,f2+η1(w4,f1x3x4)

w3,f2=w3,f2+η2(w5,f1x3x5)

AdaGradη

在学习过程中是采用方式1还是方式2哪。答案是方式2。因为在计算实际问题时可能特征分布在多个域中,如果按照方式1则需要把每个域中的信息累加起来,结果是编程上非常麻烦,如果按照方式2,非常符合SGD的思想,把w4,f1x3x4w5,f1x3x5看成两个样本,再带回到x3x4组合时更新下w3,f2,当访问到x3x5组合时再次更新下w3,f2,在实际编程中,具体更新哪些参数可以通过相应的索引进行访问,非常方便。

2 libFFM介绍

2.1 使用介绍

1)编译
下载libffm-1.13.tar.gz,解压,在libffm-1.13下直接make,会产生两个可执行的文件ffm-train和ffm-predict分别用于训练和预测结果。
2)输入数据格式

<label>\t<field1>:<index1>:<value1>\t<field2>:<index2>:<value2>\t...fieldi表示域的id,indexi表示特征的id,它们都是非负值。0 1:7759:0.3651 2:7921:0.3651 3:8661:0.3651 4:9619:0.36511 1:7633:0.3651 2:8195:0.3651 3:9952:0.3651 4:9619:0.3651**注意:**libFFM中使用的是“-11”损失函数,所以label只能取-1或者1,而这里的样例数据中目标是01。因为在libFFM中读取数据时将大于0的目标当成1,小于等于0的当成-1。所以数据中的01并不表示损失是01。 |

3)命令行调用方式
训练ffm-train [options] training_set_file [model_file]
options控制参数如下表

1options

-l 惩罚参数,缺省值0.00002 -k 隐变量的个数,缺省值4 -t 模型训练时的迭代次数,缺省值15 -r 初始学习率,缺省值0.2 -s OpenMP的线程数,缺省值1 -p Validation数据的路径 -v 交叉验证中的折数 –quiet 控制是否输出运行时信息 –no-norm 关闭归一化功能,缺省是对样本进行归一化处理 –no-rand 关闭随机更新功能 –on-disk 当有这个参数时,不会将数据全部读入内存,而是将训练数据存储在磁盘,此时产生一个临时文件.bin –auto-stop: 当达到最优的validation损失时自动停止,必须与-p参数一同使用

说明:

  • –no-norm:缺省状态时用每个样本向量的2范数对样本中的每个元素进行归一化,如果设置了这个参数,则不进行归一化处理。
  • –no-rand:缺省状态训练时随机从数据中选取训练样本,如果不想随机的抽取训练样本,而是想按照数据集中的样本顺序训练,则可以使用此参数,同时还要配合参数”-s
    1”使用。
  • –on-disk:如果训练数据较大内存无法加载全量数据,则可以使用此参数。需要注意的是,这种训练模式下不支持随机的抽取训练数据,所以此时需要设置参数“—no-rand”,同时这种模式下不支持交叉验证。同时还会在磁盘上产生临时的二进制文件[training_set_file].bin。
    “`

预测
ffm-predict test_file model_file output_file

作者给的几个例子

  • 使用缺省参数训练模型

    • ffm-train bigdata.tr.txt model
  • 使用如下参数训练模型

    • regularization cost = 0.001
    • latent factors = 16
    • iterations = 30
    • learning rate = 0.05
    • threads = 4
    • ffm-train -l 0.001 -k 16 -t 30 -r 0.05 -s 4 bigdata.tr.txt model
  • 使用bigdata.te.txt作为validation数据
    • ffm-train -p bigdata.te.txt bigdata.tr.txt model
  • 使用5折交叉验证
    • ffm-train -v 5 bigdata.tr.txt
  • 用–quiet参数训练时不打印训练信息
    • ffm-train –quiet bigdata.tr.txt
  • 预测
    -ffm-predict bigdata.te.txt model output

  • 基于磁盘的训练

ffm-train –no-rand –on-disk bigdata.tr.txt

  • 使用–auto-stop参数,当达到最优的validation损失时停止训练

ffm-train -p bigdata.te.txt -t 100 bigdata.tr.txt

2.2 源码分析

2.2.1 存储模型用到的数据结构

  • 存储特征的结构体如下
    fidjidv


这里写图片描述
图2

  • 存储整个训练数据集的结构体如下
    mlmXPXY

    这里写图片描述
    图3
  • XY


这里写图片描述
图4
- 存储模型结构体如下:下面会主要介绍W的结构

这里写图片描述
图5

  • W

    这里写图片描述
    图6

2.2.2 训练

  • 主要的训练方法为

shared_ptr train(ffm_problem *tr,vector &order, ffm_parameter param, ffm_problem *va=nullptr)
在该方法中采用AdaGrad对每个样本进行训练。值得一提的是该方法中采用OpenMP和SSE进行加速,为了理解加速的位置,下面将伪码写出

for i = 0:l //l为特征个数
ffm_float t = wTx(xi, model);
ffm_float expnyt = exp(-y*t);
tr_loss += log(1+expnyt);
ffm_float kappa = -y*expnyt/(1+expnyt);
wTx(begin, end, r, *model, kappa, param.eta, param.lambda, true);
end

  • 下面两条获得进程数
    ffm_int old_nr_threads = omp_get_num_threads();
    omp_set_num_threads(param.nr_threads);
    下面的指令使多线程中的ffm_float t = wTx(xi, model);
    ffm_float expnyt = exp(-y*t);结果,在tr_loss += log(1+expnyt);出合并然后继续下面的过线程
    #pragma omp parallel for schedule(static) reduction(+: tr_loss)


这里写图片描述

  • libFFM中加速技术概述

    这里写图片描述
    图7

OpenMP

代码被多个线程并行执行


这里写图片描述
图8

for循环被发送到多个线程执行


这里写图片描述
图9

for循环先被发送到多个线程,接着合并,然后再次被分发


这里写图片描述
图10

对比加速图


这里写图片描述
图11

SSE

SSE(Streaming SIMD Extensions)/AVX(Advanced Vector Extensions)是Intel公司设计,对其X86体系的SIMD扩展指令集,它基于SIMD向量化技术,增强X86多核处理器的图像和视频处理能力
常用指令如下
_mm_load_ps 从数组中读取向量到寄存器
_mm_store_ps 将寄存器中的向量存储到数组
_mm_add_ps 寄存器中的向量相加
_mm_sub_ps 寄存器中的向量相减
_mm_mul_ps 寄存器中的向量相乘
_mm_rsqrt_ps 寄存器中的向量开方倒数


这里写图片描述
图12

0 0