前言

本系列部落格文章將分享我在 Coursera 上台灣大學林軒田教授所教授的機器學習基石(Machine Learning Foundations)課程整理成的心得,並對照林教授的投影片作說明。若還沒有閱讀過 第四講 的碼農們,我建議可以先回頭去讀一下再回來喔!

在第四講中我們了解了在有限假設集合的情況下機器學習是可能的,而第五講就是想要將有限假設集合可以推廣出去,讓我們在無限的假設集合裡也可以透過一些理論慢慢收斂到一個多項式集合,如此我們就可以放心的利用機器學習來解決我們所面對的一些問題。

範例原始碼:FukuML - 簡單易用的機器學習套件

我在分享機器學習基石課程時,也跟著把每個介紹過的機器學習演算法都實作了一遍,原始碼都放在 GitHub 上了,所以大家可以去參考看看每個演算法的實作細節,看完原始碼會對課程中的數學式更容易理解。

如果大家對實作沒有興趣,只想知道怎麼使用機器學習演算法,那 FukuML 絕對會比起其他機器學習套件簡單易用,且方法及變數都會跟林軒田教授的課程類似,有看過課程的話,說不定連文件都不用看就會使用 FukuML 了。不過我還是有寫 Tutorial 啦,之後會不定期更新,讓大家可以容易上手比較重要!

熱身回顧一下

我們先來回顧一下上一講的內容,在上一講我們知道了機器學習在足夠的資料及有限的假設集合這種情況下是可行的。

更新機器學習流程圖

如果假設集合 H 是有限 M 個,然後資料量夠多 N,不管我們的演算法 A 是什麼,我們由定理可以知道 Eout 跟 Ein 是很接近的。所以如果 A 找到了一個假設 g 讓 Ein 近似於 0,那我們就可以說 Eout 大概就會是 0,因此機器學習在這樣的情況下是可行的。

有了這樣的概念,我們擴充了我們的機器學習流程圖。從輸入資料中去訓練機器學習,然後得到 Ein 近似於 0,之後再從同一個資料分佈中去測試機器學習的結果,如此可以證明機器有學習到技能。

機器學習的兩個核心問題

機器學習有兩個核心問題,我們希望 Eout 跟 Ein 是很接近的,這個意思就是說,我們希望後來測試學習的結果,會跟訓練時得到的結果很接近,這樣我們才能說機器有學習到技能。

另一個就是,我們希望 Ein 可以很小,也就是訓練的過程中,我們希望機器就可以得到很好的效果,也就是誤差很小。

那之前我們所說的有限集合 M 在這邊扮演什麼角色呢?

M 的兩難

如果假設集合 M 很小,我們可以保證 Eout 可以接近 Ein,但是因為假設集合小,可以挑選的選擇就少,也因此 Ein 可能會是一個不小的值,也就是誤差會大。

如果假設集合 M 很大,我們可以會得到一個比較好的 Ein,也就是誤差比較小,但是 M 太大我們就無法保證 Eout 會跟 Ein 接近,也就是我們無法保證學習的結果,那機器就白學了。

所以 M 是在哪個值剛好能同時解決這個兩個問題就是一個重點。

M 從哪裡來?

從上一講的定理證明中,我們知道 M 是從當較大的誤差發生的情況累積出來的,所以如果假設集合越大,那累積出來的就一個越大的數字,如果是無限集合,那就沒有上限,但如果是有限集合的話,那就會有個上限。

大誤差發生的情況有很多時候是重疊的

但其實那個上限值我們是高估的,因為許多種大誤差發生的情況都可能是很相似的情況,所以其實這些大誤差發生的情況有很大一部份是重疊的。

那我們可以轉一個想法,我們可以把無限的假設集合依照它們的類型來分類嗎?這樣就可以轉換成一個有限的集合。

一個點的時候有幾種線

之前的 PLA 演算法其實有無限多的假設集合 H,所以在平面上分類一個點的線有無限多條,但是如果說有幾種線,那就只有兩種,一種線是將 x1 分成 o 的,一種線是將 x1 分成 x 的。

兩個點的時候有幾種線

以此類推,兩個點的時候,平面上會多多少種分類這兩個點的線呢?答案是四種線。

四個點的時候有幾種線

再以此類推,四個點的時候,平面上會多多少種分類這四個點的線呢?我們會發現下圖中有兩個組合已經無法找到直線可以做好分類,我們最多只能找到 14 條可以分類四個點的線。

Effective Number of Lines

這個故事告訴我們,隨著輸入變多,線的種類會慢慢變成不是指數型成長。依這樣的概念,我們將原本無限多的線轉變為有限多的不同種類的線,我們就可以用這個有限多的不同種類的線的數字來取代 M,這個數字會小於 2 的 N 次方。

這樣我們就可以再套到霍夫丁的定理,知道這樣的過程機器學習的解決會是有效可行的。

Dichotomies

這樣的過程就是將原本無限多的線,轉換成分種類的線 Dichotomies,一個 Dichotomy 就是一種分類組合,在二元分類裡這樣組合的上界就是 2 的 N 次方,我們可以用這個數字來取代無限大的 M。

找出問題的成長函數

了解這樣的特性之後,我們只要找出我們要解的問題的成長函數,而這個成長函數不會無限增加,那機器學習就可以證明是可行的。

四種成長函數

我們這邊列出了四種成長函數,分屬不同的問題,PLA 是屬於 2D perceptrons,mH 會小於 2 的 N 次方,代進霍夫丁不等式,由於是指數型成長,並無法保証式子會成立。所以我們希望能證明 PLA 的 mH 會是一個多項式,這樣才能保證霍夫丁不等式成立。

Break Point 的概念

Break Point 指的是,當下一個輸入出現時,Dichotomies 組合不再是指數型成長的的那個點,在 2D perceptrons 這邊從我們剛剛的例子可以知道 break point 出現在 4 個輸入時。

四種成長函數的 Break Points

我們知道 positive rays 的 break point 出現在 2,他的成長函數是 big O N,positive intervals 的 break point 出現在 3,他的成長函數是 big O N 平方,那麼在 2D perceptrons 時,我們知道 break point 出現在 4,那他的成長函數是 big O N 三次方嗎?我們可以推廣成一個通式嗎?這就是下次課程的內容了。

總結

機器學習可能的兩個核心問題是 Ein 近似於 0,且 Eout 跟 Ein 要接近。PLA 無限多的線我們可以轉換成 Effective Number of Lines 也就是轉換成 Effective Numbers of Hypotheses,M 無限集合也就轉為 mH 的有限集合,然後觀察 break point 的出現,讓我們可以知道假設集合不會是一個指數型成長的情況,如此依照霍夫丁不等式所說的,我們就可以讓機器學習的理論有充分的證明為可行了。

前言

本系列部落格文章將分享我在 Coursera 上台灣大學林軒田教授所教授的機器學習基石(Machine Learning Foundations)課程整理成的心得,並對照林教授的投影片作說明。若還沒有閱讀過 第三講 的碼農們,我建議可以先回頭去讀一下再回來喔!

第四講的內容主要是讓我們知道機器學習是否真的可能,並利用數學上的定理來說明機器學習在某些情境之下是可能的,有數學上定理的支持,我們就可以放心的利用機器學習來解決我們所面對的一些問題。

