[推薦系統]利用用戶行爲數據

基於用戶行爲分析的推薦算法是個性化推薦系統的重要算法,一般將這種類型的算法稱爲協同過濾算法。協同過濾就是指用戶可以齊心協力,通過不斷地和網站互動,使自己的推薦列表能夠不斷過濾掉自己不感興趣的物品,從而越來越滿足自己的需求。

用戶行爲數據簡介

用戶行爲數據在網站上最簡單的存在形式就是日誌。網站在運行過程中都產生大量原始日誌raw log,並將其存儲在文件系統中。很多互聯網業務會把多種原始日誌按照用戶行爲彙總成會話日誌session log,其中每個會話表示一次用戶行爲和對應的服務。會話日誌通常存儲在分佈式數據倉庫中,如支持離線分析的Hadoop Hive和支持在線分析的Google Dremel。

用戶行爲在個性化推薦系統中一般分兩種—顯性反饋行爲(explicit feedback)和隱性反饋行爲(implicit feedback):

  • 顯性反饋行爲: 包括用戶明確表示對物品喜好的行爲。不同網站收集顯性反饋行爲的主要方式是評分和喜歡/不喜歡。
  • 隱性反饋行爲: 指那些不能明確反應用戶喜好的行爲。最具代表性的隱性反饋行爲就是頁面瀏覽行爲。用戶瀏覽一個物品的頁面並不代表用戶一定喜歡這個頁面展示的物品,比如可能因爲這個頁面鏈接顯示在首頁,用戶更容易點擊它而已。相比顯性反饋,隱性反饋雖然不明確,但數據量更大。

顯性反饋數據和隱性反饋數據的比較.

顯性反饋數據 隱性反饋行爲
用戶興趣 明確 不明確
數量 較少 龐大
存儲 數據庫 分佈式文件系統
實時讀取 實時 有延遲
正負反饋 都有 只有正反饋

按照反饋的方向分,可以分爲正反饋和負反饋。正反饋指用戶的行爲傾向於指用戶喜歡該物品,負反饋指用戶的行爲傾向於指用戶不喜歡該物品。在顯性反饋中,很容易區分一個用戶行爲是正反饋還是負反饋,而在隱性反饋行爲中,就相對比較難以確定。

一般來說,不同的數據集包含不同的行爲,目前比較有代表性的數據集有下面幾個:

  • 無上下文信息的隱性反饋數據集: 每一條行爲記錄僅僅包含用戶ID和物品ID。
  • 無上下文信息的顯性反饋數據集: 每一條記錄包含用戶ID、物品ID和用戶對物品的評分;
  • 有上下文信息的隱性反饋數據集: 每一條記錄包含用戶ID、物品ID和用戶對物品產生行爲的時間戳;
  • 有上下文信息的顯性反饋數據集: 每一條記錄包含用戶ID、物品ID和用戶對物品的評分和評分行爲發生的時間戳。

用戶行爲分析

在利用用戶行爲數據設計推薦算法之前,研究人員首先需要對用戶行爲數據進行分析,瞭解數據中蘊含的一般規律,才能對算法的設計起到指導作用[相當與ML中數據探索]。

用戶活躍度和物品流行度的分佈

[物品的流行度指對物品產生過行爲的用戶總數]

很多互聯網數據的研究發現,互聯網上的很多數據分佈都滿足一種稱爲Power Law的分佈,這個分佈在互聯網領域也稱爲長尾分佈。

f(x)=αxkf(x) = \alpha x^k

很多研究人員發現,用戶行爲數據也蘊含這種長尾分佈的規律。
物品流行度和用戶活躍度都近似於長尾分佈。

用戶活躍度和物品流行度的關係

僅僅基於用戶行爲數據設計的推薦算法一般稱爲協同過濾算法。協同過濾算法分爲基於鄰域的方法(neighborhood-based)、隱語義模型(latent factor model)、基於圖的隨機遊走算法(random walk on graph)等。在這些方法中,最著名的、在業界得到最廣泛應用的算法是基於鄰域的方法,而基於鄰域的方法主要包含下面兩種算法:

  • 基於用戶的協同過濾算法: 給用戶推薦和他興趣相似的其他用戶喜歡的物品;
  • 基於物品的協同過濾算法: 給用戶推薦和他之前喜歡的物品相似的物品。

基於鄰域的算法

基於鄰域的算法分爲兩類:一類是基於用戶的協同過濾算法,另一類是基於物品的協同過濾算法。

基於用戶的協同過濾算法

基於用戶的協同過濾算法是推薦系統中最古老的算法。

1.基礎算法

基於用戶的協同過濾算法主要包括兩個步驟:

  • 找到和目標用戶興趣相似的用戶集合;
  • 找到這個集合中的用戶喜歡的,且目標用戶沒有聽說過的物品推薦給目標用戶。

步驟一的關鍵在於計算兩個用戶的興趣相似度。協同過濾算法主要利用行爲的相似度計算興趣的相似度。給定用戶u和用戶v,令N(u)表示用戶u曾經有過正反饋的物品集合,令N(v)爲用戶v曾經有過正反饋的物品集合。可以通過Jaccard公式簡單計算u和v的興趣相似度:

Jaccard公式

餘弦相似度計算:
餘弦相似度

2.基於相似度計算的改進

原來的相似度計算公式,如餘弦相似度計算方法太過於粗糙。如果兩個用戶對冷門物品採取過同樣的行爲更能說明他們興趣的相似度。John S. Breese在論文中提出如下公式,根據用戶行爲計算用戶的興趣相似度:
計算公式

其中,N(i)表示物品i的流行度。公式懲罰了用戶u和v共同興趣列表中熱門物品對他們相似度的影響。

基於用戶的協同過濾算法的缺點:首先,隨着網站的用戶數目越來越大,計算用戶興趣相似度矩陣越來越困難,其運算時間複雜度和空間複雜度的增長和用戶的增長近似於平方關係;其次,基於用戶的協同過濾很難對推薦結果做出解釋。

基於物品的系統過濾算法

基於物品的協同過濾item-based collaborative filtering算法是目前業界應用最多的算法。

1.基礎算法

基於物品的協同過濾算法(簡稱ItemCF)給用戶與推薦那些和他們之前喜歡的物品相似的物品。ItemCF算法並不利用物品的內容屬性計算物品之間的相似度,主要通過分析用戶的行爲記錄就是那物品之間的相似度。該算法認爲,物品A和物品B具有很大的相似性是因爲喜歡物品A的用戶大都也喜歡物品B。

基於物品的協同過濾算法主要分爲兩步:

  • 計算物品之間的相似度;
  • 根據物品的相似度和用戶的歷史行爲給用戶生成推薦列表。

購買了該商品的用戶也經常經常購買的其他商品,從這句話的定義出發,給出定義物品相似度的計算公式:
物品相似度

其中,分母|N(i)|是喜歡物品i的用戶數,分子是同事喜歡物品i和物品j的用戶數。
但是,上述公式存在一個問題,如果物品j很熱門,很多人都喜歡,那麼Wij就會很大,接近於1.因此,該公式會造成任務物品都會和熱門的物品有很大的相似度,對致力於挖掘長尾信息的推薦系統來說不是一個好的特性。爲了避免推薦出熱門的物品,使用下面的公式:
新計算公式
這個公式懲罰了物品j的權重,因此減輕了熱門物品會和很多物品相似的可能性。

從上面的定義可以看到,在協同過濾中兩個物品產生相似度是因爲它們共同被很多用戶喜歡,也就是說每個用戶都可以通過他們的歷史興趣列表給物品”貢獻“相似度。

ItemCF算法計算物品相似度時,先建立一個用戶-物品倒排表,然後對於每個用戶,將他物品列表中的物品量量在共現矩陣C中加1.詳細代碼:

def ItemSimilarity(train):#train是用戶-物品倒排表
    C = dict()#物品i,j共現矩陣,用字典表示;
    N = dict()#物品i的流行度--喜歡物品i的用戶數目
    for user, items in train.items():
        for i in items:
            N[i] += 1
            for j in items:
                if i != j:
                    C[i][j] = C[i].get(j, 0) + 1
    W = dict()#物品相似度矩陣 字典
    for i, related_items in C.items():
        for j, cij in related_items.items():
            W[i][j] = cij / math.sqrt(N[i]*N[j])

    return W

在得到物品之間的相似度後,ItemCF通過如下公式計算用戶u對一個物品j的興趣:
用戶u對物品i的興趣計算公式

