基于keras的LeNet-5模型可视化、网络特征可视化及kernel可视化
卷积神经网络中模型、层中输出的特征和kernel的可视化,对卷积神经网络的设计起到很重要的帮助。在此,以最简单的LeNet-5和手写数字集为例,对LeNet-5进行模型可视化、网络中特征可视化话及kernel可视化。代码链接在本文最后。
1. LeNet-5详解
LeNet5 这个网络虽然很小,但是它包含了深度学习的基本模块:卷积层,池化层,全链接层。是其他深度学习模型的基础, 这里我们对LeNet5进行深入分析。同时,通过实例分析,加深对与卷积层和池化层的理解。
LeNet-5共有7层,不包含输入,每层都包含可训练参数;每个层有多个Feature Map,每个FeatureMap通过一种卷积滤波器提取输入的一种特征,然后每个FeatureMap有多个神经元。
各层参数详解:
1、INPUT层-输入层
首先是数据 INPUT 层,输入图像的尺寸统一归一化为32*32。
注意:本层不算LeNet-5的网络结构,传统上,不将输入层视为网络层次结构之一。
2、C1层-卷积层
输入图片:32*32
卷积核大小:5*5
卷积核种类:6
输出featuremap大小:28*28 (32-5+1)=28
神经元数量:28*28*6
可训练参数:(5*5+1) * 6(每个滤波器5*5=25个unit参数和一个bias参数,一共6个滤波器)
连接数:(5*5+1)*6*28*28=122304
3、S2层-池化层(下采样层)
输入:28*28
采样区域:2*2
采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid
采样种类:6
输出featureMap大小:14*14(28/2)
神经元数量:14*14*6
可训练参数:2*6(和的权+偏置)
4、C3层-卷积层
输入:S2中所有6个或者几个特征map组合
卷积核大小:5*5
卷积核种类:16
可训练参数:6*(3*5*5+1)+6*(4*5*5+1)+3*(4*5*5+1)+1*(6*5*5+1)=1516
连接数:10*10*1516=151600
5、S4层-池化层(下采样层)
输入:10*10
采样区域:2*2
采样方式:4个输入相加,乘以一个可训练参数,再加上一个可训练偏置。结果通过sigmoid
采样种类:16
输出featureMap大小:5*5(10/2)
神经元数量:5*5*16=400
可训练参数:2*16=32(和的权+偏置)
连接数:16*(2*2+1)*5*5=2000
6、C5层-卷积层
输入:S4层的全部16个单元特征map(与s4全相连)
卷积核大小:5*5
卷积核种类:120
输出featureMap大小:1*1(5-5+1)
可训练参数/连接:120*(16*5*5+1)=48120
7、F6层-全连接层
输入:c5 120维向量
计算方式:计算输入向量和权重向量之间的点积,再加上一个偏置,结果通过sigmoid函数输出。
可训练参数:84*(120+1)=10164
8、Output层-全连接层
Output层也是全连接层,共有10个节点,分别代表数字0到9,且如果节点i的值为0,则网络识别的结果是数字i。采用的是径向基函数(RBF)的网络连接方式。假设x是上一层的输入,y是RBF的输出,则RBF输出的计算方式是:
上式w_ij 的值由i的比特图编码确定,i从0到9,j取值从0到7*12-1。RBF输出的值越接近于0,则越接近于i,即越接近于i的ASCII编码图,表示当前网络输入的识别结果是字符i。该层有84x10=840个参数和连接。
2. 基于Keras的LeNet-5的实现
from keras.models import Sequential
from keras.layers import Dense,Flatten
from keras.layers.convolutional import Conv2D,MaxPooling2D
from keras.utils.np_utils import to_categorical
from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
x_train = x_train.reshape((-1,28,28,1))
y_train = to_categorical(y_train,10)
x_test = x_test.reshape((-1,28,28,1))
y_test = to_categorical(y_test,10)
#model=load_model('E:/LeNet/LeNet-5_model.h5')
model = Sequential()
model.add(Conv2D(6,(5,5),strides=(1,1),input_shape=(28,28,1),padding='valid',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(16,(5,5),strides=(1,1),padding='valid',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(120,activation='relu'))
model.add(Dense(84,activation='relu'))
model.add(Dense(10,activation='softmax'))
model.compile(optimizer='sgd',loss='categorical_crossentropy',metrics=['accuracy'])
model.summary()
model.fit(x_train,y_train,batch_size=100,epochs=50,shuffle=True)
model.save('E:/LeNet/LeNet-5_model.h5')
#[0.10342620456655367 0.9834000068902969]
loss, accuracy=model.evaluate(x_test, y_test,batch_size=100)
print(loss, accuracy)
3. LeNet-5模型可视化
keras有模块可视化工具,可以以图片方式可视化网络结构,但在这之前需要先安装下面的包:
pip install pydot
pip install graphviz
但安装完上面的包后,直接运行还是会报错。实际上问题在于执行plot_model需要graphviz的二进制文件,解决方法是在graphviz官网下载界面http://www.graphviz.org/download/选择自己操作系统对应的版本的.msi文件
例如Windows的下载链接是https://graphviz.gitlab.io/_pages/Download/windows/graphviz-2.38.msi,安装完成后将安装的Graphviz2.38/bin添加环境变量即可。
在代码中添加
from keras.utils import plot_model
plot_model(model, to_file='model.png',show_shapes=True)
生成的模型结构图片如下
4. 网络特征可视化
#----------------------------------各个层特征可视化-------------------------------
#查看输入图片
from keras import backend as K
from keras.models import load_model
import numpy as np
import matplotlib.pyplot as plt
from keras.datasets import mnist
(x_train, y_train), (x_test, y_test) = mnist.load_data()
#加载前面保存的模型
model=load_model('E:/LeNet/LeNet-5_model.h5')
#查看输入图片
fig1,ax1 = plt.subplots(figsize=(4,4))
ax1.imshow(np.reshape(x_test[12], (28, 28)))
plt.show()
image_arr=np.reshape(x_test[12], (-1,28, 28,1))
#可视化第一个MaxPooling2D
layer_1 = K.function([model.layers[0].input], [model.layers[1].output])
# 只修改inpu_image
f1 = layer_1([image_arr])[0]
# 第一层卷积后的特征图展示,输出是(1,12,12,6),(样本个数,特征图尺寸长,特征图尺寸宽,特征图个数)
re = np.transpose(f1, (0,3,1,2))
for i in range(6):
plt.subplot(2,4,i+1)
plt.imshow(re[0][i]) #,cmap='gray'
plt.show()
#可视化第二个MaxPooling2D
layer_2 = K.function([model.layers[0].input], [model.layers[3].output])
f2 = layer_2([image_arr])[0]
# 第一层卷积后的特征图展示,输出是(1,4,4,16),(样本个数,特征图尺寸长,特征图尺寸宽,特征图个数)
re = np.transpose(f2, (0,3,1,2))
for i in range(16):
plt.subplot(4,4,i+1)
plt.imshow(re[0][i]) #, cmap='gray'
plt.show()
运行结果:
5. kernel可视化
from keras import backend as K
from keras.models import load_model
import numpy as np
import matplotlib.pyplot as plt
model=load_model('E:/LeNet/LeNet-5_model.h5')
#----------------------------------可视化滤波器-------------------------------
#将张量转换成有效图像
def deprocess_image(x):
# 对张量进行规范化
x -= x.mean()
x /= (x.std() + 1e-5)
x *= 0.1
x += 0.5
x = np.clip(x, 0, 1)
# 转化到RGB数组
x *= 255
x = np.clip(x, 0, 255).astype('uint8')
return x
for i_kernal in range(10):
input_img=model.input
## 构建一个损耗函数,使所考虑的层的第n个滤波器的激活最大化,-1层softmax层
loss = K.mean(model.layers[-1].output[:,i_kernal])
# loss = K.mean(model.output[:, :,:, i_kernal])
# 计算输入图像的梯度与这个损失
grads = K.gradients(loss, input_img)[0]
# 效用函数通过其L2范数标准化张量
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)
# 此函数返回给定输入图像的损耗和梯度
iterate = K.function([input_img, K.learning_phase()], [loss, grads])
# 从带有一些随机噪声的灰色图像开始
np.random.seed(0)
#图像通道
num_channels=1
#输入图像尺寸
img_height=img_width=28
#归一化图像
input_img_data = (255- np.random.randint(0,255,(1, img_height, img_width, num_channels))) / 255.
failed = False
# run gradient ascent
print('####################################',i_kernal+1)
loss_value_pre=0
# 运行梯度上升500步
for i in range(500):
loss_value, grads_value = iterate([input_img_data,1])
if i%10 == 0:
# print(' predictions: ' , np.shape(predictions), np.argmax(predictions))
print('Iteration %d/%d, loss: %f' % (i, 500, loss_value))
print('Mean grad: %f' % np.mean(grads_value))
if all(np.abs(grads_val) < 0.000001 for grads_val in grads_value.flatten()):
failed = True
print('Failed')
break
# print('Image:\n%s' % str(input_img_data[0,0,:,:]))
if loss_value_pre != 0 and loss_value_pre > loss_value:
break
if loss_value_pre == 0:
loss_value_pre = loss_value
# if loss_value > 0.99:
# break
input_img_data += grads_value * 1 #e-3
plt.subplot(2,5, i_kernal+1)
# plt.imshow((process(input_img_data[0,:,:,0])*255).astype('uint8'), cmap='Greys') #cmap='Greys'
img_re = deprocess_image(input_img_data[0])
img_re = np.reshape(img_re, (28,28))
plt.imshow(img_re) #cmap='Greys'
plt.show()
运行结果:
layers[3]
GitHub连接:https://github.com/1057520143/LeNet
码云连接:https://gitee.com/liao1057520143/LeNet
参考博客:
https://blog.csdn.net/xinfeng2005/article/details/78697415
http://cuijiahua.com/blog/2018/01/dl_3.html
https://blog.csdn.net/da_kao_la/article/details/80025124