您将把 KNN 应用于著名的 Iris 数据集。
注意 :您可能会考虑学习 Machine Learning with Python 课程;如果想了解 ML 的发展历程及更多内容,建议阅读这篇 文章。
简介
机器学习源自计算机科学,主要研究能从经验中学习的算法设计。要“学习”,算法需要具备某些属性的数据 ,并据此寻找有意义的预测模式。总体而言,ML 任务可分为概念学习、聚类、预测建模等。ML 算法的终极目标是能够在无人干预的情况下正确做出决策。预测股票或天气就是机器学习算法的两个应用场景。 常见的机器学习算法包括决策树、朴素贝叶斯、随机森林、支持向量机、K 近邻法、K-means 聚类等。 在这类算法中,今天您将使用的是 k 近邻法。
现在的问题是:K 近邻算法到底是什么?让我们来一探究竟!
什么是 k 近邻法?
KNN(k 近邻)是一种监督学习算法。“监督”意味着它在学习阶段会使用训练数据的类别标签。它是基于实例的机器学习算法,新数据点的分类是基于已存储的带标签实例(数据点)。KNN 可用于分类和回归;但更常用于分类。
KNN 中的 k 是一个关键变量,也称为超参数,有助于准确地对数据点进行分类。更准确地说,k 是您在为新数据点分类时希望投票的最近邻数量。
图 1. KNN 可视化 来源
可以看到,当 k 的值从 1 增加到 7 时,两个类别之间的决策边界会变得更平滑。
那么,这一切是如何实现的?每当有新数据点进来时,算法如何基于存储的数据点对其进行分类?
我们快速按以下步骤理解:
首先,加载所有数据并初始化 k 的值,
然后,使用多种相似度或距离度量(如曼哈顿距离 L1、欧氏距离 L2、余弦相似度、Bhattacharyya 距离、切比雪夫距离等)计算存储数据点与待分类新数据点之间的距离,
接着,将距离值按升序或降序排序,确定前或后 k 个最近邻,
收集这 k 个最近邻的标签,并使用多数投票或加权投票来对新数据点分类。新数据点将被赋予在所有存储数据点中得分最高的某一类别标签,
最后,返回该新实例的预测类别。
预测可分两类:分类 (为新数据点赋予一个类别标签)或回归 (为新数据点赋予一个数值)。不同于分类,在回归中会将 k 个最近邻的均值赋予新数据点。
KNN 的缺点 :其一,为每个新数据点搜索最近邻的计算复杂度较高。其二,确定合适的 k 值有时较为繁琐。其三,在计算最近邻时应使用哪种距离度量也并不总是清晰明确。
理论够多了吧?现在让我们加载、分析并理解今天小教程中将使用的数据。
加载 Iris 数据
Iris 数据集包含 150 个样本,有三个类别:Iris-Setosa、Iris-Versicolor 和 Iris-Virginica。用于唯一标识这三类的四个特征/属性是 sepal-length、sepal-width、petal-length 和 petal-width。
您也可以随意使用其他公开数据集或您的私有数据集。
Sklearn 是一个广泛用于数据科学任务的 Python 机器学习库,包含多种分类、回归和聚类算法,如 support vector machines、random forests、gradient boosting、k-means、KNN 等。在 sklearn 中有名为 datasets 的库,包含可直接加载用于不同任务的多个数据集,其中就包括 Iris。它非常直观易用。现在让我们快速加载 iris 数据集。
from sklearn. datasets import load_iris
技术支持
load_iris 同时包含数据和每个样本的类别标签。我们来快速提取它们。
data = load_iris( ) . data
技术支持
data 变量将是一个形状为 (150,4) 的 numpy 数组,包含 150 个样本,每个样本有四个不同属性。每个类别各有 50 个样本。
data. shape
技术支持
( 150 , 4 )
技术支持
我们来提取类别标签。
labels = load_iris( ) . target
技术支持
labels. shape
技术支持
( 150 , )
技术支持
接下来需要合并数据和类别标签,为此将使用一个出色的 Python 库 NumPy。NumPy 支持大型多维数组和矩阵,并提供丰富的高级数学函数来操作这些数组。我们先导入它!
import numpy as np
技术支持
由于 data 是二维数组,您也需要将 labels 重塑为二维数组。
labels = np. reshape( labels, ( 150 , 1 ) )
技术支持
现在使用 numpy 库中的 concatenate 函数,并设置 axis=-1,即按第二维进行拼接。
data = np. concatenate( [ data, labels] , axis= - 1 )
技术支持
data. shape
技术支持
( 150 , 5 )
技术支持
接下来导入 Python 的数据分析库 pandas,当您希望以表格方式组织数据并对其进行操作和处理时,它非常有用。尤其是它为数值表格和时间序列提供了数据结构与操作方法。 在今天的教程中,您将大量使用 pandas。
import pandas as pd
技术支持
names = [ 'sepal-length' , 'sepal-width' , 'petal-length' , 'petal-width' , 'species' ]
技术支持
dataset = pd. DataFrame( data, columns= names)
技术支持
现在,您已经得到了包含数据和类别标签的 dataset 数据框!
在继续之前,请记住 labels 变量中的类别标签是数值,您将把这些数值转换为花名或物种。
为此,您将仅选择 class 列,并将三种数值分别替换为对应的物种名称。您将使用 inplace=True,这会直接修改数据框 dataset。
dataset[ 'species' ] . replace( 0 , 'Iris-setosa' , inplace= True )
dataset[ 'species' ] . replace( 1 , 'Iris-versicolor' , inplace= True )
dataset[ 'species' ] . replace( 2 , 'Iris-virginica' , inplace= True )
技术支持
我们打印 dataset 的前五行,看看它长什么样!
dataset. head( 5 )
技术支持
sepal-length sepal-width petal-length petal-width species 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
分析您的数据
我们快速看看三种花在可视化后的表现,以及它们彼此之间不仅在数值上、在现实中有多不同!
(来源 )
让我们使用 scatterplot 可视化上面加载的数据,看看一个变量对另一个变量的影响程度,或者说这两个变量之间的相关性有多大。
您将使用 matplotlib 库通过散点图来可视化数据。
import matplotlib. pyplot as plt
技术支持
提示 :想系统学习在 Python 中可视化数据的不同方法?请查看课程 Introduction to data visualization with matplotlib 。
plt. figure( 4 , figsize= ( 10 , 8 ) )
plt. scatter( data[ : 50 , 0 ] , data[ : 50 , 1 ] , c= 'r' , label= 'Iris-setosa' )
plt. scatter( data[ 50 : 100 , 0 ] , data[ 50 : 100 , 1 ] , c= 'g' , label= 'Iris-versicolor' )
plt. scatter( data[ 100 : , 0 ] , data[ 100 : , 1 ] , c= 'b' , label= 'Iris-virginica' )
plt. xlabel( 'Sepal length' , fontsize= 20 )
plt. ylabel( 'Sepal width' , fontsize= 20 )
plt. xticks( fontsize= 20 )
plt. yticks( fontsize= 20 )
plt. title( 'Sepal length vs. Sepal width' , fontsize= 20 )
plt. legend( prop= { 'size' : 18 } )
plt. show( )
技术支持
从上图可以明显看出,Iris setosa 的萼片长度与萼片宽度之间具有较高的相关性。另一方面,Iris versicolor 与 Iris virginica 之间的相关性较弱。与更为密集的 setosa 相比,versicolor 和 virginica 的数据点分布更分散。
我们再快速绘制一下 petal-length 与 petal-width 的图。
plt. figure( 4 , figsize= ( 8 , 8 ) )
plt. scatter( data[ : 50 , 2 ] , data[ : 50 , 3 ] , c= 'r' , label= 'Iris-setosa' )
plt. scatter( data[ 50 : 100 , 2 ] , data[ 50 : 100 , 3 ] , c= 'g' , label= 'Iris-versicolor' )
plt. scatter( data[ 100 : , 2 ] , data[ 100 : , 3 ] , c= 'b' , label= 'Iris-virginica' )
plt. xlabel( 'Petal length' , fontsize= 15 )
plt. ylabel( 'Petal width' , fontsize= 15 )
plt. xticks( fontsize= 15 )
plt. yticks( fontsize= 15 )
plt. title( 'Petal length vs. Petal width' , fontsize= 15 )
plt. legend( prop= { 'size' : 20 } )
plt. show( )
技术支持
在 petal-length 和 petal-width 上,上图同样表明 setosa 花呈现出强相关性,且高度聚集。
接下来,为了进一步验证 petal-length 与 petal-width 的相关性,我们针对三个物种绘制相关矩阵。
dataset. iloc[ : , 2 : ] . corr( )
技术支持
petal-length petal-width petal-length 1.000000 0.962865 petal-width 0.962865 1.000000
上表显示,当将三个物种合并时,petal-length 与 petal-width 的强相关性为 0.96。
我们再分别分析三个物种之间的相关性。
dataset. iloc[ : 50 , : ] . corr( )
技术支持
sepal-length sepal-width petal-length petal-width sepal-length 1.000000 0.742547 0.267176 0.278098 sepal-width 0.742547 1.000000 0.177700 0.232752 petal-length 0.267176 0.177700 1.000000 0.331630 petal-width 0.278098 0.232752 0.331630 1.000000
dataset. iloc[ 50 : 100 , : ] . corr( )
技术支持
sepal-length sepal-width petal-length petal-width sepal-length 1.000000 0.525911 0.754049 0.546461 sepal-width 0.525911 1.000000 0.560522 0.663999 petal-length 0.754049 0.560522 1.000000 0.786668 petal-width 0.546461 0.663999 0.786668 1.000000
dataset. iloc[ 100 : , : ] . corr( )
技术支持
sepal-length sepal-width petal-length petal-width sepal-length 1.000000 0.457228 0.864225 0.281108 sepal-width 0.457228 1.000000 0.401045 0.537728 petal-length 0.864225 0.401045 1.000000 0.322108 petal-width 0.281108 0.537728 0.322108 1.000000
从上面三张表可以清楚看出,setosa 与 virginica 的 petal-length 与 petal-width 的相关性分别为 0.33 和 0.32,而 versicolor 为 0.78。
接下来,通过绘制直方图来可视化特征分布:
fig = plt. figure( figsize = ( 8 , 8 ) )
ax = fig. gca( )
dataset. hist( ax= ax)
plt. show( )
技术支持
petal-length、petal-width 和 sepal-length 呈单峰分布,而 sepal-width 呈近似高斯分布。这些分析很有用,因为您可以据此考虑使用在这类分布下表现良好的算法。
接下来分析四个属性是否在同一量纲上;这对 ML 至关重要。pandas 数据框有一个内置函数 describe,可以表格形式给出数据的 count、mean、max、min。
dataset. describe( )
技术支持
sepal-length sepal-width petal-length petal-width count 150.000000 150.000000 150.000000 150.000000 mean 5.843333 3.057333 3.758000 1.199333 std 0.828066 0.435866 1.765298 0.762238 min 4.300000 2.000000 1.000000 0.100000 25% 5.100000 2.800000 1.600000 0.300000 50% 5.800000 3.000000 4.350000 1.300000 75% 6.400000 3.300000 5.100000 1.800000 max 7.900000 4.400000 6.900000 2.500000
您可以看到四个属性的量纲相近,介于 0 与 8 之间,单位为厘米;如有需要,也可进一步缩放到 0 到 1。
虽然我们知道每个类别有 50 个样本,即约占总分布的 33.3%,但我们还是再核对一下!
print ( dataset. groupby( 'species' ) . size( ) )
技术支持
species
Iris- setosa 50
Iris- versicolor 50
Iris- virginica 50
dtype : int64
技术支持
数据预处理
在加载并充分分析数据之后,是时候准备将数据馈入 ML 模型了。本节将从两方面预处理数据:数据归一化,以及将数据划分为训练集与测试集。
数据归一化
归一化数据主要有两种方式:
样本归一化 :对每个样本单独归一化,
特征归一化 :对每个特征在所有样本上以相同方式归一化。
那么,为什么或何时需要归一化数据?对于 Iris 数据是否需要标准化?
答案基本上是:大多数时候。良好的实践是对数据做归一化,以将所有样本带到相同的量纲与范围。当数据不一致时,归一化尤为关键。您可以使用上文提到的 describe() 函数检查不一致性,它会给出 max 与 min。如果某个特征的 max 和 min 明显大于另一个特征,那么将二者归一化到相同量纲非常重要。 假设 X 是范围更大的特征,Y 是范围更小的特征,那么 Y 的影响可能会被 X 的影响所淹没。在这种情况下,对 X 和 Y 进行归一化很重要。
对于 Iris 数据,并不需要归一化。
我们再打印一次 describe() 的结果,看看为何无需归一化。
dataset. describe( )
技术支持
sepal-length sepal-width petal-length petal-width count 150.000000 150.000000 150.000000 150.000000 mean 5.843333 3.057333 3.758000 1.199333 std 0.828066 0.435866 1.765298 0.762238 min 4.300000 2.000000 1.000000 0.100000 25% 5.100000 2.800000 1.600000 0.300000 50% 5.800000 3.000000 4.350000 1.300000 75% 6.400000 3.300000 5.100000 1.800000 max 7.900000 4.400000 6.900000 2.500000
sepal-length 的取值范围为 4.3 到 7.9,sepal-width 为 2 到 4.4,petal-length 为 1 到 6.9,petal-width 为 0.1 到 2.5。所有特征的取值都在 0.1 到 7.9 之间,属于可接受范围。因此,无需对 Iris 数据集做归一化。
数据划分
这是机器学习的另一重要环节,因为我们的目标是让模型在测试环境下无需人工干预即可做出决策或完成分类。因此,在将 ML 模型投入生产前,需要确保模型能在测试数据上具有良好的泛化能力。 为此,您需要训练集与测试集。回到 Iris 数据,您共有 150 个样本,将用 80% 的数据训练模型,剩余 20% 用于测试。
在数据科学中,您会经常看到一个术语 Overfitting(过拟合),它指模型对训练数据学得很好,但在测试数据上表现不佳。因此,将数据划分为训练集与测试集或验证集,通常有助于判断模型是否过拟合。
用于训练/测试划分,您将使用 sklearn 库的内置函数 train_test_split。我们来进行划分。
from sklearn. model_selection import train_test_split
train_data, test_data, train_label, test_label = train_test_split( dataset. iloc[ : , : 3 ] , dataset. iloc[ : , 3 ] , test_size= 0.2 , random_state= 42 ) 技术支持
注意,random_state 是一个种子,输入不同的 random_state 值会导致不同的数据划分。不过,如果保持 random_state 不变,多次运行结果也会保持不变。
我们快速打印训练与测试数据及其标签的形状。
train_data. shape, train_label. shape, test_data. shape, test_label. shape
技术支持
( ( 120 , 3 ) , ( 120 , ) , ( 30 , 3 ) , ( 30 , ) )
技术支持
最后,是时候将数据馈入 k 近邻算法了!
KNN 模型
在完成数据的加载、分析和预处理之后,现在将数据输入 KNN 模型。为此,您将使用 sklearn 的内置模块 neighbors 中的 KNeigborsClassifier 类。
先导入分类器。
from sklearn. neighbors import KNeighborsClassifier
技术支持
注意 :k(n_neighbors)参数通常取奇数,以避免投票分数出现平局。
为确定超参数 k 的最佳取值,您将进行所谓的 grid-search。我们将在 10 个不同的 k 值上训练与测试模型,最终选取效果最佳的那个。
初始化变量 neighbors(k),其取值范围 1-9,并各创建一个 numpy 的零矩阵 train_accuracy 与 test_accuracy,分别用于记录训练与测试精度。稍后将用它们绘图以选择最佳 neighbor 值。
neighbors = np. arange( 1 , 9 )
train_accuracy = np. zeros( len ( neighbors) )
test_accuracy = np. zeros( len ( neighbors) )
技术支持
接下来的代码就是“魔法”发生之处。我们将 enumerate 遍历九个邻居数,对每个邻居数分别在训练集与测试集上做预测,最终将精度存入 train_accuracy 与 test_accuracy 数组。
for i, k in enumerate ( neighbors) :
knn = KNeighborsClassifier( n_neighbors= k)
knn. fit( train_data, train_label)
train_accuracy[ i] = knn. score( train_data, train_label)
test_accuracy[ i] = knn. score( test_data, test_label)
技术支持
接下来使用 matplotlib 绘制训练与测试精度。通过 accuracy vs. varying number of neighbors 的图,您可以选择模型表现最佳的 k 值。
plt. figure( figsize= ( 10 , 6 ) )
plt. title( 'KNN accuracy with varying number of neighbors' , fontsize= 20 )
plt. plot( neighbors, test_accuracy, label= 'Testing Accuracy' )
plt. plot( neighbors, train_accuracy, label= 'Training accuracy' )
plt. legend( prop= { 'size' : 20 } )
plt. xlabel( 'Number of neighbors' , fontsize= 20 )
plt. ylabel( 'Accuracy' , fontsize= 20 )
plt. xticks( fontsize= 20 )
plt. yticks( fontsize= 20 )
plt. show( )
技术支持
从上图来看,当 n_neighbors=3 时,模型在训练与测试上表现都最好。因此我们固定使用 n_neighbors=3 并重新训练一次。
knn = KNeighborsClassifier( n_neighbors= 3 )
knn. fit( train_data, train_label)
train_accuracy = knn. score( train_data, train_label)
test_accuracy = knn. score( test_data, test_label)
技术支持
评估您的模型
在本教程的最后一部分,您将使用 confusion_matrix 和 classification_report 等方法在测试数据上评估模型。
先来查看模型在测试数据上的准确率。
test_accuracy
技术支持
0.9666666666666667
技术支持
太棒了!模型似乎能够以 96.66% 的准确率正确分类测试数据。是不是很惊人?只用几行代码,您就训练出了一个 ML 模型,它现在仅用四个特征就能以 96.66% 的准确率告诉您花的名称。或许它的表现已经远超人类。
混淆矩阵
混淆矩阵主要用于描述模型在测试数据(其真实值或标签已知)上的表现。
Scikit-learn 提供了用于计算混淆矩阵的函数。
prediction = knn. predict( test_data)
技术支持
下面的 plot_confusion_matrix() 函数已根据该来源 进行修改并引用。
import itertools
def plot_confusion_matrix ( cm, classes,
normalize= False ,
title= 'Confusion matrix' ,
cmap= plt. cm. Blues) :
plt. imshow( cm, interpolation= 'nearest' , cmap= cmap)
plt. title( title)
plt. colorbar( )
tick_marks = np. arange( len ( classes) )
plt. xticks( tick_marks, classes, rotation= 45 )
plt. yticks( tick_marks, classes)
fmt = '.2f' if normalize else 'd'
thresh = cm. max ( ) / 2.
for i, j in itertools. product( range ( cm. shape[ 0 ] ) , range ( cm. shape[ 1 ] ) ) :
plt. text( j, i, format ( cm[ i, j] , fmt) ,
horizontalalignment= "center" ,
color= "white" if cm[ i, j] > thresh else "black" )
plt. ylabel( 'True label' , fontsize= 30 )
plt. xlabel( 'Predicted label' , fontsize= 30 )
plt. tight_layout( )
plt. xticks( fontsize= 18 )
plt. yticks( fontsize= 18 )
class_names = load_iris( ) . target_names
cnf_matrix = confusion_matrix( test_label, prediction)
np. set_printoptions( precision= 2 )
plt. figure( figsize= ( 10 , 8 ) )
plot_confusion_matrix( cnf_matrix, classes= class_names)
plt. title( 'Confusion Matrix' , fontsize= 30 )
plt. show( )
技术支持
从上述 confusion_matrix 图可以看到,除了一个 virginica 被分类为 versicolor 外,其余花朵均被正确分类。
分类报告
分类报告通过给出每个类别的 precision、recall 和 F1 score,帮助您更细致地识别被误分类的类别。我们将使用 sklearn 库来查看分类报告。
from sklearn. metrics import classification_report
技术支持
print ( classification_report( test_label, prediction) )
技术支持
precision recall f1- score support
Iris- setosa 1.00 1.00 1.00 10
Iris- versicolor 0.90 1.00 0.95 9
Iris- virginica 1.00 0.91 0.95 11
micro avg 0.97 0.97 0.97 30
macro avg 0.97 0.97 0.97 30
weighted avg 0.97 0.97 0.97 30
技术支持
更进一步!
首先,恭喜所有坚持到最后的您!但这只是开始,前方还有很长的路。 本教程主要讲解了机器学习基础,以及一种 ML 算法(KNN)在 Python 中的实现。您使用的 Iris 数据集规模较小,也相对简单。 如果本教程激发了您继续学习的兴趣,您可以尝试使用其他数据集,或学习更多 ML 算法,并将其应用到 Iris 数据集上,观察对准确率的影响。通过这种方式,您将学到远不止理论! 如果您已充分实践本教程介绍的基础内容及其他机器学习算法,不妨在 Python 与数据分析方面继续进阶。
您将把 KNN 应用于著名的 Iris 数据集。
注意:您可能会考虑学习 Machine Learning with Python 课程;如果想了解 ML 的发展历程及更多内容,建议阅读这篇文章。
简介
机器学习源自计算机科学,主要研究能从经验中学习的算法设计。要“学习”,算法需要具备某些属性的数据,并据此寻找有意义的预测模式。总体而言,ML 任务可分为概念学习、聚类、预测建模等。ML 算法的终极目标是能够在无人干预的情况下正确做出决策。预测股票或天气就是机器学习算法的两个应用场景。
常见的机器学习算法包括决策树、朴素贝叶斯、随机森林、支持向量机、K 近邻法、K-means 聚类等。
在这类算法中,今天您将使用的是 k 近邻法。
现在的问题是:K 近邻算法到底是什么?让我们来一探究竟!
什么是 k 近邻法?
KNN(k 近邻)是一种监督学习算法。“监督”意味着它在学习阶段会使用训练数据的类别标签。它是基于实例的机器学习算法,新数据点的分类是基于已存储的带标签实例(数据点)。KNN 可用于分类和回归;但更常用于分类。
KNN 中的 k 是一个关键变量,也称为超参数,有助于准确地对数据点进行分类。更准确地说,k 是您在为新数据点分类时希望投票的最近邻数量。
可以看到,当 k 的值从 1 增加到 7 时,两个类别之间的决策边界会变得更平滑。
那么,这一切是如何实现的?每当有新数据点进来时,算法如何基于存储的数据点对其进行分类?
我们快速按以下步骤理解:
预测可分两类:分类(为新数据点赋予一个类别标签)或回归(为新数据点赋予一个数值)。不同于分类,在回归中会将 k 个最近邻的均值赋予新数据点。
KNN 的缺点:其一,为每个新数据点搜索最近邻的计算复杂度较高。其二,确定合适的 k 值有时较为繁琐。其三,在计算最近邻时应使用哪种距离度量也并不总是清晰明确。
理论够多了吧?现在让我们加载、分析并理解今天小教程中将使用的数据。
加载 Iris 数据
Iris 数据集包含 150 个样本,有三个类别:
Iris-Setosa、Iris-Versicolor和Iris-Virginica。用于唯一标识这三类的四个特征/属性是sepal-length、sepal-width、petal-length和petal-width。您也可以随意使用其他公开数据集或您的私有数据集。
Sklearn是一个广泛用于数据科学任务的 Python 机器学习库,包含多种分类、回归和聚类算法,如support vector machines、random forests、gradient boosting、k-means、KNN等。在sklearn中有名为datasets的库,包含可直接加载用于不同任务的多个数据集,其中就包括Iris。它非常直观易用。现在让我们快速加载iris数据集。load_iris同时包含数据和每个样本的类别标签。我们来快速提取它们。data变量将是一个形状为 (150,4) 的 numpy 数组,包含 150 个样本,每个样本有四个不同属性。每个类别各有 50 个样本。我们来提取类别标签。
接下来需要合并数据和类别标签,为此将使用一个出色的 Python 库
NumPy。NumPy支持大型多维数组和矩阵,并提供丰富的高级数学函数来操作这些数组。我们先导入它!由于
data是二维数组,您也需要将labels重塑为二维数组。现在使用
numpy库中的concatenate函数,并设置axis=-1,即按第二维进行拼接。接下来导入 Python 的数据分析库
pandas,当您希望以表格方式组织数据并对其进行操作和处理时,它非常有用。尤其是它为数值表格和时间序列提供了数据结构与操作方法。在今天的教程中,您将大量使用
pandas。现在,您已经得到了包含数据和类别标签的
dataset数据框!在继续之前,请记住
labels变量中的类别标签是数值,您将把这些数值转换为花名或物种。为此,您将仅选择
class列,并将三种数值分别替换为对应的物种名称。您将使用inplace=True,这会直接修改数据框dataset。我们打印
dataset的前五行,看看它长什么样!分析您的数据
我们快速看看三种花在可视化后的表现,以及它们彼此之间不仅在数值上、在现实中有多不同!
让我们使用
scatterplot可视化上面加载的数据,看看一个变量对另一个变量的影响程度,或者说这两个变量之间的相关性有多大。您将使用
matplotlib库通过散点图来可视化数据。提示:想系统学习在 Python 中可视化数据的不同方法?请查看课程 Introduction to data visualization with matplotlib。
从上图可以明显看出,Iris setosa 的萼片长度与萼片宽度之间具有较高的相关性。另一方面,Iris versicolor 与 Iris virginica 之间的相关性较弱。与更为密集的 setosa 相比,versicolor 和 virginica 的数据点分布更分散。
我们再快速绘制一下
petal-length与petal-width的图。在
petal-length和petal-width上,上图同样表明 setosa 花呈现出强相关性,且高度聚集。接下来,为了进一步验证
petal-length与petal-width的相关性,我们针对三个物种绘制相关矩阵。上表显示,当将三个物种合并时,
petal-length与petal-width的强相关性为0.96。我们再分别分析三个物种之间的相关性。
从上面三张表可以清楚看出,
setosa与virginica的petal-length与petal-width的相关性分别为0.33和0.32,而versicolor为0.78。接下来,通过绘制直方图来可视化特征分布:
petal-length、petal-width和sepal-length呈单峰分布,而sepal-width呈近似高斯分布。这些分析很有用,因为您可以据此考虑使用在这类分布下表现良好的算法。接下来分析四个属性是否在同一量纲上;这对 ML 至关重要。
pandas数据框有一个内置函数describe,可以表格形式给出数据的count、mean、max、min。您可以看到四个属性的量纲相近,介于 0 与 8 之间,单位为厘米;如有需要,也可进一步缩放到 0 到 1。
虽然我们知道每个类别有 50 个样本,即约占总分布的 33.3%,但我们还是再核对一下!
数据预处理
在加载并充分分析数据之后,是时候准备将数据馈入 ML 模型了。本节将从两方面预处理数据:数据归一化,以及将数据划分为训练集与测试集。
数据归一化
归一化数据主要有两种方式:
那么,为什么或何时需要归一化数据?对于
Iris数据是否需要标准化?答案基本上是:大多数时候。良好的实践是对数据做归一化,以将所有样本带到相同的量纲与范围。当数据不一致时,归一化尤为关键。您可以使用上文提到的
describe()函数检查不一致性,它会给出max与min。如果某个特征的max和min明显大于另一个特征,那么将二者归一化到相同量纲非常重要。假设 X 是范围更大的特征,Y 是范围更小的特征,那么 Y 的影响可能会被 X 的影响所淹没。在这种情况下,对 X 和 Y 进行归一化很重要。
对于
Iris数据,并不需要归一化。我们再打印一次
describe()的结果,看看为何无需归一化。sepal-length的取值范围为 4.3 到 7.9,sepal-width为 2 到 4.4,petal-length为 1 到 6.9,petal-width为 0.1 到 2.5。所有特征的取值都在 0.1 到 7.9 之间,属于可接受范围。因此,无需对Iris数据集做归一化。数据划分
这是机器学习的另一重要环节,因为我们的目标是让模型在测试环境下无需人工干预即可做出决策或完成分类。因此,在将 ML 模型投入生产前,需要确保模型能在测试数据上具有良好的泛化能力。
为此,您需要训练集与测试集。回到
Iris数据,您共有 150 个样本,将用 80% 的数据训练模型,剩余 20% 用于测试。在数据科学中,您会经常看到一个术语
Overfitting(过拟合),它指模型对训练数据学得很好,但在测试数据上表现不佳。因此,将数据划分为训练集与测试集或验证集,通常有助于判断模型是否过拟合。用于训练/测试划分,您将使用
sklearn库的内置函数train_test_split。我们来进行划分。注意,
random_state是一个种子,输入不同的random_state值会导致不同的数据划分。不过,如果保持random_state不变,多次运行结果也会保持不变。我们快速打印训练与测试数据及其标签的形状。
最后,是时候将数据馈入 k 近邻算法了!
KNN 模型
在完成数据的加载、分析和预处理之后,现在将数据输入 KNN 模型。为此,您将使用
sklearn的内置模块neighbors中的KNeigborsClassifier类。先导入分类器。
注意:k(n_neighbors)参数通常取奇数,以避免投票分数出现平局。
为确定超参数 k 的最佳取值,您将进行所谓的
grid-search。我们将在 10 个不同的 k 值上训练与测试模型,最终选取效果最佳的那个。初始化变量
neighbors(k),其取值范围 1-9,并各创建一个 numpy 的零矩阵train_accuracy与test_accuracy,分别用于记录训练与测试精度。稍后将用它们绘图以选择最佳neighbor值。接下来的代码就是“魔法”发生之处。我们将
enumerate遍历九个邻居数,对每个邻居数分别在训练集与测试集上做预测,最终将精度存入train_accuracy与test_accuracy数组。接下来使用
matplotlib绘制训练与测试精度。通过accuracy vs. varying number of neighbors的图,您可以选择模型表现最佳的 k 值。从上图来看,当
n_neighbors=3时,模型在训练与测试上表现都最好。因此我们固定使用n_neighbors=3并重新训练一次。评估您的模型
在本教程的最后一部分,您将使用
confusion_matrix和classification_report等方法在测试数据上评估模型。先来查看模型在测试数据上的准确率。
太棒了!模型似乎能够以 96.66% 的准确率正确分类测试数据。是不是很惊人?只用几行代码,您就训练出了一个 ML 模型,它现在仅用四个特征就能以 96.66% 的准确率告诉您花的名称。或许它的表现已经远超人类。
混淆矩阵
混淆矩阵主要用于描述模型在测试数据(其真实值或标签已知)上的表现。
Scikit-learn提供了用于计算混淆矩阵的函数。下面的
plot_confusion_matrix()函数已根据该来源进行修改并引用。从上述
confusion_matrix图可以看到,除了一个 virginica 被分类为 versicolor 外,其余花朵均被正确分类。分类报告
分类报告通过给出每个类别的
precision、recall和F1 score,帮助您更细致地识别被误分类的类别。我们将使用sklearn库来查看分类报告。更进一步!
首先,恭喜所有坚持到最后的您!但这只是开始,前方还有很长的路。
本教程主要讲解了机器学习基础,以及一种 ML 算法(KNN)在 Python 中的实现。您使用的 Iris 数据集规模较小,也相对简单。
如果本教程激发了您继续学习的兴趣,您可以尝试使用其他数据集,或学习更多 ML 算法,并将其应用到 Iris 数据集上,观察对准确率的影响。通过这种方式,您将学到远不止理论!
如果您已充分实践本教程介绍的基础内容及其他机器学习算法,不妨在 Python 与数据分析方面继续进阶。