使用Spark MLlib訓練和提供自然語言處理模型

OReillyData發表於2016-10-12

Idibon位於舊金山的一家專注於自然語言處理(NLP)的創業公司。從海量非結構化資料中識別關鍵資訊或是定製化實時互動是一些可以說明客戶如何利用我們Idibon的技術的例子。Spark ML和MLlib中的機器學習庫使得我們可以建立一個自適應的機器智慧環境,可以分析任何語言的文字,而且是遠超過Twitter每秒產生的單詞數量規模的文字量。

我們的團隊建立了一個平臺,它在分散式環境下訓練並提供成千上萬個NLP模型。這使得我們可以快速擴充套件並同時為多個使用者提供成千上萬個預測每秒。在這篇文章中,我們將會探索我們正在解決的問題的類別、我們遵循的過程、以及我們所使用的技術棧。這應該會對任何想要建立或者改進他們自己的NLP產品線的人有所幫助。

用Spark建立預測模型

我們的客戶需要自動將文件分類或者從中抽取資訊。這個需求可以是多種形式的,比如社交網路分析、資訊分類以及客戶通訊路由、新聞輿論監控、風險評分以及對低效的資料錄入過程進行自動化。所有這些任務有一個共性:建立預測模型,基於從原始文字抽取的特徵進行訓練。這個建立NLP模型的過程代表了由Spark提供的工具的一個獨特且有挑戰性的用例。

640?wx_fmt=jpeg

圖一:Idibon提供

建立一個機器學習產品的過程

一個機器學習產品可以分為三個概念化部分:預測本身、提供預測的模型、以及用來訓練該模型的資料集。

640?wx_fmt=jpeg

圖二:Michelle Casbon提供

預測

在我們的經驗中,最好是從商業問題開始並用它們來驅動資料集的選擇,而不是用資料集本身驅動專案的目標。如果你的確從一個資料集開始了,那麼儘快將資料與核心的商業需求聯絡起來是十分重要的。有了正確的問題之後,選擇有用的分類方法就變得很明確,這也是最終一個預測會提供的。

資料集

一單預測被定義好了,那麼哪些資料集是最有用的是顯而易見的,驗證你可以獲得的資料能夠支援你試圖解決的問題是十分重要的。

模型訓練

建立好任務和準備好要使用的資料之後,是時候來考慮模型了。為了生成準確的模型,我們需要訓練資料,這經常是人為生成的。這些人可能是公司內部或者諮詢公司的專家,或者很多情況下,他們是一組分析師中的一部分。

此外,許多工可以高效低成本的由像CrowdFlower這樣的眾包平臺來完成。我們喜歡他們的平臺,因為它將工作者基於其專長的領域進行分類,這在處理非英語的工作中尤其有用。

所有這些型別的工人為特定部分的資料集提交註解用來生成訓練資料。你需要用訓練資料來在新的或者餘下的資料集上做預測。基於這些預測,你可以決定下一組傳送給標註者的資料。這裡的重點是通過最少的人工判定來做出最好的模型。你持續在模型訓練、評估以及標註中迭代,在每次迭代中獲得更高的準確度。我們將這一過程稱作自適應學習,這是一種快速且高價效比的產生準確預測的方法。

操作化

為了支援自適應學習過程,我們建立了一個儘可能自動化的平臺。在沒有人工干預的情況下可以自動擴充套件的元件是支援動態波動的使用者請求API的關鍵所在。其中我們解決的一些非常困難的擴充套件性問題包括:

  • 文件儲存

  • 每秒提供對數千個獨立要求的預測

  • 支援持續訓練,無論訓練集或者模型引數是否變化都能自動化生成更新模型

  • 通過超引數調優來生成效能最好的模型

我們通過將AWS棧中的元件整合來解決問題,比如用Elastic負載均衡自動擴充套件組RDS以及Elastic快取。我們也通過New Relic以及Datadog來監控一系列指標,從而可以在一切變得離譜前警告我們。

下面是我們的基礎架構中的主要工具的高層架構解。

640?wx_fmt=jpeg

圖三:由Michelle Casbon提供

Spark的角色

我們的機器學習能力的一個核心的元件是Spark ML和MLlib中的優化功能。在NLP中利用這些會涉及到額外的持久化層,我們稱之為idiML。這使得我們可以在單個預測時利用Spark,而不是它最常見的被作為一次性處理大量資料的平臺的作用。

我們用Spark做什麼?

從更細節的層次上講,一個NLP產品有三個主要元件:

1.特徵抽取,文字被轉化為一個數值格式用以支援統計模型

2.訓練,基於每個特徵向量提供的分類生成模型

3.預測,訓練模型被用來為新的未預測的文字進行分類

每個元件的簡單例子如下所示:

640?wx_fmt=jpeg

圖四:由Michelle Casbon提供

特徵抽取

在特徵抽取階段,基於文字的資料被轉化為特徵向量的形式。這個向量代表了該文字的獨特特性且能夠通過任意的數學變換的順序生成。我們的系統被設計為可以很容易地適應於額外的特徵型別,例如從深度學習中獲取的特徵。但是為了簡潔,我們這裡只考慮基本特徵作為例子:

1.輸入:一個文件,由內容和可能有的後設資料組成

2.內容抽取:將我們感興趣的輸入部分分離出來,通常就是內容本身

3.標記化:文字分隔成單獨的單詞。在英語中,一個標記基本是一個被空格或標點符號圍繞的字串,但是在其他語言(比如說中文和日語)中,你可能需要定義什麼是一個單詞。

4.N元組:生成長度為n的單詞序列的集合,二元組和三元組是最常見的。

5.特徵查詢:為每一個獨特的特徵分配一個專門的數值索引,形成一個整數向量。這一特徵索引被儲存起來供之後的預測使用。

6.輸出:一個Spark MLlib的向量資料型別(org.apache.spark.mllib.linalg.Vector)的數值特徵向量

640?wx_fmt=jpeg

圖五:由Michelle Casbon提供

訓練

在訓練的階段,一個分類被接在一個特徵向量之後。在Spark中,這通過LabeledPoint資料型別來表示。在二元分類器中,這一分類是真或假(1.0或0.0)。

1.輸入:數值特徵向量

2.一個LabeledPoint被建立出來,由特徵向量和其對應的分類組成。這一分類是在之前的專案生命週期中人工生成的。

3.LabeledPoints的集合代表了被輸入到MLlib的LogisticRegressionWithLBFGS 方法的訓練資料的全集,該方法將基於給定的特徵向量和關聯的分類找到合適的模型

4.輸出:一個邏輯迴歸模型

640?wx_fmt=jpeg

圖六:由Michelle Casbon提供

預測

在預測的時候,在訓練時生成的模型被用來為新的文字提供分類。一個0-1之間的置信值表示了模型對預測結果的肯定程度。置信度越高,模型越是肯定。下面這些元件完成了整個預測過程:

1.輸入:與訓練資料同一領域的未預測資料

2.在未預測的文字上應用相同的特徵管道。在訓練過程中生成的特徵索引在這裡被用作查詢表。這使得一個特徵向量和訓練資料在同一個特徵空間內。

3.獲得已訓練的模型。

4.特徵向量被髮送至模型,分類作為預測結果被返回。

5.分類在使用的特定模型的上下文中被解釋,然後返回給使用者

6.輸出:對一個未預測資料的一個預測分類以及對應的置信度

640?wx_fmt=jpeg

圖七:由Michelle Casbon提供

預測資料型別

在傳統的Spark ML應用裡,預測通常是通過RDD和DataFrames來生成的,應用將文件資料載入到一列中,MLlib將預測的結果放置到另一列中。像所有的Spark應用一樣,這些預測任務可以分佈到一個叢集上來高效地處理拍位元組級別的資料量。然而,我們最需要的場景卻是與大資料相反的:我們經常需要分析一個單獨、短小的文字碎片並且儘快返回結果。理想情況下,最好是在一毫秒之內。

不出所料,DataFrame對這一用例並沒有優化,並且我們最初的基於DataFrame的原型缺乏對這個需求的支援。

對我們來說幸運的是,MLlib是通過一個高效的線性代數庫來實現的,所有我們計劃使用的演算法都包括了使用單一向量物件生成預測的無額外開銷的內部方法。這些方法看上去對我們的用例來說是完美的,所以我們設計了ldiML來極高效地將單獨文件轉化為單獨向量,哪樣的話我們可以使用Spark MLlib內部基於向量的預測方法。

