基於矩陣分解的電影推薦演算法(使用Tensorflow實現)
阿新 • • 發佈:2019-01-28
#!/usr/bin/env python # -*- coding:utf-8 -*- # 文中部分參考了: # https://blog.csdn.net/u012845311/article/details/77183491 # 改進:要劃分訓練集和測試集,並在進行模型評估階段,可參考之前做過的協同過濾推薦系統中的, # 通過prediction[ground_truth.nonzero()]來篩選只考慮測試資料集中的預測評分 import pandas as pd import numpy as np import tensorflow as tf # 第一步:------------------------收集和清洗資料 ratings_df = pd.read_csv('F:\Machine\data_sets\ml-latest-small/ratings.csv') # print(ratings_df.tail()) # tail命令用於輸入檔案中的尾部內容。tail命令預設在螢幕上顯示指定檔案的末尾5行。 # 相對應的有:ratings_df.head() movies_df = pd.read_csv('F:\Machine\data_sets\ml-latest-small/movies.csv') movies_df['movieRow'] = movies_df.index # 生成一列‘movieRow’,等於索引值index # print(movies_df.tail()) movies_df = movies_df[['movieRow', 'movieId', 'title']] # 篩選三列出來 movies_df.to_csv('F:\Machine\data_sets\ml-latest-small/moviesProcessed.csv', index=False, header=True, encoding='utf-8') # 生成一個新的檔案moviesProcessed.csv print(movies_df.tail()) ratings_df = pd.merge(ratings_df, movies_df, on='movieId') # print(ratings_df.head()) ratings_df = ratings_df[['userId', 'movieRow', 'rating']] # 篩選出三列 ratings_df.to_csv('F:\Machine\data_sets\ml-latest-small/ratingsProcessed.csv', index=False, header=True, encoding='utf-8') # 匯出一個新的檔案ratingsProcessed.csv print(ratings_df.head()) # 第二步:-----------------------建立電影評分矩陣rating和評分紀錄矩陣record userNo = ratings_df['userId'].max() + 1 # userNo的最大值 movieNo = ratings_df['movieRow'].max() + 1 # movieNo的最大值 rating = np.zeros((movieNo, userNo)) print(rating.shape) # 建立一個值都是0的資料 flag = 0 ratings_df_length = np.shape(ratings_df)[0] print(np.shape(ratings_df)) # 檢視矩陣ratings_df的第一維度是多少 for index, row in ratings_df.iterrows(): # interrows(),對錶格ratings_df進行遍歷 # rating[int(row['movieRow']), int(row['userId'])] = row['rating'] # 等價於: rating[int(row['movieRow'])][int(row['userId'])] = row['rating'] # 在rating表裡的'movieRow'行和'userId'列處,填上row的‘評分’,即ratings_df對應的評分 flag += 1 # if (ratings_df_length-flag) % 5000 == 0: # print(u'還剩多少待處理:%d' %(ratings_df_length-flag)) # print(rating[3][450]) record = rating > 0 record = np.array(record, dtype=int) print(record) # 第三步:----------------------------構建模型 def normalizeRatings(rating, record): m, n = rating.shape #m代表電影數量,n代表使用者數量 rating_mean = np.zeros((m, 1)) #每部電影的平均得分 rating_norm = np.zeros((m, n)) #處理過的評分 for i in range(m): idx = (record[i, :] != 0) #每部電影的評分,[i,:]表示每一行的所有列 rating_mean[i] = np.mean(rating[i, idx]) # 第i行,評過份idx的使用者的平均得分 # np.mean() 對所有元素求均值 rating_norm[i, idx] = rating[i, idx] - rating_mean[i] #rating_norm = 原始得分-平均得分 return rating_norm, rating_mean rating_norm, rating_mean = normalizeRatings(rating, record) rating_norm = np.nan_to_num(rating_norm) # 對值為NaNN進行處理,改成數值0 # print(rating_norm) rating_mean = np.nan_to_num(rating_mean) # 對值為NaNN進行處理,改成數值0 # print(rating_mean) # 構建模型 num_features = 12 X_parameters = tf.Variable(tf.random_normal([movieNo, num_features], stddev = 0.35)) Theta_parameters = tf.Variable(tf.random_normal([userNo, num_features], stddev = 0.35)) # tf.Variables()初始化變數 # tf.random_normal()函式用於從服從指定正太分佈的數值中取出指定個數的值,mean: 正態分佈的均值。stddev: 正態分佈的標準差。dtype: 輸出的型別 loss = 1/2 * tf.reduce_sum(((tf.matmul(X_parameters, Theta_parameters, transpose_b=True) - rating_norm) * record) ** 2) + \ 0.5*(1/2 * (tf.reduce_sum(X_parameters ** 2) + tf.reduce_sum(Theta_parameters ** 2))) # 基於內容的推薦演算法模型 train = tf.train.AdamOptimizer(1e-3).minimize(loss) # https://blog.csdn.net/lenbow/article/details/52218551 # Optimizer.minimize對一個損失變數基本上做兩件事 # 它計算相對於模型引數的損失梯度。 # 然後應用計算出的梯度來更新變數。 # 第四步:------------------------------------訓練模型 # tf.summary的用法 https://www.cnblogs.com/lyc-seu/p/8647792.html tf.summary.scalar('train_loss', loss) # 用來顯示標量資訊 summaryMerged = tf.summary.merge_all() # merge_all 可以將所有summary全部儲存到磁碟,以便tensorboard顯示。 filename = 'F:\Machine\data_sets\ml-latest-small/movie_tensorborad.csv' writer = tf.summary.FileWriter(filename) # 指定一個檔案用來儲存圖。 sess = tf.Session() # https://www.cnblogs.com/wuzhitj/p/6648610.html init = tf.global_variables_initializer() sess.run(init) # 執行 for i in range(2000): _, movie_summary = sess.run([train, summaryMerged]) # 把訓練的結果summaryMerged存在movie裡 writer.add_summary(movie_summary, i) # 把訓練的結果儲存下來 # 第五步:-------------------------------------評估模型 Current_X_parameters, Current_Theta_parameters = sess.run([X_parameters, Theta_parameters]) # Current_X_parameters為電影內容矩陣,Current_Theta_parameters使用者喜好矩陣 predicts = np.dot(Current_X_parameters, Current_Theta_parameters.T) + rating_mean # dot函式是np中的矩陣乘法,np.dot(x,y) 等價於 x.dot(y) errors = np.sqrt(np.sum(((predicts - rating) * record)**2)) # sqrt(arr) ,計算各元素的平方根 print(u'模型評估errors:', errors) # 第六步:--------------------------------------構建完整的電影推薦系統 user_id = input(u'您要想哪位使用者進行推薦?請輸入使用者編號:') sortedResult = predicts[:, int(user_id)].argsort()[::-1] # argsort()函式返回的是陣列值從小到大的索引值; argsort()[::-1] 返回的是陣列值從大到小的索引值 print(u'為該使用者推薦的評分最高的20部電影是:'.center(80, '=')) # center() 返回一個原字串居中,並使用空格填充至長度 width 的新字串。預設填充字元為空格。 idx = 0 for i in sortedResult: print(u'評分: %.2f, 電影名: %s' % (predicts[i, int(user_id)]-2, movies_df.iloc[i]['title'])) # .iloc的用法:https://www.cnblogs.com/harvey888/p/6006200.html idx += 1 if idx == 20: break 效果展示: