1. 程式人生 > 其它 >FM(因子分解機)原理與實戰

FM(因子分解機)原理與實戰

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})}\)

是特徵\(x_{j_{1}}\)和特徵\(x_{j_{2}}\)的交叉的權重,是待學習的引數。在推薦系統領域中,特徵通常是one-hot編碼,維度非常大且十分稀疏,POLY2模型有兩個缺點:一是需要考慮所有特徵的兩兩組合的情況,引數的數量達到\(O(n^{2})\)級;二是隻有當\(x_{j_{1}}\)\(x_{j_{2}}\)都不為0時\(w_{h(j_{1},j_{2})}\)才能的到更新,但在資料稀疏的情況下很難得到滿足。為了解決上述兩種問題,FM的解決方式是用兩個向量的內積\(<v_{j_{1}},v_{j_{2}}>\)代替單一的權重係數。也就是說FM模型為每個特徵都學習一個向量表示(向量的維度是超引數,可自定義),兩個特徵的交叉的權重就是這兩個特徵對應的向量表示的內積。FM模型可用數學公式述為:

\[\varnothing FM(\boldsymbol{w},\boldsymbol{x})=w_{0}+\sum_{i=1}^{n}w_{i}x_{i}+\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}} \]

1 進一步推導

FM模型的前半部分是線性模型,比較簡單,因此本節著重對後半部分二階特徵交叉項作進一步推導,最後給出矩陣實現。不妨假設特徵維度為\(n\),樣本數量為\(m\),因子數量(每個特徵對應的向量維度)為\(k\)。用\(X\)代表樣本,用\(V\)

代表需要學習的因子。其中:

\[X=\begin{bmatrix} \boldsymbol{x^{1}} & \boldsymbol{x^{2}} & \cdots & \boldsymbol{x^{m}} \end{bmatrix} =\begin{bmatrix} x^{1}_{1} & x^{2}_{1} & \cdots & x^{m}_{1}\\ x^{1}_{2} & x^{2}_{2} & \cdots & x^{m}_{2}\\ \vdots & \vdots & \vdots & \vdots \\ x^{1}_{n} & x^{2}_{n} & \cdots & x^{m}_{n} \end{bmatrix}\]\[V=\begin{bmatrix} \boldsymbol{v^{1}}\\ \boldsymbol{v^{2}}\\ \vdots \\ \boldsymbol{v^{n}} \end{bmatrix} =\begin{bmatrix} v^{1}_{1} & v^{1}_{2} & \cdots & v^{1}_{k}\\ v^{2}_{1} & v^{2}_{2} & \cdots & v^{2}_{k}\\ \vdots & \vdots & \vdots & \vdots \\ v^{n}_{1} & v^{n}_{2} & \cdots & v^{n}_{k} \end{bmatrix}\]

於是:

\[\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)來更新,這大幅降低了模型對資料稀疏性的要求。