文章目錄
- (1)資料集
- (2)核心:推薦函數
- (3)實驗結果
寫在前面:本文通過構建一個電影推薦系統,深入淺出的介紹推薦系統相關的概念、演算法,讓讀者朋友能夠在對推薦系統有比較全面的認識的基礎之上,能夠輕鬆地構建出自己的推薦系統。
1. 什麼是推薦系統(Recommendation System)
推薦系統是指根據一個群體的偏好,來為群體中的成員提供推薦的系統。現實生活中這樣的例子很多,比如豆瓣(Douban.com)讀書中的“豆瓣猜”功能,它根據你看過的一些書和相關評價,與整個豆瓣社區其它會員看過的書與評價經過一系列的計算,就能給你推薦一些你沒有讀過的,但有可能感興趣的書(如所示):
這是我讀過的或者正在讀的書:
這是豆瓣給我推薦的書:
通過上面的例子,相信大家對推薦系統都有了一個初步的認識。其實生活中還有許許多多的這樣的例子,像線上購物中的商品推薦、線上視頻播放網站的視頻推薦等。
2. 推薦系統相關理論
(1)推薦系統通常可以分為兩類:一類是基於人的推薦系統,它利用人與人之間的相似性來進行推薦;一類是基於物品的推薦,它利用物品之間的相似性來進行推薦。通俗地講,基於人的推薦,是通過分析人與人喜歡的物品,計算出人與人之間的相似性,然後做推薦;而基於物品的推薦,是通過分析某人喜歡的物品與其它物品的相似性,然後來為其做推薦。
(2)推薦系統中比較關鍵的演算法就是相似性的計算,有人與人之間的相似性計算,也有物品與物品之間的相似性計算。相似性計算函數要滿足如下特點:擁有同樣的函數簽名,以一個浮點數做為傳回值,其數值越大代表相似性越大。下面介紹幾個演算法:
a. 歐幾裡德距離(Euclidean Distance),我們知道兩個人的喜好越相似,他們的歐幾裡德距離值越小,所以需要將歐幾裡德距離轉化一下,這裡介紹一個簡單的轉化:1/(1 + dist), 這樣就能保證越相似取值越大,而且取值範圍在(0, 1]
b. 皮求遜相關係數(Pearson Correlation Coefficient), 取值範圍[-1, 1], 越相似值越大,滿足條件
c. Tanimoto系統 (Tanimoto Coefficient),最值範圍[0, 1], 越相似值越大,滿足條件
(3)在推薦系統裡,需要注意:
a. 沒有對某物品進行評價的人不能對該物品的推薦打分產生影響
b. 不能因為某人的偏執喜好(打很高或者很低的分)對推薦打分產生明顯的影響
為了避免上面的問題,通常採用加權平均的方法來計算某物品的推薦打分(詳見第三部分演算法實現)
3. 動手構建一個推薦系統
本部分通過構建一個真實的電影推薦系統,來介紹構建一個推薦的基本步驟與方法。
(1)資料集
本系統採用的資料是來自http://www.grouplens.org/node/73的資料,本次實驗採用的是如所示的第三份資料,總共有6040個使用者和3952部電影,以及1000209條相關評價。
(2)核心:推薦函數
typedef double (*ScoreFunc)(const double *, const double *, size_t); void GetRecommendation(const double ** allCritics, //[in] 所有人的打分表size_t personNum, //[in] 所有人的個數size_t size, //[in] 打分表大小size_t myIndex, //[in] 我在打分表中下標ScoreFunc scorer, //[in] 打分函數size_t recNum, //[out] 被推薦項個數int * recItems, //[out] 被推薦項列表double * recScores //[out] 被推薦項得分) {double * allRels = new double[personNum];::memset(allRels, 0, sizeof(double)*personNum);///計算所有的相關度for (size_t idx = 0; idx < personNum; ++ idx){if (idx == myIndex){continue; //it's me, just continue}allRels[idx] = scorer(allCritics[myIndex], allCritics[idx], size);} double * rels = new double[personNum];double * critics = new double[personNum];std::multimap mapScores;for (size_t itemIdx = 0; itemIdx < size; ++ itemIdx){::memset(rels, 0, sizeof(double)*personNum);::memset(critics, 0, sizeof(double)*personNum);///擷取有效相關度和對應的評分for (size_t personIdx = 0; personIdx < personNum; ++ personIdx){if (allCritics[personIdx][itemIdx] <= 0)//invalid score{rels[personIdx] = 0;critics[personIdx] = 0;}else{rels[personIdx] = allRels[personIdx];critics[personIdx] = allCritics[personIdx][itemIdx];}} ///計算加權打分double score = GetWeightedMead(critics, critics + personNum, rels, rels + personNum);mapScores.insert(std::make_pair(score, itemIdx));} ///擷取最終的推薦列表和對應的打分std::multimap::reverse_iterator oIt = mapScores.rbegin();for (size_t count = 0; count < recNum; ++ count) { recItems[count] = oIt->second;recScores[count] = oIt->first;++ oIt;} delete [] critics;delete [] rels;delete [] allRels;}上面的函數,輸入所有人的評分表,以及需要提供推薦的人的相關資訊,輸出提供的推薦列表,以及相應的打分。這裡需要說明的一點是打分函數scorer, 它是一個ScoreFunc類型的函數指標,在實際調用的時候,可以通過傳入不同的打分函數,獲得在該打分函數下的推薦列表。需要注意打分函數的簽名在這裡必須與ScoreFunc的定義一致。下面是我定義的打分函數:
double GetEuclideanScore(double dist){return (1 / (1 + dist));} double GetPearsonScore(double coef){return coef;} double GetTanimotoScore(double coef){return coef;} double GetEuclideanScore(const double * myCritics, const double * hisCritics, size_t size){double dist = GetEuclideanDistance(myCritics, myCritics + size,hisCritics, hisCritics + size); return GetEuclideanScore(dist);} double GetPearsonScore(const double * myCritics, const double * hisCritics, size_t size){double coef = GetPearsonCorrelationCoefficient(myCritics, myCritics + size,hisCritics, hisCritics + size); return GetPearsonScore(coef);} double GetTanimotoScore(const double * myCritics, const double * hisCritics, size_t size){double coef = GetTanimotoCoefficient(myCritics, myCritics + size,hisCritics, hisCritics + size); return GetTanimotoScore(coef);}上面的六個函數,前面三個是對結果進行正常化的函數,使結果滿足越相關越大。後面三個函數是真正的評價打分函數。在上面的實現中用到了GetEuclideanDistance, GetPearsonCorrelationCoefficient, GetTanimotoCoefficient, GetWeightedMead這四個函數,它們分別是計算歐幾裡德距離,Pearson相關係數,Tanimoto係數和加權平均值的函數,在這裡我就不給出具體的實現了。(3)實驗結果
我們用三種不同的打分函數,為第1000個使用者,推薦20部電影,來對比一下推薦的結果:
a. Euclidean結果:
b. Pearson結果:
c. Tanimoto結果:
從上面的結果中,可以得出如下結果:
a. 採用Euclidean打分推薦和採用Pearson打分推薦的結果中,有16個是相同的
b. 採用Pearson打分推薦和採用Tanimoto打分推薦的結果中,有15個是相同的
c. 採用Tanimoto打分推薦和採用Euclidean打分推薦的結果中,有16個是相同的
從上面的資料可以看出,雖說採用不同的打分函數進行推薦的結果存在一定的差異,但是整體上是一致的,不同的結果的相互覆蓋率都超過了75%, 這說明我們的打分函數都還是比較有效。
-
原創文章,轉載請註明出處:小武哥的部落格
本文固定連結:http://www.wuzesheng.com/?p=1277