範例原始碼:FukuML - 簡單易用的機器學習套件

我在分享機器學習基石課程時,也跟著把每個介紹過的機器學習演算法都實作了一遍,原始碼都放在 GitHub 上了,所以大家可以去參考看看每個演算法的實作細節,看完原始碼會對課程中的數學式更容易理解。

如果大家對實作沒有興趣,只想知道怎麼使用機器學習演算法,那 FukuML 絕對會比起其他機器學習套件簡單易用,且方法及變數都會跟林軒田教授的課程類似,有看過課程的話,說不定連文件都不用看就會使用 FukuML 了。不過我還是有寫 Tutorial 啦,之後會不定期更新,讓大家可以容易上手比較重要!

熱身回顧一下

我們先來回顧一下上一講的內容,在上一講我們知道了各式各樣的機器學習方法及名詞,而我們未來會專注於二元分類及迴歸這樣的問題,然後使用大量監督式標示好的資料且定義明確的特徵來進行機器學習。

看看這個問題,想想如何使用學習

有人會問,說了這麼多,如何知道機器學習是不是真的可能?說不定根本無法做到。比如這個問題,g(x)可以回答 +1 還是 -1。

見仁見智的問題無法解

像這樣的問題,你可以回答 +1,因為 +1 的圖都是對稱的,而這個圖是對稱的,所以是 +1。你可以回答 -1,因為 -1 的圖都是左上方黑色的,而這個圖是左上方黑色的,所以是 -1。

套到二元分類的問題

現在我們套到二元分類的問題,給了 Xn 及 Yn,然後機器學習出了 g,我們可以說 g 近似於 f 嗎?

天下沒有白吃的午餐

在驗證 g 的時候,如果是用原本的 D,那我們很容易的說明 g 近似於 f,但是如果資料是用 D 以外的資料來驗證,那我們無法很明確的說明 g 近似於 f,但我們要的其實就是希望 g 在 D 以外的資料也近似於 f,這有可能嗎?

利用罐子取彈珠的例子來說明是否可能

現在想像我們有一個裡面有很多橘色和綠色彈珠的罐子,我們可能無法知道橘色彈珠的真實比例,但我們可以推估出橘色彈珠出現的機率嗎?

取樣看看

我們取樣看看,比如從罐子中取出十顆彈珠,假設現在取出的是三顆橘色七顆綠色,那我們可以說罐子中的比例是 30% 橘色 70% 綠色嗎?可能不能這樣說,有可能不是這樣比例,但很可能 30% 橘色 70% 綠色是一個很接近的比例數字。

Hoeffding 不等式 1

我們可以說取樣出來的結果會很接近真實情況,是因為 Hoeffding’s Inequality 這個定理。這個定理的數字如下圖,這說明了當 N 很大,也就是我們取樣的數字很大,那們 v - u 就會是一個很小的數字,也就是我們預估的 g 跟真實的 f 的差距很小。而當 ε 很大,也就是我們可以容忍的誤差很大的話,那當然我們就可以很容易地說 g 跟真實的 f 的差距很小。只要符合了這個不等式,那就叫做 probably approximately correct(PAC)。

Hoeffding 不等式 2

通常我們不會希望容忍誤差很大,所以通常我們會取 N 很大來推估 g 是否接近 f 的真實情況。

把 Hoeffding 不等式連結到機器學習

當我們現在有一個固定的 h(x)(假設集合 H 的其中一個,他有可能是 g),我們想要知道是否接近 f(x),x 從 X 中取出,如果 h(x) ̸= f(x),就是 h 答錯,就像是罐子中的橘色彈珠,如果 h(x) = f(x),就是 h 答對,就像是罐子中的綠色彈珠,如果取樣的數字很大的話,那我們就可以用部分的已知資料來大概推估 h(x) 在未知資料的表現情況。

把這個誤差推估的概念加進我們的機器學習圖表

把這個誤差推估的概念加進我們的機器學習圖表,任何一個 h 都可以用已知的 Ein 來推估未知的 Eout,如果 N 夠大。

套進 Hoeffding 不等式

我們套進 Hoeffding 不等式,Ein 代表在已知取樣的中 h(x) 跟 f(x) 的誤差,Eout 代表其他未知的資料中 h(x) 跟 f(x) 的誤差,這在 Hoeffding 不等式的定理下,我們知道 N 很大時,Ein 與 Eout 的誤差很接近,所以我們可以說 Ein 與 Eout 是 PAC。

驗證 h(x) 好不好

所以這常常會被機器學習用在驗證得出來的 h(x) 好不好,可以用這個圖來簡單表示,只要 N 夠大,取樣的分佈一致,那他的表現結果好與不好,是可以很明確地用部分已知資料來推估它在其他未知資料表現得好不好。

好死不死取到壞資料的情況

我們還是會有好死不死取到壞資料的情況,但一樣可以用 Hoeffding 不等式說明這個機率很小,但是還是會發生。

如果有很多個 h

我們剛剛都是用一個 h 來推估是否機器學習是可能的,的確單一個 h 的時候,我們可以用取樣的資料來說明 h(x) 是否跟 f(x) 接近。那如果有很多個 h 呢?

一樣套進 Hoeffding 不等式

如果是有限個 h,如 M 個 h,那一樣我們可以用取樣的資料來說明 h(x) 是否跟 f(x) 接近。

將這樣的概念加進去機器學習圖表

如果 H 這個集合只有 M 個,然後取樣的 N 夠大,利用 A 取出 g,如果 Ein(g) 接近 0,那麼 g 就是一個 PAC 的答案了,我可由此可知機器學習是可能的。那如果 M 是無限大呢?這個就要下一講來解答了。

總結

這一張說明了,我們無法直觀地說明得到的 h 是否能就是 f,因為天下沒有白吃的午餐。但我們用定理說明的 h 可以在未知的資料中 probably approximate correct。我們將定理連結到機器學習,就可以驗證 h。然後學習的過程也可以套進這樣的概念,當 H 中的 h 數量非無限的時候,機器學習是可能的。

前言

本系列部落格文章將分享我在 Coursera 上台灣大學林軒田教授所教授的機器學習基石(Machine Learning Foundations)課程整理成的心得,並對照林教授的投影片作說明。若還沒有閱讀過 第二講 的碼農們,我建議可以先回頭去讀一下再回來喔!

第三講的內容偏向介紹各種機器學習方法,以前念論文的時候看到這些名詞都會覺得高深莫測,但其實這各式各樣的機器學習方法其實都是從最基礎的核心變化而來,所以不要被嚇到。了解各種機器學習方法的輸入輸出對於日後面對一些問題的時候,我們才能夠知道要挑選什麼機器學習方法來解決問題。

範例原始碼:FukuML - 簡單易用的機器學習套件

我在分享機器學習基石課程時,也跟著把每個介紹過的機器學習演算法都實作了一遍,原始碼都放在 GitHub 上了,所以大家可以去參考看看每個演算法的實作細節,看完原始碼會對課程中的數學式更容易理解。

如果大家對實作沒有興趣,只想知道怎麼使用機器學習演算法,那 FukuML 絕對會比起其他機器學習套件簡單易用,且方法及變數都會跟林軒田教授的課程類似,有看過課程的話,說不定連文件都不用看就會使用 FukuML 了。不過我還是有寫 Tutorial 啦,之後會不定期更新,讓大家可以容易上手比較重要!

熱身回顧一下

我們先來回顧一下上一講的內容,在上一講我們知道了如何使用 PLA 讓機器學會回答是非題這樣的兩類問題(Binary Classificaction),套到機器學習的那句名句,我們可以清楚的了解,PLA 這個演算法 A 觀察了線性可分(linear separable)的 D 及感知假設集合 H 去得到一個最好的假設 g,這一句話就可以概括到上一講的內容了。

從輸出 y 的角度看機器學習,y 只有兩個答案選一個,就叫 Binary Classification

接下來我們來了解一下各式各樣的學習方法,從輸出 y 的角度看機器學習,y 只有兩個答案選一個,就叫 Binary Classification,像是之前的是否發信用卡的例子就是 Binary Classification。

從輸出 y 的角度看機器學習,y 有多個答案選一個,就叫 Multiclass Classification

從輸出 y 的角度看機器學習,y 有多個答案選一個,就叫 Multiclass Classification,像是使用投飲機辨識錢幣的問題就是一個 Multiclass Classification 的問題,所以我們可以將分類問題推廣到分成 K 類,這樣 Binary Classificatin 就是一個 K=2 的分類問題。

從輸出 y 的角度看機器學習,y 為一個實數,就叫 Regression

從輸出 y 的角度看機器學習,y 為一個實數,就叫 Regression,像是要預估病人再過幾天病會好,這就需要用到 Regression,這也會用到許多統計學相關的工具。

從輸出 y 的角度看機器學習,y 為一個結構序列,就叫 Structured Learning

從輸出 y 的角度看機器學習,y 為一個結構序列,就叫 Structured Learning,比如一個句子的詞性分析,會需要考慮到句子中的前後文,而句子的組合可能有無限多種,因此不能單純用 Multiclass Classification 來做到,這就需要用到 Structured Learning 相關的機器學習方法。

從輸出 y 的角度看機器學習,做個小結

從輸出 y 的角度看機器學習,如果 y 是兩類,那就是 Binary Classification;如果 y 是 k 類,那就是 Multiclass Classification;如果 y 是一個實數,那就是 Regression;如果 y 是一種結構關係,那就是 Structured Learning。當然還有其他變化,不過基礎上就是 Binary Classification 及 Regression,我們可以透過這兩個基礎核心來延伸出其他機器學習方法。

從輸入的資料 Yn 的角度看機器學習,如果每個 Xn 都有明確對應的 Yn,這就叫監督式學習(Supervised Learning)

從輸入的資料 Yn 的角度看機器學習,如果每個 Xn 都有明確對應的 Yn,這就叫監督式學習(Supervised Learning),比如在訓練投飲機辨識錢幣的時候,我們很完整個告訴他什麼大小、什麼重量就是什麼幣值的錢幣,這樣就是一種監督式學習方法。

從輸入的資料 Yn 的角度看機器學習,如果每個 Xn 都沒有標示 Yn,這就叫非監督式學習(Unsupervised Learning)

從輸入的資料 Yn 的角度看機器學習,如果每個 Xn 都沒有標示 Yn,這就叫非監督式學習(Unsupervised Learning)。比如在訓練投飲機辨識錢幣的時候,我們只告訴投飲機錢幣的大小及重量,但不告訴他什麼大小及重量個錢幣是哪個幣值的錢幣,讓機器自己去觀察特徵將這些錢幣分成一群一群,這又叫做分群,這就是一種非監督式學習方法。

從輸入的資料 Yn 的角度看機器學習,如果 Xn 只有部分有標示 Yn,這就叫半監督式學習(Semi-supervised Learning)

從輸入的資料 Yn 的角度看機器學習,如果 Xn 只有部分有標示 Yn,這就叫半監督式學習(Semi-supervised Learning),有些資料較難取得的狀況下,我們會使用到半監度式學習,比如在預測藥物是否對病人有效時,由於做人體實驗成本高且可能要等一段時間來看藥效,這樣的情況下標示藥物有效或沒效的成本很高,所以就可能需要用到半監度式學習。

從輸入的資料 Yn 的角度看機器學習,如果 Yn 是很難確知描述的,只能在機器作出反應時使用處罰及獎勵的方式讓機器知道對或錯,這就叫增強式學習(Reinforcement Learning)

從輸入的資料 Yn 的角度看機器學習,如果 Yn 是很難確知描述的,只能在機器作出反應時使用處罰及獎勵的方式讓機器知道對或錯,這就叫增強式學習(Reinforcement Learning)。這樣的機器學習方式,比較像自然界生物的學習方式,就像你要教一隻狗坐下,你很難直接告訴他怎麼做,而是用獎勵或處罰的方式讓狗狗漸漸知道坐下是什麼。增強式學習也就是這樣的機器學習方法,透過一次一次經驗的累積讓機器能夠學習到一個技能。比如像是教機器學習下棋,我們也可以透過勝負讓機器漸漸學習到如何下棋會下得更好。

從輸入的資料 Yn 的角度看機器學習,做個小結

從輸入的資料 Yn 的角度看機器學習,如果明確告知每個 Yn,那就是監督式學習;如果沒有告知任何 Yn,那就是非監督式學習;如果只有部份 Yn 的資料,那就是半監督式學習;如果是用獎勵、處罰的方式來告知 Yn,那就是增強式學習。當然還有其他種機器學習方法,其中最重要的核心就是監督式學習方法。

從餵資料給機器的角度看機器學習,一次餵進全部資料,這就叫 Batch Learning

從餵資料給機器的角度看機器學習,一次餵進全部資料,這就叫 Batch Learning。監督式學習方法,可能也會常使用 Batch Learning 的方式為資料。

從餵資料給機器的角度看機器學習,可以再慢慢餵進新資料,這就叫 Online Learning

從餵資料給機器的角度看機器學習,可以再慢慢餵進新資料,這就叫 Online Learning。Batch Learging 訓練好的機器,就無法調整他的技巧,可能會有越來越不準的情況,所以 Online Learning 可以再慢慢調整、增進技巧。PLA 算法可以很容易應用在 Online Learning 上,增強式學習方法也常常是使用 Online Learning 的方式餵資料。

從餵資料給機器的角度看機器學習,機器可以問問題,然後從問題的答案再餵進資料,這就叫 Active Learning

從餵資料給機器的角度看機器學習,機器可以問問題,然後從問題的答案再餵進資料,這就叫 Active Learning。這樣的學習方法是要希望讓機器可以用一些策略問問題,然後慢慢學習、改善技巧。

從餵資料給機器的角度看機器學習,做個小結

從餵資料給機器的角度看機器學習,如果一次餵進所有資料,就叫 Batch Learning;如果後續可以再慢慢餵進資料,就叫 Online Learning;如果機器可以問問題來餵進資料,就叫 Active Learning。當然還有其他種機器學習方法,其中最重要的核心就是 Batch Learning。

從輸入 X 的角度看機器學習,如果 X 的特徵很明確定義,這就叫 Concrete Feature

從輸入 X 的角度看機器學習,如果 X 的特徵很明確定義,這就叫 Concrete Feature。Concrete Featrue 的取得通常需要人去介入,比如為何發不發信用卡要看申請者的年收入,這就是因為人們覺得年收入對於付不得付出卡費有關係。

從輸入 X 的角度看機器學習,如果 X 的特徵是用最基礎未人為整理過的,這就叫 Raw Feature

從輸入 X 的角度看機器學習,如果 X 的特徵是用最基礎未人為整理過的,這就叫 Raw Feature。比如聲音訊號的頻率,圖片的像素,這都是 Raw Feature。

從輸入 X 的角度看機器學習,如果 X 的特徵是抽象的像是編號這樣的資料,這就叫 Abstract Feature

從輸入 X 的角度看機器學習,如果 X 的特徵是抽象的像是編號這樣的資料,這就叫 Abstract Feature。這通常就需要有人去抽取出更具象的特徵資料出來,這些特徵可能包含 Concrete Feature 或 Raw Feature。

從輸入 X 的角度看機器學習,做個小結

從輸入 X 的角度看機器學習,如果 X 是明確定義的,那就是 Concrete Feature;如果 X 是未經人為定義過的,那就是 Raw Feature;如果 X 是抽象的編號,那就是 Abstract Feature。當然還有其他種特徵,其中最重要的核心就是 Concrete Feature。

總結

從輸出 y 的角度看機器學習、從輸入的資料 Yn 的角度看機器學習、從餵資料給機器的角度看機器學習、從輸入 X 的角度看機器學習都會有許多不同的機器學習方法,但重要的是了解哪些是核心,其他機器學習方法也都是從這些核心發展而來。

前言

本系列部落格文章將分享我在 Coursera 上台灣大學林軒田教授所教授的機器學習基石(Machine Learning Foundations)課程整理成的心得,並對照林教授的投影片作說明。若還沒有閱讀過 第一講 的碼農們,我建議可以先回頭去讀一下再回來喔!

範例原始碼:FukuML - 簡單易用的機器學習套件

我在分享機器學習基石課程時,也跟著把每個介紹過的機器學習演算法都實作了一遍,原始碼都放在 GitHub 上了,所以大家可以去參考看看每個演算法的實作細節,看完原始碼會對課程中的數學式更容易理解。

如果大家對實作沒有興趣,只想知道怎麼使用機器學習演算法,那 FukuML 絕對會比起其他機器學習套件簡單易用,且方法及變數都會跟林軒田教授的課程類似,有看過課程的話,說不定連文件都不用看就會使用 FukuML 了。不過我還是有寫 Tutorial 啦,之後會不定期更新,讓大家可以容易上手比較重要!

熱身回顧一下

在前一章我們基本上可以了解機器學習的架構大致上就是 *A takes D and H to get g*,也就是說我們會使用演算法來基於資料與假設集合計算出一個符合資料呈現結果的方程式 g,在這邊我們就會看到 H 會長什麼樣子,然後介紹 Perceptron Learning Algorithm(PLA)來讓機器學習如何回答是非題,比如讓機器回答銀行是否要發信用卡給申請人這樣的問題。

再看一次是否要發信用卡這個問題

是否要發信用卡這個問題我們可以想成它是一個方程式 f,而申請者的資料集合 X 丟進去就可以得到 Y 這些是否核發信用卡的記錄,我們現在不知道 f,將歷史資料 D 拿來當成訓練資料,其中每個 xi 就是申請者的資料,它會一個多維相向,比如第一個維度是年齡,第二個維度是性別…等等,然後我們會將這些資料 D 及假設集合 H 丟到機器學習演算法 A,最後算出一個最像 f 的 g,這個 g 其實就是從假設集合 H 挑出一個最好的假設的結果。

簡單的假設集合:感知器

要回答是否核發信用卡,可以用這樣簡單的想法來實現,現在我們知道申請者有很多基本資料,這些資料可以關係到是否核發信用卡,學術上就稱為是「特徵值」,這些特徵值有的重要、有的不重要,我們可以為這些特徵值依照重要性配上一個權重分數 wi,所以當這些分數加總起來之後,如果超過一個界線 threshold 時,我們就可以就可以決定核發信用卡,否則就不核發。這些 wi 及 threshold 就是所謂的假設集合,可以表示成如投影片中的線性方程式。

將假設集合的線性方程式整理一下

Threshold 我們也可以視為是一種加權分數,所以就可以將假設集合的線性方程式整理得更簡潔,整個線性方程式就變成了很簡易的兩個向量內積而已。

用二維空間來看看這個例子

假如現在申請者的只有兩個特徵值,那就可以用一個二維空間來標出每個申請者的位置,而是否核發信用卡,則用藍色圈圈來代表核發,紅色叉叉代表不核發,而假設 h 就是在這空間的一條線的法向量,可以將藍色圈圈跟紅色叉叉完美的分開來,這在機器學習上就是所謂的「分類」,Perceptrons 就是一種線性分類器。

怎麼從所有的假設中得到最好的假設 g

我們希望 g 可以跟 f 一樣完美的分類信用卡的核發與否,只要從 H 這個假設集合中挑到可以完美分類信用卡核發與否的線,我們就可以得到 g 了,但這很難,因為平面中可以有無限多條線,這樣算不完。所以我們改變想法,我們先隨便切一條線,然後如果有錯的地方,就修正這條線。

Perceptron Learning Algorithm 感知學習模型

剛剛這樣有錯就去修正的想法,就是感知學習模型(Perceptron Learning Algorithm)的核心思想,實際上我們怎麼修正呢?我們來仔細看一下。假設現在有一個點 x 分錯了,它實際是核發的點,但卻被分在不核發的那一邊,這就代表 wt 向量與 x 之間的夾角太大,那就要讓它們之間的夾角變小,我們可以很簡單的用向量相加的方式來做到。如果現在 x 分錯了,它實際是不核發,那就代表 wt 向量與 x 向量之間的夾角太小,那就要讓他們之間的夾角變大,我們可以用 wt 向量減掉 x 向量來做到。這樣的計算很容易可以用程式做到。PLA 也是一個最簡易的神經網路算法。

看看 PLA 演算法修正 h 的過程

PLA 的一些問題

PLA 這個演算法會一直修正 h 直到對所有的 D 都沒有錯誤時,就會停止。但真實世界的資料不會這麼完美,PLA 可能會有不會停止的情況發生,所以我們只能修正 PLA,只算出夠好的 h 就可以了。

什麼時候 PLA 不會停止

什麼時候 PLA 會停止,什麼時候不會停止?當資料集合 D 為線性可分的時候,PLA 就會停止,但如果不是線性可分的時候就不會停止。

處理雜訊

其實真實世界的資料就是這樣,充滿了雜訊,這些雜訊也有可能本身就是錯誤的資料,比如銀行一開始核發就是錯的,這也就是為何我們只要得到一個接近 f 的 g 就可以了,而不一定要得到完美的 f。

找出犯最少錯的線

既然真實世界的資料有雜訊,那我們就用程式找出犯最少錯的線吧!說起來簡單,做起來很難,這個問題其實是個 NP-hard 的問題。

Pocket Algorithm

所以折衷的方式就是找到夠好的線就好,我們修改一下 PLA,讓他每次計算時,如果得到更好的線,就先暫時存下來,然後算個幾百輪,我們就可以假設目前得到的線就是一個不錯的 h 了。

演算法原始碼

以上就是第二講的內容,這邊我找到了有人實作這兩個演算法的原始碼,讓大家可以參考一下。

Naive PLA

from numpy import *

def naive_pla(datas):
    w = datas[0][0]
    iteration = 0
    while True:
        iteration += 1
        false_data = 0

        for data in datas:
            t = dot(w, data[0])
            if sign(data[1]) != sign(t):
                error = data[1]
                false_data += 1
                w += error * data[0]
        print 'iter%d (%d / %d)' % (iteration, false_data, len(datas))
        if not false_data:
            break
    return w

Pocket PLA

import numpy as np

def pocket_pla(datas, limit):
    ###############
    def _calc_false(vec):
        res = 0
        for data in datas:
            t = np.dot(vec, data[0])
            if np.sign(data[1]) != np.sign(t):
                res += 1
        return res
    ###############
    w = np.random.rand(5)
    least_false = _calc_false(w)
    res = w

    for i in xrange(limit):
        data = random.choice(datas)
        t = np.dot(w, data[0])
        if np.sign(data[1]) != np.sign(t):
            t = w + data[1] * data[0]
            t_false = _calc_false(t)

            w = t

            if t_false <= least_false:
                least_false = t_false
                res = t
    return res, least_false

前言

機器學習(Machine Learning)是一門很深的課程,要直接跳進來學習其實並不容易,因此系統性由淺而深的學習過程還是必須的。這一系列部落格文章我將分享我在 Coursera 上臺灣大學林軒田教授所教授的機器學習基石(Machine Learning Foundations)課程整理成的心得,並對照林教授的投影片作說明,希望對有心學習 Machine Learning 的碼農們有些幫助。

範例原始碼:FukuML - 簡單易用的機器學習套件

我在分享機器學習基石課程時,也跟著把每個介紹過的機器學習演算法都實作了一遍,原始碼都放在 GitHub 上了,所以大家可以去參考看看每個演算法的實作細節,看完原始碼會對課程中的數學式更容易理解。

如果大家對實作沒有興趣,只想知道怎麼使用機器學習演算法,那 FukuML 絕對會比起其他機器學習套件簡單易用,且方法及變數都會跟林軒田教授的課程類似,有看過課程的話,說不定連文件都不用看就會使用 FukuML 了。不過我還是有寫 Tutorial 啦,之後會不定期更新,讓大家可以容易上手比較重要!

如何有效學習機器學習

從基礎來由淺入深,包含理論及實作技術用說故事的方式包裝,比如何時可以使用機器學習、為何機器可以學習、機器怎麼學習、如何讓機器學得更好,讓我們可以記得並加以應用。

從人的學習轉換到機器學習

人學習是為了習得一種技能,比如學習辨認男生或女生,而我們可以從觀察中累積經驗而學會辨認男生或女生,這就是人學習的過程,觀察 -> 累積經驗、學習 -> 習得技能;而機器怎麼學習呢?其實有點相似,機器為了學習一種技能,比如一樣是學習辨認男生或女生,電腦可以從觀察資料計算累積模型而學會辨認男生或女生,這就是機器學習的過程,資料 -> 計算、學習出模型 -> 習得技能。

再定義一下什麼是技能

「師爺,翻譯翻譯什麼是他媽的技能」「技能不就是技能嗎」在機器學習上,技能就是透過計算所搜集到的資料來提升一些可量測的性能,比如預測得更準確,實例上像是我們可以搜集股票的交易資料,然後透過機器學習的計算及預測後,是否可以得到更多的投資報酬。如果可以增加預測的準確度,那麼我們就可以說電腦透過機器學習得到了預測股票買賣的技能了。

舉個例子

各位勞苦功高的碼農們,現在老闆心血來潮要你寫一個可以辨識樹的圖片的程式,你會怎麼寫呢?你可能寫一個程式檢查圖片中有沒有綠綠的或是有沒有像葉子的形狀的部份等等,然後寫了幾百條規則來完成辨識樹的圖片的功能,現在功能上線了,好死不死現在來了一張樹的圖片上面剛好都沒有葉子,你寫的幾百條規則都沒用了,辨識樹的圖片的功能只能以失敗收場。機器學習可以解決這樣的問題,透過觀察資料的方式來讓電腦自己辨識樹的圖片,可能會比寫幾百條判斷規則更有效。這有點像是教電腦學會釣魚(透過觀察資料學習),而不是直接給電腦魚吃(直接寫規則給電腦)。

那麼什麼時候可以使用機器學習呢

從上個例子我們可以大概了解使用機器學習的使用時機,大致上如果觀察到現在你想要解決的問題有以下三個現象,應該就是機器學習上場的時刻了:

  1. 存在某種潛在規則
  2. 但沒有很辦法很簡單地用程式直接定義來作邏輯判斷(if else 就可以做到,就不用機器學習)
  3. 這些潛在規則有很多資料可以作為觀察、學習的來源

舉個實際的機器學習例子 1

Netflix 現在出了一個問題,如果你能讓使用者對電影喜好程度星級預測準確率提升 10%,那就可以獲得 100 萬美金,馬上讓你從碼農無產階級晉升到天龍人資產階級,而這個問題是這樣的:他們給了你大量使用者對一些電影的星級評分資料,你必須要讓電腦學到一個技能,這個技能可以預測到使用者對他還沒看過的電影評分會是多少星級,如果電腦能準確預測的話,那某種程度它就有了可以知道使用者會不會喜歡這些電影的技能,進而可以推薦使用者他們會喜歡的電影,讓他們從口袋裡拿錢過來~

舉個實際的機器學習例子 2

這邊偷偷告訴大家一個很常見的機器學習方法的模型,我們再來整理一下,其實這個問題可以轉化成這樣,使用者有很多個會喜歡這部電影的因素,比如電影中有沒有爆破場景、有沒有養眼畫面、有沒有外星人等等,這個我們就稱之為使用者的特徵值(feature),而電影本身也有很多因素,比如電影中有出現炸彈、是很有魅力的史嘉蕾·喬韓森所主演、片名是 ET 第二集等等,這個我們就稱之為電影的特徵值,我們把這兩個特徵值表示成向量(vector),如此如果使用者與電影特徵值有對應的特徵越多,那就代表使用者很有可能喜歡這部電影,而這可以很快地用向量內積的方式計算出來。也就是說,機器學習在這個問題上,只要能學習出這些會影響使用者喜好的因素也就是機器學習所說的特徵值會是什麼,那這樣當一部新的電影出來,我們只要叫電腦對一下這部新電影與使用者的特徵值的對應起來的向量內積值高不高就可以知道使用者會不會喜歡這部電影了~

將剛剛的問題用數學式來描述

我們在用銀行核發信用卡的例子來描述機器學習,我們可以把信用卡申請者的資料想成是 x,而 y 是銀行是否核發信用卡。所以這就是一個函式,它有一個潛在規則,可以讓 x 對應到 y,機器學習就是要算出這個 f 函式是什麼。現在我們有大量的信用卡對申請者核發信用卡的資料,就是 D,我們可以從資料觀察中得到一些假設,然後讓電腦去學習這些假設是對的還是錯的,慢慢習得技能,最後電腦可能會算出一個 g 函式,雖然不是完全跟 f 一樣,但跟 f 很像,所以能夠做出還蠻精確的預測。

機器學習流程

所以機器學習銀行是否核發信用卡的流程就像這樣,我們想要找到 target function,可以完整預測銀行對申請者是否要核發信用卡才會賺錢,這時我們會餵給電腦大量的資料,然後透過學習演算法找出重要的特徵值,這些重要的特徵值就可以組成函式 g,雖然跟 f 不一樣,但很接近。

機器學習的 Model

從上面的學習流程,我們可以知道最後電腦會學習出 g 可以辨認資料中較重要的特徵值,這些特徵值可能是我們一開始觀察資料所整理出來的假設,所以我們餵資料給電腦做學習演算法做計算時,也會餵一些假設進去,以銀行核發信用卡的例子就是這個申請者年薪是否有 80 萬、負債是否有 10 萬、工作是否小於兩年等等假設,這些假設就是 H,學習演算法再去計算實際資料與假設是否吻合,這個演算法就是 A,最後演算法會挑出最好的假設集合是哪些。 H 與 A 我們就稱為是機器學習 Model

機器學習的基本定義

機器學習的基本定義可以用這個圖來概括,用一句話來說的話就是「use data to compute hypothesis g that approximates target f」,你如果問我為何要用英文寫下這句話,其實只是因為這樣看起來比較像是一個偉人大學者寫的啊!拿來唬人用的!

前言

自然語言處理的其中一個重要環節就是中文斷詞的處理,比起英文斷詞,中文斷詞在先天上就比較難處理,比如電腦要怎麼知道「全台大停電」要斷詞成「全台 / 大 / 停電」呢?如果是英文「Power outage all over Taiwan」,就可以直接用空白斷成「Power / outage / all / over / Taiwan」,可見中文斷詞真的是一個大問題啊~

這樣的問題其實已經有很多解法,比如中研院也有提供「中文斷詞系統」,但就是很難用,不僅 API Call 的次數有限制,還很難串,Server 也常常掛掉,真不曉得為何中研院不將核心開源出來,讓大家可以一起來改善這種現象,總之我要棄中研院的斷詞系統而去了。

近來玩了一下 jieba 結巴這個 Python Based 的開源中文斷詞程式,感覺大好,順手發了一些 pull request,今天早上就成為 contributor 了! 感覺真爽!每次發 pull request 總是有種莫名的爽感,既期待被 merge 又怕被 reject,就跟告白的感覺類似啊~

這麼好用的開源中文斷詞系統,當然要介紹給大家用啊!

背後演算法

jieba 中文斷詞所使用的演算法是基於 Trie Tree 結構去生成句子中中文字所有可能成詞的情況,然後使用動態規劃(Dynamic programming)算法來找出最大機率的路徑,這個路徑就是基於詞頻的最大斷詞結果。對於辨識新詞(字典詞庫中不存在的詞)則使用了 HMM 模型(Hidden Markov Model)及 Viterbi 算法來辨識出來。基本上這樣就可以完成具有斷詞功能的程式了,或許我之後可以找個時間寫幾篇部落格來介紹這幾個演算法。

如何安裝

推薦用 pip 安裝 jieba 套件,或者使用 Virtualenv 安裝(未來可能會介紹如何使用 Virtualevn,這樣就可以同時在一台機器上跑不同的 Python 環境):

pip install jieba

基本斷詞用法,使用預設詞庫

Sample Code:

jieba-default-mode.py

#encoding=utf-8
import jieba

sentence = "獨立音樂需要大家一起來推廣,歡迎加入我們的行列!"
print "Input:", sentence
words = jieba.cut(sentence, cut_all=False)
print "Output 精確模式 Full Mode:"
for word in words:
    print word

sentence = "独立音乐需要大家一起来推广,欢迎加入我们的行列!"
print "Input:", sentence
words = jieba.cut(sentence, cut_all=False)
print "Output 精確模式 Full Mode:"
for word in words:
    print word

得到的斷詞結果會是:

獨立 / 音樂 / 需要 / 大家 / 一起 / 來 / 推廣 / , / 歡迎 / 加入 / 我們 / 的 / 行列

独立 / 音乐 / 需要 / 大家 / 一 / 起来 / 推广 / , / 欢迎 / 加入 / 我们 / 的 / 行列

據原作者的說法,使用預設詞庫的話,繁體中文的斷詞結果應該會比較差,畢竟原來的詞庫是簡體中文,但在這個例子中,我感覺是繁體中文的斷詞結果比較好,這應該只是特例,我們接下來試試看中文歌詞的斷詞結果如何。

中文歌詞斷詞,使用預設詞庫

現在我們使用 回聲樂團 - 座右銘 的歌詞作為中文斷詞測試範例,歌詞我們先做成一個純文字檔,內容如下:

lyric.txt

我沒有心
我沒有真實的自我
我只有消瘦的臉孔
所謂軟弱
所謂的順從一向是我
的座右銘

而我
沒有那海洋的寬闊
我只要熱情的撫摸
所謂空洞
所謂不安全感是我
的墓誌銘

而你
是否和我一般怯懦
是否和我一般矯作
和我一般囉唆

而你
是否和我一般退縮
是否和我一般肌迫
一般地困惑

我沒有力
我沒有滿腔的熱火
我只有滿肚的如果
所謂勇氣
所謂的認同感是我
隨便說說

而你
是否和我一般怯懦
是否和我一般矯作
是否對你來說
只是一場遊戲
雖然沒有把握

而你
是否和我一般退縮
是否和我一般肌迫
是否對你來說
只是逼不得已
雖然沒有藉口

Sample Code:

jieba_cut_lyric.py

#encoding=utf-8
import jieba

content = open('lyric.txt', 'rb').read()

print "Input:", content

words = jieba.cut(content, cut_all=False)

print "Output 精確模式 Full Mode:"
for word in words:
    print word

得到的斷詞結果會是:

我 / 沒 / 有心 / 我 / 沒 / 有 / 真實 / 的 / 自我 / 我 / 只有 / 消瘦 / 的 / 臉孔 / 所謂 / 軟弱 / 所謂 / 的 / 順 / 從 / 一向 / 是 / 我 / 的 / 座 / 右銘 / 而 / 我 / 沒有 / 那 / 海洋 / 的 / 寬闊 / 我 / 只要 / 熱情 / 的 / 撫 / 摸 / 所謂 / 空洞 / / 所謂 / 不安全感 / 是 / 我 / 的 / 墓誌 / 銘 / 而 / 你 / 是否 / 和 / 我 / 一般 / 怯懦 / 是否 / 和 / 我 / 一般 / 矯作 / 和 / 我 / 一般 / 囉 / 唆 / 而 / 你 / 是否 / 和 / 我 / 一般 / 退縮 / 是否 / 和 / 我 / 一般 / 肌迫 / 一般 / 地 / 困惑 / 我 / 沒 / 有力 / 我 / 沒 / 有 / 滿腔 / 的 / 熱火 / 我 / 只有 / 滿肚 / 的 / 如果 / 所謂 / 勇氣 / 所謂 / 的 / 認 / 同感 / 是 / 我 / 隨便 / 說 / 說 / 而 / 你 / 是否 / 和 / 我 / 一般 / 怯懦 / 是否 / 和 / 我 / 一般 / 矯作 / 是否 / 對 / 你 / 來 / 說 / 只是 / 一場 / 遊戲 / 雖然 / 沒 / 有把握 / 而 / 你 / 是否 / 和 / 我 / 一般 / 退縮 / 是否 / 和 / 我 / 一般 / 肌迫 / 是否 / 對 / 你 / 來 / 說 / 只是 / 逼不得已 / 雖然 / 沒有 / 藉口

我們可以從結果看出斷詞已經開始出了一些問題,比如「座右銘」被斷成了「座 / 右銘」「墓誌銘」被斷成了「墓誌 / 銘」,這應該就是因為預設詞庫是簡體中文所造成,因此繁體中文的斷詞結果會比較差,還好 jieba 也提供了可以切換詞庫的功能,並提供了一個繁體中文詞庫,所以我們可以使用切換詞庫的功能來改善斷詞結果。

中文歌詞斷詞,使用繁體詞庫

Sample Code:

jieba_cut_lyric_zh.py

#encoding=utf-8
import jieba

jieba.set_dictionary('dict.txt.big')

content = open('lyric.txt', 'rb').read()

print "Input:", content

words = jieba.cut(content, cut_all=False)

print "Output 精確模式 Full Mode:"
for word in words:
    print word

我們在程式中多加一行 jieba.set_dictionary(‘dict.txt.big’),這樣就可以將斷詞詞庫切換到 dic.txt.big 這個檔案。

得到的斷詞結果會是:

我 / 沒有 / 心 / 我 / 沒有 / 真實 / 的 / 自我 / 我 / 只有 / 消瘦 / 的 / 臉孔 / 所謂 / 軟弱 / 所謂 / 的 / 順從 / 一向 / 是 / 我 / 的 / 座右銘 / 而 / 我 / 沒有 / 那 / 海洋 / 的 / 寬闊 / 我 / 只要 / 熱情 / 的 / 撫摸 / 所謂 / 空洞 / 所謂 / 不安全感 / 是 / 我 / 的 / 墓誌銘 / 而 / 你 / 是否 / 和 / 我 / 一般 / 怯懦 / 是否 / 和 / 我 / 一般 / 矯作 / 和 / 我 / 一般 / 囉唆 / 而 / 你 / 是否 / 和 / 我 / 一般 / 退縮 / 是否 / 和 / 我 / 一般 / 肌迫 / 一般 / 地 / 困惑 / 我 / 沒有 / 力 / 我 / 沒有 / 滿腔 / 的 / 熱火 / 我 / 只有 / 滿肚 / 的 / 如果 / 所謂 / 勇氣 / 所謂 / 的 / 認同感 / 是 / 我 / 隨便說說 / 而 / 你 / 是否 / 和 / 我 / 一般 / 怯懦 / 是否 / 和 / 我 / 一般 / 矯作 / 是否 / 對 / 你 / 來說 / 只是 / 一場 / 遊戲 / 雖然 / 沒有 / 把握 / 而 / 你 / 是否 / 和 / 我 / 一般 / 退縮 / 是否 / 和 / 我 / 一般 / 肌迫 / 是否 / 對 / 你 / 來說 / 只是 / 逼不得已 / 雖然 / 沒有 / 藉口

我們可以看到「座右銘」成功斷成「座右銘」了!「墓誌銘」也成功斷成「墓誌銘」了!果然切換成繁體中文詞庫還是有用的!

台語歌詞斷詞,使用繁體詞庫

既然中文歌詞斷詞能夠得到不錯的斷詞結果了,那我們來試試看台語歌詞斷詞會是如何?在這邊我們使用 滅火器 - 島嶼天光 的歌詞作為台語斷詞測試範例,歌詞我們先做成一個純文字檔,內容如下:

lyric_tw.txt

親愛的媽媽
請你毋通煩惱我
原諒我
行袂開跤
我欲去對抗袂當原諒的人

歹勢啦
愛人啊
袂當陪你去看電影
原諒我
行袂開跤
我欲去對抗欺負咱的人

天色漸漸光
遮有一陣人
為了守護咱的夢
成做更加勇敢的人

天色漸漸光
已經不再驚惶
現在就是彼一工
換阮做守護恁的人

已經袂記
是第幾工
請毋通煩惱我
因為阮知道
無行過寒冬
袂有花開的一工

天色漸漸光
天色漸漸光
已經是更加勇敢的人

天色漸漸光
咱就大聲來唱著歌
一直到希望的光線
照光島嶼每一個人

天色漸漸光
咱就大聲來唱著歌
日頭一爬上山
就會使轉去啦
現在是彼一工
勇敢的台灣人

Sample Code:

jieba_cut_lyric_zh_tw.py

#encoding=utf-8
import jieba

jieba.set_dictionary('dict.txt.big')

content = open('lyric_tw.txt', 'rb').read()

print "Input:", content

words = jieba.cut(content, cut_all=False)

print "Output 精確模式 Full Mode:"
for word in words:
    print word

得到的斷詞結果會是:

親愛 / 的 / 媽媽 / 請 / 你 / 毋通 / 煩惱 / 我 / 原諒 / 我 / 行袂 / 開跤 / 我 / 欲 / 去 / 對抗 / 袂 / 當 / 原諒 / 的 / 人 / 歹勢 / 啦 / 愛人 / 啊 / 袂 / 當 / 陪你去 / 看 / 電影 / 原諒 / 我 / 行袂 / 開跤 / 我 / 欲 / 去 / 對抗 / 欺負 / 咱 / 的 / 人 / 天色 / 漸漸 / 光 / 遮有 / 一陣 / 人 / 為 / 了 / 守護 / 咱 / 的 / 夢 / 成 / 做 / 更加 / 勇敢的人 / 天色 / 漸漸 / 光 / 已經 / 不再 / 驚惶 / 現在 / 就是 / 彼一工 / 換阮 / 做 / 守護 / 恁 / 的 / 人 / 已經 / 袂 / 記 / 是 / 第幾 / 工 / 請 / 毋通 / 煩惱 / 我 / 因為 / 阮 / 知道 / 無行過 / 寒冬 / 袂 / 有 / 花開 / 的 / 一工 / 天色 / 漸漸 / 光 / 天色 / 漸漸 / 光 / 已經 / 是 / 更加 / 勇敢的人 / 天色 / 漸漸 / 光 / 咱 / 就 / 大聲 / 來 / 唱 / 著歌 / 一直 / 到 / 希望 / 的 / 光線 / 照光 / 島嶼 / 每 / 一個 / 人 / 天色 / 漸漸 / 光 / 咱 / 就 / 大聲 / 來 / 唱 / 著歌 / 日頭 / 一爬 / 上山 / 就 / 會 / 使 / 轉去 / 啦 / 現在 / 是 / 彼 / 一工 / 勇敢 / 的 / 台灣 / 人

原本猜想結果應該會蠻差的,畢竟詞庫中沒有台語的用詞,但是因為 HMM 的關係猜出了一些新詞,讓我們還是得到不錯的結果,「袂當」斷成了「袂」「當」「袂記」斷成了「袂」「記」「袂有」斷成了「袂」「有」等等,我們要如何改善這些結果呢?

jieba 提供了一個功能讓使用者可以增加自定義詞庫,這種無法用 HMM 判斷出來的新詞就可以得到改善,我們就來試試看吧!

台語歌詞斷詞,使用繁體詞庫加自定義詞庫

首先我們新增一個純文字檔建立自定義詞庫,格式如下:

userdict.txt

行袂開跤 2 v
袂當 4 d
袂記 4 v
袂有 4 d
唱著 4 v
每一個 4 m
會使 70 d

其中每一行代表一筆語料資料,首先填上自定義詞如:「袂當」「袂記」,然後填上權重,權重值可以依照斷詞結果做自己想做的調整,最後填上詞性,但詞性非必要填寫,詞性列表可以參考 词性对照说明.中科院版本

Sample Code:

jieba_cut_lyric_zh_tw_custom.py

#encoding=utf-8
import jieba

jieba.set_dictionary('dict.txt.big')
jieba.load_userdict("userdict.txt")

content = open('lyric_tw.txt', 'rb').read()

print "Input:", content

words = jieba.cut(content, cut_all=False)

print "Output 精確模式 Full Mode:"
for word in words:
    print word

我們在程式中多加一行 jieba.load_userdict(“userdict.txt”),這樣就可以將自定義詞庫加進來了,超級簡單的。

得到的斷詞結果會是:

親愛 / 的 / 媽媽 / 請 / 你 / 毋通 / 煩惱 / 我 / 原諒 / 我 / 行袂開跤 / 我 / 欲 / 去 / 對抗 / 袂當 / 原諒 / 的 / 人 / 歹勢 / 啦 / 愛人 / 啊 / 袂當 / 陪你去 / 看 / 電影 / 原諒 / 我 / 行袂開跤 / 我 / 欲 / 去 / 對抗 / 欺負 / 咱 / 的 / 人 / 天色 / 漸漸 / 光 / 遮有 / 一陣 / 人 / 為 / 了 / 守護 / 咱 / 的 / 夢 / 成 / 做 / 更加 / 勇敢的人 / 天色 / 漸漸 / 光 / 已經 / 不再 / 驚惶 / 現在 / 就是 / 彼一工 / 換阮 / 做 / 守護 / 恁 / 的 / 人 / 已經 / 袂記 / 是 / 第幾 / 工 / 請 / 毋通 / 煩惱 / 我 / 因為 / 阮 / 知道 / 無行過 / 寒冬 / 袂有 / 花開 / 的 / 一工 / 天色 / 漸漸 / 光 / 天色 / 漸漸 / 光 / 已經 / 是 / 更加 / 勇敢的人 / 天色 / 漸漸 / 光 / 咱 / 就 / 大聲 / 來 / 唱著 / 歌 / 一直 / 到 / 希望 / 的 / 光線 / 照光 / 島嶼 / 每 / 一個 / 人 / 天色 / 漸漸 / 光 / 咱 / 就 / 大聲 / 來 / 唱著 / 歌 / 日頭 / 一爬 / 上山 / 就 / 會使 / 轉去 / 啦 / 現在 / 是 / 彼 / 一工 / 勇敢 / 的 / 台灣 / 人

完美!

取出斷詞詞性

大部份的斷詞系統都可以列出斷詞的詞性,jieba 也有這個功能,但結果可能不是那麼好,這其實是跟所使用的語料庫有關係,不過既然是 Open Source,希望未來能有語言學家可以加入,讓 jieba 可以得到更好的效果。

Sample Code:

jieba_cut_lyric_zh_flag.py

#encoding=utf-8
import jieba
import jieba.posseg as pseg

jieba.set_dictionary('dict.txt.big')

content = open('lyric.txt', 'rb').read()

print "Input:", content

words = pseg.cut(content)

print "Output 精確模式 Full Mode:"
for word in words:
    print word.word, word.flag

得到的結果會是:

我 r
沒有 x
心 n

我 r
沒有 x
真實 x
的 uj
自我 r

...

取出斷詞位置

有時我們會需要得到斷詞在文章中的位置:

Sample Code:

jieba_cut_lyric_zh_tokenize.py

#encoding=utf-8
import jieba

jieba.set_dictionary('dict.txt.big')

content = open('lyric.txt', 'rb').read()

print "Input:", content

words = jieba.tokenize(unicode(content, 'utf-8'))

print "Output 精確模式 Full Mode:"
for tk in words:
    print "word %s\t\t start: %d \t\t end:%d" % (tk[0],tk[1],tk[2])

得到的結果會是:

word 我 start: 0 end:1
word 沒有 start: 1 end:3
word 心 start: 3 end:4
word start: 4 end:5
word 我 start: 5 end:6
word 沒有 start: 6 end:8
word 真實 start: 8 end:10
word 的 start: 10 end:11
word 自我 start: 11 end:13

...

取出文章中的關鍵詞

jieba 使用了 tf-idf 方法來實作萃取出文章中關鍵詞的功能:

Sample Code:

jieba_cut_lyric_zh_keyword.py

#encoding=utf-8
import jieba
import jieba.analyse

jieba.set_dictionary('dict.txt.big')

content = open('lyric.txt', 'rb').read()

print "Input:", content

tags = jieba.analyse.extract_tags(content, 10)

print "Output:"
print ",".join(tags)

程式中的 jieba.analyse.extract_tags(content, 10),就是告訴 jieba 我們要從這個文章中取出前 10 個 tf-idf 值最大的關鍵詞。

得到的結果會是:

沒有,所謂,是否,一般,雖然,退縮,肌迫,矯作,來說,怯懦

一開始使用這個功能的時候,會不知道 jieba 的 idf 值是從哪裡來的,看了一下 souce code 才知道原來 jieba 有提供一個 idf 的語料庫,但在實務上每個人所使用的語料庫可能會不太一樣,有時我們會想要使用自己的idf 語料庫,stop words 的語料庫也可能會想換成自己的,比如目前的結果中,最重要的「座右銘」並沒有出現在關鍵詞裡,我就會想要將「座右銘」加到 idf 語料庫,並讓 idf 值高一點,而「沒有」這個關鍵詞對我來說是沒有用的,我就會想把它加到 stop words 語料庫,這樣「沒有」就不會出現在關鍵詞裡。

可惜目前 pip 安裝的 jieba 版本並不能切換 idf 及 stop words 語料庫,所以我才會修改了一下 jieba,讓它可以支援 idf 及 stop words 語料庫的切換,目前在 github 上的版本已經可以支援 idf 及 stop words 切換的功能了!

結語

使用了 jieba 之後,其實有蠻深的感嘆,其實中研院的斷詞核心必非不好,想要收費也不是問題,但是 API 做得這麼差,根本就沒人有信心敢花錢下去使用這樣不可靠的系統,目前又有 jieba 這樣的 open source project,中研院的斷詞系統前途堪慮啊!

後記 2017/07/18

我後來有好幾次受邀演講關於中文斷詞的講題,相關的投影片都有分享在網路上了,大家也可以交叉參考:中文斷詞:斷句不要悲劇 / Head first Chinese text segmentation 投影片範例程式碼

Fukuball

我是林志傑,網路上常用的名字是 Fukuball。我使用 PHP 及 Python,對機器學習及區塊鏈技術感到興趣。 https://www.fukuball.com

Co-Founder / Head of Engineering at OurSong

Taipei, Taiwan