基于域的分解机模型(FFM)
目录
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数据集进行测试
-
from sklearn.datasets
import load_iris
-
from sklearn.model_selection
import train_test_split
-
#下载数据
-
iris_data = load_iris()
-
X = iris_data[
'data']
-
y = iris_data[
'target'] ==
2
-
X_train,X_test,y_train, y_test = train_test_split(X,y, test_size=
0.3, random_state=
0)
-
#处理数据,转化成dataframe格式,并转换成整型
-
data_train=pd.DataFrame(X_train, columns=[
'x1',
'x2',
'x3',
'x4'])
-
data_test=pd.DataFrame(X_test, columns=[
'x1',
'x2',
'x3',
'x4'])
-
-
data_train[
'int1'] = data_train[
'x1'].map(int)
-
data_train[
'int2'] = data_train[
'x2'].map(int)
-
data_train[
'int3'] = data_train[
'x3'].map(int)
-
data_train[
'int4'] = data_train[
'x4'].map(int)
-
data_train[
'clicked'] = y_train*
1
-
-
data_test[
'int1'] = data_test[
'x1'].map(int)
-
data_test[
'int2'] = data_test[
'x2'].map(int)
-
data_test[
'int3'] = data_test[
'x3'].map(int)
-
data_test[
'int4'] = data_test[
'x4'].map(int)
-
data_test[
'clicked'] = y_test*
1
-
-
#整合数据
-
data_train = data_train[[
'int1',
'int2',
'int3',
'clicked']]
-
data_test = data_test[[
'int1',
'int2',
'int3',
'clicked']]
-
-
#转换成libffm format:
-
#即:label,field_1:index_1:value_1,field_2:index_2:value_2 ...
-
ffm_fit = FFMFormatPandas()
-
-
ffm_train_data = ffm_fit.fit_transform(data_train, y=
'clicked')
-
ffm_test_data = ffm_fit.fit_transform(data_test, y=
'clicked')
-
-
import os
-
cwd = os.getcwd()
#获取当前路径
-
train_data = cwd +
'/ffm_train_data.txt'
#创建一个TXT文件
-
test_data = cwd +
'/ffm_test_data.txt'
#创建一个TXT文件
-
-
f=open(train_data,
'w')
-
for line
in ffm_train_data:
-
data=line+
"\n"
-
f.write(data)
#写入
-
-
f1=open(test_data,
'w')
-
for line
in ffm_test_data:
-
data1 = line+
"\n"
-
f1.write(data1)
#写入
使用xlearn进行训练
-
#通过 pip 安装 xLearn
-
- brew install cmake
-
- sudo pip install xlearn
-
import xlearn
as xl
-
-
# Training task
-
ffm_model = xl.create_ffm()
# Use field-aware factorization machine (ffm)
-
ffm_model.setTrain(
"./ffm_train_data.txt")
# Set the path of training dataset
-
ffm_model.setValidate(
"./ffm_test_data.txt")
# Set the path of validation dataset
-
-
# Parameters:
-
# 0. task: binary classification
-
# 1. learning rate: 0.2
-
# 2. regular lambda: 0.002
-
# 3. evaluation metric: accuracy
-
param = {
'task':
'binary',
'lr':
0.2,
'lambda':
0.002,
'metric':
'acc'}
-
-
# Start to train
-
# The trained model will be stored in model.out
-
ffm_model.setTXTModel(
"./irismodel.txt")
-
ffm_model.fit(param,
'./irismodel.out')
-
-
# Prediction task
-
ffm_model.setTest(
"./ffm_test_data.txt")
# Set the path of test dataset
-
ffm_model.setSigmoid()
# Convert output to 0-1
-
-
# Start to predict
-
# The output result will be stored in output.txt
-
ffm_model.predict(
"./irismodel.out",
"./irisoutput.txt")
可以看到预测acc=0.91
我们用FM模型做一下对比
-
from sklearn.datasets
import load_iris
-
from sklearn.model_selection
import train_test_split
-
from pyfm
import pylibfm
-
from sklearn.feature_extraction
import DictVectorizer
-
-
def load_data():
-
"""
-
调用sklearn的iris数据集,筛选正负样本并构造切分训练测试数据集
-
"""
-
iris_data = load_iris()
-
X = iris_data[
'data']
-
y = iris_data[
'target'] ==
2
-
data = [ {v: k
for k, v
in dict(zip(i, range(len(i)))).items()}
for i
in X]
-
X_train,X_test,y_train, y_test = train_test_split(data,y, test_size=
0.3, random_state=
0)
-
return X_train,X_test,y_train, y_test
-
-
X_train,X_test,y_train, y_test = load_data()
-
-
v = DictVectorizer()
-
X_train = v.fit_transform(X_train)
-
X_test = v.transform(X_test)
-
-
fm = pylibfm.FM(num_factors=
2,
-
num_iter=
20,
-
verbose=
True,
-
task=
"classification",
-
initial_learning_rate=
0.01,
-
learning_rate_schedule=
"optimal")
-
-
fm.fit(X_train, y_train)
-
-
y_preds = fm.predict(X_test)
-
y_preds_label = y_preds >
0.5
-
from sklearn.metrics
import log_loss,accuracy_score
-
print (
"Validation log loss: %.4f" % log_loss(y_test, y_preds))
-
print (
"accuracy: %.4f" % accuracy_score(y_test, y_preds_label))
Validation log loss: 0.3590 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