飞道的博客

FFM原理及python实战

416人阅读  评论(0)

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

目录

1、FFM的理解

2、FFM优化

3、优缺点

4. 使用FFM需要注意的地方

5、python实战


1、FFM的理解

场感知分解机器(Field-aware Factorization Machine ,简称FFM)最初的概念来自于Yu-Chin Juan与其比赛队员,它们借鉴了辣子Michael Jahrer的论文中field概念,提出了FM的升级版模型。通过引入field的概念,FFM把相同性质的特征归于同一个field。

给出一下输入数据:

User

Movie

Genre

Price

YuChin

3Idiots

Comedy, Drama

$9.9

对于上面表格中的genre有两个属性Comedy, Drama,那么这两个特征因该属于一个field—Genre。

在FFM中,每一维特征Xi,针对其它特征的每一种field fj,都会学习一个隐向量Vi,fj。因此,隐向量不仅与特征相关,也与field相关。

假设每条样本的n个特征属于f个field,那么FFM的二次项有nf个隐向量。而在FM模型中,每一维特征的隐向量只有一个。因此可以把FM看作是FFM的特例,即把所有的特征都归属到一个field是的FFM模型。根据FFM的field敏感特性,可以导出其模型表达式:

其中,fj是第j个特征所属的field。如果隐向量的长度为k,那么FFM的二交叉项参数就有nfk个,远多于FM模型的nk个。此外,由于隐向量与field相关,FFM的交叉项并不能够像FM那样做化简,其预测复杂度为O()。

对于上面的数据来说

PS:对于数值型特征,实际应用中通常会把价格划分为若干个区间(即连续特征离散化),然后再one-hot编码,当然不是所有的连续型特征都要做离散化,比如某广告位、某类广告/商品、抑或某类人群统计的历史CTR(pseudo-CTR)通常无需做离散化。

该条记录可以编码为5个数值特征,即User^YuChin, Movie^3Idiots, Genre^Comedy, Genre^Drama, Price^9.9。其中Genre^Comedy, Genre^Drama属于同一个field。为了说明FFM的样本格式,我们把所有的特征和对应的field映射成整数编号。

 

Field Name

Field Index

Feature Name

Feature Index

User

1

User^YuChin

1

Movie

2

Movie^3Idiots

2

Genre

3

Genre^Comedy

3

Price

4

Genre^Drama

4

 

 

Price^9.9

5

 

其中,红色表示Field编码,蓝色表示Feature编码,绿色表示样本的组合特征取值。二阶交叉项的系数是通过与Field相关的隐向量的内积得到的,具体可见下图。

 

其损失函数定义为:

其中m为样本总个数,采用的loss为logloss,y的取值为-1和1。

2、FFM优化

这里使用AdaGrad优化算法去求解参数,因为目前一些研究显示了该算法在矩阵分解上的有效性,矩阵分解属于FFM的一个特例。具体训练方法如下:

在随机梯度的每一步中,对数据点(x,y)进行采样,并更新上面公式中的。因为X在CTR中是onehot表示,而且非常稀疏的,这里只更新有非零值的维度。

使用AdaGrad去优化式子,x是稀疏的,只去更新那些x数据中非0项

对每个隐向量的维度d ,累积梯度平方的和:

η是超参数,需要人工去指定, W的初始化是通过从均匀分布[0,1/√k]中随机采样,为了防止√γ的值过大,γ的初始值设置为1。

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

 

3、优缺点

FFM优点: 
增加field的概念,同一特征针对不同field使用不同隐向量,模型建模更加准确

FFM缺点: 
计算复杂度比较高,参数个数为nfk,计算复杂度为O(kn2)  

 

4. 使用FFM需要注意的地方

•样本归一化。对样本进行归一化,否则容易造成数据溢出,梯度计算失败

•特征归一化。为了消除不同特征取值范围不同造成的问题,需要对特征进行归一化

•Early stopping。一定要设置该策略,FFM很容易过拟合

•省略零值特征。零值特征对模型没有任何贡献,省略零值特征,可以提高FFM模型训练和预测的速度,这也是稀疏样本采用FFM的显著优势

 

5、python实战

