前言
說到CTR,CVR預估,最近幾年無論是kaggle比賽,還是推薦算法、計算廣告中,使用FFM模型可以是一種標準通用的解決方案,因爲傳統的模型在遇到離散的類別特徵時,一般都是想方設法將其連續化,如做Id Embedding等,而FFM模型原生支持處理稀疏的類別特徵,而且擅長挖掘出特徵間的交叉性質,不僅效果好而且時間複雜度也不高,因此具有較高的應用價值,在很多企業中有實際的應用。
本文以一階模型作爲引入,分析了爲什麼二階模型是必要的,然後給出了具有實用性的二階模型FM以及改良版的FFM,最後簡要說明了一下FFM的實現思路。
1.從一階Linear Model開始
假設我們有下圖這樣的推薦數據,對於第一行樣本,我們用語言翻譯:用戶1在語境3下對物體2有一個點擊行爲(click=1);這裏,用戶(user),語境(context)和物體(item)都是特徵,點擊行爲是label,我們要用一個模型去擬合這個label,使這個模型能夠預測一個用戶在某語境下對某物體的的點擊率。最簡單的模型爲線性模型,即:
其中,爲各特徵中的非零項索引的集合,爲非零項對應的值,由於這裏都是類別特徵(Category Feature),所以非零項等於1, 爲非零項對應的權值,是模型要學習的參數。我們把第一行數據帶入式子得到圖中的等式如下:
讓我們分析一下線性模型的參數空間大小,假設我們一共只有10個用戶,10種物體,10種語境,那麼參數由三個向量組成:,和,每個向量的長度爲10,向量中第個元素的取值代表對應權值,那麼我們的參數空間一共是30。
這裏只是一個最簡單的例子,其實每個參數也可以用一個長度爲的向量表示,那麼,和則分別爲三個的矩陣。
2. 二階模型(Degree-2 Polynomial Model)
前面一階線性模型的缺點是無法挖掘特徵之間的相關性,各個特徵參數都是獨立學習,而往往特徵之間是有關聯的,例如用戶在中秋節的時候更容易對月餅發生點擊或者購買行爲,此時語境是中秋節,物體是月餅,當這兩個條件同時滿足時,點擊更有可能發生。爲了考慮特徵之間的相關性,我們有了二階模型,簡單的二階模型是一階模型的直接擴展:參數只有在任意兩個特徵同時取值時才確定。二階模型的表達爲:
其中,爲任意兩個特徵中的非零項索引組合的集合,而是某兩個特徵的非零項索引同時爲和時的參數。同樣,我們把第一行數據帶入二階模型如下圖:
此時,二階模型的參數擴展到了一個的矩陣,爲所有特徵取值空間的和,假設user,item,context都只有10個不同值,那麼,參數空間。需要注意的是矩陣爲一個對稱矩陣,這是因爲我們並不區分兩個特徵的取值順序,如,這兩個參數在矩陣中處於對稱位置;而且同一特徵的不同取值參數是沒有意義的,如是不存在的,一個用戶不可能存在着兩種取值,因此矩陣中有一大半參數是無效的,如上圖所示,我們把這些參數在矩陣中可以置爲0,但是其參數空間複雜度仍然爲。
3. Factorization Machines (FM)
簡單二階模型的缺點是:
- 一個參數只有在兩個特徵同時取某數纔會參與計算和更新,而對於不同的樣本,要使兩個特徵的取值重複概率很小,導致中大部分的位置由於樣本中從未出現過而得不到計算和更新。
- 在實際應用中,由於特徵的取值有可能有幾百萬種,如用戶的數量,導致非常大,使矩陣特別稀疏,去維護這樣一個大矩陣儲存開銷太大。
爲了解決這些問題,我們可以用類似矩陣分解的思路去解決:把矩陣分解成兩個小矩陣,用這兩個小矩陣的相乘結果來近似。具體來說,每個矩陣的元素由兩個向量和相乘表示,同樣,我們用代表任意兩個特徵中的非零項組合的集合,因此,FM模型能被表示爲:
爲什麼FM能夠避免簡單二階模型中的第一個問題呢? 我們用同樣的例子給出了一個FM的計算如下圖,我們可以發現:向量在參數和中都參與了計算,導致的更新條件變得寬鬆,只要是滿足出現的,都能隨之參與計算和更新,這個向量其實就代表了關於用戶1的特徵表達(feature representation)或者畫像(profile)。
爲什麼FM能夠解決簡單二階模型中的空間複雜度問題呢? FM中,矩陣被拆分爲了兩個矩陣相乘,因此參數空間爲,但於通常很小,遠小於N,而且是一個常數,因此其空間複雜度降爲了。這裏的是一個超參數,控制着信息壓縮後的保留的程度:較小的保留的信息少,但是計算快;較大的保留的信息多,雖然計算慢,但模型效果和精度較好。
那麼,最重要一個問題來了,爲什麼矩陣能夠被拆解爲兩個小矩陣相乘呢? 爲了回答這個問題,我們需要知道FM模型和矩陣分解之間的關係。我們知道,一個的方陣可以被分解爲它的特徵向量與特徵值矩陣:,其中是標準化的特徵向量組成的維矩陣,而是主對角線爲特徵值的維矩陣。FM在這裏並沒有採用所有的特徵向量而是用了前個特徵向量(按照特徵值大小排列),通常情況下,前10%甚至前1%的特徵值就能佔據全部特徵值之和的99%,因此FM分解出的矩陣()計算出來的是真實的一種近似: 。 還有一個問題,去哪了呢?嚴格來說,FM的分解應該這麼寫,此時是主對角線爲前個特徵值的維矩陣,也是模型要學習的參數,其實乘以可以看做是對特徵向量的伸縮變換,並不會改變其向量方向(因爲它是一個主對角線纔有非零值的矩陣),因此每個特徵向量經過了同樣的伸縮變換後,其向量的相對位置並沒有發生變化,相當於乘以了一個常數,所以最後計算出來的得分的相對大小不會發生變化。個人猜測:正是因爲,有時候我們並不關心預測出來的點擊率最大是多少(絕對值),而只關心哪個物體點擊率最大(相對值),因此這個經常被忽略。
4. Field-aware Factorization Machines (FFM)
FM模型雖然有着不錯的效果,但是在某種程度上來說它還是比較“粗糙”的。爲什麼呢?因爲各向量的更新條件過於寬鬆,比如參數和都會讓參與計算和更新,這樣有可能導致一個問題:若出現的次數遠大於,那麼用戶向量的更新基本上被主導,而覆蓋掉的更新。出現這種情況的根本原因是由於各特徵的取值分佈不相同,這是我們無法避免的,那麼一種改進版的FM可以解決這個問題:我們讓向量的更新在特徵間保持獨立,具體來說,我們把拆分成和兩部分,當出現時,我們讓參與計算和更新;當出現時,我們讓這部分參與計算和更新,這樣,關於這個用戶向量的更新就在各個特徵間獨立開了,因此這個向量能夠區分不同的特徵,我們說它是Field-aware,這裏的Filed就是指的特徵,所以這個改進版方法就是Field-aware FM (FFM)。FFM的表達式爲:
其中,是向量中與特徵向量交叉的那部分,下圖給出了FFM模型計算的例子:
假設數據集中一共有M個特徵,FFM把FM的參數空間擴展了倍,因爲此時的矩陣爲維。在上圖中,由於我們一共只有3域特徵(用戶,語境,物體),每個向量只需要拆分成兩塊分別於與其他兩域特徵交叉即可,若每塊向量長度爲k,那麼矩陣的維度爲,參數空間是FM的兩倍。
5.FFM 實現思路
無論是一階、二階、FM和FFM模型,其模型更新都可以使用梯度下降法,它的結構更像一個二維的一層神經網絡,每次正向傳播只有少部分非零的交叉特徵所在的神經元是激活狀態,反向傳播的時候也只有這些激活的神經元參數得到更新。與標準的神經網絡更新一樣,對於迴歸任務,損失函數選用平方差誤差;對於分類任務,選用交叉熵損失函數。
有了前面的介紹,我們已經知道了FFM的基本原理,那麼如何進行高效地實現呢?其實非常簡單,我們只需要維護一張非常大的哈希表,hash_key = feature_id + feature_value,對應的hash_value= weight_vector,其中,feature_id爲特徵所在的id,feature_value爲此特徵的值,舉個例子,對於,假設user特徵id=12,此時的key爲hash後的112(前面的1表示的是user特徵,後面的12表示的是用戶的id值),我們通過這個key在表中查到對應的權值向量,然後在這個向量中取出與item特徵交叉的那部分向量即爲。
Reference
https://www.csie.ntu.edu.tw/~r01922136/slides/ffm.pdf
https://zhuanlan.zhihu.com/p/31386807
https://tech.meituan.com/2016/03/03/deep-understanding-of-ffm-principles-and-practices.html