在這篇文章我們將介紹因式分解機模型(FM),為行文方便后文均以FM表示。FM模型結合了支持向量機與因子分解模型的優點,並且能夠用了回歸、二分類以及排序任務,速度快,是推薦算法中召回與排序的利器。FM算法和前面我們介紹的LFM模型模型都是基於矩陣分解的推薦算法,但在大型稀疏性數據中FM模型效果也不錯。本文首先將闡述FM模型原理,然后針對MovieLens數據集將FM算法用於推薦系統中的ranking階段,給出示例代碼。最后,我們將對該算法進行一個總結。
1. FM算法
FM是一個如SVM一樣通用的預測器,並且能夠在數據非常稀疏的情況下估算訓練出可靠的參數。同時,FM不僅能夠利用一階特征,通過分解參數也能夠利用二階乃至更高階特征,並且速度相對其它算法更快更可靠。在本章我將介紹FM原理並詳細討論模型方程,然后簡短說明如何將之用於多個預測任務。
1.1 FM模型
當d=2,即我們最高階只關注到二階交互特征時,FM的模型方程式定義如下:
在這里,模型參數的估計空間為:
並且,<·,·>表示兩個大小為k的向量的點積:
在V中\(v_i\)描述了第i個特征,\(k\in N_0^+\)是一個定義模型分解維度的超參數。
2-way FM(degree=2)模型能夠捕獲所有的一階特征與二階交互特征:
- \(w_0\)是一個全局偏置向量
- \(w_i\)對第i個變量(特征)進行建模
- \(w_{ij}\)對第i個變量(特征)與第j個變量(特征)之間的交互作用進行了建模。在這里,FM並非定義出\(w_{ij} \in R\) ,而是通過分解參數W對交互建模。這也是FM在稀疏情況下能進行高質量參數估計的關鍵點。
看到這里可能會有一個疑問,FM是如何通過分解參數對交互建模的呢?
眾所周知,在當\(k\)足夠大的時候,任何一個正定矩陣\(W\)都能存在一個向量\(V\)使得\(W=VV^t\)。這表明,如果選擇的\(k\)足夠大則FM可以表示任何交互矩陣。然而,在數據十分稀疏的情況下,\(k\)只需要取一個較小的值就行,這是因為沒有足夠的數據來估計復雜的交互矩陣\(W\),並且限制\(k\)的大小反而還能提高模型的泛化性能。
1.2 稀疏數據下的參數估計
在數據十分稀疏的情況下,通常沒有充足的數據直接估計變量之間的交互參數\(w_{ij}\)。FM模型在這種場景下能夠直接估計變量之間的交互是因為其通過分解交互參數來破壞參數\(w_{ij}\)的獨立性,從而間接的達到訓練參數的目的。這意味着一個交互數據也有助於估計相關交互的參數。為了讓讀者理解清晰,我們將通過一個栗子來闡述:
假設我們擁有電影評分系統的評分數據。系統記錄了某個時間\(t \in R\)某個用戶\(u \in U\)為電影\(i \in I\)評分 \(r\in {1, 2, 3, 4, 5}\).
U = {A, B, C}
I = {TI, NH, SW, ST}
觀察到數據形式為:
S = {(A, TI, 2010-1, 5), (A, NH, 2010-2, 3), (A, SW, 2010-4, 1), (B, SW, 2009-5, 4), (B, ST,2009-8,5), (C, TI, 2009-9, 1), (C, SW, 2009-12,5)}
在這里,假設我們要估計A和ST組合下的評分y,顯然訓練數據中不存在變量\(x_A\)和變量\(x_{ST}\)都不為零的數據\(x\),因此直接估計交互參數\(w_{A,ST}\)沒有任何作用(\(w_{A,ST}=0\))。但是通過分解交互參數\(w_{A,ST}\)為\(<v_{A},v_{ST}>\)我們就可以估計出該交互參數值。這是因為用戶A對別的電影的評分能夠幫助估計到參數\(V_A\),而別的用戶對ST的評分也能幫助估計參數\(V_{ST}\)。
1.3 公式演算
如方程(1)所示,其時間復雜度為\(O(kn^2)\),但通過重新演算可將其時間復雜度降低到線性。如下所示:
該等式的時間復雜度與k,n線性相關,為\(O(kn)\)
2. FM用於ranking
在本節,我將針對MovieLens數據集(ml-100k)走完推薦系統的數據預處理、召回、排序階段並作介紹。其中FM用於ranking階段。
2.1 數據預處理及召回策略
如圖所示,將用戶與電影使用one-hot編碼,同時提取歷史評分矩陣,用戶職業進行one-hot編碼,性別也是。召回階段,簡單粗暴的基於規則進行匹配item,具體詳見github。
2.2 ranking階段
實現FM模型,通過FM模型訓練參數得到召回數據中用戶對召回物品的評分並進行排序。算法實現代碼如下。
# 定義FM模型
class FM_model(torch.nn.Module):
"""FM Model"""
def __init__(self, n, k):
super(FM_model, self).__init__()
self.n = n
self.k = k
self.linear = torch.nn.Linear(self.n, 1, bias=True)
self.v = torch.nn.Parameter(torch.rand(self.n, self.k))
def fm_layer(self, x):
# w_i * x_i 線性部分
linear_part = self.linear(x)
print(linear_part.shape)
# pairwise interactions part 1
inter_part1 = torch.mm(x, self.v)
# pairwise interactions part 2
inter_part2 = torch.mm(torch.pow(x, 2), torch.pow(self.v, 2))
inter_part = 0.5 * torch.sum(torch.sub(torch.pow(inter_part1, 2), inter_part2), dim=1).reshape(-1, 1)
output = linear_part + inter_part
return output
def forward(self, x):
output = self.fm_layer(x)
return output
詳細代碼,請前往github查看FM算法實現
3.小結
在本文中,我們介紹了FM模型。FM將SVM的通用性和因式分解模型的優勢結合在一起。與SVM相比,FM能夠在數據極其稀疏的情況下估計模型參數,並且時間復雜度與分解維數k和特征量n線性相關,速度較快,是推薦系統中召回與排序的利器。為了方便大家學習,作者特意針對MovieLens數據集走了一遍預處理、召回、排序的流程,希望能夠幫助大家理解該算法,如果還有不明白的建議直接閱讀論文,論文地址:因子分解機,召回和排序的利器,速度快效果好Factorization Machines