初學者的機器學習入門實戰教程!

spearhead_cai發表於2019-03-22

原文連結:www.pyimagesearch.com/2019/01/14/…

作者:Adrian Rosebrock

這是一篇手把手教你使用 Python 實現機器學習演算法,並在數值型資料和影像資料集上執行模型的入門教程,當你看完本文後,你應當可以開始你的機器學習之旅了!

本教程會採用下述兩個庫來實現機器學習演算法:

  • scikit-learn
  • Keras

此外,你還將學習到:

  • 評估你的問題
  • 準備資料(原始資料、特徵提取、特徵工程等等)
  • 檢查各種機器學習演算法
  • 檢驗實驗結果
  • 深入瞭解效能最好的演算法

在本文會用到的機器學習演算法包括:

  1. KNN
  2. 樸素貝葉斯
  3. 邏輯迴歸
  4. SVM
  5. 決策樹
  6. 隨機森林
  7. 感知機
  8. 多層前向網路
  9. CNNs

安裝必備的 Python 機器學習庫

開始本教程前,需要先確保安裝了一下的 Python 庫:

  • Numpy:用於 Python 的數值處理
  • PIL:一個簡單的影像處理庫
  • scikit-learn:包含多種機器學習演算法(注意需要採用 0.2+ 的版本,所以下方安裝命令需要加上 --upgrade )
  • Kears 和 TensorFlow:用於深度學習。本教程可以僅採用 CPU 版本的 TensorFlow
  • OpenCV:本教程並不會採用到 OpenCV,但 imutils 庫依賴它;
  • imutils :作者的影像處理/計算機視覺庫

安裝命令如下,推薦採用虛擬環境(比如利用 anaconda 建立一個新的環境):

$ pip install numpy
$ pip install pillow
$ pip install --upgrade scikit-learn
$ pip install tensorflow # or tensorflow-gpu
$ pip install keras
$ pip install opencv-contrib-python
$ pip install --upgrade imutils
複製程式碼

資料集

本教程會用到兩個資料集來幫助更好的瞭解每個機器學習演算法的效能。

第一個資料集是 Iris(鳶尾花) 資料集。這個資料集的地位,相當於你剛開始學習一門程式語言時,敲下的 “Hello,World!”

這個資料集是一個數值型的資料,如下圖所示,其實就是一個表格資料,每一行代表一個樣本,然後每一列就是不同的屬性。這個資料集主要是收集了三種不同的鳶尾花的資料,分別為:

  • Iris Setosa
  • Iris Versicolor
  • Iris Virginica

對應圖中最後一列 Class label,然後還有四種屬性,分別是:

  • Sepal length--萼片長度
  • Sepal width--萼片寬度
  • Petal length--花瓣長度
  • Petal width--花瓣寬度

初學者的機器學習入門實戰教程!

這個資料集可能是最簡單的機器學習資料集之一了,通常是用於教導程式設計師和工程師的機器學習和模式識別基礎的資料集。

對於該資料集,我們的目標就是根據給定的四個屬性,訓練一個機器學習模型來正確分類每個樣本的類別。

需要注意的是,其中有一個類別和另外兩個類別是線性可分的,但這兩個類別之間卻並非線性可分,所以我們需要採用一個非線性模型來對它們進行分類。當然了,在現實生活中,採用非線性模型的機器學習演算法是非常常見的。

第二個資料集是一個三場景的影像資料集。這是幫助初學者學習如何處理影像資料,並且哪種演算法在這兩種資料集上效能最優。

下圖是這個三場景資料集的部分圖片例子,它包括森林、高速公路和海岸線三種場景,總共是 948 張圖片,每個類別的具體圖片數量如下:

  • Coast: 360
  • Forest: 328
  • Highway: 260

初學者的機器學習入門實戰教程!

這個三場景資料集是取樣於一個八場景資料集中,作者是 Oliva 和 Torralba 的 2001 年的一篇論文,Modeling the shape of the scene: a holistic representation of the spatial envelope

利用 Python 實現機器學習的步驟

無論什麼時候實現機器學習演算法,推薦採用如下流程來開始:

  • 評估你的問題
  • 準備資料(原始資料、特徵提取、特徵工程等等)
  • 檢查各種機器學習演算法
  • 檢驗實驗結果
  • 深入瞭解效能最好的演算法

這個流程會隨著你機器學習方面的經驗的積累而改善和優化,但對於初學者,這是我建議入門機器學習時採用的流程。

所以,現在開始吧!第一步,就是評估我們的問題,問一下自己:

  • 資料集是哪種型別?數值型,類別型還是影像?
  • 模型的最終目標是什麼?
  • 如何定義和衡量“準確率”呢?
  • 以目前自身的機器學習知識來看,哪些演算法在處理這類問題上效果很好?

最後一個問題非常重要,隨著你使用 Python 實現機器學習的次數的增加,你也會隨之獲得更多的經驗。根據之前的經驗,你可能知道有一種演算法的效能還不錯。

因此,接著就是準備資料,也就是資料預處理以及特徵工程了。

一般來說,這一步,包括了從硬碟中載入資料,檢查資料,然後決定是否需要做特徵提取或者特徵工程。

特徵提取就是應用某種演算法通過某種方式來量化資料的過程。比如,對於影像資料,我們可以採用計算直方圖的方法來統計影像中畫素強度的分佈,通過這種方式,我們就得到描述影像顏色的特徵。

特徵工程則是將原始輸入資料轉換成一個更好描述潛在問題的特徵表示的過程。當然特徵工程是一項更先進的技術,這裡建議在對機器學習有了一定經驗後再採用這種方法處理資料。

第三步,就是檢查各種機器學習演算法,也就是實現一系列機器學習演算法,並應用在資料集上。

這裡,你的工具箱應當包含以下幾種不同型別的機器學習演算法:

  1. 線性模型(比如,邏輯迴歸,線性 SVM)
  2. 非線性模型(比如 RBF SVM,梯度下降分類器)
  3. 樹和基於整合的模型(比如 決策樹和隨機森林)
  4. 神經網路(比如 多層感知機,卷積神經網路)

應當選擇比較魯棒(穩定)的一系列機器學習模型來評估問題,因為我們的目標就是判斷哪種演算法在當前問題的效能很好,而哪些演算法很糟糕。

決定好要採用的模型後,接下來就是訓練模型並在資料集上測試,觀察每個模型在資料集上的效能結果。

在多次實驗後,你可能就是有一種“第六感”,知道哪種演算法更適用於哪種資料集。

比如,你會發現:

  • 對於有很多特徵的資料集,隨機森林演算法的效果很不錯;
  • 而邏輯迴歸演算法可以很好處理高維度的稀疏資料;
  • 對於影像資料,CNNs 的效果非常好。

而以上的經驗獲得,當然就需要你多動手,多進行實戰來深入瞭解不同的機器學習演算法了!

開始動手吧!

接下來就開始敲程式碼來實現機器學習演算法,並在上述兩個資料集上進行測試。本教程的程式碼檔案目錄如下,包含四份程式碼檔案和一個 3scenes資料夾,該資料夾就是三場景資料集,而 Iris 資料集直接採用 scikit-learn 庫載入即可。

├── 3scenes
│   ├── coast [360 entries]
│   ├── forest [328 entries]
│   └── highway [260 entries]
├── classify_iris.py
├── classify_images.py
├── nn_iris.py
└── basic_cnn.py
複製程式碼

程式碼和資料集檔案可以在公眾號後臺,也就是公眾號會話介面回覆 『py_ml』獲取!

首先是實現 classify_iris.py,這份程式碼是採用機器學習演算法來對 Iris 資料集進行分類。

首先匯入需要的庫:

from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import load_iris
import argparse

# 設定引數
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--model", type=str, default="knn", help="type of python machine learning model to use")
args = vars(ap.parse_args())

# 定義一個儲存模型的字典,根據 key 來選擇載入哪個模型
models = {
    "knn": KNeighborsClassifier(n_neighbors=1),
    "naive_bayes": GaussianNB(),
    "logit": LogisticRegression(solver="lbfgs", multi_class="auto"),
    "svm": SVC(kernel="rbf", gamma="auto"),
    "decision_tree": DecisionTreeClassifier(),
    "random_forest": RandomForestClassifier(n_estimators=100),
    "mlp": MLPClassifier()
}
複製程式碼

可以看到在 sklearn 庫中就整合了我們將要實現的幾種機器學習演算法的程式碼,包括:

  • KNN
  • 樸素貝葉斯
  • 邏輯迴歸
  • SVM
  • 決策樹
  • 隨機森林
  • 感知機

我們直接呼叫 sklearn 中相應的函式來實現對應的演算法即可,比如對於 knn 演算法,直接呼叫 sklearn.neighbors 中的 KNeighborsClassifier() 即可,只需要設定引數 n_neighbors ,即最近鄰的個數。

這裡直接用一個 models 的字典來儲存不同模型的初始化,然後根據引數 --model 來呼叫對應的模型,比如命令輸入 python classify_irs.py --model knn 就是呼叫 knn 演算法模型。

接著就是載入資料部分:

print("[INFO] loading data...")
dataset = load_iris()
(trainX, testX, trainY, testY) = train_test_split(dataset.data,
	dataset.target, random_state=3, test_size=0.25)
複製程式碼

這裡直接呼叫 sklearn.datasets 中的 load_iris() 載入資料,然後採用 train_test_split 來劃分訓練集和資料集,這裡是 75% 資料作為訓練集,25% 作為測試集。

最後就是訓練模型和預測部分:

# 訓練模型
print("[INFO] using '{}' model".format(args["model"]))
model = models[args["model"]]
model.fit(trainX, trainY)

# 預測並輸出一份分類結果報告
print("[INFO] evaluating")
predictions = model.predict(testX)
print(classification_report(testY, predictions, target_names=dataset.target_names))
複製程式碼

完整版程式碼程式碼如下:

from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import load_iris
import argparse

# 設定引數
ap = argparse.ArgumentParser()
ap.add_argument("-m", "--model", type=str, default="knn", help="type of python machine learning model to use")
args = vars(ap.parse_args())

# 定義一個儲存模型的字典,根據 key 來選擇載入哪個模型
models = {
    "knn": KNeighborsClassifier(n_neighbors=1),
    "naive_bayes": GaussianNB(),
    "logit": LogisticRegression(solver="lbfgs", multi_class="auto"),
    "svm": SVC(kernel="rbf", gamma="auto"),
    "decision_tree": DecisionTreeClassifier(),
    "random_forest": RandomForestClassifier(n_estimators=100),
    "mlp": MLPClassifier()
}

# 載入 Iris 資料集,然後進行訓練集和測試集的劃分,75%資料作為訓練集,其餘25%作為測試集
print("[INFO] loading data...")
dataset = load_iris()
(trainX, testX, trainY, testY) = train_test_split(dataset.data, dataset.target, random_state=3, test_size=0.25)

# 訓練模型
print("[INFO] using '{}' model".format(args["model"]))
model = models[args["model"]]
model.fit(trainX, trainY)

# 預測並輸出一份分類結果報告
print("[INFO] evaluating")
predictions = model.predict(testX)
print(classification_report(testY, predictions, target_names=dataset.target_names))
複製程式碼

接著就是採用三場景影像資料集的分類預測程式碼 classify_images.py ,跟 classify_iris.py 的程式碼其實是比較相似的,首先匯入庫部分,增加以下幾行程式碼:

from sklearn.preprocessing import LabelEncoder
from PIL import Image
from imutils import paths
import numpy as np
import os
複製程式碼

其中 LabelEncoder 是為了將標籤從字串編碼為整型,然後其餘幾項都是處理影像相關。

