計算機如何理解我們的語言?NLP is fun!
來自專欄 AI科技大本營6 人贊了文章
作者 | Adam Geitgey
譯者 | 婉清
編輯 | Jane
【導讀】我們從日常每天都會用到的推薦系統到現在研究火熱的開放性聊天、對話機器人,越來越多的產品與應用的背後都需要自然語言處理(NLP)和知識圖譜的技術。也有越來越多的學者與工作人員投身於 NLP 領域的研究。為什麼要研究NLP呢?如果計算機想要更好的理解人類的語言,擁有更好的人機交互體驗,都離不開 NLP。那麼,計算機到底是如何理解人類語言的?接下來讓我們跟著作者 Adam Geitgey ,和他一起體會自然語言處理技術里那些有意思的事情。
計算機非常擅長處理像電子表格、資料庫這樣的結構化數據。但是,人與人之間是用語言來交流的,而不是用表格。這對計算機來說就很倒霉了。
然而不幸的是,我們並不是生活在所有數據都是結構化的歷史交替版本中
這個世界上的許多信息都是非結構化的,如英語,或者其他人類語言寫成的原文。那麼,如何讓計算機理解這種非結構化文本並從中提取數據呢?
自然語言處理(Natural Language Processing,NLP)是人工智慧的子領域之一,其重點是使計算機能夠理解和處理人類語言。在本文中,我們將知曉NLP是如何工作的,並學習如何使用Python編寫能夠從原始文本提取信息的程序。(註:作者在文中選擇的語言對象是英語)
一、計算機能夠理解語言嗎?
自從計算機問世以來,為了能夠開發出可以理解語言的程序,程序員們一直在努力。為什麼一定要這麼做呢?理由很簡單:人類運用語言已經有千年的歷史,如果計算機能夠讀懂這些,對人們將會非常有幫助。
計算機雖然還不能像人類那樣真正地理解語言,但是它們現在已經可以做很多事情了。在某些領域中,可以用NLP來做的那些事情,已經能夠讓人感到很神奇了。如果將NLP技術應用到你自己的項目,也許可以節省很多時間。
好消息是,NLP的最近進展可以通過開源Python庫(如spaCy、textcy 和 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)
這一段包含了幾個有用的事實。如果計算機能夠讀懂這段文字,並理解London(倫敦)是一座城市,位於England(英格蘭),由Romans(羅馬人)建立的地方,那就太好了。但是要實現這一目標,就必須教會計算機學習書面語言的最基本的概念,然後再繼續前進。
第一步:句子切分(Sentence Segmentation)
工作流的第一步,是將文本切分成單獨的句子。我們得到的是:
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工作流通常會使用更為複雜的技術,就算文檔格式不是很乾凈利落,也能正常工作。
第二步:單詞標記(Word Tokenization)
現在我們已經將文本切分成了句子,這樣就可以做到一次處理一個句子。就從這段文本的第一條句子開始吧:
「London is the capital and most populous city of England and the United Kingdom.」
NLP工作流中的下一步就是將這個句子切分成單獨的單詞或標記。這就是所謂的「標記」(Tokenization)。以下是單詞標記的結果:
「London」, 「is」, 「 the」, 「capital」, 「and」, 「most」, 「populous」, 「city」, 「of」, 「England」, 「and」, 「the」, 「United」, 「Kingdom」, 「.」
標記在英語中很容易做到。只要單詞之間有空格,我們就可以將它們分開。我們還將標點符號視為單獨的標記,因為標點符號也有意義。
第三步:預測每個標記的詞性
接下來,我們將查看每個標記並試著猜測它的詞性:名詞、動詞還是形容詞等等。只要知道每個單詞在句子中的作用,我們就可以開始理解這個句子在表達什麼。
我們可以通過將每個單詞(以及周圍的一些額外單詞)輸入到預訓練的詞性分類模型來實現,如下圖所示:
需要記住一點:這種模型完全基於統計數據,實際上它並不能像人類那樣理解單詞的含義。它只知道如何根據以前所見過的類似句子和單詞來猜測詞性。
在處理完整個句子後,我們會得到這樣的結果,如下圖所示:
有了這些信息之後,我們就可以開始收集一些非常基本的含義。比如,我們看到這個句子中的名詞包括「London」和「capital」,所以可以認為這個句子可能是在說 London。
第四步:文本詞形還原(Text Lemmatization)
在英語(以及大多數語言)中,單詞是以不同的形式出現的。看看下面這兩個句子:
1.I had a pony.
2.I had two ponies.
這兩個句子都有名詞「pony」,但是它們的詞性不同。當計算機處理文本時,了解每個單詞的基本形式是很有幫助的,唯有如此你才能知道這兩個句子是在討論同一個概念。否則,字元串「pony」和「ponies」在計算機看來就是兩個完全不同的單詞。
在NLP中,我們將發現這一過程叫「詞形還原」(Lemmatization),就是找出句子中每個單詞的最基本的形式或引理。
這同樣也適用於動詞。我們還可以通過找到動詞的詞根,以非共軛形式(unconjugated form)來引申動詞。所以,「I had two ponies」就變成了「I [have] two [pony].」
詞形還原通常是通過查找單詞生成表格來完成的,也可能有一些自定義規則來處理你以前從未見過的單詞。
下面是句子詞形還原之後添加動詞的詞根形式之後的樣子:
我們所做的唯一改變就是將「is」變成「be」。
第五步:識別停止詞(Identifying Stop Words)
接下來,我們要考慮句子中每個單詞的重要性。英語中有很多填充詞,經常出現「and」、「the」和「a」。當對文本進行統計時,這些填充詞會帶來很多噪音,因為它們比其他詞出現得更頻繁。一些NLP工作流會將它們標記為停止詞(stop words),即在進行任何統計分析之前可能想要過濾掉的單詞。
這個句子使用停用詞變灰之後看上去像下面的樣子:
我們是通過檢查已知停止詞的編碼列表來識別停止詞的。但是,並沒有一個適合所有應用的標準停止詞列表。因此,要忽略的單詞列表可能因應用而異。
例如,如果你要構建搖滾樂隊的搜索引擎,你要確保不會忽略「The」這個單詞。因為「The」這個單詞不僅僅出現在很多樂隊的名字中,上世紀80年代還有一支著名的搖滾樂隊就叫「The The」!
第六步:依存句法分析(Dependency Parsing)
下一步就是弄清楚句子中所有單詞是如何相互關聯的,這稱為依存句法分析(Dependency Parsing)。
我們的目標就是構建一棵依存樹,為句子中每個單詞分配一個母詞(parent word)。這棵樹的根就是句子中的主動詞(main verb)。這個句子的解析樹的開頭看上去如下圖所示:
但我們還可以更進一步。除了識別每個單詞的母詞之外,我們還可以預測這兩個單詞之間存在的關係類型:
這棵解析樹向我們展示了這個句子的主語是名詞「London」,它與單詞「capital」有「be」的關係。這樣,我們終於知道了一些有用的信息:London是一個capital(首都)!如果我們按照完整的解析樹來解析這個句子(超出所示內容),我們甚至會發現:London是英國的首都。
就像我們之前使用機器學習模型預測詞性一樣,依存語法分析也可以通過將單詞輸入到機器學習模型中並輸出結果來實現。但是,解析單詞的依存關係是一項特別複雜的任務,需要另起一篇文章來詳細解釋。如果你好奇它是如何工作的,可以去參閱Matthew Honnibal寫的一篇優秀文章《Parsing English in 500 Lines of Python》,我們在文末附上了閱讀鏈接。
要注意的是:儘管這篇文章的作者在 2015 年聲稱,這種方法現在已成為標準。但是實際上,它已經過時了,甚至連作者都不再使用這個方法了。2016年,Google 發布了一個新的依存句法分析器,名為 Parsey McParseface,它使用了一種新的深度學習方法,迅速在整個行業流行開來,其性能超過了以前的基準測試。一年後,他們發布了一種叫做 ParseySaurus 的新模型,實現了進一步的改進。換句話說,句法分析技術仍然是一個活躍的研究領域,還在不斷變化和改進。
此外,英語中有很多句子是模稜兩可的,很難分析。在這些情況下,模型會根據句子的分析版本進行猜測,但是並不完美,有時候模型會出現令人尷尬的錯誤。但隨著時間的推移,我們的NLP模型將繼續以合理的方式更好地分析文本。
第六b步:查找名詞短語
到目前為止,我們把句子中的每個單詞都視為一個獨立的實體。但有時候將表示一個想法或事物的單詞放在一起更有意義。我們可以用依存句法解析樹中的信息,自動將所有討論同一事物的單詞分組在一起。
例如,下面這個形式:
我們可以對名詞短語進行分組來生成如下圖所示:
是否採取這一步驟,要取決於我們的最終目標。但是,如果我們不需要關心哪些單詞的額外細節,而是更關心提取完整的意思,那麼這通常是簡化句子的快速而簡單的方法。
第七步:命名實體識別(NER)
既然我們已經完成了所有這些艱苦的工作,我們終於可以越過初級語法,開始真正地提取句子的意思。
在這個句子中,我們有下列名詞:
如上圖所示,有些名詞表示世界上真實的事物。例如,「London」、「England」和「United Kingdom」代表的是地圖上的物理位置。能夠檢測到這一點,真實太好了!有了這些信息,我們就可以使用NLP自動提取文本中提到的真實世界位置列表。
命名實體識別(Named Entity Recognition,NER)的目標是用它們所代表的真實概念來檢測和標記這些名詞。在我們的NER標記模型中運行每個標記之後,這條句子看起來如下圖所示:
但是,NER系統並非只是簡單地進行字典查找。相反,它們使用單詞如何出現在句子中的上下文和統計模型來猜測單詞所代表的名詞類型。一個優秀的NER系統可以通過上下文線索來區分人名「Brooklyn Decker」和地名「Brooklyn」之間的區別。
下面是典型NER系統可以標記的一些對象:
- 人名
- 公司名稱
- 地理位置(包括物理位置和行政位置)
- 產品名
- 日期和時間
- 金額
- 事件名稱
NER 有很多用途,因為它可以很容易地從文本中獲取結構化數據。這是快速從 NLP工作流中獲取價值的最簡單方法之一。
第八步:指代消解
至此,我們已經對句子有了一個有用的表述。我們知道了每個單詞的詞性,這些單詞之間的關係,以及哪些單詞表示命名實體。
但是,我們仍然有一個很大的問題。在英語中有大量像「he」、「she」、「it」這樣的代詞。這些代詞是我們使用的「快捷方式」,這樣某些名稱就不用在每條句子中反覆出現。人們可以根據文本中上下文來理解這些代詞的含義。但NLP模型做不到這一點,它不會知道這些代詞代表的是什麼意思,因為它只能逐句檢測每個句子。
讓我們看一下文本中的第三句話:
「It was founded by the Romans, who named it Londinium.」
如果我們用 NLP工作流來分析這個句子,模型只知道「It」是由 Romans 創立的。但是,我們讀過後都知道,「It」指的是「London」,這句的意思是:London是由 Romans 創立的。
指代消解(Coreference Resolution)的目標是,通過跟蹤句子中的代詞來找到相同的映射。我們要弄清楚所有指向同一個實體的代詞。
如下圖所示,是文本中為「London」一詞進行指代消解的結果:
通過將指代消解、解析樹和命名實體信息相結合,我們應該能夠從這段文本中提取大量的信息!
指代消解是我們工作流準備實施中最困難的步驟之一。它比句子解析還要難。深度學習方面的最新進展已經產生了更為準確的新方法,但是還不夠完美。
四、用Python編寫NLP工作流
如下圖所示,是我們完整的NLP工作流概述:
註:指代消解是可選步驟,並非必選步驟。上圖所示的這些是典型的NLP工作流中的步驟,但你可以跳過某些步驟或重新排序步驟,這要取決於你想做的事情以及NLP庫的實現方式。例如,某些像spaCy這樣的庫使用依存句法分析的結果在工作流中進行句子切割。
首先,假設你已經安裝了Python 3,那麼可以按照下面的步驟安裝 spaCy:
# Install spaCy pip3 install -U spacy# Download the large English model for spaCypython3 -m spacy download en_core_web_lg# Install textacy which will also be usefulpip3 install -U textacy
然後,下面是運行NLP工作流的代碼:
import spacy# Load the large English NLP modelnlp = spacy.load(en_core_web_lg)# The text we want to examinetext = """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."""# Parse the text with spaCy. This runs the entire pipeline.doc = nlp(text)# doc now contains a parsed version of text. We can use it to do anything we want!# For example, this will print out all the named entities that were detected:for entity in doc.ents: print(f"{entity.text} ({entity.label_})")
運行後將獲得我們的文本中檢測到的命名實體和實體類型的列表:
London (GPE)England (GPE)the United Kingdom (GPE)the River Thames (FAC)Great Britain (GPE)London (GPE)two millennia (DATE)Romans (NORP)Londinium (PERSON)
注意,在「Londinium」一詞上有一個錯誤:認為該詞是人名而非地名。之所以出現這種錯誤是因為訓練集中沒有與之類似的東西,它所能做出的最好猜測。如果你要解析具有此類唯一或專用術語的文本,你就需要對命名實體檢測進行一些模型微調。
讓我們考慮一下檢測實體,並將其進行扭曲以構建一個數據清理器。通過查閱海量文檔並試圖手工編輯所有的名稱,需要耗費數年的時間。但是對於NLP來說,這實在是小菜一碟。這裡有一個簡單的 scrubber,可以很輕鬆地刪除掉它所檢測到的所有名稱:
import spacy# Load the large English NLP modelnlp = spacy.load(en_core_web_lg)# Replace a token with "REDACTED" if it is a namedef replace_name_with_placeholder(token): if token.ent_iob != 0 and token.ent_type_ == "PERSON": return "[REDACTED] " else: return token.string# Loop through all the entities in a document and check if they are namesdef scrub(text): doc = nlp(text) for ent in doc.ents: ent.merge() tokens = map(replace_name_with_placeholder, doc) return "".join(tokens)s = """In 1950, Alan Turing published his famous article "Computing Machinery and Intelligence". In 1957, Noam Chomsky』s Syntactic Structures revolutionized Linguistics with universal grammar, a rule based system of syntactic structures."""print(scrub(s))
運行這段代碼會得到下面的結果
In 1950, [REDACTED] published his famous article "Computing Machinery and Intelligence". In 1957, [REDACTED] Syntactic Structures revolutionized Linguistics with universal grammar, a rule based system of syntactic structures.
提取事實
除了用 spaCy 外,還可以用一個叫 textacy 的 python 庫,它在spaCy 的基礎上,實現了幾種常見的數據提取演算法。
textacy 實現的演算法中,有一種叫半結構化語句提取(Semi-structured Statement Extraction)演算法。我們可以使用這個演算法進行搜索解析樹,查找主語是「London」且動詞為「be」的簡單語句。這有助於我們找到有關「London」的事實。
代碼如下所示:
import spacyimport textacy.extract# Load the large English NLP modelnlp = spacy.load(en_core_web_lg)# The text we want to examinetext = """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."""# Parse the document with spaCydoc = nlp(text)# Extract semi-structured statementsstatements = textacy.extract.semistructured_statements(doc, "London")# Print the resultsprint("Here are the things I know about London:")for statement in statements: subject, verb, fact = statement print(f" - {fact}")
運行結果如下:
Here are the things I know about London:- the capital and most populous city of England and the United Kingdom. - a major settlement for two millennia.
這麼簡單的演示,也許不會讓人感到印象特別深刻。但是如果你在維基百科的「London」詞條上運行同樣的代碼,而不是僅僅使用三條句子來演示,你就會得到更加令人印象深刻的結果:
Here are the things I know about London:
- the capital and most populous city of England and the United Kingdom - a major settlement for two millennia - the worlds most populous city from around 1831 to 1925 - beyond all comparison the largest town in England - still very compact - the worlds largest city from about 1831 to 1925 - the seat of the Government of the United Kingdom - vulnerable to flooding - "one of the Worlds Greenest Cities" with more than 40 percent green space or open water - the most populous city and metropolitan area of the European Union and the second most populous in Europe - the 19th largest city and the 18th largest metropolitan region in the world - Christian, and has a large number of churches, particularly in the City of London - also home to sizeable Muslim, Hindu, Sikh, and Jewish communities - also home to 42 Hindu temples - the worlds most expensive office market for the last three years according to world property journal (2015) report - one of the pre-eminent financial centres of the world as the most important location for international finance - the world top city destination as ranked by TripAdvisor users - a major international air transport hub with the busiest city airspace in the world - the centre of the National Rail network, with 70 percent of rail journeys starting or ending in London - a major global centre of higher education teaching and research and has the largest concentration of higher education institutes in Europe - home to designers Vivienne Westwood, Galliano, Stella McCartney, Manolo Blahnik, and Jimmy Choo, among others - the setting for many works of literature - a major centre for television production, with studios including BBC Television Centre, The Fountain Studios and The London Studios - also a centre for urban music - the "greenest city" in Europe with 35,000 acres of public parks, woodlands and gardens - not the capital of England, as England does not have its own government
現在事情變得有趣了!這就是我們自動收集的大量信息。
你還可以試著安裝neuralcoref庫並在工作流中添加指代消解。這樣一來你就能得到更多的事實,因為它會捕捉到那些提到「it」的句子,而不是直接提及「London」的句子。
我們還能做什麼?
查看spaCy的文檔和textacy的文檔,可以看到許多解析文本的方法示例。在本文中,我們只是用了一個小小的樣本示例。
這裡還有一個實例:假設你正在構建一個網站,讓用戶使用我們在上一個示例中提取的信息查看世界上每座城市的信息。
如果在你的網站上有搜索功能,那麼就可以像 Google 那樣自動填充常見的搜索查詢,這點子可能很不錯,如下圖所示:
Google對「London」的自動填充建議
但是要做到這一點,我們需要一個可能完成的列表來為用戶提供建議。可以使用NLP來快速生成這些數據。
要怎麼生成這些數據呢?這裡有一種方法,可以從文檔中提取頻繁提到的名詞塊:
import spacyimport textacy.extract# Load the large English NLP modelnlp = spacy.load(en_core_web_lg)# The text we want to examinetext = """London is [.. shortened for space ..]"""# Parse the document with spaCydoc = nlp(text)# Extract noun chunks that appearnoun_chunks = textacy.extract.noun_chunks(doc, min_freq=3)# Convert noun chunks to lowercase stringsnoun_chunks = map(str, noun_chunks)noun_chunks = map(str.lower, noun_chunks)# Print out any nouns that are at least 2 words longfor noun_chunk in set(noun_chunks): if len(noun_chunk.split(" ")) > 1: print(noun_chunk)
得到如下的輸出:
westminster abbey
natural history museum west end east end st pauls cathedral royal albert hall london underground great fire british museum london eye…. etc ….
本文例舉的內容只是你可以用NLP做的一小部分。在後續文章中,我們將會討論NLP的其他應用,如文本分類,以及像Amazon Alexa這樣的系統如何解析問題。
現在你就可以安裝spaCy,開始嘗試一下吧!如果你不是Python用戶,使用的是不同的NLP庫,文章中這些步驟,在你的處理過程中仍是有借鑒可取之處的。
原文鏈接:
https://medium.com/@ageitgey/natural-language-processing-is-fun-9a0bff37854e參考鏈接:[1] spaCy https://spacy.io/ [2] textacy http://textacy.readthedocs.io/en/latest/ [3] neuralcoref https://github.com/huggingface/neuralcoref [4] Wikipedia article "London" https://en.wikipedia.org/wiki/London [5] Parsing English in 500 Lines of Pythonhttps://explosion.ai/blog/parsing-english-in-python [6] displaCy Dependency Visualizer Demos https://explosion.ai/demos/displacy [7] displaCy Named Entity Visualizer Demos https://explosion.ai/demos/displacy-ent [8] State-of-the-art neural coreference resolution for chatbots https://medium.com/huggingface/state-of-the-art-neural-coreference-resolution-for-chatbots-3302365dcf30 [9] Neural Coreference - Hugging Face https://medium.com/@ageitgey/natural-language-processing-is-fun-9a0bff37854e [10] raining the named entity recognizer https://spacy.io/usage/training#section-ner [11] API Reference - textacy 0.4.1 documentation https://textacy.readthedocs.io/en/stable/api_reference.html#textacy.extract.semistructured_statements
——【完】——
關注AI科技大本營知乎專欄,持續獲得更多乾貨內容
推薦閱讀:
※聊聊轉行計算機(一)
※2018年全球大學排行榜新鮮出爐 清華大學計算機專業名列第一
※計算機專業保研思路小議
※掃盲第8波:計算機專業都學了啥?
※通過計算機網路的一些題目加深理解