卷积神经网络的可视化:可视化卷积神经网络中间输出(中间激活),可视化卷积神经网络的过滤器,可视化图像中类激活的热力图。
特征图中,第一层是更重边缘探测器的集合,随着层数加深,激活变得越来越抽象,激活的稀疏度随着层数的加深而增大。
深度神经网络可以有效的作为信息蒸馏管道,输入原始数据,反复对其进行变换,过滤无关信息,放大和细化有用的信息。
from keras.models import load_model
model=load_model('cats_and_dogs_small_2.h5')
model.summary()
#预处理单张图像
img_path='D:\\jupyter_code\\kaggle\\train1\\test\\cats\\cat.1700.jpg'
from keras.preprocessing import image
import numpy as np
#图像预处理为一个4D张量
img=image.load_img(img_path,target_size=(150,150))
#将python图像库转换为一个numpy数组
img_tensor=image.img_to_array(img)
#拓展转换后的数组形状,完成4D张量的转换
img_tensor=np.expand_dims(img_tensor,axis=0)
img_tensor/=255.
print(img_tensor.shape)
import matplotlib.pyplot as plt
plt.imshow(img_tensor[0])
plt.show()
#用输入输出张量列表将模型实例化,(图像批量作为输入,输出所有卷积层的激活)
from keras import models
#提取前8层的输出
layer_outputs=[layer.output for layer in model.layers[:8]]
activation_model=models.Model(inputs=model.input,outputs=layer_outputs)
#返回8个Numpy数组组成的列表,每个层对应激活一个numpy数组。
activations=activation_model.predict(img_tensor)
first_layer_activation=activations[0]
print(first_layer_activation.shape)
#打印特征图形状可以看到有32个通道,每层对应4个通道
#将第7个通道可视化
import matplotlib.pyplot as plt
plt.matshow(first_layer_activation[0, :,:,7],cmap='viridis')
#将每个中间激活的通道可视化
#为层添加
layer_names=[]
for layer in model.layers[:8]:
layer_names.append(layer.name)
images_per_row=16
#显示特征图
for layer_names,layer_activation in zip(layer_names,activations):
#特征图中特征格式
n_feature=layer_activation.shape[-1]
#特征形状
size=layer_activation.shape[1]
n_cols=n_feature//images_per_row
display_grid=np.zeros((size*n_cols,images_per_row*size))
#将过滤器平铺到打的水平网络
for col in range(n_cols):
for row in range(images_per_row):
channel_image=layer_activation[0,:,:,col*images_per_row+row]
channel_image-=channel_image.mean()
channel_image/=channel_image.std()
channel_image*=64
channel_image+=128
channel_image=np.clip(channel_image,0,255).astype('uint8')
display_grid[col*size:(col+1)*size,
row*size:(row+1)*size]=channel_image
scale=1./size
plt.figure(figsize=(scale*display_grid.shape[1],
scale*display_grid.shape[0]))
plt.title(layer_names)
plt.grid(False)
plt.imshow(display_grid,aspect='auto',cmap='viridis')
2.可视化卷积神经网络的过滤器(显示过滤器响应的视觉模式)
显示每个过滤器所响应的视觉模式,通过在输入空间中进行梯度上升来实现:从空白输入图像开始,将梯度下降应用于卷积神经网络输入图像的值,目的是让某个过滤器的响应最大化。得到的输入图像是选定过滤器具有最大响应的图像。
卷积神经网络中每一层都设置一组过滤器,以便其输入表示为过滤器的组合。随着层数加深,卷积神经网络中过滤变得越来越复杂,越来越精细。
#可视化卷积神经网络的过滤器
#为过滤器的可视化定义损失张量
from keras.applications import VGG16
#此处的k为TensorFlow
from keras import backend as K
model=VGG16(weights='imagenet',include_top=False)
layer_name='block3_conv1'
filter_index=0
layer_output=model.get_layer(layer_name).output
loss=K.mean(layer_output[:,:,:,filter_index])
#获取损失相对于输入的梯度,此处K.gradients相当于tf.gradients,实现对ys(loss),xs(model.input)求导
grads=K.gradients(loss,model.input)[0]
#梯度标准化技巧
grads/=(K.sqrt(K.mean(K.square(grads)))+1e-5)
#给定Numpy输入值,得到Numpy输出值
#iterare将一个Numpy张量转换为2个Numpy张量组成的列表
iterate=K.function([model.input],[loss,grads])
import numpy as np
loss_value,grads_value=iterate([np.zeros((1,150,150,3))])
#通过随机梯度下降让损失最大化
input_img_data=np.random.random((1,150,150,3))*20+128
#每次梯度更新的步长
step=1.
for i in range(40):
#计算损失值和梯度值
loss_value,grads_value=iterate([input_img_data])
#沿着让损失最大化的方向调节输入图像(梯度上升)
input_img_data+=grads_value*step
#将张量转换为有效图像的使用函数
def deprocess_image(x):
#张量标准化,均值为0 ,标准差为0.1
x-=x.mean()
x/=(x.std()+1e-5)
x*=0.1
#将x裁剪到(0,1)区间
x+=0.5
x=np.clip(x,0,1)
#x转RGB数组
x*=255
x=np.clip(x,0,255).astype('uint8')
return x
#生成过滤器的可视化函数
def generate_pattern(layer_name,filter_index,size=150):
#构建损失函数,该层第n个过滤器的激活最大化
layer_output=model.get_layer(layer_name).output
loss=K.mean(layer_output[:,:,:,filter_index])
#计算损失相对于输入图像的梯度
grads=K.gradients(loss,model.input)[0]
#梯度标准化
grads/=(K.sqrt(K.mean(K.square(grads)))+1e-5)
#返回给定输入图像的损失和梯度
iterate=K.function([model.input],[loss,grads])
input_img_data=np.random.random((1,size,size,3))*20+128
#运行40次梯度上升
step=1.
for i in range(40):
loss_value,grads_value=iterate([input_img_data])
input_img_data+=grads_value*step
img=input_img_data[0]
return deprocess_image(img)
plt.imshow(generate_pattern('block3_conv1',0))
layer_name='block2_conv1'
size=64
margin=5
results=np.zeros((8*size+7*margin,8*size+7*margin,3))
for i in range(8):
for j in range(8):
filter_img=generate_pattern(layer_name,i+(j*8),size=size)
horizontal_start=i*size+i*margin
horizontal_end=horizontal_start+size
vertical_start=j*size+j*margin
vertical_end=vertical_start+size
results[horizontal_start:horizontal_end,vertical_start:vertical_end,:]=filter_img
plt.figure(figsize=(20,20))
plt.imshow(results)
3.可视化类激活的热力图(了解一张图像的那一部分让神经网络做出了分类决策)
类激活图可视化:类激活热力图是与特定输出类别相关的二维分数网络,对任何输入图像的每个位置都要进行计算,表示每个位置对类别的重要程度。
实现方法:给定输入图像,对于卷积层的输出特征图,用类别相对于通道的梯度对这个特征图这个每个通道进行加权。
由“每个通道对类别的重要程度”对 “不同通道的激活程度”的空间图加权,得到“类别的激活程度”(A->B)&(A->C)à(B->C)