對於影像資料,如果直接採用原始畫素資訊輸入模型中,大部分的機器學習演算法效果都很不理想,所以這裡採用特徵提取方法,主要是統計影像顏色通道的均值和標準差資訊,總共是 RGB 3個通道,每個通道各計算均值和標準差,然後結合在一起,得到一個六維的特徵,函式如下所示:

def extract_color_stats(image):
    '''
    將圖片分成 RGB 三通道,然後分別計算每個通道的均值和標準差,然後返回
    :param image:
    :return:
    '''
    (R, G, B) = image.split()
    features = [np.mean(R), np.mean(G), np.mean(B), np.std(R), np.std(G), np.std(B)]

    return features
複製程式碼

然後同樣會定義一個 models 字典,程式碼一樣,這裡就不貼出來了,然後影像載入部分的程式碼如下:

# 載入資料並提取特徵
print("[INFO] extracting image features...")
imagePaths = paths.list_images(args['dataset'])
data = []
labels = []

# 迴圈遍歷所有的圖片資料
for imagePath in imagePaths:
    # 載入圖片,然後計算圖片的顏色通道統計資訊
    image = Image.open(imagePath)
    features = extract_color_stats(image)
    data.append(features)

    # 儲存圖片的標籤資訊
    label = imagePath.split(os.path.sep)[-2]
    labels.append(label)

# 對標籤進行編碼,從字串變為整數型別
le = LabelEncoder()
labels = le.fit_transform(labels)

# 進行訓練集和測試集的劃分,75%資料作為訓練集,其餘25%作為測試集
(trainX, testX, trainY, testY) = train_test_split(data, labels, test_size=0.25)
複製程式碼

上述程式碼就完成從硬碟中載入圖片的路徑資訊,然後依次遍歷,讀取圖片,提取特徵,提取標籤資訊,儲存特徵和標籤資訊,接著編碼標籤,然後就是劃分訓練集和測試集。

接著是相同的訓練模型和預測的程式碼,同樣沒有任何改變,這裡就不列舉出來了。

完整版如下:

from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from PIL import Image
from imutils import paths
import numpy as np
import argparse
import os


def extract_color_stats(image):
    '''
    將圖片分成 RGB 三通道,然後分別計算每個通道的均值和標準差,然後返回
    :param image:
    :return:
    '''
    (R, G, B) = image.split()
    features = [np.mean(R), np.mean(G), np.mean(B), np.std(R), np.std(G), np.std(B)]

    return features


# 設定引數
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", type=str, default="3scenes",
                help="path to directory containing the '3scenes' dataset")
ap.add_argument("-m", "--model", type=str, default="knn",
                help="type of python machine learning model to use")
args = vars(ap.parse_args())

# 定義一個儲存模型的字典,根據 key 來選擇載入哪個模型
models = {
    "knn": KNeighborsClassifier(n_neighbors=1),
    "naive_bayes": GaussianNB(),
    "logit": LogisticRegression(solver="lbfgs", multi_class="auto"),
    "svm": SVC(kernel="rbf", gamma="auto"),
    "decision_tree": DecisionTreeClassifier(),
    "random_forest": RandomForestClassifier(n_estimators=100),
    "mlp": MLPClassifier()
}
# 載入資料並提取特徵
print("[INFO] extracting image features...")
imagePaths = paths.list_images(args['dataset'])
data = []
labels = []

# 迴圈遍歷所有的圖片資料
for imagePath in imagePaths:
    # 載入圖片,然後計算圖片的顏色通道統計資訊
    image = Image.open(imagePath)
    features = extract_color_stats(image)
    data.append(features)

    # 儲存圖片的標籤資訊
    label = imagePath.split(os.path.sep)[-2]
    labels.append(label)

# 對標籤進行編碼,從字串變為整數型別
le = LabelEncoder()
labels = le.fit_transform(labels)

# 進行訓練集和測試集的劃分,75%資料作為訓練集,其餘25%作為測試集
(trainX, testX, trainY, testY) = train_test_split(data, labels, random_state=3, test_size=0.25)
# print('trainX numbers={}, testX numbers={}'.format(len(trainX), len(testX)))

# 訓練模型
print("[INFO] using '{}' model".format(args["model"]))
model = models[args["model"]]
model.fit(trainX, trainY)

# 預測並輸出分類結果報告
print("[INFO] evaluating...")
predictions = model.predict(testX)
print(classification_report(testY, predictions, target_names=le.classes_))
複製程式碼

完成這兩份程式碼後,我們就可以開始執行下程式碼,對比不同演算法在兩個資料集上的效能。

因為篇幅的原因,這裡我會省略原文對每個演算法的介紹,具體的可以檢視之前我寫的對機器學習演算法的介紹:

KNN

這裡我們先執行下 classify_irs,呼叫預設的模型 knn ,看下 KNNIris 資料集上的實驗結果,如下所示:

初學者的機器學習入門實戰教程!

其中主要是給出了對每個類別的精確率、召回率、F1 以及該類別測試集數量,即分別對應 precision, recall, f1-score, support 。根據最後一行第一列,可以看到 KNN 取得 95% 的準確率。

接著是在三場景圖片資料集上的實驗結果:

初學者的機器學習入門實戰教程!

這裡 KNN 取得 72% 的準確率。

(ps:實際上,執行這個演算法,不同次數會有不同的結果,原文作者給出的是 75%,其主要原因是因為在劃分訓練集和測試集的時候,程式碼沒有設定引數 random_state,這導致每次執行劃分的訓練集和測試集的圖片都是不同的,所以執行結果也會不相同!)

樸素貝葉斯

接著是樸素貝葉斯演算法,分別測試兩個資料集,結果如下:

初學者的機器學習入門實戰教程!

初學者的機器學習入門實戰教程!

同樣,樸素貝葉斯在 Iris 上有 98% 的準確率,但是在影像資料集上僅有 63% 的準確率。

那麼,我們是否可以說明 KNN 演算法比樸素貝葉斯好呢?

當然是不可以的,上述結果只能說明在三場景影像資料集上,KNN 演算法優於樸素貝葉斯演算法。

實際上,每種演算法都有各自的優缺點和適用場景,不能一概而論地說某種演算法任何時候都優於另一種演算法,這需要具體問題具體分析。

邏輯迴歸

接著是邏輯迴歸演算法,分別測試兩個資料集,結果如下:

初學者的機器學習入門實戰教程!

初學者的機器學習入門實戰教程!

同樣,邏輯迴歸在 Iris 上有 98% 的準確率,但是在影像資料集上僅有 77% 的準確率(對比原文作者的邏輯迴歸準確率是 69%)

支援向量機 SVM

接著是 SVM 演算法,分別測試兩個資料集,結果如下:

初學者的機器學習入門實戰教程!

初學者的機器學習入門實戰教程!

同樣,SVM 在 Iris 上有 98% 的準確率,但是在影像資料集上僅有 76% 的準確率(對比原文作者的準確率是 83%,主要是發現類別 coast 差別有些大)

決策樹

接著是決策樹演算法,分別測試兩個資料集,結果如下:

初學者的機器學習入門實戰教程!

初學者的機器學習入門實戰教程!

同樣,決策樹在 Iris 上有 98% 的準確率,但是在影像資料集上僅有 71% 的準確率(對比原文作者的決策樹準確率是 74%)

隨機森林

接著是隨機森林演算法,分別測試兩個資料集,結果如下:

初學者的機器學習入門實戰教程!

初學者的機器學習入門實戰教程!

同樣,隨機森林在 Iris 上有 96% 的準確率,但是在影像資料集上僅有 77% 的準確率(對比原文作者的決策樹準確率是 84%)

注意了,一般如果決策樹演算法的效果還不錯的話,隨機森林演算法應該也會取得不錯甚至更好的結果,這是因為隨機森林實際上就是多棵決策樹通過整合學習方法組合在一起進行分類預測。

多層感知機

最後是多層感知機演算法,分別測試兩個資料集,結果如下:

初學者的機器學習入門實戰教程!

初學者的機器學習入門實戰教程!

同樣,多層感知機在 Iris 上有 98% 的準確率,但是在影像資料集上僅有 79% 的準確率(對比原文作者的決策樹準確率是 81%).

深度學習以及深度神經網路

神經網路

最後是實現深度學習的演算法,也就是 nn_iris.pybasic_cnn.py 這兩份程式碼。

(這裡需要注意 TensorFlowKeras 的版本問題,我採用的是 TF=1.2Keras=2.1.5)

首先是 nn_iris.py 的實現,同樣首先是匯入庫和資料的處理:

from keras.models import Sequential
from keras.layers.core import Dense
from keras.optimizers import SGD
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import load_iris

# 載入 Iris 資料集,然後進行訓練集和測試集的劃分,75%資料作為訓練集,其餘25%作為測試集
print("[INFO] loading data...")
dataset = load_iris()
(trainX, testX, trainY, testY) = train_test_split(dataset.data,
                                                  dataset.target, test_size=0.25)

# 將標籤進行 one-hot 編碼
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)
複製程式碼

這裡我們將採用 Keras 來實現神經網路,然後這裡需要將標籤進行 one-hot 編碼,即獨熱編碼。

接著就是搭建網路模型的結構和訓練、預測程式碼:

# 利用 Keras 定義網路模型
model = Sequential()
model.add(Dense(3, input_shape=(4,), activation="sigmoid"))
model.add(Dense(3, activation="sigmoid"))
model.add(Dense(3, activation="softmax"))

# 採用梯度下降訓練模型
print('[INFO] training network...')
opt = SGD(lr=0.1, momentum=0.9, decay=0.1 / 250)
model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=["accuracy"])
H = model.fit(trainX, trainY, validation_data=(testX, testY), epochs=250, batch_size=16)

# 預測
print('[INFO] evaluating network...')
predictions = model.predict(testX, batch_size=16)
print(classification_report(testY.argmax(axis=1), predictions.argmax(axis=1), target_names=dataset.target_names))
複製程式碼

這裡是定義了 3 層全連線層的神經網路,前兩層採用 Sigmoid 啟用函式,然後最後一層是輸出層,所以採用 softmax 將輸出變成概率值。接著就是定義了使用 SGD 的優化演算法,損失函式是 categorical_crossentropy,迭代次數是 250 次,batch_size 是 16。

完整版如下:

from keras.models import Sequential
from keras.layers.core import Dense
from keras.optimizers import SGD
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.datasets import load_iris

# 載入 Iris 資料集,然後進行訓練集和測試集的劃分,75%資料作為訓練集,其餘25%作為測試集
print("[INFO] loading data...")
dataset = load_iris()
(trainX, testX, trainY, testY) = train_test_split(dataset.data,
                                                  dataset.target, test_size=0.25)

# 將標籤進行 one-hot 編碼
lb = LabelBinarizer()
trainY = lb.fit_transform(trainY)
testY = lb.transform(testY)

# 利用 Keras 定義網路模型
model = Sequential()
model.add(Dense(3, input_shape=(4,), activation="sigmoid"))
model.add(Dense(3, activation="sigmoid"))
model.add(Dense(3, activation="softmax"))

# 採用梯度下降訓練模型
print('[INFO] training network...')
opt = SGD(lr=0.1, momentum=0.9, decay=0.1 / 250)
model.compile(loss='categorical_crossentropy', optimizer=opt, metrics=["accuracy"])
H = model.fit(trainX, trainY, validation_data=(testX, testY), epochs=250, batch_size=16)

# 預測
print('[INFO] evaluating network...')
predictions = model.predict(testX, batch_size=16)
print(classification_report(testY.argmax(axis=1),
                            predictions.argmax(axis=1), target_names=dataset.target_names))
複製程式碼

直接執行命令 python nn_iris.py, 輸出的結果如下:

初學者的機器學習入門實戰教程!

這裡得到的是 100% 的準確率,和原文的一樣。當然實際上原文給出的結果如下圖所示,可以看到其實類別數量上是不相同的。

初學者的機器學習入門實戰教程!

CNN

最後就是實現 basic_cnn.py 這份程式碼了。

同樣首先是匯入必須的庫函式:

from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dense
from keras.optimizers import Adam
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from PIL import Image
from imutils import paths
import numpy as np
import argparse
import os

# 配置引數
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", type=str, default="3scenes",
                help="path to directory containing the '3scenes' dataset")
args = vars(ap.parse_args())
複製程式碼

同樣是要匯入 Keras 來建立 CNN 的網路模型,另外因為是處理影像資料,所以 PILimutils 也是要匯入的。

然後是載入資料和劃分訓練集和測試集,對於載入資料,這裡直接採用原始影像畫素資料,只需要對影像資料做統一尺寸的調整,這裡是統一調整為 32×32,並做歸一化到 [0,1] 的範圍。

# 載入資料並提取特徵
print("[INFO] extracting image features...")
imagePaths = paths.list_images(args['dataset'])
data = []
labels = []

# 迴圈遍歷所有的圖片資料
for imagePath in imagePaths:
    # 載入圖片,然後調整成 32×32 大小,並做歸一化到 [0,1]
    image = Image.open(imagePath)
    image = np.array(image.resize((32, 32))) / 255.0
    data.append(image)

    # 儲存圖片的標籤資訊
    label = imagePath.split(os.path.sep)[-2]
    labels.append(label)

# 對標籤編碼,從字串變為整型
lb = LabelBinarizer()
labels = lb.fit_transform(labels)

# 劃分訓練集和測試集
(trainX, testX, trainY, testY) = train_test_split(np.array(data), np.array(labels), test_size=0.25)
複製程式碼

接著定義了一個 4 層的 CNN 網路結構,包含 3 層卷積層和最後一層輸出層,優化演算法採用的是 Adam 而不是 SGD 。程式碼如下所示:

# 定義 CNN 網路模型結構
model = Sequential()
model.add(Conv2D(8, (3, 3), padding="same", input_shape=(32, 32, 3)))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(16, (3, 3), padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(32, (3, 3), padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Flatten())
model.add(Dense(3))
model.add(Activation("softmax"))

# 訓練模型
print("[INFO] training network...")
opt = Adam(lr=1e-3, decay=1e-3 / 50)
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
H = model.fit(trainX, trainY, validation_data=(testX, testY),
              epochs=50, batch_size=32)

# 預測
print("[INFO] evaluating network...")
predictions = model.predict(testX, batch_size=32)
print(classification_report(testY.argmax(axis=1),
                            predictions.argmax(axis=1), target_names=lb.classes_))
複製程式碼

完整版如下:

from keras.models import Sequential
from keras.layers.convolutional import Conv2D
from keras.layers.convolutional import MaxPooling2D
from keras.layers.core import Activation
from keras.layers.core import Flatten
from keras.layers.core import Dense
from keras.optimizers import Adam
from sklearn.preprocessing import LabelBinarizer
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from PIL import Image
from imutils import paths
import numpy as np
import argparse
import os

# 配置引數
ap = argparse.ArgumentParser()
ap.add_argument("-d", "--dataset", type=str, default="3scenes",
                help="path to directory containing the '3scenes' dataset")
args = vars(ap.parse_args())

# 載入資料並提取特徵
print("[INFO] extracting image features...")
imagePaths = paths.list_images(args['dataset'])
data = []
labels = []

# 迴圈遍歷所有的圖片資料
for imagePath in imagePaths:
    # 載入圖片,然後調整成 32×32 大小,並做歸一化到 [0,1]
    image = Image.open(imagePath)
    image = np.array(image.resize((32, 32))) / 255.0
    data.append(image)

    # 儲存圖片的標籤資訊
    label = imagePath.split(os.path.sep)[-2]
    labels.append(label)

# 對標籤編碼,從字串變為整型
lb = LabelBinarizer()
labels = lb.fit_transform(labels)

# 劃分訓練集和測試集
(trainX, testX, trainY, testY) = train_test_split(np.array(data), np.array(labels), test_size=0.25)

# 定義 CNN 網路模型結構
model = Sequential()
model.add(Conv2D(8, (3, 3), padding="same", input_shape=(32, 32, 3)))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(16, (3, 3), padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Conv2D(32, (3, 3), padding="same"))
model.add(Activation("relu"))
model.add(MaxPooling2D(pool_size=(2, 2), strides=(2, 2)))
model.add(Flatten())
model.add(Dense(3))
model.add(Activation("softmax"))

# 訓練模型
print("[INFO] training network...")
opt = Adam(lr=1e-3, decay=1e-3 / 50)
model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])
H = model.fit(trainX, trainY, validation_data=(testX, testY),
              epochs=50, batch_size=32)

# 預測
print("[INFO] evaluating network...")
predictions = model.predict(testX, batch_size=32)
print(classification_report(testY.argmax(axis=1),
                            predictions.argmax(axis=1), target_names=lb.classes_))
複製程式碼

執行命令 python basic_cnn.py, 輸出結果如下:

初學者的機器學習入門實戰教程!

CNN 的準確率是達到 90%,它是優於之前的幾種機器學習演算法的結果。


小結

最後,這僅僅是一份對機器學習完全是初學者的教程,其實就是簡單呼叫現有的庫來實現對應的機器學習演算法,讓初學者簡單感受下如何使用機器學習演算法,正如同在學習程式語言的時候,對著書本的程式碼例子敲起來,然後執行程式碼,看看自己寫出來的程式的執行結果。

通過這份簡單的入門教程,你應該明白的是:

  1. 沒有任何一種演算法是完美的,可以完全適用所有的場景,即便是目前很熱門的深度學習方法,也存在它的侷限性,所以應該具體問題具體分析
  2. 記住開頭推薦的 5 步機器學習操作流程,這裡再次複習一遍:
    • 評估你的問題
    • 準備資料(原始資料、特徵提取、特徵工程等等)
    • 檢查各種機器學習演算法
    • 檢驗實驗結果
    • 深入瞭解效能最好的演算法
  3. 最後一點,是我執行演算法結果,和原文作者的結果會不相同,這實際上就是每次取樣資料,劃分訓練集和測試集不相同的原因!這其實也說明了資料非常重要,對於機器學習來說,好的資料很重要

接著,根據這份教程,你可以繼續進一步瞭解每種機器學習演算法,瞭解每種演算法的基本原理和實現,嘗試自己手動實現,而不是簡單呼叫現有的庫,這樣更加能加深印象,這裡推薦《機器學習實戰》,經典的機器學習演算法都有介紹,並且都會帶你一步步實現演算法!

最後,極力推薦大家去閱讀下原文作者的部落格,原文作者也是一個大神,他的部落格地址如下:

www.pyimagesearch.com/

他的部落格包含了 Opencv、Python、機器學習和深度學習方面的教程和文章,而且作者喜歡通過實戰學習,所以很多文章都是通過一些實戰練習來學習某個知識點或者某個演算法,正如同本文通過實現這幾種常見的機器學習演算法在兩個不同型別資料集上的實戰來帶領初學者入門機器學習。


對該教程的程式碼和資料集獲取方式如下:

  1. 關注公眾號“機器學習與計算機視覺
  2. 在微信公眾號後臺留言 『py_ml

歡迎關注我的微信公眾號--機器學習與計算機視覺,或者掃描下方的二維碼,大家一起交流,學習和進步!

初學者的機器學習入門實戰教程!

往期精彩推薦

機器學習系列
Github專案 & 資源教程推薦

相關文章