做出预测后,您需要知道它们是否准确。
有一些标准度量可以用来总结一组预测的准确性。
了解一组预测的准确性,可以让您估计您问题中给定的机器学习模型的优劣,
在本教程中,您将学习如何在 Python 中从零开始实现四个标准预测评估指标。
阅读本教程后,您将了解
- 如何实现分类准确率。
- 如何实现和解释混淆矩阵。
- 如何实现回归的平均绝对误差。
- 如何实现回归的均方根误差。
通过我的新书《从零开始的机器学习算法》启动您的项目,其中包括逐步教程和所有示例的 Python 源代码文件。
让我们开始吧。
- 2018 年 8 月更新:测试并更新以与 Python 3.6 配合使用。

如何在 Python 中从零开始实现机器学习算法性能指标
图片来源:Hernán Piñera,保留部分权利。
描述
在训练机器学习模型时,您必须评估一组预测的质量。
分类准确率和均方根误差等性能指标可以为您提供一组预测的准确性,以及生成它们的模型的优劣的清晰客观的认识。
这很重要,因为它允许您区分和选择
- 用于训练相同机器学习模型的数据的不同变换。
- 在相同数据上训练的不同机器学习模型。
- 在相同数据上训练的机器学习模型的不同配置。
因此,性能指标是从零开始实现机器学习算法所需的基本构建块。
教程
本教程分为 4 个部分
- 1. 分类准确率。
- 2. 混淆矩阵。
- 3. 平均绝对误差。
- 4. 均方根误差。
这些步骤将为您评估机器学习算法所做预测提供所需的基础。
1. 分类准确率
评估分类问题中一组预测的快速方法是使用准确率。
分类准确率是正确预测的数量与所有预测数量的比率。
它通常以百分比表示,最差的准确率为 0%,最好的准确率为 100%。
1 |
准确率 = 正确预测 / 总预测 * 100 |
我们可以将其实现为一个函数,该函数将预期结果和预测作为参数。
下面是名为 accuracy_metric() 的函数,它以百分比形式返回分类准确率。请注意,我们使用“==”来比较实际值和预测值的相等性。这允许我们比较整数或字符串,这是我们加载分类数据时可能选择使用的两种主要数据类型。
1 2 3 4 5 6 7 |
# 计算两个列表之间的准确率百分比 def accuracy_metric(actual, predicted): correct = 0 for i in range(len(actual)): if actual[i] == predicted[i]: correct += 1 return correct / float(len(actual)) * 100.0 |
我们可以设计一个小型数据集来测试此函数。下面是一组 10 个实际和预测的整数值。这组预测中有两个错误。
1 2 3 4 5 6 7 8 9 10 11 |
实际值 预测值 0 0 0 1 0 0 0 0 0 0 1 1 1 0 1 1 1 1 1 1 |
下面是使用此数据集测试 accuracy_metric() 函数的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 计算两个列表之间的准确率百分比 def accuracy_metric(actual, predicted): correct = 0 for i in range(len(actual)): if actual[i] == predicted[i]: correct += 1 return correct / float(len(actual)) * 100.0 # 测试准确率 实际值 = [0,0,0,0,0,1,1,1,1,1] 预测值 = [0,1,0,0,0,1,0,1,1,1] 准确率 = accuracy_metric(实际值, 预测值) print(准确率) |
运行此示例会产生预期的 80% 或 8/10 的准确率。
1 |
80.0 |
当您有少量类别值(例如 2 个,也称为二元分类问题)时,准确率是一个很好的指标。
当您有更多类别值时,准确率开始失去意义,您可能需要从不同的角度(例如混淆矩阵)查看结果。
2. 混淆矩阵
混淆矩阵提供了所有预测与预期实际值进行比较的摘要。
结果以矩阵形式呈现,每个单元格中都有计数。实际类别值的计数水平汇总,而每个类别值的预测计数垂直呈现。
一组完美的预测在矩阵的左上角到右下角显示为对角线。
混淆矩阵对于分类问题的价值在于,您可以清楚地看到哪些预测是错误的以及发生了哪种类型的错误。
让我们创建一个函数来计算混淆矩阵。
我们可以从定义函数开始,该函数根据实际类别值列表和预测列表计算混淆矩阵。
该函数在下面列出,名为 confusion_matrix()。它首先列出所有唯一的类别值,并为每个类别值分配一个唯一的整数或混淆矩阵中的索引。
混淆矩阵始终是正方形的,类别值的数量表示所需的行数和列数。
在这里,矩阵的第一个索引是实际值的行,第二个索引是预测值的列。创建方形混淆矩阵并将其初始化为每个单元格中的零计数后,只需遍历所有预测并递增每个单元格中的计数即可。
该函数返回两个对象。第一个是唯一的类别值集,以便在绘制混淆矩阵时可以显示它们。第二个是混淆矩阵本身,其中包含每个单元格中的计数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 计算混淆矩阵 def confusion_matrix(actual, predicted): unique = set(actual) matrix = [list() for x in range(len(unique))] for i in range(len(unique)): matrix[i] = [0 for x in range(len(unique))] lookup = dict() for i, value in enumerate(unique): lookup[value] = i for i in range(len(actual)): x = lookup[actual[i]] y = lookup[predicted[i]] matrix[y][x] += 1 return unique, matrix |
让我们用一个例子来具体说明。
下面是另一个虚构数据集,这次有 3 个错误。
1 2 3 4 5 6 7 8 9 10 11 |
实际值 预测值 0 0 0 1 0 1 0 0 0 0 1 1 1 0 1 1 1 1 1 1 |
我们可以按如下方式计算并打印此数据集的混淆矩阵
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 混淆矩阵计算示例 # 计算混淆矩阵 def confusion_matrix(actual, predicted): unique = set(actual) matrix = [list() for x in range(len(unique))] for i in range(len(unique)): matrix[i] = [0 for x in range(len(unique))] lookup = dict() for i, value in enumerate(unique): lookup[value] = i for i in range(len(actual)): x = lookup[actual[i]] y = lookup[predicted[i]] matrix[y][x] += 1 return unique, matrix # 用整数测试混淆矩阵 实际值 = [0,0,0,0,0,1,1,1,1,1] 预测值 = [0,1,1,0,0,1,0,1,1,1] unique, matrix = confusion_matrix(actual, predicted) print(unique) print(matrix) |
运行此示例会产生以下输出。该示例首先打印唯一值列表,然后打印混淆矩阵。
1 2 |
{0, 1} [[3, 1], [2, 4]] |
这样很难解释结果。如果能按预期显示行和列的矩阵会有所帮助。
下面是正确显示矩阵的函数。
该函数名为 print_confusion_matrix()。它将列命名为 P(预测),将行命名为 A(实际)。每列和每行都以其对应的类别值命名。
矩阵的布局是假设每个类别标签都是单个字符或单个数字整数,并且计数也是单个数字整数。您可以将其扩展以处理较大的类别标签或预测计数,作为练习。
1 2 3 4 5 6 |
# 美观地打印混淆矩阵 def print_confusion_matrix(unique, matrix): print('(A)' + ' '.join(str(x) for x in unique)) print('(P)---') for i, x in enumerate(unique): print("%s| %s" % (x, ' '.join(str(x) for x in matrix[i]))) |
我们可以将所有函数组合在一起,并显示一个可读的混淆矩阵。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# 计算和显示精美混淆矩阵的示例 # 计算混淆矩阵 def confusion_matrix(actual, predicted): unique = set(actual) matrix = [list() for x in range(len(unique))] for i in range(len(unique)): matrix[i] = [0 for x in range(len(unique))] lookup = dict() for i, value in enumerate(unique): lookup[value] = i for i in range(len(actual)): x = lookup[actual[i]] y = lookup[predicted[i]] matrix[y][x] += 1 return unique, matrix # 美观地打印混淆矩阵 def print_confusion_matrix(unique, matrix): print('(A)' + ' '.join(str(x) for x in unique)) print('(P)---') for i, x in enumerate(unique): print("%s| %s" % (x, ' '.join(str(x) for x in matrix[i]))) # 用整数测试混淆矩阵 实际值 = [0,0,0,0,0,1,1,1,1,1] 预测值 = [0,1,1,0,0,1,0,1,1,1] unique, matrix = confusion_matrix(actual, predicted) print_confusion_matrix(unique, matrix) |
运行示例会产生以下输出。我们可以看到顶部和底部为类别标签 0 和 1。沿着矩阵从左上到右下的对角线,我们可以看到 3 个 0 的预测是正确的,4 个 1 的预测是正确的。
查看其他单元格,我们可以看到 2 + 1 或 3 个预测错误。我们可以看到,有 2 个预测被错误地预测为 1,而实际是 0。我们还可以看到 1 个预测被错误地预测为 0,而实际是 1。
1 2 3 4 |
(A)0 1 (P)--- 0| 3 1 1| 2 4 |
混淆矩阵始终是除分类准确率之外的一个好主意,有助于解释预测。
3. 平均绝对误差
回归问题是预测实值的问题。
一个容易考虑的指标是预测值与预期值之间的误差。
平均绝对误差 (MAE) 是一个很好的首选误差指标。
它计算为绝对误差值的平均值,“绝对”意味着“变为正数”,以便它们可以相加。
1 |
MAE = sum( abs(predicted_i - actual_i) ) / 总预测数 |
下面是名为 mae_metric() 的函数,它实现了这个指标。如上所述,它期望一个实际结果值列表和一个预测列表。我们使用内置的 abs() Python 函数来计算求和的绝对误差值。
1 2 3 4 |
def mae_metric(actual, predicted): sum_error = 0.0 for i in range(len(actual)): sum_error += abs(predicted[i] - actual[i]) |
我们可以设计一个小型回归数据集来测试此函数。
1 2 3 4 5 6 |
实际值 预测值 0.1 0.11 0.2 0.19 0.3 0.29 0.4 0.41 0.5 0.5 |
只有一个预测 (0.5) 是正确的,而所有其他预测都错误了 0.01。因此,我们预计这些预测的平均绝对误差(或平均正误差)将略低于 0.01。
下面是一个示例,它使用虚构数据集测试 mae_metric() 函数。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 计算平均绝对误差 def mae_metric(actual, predicted): sum_error = 0.0 for i in range(len(actual)): sum_error += abs(predicted[i] - actual[i]) return sum_error / float(len(actual)) # 测试 RMSE 实际值 = [0.1, 0.2, 0.3, 0.4, 0.5] 预测值 = [0.11, 0.19, 0.29, 0.41, 0.5] mae = mae_metric(实际值, 预测值) print(mae) |
运行此示例将打印以下输出。我们可以看到,正如预期的那样,MAE 约为 0.008,略低于 0.01 的小值。
1 |
0.007999999999999993 |
4. 均方根误差
另一种计算一组回归预测误差的流行方法是使用均方根误差。
缩写为 RMSE,该指标有时也称为均方误差或 MSE,去掉了计算和名称中的“根”部分。
RMSE 计算为实际结果与预测之间平方差的均值的平方根。
将每个误差平方会使值变为正数,均方误差的平方根会将误差指标返回到原始单位进行比较。
1 |
RMSE = sqrt( sum( (predicted_i - actual_i)^2 ) / 总预测数) |
下面是名为 rmse_metric() 的函数中的实现。它使用 math 模块中的 sqrt() 函数,并使用 ** 运算符将误差提升到 2 次方。
1 2 3 4 5 6 7 8 |
# 计算均方根误差 def rmse_metric(actual, predicted): sum_error = 0.0 for i in range(len(actual)): prediction_error = predicted[i] - actual[i] sum_error += (prediction_error ** 2) mean_error = sum_error / float(len(actual)) return sqrt(mean_error) |
我们可以在用于测试上面平均绝对误差计算的相同数据集上测试此指标。
下面是一个完整的示例。同样,我们预计误差值通常接近 0.01。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
from math import sqrt # 计算均方根误差 def rmse_metric(actual, predicted): sum_error = 0.0 for i in range(len(actual)): prediction_error = predicted[i] - actual[i] sum_error += (prediction_error ** 2) mean_error = sum_error / float(len(actual)) return sqrt(mean_error) # 测试 RMSE 实际值 = [0.1, 0.2, 0.3, 0.4, 0.5] 预测值 = [0.11, 0.19, 0.29, 0.41, 0.5] rmse = rmse_metric(actual, predicted) print(rmse) |
运行示例,我们看到以下结果。结果略高,为 0.0089。
RMSE 值总是略高于 MSE 值,随着预测误差的增加,这种差异会更加明显。这是使用 RMSE 而不是 MSE 的一个优点,因为它会对较大的误差给予更差的分数。
1 |
0.00894427190999915 |
扩展
您只看到了最广泛使用的性能指标中的一小部分。
您可能需要许多其他性能指标。
下面列出了您可能希望实现以扩展本教程的 5 个额外性能指标
- 分类的精确度。
- 分类的召回率。
- 分类的 F1 分数。
- 分类的 ROC 曲线下面积或 AUC。
- 回归的拟合优度或 R^2(R 平方)。
您是否实现了这些扩展中的任何一个?
在下面的评论中分享您的经验。
回顾
在本教程中,您学习了如何在 Python 中从零开始实现算法预测性能指标。
具体来说,你学到了:
- 如何实现和解释分类准确率。
- 如何实现和解释分类问题的混淆矩阵。
- 如何实现和解释回归的平均绝对误差。
- 如何实现和解释回归的均方根误差。
你有什么问题吗?
在评论中提出您的问题,我将尽力回答。
我认为准确率指标可能会给出错误的结果概念。主要是因为假阳性。
它很容易实现,但在我看来,可能不是最好的选择,尽管有许多开发人员使用它。
我同意 Joao,通常对于分类问题,对数损失、kappa 甚至 F1 都是更好的衡量标准。
不过,准确率是一个很好的起点,特别是对于初学者。
另一个关于机器学习算法验证的评论……最好的选择是将数据分成三组:测试集、训练集和验证集,因为只有测试集和训练集可能会导致学习系统作弊。
再一次,很少有作者使用这种方法。
你好 Joao,如果数据充足,使用 3 个数据集是一个很好的实践。
嗨,Jason,
在没有任何已发布结果的情况下,如何验证我获得的均方误差 (MSE) 是否良好。例如,在一个问题中,当实际值在 953 到 1616 之间时,MSE 为 400 – 500。在另一个问题中,当实际值在 0 到 85 之间时,MSE 约为 25-30。我怎么能说我得到了好的 MSE?我考虑过归一化,但我再次认为它只会按比例缩小。
一些论文中提到的 Pred 度量如何?它是否有助于找到好的 MSE 值?
好问题,我在这里回答了
https://machinelearning.org.cn/faq/single-faq/how-to-know-if-a-model-has-good-performance
非常感谢您的快速回复。
阅读上述文章和其他文章后,我认为我需要实现零规则算法并查看我的基线。
在基线时间序列预测的一篇文章中,使用朴素预测得到的 MSE = 17730,这看起来相当高。那么如何知道下限呢。15000 好还是 150 好?
多元时间序列的朴素预测呢?
最低限度是零误差。
“好”只能与朴素方法相比来衡量。这是我们能做的最好的事情。通过更复杂的朴素预测来降低这个限制。
谢谢。我试试看,如果还有问题再回来。
我们如何计算归一化均方误差?我在 Scikit 文档中找不到它。您能帮忙吗?
我相信您指的是均方根误差 (RMSE)。
您可以计算 MSE,然后计算结果的平方根。
为什么混淆矩阵代码中的 enumerate 如此有意义?我尝试在没有它的情况下运行,它给出了相同的结果
抱歉,我不太明白。您能详细说明一下吗?
首先,我将数据归一化到 0 到 1 之间,因为数据值在 2k 和 3k 之间。经过这个过程,RMSE 为 0.184907340558712。我想知道,我能将这个分数解释为 18% 吗?如果不能,我应该如何解释这个分数。
不行。
但您可以对预测进行逆变换,然后计算误差,这将给出您可以在原始变量/单位的上下文中解释的值。
如何在不使用 sklearn 的情况下计算 TPR FPR ROC?
嗨 Sunny... 以下讨论可能对您感兴趣
https://stackoverflow.com/questions/61321778/how-to-calculate-tpr-and-fpr-in-python-without-using-sklearn