推荐系统组队学习04、FM因子分解机

本文深入解析了FM(Factorization Machines)模型的基础概念,如共轭转置矩阵、Cholesky分解,以及其从线性回归发展而来的特点。重点讲解了FM模型的原理、训练方法和特征工程,包括libFM软件应用实例。通过实际案例演示了如何用FM模型进行糖尿病分类预测,并探讨了FM模型在数据稀疏场景的优势和与其他模型的区别。
摘要由CSDN通过智能技术生成

一、FM模型基础

1. FM模型提出

参考论文
Factorization Machines

2. 共轭转置矩阵

转置矩阵:

  • 将矩阵的行列对换。

共轭转置矩阵:

  • 将矩阵的行列对换且每个元素共轭。

3. 正定矩阵

一种实对称矩阵:可理解为所有特征值都大于0的对称矩阵。

4. Cholesky分解

将对称正定矩阵化为一个三角矩阵与其共轭转置矩阵的积的分解。
A是n阶对称正定矩阵,则存在唯一的对角元均为正数的下三角矩阵L,使得:
A = L L T A = LL^T A=LLT
则分解该A被称为Cholesky分解。

5. 线性回归模型

  1. 特征之间相互独立不相关
  2. 特征的作用可以叠加

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=1nwixi+i=1nj>=1nwijxixj

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=1nwixi+i=1nj=i+1n<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=1nwixi+i=1nj=i+1n<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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值