NeuralCF神經(jīng)網(wǎng)絡(luò)協(xié)同過濾
NeuralCF
1.動機(jī)
在前面的組隊學(xué)習(xí)中,我們學(xué)習(xí)了最經(jīng)典的推薦算法,協(xié)同過濾。在前深度學(xué)習(xí)的時代,協(xié)同過濾曾經(jīng)大放異彩,但隨著技術(shù)的發(fā)展,協(xié)同過濾相比深度學(xué)習(xí)模型的弊端就日益顯現(xiàn)出來了,因為它是通過直接利用非常稀疏的共現(xiàn)矩陣進(jìn)行預(yù)測的,所以模型的泛化能力非常弱,遇到歷史行為非常少的用戶,就沒法產(chǎn)生準(zhǔn)確的推薦結(jié)果了。雖然,我們可以通過矩陣分解算法增強(qiáng)它的泛化能力,但因為矩陣分解是利用非常簡單的內(nèi)積方式來處理用戶向量和物品向量的交叉問題的,所以,它的擬合能力也比較弱。這該怎么辦呢?不是說深度學(xué)習(xí)模型的擬合能力都很強(qiáng)嗎?我們能不能利用深度學(xué)習(xí)來改進(jìn)協(xié)同過濾算法呢?當(dāng)然是可以的。2017 年,新加坡國立的研究者就使用深度學(xué)習(xí)網(wǎng)絡(luò)來改進(jìn)了傳統(tǒng)的協(xié)同過濾算法,取名 NeuralCF(神經(jīng)網(wǎng)絡(luò)協(xié)同過濾)。NeuralCF 大大提高了協(xié)同過濾算法的泛化能力和擬合能力,讓這個經(jīng)典的推薦算法又重新在深度學(xué)習(xí)時代煥發(fā)生機(jī)。這章節(jié),我們就一起來學(xué)習(xí)并實現(xiàn) NeuralCF!
2.模型結(jié)構(gòu)及原理
Neural collaborative filtering framework
為了允許神經(jīng)網(wǎng)絡(luò)對協(xié)同過濾進(jìn)行一個完整的處理,我們采用上圖展示的多層感知機(jī)去模擬一個用戶項目交互,它的一層的輸出作為下一層的輸入。底部輸入層包括兩個特征向量和 ,分別用來描述用戶和項目。 他們可以進(jìn)行定制,用以支持廣泛的用戶和項目的建模,例如上下文感知,基于內(nèi)容,和基于鄰居的構(gòu)建方式。由于本章工作的重點是純的協(xié)同過濾模型設(shè)置,我們僅使用一個用戶和一個項目作為輸入特征,它使用編碼將它們轉(zhuǎn)化為二值化稀疏向量。注意到,我們對輸入使用這樣的通用特征表示,可以很容易地使用的內(nèi)容特征來表示用戶和項目,以調(diào)整解決冷啟動問題。
輸入層上面是嵌入層();它是一個全連接層,用來將輸入層的稀疏特征向量映射為一個稠密向量()。所獲得的用戶(項目)的嵌入(就是一個稠密向量)可以被看作是在潛在因素模型的上下文中用于描述用戶(項目)的潛在向量。然后我們將用戶嵌入和項目嵌入送入多層神經(jīng)網(wǎng)絡(luò)中,我們將它稱為神經(jīng)網(wǎng)絡(luò)協(xié)同過濾層,它將潛在向量映射為預(yù)測分?jǐn)?shù)。NCF層的每一層都可以被定制,用以發(fā)現(xiàn)用戶-項目交互的某些潛在結(jié)構(gòu)。最后一個隱藏層 的維度尺寸決定了模型的能力。最終輸出層是預(yù)測分?jǐn)?shù),通過最小化預(yù)測值和其目標(biāo)值之間逐點損失進(jìn)行訓(xùn)練。
論文中主要運(yùn)用均方誤差()進(jìn)行回歸:
其中表示交互矩陣中觀察到的條目(如對電影有明確的評分,評級), 表示負(fù)樣本(,可以將未觀察的樣本全體視為負(fù)樣本,或者采取抽樣的方式標(biāo)記為負(fù)樣本); 是一個超參數(shù),用來表示訓(xùn)練樣本的權(quán)重。雖然均方誤差可以通過假設(shè)觀測服從高斯分布來作出解釋,但是它不適合處理隱性數(shù)據(jù)()。這是因為對于隱含數(shù)據(jù)來說,目標(biāo)值 是二進(jìn)制值或,表示是否與進(jìn)行了互動。在下文中提出了逐點學(xué)習(xí)NCF的概率學(xué)方法,特別注重隱性數(shù)據(jù)的二進(jìn)制屬性。
考慮到隱性反饋的一類性質(zhì),我們可以將的值作為一個標(biāo)簽------表示項目和用戶相關(guān),表達(dá)不相關(guān)。這樣一來預(yù)測分?jǐn)?shù)就代表了項目和用戶相關(guān)的可能性大小。為了賦予NCF這樣的概率解釋,我們需要將網(wǎng)絡(luò)輸出限制到的范圍內(nèi),通過使用概率函數(shù)(邏輯函數(shù)或者函數(shù))作為激活函數(shù)作用在輸出層,我們可以很容易地實現(xiàn)數(shù)據(jù)壓縮。經(jīng)過以上設(shè)置后,我們這樣定義似然函數(shù):
對似然函數(shù)取負(fù)對數(shù),我們得到(負(fù)對數(shù)可以用來表示函數(shù),而且還能消除小數(shù)乘法的下溢出問題):
這是NCF需要去最小化的目標(biāo)函數(shù),并且可以通過使用隨機(jī)梯度下降()來進(jìn)行訓(xùn)練優(yōu)化。這個函數(shù)和交叉熵?fù)p失函數(shù)是一樣的。通過在NCF上使用這樣一個概率處理,我們把隱性反饋的推薦問題當(dāng)做一個二分類問題來解決。對于負(fù)樣本 ,我們在每次迭代均勻地從未觀察到的相互作用中采樣(作為負(fù)樣本)并且對照可觀察到交互的數(shù)量,控制采樣比率。
我們現(xiàn)在來證明MF是如何被解釋為我們的NCF框架的一個特例。由于MF是推薦領(lǐng)域最流行的模型,并已在眾多文獻(xiàn)中被廣泛的研究,復(fù)現(xiàn)它能證明NCF可以模擬大部分的分解模型。由于輸入層是用戶(項目)ID中的一個編碼,所獲得的嵌入向量可以被看作是用戶(項目)的潛在向量。我們用表示用戶的潛在向量,表示項目的潛在向量 ,我們定義第一層神經(jīng)CF層的映射函數(shù)為:
其中表示向量的逐元素乘積。然后,我們將向量映射到輸出層:
其中和分別表示輸出層的激活函數(shù)和連接權(quán)。直觀地講,如果我們將看做一個恒等函數(shù), 權(quán)重全為1,顯然這就是我們的MF模型。在NCF的框架下,MF可以很容易地被泛化和推廣。例如,如果我們允許從沒有一致性約束(uniform constraint)的數(shù)據(jù)中學(xué)習(xí),則會形成MF的變體,它允許潛在維度的不同重要性(For example, if we allow h to be learnt from data without the uniform constraint, it will result in a variant of MF that allows varying importance of latent dimensions)。如果我們用一個非線性函數(shù)將進(jìn)一步推廣MF到非線性集合,使得模型比線性MF模型更具有表現(xiàn)力。在NCF下實現(xiàn)一個更一般化的MF,它使用Sigmoid函數(shù)作為激活函數(shù),通過學(xué)習(xí) 。稱為GMF(Generalized Matrix Factorization,廣義矩陣分解)。
GMF,它應(yīng)用了一個線性內(nèi)核來模擬潛在的特征交互;MLP,使用非線性內(nèi)核從數(shù)據(jù)中學(xué)習(xí)交互函數(shù)。接下來的問題是:我們?nèi)绾文軌蛟贜CF框架下融合GMF和MLP,使他們能夠相互強(qiáng)化,以更好地對復(fù)雜的用戶-項目交互建模?一個直接的解決方法是讓GMF和MLP共享相同的嵌入層(Embedding Layer),然后再結(jié)合它們分別對相互作用的函數(shù)輸出。這種方式和著名的神經(jīng)網(wǎng)絡(luò)張量(NTN,Neural Tensor Network)有點相似。然而,共享GMF和MLP的嵌入層可能會限制融合模型的性能。例如,它意味著,GMF和MLP必須使用的大小相同的嵌入;對于數(shù)據(jù)集,兩個模型的最佳嵌入尺寸差異很大,使得這種解決方案可能無法獲得最佳的組合。為了使得融合模型具有更大的靈活性,我們允許GMF和MLP學(xué)習(xí)獨立的嵌入,并結(jié)合兩種模型通過連接他們最后的隱層輸出。
3.代碼實現(xiàn)
從模型的結(jié)構(gòu)上來看,NeuralCF的模型其實是在矩陣分解上進(jìn)行了加強(qiáng),用MLP代替了inner product,下面是構(gòu)建模型的核心代碼,詳細(xì)代碼參考github。
def NCF(dnn_feature_columns):
# 構(gòu)建輸入層,即所有特征對應(yīng)的Input()層,這里使用字典的形式返回,方便后續(xù)構(gòu)建模型
_, sparse_input_dict = build_input_layers(dnn_feature_columns) # 沒有dense特征
# 構(gòu)建模型的輸入層,模型的輸入層不能是字典的形式,應(yīng)該將字典的形式轉(zhuǎn)換成列表的形式
# 注意:這里實際的輸入與Input()層的對應(yīng),是通過模型輸入時候的字典數(shù)據(jù)的key與對應(yīng)name的Input層
input_layers = list(sparse_input_dict.values())
# 創(chuàng)建兩份embedding向量, 由于Embedding層的name不能相同,所以這里加入一個prefix參數(shù)
GML_embedding_dict = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False, prefix='GML')
MLP_embedding_dict = build_embedding_layers(dnn_feature_columns, sparse_input_dict, is_linear=False, prefix='MLP')
# 構(gòu)建GML的輸出
GML_user_emb = Flatten()(GML_embedding_dict['user_id'](sparse_input_dict['user_id'])) # B x embed_dim
GML_item_emb = Flatten()(GML_embedding_dict['movie_id'](sparse_input_dict['movie_id'])) # B x embed_dim
GML_out = tf.multiply(GML_user_emb, GML_item_emb) # 按元素相乘
# 構(gòu)建MLP的輸出
MLP_user_emb = Flatten()(MLP_embedding_dict['user_id'](sparse_input_dict['user_id'])) # B x embed_dim
MLP_item_emb = Flatten()(MLP_embedding_dict['movie_id'](sparse_input_dict['movie_id'])) # B x embed_dim
MLP_dnn_input = Concatenate(axis=1)([MLP_user_emb, MLP_item_emb]) # 兩個向量concat
MLP_dnn_out = get_dnn_out(MLP_dnn_input, (32, 16))
# 將dense特征和Sparse特征拼接到一起
concat_out = Concatenate(axis=1)([GML_out, MLP_dnn_out])
# 輸入到dnn中,需要提前定義需要幾個殘差塊
# output_layer = Dense(1, 'sigmoid')(concat_out)
output_layer = Dense(1)(concat_out)
model = Model(input_layers, output_layer)
return model
為了方便大家的閱讀,我們這里還給大家畫了一個整體的模型架構(gòu)圖,幫助大家更好的了解每一塊以及前向傳播。
image-20210307191533086
下面是一個通過keras畫的模型結(jié)構(gòu)圖,為了更好的顯示,數(shù)值特征和類別特征都只是選擇了一小部分,畫圖的代碼也在github中。
NCF
4.思考
如何用雙塔結(jié)構(gòu)實現(xiàn)NeuralCF?
5.參考資料
deepctr
論文原文
AI上推薦 之 NeuralCF與PNN模型(改變特征交叉方式)
論文筆記:Neural Collaborative Filtering(NCF)
谷歌雙塔模型
