DAY 43 复习日
@浙大疏锦行https://blog.csdn.net/weixin_45655710
第一步:寻找并准备图像数据集
在Kaggle等平台上,你可以找到大量用于图像分类任务的数据集,例如英特尔图像分类数据集 (Intel Image Classification) 或手写数字识别数据集 (Digit Recognizer) 。
对于初学者,一个更便捷的选择是使用像TensorFlow或PyTorch这样深度学习框架内置的数据集,例如CIFAR-10。这个数据集包含了10个类别的60,000张32x32彩色图像,非常适合用于学习和实验3。
数据加载与预处理
使用TensorFlow的Keras API可以轻松加载并准备CIFAR-10数据集。主要步骤包括加载数据和将像素值归一化到0到1的范围内,这有助于模型更快、更稳定地收敛。
import tensorflow as tf
from tensorflow.keras import datasets, layers, models
import matplotlib.pyplot as plt# 加载CIFAR10数据集
(train_images, train_labels), (test_images, test_labels) = datasets.cifar10.load_data()# 将像素值归一化到0和1之间
train_images, test_images = train_images / 255.0, test_images / 255.0# 类别名称
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer','dog', 'frog', 'horse', 'ship', 'truck']
第二步:构建并训练CNN模型
CNN是处理图像数据的核心工具。一个基础的CNN模型通常由卷积层(Conv2D
)和池化层(MaxPooling2D
)交替堆叠而成,最后连接全连接层进行分类。
构建模型
以下是一个简单的CNN模型结构示例,它包含三个卷积层,用于从输入图像中提取特征。
model = models.Sequential()
# 第一个卷积-池化层
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(32, 32, 3)))
model.add(layers.MaxPooling2D((2, 2)))
# 第二个卷积-池化层
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
# 第三个卷积层
model.add(layers.Conv2D(64, (3, 3), activation='relu'))# 添加全连接层进行分类
model.add(layers.Flatten())
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(10)) # 输出层有10个节点,对应10个类别model.summary()
编译与训练
模型构建完成后,需要进行编译,指定优化器、损失函数和评估指标。然后,使用fit
方法在训练数据上进行训练。
# 编译模型
model.compile(optimizer='adam',loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),metrics=['accuracy'])# 训练模型
history = model.fit(train_images, train_labels, epochs=10, validation_data=(test_images, test_labels))
第三步:使用Grad-CAM进行可视化
Grad-CAM(梯度加权类激活图)是一种可视化技术,它能生成一个热力图(heatmap),显示出图像中哪些区域对于模型的特定预测贡献最大。这对于理解和调试“黑箱”一样的深度学习模型非常有帮助。
Grad-CAM实现原理
-
选择目标层: 通常选择模型中最后一个卷积层的输出作为目标。
-
计算梯度: 计算模型对于某个类别的预测得分相对于目标层输出特征图(feature maps)的梯度。
-
权重计算: 对梯度图进行全局平均池化,得到每个特征图通道的“重要性”权重。
-
生成热力图: 将特征图与对应的权重相乘并求和,然后通过ReLU激活函数(只保留正值部分)生成最终的热力图。
-
叠加显示: 将标准化的热力图缩放到原始图像大小,并与原图叠加显示。
import numpy as np
import tensorflow as tf
import cv2def make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):# 创建一个子模型,输出最后一个卷积层的激活图和模型的最终预测grad_model = tf.keras.models.Model([model.inputs], [model.get_layer(last_conv_layer_name).output, model.output])# 使用GradientTape记录梯度计算过程with tf.GradientTape() as tape:last_conv_layer_output, preds = grad_model(img_array)if pred_index is None:pred_index = tf.argmax(preds[0])class_channel = preds[:, pred_index]# 计算预测类别对于最后一个卷积层输出的梯度grads = tape.gradient(class_channel, last_conv_layer_output)# 计算每个通道的平均梯度,作为权重pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))# 将权重与特征图相乘,得到热力图last_conv_layer_output = last_conv_layer_output[0]heatmap = last_conv_layer_output @ pooled_grads[..., tf.newaxis]heatmap = tf.squeeze(heatmap)# 归一化热力图heatmap = tf.maximum(heatmap, 0) / tf.math.reduce_max(heatmap)return heatmap.numpy()def display_gradcam(img, heatmap, alpha=0.4):# 将热力图调整为图像大小heatmap = cv2.resize(heatmap, (img.shape[1], img.shape[0]))heatmap = np.uint8(255 * heatmap)heatmap = cv2.applyColorMap(heatmap, cv2.COLORMAP_JET)# 将热力图叠加到原始图像上superimposed_img = heatmap * alpha + img * (1-alpha)superimposed_img = np.clip(superimposed_img, 0, 255).astype(np.uint8)return superimposed_img
通过调用这些函数,你可以看到模型在进行预测时,究竟“关注”了图像的哪个部分。例如,在识别一只猫时,热力图可能会高亮显示猫的脸部和耳朵区域。
进阶:将代码拆分成多个文件
当项目变得复杂时,将所有代码放在一个文件中会变得难以阅读和维护。将代码按功能拆分到不同文件中是一种良好的编程实践,这能提高代码的可读性、复用性和组织性。
为什么要拆分文件?
-
关注点分离: 每个文件专注于一个特定功能,如数据处理、模型定义或可视化。
-
提高可读性: 小而专注的文件更容易理解和导航。
-
易于复用: 可以轻松地在项目的不同部分导入和重用特定功能的代码。
如何拆分?
你可以创建一个项目文件夹,并在其中创建多个.py文件。Python允许你使用import
语句从同一目录下的其他文件中导入函数、类或变量。
一个典型的项目结构可能如下:
-
main.py
: 项目主入口,负责调用其他模块,协调整个训练和评估流程。 -
data_loader.py
: 包含加载和预处理数据的函数。 -
model.py
: 定义CNN模型的结构。 -
gradcam.py
: 包含make_gradcam_heatmap
等可视化相关的函数。 -
train.py
: 包含模型训练的逻辑。 -
visualize.py
: 用于加载已训练好的模型,并对指定图像生成Grad-CAM可视化。
# model.py
from tensorflow.keras import layers, modelsdef create_cnn_model():model = models.Sequential([...]) # 省略模型定义代码return model
# train.py
from model import create_cnn_model
from data_loader import load_cifar10_data# 加载数据
(train_images, train_labels), _ = load_cifar10_data()# 创建并训练模型
model = create_cnn_model()
model.compile(...)
model.fit(...)
model.save('cifar10_cnn.h5')