FM(因子分解機)原理與實戰
阿新 • • 發佈:2021-08-09
0 概述
因子分解機(Factorization Machine,FM)於2010年被首次提出,其目的是解決資料稀疏問題以及特徵組合爆炸問題,是曾經火爆學術界的推薦模型,雖然近幾年基於深度學習的推薦演算法是眾多學者的研究熱點,但因FM實現簡單,效果強大,其思想仍值得我們深入研究。此外FM與深度學習技術的結合,使推薦效果得到了更好地提升。首先了解一下POLY2模型,其公式如下:
\[\varnothing POLY2(\boldsymbol{w},\boldsymbol{x})=\sum_{j_{1}=1}^{n-1}\sum_{j_{2}=j_{1}+1}^{n}w_{h(j_{1},j_{2})}x_{j_{1}}x_{j_{2}} \]式中\(w_{h(j_{1},j_{2})}\)
1 進一步推導
FM模型的前半部分是線性模型,比較簡單,因此本節著重對後半部分二階特徵交叉項作進一步推導,最後給出矩陣實現。不妨假設特徵維度為\(n\),樣本數量為\(m\),因子數量(每個特徵對應的向量維度)為\(k\)。用\(X\)代表樣本,用\(V\)
於是:
\[\sum_{j_{1}=1}^{n-1}\sum_{j_{2}=j_{1}+1}^{n}<v_{j_{1}},v_{j_{2}}>x_{j_{1}}x_{j_{2}} \]\[=\frac{1}{2}\sum_{i=1}^{n}\sum_{j=1}^{n}<v_{i},v_{j}>x_{i}x_{j}-\frac{1}{2}\sum_{i=1}^{n}<v_{i},v_{i}>x_{i}x_{i} \]\[=\frac{1}{2}(\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{f=1}^{k}v_{i,f}*v_{j,f}*x_{i}x_{j}-\sum_{i=1}^{n}\sum_{f=1}^{k}v_{i,f}*v_{i,f}*x_{i}x_{i}) \]\[=\frac{1}{2}\sum_{f=1}^{k}(\sum_{i=1}^{n}\sum_{j=1}^{n}v_{i,f}*v_{j,f}*x_{i}x_{j}-\sum_{i=1}^{n}v_{i,f}*v_{i,f}*x_{i}x_{i}) \]\[=\frac{1}{2}\sum_{f=1}^{k}((\sum_{i=1}^{n}v_{i,f}*x_{i})(\sum_{j=1}^{n}v_{j,f}*x_{j})-\sum_{i=1}^{n}v_{i,f}^{2}*x_{i}^{2}) \]\[=\frac{1}{2}\sum_{f=1}^{k}((\sum_{i=1}^{n}v_{i,f}*x_{i})^{2}-\sum_{i=1}^{n}v_{i,f}^{2}*x_{i}^{2})) \]寫成矩陣的形式:
\[\varnothing FM(\boldsymbol{W},\boldsymbol{V},\boldsymbol{X})=W^{'}\cdot X^{'}\cdot \boldsymbol{I}_{m\times 1}+\boldsymbol{I}_{1\times k}\cdot [(V^{T}\cdot X)^{2}-(V^{T})^{2}\cdot X^{2}]\cdot \boldsymbol{I}_{m\times 1} \]其中加號左邊為線性部分\(X^{'}\)是\(X\)的增廣形式,加號的後半部分為二階特徵交叉部分
2 基於pytorch實現
本節將利用深度學習框架pytorch實現FM演算法,資料集選用MovieLens-1M。
2.1 檔案結構
從上到下檔案的含義為:
- data_process.py 資料預處理檔案,原始資料集包含三種檔案(分別是user、movie、rating檔案)需要以rating檔案為依據,將rating檔案的每一行拼接上使用者的特徵、電影的特徵。在處理類別型資料時採用one-hot編碼,對模型訓練沒有幫助的特徵(如郵政編碼、時間戳等)直接刪除。
- fm.py 矩陣分解模型實現的程式碼包含在此檔案中。
- movies.dat 儲存了所有的電影資訊
- orig_data.csv 資料如處理後產生的最終用於訓練模型的資料,先存在一個檔案中,方便後續使用
- rating.dat 儲存了使用者對電影的評分
- test_data.csv 用於測試的資料集
- train_data.csv 用於訓練的資料集
- user.dat 儲存了使用者相關資訊
2.2 主要程式碼段
完整程式碼已上傳到github,請點選點選這裡下載。訓練100個epoch,訓練集和測試集誤差如下圖所示:
FM模型的實現如下:
class FM(nn.Module):
def __init__(self, n, k):
super(FM, self).__init__()
self.n = n # 特徵數
self.k = k # 因子數
self.linear_part = nn.Linear(self.n, 1, bias=True)
self.v = nn.Parameter(torch.rand(self.k, self.n))
def fm(self, x):
linear_part = self.linear_part(x)
cross_part1 = torch.mm(x, self.v.t())
cross_part2 = torch.mm(torch.pow(x, 2), torch.pow(self.v, 2).t())
cross_part = torch.sum(torch.sub(torch.pow(cross_part1, 2), cross_part2), dim=1)
output = linear_part.transpose(1, 0) + 0.5 * cross_part
return output
def forward(self, x):
output = self.fm(x)
return output
3 FM為什麼能緩解資料稀疏性問題
這裡引用《深度學習推薦系統》————王喆著裡面的解釋
假設在某推薦場景下,樣本有兩個特徵,分別是頻道(channel)和品牌(brand),某訓練樣本的特徵組合是(ESPN, Adidas)。在POLY2中,只有當ESPN和Adidas同時出現在一個樣本中時,模型才能學到這兩個特徵組合對應的權重;而在FM模型中,ESPN的隱向量可以通過(ESPN, Gucci)來更新,Adidas的隱向量也可以通過(NBC, Adidas)來更新,這大幅降低了模型對資料稀疏性的要求。