Apache Mahout 簡介

轉自:http://www.ibm.com/developerworks/cn/java/j-mahout/

Apache Mahout 簡介

通過可伸縮、商業友好的機器學習來構建智能應用程序

Grant Ingersoll, 技術人員, Lucid Imagination

簡介: 當研究院和企業能獲取足夠的專項研究預算之後,能從數據和用戶輸入中學習的智能應用程序將變得更加常見。人們對機器學習技巧(比如說集羣、協作篩選和分類)的需求前所未有地增長,無論是查找一大羣人的共性還是自動標記海量 Web 內容。Apache Mahout 項目旨在幫助開發人員更加方便快捷地創建智能應用程序。Mahout 的創始者 Grant Ingersoll 介紹了機器學習的基本概念,並演示瞭如何使用 Mahout 來實現文檔集羣、提出建議和組織內容。

在信息時代,公司和個人的成功越來越依賴於迅速有效地將大量數據轉化爲可操作的信息。無論是每天處理數以千計的個人電子郵件消息,還是從海量博客文章中推測用戶的意圖,都需要使用一些工具來組織和增強數據。 這其中就蘊含着機器學習 領域以及本文章所介紹項目的前景:Apache Mahout(見 參考資料)。

機器學習是人工智能的一個分支,它涉及通過一些技術來允許計算機根據之前的經驗改善其輸出。此領域與數據挖掘密切相關,並且經常需要使用各種技巧,包括統計學、概率論和模式識別等。雖然機器學習並不是一個新興領域,但它的發展速度是毋庸置疑的。許多大型公司,包括 IBM®、Google、Amazon、Yahoo! 和 Facebook,都在自己的應用程序中實現了機器學習算法。此外,還有許多公司在自己的應用程序中應用了機器學習,以便學習用戶以及過去的經驗,從而獲得收益。

在簡要概述機器學習的概念之後,我將介紹 Apache Mahout 項目的特性、歷史和目標。然後,我將演示如何使用 Mahout 完成一些有趣的機器學習任務,這需要使用免費的 Wikipedia 數據集。

機器學習 101

機器學習可以應用於各種目的,從遊戲、欺詐檢測到股票市場分析。它用於構建類似於 Netflix 和 Amazon 所提供的系統,可根據用戶的購買歷史向他們推薦產品,或者用於構建可查找特定時間內的所有相似文章的系統。它還可以用於根據類別(體育、經濟和戰爭等)對網頁自動進行分類,或者用於標記垃圾電子郵件。本文無法完全列出機器學習的所有應用。如果您希望更加深入地探究該領域,我建議您參閱 參考資料

可以採用一些機器學習方法來解決問題。我將重點討論其中最常用的兩個 — 監管 和無監管 學習 — 因爲它們是 Mahout 支持的主要功能。

監管學習的任務是學習帶標籤的訓練數據的功能,以便預測任何有效輸入的值。監管學習的常見例子包括將電子郵件消息分類爲垃圾郵件,根據類別標記網頁,以及識別手寫輸入。創建監管學習程序需要使用許多算法,最常見的包括神經網絡、Support Vector Machines (SVMs) 和 Naive Bayes 分類程序。

無監管學習的任務是發揮數據的意義,而不管數據的正確與否。它最常應用於將類似的輸入集成到邏輯分組中。它還可以用於減少數據集中的維度數據,以便只專注於最有用的屬性,或者用於探明趨勢。無監管學習的常見方法包括 k-Means、分層集羣和自組織地圖。

在本文中,我將重點討論 Mahout 當前已實現的三個具體的機器學習任務。它們正好也是實際應用程序中相當常見的三個領域:

  • 協作篩選
  • 集羣
  • 分類

在研究它們在 Mahout 中的實現之前,我將從概念的層面上更加深入地討論這些任務。

協作篩選

協作篩選 (CF) 是 Amazon 等公司極爲推崇的一項技巧,它使用評分、單擊和購買等用戶信息爲其他站點用戶提供推薦產品。CF 通常用於推薦各種消費品,比如說書籍、音樂和電影。但是,它還在其他應用程序中得到了應用,主要用於幫助多個操作人員通過協作來縮小數據範圍。您可能已經在 Amazon 體驗了 CF 的應用,如 圖 1 所示:


圖 1. Amazon 上的協作篩選示例
Amazon 上的協作篩選示例 

CF 應用程序根據用戶和項目歷史向系統的當前用戶提供推薦。生成推薦的 4 種典型方法如下:

  • 基於用戶:通過查找相似的用戶來推薦項目。由於用戶的動態特性,這通常難以定量。

  • 基於項目:計算項目之間的相似度並做出推薦。項目通常不會過多更改,因此這通常可以離線完成。

  • Slope-One:非常快速簡單的基於項目的推薦方法,需要使用用戶的評分信息(而不僅僅是布爾型的首選項)。

  • 基於模型:通過開發一個用戶及評分模型來提供推薦。

所有 CF 方法最終都需要計算用戶及其評分項目之間的相似度。可以通過許多方法來計算相似度,並且大多數 CF 系統都允許您插入不同的指標,以便確定最佳結果。

集羣

對於大型數據集來說,無論它們是文本還是數值,一般都可以將類似的項目自動組織,或集羣,到一起。舉例來說,對於全美國某天內的所有的報紙新聞,您可能希望將所有主題相同的文章自動歸類到一起;然後,可以選擇專注於特定的集羣和主題,而不需要閱讀大量無關內容。另一個例子是:某臺機器上的傳感器會持續輸出內容,您可能希望對輸出進行分類,以便於分辨正常和有問題的操作,因爲普通操作和異常操作會歸類到不同的集羣中。

與 CF 類似,集羣計算集合中各項目之間的相似度,但它的任務只是對相似的項目進行分組。在許多集羣實現中,集合中的項目都是作爲矢量表示在 n 維度空間中的。通過矢量,開發人員可以使用各種指標(比如說曼哈頓距離、歐氏距離或餘弦相似性)來計算兩個項目之間的距離。然後,通過將距離相近的項目歸類到一起,可以計算出實際集羣。

可以通過許多方法來計算集羣,每種方法都有自己的利弊。一些方法從較小的集羣逐漸構建成較大的集羣,還有一些方法將單個大集羣分解爲越來越小的集羣。在發展成平凡集羣表示之前(所有項目都在一個集羣中,或者所有項目都在各自的集羣中),這兩種方法都會通過特定的標準退出處理。流行的方法包括 k-Means 和分層集羣。如下所示,Mahout 也隨帶了一些不同的集羣方法。

分類

分類(通常也稱爲歸類)的目標是標記不可見的文檔,從而將它們歸類不同的分組中。機器學習中的許多分類方法都需要計算各種統計數據(通過指定標籤與文檔的特性相關),從而創建一個模型以便以後用於分類不可見的文檔。舉例來說,一種簡單的分類方法可以跟蹤與標籤相關的詞,以及這些詞在某個標籤中的出現次數。然後,在對新文檔進行分類時,系統將在模型中查找文檔中的詞並計算概率,然後輸出最佳結果並通過一個分類來證明結果的正確性。

分類功能的特性可以包括詞彙、詞彙權重(比如說根據頻率)和語音部件等。當然,這些特性確實有助於將文檔關聯到某個標籤並將它整合到算法中。

機器學習這個領域相當廣泛和活躍。理論再多終究需要實踐。接下來,我將繼續討論 Mahout 及其用法。

Mahout 簡介

Apache Mahout 是 Apache Software Foundation (ASF) 開發的一個全新的開源項目,其主要目標是創建一些可伸縮的機器學習算法,供開發人員在 Apache 在許可下免費使用。該項目已經發展到了它的最二個年頭,目前只有一個公共發行版。Mahout 包含許多實現,包括集羣、分類、CP 和進化程序。此外,通過使用 Apache Hadoop 庫,Mahout 可以有效地擴展到雲中(見 參考資料)。

Mahout 的歷史

背景知識

mahout 的意思是大象的飼養者及驅趕者。Mahout 這個名稱來源於該項目(有時)使用 Apache Hadoop — 其徽標上有一頭黃色的大象 — 來實現可伸縮性和容錯性。

Mahout 項目是由 Apache Lucene(開源搜索)社區中對機器學習感興趣的一些成員發起的,他們希望建立一個可靠、文檔翔實、可伸縮的項目,在其中實現一些常見的用於集羣和分類的機器學習算法。該社區最初基於 Ng et al. 的文章 “Map-Reduce for Machine Learning on Multicore”(見 參考資料),但此後在發展中又併入了更多廣泛的機器學習方法。Mahout 的目標還包括:

  • 建立一個用戶和貢獻者社區,使代碼不必依賴於特定貢獻者的參與或任何特定公司和大學的資金。

  • 專注於實際用例,這與高新技術研究及未經驗證的技巧相反。

  • 提供高質量文章和示例。

