我们无法知道哪种算法对于给定的问题是最好的。
因此,我们需要设计一个测试工具,用于评估不同的机器学习算法。
在本教程中,您将学习如何从头开始在 Python 中开发机器学习算法测试工具。
完成本教程后,您将了解:
- 如何实现训练-测试算法测试工具。
- 如何实现 k 折交叉验证算法测试工具。
通过我的新书《从零开始的机器学习算法》来启动您的项目,其中包含分步教程以及所有示例的Python 源代码文件。
让我们开始吧。
- 更新于 2017 年 1 月:更改了 `cross_validation_split()` 中 `fold_size` 的计算,使其始终为整数。修复了 Python 3 的问题。
- **2018 年 3 月更新**:添加了下载数据集的备用链接,因为原始链接似乎已被删除。
- 2018 年 8 月更新:测试并更新以与 Python 3.6 配合使用。

如何使用 Python 从零开始创建算法测试工具
照片由 Chris Meller 拍摄,部分权利保留。
描述
测试工具提供了一种在数据集上评估机器学习算法的一致方法。
它包括 3 个要素
- 用于拆分数据集的重采样方法。
- 要评估的机器学习算法。
- 用于评估预测的性能指标。
数据集的加载和准备是使用测试工具之前的先决步骤。
测试工具必须允许评估不同的机器学习算法,同时保持数据集、重采样方法和性能指标不变。
在本教程中,我们将通过一个真实数据集来演示测试工具。
使用的数据集是皮马印第安人糖尿病数据集。它包含 768 行和 9 列。文件中的所有值都是数值,特别是浮点值。
零规则算法将在教程中进行评估。零规则算法总是预测训练数据集中观测值最多的类别。
教程
本教程分为两个主要部分
- 训练-测试算法测试工具。
- 交叉验证算法测试工具。
这些测试工具将为您提供评估给定预测建模问题中一系列机器学习算法所需的基础。
1. 训练-测试算法测试工具
训练-测试拆分是一种简单的重采样方法,可用于评估机器学习算法。
因此,它是开发测试工具的一个良好起点。
我们可以假设之前已经开发了一个将数据集拆分为训练集和测试集的功能,以及一个评估预测集准确性的功能。
我们需要一个函数,该函数可以接受数据集和算法,并返回性能分数。
下面是一个名为 evaluate_algorithm() 的函数,它实现了这一点。它接受 3 个固定参数,包括数据集、算法函数和训练-测试拆分的拆分百分比。
首先,将数据集拆分为训练和测试元素。然后,复制测试集,并将每个输出值清空,将其设置为 None,以防止算法意外作弊。
作为参数提供的算法是一个函数,它期望在训练集和测试集上进行准备然后进行预测。算法可能需要额外的配置参数。这通过在 evaluate_algorithm() 函数中使用可变参数 *args 并将它们传递给算法函数来处理。
算法函数应该返回一个预测列表,每个训练数据集的行对应一个预测。这些预测将与来自未修改测试集的实际输出值进行比较,由 accuracy_metric() 函数进行比较。
最后,返回准确率。
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# 使用训练/测试拆分评估算法 def evaluate_algorithm(dataset, algorithm, split, *args): train, test = train_test_split(dataset, split) test_set = list() for row in test: row_copy = list(row) row_copy[-1] = None test_set.append(row_copy) predicted = algorithm(train, test_set, *args) actual = [row[-1] for row in test] accuracy = accuracy_metric(actual, predicted) return accuracy |
评估函数确实做了一些强的假设,但如果需要,可以轻松更改。
具体来说,它假设数据集中的最后一列始终是输出值。可以使用不同的列。使用 accuracy_metric() 假设问题是分类问题,但对于回归问题,可以将其更改为均方误差。
让我们通过一个实际示例来组合这些。
我们将使用皮马印第安人糖尿病数据集并评估零规则算法。
|
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
# 训练-测试测试工具 from random import seed from random import randrange from csv import reader # 加载 CSV 文件 def load_csv(filename): file = open(filename, "rb") lines = reader(file) dataset = list(lines) return dataset # 将字符串列转换为浮点数 def str_column_to_float(dataset, column): for row in dataset: row[column] = float(row[column].strip()) # 将数据集拆分为训练集和测试集 def train_test_split(dataset, split): train = list() train_size = split * len(dataset) dataset_copy = list(dataset) while len(train) < train_size: index = randrange(len(dataset_copy)) train.append(dataset_copy.pop(index)) return train, dataset_copy # 计算准确率百分比 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 # 使用训练/测试拆分评估算法 def evaluate_algorithm(dataset, algorithm, split, *args): train, test = train_test_split(dataset, split) test_set = list() for row in test: row_copy = list(row) row_copy[-1] = None test_set.append(row_copy) predicted = algorithm(train, test_set, *args) actual = [row[-1] for row in test] accuracy = accuracy_metric(actual, predicted) return accuracy # 零规则分类算法 def zero_rule_algorithm_classification(train, test): output_values = [row[-1] for row in train] prediction = max(set(output_values), key=output_values.count) predicted = [prediction for i in range(len(test))] return predicted # 在糖尿病数据集上测试零规则算法 seed(1) # 加载并准备数据 filename = 'pima-indians-diabetes.csv' dataset = load_csv(filename) for i in range(len(dataset[0])): str_column_to_float(dataset, i) # 评估算法 split = 0.6 accuracy = evaluate_algorithm(dataset, zero_rule_algorithm_classification, split) print('Accuracy: %.3f%%' % (accuracy)) |
数据集被拆分为 60% 用于训练模型,40% 用于评估。
请注意,零规则算法 zero_rule_algorithm_classification 的名称是如何作为参数传递给 evaluate_algorithm() 函数的。您可以看到这个测试工具如何一次又一次地用于不同的算法。
运行上面的示例会打印出模型的准确率。
|
1 |
准确率: 67.427% |
2. 交叉验证算法测试工具
交叉验证是一种重采样技术,可提供对算法在未见过数据上的性能更可靠的估计。
它需要创建和评估 k 个模型在数据的不同子集上,因此计算成本更高。尽管如此,它是评估机器学习算法的黄金标准。
与上一节一样,我们需要创建一个函数来将重采样方法、在数据集上评估算法以及性能计算方法结合起来。
与上面不同的是,算法必须在数据集的不同子集上进行多次评估。这意味着我们需要在 evaluate_algorithm() 函数中添加额外的循环。
下面是一个实现带交叉验证的算法评估的函数。
首先,将数据集拆分为 n_folds 个组,称为折。
接下来,我们循环,让每个折都有机会被排除在训练之外并用于评估算法。创建折列表的副本,并从该列表中删除保留的折。然后将折列表展平为一条长列表,以匹配算法对训练数据集的期望。这是使用 sum() 函数完成的。
一旦准备好训练数据集,循环内的其余函数就与上面相同。复制测试数据集(折),并清除输出值以避免算法意外作弊。算法在训练数据集上进行准备,并在测试数据集上进行预测。预测被评估并存储在列表中。
与训练-测试算法测试工具不同,它返回一个分数列表,每个交叉验证折对应一个分数。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# 使用交叉验证分割评估算法 def evaluate_algorithm(dataset, algorithm, n_folds, *args): folds = cross_validation_split(dataset, n_folds) scores = list() for fold in folds: train_set = list(folds) train_set.remove(fold) train_set = sum(train_set, []) test_set = list() for row in fold: row_copy = list(row) test_set.append(row_copy) row_copy[-1] = None predicted = algorithm(train_set, test_set, *args) actual = [row[-1] for row in fold] accuracy = accuracy_metric(actual, predicted) scores.append(accuracy) return scores |
虽然代码稍微复杂一些,运行也更慢,但该函数提供了更稳健的算法性能估计。
我们可以通过在糖尿病数据集上使用零规则算法的完整示例来组合所有这些。
|
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# 交叉验证测试工具 from random import seed from random import randrange from csv import reader # 加载 CSV 文件 def load_csv(filename): file = open(filename, "rb") lines = reader(file) dataset = list(lines) return dataset # 将字符串列转换为浮点数 def str_column_to_float(dataset, column): for row in dataset: row[column] = float(row[column].strip()) # 将数据集分成 k 折 def cross_validation_split(dataset, n_folds): dataset_split = list() dataset_copy = list(dataset) fold_size = int(len(dataset) / n_folds) for i in range(n_folds): fold = list() while len(fold) < fold_size: index = randrange(len(dataset_copy)) fold.append(dataset_copy.pop(index)) dataset_split.append(fold) return dataset_split # 计算准确率百分比 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 # 使用交叉验证分割评估算法 def evaluate_algorithm(dataset, algorithm, n_folds, *args): folds = cross_validation_split(dataset, n_folds) scores = list() for fold in folds: train_set = list(folds) train_set.remove(fold) train_set = sum(train_set, []) test_set = list() for row in fold: row_copy = list(row) test_set.append(row_copy) row_copy[-1] = None predicted = algorithm(train_set, test_set, *args) actual = [row[-1] for row in fold] accuracy = accuracy_metric(actual, predicted) scores.append(accuracy) 返回 分数 # 零规则分类算法 def zero_rule_algorithm_classification(train, test): output_values = [row[-1] for row in train] prediction = max(set(output_values), key=output_values.count) predicted = [prediction for i in range(len(test))] return predicted # 在糖尿病数据集上测试零规则算法 seed(1) # 加载并准备数据 filename = 'pima-indians-diabetes.csv' dataset = load_csv(filename) for i in range(len(dataset[0])): str_column_to_float(dataset, i) # 评估算法 n_folds = 5 scores = evaluate_algorithm(dataset, zero_rule_algorithm_classification, n_folds) print('Scores: %s' % scores) print('Mean Accuracy: %.3f%%' % (sum(scores)/len(scores))) |
共使用了 5 个交叉验证折来评估零规则算法。因此,evaluate_algorithm() 算法返回了 5 个分数。
运行此示例将同时打印计算出的分数列表和平均分数。
|
1 2 |
分数: [62.091503267973856, 64.70588235294117, 64.70588235294117, 64.70588235294117, 69.28104575163398] 平均准确率: 65.098% |
现在您拥有了两个不同的测试工具,可以用来评估您自己的机器学习算法。
扩展
本节列出了本教程可能需要考虑的扩展内容。
- 参数化评估。传入用于评估预测的函数,使您能够无缝地处理回归问题。
- 参数化重采样。传入用于计算重采样拆分的函数,使您可以轻松地在训练-测试方法和交叉验证方法之间切换。
- 标准差分数。计算标准差,以了解使用交叉验证评估算法时的分数分布。
你尝试过这些扩展吗?
在下面的评论中分享您的经验。
回顾
在本教程中,您学习了如何从头开始创建测试工具来评估您的机器学习算法。
具体来说,您现在知道
- 如何实现和使用训练-测试算法测试工具。
- 如何实现和使用交叉验证算法测试工具。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。







谢谢,这肯定能帮助我开始机器学习。
很高兴听到这个,Timothy。
这与使用 Python scikit-learn 中的内置 cross_val_score 有什么区别?
很好的问题。
在实践中使用 scikit-learn。
如果您想了解所有这些方法如何从头开始工作,请尝试自己实现它们。
使用 weka 工具来包装算法怎么样?正如您上面所说,这与使用 scikit-learn 中的内置函数相同吗?它止于实践,而不是学习原理。
Anand,您能详细说明一下您的意思吗?
在我的实现中,我遇到了以下问题
第 163 行,k_cross_validate
train_set.remove(fold)
ValueError:包含多个元素的数组的真值不明确。请使用 a.any() 或 a.all()
抱歉,我以前没见过这个错误。您使用的是 Python 2 吗?