在之前的教程中,我们探索了逻辑回归,这是一个简单但流行的机器学习算法,用于 OpenCV 库中实现的二元分类。
到目前为止,我们已经看到了逻辑回归如何应用于我们自己生成的一个自定义二分类数据集。
在本教程中,您将学习如何通过将其应用于图像分类任务来修改标准逻辑回归算法(其本身专为二元分类而设计)以适应多类分类问题。
完成本教程后,您将了解:
- 逻辑回归算法的一些最重要的特征。
- 逻辑回归算法如何为多类分类问题进行修改。
- 如何将逻辑回归应用于图像分类问题。
通过我的书《OpenCV 机器学习》启动您的项目。它提供了带有可用代码的自学教程。
让我们开始吧。

使用 OpenCV 进行图像分类的逻辑回归
图片来自 David Marcu,部分权利保留。
教程概述
本教程分为三个部分;它们是:
- 逻辑回归的 Recap
- 修改逻辑回归以适应多类分类问题
- 将逻辑回归应用于多类分类问题
逻辑回归的 Recap
在之前的教程中,我们开始探索 OpenCV 对逻辑回归算法的实现。到目前为止,我们已将其应用于我们自己生成的自定义二分类数据集,该数据集包含聚集在两个簇中的二维点。
遵循 Jason Brownlee 关于逻辑回归的教程,我们还回顾了逻辑回归的重要知识点。我们已经看到,逻辑回归与线性回归密切相关,因为它们都在生成实值输出时涉及特征的线性组合。然而,逻辑回归通过应用逻辑(或 sigmoid)函数来扩展此过程。因此得名。它将实值输出映射到 [0, 1] 范围内的概率值。然后,如果该概率值超过 0.5 的阈值,则将其分类为属于默认类别;否则,将其分类为属于非默认类别。这使得逻辑回归本质上成为一种用于*二元*分类的方法。
逻辑回归模型由与输入数据中特征数量相同的系数以及一个额外的偏置值表示。这些系数和偏置值在训练过程中使用梯度下降或最大似然估计(MLE)技术进行学习。
修改逻辑回归以适应多类分类问题
如前一节所述,标准的逻辑回归方法由于逻辑函数以及随后的阈值处理过程将特征的线性组合的实值输出映射到类别 0 或类别 1,因此仅用于二类问题。
因此,要使用逻辑回归处理多类分类问题(或涉及两个以上类别的分类问题),需要修改标准算法。
一种实现此目的的技术涉及将多类分类问题分解为多个二元(或二类)分类子问题。然后可以将标准逻辑回归方法应用于每个子问题。这就是 OpenCV 实现多类逻辑回归的方式。
… 逻辑回归同时支持二元和多类分类(对于多类,它会创建多个二类分类器)。
一种此类技术被称为*一对一*方法,它涉及为数据集中的每个唯一类别对训练一个单独的二元分类器。在预测期间,这些二元分类器中的每一个都会对其训练的两个类别中的一个进行投票,并且获得最多投票的类别被认为是预测类别。
还有其他技术可以使用逻辑回归实现多类分类,例如通过*一对余*方法。您可以在这些教程中找到更多信息 [1, 2]。
将逻辑回归应用于多类分类问题
为此,我们将使用OpenCV 中的数字数据集,尽管我们开发的代码也可以应用于其他多类数据集。
我们的第一步是加载 OpenCV 数字图像,将其划分为许多显示 0 到 9 手写数字的子图像,并创建相应的真实标签,这些标签将使我们能够稍后量化训练好的逻辑回归模型的准确性。对于这个特定的例子,我们将把 80% 的数据集图像分配给训练集,将剩余的 20% 的图像分配给测试集。
1 2 3 4 5 |
# 加载数字图像 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) |
接下来,我们将遵循与上一个教程类似的过程,在该教程中我们在二分类数据集上训练和测试了逻辑回归算法,修改了一些参数以适应更大的多类数据集。
第一个步骤是再次创建逻辑回归模型本身
1 2 |
# 创建一个空的逻辑回归模型 lr_digits = ml.LogisticRegression_create() |
我们可以再次确认 OpenCV 将批量梯度下降作为其默认训练方法(由值 0 表示),然后继续将其更改为小批量梯度下降方法,并指定小批量大小。
1 2 3 4 5 6 |
# 检查默认训练方法 print('训练方法:', lr_digits.getTrainMethod()) # 将训练方法设置为小批量梯度下降和指定小批量大小 lr_digits.setTrainMethod(ml.LogisticRegression_MINI_BATCH) lr_digits.setMiniBatchSize(400) |
不同的小批量大小肯定会影响模型的训练和预测准确性。
在这个例子中,我们选择小批量大小是基于一种实用的启发式方法,即尝试了几个小批量大小,并确定了一个能够获得足够高预测准确性的值(正如我们稍后将看到的)。然而,您应该遵循一种更系统的方法,这可以为您提供关于提供计算成本和预测准确性之间更好折衷的小批量大小的更明智的决定。
接下来,我们将定义我们希望运行所选训练算法的迭代次数,然后才能终止它。
1 2 |
# 设置迭代次数 lr.setIterations(10) |
现在我们可以使用训练数据训练逻辑回归模型了。
1 2 |
# 在训练数据集上训练逻辑回归器 lr_digits.train(digits_train_imgs.astype(float32), ml.ROW_SAMPLE, digits_train_labels.astype(float32)) |
在我们之前的教程中,我们打印了学习到的系数,以发现模型是如何定义的,该模型最好地分离了我们处理的二类样本。
这次我们不打印学习到的系数,主要是因为它们太多了,因为我们现在处理的是更高维度的输入数据。
我们将改为打印学习到的系数的数量(而不是系数本身)以及输入特征的数量,以便能够比较两者。
1 2 3 |
# 打印学习到的系数的数量和输入特征的数量 print('系数数量:', len(lr_digits.get_learnt_thetas()[0])) print('输入特征数量:', len(digits_train_imgs[0, :])) |
1 2 |
系数数量:401 输入特征数量:400 |
果然,我们发现系数的数量与输入特征的数量相同,再加上一个额外的偏置值,正如我们所预期的那样(我们处理的是 20x20 像素的图像,并将像素值本身作为输入特征,因此每张图像有 400 个特征)。
我们可以通过在测试数据集上尝试来测试此模型预测目标类别标签的效果。
1 2 3 4 5 6 |
# 预测测试数据的目标标签 _, y_pred = lr_digits.predict(digits_test_imgs.astype(float32)) # 计算并打印获得的准确度 accuracy = (sum(y_pred[:, 0] == digits_test_labels[:, 0]) / digits_test_labels.size) * 100 print('准确率:', accuracy, '%') |
1 |
准确率:88.8 % |
作为最后一步,让我们生成并绘制一个混淆矩阵,以更深入地了解哪些数字被错误地识别为其他数字。
1 2 3 4 5 |
# 生成并绘制混淆矩阵 cm = confusion_matrix(digits_test_labels, y_pred) disp = ConfusionMatrixDisplay(confusion_matrix=cm) disp.plot() show() |
这样,我们可以看到表现最差的类别是 5 和 2,它们最常被误认为 8。
完整的代码列表如下
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 |
from cv2 import ml from sklearn.datasets import make_blobs from sklearn import model_selection as ms from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay from numpy import float32 from matplotlib.pyplot import show from digits_dataset import split_images, split_data # 加载数字图像 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) # 创建一个空的逻辑回归模型 lr_digits = ml.LogisticRegression_create() # 检查默认训练方法 print('训练方法:', lr_digits.getTrainMethod()) # 将训练方法设置为小批量梯度下降和指定小批量大小 lr_digits.setTrainMethod(ml.LogisticRegression_MINI_BATCH) lr_digits.setMiniBatchSize(400) # 设置迭代次数 lr_digits.setIterations(10) # 在训练数据集上训练逻辑回归器 lr_digits.train(digits_train_imgs.astype(float32), ml.ROW_SAMPLE, digits_train_labels.astype(float32)) # 打印学习到的系数的数量和输入特征的数量 print('系数数量:', len(lr_digits.get_learnt_thetas()[0])) print('输入特征数量:', len(digits_train_imgs[0, :])) # 预测测试数据的目标标签 _, y_pred = lr_digits.predict(digits_test_imgs.astype(float32)) # 计算并打印获得的准确度 accuracy = (sum(y_pred[:, 0] == digits_test_labels[:, 0]) / digits_test_labels.size) * 100 print('准确率:', accuracy, '%') # 生成并绘制混淆矩阵 cm = confusion_matrix(digits_test_labels, y_pred) disp = ConfusionMatrixDisplay(confusion_matrix=cm) disp.plot() show() |
在本教程中,我们将逻辑回归方法(其本身专为二元分类而设计)应用于多类分类问题。我们使用像素值作为表示每个图像的输入特征,在选定的参数值下获得了 88.8% 的分类准确率。
那么,尝试在从图像提取的 HOG 特征上训练逻辑回归算法能否提高此准确率?
想开始学习 OpenCV 机器学习吗?
立即参加我的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
进一步阅读
如果您想深入了解此主题,本节提供了更多资源。
书籍
- OpenCV 机器学习, 2017.
- 使用 Python 精通 OpenCV 4, 2019.
网站
总结
在本教程中,您学习了如何通过将其应用于图像分类任务来修改标准逻辑回归算法(其本身专为二元分类而设计)以适应多类分类问题。
具体来说,你学到了:
- 逻辑回归算法的一些最重要的特征。
- 逻辑回归算法如何为多类分类问题进行修改。
- 如何将逻辑回归应用于图像分类问题。
你有什么问题吗?
在下面的评论中提出您的问题,我将尽力回答。
暂无评论。