640?wx_fmt=png

圖8:2014在MacBook Pro Retina上進行的效能測試。單一文件效能的較大差距是因為測試中現在沒有辦法利用多核能力。由Michelle Casbon提供

對於單個預測來說,我們觀察到使用Spark MLlib的向量型別比RDD型別在速度的改進最多可以達到兩個數量級。兩種資料型別的速度差異在較小的批數量下最為明顯。考慮到RDD是為處理大量資料而設計的,這種差異就比較合理了。在實時的網際網路環境中(比如說我們的場景),小批量目前來看是最為常見的應用場景。由於分散式處理已經構建在我們的伺服器和負載均衡上,Spark核心庫中的分散式元件對於在小資料環境中的獨立預測不是必要的。正如我們在開發ldiML所獲得經驗,Saprk MLlib對低延遲和實時應用來說是一個相當有用的高效能的機器學習庫。在最壞的情況下,ldiML的效能足以在中端膝上型電腦上為每一條Tweet實時做出情感分析。

640?wx_fmt=jpeg

圖九:由羅伯·曼羅提供,經允許使用

將其融入到我們含有ldiML的已有平臺中

為了提供儘可能準確的模型,我們想要能夠支援不同型別的機器學習庫。Spark有獨特的做法,我們想要讓我們的主要程式碼與特質隔離開。這指的是作為一個持久層(ldiML),可以使得我們將Spark的功能和我們自己寫的自然語言處理的程式碼結合起來。舉例來說,在進行超引數調優的時候,我們可以通過將來自我們自己庫的和Spark的元件整合起來訓練模型。這使得我們可以自動地選擇每一個模型上效能最佳的實現,而不是為所有的模型只選一種配置。

640?wx_fmt=jpeg

圖10, 由Michelle Casbon提供

為什麼是一個持久層?

使用持久層使得我們可以實施數千個模型的訓練和提供服務。這裡列出了ldiML給我們所提供的:

  • 一種可以在訓練中儲存引數的方法。為了返回對應的預測,這是必要的。

  • 對產品中每一部分進行版本控制的能力。這使得我們可以在進行程式碼更新後支援向後相容。版本控制也指可以回滾和支援專案週期中之前迭代的模型。

  • 為每個模型自動選擇最佳演算法的能力。在超引數調優的時候,不同機器學習庫的實現被用來組合並評估結果。

  • 通過標準化面向開發人員的元件快速吸收新的NLP特徵的能力。這裡提供了一個隔離層使得我們的特徵工程師和資料科學家不需要學習如何與新的工具進行互動。

  • 在任何環境中部署的能力。我們目前在EC2例項上使用Docker容器,但是我們的架構意味著我們也可以利用例如亞馬遜Lambda服務提供的快速實施部署能力

  • 單獨的基於通用InputStreams和OutputStreams的儲存和載入框架,將我們從磁碟讀寫的需求上解放。

  • 一個slf4j形式的日誌抽象,避免我們與任何特定框架之間的緊密繫結。

更快、更靈活的高效能系統

NLP與其他形式的機器學習不同,因為它直接操作人類產生的資料。這通常要比機器生成的資料要混亂隨意的多,由於語言本身就是模糊的,由此甚至在人類之間都會有不一致的解釋性。我們的目標是儘可能自動化NLP產品線,使得資源可以更高效地被利用起來:機器與人互相協作最終更好的幫助人。為了到達這一步並跨越語言的障礙,我們正在用諸如Spark的工具來建立高效能的系統,它們將是前所未有的快和靈活。

640?wx_fmt=jpeg

Michelle Casbon

Michelle Casbon是ldibon的資深資料科學工程師,她致力於將語言技術帶給世界上所有的語言。她具有十多年的開發經驗並涉及不同的行業,包括多媒體、投資銀行、醫療保健、零售業以及地理資訊服務。Michelle在劍橋大學完成了碩士學位,專注於NLP、語音識別、語音合成以及機器翻譯。她熱愛開源技術,並在Apache Spark專案裡做出了可觀的貢獻。


歡迎關注OReillyData公眾號,閱讀更多強文。

640?wx_fmt=jpeg


相關文章