本文簡要介紹Python天然語言處理(NLP),使用Python的NLTK庫。NLTK是Python的天然語言處理工具包,在NLP領域中,最常使用的一個Python庫。html5

什麼是NLP?

簡單來講,天然語言處理(NLP)就是開發可以理解人類語言的應用程序或服務。python

這裏討論一些天然語言處理(NLP)的實際應用例子,如語音識別、語音翻譯、理解完整的句子、理解匹配詞的同義詞,以及生成語法正確完整句子和段落。git

這並非NLP能作的全部事情。github

NLP實現

搜索引擎: 好比谷歌,Yahoo等。谷歌搜索引擎知道你是一個技術人員,因此它顯示與技術相關的結果;web

社交網站推送:好比Facebook News Feed。若是News Feed算法知道你的興趣是天然語言處理,就會顯示相關的廣告和帖子。正則表達式

語音引擎:好比Apple的Siri。算法

垃圾郵件過濾:如谷歌垃圾郵件過濾器。和普通垃圾郵件過濾不一樣,它經過了解郵件內容裏面的的深層意義,來判斷是否是垃圾郵件。shell

NLP庫

下面是一些開源的天然語言處理庫(NLP):

  • Natural language toolkit (NLTK);
  • Apache OpenNLP;
  • Stanford NLP suite;
  • Gate NLP library

其中天然語言工具包(NLTK)是最受歡迎的天然語言處理庫(NLP),它是用Python編寫的,並且背後有很是強大的社區支持。

NLTK也很容易上手,實際上,它是最簡單的天然語言處理(NLP)庫。

在這個NLP教程中,咱們將使用Python NLTK庫。

安裝 NLTK

若是您使用的是Windows/Linux/Mac,您可使用pip安裝NLTK:

pip install nltk

打開python終端導入NLTK檢查NLTK是否正確安裝:

import nltk 

若是一切順利,這意味着您已經成功地安裝了NLTK庫。首次安裝了NLTK,須要經過運行如下代碼來安裝NLTK擴展包:

import nltk nltk.download() 

這將彈出NLTK 下載窗口來選擇須要安裝哪些包: 

您能夠安裝全部的包,由於它們的大小都很小,因此沒有什麼問題。

使用Python Tokenize文本

首先,咱們將抓取一個web頁面內容,而後分析文本瞭解頁面的內容。

咱們將使用urllib模塊來抓取web頁面:

import urllib.request response = urllib.request.urlopen('http://php.net/') html = response.read() print (html) 

從打印結果中能夠看到,結果包含許多須要清理的HTML標籤。 而後BeautifulSoup模塊來清洗這樣的文字:

from bs4 import BeautifulSoup import urllib.request response = urllib.request.urlopen('http://php.net/') html = response.read() soup = BeautifulSoup(html,"html5lib") # 這須要安裝html5lib模塊 text = soup.get_text(strip=True) print (text) 

如今咱們從抓取的網頁中獲得了一個乾淨的文本。 下一步,將文本轉換爲tokens,像這樣:

from bs4 import BeautifulSoup import urllib.request response = urllib.request.urlopen('http://php.net/') html = response.read() soup = BeautifulSoup(html,"html5lib") text = soup.get_text(strip=True) tokens = text.split() print (tokens) 

統計詞頻

text已經處理完畢了,如今使用Python NLTK統計token的頻率分佈。

能夠經過調用NLTK中的FreqDist()方法實現:

from bs4 import BeautifulSoup import urllib.request import nltk response = urllib.request.urlopen('http://php.net/') html = response.read() soup = BeautifulSoup(html,"html5lib") text = soup.get_text(strip=True) tokens = text.split() freq = nltk.FreqDist(tokens) for key,val in freq.items(): print (str(key) + ':' + str(val)) 

若是搜索輸出結果,能夠發現最多見的token是PHP。 您能夠調用plot函數作出頻率分佈圖:

freq.plot(20, cumulative=False) # 須要安裝matplotlib庫 

這上面這些單詞。好比of,a,an等等,這些詞都屬於停用詞。

通常來講,停用詞應該刪除,防止它們影響分析結果。

處理停用詞

NLTK自帶了許多種語言的停用詞列表,若是你獲取英文停用詞:

from nltk.corpus import stopwords stopwords.words('english') 

如今,修改下代碼,在繪圖以前清除一些無效的token:

clean_tokens = list() sr = stopwords.words('english') for token in tokens: if token not in sr: clean_tokens.append(token) 

最終的代碼應該是這樣的:

from bs4 import BeautifulSoup import urllib.request import nltk from nltk.corpus import stopwords response = urllib.request.urlopen('http://php.net/') html = response.read() soup = BeautifulSoup(html,"html5lib") text = soup.get_text(strip=True) tokens = text.split() clean_tokens = list() sr = stopwords.words('english') for token in tokens: if not token in sr: clean_tokens.append(token) freq = nltk.FreqDist(clean_tokens) for key,val in freq.items(): print (str(key) + ':' + str(val)) 

如今再作一次詞頻統計圖,效果會比以前好些,由於剔除了停用詞:

freq.plot(20,cumulative=False) 

使用NLTK Tokenize文本

在以前咱們用split方法將文本分割成tokens,如今咱們使用NLTK來Tokenize文本。

文本沒有Tokenize以前是沒法處理的,因此對文本進行Tokenize很是重要的。token化過程意味着將大的部件分割爲小部件。

你能夠將段落tokenize成句子,將句子tokenize成單個詞,NLTK分別提供了句子tokenizer和單詞tokenizer。

假若有這樣這段文本:

Hello Adam, how are you? I hope everything is going well. Today is a good day, see you dude.

使用句子tokenizer將文本tokenize成句子:

from nltk.tokenize import sent_tokenize mytext = "Hello Adam, how are you? I hope everything is going well. Today is a good day, see you dude." print(sent_tokenize(mytext)) 

輸出以下:

['Hello Adam, how are you?', 'I hope everything is going well.', 'Today is a good day, see you dude.'] 

這是你可能會想,這也太簡單了,不須要使用NLTK的tokenizer均可以,直接使用正則表達式來拆分句子就行,由於每一個句子都有標點和空格。

那麼再來看下面的文本:

Hello Mr. Adam, how are you? I hope everything is going well. Today is a good day, see you dude. 

這樣若是使用標點符號拆分,Hello Mr將會被認爲是一個句子,若是使用NLTK:

from nltk.tokenize import sent_tokenize mytext = "Hello Mr. Adam, how are you? I hope everything is going well. Today is a good day, see you dude." print(sent_tokenize(mytext)) 

輸出以下:

['Hello Mr. Adam, how are you?', 'I hope everything is going well.', 'Today is a good day, see you dude.'] 

這纔是正確的拆分。

接下來試試單詞tokenizer:

from nltk.tokenize import word_tokenize mytext = "Hello Mr. Adam, how are you? I hope everything is going well. Today is a good day, see you dude." print(word_tokenize(mytext)) 

輸出以下:

['Hello', 'Mr.', 'Adam', ',', 'how', 'are', 'you', '?', 'I', 'hope', 'everything', 'is', 'going', 'well', '.', 'Today', 'is', 'a', 'good', 'day', ',', 'see', 'you', 'dude', '.'] 

Mr.這個詞也沒有被分開。NLTK使用的是punkt模塊的PunktSentenceTokenizer,它是NLTK.tokenize的一部分。並且這個tokenizer通過訓練,能夠適用於多種語言。

非英文Tokenize

Tokenize時能夠指定語言:

from nltk.tokenize import sent_tokenize mytext = "Bonjour M. Adam, comment allez-vous? J'espère que tout va bien. Aujourd'hui est un bon jour." print(sent_tokenize(mytext,"french")) 

輸出結果以下:

['Bonjour M. Adam, comment allez-vous?', "J'espère que tout va bien.", "Aujourd'hui est un bon jour."] 

同義詞處理

使用nltk.download()安裝界面,其中一個包是WordNet。

WordNet是一個爲天然語言處理而創建的數據庫。它包括一些同義詞組和一些簡短的定義。

您能夠這樣獲取某個給定單詞的定義和示例:

from nltk.corpus import wordnet syn = wordnet.synsets("pain") print(syn[0].definition()) print(syn[0].examples()) 

輸出結果是:

a symptom of some physical hurt or disorder ['the patient developed severe pain and distension'] 

WordNet包含了不少定義:

from nltk.corpus import wordnet syn = wordnet.synsets("NLP") print(syn[0].definition()) syn = wordnet.synsets("Python") print(syn[0].definition()) 

結果以下:

the branch of information science that deals with natural language information large Old World boas 

能夠像這樣使用WordNet來獲取同義詞:

from nltk.corpus import wordnet synonyms = [] for syn in wordnet.synsets('Computer'): for lemma in syn.lemmas(): synonyms.append(lemma.name()) print(synonyms) 

輸出:

['computer', 'computing_machine', 'computing_device', 'data_processor', 'electronic_computer', 'information_processing_system', 'calculator', 'reckoner', 'figurer', 'estimator', 'computer'] 

反義詞處理

也能夠用一樣的方法獲得反義詞:

from nltk.corpus import wordnet antonyms = [] for syn in wordnet.synsets("small"): for l in syn.lemmas(): if l.antonyms(): antonyms.append(l.antonyms()[0].name()) print(antonyms) 

輸出:

['large', 'big', 'big'] 

詞幹提取

語言形態學和信息檢索裏,詞幹提取是去除詞綴獲得詞根的過程,例如working的詞幹爲work。

搜索引擎在索引頁面時就會使用這種技術,因此不少人爲相同的單詞寫出不一樣的版本。

有不少種算法能夠避免這種狀況,最多見的是波特詞幹算法。NLTK有一個名爲PorterStemmer的類,就是這個算法的實現:

from nltk.stem import PorterStemmer stemmer = PorterStemmer() print(stemmer.stem('working')) print(stemmer.stem('worked')) 

輸出結果是:

work
work

還有其餘的一些詞幹提取算法,好比 Lancaster詞幹算法。

非英文詞幹提取

除了英文以外,SnowballStemmer還支持13種語言。

支持的語言:

from nltk.stem import SnowballStemmer print(SnowballStemmer.languages) 
'danish', 'dutch', 'english', 'finnish', 'french', 'german', 'hungarian', 'italian', 'norwegian', 'porter', 'portuguese', 'romanian', 'russian', 'spanish', 'swedish' 

你可使用SnowballStemmer類的stem函數來提取像這樣的非英文單詞:

from nltk.stem import SnowballStemmer french_stemmer = SnowballStemmer('french') print(french_stemmer.stem("French word")) 

單詞變體還原

單詞變體還原相似於詞幹,但不一樣的是,變體還原的結果是一個真實的單詞。不一樣於詞幹,當你試圖提取某些詞時,它會產生相似的詞:

from nltk.stem import PorterStemmer stemmer = PorterStemmer() print(stemmer.stem('increases')) 

結果:

increas

如今,若是用NLTK的WordNet來對同一個單詞進行變體還原,纔是正確的結果:

from nltk.stem import WordNetLemmatizer lemmatizer = WordNetLemmatizer() print(lemmatizer.lemmatize('increases')) 

結果:

increase

結果可能會是一個同義詞或同一個意思的不一樣單詞。

有時候將一個單詞作變體還原時,老是獲得相同的詞。

這是由於語言的默認部分是名詞。要獲得動詞,能夠這樣指定:

from nltk.stem import WordNetLemmatizer lemmatizer = WordNetLemmatizer() print(lemmatizer.lemmatize('playing', pos="v")) 

結果:

play

實際上,這也是一種很好的文本壓縮方式,最終獲得文本只有原先的50%到60%。

結果還能夠是動詞(v)、名詞(n)、形容詞(a)或副詞(r):

from nltk.stem import WordNetLemmatizer lemmatizer = WordNetLemmatizer() print(lemmatizer.lemmatize('playing', pos="v")) print(lemmatizer.lemmatize('playing', pos="n")) print(lemmatizer.lemmatize('playing', pos="a")) print(lemmatizer.lemmatize('playing', pos="r")) 

輸出:

play
playing
playing
playing

詞幹和變體的區別

經過下面例子來觀察:

from nltk.stem import WordNetLemmatizer from nltk.stem import PorterStemmer stemmer = PorterStemmer() lemmatizer = WordNetLemmatizer() print(stemmer.stem('stones')) print(stemmer.stem('speaking')) print(stemmer.stem('bedroom')) print(stemmer.stem('jokes')) print(stemmer.stem('lisa')) print(stemmer.stem('purple')) print('----------------------') print(lemmatizer.lemmatize('stones')) print(lemmatizer.lemmatize('speaking')) print(lemmatizer.lemmatize('bedroom')) print(lemmatizer.lemmatize('jokes')) print(lemmatizer.lemmatize('lisa')) print(lemmatizer.lemmatize('purple')) 

輸出:

stone
speak
bedroom
joke
lisa
purpl
--------------------- stone speaking bedroom joke lisa purple 

詞幹提取不會考慮語境,這也是爲何詞幹提取比變體還原快且準確度低的緣由。

我的認爲,變體還原比詞幹提取更好。單詞變體還原返回一個真實的單詞,即便它不是同一個單詞,也是同義詞,但至少它是一個真實存在的單詞。

若是你只關心速度,不在乎準確度,這時你能夠選用詞幹提取。

在此NLP教程中討論的全部步驟都只是文本預處理。在之後的文章中,將會使用Python NLTK來實現文本分析。

我已經儘可能使文章通俗易懂。但願能對你有所幫助。