入行AI,程序員為什麼要學習NLP?

“認知”的核心技術是自然語言處理,這是人工智能領域中的一個重要方向,研究各種理論和方法,涉及的領域較多,主要包括機器翻譯、閱讀理解、智能寫作、對話系統、基礎技術和語義計算等。

計算機能“理解並開口說話”,意味著與人類可進行更廣泛的交流,從而一步步逼近並超越“圖靈測試”,讓人工智能掀開新的篇章。

福利:【關注】異步社區頭條號,點擊抽獎,即有機會獲得《Python和NLTK自然語言處理

(此處已添加小程序,請到今日頭條客戶端查看)


入行AI,程序員為什麼要學習NLP?


語言是我們日常生活的核心部分,處理與語言相關的任何問題都是非常有趣的。我希望此書能夠讓你一嗅NLP的芬芳,激勵你去了解更令人驚奇的NLP概念,並鼓勵你開發一些具有挑戰性的NLP應用。

研究人類語言的過程稱為NLP。深入研究語言的人稱為語言學家,而“計算語言學家”這個專有名詞適用於應用計算研究語言處理的人。從本質上講,計算語言學家是深入瞭解語言的計算機科學家,計算語言學家可以運用計算技能,對語言的不同方面進行建模。計算語言學家解決的是語言理論方面的問題,NLP只不過是計算語言學的應用。

NLP更多探討的是應用計算機,處理不同語言的細微差別,以及使用NLP技術構建現實世界的應用。在實際情景下,NLP類似於教孩子學語言。一些最常見的任務(如理解單詞和句子,形成在語法和結構上正確的句子)對人類而言是很自然。在NLP領域,把這樣的一些任務轉化為標記解析(tokenization)、組塊(chunking)、詞性標註(part of speech tagging)、解析(parsing)、機器翻譯(machine translation)、語音識別(speech recognition),這些任務中的大部分依然是計算機所面臨的最嚴峻的挑戰。

本文主要內容如下。

  • NLP及其相關概念。
  • 安裝Python、NLTK和其他庫的方法。
  • 編寫一些非常基本的Python和NLTK代碼片段的方法。

如果你從來沒有聽說過NLP這個詞,那麼請花一些時間來閱讀這裡提到的任何一本書籍,只要閱讀最初幾章即可。至少要快速閱讀一些與NLP相關的維基百科網頁。

  • 由Daniel Jurafsky和James H. Martin合著的《Speech and Language Processing》。
  • 由Christopher D. Manning和Hinrich Schütze合著的《Statistical Natural Language Processing》。

為什麼要學習NLP

本節從Gartner的技術成熟度曲線開始討論,從這條曲線上,你可以清楚地看到NLP處在技術成熟度曲線的頂部。目前,NLP是行業所需的稀有技能之一。在大數據到來之後,NLP面臨的主要的挑戰是,NLP需要大量不但精通結構化數據而且擅長於處理半結構化或非結構化數據的技術人員。我們正在生成拍字節量級的網絡博客、推特信息、臉書(Facebook)的推送信息、聊天記錄、電子郵件和評論。一些公司正在收集所有這些不同種類的數據,以便更好地為客戶定位,並從中得到有意義的見解。為了處理這些非結構化數據源,我們需要了解NLP的技術人員。

我們身處信息時代;我們甚至不能想象生活中沒有谷歌。我們使用Siri來處理大多數基本的語音功能。我們使用垃圾郵件過濾器過濾垃圾郵件。在Word文檔中,我們需要拼寫檢查器。在我們周圍,存在許多NLP在現實世界中應用的例子。

入行AI,程序員為什麼要學習NLP?

(圖片來自gartner網站)

下面也提供一些你能夠使用但是沒有意識它們是建立在NLP上的令人讚歎的NLP應用的示例。

  • 拼寫校正(微軟的Word/任何其他編輯器)
  • 搜索引擎(谷歌、必應、雅虎和WolframAlpha)
  • 語音引擎(Siri和谷歌語音)
  • 垃圾郵件分類(所有的電子郵件服務)
  • 新聞推送(谷歌和雅虎等)
  • 機器翻譯(谷歌翻譯等)
  • IBM的沃森

