---
# System prepended metadata

title: Lesson 11 | 機器學習概論

---

# 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建立的梯度提升機是各項賽事中的常勝軍，幾乎所有的比賽中都看的到他的身影。

    * 他的概念是在建構樹的過程中改變樣本權重，使每一棵樹的變化性更大。
