4.【Python】分類演算法—Factorization Machine(FM,因子分解機)
4.【Python】分類演算法—Factorization Machine(FM,因子分解機)
現實生活中存在大量非線性可分的分類問題,而Logistic Regression演算法只能解決線性可分的二分類問題。Factorization Machine作為Logistic Regression的改進,可解決非線性分類問題。
為了解決非線性分類的問題,第一種方法是採用核函式將非線性可分問題近似為線性可分問題;第二種方法是對Logistic Regression演算法進行擴充,例如Factorization Machine(FM,因子分解機),是一種基於矩陣分解的機器學習演算法。
文章目錄
前言
FM演算法是對Logistic Regression演算法的擴充套件,包含Logistic Regression的非線性項,還包含非線性的交叉項,可以利用矩陣分解的方法對交叉項進行學習,進而確定係數。
一、FM模型
1.因子分解機FM模型
對於二分類即度為2的模型,前兩部分屬於傳統的線性模型,第三部分是兩個非線性互異特徵向量的關係。
⟨
V
i
,
V
j
⟩
\left \langle V_{i},V_{j} \right \rangle
⟨Vi,Vj⟩表示的是兩個大小為k的向量
⟨
V
i
⟩
\left \langle V_{i} \right \rangle
⟨Vi⟩和向量
⟨
V
j
⟩
\left \langle V_{j} \right \rangle
⟨Vj⟩的點積。
⟨
V
i
⟩
\left \langle V_{i} \right \rangle
⟨Vi⟩為係數矩陣V的第i維向量,且k的大小為FM演算法的度。
2.損失函式
這裡主要用於二分類問題,在二分類中一般採用logit loss作為優化損失函式,表示式如下:
二、交叉項的處理
1.交叉項係數
FM演算法是在基本線性迴歸模型上引入交叉項,若物件為稀疏資料,則可能出現樣本中沒有交叉特徵向量
x
i
x_{i}
xi的情況,不能對交叉項係數進行估計,所以引入向量的點積對交叉項係數
w
i
,
j
w_{i,j}
wi,j進行估計。
其中,
則
2.模型求解
對交叉項進行單獨求解,將其化解為形如
(
(
a
+
b
+
c
)
2
−
a
2
−
b
2
−
c
2
)
/
2
((a+b+c)^{2}-a^2-b^2-c^2)/2
((a+b+c)2−a2−b2−c2)/2,前半部分為交叉項的變式,後半部分為0向量。
三、FM演算法的求解
前面介紹的演算法一般採用梯度下降法,但是在求解迭代過程中一般需要全部資料進行訓練學習,所以在資料量較大時採用隨機梯度下降法。
1.隨機梯度下降法求解模型引數
隨機梯度下降法(Stochastic Gradient Descent)在迭代過程中根據一個資料樣本對模型引數進行調整,其優化過程為:
在採用隨機梯度下降法進行學習時,主要對損失函式求偏導數:
其中
∂
y
^
∂
θ
\frac{\partial \widehat{y}}{\partial \theta }
∂θ∂y
為:
2.利用隨機梯度下降法訓練FM模型引數
主要步驟為先求初始化權重w和交叉項的係數矩陣V,然後對每個樣本進行訓練求權重和交叉項,最後直到滿足條件才進行終止。
首先定義梯度下降法函式stocGradAscent,函式輸入為特徵dataMatrix、樣本標籤classLabels、係數矩陣維數k、隨機梯度下降法的最大迭代次數max_iter、隨機梯度下降法的學習率alpha,函式輸出為一次項的權重 w 0 w_{0} w0、常數項的權重 w i w_{i} wi、交叉項的係數矩陣V。
程式碼如下:
def stocGradAscent(dataMatrix,classLabels,k,max_iter,alpha):
'''利用隨機梯度下降法訓練FM模型
input:dataMatrix(mat)特徵
classLabels(mat)標籤
k(int)v的維數
max_iter(int)最大迭代次數
alpha(float)學習率
output:w0(float),w(mat),v(mat):權重
'''
m,n = np.shape(dataMatrix)
#1.初始化引數
w = np.zeros((n,1))
w0 = 0 #偏置項
v= initialize_v(n,k) #初始化v
#2.訓練
for it in range(max_iter):
print("iteration:", it)
for x in range(m): #隨機優化,對每一個樣本而言的
inter_1 = dataMatrix[x] * v
inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * np.multiply(v,v) #multiply對應元素相乘
#完成交叉項
interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2
p = w0 + dataMatrix[x] * w + interaction #計算預測的輸出
loss = sigmoid(classLabels[x] * p[0, 0] - 1)
#常數項權重的修正
w0 = w0 - alpha * loss * classLabels[x]
for i in range(n):
if dataMatrix[x, i] != 0 :
#一次項權重的修正
w[i, 0] = w[i, 0] - alpha * loss * classLabels[x] * dataMatrix[x, i]
for j in range(k):
#交叉項係數矩陣的修正
v[i,j] = v[i,j] - alpha * loss * classLabels[x] * (dataMatrix[x,i] * inter_1[0,j] - v[i,j] * dataMatrix[x,i] * dataMatrix[x,i])
#計算損失函式的值
if it %1000 == 0:
print("\t----iter:", it, ", cost:", getCost(getPrediction(np.mat(dataTrain), w0, w, v), classLabels))
#3.返回最終的FM模型的引數
return w0, w, v
在初始化權重時,為簡化步驟將所有權重都化為0。在對交叉項係數矩陣初始化時,使用正態分佈對其進行初始化,定義initialize_v函式如下所示。
程式碼如下:
from random import normalvariate #正態分佈
#初始化交叉項的權重
def initialize_v(n, k):
'''初始化交叉項
input: n(int)特徵的個數
k(int)FM模型的超引數
output: v(mat):交叉項的係數權重
'''
v = np.mat(np.zeros(n, k))
for i in range(n):
for j in range(k):
#利用正態分佈生成每一個權重
v[i, j] = normalvariate(0, 0.2)
return v
其中損失函式採用sigmoid函式,定義sigmoid函式如下。
程式碼如下:
#定義sigmoid函式
def sigmoid(inx):
return 1.0/(1 + np.exp(-inx))
在每完成1000次迭代以後,計算一次當前的損失值,定義計算損失的函式getCost如下。
程式碼如下:
#計算損失函式的值
#定義getcost函式
def getCost(predict, classLabels):
'''計算預測準確性
input: predict(list)預測值
classLabels(list)標籤值
output: error(float)計算損失函式的值
'''
m = len(predict)
error = 0.0
for i in range(m):
error -= np.log(sigmoid(predict[i] * classLabels[i]))
return error
三、FM演算法實踐
採用非線性可分的資料集作為訓練資料,主要分為兩個部分,一為利用訓練資料集對模型進行訓練,二為利用測試集對資料進行預測。
1.訓練FM模型
FM訓練模型的主函式如下,首先利用loadDataSet函式匯入訓練資料,然後利用stocGradAscent函式訓練模型,其後利用getPrediction函式對訓練資料進行預測,利用getAccuracy函式將預測值和樣本進行比較得到準確率,最後利用save_model函式儲存模型。
定義匯入訓練資料的loadDataSet函式,程式碼如下:
def loadDataSet(data):
'''匯入訓練資料
input: data(string)訓練資料
output: dataMat(list)特徵
labelMat(list)標籤
'''
dataMat = []
labelMat = []
fr = open(data) #開啟檔案
for line in fr.readlines():
lines = line.strip().split("\t")
lineArr = []
for i in range(len(lines) - 1):
lineArr.append(float(lines[i]))
dataMat.append(lineArr)
labelMat.append(float(lines[-1]) * 2 - 1) #轉換成{-1,1}
fr.close()
return dataMat,labelMat
由於FM模型中使用的是log損失函式,在匯入標籤的過程中,需要將原始資料範圍{0,1}轉換為{-1,1}。
定義預測訓練樣本的getPrediction函式,程式碼如下:
def getPrediction(dataMatrix, w0, w, v):
'''得到預測值
input: dataMatrix(mat)特徵
w(int)常數項權重
w0(int)一次項權重
v(float)交叉項權重
output: result(list)預測的結果
'''
m = np.shape(dataMatrix)[0]
result = []
for x in range(m):
inter_1 = dataMatrix[x] * v
inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * np.multiply(v, v) # multiply對應元素相乘
# 完成交叉項
interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2
p = w0 + dataMatrix[x] * w + interaction # 計算預測的輸出
pre = sigmoid(p[0, 0])
result.append(pre)
return result
定義getAccuracy函式評價模型預測效果,程式碼如下:
def getAccuracy(predict, classLabels):
'''計算預測準確性
input: predict(list)預測值
classLabels(list)標籤
output: float(error) / allItem(float)錯誤率
'''
m = len(predict)
allItem = 0
error = 0
for i in range(m):
allItem += 1
if float(predict[i]) < 0.5 and classLabels[i] == 1.0:
error += 1
elif float(predict[i]) >= 0.5 and classLabels[i] == -1.0:
error += 1
else:
continue
return float(error) / allItem
定義save_model函式儲存FM模型的偏置項和權重,程式碼如下:
def save_model(file_name, w0, w, v):
'''儲存訓練好的模型
input: file_name(string):儲存的檔名
w0(float):偏置項
w(mat): 一次項的權重
v(mat): 交叉項的權重
'''
f = open(file_name, "w")
#1.儲存w0
f.write(str(w0) + "\n")
#2.儲存一次項的權重
w_array = []
m = np.shape(w)[0]
for i in range(m):
w_array.append(str(w[i, 0]))
f.write("\t".join(w_array) + "\n")
#3.儲存交叉項的權重
m1, n1 = np.shape(v)
for i in range(m1):
v_tmp = []
for j in range(n1):
v_tmp.append(str(v[i, j]))
f.write("\t".join(v_tmp) + "\n")
f.close()
完整程式碼如下:
# conding:UTF-8
import numpy as np
from random import normalvariate #正態分佈
def loadDataSet(data):
'''匯入訓練資料
input: data(string)訓練資料
output: dataMat(list)特徵
labelMat(list)標籤
'''
dataMat = []
labelMat = []
fr = open(data) #開啟檔案
for line in fr.readlines():
lines = line.strip().split("\t")
lineArr = []
for i in range(len(lines) - 1):
lineArr.append(float(lines[i]))
dataMat.append(lineArr)
labelMat.append(float(lines[-1]) * 2 - 1) #轉換成{-1,1}
fr.close()
return dataMat,labelMat
#初始化交叉項的權重
def initialize_v(n, k):
'''初始化交叉項
input: n(int)特徵的個數
k(int)FM模型的超引數
output: v(mat):交叉項的係數權重
'''
v = np.mat(np.zeros(n, k))
for i in range(n):
for j in range(k):
#利用正態分佈生成每一個權重
v[i, j] = normalvariate(0, 0.2)
return v
#定義sigmoid函式
def sigmoid(inx):
return 1.0/(1 + np.exp(-inx))
#計算損失函式的值
#定義getcost函式
def getCost(predict, classLabels):
'''計算預測準確性
input: predict(list)預測值
classLabels(list)標籤值
output: error(float)計算損失函式的值
'''
m = len(predict)
error = 0.0
for i in range(m):
error -= np.log(sigmoid(predict[i] * classLabels[i]))
return error
def stocGradAscent(dataMatrix,classLabels,k,max_iter,alpha):
'''利用隨機梯度下降法訓練FM模型
input:dataMatrix(mat)特徵
classLabels(mat)標籤
k(int)v的維數
max_iter(int)最大迭代次數
alpha(float)學習率
output:w0(float),w(mat),v(mat):權重
'''
m,n = np.shape(dataMatrix)
#1.初始化引數
w = np.zeros((n,1))
w0 = 0 #偏置項
v= initialize_v(n,k) #初始化v
#2.訓練
for it in range(max_iter):
print("iteration:", it)
for x in range(m): #隨機優化,對每一個樣本而言的
inter_1 = dataMatrix[x] * v
inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * np.multiply(v,v) #multiply對應元素相乘
#完成交叉項
interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2
p = w0 + dataMatrix[x] * w + interaction #計算預測的輸出
loss = sigmoid(classLabels[x] * p[0, 0] - 1)
w0 = w0 - alpha * loss * classLabels[x]
for i in range(n):
if dataMatrix[x, i] != 0 :
w[i, 0] = w[i, 0] - alpha * loss * classLabels[x] * dataMatrix[x, i]
for j in range(k):
v[i,j] = v[i,j] - alpha * loss * classLabels[x] * (dataMatrix[x,i] * inter_1[0,j] - v[i,j] * dataMatrix[x,i] * dataMatrix[x,i])
#計算損失函式的值
if it %1000 == 0:
print("\t----iter:", it, ", cost:", getCost(getPrediction(np.mat(dataTrain), w0, w, v), classLabels))
#3.返回最終的FM模型的引數
return w0, w, v
def getPrediction(dataMatrix, w0, w, v):
'''得到預測值
input: dataMatrix(mat)特徵
w(int)常數項權重
w0(int)一次項權重
v(float)交叉項權重
output: result(list)預測的結果
'''
m = np.shape(dataMatrix)[0]
result = []
for x in range(m):
inter_1 = dataMatrix[x] * v
inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * np.multiply(v, v) # multiply對應元素相乘
# 完成交叉項
interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2
p = w0 + dataMatrix[x] * w + interaction # 計算預測的輸出
pre = sigmoid(p[0, 0])
result.append(pre)
return result
def getAccuracy(predict, classLabels):
'''計算預測準確性
input: predict(list)預測值
classLabels(list)標籤
output: float(error) / allItem(float)錯誤率
'''
m = len(predict)
allItem = 0
error = 0
for i in range(m):
allItem += 1
if float(predict[i]) < 0.5 and classLabels[i] == 1.0:
error += 1
elif float(predict[i]) >= 0.5 and classLabels[i] == -1.0:
error += 1
else:
continue
return float(error) / allItem
def save_model(file_name, w0, w, v):
'''儲存訓練好的模型
input: file_name(string):儲存的檔名
w0(float):偏置項
w(mat): 一次項的權重
v(mat): 交叉項的權重
'''
f = open(file_name, "w")
#1.儲存w0
f.write(str(w0) + "\n")
#2.儲存一次項的權重
w_array = []
m = np.shape(w)[0]
for i in range(m):
w_array.append(str(w[i, 0]))
f.write("\t".join(w_array) + "\n")
#3.儲存交叉項的權重
m1, n1 = np.shape(v)
for i in range(m1):
v_tmp = []
for j in range(n1):
v_tmp.append(str(v[i, j]))
f.write("\t".join(v_tmp) + "\n")
f.close()
if __name__ == '__main__':
#1.匯入訓練資料
print("-------1.load data-------")
dataTrain,labelTrain = loadDataSet("data.txt")
print("-------2.learning--------")
#2.利用隨機梯度訓練FM模型
w0, w, v = stocGradAscent(np.mat(dataTrain), labelTrain, 3, 10000, 0.01)
predict_result = getPrediction(np.mat(dataTrain), w0, w, v) #計算訓練的準確性
print("-------training error: %f " %(1 - getAccuracy(predict_result, labelTrain)))
print("-------3.save model------")
#3.儲存訓練好的FM模型
save_model("weights", w0, w, v)
2.FM測試模型
上述將FM分類模型訓練完成後,將係數都儲存在weights檔案中,建立FM_test.py檔案。
FM測試模型主要是對新資料進行預測,主要步驟為:定義loadDataSet函式匯入測試資料,定義loadModel函式匯入訓練模型資料,定義save_result模型儲存最終的預測結果。
主函式程式碼如下:
if __name__ == '__main__':
#1.匯入測試資料
dataTest = loadDataTest("test_data.txt")
#2.匯入FM訓練模型
w0, w, v = loadModel("weights")
#3.預測
result = getPrediction(dataTest, w0, w, v)
#4.儲存最終的預測結果
save_result = ("predict_result", result)
函式loadDataTest的輸入為測試資料的位置,輸出為測試資料的特徵,程式碼如下:
def loadDataSet(data):
'''匯入測試資料
input: data(string)訓練資料
output: dataMat(list)特徵
'''
dataMat = []
fr = open(data) #開啟檔案
for line in fr.readlines():
lines = line.strip().split("\t")
lineArr = []
for i in range(len(lines)):
lineArr.append(float(lines[i]))
dataMat.append(lineArr)
fr.close()
return dataMat
函式loadModel輸出的是FM訓練模型中的引數包括偏置項 w 0 w_{0} w0,一次項的權重 w i w_{i} wi,交叉項的權重V,程式碼如下:
def loadModel(model_file):
'''匯入FM模型
input: model_file(string)FM模型
output:w0, np.mat(w).T, np.mat(v) FM模型的引數
'''
f = open(model_file)
line_index = 0
w0 = 0.0
w = []
v = []
for line in f.readlines():
lines = line.strip().split("\t")
if line_index == 0: #w0
w0 = float(lines[0].strip())
elif line_index == 1: #w
for x in lines:
w.append(float(x.strip()))
else:
v_tmp = []
for x in lines:
v_tmp.append(float(x.strip()))
v.append()
line_index += 1
f.close()
return w0, np.mat(w).T, np.mat(v)
函式save_result將FM測試模型的預測結果儲存到檔案中,程式碼如下:
def save_result(file_name, result):
'''儲存最終的預測結果
input: file_name(string)需要儲存的檔名
result(mat):對測試資料的預測結果
'''
f = open(file_name, "w")
f.write("\n".join(str(x) for x in result))
f.close()
完整程式碼如下:
# conding:UTF-8
import numpy as np
def loadDataTest(data):
'''匯入測試資料
input: data(string)訓練資料
output: dataMat(list)特徵
'''
dataMat = []
fr = open(data) #開啟檔案
for line in fr.readlines():
lines = line.strip().split("\t")
lineArr = []
for i in range(len(lines)):
lineArr.append(float(lines[i]))
dataMat.append(lineArr)
fr.close()
return dataMat
def loadModel(model_file):
'''匯入FM模型
input: model_file(string)FM模型
output:w0, np.mat(w).T, np.mat(v) FM模型的引數
'''
f = open(model_file)
line_index = 0
w0 = 0.0
w = []
v = []
for line in f.readlines():
lines = line.strip().split("\t")
if line_index == 0: #w0
w0 = float(lines[0].strip())
elif line_index == 1: #w
for x in lines:
w.append(float(x.strip()))
else:
v_tmp = []
for x in lines:
v_tmp.append(float(x.strip()))
v.append()
line_index += 1
f.close()
return w0, np.mat(w).T, np.mat(v)
#定義sigmoid函式
def sigmoid(inx):
return 1.0/(1 + np.exp(-inx))
def getPrediction(dataMatrix, w0, w, v):
'''得到預測值
input: dataMatrix(mat)特徵
w(int)常數項權重
w0(int)一次項權重
v(float)交叉項權重
output: result(list)預測的結果
'''
m = np.shape(dataMatrix)[0]
result = []
for x in range(m):
inter_1 = dataMatrix[x] * v
inter_2 = np.multiply(dataMatrix[x], dataMatrix[x]) * np.multiply(v, v) # multiply對應元素相乘
# 完成交叉項
interaction = np.sum(np.multiply(inter_1, inter_1) - inter_2) / 2
p = w0 + dataMatrix[x] * w + interaction # 計算預測的輸出
pre = sigmoid(p[0, 0])
result.append(pre)
return result
def save_result(file_name, result):
'''儲存最終的預測結果
input: file_name(string)需要儲存的檔名
result(mat):對測試資料的預測結果
'''
f = open(file_name, "w")
f.write("\n".join(str(x) for x in result))
f.close()
if __name__ == '__main__':
#1.匯入測試資料
dataTest = loadDataTest("test_data.txt")
#2.匯入FM訓練模型
w0, w, v = loadModel("weights")
#3.預測
result = getPrediction(dataTest, w0, w, v)
#4.儲存最終的預測結果
save_result("predict_result", result)
總結
本文主要介紹了Factorization Machine(FM,因子分解機)分類演算法,與Softmax 和Logistic分類演算法不同的是,此演算法可用於非線性分類,因此在函式模型上多了一項交叉項表示非線性向量的互動。python的實現過程主要分為兩部分——訓練和測試。
參考文獻:《Python機器學習演算法》
相關文章
- 萬字長文,詳解推薦系統領域經典模型FM因子分解機模型
- 1366: 分解質因子
- 整數因子分解問題
- FM演算法python實現演算法Python
- python實現FM演算法Python演算法
- 「DeepFM: A Factorization-Machine based Neural Network for CTR Prediction」- 論文摘要Mac
- 「DeepFM:A Factorization-Machine based Neural Network for CTR Prediction」- 論文摘要Mac
- 質數判斷、質因子分解、質數篩
- 初等數論-01-整數的因子分解
- 分類演算法-支援向量機 SVM演算法
- Python機器學習筆記:奇異值分解(SVD)演算法Python機器學習筆記演算法
- 推薦系統特徵構建新進展:極深因子分解機模型 | KDD 2018特徵模型
- 機器學習3-分類演算法機器學習演算法
- 機器學習--有監督學習--分類演算法(預測分類)機器學習演算法
- 什麼是Python演算法?分為哪幾類?Python演算法
- 機器學習4-分類演算法2機器學習演算法
- Python演算法分為哪幾類?具備哪些特徵?Python演算法特徵
- codetop演算法分類演算法
- 分類演算法-AdaBoot 演算法演算法boot
- 演算法題 - Shuffling Machine演算法Mac
- 分類演算法-多層感知機 Multi-layer Perceptron演算法
- KNN演算法——分類部分KNN演算法
- 分類演算法-邏輯迴歸與二分類演算法邏輯迴歸
- python演算法:分糖果Python演算法
- 分類演算法-k 鄰近演算法演算法
- 4. union-find演算法演算法
- 惟一分解定理
- 概率分類之樸素貝葉斯分類(垃圾郵件分類python實現)Python
- 【Python機器學習實戰】聚類演算法(1)——K-Means聚類Python機器學習聚類演算法
- 機器學習經典分類演算法 —— k-近鄰演算法(附python實現程式碼及資料集)機器學習演算法Python
- 基於TOTP演算法的Github兩步驗證2FA(雙因子)機制Python3.10實現演算法GithubPython
- 4.《python自省指南》學習Python
- 目標跟蹤演算法分類演算法
- python建立分類器小結Python
- 從零開始學機器學習——瞭解分類演算法機器學習演算法
- Tarjan演算法(強連通分量分解)演算法
- 人工智慧-機器學習-演算法:非負矩陣分解(NMF)人工智慧機器學習演算法矩陣
- Python運算子分為哪幾類?Python教程!Python