構建這些應用需要一種非常特殊的技能集,你需要對語言非常瞭解,並具有可以有效處理語言的工具。因此,讓NLP成為最具優勢的領域之一的原因不是廣告宣傳,而是可以使用NLP創建的這種應用使得NLP成為必備的最獨特技能之一。

為了實現上述的一些應用,以及其他基本的NLP預處理,我們有很多可用的開源工具。在這些工具中,有一些是某些組織為建立自己的NLP應用而開發的,而有一些是開源的。這裡是一張可用的NLP工具列表。

  • GATE
  • Mallet
  • Open NLP
  • UIMA
  • 斯坦福工具包
  • Genism
  • 自然語言工具包(NLTK)

大部分工具是用Java編寫的,具有相似的功能。其中一些工具非常健壯,可以獲得NLP工具的不同版本。但是,當涉及易於使用易於解釋這兩個概念的時候,NLTK得分最高。由於Python(NLTK的編碼語言)的學習曲線非常快,因此NLTK也是非常易於學習的工具包。NLTK已經將大部分的NLP任務納入籃中,非常優雅,容易用於工作中。出於所有這些原因,NLTK已成為NLP界最流行的庫之一。

建議使用Anaconda和Canopy Python的發行版本。理由是,這些版本綁定了一些庫,如scipy、numpy、scikit等,你可以使用這些庫進行數據分析,開發出與NLP有關的應用,以及把這些庫應用於相關領域。即使NLTK也是這個發行版本的一部分。

下面測試所有功能。

請在相應的操作系統中,啟動終端。然後運行:

$ python

這應該打開了Python解釋器。

1Python 2.6.6 (r266:84292, Oct 15 2013, 07:32:41)
2[GCC 4.4.7 20120313 (Red Hat 4.4.7-4)] on linux2
3Type "help", "copyright", "credits" or "license" for more information.
4>>>

我希望你得到的輸出與這個類似。你有可能得到一個不同的輸出,但是在理想情況下,你獲得了Python的最新版本(建議的版本是2.7)信息、編譯器GCC的信息,以及操作系統的詳細信息。Python的最新版本是3.0+,但是,與任何其他開源的系統一樣,我們應該試圖保持相對穩定的版本,而不是跳躍到最新版本。如果你已經使用了Python 3.0+,那麼請從python網站了解新版本中又添加了哪些新特徵。

基於UNIX的系統將Python作為默認程序。Windows用戶可以設置路徑,讓Python正常運行。下面檢查一下是否已經正確安裝了NLTK。

1>>>import nltk
2>>>print "Python and NLTK installed successfully"
3Python and NLTK installed successfully

我們準備好了。

從Python的基本知識開始

本節不會深入探討Python。然而,我們會讓你快速瀏覽一遍Python的基本知識。同樣,為了讀者的利益,我認為應該來一段5分鐘的Python之旅。接下來的幾節將談論數據結構的基本知識、一些常用的函數,以及Python的一般構建方式。

列表

在Python中,列表是最常用的數據結構之一。它們幾乎相當於其他編程語言中的數組。下面從Python列表所提供的一些最重要的函數開始講述。

在Python控制檯中,嘗試輸入以下內容。

11>>> lst=[1,2,3,4]
22>>> # mostly like arrays in typical languages
33>>>print lst
44[1, 2, 3, 4]

可以使用更加靈活的索引來訪問Python列表。下面是一些例子。

>>>print 'First element' +lst[0]

你會得到這樣的錯誤消息:

TypeError: cannot concatenate 'str' and 'int' objects

原因是Python是一種解釋性語言,我們在聲明變量時,不需要初始化變量並聲明變量的類型,Python只有在計算表達式時,才檢查變量類型。在列表中,對象是整數類型的,因此它們不能與print函數串接。這個函數只接受字符串對象。出於這個原因,我們需要將列表元素轉換為字符串。這個過程也稱為強制類型轉換(typecasting)。

1>>>print 'First element :' +str(lst[0])
2>>>print 'last element :' +str(lst[-1])
3>>>print 'first three elements :' +str(lst[0:2])
4>>>print 'last three elements :'+str(lst[-3:])
5First element :1
6last element :4
7first three elements :[1, 2,3]
8last three elements :[2, 3, 4]

自助

瞭解更多不同的數據類型和函數的最佳方法是使用幫助函數,如help() 和 dir(lst)。

