【Deep Learning with Python】C5 Activation,Filter可视化
Activation
总的来说,拿到中间层次输出,用一张图片输入,可视化。如何获得中间层次。这一节的程序很有用。
- 多输出模型
from keras import models
layer_outputs = [layer.output for layer in model.layers[:8]]
activation_model = models.Model(inputs=model.input, outputs=layer_outputs)
上述代码使用了8个中间层作为输出层。结果将返回一个8维的列表。
- CNN特征抽取的理解更正
第一层
最后一层
观察到一个问题:前面的层次输出非常具体,后面的层次反而看不懂。这和我的直觉是相反的。
我理解的有问题
- 前面的卷积核提取线段边缘特征之后,合并仍然会比较接近原图,但是后期的一个值可能就代表一整只猫
- 后期的特征高度融合,会变得越来越复杂,越来越抽象。也可以这么理解,它会保持住越来越少的整体原图信息,却会获得越来越多关于本类别的信息。
- 后期的卷积核所具备的捕获特征的能力会变得越来越强。
- 越往后的activation**的越少,这意味着,该卷积无法捕获相应信息。
- 知识蒸馏
从这个角度来理解特征高度抽象过程。文中给出了人类的一种类比,比如单车,尽管我们看过无数单车,但是我们没有办法回忆起大部分细节。只能记住分把单车和其他东西区分开来的信息,换句话说,我们并不是只能记得个大概,我们的记忆只保留了关键信息,将无用的信息筛除掉。
Filter
卷积核可视化还是比较难理解的。每一个卷积核代表着一种特征。如果图像中某块区域与某个卷积核的结果越大,那么该区域就越“像”该卷积核。
这里使用了“梯度上升”的方式,随机输入,利用梯度,使得卷积核的输出最大,方便可视化。实现不是很好理解,因为涉及了很多底层的api。
上图为一部分卷积核的可视结果,不要和activation的可视化混淆。
可以看到,卷积核的可视化,很类似某些纹理,和傅里叶变换类似,卷积核就像一个个的三角函数,复合成复杂的图像。
def generate_pattern(layer_name, filter_index, size=150):
# 得到某层的输出activation
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)
# 这是个有意思的函数,tf中定义好图,如何打包遍历,用到就是function
iterate = K.function([model.input], [loss, grads])
# 随机输入,利用梯度最大化,使其最终和filter非常像
input_img_data = np.random.random((1, size, size, 3)) * 20 + 128.
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)
可视化技巧
- 大量展示
原理是新建一个大的数组,一个一个排上去,所以coding有些技巧。
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)
i和j从0开始,刚好使得完美控制margin
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)
- 让数据可视化更加好看
def deprocess_image(x):
这两步是标准化
x -= x.mean()
x /= (x.std() + 1e-5)
这应该是一个放大因子
x *= 0.1
这一步是标准化中减去mean的逆操作
x += 0.5
防止超过范围
x = np.clip(x, 0, 1)
x *= 255
x = np.clip(x, 0, 255).astype('uint8')
return x
Reference
Main3.py