
通过决策树的眼睛看图像
图片由 Editor | ChatGPT 提供
在本文中,您将学习到
- 将非结构化、原始图像数据转换为结构化、信息丰富的特征。
- 基于提取的图像特征训练用于图像分类的决策树分类器。
- 将上述概念应用于 CIFAR-10 数据集进行图像分类。
引言
众所周知,**基于决策树的模型**在各种分类和回归任务中表现出色,通常基于结构化的表格数据。然而,当与正确的工具结合使用时,决策树也可以成为处理**非结构化数据**(如**文本或图像**)甚至时间序列数据的强大预测工具。
本文演示了**如何让决策树理解已转换为结构化、有意义特征的图像数据**。更具体地说,我们将展示如何将原始的像素级图像数据转换为更高级别的特征,这些特征描述了图像的颜色直方图和边缘计数等属性。然后,我们将利用这些信息通过训练决策树来进行预测任务,例如分类——所有这些都借助 Python 的 **scikit-learn** 库。
想象一下:这就像让决策树的行为更接近我们人眼的工作方式。
基于图像特征构建用于图像分类的决策树
我们将用于本教程的 CIFAR-10 数据集是低分辨率、32x32 像素的彩色图像集合,每个像素由三个 RGB 值描述,定义其颜色。
尽管其他常用的图像分类模型(如神经网络)可以处理像素网格形式的图像,但决策树是为处理结构化数据而设计的;因此,我们的主要目标是将原始图像数据转换为这种结构化格式。
我们开始加载数据集,该数据集可在 TensorFlow 库中免费获取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from tensorflow.keras.datasets import cifar10 import numpy as np import matplotlib.pyplot as plt (X_train, y_train), (X_test, y_test) = cifar10.load_data() y_train = y_train.flatten() y_test = y_test.flatten() class_names = ['airplane','automobile','bird','cat','deer', 'dog','frog','horse','ship','truck'] print("Training set:", X_train.shape, y_train.shape) print("Test set:", X_test.shape, y_test.shape) # 可选:显示几个样本(参见上面的文章图片) fig, axes = plt.subplots(1, 5, figsize=(10, 3)) for i, ax in enumerate(axes): ax.imshow(X_train[i]) ax.set_title(class_names[y_train[i]]) ax.axis('off') plt.show() |
请注意,加载的数据集已分为训练集和测试集,输出标签(10个不同类别)也与输入图像数据分开。我们只需要如上所示使用 Python 元组正确分配这些元素。为清晰起见,我们还将类别名称存储在 Python 列表中。
接下来,我们定义代码的核心函数。该函数名为 `extract_features()`,它接收一个图像作为输入并提取所需的图像特征。在我们的示例中,我们将提取与两个主要图像属性相关的特征:每个 RGB 通道(红色、绿色和蓝色)的颜色直方图,以及边缘强度度量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
from skimage.color import rgb2gray from skimage.filters import sobel def extract_features(images, bins_per_channel=8): features = [] for img in images: # Color histogram for each of the 3 RGB channels hist_features = [] for c in range(3): hist, _ = np.histogram(img[:,:,c], bins=bins_per_channel, range=(0, 255)) hist_features.extend(hist) # Edge detection on grayscale image gray_img = rgb2gray(img) edges = sobel(gray_img) edge_strength = np.sum(edges > 0.1) # Merging features features.append(hist_features + [edge_strength]) return np.array(features, dtype=np.float32) |
每个计算出的颜色直方图的 bin 数量设置为 8,以便描述图像颜色属性的信息密度保持在合理水平。对于边缘检测,我们使用了 `skimage` 中的两个函数:`rgb2gray` 和 `sobel`,它们共同帮助检测原始图像的灰度版本上的边缘。
这两组特征被组合在一起,并为数据集中的每张图像重复此过程。
现在我们调用该函数两次:一次用于训练集,一次用于测试集。
1 2 3 4 |
X_train_feats = extract_features(X_train) X_test_feats = extract_features(X_test) print("Feature vector size:", X_train_feats.shape[1]) |
由此产生的包含 RGB 通道直方图和检测到的边缘信息的特征数量为 25。
这是最难的部分!现在我们基本准备好训练一个基于决策树的分类器,该分类器以提取的特征作为输入,而不是原始图像数据。如果您已经熟悉训练 scikit-learn 模型,整个过程就一目了然了:我们只需要确保将提取的特征(而不是原始图像)作为训练和评估的输入。
1 2 3 4 5 6 7 8 9 10 11 |
from sklearn.tree import DecisionTreeClassifier from sklearn.metrics import classification_report, accuracy_score dt_model = DecisionTreeClassifier(random_state=42, max_depth=20) dt_model.fit(X_train_feats, y_train) y_pred_dt = dt_model.predict(X_test_feats) print("MODEL 1. Decision Tree (Color histograms + Edge count):") print("Accuracy:", accuracy_score(y_test, y_pred_dt)) print(classification_report(y_test, y_pred_dt, target_names=class_names)) |
结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Accuracy: 0.2594 precision recall f1-score support airplane 0.33 0.33 0.33 1000 automobile 0.30 0.32 0.31 1000 bird 0.23 0.24 0.24 1000 cat 0.17 0.18 0.17 1000 deer 0.24 0.21 0.23 1000 dog 0.18 0.19 0.19 1000 frog 0.31 0.31 0.31 1000 horse 0.22 0.20 0.21 1000 ship 0.35 0.32 0.33 1000 truck 0.28 0.30 0.29 1000 accuracy 0.26 10000 macro avg 0.26 0.26 0.26 10000 weighted avg 0.26 0.26 0.26 10000 |
不幸的是,决策树在提取的图像特征上表现非常差。而**猜猜看**:这是完全正常且符合预期的。
将 32x32 的彩色图像简化为仅 25 个解释性特征是一种过度简化,它忽略了图像中的细微线索和更深层次的细节,这些细节有助于区分飞机和鸟类,或者狗和猫。请记住,属于同一类别的图像子集(例如“飞机”)在颜色分布等属性上也存在很大的类内差异。但这里重要的收获是学习用于决策树分类器的图像特征提取的方法和局限性;在本教程中,实现高准确率不是我们的主要目标!
尽管如此,如果我们训练一个更先进的基于树的模型,比如随机森林分类器,情况会好转吗?让我们找出答案。
1 2 3 4 5 6 7 8 9 10 |
from sklearn.ensemble import RandomForestClassifier rf_model = RandomForestClassifier(n_estimators=100, random_state=42, n_jobs=-1) rf_model.fit(X_train_feats, y_train) y_pred_rf = rf_model.predict(X_test_feats) print("MODEL 2. Random Forest (Color histograms + Edge count)") print("Accuracy:", accuracy_score(y_test, y_pred_rf)) print(classification_report(y_test, y_pred_rf, target_names=class_names)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
MODEL 2. Random Forest (Color histograms + Edge count) Accuracy: 0.3952 precision recall f1-score support airplane 0.49 0.52 0.51 1000 automobile 0.37 0.48 0.42 1000 bird 0.36 0.30 0.33 1000 cat 0.27 0.19 0.22 1000 deer 0.38 0.34 0.36 1000 dog 0.32 0.29 0.30 1000 frog 0.45 0.50 0.47 1000 马 0.39 0.35 0.36 1000 船 0.46 0.53 0.49 1000 卡车 0.41 0.47 0.44 1000 准确率 0.40 10000 宏平均 平均值 0.39 0.40 0.39 10000 加权平均 平均值 0.39 0.40 0.39 10000 |
这里有所改进,但仍远未完美。想做一些家庭作业吗?尝试将本文学到的知识应用到更简单的数据集,例如 MNIST 或 Fashion MNIST,看看效果如何。它只在分类飞机方面勉强及格,在其他九个类别上仍然不及格!
最后一次尝试:使用 HOG 添加更深层的特征
如果之前提取的特征的信息层次可以说是太浅了,那么添加更多能捕捉图像更细微方面的特征怎么样?其中一种可能是 HOG(方向梯度直方图),它可以捕捉形状和纹理等属性,并增加大量的额外特征。
以下代码扩展了特征提取过程,并将其应用于训练另一个随机森林分类器(祈祷有好运)。
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 |
from skimage.color import rgb2gray from skimage.feature import hog from skimage.filters import sobel import numpy as np def extract_rich_features(images, bins_per_channel=16, hog_pixels_per_cell=(8,8)): features = [] for img in images: hist_features = [] for c in range(3): # R, G, B hist, _ = np.histogram(img[:,:,c], bins=bins_per_channel, range=(0, 255)) hist_features.extend(hist) gray_img = rgb2gray(img) hog_features = hog( gray_img, pixels_per_cell=hog_pixels_per_cell, cells_per_block=(1,1), orientations=9, block_norm='L2-Hys', feature_vector=True ) edges = sobel(gray_img) edge_density = np.sum(edges > 0.1) / edges.size combined = np.hstack([hist_features, hog_features, edge_density]) features.append(combined) return np.array(features, dtype=np.float32) X_train_feats = extract_rich_features(X_train) X_test_feats = extract_rich_features(X_test) print("新特征向量大小:", X_train_feats.shape[1]) |
训练一个新分类器(我们现在有 193 个特征,而不是 25 个!)
1 2 3 4 5 6 7 8 9 10 11 |
from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import classification_report, accuracy_score rf_model = RandomForestClassifier(n_estimators=100, random_state=42, n_jobs=-1) rf_model.fit(X_train_feats, y_train) y_pred_rf = rf_model.predict(X_test_feats) print("模型 3. 随机森林(颜色直方图 + HOG + 边缘密度)") print("Accuracy:", accuracy_score(y_test, y_pred_rf)) print(classification_report(y_test, y_pred_rf, target_names=class_names)) |
结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
模型 3. 随机 森林 (颜色 直方图 + HOG + 边缘 密度) 准确率: 0.486 precision recall f1-score support 飞机 0.57 0.62 0.59 1000 汽车 0.56 0.67 0.61 1000 鸟 0.45 0.32 0.37 1000 猫 0.34 0.25 0.29 1000 鹿 0.43 0.43 0.43 1000 狗 0.39 0.42 0.40 1000 青蛙 0.52 0.56 0.54 1000 马 0.49 0.44 0.46 1000 船 0.54 0.59 0.56 1000 卡车 0.49 0.58 0.53 1000 准确率 0.49 10000 宏平均 平均值 0.48 0.49 0.48 10000 加权平均 平均值 0.48 0.49 0.48 10000 |
好吧,虽然缓慢但稳定,我们设法取得了一点小小的进步,至少现在有几个类别在某些评估指标上获得了及格分,而不仅仅是“飞机”。但距离目标仍有很长的路要走:学到了宝贵的一课。
总结
本文展示了如何训练能够处理从图像数据中提取的视觉特征(如颜色通道分布和检测到的边缘)的决策树模型,并强调了这种方法的优势和局限性。
暂无评论。