FM因子分解机
一、FM模型基础
1. FM模型提出
2. 共轭转置矩阵
转置矩阵:
- 将矩阵的行列对换。
共轭转置矩阵:
- 将矩阵的行列对换且每个元素共轭。
3. 正定矩阵
一种实对称矩阵:可理解为所有特征值都大于0的对称矩阵。
4. Cholesky分解
将对称正定矩阵化为一个三角矩阵与其共轭转置矩阵的积的分解。
A是n阶对称正定矩阵,则存在唯一的对角元均为正数的下三角矩阵L,使得:
A
=
L
L
T
A = LL^T
A=LLT
则分解该A被称为Cholesky分解。
5. 线性回归模型
- 特征之间相互独立不相关
- 特征的作用可以叠加
6. 多项式回归模型
特征两两组合,得到二阶多项式回归模型。
y
=
w
0
+
∑
i
=
1
n
w
i
x
i
+
∑
i
=
1
n
∑
j
>
=
1
n
w
i
j
x
i
x
j
y = w_0 + \sum_{i=1}^{n}w_ix_i + \sum_{i=1}^{n} \sum_{j>=1}^{n}{w_{ij}x_ix_j}
y=w0+i=1∑nwixi+i=1∑nj>=1∑nwijxixj
FM模型:
上述方程中,
w
i
j
w_{ij}
wij可以组成一个对称矩阵W。
根据Cholesky分解,可以分解成
W
=
V
V
T
W = VV^T
W=VVT
即
w
i
j
=
<
V
i
,
V
j
>
w_{ij} = <V_i,V_j>
wij=<Vi,Vj>
故二次项方程可以写成
y
=
w
0
+
∑
i
=
1
n
w
i
x
i
+
∑
i
=
1
n
∑
j
=
i
+
1
n
<
v
i
,
j
i
>
x
i
x
j
y = w_0 + \sum_{i=1}^{n}w_ix_i + \sum_{i=1}^{n}\sum_{j=i+1}^{n}<v_i,j_i>x_ix_j
y=w0+i=1∑nwixi+i=1∑nj=i+1∑n<vi,ji>xixj
既得FM模型的方程。
二、FM模型原理
1.FM模型原理
FM模型的方程:
y
=
w
0
+
∑
i
=
1
n
w
i
x
i
+
∑
i
=
1
n
∑
j
=
i
+
1
n
<
v
i
,
j
i
>
x
i
x
j
y = w_0 + \sum_{i=1}^{n}w_ix_i + \sum_{i=1}^{n}\sum_{j=i+1}^{n}<v_i,j_i>x_ix_j
y=w0+i=1∑nwixi+i=1∑nj=i+1∑n<vi,ji>xixj
v
i
v_i
vi是第i维特征的隐向量,<·,·>代表向量点积。
隐向量的长度为k(k<<n),包含k个描述特征的因子。
FM模型关键是:特征两两相关
2.FM模型计算
FM模型的二次项等价化简过程如下:
FM模型最后化简如下图所示:
FM模型的时间复杂度降级到线性。
3.FM模型训练
训练方法:随机梯度下降。
4.FM模型特征工程
- FM的特征是显示的
- FM的特征是自动组合
三、FM模型应用
FM模型可以用于回归任务、二分类任务、排名任务。
1. libFM
libFM是用于分解机的软件实现,具有**随机梯度下降(SGD)和交替最小二乘(ALS)优化以及使用马尔可夫链蒙特卡洛(MCMC)**进行的贝叶斯推理。
- SGD:随机梯度下降法
- ALS:交替最小二乘法
- MCMC:马尔科夫链蒙特卡罗法
四、FM模型总结
1、FM模型优点
FM模型适用与数据稀疏场景。
2、线性回归 VS FM
FM模型由线性回归模型演化出来。
最大区别是:线性回归模型的特征独立,而FM模型的特征两两相关。
3、FM模型演化
在FM模型的基础上,发展出FFM、DeepFM、xDeepFM等模型。
FM模型反映了特征两两相关,但很多时候某一类特征之间存在关系,或者全部特征都存在关系,这时候FM模型并不适用,所以在FM模型基础上发展出FFM、DeepFM、xDeepFM等模型。
# FM糖尿病分类预估
import numpy as np
import pandas as pd
import random
import os
from math import exp
from numpy import * # 导入numpy库函数
from random import normalvariate # 正态分布
from datetime import datetime
url = 'D:\编程学习\Machine-Learning-with-Python-master\\'
# *************************************************
# 功能:数据及切分
# 输入:数据文件名和训练集、测试集切分比例
# 输出:list类型的训练数据集和测试集
# *************************************************
dict = {}
dict2 = {}
def loadData(filename,ratio):
diabetes_data = pd.read_csv(filename + 'diabetes.csv')
print(len(diabetes_data))
for row in range(len(diabetes_data)):
if random.random() < ratio:
dict[row] = diabetes_data.iloc[row]
else:
dict2[row] = diabetes_data.iloc[row]
diabetes_train_Data = pd.DataFrame(dict).T.reset_index(drop=True)
diabetes_test_Data = pd.DataFrame(dict2).T.reset_index(drop=True)
# drop=True: 把原来的索引index列去掉,丢掉。
# inplace=True:不创建新的对象,直接对原始对象进行修改;
return diabetes_train_Data,diabetes_test_Data
train_data,test_data = loadData(url,0.8)
print(train_data)
print(test_data)
# 数据预处理
def preprocessData(data):
feature = data.iloc[:,:-1]
label = data.iloc[:,-1].map(lambda x:1 if x==1 else -1)
# 将数组按行归一化处理
zmax,zmin = feature.max(axis=0),feature.min(axis=0)
feature = (feature-zmin) / (zmax - zmin)
label = np.array(label)
return feature,label
train_data_feature,train_data_label = preprocessData(train_data)
print(mat(train_data_feature))
def sigmoid(inx):
return 1.0 / (1 + exp(-inx))
def FM(trainMatrix,label,k,iter):
'''
:param trainMatrix: 特征矩阵
:param label: 类别矩阵
:param k: 辅助向量的大小
:param iter: 迭代次数
:return: 常数项w_0, 一阶特征系数w, 二阶交叉特征系数v
'''
# trainMatrix是矩阵,label为列表
m,n = shape(trainMatrix) # 矩阵的行列数,即样本数m和特征数n
alpha = 0.1
# 初始化参数
w = zeros((n,1)) # 一阶特征的系数
w_0 = 0 # 常数项
v = normalvariate(0,0.2) * ones((n,k)) #即生成辅助向量(n*k),用来训练二阶交叉特征的系数
for it in range(iter):
for x in range(m): # 随机优化,每次使用一个样本
# 二次项的计算
inter_1 = trainMatrix[x] * v # 每个样本(1*n)x(n*k),得到k维向量
inter_2 = multiply(trainMatrix[x],trainMatrix[x]) * multiply(v,v) #二阶交叉项计算,得到k维向量
interaction = sum(multiply(inter_1,inter_1) - inter_2 ) / 2 #二阶交叉项计算完成
# 计算预测的输出,即FM的全部项之和
p = w_0 + trainMatrix[x] * w + interaction
# 计算损失
loss = 1 - sigmoid(label[x] * p[0,0])
w_0 = w_0 + alpha * loss * label[x]
for i in range(n):
if trainMatrix[x,i] != 0:
w[i,0] = w[i,0] + alpha * loss * label[x] * trainMatrix[x,i]
for j in range(k):
v[i,j] = v[i,j] + alpha*loss*label[x]*(trainMatrix[x,i]*inter_1[0,j]-v[i,j]*trainMatrix[x,i]*trainMatrix[x,i])
print("第{}次迭代后的损失为{}".format(it, loss))
# 常数项w_0, 一阶特征系数w(n维向量——n个特征), 二阶交叉特征系数v(n个k维向量)
return w_0, w, v
def getAccuracy(dataMatrix,classLabels,w_0,w,v):
m,n = shape(dataMatrix)
allItem = 0
error = 0
result = []
for x in range(m): # 计算每一个样本的误差
allItem += 1
inter_1 = dataMatrix[x] * v
inter_2 = multiply(dataMatrix[x], dataMatrix[x]) * multiply(v, v)
interaction = sum(multiply(inter_1, inter_1) - inter_2) / 2.
p = w_0 + dataMatrix[x] * w + interaction # 计算预测的输出
pre = sigmoid(p[0, 0])
result.append(pre)
if pre < 0.5 and classLabels[x] == 1.0:
error += 1
elif pre >= 0.5 and classLabels[x] == -1.0:
error += 1
else:
continue
return float(error) / allItem
if __name__ == '__main__':
train = train_data
test = test_data
dataTrain, labelTrain = preprocessData(train)
dataTest, labelTest = preprocessData(test)
date_startTrain = datetime.now()
print("开始训练")
w_0, w, v = FM(mat(dataTrain), labelTrain, 20, 10)
print("训练准确性为:%f" % (1 - getAccuracy(mat(dataTrain), labelTrain, w_0, w, v)))
date_endTrain = datetime.now()
print("训练用时为:%s" % (date_endTrain - date_startTrain))
print("开始测试")
print("测试准确性为:%f" % (1 - getAccuracy(mat(dataTest), labelTest, w_0, w, v)))
>>>
开始训练
第0次迭代后的损失为0.7133945496465111
训练准确性为:0.726678
训练用时为:0:00:00.699208
开始测试
测试准确性为:0.713376
Process finished with exit code 0