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機器學習演算法》
相關文章
- 分解機(Factorization Machines)推薦演算法原理Mac演算法
- 萬字長文,詳解推薦系統領域經典模型FM因子分解機模型
- Machine Learning(1)-分類演算法Mac演算法
- Oracle 連線因式分解(Join Factorization)Oracle
- FM演算法python實現演算法Python
- python實現FM演算法Python演算法
- Find Terrorists(素數篩選+素因子分解)Error
- 遞迴演算法程式設計整數因子分解問題的遞迴演算法遞迴演算法程式設計
- NMF 非負矩陣分解(Non-negative Matrix Factorization)實踐矩陣
- HDU44979 GCD and LCM (素因子分解+計數)GC
- HDU 5317 RGCDQ (素因子分解+預處理)GC
- 質數判斷、質因子分解、質數篩
- 「DeepFM: A Factorization-Machine based Neural Network for CTR Prediction」- 論文摘要Mac
- 「DeepFM:A Factorization-Machine based Neural Network for CTR Prediction」- 論文摘要Mac
- 分類演算法-支援向量機 SVM演算法
- CodeForces - 463E Caisa and Tree (dfs+素因子分解)AI
- 推薦系統特徵構建新進展:極深因子分解機模型 | KDD 2018特徵模型
- 什麼是Python演算法?分為哪幾類?Python演算法
- Python機器學習筆記:奇異值分解(SVD)演算法Python機器學習筆記演算法
- 分類演算法-多層感知機 Multi-layer Perceptron演算法
- POJ 1325-Machine Schedule(二分圖匹配-匈牙利演算法)Mac演算法
- Python演算法分為哪幾類?具備哪些特徵?Python演算法特徵
- KNN演算法——分類部分KNN演算法
- codetop演算法分類演算法
- 分類演算法-AdaBoot 演算法演算法boot
- 分類演算法-邏輯迴歸與二分類演算法邏輯迴歸
- 4. union-find演算法演算法
- 分類演算法-k 鄰近演算法演算法
- 惟一分解定理
- 機器學習--有監督學習--分類演算法(預測分類)機器學習演算法
- 機器學習3-分類演算法機器學習演算法
- Machine Learning:PageRank演算法Mac演算法
- 【機器學習】支援向量機分類機器學習
- Mahout分類演算法學習之實現Naive Bayes分類示例演算法AI
- 唱佛機(Buddha Machine)Mac
- python演算法:分糖果Python演算法
- 概率分類之樸素貝葉斯分類(垃圾郵件分類python實現)Python
- 《機器學習實戰》基於樸素貝葉斯分類演算法構建文字分類器的Python實現機器學習演算法文字分類Python