我们这里使用iris_data数据集进行测试


  
  1. from sklearn.datasets import load_iris
  2. from sklearn.model_selection import train_test_split
  3. #下载数据
  4. iris_data = load_iris()
  5. X = iris_data[ 'data']
  6. y = iris_data[ 'target'] == 2
  7. X_train,X_test,y_train, y_test = train_test_split(X,y, test_size= 0.3, random_state= 0)
  8. #处理数据,转化成dataframe格式,并转换成整型
  9. data_train=pd.DataFrame(X_train, columns=[ 'x1', 'x2', 'x3', 'x4'])
  10. data_test=pd.DataFrame(X_test, columns=[ 'x1', 'x2', 'x3', 'x4'])
  11. data_train[ 'int1'] = data_train[ 'x1'].map(int)
  12. data_train[ 'int2'] = data_train[ 'x2'].map(int)
  13. data_train[ 'int3'] = data_train[ 'x3'].map(int)
  14. data_train[ 'int4'] = data_train[ 'x4'].map(int)
  15. data_train[ 'clicked'] = y_train* 1
  16. data_test[ 'int1'] = data_test[ 'x1'].map(int)
  17. data_test[ 'int2'] = data_test[ 'x2'].map(int)
  18. data_test[ 'int3'] = data_test[ 'x3'].map(int)
  19. data_test[ 'int4'] = data_test[ 'x4'].map(int)
  20. data_test[ 'clicked'] = y_test* 1
  21. #整合数据
  22. data_train = data_train[[ 'int1', 'int2', 'int3', 'clicked']]
  23. data_test = data_test[[ 'int1', 'int2', 'int3', 'clicked']]
  24. #转换成libffm format:
  25. #即:label,field_1:index_1:value_1,field_2:index_2:value_2 ...
  26. ffm_fit = FFMFormatPandas()
  27. ffm_train_data = ffm_fit.fit_transform(data_train, y= 'clicked')
  28. ffm_test_data = ffm_fit.fit_transform(data_test, y= 'clicked')
  29. import os
  30. cwd = os.getcwd() #获取当前路径
  31. train_data = cwd + '/ffm_train_data.txt' #创建一个TXT文件
  32. test_data = cwd + '/ffm_test_data.txt' #创建一个TXT文件
  33. f=open(train_data, 'w')
  34. for line in ffm_train_data:
  35. data=line+ "\n"
  36. f.write(data) #写入
  37. f1=open(test_data, 'w')
  38. for line in ffm_test_data:
  39. data1 = line+ "\n"
  40. f1.write(data1) #写入

使用xlearn进行训练


  
  1. #通过 pip 安装 xLearn
  2. - brew install cmake
  3. - sudo pip install xlearn

  
  1. import xlearn as xl
  2. # Training task
  3. ffm_model = xl.create_ffm() # Use field-aware factorization machine (ffm)
  4. ffm_model.setTrain( "./ffm_train_data.txt") # Set the path of training dataset
  5. ffm_model.setValidate( "./ffm_test_data.txt") # Set the path of validation dataset
  6. # Parameters:
  7. # 0. task: binary classification
  8. # 1. learning rate: 0.2
  9. # 2. regular lambda: 0.002
  10. # 3. evaluation metric: accuracy
  11. param = { 'task': 'binary', 'lr': 0.2, 'lambda': 0.002, 'metric': 'acc'}
  12. # Start to train
  13. # The trained model will be stored in model.out
  14. ffm_model.setTXTModel( "./irismodel.txt")
  15. ffm_model.fit(param, './irismodel.out')
  16. # Prediction task
  17. ffm_model.setTest( "./ffm_test_data.txt") # Set the path of test dataset
  18. ffm_model.setSigmoid() # Convert output to 0-1
  19. # Start to predict
  20. # The output result will be stored in output.txt
  21. ffm_model.predict( "./irismodel.out", "./irisoutput.txt")

可以看到预测acc=0.91

我们用FM模型做一下对比


  
  1. from sklearn.datasets import load_iris
  2. from sklearn.model_selection import train_test_split
  3. from pyfm import pylibfm
  4. from sklearn.feature_extraction import DictVectorizer
  5. def load_data():
  6. """
  7. 调用sklearn的iris数据集,筛选正负样本并构造切分训练测试数据集
  8. """
  9. iris_data = load_iris()
  10. X = iris_data[ 'data']
  11. y = iris_data[ 'target'] == 2
  12. data = [ {v: k for k, v in dict(zip(i, range(len(i)))).items()} for i in X]
  13. X_train,X_test,y_train, y_test = train_test_split(data,y, test_size= 0.3, random_state= 0)
  14. return X_train,X_test,y_train, y_test
  15. X_train,X_test,y_train, y_test = load_data()
  16. v = DictVectorizer()
  17. X_train = v.fit_transform(X_train)
  18. X_test = v.transform(X_test)
  19. fm = pylibfm.FM(num_factors= 2,
  20. num_iter= 20,
  21. verbose= True,
  22. task= "classification",
  23. initial_learning_rate= 0.01,
  24. learning_rate_schedule= "optimal")
  25. fm.fit(X_train, y_train)
  26. y_preds = fm.predict(X_test)
  27. y_preds_label = y_preds > 0.5
  28. from sklearn.metrics import log_loss,accuracy_score
  29. print ( "Validation log loss: %.4f" % log_loss(y_test, y_preds))
  30. print ( "accuracy: %.4f" % accuracy_score(y_test, y_preds_label))

   
  1. Validation log loss: 0.3590
  2. accuracy: 0.7556

而且这里我们FFM的lr=0.2,FM的lr=0.01

FFM确实比FM更精准一些

 

 

参考:

https://zhuanlan.zhihu.com/p/50692817

https://xlearn-doc-cn.readthedocs.io/en/latest/index.html

 


转载:https://blog.csdn.net/Andy_shenzl/article/details/105069988
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场