NMF 非負矩陣分解(Non-negative Matrix Factorization)實踐

1. NMF-based 推薦算法

在例如Netflix或MovieLens這樣的推薦系統中,有用戶和電影兩個集合。給出每個用戶對部分電影的打分,希望預測該用戶對其他沒看過電影的打分值,這樣可以根據打分值爲其做出推薦。用戶和電影的關係,可以用一個矩陣來表示,每一列表示用戶,每一行表示電影,每個元素的值表示用戶對已經看過的電影的打分。下面來簡單介紹一下基於NMF的推薦算法。

在python當中有一個包叫做sklearn,專門用來做機器學習,各種大神的實現算法都在裏面。本文使用

from sklearn.decomposition import NMF


數據

電影的名稱,使用10個電影作爲例子:

item = [
    '希特勒回來了', '死侍', '房間', '龍蝦', '大空頭',
    '極盜者', '裁縫', '八惡人', '實習生', '間諜之橋',
]

用戶名稱,使用15個用戶作爲例子:

user = ['五柳君', '帕格尼六', '木村靜香', 'WTF', 'airyyouth',
        '橙子c', '秋月白', 'clavin_kong', 'olit', 'You_某人',
        '凜冬將至', 'Rusty', '噢!你看!', 'Aron', 'ErDong Chen']

用戶評分矩陣:

RATE_MATRIX = np.array(
    [[5, 5, 3, 0, 5, 5, 4, 3, 2, 1, 4, 1, 3, 4, 5],
     [5, 0, 4, 0, 4, 4, 3, 2, 1, 2, 4, 4, 3, 4, 0],
     [0, 3, 0, 5, 4, 5, 0, 4, 4, 5, 3, 0, 0, 0, 0],
     [5, 4, 3, 3, 5, 5, 0, 1, 1, 3, 4, 5, 0, 2, 4],
     [5, 4, 3, 3, 5, 5, 3, 3, 3, 4, 5, 0, 5, 2, 4],
     [5, 4, 2, 2, 0, 5, 3, 3, 3, 4, 4, 4, 5, 2, 5],
     [5, 4, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0],
     [5, 4, 3, 3, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
     [5, 4, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2],
     [5, 4, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]]
)

用戶和電影的NMF分解矩陣,其中nmf_model爲NMF的類,user_dis爲W矩陣,item_dis爲H矩陣,R設置爲2:

nmf_model = NMF(n_components=2) # 設有2個主題
item_dis = nmf_model.fit_transform(RATE_MATRIX)
user_dis = nmf_model.components_

先來看看我們的矩陣最後是什麼樣子:

print('用戶的主題分佈:')
print(user_dis)
print('電影的主題分佈:')
print(item_dis)

這裏寫圖片描述

雖然把矩陣都顯示出來了,但是仍然看着不太好觀察,於是我們可以把電影主題分佈矩陣和用戶分佈矩陣畫出來:

plt1 = plt
plt1.plot(item_dis[:, 0], item_dis[:, 1], 'ro')
plt1.draw()#直接畫出矩陣,只打了點,下面對圖plt1進行一些設置

plt1.xlim((-1, 3))
plt1.ylim((-1, 3))
plt1.title(u'the distribution of items (NMF)')#設置圖的標題

count = 1
zipitem = zip(item, item_dis)#把電影標題和電影的座標聯繫在一起

for item in zipitem:
    item_name = item[0]
    data = item[1]
    plt1.text(data[0], data[1], item_name,
              fontproperties=fontP, 
              horizontalalignment='center',
              verticalalignment='top')

這裏寫圖片描述

做到這裏,我們從上面的圖可以看出電影主題劃分出來了,使用KNN或者其他距離度量算法可以把電影分爲兩大類,也就是根據之前的NMF矩陣分解時候設定的n_components=2有關。後面對這個n_components的值進行解釋。

好我們再來看看用戶的主題劃分:

user_dis = user_dis.T #把轉置用戶分佈矩陣
plt1 = plt
plt1.plot(user_dis[:, 0], user_dis[:, 1], 'ro')
plt1.xlim((-1, 3))
plt1.ylim((-1, 3))
plt1.title(u'the distribution of user (NMF)')#設置圖的標題

zipuser = zip(user, user_dis)#把電影標題和電影的座標聯繫在一起
for user in zipuser:
    user_name = user[0]
    data = user[1]
    plt1.text(data[0], data[1], user_name,
              fontproperties=fontP, 
              horizontalalignment='center',
              verticalalignment='top')

plt1.show()#直接畫出矩陣,只打了點,下面對圖plt1進行一些設置

這裏寫圖片描述

從上圖可以看出來,用戶’五柳君’, ‘帕格尼六’, ‘木村靜香’, ‘WTF’具有類似的距離度量相似度,其餘11個用戶具有類似的距離度量相似度。

推薦

對於NMF的推薦很簡單

  • 1.求出用戶沒有評分的電影,因爲在numpy的矩陣裏面保留小數位8位,判斷是否爲零使用1e-8(後續可以方便調節參數),當然你沒有那麼嚴謹的話可以用 = 0。
  • 2.求過濾評分的新矩陣,使用NMF分解的用戶特徵矩陣和電影特徵矩陣點乘。
  • 3.求出要求得用戶沒有評分的電影列表並根據大小排列,就是最後要推薦給用戶的電影id了。
filter_matrix = RATE_MATRIX < 1e-8
rec_mat = np.dot(item_dis, user_dis)
print('重建矩陣,並過濾掉已經評分的物品:')
rec_filter_mat = (filter_matrix * rec_mat).T
print(rec_filter_mat)

rec_user = '凜冬將至'  # 需要進行推薦的用戶
rec_userid = user.index(rec_user)  # 推薦用戶ID
rec_list = rec_filter_mat[rec_userid, :]  # 推薦用戶的電影列表

print('推薦用戶的電影:')
print(np.nonzero(rec_list))

這裏寫圖片描述

通過上面結果可以看出來,推薦給用戶’凜冬將至’的電影可以有’極盜者’, ‘裁縫’, ‘八惡人’, ‘實習生’。


誤差

下面看一下分解後的誤差

a = NMF(n_components=2)  # 設有2個主題
W = a.fit_transform(RATE_MATRIX)
H = a.components_
print(a.reconstruction_err_)

b = NMF(n_components=3)  # 設有3個主題
W = b.fit_transform(RATE_MATRIX)
H = b.components_
print(b.reconstruction_err_)

c = NMF(n_components=4)  # 設有4個主題
W = c.fit_transform(RATE_MATRIX)
H = c.components_
print(c.reconstruction_err_)

d = NMF(n_components=5)  # 設有5個主題
W = d.fit_transform(RATE_MATRIX)
H = d.components_
print(d.reconstruction_err_)

上面的誤差分別是13.823891101850649, 10.478754611794432, 8.223787135382624, 6.120880939704367

在矩陣分解當中忍受誤差是有必要的,但是對於誤差的多少呢,筆者認爲通過NMF計算出來的誤差不用太着迷,更要的是看你對於主題的設置分爲多少個。很明顯的是主題越多,越接近原始的矩陣誤差越少,所以先確定好業務的需求,然後定義應該聚類的主題個數。


總結

以上雖使用NMF實現了推薦算法,但是根據Netfix的CTO所說,NMF他們很少用來做推薦,用得更多的是SVD。對於矩陣分解的推薦算法常用的有SVD、ALS、NMF。對於那種更好和對於文本推薦系統來說很重要的一點是搞清楚各種方法的內在含義啦。這裏推薦看一下《SVD和SVD++的區別》、《ALS推薦算法》、《聚類和協同過濾的區別》三篇文章(後面補上)。

好啦,簡單來說一下SVD、ALS、NMF三種算法在實際工程應用中的區別。

  • 對於一些明確的數據使用SVD(例如用戶對item 的評分)
  • 對於隱含的數據使用ALS(例如 purchase history購買歷史,watching habits瀏覽興趣 and browsing activity活躍記錄等)
  • NMF用於聚類,對聚類的結果進行特徵提取。在上面的實踐當中就是使用了聚類的方式對不同的用戶和物品進行特徵提取,剛好特徵可以看成是推薦間的相似度,所以可以用來作爲推薦算法。但是並不推薦這樣做,因爲對比起SVD來說,NMF的精確率和召回率並不顯著。

關於NMF更詳細的解釋請見我的另外一篇文章《NMF 非負矩陣分解(Non-negative Matrix Factorization)解釋》


引用
- [1] http://www.csie.ntu.edu.tw/~cjlin/nmf/ Chih-Jen Lin寫的NMF算法和關於NMF的論文,07年發的論文,極大地提升了NMF的計算過程。
- [2] https://github.com/chenzomi12/NMF-example 本文代碼
- [3] http://www.nature.com/nature/journal/v401/n6755/abs/401788a0.html神關於NMF1990論文
- [4]NMF非負矩陣分解–原理與應用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章