参考论文 Factorization Machines
转置矩阵:
将矩阵的行列对换。共轭转置矩阵:
将矩阵的行列对换且每个元素共轭。一种实对称矩阵:可理解为所有特征值都大于0的对称矩阵。
将对称正定矩阵化为一个三角矩阵与其共轭转置矩阵的积的分解。 A是n阶对称正定矩阵,则存在唯一的对角元均为正数的下三角矩阵L,使得: A = L L T A = LL^T A=LLT 则分解该A被称为Cholesky分解。
特征两两组合,得到二阶多项式回归模型。 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模型的方程: 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模型关键是:特征两两相关
FM模型的二次项等价化简过程如下:
FM模型最后化简如下图所示:
FM模型的时间复杂度降级到线性。
训练方法:随机梯度下降。
FM模型可以用于回归任务、二分类任务、排名任务。
libFM是用于分解机的软件实现,具有**随机梯度下降(SGD)和交替最小二乘(ALS)优化以及使用马尔可夫链蒙特卡洛(MCMC)**进行的贝叶斯推理。
SGD:随机梯度下降法ALS:交替最小二乘法MCMC:马尔科夫链蒙特卡罗法FM模型适用与数据稀疏场景。
FM模型由线性回归模型演化出来。
最大区别是:线性回归模型的特征独立,而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