NPL 太難怎麼辦?教你 8 步實現代碼編寫!
計算機很擅長處理結構化的數據,比如工作表、資料庫的表等。但我們人類的溝通通常使用辭彙,而不是表。對於計算機來說,這是件很不幸的事情。
不幸的是,我們並沒有生活在這個所有數據都是結構化的平行歷史中。
這個世界中的許多信息都是非結構化的,比如英文文本,或任何其他人類語言。我們怎樣才能讓計算機理解非結構化的文字,並從中提取出數據呢?
自然語言處理(Natural Language Processing,簡稱NLP)是人工智慧的一個子領域,它專註於讓計算機理解並處理人類的語言。我們來看看NLP的工作原理,學學如何寫一段Python程序從人類語言的文本中提取信息吧。
注意:如果你不關心NLP的工作原理,只希望複製粘貼代碼,可以直接跳到「用Python編寫NPL的流水線代碼」一節。
計算機能理解人類語言嗎?
自從計算機誕生以來,程序員們就在試圖編寫能理解英語等語言的程序。理由很明顯,人類幾千年來一直在用這些語言寫東西,如果計算機能閱讀並理解這些東西,那對人類非常有幫助。
計算機還不能真正地像人類那樣理解英語,但它們已經能做得很好了!在某些特定的領域,NLP的能力已經像魔法一樣了。在項目中使用NLP也許可以節省不少時間。
而且更好的是,NLP最新的進展可以很容易地通過開源的Python庫訪問到,如spaCy、textacy和neuralcoref等。你只需要寫幾行Python就可以。
從文本提取含義很困難
閱讀並理解英語的過程非常複雜,這還沒有考慮英語其實根本沒有合乎邏輯並且一致的規則。比如,這個新聞標題是什麼意思?
「Environmental regulators grill business owner over illegal coal fires.」
規制者是在質問企業家關於非法燒煤的問題,還是規制者真的把企業家放在火上燒?可見,讓計算機分析英語是非常複雜的。
讓機器學習做任何複雜的事情通常意味著要建立一個流水線。其思想就是將問題分解成許多非常小的問題,然後用機器學習分別解決每個小問題。然後將幾個機器學習模型連接起來讓它們互相提供數據,就可以做非常複雜的事情。
這正是我們要在NLP上應用的策略。我們將把理解英語的整個過程分解成幾個小步驟,看看每個步驟是怎樣工作的。
一步步建立NLP流水線
我們來看一段來自維基百科的文本:
London is the capital and most populous city of England and the United Kingdom. Standing on the River Thames in the south east of the island of Great Britain, London has been a major settlement for two millennia. It was founded by the Romans, who named it Londinium.(來源:維基百科條目「London」)
這段文字包含幾個有用的內容。我們希望計算機閱讀這段文本後能理解,倫敦是一個城市,倫敦位於英格蘭,倫敦由羅馬人建立等等。但為了達到這個目標,首先我們需要讓計算機理解書面語言的最基本的概念。
第1步:句子分片
流水線的第一步就是將文本分解成獨立的句子。我們可以得到:
1.「London is the capital and most populous city of England and the United Kingdom.」
2.「Standing on the River Thames in the south east of the island of Great Britain, London has been a major settlement for two millennia.」
3.「It was founded by the Romans, who named it Londinium.」
我們可以假設每個英文句子都是獨立的含義,寫程序理解單個句子要比理解整個段落容易得多。
編寫分句的模型非常容易,只需要在看到標點符號時進行分句即可。但現代的NLP流水線同時會用更複雜的技術,即使文檔的格式不那麼清晰也能正常分解。
第2步:將辭彙變成標記(token)
現在我們已經把文檔變成了句子,我們可以每次處理一個句子了。首先從文檔的第一個句子開始:
「London is the capital and most populous city of England and the United Kingdom.」
流水線中的下一步是將這個句子分解成獨立的單詞,或者叫標記(token)。這一步叫做標記化(tokenization)。下面是結果:
「London」, 「is」, 「 the」, 「capital」, 「and」, 「most」, 「populous」, 「city」, 「of」, 「England」, 「and」, 「the」, 「United」, 「Kingdom」, 「.」
對於英語來說標記化很容易,只需要利用空格將單詞分隔開即可。我們還要把標點符號作為標記來處理,因為標點符號也是有意義的。
第3步:預測每個標記的詞性
下一步,我們將查看每個標記,並猜測它的詞性——是名詞、動詞、還是形容詞?等等。弄清楚每個單詞的角色能幫我們分析句子的含義。
我們可以把每個單詞(以及它上下文環境中的一些單詞)提供給已經訓練好的詞性分類模型:
詞性模型是使用幾百萬個已經標註了每個單詞詞性的英語句子訓練過的,它被訓練成能夠重複這種行為。
要注意這個模型完全是依據統計學的,它並不像人類那樣理解每個單詞的實際意義。它只知道怎樣根據以前見過的類似句子來猜測詞性。
在處理完整個句子之後,我們得到類似下面的結果:
有了這些信息,我們就可以提取一些最基本的含義了。例如,我們看到句子中的名詞包含「London」和「capital」,所以這個句子很可能就是在討論倫敦。
第4步:文本詞形還原(lemmatization)
在英語(及大多數語言)中,單詞會有不同的詞形。例如下面兩個句子:
I had a pony.
I had two ponies.
兩個句子都在談論名詞pony,但它們使用的是不同的變形。在使用計算機處理文本時,知道每個詞的基本形很有幫助,這樣可以知道兩個句子討論的是同一個概念。否則「pony」和「ponies」兩個字元串會被計算機當作兩個完全不同的單詞。
在NLP中,這個過程叫做詞形還原(lemmatization),即找出句子中每個單詞的最基本詞形(lemma)。
動詞也有同樣的問題。動詞的詞形還原可以找出它們的詞根,即沒有發生變化的形式。因此「I had two ponies」將會變成「I [have] two [pony]」。
詞性還原通常通過查找表的方式進行,根據它們的詞性,在詞形表中找出原形,可能還要加上一些特殊規則來處理一些沒見過的詞。
下面是經過詞形還原,添加了詞根形式之後的句子:
唯一的變化就是把「is」變成了「be」。
第5步:確定停止詞
接下來,我們要考慮句子中每個單詞的重要性。英語有許多出現頻率非常高的填充詞,如「and」、「the」和「a」等。在進行詞頻統計時,這些單詞會引入許多雜訊,因為它們出現的頻率比其他單詞高得多。一些NLP流水線會將這些詞標記為停止詞(stop words),意思是在進行統計分析之前要過濾掉這些詞。
將停止詞標記成灰色後,我們的句子如下所示:
識別停止詞只需要檢查一個固定的已知停止詞的辭彙表即可。不過,沒有能適用於所有應用的標準停止詞列表。不同應用程序需要忽略的單詞可能並不相同。
例如,如果你要建立一個搖滾樂隊搜索引擎,你就不能忽略單詞「The」。因為單詞「The」不僅出現在許多樂隊名稱里,八十年代還有個著名的搖滾樂隊就叫The The!
第6步:依賴解析
下一步是要找出句子中所有單詞之間的關係。這一步叫做依賴解析(dependency parsing)。
目標是建立一棵樹,給句子中的每個單詞找出一個唯一的父單詞。樹的根節點就是句子的主要動詞。這裡是句子的解析樹最初的樣子:
但我們還可以進一步。除了找出每個詞的父單詞之外,我們還能預測兩個單詞之間的關係類型:
這個解析樹顯示,句子的主語是名詞「London」,它和「capital」是「be」關係。我們終於知道了些有用的東西——倫敦是首都!如果我們遍歷句子的整個解析樹(圖中沒有畫出),我們甚至還能發現倫敦是英國的首都。
就像我們之前利用機器學習模型預測詞性一樣,依賴解析的原理也是將單詞提供給一個機器學習模型,並計算出結果。但解析單詞之間的依賴通常是個很複雜的任務,想要解釋清楚其原理,那又是一整篇文章了。如果想知道它的工作原理,可以從Matthew Honnibal的這篇文章《Parsing English in 500 Lines of Python》(https://explosion.ai/blog/parsing-english-in-python)開始閱讀。
但是,儘管作者在2015年說這種方式是標準方法,但實際上現在已經過時,即使作者自己都不再使用這種方法了。在2016年,Google發布了一個新的依賴解析器,叫做Parsey McParseface,它使用深度學習方法,其能力在各種測試中表現優異,於是迅速在業界流傳開來。一年之後,他們發布了一個更新的模型,叫做ParseySaurus,有了進一步提高。換句話說,解析的技術現在依然是個活躍的研究領域,依然在不斷改變和進步。
還要記住一點很重要的事情:許多英語句子是模糊的,本身就很難解析。這種情況下,模型會根據句子的不同分析結果進行猜測,但有時模型並不完美,也會弄出笑話。但我們的NLP模型還在不斷進步,越來越能夠以合理的方式解析文本。
想要在自己的句子上試試依賴解析嗎?spaCy團隊給出了一個很不錯的互動式演示(https://explosion.ai/demos/displacy)。
第6b步:找出名詞片語
到現在為止,我們是把句子中的每個單詞當作獨立的實體進行處理的。但有時候對表示同一個意思的單詞進行分組可能更有道理。我們可以使用依賴解析樹提供的信息,自動地將單詞分成表示同一含義的組。
例如,下面的句子:
可以將名詞分組,得到下面的句子:
是否需要做這一步,取決於我們的最終目標。但如果我們不需要知道究竟哪個詞是形容詞,而更關心整句的含義,那麼這一步通常能很容易地將句子簡化。
第7步:命名實體識別(NER)
現在我們完成了所有的力氣活兒,終於可以從語法中解脫出來,開始提取語義的實際工作了。
我們句子中的名詞如下所示:
一些名詞表示真實存在的東西。例如,「London」、「England」和「United Kingdom」都表示地圖上的物理位置。最好能夠檢測出它們!有了這些信息,我們就能利用NLP自動找出文檔中提到的物理位置。
命名實體識別(Named Entity Recognition,簡稱NER)的目的就是檢測並標記這些表示真實世界中存在的概念的名詞。把句子中的每個標記通過NER模型之後得到的結果如下:
但NER系統並不只是簡單的字典查找而已。它還會通過單詞在句子中出現位置的上下文,和一個統計模型來猜測單詞表示的名詞的類別。好的NER系統能通過上下文提示區分出人名「Brooklyn Decker」和地名「Brooklyn」。
下面是通常的NER系統能標記的一些對象:
人名;
公司名;
地理位置(包括物理位置和政治位置);
產品名;
日期和時間;
金錢數量;
事件名。
NER可以簡化從文本中提取結構化數據的過程,因此得到了非常廣泛的應用。這是從NLP流水線中快速獲得價值的最簡單的方法之一。
想要試試命名實體識別嗎?spaCy也提供了另一個互動式的演示(https://explosion.ai/demos/displacy-ent)。
第8步:相互引用解析
此時,我們已經擁有了該句子的有意義的表示。我們知道了每個詞的詞性,與其他單詞的關係,以及哪些詞是關於命名實體的。
但是,我們還有個重要的問題。英語里有很多代詞,如he、she和it等。我們經常使用這些代稱,以避免在句子中反覆寫出具體的名字。人類能根據上下文跟蹤這些單詞的意思。但NLP模型還不知道代詞的意思,因為它每次只會檢查一個句子。
我們來看看文檔中的第三個句子:
「It was founded by the Romans, who named it Londinium.」
用我們的NLP流水線分析這句話,我們知道「it」是由羅馬人建立的。但如果能知道「London」是由羅馬人建立的就會好得多。
人類閱讀這句話時很容易就能發現「it」指代的是「London」。相互引用解析(coreference resolution)的作用就是通過在句子間跟蹤代詞,找出這些同義的映射。我們希望能找出所有指代同一個實體的單詞。
在文檔上對「London」進行相互引用解析,得到以下結果:
把相互引用解析的結果跟解析樹和命名實體的信息聯合起來,我們就能從文檔中提取出更多的信息!
相互引用解析是我們要實現的流水線中最難的步驟之一。它甚至比句子分析還要困難。最近深度學習方面的進展給出了更精確的實現,但仍然不夠完美。想了解具體情況可以參考這裡(https://medium.com/huggingface/state-of-the-art-neural-coreference-resolution-for-chatbots-3302365dcf30)。
希望嘗試下相互引用解析?可以看看Hugging Face的這個很不錯的相互引用解析演示(https://huggingface.co/coref/)。
用Python編寫NLP流水線
下面是NLP流水線的概貌:
相互引用解析是個可選的步驟,不一定每次都會做。
注意:在繼續之前,應當提一下,這些只是常見的NLP流水線中的步驟,但你可以根據你的目的以及NLP庫的實現情況決定跳過某些步驟,或者重新排列它們的順序。例如,像spaCy等庫會利用依賴解析的結果,在流水線中更晚的地方進行句子分片。
我們該如何編寫這個流水線呢?感謝spaCy等偉大的Python庫,這些工作已經由他們完成了!這些步驟都已經編碼好,可以拿來就用。
首先,假設你安裝了Python 3,你可以這樣安裝spaCy:
然後在一段文本上運行NLP,代碼如下所示:
運行後就能得到一個從文檔中檢測出命名實體及類型列表:
從這裡(https://spacy.io/usage/linguistic-features#entity-types)可以看到每個實體代碼的含義。
注意它犯了一個錯誤,把「Londinium」當做了人名,而不是地名。很可能是因為它的訓練數據集中不包括這個詞,所以它做了最好的猜測。如果你要分析ID文本包含獨有或專用的術語,那麼命名實體檢測通常需要一些模型調優(https://spacy.io/usage/training#section-ner)。
我們用實體檢測的思路做一個數據清洗程序。假設你要遵守新的GDPR隱私規制,而且你在幾千份文檔中發現了能用於識別個人的信息,如人名等。你的任務是刪除文檔中的所有名字。
手工瀏覽幾千份文檔並刪除所有名字可能會花掉幾年的時間。但用NLP就像呼吸一樣容易。下面這個簡單的清洗程序能刪除所有檢測到的名字:
運行這段代碼可以看到它工作得很好:
提取事實
能用spaCy直接實現的功能非常棒。但你還可以把解析過的spaCy輸出作為輸入,提供給更複雜的數據提取演算法。有一個Python庫叫做textacy(http://textacy.readthedocs.io/en/stable/),在spaCy的基礎上實現了集中常見的數據提取演算法。這是個很好的七點。
它實現的演算法之一叫做半結構化聲明提取(https://textacy.readthedocs.io/en/stable/api_reference.html#textacy.extract.semistructured_statements)。我們可以用它在解析樹中搜索簡單的聲明,聲明的主語為「London」,動詞為任意形式的「be」,這樣可以找出許多關於倫敦的事實。
下面是代碼:
下面是輸出結果:
看起來也不並不那麼驚艷,但如果在整個倫敦的維基百科條目上運行同樣的代碼,就能得到令人驚訝的結果:
現在事情開始有意思了!這些自動收集的信息數量很龐大。
還可以安裝neuralcoref庫(https://github.com/huggingface/neuralcoref),向流水線中加入互相引用解析。這樣能得到更多的事實,因為它能抓住那些主語是「it」而不是「London」的句子。
還能做什麼?
閱讀spaCy的文檔(https://spacy.io/api/doc)和textacy的文檔(http://textacy.readthedocs.io/en/latest/),你會發現許多使用各種方法解析文本的例子。我們現在介紹的只是滄海一粟。
這裡還有另一個實際的例子:想像你在建一個網站,利用我們從上面的例子中提取的信息,允許用戶查看每個城市的信息。
如果網站上有個搜索功能,可以像Google那樣提供常用的搜索自動完成:
Google自動完成了對「London」給出的建議
要實現這一點,我們需要一個可能的自動完成列表才能給用戶提供建議。我們可以用NLP迅速地生成這些數據。
下面的方法可以從文檔中提取出經常被提到的名詞:
在倫敦的維基百科條目上運行這段代碼,可以得到以下的輸出:
繼續深入
這只是個NLP最簡單的介紹。以後的文章里我們會討論其他NLP的應用,如文本分類,以及像Amazon Alexa那種系統怎樣解析問題等。
但現在,你可以安裝spaCy(https://spacy.io/)盡情嘗試了!如果你不用Python,也可以選其他的NLP庫,概念應該都是相同的。
原文:https://medium.com/@ageitgey/natural-language-processing-is-fun-9a0bff37854e
作者:Adam Geitgey,對計算機和機器學習感興趣,喜歡寫有關機器學習的文章。
譯者:彎月,責編:郭芮
※剛剛,中興通訊董事長殷一民道歉!
※人工智慧迎來寒冬,自動駕駛汽車發展受阻
TAG:CSDN |