Apache Mahout:適合全部人的可擴展機器學習框架

在軟件的世界中,兩年就像是無比漫長的時光。在過去兩年中,咱們看到了社交媒體的風生水起、大規模集羣計算的商業化(歸功於 Amazon 和 RackSpace 這樣的參與者),也看到了數據的迅猛增加以及咱們詮釋這些數據的能力的顯著提高。「Apache Mahout 簡介」 最初在 developerWorks 上發表也已是兩年以前的事情。這以後,Mahout 社區(以及項目的代碼庫和功能)取得了長足的發展。Mahout 也獲得了全球各地各類規模的企業的積極採用。html

在我撰寫的 Apache Mahout 簡介 中,我介紹了許多機器學習的概念以及使用 Mahout 提供的一套算法的基礎知識。我在那篇文章中介紹的概念仍然有效,但這套算法已經發生了顯著的變化。這篇文章不會重述基礎知識,而是重點關注 Mahout 的當前狀態,以及如何利用 Amazon 的 EC2 服務和包含 700 萬個電子郵件文檔的數據集在一個計算集羣上擴展 Mahout。如需回顧基礎知識,請參閱 參考資料 部分,特別是《Mahout 實戰》 一書。此外,我假設讀者具有 Apache Hadoop 和 Map-Reduce 範式方面的基本知識。(有關 Hadoop 的更多信息,請參閱 參考資料 部分。)java

Mahout 現狀git

Mahout 在極短的時間內取得了長足的發展。項目的關注點仍然能夠概括爲我所說的 「3 個要點」:協同過濾(推薦機制)、聚類和分類。除此以外,這個項目還增長了其餘一些功能。我將重點強調兩個領域中的一些關鍵擴展和改進:機器學習的核心算法(實現),以及包括輸入/輸出工具、與其餘庫的集成點和更多參考示例的支持基礎架構。然而,務必注意,本文對於現狀的敘述並不完整。此外,因爲篇幅有限,我只能經過寥寥數語簡單介紹各項改進。建議讀者閱讀 Mahout 網站的「新聞」部分和各 Mahout 發佈版的發佈說明,以瞭解這方面的更多信息。github

算法,算法,仍是算法web

只要嘗試解決過機器學習問題,您就應該認識到,沒有任何一種可以適合全部狀況的萬能算法。爲此,Mahout 添加了衆多新實現。表 1 包含我總結出的 Mahout 中最重要的新算法實現,還列舉了一些用例示例。在這篇文章稍後的內容中,我將實際運用其中的幾個算法。算法


表 1. Mahout 中的新算法
算法 簡要描述 用例
邏輯迴歸,使用隨機梯度降低法 (SGD) 求解 速度超快、簡單的順序分類器,可以在要求嚴格的環境中實如今線學習 爲用戶推薦廣告,將文本分類爲多種類別
隱藏式馬可夫模型 (HMM) 經典分類算法的順序和並行實現,設計用於在基本生成過程不可知的狀況下建模實際過程。 文本詞類標註、語音識別
奇異值分解 (SVD) 旨在減小大型矩陣內的噪聲,使噪音更小、更容易處理 做爲聚類、推薦機制和分類的前導,自動執行特性選擇
狄利克雷聚類 基於模型的聚類方法,根據數據是否適合基本模型來肯定從屬關係 在數據存在重疊或層次時很是有用
譜聚類 一系列相似的方法,使用基於圖形的方法來肯定聚類從屬關係 與全部聚類算法類似,適用於探索大規模、不可見的數據集
Minhash 聚類 利用散列戰略,將相似的項目組織在一塊兒,從而生成聚類 與其餘聚類方法相同
大量推薦機制改進 分佈式共生、SVD、交替最小二乘法 交友網站、電子商務、電影或書籍推薦
並置 支持 「Map-Reduce」 的並置實現 以統計學的方式在文本中查找關注的詞組

Mahout 還新增了對用戶極爲有用的大量低級數學算法(請參見數學包),表 1 所述算法使用了其中的許多低級數學算法,但這些低級數學算法是爲通常用途設計的,所以有可能也適合您的須要。mongodb

改進和擴展 Mahout 的基礎架構shell

Mahout 命令行

不久以前,Mahout 發佈了一個 shell 腳本,處理類路徑、環境變量和其餘設置項,從而使 Mahout 程序(包含main() 的 Mahout 程序)的運行更加輕鬆。該腳本 — 名爲 mahout — 位於 $MAHOUT_HOME/bin 目錄中。數據庫

利用開放源碼項目並竭力使項目的代碼與本身的代碼協同工做的人越多,基礎架構就越充實。對於 Mahout 來講,這種演進方式促成了多項改進。最顯著的一項就是通過重大改進、一致的命令行界面,它使得在本地和 Apache Hadoop 上提交和運行任務更加輕鬆。這個新腳本位於 Mahout 頂層目錄(下文中將稱之爲 $MAHOUT_HOME)下的 bin 目錄中。(請參閱側欄中的 Mahout 命令行。)apache

任何機器學習庫都有兩個關鍵組件,便可靠的數學庫和一個有效的集合包。數學庫(位於 $MAHOUT_HOME 下的數學模塊中)提供了多種功能:範圍從表示向量、矩陣的數據結構、操做這些數據結構的相關操做符一直到生成隨機數的工具和對數似然值等有用的統計數據(請參閱 參考資料)。Mahout 的集合庫包含的數據結構與 Java 集合提供的數據結構類似(MapList 等),不一樣之處在於它們原生地支持 Java 原語,例如 intfloat 和 double,而非其 Object 對應部分 IntegerFloat 和 Double。這一點很是重要,由於在處理擁有數百萬項特徵的數據集時,須要精打細算地考慮每一個位。此外,在較大的規模上,原語及其 Object 對應部分之間的封包成本將成爲嚴重的問題。

Mahout 還引入了一種新的集成模塊,其中包含的代碼旨在補充或擴展 Mahout 的核心功能,但並不是全部用戶在全部狀況下都須要使用這種模塊。例如,推薦機制(協同過濾)代碼如今支持將其模型存儲在數據庫(經過 JDBC)、MongoDB 或 Apache Cassandra 中(請參閱 參考資料 部分)。集成模塊還包含多種將數據轉爲 Mahout 格式的機制,以及評估所獲得的結果的機制。例如,其中包含可將充存滿文本文件的目錄轉爲 Mahout 向量格式的工具(請參閱集成模塊內的 org.apache.mahout.text 包)。

最後,Mahout 提供了大量新示例,包括經過 Netfix 數據集計算推薦內容、聚類 Last.fm 音樂以及其餘許多示例。此外,我爲這篇文章開發的示例也添加了 Mahout 的代碼庫。建議您抽出一些時間,進一步研究一下示例模塊(位於 $MAHOUT_HOME/examples 中)。

如今,您已經瞭解了 Mahout 的現狀,下面咱們將深刻探究主要內容:如何外擴 Mahout。

回頁首

在雲中擴展 Mahout

使 Mahout 有效實現擴展並不容易,沒法經過簡單地爲 Hadoop 集羣添加更多節點來實現此擴展。在決定 Mahout 可否可以有效進行擴展時,算法選擇、節點數量、特性選擇和數據的稀疏性等因素(以及內存、帶寬和處理器速度這些常見要素)扮演着相當重要的角色。爲了便於討論,我將演示一個示例,合理地利用 Amazon 的 EC2 計算基礎架構和 Hadoop(請參閱 參考資料),對 Apache Software Foundation (ASF) 提供的一個公共郵件存檔數據集運行某些 Mahout 算法。

在 「設置」 小節以後的每一小節中都會探討外擴 Mahout 的部分關鍵問題,並研究在 EC2 上運行示例的語法。

設置

示例的設置包括兩個部分:一個本地設置和一個 EC2(雲)設置。要運行示例,您須要:

  1. Apache Maven 3.0.2 或更高的版本。
  2. Git 版本控制系統(您可能還須要一個 Github 賬戶)。
  3. 一個基於 *NIX 的操做系統,例如 Linux 或 Apple OS X。Cygwin 可在 Windows® 中正常工做,但我沒有作過這方面的測試。

爲了實現本地設置,請在命令行中運行如下命令:

  1. mkdir -p scaling_mahout/data/sample
  2. git clone git://github.com/lucidimagination/mahout.git mahout-trunk
  3. cd mahout-trunk
  4. mvn install(Mahout 的測試須要必定的運行時間,若是但願跳過該測試,請添加一個 -DskipTests
  5. cd bin
  6. /mahout(您應看到可運行的項目清單,例如 kmeans

這將編譯並恰當地安裝您須要的所有代碼。單獨 下載示例數據,將其保存在 scaling_mahout/data/sample 目錄中,並解壓縮(tar -xf scaling_mahout.tar.gz)。出於測試方面的目的,這裏僅包含將在 EC2 上使用的一個較小的數據子集。

爲完成 Amazon 設置,您須要提供一個 Amazon Web 服務 (AWS) 賬戶(請注意安全密鑰、訪問密鑰和賬戶 ID),還須要對 Amazon 的 EC2 和 Elastic Block Store (EBS) 服務的工做原理有必定的瞭解。按照 Amazon 網站上的文檔說明,得到必要的訪問權限。

知足了先決條件以後,便可啓動集羣。最佳作法或許是從單獨一個節點開始,而後根據須要添加更多的節點。固然須要注意,在 EC2 上運行須要付費。所以請務必在運行完成後關閉節點。

要啓動一個集羣,以便使用本文中的示例,請按如下步驟操做:

  1. 從一個 ASF 鏡像 下載 Hadoop 0.20.203.0,並在本地解壓縮。
  2. cd hadoop-0.20.203.0/src/contrib/ec2/bin
  3. 在編輯器中打開 hadoop-ec2-env.sh,並執行如下操做:
    1. 填寫您的 AWS_ACCOUNT_IDAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYEC2_KEYDIRKEY_NAME 和PRIVATE_KEY_PATH。如需瞭解更多信息,請參見 Mahout Wiki 中的 「使用現有 Hadoop AMI」 頁面(請參閱 參考資料部分)。
    2. 將 HADOOP_VERSION 設置爲 0.20.203.0
    3. 將 S3_BUCKET 設置爲 490429964467
    4. 設置 ENABLE_WEB_PORTS=true
    5. 設置 INSTANCE_TYPE,至少應設置爲 m1.xlarge
  4. 在編輯器中打開 hadoop-ec2-init-remote.sh,並執行如下操做:
    1. 在建立 hadoop-site.xml 的部分中,添加如下屬性:
      <property>
      <name>mapred.child.java.opts></name>
      <value>-Xmx8096m></value>
      </property>

      注意:若是但願運行分類,則須要使用更大的實例和更多的內存。我使用了兩個 X-Large 實例和 12 GB 的 Java Heap 值。
    2. 將 mapred.output.compress 更改成 false
  5. 啓動您的集羣:
    ./hadoop-ec2 launch-cluster mahout-clustering X

    X 是您但願啓動的節點數量(例如 2 或 10)。建議先使用較小的值,在熟悉以後添加更多節點。這種作法有助於控制成本。
  6. 爲 ASF 公共數據集建立一個 EBS 卷(快照:snap--17f7f476),將其附加到 /dev/sdh 中的主節點實例(這是 mahout-clustering-master 安全組中的實例)。(參考資料 部分中提供了 EC2 在線文檔內具體指南的連接。)
    1. 若是使用的是 EC2 命令行 API(請參閱 參考資料 部分),則可執行:
      1. ec2-create-volume --snapshot snap-17f7f476 --z ZONE
      2. ec2-attach-volume $VOLUME_NUMBER -i $INSTANCE_ID -d /dev/sdh,其中 $VOLUME_NUMBER 是 create-volume 步驟的輸出,$INSTANCE_ID 是 launch-cluster 命令啓動的主節點的 ID
    2. 此外,也能夠經過 AWS Web 控制檯完成此任務。
  7. 將 setup-asf-ec2.sh 腳本(請參閱 下載 部分)上傳到主實例:
    ./hadoop-ec2 push mahout-clustering $PATH/setup-asf-ec2.sh

  8. 登陸您的集羣:
    ./hadoop-ec2 login mahout-clustering

  9. 執行 shell 腳原本更新系統、安裝 Git 和 Mahout,並清除部分存檔,以使之更易於運行:
    ./setup-asf-ec2.sh

完成具體設置以後,下一步咱們來看看如何運用一些較爲流行的 Mahout 算法,並對其進行上擴。我主要關注的是上擴的實際任務,但在此過程當中也會介紹有關特性選擇的一些問題,以及作出某些特定選擇的緣由。

推薦

協同過濾是 Mahout 最流行、最易用的功能之一,所以也是討論如何外擴 Mahout 的合理起點。再次提醒,咱們使用的是來自 ASF 的郵件存檔。對於推薦任務來講,一種有趣的可能性就是搭建一個系統,根據其餘用戶已經閱讀過的郵件線程向一名用戶推薦他可能會感興趣的郵件線程。爲將做爲協同過濾問題處理,我將定義一個項,系統會根據郵件標頭中的消息 ID 和引用,肯定是否將其做爲郵件線程進行推薦。用戶將由郵件消息中的 「發件人」 地址定義。換句話說,我關心的是誰發出或回覆了一個郵件消息。至於偏好自己的值,我直接將與郵件線程的交互做爲一個布爾偏好處理:若是用戶 X 與線程 Y 交互,則偏好值爲 on;不然爲 off。這種選擇惟一的負面效應就是咱們必須使用可處理布爾偏好的似然性指標,例如 Tanimoto 似然法或對數似然法 (log-likelihood)。這一般能加快計算速度,而且可能減小系統內的噪聲,但您的經歷可能有所不一樣,因此您可能但願嘗試使用不一樣的權重。

線程、消息 ID、避免好高騖遠

請注意,我處理消息線程的方法並不完美,存在郵件列表中常見的線程劫持 (thread hijacking)。線程劫持的含義是,某人經過回覆郵件列表中已有消息的方式發出新消息(即具備新主題/話題的消息),附帶傳遞了原始消息引用內容。這篇文章沒有嘗試解決這個問題,而是直接選擇忽略,但實際解決方案可能須要正面解決此問題。所以,我選擇了 「足夠好」 的方法,力求完美。

協同過濾方面的特性選擇較爲簡單(用戶、項、可選偏好),所以咱們能夠跳過這一步,直接介紹如何從原始郵件存檔中獲取在本地運行的內容,而後再在雲中運行。請注意,在不少狀況下,最後一步一般不是必需的,由於在單獨一臺機器上可足夠快地得到結果,沒有必要引入 Hadoop 使問題複雜化。按照大體估算,Mahout 社區基準測試代表可在單獨一個節點上爲多達 1 億名用戶提供推薦。電子郵件數據達不到如此之多的項數(約爲 700 萬條消息),但不管如何,我仍會繼續在 Hadoop 上運行它。

爲了查看代碼的實際效果,我在 $MAHOUT_HOME/examples/bin/build-asf-email.sh 文件中的 shell 腳本內打包了必要的步驟。請執行這個 shell 腳本,傳遞您的輸入數據位置和所需的結果輸出位置,例如:

./build-asf-email.sh ./scaling_mahout/data/sample/content ./scaling_mahout/output/

看到提示時,選擇 recommender(選項 1),靜候 Mahout 和 Hadoop 冗長的日誌輸出完成。完成後,您將看到相似清單 1 所示的內容:


清單 1. 運行推薦機制代碼的輸出示例 
11/09/08 09:57:37 INFO mapred.JobClient: Reduce output records=2072
11/09/08 09:57:37 INFO mapred.JobClient: Spilled Records=48604
11/09/08 09:57:37 INFO mapred.JobClient: Map output bytes=10210854
11/09/08 09:57:37 INFO mapred.JobClient: Combine input records=0
11/09/08 09:57:37 INFO mapred.JobClient: Map output records=24302
11/09/08 09:57:37 INFO mapred.JobClient: SPLIT_RAW_BYTES=165
11/09/08 09:57:37 INFO mapred.JobClient: Reduce input records=24302
11/09/08 09:57:37 INFO driver.MahoutDriver: Program took 74847 ms

此做業的結果是面向輸入數據內全部用戶的所有推薦。結果存儲在輸出目錄的子目錄 prefs/recommendations 中,包含名稱以 part-r- 開頭的一個或多個文本文件。(這就是 Hadoop 輸出文件的方式。)查看其中的一個文件便可注意到推薦已格式化爲如下形式(同時還有一條警告):

user_id [item_id:score, item_id:score, ...]

例如,用戶 ID 25 具備針對電子郵件 ID 26295 和 35548 的推薦。警告指出,user_id 和 item_id 並不是原始 ID,而是從原始 ID 映射而來的整數。爲幫助您理解這樣作的緣由,我將解釋一下在執行 shell 腳本時實際發生的狀況。

在生成推薦結果的過程當中,涉及三個步驟:

  1. 使用 Mahout 的 SequenceFilesFromMailArchives,將原始 mbox 文件轉換爲 Hadoop 的 SequenceFile 格式。
  2. 從消息中提取消息 ID 和 From 簽名,並以 Mahout 可以理解的格式輸出結果。
  3. 運行 Mahout 的 RecommenderJob 類。

本文不會詳細介紹第 1 步,只建議對此感興趣的讀者參閱代碼。

對於第 2 步,須要花一些工夫從文件中提取相關的信息片斷(消息 ID、回覆引用和 From 地址),隨後以三元組(FromID、Message-ID、偏好)的形式存儲它們,以供 RecommenderJob 使用。此過程由 MailToPrefsDriver 驅動,其中包含三個 「Map-Reduce」 做業:

  1. 建立一個字典,將基於字符串的 Message-ID 映射爲惟一的 long 值。
  2. 建立一個字典,將基於字符串的 From 電子郵件地址映射爲惟一的 long 值。
  3. 提取 Message-ID、References 和 From;使用第 1 步和第 2 步中建立的字典將其映射爲 long 值,將三元組輸出到一個文本文件中。

完成全部這些操做以後,便可開始生成一些推薦。爲了建立推薦,RecommenderJob 將執行圖 1 所示的步驟:


圖 1. 推薦機制做業流
圖 1. 推薦機制做業流  

完成工做流中大部分工做的主要步驟是 「計算共生(calculate co-occurrences)」 步驟。這個步驟負責在整個矩陣內執行成對比較,尋找共性。除此以外,對於執行矩陣內任何行之間的成對計算(而不只僅限於評分/評論),這個步驟(由 Mahout RowSimilarityJob支持實現)一般也很是有用。

shell 腳本中使用如下命令調用了 RecommenderJob

bin/mahout recommenditembased --input $PREFS_REC_INPUT --output $RECS_OUT --tempDir
    $PREFS_TMP --similarityClassname SIMILARITY_LOGLIKELIHOOD
   >

第一個參數告訴 Mahout 要運行哪一個命令(RecommenderJob);其餘幾個參數(input/output/tempDir)的含義不言自明。similarityClassname 告訴 Mahout 在計算共生時如何計算各項之間的似然性。我選擇使用對數似然性是由於它的簡單性、速度和質量。

得到結果以後,下一步就是評估結果。Mahout 附帶了一個評估包(org.apache.mahout.cf.taste.eval),提供了一些有用的工具,可幫助您檢查結果的質量。遺憾的是,它們沒法與基於 Hadoop 的算法一塊兒使用,但在其餘一些狀況下可能很是有用。這些工具抽出必定百分比的數據做爲測試數據,隨後將這些數據與系統生成的數據對比,從而判斷質量。

這就是生成推薦所需的所有操做,這種作法的優勢在於能夠直接在集羣上運行。爲此,請登陸您以前設置的 EC2 集羣,並運行與以前相同的 shell 腳本(/mnt/asf-email/mahout-trunk/examples/bin)。在向集羣添加節點時,您應看到運行這些步驟所需的總時間縮短了。例如,在本地機器上運行一個完整的數據集須要超過三天的時間才能完成。在 EC2 上的一個 10 節點集羣中,運行主要推薦任務加上將電子郵件轉換爲可用格式的準備時間只需約 60 分鐘。

最後一部分是將推薦做爲應用程序的一部分使用,這部分留給讀者做爲練習。典型狀況下,若是系統中有大量的項和用戶,推薦將按期生成 — 根據業務需求的不一樣,生成頻率大體爲每小時一次至天天一次。不管如何,若是系統中的用戶和推薦達到必定的數量,那麼對所生成的推薦進行的更改將會更加微妙。

接下來,咱們來觀察一下電子郵件消息的分類,在某些狀況下,可將此視爲一種上下文推薦系統

分類

Mahout 擁有多種分類算法,其中大多數算法都是爲了在 Hadoop 上運行而編寫的(只有一個值得注意的例外,即隨機梯度降低法)。出於本文的目的,我將使用貝氏分類器 (naïve bayes classifier),許多人最初都選擇這種分類器,在有效擴展的時候,它也能給出合理的結果。有關其餘分類器的更多細節,請參見《Mahout 實戰》一書中的相關章節,或者 Mahout wiki 中的 「算法」 部分(請參閱 參考資料 部分)。

電子郵件文檔按照 Apache 項目(Lucene、Mahout、Tomcat 等)細分,一般每一個項目有兩個或兩個以上的郵件列表(用戶、開發等等)。考慮到 ASF 電子郵件數據集是按照項目分區的,所以一個合情合理的分類問題就是:嘗試預測一條新傳入的消息應交付至哪一個項目。例如,一條新消息是屬於 Lucene 郵件列表仍是屬於 Tomcat 郵件列表?

要使 Mahout 的分類算法發揮效力,必須實現一個表示要識別的模式的模型,隨後根據一個數據子集進行測試。在大多數分類問題中,都必須有一名或多名人員身體力行,手動標註將要在訓練中使用的數據子集。但在本文所討論的狀況下,數據集已經按項目進行了劃分,所以無需手動標註,但我要依靠人們在發送電子郵件時一般可以選取正確的列表這個事實,而咱們都知道,事情並不是老是能這樣順利。

就像在推薦機制示例中所介紹的同樣,必要的步驟已經預先打包在 build-asf-email.sh 腳本之中,只要從菜單中選擇選項 3(隨後在出現第二條提示時選擇選項 2,即標準貝氏分類器)便可執行分類。相似於推薦,外擴代碼的部分工做是準備好要使用的數據。對於文本分類,這實際上意味着對特性進行編碼,隨後根據特性建立向量,但還包括設置訓練和測試集。須要執行的所有步驟以下:

  1. 使用 Mahout 的 SequenceFilesFromMailArchives,將原始 mbox 文件轉換爲 Hadoop 的 SequenceFile 格式。(請注意,這裏的運行時選項略有不一樣。)
    bin/mahout org.apache.mahout.text.SequenceFilesFromMailArchives --charset "UTF-8" 
        --body --subject --input $ASF_ARCHIVES --output $MAIL_OUT

  2. 將 SequenceFile 條目轉爲稀疏向量,並修改標籤:
    1. bin/mahout seq2sparse --input $MAIL_OUT --output $SEQ2SP --norm 2 --weight TFIDF --namedVector --maxDFPercent 90 --minSupport 2 --analyzerName org.apache.mahout.text.MailArchivesClusteringAnalyzer
    2. bin/mahout org.apache.mahout.classifier.email.PrepEmailDriver --input $SEQ2SP --output $SEQ2SPLABEL --maxItemsPerLabel 1000
  3. 將輸入拆分爲訓練(training)集和測試集:
    bin/mahout split --input $SEQ2SPLABEL --trainingOutput $TRAIN --testOutput $TEST
        --randomSelectionPct 20 --overwrite --sequenceFiles

  4. 運行貝氏分類器,執行訓練和測試:
    1. bin/mahout trainnb -i $TRAIN -o $MODEL -extractLabels --labelIndex $LABEL
    2. bin/mahout testnb -i $TEST -m $MODEL --labelIndex $LABEL

值得注意的兩個重要步驟就是第 2 步和第 4 步。第 2a 步是主要的特性選擇和編碼步驟,一系列的輸入參數負責控制如何將輸入文本表示爲向量中的權重。表 2 具體列舉了第 2 步中與特性選擇相關的選項:


表 2. 建立向量時的特性選擇選項
選項 描述 示例和備註
--norm norm 經過一個計算向量長度的函數 (norm) 來修改全部向量 1 norm = 曼哈頓距離,2 norm = 歐幾里得距離
--weight 計算任何給定特性的權重,並將其做爲 TF-IDF(術語頻率,逆向文檔頻率)或者單純的術語頻率。 TF-IDF 是搜索和機器學習中經常使用的權重模式,一般用於將文本表示爲向量。
--maxDFPercent--minSupport 這些選項均用於刪除文檔集合中出現得過於頻繁(最大值)或過於不頻繁的術語 在自動刪除對於計算價值不高的高頻術語或低頻術語時很是有用
--analyzerName 一個 Apache Lucene 分析器類,可用於對文檔中的詞執行分詞、詞幹化、刪除或其餘更改 如需進一步瞭解 Lucene,請參閱 參考資料 部分

第 2a 步中的分析過程值得深究,由於它承擔了特性選擇的大部分工做。Lucene Analyzer 包括一個 Tokenizer 類以及零個或多個TokenFilter 類。Tokenizer 負責將原始輸入劃分紅零個或多個標記(例如,單詞)。TokenFilter 實例彼此鏈接,隨後修改Tokenizer 生成的標記。例如,示例中所用的 Analyzer

  1. 按照空格分詞,此外還會處理一些標點符號的邊緣狀況。
  2. 將全部標記轉爲小寫。
  3. 將非 ASCII 字符轉爲 ASCII,儘量採用轉換音調符號等方法。
  4. 刪除字符數超過 40 個的標記。
  5. 刪除中止詞(請參見代碼中提供的清單,因爲內容過多,此處不便說明)。
  6. 使用 Porter 詞幹分析器提取標記的詞幹(請參閱 參考資料 部分)

這項分析的最終結果將使各文檔的對應向量顯著減小,並消除向量中可能會使分類器產生混淆的常見 「噪聲」 詞(theaan 等)。這個 Analyzer 以迭代的方法開發,首先在電子郵件中查找示例,隨後經過 Analyzer 對其加以處理並檢查輸出,從而判斷處理效果。遺憾的是,這個流程能夠說是科學的,但也僅僅是一種直觀的體驗。流程和結果並不完美,但足夠好。

第 2b 步對數據稍加轉化以便處理,還刪除了一些內容,使各類不一樣的標籤平均地出如今訓練數據中。這一點很是重要,由於我最初嘗試處理數據時產生了機器學習中的常見問題,即相關訓練示例過多的標籤的過分擬合 (overfitting)。實際上,對完整的數據集運行聚類時,將 --maxItemsPerLabel 的設置下降至 1000 仍然不夠好,沒法獲得良好的結果,由於部分郵件列表中發佈的內容少於 1000。這極可能是由 Mahout 的一個 bug 致使的,社區目前仍然在對此進行研究。

第 4 步是執行實際工做的部分,包括構建模型和隨後測試模型是否有效。在第 4a 步中,--extractLabels 選項告訴 Mahout 根據輸入判斷訓練標籤。(另一種作法是直接傳入標籤。)這個步驟的輸出是一個可經過org.apache.mahout.classifier.naivebayes.NaiveBayesModel 類讀取的文件。第 4b 步獲取模型和測試數據,檢查訓練成效如何。輸出結果是一個混合矩陣,如 「Apache Mahout 簡介」 一文所述。對於示例數據,輸出結果如清單 2 所示:


清單 2. 運行分類器代碼的輸出示例
Correctly Classified Instances : 41523 61.9219%
Incorrectly Classified Instances : 25534 38.0781%
Total Classified Instances : 67057
=======================================================
Confusion Matrix
-------------------------------------------------------
a b c d e f ><--Classified as
190440 12 1069 0 0 | 20125 a= cocoon_apache_org_dev
2066 0 1 477 0 0 | 2544 b= cocoon_apache_org_docs
165480 2370 704 0 0 | 19622 c= cocoon_apache_org_users
58 0 0 201090 0 | 20167 d= commons_apache_org_dev
147 0 1 4451 0 0 | 4599 e= commons_apache_org_user

您應注意到,這對分類器來講其實是一種很是糟糕的顯示(儘管好於猜想)。產生這種糟糕的顯示的緣由極可能是給定 Apache 項目的用戶和開發郵件列表的詞彙過於相近,難以區分。16,548 條 cocoon_user 消息被錯誤地分類爲 cocoon_dev 便可說明這一點。實際上,若是直接使用項目名稱從新運行任務,而不區分示例數據中的用戶類表和開發列表,將獲得清單 3 所示的結果:


清單 3. 僅使用項目名稱從新運行分類器代碼的輸出示例
Correctly Classified Instances : 38944 96.8949%
Incorrectly Classified Instances : 1248 3.1051%
Total Classified Instances : 40192

=======================================================
Confusion Matrix
-------------------------------------------------------
a b c ><--Classified as
18733 1241 0 | 19974 a = cocoon_apache_org
7 20211 0 | 20218 b = commons_apache_org

您必定贊成,96% 的準確度比 61% 好太多了!實際上,該結果過於完美,不多是真的。這樣的評分可能源於這個特定的小數據集的本質,或者是有待調查的更深層面的問題。事實上,這樣的評分應警示咱們進一步開展調查,添加數據並檢查生成這種評分的代碼。就目前而言,個人目的是經過這個示例來展現結果的效果,所以再也不深究。然而,咱們應該嘗試利用其餘技術或者更好的特性選擇,或許還應該採用更多的訓練示例,以便提升準確性。對結果執行交叉驗證 也是一種常見的作法。交叉驗證包括從訓練樣本中反覆獲取部分數據,並將其歸入測試樣本,或者將這部分數據留出不用。隨後系統將判斷所有運行過程的質量,而不只考慮一次。

將此歸入雲就像是使用推薦機制同樣簡單。整個腳本將在您的集羣中運行,只需傳入恰當的路徑便可。若是在 EC2 的 10 節點集羣上運行,除一般的準備工做以外,訓練和測試的整個過程只需短短几分鐘。遺憾的是,若是您這樣運行,則對雲中整個數據集的運行質量將降低,由於某些郵件列表所包含的數據點極少。這些問題極可能未獲得充分的考慮。

實現生產的後幾步包括使模型做爲運行時系統的一部分,設置工做流以確保從系統得到反饋時更新模型。下面,我將介紹聚類。

聚類

與分類類似,Mahout 也有無數種聚類算法,每種算法都有着不同凡響的特色。例如,K-Means 的伸縮能力出色,但須要您預先指定須要的聚類數量;而狄利克雷聚類要求您選擇所需的模型分佈和聚類數量。聚類與分類有不少共同之處,在某些狀況下甚至能夠共同使用,將聚類做爲分類的一部分。除此以外,分類的大部分數據準備工做都與聚類類似,例如將原始內容轉換爲順序文件,隨後再轉換爲稀疏向量,所以您能夠參考 分類 部分,瞭解這些信息。

對於聚類來講,要回答的主要問題是:咱們可否根據內容的類似度來合情合理地組織全部消息(而不管項目如何)?例如,或許 Apache Solr 郵件列表中有關使用 Apache Tomcat 做爲一種 Web 容器的消息與針對 Tomcat 項目的消息更爲接近,而非原始項目。

就本例而言,前幾個步驟與分類類似,但在轉換爲稀疏向量以後就發生了差異。具體步驟以下:

  1. 與分類的第 1 步和第 2 步相同的步驟。
  2. $ bin/mahout kmeans --input "$SEQ2SP/tfidf-vectors" --output $CLUST_OUT -k 50 --maxIter 20 --distanceMeasure org.apache.mahout.common.distance.CosineDistanceMeasure --clustering --method mapreduce --clusters "$CLUST_OUT/clusters"

本例中運行 K-Means 來執行聚類,但 shell 腳本也支持運行狄利克雷聚類。(執行腳本時,您將看到選擇但願運行哪一種算法的提示。)在以前的示例中,值得深刻研究的參數以下:

  1. -k:要建立的聚類的數量。我隨意地選擇了 50,但也能夠選擇其餘值。
  2. --maxIter:K-Means 是一種迭代式算法,聚類中心將做爲各次迭代的一部分更新。有時憑算法自己不能保證成功完成,所以可利用此參數來確保算法完成。
  3. --distanceMeasure:肯定當前質心(centroid)與所檢查的點之間的類似度的距離指標。在本例中,我選擇了餘弦距離指標,這一般是適合文本數據的合理選擇。Mahout 還有多種其餘實現,值得您抽出時間用本身的數據進行嘗試。
  4. --clustering:告訴 Mahout 輸出哪些屬於哪一個質心的點。默認狀況下,Mahout 僅計算質心,由於這一般就能知足所有的要求。

在運行完成以後,您可使用 Mahout 的 ClusterDump 程序轉儲聚類質心(和相關的點)。最終結果將存儲在 kmeans 目錄下名稱以 clusters- 開頭、以 -final 結尾的子目錄中。具體的值將取決於運行任務使用了多少次迭代,例如 clusters-2-final 是第三次迭代的輸出結果。例如,如下命令將轉儲聚類,而不是運行小規模數據樣本:

bin/mahout clusterdump --seqFileDir ../output/ibm/clustering/kmeans/clusters-2-final
    --pointsDir ../output/ibm/clustering/kmeans/clusteredPoints/

--seqFileDir 指向所建立的質心,-pointsDir 是聚類點的目錄。清單 4 給出了小部分結果抽樣:


清單 4. 運行 ClusterDumper 的輸出示例
:VL-337881{n=4420 c=[
  Top Terms:
    user                                    =>0.060885823267350335
    mailscann                               => 0.05059369006868677
    cocoon                                  =>0.048781178576134204
    virus                                   => 0.04285897589148712
    derek                                   => 0.04084340722527813
    legal                                   =>0.040052624979813184
    scan                                    => 0.03861016730680097
    danger                                  => 0.03848600584647758
    csir                                    => 0.03712359352614157
    transtec                                => 0.03388019099942435
  Weight : [props - optional]:  Point:
  1.0 : [distance=0.888270593967813]: 
  /cocoon.apache.org/dev/200004.gz/NBBBIDJGFOCOKIOHGDPBKEPPEEAA.XXXXX = 
  [ag:0.165, briefli:0.250, chang:0.075, close:0.137, cocoon:0.060, 
  cocoon1:0.226, cocoon2:0.218, concept:0.277, develop:0.101, differ:0.144, 
  explain:0.154, greet:0.197, klingenderstrass:0.223, langham:0.223, look:0.105, 
  mailserv:0.293, matthew:0.277, mlangham:0.240, paderborn:0.215, processor:0.231, 
  produc:0.202, put:0.170, scan:0.180, tel:0.163, understand:0.127, virus:0.194]

在 清單 4 中,請注意輸出包括一個術語列表,算法已經肯定這是聚類中最有表明性的術語列表。在生成用於生產的標籤、在準備步驟中調優特性選擇時,這可能頗有幫助,由於中止詞(本例中,user 極可能就是一箇中止此)每每出如今前幾回試運行數據時的列表中。

正如您期待的那樣,在集羣中運行此任務就像在本地運行同樣簡單 — 就像這兩個示例中同樣簡單。在個人測試中,除了轉換內容所用的時間以外(約爲 150 分鐘),10 個節點上的實際聚類做業僅用了大約 40 分鐘。

遺憾的是,對於聚類,儘管 Mahout 確實提供了一些評估工具(請參見輸出頂級術語的 CDbwEvaluator 和 ClusterDumper 選項),但評估結果每每會簡化爲 「氣味測試(smell test)」。在氣味測試中,可視化聚類每每是最有益的作法,但不少圖形可視化工具包難以處理大型數據集,所以您可能須要依靠本身的設備實現可視化。

就像推薦和分類同樣,實現生產的後續步驟包括肯定獲取數據的工做流以及執行處理的頻率,固然,還包括在您的業務環境中實際應用。您可能須要嘗試不一樣的算法,肯定哪一種算法最適合您的數據。

回頁首

Mahout 將來展望

Apache Mahout 仍在經過多種方式向前發展。目前,社區的關注重點是經過執行性能測試、文檔整理、API 改進和新算法添加來促進 1.0 發佈版的推出。下一個發佈版(即 0.6 版)頗有可能在 2011 年的年末或不久以後推出。在較爲深刻的層面,該社區也開始着眼於分佈式、內存中的機器學習問題解決方法。在不少狀況下,單憑一臺機器難以處理複雜的機器學習問題,但 Hadoop 又引入了過多磁盤 I/O 方面的負擔。不管採用哪一種方法,關注可伸縮性的 Mahout 都能很好地幫助您解決當前最艱難的大數據問題,使得複雜機器學習算法的使用更加輕鬆。

致謝

特別感謝 Timothy Potter 協助我完成 AMI 打包,感謝 Mahout 專家 Sebastian Schelter、Jake Mannix 和 Sean Owen 的技術審查。本文的部份內容由 Amazon Apache 測試計劃提供支持。


回頁首

下載

描述 名字 大小 下載方法
Shell 腳本 j-mahout-scaling.zip 2KB HTTP

關於下載方法的信息


參考資料

學習

  • "Apache Mahout 簡介」(Grant Ingersoll,developerWorks,2009 年 9 月):Mahout 聯合創始人 Ingersoll 介紹了機器學習的基本概念,展現瞭如何使用 Mahout 來聚類文檔、提供推薦以及組織內容。

  • Apache Mahout:隨時關注 Apache Mahout 主頁,瞭解全部最新新聞、得到文檔和新下載內容。 

  • Powered By Mahout:Mahout 的 「powered by」 頁面列舉了願意公開其使用 Mahout 算法的狀況的衆多公司。 

  • Taming Text(Grant S. Ingersoll、Thomas S. Morton 和 Andrew L. Farris,Manning Publications,2011 年):這本書介紹了 Mahout 和用於構建基於文本的應用程序的相關開放源碼技術。 

  • Mahout 實戰 (Sean Owen、Robin Anil、Ted Dunning 和 Ellen Friedman,Manning Publications,2011 年):這本書是理解 Mahout 的終極指南。 

  • Apache Hadoop:瞭解 Hadoop。 

  • 算法:在 Mahout Wiki 中閱讀有關 Mahout 實現的算法的更多內容,並進一步瞭解社區關注的領域。 

  • 驚喜和巧合:在 Mahout 專家 Ted Dunning 的博客中瞭解有關對數似然性測量的有用之處。 

  • MongoDB:訪問 MongoDB 網站,發現有關 MongoDB 的更多內容。 

  • Apache Cassandra:訪問 Apache Cassandra 網站,得到有關 Apache Cassandra 的更多信息。在 Sean Owen 的博客文章 「Recommending (from) Cassandra」 中進一步瞭解如何將其與 Mahout 一塊兒使用。 

  • Amazon Web 服務,包括 EC2 在內:進一步瞭解 Amazon 的 EC2 基礎架構。 

  • 使用現有 Hadoop AMI:Mahout wiki 中的這個頁面詳述了在 EC2 中啓動 Hadoop 集羣的流程。 

  • Amazon EC2 用戶指南:查詢 EC2 用戶指南,瞭解本文中的 設置 步驟:
  • Message-ID:進一步瞭解郵件標頭和 Message-ID(消息 ID)。 

  • Apache Lucene:訪問 Apache Lucene 網站,瞭解 Apache Lucene 的更多信息。 

  • Martin Porter 博士的雪球式詞幹分析器:這些詞幹分析器一般適用於處理搜索和機器學習項目中的文本。 

  • 瀏覽 技術書店,閱讀有關這些主題和其餘技術主題的圖書。 

  • developerWorks 中國網站 Java 技術專區:查看數百篇關於 Java 編程各個方面的文章。 

得到產品和技術

討論

  • Mahout 用戶郵件列表:訂閱此郵件列表,以便提出問題、分享知識、討論話題。 

  • 加入 developerWorks 社區。與其餘 developerWorks 用戶聯繫,瀏覽開發人員推進的博客、論壇、小組和 wiki。

關於做者

Grant Ingersoll

Grant Ingersoll 是 Lucid Imagination 的創始人和技術成員之一。Grant 的編程興趣包括信息檢索、機器學習、文本分類和提取。Grant 是 Apache Mahout 機器學習項目的創始人之一,而且是 Apache Lucene 和 Apache Solr 項目的負責人和發言人。他仍是描述使用開源工具進行天然語言處理的圖書 Taming Text(Manning 出版社,即將上市)的合著者。

相關文章
相關標籤/搜索