1、知識點
""" 推薦系統 1、相似度計算: 1、歐幾里德距離 2、皮爾遜相關系數 3、Cosin距離 2、推薦相似度選擇: 1、固定數量的鄰居 2、基於相似度門檻的鄰居 3、基於用戶的協同過濾:根據用戶和其他用戶之間的相關系數值,選擇值越小的用戶數據,並和該用戶比較,推薦物品 需要解決的問題:1、已知用戶評分矩陣R(一般很稀疏) 2、推斷矩陣中空格empty cells處的值 基於用戶的協同過濾不利於對全0矩陣進行推薦,其解決方案: 1、相似度計算最好使用皮爾遜相似度 2、考慮共同打分物品的數目 3、對打分進行歸一化處理 4、設置一個相似度閾值 缺點:1、稀疏問題(很多值為0) 2、大用戶量不利於計算,因此不推薦基於用戶的協同過濾 row:為用戶 ,column:為物品 場景:實時新聞、突發情況(實時數據) 4、基於物品的協同過濾:根據物品和物品之間的相似度,然后取閾值,獲取相鄰的值,即推薦物品 (一般使用皮爾遜相關系數) 優點:1、計算性能高,用戶數量大於物品數量 row:為用戶物品 ,column:為用戶 場景:圖書、電子商務、電影(離線數據) 5、用戶冷啟動問題(即用戶注冊的時候): 1、引導用戶把自己的一些屬性表達出來 2、利用現有的開發數據平台 3、根據用戶注冊的屬性 4、推薦排序榜單 6、物品冷啟動問題: 1、文本分析 2、主題模型 3、打標簽 4、推薦排行榜單 7、隱語義模型:將矩陣進行分解,然后求解隱含特征F,從而可以根據F得到一個先的具有填充值的NM矩陣,從而實現推薦,即NM = NF * FM 1、從數據出發,今天個性化推薦 2、用戶和物品之間有着隱含聯系 3、隱含因子讓計算機能理解就好 4、將用戶和物品通過中介隱含因子聯系起來 隱語義模型參數選擇: 1、隱特征的個數F,通常F=100 2、學習率alpha 別太大 3、正則化參數lambda,別太大 4、負樣本和正樣本比例ratio 8、協同過濾與 隱語義對比 1、原理:協同過濾基於統計方法,隱語義基於建模 2、空間復雜度,隱語義模型較小 3、實時推薦依舊難,目前離線計算多 9、模型評估指標 1、准確率 :均方誤差 2、召回率 :正負樣本中,推薦正樣本中正確的比例 3、覆蓋率 4、多樣性 推薦難點: 1、計算量解決 2、模型的好壞怎么評估,怎么更新
- L1正則化可以產生稀疏權值矩陣,即產生一個稀疏模型,可以用於特征選擇
- L2正則化可以防止模型過擬合(overfitting);一定程度上,L1也可以防止過擬合
suprise 推薦系統api url:https://surprise.readthedocs.io/en/stable/getting_started.html 數據地址:http://files.grouplens.org/datasets/movielens/ml-100k-README.txt 基於用戶的協同過濾: """
2、代碼實現推薦案例
# coding = utf-8 from __future__ import (absolute_import,division,print_function,unicode_literals) from surprise import SVD,KNNBasic from surprise import Dataset from surprise import evaluate,print_perf from surprise import GridSearch import pandas as pd import os import io from surprise import KNNBaseline def collaborativeFiltering(): """ 協同過濾算法 :return: """ #1、加載數據 data = Dataset.load_builtin('ml-100k') data.split(n_folds=3)#3折,結果有3個 #2、實例化協同過濾算法對象 algo = KNNBasic() pref =evaluate(algo,data,measures=['RMSE','MAE'])#算法模型評估 print_perf(pref) def matrixFactorization(): """ SVD,矩陣分解 :return: """ param_grid = {'n_epochs':[5,10],'lr_all':[0.002,0.005],'reg_all':[0.4,0.6]} grid_search = GridSearch(SVD,param_grid,measures=['RMSE','FCP']) data = Dataset.load_builtin('ml-100k') data.split(n_folds=3) # 3折,結果有3個 grid_search.evaluate(data) print(grid_search.best_score['RMSE']) #最好的得分值 print(grid_search.best_params['RMSE']) # 最好的得分值 result_df =pd.DataFrame.from_dict(grid_search.cv_results) print(result_df) def recommendItem(): data = Dataset.load_builtin('ml-100k') #加載u.data文件 trainset = data.build_full_trainset() sim_options = {'name':'pearson_baseline','user_based':False} #皮爾遜相似度pearson_baseline ,'user_based':False表示基於物品的協同過濾 algo = KNNBaseline(sim_options=sim_options) #協同過濾 algo.train(trainset) #訓練 #獲取電影的id rid_to_name,name_to_rid = read_item_names() toy_story_raw_id = name_to_rid['Now and Then (1995)'] print(toy_story_raw_id) #數據文件中的id toy_story_inner_id =algo.trainset.to_inner_iid(toy_story_raw_id) print(toy_story_inner_id)#實際計算矩陣中的id toy_story_neighbors_id = algo.get_neighbors(toy_story_inner_id,k=10) print(toy_story_neighbors_id) #實際計算矩陣中的id toy_story_neighbors_id = (algo.trainset.to_raw_iid(inner_id) for inner_id in toy_story_neighbors_id) #數據文件中的id toy_story_neighbors_name = (rid_to_name[rid] for rid in toy_story_neighbors_id) #數據文件中的name print("##########推薦item#########") for movie in toy_story_neighbors_name: print(movie) def read_item_names(): file_name=('./ml-100k/u.item') rid_to_name={} name_to_rid={} with io.open(file_name,'r',encoding='ISO-8859-1') as f: for line in f: line = line.split('|') rid_to_name[line[0]] = line[1] name_to_rid[line[1]]=line[0] return rid_to_name,name_to_rid if __name__ == '__main__': recommendItem()
3、基於物品的協同過濾圖
4、代碼案例2
import recsys.algorithm recsys.algorithm.VERBOSE = True from recsys.algorithm.factorize import SVD from recsys.datamodel.data import Data from recsys.evaluation.prediction import RMSE import os,sys tmpfile = "/tmp/movielens.zip" moviefile = "./ml-1m/movies.dat" class RecommendSystem(object): def __init__(self, filename, sep, **format): self.filename = filename self.sep = sep self.format = format # 訓練參數 self.k = 100 self.min_values = 10 self.post_normalize = True self.svd = SVD() # 判斷是否加載 self.is_load = False # 添加數據處理 self.data = Data() # 添加模型評估 self.rmse = RMSE() def get_data(self): """ 獲取數據 :return: None """ # 如果模型不存在 if not os.path.exists(tmpfile): # 如果數據文件不存在 if not os.path.exists(self.filename): sys.exit() # self.svd.load_data(filename=self.filename, sep=self.sep, format=self.format) # 使用Data()來獲取數據 self.data.load(self.filename, sep=self.sep, format=self.format) train, test = self.data.split_train_test(percent=80) return train, test else: self.svd.load_model(tmpfile) self.is_load = True return None, None def train(self, train): """ 訓練模型 :param train: 訓練數據 :return: None """ if not self.is_load: self.svd.set_data(train) self.svd.compute(k=self.k, min_values=self.min_values, post_normalize=self.post_normalize, savefile=tmpfile[:-4]) return None def rs_predict(self, itemid, userid): """ 評分預測 :param itemid: 電影id :param userid: 用戶id :return: None """ score = self.svd.predict(itemid, userid) print "推薦的分數為:%f" % score return score def recommend_to_user(self, userid): """ 推薦給用戶 :param userid: 用戶id :return: None """ recommend_list = self.svd.recommend(userid, is_row=False) # 讀取文件里的電影名稱 movie_list = [] for line in open(moviefile, "r"): movie_list.append(' '.join(line.split("::")[1:2])) # 推薦具體電影名字和分數 for itemid, rate in recommend_list: print ("給您推薦了%s,我們預測分數為%s" %(movie_list[itemid],rate)) return None def evaluation(self, test): """ 模型的評估 :param test: 測試集 :return: None """ # 如果模型不是直接加載 if not self.is_load: # 循環取出測試集里面的元組數據<評分,電影,用戶> for value, itemid, userid in test.get(): try: predict = self.rs_predict(itemid, userid) self.rmse.add(value, predict) except KeyError: continue # 計算返回誤差(均方誤差) error = self.rmse.compute() print ("模型誤差為%s:" % error) return None if __name__ == "__main__": rs = RecommendSystem("./ml-1m/ratings.dat", "::", row=1, col=0, value=2, ids=int) train, test = rs.get_data() rs.train(train) rs.evaluation(test) # rs.rs_predict(1,1) rs.recommend_to_user(1)