推薦系統(tǒng)遇上深度學(xué)習(xí)(二十)--貝葉斯個(gè)性化排序(BPR)算法原理及實(shí)戰(zhàn)

排序推薦算法大體上可以分為三類秃臣,第一類排序算法類別是點(diǎn)對方法(Pointwise Approach)梳毙,這類算法將排序問題被轉(zhuǎn)化為分類、回歸之類的問題般婆,并使用現(xiàn)有分類到腥、回歸等方法進(jìn)行實(shí)現(xiàn)。第二類排序算法是成對方法(Pairwise Approach)蔚袍,在序列方法中乡范,排序被轉(zhuǎn)化為對序列分類或?qū)π蛄谢貧w。所謂的pair就是成對的排序,比如(a,b)一組表明a比b排的靠前篓足。第三類排序算法是列表方法(Listwise Approach)段誊,它采用更加直接的方法對排序問題進(jìn)行了處理。它在學(xué)習(xí)和預(yù)測過程中都將排序列表作為一個(gè)樣本栈拖。排序的組結(jié)構(gòu)被保持连舍。

之前我們介紹的算法大都是Pointwise的方法,今天我們來介紹一種Pairwise的方法:貝葉斯個(gè)性化排序(Bayesian Personalized Ranking, 以下簡稱BPR)

1涩哟、BPR算法簡介

1.1 基本思路

在BPR算法中索赏,我們將任意用戶u對應(yīng)的物品進(jìn)行標(biāo)記,如果用戶u在同時(shí)有物品i和j的時(shí)候點(diǎn)擊了i贴彼,那么我們就得到了一個(gè)三元組<u,i,j>潜腻,它表示對用戶u來說,i的排序要比j靠前器仗。如果對于用戶u來說我們有m組這樣的反饋融涣,那么我們就可以得到m組用戶u對應(yīng)的訓(xùn)練樣本。

這里精钮,我們做出兩個(gè)假設(shè):

  1. 每個(gè)用戶之間的偏好行為相互獨(dú)立威鹿,即用戶u在商品i和j之間的偏好和其他用戶無關(guān)。
  2. 同一用戶對不同物品的偏序相互獨(dú)立轨香,也就是用戶u在商品i和j之間的偏好和其他的商品無關(guān)忽你。

為了便于表述,我們用>u符號表示用戶u的偏好臂容,上面的<u,i,j>可以表示為:i >u j科雳。

在BPR中,我們也用到了類似矩陣分解的思想脓杉,對于用戶集U和物品集I對應(yīng)的U*I的預(yù)測排序矩陣糟秘,我們期望得到兩個(gè)分解后的用戶矩陣W(|U|×k)和物品矩陣H(|I|×k),滿足:

那么對于任意一個(gè)用戶u丽已,對應(yīng)的任意一個(gè)物品i蚌堵,我們預(yù)測得出的用戶對該物品的偏好計(jì)算如下:

而模型的最終目標(biāo)是尋找合適的矩陣W和H,讓X-(公式打不出來沛婴,這里代表的是X上面有一個(gè)橫線,即W和H矩陣相乘后的結(jié)果)和X(實(shí)際的評分矩陣)最相似督赤∴业疲看到這里,也許你會說躲舌,BPR和矩陣分解沒有什區(qū)別呀丑婿?是的,到目前為止的基本思想是一致的,但是具體的算法運(yùn)算思路羹奉,確實(shí)千差萬別的秒旋,我們慢慢道來。

1.2 算法運(yùn)算思路

BPR 基于最大后驗(yàn)估計(jì)P(W,H|>u)來求解模型參數(shù)W,H,這里我們用θ來表示參數(shù)W和H, >u代表用戶u對應(yīng)的所有商品的全序關(guān)系,則優(yōu)化目標(biāo)是P(θ|>u)诀拭。根據(jù)貝葉斯公式迁筛,我們有:

由于我們求解假設(shè)了用戶的排序和其他用戶無關(guān),那么對于任意一個(gè)用戶u來說耕挨,P(>u)對所有的物品一樣细卧,所以有:

這個(gè)優(yōu)化目標(biāo)轉(zhuǎn)化為兩部分。第一部分和樣本數(shù)據(jù)集D有關(guān)筒占,第二部分和樣本數(shù)據(jù)集D無關(guān)贪庙。

第一部分

對于第一部分,由于我們假設(shè)每個(gè)用戶之間的偏好行為相互獨(dú)立翰苫,同一用戶對不同物品的偏序相互獨(dú)立止邮,所以有:

上面的式子類似于極大似然估計(jì),若用戶u相比于j來說更偏向i奏窑,那么我們就希望P(i >u j|θ)出現(xiàn)的概率越大越好导披。

上面的式子可以進(jìn)一步改寫成:

而對于P(i >u j|θ)這個(gè)概率,我們可以使用下面這個(gè)式子來代替:

其中良哲,σ(x)是sigmoid函數(shù)盛卡,σ里面的項(xiàng)我們可以理解為用戶u對i和j偏好程度的差異,我們當(dāng)然希望i和j的差異越大越好筑凫,這種差異如何體現(xiàn)滑沧,最簡單的就是差值:

省略θ我們可以將式子簡略的寫為:

因此優(yōu)化目標(biāo)的第一項(xiàng)可以寫作:

哇,是不是很簡單的思想巍实,對于訓(xùn)練數(shù)據(jù)中的<u,i,j>滓技,用戶更偏好于i,那么我們當(dāng)然希望在X-矩陣中ui對應(yīng)的值比uj對應(yīng)的值大棚潦,而且差距越大越好令漂!

第二部分
回想之前我們通過貝葉斯角度解釋正則化的文章:http://www.reibang.com/p/4d562f2c06b8

當(dāng)θ的先驗(yàn)分布是正態(tài)分布時(shí),其實(shí)就是給損失函數(shù)加入了正則項(xiàng)丸边,因此我們可以假定θ的先驗(yàn)分布是正態(tài)分布:

所以:

因此叠必,最終的最大對數(shù)后驗(yàn)估計(jì)函數(shù)可以寫作:

剩下的我們就可以通過梯度上升法(因?yàn)槭且屔鲜阶畲蠡?來求解了。我們這里就略過了妹窖,BPR的思想已經(jīng)很明白了吧纬朝,哈哈!讓我們來看一看如何實(shí)現(xiàn)吧骄呼。

2共苛、算法實(shí)現(xiàn)

本文的github地址為:https://github.com/princewen/tensorflow_practice/tree/master/recommendation/Basic-BPR-Demo

所用到的數(shù)據(jù)集是movieslen 100k的數(shù)據(jù)集判没,下載地址為:http://grouplens.org/datasets/movielens/

數(shù)據(jù)預(yù)處理

首先,我們需要處理一下數(shù)據(jù)隅茎,得到每個(gè)用戶打分過的電影澄峰,同時(shí),還需要得到用戶的數(shù)量和電影的數(shù)量辟犀。

def load_data():
    user_ratings = defaultdict(set)
    max_u_id = -1
    max_i_id = -1
    with open('data/u.data','r') as f:
        for line in f.readlines():
            u,i,_,_ = line.split("\t")
            u = int(u)
            i = int(i)
            user_ratings[u].add(i)
            max_u_id = max(u,max_u_id)
            max_i_id = max(i,max_i_id)


    print("max_u_id:",max_u_id)
    print("max_i_idL",max_i_id)

    return max_u_id,max_i_id,user_ratings

下面我們會對每一個(gè)用戶u俏竞,在user_ratings中隨機(jī)找到他評分過的一部電影i,保存在user_ratings_test,后面構(gòu)造訓(xùn)練集和測試集需要用到踪蹬。

def generate_test(user_ratings):
    """
    對每一個(gè)用戶u胞此,在user_ratings中隨機(jī)找到他評分過的一部電影i,保存在user_ratings_test,我們?yōu)槊總€(gè)用戶取出的這一個(gè)電影跃捣,是不會在訓(xùn)練集中訓(xùn)練到的漱牵,作為測試集用。
    """
    user_test = dict()
    for u,i_list in user_ratings.items():
        user_test[u] = random.sample(user_ratings[u],1)[0]
    return user_test

構(gòu)建訓(xùn)練數(shù)據(jù)
我們構(gòu)造的訓(xùn)練數(shù)據(jù)是<u,i,j>的三元組疚漆,i可以根據(jù)剛才生成的用戶評分字典得到酣胀,j可以利用負(fù)采樣的思想,認(rèn)為用戶沒有看過的電影都是負(fù)樣本:

def generate_train_batch(user_ratings,user_ratings_test,item_count,batch_size=512):
    """
    構(gòu)造訓(xùn)練用的三元組
    對于隨機(jī)抽出的用戶u娶聘,i可以從user_ratings隨機(jī)抽出闻镶,而j也是從總的電影集中隨機(jī)抽出,當(dāng)然j必須保證(u,j)不在user_ratings中

    """
    t = []
    for b in range(batch_size):
        u = random.sample(user_ratings.keys(),1)[0]
        i = random.sample(user_ratings[u],1)[0]
        while i==user_ratings_test[u]:
            i = random.sample(user_ratings[u],1)[0]

        j = random.randint(1,item_count)
        while j in user_ratings[u]:
            j = random.randint(1,item_count)

        t.append([u,i,j])

    return np.asarray(t)

構(gòu)造測試數(shù)據(jù)
同樣構(gòu)造三元組丸升,我們剛才給每個(gè)用戶單獨(dú)抽出了一部電影铆农,這個(gè)電影作為i,而用戶所有沒有評分過的電影都是負(fù)樣本j:

def generate_test_batch(user_ratings,user_ratings_test,item_count):
    """
    對于每個(gè)用戶u狡耻,它的評分電影i是我們在user_ratings_test中隨機(jī)抽取的墩剖,它的j是用戶u所有沒有評分過的電影集合,
    比如用戶u有1000部電影沒有評分夷狰,那么這里該用戶的測試集樣本就有1000個(gè)
    """
    for u in user_ratings.keys():
        t = []
        i = user_ratings_test[u]
        for j in range(1,item_count + 1):
            if not(j in user_ratings[u]):
                t.append([u,i,j])
        yield np.asarray(t)

模型構(gòu)建
首先回憶一下我們需要學(xué)習(xí)的參數(shù)θ岭皂,其實(shí)就是用戶矩陣W(|U|×k)和物品矩陣H(|I|×k)對應(yīng)的值,對于我們的模型來說沼头,可以簡單理解為由id到embedding的轉(zhuǎn)化爷绘,因此有:

u = tf.placeholder(tf.int32,[None])
i = tf.placeholder(tf.int32,[None])
j = tf.placeholder(tf.int32,[None])

user_emb_w = tf.get_variable("user_emb_w", [user_count + 1, hidden_dim],
                             initializer=tf.random_normal_initializer(0, 0.1))
item_emb_w = tf.get_variable("item_emb_w", [item_count + 1, hidden_dim],
                             initializer=tf.random_normal_initializer(0, 0.1))

u_emb = tf.nn.embedding_lookup(user_emb_w, u)
i_emb = tf.nn.embedding_lookup(item_emb_w, i)
j_emb = tf.nn.embedding_lookup(item_emb_w, j)

回想一下我們要優(yōu)化的目標(biāo),第一部分是ui和uj對應(yīng)的預(yù)測值的評分之差,再經(jīng)由sigmoid變換得到的[0,1]值,我們希望這個(gè)值越大越好,對于損失來說,當(dāng)然是越小越好玉凯。因此,計(jì)算如下:

x = tf.reduce_sum(tf.multiply(u_emb,(i_emb-j_emb)),1,keep_dims=True)
loss1 = - tf.reduce_mean(tf.log(tf.sigmoid(x)))

第二部分是我們的正則項(xiàng),參數(shù)就是我們的embedding值蔚约,所以正則項(xiàng)計(jì)算如下:

l2_norm = tf.add_n([
        tf.reduce_sum(tf.multiply(u_emb, u_emb)),
        tf.reduce_sum(tf.multiply(i_emb, i_emb)),
        tf.reduce_sum(tf.multiply(j_emb, j_emb))
    ])

因此坑赡,我們模型整個(gè)的優(yōu)化目標(biāo)可以寫作:

regulation_rate = 0.0001
bprloss = regulation_rate * l2_norm - tf.reduce_mean(tf.log(tf.sigmoid(x)))

train_op = tf.train.GradientDescentOptimizer(0.01).minimize(bprloss)

至此,我們整個(gè)模型就介紹完了么抗,如果大家想要了解完整的代碼實(shí)現(xiàn)毅否,可以參考github喲。

3蝇刀、總結(jié)

1.BPR是基于矩陣分解的一種排序算法螟加,它不是做全局的評分優(yōu)化,而是針對每一個(gè)用戶自己的商品喜好分貝做排序優(yōu)化吞琐。
2.它是一種pairwise的排序算法捆探,對于每一個(gè)三元組<u,i,j>,模型希望能夠使用戶u對物品i和j的差異更明顯站粟。
3.同時(shí)黍图,引入了貝葉斯先驗(yàn),假設(shè)參數(shù)服從正態(tài)分布奴烙,在轉(zhuǎn)換后變?yōu)榱薒2正則助被,減小了模型的過擬合。

參考文獻(xiàn)

1切诀、http://www.cnblogs.com/pinard/p/9128682.html
2揩环、http://www.cnblogs.com/pinard/p/9163481.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市幅虑,隨后出現(xiàn)的幾起案子丰滑,更是在濱河造成了極大的恐慌,老刑警劉巖倒庵,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件褒墨,死亡現(xiàn)場離奇詭異,居然都是意外死亡哄芜,警方通過查閱死者的電腦和手機(jī)貌亭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來认臊,“玉大人圃庭,你說我怎么就攤上這事∈纾” “怎么了剧腻?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長涂屁。 經(jīng)常有香客問我书在,道長,這世上最難降的妖魔是什么拆又? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任儒旬,我火速辦了婚禮栏账,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘栈源。我一直安慰自己挡爵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布甚垦。 她就那樣靜靜地躺著茶鹃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪艰亮。 梳的紋絲不亂的頭發(fā)上闭翩,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機(jī)與錄音迄埃,去河邊找鬼疗韵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛调俘,可吹牛的內(nèi)容都是我干的伶棒。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼彩库,長吁一口氣:“原來是場噩夢啊……” “哼肤无!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起骇钦,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤宛渐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后眯搭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窥翩,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年鳞仙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寇蚊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棍好,死狀恐怖仗岸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情借笙,我是刑警寧澤扒怖,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站业稼,受9級特大地震影響盗痒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜低散,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一俯邓、第九天 我趴在偏房一處隱蔽的房頂上張望骡楼。 院中可真熱鬧,春花似錦看成、人聲如沸君编。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至祠乃,卻和暖如春梦重,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背亮瓷。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工琴拧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘱支。 一個(gè)月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓蚓胸,卻偏偏與公主長得像,于是被迫代替她去往敵國和親除师。 傳聞我的和親對象是個(gè)殘疾皇子沛膳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容