這裏N(u)是用戶喜歡的物品集合,S(j,K)是和物品j最相似的K個物品的集合,wji是物品j和i的相似度,rui是用戶u對物品i的興趣。(對於隱反饋數據集,如果用戶u對物品i有過行爲,即可令rui=1.) 該公式的含義是:和用戶歷史上感興趣的物品[N(u)裏]越相似的物品,越有可能在用戶的推薦列表中獲得比較高的排名。 [在S集合中篩選掉已經喜歡的物品]實現代碼:

def Recommendation(train, user_id, W, K):
    rank = dict()
    ru = train[user_id]#用戶喜歡物品字典,物品:rui(用戶u對物品i的興趣,默認爲1)
    for i, rui in ru.items():
        # 選擇物品i的相似度矩陣,並由大到小排序;然後選擇前K個物品
        si = sorted(W[i].items(),key=lambda a:a[1],reverse=True)
        for j, wij in si[:K]:
            if j in ru:#排除已經喜歡的物品
                continue
            rank[j] += wij * rui

    return rank

ItemCF算法的一個優勢是可以提供推薦解釋,即利用用戶歷史上喜歡的物品爲現在的推薦結果進行解釋。帶解釋的ItemCF算法:

def Recommendation(train, user_id, W, K):
    rank = dict()
    reason = dict()
    ru = train[user_id]
    for i, rui in ru.items():
        si = sorted(W[i].items(),key=lambda a:a[1], reverse=True)
        for j, wij in si[:K]:
            if j not in ru:
                rank[j] += wij * rui
                reason[j][i] = wij * rui
    return rank, reason

對不同 K 值的測量可以看到:

  • 準確率和召回率和 K 也不成線性關係;選擇合適的K對獲得最高精度是非常重要的;
  • K 和流行度不完全正相關: 隨着K的增加,結果流行度會逐漸提高,但當K增加大到一定程度,流行度就不會再有明顯變化;
  • K 增大會降低系統的覆蓋率。

2.用戶活躍度對物品相似度的影響

在協同過濾中兩個物品蒼生相似度是因爲它們共同出現在很多用戶的興趣列表中。換句話說,每個用戶的興趣列表都對物品的相似度產生貢獻。但每個用戶的貢獻不應該都相同

John S.Breese在論文中提出一個IUF(Inversr User Frequence)用戶活躍度對數的倒數的參數,他認爲活躍用戶對物品相似度的貢獻應該小於不活躍的用戶,提出應該增加IUF參數來修正物品相似度的計算公式:
物品相似度修正公式

N(i)、N(j)物品i,j的流行度;u是同時購買物品i和物品j的用戶,N(u)是用戶喜歡的物品數[用來表示用戶u的活躍度]。上面公式只是對活躍用戶做了一種軟性的懲罰。
但對於很多過於活躍的用戶,爲了避免相似度矩陣過於稠密,在實際運算中一般直接忽略他的興趣列表,不將其納入到相似度計算的數據集中。ItemCF-IUF實現:

def ItemSimilarity(train):
    C = dict()#分子
    N = dict()#物品i的流行度
    for u, items in train.items():
        for i in items:
            N[i] += 1
            for j in items:
                if i != j:
                    C[i][j] += 1 / math.log(1 + len(items)*1.0)
    W = dict()#相似度矩陣
    for i, related_items in C.items():
        for j, cij in related_items.items():
            W[i][j] = cij / math.sqrt(N[i] * N[j])

    return W

3.物品相似度的歸一化

在研究匯中發現如果將ItemCF的相似度矩陣按最大值歸一化,可以提高推薦的準確率。其研究表明,如果已經得到了物品相似度矩陣w,可以用如下公式得到歸一化之後的相似度矩陣w’:
歸一化相似度矩陣公式

按照行進行歸一化。歸一化的好處不僅僅在於增加推薦的準確度,還可以提高推薦的覆蓋率和多樣性

UserCF和ItemCF的綜合比較

UserCF的推薦結果着重於反應和用戶興趣相似的小羣體的熱點,ItemCF的推薦結果着重於維繫用戶的歷史興趣。換句話說,UserCF的推薦更社會化,反映了用戶所在的小型羣體中物品的熱門程度,而ItemCF的推薦更加個性化,反映了用戶自己的興趣傳承

從技術上考慮, UserCF 需要維護一個用戶相似度的矩陣,而 ItemCF 需要維護一個物品相似度矩陣。從存儲的角度說,如果用戶很多,那麼維護用戶興趣相似度矩陣需要很大的空間,同理,如果物品很多,那麼維護物品相似度矩陣代價較大。

UserCF和ItemCF優缺點對比.

UserCF ItemCF
性能 適用於用戶較少的場合,如果用戶很多,計算用戶相似度矩陣代價很大 適用於物品數明顯小魚用戶數的場合,如果物品很多(網頁),計算物品相似度矩陣代價很大
領域 時效性強,用戶個性化興趣不太明顯的領域 長尾物品豐富,用戶個性化需求強烈的領域
實時性 用戶有新行爲,不一定造成推薦結果的立即變化 用戶有新行爲,一定會導致推薦結果的實時變化
冷啓動 在新用戶對很少的物品產生行爲的情況下,不能立即對他進行個性化推薦,因爲用戶性相似度表是每隔一段時間離線計算的 新物品上線後一段時間,一旦有用戶對物品產生行爲,就可以將新物品推薦給和對它產生行爲的用戶興趣相似的其他用戶 新用戶只要對一個物品產生行爲,就可以給他推薦和該物品相關的其他物品 但沒有辦法在不離線更新物品相似度表的情況下將新物品推薦給用戶
推薦利用 很難提供令用戶信服的推薦解釋 利用用戶的歷史行爲給用戶做推薦解釋,可以令用戶比較信服

離線實驗的性能在選擇推薦算法時病不起決定作用。首先應該滿足產品的需求,然後需要看實現代價。

隱語義模型

LFM(latent factor model)隱語義模型,該算法最早在文本挖掘領域被提出,用於找到文本的隱含語義。相關的名詞有LSI、pLSI、LDA和Topic Model.

  1. 基礎算法

隱語義模型的核心思想是通過隱含特徵(latent factor)聯繫用戶興趣和物品。簡單說就是對物品的興趣分類,對於用戶,首先確定他的興趣分類,然後從分類中選擇他可能喜歡的物品。基於興趣分類的方法大概解決3個問題:

  • 如何給物品分類?
  • 如何確定用戶對哪些類的物品感興趣,以及感興趣的程度?
  • 對於一個給定的類,選擇哪些屬於這個類的物品推薦給用戶,以及如何確定這些物品在一個類中的權重?

這裏的對物品分類的問題,可以用隱含語義分析技術較好地解決。它基於用戶行爲統計做分類,和專家標記相比:

  • 分類來源於對用戶行爲的統計,能代表各種用戶的看法;
  • 通過指定最終分類的個數,能控制分類的粒度;數目越大,分類粒度越細;
  • 能給一個物品多個分類:隱語義模型計算物品屬於每個類的權重,不是硬性地被分到某個類中;
  • 帶維度屬性,屬於多維度或同維度;
  • 可以確定物品在某個分類中的權重:通過統計用戶行爲決定物品在每個類中的權重;

這些都是專家標記不能或者很難做到的。

LFM通過如下公式計算用戶u對物品i的興趣:
興趣
公式中puk和qik是模型的參數,其中puk度量了用戶u的興趣和第k個隱類的關係,而qik度量了第k個隱類和物品i之間的關係。
這兩個參數的計算方式需要一個訓練集,對於每個用戶u,訓練集裏包含了用戶u喜歡的物品和不感興趣的物品,通過學習這個數據集,就可以獲得上面的模型參數。

推薦系統的用戶行爲分爲顯性反饋和隱性反饋。LFM在顯性反饋數據(評分數據)上解決評分預測問題並達到了很好的精度。如果是隱性數據集,這種數據集的特點是隻有正樣本(用戶喜歡什麼物品),沒有負樣本(用戶對什麼物品不感興趣)。

在隱性反饋數據集上應用LFM解決TopN推薦的第一個關鍵問題就是如何給每個用戶生成負樣本。

對負樣本採樣時應該遵循以下原則:

  • 對每個用戶,要保證正負樣本的平衡(數目相似);
  • 對每個用戶採樣負樣本時,要選取那些很熱門,而用戶卻沒有行爲的物品。

一般認爲,很熱門而用戶卻沒有行爲更加代表用戶對這個物品不感興趣。因爲對於冷門的物品,用戶可能是壓根沒在網站中發現這個物品,所以談不上是否感興趣。

需要優化的損失函數如下:
損失函數

其中,後兩項是用來防止過擬合的正則化項,lambda可以通過實驗獲得。通過梯度下降算法對損失函數進行優化求解,得到兩個參數指。

在LFM中,重要的參數有4個:

  • 隱特徵的個數F;
  • 學習速率alpha;
  • 正則化參數lambda;
  • 負樣本/正樣本比例radio。

通過實驗發現,radio對LFM的性能影響最大。

2.LFM和基於鄰域的方法的比較

LFM是一種基於機器學習的方法,具有比較好的理論基礎。和基於鄰域的方法相比,各有優缺點。

  • 理論基礎 LFM具有比較好的理論基礎,是一種學習方法,通過優化一個設定的指標建立最優的模型。基於鄰域的方法更多的是一種基於統計的方法,並沒有學習過程。
  • 離線計算的空間複雜度 基於鄰域的方法需要維護一張離線的相關表。

基於圖的模型

用戶行爲很容易用二分圖表示,因此很多圖的算法都可以應用到推薦系統中。

1.用戶行爲數據的二分圖表示

在研究基於圖的模型之前,首先需要將用戶行爲數據表示成圖的形式。這裏討論用戶行爲數據是由一系列二元組組成的,其中每個二元組(u,i)表示用戶u對物品i產生過行爲;這種數據集很容易用一個二分圖表示。

二分圖模型

2.基於圖的推薦算法

在二分圖上給用戶進行個性化推薦。如果將個性化推薦算法放到二分圖模型上,那麼給用戶u推薦物品的任務就可以轉換爲度量用戶頂點vu和與vu沒有邊直接相連的物品節點在圖上的相關性,相關性越高的物品在推薦列表中的權重就越高。

度量圖中兩個頂點之間相關性的方法有很多,但一般來說圖中頂點的相關性主要取決於下面3個因素:

  • 兩個頂點之間的路徑數;
  • 兩個頂點之間路徑的長度;
  • 兩個頂點之間的路徑經過的頂點。

相關性高的一堆頂點一般具有如下特徵:

  • 兩個頂點之間有很多路徑相連;
  • 連接兩個頂點之間的路徑長度都比較短;
  • 連接兩個頂點之間的路徑不會經過出度比較大的頂點。

基於上面3個主要因素,設計了很多計算圖中頂點之間相關性的方法。比如隨機遊走PersonalRank算法。

假設要給用戶u進行個性化推薦,可以從用戶u對應的節點vu開始在用戶物品二分圖上進行隨機遊走。遊走到任何一個節點時,首先按照概率alpha決定是繼續遊走,還是停止這次遊走並從vu節點開始重新遊走。如果決定繼續遊走,那麼就從當前節點指向的節點中按照均勻分佈隨機選擇一個節點作爲遊走下次經過的節點。這樣,經過很多次隨機遊走後,每個物品節點被訪問到的概率後收斂到一個數。最終的推薦列表中物品的權重就是物品節點的訪問概率

表示公式如:
遊走公式

alpha遊走概率,1-alpha停留概率;

代碼實現:

def PersonalRank(G, alpha, root, max_step):
    rank = dict()#推薦結果
    rank = {x : 0 for x in G.keys()}
    rank[root] = 1

    for k in range(max_step):
        tmp = {x : 0 for x in G.keys()}

        #取節點i和它的出邊尾節點集合ri
        for i, ri in G.items():
        #取i->j邊的尾節點j以及邊E(i,j)的權重wij, 邊的權重都爲1,
            for j, wij in ri.items():
                if j not in tmp:
                    tmp[j] = 0
                tmp[j] += alpha * rank[i] / (1.0 * len(ri))
                if j == root:
                    tmp[j] += 1 - alpha
        rank = tmp

    return rank

雖然PersonalRank算法可以通過隨機遊走進行比較好的理論解釋,但該算法在時間複雜度上有明顯的缺點。因爲在爲每個用戶進行推薦時,都需要在整個用戶物品二分圖上進行迭代,知道整個圖上的每個頂點的PR值收斂。這一過程時間複雜度非常高,不僅無法在線提供實時推薦,甚至離線生成推薦結果也很耗時。

爲了解決PersonalRank每次都需要在全圖迭代並造成時間複雜度高的問題,給出兩種解決方法。第一種,減少迭代次數,在收斂之前就停止。會影響最終的精度,但一般來說影響不會特別大;另一種就是從矩陣論出發,重新設計算法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章