畢業(yè)設(shè)計 推薦系統(tǒng)設(shè)計與實現(xiàn)
0 前言
?? 這兩年開始畢業(yè)設(shè)計和畢業(yè)答辯的要求和難度不斷提升,傳統(tǒng)的畢設(shè)題目缺少創(chuàng)新和亮點(diǎn),往往達(dá)不到畢業(yè)答辯的要求,這兩年不斷有學(xué)弟學(xué)妹告訴學(xué)長自己做的項目系統(tǒng)達(dá)不到老師的要求。
?? ?推薦系統(tǒng)設(shè)計與實現(xiàn)
??學(xué)長這里給一個題目綜合評分(每項滿分5分)
難度系數(shù):3分
工作量:3分
創(chuàng)新點(diǎn):4分
畢設(shè)幫助,選題指導(dǎo),技術(shù)解答,歡迎打擾,見B站個人主頁
https://space.bilibili.com/33886978?
簡介
推薦系統(tǒng),是當(dāng)今互聯(lián)網(wǎng)背后的無名英雄。
我們在某寶首頁看見的商品,某條上讀到的新聞,某度上的搜索列表,甚至在各種地方看見的廣告,都有賴于推薦算法和系統(tǒng).
本片文章講述有哪些常用的推薦算法, 協(xié)同過濾推薦算法的原理, 以及如何使用協(xié)同過濾算法設(shè)計一個商品推薦畢業(yè)設(shè)計系統(tǒng).
常見推薦算法
協(xié)同過濾
協(xié)同過濾(Collaborative Filtering)作為推薦算法中最經(jīng)典的類型,包括在線的協(xié)同和離線的過濾兩部分。所謂在線協(xié)同,就是通過在線數(shù)據(jù)找到用戶可能喜歡的物品,而離線過濾,則是過濾掉一些不值得推薦的數(shù)據(jù),比比如推薦值評分低的數(shù)據(jù),或者雖然推薦值高但是用戶已經(jīng)購買的數(shù)據(jù)。
協(xié)同過濾的模型一般為m個物品,m個用戶的數(shù)據(jù),只有部分用戶和部分?jǐn)?shù)據(jù)之間是有評分?jǐn)?shù)據(jù)的,其它部分評分是空白,此時我們要用已有的部分稀疏數(shù)據(jù)來預(yù)測那些空白的物品和數(shù)據(jù)之間的評分關(guān)系,找到最高評分的物品推薦給用戶。
一般來說,協(xié)同過濾推薦分為三種類型。第一種是基于用戶(user-based)的協(xié)同過濾,第二種是基于項目(item-based)的協(xié)同過濾,第三種是基于模型(model based)的協(xié)同過濾。
基于用戶的協(xié)同過濾的基本原理是,根據(jù)所有用戶對物品或者信息的偏好,發(fā)現(xiàn)與當(dāng)前用戶口味和偏好相似的用戶群,然后基于這些用戶的歷史偏好,為當(dāng)前用戶進(jìn)行推薦。

假設(shè)用戶A喜歡物品A、物品C,用戶B喜歡物品B,用戶C喜歡物品A、物品C和物品D。從這些用戶的歷史偏好中,我們可以看出用戶A和用戶C的偏好是類似的。同時我們可以看到用戶C喜歡物品D,所以我們可以猜想用戶A可能也喜歡物品D,因此可以把物品D推薦給用戶A。
分解矩陣
這是一個非常優(yōu)雅的推薦算法,因為當(dāng)涉及到矩陣分解時,我們通常不會太多地去思考哪些項目將停留在所得到矩陣的列和行中。但是使用這個推薦引擎,我們清楚地看到,u是第i個用戶的興趣向量,v是第j個電影的參數(shù)向量。

所以我們可以用u和v的點(diǎn)積來估算x(第i個用戶對第j個電影的評分)。我們用已知的分?jǐn)?shù)構(gòu)建這些向量,并使用它們來預(yù)測未知的得分。
例如,在矩陣分解之后,Ted的向量是(1.4; .8),商品A的向量是(1.4; .9),現(xiàn)在,我們可以通過計算(1.4; .8)和(1.4; .9)的點(diǎn)積,來還原商品A-Ted的得分。結(jié)果,我們得到2.68分。

聚類
上面兩種算法都極其簡單,適用于小型系統(tǒng)。在這兩種方法中,我們把推薦問題當(dāng)做一個有監(jiān)督機(jī)器學(xué)習(xí)任務(wù)來解決。
現(xiàn)在,該開始用無監(jiān)督學(xué)習(xí)來解決問題了。
假設(shè)我們正在建立一個大型推薦系統(tǒng),這時協(xié)同過濾和矩陣分解花費(fèi)的時間更長了。第一個浮現(xiàn)在腦海里的解決之道,就是聚類。
業(yè)務(wù)開展之初,缺乏之前的用戶數(shù)據(jù),聚類將是最好的方法。
不過,聚類是一種比較弱的個性化推薦,因為這種方法的本質(zhì)是識別用戶組,并對這個組內(nèi)的用戶推薦相同的內(nèi)容。
當(dāng)我們有足夠數(shù)據(jù)時,最好使用聚類作為第一步,來縮減協(xié)同過濾算法中相關(guān)鄰居的選擇范圍。這個方法還能挺高復(fù)雜推薦系統(tǒng)的性能。
每個聚類都會根據(jù)其中用戶的偏好,來分配一組典型的偏好。每個聚類中的用戶,都會收到為這個聚類計算出的推薦內(nèi)容。
深度學(xué)習(xí)
在過去的十年中,神經(jīng)網(wǎng)絡(luò)已經(jīng)取得了巨大的飛躍。如今,神經(jīng)網(wǎng)絡(luò)已經(jīng)得以廣泛應(yīng)用,并逐漸取代傳統(tǒng)的機(jī)器學(xué)習(xí)方法。
接下來,我要介紹一下YouTube如何使用深度學(xué)習(xí)方法來做個性化推薦。
毫無疑問,由于體量龐大、動態(tài)庫和各種觀察不到的外部因素,為YouTube用戶提供推薦內(nèi)容是一項非常具有挑戰(zhàn)性的任務(wù)。
根據(jù)《Deep Neural Networks for YouTube Recommendations》( https://static.googleusercontent.com/media/research.google.com/ru//pubs/archive/45530.pdf ),YouTube的推薦系統(tǒng)算法由兩個神經(jīng)網(wǎng)絡(luò)組成:一個用于候選生成,一個用于排序。如果你沒時間仔細(xì)研究論文,可以看看我們下面給出的簡短總結(jié)。

以用戶的瀏覽歷史為輸入,候選生成網(wǎng)絡(luò)可以顯著減小可推薦的視頻數(shù)量,從龐大的庫中選出一組最相關(guān)的視頻。這樣生成的候選視頻與用戶的相關(guān)性最高,然后我們會對用戶評分進(jìn)行預(yù)測。
這個網(wǎng)絡(luò)的目標(biāo),只是通過協(xié)同過濾提供更廣泛的個性化。

進(jìn)行到這一步,我們得到一組規(guī)模更小但相關(guān)性更高的內(nèi)容。我們的目標(biāo)是仔細(xì)分析這些候選內(nèi)容,以便做出最佳的選擇。
這個任務(wù)由排序網(wǎng)絡(luò)完成。
所謂排序就是根據(jù)視頻描述數(shù)據(jù)和用戶行為信息,使用設(shè)計好的目標(biāo)函數(shù)為每個視頻打分,得分最高的視頻會呈獻(xiàn)給用戶。

通過這兩步,我們可以從非常龐大的視頻庫中選擇視頻,并面向用戶進(jìn)行有針對性的推薦。這個方法還能讓我們把其他來源的內(nèi)容也容納進(jìn)來。

推薦任務(wù)是一個極端的多類分類問題。這個預(yù)測問題的實質(zhì),是基于用戶(U)和語境(C),在給定的時間t精確地從庫(V)中上百萬的視頻類(i)中,對特定的視頻觀看(Wt)情況進(jìn)行分類。
協(xié)同過濾原理
使用基于物品的協(xié)同過濾,需要維護(hù)一個物品相似度矩陣;使用基于用戶的協(xié)同過濾,需要維護(hù)一個用戶相似度矩陣。

兩用戶之間的相似度的計算其實很簡單,用戶i與用戶j的相似度 = (i、j都打開過的網(wǎng)頁數(shù))/根號(i打開過的網(wǎng)頁數(shù) * j打開過的網(wǎng)頁數(shù))。這個計算與“基于物品的協(xié)同過濾”中的物品之間相似度的計算是類似的。

上面是一個用戶相似度計算的案例。我們試著計算A和D之間的相似度。從“用戶打開過的網(wǎng)頁”可以看出,A和D都打開過的網(wǎng)頁只有d,也就是1個。用戶A打開過的網(wǎng)頁數(shù)=3,用戶D打開過的網(wǎng)頁數(shù)=3。所以A和D的相似度=1/根號(3*3)。其他的計算也是類似的。
有了用戶之間的相似度之后,就可以計算推薦度了。假設(shè)e是剛剛發(fā)布的文章,這時候用戶B、C、D都瀏覽到e新聞的標(biāo)題,其中C、D點(diǎn)擊了,我們就可以計算A對e的興趣度。
A對e的興趣度=A與B的相似度B對e的興趣度 + A與C的相似度C對e的興趣度 + A與D的相似度*D對e的興趣度。 因為我們這里用的不是評分制,而是考慮是否點(diǎn)擊,那么D點(diǎn)擊了e,D對e的興趣度=1。
A對e的興趣度 = 1/根號(6)1 + ?1/根號(6)1 + ?1/根號(9)*1
所以,比如100篇新的文章出來之后,對部分用戶進(jìn)行了曝光,然后就可以根據(jù)用戶相似度,來預(yù)計其他用戶對這篇文章的興趣度,進(jìn)而挑選這100篇中預(yù)計興趣度最高的30篇曝光給這群用戶。
系統(tǒng)設(shè)計
示例代碼(py)
from abc import ABCMeta, abstractmethod
import numpy as np
from collections import defaultdict
class CF_base(metaclass=ABCMeta):
? ?def __init__(self, k=3):
? ? ? ?self.k = k
? ? ? ?self.n_user = None
? ? ? ?self.n_item = None
? ?@abstractmethod
? ?def init_param(self, data):
? ? ? ?pass
? ?@abstractmethod
? ?def cal_prediction(self, *args):
? ? ? ?pass
? ?@abstractmethod
? ?def cal_recommendation(self, user_id, data):
? ? ? ?pass
? ?def fit(self, data):
? ? ? ?# 計算所有用戶的推薦物品
? ? ? ?self.init_param(data)
? ? ? ?all_users = []
? ? ? ?for i in range(self.n_user):
? ? ? ? ? ?all_users.append(self.cal_recommendation(i, data))
? ? ? ?return all_users
class CF_knearest(CF_base):
? ?"""
? ?基于物品的K近鄰協(xié)同過濾推薦算法
? ?"""
? ?def __init__(self, k, criterion='cosine'):
? ? ? ?super(CF_knearest, self).__init__(k)
? ? ? ?self.criterion = criterion
? ? ? ?self.simi_mat = None
? ? ? ?return
? ?def init_param(self, data):
? ? ? ?# 初始化參數(shù)
? ? ? ?self.n_user = data.shape[0]
? ? ? ?self.n_item = data.shape[1]
? ? ? ?self.simi_mat = self.cal_simi_mat(data)
? ? ? ?return
? ?def cal_similarity(self, i, j, data):
? ? ? ?# 計算物品i和物品j的相似度
? ? ? ?items = data[:, [i, j]]
? ? ? ?del_inds = np.where(items == 0)[0]
? ? ? ?items = np.delete(items, del_inds, axis=0)
? ? ? ?if items.size == 0:
? ? ? ? ? ?similarity = 0
? ? ? ?else:
? ? ? ? ? ?v1 = items[:, 0]
? ? ? ? ? ?v2 = items[:, 1]
? ? ? ? ? ?if self.criterion == 'cosine':
? ? ? ? ? ? ? ?if np.std(v1) > 1e-3: ?# 方差過大,表明用戶間評價尺度差別大需要進(jìn)行調(diào)整
? ? ? ? ? ? ? ? ? ?v1 = v1 - v1.mean()
? ? ? ? ? ? ? ?if np.std(v2) > 1e-3:
? ? ? ? ? ? ? ? ? ?v2 = v2 - v2.mean()
? ? ? ? ? ? ? ?similarity = (v1 @ v2) / np.linalg.norm(v1, 2) / np.linalg.norm(v2, 2)
? ? ? ? ? ?elif self.criterion == 'pearson':
? ? ? ? ? ? ? ?similarity = np.corrcoef(v1, v2)[0, 1]
? ? ? ? ? ?else:
? ? ? ? ? ? ? ?raise ValueError('the method is not supported now')
? ? ? ?return similarity
? ?def cal_simi_mat(self, data):
? ? ? ?# 計算物品間的相似度矩陣
? ? ? ?simi_mat = np.ones((self.n_item, self.n_item))
? ? ? ?for i in range(self.n_item):
? ? ? ? ? ?for j in range(i + 1, self.n_item):
? ? ? ? ? ? ? ?simi_mat[i, j] = self.cal_similarity(i, j, data)
? ? ? ? ? ? ? ?simi_mat[j, i] = simi_mat[i, j]
? ? ? ?return simi_mat
? ?def cal_prediction(self, user_row, item_ind):
? ? ? ?# 計算預(yù)推薦物品i對目標(biāo)活躍用戶u的吸引力
? ? ? ?purchase_item_inds = np.where(user_row > 0)[0]
? ? ? ?rates = user_row[purchase_item_inds]
? ? ? ?simi = self.simi_mat[item_ind][purchase_item_inds]
? ? ? ?return np.sum(rates * simi) / np.linalg.norm(simi, 1)
? ?def cal_recommendation(self, user_ind, data):
? ? ? ?# 計算目標(biāo)用戶的最具吸引力的k個物品list
? ? ? ?item_prediction = defaultdict(float)
? ? ? ?user_row = data[user_ind]
? ? ? ?un_purchase_item_inds = np.where(user_row == 0)[0]
? ? ? ?for item_ind in un_purchase_item_inds:
? ? ? ? ? ?item_prediction[item_ind] = self.cal_prediction(user_row, item_ind)
? ? ? ?res = sorted(item_prediction, key=item_prediction.get, reverse=True)
? ? ? ?return res[:self.k]
class CF_svd(CF_base):
? ?"""
? ?基于矩陣分解的協(xié)同過濾算法
? ?"""
? ?def __init__(self, k=3, r=3):
? ? ? ?super(CF_svd, self).__init__(k)
? ? ? ?self.r = r ?# 選取前k個奇異值
? ? ? ?self.uk = None ?# 用戶的隱因子向量
? ? ? ?self.vk = None ?# 物品的隱因子向量
? ? ? ?return
? ?def init_param(self, data):
? ? ? ?# 初始化,預(yù)處理
? ? ? ?self.n_user = data.shape[0]
? ? ? ?self.n_item = data.shape[1]
? ? ? ?self.svd_simplify(data)
? ? ? ?return data
? ?def svd_simplify(self, data):
? ? ? ?# 奇異值分解以及簡化
? ? ? ?u, s, v = np.linalg.svd(data)
? ? ? ?u, s, v = u[:, :self.r], s[:self.r], v[:self.r, :] ?# 簡化
? ? ? ?sk = np.diag(np.sqrt(s)) ?# r*r
? ? ? ?self.uk = u @ sk ?# m*r
? ? ? ?self.vk = sk @ v ?# r*n
? ? ? ?return
? ?def cal_prediction(self, user_ind, item_ind, user_row):
? ? ? ?rate_ave = np.mean(user_row) ?# 用戶已購物品的評價的平均值(未評價的評分為0)
? ? ? ?return rate_ave + self.uk[user_ind] @ self.vk[:, item_ind] ?# 兩個隱因子向量的內(nèi)積加上平均值就是最終的預(yù)測分值
? ?def cal_recommendation(self, user_ind, data):
? ? ? ?# 計算目標(biāo)用戶的最具吸引力的k個物品list
? ? ? ?item_prediction = defaultdict(float)
? ? ? ?user_row = data[user_ind]
? ? ? ?un_purchase_item_inds = np.where(user_row == 0)[0]
? ? ? ?for item_ind in un_purchase_item_inds:
? ? ? ? ? ?item_prediction[item_ind] = self.cal_prediction(user_ind, item_ind, user_row)
? ? ? ?res = sorted(item_prediction, key=item_prediction.get, reverse=True)
? ? ? ?return res[:self.k]
if __name__ == '__main__':
? ?# data = np.array([[4, 3, 0, 5, 0],
? ?# ? ? ? ? ? ? ? ? ?[4, 0, 4, 4, 0],
? ?# ? ? ? ? ? ? ? ? ?[4, 0, 5, 0, 3],
? ?# ? ? ? ? ? ? ? ? ?[2, 3, 0, 1, 0],
? ?# ? ? ? ? ? ? ? ? ?[0, 4, 2, 0, 5]])
? ?data = np.array([[3.5, 1.0, 0.0, 0.0, 0.0, 0.0],
? ? ? ? ? ? ? ? ? ? [2.5, 3.5, 3.0, 3.5, 2.5, 3.0],
? ? ? ? ? ? ? ? ? ? [3.0, 3.5, 1.5, 5.0, 3.0, 3.5],
? ? ? ? ? ? ? ? ? ? [2.5, 3.5, 0.0, 3.5, 4.0, 0.0],
? ? ? ? ? ? ? ? ? ? [3.5, 2.0, 4.5, 0.0, 3.5, 2.0],
? ? ? ? ? ? ? ? ? ? [3.0, 4.0, 2.0, 3.0, 3.0, 2.0],
? ? ? ? ? ? ? ? ? ? [4.5, 1.5, 3.0, 5.0, 3.5, 0.0]])
? ?# cf = CF_svd(k=1, r=3)
? ?cf = CF_knearest(k=1)
? ?print(cf.fit(data))
系統(tǒng)展示
系統(tǒng)界面

推薦效果

畢設(shè)幫助,選題指導(dǎo),技術(shù)解答,歡迎打擾,見B站個人主頁
https://space.bilibili.com/33886978?