Python機器學習基礎教程-第1章-鳶尾花的例子KNN


前言

本系列教程基本就是摘抄《Python機器學習基礎教程》中的例子內容。

為了便於跟蹤和學習,本系列教程在Github上提供了jupyter notebook 版本:

Github倉庫:https://github.com/Holy-Shine/Introduciton-2-ML-with-Python-notebook

系列教程總目錄
Python機器學習基礎教程

引子

假設有一名植物學愛好者對她發現的鳶尾花的品種很感興趣。她收集了每朵鳶尾花的一些測量數據:花瓣的長度和寬度以及花萼的長度和寬度,所有測量結果的單位都是厘米(見圖 1-1)。

圖1-1:鳶尾花局部

她還有一些鳶尾花的測量數據,這些花之前已經被植物學專家鑒定為屬於 setosa、versicolor 或 virginica 三個品種之一。對於這些測量數據,她可以確定每朵鳶尾花所屬的品種。我們假設這位植物學愛好者在野外只會遇到這三種鳶尾花。

我們的目標是構建一個機器學習模型,可以從這些已知品種的鳶尾花測量數據中進行學習,從而能夠預測新鳶尾花的品種。

因為我們有已知品種的鳶尾花的測量數據,所以這是一個監督學習問題。在這個問題中,我們要在多個選項中預測其中一個(鳶尾花的品種)。這是一個分類(classification)問題的示例。可能的輸出(鳶尾花的不同品種)叫作類別(class)。數據集中的每朵鳶尾花都屬於三個類別之一,所以這是一個三分類問題

單個數據點(一朵鳶尾花)的預期輸出是這朵花的品種。對於一個數據點來說,它的品種叫作標簽(label)。

1. 初識數據

先加載必要的python庫

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import mglearn  # 倉庫已付,主要用來美化圖像
%matplotlib inline  

本例中我們用到了鳶尾花(Iris)數據集,這是機器學習和統計學中一個經典的數據集。它包含在 scikit-learn 的 datasets 模塊中。我們可以調用 load_iris 函數來加載數據:

from sklearn.datasets import load_iris
iris_dataset =load_iris()

load_iris 返回的 iris 對象是一個 Bunch 對象,與字典非常相似,里面包含鍵和值,可以輸出查看一些里面的鍵:

print("Keys of iris_dataset: \n{}".format(iris_dataset.keys()))

[out] :

Keys of iris_dataset:

dict_keys(['filename', 'feature_names', 'target', 'target_names', 'DESCR', 'data'])

DESCR 鍵對應的值是數據集的簡要說明。我們這里給出說明的開頭部分(你可以自己查看其余的內容):

print(iris_dataset['DESCR'][:193] + "\n...")

[out] :

.. _iris_dataset:

Iris plants dataset

**Data Set Characteristics:**

​ :Number of Instances: 150 (50 in each of three classes)
​ :Number of Attributes: 4 numeric, pre

...

target_names 鍵對應的值是一個字符串數組,里面包含我們要預測的花的品種:

print("Target names: {}".format(iris_dataset['target_names']))

[out] :

Target names: ['setosa' 'versicolor' 'virginica']

feature_names 鍵對應的值是一個字符串列表,對每一個特征進行了說明:

print("Feature names: \n{}".format(iris_dataset['feature_names']))

[out] :

Feature names:
['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']

數據包含在 target 和 data 字段中。 data 里面是花萼長度、花萼寬度、花瓣長度、花瓣寬度的測量數據,格式為 NumPy 數組:

print("Type of data: {}".format(type(iris_dataset['data'])))

[out] :

Type of data: <class 'numpy.ndarray'>

data 數組的每一行對應一朵花,列代表每朵花的四個測量數據:

print("Shape of data: {}".format(iris_dataset['data'].shape))

[out] :

Shape of data: (150, 4)

可以看出,數組中包含 150 朵不同的花的測量數據。前面說過,機器學習中的個體叫作樣本(sample),其屬性叫作特征(feature)。 data 數組的形狀(shape)是樣本數乘以特征數。這是 scikit-learn 中的約定,你的數據形狀應始終遵循這個約定。下面給出前 5 個樣本的特征數值:

print("First five rows of data:\n{}".format(iris_dataset['data'][:5]))

[out] :

First five rows of data:
[[5.1 3.5 1.4 0.2]
[4.9 3. 1.4 0.2]
[4.7 3.2 1.3 0.2]
[4.6 3.1 1.5 0.2]
[5. 3.6 1.4 0.2]]

從數據中可以看出,前 5 朵花的花瓣寬度都是 0.2cm,第一朵花的花萼最長,是 5.1cm。target 數組包含的是測量過的每朵花的品種,也是一個 NumPy 數組:

print("Type of target: {}".format(type(iris_dataset['target'])))

[out] :

Type of data: <class 'numpy.ndarray'>

target 是一維數組,每朵花對應其中一個數據:

print("Shape of target: {}".format(iris_dataset['target'].shape))

[out] :

Shape of target: (150,)

品種被轉換成從 0 到 2 的整數:

print("Target:\n{}".format(iris_dataset['target']))

[out] :

Target:
[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]

上述數字的代表含義由 iris['target_names'] 數組給出:0 代表 setosa,1 代表 versicolor, 2 代表 virginica。

2. 訓練和測試數據

我們想要利用這些數據構建一個機器學習模型,用於預測新測量的鳶尾花的品種。但在將模型應用於新的測量數據之前,我們需要知道模型是否有效,也就是說,我們是否應該相信它的預測結果。

不幸的是,我們不能將用於構建模型的數據用於評估模型。因為我們的模型會一直記住整個訓練集,所以對於訓練集中的任何數據點總會預測正確的標簽。這種“記憶”無法告訴我們模型的泛化(generalize)能力如何(換句話說,在新數據上能否正確預測)。

我們要用新數據來評估模型的性能。新數據是指模型之前沒有見過的數據,而我們有這些新數據的標簽。通常的做法是將收集好的帶標簽數據(此例中是 150 朵花的測量數據)分成兩部分。一部分數據用於構建機器學習模型,叫作訓練數據(training data)或訓練集(training set)。其余的數據用來評估模型性能,叫作測試數據(test data)、測試集(test set)或留出集(hold-out set)。

scikit-learn 中的 train_test_split 函數可以打亂數據集並進行拆分。這個函數將 75% 的行數據及對應標簽作為訓練集,剩下 25% 的數據及其標簽作為測試集。訓練集與測試集的分配比例可以是隨意的,但使用 25% 的數據作為測試集是很好的經驗法則。

scikit-learn 中的數據通常用大寫的 X 表示,而標簽用小寫的 y 表示。這是受到了數學標准公式 f(x)=y 的啟發,其中 x 是函數的輸入,y 是輸出。我們用大寫的 X 是因為數據是一個二維數組(矩陣),用小寫的 y 是因為目標是一個一維數組(向量),這也是數學中
的約定。

對數據調用 train_test_split ,並對輸出結果采用下面這種命名方法:

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(iris_dataset['data'], iris_dataset['target'], random_state=0)

在對數據進行拆分之前, train_test_split 函數利用偽隨機數生成器將數據集打亂。如果我們只是將最后 25% 的數據作為測試集,那么所有數據點的標簽都是 2 ,因為數據點是按標簽排序的(參見之前 iris['target'] 的輸出)。測試集中只有三個類別之一,這無法告訴我們模型的泛化能力如何,所以我們將數據打亂,確保測試集中包含所有類別的數據。

為了確保多次運行同一函數能夠得到相同的輸出,我們利用 random_state 參數指定了隨機數生成器的種子。這樣函數輸出就是固定不變的,所以這行代碼的輸出始終相同。本書用到隨機過程時,都會用這種方法指定 random_state 。

train_test_split 函數的輸出為 X_train 、 X_test 、 y_train 和 y_test ,它們都是 NumPy數組。 X_train 包含 75% 的行數據, X_test 包含剩下的 25%:

print("X_train shape: {}".format(X_train.shape))
print("y_train shape: {}".format(y_train.shape))

[out] :

X_train shape: (112, 4)
y_train shape: (112,)

print("X_test shape: {}".format(X_test.shape))
print("y_test shape: {}".format(y_test.shape))

[out] :

X_test shape: (38, 4)
y_test shape: (38,)

3. 觀察數據

在構建機器學習模型之前,通常最好檢查一下數據,看看如果不用機器學習能不能輕松完成任務,或者需要的信息有沒有包含在數據中。此外,檢查數據也是發現異常值和特殊值的好方法。舉個例子,可能有些鳶尾花的測量單位是英寸而不是厘米。在現實世界中,經常會遇到不一致的數據和意料之外的測量數據。

檢查數據的最佳方法之一就是將其可視化。一種可視化方法是繪制散點圖(scatter plot)。數據散點圖將一個特征作為 x 軸,另一個特征作為 y 軸,將每一個數據點繪制為圖上的一個點。不幸的是,計算機屏幕只有兩個維度,所以我們一次只能繪制兩個特征(也可能是3 個)。用這種方法難以對多於 3 個特征的數據集作圖。解決這個問題的一種方法是繪制散點圖矩陣(pair plot),從而可以兩兩查看所有的特征。如果特征數不多的話,比如我們這里有 4 個,這種方法是很合理的。但是你應該記住,散點圖矩陣無法同時顯示所有特征之間的關系,所以這種可視化方法可能無法展示數據的某些有趣內容。

下面的代碼輸出訓練集中特征的散點圖矩陣。數據點的顏色與鳶尾花的品種相對應。為了繪制這張圖,我們首先將 NumPy 數組轉換成 pandas DataFrame 。 pandas 有一個繪制散點圖矩陣的函數,叫作 scatter_matrix 。矩陣的對角線是每個特征的直方圖:

# 利用X_train中的數據創建DataFrame
# 利用iris_dataset.feature_names中的字符串對數據列進行標記
iris_dataframe=pd.DataFrame(X_train, columns=iris_dataset.feature_names)
# 利用DataFrame創建散點圖矩陣,按y_train着色
grr=pd.plotting.scatter_matrix(iris_dataframe, c=y_train, figsize=(15,15),marker='o',hist_kwds={'bins':20},s=60,alpha=.8,cmap=mglearn.cm3)

[out] :

下載.png

從圖中可以看出,利用花瓣和花萼的測量數據基本可以將三個類別區分開。這說明機器學習模型很可能可以學會區分它們。

4. KNN算法

現在我們可以開始構建真實的機器學習模型了。 scikit-learn 中有許多可用的分類算法。這里我們用的是 k 近鄰分類器,這是一個很容易理解的算法。構建此模型只需要保存訓練集即可。要對一個新的數據點做出預測,算法會在訓練集中尋找與這個新數據點距離最近的數據點,然后將找到的數據點的標簽賦值給這個新數據點。

k 近鄰算法中 k 的含義是,我們可以考慮訓練集中與新數據點最近的任意 k 個鄰居(比如說,距離最近的 3 個或 5 個鄰居),而不是只考慮最近的那一個。然后,我們可以用這些鄰居中數量最多的類別做出預測。第 2 章會進一步介紹這個算法的細節,現在我們只考慮一個鄰居的情況。

scikit-learn 中所有的機器學習模型都在各自的類中實現,這些類被稱為 Estimator類。k 近鄰分類算法是在 neighbors 模塊的 KNeighborsClassifier 類中實現的。我們需要將這個類實例化為一個對象,然后才能使用這個模型。這時我們需要設置模型的參數。KNeighborsClassifier 最重要的參數就是鄰居的數目,這里我們設為 1 :

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=1)

knn 對象對算法進行了封裝,既包括用訓練數據構建模型的算法,也包括對新數據點進行預測的算法。它還包括算法從訓練數據中提取的信息。對於 KNeighborsClassifier 來說,里面只保存了訓練集。

想要基於訓練集來構建模型,需要調用 knn 對象的 fit 方法,輸入參數為 X_train 和 y_train ,二者都是 NumPy 數組,前者包含訓練數據,后者包含相應的訓練標簽:

knn.fit(X_train, y_train)

[out] :

KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=1, p=2,
weights='uniform')

fit 方法返回的是 knn 對象本身並做原處修改,因此我們得到了分類器的字符串表示。從中可以看出構建模型時用到的參數。幾乎所有參數都是默認值,但你也會注意到 n_neighbors=1 ,這是我們傳入的參數。 scikit-learn 中的大多數模型都有很多參數,但多用於速度優化或非常特殊的用途。你無需關注這個字符串表示中的其他參數。打印 scikit-learn 模型會生成非常長的字符串,但不要被它嚇到。我們會在第 2 章講到所有重要的參數。在本書的其他章節中,我們不會給出 fit 的輸出,因為里面沒有包含任何新的信息。

5. 預測和評估

這里需要用到之前創建的測試集。這些數據沒有用於構建模型,但我們知道測試集中每朵鳶尾花的實際品種。

因此,我們可以對測試數據中的每朵鳶尾花進行預測,並將預測結果與標簽(已知的品種)進行對比。我們可以通過計算精度(accuracy)來衡量模型的優劣,精度就是品種預測正確的花所占的比例:

y_pred = knn.predict(X_test)
print("Test set predictions:\n {}".format(y_pred))

[out] :

Test set predictions:
[2 1 0 2 0 2 0 1 1 1 2 1 1 1 1 0 1 1 0 0 2 1 0 0 2 0 0 1 1 0 2 1 0 2 2 1 0
2]

print("Test set score: {:.2f}".format(np.mean(y_pred == y_test)))

[out] :

Test set score: 0.97

對於這個模型來說,測試集的精度約為 0.97,也就是說,對於測試集中的鳶尾花,我們的預測有 97% 是正確的。根據一些數學假設,對於新的鳶尾花,可以認為我們的模型預測結果有 97% 都是正確的。對於我們的植物學愛好者應用程序來說,高精度意味着模型足夠可信,可以使用。在后續章節中,我們將討論提高性能的方法,以及模型調參時的注意事項。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM