機器學習:推薦系統(五. 以矩陣分來解協作過濾)

weixin_33912246發表於2017-12-18

Collaborative Filtering with Matrix Factorization
8688015-e3ffc2e6c76fa927.png

前言叨B叨

嗯嗯,又到了某些同學最愛的摳腚階段了,睜大眼睛跟我做哈

目錄

  1. 使用者和產品的潛在特徵
  2. 編寫推薦系統
  3. 矩陣分解工作原理
  4. 使用潛在表徵來找到類似的產品

1. 使用者和產品的潛在特徵

我們可以通過為每個使用者和每部電影分配屬性,然後將它們相乘併合並結果來估計使用者喜歡電影的程度。
8688015-d56b470bee54b040.png

相同的計算可以表示為矩陣乘法問題。首先,我們把使用者屬性放在一個名為U的矩陣中,在這個例子中是5,-2,1,-5和5。然後,我們把電影屬性放在一個名為M的矩陣中,我們使用矩陣乘法來找出使用者的評分。
8688015-db2a6facbd3a2762.png

但要做到這一點,我們必須已經知道使用者屬性和電影屬性。
為每個使用者和每部電影提供屬性評級並不容易。我們需要找到一種自動的方法。我們來看看電影評分矩陣,
8688015-8ec9db10e4406c6c.png

它顯示了我們資料集中的所有使用者如何評價電影。這個矩陣非常稀疏,但它給了我們很多資訊。例如,我們知道使用者ID2給電影1號五顆星。所以,基於此,我們可以猜測,這個使用者的屬性可能類似於電影的屬性,因為它們匹配的很好。換句話說,我們有一些線索可以使用。

讓我們看看我們如何利用這些線索來了解每部電影和每個使用者。在我們剛剛看到的等式中,U乘M等於電影等級,我們已經知道一些使用者的實際電影等級。我們已經擁有的電影評分矩陣是我們方程式的解決方案。雖然它是解決方案的一部分,但是這個陣列仍然有很多漏洞,但對於我們來說,這已經足夠了。
8688015-8a5875431b478b3a.png

實際上,我們可以使用目前為止我們所知道的電影評級,然後逆向找到滿足該等式的U矩陣和M矩陣。當然,這才是最酷的部分。

當我們將U和M相乘時,他們實際上會給我們一個完整的矩陣,我們可以使用那個完成的矩陣來推薦電影。讓我們回顧一下我們將如何構建這個推薦系統。
8688015-fc55431bfca458b4.png

首先,我們建立了我們在資料集中所有使用者評論的矩陣。接下來,我們從已知的評論中分解出一個U矩陣和一個M矩陣。最後,我們將把我們找到的U和M矩陣相乘,得到每個使用者和每部電影的評分。但是還有一個問題。以前,當我們為每個使用者和每部電影手工建立屬性時,我們知道每個屬性的含義。
我們知道第一個屬性代表動作,第二個代表劇情,等等。但是當我們使用矩陣分解來提出U和M時,我們不知道每個值是什麼意思。我們所知道的是,每個價值都代表了一些讓使用者感覺被某些電影吸引的特徵。我們不知道如何用文字來描述這些特徵。因此,U和M被稱為潛在向量。潛在的詞意味著隱藏。換句話說,這些向量是隱藏的資訊,我們通過檢視評論資料和反向推導。

2. 編寫推薦系統

我們來編寫推薦系統的主要程式碼。開啟Chapter 5/factor_review_matrix.py。
首先,我將使用pandas read_csv函式將檢查資料集載入到名為raw_dataset_df的資料集中。

raw_dataset_df = pd.read_csv('movie_ratings_data_set.csv')

然後我們使用pandas資料透視表函式來構建評論矩陣。在這一點上,ratings_df包含一個稀疏的評論陣列。

ratings_df = pd.pivot_table(raw_dataset_df, index='user_id', columns='movie_id', aggfunc=np.max)

接下來,我們希望將陣列分解以找到使用者屬性矩陣和我們可以重新乘回的電影屬性矩陣來重新建立收視率資料。
為此,我們將使用低秩矩陣分解演算法。我已經在matrix_factorization_utilities.py中包含了這個實現。我們將在下一個視訊中詳細討論它是如何工作的,但讓我們繼續使用它。首先,我們傳遞了評分資料,但是我們將呼叫pandas的as_matrix()函式,以確保我們作為一個numpy矩陣資料型別傳入。

U, M = matrix_factorization_utilities.low_rank_matrix_factorization(ratings_df.as_matrix(),
                                                                    num_features=15,
                                                                    regularization_amount=0.1)

接下來,這個方法接受一個名為num_features的引數。 Num_features控制為每個使用者和每個電影生成多少個潛在特徵。
我們將以15為起點。這個函式還有個引數regularization_amount。現在讓我們傳入0.1。在後面的文章中我們將討論如何調整這個引數。

函式的結果是U矩陣和M矩陣,每個使用者和每個電影分別具有15個屬性。現在,我們可以通過將U和M相乘來得到每部電影的評分。但不是使用常規的乘法運算子,而是使用numpy的matmul函式,所以它知道我們要做矩陣乘法。

predicted_ratings = np.matmul(U, M)

結果儲存在一個名為predicted_ratings的陣列中。最後,我們將predict_ratings儲存到一個csv檔案。

predicted_ratings_df = pd.DataFrame(index=ratings_df.index,
                                    columns=ratings_df.columns,
                                    data=predicted_ratings)

predicted_ratings_df.to_csv("predicted_ratings.csv")

首先,我們將建立一個新的pandas資料框來儲存資料。對於這個資料框,我們會告訴pandas使用與ratings_df資料框中相同的行和列名稱。然後,我們將使用pandas csv函式將資料儲存到檔案。執行這個程式後可以看到,它建立了一個名為predicted_ratings.csv的新檔案。我們可以使用任何電子表格應用程式開啟該檔案。
8688015-c52e131ac6ab9325.png

這個資料看起來就像我們原來的評論資料,現在每個單元格都填滿了。現在我們評估下每個單個使用者會為每個單獨的電影評分。例如,我們可以看到使用者3評級電影4,他們會給它一個四星級的評級。現在我們知道所有這些評分,我們可以按照評分順序向使用者推薦電影。讓我們看看使用者1號,看看我們推薦給他們的電影。在所有這些電影中,如果我們排除了使用者以前評價過的電影,右邊34號電影是最高分的電影,所以這是我們應該推薦給這個使用者的第一部電影。
當使用者觀看這部電影時,我們會要求他們評分。如果他們的評價與我們預測的不一致,我們將新增新評級並重新計算此矩陣。這將有助於我們提高整體評分。我們從中獲得的評分越多,我們的評分陣列中就會出現的孔越少,我們就有更好的機會為U和M矩陣提供準確的值。

3. 矩陣分解工作原理

因為評分矩陣等於將使用者屬性矩陣乘以電影屬性矩陣的結果,所以我們可以使用矩陣分解反向工作以找到U和M的值。在程式碼中,我們使用稱為低秩矩陣分解的演算法,去做這個。我們來看看這個演算法是如何工作的。矩陣分解是一個大矩陣可以分解成更小的矩陣的思想。所以,假設我們有一個大的數字矩陣,並且假設我們想要找到兩個更小的矩陣相乘來產生那個大的矩陣,我們的目標是找到兩個更小的矩陣來滿足這個要求。
如果您碰巧是線性代數的專家,您可能知道有一些標準的方法來對矩陣進行因式分解,比如使用一個稱為奇異值分解的過程。但是,這是有這麼一個特殊的情況下,將無法正常工作。問題是我們只知道大矩陣中的一些值。大矩陣中的許多條目是空白的,或者使用者還沒有檢查特定的電影。所以,我們不是直接將評級陣列分成兩個較小的矩陣,而是使用迭代演算法估計較小的矩陣的值。我們會猜測和檢查,直到我們接近正確的答案。
哎哎等等, 咋回事呢?首先,我們將建立U和M矩陣,但將所有值設定為隨機數。因為U和M都是隨機數,所以如果我們現在乘以U和M,結果是隨機的。下一步是檢查我們的計算評級矩陣與真實評級矩陣與U和M的當前值有多不同。但是我們將忽略評級矩陣中所有沒有資料的點,只看在我們有實際使用者評論的地方。我們將這種差異稱為成本。成本就是錯誤率。
接下來,我們將使用數字優化演算法來搜尋最小成本。數值優化演算法將一次調整U和M中的數字。目標是讓每一步的成本函式更接近於零。我們將使用的函式稱為fmin_cg。它搜尋使函式返回最小可能輸出的輸入。它由SciPy庫提供。最後,fmin_cg函式將迴圈數百次,直到我們得到儘可能小的代價。當成本函式的價值如我們所能得到的那樣低,那麼U和M的最終值就是我們將要使用的。
但是因為它們只是近似值,所以它們不會完全完美。當我們將這些U矩陣和M矩陣相乘來計算電影評級時,將其與原始電影評級進行比較,我們會看到還是有一些差異。但是隻要我們接近,少量的差異就無關緊要了。

4. 使用潛在特徵來找到類似的產品

8688015-7a7fb9a486b2d496.png

搜尋引擎是使用者發現新網站的常用方式。當第一次使用者從搜尋引擎訪問您的網站時,您對使用者尚不足以提供個性化推薦,直到使用者輸入一些產品評論時,我們的推薦系統還不能推薦他們。在這種情況下,我們可以向使用者展示與他們已經在檢視的產品類似的產品。目標是讓他們在網站上,讓他們看更多的產品。你可能在網上購物網站上看到過這個功能,如果你喜歡這個產品,你可能也會喜歡這些其他的產品。
通過使用矩陣分解計算產品屬性,我們可以計算產品相似度。讓我們來看看find_similar_products.py。首先,我們將使用pandas的讀取CSV功能載入電影評級資料集。

