## 情境
你是一間公司產線的品管工程師,老闆想透過影像的判斷,來精簡 QC 部門的編制。現在產線都已經佈署好 camera 了,傳到伺服器上的,是一張又一張的影像檔。你該怎麼運用機器學習的知識,做簡單的分類呢? (PASS/NG ?)
## Scikit-learn
Scikit-Learn 為 Python 的一個開源的機器學習框架,基本上是用 CPU 訓練模型的,算是相當適合入門的機器學習框架。當然效能上不會比 GPU 訓練的好。
## 安裝 Sklearn
```bash=
$ pip3 install scikit-learn
```
<kbd>![image.png](https://hackmd.io/_uploads/rJqRiqz7a.png)
</kbd>
## 預先知識
還記得我們在「機器學習」的章節裡面,學到的機器學習套件工作流程嗎?
<kbd>![image.png](https://hackmd.io/_uploads/SJLBu3fmT.png)</kbd>
## 資料集
scikit-learn 附帶了一些標準資料集,例如用於分類的鳶尾花和數字資料集,以及用於迴歸的糖尿病資料集。可以省去我們蒐集資料的工作。
<kbd>![image.png](https://hackmd.io/_uploads/SJLBu3fmT.png)</kbd>
## 範例: Iris 鳶尾花
#### 描述:
鳶尾花資料集是非常著名的生物資訊資料集之一,取自美國加州大學歐文分校的機器學習資料庫 ( http://archive.ics.uci.edu/ml/datasets/Iris )
資料的筆數為150筆,共有五個欄位:
1. 花萼長度 (Sepal Length) :計算單位是公分。
2. 花萼寬度 (Sepal Width) :計算單位是公分。
3. 花瓣長度 (Petal Length) :計算單位是公分。
4. 花瓣寬度 (Petal Width) :計算單位是公分。
5. 類別 (Class):可分為 Setosa,Versicolor 和 Virginica 三個品種。
<kbd>![image.png](https://hackmd.io/_uploads/BJEg2szQ6.png)
</kbd>
<kbd>![image.png](https://hackmd.io/_uploads/BkGkb2G76.png)
</kbd>
( 圖片來源: http://articles.concreteinteractive.com/ )
我們將以 Python 來示範,載入一個 Iris 鳶尾花資料集。
#### 示範:
```python=
from sklearn import datasets
iris = datasets.load_iris()
print(iris)
```
#### 範例輸出:
<kbd>![image.png](https://hackmd.io/_uploads/rJS51nGXT.png)
</kbd>
#### 輸出解說:
- 輸出格式類似字典 ( dict )
- key 有
- data, target, target name, DESCR, feature_names
- DESCR : 為該資料的描述文字
- data : 資料內容
- feature_names : 欄位名稱
- target : 目標 ( 你希望模型預測的數據 )
- target_name : 目標標籤
## 資料集結構解析
#### DESCR:
描述
#### Data:
這些資料庫的值,你可以看到 N 個陣列,每個陣列有 4 個值,這些值代表什麼,請看 feature_names。
[[ 5.1 3.5 1.4 0.2] ... ]
#### feature_names:
```
['sepal length 花萼長度 (cm)', 'sepal width 花萼寬度 (cm)', 'petal length 花蕊長度 (cm)', 'petal width 花蕊寬度 (cm)']
```
#### target:
這些資料被分為 3 大類:0, 1, 2
```
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2]
```
#### target_name:
分類名稱。
['setosa' 'versicolor' 'virginica']
## Wrokshop: Scikit-learn 資料結構
#### 題目:
我們已經知道,Scikit-learn 的資料集類似一個 dict 的格式。我們能不能,將這個格式,轉為我們更容易閱讀的 Pandas DataFrame 呢?試試看!
#### 預期輸出:
```bash=
sepal length (cm) sepal width (cm) petal length (cm) petal width (cm)
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
3 4.6 3.1 1.5 0.2
4 5.0 3.6 1.4 0.2
.. ... ... ... ...
145 6.7 3.0 5.2 2.3
146 6.3 2.5 5.0 1.9
147 6.5 3.0 5.2 2.0
148 6.2 3.4 5.4 2.3
149 5.9 3.0 5.1 1.8
[150 rows x 4 columns]
```
#### 解答:
- 解答已隱藏
<!--
```python=
import pandas as pd
from sklearn import datasets
iris = datasets.load_iris()
x = pd.DataFrame(iris["data"], columns=iris["feature_names"])
print(x)
```
-->
## 切割資料
#### 情境:
Scikit-learn 幫我們預存了很多好用的資料集,現在,我們有一個「切割資料」的需求,我們想將資料切割,分為:
- Training data (訓練資料): 用來做模型訓練
- Test data(測試資料): 用來驗證訓練好的模型
<kbd>![image.png](https://hackmd.io/_uploads/SJLBu3fmT.png)</kbd>
我們便可以透過 Scikit-Learn 的 train_test_split() 這個函式來做到簡單的資料分割。
#### API reference:
- [sklearn.model_selection.train_test_split](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html)
#### 參數解說:
- arrays : 原始的資料,就如同之前描述的 data 一般,是我們打算切成 Training data 以及 Test data 的原始資料
- test_size : 測試用資料的 size
- train_size : 訓練用資料的 size
- random_state : 亂數種子,可以固定我們切割資料的結果
- shuffle : 分割前是否對資料進行打亂 ( True/False )
- stratify : 分割後的訓練集和測試集中的類別分佈與原始數據集中的類別分佈相似 ( True/False )
- 類別分佈相似: 取樣比例是否相同,例如鳶尾花長花瓣、短花瓣比例
- Returns : list, length=2 * len(arrays) ( 2 倍 array input)
#### API 範例:
```python=
from sklearn.model_selection import train_test_split
data = [n for n in range(1, 11)]
print(data)
train_data, test_data = train_test_split(data, random_state=777, train_size=0.8)
print(train_data)
print(test_data)
```
#### API 範例輸出:
```bash=
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[9, 4, 5, 6, 2, 10, 7, 8]
[3, 1]
```
#### 範例:
1. 鳶尾花
2. 測試集
3. 訓練集
4. 訓練資料大小為 70%
```python=
# X_train - 訓練用 raw_iris["data"]
# X_test - 訓練用 raw_iris["target"]
# y_train - 測試用 raw_iris["data"]
# y_test - 測試用 raw_iris["target"]
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris["data"], iris["target"], test_size=0.3)
print("X_train = " + str(X_train))
print("X_test = " + str(X_test))
```
## 選用演算法
#### 情境:
現在,你已經會載入 Scikit-learn 提供給你的資料集,也會做簡單的分割了。既然如此,我們先選好演算法,來訓練模型吧!
#### 演算法地圖:
Scikit-learn 提供相當多樣的演算法來讓我們訓練模型,通常,我們會根據下面的心智圖,來決定要用那一個演算法!
1. 樣本資料是否大於 50 筆
2. 是否為分類問題
3. 是否有標籤好的資料
4. 樣本資料是否小於 100K
5. 是:選擇 Linear SVD 模型
6. 是否是文字資料
- 英文心智圖
<kbd>![](https://i.imgur.com/SpmGajV.png)</kbd>
- 中文心智圖
<kbd>![](https://i.imgur.com/y33wf3f.png)</kbd>
#### API reference:
- 我們示範其中一種,並且闡述怎麼使用
- 如果您在心智圖中,找到若干其他種
- 請參考同樣的方法查找 API reference
- API
- [sklearn.neighbors.KNeighborsClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html)
#### 參數解說:
- n_neighbors : kneighbors 查詢所使用的鄰居數。(default = 5)
- weights: 權重 (default = uniform (均等))
- algorithm : 演算法 (default = auto)
- leaf_size : 構造的 kd 樹和 ball 樹的大小
- p : 距離度量公式 (default = 2)
- metric : 字串或可調用 (default = minkowski)
- metric_params : 其他距離參數 (default = None)
- n_jobs : 臨近點搜尋並行工作數 (default = None)
#### 範例:
```python=
knn = KNeighborsClassifier()
```
## 訓練模型
#### 情境:
選用了某個演算法後(這裡以 KNN 為例子),你就可以使用這個演算法了!
#### API reference:
- [fit](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html#sklearn.neighbors.KNeighborsClassifier.fit)
#### 參數解說:
- X : Training data.
- y : Target values
- Return : The fitted k-nearest neighbors classifier.
#### 範例:
```python=
from sklearn.neighbors import KNeighborsClassifier
X_train, X_test, y_train, y_test = train_test_split(raw_iris["data"], raw_iris["target"], test_size=0.3)
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
```
## 預測
#### 情境:
訓練好了,就可以根據預測所提供資料的類別標籤。
#### API Reference:
- [predict](https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html#sklearn.neighbors.KNeighborsClassifier.predict)
#### 參數解說:
- X : Test samples
- Return : Class labels for each data sample
#### 範例:
```python=
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
print(knn.predict(X_test))
```
## Workshop: 鳶尾花預測
#### 情境:
在這裡,我們將資料 (data) 與分類 (target) 區分開來,以下例子為,將測試資料 (資料庫中分類好的) 擷取任意的 30%,其餘的 70% 為需要 training 的資料。
之後我們會
1. 打亂 70% 的資料
2. 讓 70% 資料重新學習
3. 然後拿來與 30% 的資料比對正確性
#### 提示:
- 演算法心智圖
<kbd>![image.png](https://hackmd.io/_uploads/HJRhkQYm6.png)
</kbd>
#### 預期輸出:
- 0, 1, 2 代表預測分類
```bash=
knn.predict:
[1 0 2 2 0 0 2 0 1 0 0 0 2 1 2 0 0 1 1 0 2 2 2 1 1 0 2 0 1 2 0 0 0 1 1 0 2
0 0 1 0 1 2 0 2]
y_test:
[1 0 2 2 0 0 2 0 1 0 0 0 2 1 2 0 0 1 1 0 1 2 2 1 1 0 2 0 1 2 0 0 0 1 1 0 2
0 0 1 0 1 2 0 1]
```
#### 解答:
- 解答已經隱藏
<!--
```python=
# X_train - 訓練用 raw_iris["data"]
# X_test - 測試用 raw_iris["target"]
# y_train - 訓練用 raw_iris["data"]
# y_test - 測試用 raw_iris["target"]
X_train, X_test, y_train, y_test = train_test_split(raw_iris["data"], raw_iris["target"], test_size=0.3)
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)
print(knn.predict(X_test))
print(y_test)
```
-->