Idibon位于舊金山的一家專注于自然語言處理(NLP)的創(chuàng)業(yè)公司。從海量非結(jié)構(gòu)化數(shù)據(jù)中識別關(guān)鍵信息或是定制化實時交互是一些可以說明客戶如何利用我們Idibon的技術(shù)的例子。Spark ML和MLlib中的機(jī)器學(xué)習(xí)庫使得我們可以創(chuàng)建一個自適應(yīng)的機(jī)器智能環(huán)境,可以分析任何語言的文本,而且是遠(yuǎn)超過Twitter每秒產(chǎn)生的單詞數(shù)量規(guī)模的文本量。
我們的團(tuán)隊建立了一個平臺,它在分布式環(huán)境下訓(xùn)練并提供成千上萬個NLP模型。這使得我們可以快速擴(kuò)展并同時為多個用戶提供成千上萬個預(yù)測每秒。在這篇文章中,我們將會探索我們正在解決的問題的類別、我們遵循的過程、以及我們所使用的技術(shù)棧。這應(yīng)該會對任何想要建立或者改進(jìn)他們自己的NLP產(chǎn)品線的人有所幫助。
用Spark建立預(yù)測模型
我們的客戶需要自動將文檔分類或者從中抽取信息。這個需求可以是多種形式的,比如社交網(wǎng)絡(luò)分析、信息分類以及客戶通信路由、新聞輿論監(jiān)控、風(fēng)險評分以及對低效的數(shù)據(jù)錄入過程進(jìn)行自動化。所有這些任務(wù)有一個共性:建立預(yù)測模型,基于從原始文本抽取的特征進(jìn)行訓(xùn)練。這個創(chuàng)建NLP模型的過程代表了由Spark提供的工具的一個獨特且有挑戰(zhàn)性的用例。

圖一:Idibon提供
建立一個機(jī)器學(xué)習(xí)產(chǎn)品的過程
一個機(jī)器學(xué)習(xí)產(chǎn)品可以分為三個概念化部分:預(yù)測本身、提供預(yù)測的模型、以及用來訓(xùn)練該模型的數(shù)據(jù)集。

圖二:Michelle Casbon提供
預(yù)測
在我們的經(jīng)驗中,最好是從商業(yè)問題開始并用它們來驅(qū)動數(shù)據(jù)集的選擇,而不是用數(shù)據(jù)集本身驅(qū)動項目的目標(biāo)。如果你的確從一個數(shù)據(jù)集開始了,那么盡快將數(shù)據(jù)與核心的商業(yè)需求聯(lián)系起來是十分重要的。有了正確的問題之后,選擇有用的分類方法就變得很明確,這也是最終一個預(yù)測會提供的。
數(shù)據(jù)集
一單預(yù)測被定義好了,那么哪些數(shù)據(jù)集是最有用的是顯而易見的,驗證你可以獲得的數(shù)據(jù)能夠支持你試圖解決的問題是十分重要的。
模型訓(xùn)練
建立好任務(wù)和準(zhǔn)備好要使用的數(shù)據(jù)之后,是時候來考慮模型了。為了生成準(zhǔn)確的模型,我們需要訓(xùn)練數(shù)據(jù),這經(jīng)常是人為生成的。這些人可能是公司內(nèi)部或者咨詢公司的專家,或者很多情況下,他們是一組分析師中的一部分。
此外,許多任務(wù)可以高效低成本的由像CrowdFlower這樣的眾包平臺來完成。我們喜歡他們的平臺,因為它將工作者基于其專長的領(lǐng)域進(jìn)行分類,這在處理非英語的工作中尤其有用。
所有這些類型的工人為特定部分的數(shù)據(jù)集提交注解用來生成訓(xùn)練數(shù)據(jù)。你需要用訓(xùn)練數(shù)據(jù)來在新的或者余下的數(shù)據(jù)集上做預(yù)測?;谶@些預(yù)測,你可以決定下一組發(fā)送給標(biāo)注者的數(shù)據(jù)。這里的重點是通過最少的人工判定來做出最好的模型。你持續(xù)在模型訓(xùn)練、評估以及標(biāo)注中迭代,在每次迭代中獲得更高的準(zhǔn)確度。我們將這一過程稱作自適應(yīng)學(xué)習(xí),這是一種快速且高性價比的產(chǎn)生準(zhǔn)確預(yù)測的方法。
操作化
為了支持自適應(yīng)學(xué)習(xí)過程,我們建立了一個盡可能自動化的平臺。在沒有人工干預(yù)的情況下可以自動擴(kuò)展的組件是支持動態(tài)波動的用戶請求API的關(guān)鍵所在。其中我們解決的一些非常困難的擴(kuò)展性問題包括:
- 文檔存儲
- 每秒提供對數(shù)千個獨立要求的預(yù)測
- 支持持續(xù)訓(xùn)練,無論訓(xùn)練集或者模型參數(shù)是否變化都能自動化生成更新模型
- 通過超參數(shù)調(diào)優(yōu)來生成性能最好的模型
我們通過將AWS棧中的組件整合來解決問題,比如用Elastic負(fù)載均衡、自動擴(kuò)展組、RDS以及Elastic緩存。我們也通過New Relic以及Datadog來監(jiān)控一系列指標(biāo),從而可以在一切變得離譜前警告我們。
下面是我們的基礎(chǔ)架構(gòu)中的主要工具的高層架構(gòu)解。

圖三:由Michelle Casbon提供
Spark的角色
我們的機(jī)器學(xué)習(xí)能力的一個核心的組件是Spark ML和MLlib中的優(yōu)化功能。在NLP中利用這些會涉及到額外的持久化層,我們稱之為idiML。這使得我們可以在單個預(yù)測時利用Spark,而不是它最常見的被作為一次性處理大量數(shù)據(jù)的平臺的作用。
我們用Spark做什么?
從更細(xì)節(jié)的層次上講,一個NLP產(chǎn)品有三個主要組件:
1.特征抽取,文本被轉(zhuǎn)化為一個數(shù)值格式用以支持統(tǒng)計模型
2.訓(xùn)練,基于每個特征向量提供的分類生成模型
3.預(yù)測,訓(xùn)練模型被用來為新的未預(yù)測的文本進(jìn)行分類
每個組件的簡單例子如下所示:

圖四:由Michelle Casbon提供
特征抽取
在特征抽取階段,基于文本的數(shù)據(jù)被轉(zhuǎn)化為特征向量的形式。這個向量代表了該文本的獨特特性且能夠通過任意的數(shù)學(xué)變換的順序生成。我們的系統(tǒng)被設(shè)計為可以很容易地適應(yīng)于額外的特征類型,例如從深度學(xué)習(xí)中獲取的特征。但是為了簡潔,我們這里只考慮基本特征作為例子:
1.輸入:一個文檔,由內(nèi)容和可能有的元數(shù)據(jù)組成
2.內(nèi)容抽取:將我們感興趣的輸入部分分離出來,通常就是內(nèi)容本身
3.標(biāo)記化:文本分隔成單獨的單詞。在英語中,一個標(biāo)記基本是一個被空格或標(biāo)點符號圍繞的字符串,但是在其他語言(比如說中文和日語)中,你可能需要定義什么是一個單詞。
4.N元組:生成長度為n的單詞序列的集合,二元組和三元組是最常見的。
5.特征查找:為每一個獨特的特征分配一個專門的數(shù)值索引,形成一個整數(shù)向量。這一特征索引被存儲起來供之后的預(yù)測使用。
6.輸出:一個Spark MLlib的向量數(shù)據(jù)類型(org.apache.spark.mllib.linalg.Vector)的數(shù)值特征向量

圖五:由Michelle Casbon提供
訓(xùn)練
在訓(xùn)練的階段,一個分類被接在一個特征向量之后。在Spark中,這通過LabeledPoint數(shù)據(jù)類型來表示。在二元分類器中,這一分類是真或假(1.0或0.0)。
1.輸入:數(shù)值特征向量
2.一個LabeledPoint被創(chuàng)建出來,由特征向量和其對應(yīng)的分類組成。這一分類是在之前的項目生命周期中人工生成的。
3.LabeledPoints的集合代表了被輸入到MLlib的LogisticRegressionWithLBFGS?方法的訓(xùn)練數(shù)據(jù)的全集,該方法將基于給定的特征向量和關(guān)聯(lián)的分類找到合適的模型
4.輸出:一個邏輯回歸模型

圖六:由Michelle Casbon提供
預(yù)測
在預(yù)測的時候,在訓(xùn)練時生成的模型被用來為新的文本提供分類。一個0-1之間的置信值表示了模型對預(yù)測結(jié)果的肯定程度。置信度越高,模型越是肯定。下面這些組件完成了整個預(yù)測過程:
1.輸入:與訓(xùn)練數(shù)據(jù)同一領(lǐng)域的未預(yù)測數(shù)據(jù)
2.在未預(yù)測的文本上應(yīng)用相同的特征管道。在訓(xùn)練過程中生成的特征索引在這里被用作查找表。這使得一個特征向量和訓(xùn)練數(shù)據(jù)在同一個特征空間內(nèi)。
3.獲得已訓(xùn)練的模型。
4.特征向量被發(fā)送至模型,分類作為預(yù)測結(jié)果被返回。
5.分類在使用的特定模型的上下文中被解釋,然后返回給用戶
6.輸出:對一個未預(yù)測數(shù)據(jù)的一個預(yù)測分類以及對應(yīng)的置信度

圖七:由Michelle Casbon提供
預(yù)測數(shù)據(jù)類型
在傳統(tǒng)的Spark ML應(yīng)用里,預(yù)測通常是通過RDD和DataFrames來生成的,應(yīng)用將文檔數(shù)據(jù)加載到一列中,MLlib將預(yù)測的結(jié)果放置到另一列中。像所有的Spark應(yīng)用一樣,這些預(yù)測任務(wù)可以分布到一個集群上來高效地處理拍字節(jié)級別的數(shù)據(jù)量。然而,我們最需要的場景卻是與大數(shù)據(jù)相反的:我們經(jīng)常需要分析一個單獨、短小的文本碎片并且盡快返回結(jié)果。理想情況下,最好是在一毫秒之內(nèi)。
不出所料,DataFrame對這一用例并沒有優(yōu)化,并且我們最初的基于DataFrame的原型缺乏對這個需求的支持。
對我們來說幸運(yùn)的是,MLlib是通過一個高效的線性代數(shù)庫來實現(xiàn)的,所有我們計劃使用的算法都包括了使用單一向量對象生成預(yù)測的無額外開銷的內(nèi)部方法。這些方法看上去對我們的用例來說是完美的,所以我們設(shè)計了ldiML來極高效地將單獨文檔轉(zhuǎn)化為單獨向量,哪樣的話我們可以使用Spark MLlib內(nèi)部基于向量的預(yù)測方法。

圖8:2014在MacBook Pro Retina上進(jìn)行的性能測試。單一文檔性能的較大差距是因為測試中現(xiàn)在沒有辦法利用多核能力。由Michelle Casbon提供
對于單個預(yù)測來說,我們觀察到使用Spark MLlib的向量類型比RDD類型在速度的改進(jìn)最多可以達(dá)到兩個數(shù)量級。兩種數(shù)據(jù)類型的速度差異在較小的批數(shù)量下最為明顯??紤]到RDD是為處理大量數(shù)據(jù)而設(shè)計的,這種差異就比較合理了。在實時的互聯(lián)網(wǎng)環(huán)境中(比如說我們的場景),小批量目前來看是最為常見的應(yīng)用場景。由于分布式處理已經(jīng)構(gòu)建在我們的服務(wù)器和負(fù)載均衡上,Spark核心庫中的分布式組件對于在小數(shù)據(jù)環(huán)境中的獨立預(yù)測不是必要的。正如我們在開發(fā)ldiML所獲得經(jīng)驗,Saprk MLlib對低延遲和實時應(yīng)用來說是一個相當(dāng)有用的高性能的機(jī)器學(xué)習(xí)庫。在最壞的情況下,ldiML的性能足以在中端筆記本電腦上為每一條Tweet實時做出情感分析。

圖九:由羅伯·曼羅提供,經(jīng)允許使用
將其融入到我們含有l(wèi)diML的已有平臺中
為了提供盡可能準(zhǔn)確的模型,我們想要能夠支持不同類型的機(jī)器學(xué)習(xí)庫。Spark有獨特的做法,我們想要讓我們的主要代碼與特質(zhì)隔離開。這指的是作為一個持久層(ldiML),可以使得我們將Spark的功能和我們自己寫的自然語言處理的代碼結(jié)合起來。舉例來說,在進(jìn)行超參數(shù)調(diào)優(yōu)的時候,我們可以通過將來自我們自己庫的和Spark的組件整合起來訓(xùn)練模型。這使得我們可以自動地選擇每一個模型上性能最佳的實現(xiàn),而不是為所有的模型只選一種配置。

圖10, 由Michelle Casbon提供
為什么是一個持久層?
使用持久層使得我們可以實施數(shù)千個模型的訓(xùn)練和提供服務(wù)。這里列出了ldiML給我們所提供的:
- 一種可以在訓(xùn)練中保存參數(shù)的方法。為了返回對應(yīng)的預(yù)測,這是必要的。
- 對產(chǎn)品中每一部分進(jìn)行版本控制的能力。這使得我們可以在進(jìn)行代碼更新后支持向后兼容。版本控制也指可以回滾和支持項目周期中之前迭代的模型。
- 為每個模型自動選擇最佳算法的能力。在超參數(shù)調(diào)優(yōu)的時候,不同機(jī)器學(xué)習(xí)庫的實現(xiàn)被用來組合并評估結(jié)果。
- 通過標(biāo)準(zhǔn)化面向開發(fā)人員的組件快速吸收新的NLP特征的能力。這里提供了一個隔離層使得我們的特征工程師和數(shù)據(jù)科學(xué)家不需要學(xué)習(xí)如何與新的工具進(jìn)行交互。
- 在任何環(huán)境中部署的能力。我們目前在EC2實例上使用Docker容器,但是我們的架構(gòu)意味著我們也可以利用例如亞馬遜Lambda服務(wù)提供的快速實施部署能力。
- 單獨的基于通用InputStreams和OutputStreams的存儲和加載框架,將我們從磁盤讀寫的需求上解放。
- 一個slf4j形式的日志抽象,避免我們與任何特定框架之間的緊密綁定。
更快、更靈活的高性能系統(tǒng)
NLP與其他形式的機(jī)器學(xué)習(xí)不同,因為它直接操作人類產(chǎn)生的數(shù)據(jù)。這通常要比機(jī)器生成的數(shù)據(jù)要混亂隨意的多,由于語言本身就是模糊的,由此甚至在人類之間都會有不一致的解釋性。我們的目標(biāo)是盡可能自動化NLP產(chǎn)品線,使得資源可以更高效地被利用起來:機(jī)器與人互相協(xié)作最終更好的幫助人。為了到達(dá)這一步并跨越語言的障礙,我們正在用諸如Spark的工具來建立高性能的系統(tǒng),它們將是前所未有的快和靈活。
Michelle Casbon
Michelle Casbon是ldibon的資深數(shù)據(jù)科學(xué)工程師,她致力于將語言技術(shù)帶給世界上所有的語言。她具有十多年的開發(fā)經(jīng)驗并涉及不同的行業(yè),包括多媒體、投資銀行、醫(yī)療保健、零售業(yè)以及地理信息服務(wù)。Michelle在劍橋大學(xué)完成了碩士學(xué)位,專注于NLP、語音識別、語音合成以及機(jī)器翻譯。她熱愛開源技術(shù),并在Apache Spark項目里做出了可觀的貢獻(xiàn)。

