机器学习项目的入门框架





5.00/5 (2投票s)
介绍执行统计学习或构建机器学习模型的主要步骤的简化说明
引言
在这篇文章中,我将演示一种用于机器学习项目的框架。如您所知,机器学习通常是关于从数据中提取知识,因此,大多数机器学习项目将依赖于来自特定领域的数据集(称为数据集),在此领域中,我们正在研究某个问题以构建适合它的预测模型。该模型应遵循一系列步骤来完成其目的。在以下部分中,我将实际地介绍一个简化的解释,说明执行统计学习或构建机器学习模型的主要步骤。
背景
我假设解释项目是使用Python编程语言在Jupyter Notebook(IPython)中实现的,依赖于Numpy、Pandas和Scikit-Learn包。
问题陈述
为了构建更好的模型,您应该清楚地定义您正在尝试解决的问题,包括您将用来实现所需解决方案的策略。我选择了一个简单的鸢尾花物种分类应用,我们将创建一个简单的机器学习模型,该模型可用于通过识别与每朵鸢尾花相关的某些测量值来区分鸢尾花的物种,例如花瓣的长度和宽度以及萼片的长度和宽度,所有这些都以厘米为单位。我们将依赖于专家先前识别的测量数据集,这些花被分类为“setosa”、“versicolor”或“virginica”物种。我们的任务是构建一个可以从这些测量值中学习的模型,以便我们可以预测新鸢尾花的物种。
算法选择
根据所研究问题的性质和特点,我们需要选择适合解决该问题的算法和技术。由于我们有已知正确鸢尾花物种的测量值,这是一个**监督学习问题**。在这个问题中,我们希望预测几个选项之一(鸢尾花物种)。这是一个**分类问题**的例子。可能的输出(不同物种的鸢尾花)被称为类别。数据集中的每朵鸢尾花都属于三个类别之一,因此这是一个三类分类问题。单个数据点(一朵鸢尾花)的期望输出是该花的物种。对于特定的数据点,它所属的物种被称为其标签或类别。
项目准备
为了开始处理我们的项目数据,我们首先需要导入实现中所需的功能,例如Python库,设置我们的环境以允许我们完成任务并成功加载我们的数据集。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# For Pretty display for plots in jupyter notebooks
%matplotlib inline
# And to allow the use of the function display() for pandas data frames
from IPython.display import display
# To Ignore warnings of loaded modules during runtime execution if needed
import warnings
warnings.filterwarnings("ignore")
然后,我们开始将数据集加载到 `pandas DataFrame` 中。这里将使用的数据是 `Iris` 数据集,这是一个机器学习和统计学中的经典数据集。它包含在 `scikit-learn` 的 `datasets` 模块中。
from sklearn.datasets import load_iris
iris=load_iris()
# printing feature names
print('features: %s'%iris['feature_names'])
# printing species of iris
print('target categories: %s'%iris['target_names'])
# iris data shape
print("Shape of data: {}".format(iris['data'].shape))
full_data=pd.DataFrame(iris['data'],columns=iris['feature_names'])
# converting to pandas DataFrame
full_data['Classification']=iris['target']
full_data['Classification']=
full_data['Classification'].apply(lambda x: iris['target_names'][x])
# Note: Here we are using built-in dataset that comes
with Sci-kit Learn library but in practice,
# the data set often comes as csv files so we could
use a code looks like the following commented one:
# myfile='MyFileName.csv'
# full_data=pd.read_csv(myfile, thousands=',',
delimiter=';',encoding='latin1',na_values="n/a")
输出如下
features: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
target categories: ['setosa' 'versicolor' 'virginica']
Shape of data: (150, 4)
鸢尾花数据包含萼片长度、萼片宽度、花瓣长度和花瓣宽度的数字测量值,并存储为 **Numpy 数组**,因此我们已将其转换为 Pandas **DataFrame
**。
注意:这里我们使用的是 **Sci-kit Learn** 库自带的内置数据集,但在实际应用中,数据集通常以 **csv** 文件形式出现,所以我们可以使用类似以下注释代码的代码
#myfile='MyFileName.csv' #full_data=pd.read_csv(myfile, thousands=',', delimiter=';', encoding='latin1', na_values="n/a")
然后我们开始将数据集加载到 pandas `DataFrame` 中。这里将使用的数据是 `Iris` 数据集,这是一个机器学习和统计学中的经典数据集。它包含在 `scikit-learn` 的 `datasets` 模块中。
from sklearn.datasets import load_iris
iris=load_iris()
#printing feature names
print('features: %s'%iris['feature_names'])
#printing species of iris
print('target categories: %s'%iris['target_names'])
#iris data shape
print("Shape of data: {}".format(iris['data'].shape))
输出如下
features: ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
target categories: ['setosa' 'versicolor' 'virginica']
Shape of data: (150, 4)
鸢尾花的数据包含萼片长度、萼片宽度、花瓣长度和花瓣宽度的数值测量,并存储为 `NumPy 数组`。因此,为了将其转换为 `pandas DataFrame`,我们将编写以下代码
full_data=pd.DataFrame(iris['data'],columns=iris['feature_names'])
full_data['Classification']=iris['target']
full_data['Classification']=full_data['Classification'].apply(lambda x: iris['target_names'][x])
数据探索
在构建机器学习模型之前,我们必须首先分析用于问题的数据集,并且我们总是期望评估可能需要预处理的常见问题。因此,数据探索是在进行模型实现之前必须采取的必要行动。这可以通过显示数据的一个快速样本,描述数据类型,了解其形状或维度,如果需要,获取与加载数据集相关的基本统计信息和信息,以及探索输入特征和数据中可能需要解决的任何异常或有趣特征来完成。数据探索让您更深入地了解您的数据集,包括数据集模式、值分布、缺失值和分类特征的基数。
为了开始探索由 `load_iris()` 返回并存储在 `mydata pandas DataFrame` 中的 `iris` 对象所代表的数据集,我们可以使用 `.head()` 函数显示前几个条目进行检查。
display(full_data.head())
显示有关数据结构的更多信息
full_data.info()
或full_data.dtypes
检查数据集中是否存在任何 null
值
full_data.isnull().sum()
或者使用以下函数获取有关这些 null
值的更多详细信息
def NullValues(theData):
null_data = pd.DataFrame(
{'columns': theData.columns,
'Sum': theData.isnull().sum(),
'Percentage': theData.isnull().sum() * 100 / len(theData),
'zeros Percentage': theData.isin([0]).sum() * 100 / len(theData)
}
)
return null_data
从我们数据集的上述样本中,我们可以看到数据集包含150种不同花朵的测量值。在机器学习中,单个项目被称为**示例**、实例或样本,它们的属性(五个列)被称为**特征**(四个特征和一个列是每个实例或示例的`目标`或类别)。数据数组的形状是样本数量乘以特征数量。这是`scikit-learn`中的一个约定,我们的数据将始终假定为这种形状。
下面详细解释了我们数据集中包含的特征和类别
- 萼片长度:表示指定鸢尾花萼片的长度,单位为厘米
- 萼片宽度:表示指定鸢尾花萼片的宽度,单位为厘米
- 花瓣长度:表示指定鸢尾花花瓣的长度,单位为厘米
- 花瓣宽度:表示指定鸢尾花花瓣的宽度,单位为厘米
为了显示新 `数据集` 的统计信息,我们可以使用
full_data.describe()
您可以比较最小值和最大值,看看范围是否很大,以及是否需要对数值特征进行缩放。
由于我们对鸢尾花的分类感兴趣,即我们只根据给定的测量值或特征观察每朵花的类别或标签,我们可以从该数据集中删除“分类”特征,并将其存储为自己的独立变量“Labels”。我们将使用这些标签作为我们的预测目标。以下代码将删除“分类”作为数据集的特征,并将其存储在“Labels”中。
Labels = full_data['Classification']
mydata = full_data.drop('Classification', axis = 1)display(mydata.head())display(Labels.head())
`Classification` 特征已从 `DataFrame` 中移除。请注意,数据(鸢尾花数据)和 `Labels`(鸢尾花的标签或分类)现在是成对的。这意味着对于任何鸢尾花 `mydata.loc[i]`,它们都有一个分类或标签 `Labels[i]`。
为了通过移除不符合特定提供条件的数据来过滤输入数据,以下函数将数据列表作为输入,并返回一个过滤后的列表,如以下代码所示
def filter_data(data, conditionField,conditionOperation,conditionValue):
"""
Remove elements that do not match the condition provided.
Takes a data list as input and returns a filtered list.
Conditions passed as separate parameters for the field, operation and value.
"""
#field, op, value = condition.split(" ") # Example: ["field == 'value'", 'field < 18']
field, op, value = conditionField,conditionOperation,conditionValue
# convert value into number or strip excess quotes if string
try:
value = float(value)
except:
value = value.strip("\'\"")
# get booleans for filtering
if op == ">":
matches = data[field] > value
elif op == "<":
matches = data[field] < value
elif op == ">=":
matches = data[field] >= value
elif op == "<=":
matches = data[field] <= value
elif op == "==":
matches = data[field] == value
elif op == "!=":
matches = data[field] != value
else: # catch invalid operation codes
raise Exception("Invalid comparison operator. Only >, <, >=, <=, ==, != allowed.")
# filter data and outcomes
data = data[matches].reset_index(drop = True)
return data
例如,我们可以将数据过滤为萼片宽度(cm)小于3的所有花朵列表,如下所示
filtered_data=filter_data(mydata, 'sepal width (cm)','<','3')
display(filtered_data.head())
数据可视化
在构建机器学习模型之前,最好查看我们的数据,以便更深入地了解构成数据的各种组件之间的关系。检查我们的数据是发现异常和特殊情况的好方法。例如,也许您的一些鸢尾花是用英寸而不是厘米测量的。在现实世界中,数据中的不一致和意外测量非常常见。检查数据的最佳方法之一是对数据进行一些可视化。我们可以使用python的`matplotlib`库或`seaborn`库,使用不同类型的图(如条形图、箱线图、散点图等)来绘制和可视化数据。
以下函数显示了我们数据中任意单个属性的条形图(BarGraph)
def BarGraph(theData,target,attributeName):
xValues = np.unique(target)
yValues=[]
for label in xValues:
yValues.append(theData.loc[target==label, attributeName].idxmax())
plt.bar(xValues,yValues)
plt.xticks(xValues, target)
plt.title(attributeName)
plt.show()
下面的代码通过显示“萼片长度(cm)”的条形图作为示例来测试上述函数。
BarGraph(mydata,Labels,'sepal length (cm)')
此外,我们可以使用以下函数同时显示多个条形图
def BarGraphs(theData,target,attributeNamesList,graphsTitle='Attributes Classifications'):
xValues = np.unique(target)
fig, ax = plt.subplots(nrows=int(len(attributeNamesList)/2), ncols=2,figsize=(16, 8))
k=0
for row in ax:
for col in row:
yValues=[]
for label in xValues:
yValues.append(theData.loc[target==label, attributeNamesList[k]].idxmax())
col.set_title(attributeNamesList[k])
#col.set(xlabel=" x Label", ylabel=' y Label')
col.bar(xValues,yValues)
k=k+1
fig.suptitle(graphsTitle)
plt.show()
示例
BarGraphs(mydata,Labels,['sepal length (cm)','sepal width (cm)',
'petal length (cm)','petal width (cm)'])
可视化数据集中值的分布可以更深入地了解数据集中属性的分布,以检查该分布的性质是正态分布还是均匀分布,这可以按如下方式完成
def Distribution(theData,datacolumn,type='value'):
if type=='value':
print("Distribution for {} ".format(datacolumn))
theData[datacolumn].value_counts().plot(kind='bar')
elif type=='normal':
attr_mean=theData[datacolumn].mean() # Mean of the attribute values
attr_std_dev=full_data[datacolumn].std() # Standard Deviation of the attribute values
ndist=np.random.normal(attr_mean, attr_std_dev, 100)
norm_ax = sns.distplot(ndist, kde=False )
plt.show()
plt.close()
elif type=='uniform':
udist = np.random.uniform(-1,0,1000)
uniform_ax = sns.distplot(udist, kde=False )
plt.show()
elif type=='hist':
theData[datacolumn].hist()
为了测试 `Classification` 属性的值分布可视化,我们可以编写
Distribution(full_data, 'Classification')
另一个可视化示例是显示某个属性的直方图。我们使用以下方法
Distribution(full_data, 'sepal length (cm)',type='hist')
sns.distplot(full_data['sepal length (cm)'])
看起来属性 `sepal length (cm)` 呈正态分布。
为了探索这些特征之间的相关性,我们可以在`seaborn`库中使用`heatmap`。我们可以看到萼片长度和萼片宽度特征之间略有相关性。
plt.figure(figsize=(10,11))
sns.heatmap(mydata.corr(),annot=True)
plt.plot()
为了观察数据集中特征之间的关系,我们可以使用**散点图**。数据的散点图将一个特征沿x轴,另一个特征沿y轴,并为每个数据点绘制一个点。为了根据萼片长度宽度特征绘制数据分布,我们可以使用下面的代码
sns.FacetGrid(full_data,hue="Classification").map(plt.scatter,"sepal length (cm)",
"sepal width (cm)").add_legend()
plt.show()
要绘制具有三个以上特征的数据集,我们可以使用 `pair plot`,它会查看所有可能的特征对。如果您拥有少量特征,例如我们这里的四个特征,这是非常合理的。但是,您应该记住,`pair plot` 无法同时显示所有特征的相互作用,因此以这种方式可视化数据时,数据的某些有趣方面可能不会显现出来。我们可以像下面这样使用 `seaborn` 库中的 `pairplot` 函数
sns.pairplot(mydata)
另一种绘制散点图的方法是使用 `pandas` 库中 `plotting` 模块中存在的 `scatter matrix`。以下代码从数据框创建散点矩阵,并按类别或标签进行颜色区分
from pandas.plotting import scatter_matrix
colors = list()
palette = {0: "red", 1: "green", 2: "blue"}
for c in np.nditer(iris.target): colors.append(palette[int(c)])
grr = scatter_matrix(mydata, alpha=0.3,figsize=(10, 10),
diagonal='hist', color=colors, marker='o', grid=True)
从这些图表中,我们可以看到这三个类别似乎通过萼片和花瓣的测量值相对较好地分开了。这意味着机器学习模型很可能能够学会将它们分开。
为了显示物种中长度和宽度的密度,我们可以使用所有输入变量与输出变量(即物种)的 小提琴图
。
plt.figure(figsize=(12,10))
plt.subplot(2,2,1)
sns.violinplot(x="Classification",y="sepal length (cm)",data=full_data)
plt.subplot(2,2,2)
sns.violinplot(x="Classification",y="sepal width (cm)",data=full_data)
plt.subplot(2,2,3)
sns.violinplot(x="Classification",y="petal length (cm)",data=full_data)
plt.subplot(2,2,4)
sns.violinplot(x="Classification",y="petal width (cm)",data=full_data)
较**细**的部分表示**密度较低**,而较**粗**的部分表示**密度较高**。
类似地,我们也可以使用 `boxplot` 来查看**分类特征** `Classification` 如何与所有其他输入分布,并检查是否存在**异常值**。
plt.figure(figsize=(12,10))
plt.subplot(2,2,1)
sns.boxplot(x="Classification",y="sepal length (cm)",data=full_data)
plt.subplot(2,2,2)
sns.boxplot(x="Classification",y="sepal width (cm)",data=full_data)
plt.subplot(2,2,3)
sns.boxplot(x="Classification",y="petal length (cm)",data=full_data)
plt.subplot(2,2,4)
sns.boxplot(x="Classification",y="petal width (cm)",data=full_data)
检查基数
def count_unique_values(theData, categorical_columns_list):
cats = theData[categorical_columns_list]
rValue = pd.DataFrame({'cardinality': cats.nunique()})
return rValue
将数据集拆分为训练数据和测试数据
我们不能使用构建模型的数据来评估它。这是因为我们的模型总是可以简单地记住整个训练集,因此对于训练集中的任何点,它将始终预测正确的标签。这种**记忆**并不能表明我们的模型是否能很好地泛化,即它是否也能在新数据上表现良好。因此,在使用能够从未知数据进行预测的机器学习模型之前,我们应该有某种方法来知道它是否真正有效。因此,我们需要将带标签的数据分成两部分。一部分数据用于构建我们的机器学习模型,称为`训练数据`或`训练集`。其余数据将用于衡量模型的表现;这称为`测试数据`或`测试集`。
`scikit-learn` 包含一个函数,可以为您**打乱**数据集并将其**分割**:`train_test_split` 函数。此函数从数据中提取 **75%** 的行作为 `训练集`,以及这些数据对应的标签。剩余的 **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(mydata,Labels, random_state=0)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))
在进行拆分之前,`train_test_split` 函数使用伪随机数生成器打乱数据集。如果我们将数据的最后 25% 作为测试集,所有数据点都将具有标签 2,因为数据点是按标签排序的(请参阅前面显示的 `iris['target']` 的输出)。使用仅包含三类中的一类的测试集并不能很好地告诉我们模型泛化得如何,因此我们打乱数据以确保测试数据包含所有类别的数据。为了确保多次运行相同函数时我们能得到相同的输出,我们使用 `random_state` 参数为伪随机数生成器提供一个固定的种子。这将使结果具有确定性,因此这一行将始终具有相同的输出。`train_test_split` 函数的输出是 `X_train`、`X_test`、`y_train` 和 `y_test`,它们都是 NumPy 数组。`X_train` 包含数据集 75% 的行,`X_test` 包含剩余的 25%。
构建模型
现在我们可以开始构建实际的机器学习模型了。`scikit-learn` 中有许多分类算法可供我们使用。这里,我们将使用一个易于理解的**k-近邻分类器**。构建这个模型只包括存储训练集。为了对新的数据点进行预测,该算法会找到训练集中最接近新数据点的点。然后,它将这个训练点的标签分配给新的数据点。
`k-近邻`中的`k`表示,除了使用最接近新数据点的邻居之外,我们还可以考虑任意固定数量k个训练集中的邻居(例如,最近的三个或五个邻居)。然后,我们可以使用这些邻居中的多数类别进行预测。为了简化,我们只使用一个邻居。
`scikit-learn` 中的所有机器学习模型都以其自己的类实现,这些类被称为 `Estimator` 类。**k-近邻分类算法**在 `neighbors` 模块的 `KNeighborsClassifier 类` 中实现。在使用模型之前,我们需要将该类实例化为一个对象。此时我们将设置模型的任何参数。`KNeighborsClassifier` 最重要的参数是`邻居数量`,我们将其设置为`1`
from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=1)
`knn` 对象封装了用于从训练数据构建模型以及对新数据点进行预测的算法。它还将保存算法从训练数据中提取的信息。对于 `KNeighborsClassifier`,它将只存储训练集。为了在训练集上构建模型,我们调用 `knn` 对象的 `fit` 方法,该方法接受包含训练数据的 `NumPy` 数组 `X_train` 和包含相应训练标签的 `NumPy` 数组 `y_train` 作为参数。
knn.fit(X_train, y_train)
`fit` 方法返回 `knn` 对象本身(并原地修改它),因此我们得到了分类器的字符串表示。该表示显示了创建模型时使用的参数。几乎所有参数都是默认值,但您也可以找到我们传递的参数 `n_neighbors=1`。`scikit-learn` 中的大多数模型都有许多参数,但其中大部分要么是速度优化,要么是用于非常特殊的用例。您无需担心此表示中显示的其他参数。打印 `scikit-learn` 模型可能会产生非常长的字符串,但不要被这些吓倒。因此,我们将不显示 `fit` 的输出,因为它不包含任何新信息。
现在我们可以使用这个模型对我们可能不知道正确标签的新数据进行预测。想象一下,我们发现一朵野生的鸢尾花,其萼片长5厘米,萼片宽2.9厘米,花瓣长1厘米,花瓣宽0.2厘米。这将是哪种鸢尾花呢?我们可以将这些数据放入一个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))
我们的模型预测这种新的鸢尾花属于 `setosa` 类别或标签或物种。但是我们如何知道我们是否可以信任我们的模型呢?我们不知道这个样本的正确物种,这正是构建模型的全部意义所在!
模型评估
对于任何项目,我们都需要清楚地定义将用于衡量模型性能或项目结果的指标或计算,即为了衡量预测的性能,我们需要一个指标来根据给定示例的真实分类来评分我们的预测。这些计算和指标应根据问题的特点和问题领域进行 обосно。在机器学习分类模型中,我们通常使用各种性能衡量指标。在分类问题中常用的衡量指标中,我们可以提到`准确率(A)`、`精确率(P)`、`召回率(R)`和`F1-分数`等。
准确性
`准确率`或分类率衡量分类器正确的频率,它被定义为我们模型正确预测的比例。准确率分数由以下公式计算
为了了解我们的预测有多**准确**,我们将计算我们诊断预测正确的案例比例。下面的代码将创建我们的 `GetAccuracy()` 函数,该函数计算**准确率分数**。
def GetAccuracy(datasetClasses, PredictedClasses):
""" Returns accuracy score for input Dataset Classes/labels and Predicted Classes/labels """
# Ensure that the number of predictions matches number of classes/labels
if len(datasetClasses) == len(PredictedClasses):
# Calculate and return the accuracy as a percent
return "Predictions have an accuracy of
{:.2f}%.".format((datasetClasses == PredictedClasses).mean()*100)
else:
return "Number of predictions does not match number of Labels/Classes!"
**示例:** *在前五朵花中,如果我们预测它们全部是列表 `predictions = ['setosa','versicolor','versicolor','setosa','setosa']`,那么我们期望预测的准确率如下
# Test the 'accuracy_score' function
predictions = ['setosa','versicolor','versicolor','setosa','setosa']
print(GetAccuracy(Labels[:5], predictions))Predictions have an accuracy of 60.00%.
此外,作为第二个例子,我们可以假设预测是数据集中所有花都被预测为 `setosa`。因此,下面的代码将始终预测数据集中所有花都是 `setosa`。
def predictions_example(data):
predictions = []
for flower in data:
# Predict the survival of 'passenger'
predictions.append('setosa')
# Return our predictions
return pd.Series(predictions)# Make the predictions
predictions = predictions_example(Labels)
- 要了解所有花朵物种都是 `setosa` 的预测准确度如何?以下代码显示了此预测的准确度。
print(GetAccuracy(Labels, predictions))Predictions have an accuracy of 33.33%.
混淆矩阵
在统计**分类**中,我们可以使用所谓的**混淆矩阵**,也称为**误差矩阵**。混淆矩阵被认为是分类问题上预测结果的摘要。正确和不正确的预测数量用计数值总结,并按每个类别细分。这是混淆矩阵的关键。混淆矩阵显示了分类模型在进行预测时感到困惑的方式。它不仅让我们深入了解分类器正在犯的错误,更重要的是,它让我们了解正在犯的错误的类型。
**混淆矩阵**通常用作一个表格,用于描述**分类**模型(或“分类器”)在一组已知真实/实际值的测试数据上的性能。它们根据**正类别**(即**阳性**观察结果,例如:胸部X光图像显示存在肺炎)和**负类别**(即**非阳性**观察结果,例如:胸部X光图像显示不存在肺炎,即正常)来定义。要使用混淆矩阵计算性能指标,我们应该计算以下四个值:
- 真阳性(
TP
):观测结果为`阳性`,并且预测为`阳性`。 - 假阳性(
FP
):观测结果为`阴性`,但预测为`阳性`(**第一类错误**)。 - 真阴性(
TN
):观测结果为`阴性`,并且预测为`阴性`。 - 假阴性(
FN
):观测结果为`阳性`,但预测为`阴性`(**第二类错误**)。
这里的`真`表示正确分类为阳性或阴性的情况,而`假`表示错误分类为阳性或阴性的情况。
混淆矩阵通常用于监督学习算法(在无监督学习中,它通常被称为匹配矩阵),并且大多数性能指标都是从混淆矩阵计算的。因此,如果我们考虑`准确率`分数,它将使用混淆矩阵计算为 ((TP+TN)/total),如下所示
准确率衡量存在问题,因为它假设两种错误成本相同。99% 的准确率可能是优秀、良好、平庸、糟糕或可怕,具体取决于问题。如果数据集中的类别大小相似,这可能是一个合理的初始衡量标准。
精度
精确率反映了报告的实际案例/类别中属实的比例(即,正确识别的阳性样本的比例)。它通过将正确分类的阳性样本总数除以预测的阳性样本总数(TP/预测为是)来计算,如下所示
当模型预测为“是”时,精确率用于衡量其正确的频率。高精确率表示被标记为阳性的示例确实是阳性的(FP 数量少)。
召回率
我们可以将**召回率**定义为分类器找到的真实类别的比例(TP/实际为是),即正确识别的实际阳性样本的比例。它有时被称为**灵敏度**,它是正确分类的阳性样本总数除以阳性样本总数的比率,计算公式如下
当实际为“是”时,召回率用于衡量分类器预测“是”的频率。它被称为**灵敏度**或**真阳性率 (TPR)**。高召回率表示类别被正确识别(FN 数量少)。灵敏度是一个指标,它告诉我们在数据集中所有阳性病例中,有多少被算法成功识别,即真阳性。换句话说,它衡量了准确识别的阳性病例的比例。您可以将高灵敏度测试视为善于排除阴性。如果某人在高灵敏度算法上结果为阴性,那么他们极不可能患有阳性,因为高灵敏度算法的假阴性率低。
请注意
高召回率,低精确率:这意味着大多数阳性样本被正确识别(低FN),但存在大量假阳性。
低召回率,高精确率:这表明我们错过了许多阳性样本(高FN),但我们预测为阳性的样本确实是阳性的(低FP)。
F1-分数
由于我们有两种度量(精确率和召回率),因此拥有一个同时代表它们的度量会有所帮助。我们计算一个F-度量,它使用**调和平均值**代替算术平均值,因为它对极端值惩罚更多。F-度量将始终更接近精确率或召回率的较小值。**F1-分数**是衡量测试准确性的指标,它以精确率和召回率(精确率和召回率之间的调和平均值)表示,它可以被认为是衡量对假阴性和假阳性惩罚相等但根据它们对完整集的反向分数贡献加权以考虑大型类层次结构的指标。它计算如下:
漏报率(假阳性率)
它在实际为“否”时使用,并衡量分类器预测“是”的频率。计算公式为(FP/实际为否)。
特异性
特异性衡量数据集中所有阴性病例中,有多少被算法成功识别,即真阴性。换句话说,它衡量了准确识别的阴性病例的比例。您可以将高度特异性测试视为善于排除阴性。如果某人在高度特异性测试中结果为阳性,那么他们极有可能患有阳性,因为高度特异性算法的假阳性率低。它也称为**真阴性率**,当实际为“否”时使用,因此它衡量分类器预测“否”的频率。
为了尝试计算我们前面提到的评估指标,这就是我们之前创建的测试集发挥作用的地方。这些数据没有用于构建模型,但我们确实知道测试集中每朵鸢尾花的正确物种是什么。因此,我们可以对测试数据中的每朵鸢尾花进行预测,并将其与标签(已知物种)进行比较。使用以下代码
y_pred = knn.predict(X_test)
print("Test set predictions:\n {}".format(y_pred))Test set predictions:
['virginica' 'versicolor' 'setosa' 'virginica' 'setosa' 'virginica'
'setosa' 'versicolor' 'versicolor' 'versicolor' 'virginica' 'versicolor'
'versicolor' 'versicolor' 'versicolor' 'setosa' 'versicolor' 'versicolor'
'setosa' 'setosa' 'virginica' 'versicolor' 'setosa' 'setosa' 'virginica'
'setosa' 'setosa' 'versicolor' 'versicolor' 'setosa' 'virginica'
'versicolor' 'setosa' 'virginica' 'virginica' 'versicolor' 'setosa'
'virginica']
我们可以通过计算**准确率**来衡量模型的表现,准确率是正确预测物种的花朵的比例
print(GetAccuracy(y_test, y_pred))Predictions have an accuracy of 97.37%.
我们还可以使用 `knn` 对象的 `score` 方法,它会为我们计算测试集的**准确率**
print("Test set score: {:.2f}".format(knn.score(X_test, y_test)))Test set score: 0.97
对于这个模型,测试集的**准确率**约为 0.97,这意味着我们对测试集中 97% 的鸢尾花做出了正确的预测。在某些数学假设下,这意味着我们可以预期我们的模型对新鸢尾花有 97% 的时间是正确的。对于我们的应用,这种高准确率意味着我们的模型可能足够值得信赖。在大多数情况下,我们最初构建的模型会进行微调以提高其性能,并且我们会反复重新评估它,以获得最终接受用于应用的模。
现在,让我们使用 `SciKit-Learn` 来创建我们的**混淆矩阵**,并计算性能评估指标
from sklearn.metrics import confusion_matrix
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
import itertoolsCM = confusion_matrix(y_test, y_pred)print ('Confusion Matrix :')
print(CM)Confusion Matrix :
[[13 0 0]
[ 0 15 1]
[ 0 0 9]]print ('Accuracy Score :',
accuracy_score(y_test, y_pred) )Accuracy Score : 0.9736842105263158print ('Report : ')
print(classification_report(y_test, y_pred))Report :
precision recall f1-score support setosa 1.00 1.00 1.00 13
versicolor 1.00 0.94 0.97 16
virginica 0.90 1.00 0.95 9 accuracy 0.97 38
macro avg 0.97 0.98 0.97 38
weighted avg 0.98 0.97 0.97 38
绘制混淆矩阵
def plot_confusion_matrix(cm, classes,normalize=False,title='Confusion matrix',cmap=plt.cm.Blues):
"""
This function prints and plots the confusion matrix.
Normalization can be applied by setting `normalize=True`.
"""
if normalize:
cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
print("Normalized confusion matrix")
else:
print('Confusion matrix, without normalization')
print(cm) 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')
plt.xlabel('Predicted label')
plt.tight_layout()# Plot confusion matrix
plt.figure()
plot_confusion_matrix(CM, classes=iris['target_names'],
title='Confusion matrix, without normalization',normalize=True)
plt.show()
历史
- 2020年5月14日:初始版本