【機器學習實戰】科學處理鳶尾花數據集

目錄

一、案例分析

二、數據處理

2.1 回答問題

2.2 檢查數據

2.3 清理數據

2.4 測試數據

三、用 scikit-learn 來預測數據

3.1 選出特徵 (輸入變量) 和標記 (輸出變量)

3.2 劃分訓練集和測試集

3.3 用模型來學習

四、思考題


一、案例分析

假設我們要創建一個智能手機應用程序,從智能手機拍攝的照片中自動識別花的種類。 我們需創建一個演示機器學習模型,測量花的萼片長度 (sepal length),萼片寬度 (sepal width),花瓣長度 (petal length) 和花瓣寬度 (petal width) 四個變量,並根據這些測量識別物種。

如圖,花的萼片和花瓣。(萼片是花的最外一環)

三種類型的鳶尾花,如圖所示:

根據當地研究人員測量的每種鳶尾花的四個數據 (萼片長/寬和花瓣長/寬),我們最終目的是想正確的分類這三種花。

 

二、數據處理

2.1 回答問題

任何數據分析項目的第一步就是提出想要解決的問題,併爲成功解決該問題而定義一個度量 。一些常見的問題如下:

(1)在查看數據之前是否瞭解數據分析問題的類型,是迴歸,分類還是聚類問題?(明晰問題本質)

這是個根據萼片長度,萼片寬度,花瓣長度和花瓣寬度四個測量指標的分類問題

(2)是否在一開始就定義了成功的度量?(設定量化指標)

因爲是分類問題,所以可以使用查準率,即正確分類花的百分比,來量化模型的表現。我們的數據主管告訴我們應該實現 90% 的準確性。

(3)現有數據是否解決分類問題?(瞭解數據侷限性)

我們目前的數據集只有三種類型的鳶尾花。從這個數據集建立的模型將只適用於那些鳶尾花,未來創建一個通用的花分類器需要更多的數據

(注意:思考這些問題執行有效數據分析的重要一步,不可忽略哦。)

2.2 檢查數據

在花費太多時間分析數據之前,提早檢查並修正這些數據錯誤能節省大量時間。一般來說,我們希望回答以下問題:

  1. 數據格式有什麼問題嗎?
  2. 數據數值有什麼問題嗎?
  3. 數據需要修復或刪除嗎?

首先引進 python 裏面的幾個包,numpy 是爲了做數學運算,pandas 是爲了處理數據,matplotlib 是爲了畫圖,seaborn 是爲了畫高級圖。代碼如下:

import numpy as np     # 用來做數學運算
import pandas as pd    # 用來處理數據表
import seaborn as sns   # 用來畫高級統計圖

# 將所有圖都在 Notebook 裏顯示
%matplotlib inline               
import matplotlib.pyplot as plt  # 用來畫圖

from sklearn.model_selection import train_test_split     # 做交叉驗證,劃分訓練集和測試集
from sklearn.tree import DecisionTreeClassifier           # 用決策樹來分類

檢查點 1. 數據格式 (format)

首先用 pandas 讀取 csv 文件並將數據存成數據表 (data frame) 格式。

iris_data = pd.read_csv('data/iris-data.csv', na_values=['NA'])   # 從名爲iris_data的csv文件讀數據存成數據表
#第二個參數用來把 csv 裏面空白處用 NaN 代替
iris_data.head(5).append(iris_data.tail())    # 展示數據表前5個和後5個數據
  sepal_length_cm sepal_width_cm petal_length_cm petal_width_cm class
0 5.1 3.5 1.4 0.2 Iris-setosa
1 4.9 3.0 1.4 0.2 Iris-setosa
2 4.7 3.2 1.3 0.2 Iris-setosa
3 4.6 3.1 1.5 0.2 Iris-setosa
4 5.0 3.6 1.4 0.2 Iris-setosa
145 6.7 3.0 5.2 2.3 Iris-virginica
146 6.3 2.5 5.0 2.3 Iris-virginica
147 6.5 3.0 5.2 2.0 Iris-virginica
148 6.2 3.4 5.4 2.3 Iris-virginica
149 5.9 3.0 5.1 1.8 Iris-virginica

檢查點 2. 數據統計 (statistics)

接下來,檢查數據的分佈可以識別異常值。我們從數據集的彙總統計數據開始。

iris_data.describe()    # 檢查四列數據的個數,平均數,標準差,最小值,最大值和25,50,75的百分位數
  sepal_length_cm sepal_width_cm petal_length_cm petal_width_cm
count 150.000000 150.000000 150.000000 145.000000
mean 5.644627 3.054667 3.758667 1.236552
std 1.312781 0.433123 1.764420 0.755058
min 0.055000 2.000000 1.000000 0.100000
25% 5.100000 2.800000 1.600000 0.400000
50% 5.700000 3.000000 4.350000 1.300000
75% 6.400000 3.300000 5.100000 1.800000
max 7.900000 4.400000 6.900000 2.500000

從該表中看到幾個有用的值。 例如,我們看到缺少 5 條花瓣寬度的數據(表裏 count 那一行的萼片長度,萼片寬度和花瓣長度的個數都是 150 個,唯獨花瓣寬度是 145 個)。此外,這樣的表給不了太多有用信息,除非我們知道數據應該在一個特定的範圍 (如萼片長度的最小值是 0.055,和它其他指標如均值和幾個百分位數都不是一個數量級的,很有可能是測量錯誤)。 

比起一串枯燥的數值,我們可能更喜歡絢爛的繪圖。接下來可視化數據,它能使異常值立即脫穎而出。

sns.pairplot(iris_data.dropna(), hue='class')    # 畫散點矩陣圖

  • 第一個參數 iris_data.dropna() 就是除去 NaN 的數據表,這麼做原因很簡單,圖裏不可能顯示的出 NaN 值的;
  • 第二個參數 hue = 'class' 就是根據類 (class) 下不同的值賦予不同的顏色 (hue 就是色彩的意思) 。

散點矩陣圖繪製前四列變量(萼片長/寬和花瓣長/寬)的相關係數圖,而且用不同顏色區分不同的類下面的這四個變量。 從上圖可知,橫軸縱軸都有四個變量,那麼總共可以畫出 16 (4*4) 張小圖。

  • 對角線上的 4 張都是某個變量和自己本身的關係,由於自己和自己的相關係數永遠是 1,畫出相關係數圖意義不大。
  • 非對角線的 12 張就是某個變量和另一個變量的關係。比如第一行第二列的圖描述的就是萼片長度 (看縱軸第一個 sepal_length_cm 字樣) 和萼片寬度 (看橫軸第二個 sepal_width_cm 字樣)。

從散點矩陣圖中,我們可以迅速看出數據集的一些問題:

(1)圖的右側標註這五個類 (Iris-setosa, Iris-setossa, Iris-versicolor, versicolor, Iris-virginica),但原本要分類的花只有三類 (Iris-setosa, Iris-versicolor, Iris-virginica)。這意味着在記錄數據時可能會犯下一些錯誤。

(2)在測量中有一些明顯的異常值可能是錯誤的。

  • 例如第一行後三張小圖,對於 Iris-setosa (山鳶尾花,藍點),一個萼片寬度值落在其正常範圍之外;
  • 例如第二行第一,三,四張小圖,對於 Iris-versicolor (變色鳶尾花,紅點) ,幾個萼片長度值都接近零。

下一步我們的任務是要處理錯誤的數據。

2.3 清理數據

修正 1. 數據類(class)

問題:按理應該只有三個類,圖中卻顯示五個。

原因是:標記數據時忘記在 Iris-versicolor 之前添加 Iris-。另一個類 Iris-setossa 他們只是多打了一個 s。讓我們使用代碼來修復這些錯誤。

iris_data['class'].unique()    # 查看數據表的類的不重複值,有5個,但按理說只有3個
array(['Iris-setosa', 'Iris-setossa', 'Iris-versicolor', 'versicolor',
       'Iris-virginica'], dtype=object)
iris_data.loc[iris_data['class'] == 'versicolor', 'class'] = 'Iris-versicolor'   # 將 versicolor 改爲 Iris-versicolor
iris_data.loc[iris_data['class'] == 'Iris-setossa', 'class'] = 'Iris-setosa'     # 將 Iris-setossa 改爲 Iris-setosa

iris_data['class'].unique()    # 再查看數據表的類的不重複值,現在只有3個
array(['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'], dtype=object)

更新後的散點矩陣圖如下:

現在只有三個類而分別是 Iris-setosa, Iris-versicolor 和 Iris-virginica。

修正點 2. 異常數值 (outliers)

修復異常值是一件棘手的事情。因爲我們很難判斷異常值是否由測量誤差引起,或者是不正確的單位記錄數據,或者是真正的異常。如果我們決定排除任何數據,需要記錄排除的數據並提供排除該數據的充分理由。由上節所知,我們有兩種類型的異常值。

問題:山鳶尾花的一個萼片寬度值落在其正常範圍之外 (黑色圓框)。

我們發現,山鳶尾花 (Iris-setosa) 的萼片寬度 (sepal_width_cm) 不可能低於 2.5 釐米。顯然,這個記錄是錯誤的,這種情況下最有效的方法是刪除它而不是花時間查找原因。但是,我們仍需要知道有多少個類似這樣的錯誤數據,如果很少刪除它沒有問題,如果很多我們需要查明原因。

# 查看 Iris-setosa 裏萼片寬度小於2.5釐米的數據,只有一個
iris_data.loc[(iris_data['class'] == 'Iris-setosa') & (iris_data['sepal_width_cm'] < 2.5)]    
  sepal_length_cm sepal_width_cm petal_length_cm petal_width_cm class
41 4.5 2.3 1.3 0.3 Iris-setosa
iris_data.loc[iris_data['class'] == 'Iris-setosa', 'sepal_width_cm'].hist()

第一行代碼是用數據表裏的 loc[] 函數來找到類值爲 Iris-setoa (因爲是藍點) 並且 sepal width 小於 2.5 的所有行。最後發現只有一個這樣的數據,而上圖的條形圖也確認了這樣的異常值只有一個。因此可以直接刪除此數據。

# 去掉 Iris-setosa 裏萼片寬度大於2.5釐米的數據,然後畫出其條形圖
iris_data = iris_data.loc[(iris_data['class'] != 'Iris-setosa') | (iris_data['sepal_width_cm'] >= 2.5)]
iris_data.loc[iris_data['class'] == 'Iris-setosa', 'sepal_width_cm'].hist()

第一行代碼將類值爲 Iris-setosa 並且 sepal width 大於 2.5 的所有數據都新存到 iris_data 中。從上面條形圖也看到了再沒有這個異常值。現在所有的山鳶尾花的萼片寬度都大於 2.5 釐米。

問題:變色鳶尾花的幾個萼片長度值接近與零 (黑色橢圓框)。

我們發現,所有這些接近零的 sepal_length_cm 似乎錯位了兩個數量級。

# 查看 Iris-versicolor 裏的萼片長度接近於零的所有數據
iris_data.loc[(iris_data['class'] == 'Iris-versicolor') & (iris_data['sepal_length_cm'] < 1.0)]
  sepal_length_cm sepal_width_cm petal_length_cm petal_width_cm class
77 0.067 3.0 5.0 1.7 Iris-versicolor
78 0.060 2.9 4.5 1.5 Iris-versicolor
79 0.057 2.6 3.5 1.0 Iris-versicolor
80 0.055 2.4 3.8 1.1 Iris-versicolor
81 0.055 2.4 3.7 1.0 Iris-versicolor
# 畫出其條形圖
iris_data.loc[iris_data['class'] == 'Iris-versicolor', 'sepal_length_cm'].hist()

第一行代碼是用數據表裏的 loc[] 函數來找到類值爲 Iris-versicolor (因爲是紅點) 並且 sepal length 接近零的所有行,發現有五個數據,而條形圖最左邊顯示的數據個數也確認了是五個。

# 將萼片長度乘以100倍,從單位米換成單位釐米
iris_data.loc[(iris_data['class'] == 'Iris-versicolor') &
              (iris_data['sepal_length_cm'] < 1.0),
              'sepal_length_cm'] *= 100.0

iris_data.loc[iris_data['class'] == 'Iris-versicolor', 'sepal_length_cm'].hist()

修正點 3. 缺失數值 (missing value)

我們還有些 NaN 數據。通常我們有兩種方式來處理這類數據。

  1. 刪除 (deletion)
  2. 插補 (imputation)

在本例中刪除不是理想的做法,特別是考慮到它們都在 Iris-setosa 下,如圖

所有缺失的值都屬於 Iris-setosa類,直接刪除可能會對日後數據分析帶來偏差。此外,可以用插補方法,其最常見的方法平均插補 (mean imputation)。其做法就是“假設知道測量的值落在一定範圍內,就可以用該測量的平均值填充空值”。

# 查看所有有NaN值的行數據,發現只有花瓣寬度 (petal_width_cm) 列下才有
iris_data.loc[(iris_data['sepal_length_cm'].isnull()) |
              (iris_data['sepal_width_cm'].isnull()) |
              (iris_data['petal_length_cm'].isnull()) |
              (iris_data['petal_width_cm'].isnull())]
  sepal_length_cm sepal_width_cm petal_length_cm petal_width_cm class
7 5.0 3.4 1.5 NaN Iris-setosa
8 4.4 2.9 1.4 NaN Iris-setosa
9 4.9 3.1 1.5 NaN Iris-setosa
10 5.4 3.7 1.5 NaN Iris-setosa
11 4.8 3.4 1.6 NaN Iris-setosa
# 畫出其條形圖
iris_data.loc[iris_data['class'] == 'Iris-setosa', 'petal_width_cm'].hist()

接下來用 hist() 函數畫出 Iris-setosa 花瓣寬度的條形圖,可以清楚看到大多數寬度在 0.25 左右。

# 用平均值來代替NaN值
average_petal_width = iris_data.loc[iris_data['class'] == 'Iris-setosa', 'petal_width_cm'].mean()

iris_data.loc[(iris_data['class'] == 'Iris-setosa') &
              (iris_data['petal_width_cm'].isnull()),
              'petal_width_cm'] = average_petal_width

iris_data.loc[(iris_data['class'] == 'Iris-setosa') &
              (iris_data['petal_width_cm'] == average_petal_width)]
  sepal_length_cm sepal_width_cm petal_length_cm petal_width_cm class
7 5.0 3.4 1.5 0.25 Iris-setosa
8 4.4 2.9 1.4 0.25 Iris-setosa
9 4.9 3.1 1.5 0.25 Iris-setosa
10 5.4 3.7 1.5 0.25 Iris-setosa
11 4.8 3.4 1.6 0.25 Iris-setosa

然後用 mean() 準確求出其寬度的平均值,將其 NaN 值全部用平均值代替,最後打出那 5 行插補後的數據表。

# 確保所有NaN值都已被更新
iris_data.loc[(iris_data['sepal_length_cm'].isnull()) |
              (iris_data['sepal_width_cm'].isnull()) |
              (iris_data['petal_length_cm'].isnull()) |
              (iris_data['petal_width_cm'].isnull())]

爲了確保所有 NaN 值已被替換,再次用 iris_data[A].isnull() 語句來查看,出來的結果是一個只有列標題的空數據表。這表示表內已經沒有 NaN 值了。

2.4 測試數據

1. 存儲數據(save data)

iris_data.to_csv('data/iris-data-clean.csv', index = False)    # 將整理好的數據寫到一個名叫iris-data-clean的csv文件裏,沒有行標

iris_data_clean = pd.read_csv('data/iris-data-clean.csv')      # 重新從iris-data-clean的csv文件裏讀數據存成iris_data_clean數據表

讓我們再看看基於乾淨數據畫的散點矩陣圖吧。

sns.pairplot(iris_data_clean, hue='class')  # 查看散點矩陣圖

從上圖可看到:

  1. 五個類變成三個類;
  2. 異常值全部刪除或修正了。

2. 聲明數據(assert data)

爲了防止一些數據問題沒有解決,我們可以用 assert 語句來做聲明。該語句好處是,在運行時如果聲明語句爲真,沒有任何事發生,反之會報錯而警告我們有哪些錯誤數據需要注意且修正。

# 聲明花只有三種類型
assert len(iris_data_clean['class'].unique()) == 3

# 聲明變色鳶尾花的萼片長度應該大於 2.5 釐米
assert iris_data_clean.loc[iris_data_clean['class'] == 'Iris-versicolor', 'sepal_length_cm'].min() >= 2.5

# 數據不應該有缺失
assert len(iris_data_clean.loc[(iris_data_clean['sepal_length_cm'].isnull()) |
                               (iris_data_clean['sepal_width_cm'].isnull()) |
                               (iris_data_clean['petal_length_cm'].isnull()) |
                               (iris_data_clean['petal_width_cm'].isnull())]) == 0

如果任何聲明被違反,我們應該立即停止分析,而回到整理階段。

 

三、用 scikit-learn 來預測數據

3.1 選出特徵 (輸入變量) 和標記 (輸出變量)

# 讀數據
iris_data_clean = pd.read_csv('data/iris-data-clean.csv')

# 選出特徵
all_inputs = iris_data_clean[['sepal_length_cm', 'sepal_width_cm',
                             'petal_length_cm', 'petal_width_cm']].values

# 選出標記
all_classes = iris_data_clean['class'].values

3.2 劃分訓練集和測試集

(training_inputs, test_inputs, training_classes, 
 test_classes) = train_test_split(all_inputs, all_classes, train_size=0.75, random_state=1)

用 75% 和 25% 的比例來劃分訓練集和測試集,設一個 random_state 是爲了在機器學習中每次出的結果一樣,便於找問題。

3.3 用模型來學習

# 創建決策樹分類器
decision_tree_classifier = DecisionTreeClassifier()

# 訓練數據
decision_tree_classifier.fit(training_inputs, training_classes)

# 分類精度
decision_tree_classifier.score(test_inputs, test_classes)
0.9736842105263158

再一次感嘆 scikit-learn 的強大和好用。

 

四、思考題

這時候有人會問了:

  1. 這個訓練集和測試集劃分是隨機的,一次不足以說明你查準率高;
  2. 這數據太少沒代表性,換套數據模型可能過擬合;
  3. 特徵選擇有很大學問,憑什麼就選萼片長度,萼片寬度,花瓣長度和花瓣寬度這四個變量?

有待學習,思考哦。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章