可以使用dir(Python對象)命令,列出給定Python對象的所有給定的屬性。如果傳入一個列表對象,那麼這個函數會列出所有可以使用列表執行的酷炫的操作。

 1>>>dir(lst)
2>>>' , '.join(dir(lst))
3'__add__ , __class__ , __contains__ , __delattr__ , __delitem__ , __
4delslice__ , __doc__ , __eq__ , __format__ , __ge__ , __getattribute__
5, __getitem__ , __getslice__ , __gt__ , __hash__ , __iadd__ , __imul__
6, __init__ , __iter__ , __le__ , __len__ , __lt__ , __mul__ , __ne__ ,
7__new__ , __reduce__ , __reduce_ex__ , __repr__ , __reversed__ , __rmul__
8, __setattr__ , __setitem__ , __setslice__ , __sizeof__ , __str__ , __
9subclasshook__ , append , count , extend , index , insert , pop , remove
10, reverse , sort'

使用help(Python對象)命令,我們可以得到給定Python對象的詳細文檔,並且這個命令也給出一些示例,告訴我們如何使用Python對象。

1>>>help(lst.index)
2Help on built-in function index:
3index(...)

4 L.index(value, [start, [stop]]) -> integer -- return first index of value.
5This function raises a ValueError if the value is not present.

因此,在Python的任何數據類型上,都可以使用help和dir,並且這是一種非常不錯的方式,可用於瞭解關於函數和對象的其他詳細信息。這也提供了一些基本示例,供你在工作中參考,在大部分情況下,這些示例非常有用。

在Python和其他語言中,字符串都非常相似,但是對字符串的操作是Python的主要特徵之一。在Python中,使用字符串非常容易。在Java/C中,即使是一些很簡單的操作(例如將字符串分割),我們也需要花很大的精力才能做到。然而,在Python中,你會發現這是多麼容易。

在應用任何Python對象和函數時,你都可以從先前的help函數中獲得幫助。下面使用最常用的數據類型字符串給你提供更多的例子。

  • Split:這是基於一些分隔符分割字符串的方法。如果不提供任何參數,這個方法默認以空格作為分隔符。
1 >>> mystring="Monty Python ! And the holy Grail ! \n"
2 >>> print mystring.split()
3 ['Monty', 'Python', '!', 'and', 'the', 'holy', 'Grail', '!']
  • Strip:這個方法可以刪除字符串的尾隨空格,例如'\n'和'\n\R'。
1 >>> print mystring.strip()
2 >>>Monty Python ! and the holy Grail !

你是否發現'\n'字符被移除了?還有其他方法(如lstrip()和rstrip())可以移除字符串左側和右側的尾隨空格。

  • Upper/Lower:使用這些方法,可以改變字符串中字母的大小寫。
1 >>> print (mystring.upper()
2 >>>MONTY PYTHON !AND THE HOLY GRAIL !
  • Repalce:這個方法可以替換字符串中的子字符串。
1 >>> print mystring.replace('!','''''')
2 >>> Monty Python and the holy Grail

剛才談到的是一些最常用的字符串函數,函數庫中還存在大量的字符串函數。

提示:

要了解更多函數和示例,請瀏覽Python網站。

正則表達式

NLP發燒友的另外一個重要技能是使用正則表達式工作。正則表達式描述了字符串的有效模式匹配。我們大量使用模式提取從眾多雜亂無章的文本數據中獲得有意義的信息。以下是讀者所需要的正則表達式。在我一生中,我所用的正則表達式都不會超過這個範圍。

  • (句點):這個表達式匹配除了換行符\ n外的任意單個字符。
  • \ w:這個表達式匹配[a~z A~Z 0~9]中的某個字符或數字。
  • \ W:匹配任何非單詞字符。
  • \ s:這個表達式匹配單個空白字符——空格、換行符(\n)、回車符(\r)、製表符(\t)、換頁符(\f)。
  • \ S:
    這個表達式匹配任何非空白字符。
  • \ t:這個表達式執行tab操作。
  • \ n:這個表達式用於換行符。
  • \ r:這個表達式用於回車符。
  • \ d:十進制數字[0~9]。
  • ^:這個表達式在字符串開始處使用。
  • $:這個表達式在字符串末尾處使用。
  • \:這個表達式用於抵消特殊字符的特殊性。

例如,要匹配$符號,可以在它前面加上\。

在現行的例子(即mystring是相同的字符串對象)中,搜索一些內容,並且試圖在此字符串對象上尋找一些模式。子字符串搜索是re模塊的其中一個通用用例。下面實現這一功能。

1>>># We have to import re module to use regular expression
2>>>import re
3>>>if re.search('Python',mystring):
4>>> print "We found python "
5>>>else:
6>>> print "NO "

一旦執行代碼,得到的消息如下。

We found python

可以使用正則表達式進行更多的模式查找。為了找到字符串中的所有模式,我們使用的其中一個普通的函數是findall。這個函數搜索字符串中特定的模式,並且會給出一個包含所有匹配對象的列表。

1>>>import re
2>>>print re.findall('!',mystring)
3['!', '!']

正如我們所見,在mystring中,有兩個“!”實例,findall使用一個列表,返回了這兩個對象。

詞典

詞典是另一種最常用的數據結構,在其他編程語言中,這也稱為關聯數組/關聯記憶(associative array/memory)。詞典是使用鍵(key)進行索引的數據結構,這些鍵可以是任何不可變的類型,如字符串和數字可以用作鍵。

詞典是非常方便的數據結構,廣泛應用於各種編程語言中來實現多種算法。在眾多的編程語言中,Python詞典是其中一個優雅地實現了散列表的詞典。在其他語言中,相同的任務,可能需要花費更多的時間進行更繁重的編碼工作,但是使用詞典,工作就變得非常容易。最棒的事情是,程序員僅僅使用少量的代碼塊,就可以建立非常複雜的數據結構。這使得程序員擺脫了數據結構本身,花更多時間專注於算法。

我使用詞典中一個很常見的用例,在給定的文本中,獲得單詞的頻率分佈。使用以下幾行代碼,就可以得到單詞的頻率。你可試著使用任意其他的語言執行相同的任務,馬上就會明白Python是多麼讓人讚歎不已。

 1>>># declare a dictionary
2>>>word_freq={}
3>>>for tok in string.split():
4>>> if tok in word_freq:
5>>> word_freq [tok]+=1
6>>> else:
7>>> word_freq [tok]=1
8>>>print word_freq
9{'!': 2, 'and': 1, 'holy': 1, 'Python': 1, 'Grail': 1, 'the': 1, 'Monty':
101}

編寫函數

正如其他編程語言,Python也有其編寫函數的方式。在Python中,函數以關鍵字def開始,然後是函數名和圓括號()。這與其他編程語言相似,即任何參數和參數的類型都放在圓括號內。實際的代碼以冒號(:)開頭。代碼的初始行通常是文檔字符串(註釋),然後才是代碼體,函數使用return語句結束。例如,在給定的例子中,函數wordfreq以def關鍵字開始,這個函數沒有參數,並且以return語句結束。

 1>>>import sys
2>>>def wordfreq (mystring):
3>>> '''
4>>> Function to generated the frequency distribution of the given text
5>>> '''
6>>> print mystring
7>>> word_freq={}
8>>> for tok in mystring.split():
9>>> if tok in word_freq:
10>>> word_freq [tok]+=1
11>>> else:
12>>> word_freq [tok]=1
13>>> print word_freq
14>>>def main():
15>>> str="This is my fist python program"
16>>> wordfreq(str)
17>>>if __name__ == '__main__':
18>>> main()

這與上一節中所寫的代碼是相同的,使用函數的形式進行編寫的思想使得代碼可重用和可讀。雖然在編寫Python代碼時解釋器方式也很常見,但是對於大型程序,使用函數/類是一種非常好的做法,這也是一種編程範式。我們也希望用戶能夠編寫和運行第一個Python程序。你需要按照下列步驟來實現這一目標。

(1)在首選的文本編輯器中,打開一個空的Python文件mywordfreq.py。

(2)編寫或複製以上代碼段中的代碼到文件中。

(3)在操作系統中,打開命令提示符窗口。

(4)運行以下命令。

$ python mywordfreq,py "This is my fist python program !!"

(5)輸出應該為:

{'This': 1, 'is': 1, 'python': 1, 'fist': 1, 'program': 1, 'my': 1}

現在,對Python提供的一些常見的數據結構,你有了一個非常基本的瞭解。你可以寫一個完整並且能夠運行的Python程序。我認為這些已經足夠了,使用這些Python的入門知識,你可以看懂本書前幾章。

提示:請觀看維基百科網站中的一些Python教程,學習更多的Python命令。

NLTK

無須進一步研究自然語言處理的理論,下面開始介紹NLTK。我們從一些NLTK的基本示例用例開始。你們中的一些人,可能已經做過了類似的事情。首先,本節會給出一些典型Python程序員的做法,然後會轉到NLTK,尋找一個更加高效、更加強大和更加清晰的解決方案。

下面從某個示例文本內容的分析開始。對於當前的例子,從Python的主頁上獲得了一些內容如下所示。

1>>>import urllib2
2>>># urllib2 is use to download the html content of the web link
3>>>response = urllib2.urlopen('http://python.org/')

4>>># You can read the entire content of a file using read() method
5>>>html = response.read()
6>>>print len(html)
747020

由於我們對在這個URL中所討論的主題類型沒有任何線索,因此從探索性數據分析(EDA)開始。一般來說,在文本領域,EDA具有多種含義,但是這裡討論一種簡單的情況,即在文檔中,何種術語佔據了主導地位。主題是什麼?它們出現的頻率有多大?這一過程將涉及某種層次的預處理步驟。我們首先使用純Python方式,嘗試執行這個任務,然後會使用NLTK執行這個任務。

讓我們從清理HTML標籤開始。完成這個任務的一種方式是僅僅選擇包括了數字和字符的標記(token)。任何能夠使用正則表達式工作的人員應該能夠將HTML字符串轉換成標記列表。

1>>># Regular expression based split the string
2>>>tokens = [tok for tok in html.split()]
3>>>print "Total no of tokens :"+ str(len(tokens))
4>>># First 100 tokens
5>>>print tokens[0:100]
6Total no of tokens :2860
7['', '', '', ''type="text/css"', 'media="not', 'print,', 'braille,'...]
8

正如你所看到的,使用前面的方法,存在過量的HTML標籤和其他無關緊要的字符。執行同一任務的相對清潔的版本,如下所示。

 1>>>import re
2>>># using the split function
3>>>#https://docs.python.org/2/library/re.html
4>>>tokens = re.split('\W+',html)
5>>>print len(tokens)
6>>>print tokens[0:100]
75787
8['', 'doctype', 'html', 'if', 'lt', 'IE', '7', 'html', 'class', 'no',
9'js', 'ie6', 'lt', 'ie7', 'lt', 'ie8', 'lt', 'ie9', 'endif', 'if',
10'IE', '7', 'html', 'class', 'no', 'js', 'ie7', 'lt', 'ie8', 'lt', 'ie9',
11'endif', 'if', 'IE', '8', 'msapplication', 'tooltip', 'content', 'The',
12'official', 'home', 'of', 'the', 'Python', 'Programming', 'Language',
13'meta', 'name', 'apple' ...]

現在,這看起來清爽多了。但是,你可以做更多的事情,使代碼變得更加簡潔。這裡將這項工作留給你,讓你嘗試移除儘可能多的噪聲。可以清除一些仍然彈出的HTML標籤。在這個例子中,字長為1的單詞(如7和8這樣的元素)僅僅是噪聲,你可能希望以字長作為標準,移除這些單詞。現在,與其從頭開始編寫一些預處理步驟的代碼,不如將目光轉移到NLTK,使用NTLK執行相同的任務。有一個函數clean_html(),這個函數可以執行所需要的所有清潔工作。

1>>>import nltk
2>>># http://www.nltk.org/api/nltk.html#nltk.util.clean_html
3>>>clean = nltk.clean_html(html)
4>>># clean will have entire string removing all the html noise
5>>>tokens = [tok for tok in clean.split()]
6>>>print tokens[:100]
7['Welcome', 'to', 'Python.org', 'Skip', 'to', 'content', '▼',
8'Close', 'Python', 'PSF', 'Docs', 'PyPI', 'Jobs', 'Community', '▲',
9'The', 'Python', 'Network', '≡', 'Menu', 'Arts', 'Business' ...]

這很酷炫,對吧?這種方法絕對更加清潔,也更容易執行。

下面嘗試獲得這些術語的頻率分佈。首先,我們使用純Python的方式執行這個任務,然後,我將告訴你NLTK的秘訣。

 1>>>import operator
2>>>freq_dis={}
3>>>for tok in tokens:
4>>> if tok in freq_dis:
5>>> freq_dis[tok]+=1
6>>> else:
7>>> freq_dis[tok]=1
8>>># We want to sort this dictionary on values ( freq inthis case )
9>>>sorted_freq_dist= sorted(freq_dis.items(), key=operator.itemgetter(1),
10reverse=True)
11>>> print sorted_freq_dist[:25]
12[('Python', 55), ('>>>', 23), ('and', 21), ('to', 18), (',', 18), ('the',
1314), ('of', 13), ('for', 12), ('a', 11), ('Events', 11), ('News', 11),
14('is', 10), ('2014-', 10), ('More', 9), ('#', 9), ('3', 9), ('=', 8),
15('in', 8), ('with', 8), ('Community', 7), ('The', 7), ('Docs', 6),
16('Software', 6), (':', 6), ('3:', 5), ('that', 5), ('sum', 5)]

自然而然地,由於這是Python主頁,因此Python和(>>>)解釋器符號是最常見的術語,這也展示了網站的第一感覺。

一個更好並且更有效的方法是使用NLTK的FreqDist()函數。為了進行對比,我們可以觀察之前開發的執行相同任務的代碼。

 1>>>import nltk
2>>>Freq_dist_nltk=nltk.FreqDist(tokens)
3>>>print Freq_dist_nltk
4>>>for k,v in Freq_dist_nltk.items():
5>>> print str(k)+':'+str(v)
6>>': 23, 'and': 21, ',': 18, 'to': 18, 'the':
714, 'of': 13, 'for': 12, 'Events': 11, 'News': 11, ...>
8Python:55
9>>>:23
10and:21
11,:18
12to:18

13the:14
14of:13
15for:12
16Events:11
17News:11
18

現在,讓我們做一些更有趣的事情,畫出這些頻率分佈。

1>>>Freq_dist_nltk.plot(50, cumulative=False)
2>>># below is the plot for the frequency distributions
入行AI,程序員為什麼要學習NLP?

可以看到,累積頻率持續增長,整體上,曲線有一條長長的尾巴。一些噪聲依然存在,一些單詞(如the、of、for和=)是毫無用處的。對於這些單詞(如the、a、an等),使用術語停用詞(stop word)來稱呼它們。由於在大部分文檔中不定代詞一般都會出現,因此這些詞沒有什麼判別力,不能傳達太多的信息。在大多數的NLP和信息檢索任務中,人們通常會刪除停用詞。讓我們再次回到當前的示例中。

1>>>stopwords=[word.strip().lower() for word in open("PATH/english.stop.
2txt")]
3>>>clean_tokens=[tok for tok in tokens if len(tok.lower())>1 and (tok.
4lower() not in stopwords)]
5>>>Freq_dist_nltk=nltk.FreqDist(clean_tokens)
6>>>Freq_dist_nltk.plot(50, cumulative=False)
入行AI,程序員為什麼要學習NLP?

現在,這看起來乾淨多了!在完成了這麼多任務後,可以訪問Wordle,將頻率分佈轉換成CSV格式。你應該能夠得到以下詞雲。


入行AI,程序員為什麼要學習NLP?

《Python和NLTK自然語言處理》

書號:978-7-115-50334-3

推薦理由:NLTK是自然語言處理領域中非常受歡迎和廣泛使用的Python庫。NLTK的優點在於其簡單性,其中大多數複雜的自然語言處理任務使用幾行代碼即可完成。本書旨在講述如何用Python和NLTK解決各種自然語言處理任務並開發機器學習方面的應用。本書介紹了NLTK的基本模塊,講述了採用NLTK實現自然語言處理的大量技巧,討論了一些文本處理方法和語言處理技術,展示了使用Python實現NLP項目的大量實踐經驗。本書主要內容包括文本挖掘/NLP任務中所需的所有預處理步驟,如何使用Python 3的NLTK 3進行文本處理,如何通過Python開展NLP項目。

本書適合NLP和機器學習領域的愛好者、Python程序員以及機器學習領域的研究人員閱讀。


- END -


分享到:


相關文章: