TensorFlow实战——深度学习训练个性化推荐系统
請(qǐng)安裝TensorFlow1.0,Python3.5
項(xiàng)目地址:https://github.com/xiaobingchan/movie_recommender
前言
本項(xiàng)目使用文本卷積神經(jīng)網(wǎng)絡(luò),并使用MovieLens數(shù)據(jù)集完成電影推薦的任務(wù)。
推薦系統(tǒng)在日常的網(wǎng)絡(luò)應(yīng)用中無(wú)處不在,比如網(wǎng)上購(gòu)物、網(wǎng)上買(mǎi)書(shū)、新聞app、社交網(wǎng)絡(luò)、音樂(lè)網(wǎng)站、電影網(wǎng)站等等等等,有人的地方就有推薦。根據(jù)個(gè)人的喜好,相同喜好人群的習(xí)慣等信息進(jìn)行個(gè)性化的內(nèi)容推薦。比如打開(kāi)新聞?lì)惖腶pp,因?yàn)橛辛藗€(gè)性化的內(nèi)容,每個(gè)人看到的新聞首頁(yè)都是不一樣的。
這當(dāng)然是很有用的,在信息爆炸的今天,獲取信息的途徑和方式多種多樣,人們花費(fèi)時(shí)間最多的不再是去哪獲取信息,而是要在眾多的信息中尋找自己感興趣的,這就是信息超載問(wèn)題。為了解決這個(gè)問(wèn)題,推薦系統(tǒng)應(yīng)運(yùn)而生。
協(xié)同過(guò)濾是推薦系統(tǒng)應(yīng)用較廣泛的技術(shù),該方法搜集用戶的歷史記錄、個(gè)人喜好等信息,計(jì)算與其他用戶的相似度,利用相似用戶的評(píng)價(jià)來(lái)預(yù)測(cè)目標(biāo)用戶對(duì)特定項(xiàng)目的喜好程度。優(yōu)點(diǎn)是會(huì)給用戶推薦未瀏覽過(guò)的項(xiàng)目,缺點(diǎn)呢,對(duì)于新用戶來(lái)說(shuō),沒(méi)有任何與商品的交互記錄和個(gè)人喜好等信息,存在冷啟動(dòng)問(wèn)題,導(dǎo)致模型無(wú)法找到相似的用戶或商品。
為了解決冷啟動(dòng)的問(wèn)題,通常的做法是對(duì)于剛注冊(cè)的用戶,要求用戶先選擇自己感興趣的話題、群組、商品、性格、喜歡的音樂(lè)類(lèi)型等信息,比如豆瓣FM:
先來(lái)看看數(shù)據(jù)
本項(xiàng)目使用的是MovieLens 1M 數(shù)據(jù)集,包含6000個(gè)用戶在近4000部電影上的1億條評(píng)論。
數(shù)據(jù)集分為三個(gè)文件:用戶數(shù)據(jù)users.dat,電影數(shù)據(jù)movies.dat和評(píng)分?jǐn)?shù)據(jù)ratings.dat。
用戶數(shù)據(jù)
分別有用戶ID、性別、年齡、職業(yè)ID和郵編等字段。
數(shù)據(jù)中的格式:UserID::Gender::Age::Occupation::Zip-code
Gender is denoted by a “M” for male and “F” for female
Age is chosen from the following ranges:
1: “Under 18”
18: “18-24”
25: “25-34”
35: “35-44”
45: “45-49”
50: “50-55”
56: “56+”
Occupation is chosen from the following choices:
0: “other” or not specified
1: “academic/educator”
2: “artist”
3: “clerical/admin”
4: “college/grad student”
5: “customer service”
6: “doctor/health care”
7: “executive/managerial”
8: “farmer”
9: “homemaker”
10: “K-12 student”
11: “l(fā)awyer”
12: “programmer”
13: “retired”
14: “sales/marketing”
15: “scientist”
16: “self-employed”
17: “technician/engineer”
18: “tradesman/craftsman”
19: “unemployed”
20: “writer”
其中UserID、Gender、Age和Occupation都是類(lèi)別字段,其中郵編字段是我們不使用的。
電影數(shù)據(jù)
分別有電影ID、電影名和電影風(fēng)格等字段。
數(shù)據(jù)中的格式:MovieID::Title::Genres
Titles are identical to titles provided by the IMDB (including
year of release)
Genres are pipe-separated and are selected from the following genres:
Action
Adventure
Animation
Children’s
Comedy
Crime
Documentary
Drama
Fantasy
Film-Noir
Horror
Musical
Mystery
Romance
Sci-Fi
Thriller
War
Western
MovieID是類(lèi)別字段,Title是文本,Genres也是類(lèi)別字段
評(píng)分?jǐn)?shù)據(jù)
分別有用戶ID、電影ID、評(píng)分和時(shí)間戳等字段。
數(shù)據(jù)中的格式:UserID::MovieID::Rating::Timestamp
UserIDs range between 1 and 6040
MovieIDs range between 1 and 3952
Ratings are made on a 5-star scale (whole-star ratings only)
Timestamp is represented in seconds since the epoch as returned by time(2)
Each user has at least 20 ratings
評(píng)分字段Rating就是我們要學(xué)習(xí)的targets,時(shí)間戳字段我們不使用。
說(shuō)說(shuō)數(shù)據(jù)預(yù)處理
UserID、Occupation和MovieID不用變。
Gender字段:需要將‘F’和‘M’轉(zhuǎn)換成0和1。
Age字段:要轉(zhuǎn)成7個(gè)連續(xù)數(shù)字0~6。
Genres字段:是分類(lèi)字段,要轉(zhuǎn)成數(shù)字。首先將Genres中的類(lèi)別轉(zhuǎn)成字符串到數(shù)字的字典,然后再將每個(gè)電影的Genres字段轉(zhuǎn)成數(shù)字列表,因?yàn)橛行╇娪笆嵌鄠€(gè)Genres的組合。
Title字段:處理方式跟Genres字段一樣,首先創(chuàng)建文本到數(shù)字的字典,然后將Title中的描述轉(zhuǎn)成數(shù)字的列表。另外Title中的年份也需要去掉。
Genres和Title字段需要將長(zhǎng)度統(tǒng)一,這樣在神經(jīng)網(wǎng)絡(luò)中方便處理。空白部分用‘< PAD >’對(duì)應(yīng)的數(shù)字填充。
數(shù)據(jù)預(yù)處理的代碼可以在項(xiàng)目中找到:load_data函數(shù)
模型設(shè)計(jì)
通過(guò)研究數(shù)據(jù)集中的字段類(lèi)型,我們發(fā)現(xiàn)有一些是類(lèi)別字段,通常的處理是將這些字段轉(zhuǎn)成one hot編碼,但是像UserID、MovieID這樣的字段就會(huì)變成非常的稀疏,輸入的維度急劇膨脹,這是我們不愿意見(jiàn)到的,畢竟我這小筆記本不像大廠動(dòng)輒能處理數(shù)以億計(jì)維度的輸入:)
所以在預(yù)處理數(shù)據(jù)時(shí)將這些字段轉(zhuǎn)成了數(shù)字,我們用這個(gè)數(shù)字當(dāng)做嵌入矩陣的索引,在網(wǎng)絡(luò)的第一層使用了嵌入層,維度是(N,32)和(N,16)。
電影類(lèi)型的處理要多一步,有時(shí)一個(gè)電影有多個(gè)電影類(lèi)型,這樣從嵌入矩陣索引出來(lái)是一個(gè)(n,32)的矩陣,因?yàn)橛卸鄠€(gè)類(lèi)型嘛,我們要將這個(gè)矩陣求和,變成(1,32)的向量。
電影名的處理比較特殊,沒(méi)有使用循環(huán)神經(jīng)網(wǎng)絡(luò),而是用了文本卷積網(wǎng)絡(luò),下文會(huì)進(jìn)行說(shuō)明。
從嵌入層索引出特征以后,將各特征傳入全連接層,將輸出再次傳入全連接層,最終分別得到(1,200)的用戶特征和電影特征兩個(gè)特征向量。
我們的目的就是要訓(xùn)練出用戶特征和電影特征,在實(shí)現(xiàn)推薦功能時(shí)使用。得到這兩個(gè)特征以后,就可以選擇任意的方式來(lái)擬合評(píng)分了。我使用了兩種方式,一個(gè)是上圖中畫(huà)出的將兩個(gè)特征做向量乘法,將結(jié)果與真實(shí)評(píng)分做回歸,采用MSE優(yōu)化損失。因?yàn)楸举|(zhì)上這是一個(gè)回歸問(wèn)題,另一種方式是,將兩個(gè)特征作為輸入,再次傳入全連接層,輸出一個(gè)值,將輸出值回歸到真實(shí)評(píng)分,采用MSE優(yōu)化損失。
實(shí)際上第二個(gè)方式的MSE loss在0.8附近,第一個(gè)方式在1附近,5次迭代的結(jié)果。
文本卷積網(wǎng)絡(luò)
網(wǎng)絡(luò)看起來(lái)像下面這樣
圖片來(lái)自Kim Yoon的論文:Convolutional Neural Networks for Sentence Classification
將卷積神經(jīng)網(wǎng)絡(luò)用于文本的文章建議你閱讀Understanding Convolutional Neural Networks for NLP
網(wǎng)絡(luò)的第一層是詞嵌入層,由每一個(gè)單詞的嵌入向量組成的嵌入矩陣。下一層使用多個(gè)不同尺寸(窗口大小)的卷積核在嵌入矩陣上做卷積,窗口大小指的是每次卷積覆蓋幾個(gè)單詞。這里跟對(duì)圖像做卷積不太一樣,圖像的卷積通常用2x2、3x3、5x5之類(lèi)的尺寸,而文本卷積要覆蓋整個(gè)單詞的嵌入向量,所以尺寸是(單詞數(shù),向量維度),比如每次滑動(dòng)3個(gè),4個(gè)或者5個(gè)單詞。第三層網(wǎng)絡(luò)是max pooling得到一個(gè)長(zhǎng)向量,最后使用dropout做正則化,最終得到了電影Title的特征。
核心代碼講解
完整代碼請(qǐng)見(jiàn)項(xiàng)目
#嵌入矩陣的維度
embed_dim = 32
#用戶ID個(gè)數(shù)
uid_max = max(features.take(0,1)) + 1 # 6040
#性別個(gè)數(shù)
gender_max = max(features.take(2,1)) + 1 # 1 + 1 = 2
#年齡類(lèi)別個(gè)數(shù)
age_max = max(features.take(3,1)) + 1 # 6 + 1 = 7
#職業(yè)個(gè)數(shù)
job_max = max(features.take(4,1)) + 1# 20 + 1 = 21
#電影ID個(gè)數(shù)
movie_id_max = max(features.take(1,1)) + 1 # 3952
#電影類(lèi)型個(gè)數(shù)
movie_categories_max = max(genres2int.values()) + 1 # 18 + 1 = 19
#電影名單詞個(gè)數(shù)
movie_title_max = len(title_set) # 5216
#對(duì)電影類(lèi)型嵌入向量做加和操作的標(biāo)志,考慮過(guò)使用mean做平均,但是沒(méi)實(shí)現(xiàn)mean
combiner = "sum"
#電影名長(zhǎng)度
sentences_size = title_count # = 15
#文本卷積滑動(dòng)窗口,分別滑動(dòng)2, 3, 4, 5個(gè)單詞
window_sizes = {2, 3, 4, 5}
#文本卷積核數(shù)量
filter_num = 8
#電影ID轉(zhuǎn)下標(biāo)的字典,數(shù)據(jù)集中電影ID跟下標(biāo)不一致,比如第5行的數(shù)據(jù)電影ID不一定是5
movieid2idx = {val[0]:i for i, val in enumerate(movies.values)}
超參
# Number of Epochs
num_epochs = 5
# Batch Size
batch_size = 256
dropout_keep = 0.5
# Learning Rate
learning_rate = 0.0001
# Show stats for every n number of batches
show_every_n_batches = 20
save_dir = './save'
輸入
定義輸入的占位符
def get_inputs():
? ? uid = tf.placeholder(tf.int32, [None, 1], name="uid")
? ? user_gender = tf.placeholder(tf.int32, [None, 1], name="user_gender")
? ? user_age = tf.placeholder(tf.int32, [None, 1], name="user_age")
? ? user_job = tf.placeholder(tf.int32, [None, 1], name="user_job")
? ? movie_id = tf.placeholder(tf.int32, [None, 1], name="movie_id")
? ? movie_categories = tf.placeholder(tf.int32, [None, 18], name="movie_categories")
? ? movie_titles = tf.placeholder(tf.int32, [None, 15], name="movie_titles")
? ? targets = tf.placeholder(tf.int32, [None, 1], name="targets")
? ? LearningRate = tf.placeholder(tf.float32, name = "LearningRate")
? ? dropout_keep_prob = tf.placeholder(tf.float32, name = "dropout_keep_prob")
? ? return uid, user_gender, user_age, user_job, movie_id, movie_categories, movie_titles, targets, LearningRate, dropout_keep_prob
構(gòu)建神經(jīng)網(wǎng)絡(luò)
定義User的嵌入矩陣
def get_user_embedding(uid, user_gender, user_age, user_job):
? ? with tf.name_scope("user_embedding"):
? ? ? ? uid_embed_matrix = tf.Variable(tf.random_uniform([uid_max, embed_dim], -1, 1), name = "uid_embed_matrix")
? ? ? ? uid_embed_layer = tf.nn.embedding_lookup(uid_embed_matrix, uid, name = "uid_embed_layer")
? ? ? ? gender_embed_matrix = tf.Variable(tf.random_uniform([gender_max, embed_dim // 2], -1, 1), name= "gender_embed_matrix")
? ? ? ? gender_embed_layer = tf.nn.embedding_lookup(gender_embed_matrix, user_gender, name = "gender_embed_layer")
? ? ? ? age_embed_matrix = tf.Variable(tf.random_uniform([age_max, embed_dim // 2], -1, 1), name="age_embed_matrix")
? ? ? ? age_embed_layer = tf.nn.embedding_lookup(age_embed_matrix, user_age, name="age_embed_layer")
? ? ? ? job_embed_matrix = tf.Variable(tf.random_uniform([job_max, embed_dim // 2], -1, 1), name = "job_embed_matrix")
? ? ? ? job_embed_layer = tf.nn.embedding_lookup(job_embed_matrix, user_job, name = "job_embed_layer")
? ? return uid_embed_layer, gender_embed_layer, age_embed_layer, job_embed_layer
將User的嵌入矩陣一起全連接生成User的特征
def get_user_feature_layer(uid_embed_layer, gender_embed_layer, age_embed_layer, job_embed_layer):
? ? with tf.name_scope("user_fc"):
? ? ? ? #第一層全連接
? ? ? ? uid_fc_layer = tf.layers.dense(uid_embed_layer, embed_dim, name = "uid_fc_layer", activation=tf.nn.relu)
? ? ? ? gender_fc_layer = tf.layers.dense(gender_embed_layer, embed_dim, name = "gender_fc_layer", activation=tf.nn.relu)
? ? ? ? age_fc_layer = tf.layers.dense(age_embed_layer, embed_dim, name ="age_fc_layer", activation=tf.nn.relu)
? ? ? ? job_fc_layer = tf.layers.dense(job_embed_layer, embed_dim, name = "job_fc_layer", activation=tf.nn.relu)
? ? ? ? #第二層全連接
? ? ? ? user_combine_layer = tf.concat([uid_fc_layer, gender_fc_layer, age_fc_layer, job_fc_layer], 2) ?#(?, 1, 128)
? ? ? ? user_combine_layer = tf.contrib.layers.fully_connected(user_combine_layer, 200, tf.tanh) ?#(?, 1, 200)
? ? ? ? user_combine_layer_flat = tf.reshape(user_combine_layer, [-1, 200])
? ? return user_combine_layer, user_combine_layer_flat
定義Movie ID的嵌入矩陣
def get_movie_id_embed_layer(movie_id):
? ? with tf.name_scope("movie_embedding"):
? ? ? ? movie_id_embed_matrix = tf.Variable(tf.random_uniform([movie_id_max, embed_dim], -1, 1), name = "movie_id_embed_matrix")
? ? ? ? movie_id_embed_layer = tf.nn.embedding_lookup(movie_id_embed_matrix, movie_id, name = "movie_id_embed_layer")
? ? return movie_id_embed_layer
對(duì)電影類(lèi)型的多個(gè)嵌入向量做加和
def get_movie_categories_layers(movie_categories):
? ? with tf.name_scope("movie_categories_layers"):
? ? ? ? movie_categories_embed_matrix = tf.Variable(tf.random_uniform([movie_categories_max, embed_dim], -1, 1), name = "movie_categories_embed_matrix")
? ? ? ? movie_categories_embed_layer = tf.nn.embedding_lookup(movie_categories_embed_matrix, movie_categories, name = "movie_categories_embed_layer")
? ? ? ? if combiner == "sum":
? ? ? ? ? ? movie_categories_embed_layer = tf.reduce_sum(movie_categories_embed_layer, axis=1, keep_dims=True)
? ? # ? ? elif combiner == "mean":
? ? return movie_categories_embed_layer
Movie Title的文本卷積網(wǎng)絡(luò)實(shí)現(xiàn)
def get_movie_cnn_layer(movie_titles):
? ? #從嵌入矩陣中得到電影名對(duì)應(yīng)的各個(gè)單詞的嵌入向量
? ? with tf.name_scope("movie_embedding"):
? ? ? ? movie_title_embed_matrix = tf.Variable(tf.random_uniform([movie_title_max, embed_dim], -1, 1), name = "movie_title_embed_matrix")
? ? ? ? movie_title_embed_layer = tf.nn.embedding_lookup(movie_title_embed_matrix, movie_titles, name = "movie_title_embed_layer")
? ? ? ? movie_title_embed_layer_expand = tf.expand_dims(movie_title_embed_layer, -1)
? ? #對(duì)文本嵌入層使用不同尺寸的卷積核做卷積和最大池化
? ? pool_layer_lst = []
? ? for window_size in window_sizes:
? ? ? ? with tf.name_scope("movie_txt_conv_maxpool_{}".format(window_size)):
? ? ? ? ? ? filter_weights = tf.Variable(tf.truncated_normal([window_size, embed_dim, 1, filter_num],stddev=0.1),name = "filter_weights")
? ? ? ? ? ? filter_bias = tf.Variable(tf.constant(0.1, shape=[filter_num]), name="filter_bias")
? ? ? ? ? ? conv_layer = tf.nn.conv2d(movie_title_embed_layer_expand, filter_weights, [1,1,1,1], padding="VALID", name="conv_layer")
? ? ? ? ? ? relu_layer = tf.nn.relu(tf.nn.bias_add(conv_layer,filter_bias), name ="relu_layer")
? ? ? ? ? ? maxpool_layer = tf.nn.max_pool(relu_layer, [1,sentences_size - window_size + 1 ,1,1], [1,1,1,1], padding="VALID", name="maxpool_layer")
? ? ? ? ? ? pool_layer_lst.append(maxpool_layer)
? ? #Dropout層
? ? with tf.name_scope("pool_dropout"):
? ? ? ? pool_layer = tf.concat(pool_layer_lst, 3, name ="pool_layer")
? ? ? ? max_num = len(window_sizes) * filter_num
? ? ? ? pool_layer_flat = tf.reshape(pool_layer , [-1, 1, max_num], name = "pool_layer_flat")? ? ? ? dropout_layer = tf.nn.dropout(pool_layer_flat, dropout_keep_prob, name = "dropout_layer")
? ? return pool_layer_flat, dropout_layer
將Movie的各個(gè)層一起做全連接
構(gòu)建計(jì)算圖
訓(xùn)練網(wǎng)絡(luò)
%matplotlib inline %config InlineBackend.figure_format = 'retina' import matplotlib.pyplot as plt import time import datetimelosses = {'train':[], 'test':[]}with tf.Session(graph=train_graph) as sess:#搜集數(shù)據(jù)給tensorBoard用# Keep track of gradient values and sparsitygrad_summaries = []for g, v in gradients:if g is not None:grad_hist_summary = tf.summary.histogram("{}/grad/hist".format(v.name.replace(':', '_')), g)sparsity_summary = tf.summary.scalar("{}/grad/sparsity".format(v.name.replace(':', '_')), tf.nn.zero_fraction(g))grad_summaries.append(grad_hist_summary)grad_summaries.append(sparsity_summary)grad_summaries_merged = tf.summary.merge(grad_summaries)# Output directory for models and summariestimestamp = str(int(time.time()))out_dir = os.path.abspath(os.path.join(os.path.curdir, "runs", timestamp))print("Writing to {}\n".format(out_dir))# Summaries for loss and accuracyloss_summary = tf.summary.scalar("loss", loss)# Train Summariestrain_summary_op = tf.summary.merge([loss_summary, grad_summaries_merged])train_summary_dir = os.path.join(out_dir, "summaries", "train")train_summary_writer = tf.summary.FileWriter(train_summary_dir, sess.graph)# Inference summariesinference_summary_op = tf.summary.merge([loss_summary])inference_summary_dir = os.path.join(out_dir, "summaries", "inference")inference_summary_writer = tf.summary.FileWriter(inference_summary_dir, sess.graph)sess.run(tf.global_variables_initializer())saver = tf.train.Saver()for epoch_i in range(num_epochs):#將數(shù)據(jù)集分成訓(xùn)練集和測(cè)試集,隨機(jī)種子不固定train_X,test_X, train_y, test_y = train_test_split(features, ?targets_values, ?test_size = 0.2, ?random_state = 0) ?train_batches = get_batches(train_X, train_y, batch_size)test_batches = get_batches(test_X, test_y, batch_size)#訓(xùn)練的迭代,保存訓(xùn)練損失for batch_i in range(len(train_X) // batch_size):x, y = next(train_batches)categories = np.zeros([batch_size, 18])for i in range(batch_size):categories[i] = x.take(6,1)[i]titles = np.zeros([batch_size, sentences_size])for i in range(batch_size):titles[i] = x.take(5,1)[i]feed = {uid: np.reshape(x.take(0,1), [batch_size, 1]),user_gender: np.reshape(x.take(2,1), [batch_size, 1]),user_age: np.reshape(x.take(3,1), [batch_size, 1]),user_job: np.reshape(x.take(4,1), [batch_size, 1]),movie_id: np.reshape(x.take(1,1), [batch_size, 1]),movie_categories: categories, ?#x.take(6,1)movie_titles: titles, ?#x.take(5,1)targets: np.reshape(y, [batch_size, 1]),dropout_keep_prob: dropout_keep, #dropout_keeplr: learning_rate}step, train_loss, summaries, _ = sess.run([global_step, loss, train_summary_op, train_op], feed) ?#costlosses['train'].append(train_loss)train_summary_writer.add_summary(summaries, step) ?## Show every <show_every_n_batches> batchesif (epoch_i * (len(train_X) // batch_size) + batch_i) % show_every_n_batches == 0:time_str = datetime.datetime.now().isoformat()print('{}: Epoch {:>3} Batch {:>4}/{} ? train_loss = {:.3f}'.format(time_str,epoch_i,batch_i,(len(train_X) // batch_size),train_loss))#使用測(cè)試數(shù)據(jù)的迭代for batch_i ?in range(len(test_X) // batch_size):x, y = next(test_batches)categories = np.zeros([batch_size, 18])for i in range(batch_size):categories[i] = x.take(6,1)[i]titles = np.zeros([batch_size, sentences_size])for i in range(batch_size):titles[i] = x.take(5,1)[i]feed = {uid: np.reshape(x.take(0,1), [batch_size, 1]),user_gender: np.reshape(x.take(2,1), [batch_size, 1]),user_age: np.reshape(x.take(3,1), [batch_size, 1]),user_job: np.reshape(x.take(4,1), [batch_size, 1]),movie_id: np.reshape(x.take(1,1), [batch_size, 1]),movie_categories: categories, ?#x.take(6,1)movie_titles: titles, ?#x.take(5,1)targets: np.reshape(y, [batch_size, 1]),dropout_keep_prob: 1,lr: learning_rate}step, test_loss, summaries = sess.run([global_step, loss, inference_summary_op], feed) ?#cost#保存測(cè)試損失losses['test'].append(test_loss)inference_summary_writer.add_summary(summaries, step) ?#time_str = datetime.datetime.now().isoformat()if (epoch_i * (len(test_X) // batch_size) + batch_i) % show_every_n_batches == 0:print('{}: Epoch {:>3} Batch {:>4}/{} ? test_loss = {:.3f}'.format(time_str,epoch_i,batch_i,(len(test_X) // batch_size),test_loss))# Save Modelsaver.save(sess, save_dir) ?#, global_step=epoch_iprint('Model Trained and Saved')在 TensorBoard 中查看可視化結(jié)果
獲取 Tensors
指定用戶和電影進(jìn)行評(píng)分
這部分就是對(duì)網(wǎng)絡(luò)做正向傳播,計(jì)算得到預(yù)測(cè)的評(píng)分
生成Movie特征矩陣
將訓(xùn)練好的電影特征組合成電影特征矩陣并保存到本地
生成User特征矩陣
將訓(xùn)練好的用戶特征組合成用戶特征矩陣并保存到本地
開(kāi)始推薦電影
使用生產(chǎn)的用戶特征矩陣和電影特征矩陣做電影推薦
推薦同類(lèi)型的電影
思路是計(jì)算當(dāng)前看的電影特征向量與整個(gè)電影特征矩陣的余弦相似度,取相似度最大的top_k個(gè),這里加了些隨機(jī)選擇在里面,保證每次的推薦稍稍有些不同。
推薦您喜歡的電影
思路是使用用戶特征向量與電影特征矩陣計(jì)算所有電影的評(píng)分,取評(píng)分最高的top_k個(gè),同樣加了些隨機(jī)選擇部分。
看過(guò)這個(gè)電影的人還看了(喜歡)哪些電影
首先選出喜歡某個(gè)電影的top_k個(gè)人,得到這幾個(gè)人的用戶特征向量。
然后計(jì)算這幾個(gè)人對(duì)所有電影的評(píng)分
選擇每個(gè)人評(píng)分最高的電影作為推薦
同樣加入了隨機(jī)選擇
結(jié)論
以上就是實(shí)現(xiàn)的常用的推薦功能,將網(wǎng)絡(luò)模型作為回歸問(wèn)題進(jìn)行訓(xùn)練,得到訓(xùn)練好的用戶特征矩陣和電影特征矩陣進(jìn)行推薦。
?
?
總結(jié)
以上是生活随笔為你收集整理的TensorFlow实战——深度学习训练个性化推荐系统的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux五种清理系统垃圾的方式
- 下一篇: 网络编程C/S模型怎样才能实现真正的聊天