chapter3:協同過濾-隱式評級及基於物品的過濾

CopperDong發表於2017-10-04

   前面提到,有一些證據表明,使用者通常不使用細粒度的區分機制,而是傾向於要不給最高評分要不給最低評分。這種非此即彼的極端評級方式有時可能會導致結果無法使用。本章將考察對協同過濾的調優方法,以便更高效低產生更精確的推薦結果。

   顯示評級:指使用者顯示地給出物品的評級結果。如點贊/點差/評分

   隱式評級:觀察使用者的行為來獲得結果。如跟蹤使用者在紐約時報線上上的點選軌跡,對某個使用者的點選行為觀察幾周之後,就能夠構建該使用者的合理畫像(profile),比如,他不喜歡體育新聞但是好像喜歡技術新聞。如果他點選了iPhone的廣告,那麼或許他對該產品感興趣。另一種隱式評級來自使用者實際的購買結果。

一、顯示評級的問題

   問題1:使用者大都具有惰性,不願對物品評級

   問題2:使用者可能撒謊或者只給出部分資訊

   問題3:使用者不會更新其評級結果

   隱式評級有哪些問題呢?

      可能並不是給自己買,導致使用者畫像很奇怪

二、隱式資料

網頁: 點選指向某個網頁的連結

    瀏覽頁面的時間

    重複的訪問

    將一個網頁指向其他網頁

    在Hulu上觀看的視訊

音樂播放器: 使用者播放的歌曲

       使用者跳過的歌曲

       某首歌曲播放的次數

無論是顯示資料還是隱式資料,第二章介紹的演算法都可以適用

三、成功帶來的問題

  假設你有100萬使用者,進行一次推薦時需要計算100萬次距離計算

  所以,基於鄰居的推薦系統的最主要缺點是延遲性太差。幸運的是,該問題有辦法解決

  1、基於使用者的過濾(也稱為基於記憶體的協同過濾)

    有兩個問題:擴充套件性和稀疏性

  2、基於物品的過濾(基於模型的協同過濾)

    可以計算出最相似的兩件物品

四、調整後的餘弦相似度

  論文Item-based collaborative filtering recommendation algorithms


(Ru,i - Ru) 指的是使用者u給物品i的評分減去使用者u對所有物品的評分的平均值。S(i, j)指物品i和物品j之間的相似度、

def computeSimilarity(band1, band2, userRatings):
   averages = {}
   for (key, ratings) in userRatings.items():
      averages[key] = (float(sum(ratings.values()))
                      / len(ratings.values()))

   num = 0  # numerator
   dem1 = 0 # first half of denominator
   dem2 = 0
   for (user, ratings) in userRatings.items():
      if band1 in ratings and band2 in ratings:
         avg = averages[user]
         num += (ratings[band1] - avg) * (ratings[band2] - avg)
         dem1 += (ratings[band1] - avg)**2
         dem2 += (ratings[band2] - avg)**2
   return num / (sqrt(dem1) * sqrt(dem2))

我們已經得到了相似度矩陣,如果能夠利用該矩陣進行預測那就太好了(比如,我想知道David有多喜歡Kacey Musgraves?)

p(u, i) 指使用者對物品i的喜歡程度


五、Slope One演算法

另一種流行的基於物品過濾的演算法是Slop One,主要優點是簡潔性,因此它很容易實現

論文Slope One Predictors for online Rating-Based Collaborative Filtering 值得閱讀


可以將Slope One看成兩部分

  第一部分,事先計算的部分(批處理模式,可以是半夜或任何時間進行),稱為每對物品之間的偏差(deviation),可以得到物品偏差構成的資料庫


  第二部分,利用偏差實際預測,利用加權Slope One演算法進行預測


基於Python的實現

   def computeDeviations(self):
      # for each person in the data:
      #    get their ratings
      for ratings in self.data.values():
         # for each item & rating in that set of ratings:
         for (item, rating) in ratings.items():
            self.frequencies.setdefault(item, {})
            self.deviations.setdefault(item, {})                    
            # for each item2 & rating2 in that set of ratings:
            for (item2, rating2) in ratings.items():
               if item != item2:
                  # add the difference between the ratings to our
                  # computation
                  self.frequencies[item].setdefault(item2, 0)
                  self.deviations[item].setdefault(item2, 0.0)
                  self.frequencies[item][item2] += 1
                  self.deviations[item][item2] += rating - rating2
        
      for (item, ratings) in self.deviations.items():
         for item2 in ratings:
            ratings[item2] /= self.frequencies[item][item2]
   def slopeOneRecommendations(self, userRatings):
      recommendations = {}
      frequencies = {}
      # for every item and rating in the user's recommendations
      for (userItem, userRating) in userRatings.items():
         # for every item in our dataset that the user didn't rate
         for (diffItem, diffRatings) in self.deviations.items():
            if diffItem not in userRatings and \
               userItem in self.deviations[diffItem]:
               freq = self.frequencies[diffItem][userItem]
               recommendations.setdefault(diffItem, 0.0)
               frequencies.setdefault(diffItem, 0)
               # add to the running sum representing the numerator
               # of the formula
               recommendations[diffItem] += (diffRatings[userItem] +
                                             userRating) * freq
               # keep a running sum of the frequency of diffitem
               frequencies[diffItem] += freq
      recommendations =  [(self.convertProductID2name(k),
                           v / frequencies[k])
                          for (k, v) in recommendations.items()]
      # finally sort and return
      recommendations.sort(key=lambda artistTuple: artistTuple[1],
                           reverse = True)
      # I am only going to return the first 50 recommendations
      return recommendations[:50]

六、MovieLens資料集

明尼蘇達大學GroupLens研究專案所收集的MovieLens資料集包含使用者對影片的評分 www.grouplens.org

這裡使用了其中最小規模的資料集ml-100k

>>> import recommender3
>>> r = recommender3.recommender(0)
>>> r.loadMovieLens('ml-100k/')
102625
>>> r.showUserTopItems('1', 50)
When Harry Met Sally... (1989)	5
Jean de Florette (1986)	5
Godfather, The (1972)	5
...
>>> r.computeDeviations()   #在我的筆記本上大概需要30秒
>>> r.slopeOneRecommendations(r.data['1'])
>>> r.slopeOneRecommendations(r.data['25'])
最後:
1、你可以對MovieLens資料集中的10部影片進行評級,看看Slope One推薦系統會給你推薦什麼影片?你是否喜歡

2、實現調整的餘弦相似度計算方法,將其效能與Slope One進行比較

3、執行Booking Crossing資料集,dataset有27萬本書被評分,因此需要一個270000x270000的字典儲存偏差值,這大概需要730億個字典條目。對於MovieLens資料集,其字典的稀疏度如何?修改程式碼以便能夠處理更大的資料集



相關文章