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

前言

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

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

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

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

引子

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

圖1-1:鳶尾花局部

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

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

由於咱們有已知品種的鳶尾花的測量數據,因此這是一個監督學習問題。在這個問題中,咱們要在多個選項中預測其中一個(鳶尾花的品種)。這是一個分類(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% 都是正確的。對於咱們的植物學愛好者應用程序來講,高精度意味着模型足夠可信,可使用。在後續章節中,咱們將討論提升性能的方法,以及模型調參時的注意事項。

相關文章
相關標籤/搜索