繼搜索引擎以後,推薦系統改變了用戶與網站之間的交互方式,在提升用戶參與度和多樣化推薦產品方面有重要的應用。亞馬遜有35%的利潤來源於它的推薦系統,Netflix有75%的用戶根據推薦系統選擇電影。html
推薦系統是一個很是大的話題,本文介紹一種經常使用的基於模型的協同過濾算法——SVD(奇異值分解),在python中的使用。python
假設咱們用m個用戶,n個商品,每一個用戶對每一個商品的評分能夠組成一個m*n的二維矩陣。固然,這個矩陣中會有很是多的值是不知道的,多是用戶沒有用過這個商品,也有可能用戶使用後沒有進行評分。以下圖所示git
圖中空白位置即未知的值。接下來,咱們須要作的是根據這個殘缺的二維矩陣中已知的值,預測出未知的值,即預測出每個用戶對每個商品的評分。github
能夠想象,當矩陣被預測值補充完整以後,矩陣的每一行即表示一個用戶對全部商品的評分,能夠從這些評分中提取評分最高的幾個商品推薦給用戶,這樣咱們就完成了一個推薦系統模型。算法
接下來,就是如何經過已知值預測未知值的問題了,這裏咱們採用矩陣分解的方式,如圖所示bash
中間矩陣能夠拆分爲左邊和上邊兩個矩陣的乘積,這就是奇異值分解,一個矩陣老是能夠拆分紅兩個矩陣相乘,SVD的原理能夠見這篇博客,SVD方法在推薦系統中應用的原理能夠參考這篇博客,下面,咱們主要來說講如何在python中使用。ide
首先須要安裝surprise庫,用下面這條命令函數
pip install scikit-surprise
複製代碼
假設咱們如今有這樣的數據(注:load_movielens
函數不是庫內置的,須要拿數據文件並本身定義,詳情見文末連接中的做者源碼)測試
movielens_df: pd.DataFrame = load_movielens()
movielens_df.head(5)
user_id movie_title rating
36649 User 742 Jerry Maguire (1996) 4
2478 User 908 Usual Suspects, The (1995) 3
82838 User 758 Real Genius (1985) 4
69729 User 393 Things to Do in Denver when You're Dead (1995) 3 36560 User 66 Jerry Maguire (1996) 4 複製代碼
即用戶與電影之間的評分對應關係,下面導入須要的模塊網站
from surprise import SVD
from surprise import Dataset, Reader
from surprise.model_selection import cross_validate, train_test_split
複製代碼
下面開始正式建模
第一步:初始化reader
,指定評分範圍爲1分到5分
reader = Reader(rating_scale=(1, 5))
複製代碼
第二步:初始化數據,傳入的數據只能有3列,必須按照這樣的順序[user_id, product_id, rating]
data = Dataset.load_from_df(movielens_df, reader)
複製代碼
這裏須要注意:data
變量已經不是DataFrame類型了,而是surprise庫中的一種數據類型。從上面movielens_df
的結果能夠看出,咱們的數據不是本文最初提到的矩陣形式,因此這一步轉換就會將數據轉化成surprise庫須要的形式,便於以後的算法求解。
第三步:拆分訓練集與測試集,75%的樣本做爲訓練集,25%的樣本做爲測試集
trainset, testset = train_test_split(data, test_size=.25)
複製代碼
這裏的trainset
的類型是surprise.dataset.Trainset
類型,咱們能夠查看數據的基本信息
trainset.n_users # 943
trainset.n_items # 596
複製代碼
這說明咱們要用於訓練的樣本共有943個用戶,596個商品。
第四步:訓練模型,指定有100個隱含特徵,使用訓練集進行訓練
model = SVD(n_factors=100)
model.fit(trainset)
複製代碼
這裏須要說明一下,100個隱含特徵是指,本來943*596的矩陣會被拆分紅943*100和100*596的兩個矩陣乘積,n_factors
值能夠任意指定只要不超過596便可,可是設置不一樣的值將會擬合出不一樣的模型,須要選擇使結果較優的值。
咱們也能夠查看拆分出來的兩個矩陣
model.pu.shape # (943, 100)
model.qi.shape # (596, 100)
複製代碼
預測一個用戶對一個電影的評分
指定用戶和電影名便可
a_user = "User 196"
a_product = "Toy Story (1995)"
model.predict(a_user, a_product)
# Prediction(uid='User 196', iid='Toy Story (1995)', r_ui=None, est=3.93380711688207, details={'was_impossible': False})
複製代碼
電影之間相關性
這裏咱們須要寫get_vector_by_movie_title
和cosine_distance
函數(詳情見文末連接中做者源碼)
以後咱們就能夠實現輸入兩個電影名稱,便可得到他們之間的相關性
toy_story_vec = get_vector_by_movie_title('Toy Story (1995)', model)
wizard_of_oz_vec = get_vector_by_movie_title('Wizard of Oz, The (1939)', model)
similarity_score = cosine_distance(toy_story_vec, wizard_of_oz_vec)
similarity_score
# 0.9461284008856982
複製代碼
這是徹底不考慮導演等電影特徵計算出來的電影類似度,由於咱們只使用了評分數據。
尋找與一個電影最類似的電影
首先須要實現get_top_similarities
函數,得到最類似的五個電影,最後效果以下
get_top_similarities('Star Wars (1977)', model)
vector cosine distance movie title
0 0.000000 Star Wars (1977)
1 0.262668 Empire Strikes Back, The (1980)
2 0.295667 Return of the Jedi (1983)
3 0.435423 Raiders of the Lost Ark (1981)
複製代碼
1.視頻 Daniel Pyrathon - A practical guide to Singular Value Decomposition in Python - PyCon 2018
3.視頻配套代碼