作者:Kung-Hsiang, Huang (Steeve)
編譯:ronghuaiyang
今天給大家回顧一篇論文,神經協同過濾,看名字就知道,神經網絡版本的協同過濾,推薦算法的經典的方法之一。
神經協同過濾(NCF)是新加坡國立大學、哥倫比亞大學、山東大學、德州農工大學於 2017 年共同發表的一篇論文。利用神經網絡的靈活性、複雜性和非線性,建立了一個推薦系統。證明了傳統的推薦系統矩陣分解是神經協同過濾的一個特例。此外,它還表明 NCF 在兩個公共數據集中的表現優於最先進的模型。本文將解釋 NCF 的概念,並演示如何在 Pytorch 中實現它。
在深入研究論文之前,你應該知道什麼是推薦系統,以及一些基本的推薦系統。
我們從矩陣分解開始。它將效用矩陣分解為兩個子矩陣。在預測過程中,我們將兩個子矩陣相乘來重建預測的效用矩陣。對效用矩陣進行因式分解,使兩者相乘的損失與真實效用矩陣的損失最小化。一個常用的損失函數是均方誤差。
本質上,每個用戶和物品都被投射到潛空間中,由潛向量表示。兩個潛向量越相似,對應的用戶偏好越相關。由於我們將效用矩陣分解成相同的潛在空間,所以我們可以用餘弦相似度或點積來度量任意兩個潛向量的相似度。實際上,每個用戶/物品的預測是通過對應的潛向量的點積計算出來的。
預測等於潛向量的內積
然而,本文認為點積限制了用戶和物品潛向量的表達。讓我們考慮下面的情況。我們首先關注效用矩陣的前三行。
設 S{x,y}表示用戶 x 與用戶 y 之間的相似度,通過計算用戶 1、2、3 之間的餘弦相似度,可知 S{2, 3} > S{1, 2} > S{1, 3}。在不失一般性的情況下,我們將用戶映射到一個二維的潛空間,如下所示。
現在,我們考慮用戶 4。通過與其他算法的相似性比較,我們得到了 S{1,4} > S{3,4} > S{2,4}。但是,無論我們把潛向量 P4 放在 P1 的左右,它都必然會比 P3 更接近 P2。
因此,這個例子顯示了內積在充分模擬用戶和項目在潛在空間中的交互方面的局限性。
本文提出了如下圖所示的神經協同濾波方法。在輸入層,用戶和物品是獨熱編碼的。然後,通過相應的嵌入層映射到隱藏空間。神經網絡 FC 層可以是任何類型的神經元連接。例如,多層感知器可以放在這裡。該模型具有神經 CF 層的複雜連接和非線性,能夠較好地估計潛空間中用戶與物品之間的複雜交互作用。
NCF結構
那麼,NCF 是如何成為泛化版本的矩陣分解的呢?讓我在下圖中給你展示。我們首先用乘法層替換神經 CF 層,乘法層對兩個輸入執行元素級的乘法。然後利用線性激活函數,將乘法層到輸出層的權值設為 K×1 維的固定單位矩陣(全一矩陣)。
然後,我們有以下方程。
未被發現的交互 ŷ_ui 表示預測值(u, i)進入了重建的效用矩陣。L 為線性激活函數,⊙ 為元素乘操作。p_u 和 q_i 分別是用戶和項目的潛在向量,J 是維數(K,1)的單位矩陣。因為 J 是一個單位矩陣,所以在這個線性函數裡面變成了潛向量 p_u 和 q_i 的內積。此外,由於線性函數的輸入和輸出是相同的,所以可以歸結為最後一行。預測標籤是對應用戶和物品的潛向量的內積。這個方程與矩陣分解部分所示的方程相同。從而證明了矩陣分解是 NCF 的一個特例。
為了引入額外的非線性能力,提出的最終模型 NeuMF 除了廣義矩陣分解(GMP)層外,還包括一個多層感知器(MLP)模塊。
GMF 和 MLP 模塊的輸出連接到 sigmoid 激活輸出層。
本文對 NCF 模型和其他模型進行了評價。也就是說,每個用戶的最後一次交互被保留下來進行評估。考慮兩個評估指標,命中率@10 和 NDCG@10。命中率@K 表示每個用戶獲得 10 個推薦的預測命中率。假設我們為每個用戶推薦 10 個項目,10 個用戶中的 4 個與我們推薦的項目進行交互,然後點擊 Ratio@10=0.4。另一方面,NDCG 可以被看作是命中率的擴展,它還考慮了命中的順序。這意味著如果你的命中發生在較高的推薦上,NDCG 將更高。
性能比較如下圖所示。在所有情況下,NeuMF 的表現都優於其他模型。
此外,本文還證明了對 NeuMF 各個模塊進行預訓練的有效性。分別訓練 GMF 和 MLP 後,將訓練後的 GMF 和 MLP 的權重設置為 NeuMF 的初始化。
在本節中,我將向你展示如何在 Pytorch 中輕鬆實現 NeuMF。你只需為每個模型實現兩個函數,__init__函數指定模型結構,而forward()函數定義如何將輸入張量進行前向傳播。
def __init__(self, config): super(NeuMF, self).__init__() #mf part self.embedding_user_mf = torch.nn.Embedding(num_embeddings=self.num_users, embedding_dim=self.latent_dim_mf) self.embedding_item_mf = torch.nn.Embedding(num_embeddings=self.num_items, embedding_dim=self.latent_dim_mf) #mlp part self.embedding_user_mlp = torch.nn.Embedding(num_embeddings=self.num_users, embedding_dim=self.latent_dim_mlp) self.embedding_item_mlp = torch.nn.Embedding(num_embeddings=self.num_items, embedding_dim=self.latent_dim_mlp) self.fc_layers = torch.nn.ModuleList() for idx, (in_size, out_size) in enumerate(zip(config['layers'][:-1], config['layers'][1:])): self.fc_layers.append(torch.nn.Linear(in_size, out_size)) self.logits = torch.nn.Linear(in_features=config['layers'][-1] + config['latent_dim_mf'] , out_features=1) self.sigmoid = torch.nn.Sigmoid()
研究結果表明,採用單獨的嵌入層,MLP 和 GMF 的嵌入效果更好。因此,我們為這兩個模塊定義了指定的嵌入層。此外,ModuleList 還用於構建多層感知器。
def forward(self, user_indices, item_indices, titles): user_embedding_mlp = self.embedding_user_mlp(user_indices) item_embedding_mlp = self.embedding_item_mlp(item_indices) user_embedding_mf = self.embedding_user_mf(user_indices) item_embedding_mf = self.embedding_item_mf(item_indices) #### mf part mf_vector =torch.mul(user_embedding_mf, item_embedding_mf) mf_vector = torch.nn.Dropout(self.config.dropout_rate_mf)(mf_vector) #### mlp part mlp_vector = torch.cat([user_embedding_mlp, item_embedding_mlp], dim=-1) # the concat latent vector for idx, _ in enumerate(range(len(self.fc_layers))): mlp_vector = self.fc_layers[idx](mlp_vector "idx") mlp_vector = torch.nn.ReLU()(mlp_vector) mlp_vector = torch.nn.Dropout(self.config.dropout_rate_mlp)(mlp_vector) vector = torch.cat([mlp_vector, mf_vector], dim=-1) logits = self.logits(vector) output = self.sigmoid(logits) return output
forward()函數相當簡單。我們只是讓用戶和物品索引在定義的網絡中流動。特別要注意的是,我在 GMP 和 MLP 模塊的末尾添加了 Dropout 層,因為我發現這有助於網絡的正則化和性能的提高。
英文原文:https://towardsdatascience.com/paper-review-neural-collaborative-filtering-explanation-implementation-ea3e031b7f96