利用sklearn分析鸢尾花
前面两篇文章提到了机器学习的入门的几个基础库及拓展练习,现在我们就对前面知识点进行汇总进行一个简单的机器学习应用,并构建模型。
练习即假定一名植物专家收集了每一朵鸢尾花的测量数据:花瓣的长度和宽度以及花萼的长度和宽度,所有测量结果的单位都是厘米。这些数据经过植物学专家分类成三个种类:setosa、versicolor、virginica。然后根据测量数据,确定每朵鸢尾花所属的品种,在这里我们将构建一个机器学习模型,从已知的品种进行学习,从而能够预测新的品种。
在这篇博客里面,我们以鸢尾花数据集来测试,这个数据集包含在scikit-learn的datasets模块中,我们可以调用load_iris函数来加载数据:
from sklearn.datasets import load_iris iris_dataset = load_iris() print("Keys of iris_dataset:\n{}".format(iris_dataset.keys()))# load_iris返回的iris对象是一个Bunch对象,与字典非常相似,里面包含键和值Out[]: Keys of iris_dataset: dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names'])
DESCR键对应的值是数据集的简要说明,比如:
print(iris_dataset['DESCR'][:193] + "\n...") Out[]: Iris Plants Database ==================== Notes ----- Data Set Characteristics: :Number of Instances: 150 (50 in each of three classes) :Number of Attributes: 4 numeric, predictive att ...
而我们打印iris_dataset
可以看到,target_get键对应的是一个字符串数组,里面包含我们需要预测的花的品种。
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数组。
data数组的每一行对应一朵花,列代表每朵花的四个测量数据:
print("Shape of data:{}".format(iris_dataset['data'].shape)) Out[]:# 数组中包含150朵不同的花的测量数据,机器学习中的个体叫做样本,属性叫作特征,data数组的形状(shape)是样本数乘以特征数Shape of data:(150, 4)
我们可以输出前五个样本的特征数值:
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]]
我们还可以从target看到品种被转换成0到2的整数:
print("Target:\n{}".format(iris_dataset['target'])) Out[]:# 0-setosa, 1-versicolor, 2-virginicaTarget: [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]
在弄清数据模型里每个键的含义后,我们开始利用数据构建一个机器模型,用于预测欣测量的品种,通常来说,我们在测量新数据之前,要知道这个模型是否有效,而我们不能直接将构建的模型去判定新的问题,因为我们的模型会一直记住整个训练集,所以对于训练集中的任何数据点总会预测正确的标签。这种记忆无法告诉我们模型的泛化能力如何(新数据上能否正确预测)。
所以我们要用欣数据来评估模型的性能,新数据即之前没有出现过的数据,而我们有这些新数据的标签。通常的做法是将收集好的带标签的数据(150朵花的测量数据)分成两部分。一部分数据用于构建机器学习模型,叫作训练数据或训练集。其余数据用来评估模型性能,脚测试数据、测试集或留出集。
scikit-learn种的train_test_split函数可以打乱数据集并进行拆分。这个函数将75%的行数据及对应标签作为训练集,剩下25%的数据及其标签作为测试集。训练集与测试集分配比例可以随意分配。
scikit-learn种的数据通常用大写X表示,而标签用小写的y去表示。即我们数学函数里面f(x)=y, x是函数的输入,y是输出。我们用大写的X是因为数据是一个二维数组,用小写的y是因为目标是一个以为数组(向量)。
现在我们开始拆分数据:
# 对数据进行拆分之前,用train_test_split函数利用伪随机数生成器将数据集打乱。#因为如果将最后25%的数据作为测试集,那么所有数据点的标签都是2(因为数据点是按顺序排的)#所以打乱顺序确保测试集包含所有类别数据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)
为了确保多次运行同一函数能够得到相同的输出,我们利用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,)123456
# 测试集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,)
在划分好数据后,我们开始检查一下数据,检查数据的最佳方法之一就是将其可视化,一种可视化方法是绘制散点图。为了绘制这张图,我们首先将NumPy数组转化成pandas DataFrame。pandas有一个绘制散点图矩阵的函数,scatter_matrix。
#利用X_train中的数据创建DataFrame# 利用iris_dataset.fearure_names中的字符串对数据列进行标记import numpy as npimport pandas as pdimport mglearnfrom pandas import DataFrame iris_dataframe = pd.DataFrame(X_train, columns=iris_dataset.feature_names)# 利用DataFrame创建散点图矩阵,按y_train着色grr = pd.scatter_matrix(iris_dataframe, c=y_train, figsize=(15,15), marker='o', hist_kwds={'bins':20}, s=60, alpha=.8, cmap=mglearn.cm3) grr
我们可以看到数据集的散点图矩阵,按类别标签着色,花瓣和花萼的测量数据基本可以将三个类别区别开。
构建模型–K近邻算法
现在可以开始构建真实的机器学习模型了。scikit-learn中有许多可用的分类算法,这里我们使用k近邻分类器。构建此模型只需要保存训练集即可,要对一个新的数据点做出预测,算法会在训练集中寻找与这个新数据点距离最近的数据点,然后将找到的数据点的标签赋值给这个新数据点。
k邻近算法中k的含义是,我们可以考虑训练集中与新数据点最近的任意k个邻居(比如说,距离最近的3个或5个邻居),而不是只考虑最近的哪一个。然后我们可以用这些邻居中数量最多的类别做出预测。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=1, n_neighbors=1, p=2, weights='uniform')
从这里我们可以看到,fit方法返回的是knn对象本身并做原处修改,因此我们得到了分类器的字符串表示。从中我们可以看出构建模型时用到的参数。几乎所有参数都是默认值,但我们可以看到n_neighbors=1,这是我们传入的参数。
现在我们可以用这个模型对新数据进行预测了,我们可能并不知道这些新数据的正确标签。想象一下,我们在野外发现了鸢尾花,花萼长5cm,宽2.9cm,花瓣长1cm,宽0.2cm。这朵鸢尾花属于哪个品种?
我们可以将这些数据放在一个numpy数组中,再次计算形状,数组形状为样本数(1)乘以特征数(4):
X_new = np.array([[5, 2.9, 1, 0.2]]) print("X_new.shap:{}".format(X_new.shape)) Out[]: X_new.shap:(1, 4)
【注意】我们这朵花的测量数据转换为二维numpy数组的一行,这是因为scikit-learn的输入数据必须是二维数组。
接着我们调用knn对象的predict方法来进行预测:
prediction = knn.predict(X_new) print("Predicition{}".format(prediction)) print("Predicition target name:{}".format( iris_dataset['target_names'][prediction])) Out[]: Predicition[0] Predicition target name:['setosa']
根据模型的预测,我们可以看到这朵新的鸢尾花属于类别0,也就是说它属于setosa的品种。但是我们不知道能不能相信这个模型,也不知道这个样本实际品种。
所以这里就需要我们之前创建的测试集。这些数据没有用于构建模型,但我们知道测试集中每朵鸢尾花的实际品种。因此,我们可以对测试数据中的每朵鸢尾花进行预测,并将预测结果与标签(已知品种)进行对比。我们可以通过计算精度来衡量模型的准确,精度就是品种预测正确的花所占比例:
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
或者,我们可以用knn对象的score方法来计算测试集的准确度:
print("Test set score:{:.2f}".format(knn.score(X_test, y_test))) Out[]: Test set score:0.97
测试中我们可以看到,精度为97%,也就是说97%的正确率。至此,我们就完成来所有的对鸢尾花的分析、计算及预测。