在貝葉斯個性化排序(BPR)算法小結中,咱們對貝葉斯個性化排序(Bayesian Personalized Ranking, 如下簡稱BPR)的原理作了討論,本文咱們將從實踐的角度來使用BPR作一個簡單的推薦。因爲現有主流開源類庫都沒有BPR,同時它又比較簡單,所以用tensorflow本身實現一個簡單的BPR的算法,下面咱們開始吧。html
BPR算法是基於矩陣分解的排序算法,它的算法訓練集是一個個的三元組\(<;u,i,j>;\),表示對用戶u來講,商品i的優先級要高於商品j。訓練成果是兩個分解後的矩陣\(W\)和\(H\),假設有m個用戶,n個物品,那麼\(W\)的維度是\(m \times k\), \(H\)的維度是\(n \times k\)。其中k是一個須要本身定義的較小的維度。對於任意一個用戶u,咱們能夠計算出它對商品i的排序評分爲\(\overline{x}_{ui} = w_u \bullet h_i\)。將u對全部物品的排序評分中找出最大的若干個,就是咱們對用戶u的真正的推薦集合。python
本文咱們基於MovieLens 100K的數據作BPR推薦示例,數據下載連接在這。這個數據集有943個用戶對1682部電影的打分。因爲BPR是排序算法,所以數據集裏的打分會被咱們忽略,主要是假設用戶看過的電影會比用戶滿意看的電影的排序評分高。因爲tensorflow須要批量梯度降低,所以咱們須要本身劃分若干批訓練集和測試集。git
下面咱們開始算法的流程,參考了github上一個較舊的BPR代碼於此,有刪改和加強。github
完整代碼參見個人github:https://github.com/ljpzzz/machinelearning/blob/master/classic-machine-learning/bpr.ipynb算法
首先是載入類庫和數據,代碼以下:微信
import numpy import tensorflow as tf import os import random from collections import defaultdict def load_data(data_path): user_ratings = defaultdict(set) max_u_id = -1 max_i_id = -1 with open(data_path, 'r') as f: for line in f.readlines(): u, i, _, _ = line.split("\t") u = int(u) i = int(i) user_ratings[u].add(i) max_u_id = max(u, max_u_id) max_i_id = max(i, max_i_id) print ("max_u_id:", max_u_id) print ("max_i_id:", max_i_id) return max_u_id, max_i_id, user_ratings data_path = os.path.join('D:\\tmp\\ml-100k', 'u.data') user_count, item_count, user_ratings = load_data(data_path)
輸出爲數據集裏面的用戶數和電影數。同時,每一個用戶看過的電影都保存在user_ratings中。session
max_u_id: 943
max_i_id: 1682
下面咱們會對每個用戶u,在user_ratings中隨機找到他評分過的一部電影i,保存在user_ratings_test,後面構造訓練集和測試集須要用到。app
def generate_test(user_ratings): user_test = dict() for u, i_list in user_ratings.items(): user_test[u] = random.sample(user_ratings[u], 1)[0] return user_test user_ratings_test = generate_test(user_ratings)
接着咱們須要獲得TensorFlow迭代用的若干批訓練集,獲取訓練集的代碼以下,主要是根據user_ratings找到若干訓練用的三元組\(<;u,i,j>;\),對於隨機抽出的用戶u,i能夠從user_ratings隨機抽出,而j也是從總的電影集中隨機抽出,固然j必須保證\((u,j)\)不出如今user_ratings中。dom
def generate_train_batch(user_ratings, user_ratings_test, item_count, batch_size=512): t = [] for b in range(batch_size): u = random.sample(user_ratings.keys(), 1)[0] i = random.sample(user_ratings[u], 1)[0] while i == user_ratings_test[u]: i = random.sample(user_ratings[u], 1)[0] j = random.randint(1, item_count) while j in user_ratings[u]: j = random.randint(1, item_count) t.append([u, i, j]) return numpy.asarray(t)
下一步是產生測試集三元組\(<;u,i,j>;\)。對於每一個用戶u,它的評分電影i是咱們在user_ratings_test中隨機抽取的,它的j是用戶u全部沒有評分過的電影集合,好比用戶u有1000部電影沒有評分,那麼這裏該用戶的測試集樣本就有1000個。函數
def generate_test_batch(user_ratings, user_ratings_test, item_count): for u in user_ratings.keys(): t = [] i = user_ratings_test[u] for j in range(1, item_count+1): if not (j in user_ratings[u]): t.append([u, i, j]) yield numpy.asarray(t)
有了訓練集和測試集,下面是用TensorFlow構建BPR算法的數據流,代碼以下,其中hidden_dim就是咱們矩陣分解的隱含維度k。user_emb_w對應矩陣\(W\), item_emb_w對應矩陣\(H\)。若是你們看過以前寫的BPR的算法原理篇,下面的損失函數的構造,相信你們都會很熟悉。
def bpr_mf(user_count, item_count, hidden_dim): u = tf.placeholder(tf.int32, [None]) i = tf.placeholder(tf.int32, [None]) j = tf.placeholder(tf.int32, [None]) with tf.device("/cpu:0"): user_emb_w = tf.get_variable("user_emb_w", [user_count+1, hidden_dim], initializer=tf.random_normal_initializer(0, 0.1)) item_emb_w = tf.get_variable("item_emb_w", [item_count+1, hidden_dim], initializer=tf.random_normal_initializer(0, 0.1)) u_emb = tf.nn.embedding_lookup(user_emb_w, u) i_emb = tf.nn.embedding_lookup(item_emb_w, i) j_emb = tf.nn.embedding_lookup(item_emb_w, j) # MF predict: u_i >; u_j x = tf.reduce_sum(tf.multiply(u_emb, (i_emb - j_emb)), 1, keep_dims=True) # AUC for one user: # reasonable iff all (u,i,j) pairs are from the same user # # average AUC = mean( auc for each user in test set) mf_auc = tf.reduce_mean(tf.to_float(x >; 0)) l2_norm = tf.add_n([ tf.reduce_sum(tf.multiply(u_emb, u_emb)), tf.reduce_sum(tf.multiply(i_emb, i_emb)), tf.reduce_sum(tf.multiply(j_emb, j_emb)) ]) regulation_rate = 0.0001 bprloss = regulation_rate * l2_norm - tf.reduce_mean(tf.log(tf.sigmoid(x))) train_op = tf.train.GradientDescentOptimizer(0.01).minimize(bprloss) return u, i, j, mf_auc, bprloss, train_op
有了算法的數據流圖,訓練集和測試集也有了,如今咱們能夠訓練模型求解\(W,H\)這兩個矩陣了,注意咱們在原理篇是最大化對數後驗估計函數, 而這裏是最小化取了負號後對應的對數後驗估計函數,實際是同樣的。代碼以下:
with tf.Graph().as_default(), tf.Session() as session: u, i, j, mf_auc, bprloss, train_op = bpr_mf(user_count, item_count, 20) session.run(tf.initialize_all_variables()) for epoch in range(1, 4): _batch_bprloss = 0 for k in range(1, 5000): # uniform samples from training set uij = generate_train_batch(user_ratings, user_ratings_test, item_count) _bprloss, _train_op = session.run([bprloss, train_op], feed_dict={u:uij[:,0], i:uij[:,1], j:uij[:,2]}) _batch_bprloss += _bprloss print ("epoch: ", epoch) print ("bpr_loss: ", _batch_bprloss / k) print ("_train_op") user_count = 0 _auc_sum = 0.0 # each batch will return only one user's auc for t_uij in generate_test_batch(user_ratings, user_ratings_test, item_count): _auc, _test_bprloss = session.run([mf_auc, bprloss], feed_dict={u:t_uij[:,0], i:t_uij[:,1], j:t_uij[:,2]} ) user_count += 1 _auc_sum += _auc print ("test_loss: ", _test_bprloss, "test_auc: ", _auc_sum/user_count) print ("") variable_names = [v.name for v in tf.trainable_variables()] values = session.run(variable_names) for k,v in zip(variable_names, values): print("Variable: ", k) print("Shape: ", v.shape) print(v)
這裏我k取了20, 迭代次數3, 主要是爲了快速輸出結果。若是要作一個較好的BPR算法,須要對k值進行選擇迭代,而且迭代次數也要更多一些。這裏個人輸出以下,供參考。
epoch: 1
bpr_loss: 0.7236263042427249
_train_op
test_loss: 0.76150036 test_auc: 0.4852939894020929
epoch: 2
bpr_loss: 0.7229681559433149
_train_op
test_loss: 0.76061743 test_auc: 0.48528061393838007
epoch: 3
bpr_loss: 0.7223725006756341
_train_op
test_loss: 0.7597519 test_auc: 0.4852617720521252
Variable: user_emb_w:0
Shape: (944, 20)
[[ 0.08105529 0.04270628 -0.12196594 ... 0.02729403 0.1556453
-0.07148876]
[ 0.0729574 0.01720054 -0.08198593 ... 0.05565814 -0.0372898
0.11935959]
[ 0.03591165 -0.11786834 0.04123168 ... 0.06533947 0.11889934
-0.19697346]
...
[-0.05796075 -0.00695129 0.07784595 ... -0.03869986 0.10723818
0.01293885]
[ 0.13237114 -0.07055715 -0.05505611 ... 0.16433473 0.04535925
0.0701588 ]
[-0.2069717 0.04607181 0.07822093 ... 0.03704183 0.07326393
0.06110878]]
Variable: item_emb_w:0
Shape: (1683, 20)
[[ 0.09130769 -0.16516572 0.06490657 ... 0.03657753 -0.02265425
0.1437734 ]
[ 0.02463264 0.13691436 -0.01713235 ... 0.02811887 0.00262074
0.08854961]
[ 0.00643777 0.02678963 0.04300125 ... 0.03529688 -0.11161
0.11927075]
...
[ 0.05260892 -0.03204868 -0.06910443 ... 0.03732759 -0.03459863
-0.05798787]
[-0.07953933 -0.10924194 0.11368059 ... 0.06346208 -0.03269136
-0.03078123]
[ 0.03460099 -0.10591184 -0.1008586 ... -0.07162578 0.00252131
0.06791534]]
如今咱們已經獲得了\(W,H\)矩陣,就能夠對任意一個用戶u的評分排序了。注意輸出的\(W,H\)矩陣分別在values[0]和values[1]中。
那麼咱們如何才能對某個用戶推薦呢?這裏咱們以第一個用戶爲例,它在\(W\)中對應的\(w_0\)向量爲value[0][0],那麼咱們很容易求出這個用戶對全部電影的預測評分, 代碼以下:
session1 = tf.Session() u1_dim = tf.expand_dims(values[0][0], 0) u1_all = tf.matmul(u1_dim, values[1],transpose_b=True) result_1 = session1.run(u1_all) print (result_1)
輸出爲一個評分向量:
[[-0.01707731 0.06217583 -0.01760234 ... 0.067231 0.08989487
-0.05628442]]
如今給第一個用戶推薦5部電影,代碼以下:
print("如下是給用戶0的推薦:") p = numpy.squeeze(result_1) p[numpy.argsort(p)[:-5]] = 0 for index in range(len(p)): if p[index] != 0: print (index, p[index])
輸出以下:
如下是給用戶0的推薦:
54 0.1907271
77 0.17746378
828 0.17181025
1043 0.16989286
1113 0.17458326
以上就是用tensorflow來構建BPR算法模型,並用該算法模型作movieLens 100K推薦的過程。實際作產品項目中,若是要用到BPR算法,一是要注意對隱藏維度k的調參,另外儘可能多迭代一些輪數。
另外咱們能夠在BPR損失函數那一塊作文章。好比咱們能夠對\(\overline{x}_{uij} = \overline{x}_{ui} - \overline{x}_{uj}\)這個式子作改進,加上一個基於評分時間的衰減係數,這樣咱們的排序推薦還能夠考慮時間等其餘因素。
以上就是用tensorflow學習BPR的所有內容。
(歡迎轉載,轉載請註明出處。歡迎溝通交流: 微信:nickchen121)