練習題︱ python 協同過濾ALS模型實現:商品推薦 + 用戶人羣放大

之前的一個練習題:練習題︱豆瓣圖書的推薦與搜索、簡易版知識引擎構建(neo4j)提及了幾種簡單的推薦方式。
但是在超大規模稀疏數據上,一般會採用一些規模化的模型,譬如spark-ALS就是其中一款。
這邊,筆者也是想調研一下這個模型的操作性,所有就先用單機版的測試一下;對應的spark.mlib有分佈式的版本。

練習代碼可見:mattzheng/pyALS



1 ALS算法 - Alternating Least Square - 交替最小二乘法

1.1 理論介紹

參考:
在線圖書推薦系統的實現含源碼(協同過濾)
如何解釋spark mllib中ALS算法的原理?

是協同過濾的一種,並被集成到Spark的Mllib庫中。
對於一個users-products-rating的評分數據集,ALS會建立一個userproduct的mn的矩陣其中,m爲users的數量,n爲products的數量但是在這個數據集中,並不是每個用戶都對每個產品進行過評分,所以這個矩陣往往是稀疏的,
用戶i對產品j的評分往往是空的ALS所做的事情就是將這個稀疏矩陣通過一定的規律填滿,這樣就可以從矩陣中得到任意一個user對任意一個product的評分,ALS填充的評分項也稱爲用戶i對產品j的預測得分所以說,ALS算法的核心就是通過什麼樣子的規律來填滿。

在這裏插入圖片描述
矩陣因子分解(如奇異值分解,奇異值分解+ +)將項和用戶都轉化成了相同的潛在空間,它所代表了用戶和項之間的潛相互作用。矩陣分解背後的原理是潛在特徵代表了用戶如何給項進行評分。給定用戶和項的潛在描述,我們可以預測用戶將會給還未評價的項多少評分。
在這裏插入圖片描述

優勢:

  • 支持訓練
  • 不用輸入一個超大matrix矩陣

劣勢:

  • 不支持全新的內容輸入,用戶ID只能是現有的
  • 增量訓練不支持

輸入訓練:

# [[1, 1, 4.0], [1, 3, 4.0], [1, 6, 4.0], [1, 47, 5.0], [1, 50, 5.0]]  
# (用戶ID,購物ID,評分)

全部應用用戶ID/購物ID即可。

關於增量訓練:
在文章在線圖書推薦系統的實現含源碼(協同過濾)中是,我們借用Spark的ALS算法的訓練和預測函數,每次收到新的數據後,將其更新到訓練數據集中,然後更新ALS訓練得到的模型。
感覺是全部重新訓練?

1.2 58同城的推薦場景實戰

相對來說,在一些推薦場景該方法還是有一定效力的【參考:Embedding技術在房產推薦中的應用】:
在這裏插入圖片描述
在這些推薦場景中都離不開兩類相似性的計算:

  • 一類是用戶和房源之間的相關性
  • 另一類是兩個房源之間的相關性。

具體該怎麼計算這兩類相關性呢?我們首先需要把房源和用戶這兩個實體用向量表徵出來,然後通過計算向量的差異,衡量用戶和房源、房源和房源是否相似。

用戶矩陣和評分矩陣都有“豪華指數”和“剛需指數”這兩個維度。當然這兩個維度的表述是我們在矩陣分解完成之後,人爲總結的。其實,用戶矩陣和物品矩陣可以理解爲針對用戶和房源的Embedding。從用戶矩陣中可以看出,User1對豪宅的偏好度比較高,所以他對耀華路550弄不太感興趣。同時,從物品矩陣中可以看出,湯臣一品和上海康城的相似度應該是大於湯臣一品和耀華路550弄的相似度。
在這裏插入圖片描述


2 pyALS

這邊感謝 協同過濾(ALS)的原理及Python實現手寫了一個版本,可以便於做小規模的測試als.py
這邊筆者在此基礎上進行了一些測試性工作。
訓練步驟:

  • 數據預處理
  • 變量k合法性檢查
  • 生成隨機矩陣U
  • 交替計算矩陣U和矩陣I,並打印RMSE信息,直到迭代次數達到max_iter
  • 保存最終的RMSE

2.1 商品推薦

所使用的數據是【用戶ID,電影ID,評分數據】
在這裏插入圖片描述
先是訓練環節:

# 加載數據
path = 'data/movie_ratings.csv'
X = load_movie_ratings(path) # 100836
# [[1, 1, 4.0], [1, 3, 4.0], [1, 6, 4.0], [1, 47, 5.0], [1, 50, 5.0]]  
# (用戶ID,購物ID,評分)

# 訓練模型
from ALS.pyALS import ALS
model = ALS()
model.fit(X, k=20, max_iter=2)

>>> Iterations: 1, RMSE: 3.207636
>>> Iterations: 2, RMSE: 0.353680

其中X的格式是一個有序的列表性,K代表表徵的維度,max_iter表示迭代次數。
k / max_iter越大迭代的時間就越長。

然後是預測:

# 商品推薦
print("Showing the predictions of users...")
# Predictions
user_ids = range(1, 5)
predictions = model.predict(user_ids, n_items=2)
for user_id, prediction in zip(user_ids, predictions):
    _prediction = [format_prediction(item_id, score)
                   for item_id, score in prediction]
    print("User id:%d recommedation: %s" % (user_id, _prediction))

在這裏插入圖片描述

2.2 人羣放大

這個模塊其實實驗是藉助user的embedding作爲用戶向量來求解相似性高的人羣。
大致的操作步驟爲:

  • 先將訓練得到的用戶user_embedding 和商品的item_embedding都進行.txt保存
  • gensim加載
  • 求人羣相似

這裏筆者偷懶,直接藉助gensim來進行相似性求解。

# 將用戶矩陣+商品矩陣,像word2vec一樣進行保存
user_matrix = np.array(model.user_matrix.data)
item_matrix = np.array(model.item_matrix.data)
print(user_matrix.shape,item_matrix.shape) # ((20, 610), (20, 9724))

user_embedding = {model.user_ids[n]:user_matrix.T[n] for n in range(len(model.user_ids))}
item_embedding = {model.item_ids[n]:item_matrix.T[n] for n in range(len(model.user_ids))}


wordvec_save2txt(user_embedding,save_path = 'w2v/user_embedding_10w_50k_10i.txt',encoding = 'utf-8-sig')
wordvec_save2txt(item_embedding,save_path = 'w2v/item_embedding_10w_50k_10i.txt',encoding = 'utf-8-sig')

然後根據此用戶向量來求解相似用戶

embedding = gensim.models.KeyedVectors.load_word2vec_format('w2v/user_embedding_10w_50k_10i.txt',binary=False)
embedding.init_sims(replace=True)  # 神奇,很省內存,可以運算most_similar

# 向量求相似
item_a = 1
simi = embedding.most_similar(str(item_a), topn=50)
#[('79', 0.9031778573989868),
# ('27', 0.882379412651062)]

當然這裏單個人求解在實際使用上不可行,因爲種子人羣 / 總人羣量級都比較大,所以一開始會需要聚類。
關於Look - Like還會有很多內容需要注意。
在這裏插入圖片描述

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