df = pd.read_csv('movie_ratings_data_set.csv')

movies_df = pd.read_csv('movies.csv', index_col='movie_id')

我們還會使用read_csv將movies.csv載入到名為movies_df的資料框中。

df = pd.read_csv('movie_ratings_data_set.csv')
ratings_df = pd.pivot_table(df, index='user_id', columns='movie_id', aggfunc=np.max)


U, M = matrix_factorization_utilities.low_rank_matrix_factorization(ratings_df.as_matrix(),
                                                                    num_features=15,
                                                                    regularization_amount=1.0)

然後,我們將使用pandas的資料透視表函式(pivot_table)來建立評分矩陣,我們將使用矩陣分解來計算U和M矩陣。現在,每個電影都由矩陣中的一列表示。
首先,我們使用numpy的轉置函式來觸發矩陣,使每一列變成一行。

M = np.transpose(M)

這只是使資料更容易處理,它不會改變資料本身。在矩陣中,每個電影有15個唯一的值代表該電影的特徵。這意味著其他電影幾乎相同的電影應該是非常相似的。要找到類似這個電影的其他電影,我們只需要找到其他電影的編號是最接近這部電影的數字。這只是一個減法問題。讓我們選擇使用者正在看的主要電影,讓我們選擇電影ID5。

movie_id = 5

movie_information = movies_df.loc[movie_id]

如果你喜歡,你可以選擇其他的電影。現在,我們來看看電影ID5的標題和流派。我們可以通過檢視movies_df資料框並使用pandas的loc函式通過其索引查詢行來做到這一點。讓我們列印出該電影的標題和流派。

print("We are finding movies similar to this movie:")


print("Movie title: {}".format(movie_information.title))


print("Genre: {}".format(movie_information.genre))

接下來,讓我們從矩陣中獲取電影ID為5的電影屬性。我們必須在這裡減去一個,因為M是0索引,但電影ID從1開始。現在,讓我們列印出這些電影屬性,以便我們看到它們,這些屬性我們準備好找到類似的電影。

current_movie_features = M[movie_id - 1]

print("The attributes for this movie are:")

print(current_movie_features)
# The main logic for finding similar movies:

# 1. Subtract the current movie's features from every other movie's features

difference = M - current_movie_features

# 2. Take the absolute value of that difference (so all numbers are positive)

absolute_difference = np.abs(difference)

# 3. Each movie has 15 features. Sum those 15 features to get a total 'difference score' for each movie

total_difference = np.sum(absolute_difference, axis=1)

# 4. Create a new column in the movie list with the difference score for each movie

movies_df['difference_score'] = total_difference

# 5. Sort the movie list by difference score, from least different to most different

sorted_movie_list = movies_df.sort_values('difference_score')

# 6. Print the result, showing the 5 most similar movies to movie_id #1

print("The five most similar movies are:")

print(sorted_movie_list[['title', 'difference_score']][0:5])

第一步是從其他電影中減去這部電影的屬性。這一行程式碼從矩陣的每一行中分別減去當前的電影特徵。這給了我們當前電影和資料庫中其他電影之間的分數差異。您也可以使用四個迴圈來一次減去一個電影,但使用numpy,我們可以在一行程式碼中完成。第二步是取我們在第一步計算出的差值的絕對值,numpy的ABS函式給我們絕對值,這只是確保任何負數出來都是正值。
接下來,我們將每個電影的15個單獨的屬性差異合併為一個電影的總差異分數。 numpy的總和功能將做到這一點。我們還會傳入訪問許可權等於一個來告訴numpy總結每行中的所有數字,併為每行產生一個單獨的總和。在這一點上,我們完成了計算。我們只是將計算得分儲存回電影列表中,以便我們能夠列印每部電影的名稱。在第五步中,我們按照我們計算的差異分數對電影列表進行排序,以便在列表中首先顯示最少的不同電影。
這裡pandas提供了一個方便的排序值函式。最後,在第六步中,我們列印排序列表中的前五個電影。這些是與當前電影最相似的電影。

好的,我們來執行這個程式。使用者正在看的電影被稱為大城市法官二,我們可以看到我們為這部電影計算的15個屬性。這是我們發現的五個最相似的電影。第一部電影是使用者已經看過的電影。
這是有道理的,電影將是最相似的,所以我們可以忽略第一行。接下來的四部電影是我們向使用者展示的類似專案。根據他們的頭銜,這些電影看起來可能非常相似。他們似乎都是關於犯罪和調查的電影。續集,大城市法官三,都在名單上。這是使用者可能也會感興趣的電影。您可以更改電影ID並再次執行該程式,以檢視與其他電影類似的內容。

結語

下一節將會講解如何使用推薦系統.

你的 關注-收藏-轉發 是我繼續分享的動力!

相關文章