本章包含以下內容:
- 首先看一下實戰中的推薦系統
- 推薦引擎的精度評價
- 評價一個引擎的准確率和召回率
- 在真實數據集:GroupLens 上評價推薦系統
我們每天都會對喜歡的、不喜歡的、甚至不關心的事情有很多觀點。這些事情往往發生的不知不覺。你在收音機上聽歌,因為它容易記住或者因為聽起來可怕而關注它 — 又或者根本不去關注它。同樣的事情有可能發生在T恤衫,色拉,發型,滑雪勝地,面孔,電視節目。
盡管人們的愛好差異很大,但他們仍然遵循某種模式。人們傾向於喜歡一些事物,這些事物類似於他們自己喜歡的其他事物。因為我喜歡培根-生菜-西 紅柿三明治,你可能猜到我可能也喜歡總匯三明治,類似於前者的帶有火雞肉的三明治。另外,人們傾向於喜歡興趣和他們相似的人所喜歡的事物。當一個朋友進入設計學校,發現幾乎周圍所有同學都用一個蘋果電腦 — 不用吃驚,他已經是一個終生的蘋果用戶。
這些模式可以用來預測喜好與否。如果將一個陌生人帶到你面前並問你,她是否喜歡指環王三部曲,你只能胡亂猜測了。但是,如果她告訴我們她喜歡頭 兩部指環王,如果她不喜歡第3部,你會感到震驚。另一種情況,如果她說討厭指環或者問“什么王?”,你可以准確的猜測她對指環王不感興趣。
推薦系統是關於這些預測偏好模型的系統,並且使用他們為你發現新的、稱心的事物。
2.1 推薦系統是什么
你因為某種原因從書架上拿起這本書。可能你看到它臨近其他的你熟知的書,發現他們是有用的,想到書店是因為喜歡那些書的人也會喜歡這本書才把他們放到一起。也可能你看到這本書在你同事的書架上,你和這個同事經常分享機器學習方面的知識,或者他直接將這本書推薦給你。
后面的章節探索了人們實現推薦系統的很多方法,發現新事物,當然有這些過程在Mahout軟件中是如何實現的。我們已經提到一些策略:為了發現你喜歡的items,你可能去尋找有相同興趣的人。另一方面,你可以由其他人的明顯偏好計算出類似你喜歡的items的其他items。這描述了兩大類推 薦系統算法:“user-based”和“item-based”推薦系統。
2.1.1 協同過濾 vs 基於內容的推薦
嚴格的講,上面章節提到例子是“協同過濾” — 僅僅基於user和item關系的生產推薦系統。這些技術不需要item自己的屬性信息。在某種程度上,這是一個優點,該推薦系統框架不需要關心“item”是書、主題公園、花、甚至人,因為他們的任何屬性都沒有作為輸入數據。
有很多其他的方法基於item的屬性,一般被稱之為“基於內容”的推薦技術。例如,如果一個朋友推薦一本書給你,因為這是一本Manning出版的書,這個朋友喜歡其他Manning的書,那么你的這個朋友在做類似於基於內容推薦的事情。這種想法基於書籍的屬性:出版社。Mahout推薦框架沒 有直接實現這些技術,盡管它提供一些方法將item的屬性信息添加到計算中。基於這些,技術上稱前面的方法為協同過濾框架。
這些技術沒有沒什么問題;正相反,他們工作的很好。他們是特定領域的方法,可能很難有效地將他們整理到一個框架中。為了建立一個基於內容的圖書 推薦系統,我們需要決定書籍的哪些屬性對推薦系統是有意義的、作用多少程度 — 頁數、作者、出版社、顏色、字體。但是這些知識不能簡單的應用於別的領域;像圖書推薦的方法對披薩配料推薦是沒什么幫助的。
基於這個原因,Mahout沒有過多涉及這種類型的推薦。可以將這些想法融入進來,但首要任務是Mahout提供些什么。其中的一個樣例將在下 一個章節中講解,在那里你將建立一個約會網站的推薦引擎。在介紹完Mahout基於協同過濾推薦的實現后,你有機會探索它和基於內容的推薦之間關系的細 節。
2.1.2 推薦系統成為主流
現在,很多人已經看到了推薦系統在現實網站上的實現,像Amazon或者Netflix:基於瀏覽和交易歷史,頁面將產生一個認為對你有吸引力 的商品列表。這類推薦引擎在上世紀九十年代已經出現,但直到現在它仍然是使用大型計算機的專門研究者的領域。因為這些技術已經變更加主流,對它們的需求在增加,開源實現的提供也是一樣。隨着理解的深入和計算資源越來越廉價,推薦引擎變得越來越容易接近和廣泛使用。
實際上,推薦技術不只是為用戶推薦DVD類似的東西。這種方法非常通用,可以用來評估很多事物之間的關系的強弱。
在社區網絡中,一個推薦系統可以為用戶推薦用戶。
2.2 運行你的第一個推薦引擎
Mahout包含了一個推薦引擎 — 有很多類型,實際上都是源於傳統的user-based和item-based推薦系統。它還包含了基於“slope-one”技術的實現,一個新的、有 效的方法。你將會找到很多實驗性的、初步的的SVD(singular value decomposition)實現。下面的章節將重新看Mahout的內容。在這些章節中,你將看到數據展示、瀏覽可用的推薦算法,評價推薦系統的有效 性,針對特殊問題協調和定制推薦系統,最后看一下分布式計算。
2.2.1 創建輸入數據
為了探索Mahout中的推薦系統,最好從一個很小的示例入手。推薦系統的輸入是必要的 — 這些數據是推薦的基礎。因為非常熟悉的推薦系統引擎將item推薦給user,很容易的認為偏好是user和item之間的聯系 — 盡管前面提到了user和item可以是任何事物。偏好包含了一個user ID和一個item ID,通常情況下,一個數值代表了user對item偏好的強度。Mahout中ID都是數值,實際上是整數。偏好值可以是任何值,值越大代表這正向偏好 越大。例如,這些值可能是1到5的打分,user將1給於他不喜歡的,5給他很喜歡的。
創建一個文本文件包含用戶數據,命名為“1”到“5”,他們對四本書的偏好,簡單的稱之為“101”到“104”。在現實情況中,這些可能是公 司數據庫中消費者ID和產品ID;Mahout並不需要user和item的ID一定為數值類型。使用下面的格式,生成文件intro.csv。
1,101,5.0
1,102,3.0
1,103,2.5
2,101,2.0
2,102,2.5
2,103,5.0
2,104,2.0
3,101,2.5
3,104,4.0
3,105,4.5
3,107,5.0
4,101,5.0
4,103,3.0
4,104,4.5
4,106,4.0
5,101,4.0
5,102,3.0
5,103,2.0
5,104,4.0
5,105,3.5
5,106,4.0
經過一些學習之后,趨勢就顯現出來了。用戶1和用戶5具有相同的興趣。他們都喜歡101這本書,對102的喜歡弱一些,對103的喜歡更弱。同理,用戶1和4具有相同的興趣,他們都喜歡101和103,沒有信息顯示用戶4喜歡102。另一方面,用戶1和用戶2的興趣好像正好相反,用戶1喜歡 101,但用戶2討厭101,用戶1喜歡103而用戶2正好相反。用戶1和3的交集很少,只有101這本書顯示了他們的興趣。看圖2.1可能顯現了 user和item之間的關系,可能是正的也可能是負的。
2.2.2 創建推薦系統
那么你應該給用戶1推薦哪本書?不是101, 102或者103,因為用戶已經知道自己對他們感興趣,推薦系統需要發現新的事物。直覺告訴我們,用戶4、5與用戶1類似,所以推薦一些用戶4和5喜歡的 書籍給用戶1可能是不錯的。這樣使得104、105和106成為可能的推薦。整體上看,104是最有可能的一個推薦,這基於item 104的4.5和4.0的偏好打分。現在運行下面的代碼:
Listing 2.2 a simple user-based recommender program withmahout 代碼
import java.io.File; import java.io.IOException; import java.util.List; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.eval.RecommenderEvaluator; import org.apache.mahout.cf.taste.impl.common.LongPrimitiveIterator; import org.apache.mahout.cf.taste.impl.eval.AverageAbsoluteDifferenceRecommenderEvaluator; import org.apache.mahout.cf.taste.impl.model.file.FileDataModel; import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood; import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender; import org.apache.mahout.cf.taste.impl.similarity.EuclideanDistanceSimilarity; import org.apache.mahout.cf.taste.model.DataModel; import org.apache.mahout.cf.taste.recommender.RecommendedItem; import org.apache.mahout.cf.taste.recommender.Recommender; import org.apache.mahout.cf.taste.similarity.UserSimilarity; public class UserCF { final static int NEIGHBORHOOD_NUM = 2; final static int RECOMMENDER_NUM = 3; public static void main(String[] args) throws IOException, TasteException { String file = "D:/yangkl/人工智能/datafile/item.csv"; DataModel model = new FileDataModel(new File(file)); UserSimilarity user = new EuclideanDistanceSimilarity(model); NearestNUserNeighborhood neighbor = new NearestNUserNeighborhood(NEIGHBORHOOD_NUM, user, model); Recommender r = new GenericUserBasedRecommender(model, neighbor, user); LongPrimitiveIterator iter = model.getUserIDs(); while (iter.hasNext()) { long uid = iter.nextLong(); List<RecommendedItem> list1 = r.recommend(uid, RECOMMENDER_NUM); System.out.printf("uid:%s", uid); for (RecommendedItem ritem : list1) { System.out.printf("(%s,%f)", ritem.getItemID(), ritem.getValue()); } System.out.println(); } } }
A 加載數據文件
B 創建推薦系統引擎Createthe recommender engine
C 對user1, 推薦一個item
為了簡潔,后面許多其他章節的示例中,代碼列表將省略imports、類聲明、方法聲明,只是重復程序語句。為展示很好的展示各個模塊之間的關系,請看圖2.2。並不是Mahout中所有的推薦都是這樣的,但這幅圖可以給你一個樣例的邏輯的初步印象。
接下來兩章,在更細節的討論這些模塊之前,我們可以總結一下每個模塊所扮演的角色。DataModel存儲了所有的偏好信息,提供了對user 和item信息的訪問。UserSimiliarity提供了兩個用戶如何相似的概念,這可能基於很多可能的矩陣和計算之一。 UserNeighborhood定義了一個給定用戶的用戶組的概念。最終,一個推薦系統將這些模塊組合在一起將items推薦給users和相關功能。
2.2.3 分析輸出
使用你細化的IDE編譯運行,運行程序的輸出應該是:RecommendedItem[item:104,value:4.257081]
請求一個推薦結果並得到一個。推薦系統引擎將書104推薦給用戶1。甚至,這樣做是因為推薦系統引擎將用戶1對書104的偏好是4.3,這是所有推薦結果的最高打分。
這個結果並不算壞。107沒有出現,本應該也是可以推薦的,但它只是和另一個具有不同愛好的user相關聯。選104而不是106,因為104的打分高一些。還有,輸出結果包含了一個用戶1喜歡104的評估值 — 是介於用戶4和5所表示的介於4.0和4.5的一個值。
直接看數據正確的結果並不明顯,但是推薦系統引擎推薦了一個得體的結果。如果對從這個簡單程序給出的有用並不明顯的結果感到有一種愉快的刺痛,那么機器學習的世界是適合你的。
小數據集、產生推薦結果是一件微不足道的事情。現實中,數據集很大,並且噪聲數據很多。例如,想象一個新聞網站推薦新聞給用戶。偏好從文章的點 擊中獲取。但是,這里面的很多點擊都是偽造的 — 可能很多讀者點擊一篇文章但他不一定喜歡它,或者點錯了。可能很多點擊都是在未登錄的時候發生的,不能將其關聯到一個用戶。想象一下數據集有多大,可能是每月幾十億的點擊量。
要從數據集產生正確的推薦結果並快速計算出是一件不一般的事情。稍后我們將展示工具Mahout如何解決這些問題。他將展示標准方法如何產生差的推薦結果或者占用了大量的cpu和內存時間,如何配置Mahout以提升性能。
2.3 評價推薦系統
整個評估的原理就是將數據集中的一部分數據作為測試數據。也就是這一部分數據程序不可見,然后推薦引擎通過剩余的訓練數據推測測試數據的值,然后將推測值與真實的測試數據進行比較。
比較的方法有平均差值,就是每一項與真實數據做差然后求平均值,另一種方式就是均方根。就是差值求平方和,然后求平方和的平均值,然后取平方根。這種方式會放大差值的影響,比如推薦的結果中差一個星值,產生的影響遠大於1倍的影響就可以使用這種評估算法。
“真實偏好”並不充分,沒有人會知道你將來是否會喜歡一些新的item。推薦引擎可以通過設置一部分真實數據作為測試數據。這些測試數據偏好在訓練集中並不展示偏好值 — 要求推薦系統對這些缺少偏好值的數據作出評估,並比較和實際值的差距。
對於推薦系統產生一系列打分是很簡單的。例如,
計算評估值和實際值之間的平均距離,在這種方法下,分值越低越好。0.0表示非常好的評估 — 評估值和實際值根本沒有差距。
均方根(root-mean-square)也是一種方法,也是分值越低越好。
上面的表中展示了實際偏好度和評估偏好度集合的不同值,以及如何將它們轉化為打分。均方根能比較重的處罰距離遠的,例如item2,這是基於某種考慮在內的。因為平均距離容易理解,接下來的示例將使用它作為評估方法。
2.3.1 運行RecommenderEvaluator
下面是代碼示例:
大部分的操作發生在evaluate()這個方法中。內部,RecommenderEvaluator將數據划分為訓練集和測試集,創建一個新的訓練DataModel和推薦引擎測試,比價評估結果和實際結果。
注意,沒有將Recommender傳給方法,這是因為在其內部,將基於創建的訓練集的DataModel創建一個Recommender。所以調用者必須提供一個RecommenderBuilder對象用於從DataModel創建Recommender。
2.3.3 評估結果
程序打印出了評估結果:一個表明推薦系統表現如何的打分。在這種情況下你能看到很簡單的1.0。盡管評價器內部有很多隨機方法去選擇測試數據,結果可能是一致的因為RandomUtils.useTestSeed()的使用,每次選取的隨機數都一樣。這只用於示例、單元測試來保證重復的結果。不要在真是數據上用它。
AverageAbsoluteDifferenceRecommenderEvaluator
基於AverageAbsoluteDifferenceRecommenderEvaluator實現,得到的這個值是什么含義?1.0意味着,平均意義上,推薦系統評估偏好和實際偏好的的距離是1.0.
1.0早1-5規模上並不大,但是我們的數據太少。如果數據集被隨機划分結果可能不一樣,因此訓練、測試數據集可能每次跑都不一樣。
這種技術可以應用於任何Recommender和DataModel。使用均方根打分的實現類RMSRecommenderEvaluator
替代AverageAbsoluteDifferenceRecommenderEvaluator。
evaluate()的null參數是DataModelBuilder的實例,用於控制訓練DataModel是如何從訓練數據上建立的。正常情況下默認就好,如果需要,可以使用特別實現的DataModel。DataModelBuilder用於將DataModel注入評價過程中。
參數1.0表示使用整個數據集的比例。這樣用於產生一個很快的、可能精度低一些的評價方式。例如,0.1可能意味着用數據集的10%,忽略其他90%。這對於快速檢測到Recommender的細微變化是非常有用的。
2.4 評估准確率和召回率
借用更普遍的看法,我們接收經典的信息檢索矩陣去評價推薦系統:准確率和召回率。這些是用於搜索引擎的術語,通過query從眾多可能結果中返回最好結果集。
一個搜索引擎不應該在靠前的結果中返回不相關的搜索結果,即使他致力於得到盡可能多的相關結果。”准確率”是指在靠前的結果中相關結果所占的比 例,當然這種相關是某種程度上我們定義的相關。”precisionat 10″應該是從前10個結果中判斷得到的准確率。“召回率”靠前的結果中相關結果占的比例。看圖2.3可以有一些直觀的概念。
這些術語也可以應用到推薦系統中:准確率是靠前的推薦中好的推薦所占的比例,召回率是指出現在靠前推薦中好的推薦占整個好的推薦的比例。
2.4.1 運行RecommenderIRStatsEvaluator
Mahout提供了非常簡單的方式為推薦系統計算結果。
2.5 評價GroupLen數據集
有了這些工具在手,我們不僅可以考慮速度,還要考慮推薦系統的效率。盡管大量數據集的例子還在幾章以后,小數據集上評價性能已成為可能。
2.5.1 抽取推薦系統輸入
GroupLens (http://grouplens.org/)是一個研究型的項目。提供了很多不同大小的數據集。每個都是用戶對電影打分的真實數據。這是幾個大的真實可用數據集之一,更多的將會在本書后續探尋。從GroupLens網站上下載“100K data set”,http://www.grouplens.org/node/73.解壓下載文件,在解壓后后,有一個叫ua.base的文件。該件tab分割userIDs, itemIDs, ratings(偏好值)和其他信息。
這個文件能使用嗎?Tabs, 不是逗號,分隔字段,在每行結尾還有一個額外的字段。答案是肯定的。該文件可以類似FileDataModel的讀取。回到前面列表2.3,試着用 ua.base的路徑代替小文件路徑。重新跑一遍數據。這時,評估可能需要幾分鍾,因為數據量是100k。
最后,你應該得到一個大約0.9的值。這不算壞,盡管值幾乎分布在1-5區間內,聽起來還不錯。可能特殊的推薦系統對這種類型的數據來講是不完全的?
2.5.2 其他推薦系統實驗
在這個數據集上測試“slope-one” ,一個簡單的算法,在后面的章節中會講到。很容易替換RecommenderBuilder,使用org.apache.mahout.cf.taste.impl.recommender.slopeone.SlopeOneRecommeder, 像這樣:
運行評價方法。你應該發現它很快,得到一個大約是0.748的值。正朝好的方向發展。
這並不能說明slope-one算法總是很快、很好。在給定數據集上,每一個算法都有自己的特性和屬性,這些屬性很難去檢測出來。例如,slope-one算法運行時能夠很快的計算推薦過程,但是它需要在計算前需要花費很大一部分時間構建內部數據結構。基於用戶的推薦引擎,能夠在其他數據集上擁有很快的計算速度和更高的准確度,我們將在第四章探索各種算法的優點。
2.6 總結
在這一章我們介紹了推薦引擎的思想。我們創建了一些簡單的Mahout Recommender的輸入,運行一個簡單計算,然后輸出結果。
然后,我們花時間介紹了推薦引擎系統的數據結果的質量評價,因為接下來的章節會頻繁用到。這一章包含了評價推薦引擎的准確性,像傳統的准確性和召回率。最后,我們嘗試去評價GroupLens的真實數據集,觀察如何將評價結果用於推薦引擎的性能提升上。
在我們繼續詳細學推薦引擎之前,需要花一些時間去了解另一個Mahout中推薦系統的基本概念:數據表示。接下來一章會着重講這一點。