卷积神经网络构建,新图片预测与类激活图——提高CNN模型的可解释性

卷积神经网络构建,新图片预测与类激活图——提高CNN模型的可解释性


前言

卷积神经网络(CNN)作为最基础的图像分类与识别的深度学习模型,在很多领域都有着应用,例如人脸识别,医学图像分类等。本文从CNN出发,介绍基本CNN模型的构建,逐渐深入到复杂CNN变体VGG16的搭建。同时为了提高CNN这类模型的可解释性,使用已保存的CNN和VGG16模型预测批量的新图片,借助类激活图(CAM, class activation map) 对分类结果进行解释。最后绘制受试者工作特性曲线(ROC),对整体预测结果进行分析。


一、卷积神经网络CNN与VGG16 介绍

这部分只做卷积神经网络(CNN)和VGG16的简单介绍,使读者有个基本的了解,具体详细原理及参数请参考其他资料。CNN模型主要结构包括输入层、卷积层、池化层、全连接层和输出层。其中卷积层和池化层是CNN模型关键的功能层。卷积层的作用是对图片进行卷积,从中提取特征。卷积层由不同卷积核组成,常用3 * 3大小卷积核,不同卷积层提取不同特征,例如有的提取边界线条、有的提取整个色块。形象的说,卷积核就像扫描仪,对图片各部分进行扫描,但是这个扫描仪只对它感兴趣的形状重视,不重视的形状重视程度降低。而池化层则是为了降低卷积层提取的特征维度,常见池化的方式包括最大池化和平均池化,池化常用的窗口大小为2 * 2,移动步幅为2。全连接层则是为了将得到的特征展平,方便使用激活函数进行分类。VGG16则是一个16层的卷积神经网络(不包括最大池化层与softmax层),是牛津大学视觉几何团队开发的,目前也常被用于图像分类的任务中。

二、搭建CNN模型

本次代码在python3.7版本的tensroflow框架下搭建,使用了Jupyter notebook来运行。主要任务是对实现猫、狗、兔图片进行多分类,其中训练集数据各有200张,验证集各50张左右,测试集新图片各10张。需要说明的是,本文只是为了说明模型搭建和结果分析,固未使用更多数据,也没有进行更多的调优。读者可根据实际情况进行数据量的扩充和模型的调优。

1.输入数据与图片增强

代码如下():

import os #输入数据
from glob import glob
train_dir = 'D:/AM Learning/data/kaggle/3categories/train'      #训练集的保存路径
test_dir = 'D:/AM Learning/data/kaggle/3categories/validation'  #验证集的保存路径

nb_train_samples = 600                 #训练集的图片总数量
nb_validation_samples = 161            验证集的图片数量
epochs = 25
batch_size = 20


from keras.preprocessing.image import ImageDataGenerator #对图片进行处理
#(通过数据增强对图片进行变换) 
train_datagen = ImageDataGenerator(rescale=1./255,rotation_range =40,   #(训练集图片增强)
                                   width_shift_range=0.2, height_shift_range=0.2,
                                   shear_range=0.2,zoom_range=0.2,horizontal_flip=True)
test_datagen = ImageDataGenerator(rescale=1./255)           #(验证集图片像素标准化)
 
train_generator = train_datagen.flow_from_directory(train_dir,
                                                    target_size=(150, 150),         #(图片大小)
                                                    batch_size=batch_size,
                                                    class_mode='categorical')      #(分类类别,二分类对应binary,多分类对应categorical)
validation_generator = test_datagen.flow_from_directory(test_dir,
                                                      target_size=(150, 150),
                                                      batch_size=batch_size,
                                                      class_mode='categorical')

2.搭建CNN模型与编译

其中模型的卷积层层数、卷积核数等可根据读者需求调整,代码如下:

from keras import models 
from keras import layers
from keras import optimizers
 
model = models.Sequential()
model.add(layers.Conv2D(16,(3,3),activation='relu',input_shape=(150,150,3)))#16个卷积核,每个卷积核大小为3*3
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
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(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
#model.add(layers.Dropout(0.5))
model.add(layers.Dense(64, activation='relu'))
model.add(layers.Dense(3, activation='softmax'))      #数字3表示图片的类别,用于分类的激活函数二分类使用sigmoid,多分类单标签使用softmax

#编译模型
model.compile(optimizer=optimizers.RMSprop(lr=1e-5),   #设置激活函数和学习率,可以根据实际需要调整
              loss='categorical_crossentropy',         #误差,二分类对应binary_crossentorpy
              metrics=['acc'])
              
#查看模型结构及内部参数量
model.summary()

#打印不同类别对应的标签
print(train_generator.class_indices)       

3.训练模型并保存

#训练模型
history = model.fit_generator(train_generator,   #利用批量生成器拟合模型
                              steps_per_epoch=nb_train_samples/batch_size,
                              epochs=25,
                              validation_data=validation_generator,
                              validation_steps=nb_validation_samples/batch_size)
#以“3categories”的名字保存模型
model.save('3categories.h5')

4.输出训练结果

#打印训练集结果与验证集结果
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(acc)+1)
plt.plot(epochs, acc, 'bo', label = 'Training acc')
plt.plot(epochs, val_acc, 'b', label = 'Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'ro', label = 'Training loss')
plt.plot(epochs, val_loss, 'r', label = 'Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

训练集和验证及结果如图:


四、批量新图片的分类及类激活图

在训练好CNN模型之后,需要在新的图片上验证其泛化能力,即新数据上分类或预测的准确率。本次还借助类激活图对CNN模型的结果进行了分析。类激活图是指对输入图像生成类激活的热力图,具体方法是对输入图像的一层卷积层提取的特征图,用类别相对于通道的梯度对这个特征图中的每个通道进行加权。直观来说,就是用“每个通道对类别的重要程度”对“输入图像对不同的通道的激活强度”的空间进行加权,从而得到了“输入图像对类别的激活强度”的空间图。

1.导入模型和要分类的图片

import keras
from tensorflow.keras.applications import VGG16
#导入已保存地CNN模型
model = keras.models.load_model('3categories.h5')
# 导入新图片(使用目标图像的本地路径)
img_path = 'D:/AM Learning/data/kaggle/3categories/test/1/dog.19.jpg'

2.实现分类并输出结果

from tensorflow.keras.preprocessing import image
from keras.preprocessing.image import img_to_array
from keras.applications.vgg16 import preprocess_input
import numpy as np
import cv2  
# (大小为 150×150 的Python图像库(PIL,Python imaging library)图像)
img = cv2.imread(img_path)
# x形 状 为 (150, 150, 3) 的float32 格式的 Numpy 数组 ,保持和训练时用的图片尺寸大小相同
img_arr = cv2.resize(img,(150,150))  
ima_arr = img_to_array(img_arr)/255.0
img_arr = np.expand_dims(img_arr,axis=0)
img_arr=preprocess_input(img_arr)
#预测目标分类
preds = model.predict(img_arr)
proba = np.max(preds) 
print('Predicted:',preds)  #不同类别的可能性
print('prob:',proba)       #最终预测的图片类别对应的可能性大小

3.绘制类映射图

from keras import backend as K
index=np.argmax(preds[0])
cat_output = model.output[:, index] 
# conv2d_4层的输出特征图,它是模型的最后一个卷积层,读者根据本身实际情况调整卷积层的名字
last_conv_layer = model.get_layer('conv2d_4')
 
# “图片”类别相对于 conv2d_4输出特征图的梯度
grads = K.gradients(cat_output, last_conv_layer.output)[0]
 
# 形状为 (512,) 的向量,每个元素是特定特征图通道的梯度平均大小
pooled_grads = K.mean(grads, axis=(0, 1, 2))
  
# 访问刚刚定义的量:对于给定的样本图像,pooled_grads 和 block5_conv3 层的输出特征图 
iterate = K.function([model.input], [pooled_grads, last_conv_layer.output[0]])
 
# 对于样本图像, 这两个量都是 Numpy 数组
pooled_grads_value, conv_layer_output_value = iterate([img_arr])

# 将特征图数组的每个通道乘以“这个通道 对类别的重要程度”
for i in range(conv_layer_output_value.shape[-1]):
    conv_layer_output_value[:, :, i] *= pooled_grads_value[i]
 # 得到的特征图的逐通道平均值即为类激活的热力图
heatmap = np.mean(conv_layer_output_value, axis=-1)
import matplotlib.pyplot as plt
max_heat = np.max(heatmap)
if max_heat == 0:
    max_heat = 1e-10
heatmap /= max_heat
plt.matshow(heatmap)
plt.show()

选择了两张新图片测试,结果如下图。可以看见“狗”这个类别分类时更关注“黑色”和和“嘴”这两个特征,而兔子更关注”身体“和“白色”这两个特征。当然不同的训练模型会有不同的结果,这里只说原理,不详细讨论结果。具体问题具体分析。
在这里插入图片描述

结论

本文阐述了卷积神经网络CNN的搭建过程,并对新的图片进行预测。同时还阐述了类激活图的搭建预分析。但是还存在以下问题:(1)进行新图片预测时,只能单张的预测,效率较低,应该考虑批量图片的预测;(2)图片数据量较少时,预测准确率较低,应考虑较复杂的模型。以上这两个问题将会在后面进行讨论。
本文参考了《python深度学习》与部分网友的思路,若侵权请联系本人。

  • 12
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值