974
阿裏雲
技術社區[雲棲]
探索 Python、機器學習和 NLTK 庫
簡介: 機器學習取決於 IT、數學和自然語言的交集,在大數據應用程序中會通常用到機器學習。本文將討論 Python 編程語言和它的 NLTK 庫,然後將它們應用於一個機器學習項目。
挑戰:使用機器學習對 RSS 提要進行分類
最近,我接到一項任務,要求為客戶創建一個 RSS 提要分類子係統。目標是讀取幾十個甚至幾百個 RSS 提要,將它們的許多文章自動分類到幾十個預定義的主題領域當中。客戶網站的內容、導航和搜索功能都將由這個每日自動提要檢索和分類結果驅動。
客戶建議使用機器學習,或許還會使用 Apache Mahout 和 Hadoop 來實現該任務,因為客戶最近閱讀了有關這些技術的文章。但是,客戶的開發團隊和我們的開發團隊都更熟悉 Ruby,而不是 Java™ 技術。本文將介紹解決方案的技術之旅、學習過程和最終實現。
什麼是機器學習?
我的第一個問題是,“究竟什麼是機器學習?” 我聽說過這個術語,並且隱約知道超級計算機 IBM® Watson 最近使用該技術在一場 Jeopardy 比賽中擊敗了人類競爭者。作為購物者和社交網絡活動參與者,我也知道 Amazon.com 和 Facebook 根據其購物者數據在提供建議(如產品和人)方麵表現良好。總之,機器學習取決於 IT、數學和自然語言的交集。它主要關注以下三個主題,但客戶的解決方案最終僅涉及前兩個主題:
-
分類。根據類似項目的一組訓練數據,將相關的項分配到任意預定義的類別
-
建議。根據類似項目的觀察來建議采用的項
-
集群。在一組數據內確定子組
Mahout 和 Ruby 的選擇
理解了機器學習是什麼之後,下一步是確定如何實現它。根據客戶的建議,Mahout 是一個合適的起點。我從 Apache 下載了代碼,並開始了學習使用 Mahout 及其兄弟 Hadoop 實現機器學習的過程。不幸的是,我發現即使對於有經驗的 Java 開發人員而言,Mahout 的學習曲線也很陡峭,並且不存在可用的樣例代碼。同樣不幸的是,機器學習缺乏基於 Ruby 的框架或 gem。
發現 Python 和 NLTK
我繼續搜索解決方案,並且在結果集中一直遇到 "Python"。作為一名 Ruby 開發人員,雖然我還沒有學過該語言,但我也知道 Python 是一個麵向相似對象的、基於文本的、可理解和動態的編程語言。盡管兩種語言之間存在一些相似之處,但我多年來都忽視了學習 Python,將它視為一項多餘的技能集。Python 是我的 “盲點”,我懷疑許多 Ruby 開發人員同行都是這樣認為的。
搜索機器學習的書籍,並更深入研究它們的目錄,我發現,有相當高比例的此類係統在使用 Python 作為其實現語言,並使用了一個被稱為 Natural Language Toolkit(NLTK,自然語言工具包)的庫。通過進一步的搜索,我發現 Python 的應用比我意識到的還要廣泛,如 Google App Engine、YouTube 和使用 Django 框架構建的網站。它甚至還預安裝在我每天都使用的 Mac OS X 工作站上!此外,Python 為數學、科學和工程提供了有趣的標準庫(例如,NumPy 和
SciPy)。
我決定推行一個 Python 解決方案,因為我找到了非常好的編碼示例。例如,下麵這一行代碼就是通過 HTTP 讀取 RSS 提要並打印其內容所需的所有代碼:
1
|
print feedparser.parse( "https://feeds.nytimes.com/nyt/rss/Technology" )
|
快速掌握 Python
在學習一門新的編程語言時,最容易的部分往往是學習語言本身。較難的部分是了解它的生態係統:如何安裝它、添加庫、編寫代碼、構造代碼文件、執行它、調試它並編寫單元測試。本節將簡要介紹這些主題;請務必參閱 參考資料,以獲得有關詳細信息的鏈接。
pip
Python Package Index (pip) 是 Python 的標準軟件包管理器。您可以使用該程序將庫添加到您的係統。它類似於 Ruby 庫的 gem。為了將 NLTK 庫添加到您的係統,您可以輸入以下命令:
為了顯示在您的係統上已安裝的 Python 庫的列表,請運行以下命令:
運行程序
執行 Python 程序同樣很簡單。獲得一個名稱為 locomotive_main.py 的程序和三個參數,然後您就可以使用 Python 程序編譯並執行它:
1
|
$
python locomotive_main.py arg1 arg2 arg3
|
Python 使用 清單 1 中的if __name__ == "__main__":語法來確定文件本身是從命令行執行的還是從其他代碼導入的。為了讓文件變得可以執行,需要添加"__main__"檢測。
清單 1. Main 檢測
5
|
if __name__ = = "__main__" :
|
6
|
start_time = time.time()
|
8
|
app = locomotive.app.Application()
|
9
|
...
additional logic ...
|
virtualenv
大多數 Ruby 開發人員熟悉係統範圍的庫或 gem 的問題。使用一組係統範圍內的庫的做法一般是不可取的,因為您的其中一個項目可能依賴於某個給定的庫的版本 1.0.0,而另一個項目則依賴於版本 1.2.7。同樣,Java 開發人員都知道係統範圍的 CLASSPATH 存在同樣的問題。就像 Ruby 社區使用其rvm工具,而 Python 社區使用virtualenv工具(請參閱 參考資料,以獲得相關鏈接)來創建獨立的執行環境,其中包含特定版本的
Python 和一組庫。清單 2 中的命令顯示了如何為您 p1 項目創建一個名為 p1_env 的虛擬環境,其中包含feedparser、numpy、scipy和nltk庫。
清單 2. 使用 virualenv 創建一個虛擬環境的命令
1
|
$
sudo pip install virtualenv $ cd ~ $ mkdir p1 $ cd p1 $ virtualenv p1_env - - distribute
$ source p1_env / bin / activate
(p1_env)[~ / p1]$
pip install feedparser (p1_env)[~ / p1]$
pip install numpy (p1_env)[~ / p1]$
pip install scipy (p1_env)[~ / p1]$
pip install nltk (p1_env)[~ / p1]$
pip freeze
|
每次在一個 shell 窗口使用您的項目時,都需要 “獲得” 您的虛擬環境激活腳本。請注意,在激活腳本被獲得後,shell 提示符會改變。當在您的係統上創建和使用 shell 窗口,輕鬆地導航到您的項目目錄,並啟動其虛擬環境時,您可能想在您的 ~/.bash_profile 文件中添加以下條目:
1
|
$
alias p1 = "cd
~/p1 ; source p1_env/bin/activate"
|
代碼庫結構
在完成簡單的單文件 “Hello World” 程序的編寫之後,Python 開發人員需要理解如何正確地組織其代碼庫的目錄和文件名。Java 和 Ruby 語言在這方麵都有各自的要求,Python 也沒有什麼不同。簡單來說,Python 使用包 的概念對相關的代碼進行分組,並提供了明確的名稱空間。出於演示目的,在本文中,代碼存在於某個給定項目的根目錄中,例如 ~/p1。在這個目錄中,存在一個用於相同名稱的 Python
包的 locomotive 目錄。 清單 3 顯示了這個目錄結構。
清單 3. 示例目錄結構
08
|
category_associations.py
|
17
|
category_associations_test.py
|
請注意名稱古怪的 __init__.py 文件。這些文件指示 Python 為您的包加載必要的庫和特定的應用程序代碼文件,它們都位於相同的目錄中。 清單
4 顯示了文件 locomotive/__init__.py 的內容。
清單 4. locomotive/__init__.py
01
|
#
system imports; loads installed packages
|
06
|
#
application imports; these load your specific *.py files
|
09
|
import category_associations
|
有了結構如 清單 4 所示的 locomotive 包之後,在項目的根目錄中的主程序就可以導入並使用它。例如,文件 locomotive_main.py
包含以下導入:
1
|
import sys #
>-- system library
|
2
|
import time #
>-- system library
|
3
|
import locomotive #
>-- custom application code library in the "locomotive" directory
|
測試
Pythonunittest標準庫提供一個非常好的測試解決方案。熟悉 JUnit 的 Java 開發人員和熟悉 Test::Unit 框架的 Ruby 開發人員應該會覺得 清單
5 中的 Pythonunittest代碼很容易理解。
清單 5. Python unittest
01
|
class AppTest(unittest.TestCase):
|
04
|
self .app = locomotive.app.Application()
|
09
|
def test_development_feeds_list( self ):
|
10
|
feeds_list = self .app.development_feeds_list()
|
11
|
self .assertTrue( len (feeds_list) = = 15 )
|
12
|
self .assertTrue( 'feed://news.yahoo.com/rss/stock-markets' in feeds_list)
|
清單 5 中的代碼還演示了 Python 的一個顯著的特點:所有的代碼必須一致縮進,否則無法成功編譯。tearDown(self)方法可能在開始時看起來有點古怪。您可能會問,為什麼測試總是被硬編碼為通過?事實上並非如此。這隻是在
Python 中編寫空方法的一種方式。
工具
我真正需要的是一個具備語法突出顯示、代碼完成和斷點調試功能的集成開發環境 (IDE),用該環境幫助我掌握我的 Python 學習曲線。作為使用 Eclipse IDE 進行 Java 開發的一名用戶,pyeclipse插件是我考慮的下一個工具。雖然該插件有時比較慢,但它工作得相當不錯。我最終投資了 PyCharm IDE,它滿足了我的所有 IDE 要求。
在掌握了 Python 及其生態係統的基本知識之後,終於來到開始實現機器學習解決方案的時候。
使用 Python 和 NLTK 實現分類
實現解決方案涉及捕獲模擬的 RSS 提要、整理其文本、使用一個NaiveBayesClassifier和 kNN 算法對類別進行分類。下麵將會介紹這些操作中的每一個。
捕獲和解析提要
該項目特別具有挑戰性,因為客戶還沒有定義目標 RSS 提要列表。因此,也不存在 “訓練數據”。所以,在初始開發期間必須模擬提要和訓練數據。
我用來獲得示例提要數據的第一個方法是隻提取在某個文本文件中指定的列表中的 RSS 提要。Python 提供了一個很好的 RSS 提要解析庫,其名稱為feedparser,它抽象不同的 RSS 和 Atom 格式之間的差異。簡單的基於文本的對象序列化的另一個有用的庫被幽默地稱為pickle(泡菜)。這兩個庫在 清單
6 的代碼中均有使用,清單 6 中的代碼將每一個 RSS 提要捕獲為 “醃製過的” 對象文件,以備後用。如您所見,Python 代碼非常簡潔,且功能強大。
清單 6. CaptureFeeds 類
07
|
for (i,
url) in enumerate ( self .rss_feeds_list()):
|
08
|
self .capture_as_pickled_feed(url.strip(),
i)
|
10
|
def rss_feeds_list( self ):
|
11
|
f = open ( 'feeds_list.txt' , 'r' )
|
16
|
def capture_as_pickled_feed( self ,
url, feed_index):
|
17
|
feed = feedparser.parse(url)
|
18
|
f = open ( 'data/feed_' + str (feed_index) + '.pkl' , 'w' )
|
22
|
if __name__ = = "__main__" :
|
下一步的挑戰性之大是出乎意料的。現在,我有了樣例提要數據,必須對它進行分類,以便將它用作訓練數據。訓練數據 是向您的分類算法提供的數據集,以便您能從中進行學習。
例如,我使用的樣例提要包括了體育電視網絡公司 ESPN。提要的項目之一是關於 Denver Broncos 橄欖球隊的 Tim Tebow 被轉會到 New York Jets 橄欖球隊,在同一時間,Broncos 簽了他們新的四分衛 Peyton Manning。提要結果中的另一個項目是 Boeing Company 和它的新噴氣式飛機 (jet)。所以,這裏的問題是,應該將哪些具體的類別值分配給第一個故事?tebow、broncos、manning、jets、quarterback、trade和nfl這些值都是合適的。但隻有一個值可以在訓練數據中被指定為訓練數據類別。同樣,在第二個故事中,類別應該是boeing還是jet?困難的部分在於這些細節。如果您的算法要產生精確的結果,那麼大型訓練數據集的準確手工分類非常關鍵。要做到這一點,不應該低估所需的時間。
我需要使用更多的數據,而且這些數據必須已進行了準確的分類,這種情況很快就變得明顯。我可以在哪裏找到這樣的數據呢?進入 Python NLTK。除了是一個出色的語言文本處理庫之外,它甚至還帶有可下載的示例數據集,或是其術語中的文集, 以及可以輕鬆訪問此下載數據的應用程序編程接口。要安裝 Reuters 文集,可以運行如下所示的命令。會有超過 10,000 篇新聞文章將下載到您的 ~/nltk_data/corpora/reuters/
目錄中。與 RSS 提要項目一樣,每篇 Reuters 新聞文章中都包含一個標題和一個正文,所以這個 NLTK 預分類的數據非常適合於模擬 RSS 提要。
1
|
$
python #
enter an interactive Python shell
|
2
|
>>> import nltk #
import the nltk library
|
3
|
>>>
nltk.download() #
run the NLTK Downloader, then enter 'd' Download
|
4
|
Identifier>
reuters #
specify the 'reuters' corpus
|
特別令人感興趣的是文件 ~/nltk_data/corpora/reuters/cats.txt。它包含了一個列表,其中包含文章文件名稱,以及為每個文章文件分配的類別。文件看起來如下所示,所以,子目錄 test 中的文件 14828 中的文章與主題grain有關。
自然語言是混亂的
RSS 提要分類算法的原始輸入,當然是以英語書寫的文本。原始,確實如此。
從計算機處理的角度來看,英語或任何自然語言(口語或普通的語言)都是極不規範和不準確的。首先,存在大小寫的問題。單詞 Bronco 是否等於 bronco?答案是,也許是。接下來,您要應付標點和空格。bronco. 是否等於 bronco 或 bronco,?算是吧。然後,有複數形式和相似的單詞。run、running 和 ran 是否相等?這取決於不同的情況。這三個詞有一個共同的 詞根。如果將自然語言詞匯嵌入在標記語言(如
HTML)中,情況會怎麼樣呢?在這種情況下,您必須處理像<strong>bronco</strong>這樣的文本。最後,還有一個問題,就是那些經常使用但基本上毫無意義的單詞,像 a、and 和 the。這些所謂的停用詞非常礙事。自然語言非常淩亂;在處理之前,需要對它們進行整理。
幸運的是,Python 和 NLTK 讓您可以收拾這個爛攤子。在 清單 7 中,RssItem類的normalized_words方法可以處理所有這些問題。請特別注意
NLTK 如何隻使用一行代碼就能夠清潔嵌入式 HTML 標記的原始文章文本!使用一個正則表達式刪除標點,然後每個單詞被拆分,並規範化為小寫。
清單 7. RssItem 類
03
|
regex = re. compile ( '[%s]' % re.escape(string.punctuation))
|
05
|
def normalized_words( self ,
article_text):
|
07
|
oneline = article_text.replace( '\n' , '
' )
|
08
|
cleaned = nltk.clean_html(oneline.strip())
|
09
|
toks1 = cleaned.split()
|
11
|
translated = self .regex.sub('',
t1)
|
12
|
toks2 = translated.split()
|
14
|
t2s = t2.strip().lower()
|
15
|
if self .stop_words.has_key(t2s):
|
隻需這一行代碼就可以從 NLTK 獲得停用詞列表;並且還支持其他自然語言:
1
|
nltk.corpus.stopwords.words( 'english' )
|
NLTK 還提供了一些 “詞幹分析器” 類,以便進一步規範化單詞。請查看有關詞幹、詞形歸並、句子結構和語法的 NLTK 文檔,了解有關的更多信息。
使用 Naive Bayes 算法進行分類
算法在 NLTK 中被廣泛使用並利用nltk.NaiveBayesClassifier類實現。Bayes 算法根據特性在其數據集中的每個存在或不存在對項目進行分類。在 RSS 提要項目的情況下,每一個特性都是自然語言的一個給定的(清潔過的)單詞。該算法是 “樸實” 的,因為它假設特性(在本例中,單詞)之間沒有任何關係。
然而,英語這種語言包含超過 250,000 個單詞。當然,我不希望為了將 RSS 提要項目傳遞給算法就要為每個 RSS 提要項目創建一個包含 250,000 個布爾值的對象。那麼,我會使用哪些單詞?簡單來說,答案是在培訓數據組中除了停用詞之外最常見的單詞。NLTK 提供了一個優秀的類,即nltk.probability.FreqDist,我可以用它來識別這些最常用的單詞。在 清單
8 中,collect_all_words方法返回來自所有培訓文章的所有單詞的一個數組。
然後,此數組被傳遞給identify_top_words方法,以確定最頻繁的單詞。nltk.FreqDist類的一個有用的特性是,它實質上是一個散列,但是它的鍵按其對應的值或計數 排序。因此,使用[:1000]Python 語法可以輕鬆獲得最頻繁的 1000 個單詞。
清單 8. 使用 nltk.FreqDist 類
01
|
def collect_all_words( self ,
items):
|
04
|
for w in item.all_words:
|
08
|
def identify_top_words( self ,
all_words):
|
09
|
freq_dist = nltk.FreqDist(w.lower() for w in all_words)
|
10
|
return freq_dist.keys()[: 1000 ]
|
對於利用 NLTK Reuters 文章數據模擬的 RSS 提要項目,我需要確定每個項目的類別。為此,我讀取前麵提到的 ~/nltk_data/corpora/reuters/cats.txt 文件。用 Python 讀取一個文件非常簡單,如下所示:
1
|
def read_reuters_metadata( self ,
cats_file):
|
2
|
f = open (cats_file, 'r' )
|
接下來的步驟是獲得每個 RSS 提要項目的特性。RssItem類的features方法(如下所示)可以做到這一點。在該方法中,在文章中的all_words數組首先被減少到一個較小的set對象,以消除重複的單詞。然後會遍曆top_words,並在該 set 中進行比較,確定是否存在重複的單詞。隨後返回 1000 個布爾值組成的一個散列,以w_為鍵,後麵是單詞本身。這個 Python 非常簡潔。
1
|
def features( self ,
top_words):
|
2
|
word_set = set ( self .all_words)
|
5
|
features[ "w_%s" % w] = (w in word_set)
|
接下來,我收集了訓練集的 RSS 提要項目和它們各自的特性,並將它們傳遞給算法。清單 9 中的代碼演示了這個任務。請注意,分類器被訓練成為隻有一行代碼。
1
|
def classify_reuters( self ):
|
5
|
features = item.features(top_words)
|
6
|
tup = (features,
item.category) #
tup is a 2-element tuple
|
7
|
featuresets.append(tup)
|
8
|
classifier = nltk.NaiveBayesClassifier.train(training_set)
|
NaiveBayesClassifier在運行中的 Python 程序的內存中,它現在是經過訓練的。現在,我隻需遍曆需要進行分類的 RSS 提要項目集,並要求分類器猜測每個項目的類別。這很簡單。
1
|
for item in rss_items_to_classify:
|
2
|
features = item.features(top_words)
|
3
|
category = classifier.classify(feat)
|
變得不那麼樸實
如前所述,算法假設每個特性之間是沒有關係的。因此,像 "machine learning" 和 "learning machine",或者 "New York Jet" 和 "jet to New York" 這樣的短語是等效的(to 是一個停用詞)。在自然的語言上下文中,這些單詞之間有明顯的關係。所以,我怎麼會讓算法變得 “不那麼天真”,並識別這些單詞的關係?
其中一個技巧是在特性集內包括常見的雙字詞(兩個單詞為一組)和三字詞(三個單詞為一組)。NLTK 以nltk.bigrams(...)和nltk.trigrams(...)的形式對此提供了支持,現在我們對此應該不再感到驚訝了。正如可以從訓練數據組收集最常用的 n 個單詞那樣,也可以識別最常用的雙字詞和三字詞,並將它們用作特性。
您的結果會有所不同
對數據和算法進行完善是一門藝術。您是否應該進一步規範化單詞集,也許應該包括詞根?或者包括超過 1000 個最常用單詞?少一點是否合適?或者是否應該使用更大的訓練數據集?是否應該添加更多信用詞或 “停用詞根”?這些都是您要問自己的正確問題。使用它們進行實驗,通過試錯法,您可以會為您的數據實現最佳算法。我發現,85% 是一個很好的分類成功率。
利用 k-Nearest Neighbors 算法提出建議
客戶希望顯示在選定類別或相似類別中的 RSS 提要項目。現在,這些項目已經用 Naive Bayes 算法進行分類,這一要求的第一部分已得到了滿足。較難的部分是實現 “或相似類別” 的要求。這是機器學習建議器係統開始發揮作用的地方。建議器係統 根據其他項目的相似性來建議一個項目。Amazon.com 的產品建議和 Facebook 的朋友建議就是此功能的很好的示例。
k-Nearest Neighbors (kNN) 是最常用的建議算法。思路是向它提供一組標簽(即類別), 並且每個標簽都對應一個數據集。然後,該算法對各數據集進行了比較,以識別相似的項目。數據集由多個數值數組構成,數值的範圍往往被規範化為從 0 到 1。然後,它可以從數據集識別相似的標簽。與隻產生一個結果的 Naive Bayes
不同,kNN 可以產生一個有排名的列表,其中包含若幹(即,k 的值)個建議。
我發現,建議器算法比分類算法更容易理解和實現,但對於本文來說,其代碼過於冗長,並且有複雜的數學,無法在這裏詳述。請參閱由 Manning 出版的一本很好的新書 Machine Learning in Action,獲取 kNN 編碼示例(請參閱 參考資料 中的鏈接)。在
RSS 提要項目實現的過程中,標簽值是項目類別,而數據集是最常用的 1000 個單詞的值數組。同樣,在構建這個數組時,一部分屬於科學範疇,一部分屬於數學範疇,還有一部分屬於藝術範疇。在數組中,每個單詞的值都可以是簡單的 0 或 1 的布爾值、文章中單詞出現次數的百分比、該百分比的指數值,或一些其他值。
結束語
探索 Python、NLTK 和機器學習一直是一個有趣的、令人愉快的經驗。Python 語言強大而又簡潔,現在已成為我的開發工具包的核心部分。它非常適合於機器學習、自然語言和數學/科學應用程序。雖然本文中並沒有提到,但我還發現 Python 對於圖表和繪圖非常有用。如果 Python 同樣是您的盲點,我建議您了解一下它。
簡介: 機器學習取決於 IT、數學和自然語言的交集,在大數據應用程序中會通常用到機器學習。本文將討論 Python 編程語言和它的 NLTK 庫,然後將它們應用於一個機器學習項目。
挑戰:使用機器學習對 RSS 提要進行分類
最近,我接到一項任務,要求為客戶創建一個 RSS 提要分類子係統。目標是讀取幾十個甚至幾百個 RSS 提要,將它們的許多文章自動分類到幾十個預定義的主題領域當中。客戶網站的內容、導航和搜索功能都將由這個每日自動提要檢索和分類結果驅動。
客戶建議使用機器學習,或許還會使用 Apache Mahout 和 Hadoop 來實現該任務,因為客戶最近閱讀了有關這些技術的文章。但是,客戶的開發團隊和我們的開發團隊都更熟悉 Ruby,而不是 Java™ 技術。本文將介紹解決方案的技術之旅、學習過程和最終實現。
什麼是機器學習?
我的第一個問題是,“究竟什麼是機器學習?” 我聽說過這個術語,並且隱約知道超級計算機 IBM® Watson 最近使用該技術在一場 Jeopardy 比賽中擊敗了人類競爭者。作為購物者和社交網絡活動參與者,我也知道 Amazon.com 和 Facebook 根據其購物者數據在提供建議(如產品和人)方麵表現良好。總之,機器學習取決於 IT、數學和自然語言的交集。它主要關注以下三個主題,但客戶的解決方案最終僅涉及前兩個主題:
-
分類。根據類似項目的一組訓練數據,將相關的項分配到任意預定義的類別
-
建議。根據類似項目的觀察來建議采用的項
-
集群。在一組數據內確定子組
Mahout 和 Ruby 的選擇
理解了機器學習是什麼之後,下一步是確定如何實現它。根據客戶的建議,Mahout 是一個合適的起點。我從 Apache 下載了代碼,並開始了學習使用 Mahout 及其兄弟 Hadoop 實現機器學習的過程。不幸的是,我發現即使對於有經驗的 Java 開發人員而言,Mahout 的學習曲線也很陡峭,並且不存在可用的樣例代碼。同樣不幸的是,機器學習缺乏基於 Ruby 的框架或 gem。
發現 Python 和 NLTK
我繼續搜索解決方案,並且在結果集中一直遇到 "Python"。作為一名 Ruby 開發人員,雖然我還沒有學過該語言,但我也知道 Python 是一個麵向相似對象的、基於文本的、可理解和動態的編程語言。盡管兩種語言之間存在一些相似之處,但我多年來都忽視了學習 Python,將它視為一項多餘的技能集。Python 是我的 “盲點”,我懷疑許多 Ruby 開發人員同行都是這樣認為的。
搜索機器學習的書籍,並更深入研究它們的目錄,我發現,有相當高比例的此類係統在使用 Python 作為其實現語言,並使用了一個被稱為 Natural Language Toolkit(NLTK,自然語言工具包)的庫。通過進一步的搜索,我發現 Python 的應用比我意識到的還要廣泛,如 Google App Engine、YouTube 和使用 Django 框架構建的網站。它甚至還預安裝在我每天都使用的 Mac OS X 工作站上!此外,Python 為數學、科學和工程提供了有趣的標準庫(例如,NumPy 和
SciPy)。
我決定推行一個 Python 解決方案,因為我找到了非常好的編碼示例。例如,下麵這一行代碼就是通過 HTTP 讀取 RSS 提要並打印其內容所需的所有代碼:
1
|
print feedparser.parse( "https://feeds.nytimes.com/nyt/rss/Technology" )
|
快速掌握 Python
在學習一門新的編程語言時,最容易的部分往往是學習語言本身。較難的部分是了解它的生態係統:如何安裝它、添加庫、編寫代碼、構造代碼文件、執行它、調試它並編寫單元測試。本節將簡要介紹這些主題;請務必參閱 參考資料,以獲得有關詳細信息的鏈接。
pip
Python Package Index (pip) 是 Python 的標準軟件包管理器。您可以使用該程序將庫添加到您的係統。它類似於 Ruby 庫的 gem。為了將 NLTK 庫添加到您的係統,您可以輸入以下命令:
為了顯示在您的係統上已安裝的 Python 庫的列表,請運行以下命令:
運行程序
執行 Python 程序同樣很簡單。獲得一個名稱為 locomotive_main.py 的程序和三個參數,然後您就可以使用 Python 程序編譯並執行它:
1
|
$
python locomotive_main.py arg1 arg2 arg3
|
Python 使用 清單 1 中的if __name__ == "__main__":語法來確定文件本身是從命令行執行的還是從其他代碼導入的。為了讓文件變得可以執行,需要添加"__main__"檢測。
清單 1. Main 檢測
5
|
if __name__ = = "__main__" :
|
6
|
start_time = time.time()
|
8
|
app = locomotive.app.Application()
|
9
|
...
additional logic ...
|
virtualenv
大多數 Ruby 開發人員熟悉係統範圍的庫或 gem 的問題。使用一組係統範圍內的庫的做法一般是不可取的,因為您的其中一個項目可能依賴於某個給定的庫的版本 1.0.0,而另一個項目則依賴於版本 1.2.7。同樣,Java 開發人員都知道係統範圍的 CLASSPATH 存在同樣的問題。就像 Ruby 社區使用其rvm工具,而 Python 社區使用virtualenv工具(請參閱 參考資料,以獲得相關鏈接)來創建獨立的執行環境,其中包含特定版本的
Python 和一組庫。清單 2 中的命令顯示了如何為您 p1 項目創建一個名為 p1_env 的虛擬環境,其中包含feedparser、numpy、scipy和nltk庫。
清單 2. 使用 virualenv 創建一個虛擬環境的命令
1
|
$
sudo pip install virtualenv $ cd ~ $ mkdir p1 $ cd p1 $ virtualenv p1_env - - distribute
$ source p1_env / bin / activate
(p1_env)[~ / p1]$
pip install feedparser (p1_env)[~ / p1]$
pip install numpy (p1_env)[~ / p1]$
pip install scipy (p1_env)[~ / p1]$
pip install nltk (p1_env)[~ / p1]$
pip freeze
|
每次在一個 shell 窗口使用您的項目時,都需要 “獲得” 您的虛擬環境激活腳本。請注意,在激活腳本被獲得後,shell 提示符會改變。當在您的係統上創建和使用 shell 窗口,輕鬆地導航到您的項目目錄,並啟動其虛擬環境時,您可能想在您的 ~/.bash_profile 文件中添加以下條目:
1
|
$
alias p1 = "cd
~/p1 ; source p1_env/bin/activate"
|
代碼庫結構
在完成簡單的單文件 “Hello World” 程序的編寫之後,Python 開發人員需要理解如何正確地組織其代碼庫的目錄和文件名。Java 和 Ruby 語言在這方麵都有各自的要求,Python 也沒有什麼不同。簡單來說,Python 使用包 的概念對相關的代碼進行分組,並提供了明確的名稱空間。出於演示目的,在本文中,代碼存在於某個給定項目的根目錄中,例如 ~/p1。在這個目錄中,存在一個用於相同名稱的 Python
包的 locomotive 目錄。 清單 3 顯示了這個目錄結構。
清單 3. 示例目錄結構
08
|
category_associations.py
|
17
|
category_associations_test.py
|
請注意名稱古怪的 __init__.py 文件。這些文件指示 Python 為您的包加載必要的庫和特定的應用程序代碼文件,它們都位於相同的目錄中。 清單
4 顯示了文件 locomotive/__init__.py 的內容。
清單 4. locomotive/__init__.py
01
|
#
system imports; loads installed packages
|
06
|
#
application imports; these load your specific *.py files
|
09
|
import category_associations
|
10
|
<最後更新:2017-04-04 07:03:02