| 1.1 引言
前面我们粗浅地介绍了机器学习中的监督学习、无监督学习、分类、聚类等概念。参见---《机器学习---概述》。同时介绍了机器学习中数据及数据的处理过程---《机器学习---数据简介及数据清洗概述》,今天我们来介绍或许在入门机器学习过程中每个人都可能会接触的一个项目---鸢尾花分类。我们会构建一个简单的机器学习应用,并构建我们的第一个模型。同时还将介绍一些核心概念和术语。
| 2.1 机器学习常用库
我们之前介绍过Numpy、Pandas、Matplotlib。如果忘记了或者没学过可以参考---《数据科学---使用Numpy进行数学计算》、《数据科学---使用Pandas进行操作数据》、《数据科学---使用Matplotlib进行数据可视化》。
Scikit-Learn:Scikit-learn(曾叫做scikits.learn还叫做sklearn)是用于Python编程语言的自由软件机器学习库。它的特征是具有各种分类、回归和聚类算法,包括支持向量机、随机森林、梯度提升、k-平均聚类和DBSCAN,它被设计协同于Python数值和科学库NumPy和SciPy。推荐安装使用Anaconda,它包含了大部分的机器学习工具(anaconda安装方法请自行求助搜索引擎)。
| 3.1 鸢尾花分类
语境:
鸢尾花数据集是英国统计学家和生物学家 Ronald Fisher 在其 1936 年的论文 The use of multiple measure in taxonomic questions 中引入的多元数据集。它有时被称为安德森鸢尾花数据集,因为埃德加安德森收集数据以量化三种相关物种鸢尾花的形态变异。该数据集包含来自三种鸢尾花(鸢尾花Setosa、维吉尼亚鸢尾virginica和杂色鸢尾versicolor)中的每种鸢尾花的 50 个样本。从每个样本中测量了四个特征:萼片和花瓣的长度和宽度,以厘米为单位。
我们的目标是构建一个机器学习模型,可以从这些已知品种的鸢尾花测量数据中进行学习,从而能够预测新鸢尾花的品种
※※※鸢尾花分类步骤:
1. 加载数据
2. 分析和可视化数据集
3. 模型训练。
4. 做出预测。
5. 评估模型。
| 3.11 加载数据
本例中我们用到了鸢尾花(Iris)数据集,这是机器学习和统计学中一个经典的数据集。它包含在 scikit-learn 的 datasets 模块中。我们可以调用 load_iris 函数来加载数据:
from sklearn.datasets import load_iris
iris_dataset = load_iris()
该数据集包含 5 个属性下的 150 条记录 - 花瓣长度、花瓣宽度、萼片长度、萼片宽度和类别(物种)。
load_iris 返回的 iris 对象是一个 Bunch 对象,与字典非常相似,里面包含键和值:
我们可以尝试打印一下:
DESCR键对应的值是数据集的简要说明。我们这里给出说明的开头部分(你可以自己查看其余的内容):
print(iris_dataset['DESCR'][:193] + "\n...")
target_names键对应的值是一个字符串数组,里面包含我们要预测的花的品种:
print("Target names: {}".format(iris_dataset['target_names']))
feature_names 键对应的值是一个字符串列表,对每一个特征进行了说明:
print("Feature names: \n{}".format(iris_dataset['feature_names']))
数据包含在 target 和 data 字段中。data 里面是花萼长度、花萼宽度、花瓣长度、花瓣宽度的测量数据,格式为 NumPy 数组:
print("Type of data: {}".format(type(iris_dataset['data'])))
data 数组的每一行对应一朵花,列代表每朵花的四个测量数据:
print("Shape of data: {}".format(iris_dataset['data'].shape))
可以看出,数组中包含 150 朵不同的花的测量数据。前面说过,机器学习中的个体叫作样 本(sample),其属性叫作特征(feature)。data 数组的形状(shape)是样本数乘以特征数。这是 scikit-learn 中的约定,你的数据形状应始终遵循这个约定。下面给出前 5 个样本的特征数值:
print("First five rows of data:\n{}".format(iris_dataset['data'][:5]))
从数据中可以看出,前 5 朵花的花瓣宽度都是 0.2cm,第一朵花的花萼最长,是 5.1cm。
target 数组包含的是测量过的每朵花的品种,也是一个 NumPy 数组:
print("Type of target: {}".format(type(iris_dataset['target'])))
target 是一维数组,每朵花对应其中一个数据
print("Shape of target: {}".format(iris_dataset['target'].shape))
品种被转换成从 0 到 2 的整数:
print("Target:\n{}".format(iris_dataset['target']))
Tips: 上述数字的代表含义由 iris['target_names'] 数组给出:0 代表 setosa,1 代表 versicolor, 2 代表 virginica。
| 3.12 分析和可视化数据集
scikit-learn 中的 train_test_split 函数可以打乱数据集并进行拆分。这个函数将 75% 的行数据及对应标签作为训练集,剩下 25% 的数据及其标签作为测试集。训练集与测试集的分配比例可以是随意的,但使用 25% 的数据作为测试集是很好的经验法则。scikit-learn 中的数据通常用大写的 X 表示,而标签用小写的 y 表示。这是受到了数学标准公式 f(x)=y 的启发,其中 x 是函数的输入,y 是输出。我们用大写的 X 是因为数据是一个二维数组(矩阵),用小写的 y 是因为目标是一个一维数组(向量),这也是数学中的约定。
在对数据进行拆分之前,train_test_split 函数利用伪随机数生成器将数据集打乱。如果我们只是将最后 25% 的数据作为测试集,那么所有数据点的标签都是 2,因为数据点是按标签排序的(参见之前 iris['target'] 的输出)。测试集中只有三个类别之一,这无法告诉我们模型的泛化能力如何,所以我们将数据打乱,确保测试集中包含所有类别的数据。
Tips : 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))
print("X_test shape: {}".format(X_test.shape))
print("y_test shape: {}".format(y_test.shape))
绘制训练集中特征的散点图矩阵。数据点的颜色与鸢尾花的品种相对应。为了绘制这张图,我们首先将 NumPy 数组转换成 pandas DataFrame。pandas 有一个绘制散点图矩阵的函数,叫作 scatter_matrix。矩阵的对角线是每个特征的直方图:
iris_dataframe = pd.DataFrame(X_train, columns=iris_dataset.feature_names)
grr = pd.plotting.scatter_matrix(iris_dataframe, c=y_train, figsize=(15, 15), marker='o',
hist_kwds={'bins': 20}, s=60, alpha=.8)
从图中可以看出,利用花瓣和花萼的测量数据基本可以将三个类别区分开。这说明机器学习模型很可能可以学会区分它们。
| 3.13 模型训练---KNN
现在我们可以开始构建真实的机器学习模型了。scikit-learn 中有许多可用的分类算法。这里我们用的是 k 近邻( k-nearest neighbors algorithm)分类器,这是一个很容易理解的算法。构建此模型只需要保存训练集即可。要对一个新的数据点做出预测,算法会在训练集中寻找与这个新数据点距离最近的数据点,然后将找到的数据点的标签赋值给这个新数据点。
k 近邻算法中 k 的含义是,我们可以考虑训练集中与新数据点最近的任意 k 个邻居(比如说,距离最近的 3 个或 5 个邻居),而不是只考虑最近的那一个。然后,我们可以用这些邻居中数量最多的类别做出预测。后面会进一步介绍这个算法的细节,现在我们只考虑一个邻居的情况。scikit-learn 中所有的机器学习模型都在各自的类中实现,这些类被称为 Estimator类。k 近邻分类算法是在 neighbors 模块的 KNeighborsClassifier 类中实现的。我们需要将这个类实例化为一个对象,然后才能使用这个模型。这时我们需要设置模型的参数。KNeighborsClassifier 最重要的参数就是邻居的数目,这里我们设为 1:
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=1)
想要基于训练集来构建模型,需要调用 knn 对象的 fit 方法,输入参数为 X_train 和 y_train,二者都是 NumPy 数组,前者包含训练数据,后者包含相应的训练标签:
knn.fit(X_train, y_train)
| 3.14 做出预测
现在我们可以用这个模型对新数据进行预测了,我们可能并不知道这些新数据的正确标签。想象一下,我们在野外发现了一朵鸢尾花,花萼长 5cm 宽 2.9cm,花瓣长 1cm 宽0.2cm。这朵鸢尾花属于哪个品种?我们可以将这些数据放在一个 NumPy 数组中,再次计算形状,数组形状为样本数(1)乘以特征数(4):
X_new = np.array([[5, 2.9, 1, 0.2]])
print("X_new.shape: {}".format(X_new.shape))
注意,我们将这朵花的测量数据转换为二维 NumPy 数组的一行,这是因为 scikit-learn的输入数据必须是二维数组。我们调用 knn 对象的 predict 方法来进行预测:
prediction = knn.predict(X_new)
print("Prediction: {}".format(prediction))
print("Predicted target name: {}".format(
iris_dataset['target_names'][prediction]))
根据我们模型的预测,这朵新的鸢尾花属于类别 0,也就是说它属于 setosa 品种。但我们怎么知道能否相信这个模型呢?我们并不知道这个样本的实际品种,这也是我们构建模型的重点啊!
| 3.15 评估模型
这里需要用到之前创建的测试集。这些数据没有用于构建模型,但我们知道测试集中每朵鸢尾花的实际品种。因此,我们可以对测试数据中的每朵鸢尾花进行预测,并将预测结果与标签(已知的品种)进行对比。我们可以通过计算精度(accuracy)来衡量模型的优劣,精度就是品种预测正确的花所占的比例:
print("Test set predictions:\n {}".format(y_pred))
print("Test set score: {:.2f}".format(np.mean(y_pred == y_test)))
我们还可以使用 knn 对象的 score 方法来计算测试集的精度:
print("Test set score: {:.2f}".format(knn.score(X_test, y_test)))
对于这个模型来说,测试集的精度约为 0.97,也就是说,对于测试集中的鸢尾花,我们的预测有 97% 是正确的。根据一些数学假设,对于新的鸢尾花,可以认为我们的模型预测结果有 97% 都是正确的。对于我们的植物学爱好者应用程序来说,高精度意味着模型足够可信,可以使用。在后续章节中,我们将讨论提高性能的方法,以及模型调参时的注意事项。
| 3.16 完整代码
import numpy as np
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
# 加载数据集
iris_dataset = load_iris()
# 将数据拆分为训练和测试数据集。
X_train, X_test, y_train, y_test = train_test_split(
iris_dataset['data'], iris_dataset['target'], random_state=0)
# KNN算法
knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train, y_train)
# 模型预测
X_new = np.array([[5, 2.9, 1, 0.2]])
prediction = knn.predict(X_new)
print("Prediction: {}".format(prediction))
print("Predicted target name: {}".format(
iris_dataset['target_names'][prediction]))
# 模型评估
y_pred = knn.predict(X_test)
print("Test set predictions:\n {}".format(y_pred))
print("Test set score: {:.2f}".format(np.mean(y_pred == y_test)))
print("Test set score: {:.2f}".format(knn.score(X_test, y_test)))
当然,你也可以访问github存储库来下载代码:
https://github.com/Zesheng-Wang/Machine-Learning
References:
https://www.kaggle.com/arshid/iris-flower-dataset
https://www.datacamp.com/community/tutorials/machine-learning-in-r
https://en.wikipedia.org/wiki/Iris_flower_data_set
code with mosh:what is machine learning
Python机器学习基础教程
| 4.1 写在最后
学习不是一蹴而就的,机器学习所涉及的内容非常宽泛,后面可能还会涉及一些数学公式。不过作为一种面向应用的方式方法,在不同的场景下同样有着不同的解决方式,希望今天的内容可以帮你跑通机器学习最简模型,帮你打下坚实的基础。
勘误:
由于我自己也不是资深编程高手,在创作此内容时尽管已经力求精准,查阅了诸多资料,还是难保有所疏漏,如果各位发现有误可以公众号内留言,欢迎指正。
你要偷偷学Python,然后惊艳所有人。
-END-
感谢大家的关注
你关心的,都在这里