随机森林算法是集成机器学习算法家族的一部分,是装袋决策树的一种流行变体。它也在 OpenCV 库中实现。
在本教程中,您将学习如何使用 OpenCV 的随机森林算法进行图像分类,首先使用相对简单的钞票数据集,然后使用 OpenCV 的数字数据集测试该算法。
完成本教程后,您将了解:
- 随机森林算法的一些最重要的特性。
- 如何在 OpenCV 中使用随机森林算法进行图像分类。
通过我的书《OpenCV 机器学习》启动您的项目。它提供了带有可用代码的自学教程。
让我们开始吧。

使用 OpenCV 进行图像分类的随机森林
图片来源:Jeremy Bishop,保留部分权利。
教程概述
本教程分为两部分;它们是
- 随机森林工作原理回顾
- 将随机森林算法应用于图像分类
- 钞票案例研究
- 数字案例研究
随机森林工作原理回顾
Jason Brownlee 在这些教程中已经很好地解释了随机森林算法的主题 [1, 2],但我们首先回顾一些最重要的点
- 随机森林是一种集成机器学习算法,称为装袋。它是装袋决策树的一种流行变体。
- 决策树是一种分支模型,由决策节点层次结构组成,其中每个决策节点根据决策规则分割数据。训练决策树涉及通过最小化成本函数来贪婪地选择最佳分割点(即,最佳地划分输入空间的点)。
- 决策树构建决策边界的贪婪方法使其容易受到高方差的影响。这意味着训练数据集中的微小变化可能导致截然不同的树结构,进而导致模型预测。如果决策树未修剪,它还倾向于捕获训练数据中的噪声和异常值。这种对训练数据的敏感性使决策树容易过拟合。
- 装袋决策树通过组合来自多个决策树的预测来解决这种易感性,每个决策树都在通过有放回地抽样数据集创建的训练数据集的引导样本上进行训练。这种方法的局限性源于这样一个事实,即每个树都使用相同的贪婪方法进行训练,并且在训练过程中可能会多次选取一些样本,这使得树很可能共享相似(或相同)的分割点(因此,导致树相关)。
- 随机森林算法试图通过在训练数据的随机子集上训练每棵树来减轻这种相关性,该子集是通过无放回地随机抽样数据集创建的。通过这种方式,贪婪算法只能考虑数据的固定子集来创建构成每棵树的分割点,这迫使树变得不同。
- 在分类问题中,森林中的每棵树都会产生一个预测输出,最终的类别标签被识别为大多数树产生的输出。在回归问题中,最终输出是所有树产生的输出的平均值。
将随机森林算法应用于图像分类
钞票案例研究
我们首先使用本教程中使用的钞票数据集。
钞票数据集是一个相对简单的数据集,涉及预测给定钞票的真伪。该数据集包含 1,372 行,每行代表一个特征向量,其中包含从钞票照片中提取的四种不同度量,以及其对应的类别标签(真或假)。
每个特征向量中的值对应于以下内容
- 小波变换图像的方差(连续)
- 小波变换图像的偏度(连续)
- 小波变换图像的峰度(连续)
- 图像熵(连续)
- 类别标签(整数)
数据集可以从 UCI 机器学习存储库下载。
想开始学习 OpenCV 机器学习吗?
立即参加我的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
和 Jason 的教程一样,我们将加载数据集,将其字符串数字转换为浮点数,并将其划分为训练集和测试集
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 |
# 加载数据集的函数 def load_csv(filename): file = open(filename, "rt") lines = reader(file) dataset = list(lines) return dataset # 将字符串列转换为浮点数的函数 def str_column_to_float(dataset, column): for row in dataset: row[column] = float32(row[column].strip()) # 从文本文件加载数据集 data = load_csv('Data/data_banknote_authentication.txt') # 将数据集字符串数字转换为浮点数 for i in range(len(data[0])): str_column_to_float(data, i) # 将列表转换为数组 data = array(data) # 将数据集样本与真实值分开 samples = data[:, :4] target = data[:, -1, newaxis].astype(int32) # 将数据拆分为训练集和测试集 x_train, x_test, y_train, y_test = ms.train_test_split(samples, target, test_size=0.2, random_state=10) |
OpenCV 库在 ml
模块中实现了 RTrees_create
函数,它将允许我们创建一个空的决策树
1 2 |
# 创建一个空的决策树 rtree = ml.RTrees_create() |
森林中的所有树都将使用相同的参数值进行训练,尽管是在训练数据集的不同子集上。默认参数值可以自定义,但我们首先使用默认实现。我们将在下一节中很快回到自定义这些参数值
1 2 3 4 5 6 7 8 9 |
# 训练决策树 rtree.train(x_train, ml.ROW_SAMPLE, y_train) # 预测测试数据的目标标签 _, y_pred = rtree.predict(x_test) # 计算并打印获得的准确度 accuracy = (sum(y_pred.astype(int32) == y_test) / y_test.size) * 100 print('Accuracy:', accuracy[0], '%') |
1 |
准确度:96.72727272727273 % |
我们已经使用随机森林算法的默认实现在钞票数据集上获得了约 96.73% 的高准确度。
完整的代码清单如下
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 |
from csv import reader from numpy import array, float32, int32, newaxis from cv2 import ml from sklearn import model_selection as ms # 加载数据集的函数 def load_csv(filename): file = open(filename, "rt") lines = reader(file) dataset = list(lines) return dataset # 将字符串列转换为浮点数的函数 def str_column_to_float(dataset, column): for row in dataset: row[column] = float32(row[column].strip()) # 从文本文件加载数据集 data = load_csv('Data/data_banknote_authentication.txt') # 将数据集字符串数字转换为浮点数 for i in range(len(data[0])): str_column_to_float(data, i) # 将列表转换为数组 data = array(data) # 将数据集样本与真实值分开 samples = data[:, :4] target = data[:, -1, newaxis].astype(int32) # 将数据拆分为训练集和测试集 x_train, x_test, y_train, y_test = ms.train_test_split(samples, target, test_size=0.2, random_state=10) # 创建一个空的决策树 rtree = ml.RTrees_create() # 训练决策树 rtree.train(x_train, ml.ROW_SAMPLE, y_train) # 预测测试数据的目标标签 _, y_pred = rtree.predict(x_test) # 计算并打印获得的准确度 accuracy = (sum(y_pred.astype(int32) == y_test) / y_test.size) * 100 print('Accuracy:', accuracy[0], '%') |
数字案例研究
考虑将随机森林应用于 OpenCV 数字数据集中的图像。
数字数据集仍然相对简单。但是,我们将使用 HOG 方法从其图像中提取的特征向量将具有比钞票数据集更高的维度(81 个特征)。因此,我们可以认为数字数据集比钞票数据集更具挑战性。
我们将首先研究随机森林算法的默认实现如何处理高维数据
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 |
from digits_dataset import split_images, split_data from feature_extraction import hog_descriptors from numpy import array, float32 from cv2 import ml # 加载数字图像 img, sub_imgs = split_images('Images/digits.png', 20) # 从数字图像获取训练和测试数据集 digits_train_imgs, digits_train_labels, digits_test_imgs, digits_test_labels = split_data(20, sub_imgs, 0.8) # 将图像数据转换为 HOG 描述符 digits_train_hog = hog_descriptors(digits_train_imgs) digits_test_hog = hog_descriptors(digits_test_imgs) # 创建一个空的决策树 rtree_digits = ml.RTrees_create() # 预测测试数据的目标标签 _, digits_test_pred = rtree_digits.predict(digits_test_hog) # 计算并打印获得的准确度 accuracy_digits = (sum(digits_test_pred.astype(int) == digits_test_labels) / digits_test_labels.size) * 100 print('Accuracy:', accuracy_digits[0], '%') |
1 |
准确度:81.0 % |
我们发现默认实现的准确度为 81%。
这种从钞票数据集获得的准确度下降可能表明模型默认实现的容量可能不足以学习我们现在正在处理的高维数据的复杂性。
让我们调查是否可以通过更改以下内容来提高准确度
- 训练算法的终止标准,它考虑了森林中树的数量,以及模型估计性能通过袋外 (OOB) 错误测量。当前的终止标准可以通过使用
getTermCriteria
方法找到,并通过setTermCriteria
方法设置。使用后者时,树的数量可以通过TERM_CRITERIA_MAX_ITER
参数设置,而所需的准确度可以通过TERM_CRITERIA_EPS
参数指定。
- 森林中每棵树可以达到的最大可能深度。当前深度可以使用
getMaxDepth
方法找到,并使用setMaxDepth
方法设置。如果首先满足上述终止条件,则可能无法达到指定的树深度。
调整上述参数时,请记住增加树的数量可以提高模型捕捉训练数据中更复杂细节的能力;它还会线性增加预测时间,并使模型更容易过拟合。因此,请谨慎调整参数。
如果我们在创建空决策树后添加以下几行,我们可以找到树深度的默认值以及终止标准
1 2 |
print('Default tree depth:', rtree_digits.getMaxDepth()) print('Default termination criteria:', rtree_digits.getTermCriteria()) |
1 2 |
默认树深度:5 默认终止标准:(3, 50, 0.1) |
通过这种方式,我们可以看到,默认情况下,森林中的每棵树的深度(或层数)都等于 5,而树的数量和所需准确度分别设置为 50 和 0.1。getTermCriteria
方法返回的第一个值指的是所考虑的终止标准的 type
,其中值为 3 表示基于 TERM_CRITERIA_MAX_ITER
和 TERM_CRITERIA_EPS
的终止。
现在让我们尝试更改上述值以研究它们对预测准确度的影响。代码清单如下
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 |
from digits_dataset import split_images, split_data from feature_extraction import hog_descriptors from numpy import array, float32 from cv2 import ml, TERM_CRITERIA_MAX_ITER, TERM_CRITERIA_EPS # 加载数字图像 img, sub_imgs = split_images('Images/digits.png', 20) # 从数字图像获取训练和测试数据集 digits_train_imgs, digits_train_labels, digits_test_imgs, digits_test_labels = split_data(20, sub_imgs, 0.8) # 将图像数据转换为 HOG 描述符 digits_train_hog = hog_descriptors(digits_train_imgs) digits_test_hog = hog_descriptors(digits_test_imgs) # 创建一个空的决策树 rtree_digits = ml.RTrees_create() # 读取默认参数值 print('Default tree depth:', rtree_digits.getMaxDepth()) print('Default termination criteria:', rtree_digits.getTermCriteria()) # 更改默认参数值 rtree_digits.setMaxDepth(15) rtree_digits.setTermCriteria((TERM_CRITERIA_MAX_ITER + TERM_CRITERIA_EPS, 100, 0.01)) # 训练决策树 rtree_digits.train(digits_train_hog.astype(float32), ml.ROW_SAMPLE, digits_train_labels) # 预测测试数据的目标标签 _, digits_test_pred = rtree_digits.predict(digits_test_hog) # 计算并打印获得的准确度 accuracy_digits = (sum(digits_test_pred.astype(int) == digits_test_labels) / digits_test_labels.size) * 100 print('Accuracy:', accuracy_digits[0], ‘%') |
1 |
准确度:94.1 % |
我们可以看到新设置的参数值将预测准确度提高到 94.1%。
这些参数值在此处是随意设置的,以说明此示例。但始终建议采用更系统的方法来调整模型的参数,并研究每个参数如何影响其性能。
进一步阅读
如果您想深入了解此主题,本节提供了更多资源。
书籍
- OpenCV 机器学习, 2017.
- 使用 Python 精通 OpenCV 4, 2019.
网站
总结
在本教程中,您学习了如何使用 OpenCV 的随机森林算法进行图像分类,首先使用相对简单的钞票数据集,然后使用 OpenCV 的数字数据集测试该算法。
具体来说,你学到了:
- 随机森林算法的一些最重要的特性。
- 如何在 OpenCV 中使用随机森林算法进行图像分类。
你有什么问题吗?
在下面的评论中提出您的问题,我将尽力回答。
暂无评论。