# Lesson 11 | 機器學習概論 ## 第一節:機器學習概論(1) * 從這節課開始,我們將踏入機器學習的領域。機器學習可以用過往的資料建立模型,在未來拿來使用,我們將由簡單至困難,並跟著科學發展的歷程一起學習這些方法。 * 機器學習的方法分為無監督學習法及監督學習法,我們的課程內容著重在監督學習法,其主要目標是想透過原始資料的特徵(feature),並經過模型進行預測(預測的outcome可以為連續或是類別)。 我們已經知道,若我們希望準確的預測一件事情的發生,那麼最準確的方式是了解了變量之間的關係,並設計出優美的關係方程式,如此便是最準確的方法。 * 然而,有時候受限於研究的限制,我們沒有辦法將它們之間的關係描述得很精準,舉例來說,我們如何做手寫數字的辨識? ![L11-1](https://i.imgur.com/gsPDoEO.png) ## 第一節:機器學習概論(2) * 我們以一個經典的鳶尾花卉資料集來練習,這是一個多分類問題,假定每個樣本有2個特徵,因此每個點能打在座標平面上,3個類別分別以不同顏色標記: ![L11-3](https://i.imgur.com/iPjB1oJ.png) ![L11-2](https://i.imgur.com/ifHfFww.png) ```python= from sklearn.datasets import load_iris data = load_iris() data.target[[10, 25, 50]] print(data['DESCR']) list(data.target_names) ``` * 那麼我們舉常見的4種機器學習方法(決策樹/邏輯斯回歸/支持向量機/隨機森林)對上面的圖形分類,結果會大致如下: ![L11-4](https://i.imgur.com/Z2Ebaih.png) * 根據我們的觀察,可以發現邏輯斯回歸的致命缺陷,也就是他僅能處理『線性可分割』的問題,如果問題是『線性不可分割』,那邏輯斯回歸將無法非常好的進行分類。 ## 第一節:機器學習概論(3) * 儘管如此,我們還是先從邏輯斯回歸開始,我們可以在```sklearn```裡面找到此演算法: ![L11-5](https://i.imgur.com/ELsDg1g.png) * 在[這裡](https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression)可以找到API的說明。而以下是切分訓練集(train)以及測試集(test)、訓練模型以及未來預測運用的範例。 ```python= import random import numpy as np from sklearn.datasets import load_iris from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix data = load_iris() # Split Dataset train_idx = np.array(random.sample(range(150), 100)) train_x = data['data'][np.isin(range(150), train_idx) == True, 0:2] train_y = data['target'][np.isin(range(150), train_idx) == True] test_x = data['data'][np.isin(range(150), train_idx) == False, 0:2] test_y = data['target'][np.isin(range(150), train_idx) == False] # Fit Data logreg = LogisticRegression(penalty = None, multi_class = 'multinomial') logreg.fit(train_x, train_y) pred_test = logreg.predict(test_x) result = confusion_matrix(test_y, pred_test) print(result) ``` * 用邏輯斯迴歸準嗎? ```python= correct = list() for i in range(3): correct.append(result[i,i]) print("accuracy", np.sum(correct)/np.sum(result)) ``` ## 練習1 * 讓我們來面對一個複雜一點的線性不可分割的問題,請你再試試邏輯斯回歸的效果: * 請在[這裡](https://linchin.ndmctsgh.edu.tw/data/MNIST.csv)下載[MNIST](https://www.kaggle.com/c/digit-recognizer)的手寫數字資料。 * 這份資料是Kaggle上面讓世界各地的好手們比賽他們如何做手寫數字辨識,資料結構為一個785欄的資料集。 * 其中第一欄是標籤,也就是 0 至 9 * 第二欄開始是一個 28 x 28 圖片的像素,範圍大小是 0 - 255 * 我們試著讀取檔案,並且利用他來呈現幾個手寫數字吧! ```python= import random import numpy as np import pandas as pd data = pd.read_csv('MNIST.csv', header = None) # Split Dataset train_idx = np.array(random.sample(range(42000), 35000)) train_x = data[list(range(1, 785))][np.isin(range(42000), train_idx) == True] train_y = data[0][np.isin(range(42000), train_idx) == True] test_x = data[list(range(1, 785))][np.isin(range(42000), train_idx) == False] test_y = data[0][np.isin(range(42000), train_idx) == False] ``` * 視覺化呈現部分資料。 ```python= import matplotlib.pyplot as plt # Show image sub_x_plot = train_x.to_numpy() sub_x_plot = sub_x_plot.reshape((35000, 28, 28), order='F') fig, ax = plt.subplots(2, 2) ax[0, 0].imshow(sub_x_plot[7, :, :]) ax[0, 0].text(0, 2.5, train_y.tolist()[7], c="white") ax[0, 1].imshow(sub_x_plot[126, :, :]) ax[0, 1].text(0, 2.5, train_y.tolist()[126], c="white") ax[1, 0].imshow(sub_x_plot[247, :, :]) ax[1, 0].text(0, 2.5, train_y.tolist()[247], c="white") ax[1, 1].imshow(sub_x_plot[871, :, :]) ax[1, 1].text(0, 2.5, train_y.tolist()[871], c="white") ``` <details> <summary>解答</summary> * 這題其實需要處裡的地方比較少。 ```python= from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix logreg = LogisticRegression(penalty = None, multi_class = 'multinomial') logreg.fit(train_x, train_y) pred_test = logreg.predict(test_x) result = confusion_matrix(test_y, pred_test) ``` * 一樣我們看一下準確度,你也可以看一下在訓練集上面的準確度。 ```python= correct = list() for i in range(10): correct.append(result[i,i]) print("accuracy", np.sum(correct)/np.sum(result)) ``` </details> ## 第二節:決策樹(1) * 邏輯斯迴歸由於僅能處理「線性可分割」的問題,故可能效果不太好,我們需要學習其他工具來處理「線性不可分割」問題。 * 最簡單的監督機器學習法是決策樹,決策樹牽涉到了最少的數學,僅僅是使用電腦大量運算來進行分類。 * 決策樹的原理是,在大量的變項(X)中找出「一個」能對「該樣本」有「最大分類效果」的規則,並按規則持續把樣本分割,直到再也找不出規則為止! ![L11-9](https://i.imgur.com/YbRYmKA.png) ![L11-6](https://i.imgur.com/HTqhKFC.png) ## 第二節:決策樹(2) * 事實上決策樹因為對於「最大分類效果」的定義有相當多種,所以有非常多不同種類的決策樹,在這裡,讓我們來試著用iris的資料實作決策樹吧,函式預設為「gini」: ![L11-7](https://i.imgur.com/godnpix.png) ```python= from sklearn.datasets import load_iris from sklearn.tree import plot_tree from sklearn.tree import DecisionTreeClassifier data = load_iris() # Split Dataset train_idx = np.array(random.sample(range(150), 100)) train_x = data['data'][np.isin(range(150), train_idx) == True, 0:2] train_y = data['target'][np.isin(range(150), train_idx) == True] test_x = data['data'][np.isin(range(150), train_idx) == False, 0:2] test_y = data['target'][np.isin(range(150), train_idx) == False] plt.figure() # DecisionTreeClassifier(max_depth = 2) clf = DecisionTreeClassifier().fit(train_x, train_y) plot_tree(clf, filled=True) plt.title("Decision tree trained on all the iris features") plt.show() ``` ## 第二節:決策樹(3) * 接著我們來利用這個決策樹模型做預測,來看看結果: ```python= pred_test = clf.predict(test_x) result = confusion_matrix(test_y, pred_test) print(result) ``` ```python= correct = list() for i in range(3): correct.append(result[i,i]) print("accuracy", np.sum(correct)/np.sum(result)) ``` * 有沒有比邏輯斯迴歸準呢?我們試試他在手寫數字分類的效果吧! ## 練習2 * 讓我們試試看決策樹是否能更好的預測手寫數字呢? * 想想決策樹的運算過程,你應該可以想像他要一陣子才能算完。 ```python= import random import numpy as np import pandas as pd data = pd.read_csv('MNIST.csv', header = None) # Split Dataset train_idx = np.array(random.sample(range(42000), 35000)) train_x = data[list(range(1, 785))][np.isin(range(42000), train_idx) == True] train_y = data[0][np.isin(range(42000), train_idx) == True] test_x = data[list(range(1, 785))][np.isin(range(42000), train_idx) == False] test_y = data[0][np.isin(range(42000), train_idx) == False] ``` <details> <summary>解答</summary> ```python= from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import confusion_matrix clf = DecisionTreeClassifier().fit(train_x, train_y) pred_test = clf.predict(test_x) result = confusion_matrix(test_y, pred_test) correct = list() for i in range(10): correct.append(result[i,i]) print("accuracy", np.sum(correct)/np.sum(result)) ``` </details> ## 第三節:隨機森林(1) * 決策樹確實有能力解決線性不可分割問題,但他有一個重要的缺陷,就是我們Training sample是一次抽樣的結果,而這個樣本一定存在了一些抽樣誤差造成的特色與母體未必相同,我們有沒有可能因為這個錯誤的特色導致了決策樹陷入「局部極值」? * 當然是有可能的,所以學者們想出了隨機森林來解決這個問題,他的做法是把原來的樣本「隨機的」抽取一小部分當新樣本,並依此新樣本作出一棵決策樹,然後再重複抽樣產生第二棵樹,依此類推… * 接著,假設我們成功的製造了100棵小樹,要預測的時候就將這樣本經過這100棵樹,並請這100棵樹投票看看他們預測這個樣本的結果。 ![L11-10](https://i.imgur.com/yJA9cQh.png) ## 第三節:隨機森林(2) * 讓我們來試試使用套件完成隨機森林樹吧,但因為要產生很多小樹運算量驚人,我們僅設置20棵樹。 ```python= from sklearn.datasets import load_iris from sklearn.ensemble import RandomForestClassifier data = load_iris() # Split Dataset train_idx = np.array(random.sample(range(150), 100)) train_x = data['data'][np.isin(range(150), train_idx) == True, 0:2] train_y = data['target'][np.isin(range(150), train_idx) == True] test_x = data['data'][np.isin(range(150), train_idx) == False, 0:2] test_y = data['target'][np.isin(range(150), train_idx) == False] model = RandomForestClassifier(n_estimators=20, verbose = 10) model.fit(train_x, train_y) ``` * 一樣讓我們看一下,比較複雜的模型有比較準嗎? ```python= pred_test = model.predict(test_x) result = confusion_matrix(test_y, pred_test) correct = list() for i in range(3): correct.append(result[i,i]) print("accuracy", np.sum(correct)/np.sum(result)) ``` ## 練習3 * 把隨機森林應用在手寫數字分類上吧,但因為運算量的問題,請你設置20棵樹就好了。 * 僅僅20棵樹大約就花了2-3分鐘的時間計算,回家以後你可以設置多一點樹看看結果! ```python= import random import numpy as np import pandas as pd data = pd.read_csv('MNIST.csv', header = None) # Split Dataset train_idx = np.array(random.sample(range(42000), 35000)) train_x = data[list(range(1, 785))][np.isin(range(42000), train_idx) == True] train_y = data[0][np.isin(range(42000), train_idx) == True] test_x = data[list(range(1, 785))][np.isin(range(42000), train_idx) == False] test_y = data[0][np.isin(range(42000), train_idx) == False] ``` <details> <summary>解答</summary> ```python= from sklearn.ensemble import RandomForestClassifier model = RandomForestClassifier(n_estimators=20, verbose = 10) model.fit(train_x, train_y) ``` ```python= from sklearn.metrics import confusion_matrix pred_test = model.predict(test_x) result = confusion_matrix(test_y, pred_test) correct = list() for i in range(10): correct.append(result[i,i]) print("accuracy", np.sum(correct)/np.sum(result)) ``` * 是否感受到隨機森林樹的威力?準確度真是有驚人的提升,在測試資料上已經有95%的準確度了。 </details> ## 總結 * 本週我們一起使用了決策樹及隨機森林,在面對較複雜的問題時有機會超過邏輯斯回歸。 * 儘管我們不清楚X與Y實際上的關係,這兩個機器學習的方法還是有效的協助我們預測。 * 事實上,隨機森林還並非Tree-based model的最終型態,在2003年以後出現的梯度提升機(Gradient Boosting Machine)是一個比隨機森林更強的分類器 ![L11-11](https://i.imgur.com/yPe2NqX.png) * 在Kaggle比賽中,使用xgboost建立的梯度提升機是各項賽事中的常勝軍,幾乎所有的比賽中都看的到他的身影。 * 他的概念是在建構樹的過程中改變樣本權重,使每一棵樹的變化性更大。