Python 中的實用數據挖掘


本文是 2014 年 12 月我在布拉格經濟大學做的名為‘ Python 數據科學’講座的筆記。歡迎通過 @RadimRehurek 進行提問和評論。

本次講座的目的是展示一些關於機器學習的高級概念。該筆記中用具體的代碼來做演示,大家可以在自己的電腦上運行(需要安裝 IPython,如下所示)。

本次講座的聽眾需要了解一些基礎的編程(不一定是 Python),並擁有一點基本的數據挖掘背景。本次講座不是機器學習專家的“高級演講”。

這些代碼實例創建了一個有效的、可執行的原型系統:一個使用“spam”(垃圾信息)或“ham”(非垃圾信息)對英文手機短信(”短信類型“的英文)進行分類的 app。

Python

整套代碼使用 Python 語言。 python 是一種在管線(pipeline)的所有環節(I/O、數據清洗重整和預處理、模型訓練和評估)都好用的通用語言。盡管 python 不是唯一選擇,但它靈活、易於開發,性能優越,這得益於它成熟的科學計算生態系統。Python 龐大的、開源生態系統同時避免了任何單一框架或庫的限制(以及相關的信息丟失)。

IPython notebook,是 Python 的一個工具,它是一個以 HTML 形式呈現的交互環境,可以通過它立刻看到結果。我們也將重溫其它廣泛用於數據科學領域的實用工具。
 
想交互運行下面的例子(選讀)?
1. 安裝免費的 Anaconda Python 發行版,其中已經包含 Python 本身。
2. 安裝“自然語言處理”庫——TextBlob: 安裝包在這
3. 下載本文的源碼(網址: http://radimrehurek.com/data_science_python/data_science_python.ipynb 並運行: $ ipython notebook data_science_python.ipynb
4. 觀看 IPython notebook 基本用法教程  IPython tutorial video 
5. 運行下面的第一個代碼,如果執行過程沒有報錯,就可以了。
 
端到端的例子:自動過濾垃圾信息
In [1]:
 

第一步:加載數據,瀏覽一下

 
讓我們跳過真正的第一步(完善資料,了解我們要做的是什么,這在實踐過程中是非常重要的),直接到  https://archive.ics.uci.edu/ml/datasets/SMS+Spam+Collection 下載 demo 里需要用的 zip 文件,解壓到 data 子目錄下。你能看到一個大概 0.5MB 大小,名為 SMSSpamCollection 的文件:

 

這份文件包含了 5000 多份 SMS 手機信息(查看 readme 文件以獲得更多信息):
 
In [2]:
5574
 
文本集有時候也稱為“語料庫”,我們來打印 SMS 語料庫中的前 10 條信息:
In [3]:
 
我們看到一個 TSV 文件(用制表符 tab 分隔),它的第一列是標記正常信息(ham)或“垃圾文件”(spam)的標簽,第二列是信息本身。
 
這個語料庫將作為帶標簽的訓練集。通過使用這些標記了 ham/spam 例子,我們將訓練一個自動分辨 ham/spam 的機器學習模型。然后,我們可以用訓練好的模型將任意未標記的信息標記為 ham 或 spam。

learning model

 
我們可以使用 Python 的 Pandas 庫替我們處理 TSV 文件(或 CSV 文件,或 Excel 文件):
 
In [4]:
 
 
我們也可以使用 pandas 輕松查看統計信息:
 
In [5]:
 out[5]:
   
message
label    
ham count 4827
unique 4518
top Sorry, I’ll call later
freq 30
spam count 747
unique 653
top Please call our customer service representativ…
freq 4
 
 
這些信息的長度是多少:
 
In [6]:
In [7]:
Out[7]:
download
In [8]:
Out[8]:
哪些是超長信息?
In [9]:
spam 信息與 ham 信息在長度上有區別嗎?
 
In [10]:
Out[10]:
  download (1)
太棒了,但是我們怎么能讓電腦自己識別文字信息?它可以理解這些胡言亂語嗎?

 

第二步:數據預處理

這一節我們將原始信息(字符序列)轉換為向量(數字序列);

這里的映射並非一對一的,我們要用詞袋模型(bag-of-words)把每個不重復的詞用一個數字來表示。

與第一步的方法一樣,讓我們寫一個將信息分割成單詞的函數:
In [11]:
這還是原始文本的一部分:
 
In [12]:
Out[12]:
 
這是原始文本處理后的樣子:
 
In [13]:
Out[13]:
 
自然語言處理(NLP)的問題:
 
  1. 大寫字母是否攜帶信息?
  2. 單詞的不同形式(“goes”和“go”)是否攜帶信息?
  3. 嘆詞和限定詞是否攜帶信息?

換句話說,我們想對文本進行更好的標准化。

我們使用 textblob 獲取 part-of-speech (POS) 標簽:

In [14]:
Out[14]:
 
並將單詞標准化為基本形式 ( lemmas):
In [15]:
Out[15]:

 

這樣就好多了。你也許還會想到更多的方法來改進預處理:解碼 HTML 實體(我們上面看到的 &amp 和 &lt);過濾掉停用詞 (代詞等);添加更多特征,比如所有字母大寫標識等等。

 

第三步:數據轉換為向量

現在,我們將每條消息(詞干列表)轉換成機器學習模型可以理解的向量。

用詞袋模型完成這項工作需要三個步驟:

1.  對每個詞在每條信息中出現的次數進行計數(詞頻);
2. 對計數進行加權,這樣經常出現的單詞將會獲得較低的權重(逆向文件頻率);
3. 將向量由原始文本長度歸一化到單位長度(L2 范式)。

每個向量的維度等於 SMS 語料庫中包含的獨立詞的數量。

In [16]:
 

這里我們使用強大的 python 機器學習訓練庫 scikit-learn (sklearn),它包含大量的方法和選項。

我們取一個信息並使用新的 bow_tramsformer 獲取向量形式的詞袋模型計數:

In [17]:
 
In [18]:
 
 

message 4 中有 9 個獨立詞,它們中的兩個出現了兩次,其余的只出現了一次。可用性檢測,哪些詞出現了兩次?

In [19]:
 
 

整個 SMS 語料庫的詞袋計數是一個龐大的稀疏矩陣:

In [20]:
 
 

最終,計數后,使用 scikit-learn 的 TFidfTransformer 實現的 TF-IDF 完成詞語加權和歸一化。

In [21]:
 
 

單詞 “u” 的 IDF(逆向文件頻率)是什么?單詞“university”的 IDF 又是什么?

In [22]:
 
 

將整個 bag-of-words 語料庫轉化為 TF-IDF 語料庫。

In [23]:
 
 
有許多方法可以對數據進行預處理和向量化。這兩個步驟也可以稱為“特征工程”,它們通常是預測過程中最耗時間和最無趣的部分,但是它們非常重要並且需要經驗。訣竅在於反復評估:分析模型誤差,改進數據清洗和預處理方法,進行頭腦風暴討論新功能,評估等等。

第四步:訓練模型,檢測垃圾信息

我們使用向量形式的信息來訓練 spam/ham 分類器。這部分很簡單,有很多實現訓練算法的庫文件。

這里我們使用 scikit-learn,首先選擇 Naive Bayes 分類器:

In [24]:
 
 

我們來試着分類一個隨機信息:

In [25]:
 
 

太棒了!你也可以用自己的文本試試。

有一個很自然的問題是:我們可以正確分辨多少信息?

In [26]:
 
In [27]:
 
In [28]:
Out[28]:
  download (2)
我們可以通過這個混淆矩陣計算精度(precision)和召回率(recall),或者它們的組合(調和平均值)F1:
In [29]:
 
 

有相當多的指標都可以用來評估模型性能,至於哪個最合適是由任務決定的。比如,將“spam”錯誤預測為“ham”的成本遠低於將“ham”錯誤預測為“spam”的成本。

 

第五步:如何進行實驗?

在上述“評價”中,我們犯了個大忌。為了簡單的演示,我們使用訓練數據進行了准確性評估。永遠不要評估你的訓練數據。這是錯誤的。

這樣的評估方法不能告訴我們模型的實際預測能力,如果我們記住訓練期間的每個例子,訓練的准確率將非常接近 100%,但是我們不能用它來分類任何新信息。

一個正確的做法是將數據分為訓練集和測試集,在模型擬合和調參時只能使用訓練數據,不能以任何方式使用測試數據,通過這個方法確保模型沒有“作弊”,最終使用測試數據評價模型可以代表模型真正的預測性能。

In [30]:
 
 
按照要求,測試數據占整個數據集的 20%(總共 5574 條記錄中的 1115 條),其余的是訓練數據(5574 條中的 4459 條)。

讓我們回顧整個流程,將所有步驟放入 scikit-learn 的 Pipeline 中:

In [31]:
 
實際當中一個常見的做法是將訓練集再次分割成更小的集合,例如,5 個大小相等的子集。然后我們用 4 個子集訓練數據,用最后 1 個子集計算精度(稱之為“驗證集”)。重復5次(每次使用不同的子集進行驗證),這樣可以得到模型的“穩定性“。如果模型使用不同子集的得分差異非常大,那么很可能哪里出錯了(壞數據或者不良的模型方差)。返回,分析錯誤,重新檢查輸入數據有效性,重新檢查數據清洗。

在這個例子里,一切進展順利:

In [32]:
 
 

得分確實比訓練全部數據時差一點點( 5574 個訓練例子中,准確性 0.97),但是它們相當穩定:

In [33]:
 
 

我們自然會問,如何改進這個模型?這個得分已經很高了,但是我們通常如何改進模型呢?

Naive Bayes 是一個高偏差-低方差的分類器(簡單且穩定,不易過度擬合)。與其相反的例子是低偏差-高方差(容易過度擬合)的 k 最臨近(kNN)分類器和決策樹。Bagging(隨機森林)是一種通過訓練許多(高方差)模型和求均值來降低方差的方法。

 

換句話說:

  • 高偏差 = 分類器比較固執。它有自己的想法,數據能夠改變的空間有限。另一方面,也沒有多少過度擬合的空間(左圖)。
  • 低偏差 = 分類器更聽話,但也更神經質。大家都知道,讓它做什么就做什么可能造成麻煩(右圖)。
In [34]:
In [35]:
 
Out[35]:
 
  download (3)
 
 
 (我們對數據的 64% 進行了有效訓練:保留 20% 的數據作為測試集,保留剩余的 20% 做 5 折交叉驗證 = > 0.8*0.8*5574 = 3567個訓練數據。)

隨着性能的提升,訓練和交叉驗證都表現良好,我們發現由於數據量較少,這個模型難以足夠復雜/靈活地捕獲所有的細微差別。在這種特殊案例中,不管怎樣做精度都很高,這個問題看起來不是很明顯。

關於這一點,我們有兩個選擇:

  1. 使用更多的訓練數據,增加模型的復雜性;
  2. 使用更復雜(更低偏差)的模型,從現有數據中獲取更多信息。

在過去的幾年里,隨着收集大規模訓練數據越來越容易,機器越來越快。方法 1 變得越來越流行(更簡單的算法,更多的數據)。簡單的算法(如 Naive Bayes)也有更容易解釋的額外優勢(相對一些更復雜的黑箱模型,如神經網絡)。

了解了如何正確地評估模型,我們現在可以開始研究參數對性能有哪些影響。

第六步:如何調整參數?

到目前為止,我們看到的只是冰山一角,還有許多其它參數需要調整。比如使用什么算法進行訓練。

上面我們已經使用了 Navie Bayes,但是 scikit-learn 支持許多分類器:支持向量機、最鄰近算法、決策樹、Ensamble 方法等…

 

我們會問:IDF 加權對准確性有什么影響?消耗額外成本進行詞形還原(與只用純文字相比)真的會有效果嗎?

讓我們來看看:

In [37]:
In [38]:
 
 

(首先顯示最佳參數組合:在這個案例中是使用 idf=True 和 analyzer=split_into_lemmas 的參數組合)

快速合理性檢查

In [39]:
 
 

predict_proba 返回每類(ham,spam)的預測概率。在第一個例子中,消息被預測為 ham 的概率 >99%,被預測為 spam 的概率 <1%。如果進行選擇模型會認為信息是 ”ham“:

In [40]:
 
 

在訓練期間沒有用到的測試集的整體得分:

In [41]:
 
 
這是我們使用詞形還原、TF-IDF 和 Navie Bayes 分類器的 ham 檢測 pipeline 獲得的實際預測性能。

讓我們嘗試另一個分類器:支持向量機(SVM)。SVM 可以非常迅速的得到結果,它所需要的參數調整也很少(雖然比 Navie Bayes 稍多一點),在處理文本數據方面它是個好的起點。

In [42]:
In [43]:
 
 

因此,很明顯的,具有 C=1 的線性核函數是最好的參數組合。

再一次合理性檢查:

In [44]:
 
In [45]:
 
 

這是我們使用 SVM 時可以從 spam 郵件檢測流程中獲得的實際預測性能。

第七步:生成預測器

經過基本分析和調優,真正的工作(工程)開始了。

生成預測器的最后一步是再次對整個數據集合進行訓練,以充分利用所有可用數據。當然,我們將使用上面交叉驗證找到的最好的參數。這與我們開始做的非常相似,但這次深入了解它的行為和穩定性。在不同的訓練/測試子集進行評價。

最終的預測器可以序列化到磁盤,以便我們下次想使用它時,可以跳過所有訓練直接使用訓練好的模型:

In [46]:
 

加載的結果是一個與原始對象表現相同的對象:

In [47]:
 
 

生產執行的另一個重要部分是性能。經過快速、迭代模型調整和參數搜索之后,性能良好的模型可以被翻譯成不同的語言並優化。可以犧牲幾個點的准確性換取一個更小、更快的模型嗎?是否值得優化內存使用情況,或者使用 mmap 跨進程共享內存? 電動叉車

請注意,優化並不總是必要的,要從實際情況出發。

還有一些需要考慮的問題,比如,生產流水線還需要考慮魯棒性(服務故障轉移、冗余、負載平衡)、監測(包括異常自動報警)、HR 可替代性(避免關於工作如何完成的“知識孤島”、晦澀/鎖定的技術、調整結果的黑藝術)。現在,開源世界都可以為所有這些領域提供可行的解決方法,由於 OSI 批准的開源許可證,今天展示的所有工具都可以免費用於商業用途。

 

其他實用概念

數據稀疏性

在線學習,數據流

用於內存共享的 mmap,系統“冷啟動”負載時間

可擴展性、分布式(集群)處理

無監督學習

大多數數據沒有結構化。了解這些數據,其中沒有自帶的標簽(不然就成了監督學習!)。

我們如何訓練沒有標簽的內容?這是什么魔法?

分布假設“在類似語境中出現的詞傾向於具有相似的含義”。上下文=句子,文檔,滑動窗口……

查看 google 關於無監督學習的 word2vec 在線演示。簡單的模型、大量數據(Google 新聞,1000 億詞,沒有標簽)。

下一步做什么?

這個 notebook 的靜態版本(非交互版本)的 HTML,地址: http://radimrehurek.com/data_science_python (你可能已經在看了,但以防萬一)

交互式 notebook 源文件在 GitHub 上,
http://radimrehurek.com/data_science_python/data_science_python.ipynb(見上面的安裝說明)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM