HDC調(diào)試需求開發(fā)(15萬預(yù)算),能者速來!>>>
簡介
日志分析往往是商業(yè)智能的基礎(chǔ),而日益增長的日志信息條目使得大規(guī)模數(shù)據(jù)處理平臺的出現(xiàn)成為必然。MapReduce 處理數(shù)據(jù)的有效性為日志分析提供了可靠的后盾。
本文將以對訪問網(wǎng)頁用戶的日志進(jìn)行分析,進(jìn)而挖掘出用戶興趣點(diǎn)這一完整流程為例,詳細(xì)解釋 MapReduce 模型的對應(yīng)實現(xiàn),涵蓋在 MapReduce 編程中對于特殊問題的處理技巧,比如機(jī)器學(xué)習(xí)算法、排序算法、索引機(jī)制、連接機(jī)制等。文章分三部分展開:首先介紹 MapReduce 編程模型,對其原理、對任務(wù)處理流程以及適用情況進(jìn)行介紹;接下來描述了日志分析的例子 - 用戶興趣點(diǎn)挖掘的處理流程;最后對處理流程的幾個模塊分別進(jìn)行了 MapReduce 的實現(xiàn)。本文的目的在于通過 MapReduce 在日志分析領(lǐng)域的具體實現(xiàn),使讀者對 MapReduce 對實際問題的處理有較為形象的認(rèn)識。
MapReduce 編程模型簡介
隨著信息化的進(jìn)一步加深,在各個領(lǐng)域,如電信、交通、金融、零售、航天、醫(yī)藥等,數(shù)據(jù)量級都呈現(xiàn)快速增長趨勢。如何高效并且無誤地存儲、分析、理解以及利用這些大規(guī)模數(shù)據(jù),成為一個關(guān)鍵性問題。
為了應(yīng)對大規(guī)模數(shù)據(jù)處理的難題,MapReduce 編程模型應(yīng)運(yùn)而生。Google 提出的這一模型,由于良好的易用性和可擴(kuò)展性,得到了工業(yè)界和學(xué)術(shù)界的廣泛支持。Hadoop,MapReduce 的開源實現(xiàn),已經(jīng)在 Yahoo!, Facebook, IBM, 百度 , 中國移動等多家單位中使用。
MapReduce 編程模型
MapReduce 以函數(shù)方式提供了 Map 和 Reduce 來進(jìn)行分布式計算。Map 相對獨(dú)立且并行運(yùn)行,對存儲系統(tǒng)中的文件按行處理,并產(chǎn)生鍵值(key/value)對。Reduce 以 Map 的輸出作為輸入,相同 key 的記錄匯聚到同一 reduce,reduce 對這組記錄進(jìn)行操作,并產(chǎn)生新的數(shù)據(jù)集。所有 Reduce 任務(wù)的輸出組成最終結(jié)果。形式化描述如下:
Map: (k1,v1) -> list(k2,v2)
Reduce:(k2,list(v2)) ->list(v3)
MapReduce 對任務(wù)的處理流程如圖 1 所示。主要分為幾步: 用戶提交 MapReduce 程序至主控節(jié)點(diǎn),主控節(jié)點(diǎn)將輸入文件劃分成若干分片(split)。主控節(jié)點(diǎn) Master 和工作節(jié)點(diǎn) worker 啟動相應(yīng)進(jìn)程; 主控節(jié)點(diǎn)根據(jù)工作節(jié)點(diǎn)實際情況,進(jìn)行 map 任務(wù)的分配; 被分配到 map 任務(wù)的節(jié)點(diǎn)讀取文件的一個分片,按行進(jìn)行 map 處理,將結(jié)果存在本地。結(jié)果分成 R 個分片進(jìn)行存儲,R 對應(yīng)的是 Reduce 數(shù)目; Map 節(jié)點(diǎn)將存儲文件的信息傳遞給 Master 主控節(jié)點(diǎn),Master 指定 Reduce 任務(wù)運(yùn)行節(jié)點(diǎn),并告知數(shù)據(jù)獲取節(jié)點(diǎn)信息; Reduce 節(jié)點(diǎn)根據(jù) Master 傳遞的信息去 map 節(jié)點(diǎn)遠(yuǎn)程讀取數(shù)據(jù)。因為 reduce 函數(shù)按分組進(jìn)行處理,key 相同的記錄被一同處理,在 reduce 節(jié)點(diǎn)正式處理前,對所有的記錄按照 key 排序; Reduce 將處理結(jié)果寫入到分布式文件系統(tǒng)中。 圖 1 . MapReduce 處理流程圖
MapReduce 適用情況
由于 MapReduce 編程模型是對輸入按行順次處理,它更適用于對批量數(shù)據(jù)進(jìn)行處理。由于良好的可擴(kuò)展性,MapReduce 尤其適用于對大規(guī)模數(shù)據(jù)的處理。
但是,對搜索等只是需要從大量數(shù)據(jù)中選取某幾條特別的操作,MapReduce 相對于具有完善索引的系統(tǒng)而言,不再具有優(yōu)勢。因為它需要對每條數(shù)據(jù)進(jìn)行匹配,并與搜索條件相匹配的數(shù)據(jù)提取出來。而如果采用索引系統(tǒng),并不需要遍歷所有的數(shù)據(jù)。
另外,由于每次操作需要遍歷所有數(shù)據(jù),MapReduce 并不適用于需要實時響應(yīng)的系統(tǒng)。相反地,對于搜索引擎的預(yù)處理工作比如網(wǎng)頁爬蟲、數(shù)據(jù)清洗,以及日志分析等實時性要求不高的后臺處理工作,MapReduce 編程模型是足以勝任的。
日志分析應(yīng)用
互聯(lián)網(wǎng)或者大型應(yīng)用系統(tǒng)中,日志的產(chǎn)生和記錄是非常重要的事情。日志分析則是進(jìn)行數(shù)據(jù)挖掘進(jìn)而推進(jìn)下一步工作的基礎(chǔ)。比如,在購物網(wǎng)站,針 對用戶訪問網(wǎng)頁的信息,可以挖掘出用戶的興趣點(diǎn),進(jìn)而進(jìn)行物品推薦;又比如,在應(yīng)用系統(tǒng)中,通過分析用戶對系統(tǒng)部件的使用情況,可以挖掘出該系統(tǒng)中的熱點(diǎn) 部件,進(jìn)而采取相應(yīng)的措施加強(qiáng)管理;典型地,對于一個醫(yī)療衛(wèi)生系統(tǒng),根據(jù)醫(yī)生對不同病情開處方的日志記錄,可以挖掘出某種病情和藥品的對應(yīng)關(guān)系,進(jìn)而建立 一個專家推薦系統(tǒng)等。
隨著互聯(lián)網(wǎng)行業(yè)的壯大和應(yīng)用系統(tǒng)規(guī)模的擴(kuò)充,記錄相應(yīng)信息的日志數(shù)量級也在急劇擴(kuò)充。傳統(tǒng)的單機(jī)版分析程序已經(jīng)不能滿足日志分析的需求,為 此,大規(guī)模數(shù)據(jù)處理平臺成為日志分析的理想平臺。另一方面,日志分析并沒有很高的實時性要求,MapReduce 編程模型由于易用性強(qiáng)、處理數(shù)據(jù)規(guī)模大,成為日志分析的利器。
本文下面部分會以用戶訪問網(wǎng)頁日志為例,闡述如何利用 MapReduce 來分析日志,進(jìn)而挖掘出相應(yīng)信息。
用戶訪問網(wǎng)頁行為建模
一般而言 , 用戶每訪問網(wǎng)頁時 , 系統(tǒng)日志中會存儲一條記錄 : 用戶 + url + 訪問時間。用戶訪問的一系列網(wǎng)頁記錄即是推斷用戶興趣點(diǎn)的基礎(chǔ),即:用戶 + urlSet。
如何根據(jù)用戶訪問的系列 URL 信息來推測用戶興趣點(diǎn)?一般而言,由以下幾個步驟構(gòu)成: 單一網(wǎng)頁信息挖掘。根據(jù) URL 得到網(wǎng)頁內(nèi)容信息,并對網(wǎng)頁內(nèi)容進(jìn)行處理,得到代表此網(wǎng)頁的幾個關(guān)鍵詞,一般要借助機(jī)器學(xué)習(xí)算法或者專家經(jīng)驗來攫取較有價值的詞。 用戶訪問關(guān)鍵詞信息匯總。匯總用戶訪問的各個 URL 中的所有關(guān)鍵詞信息,進(jìn)而得到用戶關(guān)注的關(guān)鍵詞列表。每個關(guān)鍵詞均有不同權(quán)重,視該詞在 URL 中出現(xiàn)的次數(shù)而定。 關(guān)鍵詞擴(kuò)展及歸約。對用戶關(guān)注關(guān)鍵詞列表進(jìn)行一定的擴(kuò)展或歸約操作,得到更加具有普遍意義的詞信息,以更好地表征用戶的興趣點(diǎn)。 圖 2 .用戶興趣挖掘流程圖
單一網(wǎng)頁信息挖掘
從 URL 得到該網(wǎng)頁中有價值的詞信息,首先要對 URL 進(jìn)行重新爬取,以得到其對應(yīng)的網(wǎng)頁內(nèi)容。從網(wǎng)頁中提取關(guān)鍵詞,則需要一定的算法支持。在一篇網(wǎng)頁中,不同詞因為在不同位置或者以不同的格式出現(xiàn),對應(yīng)影響 程度也不同。比如,在網(wǎng)頁的標(biāo)題或者網(wǎng)頁內(nèi)容每一自然段段首或者段尾的詞可能更為重要;在網(wǎng)頁中有特定格式,比如加粗或者字體較大或者標(biāo)記顏色的詞可能更 為重要。
而給定一個詞,如何標(biāo)記其重要性或者對網(wǎng)頁的價值?可以將每個詞用向量的形式來進(jìn)行描述,向量中每一維度 d 表示不同的衡量標(biāo)準(zhǔn),比如 TF(在該網(wǎng)頁中出現(xiàn)的次數(shù))、DF(在所有網(wǎng)頁中出現(xiàn)的次數(shù))、是否在標(biāo)題中出現(xiàn)、是否在段首 / 段尾出現(xiàn)、是否在句首 / 句尾出現(xiàn)、顏色有無區(qū)別其它詞、詞的詞性是名詞 / 動詞 / 形容詞 / 助詞,等等。形如 w = (v1, v2, v3, v4, … ),每個維度 v 對詞是網(wǎng)頁關(guān)鍵詞的決定程度不同,這個影響因子可以通過機(jī)器學(xué)習(xí)算法訓(xùn)練而得。即:由事先指定關(guān)鍵詞的網(wǎng)頁來進(jìn)行訓(xùn)練,得到特征權(quán)重。
得到特征權(quán)重后,對于網(wǎng)頁中的每個詞,可以通過 w = sum(vi*fi) 的方式來得到其作為關(guān)鍵詞的比例。從網(wǎng)頁中選取能代表其內(nèi)容的幾個詞,應(yīng)對所有計算出權(quán)重的詞按權(quán)重從大到小依次排序,選取前幾名或者大于某個閾值的詞即可。
解決思路如圖 3 所示。 圖 3 .網(wǎng)頁關(guān)鍵詞挖掘流程圖
用戶訪問關(guān)鍵詞匯總
得到每個網(wǎng)頁的代表關(guān)鍵詞后,對于用戶訪問的關(guān)鍵詞,即可通過匯總用戶訪問的所有網(wǎng)頁的關(guān)鍵詞得到。簡單而言,用戶訪問每個詞的次數(shù)可以作 為該詞為用戶關(guān)注詞的權(quán)重。因為后面還要進(jìn)行進(jìn)一步的關(guān)鍵詞擴(kuò)展 / 歸約,為防止數(shù)值過大的情況,可以對權(quán)重進(jìn)行歸一化。當(dāng)然,也可以再加入其它策略,對詞的權(quán)重進(jìn)行進(jìn)一步調(diào)整,比如通過黑名單或者詞的共現(xiàn)頻率等方式將垃 圾詞(對描述用戶興趣點(diǎn)無作用的詞,比如“網(wǎng)易”、“淘寶”等)位置后調(diào),此處不再詳加展開。
關(guān)鍵詞擴(kuò)展及歸約 關(guān)鍵詞擴(kuò)展
上一步得到的結(jié)果是用戶在日志所記錄的時間內(nèi)訪問的關(guān)鍵詞匯總,而詞與詞之間往往是相互關(guān)聯(lián)的。比如,用戶訪問了“籃球”這個詞,那實際上 該用戶也很有可能對“球星”這個詞感興趣,因為“籃球”和“球星”兩詞存在一定關(guān)聯(lián)??梢酝ㄟ^關(guān)鍵詞擴(kuò)展,推斷出用戶對“球星”一詞也感興趣。
如何得到兩個詞之間的相關(guān)度?一般而言,同時在一個網(wǎng)頁元信息(meta)的 keyword 域里的詞很大程度是相關(guān)的。因此,統(tǒng)計 meta 中詞,就可以統(tǒng)計出來詞與詞的相關(guān)度信息。因為用戶訪問的詞與詞之間并不是孤立完全無關(guān)的,而是有一定關(guān)聯(lián)的。把 meta 中詞與詞的相關(guān)性信息加入到用戶對各個詞的關(guān)注度中,可以更好地體現(xiàn)用戶的關(guān)注方面。加入 meta 相關(guān)詞信息后,便得到了更加精確的用戶對詞的關(guān)注度列表。
網(wǎng)頁 meta 中 keyword 區(qū)域放置的往往是該網(wǎng)頁的分類信息 ( 導(dǎo)航類網(wǎng)頁 ),或者是該網(wǎng)頁的關(guān)鍵詞(正文類網(wǎng)頁)。如果是前者,meta 中的詞往往相關(guān)度較大。而不同網(wǎng)站的 meta 內(nèi)容是什么,是由網(wǎng)站的編輯決定的。所以把不同網(wǎng)站的 meta 里面共現(xiàn)的詞提取出來并匯總到一起,其實是匯聚了各個網(wǎng)站編輯們的集體智慧。這些詞的共現(xiàn)信息應(yīng)該能夠很好地表現(xiàn)出詞與詞之間的相關(guān)性。如果兩個詞總在一 起出現(xiàn),它們極有可能是相關(guān)的。
具體流程為:首先,統(tǒng)計 meta 中共同出現(xiàn)的詞對以及它們共同出現(xiàn)的次數(shù);然后,統(tǒng)計這些共現(xiàn)的詞對中的詞每個詞出現(xiàn)的次數(shù);最后,應(yīng)用公式進(jìn)行共現(xiàn)頻率的計算,得到的就是詞與詞之間的相關(guān)度。計算公式為
其中,p ij 表示詞 i 和詞 j 的相關(guān)度,m i 、m j 、m ij 分別表示詞 i、詞 j 以及詞 i 和 j 共同在網(wǎng)頁元信息(meta)的 keyword 中出現(xiàn)的次數(shù)。 圖 4 .用戶訪問關(guān)鍵詞擴(kuò)展流程圖
圖 4 描述了將詞之間的相關(guān)度加入用戶訪問關(guān)鍵詞列表中的流程:首先得到所有詞對之間的相關(guān)度信息,并以索引形式存儲;然后,對之前得到的用戶訪問關(guān)鍵詞列表中 的每個詞,查找索引得到相關(guān)的詞,如果該詞未被用戶訪問過,直接將其加入到用戶訪問列表中;否則,對兩個詞的權(quán)重都需進(jìn)行調(diào)整。 關(guān)鍵詞歸約
與關(guān)鍵詞擴(kuò)展相對應(yīng)的是關(guān)鍵詞歸約。用戶訪問的網(wǎng)頁中挖掘出的關(guān)鍵詞往往是具體的,比如用戶關(guān)注的網(wǎng)頁中提取出的詞是“足球”、“籃球”,而這些詞在劃分的時候都屬于體育類,通過關(guān)鍵詞歸約,可以推測出該用戶對”體育”比較感興趣。
而分類標(biāo)準(zhǔn)應(yīng)該如何獲得呢?在各大門戶網(wǎng)站如新浪、網(wǎng)易的首頁,都有諸如天氣、新聞、教育、軍事等各大類,在每一大類里又有各小類;在淘寶、易趣等網(wǎng)上交易平臺,更是有對商品的多級詳細(xì)分類。關(guān)鍵詞歸約,就是根據(jù)用戶訪問的關(guān)鍵詞追溯到用戶對哪些類別的內(nèi)容感興趣。
無論是關(guān)鍵詞擴(kuò)展還是歸約,都會得到更加精確的用戶訪問關(guān)鍵詞列表,對所有詞按權(quán)重由大到小進(jìn)行排列,描述的即是用戶的興趣點(diǎn)。
MapReduce 對用戶興趣挖掘的實現(xiàn)
上一部分介紹了用戶興趣點(diǎn)挖掘的流程,本部分將針對各個模塊進(jìn)行 MapReduce 的實現(xiàn)。整個應(yīng)用的輸入是用戶訪問網(wǎng)頁記錄組成的文件,文件每行表示用戶訪問網(wǎng)頁的一條記錄,形為
“用戶 URL”。期望輸出為用戶的興趣點(diǎn)文件,文件每行存儲每個用戶的興趣點(diǎn),形為:
“用戶 詞 1 權(quán)重 1 詞 2 權(quán)重 2 詞 3 權(quán)重 3 ”。
下面會對三個步驟分別講解 MapReduce 實現(xiàn)。
單一網(wǎng)頁信息挖掘
單一網(wǎng)頁信息挖掘的目的是選取出網(wǎng)頁中相對重要的關(guān)鍵詞。策略為每個詞賦予權(quán)重,并選取權(quán)重較大的詞。詞的權(quán)重獲取公式 v = sum(vi*fi) 由兩部分決定:該詞在每個特征上的取值和該特征的權(quán)重。
每個特征的權(quán)重,可由訓(xùn)練得到,輸入為給出關(guān)鍵詞的系列網(wǎng)頁。特征權(quán)重訓(xùn)練通常有特定的算法,比如 SCGIS 算法,因為訓(xùn)練集相對于整體的輸入集較小,而算法通常也較復(fù)雜,并不適合并行化,可在 MapReduce 任務(wù)開始之前進(jìn)行特征權(quán)重訓(xùn)練。
而詞在每個特征維度上對應(yīng)的取值,視特征的不同,難易程度也不同。比如詞的出現(xiàn)位置、大小寫、詞性等,在對網(wǎng)頁進(jìn)行掃描時,可以立即獲得。 而 TF( 詞在網(wǎng)頁中出現(xiàn)的次數(shù) )、DF(詞在所有網(wǎng)頁中出現(xiàn)的次數(shù))等特征并不能隨詞出現(xiàn)時立即獲取,但由于每個詞處理的程序都相同,所以可以使用 MapReduce 編程模型并行化。下面進(jìn)行具體講述。
單一網(wǎng)頁信息挖掘部分的 MapReduce 流程如圖 5 所示。 圖 5.MapReduce 實現(xiàn)單一網(wǎng)頁信息挖掘 TF 詞在網(wǎng)頁中出現(xiàn)次數(shù)信息統(tǒng)計
Map:輸入為用戶 +url 列表,對于單條記錄,進(jìn)行 url 爬蟲和分詞,得到用戶訪問的該網(wǎng)頁中包含所有詞的信息。每遇到一個詞,Map 進(jìn)行一次輸出,key 為用戶 + 網(wǎng)頁 + 詞,value 為 1。當(dāng)然,此時 Map 還可以統(tǒng)計其它信息,比如詞性(名詞 / 動詞)等,為簡化描述,此處不再詳加展開。
Reduce:Map 輸出結(jié)果中 key 相同的匯聚到一起,Reduce 對每組統(tǒng)計其包含記錄條數(shù),將用戶 + 網(wǎng)頁 + 詞仍然作為 key 進(jìn)行輸出,將每組中記錄條數(shù)作為 value 進(jìn)行輸出。
這樣,Reduce 的輸出結(jié)果文件每行對應(yīng)記錄為“ 用戶 + 網(wǎng)頁 + 詞 詞的 TF”。 清單 1. MapReduce 統(tǒng)計 TF
public class TFCal extends Configured implements Tool, Mapper,Reducer{ public void map(Text usr, Text url, OutputCollector output, Reporter reporter)throws IOException { Text[] words = callCrawl(url); // 調(diào)用爬蟲程序 for(Text word: words) // 每個詞進(jìn)行輸出 output.collect(usr + url + word, new IntWritable(1)); } public void reduce(Text key, Iterator iter,OutputCollector output, Reporter reporter) throws IOException { tf = iter 中包含元素的數(shù)目 ; output.collect(key, tf); } public void runCal(Path input, Path output) throws IOException { JobConf job = new JobConf(getConf(), TFCal.class); job.setInputPath(input); job.setOutputPath(output); job.setMapperClass(TFCal.class); job.setMapperClass(TFCal.class); job.setInputFormat(SequenceFileInputFormat.class); job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); JobClient.runJob(job); } } |
---|
如果網(wǎng)頁中內(nèi)容足以在內(nèi)存中處理,也可以只在 Map 階段完成對每個詞 TF 的統(tǒng)計,這樣可以省去 Map 和 Reduce 之間大量數(shù)據(jù)傳輸?shù)臅r間消耗。具體處理思路為:采用一個數(shù)據(jù)結(jié)構(gòu) hashMap
,Map 的每行輸入仍為用戶 +url 信息,對 url 網(wǎng)頁爬蟲的結(jié)果,每遇到一個詞,將其信息加入到 hashMap 中,key 為詞,value 為原來 value+1,與此同時,還可以統(tǒng)計該詞的其余特征值,比如詞性等。因為 Map 階段即可完成 TF 統(tǒng)計,可以將 Reduce 數(shù)目設(shè)為 0。 清單 2. Map 統(tǒng)計 TF public class TFCal2 extends Configured implements Tool, Mapper{ public void map(Text usr, Text url, OutputCollector output, Reporter reporter)throws IOException { HashMap wordCount = new HashMap(); //HashMap 統(tǒng)計 TF Text[] words = callCrawl(url); // 調(diào)用爬蟲程序 for(Text word: words){ // 統(tǒng)計詞次數(shù)信息 int cnt = wordCount.get(word); wordCount.put(word,(cnt>0)?(cnt+1):1); } Iterator iter = wordCount.entrySet().iterator(); while(iter.hasNext()){ Map.Entry entry = iter.next(); // Map 輸出,key 為用戶 +url+ 詞,value 為 TF output.collect(usr + url + entry.getKey(), entry.getValue()); } } public void runCal(Path input, Path output) throws IOException { JobConf job = new JobConf(getConf(), TFCal2.class); 設(shè)置 InputPath, outPath, MapperClass, InputFormat, OutputFormat, … job.setReduceNum(0); //Reduce 數(shù)目設(shè)為 0,不進(jìn)行 Reduce 操作。 JobClient.runJob(job); } } |
---|
DF 詞在所有網(wǎng)頁中出現(xiàn)次數(shù)信息統(tǒng)計
Map 輸入為 TF 計算階段輸出,key 為用戶 + 網(wǎng)頁 + 詞,value 為詞的 TF。Map 階段處理,將網(wǎng)頁信息去除,輸出 key 為用戶 + 詞,輸出 value 為 1。
Reduce 以 Map 輸出為輸入,用戶訪問詞相同的會被同一個 reduce 處理,reduce 中統(tǒng)計該組包含記錄的數(shù)目,即是詞的 DF。由于在計算每個詞權(quán)重時,需要得到各個特征的值,而在計算 TF 的時候可以得到該詞除 DF 外其它特征的信息,所以需要額外地獲取 DF 信息。為方便查詢 DF 的信息,應(yīng)將 Reduce 階段的輸出以索引文件的形式進(jìn)行輸出。Lucene 是可在 MapReduce 開源框架 Hadoop 上部署的索引機(jī)制,可在 Reduce 輸出時采用,key 為用戶 + 詞,value 為 DF。 清單 3. MapRedcue 統(tǒng)計 DF public class DFCal extends Configured implements Tool, Mapper,Reducer{ public void map(Text key, IntWritable url, OutputCollector output, Reporter reporter)throws IOException { 將 key 拆分成 user,url,word 三部分 output.collect(user+word, new IntWritable(1); } public void reduce(Text key, Iterator iter, OutputCollector output, Reporter reporter)throws IOException { int df = iter 中包含元素數(shù)目 ; // 建立 Lucene 索引,以 user+word 為 key,DF 作為 value,進(jìn)行存儲 Document doc = new Document(); doc.add(new Field("word", key.toString(), Field.Store.NO, Field.Index.UN_TOKENIZED)); doc.add(new Field("DF", df, Field.Store.YES,Field.Index.NO)); output.collect(new Text(), new LuceneDocumentWrapper(doc)); } public void runDFCal(Path input, Path output) throws IOException { JobConf job = new JobConf(getConf(), DFCal.class); 設(shè)置 InputPath, outPath, MapperClass, InputFormat, … job.setOutputFormat(LuceneOutputFormat); // 設(shè)置輸出格式為 LuceneOutputFormat JobClient.runJob(job); 合并各個 reduce 生成的索引文件為一個完整索引文件(Lucene 的 IndexWriter 類提供了相應(yīng)接口) } … . } |
---|
在清單 3 中出現(xiàn)的 LuceneDocumentWrapper 和 LuceneOutputFormat 均是為在 MapReduce 上使用 Lucene 索引文件作為 Map/Reduce 輸出添加的類,兩者分別繼承自 WritableComparable 和 FileOutputFormat。清單 4 提供了兩者的定義。 清單 4. Lucene 在 MapReduce 上的使用 public class LuceneDocumentWrapper implements Writable { private Document doc; public LuceneDocumentWrapper(Document doc) { this.doc = doc; } public void set(Document doc_) { doc = doc_; } public Document get() { return doc; } public void readFields(DataInput in) throws IOException { // intentionally left blank } public void write(DataOutput out) throws IOException { // intentionally left blank } } public class OutputFormat extends org.apache.hadoop.mapred.FileOutputFormat { public RecordWriter getRecordWriter(final FileSystem fs,JobConf job, String name, final Progressable progress) throws IOException { final Path perm = new Path(FileOutputFormat.getOutputPath(job), name); final Path temp = job.getLocalPath("index/_" + Integer.toString( new Random().nextInt())); // 設(shè)置臨時輸出路徑為 Reduce 節(jié)點(diǎn)本地局部路徑 final IndexWriter writer = new IndexWriter(fs.startLocalOutput(perm, temp).toString(), new StandardAnalyzer(), true); // 初始化 IndexWriter return new RecordWriter() { public void write(WritableComparable key, LuceneDocumentWrapper value) throws IOException { // 將 document 加入到索引之中 Document doc = value.get(); writer.addDocument(doc); progress.progress(); } public void close(final Reporter reporter) throws IOException { boolean closed = false; // 標(biāo)識索引是否已經(jīng)輸出完畢 Thread prog = new Thread() { public void run() { 如果索引未輸出完畢 closed != true,保持等待,并設(shè)置 reporter 狀態(tài)為 closing } }; try { prog.start(); writer.optimize(); // 索引進(jìn)行優(yōu)化并關(guān)閉 writer.close(); 拷貝本地輸出至全局文件系統(tǒng) HDFS 中 }finally{ closed = true; } } }; } } |
---|
網(wǎng)頁關(guān)鍵詞計算
獲得 DF 信息后,即可在一個新的 Map 過程中對網(wǎng)頁中每個詞計算其權(quán)重,即成為網(wǎng)頁關(guān)鍵詞的概率,此處可以指定一個閾值,若概率大于該閾值,認(rèn)為該詞可以代表網(wǎng)頁,將其輸出;否則忽略。 清單 5. Map 計算網(wǎng)頁關(guān)鍵詞 public class KeyWordCal extends Configured implements Tool, Mapper{ String fWeights[]; // 記錄特征權(quán)重 IndexSearcher searcher = null; // 用于查詢 Lucene 索引文件 public void map(Text key, Text wordInfo, OutputCollector output, Reporter reporter)throws IOException { 解析 key,從中得到 word 信息 // 查找索引文件,得到 DF Term t = new Term("word", word); Query query = new TermQuery(t); Hits hits = searcher.search(query); if (hits.length() == 1) { Document doc = hits.doc(0); String df = doc.get(“DF”); 從 wordInfo 中提取出來每個特征對應(yīng)取值 , 存儲在數(shù)組 val 中 weight = sum(val[i] × fWeights[i]); // 計算該詞作為關(guān)鍵詞權(quán)重 if(weight >= threshold) // 權(quán)重大于閾值的視為網(wǎng)頁關(guān)鍵詞 output.collect(key, new Writable(1)); // 關(guān)鍵詞輸出,key 包含用戶 + 關(guān)鍵詞,value 為 1 } } // configure 函數(shù)會在每個 Map 節(jié)點(diǎn)運(yùn)行 Map 函數(shù)對文件按行處理之前調(diào)用,通常用來做全局操作 public void configure(JobConf job) { String fWeightPath = job.getStrings(“fWeight.path”)[0]; /// 內(nèi)部獲得特征權(quán)重路徑 讀取特征權(quán)重文件,得到特征權(quán)重列表,填入 fWeights; String dfPath = job.getStrings(“DF.path”)[0]; FsDirectory fsDirectory = new FsDirectory(FileSystem.get(getConf()),dfpath, false, getConf()); searcher = new IndexSearcher(fsDirectory); } public void runkeyWordCal(String input, String output, String DFPath){ String featureWeightFile; SCGIS(featureWeightFile); // 調(diào)用機(jī)器學(xué)習(xí)算法,計算特征權(quán)重,并將權(quán)重存儲在指定文件中 JobConf job = new JobConf(getConf(),KeyWordCal.class); 設(shè)置 InputPath, outPath, MapperClass, InputFormat, OutputFormat, … job.setStrings(“fWeight.path”, featureWeightFile); // 設(shè)置參數(shù),以傳入 Map 和 configure job.setStrings(“DF.path”, DFPath); // 設(shè)置 DF 索引文件位置 JobClient.run(job); } … . } |
---|
用戶訪問關(guān)鍵詞匯總
在用戶關(guān)鍵詞匯總模塊,一共需要兩個完整的 Reduce,流程圖如圖 6 所示。 圖 6.MapReduce 實現(xiàn)用戶關(guān)鍵詞匯總 用戶訪問詞次數(shù)匯總
經(jīng)過單一網(wǎng)頁信息挖掘模塊的處理,對每個網(wǎng)頁處理完畢,輸出的記錄形為“用戶 + 網(wǎng)頁關(guān)鍵詞”,所以只需經(jīng)過一個 Reduce 就能統(tǒng)計出用戶訪問每個詞的次數(shù)。為方便后續(xù)對每個用戶的關(guān)注詞進(jìn)行匯總處理,本 Reduce 的輸出 key 為用戶,value 為詞 + 用戶訪問該詞的次數(shù)。 用戶訪問詞按次數(shù)排序
第二個 Reduce 的輸入為第一個 Reduce 的輸出,在本 Reduce 中,同一用戶訪問的詞會被匯聚到一起,為了更好地描述用戶的興趣點(diǎn),應(yīng)對所有詞按訪問次數(shù)進(jìn)行從大到小進(jìn)行排序,可以通過 Reduce 內(nèi)部對成熟排序算法,比如快速排序的調(diào)用來實現(xiàn)。 詞權(quán)重歸一化
因為不同詞的訪問次數(shù)可能差距較大,比如最常見詞訪問 20 次,而次常見詞可能訪問 10 次,如此大的差距并不利于對詞權(quán)重的進(jìn)一步調(diào)整。所以采用數(shù)據(jù)挖掘領(lǐng)域常見的歸一化策略來對詞權(quán)重進(jìn)行調(diào)整,將其調(diào)整到 [0,1] 區(qū)間內(nèi)。最簡單的策略是將最常見詞的訪問次數(shù)去除訪問每個詞的次數(shù)。Weight(w)=Times(w)/Times(MAX)。這個權(quán)重歸一化的過程 同樣可以在第二個 Reduce 過程中完成。 清單 6. 兩個 Reduce 匯總用戶訪問關(guān)鍵詞 public class UserWordCal1 extends Configured implements Tool, Reducer{ public void reduce(Text key, Iterator iter, OutputCollector output, Reporter reporter)throws IOException { 解析 key,分別得到 user 信息和 word 信息 output.collect(user, new Text(word + iter 中包含元素的個數(shù) )); //value 為用戶訪問該詞次數(shù) } … . } public class UserWordCal2 extends Configured implements Tool, Reducer{ public void reduce(Text key, Iterator iter, OutputCollector output, Reporter reporter)throws IOException { Struct Word; // 定義一個數(shù)據(jù)結(jié)構(gòu),包含兩項,分別存儲 word 和次數(shù)信息 ArrayList wList; 遍歷 iter,將訪問詞的信息填入 wList; QuickSort(wList); // 對 wList 按次數(shù)排序 Normalize(wList); // 對 wList 進(jìn)行權(quán)重歸一化 String wordInfo = “”; for(Word word: wList) // 將詞和對應(yīng)的權(quán)重信息拼接 wordInfo = wordInfo + word + word.getWeight(); output.collect(user, new Text(wordInfo)); } … . } |
---|
MapReduce 自身對排序的支持
實際上,MapReduce 本身的機(jī)制也可以實現(xiàn)排序功能。數(shù)據(jù)從 Map 方法輸出到 Reduce 方法輸入是要經(jīng)過幾個步驟的,包括:Map 端根據(jù) Reduce 數(shù)目對本地輸出進(jìn)行分組;Map 和 Reduce 之間的數(shù)據(jù)傳輸 shuffle;Reduce 端對來自多個 Map 的數(shù)據(jù)按 key 進(jìn)行排序并分組,每組傳入給 Reduce 方法進(jìn)行處理。
從這個過程可以看出,同一個 Reduce 處理的數(shù)據(jù)實際上是按照 key 排序的,如果將 Reduce 數(shù)目設(shè)為 1( job.setReduceNum(1) ),并將排序基準(zhǔn)字段在 Map 方法中設(shè)為 key,就可以實現(xiàn)數(shù)據(jù)的全局排序。
關(guān)鍵詞擴(kuò)展及歸約
關(guān)鍵詞擴(kuò)展和歸約對用戶訪問詞列表進(jìn)行的兩個不同方向的調(diào)整,本節(jié)詳細(xì)介紹一個方向,對關(guān)鍵詞擴(kuò)展進(jìn)行展開。 詞與詞相關(guān)度信息獲取
關(guān)鍵詞擴(kuò)展的關(guān)鍵是得到詞與詞的相關(guān)度,并基于此相關(guān)度對用戶訪問詞列表進(jìn)行調(diào)整。詞與詞之間相關(guān)度的計算公式已在前面小節(jié)列出。 圖 7 .MapReduce 實現(xiàn)詞與詞相關(guān)度計算
圖 7 展示了 MapReduce 計算 p ij 的流程。 統(tǒng)計詞對共現(xiàn)次數(shù)
Map:以用戶訪問 url 信息為輸入,在 Map 內(nèi)部進(jìn)行網(wǎng)頁爬蟲,并只爬取網(wǎng)頁的 meta 中 keyword 域的詞信息,每個網(wǎng)頁中對應(yīng)的詞兩兩組成詞對進(jìn)行輸出,詞對中第一個詞的拼音序 / 字母序要優(yōu)于第二個詞。輸出 key 為詞 1+ 詞 2,value 為 1。
Reduce 統(tǒng)計詞 i 和詞 j 共同出現(xiàn)的次數(shù) m ij ,在 Reduce 內(nèi)部統(tǒng)計每組包含記錄的數(shù)目,仍然以詞 1+ 詞 2 作為 key,將共現(xiàn)次數(shù)作為 value 進(jìn)行輸出。 清單 7. 詞與詞相關(guān)度計算 -1 (詞對共現(xiàn)次數(shù)統(tǒng)計) public class WordsCorrCal1 extends Configured implements Tool, Mapper, Reducer{ public void map(Text key, Text url, OutputCollector output, Reporter reporter)throws IOException { Text[] words = callCrawlKeyWord(url); // 對網(wǎng)頁爬蟲,獲取 meta 中 keyword 域的詞 for(int i = 0; i < words.length(); i++) for(int j = i + 1; j < words.length(); j++) output.collect((words[i] < words[j])? (words[i] + words[j]) : (words[j] + words[i]), new IntWriable(1)); } public void reduce(Text key, Iterator iter, OutputCollector output, Reporter reporter)throws IOException { output.collect(key, iter 中包含元素的數(shù)目 ); //value 為兩個詞共現(xiàn)次數(shù) } … . } |
---|
統(tǒng)計單個詞出現(xiàn)次數(shù)
為計算詞與詞的相關(guān)度,除獲得兩詞共現(xiàn)次數(shù)外,還應(yīng)獲得每個詞的出現(xiàn)次數(shù)??梢酝ㄟ^一個額外的 Map/Reduce 來進(jìn)行統(tǒng)計。其中,Map 以第一個 Reduce 的輸出為輸入,對每個詞對 i 和 j,輸出兩條記錄,key 分別為詞 i 和詞 j,value 均為兩詞共現(xiàn)次數(shù) ;Reduce 完成詞次數(shù)的統(tǒng)計。 清單 8. 詞與詞相關(guān)度計算 -1 (單個詞次數(shù)統(tǒng)計) public class WordsCorrCal2 extends Configured implements Tool, Mapper, Reducer{ public void map(Text wordPair, IntWritable cnt, OutputCollector output, Reporter reporter)throws IOException { 將 wordPair 分為兩個詞 word1, word2 output.collect(word1, cnt); output.collect(word2, cnt); } public void reduce(Text key, Iterator iter, OutputCollector output, Reporter reporter)throws IOException { int wordCnt = 0; while(iter.hasNext()) wordCnt += iter.next().get(); // 建立 Lucene 索引,以 word 為 key,出現(xiàn)次數(shù)作為 value,進(jìn)行存儲 Document doc = new Document(); doc.add(new Field("word", key.toString(), Field.Store.NO, Field.Index.UN_TOKENIZED)); doc.add(new Field("count", wordCnt, Field.Store.YES,Field.Index.NO)); output.collect(new Text(), new LuceneDocumentWrapper(doc)); } … . } |
---|
計算詞對相關(guān)度
現(xiàn)在有兩個文件:一個文件中記錄的是單個詞出現(xiàn)的次數(shù);另一文件記錄的則是詞對出現(xiàn)的次數(shù)。如何從這兩個文件得到詞與詞的相關(guān)度?最直觀的 思路是將單個詞出現(xiàn)次數(shù)作為索引文件輸出,key 為詞,value 為詞的次數(shù);再執(zhí)行一個 Map,以第詞對共現(xiàn)次數(shù)文件為輸入,在處理每條記錄時,查詢索引文件兩次,然后按照共現(xiàn)頻率公式計算得到結(jié)果。 清單 9. 詞與詞相關(guān)度計算 -1 (查找索引文件) public class WordsCorrCal3 extends Configured implements Tool, Mapper{ public void map(Text wordPair, IntWritable cnt, OutputCollector output, Reporter reporter)throws IOException { 將 wordPair 分為兩個詞 word1, word2 查找 Lucene 索引文件 , 得到 word1 出現(xiàn)次數(shù) cnt1 查找 Lucene 索引文件 , 得到 word2 出現(xiàn)次數(shù) cnt2 計算 Pij。Pij = cnt/(cnt1 + cnt2 – cnt); output.collect(wordPair, new FloatWritable(Pij)); } … . } |
---|
MapReduce 中的連接 -- 合并數(shù)據(jù)集的策略
當(dāng)詞信息文件較大,查詢 Lucene 索引文件的效率也會降低,因為對每個詞對,都要查找兩次詞的索引文件,所以查找索引文件的次數(shù)量級在 O(n 2 ),查詢代價也會相對較大??梢杂昧硗庖环N策略完成詞對相關(guān)度的計算。將兩個文件同時作為 Map 的輸入,在 Map 內(nèi)部判斷記錄來自于哪個文件,進(jìn)行對應(yīng)處理,在 Reduce 中完成數(shù)據(jù)集的合并。圖 8 展示了對應(yīng)流程。 圖 8.MapReduce 實現(xiàn)詞與詞相關(guān)度計算 -2 第 1 個 map-reduce 合并單個詞次數(shù)文件和詞對次數(shù)文件。
Map 輸入來自兩個不同文件:單個詞次數(shù)文件和詞對共現(xiàn)次數(shù)文件。Map 內(nèi)部對來自兩個文件的記錄進(jìn)行不同操作,最后拼成相同格式進(jìn)行輸出。輸出的 key 為單個詞,value 形如 “第二個詞 + 共現(xiàn)次數(shù) \t 單個詞次數(shù)”。沒有的部分,以空字符補(bǔ)齊。這樣,對于第一種情況,value 中以 \t 相隔的第一部分就是空字符;而對于第二種情況,value 中以 \t 相隔的第二部分為空字符。
Reduce 遍歷迭代器中記錄,對第一部分為空字符的,將 key 詞對應(yīng)次數(shù)提取出來,否則,記錄對應(yīng)的是與該詞相關(guān)的其他詞及共現(xiàn)次數(shù)信息,把這些信息放到一個數(shù)組。遍歷完畢,對數(shù)組中每個元素,進(jìn)行輸出,輸出 key 為詞 1+ 詞 2+ 共現(xiàn)次數(shù) ( 字典序在前的詞在前 ),value 為 Reduce 輸入 key 詞(詞 1 或者詞 2)的次數(shù)信息。 清單 10. 詞與詞相關(guān)度計算 -2 (拼成統(tǒng)一格式) public class WordsCorrCal_21 extends Configured implements Tool, Mapper, Reducer{ public void map(Text key, IntWritable cnt, OutputCollector output, Reporter reporter)throws IOException { String[] words = key.toString.split(“[\t]”); // 如果對應(yīng)的是詞對的輸入文件 if(words.length() == 2){ output.collect(new Text(words[0]), new Text(words[1] + ”\t” + cnt + “\t”)); output.collect(new Text(words[1]), new Text(words[0] + ”\t” + cnt + “\t”)}; ) else if(words.length() == 1) { // 如果對應(yīng)的是單個詞的輸入文件 output.collect(key, new Text(“\t” + cnt); ) } public void reduce(Text key, Iterator iter, OutputCollector output,Reporter reporter)throws IOException { ArrayList corrWords = new ArrayList(); int wordCnt; while(iter.hasNext()){ String val = iter.next().toString(); String[] vals = val.split(“[\t]”); if(vals.length() == 2) //val 存儲的是單個詞出現(xiàn)次數(shù) wordCnt = Integer.parse(vals[1]); else //val 存儲的是詞對的信息,前兩項分別是共現(xiàn)詞及共現(xiàn)次數(shù) corrWords.add(vals[0]+”\t”+vals[1]); ) for(String corrWord: corrWords){ // 輸出 key 為:詞 1+ 詞 2+ 共現(xiàn)次數(shù);輸出 value:單個詞次數(shù) String[] cor = corrWords.split(“[\t]”); output.collect((key < cor[0])?(key + “\t” + corrWord):(cor[0] + “\t” + key + cor[1]),wordCnt); } } … . } |
---|
第 2 個 map-reduce 計算共現(xiàn)頻率。
含有一個 Reduce,輸入為上一 map-reduce 的輸出,key 為詞 1+ 詞 2+ 共現(xiàn)次數(shù),value 為單個詞的次數(shù)。迭代器里分別得到兩個詞的次數(shù)信息,然后運(yùn)用共現(xiàn)頻率公式計算兩個詞的相關(guān)度。Reduce 的輸出 key 為詞 1+ 詞 2,value 為兩個詞的共現(xiàn)頻率。 清單 11. 詞與詞相關(guān)度計算 -2 (計算相關(guān)度) public class WordsCorrCal_22 extends Configured implements Tool, Reducer{ public void reduce(Text key, Iterator iter, OutputCollector output,Reporter reporter)throws IOException { int word1Cnt = iter.next().get(); int word2Cnt = iter.next().get(); 將 key 解析成 word1,word2,共現(xiàn)次數(shù) corrCnt。 float pij = corrCnt/(word1Cnt + word2Cnt - corrCnt); output.collect(new Text(word1 + word2), new FloatWritable(pij)); } … . } |
---|
第 3 個 map-reduce 建立詞相關(guān)度信息索引文件。
第 3 個 map-reduce 得到每個詞的相關(guān)詞信息,并建立索引文件。Lucene 索引文件的兩個域分別為“word”和“corrInfo”。
Map 的輸入 key 為詞 1+ 詞 2,value 為相關(guān)度。Map 將詞 1 和詞 2 拆開進(jìn)行輸出。輸出 key 為詞 1,value 為詞 2+ 相關(guān)度;輸出 key 為詞 2,value 為詞 1+ 相關(guān)度。
Reduce 把 key 相同的匯聚到一起,并把迭代器中的詞及關(guān)注度信息拼在一起,形成一個字符串,作為 corrInfo 域的內(nèi)容。 清單 12. 詞與詞相關(guān)度計算 -2 (輸出索引文件) public class WordsCorrCal_23 extends Configured implements Tool, Mapper, Reducer{ public void map(Text wordPair, FloatWritable corr, OutputCollector output,Reporter reporter)throws IOException { 將 key 解析成 word1,word2 output.collect(new Text(word1), new Text(word2 + “\t” + corr.get()); output.collect(new Text(word2), new Text(word1 + “\t” + corr.get()); } public void reduce(Text key, Iterator iter, OutputCollector output,Reporter reporter)throws IOException { String corrInfo = “”; while(iter.hasNext()) corrInfo = corrInfo + iter.next() + “\t”; // 建立 Lucene 索引,以 word 為 key,共現(xiàn)詞信息作為 value,進(jìn)行存儲 Document doc = new Document(); doc.add(new Field("word", key.toString(), Field.Store.NO, Field.Index.UN_TOKENIZED)); doc.add(new Field("corrInfo", corrInfo, Field.Store.YES,Field.Index.NO)); output.collect(new Text(), new LuceneDocumentWrapper(doc)); } … . } |
---|
用戶訪問詞列表調(diào)整 圖 9.MapReduce 實現(xiàn)用戶訪問關(guān)鍵詞擴(kuò)展
獲得詞對相關(guān)度后,即可以對用戶訪問關(guān)鍵詞列表進(jìn)行擴(kuò)展。如圖 9 所示,只需一個 Map 即可完成操作。Map 以用戶訪問詞列表為輸入,key 為用戶,value 為關(guān)鍵詞列表。對于關(guān)鍵詞列表中的每個詞 A,都會查找詞相關(guān)度索引文件,得到與詞 A 相關(guān)的詞列表 A L 。遍歷 A L ,如果 A L 中的詞 B 也被用戶訪問過,那么要將用戶訪問詞 B 的值× A 和 B 的相關(guān)度的結(jié)果加入到 A 的舊值上;如果 A L 中的詞 C 用戶沒有訪問過,則要把詞 C 加入到用戶訪問詞列表中,并將詞 A 的舊值× A 和 C 的相關(guān)度加入到詞 C 值上。Map 的輸出 key 仍為用戶,value 為用戶訪問的詞列表及對應(yīng)的新的關(guān)注度值。 清單 13. Map 實現(xiàn)關(guān)鍵詞擴(kuò)展 public class WordExp extends Configured implements Tool, Mapper{ IndexSearcher searcher = null; // 用于查詢 Lucene 索引文件 public void map(Text key, Text val, OutputCollector output,Reporter reporter)throws IOException { HashMap words; //key 為詞,value 為用戶訪問該詞的權(quán)重 HashMap wordNewInfo; // 存儲調(diào)整后的列表信息 將 val 關(guān)鍵詞信息進(jìn)行解析,依次置入 words; 拷貝 words 中信息至 wordNewInfo 中 ; for(words 中每一個關(guān)鍵詞 word){ float w1 = words.get(word); 查找 Lucene 索引文件,得到該詞相關(guān)詞列表 corrWords; for(corrWords 中每個詞 corrW){ // 如果 corrW 也被用戶訪問,修改兩個詞的權(quán)重 if((float w2 = words.get(corrW)) != null){ wordsNewInfo.put(word, wordsNewInfo.get(word) + w2 * corrW.pij); wordsNewInfo.put(corrW, wordsNewInfo.get(corrW) + w1 * corrW.pij); }else{ // 如果未被訪問,將詞加入到用戶訪問列表中 wordsNewInfo.put(corrW, w1 * corrW.pij); } } } String wordListNew = “”; for(wordNewInfo 中每個元組 entry) wordListNew = wordListNew + entry.getKey() + entry.getVal(); output.collect(key, new Text(wordListNew); } // configure 函數(shù)會在每個 Map 節(jié)點(diǎn)運(yùn)行 Map 函數(shù)對文件按行處理之前調(diào)用,通常用來做全局操作 public void configure(JobConf job) { String corListPath = job.getStrings(“corrList.path”)[0]; /// 內(nèi)部獲得特征權(quán)重路徑 FsDirectory fsDirectory = new FsDirectory(FileSystem.get(getConf()),corListPath, false,getConf()); searcher = new IndexSearcher(fsDirectory); } public void runWordExp(String input, String output, String corPath){ JobConf job = new JobConf(getConf(),WordExp.class); 設(shè)置 InputPath, outPath, MapperClass, InputFormat, OutputFormat, … job.setStrings(“corrList.path”, corPath); // 設(shè)置相關(guān)詞列表索引信息 JobClient.run(job); } … . } |
---|
結(jié)束語
MapReduce 編程模型由于其強(qiáng)大的計算能力、良好的可擴(kuò)展性和易用性,在工業(yè)界和學(xué)術(shù)界得到了廣泛使用。對大規(guī)模數(shù)據(jù)批量處理的特點(diǎn),使得 MapReduce 尤其適用于日志分析類應(yīng)用。MapReduce 編程模型的基礎(chǔ)是 Map 和 Reduce 函數(shù),如何將應(yīng)用全部或者部分轉(zhuǎn)換到這兩類計算模式,即將應(yīng)用并行化,是有一定技巧的。本文對用戶興趣點(diǎn)挖掘應(yīng)用進(jìn)行 MapReduce 的實現(xiàn),除在數(shù)據(jù)挖掘領(lǐng)域提出見解,MapReduce 的實現(xiàn)方式也為用戶進(jìn)行應(yīng)用并行化提供了參考。