特性

雖然在開源領域中相對較爲年輕,但 Mahout 已經提供了大量功能,特別是在集羣和 CF 方面。Mahout 的主要特性包括:

Map-Reduce 簡介

Map-Reduce 是 Google 開發的一種分佈式編程 API,並在 Apache Hadoop 項目中得到了實現。與分佈式文件系統相結合,它可以爲程序員提供一個定義良好的用於描述計算任務的 API,從而幫助他們簡化並行化問題的任務。(有關更多信息,請參見 參考資料)。

  • Taste CF。Taste 是 Sean Owen 在 SourceForge 上發起的一個針對 CF 的開源項目,並在 2008 年被贈予 Mahout。

  • 一些支持 Map-Reduce 的集羣實現包括 k-Means、模糊 k-Means、Canopy、Dirichlet 和 Mean-Shift。

  • Distributed Naive Bayes 和 Complementary Naive Bayes 分類實現。

  • 針對進化編程的分佈式適用性功能。

  • Matrix 和矢量庫。

  • 上述算法的示例。

Mahout 入門

Mahout 的入門相對比較簡單。首先,您需要安裝以下軟件:

您還需要本文的示例代碼(見 下載 部分),其中包括一個 Mahout 副本及其依賴關係。依照以下步驟安裝示例代碼:

  1. 解壓縮 sample.zip
  2. cd apache-mahout-examples
  3. ant install

步驟 3 將下載必要的 Wikipedia 文件將編譯代碼。所使用的 Wikipedia 文件大約爲 2.5 GB,因此下載時間將由您的寬帶決定。

建立一個推薦引擎

Mahout 目前提供了一些工具,可用於通過 Taste 庫建立一個推薦引擎 — 針對 CF 的快速且靈活的引擎。Taste 支持基於用戶和基於項目的推薦,並且提供了許多推薦選項,以及用於自定義的界面。Taste 包含 5 個主要組件,用於操作 用戶項目 和 首選項

  • DataModel:用於存儲 用戶項目 和 首選項
  • UserSimilarity:用於定義兩個用戶之間的相似度的界面
  • ItemSimilarity:用於定義兩個項目之間的相似度的界面
  • Recommender:用於提供推薦的界面
  • UserNeighborhood:用於計算相似用戶鄰近度的界面,其結果隨時可由 Recommender 使用

藉助這些組件以及它們的實現,開發人員可以構建複雜的推薦系統,提供基於實時或者離線的推薦。基於實時的推薦經常只能處理數千用戶,而離線推薦具有更好的適用性。Taste 甚至提供了一些可利用 Hadoop 離線計算推薦的工具。在許多情況中,這種合適的方法可以幫助您滿足包含大量用戶、項目和首選項的大型系統的需求。

爲了演示如何構建一個簡單的推薦系統,我需要一些用戶、項目和評分。爲此,我們會使用 cf.wikipedia.GenerateRatings 中的代碼(包含在示例代碼的源代碼中)爲 Wikipedia 文檔(Taste 稱之爲 項目)隨機生成大量 用戶 和 首選項,然後再手動補充一些關於特定話題(Abraham Lincoln)的評分,從而創建示例中的最終 recommendations.txt 文件。此方法的內涵是展示 CF 如何將對某特定話題感興趣的人導向相關話題的其他文檔。此示例的數據來源於 990(標記爲從 0 到 989)個隨機用戶,他們隨機爲集合中的所有文章隨機分配了一些評分,以及 10 個用戶(標記爲從 990 到 999),他們對集合中包含 Abraham Lincoln 關鍵字的 17 篇文章中的部分文章進行了評分。

注意虛構數據!

本文中的示例完全使用的是虛構數據。我自己完成了所有評分,模擬了 10 個對 Abraham Lincoln 感興趣的實際用戶。雖然我相信數據內部的概念很有趣,但數據本身以及所使用的值並非如此。如果您希望獲得實際數據,我建議您參閱 University of Minnesota 的 GroupLens 項目,以及 Taste 文檔(見 參考資料)。我選擇虛構數據的原因是希望在所有示例中都使用單一數據集。

首先,我將演示如何爲在 recommendations.txt 文件中指定了分數的用戶創建推薦。這是 Taste 最爲常見的應用,因此首先需要載入包含推薦的數據,並將它存儲在一個 DataModel 中。Taste 提供了一些不同的DataModel 實現,用於操作文件和數據庫。在本例中,爲簡便起見,我選擇使用 FileDataModel 類,它對各行的格式要求爲:用戶 ID、項目 ID、首選項 — 其中,用戶 ID 和項目 ID 都是字符串,而首選項可以是雙精度型。建立了模型之後,我需要通知 Taste 應該如何通過聲明一個UserSimilarity 實現來比較用戶。根據所使用的 UserSimilarity 實現,您可能還需要通知 Taste 如何在未指定明確用戶設置的情況下推斷首選項。清單 1 實現了以上代碼。(示例代碼 中的cf.wikipedia.WikipediaTasteUserDemo 包含了完整的代碼清單)。


清單 1. 創建模型和定義用戶相似度
				
//create the data model
FileDataModel dataModel = new FileDataModel(new File(recsFile));
UserSimilarity userSimilarity = new PearsonCorrelationSimilarity(dataModel);
// Optional:
userSimilarity.setPreferenceInferrer(new AveragingPreferenceInferrer(dataModel));

在 清單 1 中,我使用了 PearsonCorrelationSimilarity,它用於度量兩個變量之間的關係,但是也可以使用其他 UserSimilarity 度量。應該根據數據和測試類型來選擇相似度度量。對於此數據,我發現這種組合最爲合適,但仍然存在一些問題。有關如何選擇相似度度量的更多信息,請訪問 Mahout 網站(見 參考資料)。

爲了完成此示例,我需要構建一個 UserNeighborhood 和一個 RecommenderUserNeighborhood 可以識別與相關用戶類似的用戶,並傳遞給 Recommender,後者將負責創建推薦項目排名表。清單 2 實現了以下想法:


清單 2. 生成推薦
				
//Get a neighborhood of users
UserNeighborhood neighborhood =
        new NearestNUserNeighborhood(neighborhoodSize, userSimilarity, dataModel);
//Create the recommender
Recommender recommender =
        new GenericUserBasedRecommender(dataModel, neighborhood, userSimilarity);
User user = dataModel.getUser(userId);
System.out.println("-----");
System.out.println("User: " + user);
//Print out the users own preferences first
TasteUtils.printPreferences(user, handler.map);
//Get the top 5 recommendations
List<RecommendedItem> recommendations =
        recommender.recommend(userId, 5);
TasteUtils.printRecs(recommendations, handler.map);

您可以在命令行中運行整個示例,方法是在包含示例的目錄中執行 ant user-demo。運行此命令將打印輸出虛構用戶 995 的首選項和推薦,該用戶只是 Lincoln 的愛好者之一。清單 3 顯示了運行 ant user-demo 的輸出:


清單 3. 用戶推薦的輸出
				
 [echo] Getting similar items for user: 995 with a neighborhood of 5
     [java] 09/08/20 08:13:51 INFO file.FileDataModel: Creating FileDataModel
            for file src/main/resources/recommendations.txt
     [java] 09/08/20 08:13:51 INFO file.FileDataModel: Reading file info...
     [java] 09/08/20 08:13:51 INFO file.FileDataModel: Processed 100000 lines
     [java] 09/08/20 08:13:51 INFO file.FileDataModel: Read lines: 111901
     [java] Data Model: Users: 1000 Items: 2284
     [java] -----
     [java] User: 995
     [java] Title: August 21 Rating: 3.930000066757202
     [java] Title: April Rating: 2.203000068664551
     [java] Title: April 11 Rating: 4.230000019073486
     [java] Title: Battle of Gettysburg Rating: 5.0
     [java] Title: Abraham Lincoln Rating: 4.739999771118164
     [java] Title: History of The Church of Jesus Christ of Latter-day Saints
              Rating: 3.430000066757202
     [java] Title: Boston Corbett Rating: 2.009999990463257
     [java] Title: Atlanta, Georgia Rating: 4.429999828338623
     [java] Recommendations:
     [java] Doc Id: 50575 Title: April 10 Score: 4.98
     [java] Doc Id: 134101348 Title: April 26 Score: 4.860541
     [java] Doc Id: 133445748 Title: Folklore of the United States Score: 4.4308662
     [java] Doc Id: 1193764 Title: Brigham Young Score: 4.404066
     [java] Doc Id: 2417937 Title: Andrew Johnson Score: 4.24178

從清單 3 中可以看到,系統推薦了一些信心級別不同的文章。事實上,這些項目的分數都是由其他 Lincoln 愛好者指定的,而不是用戶 995 一人所爲。如果您希望查看其他用戶的結構,只需要在命令行中傳遞 -Duser.id=USER-ID 參數,其中 USER-ID 是 0 和 999 之間的編號。您還可以通過傳遞 -Dneighbor.size=X 來更改鄰近空間,其中,X 是一個大於 0 的整型值。事實上,將鄰近空間更改爲 10 可以生成極爲不同的結果,這是因爲阾近範圍內存在一個隨機用戶。要查看鄰近用戶以及共有的項目,可以向命令行添加 -Dcommon=true

現在,如果您所輸入的編號恰好不在用戶範圍內,則會注意到示例生成了一個 NoSuchUserException。確實,應用程序需要處理新用戶進入系統的情況。舉例來說,您可以只顯示 10 篇最熱門的文章,一組隨機文章,或者一組 “不相關” 的文章 — 或者,與其這樣,還不如不執行任何操作。

如前所述,基於用戶的方法經常不具有可伸縮性。在本例中,使用基於項目的方法是更好的選擇。幸運的是,Taste 可以非常輕鬆地實現基於項目的方法。處理項目相似度的基本代碼並沒有很大差異,如清單 4 所示:


清單 4. 項目相似度示例(摘錄自 cf.wikipedia.WikipediaTasteItemItemDemo) 
				
//create the data model
FileDataModel dataModel = new FileDataModel(new File(recsFile));
//Create an ItemSimilarity
ItemSimilarity itemSimilarity = new LogLikelihoodSimilarity(dataModel);
//Create an Item Based Recommender
ItemBasedRecommender recommender =
        new GenericItemBasedRecommender(dataModel, itemSimilarity);
//Get the recommendations
List<RecommendedItem> recommendations =
        recommender.recommend(userId, 5);
TasteUtils.printRecs(recommendations, handler.map);

與 清單 1 相同,我根據推薦文件創建了一個 DataModel,但這次並未實例化 UserSimilarity 實例,而是使用LogLikelihoodSimilarity 創建了一個 ItemSimilarity,它可以幫助處理不常見的事件。然後,我將 ItemSimilarity 提供給一個ItemBasedRecommender,最後請求推薦。完成了!您可以通過 ant item-demo 命令在示例中代碼運行它。當然,在此基礎上,您可以讓系統支持離線執行這些計算,您還可以探索其他的 ItemSimilarity 度量。注意,由於本示例中的數據是隨機的,所推薦的內容可能並不符合用戶的期望。事實上,您應該確保在測試過程中計算結果,並嘗試不同的相似度指標,因爲許多常用指標在一些邊界情況中會由於數據不足而無法提供合適的推薦。

我們再來看新用戶的例子,當用戶導航到某個項目之後,缺少用戶首選項時的操作就比較容易實現了。對於這種情況,您可以利用項目計算並向 ItemBasedRecommender 請求與相當項目最相似的項目。清單 5 展示了相關代碼:


清單 5. 相似項目演示(摘錄自 cf.wikipedia.WikipediaTasteItemRecDemo
				
//create the data model
FileDataModel dataModel = new FileDataModel(new File(recsFile));
//Create an ItemSimilarity
ItemSimilarity itemSimilarity = new LogLikelihoodSimilarity(dataModel);
//Create an Item Based Recommender
ItemBasedRecommender recommender =
        new GenericItemBasedRecommender(dataModel, itemSimilarity);
//Get the recommendations for the Item
List<RecommendedItem> simItems
        = recommender.mostSimilarItems(itemId, numRecs);
TasteUtils.printRecs(simItems, handler.map);

您可以通過在命令中執行 ant sim-item-demo 來運行 清單 5。它與 清單 4 之間的唯一差異就是,清單 5 並沒有請求推薦,而是請求輸出最相似的項目。

現在,您可以繼續深入探索 Taste。要了解更多信息,請閱讀 Taste 文檔和 [email protected] 郵件列表(見 參考資料)。接下來,我將討論如何通過利用 Mahout 的集羣功能來查找相似文章。

使用 Mahout 實現集羣

Mahout 支持一些集羣算法實現(都是使用 Map-Reduce 編寫的),它們都有一組各自的目標和標準:

  • Canopy:一種快速集羣算法,通常用於爲其他集羣算法創建初始種子。

  • k-Means(以及 模糊 k-Means):根據項目與之前迭代的質心(或中心)之間的距離將項目添加到 k 集羣中。

  • Mean-Shift:無需任何關於集羣數量的推理 知識的算法,它可以生成任意形狀的集羣。

  • Dirichlet:藉助基於多種概率模型的集羣,它不需要提前執行特定的集羣視圖。

從實際的角度來說,名稱和實現並不如它們生成的結果重要。瞭解了這一點之後,我將展示 k-Means 的運行原理,而其餘內容將由您自己去研究。請記住,要有效運行每個算法,您需要滿足它們各自的的需求。

簡單來說(詳細信息見下文),使用 Mahout 創建數據集羣的步驟包括:

  1. 準備輸入。如果創建文本集羣,您需要將文本轉換成數值表示。
  2. 使用 Mahout 中可用的 Hadoop 就緒的驅動程序運行所選集羣算法。
  3. 計算結果。
  4. 如果有必要,執行迭代。

首先,集羣算法要求數據必需採用適合處理的格式。在機器學習中,數據通常被表示爲矢量,有時也稱作特徵矢量。在集羣中,矢量是表示數據的一組權重值。我將使用通過 Wikipedia 文檔生成的矢量來演示集羣,但是也可以從其他地方獲取矢量,比如說傳感器數據或用戶資料。Mahout 隨帶了兩個 Vector 表示:DenseVector 和 SparseVector。根據所使用的數據,您需要選擇合適的實現,以便實現良好的性能。通常而言,基於文本的問題是很少的,因此應該使用 SparseVector 來處理文本。另一方面,如果大多數矢量的大多數值都是非零的,則比較適合使用 DenseVector。如果您對此不確定,可以嘗試這兩種實現來處理數據的一個子集,然後確定哪種實現的運行速度更快。

通過 Wikipedia 內容生成矢量的方法如下(我已經完成了此工作):

  1. 將內容索引編入 Lucene,確保存儲相關字段(用於生成矢量的字段)的 term 矢量。我不會討論這方面的詳細信息 — 不在本文討論範圍之內 — 但我會提供一些簡要提示以及 Lucene 上的一些參考資料。Lucene 提供了一個稱爲 EnWikiDocMaker 的類(包含在 Lucene 的 contrib/benchmark 包中),該類可以讀取 Wikipedia 文件塊中的內容並生成編入 Lucene 索引的文檔。

  2. 使用 org.apache.mahout.utils.vectors.lucene.Driver 類(位於 Mahout 的 utils 模塊中)通過 Lucene 索引創建矢量。此驅動程序提供了大量用於創建矢量的選項。Mahout wiki 頁面 “Creating Vectors from Text” 提供了更多信息(見 參考資料)。

運行這兩個步驟的結果是生成一個文件,該文件類似於與您從 Getting started with Mahout 入門 部分下載的 n2.tar.gz 文件。需要說明一下,n2.tar.gz 文件中的矢量是通過由 ant install 方法之前下載的 Wikipedia “塊” 文件中的所有文件的索引創建的。矢量將被格式化爲 Euclidean 格式(或者 L2 格式;請參見 參考資料)。在使用 Mahout 時,您可能希望嘗試採用不同的方法來創建矢量,以確定哪種方法的效果最好。

評估結果

可以採用多種方法來評估集羣結果。許多人最開始都是使用手動檢查與隨機測試相結合的方法。但是,要實現令人滿足的結果,通常都需要使用一些更加高級的計算技巧,比如說使用一些準則開發一個黃金標準。有關評估結果的更多信息,請參見 參考資料。在本例中,我使用手動檢查來判斷結果集羣是否有意義。如果要投入生產,則應該使用更加嚴格的流程。

創建了一組矢量之後,接下來需要運行 k-Means 集羣算法。Mahout 爲所有集羣算法都提供了驅動程序,包括 k-Means 算法,更合適的名稱應該是 KMeansDriver。可以直接將驅動程序作爲單獨的程序使用,而不需要 Hadoop 的支持,比如說您可以直接運行 ant k-means。有關KMeansDriver 可接受的參數的更多信息,請查看 build.xml 中的 Ant k-means 目標。完成此操作之後,您可以使用 ant dump 命令打印輸出結果。

成功在獨立模式中運行驅動程序之後,您可以繼續使用 Hadoop 的分佈式模式。爲此,您需要 Mahout Job JAR,它位於示例代碼的 hadoop 目錄中。Job JAR 包可以將所有代碼和依賴關係打包到一個 JAR 文件中,以便於加載到 Hadoop 中。您還需要下載 Hadoop 0.20,並依照 Hadoop 教程的指令,首先在準分佈式模式(也就是一個集羣)中運行,然後再採用完全分佈式模式。有關更多信息,請參見 Hadoop 網站及資源,以及 IBM 雲計算資源(參見 參考資料)。

使用 Mahout 實現內容分類

Mahout 目前支持兩種根據貝氏統計來實現內容分類的方法。第一種方法是使用簡單的支持 Map-Reduce 的 Naive Bayes 分類器。Naive Bayes 分類器爲速度快和準確性高而著稱,但其關於數據的簡單(通常也是不正確的)假設是完全獨立的。當各類的訓練示例的大小不平衡,或者數據的獨立性不符合要求時,Naive Bayes 分類器會出現故障。第二種方法是 Complementary Naive Bayes,它會嘗試糾正 Naive Bayes 方法中的一些問題,同時仍然能夠維持簡單性和速度。但在本文中,我只會演示 Naive Bayes 方法,因爲這能讓您看到總體問題和 Mahout 中的輸入。

簡單來講,Naive Bayes 分類器包括兩個流程:跟蹤特定文檔及類別相關的特徵(詞彙),然後使用此信息預測新的、未見過的內容的類別。第一個步驟稱作訓練(training),它將通過查看已分類內容的示例來創建一個模型,然後跟蹤與特定內容相關的各個詞彙的概率。第二個步驟稱作分類,它將使用在訓練階段中創建的模型以及新文檔的內容,並結合 Bayes Theorem 來預測傳入文檔的類別。因此,要運行 Mahout 的分類器,您首先需要訓練模式,然後再使用該模式對新內容進行分類。下一節將演示如何使用 Wikipedia 數據集來實現此目的。

運行 Naive Bayes 分類器

在運行訓練程序和分類器之前,您需要準備一些用於訓練和測試的文檔。您可以通過運行 ant prepare-docs 來準備一些 Wikipedia 文件(通過 install 目標下載的文件)。這將使用 Mahout 示例中的 WikipediaDatasetCreatorDriver 類來分開 Wikipedia 輸入文件。分開文檔的標準是它們的類似是否與某個感興趣的類別相匹配。感興趣的類別可以是任何有效的 Wikipedia 類別(或者甚至某個 Wikipedia 類別的任何子字符串)。舉例來說,在本例中,我使用了兩個類別:科學(science)和歷史(history)。因此,包含單詞science 或 history 的所有 Wikipedia 類別都將被添加到該類別中(不需要準確匹配)。此外,系統爲每個文檔添加了標記並刪除了標點、Wikipedia 標記以及此任務不需要的其他特徵。最終結果將存儲在一個特定的文件中(該文件名包含類別名),並採用每行一個文檔的格式,這是 Mahout 所需的輸入格式。同樣,運行 ant prepare-test-docs 代碼可以完成相同的文檔測試工作。需要確保測試和訓練文件沒有重合,否則會造成結果不準確。從理論上說,使用訓練文檔進行測試應該能實現最的結果,但實際情況可能並非如此。

設置好訓練和測試集之後,接下來需要通過 ant train 目標來運行 TrainClassifier 類。這應該會通過 Mahout 和 Hadoop 生成大量日誌。完成後,ant test 將嘗試使用在訓練時建立的模型對示例測試文檔進行分類。這種測試在 Mahout 中輸出的數據結構是混合矩陣。混合矩陣可以描述各類別有多少正確分類的結果和錯誤分類的結果。

總的來說,生成分類結果的步驟如下:

  1. ant prepare-docs
  2. ant prepare-test-docs
  3. ant train
  4. ant test

運行所有這些命令(Ant 目標 classifier-example 將在一次調用中捕獲所有它們),這將生成如清單 6 所示的彙總和混合矩陣:


清單 6. 運行 Bayes 分類器對歷史和科學主題進行分類的結果
				
[java] 09/07/22 18:10:45 INFO bayes.TestClassifier: history
                                  95.458984375    3910/4096.0
[java] 09/07/22 18:10:46 INFO bayes.TestClassifier: science
                                  15.554072096128172      233/1498.0
[java] 09/07/22 18:10:46 INFO bayes.TestClassifier: =================
[java] Summary
[java] -------------------------------------------------------
[java] Correctly Classified Instances          :       4143
                                                    74.0615%
[java] Incorrectly Classified Instances        :       1451
                                                    25.9385%
[java] Total Classified Instances              :       5594
[java]
[java] =======================================================
[java] Confusion Matrix
[java] -------------------------------------------------------
[java] a           b       <--Classified as
[java] 3910        186      |  4096        a     = history
[java] 1265        233      |  1498        b     = science
[java] Default Category: unknown: 2

中間過程的結果存儲在 base 目錄下的 wikipedia 目錄中。

獲取了結果之後,顯然還有一個問題:“我應該如何做?”彙總結果表明,正確率和錯誤率大概分別爲 75% 和 25%。這種結果看上去非常合理,特別是它比隨機猜測要好很多。但在仔細分析之後,我發現對歷史信息的預測(正確率大約爲 95%)相當出色,而對科學信息的預測則相當糟糕(大約 15%)。爲了查找其原因,我查看了訓練的輸入文件,並發現與歷史相關的示例要比科學多很多(文件大小几乎差了一倍),這可能是一個潛在的問題。

對於測試,您可以向 ant test 添加 -Dverbose=true 選項,這會顯示關於各測試輸入的信息,以及它的標籤是否正確。仔細研究此輸出,您可以查找文檔並分析它分類錯誤的原因。我還可以嘗試不同的輸入參數,或者使用更加科學數據來重新訓練模型,以確定是否能夠改善此結果。

在訓練模型時考慮使用特徵選擇也是很重要的。對於這些示例,我使用 Apache Lucene 中的 WikipediaTokenizer 來標記初始文檔,但是我沒有盡力刪除可能標記錯誤的常用術語或垃圾術語。如果要將此分類器投入生產,那麼我會更加深入地研究輸入和其他設置,以彌補性能的每個方面。

爲了確定 Science 結果是否是個意外,我嘗試了一組不同的類別:共和(Republican)與民主(Democrat)。在本例中,我希望預測新文檔是否與 Republicans 或者 Democrats 相關。爲了幫助您獨立實現此功能,我在 src/test/resources 中創建了 repubs-dems.txt 文件。然後,通過以下操作完成分類步驟:

ant classifier-example -Dcategories.file=./src/test/resources/repubs-dems.txt -Dcat.dir=rd

兩個 -D 值僅僅指向類別文件以及 wikipedia 目錄中存儲中間結果的目錄。此結果概要和混合矩陣如清單 7 所示:


清單 7. 運行 Bayes 分別器查找 Republicans 和 Democrats 的結果
				
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: --------------
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: Testing:
                                wikipedia/rd/prepared-test/democrats.txt
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: democrats      70.0
                                                                    21/30.0
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: --------------
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: Testing:
                              wikipedia/rd/prepared-test/republicans.txt
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier: republicans    81.3953488372093
                                                                    35/43.0
 [java] 09/07/23 17:06:38 INFO bayes.TestClassifier:
 [java] Summary
 [java] -------------------------------------------------------
 [java] Correctly Classified Instances          :         56           76.7123%
 [java] Incorrectly Classified Instances        :         17           23.2877%
 [java] Total Classified Instances              :         73
 [java]
 [java] =======================================================
 [java] Confusion Matrix
 [java] -------------------------------------------------------
 [java] a           b       <--Classified as
 [java] 21          9        |  30          a     = democrats
 [java] 8           35       |  43          b     = republicans
 [java] Default Category: unknown: 2

雖然最終結果在正確性方面差不多是相同的,但您可以看到我在這兩個類別中進行選擇時採取更好的方式。查看包含輸入文檔的 wikipedia/rd/prepared 目錄,我們發現兩個訓練文件在訓練示例方面更加平衡了。 此外,與 “歷史/科學” 結果相比,得到了示例也少了很多,因爲每個文件都比歷史或科學訓練集小很多。總的來說,結果至少表明平衡性得到了顯著改善。更大的訓練集可能會抵消 Republicans 和 Democrats 之間的差異,即便不行也可以暗示某個分組堅持其在 Wikipedia 上的消息是較好的選擇 — 但是,我選擇將這留給政治學者來決定。

現在,我已經展示瞭如何在獨立模式中執行分類,接下來需要將代碼添加到雲中,並在 Hadoop 集羣上運行。與集羣代碼相同,您需要 Mahout Job JAR。除此之外,我之前提到的所有算法都是支持 Map-Reduce 的,並且能夠在 Hadoop 教程所述的 Job 提交流程中運行。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章