python實現(xiàn)基于物品的協(xié)同過濾(ItemCF)電影推薦算法

最近,因為導(dǎo)師項目需要,花了幾天時間學(xué)習(xí)了項亮的《推薦系統(tǒng)實踐》,并用python實現(xiàn)了書上的Item Collaborative Filtering即基于物品的協(xié)同過濾算法,發(fā)現(xiàn)很多博客在算法的代碼的實現(xiàn)上說得很籠統(tǒng),而且項亮的書中關(guān)于協(xié)同過濾的代碼實現(xiàn)寫得又很零碎,故寫此文總結(jié)。

什么是協(xié)同過濾

協(xié)同過濾是推薦系統(tǒng)中最經(jīng)典和常用的算法,其核心思想就是它的名字:利用所有用戶的歷史行為數(shù)據(jù),用戶通過不斷地和網(wǎng)站互動,使推薦列表能夠不斷過濾掉用戶不感興趣的物品,從而越來越滿足需求。

協(xié)同過濾分為兩大類:

  1. 基于用戶的協(xié)同過濾(UserCF):當(dāng)要給目標(biāo)用戶進(jìn)行推薦時,先找出與他相似的其他用戶,再從那些用戶喜歡的項目中找出目標(biāo)用戶沒有看過的項目推薦給他。
  2. 基于項目的協(xié)同過濾(ItemCF):先找出目標(biāo)用戶歷史觀看列表里與之相似的其他項目,再對這些相似項目進(jìn)行排序并生成最后的推薦列表。

評分預(yù)測還是TopN推薦?

評分預(yù)測和TopN推薦是推薦系統(tǒng)兩種衡量預(yù)測準(zhǔn)確度的指標(biāo),大多數(shù)的推薦研究都是基于評分預(yù)測來展開的,主要是因為從事這方面早期研究的組織GroupLens主要就是針對電影評分進(jìn)行的,而且Netflix等推薦算法的大賽也主要是在解決評分預(yù)測問題,然而,評分高就是好的推薦嗎?當(dāng)然不是,有時候用戶給出好的評分并不代表他最想看它或者購買它。

部分代碼實現(xiàn)

  1. 記載數(shù)據(jù)集并劃分訓(xùn)練集與測試集
    def get_dataset(self, filename, pivot=0.75):

        trainSet_len = 0
        testSet_len = 0
        for line in self.load_file(filename):
            user, movie, rating, timestamp = line.split(',')
            if(random.random() < pivot):
                self.trainSet.setdefault(user, {})
                self.trainSet[user][movie] = rating
                trainSet_len += 1
            else:
                self.testSet.setdefault(user, {})
                self.testSet[user][movie] = rating
                testSet_len += 1
        print('劃分訓(xùn)練集與測試集成功!')
        print('訓(xùn)練集長 = %s' % trainSet_len)
        print('測試集長 = %s' % testSet_len)

這里的pivot(樞軸)因子將數(shù)據(jù)集以3:1分成了訓(xùn)練集與數(shù)據(jù)集。

  1. 計算電影之間的相似度并生成相似矩陣
    def calc_movie_sim(self):
        #建立movies_popular字典
        for user, movies in self.trainSet.items():
            for movie in movies:
            #若該movie沒在movies_popular字典中,則把其插入字典并賦值為0,否則+1,最終的movie_popular字典鍵為電影名,值為所有用戶總的觀看數(shù)
                if movie not in self.movie_popular:
                    self.movie_popular[movie] = 0
                else:
                    self.movie_popular[movie] += 1
        self.movie_count = len(self.movie_popular)
        print("訓(xùn)練集中電影總數(shù) = %d" % self.movie_count)
    
        for user, movies in self.trainSet.items():
            for m1 in movies:
                for m2 in movies:
                    if m1 == m2:
                        continue
                    #下面三步的作用是:分別將每個用戶看過的每一部電影與其他所有電影的聯(lián)系值置1,若之后又有用戶同時看了兩部電影, 則+1
                    self.movie_sim_matrix.setdefault(m1, {})
                    self.movie_sim_matrix[m1].setdefault(m2, 0)
                    self.movie_sim_matrix[m1][m2] += 1    
        print("建立電影的相似矩陣成功!")
        # print("矩陣進(jìn)行相似計算前movieId=1的一行為:")
        # print(self.movie_sim_matrix['1'])  

        # 計算電影之間的相似性
        for m1, related_movies in self.movie_sim_matrix.items():
            for m2, count in related_movies.items():
                # 注意0向量的處理,即某電影的用戶數(shù)為0
                if self.movie_popular[m1] == 0 or self.movie_popular[m2] == 0:
                    self.movie_sim_matrix[m1][m2] = 0
                else:
                    self.movie_sim_matrix[m1][m2] = count / math.sqrt(self.movie_popular[m1] * self.movie_popular[m2])
        print('計算電影的相似矩陣成功!')
       # print("電影相似矩陣中movieId=736的一行:")
       # print(self.movie_sim_matrix['736'])  
  1. 生成推薦列表
def recommend(self, user):
        K = int(self.n_sim_movie)
        N = int(self.n_rec_movie)
        rank = {}
        watched_movies = self.trainSet[user]
        for movie, rating in watched_movies.items():
            #對目標(biāo)用戶每一部看過的電影,從相似電影矩陣中取與這部電影關(guān)聯(lián)值最大的前K部電影,若這K部電影用戶之前沒有看過,則把它加入rank字典中,其鍵為movieid名,其值(即推薦度)為w(相似電影矩陣的值)與rating(用戶給出的每部電影的評分)的乘積
            for related_movie, w in sorted(self.movie_sim_matrix[movie].items(), key=itemgetter(1), reverse=True)[:K]:
                if related_movie in watched_movies:
                    continue
                rank.setdefault(related_movie, 0)
                #計算推薦度
                rank[related_movie] += w * float(rating)
        return sorted(rank.items(), key=itemgetter(1), reverse=True)[:N]

4.評估算法

    def evaluate(self):
        N = int(self.n_rec_movie)
        reuserN = input("請輸入?yún)⑴c評估的用戶數(shù)量:(總用戶數(shù)為457)")
        reuserN = int(reuserN)
        # 準(zhǔn)確率和召回率
        hit = 0
        rec_count = 0
        test_count = 0
        # 覆蓋率
        all_rec_movies = set()
        for user,m in list(self.trainSet.items())[:reuserN]:
            test_moives = self.testSet.get(user, {})
            rec_movies = self.recommend(user)
            print("用戶 %s 的電影推薦列表為:" % user)
            self.precommend(rec_movies)
            #注意,這里的w與上面recommend的w不要一樣,上面的w是指計算出的相似電影矩陣的權(quán)值,而這里是這推薦字典rank對應(yīng)的推薦度
            for movie, w in rec_movies:
                if movie in test_moives:
                    hit += 1
                all_rec_movies.add(movie)
            rec_count += N
            test_count += len(test_moives)

        precision = hit / (1.0 * rec_count)
        recall = hit / (1.0 * test_count)
        coverage = len(all_rec_movies) / (1.0 * self.movie_count)
        print('準(zhǔn)確率=%.4f\t召回率=%.4f\t覆蓋率=%.4f' % (precision, recall, coverage))

最后,附上ItemCF數(shù)據(jù)集及完整代碼地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容