keras_猫狗分类案例(三)_卷机神经网络的可视化(可视化卷积神经网络的过滤器)

可视化卷积神经网络的过滤器
参考:
https://cloud.tencent.com/developer/article/1502652

	想要观察卷积神经网络学到的过滤器,另一种简单的方法是显示每个过滤器所响应的视觉模式。这可以通过在**输入空间中进行梯度上升**来实现:从空白输入图像开始,将梯度下降应用于卷积神经网络输入图像的值,其目的是让某个过滤器的**响应最大化**。得到的输入图像是选定过滤器具有最大响应的图像

# 这个过程很简单:我们需要构建一个损失函数,其目的是让某个卷积层的某个过滤器的值最
# 大化;然后,我们要使用随机梯度下降来调节输入图像的值,以便让这个激活值最大化。例如,
# 对于在 ImageNet上预训练的 VGG16网络,其block3_conv1层第 0个过滤器激活的损失如下所示。
from keras.applications import VGG16
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])
To implement gradient descent, we will need the gradient of this loss with respect to the model's input. To do this, we will use the gradients function packaged with the backend module of Keras:


# 调用 gradients 返回的是一个张量列表(本
# 例中列表长度为 1)。因此,只保留第一个元素,它是一个张量
# The call to `gradients` returns a list of tensors (of size 1 in this case)
# hence we only keep the first element -- which is a tensor.
# 代码清单 5-33 获取损失相对于输入的梯度# 调用 gradients 返回的是一个张量列表(本
# 例中列表长度为 1)。因此,只保留第一个元素,它是一个张量
grads = K.gradients(loss, model.input)[0]
A non-obvious trick to use for the gradient descent process to go smoothly is to normalize the gradient tensor, by dividing it by its L2 norm (the square root of the average of the square of the values in the tensor). This ensures that the magnitude of the updates done to the input image is always within a same range.

为了让梯度下降过程顺利进行,一个非显而易见的技巧是将梯度张量除以其 L2 范数(张量
# 中所有值的平方的平均值的平方根)来标准化。这就确保了输入图像的更新大小始终位于相同
# 的范围。
# We add 1e-5 before dividing so as to avoid accidentally dividing by 0.
# 代码清单 5-34 梯度标准化技巧
# 做除法前加上 1e–5,以防不小心除以 0
grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)# 为了让梯度下降过程顺利进行,一个非显而易见的技巧是将梯度张量除以其 L2 范数(张量
# 中所有值的平方的平均值的平方根)来标准化。这就确保了输入图像的更新大小始终位于相同
# 的范围。
Now we need a way to compute the value of the loss tensor and the gradient tensor, given an input image. We can define a Keras backend function to do this: iterate is a function that takes a Numpy tensor (as a list of tensors of size 1) and returns a list of two Numpy tensors: the loss value and the gradient value.

现在你需要一种方法:给定输入图像,它能够计算损失张量和梯度张量的值。你可以定义
# 一个 Keras 后端函数来实现此方法: iterate 是一个函数,它将一个 Numpy 张量(表示为长度
# 为 1 的张量列表)转换为两个 Numpy 张量组成的列表,这两个张量分别是损失值和梯度值。
# 代码清单 5-35 给定 Numpy 输入值,得到 Numpy 输出值
# 现在你需要一种方法:给定输入图像,它能够计算损失张量和梯度张量的值。你可以定义
# 一个 Keras 后端函数来实现此方法: iterate 是一个函数,它将一个 Numpy 张量(表示为长度
# 为 1 的张量列表)转换为两个 Numpy 张量组成的列表,这两个张量分别是损失值和梯度值。
# 代码清单 5-35 给定 Numpy 输入值,得到 Numpy 输出值
​
iterate = K.function([model.input], [loss, grads])# Let's test it:
import numpy as np
loss_value, grads_value = iterate([np.zeros((1, 150, 150, 3))])
At this point we can define a Python loop to do stochastic gradient descent:

# We start from a gray image with some noise
# 现在你可以定义一个 Python 循环来进行随机梯度下降。
# 代码清单 5-36 通过随机梯度下降让损失最大化
​
input_img_data = np.random.random((1, 150, 150, 3)) * 20 + 128.# Run gradient ascent for 40 steps
# 每次梯度更新的步长
# 运行 40 次梯度上升
step = 1.  # this is the magnitude of each gradient update
for i in range(40):
    # Compute the loss value and gradient value
    loss_value, grads_value = iterate([input_img_data])
    # Here we adjust the input image in the direction that maximizes the loss
    input_img_data += grads_value * step
The resulting image tensor will be a floating point tensor of shape (1, 150, 150, 3), with values that may not be integer within [0, 255]. Hence we would need to post-process this tensor to turn it into a displayable image. We do it with the following straightforward utility function:

得到的图像张量是形状为 (1, 150, 150, 3) 的浮点数张量,其取值可能不是 [0, 255]# 间内的整数。因此,你需要对这个张量进行后处理,将其转换为可显示的图像。下面这个简单
# 的实用函数可以做到这一点。
# 代码清单 5-37 将张量转换为有效图像的实用函数
# 得到的图像张量是形状为 (1, 150, 150, 3) 的浮点数张量,其取值可能不是 [0, 255] 区
# 间内的整数。因此,你需要对这个张量进行后处理,将其转换为可显示的图像。下面这个简单
# 的实用函数可以做到这一点。
# 代码清单 5-37 将张量转换为有效图像的实用函数def deprocess_image(x):
    # normalize tensor: center on 0., ensure std is 0.1
    x -= x.mean()
    x /= (x.std() + 1e-5)
    x *= 0.1# clip to [0, 1]
    x += 0.5
    x = np.clip(x, 0, 1)# convert to RGB array
    x *= 255
    x = np.clip(x, 0, 255).astype('uint8')
    return x
Now we have all the pieces, let's put them together into a Python function that takes as input a layer name and a filter index, and that 
returns a valid image tensor representing the pattern that maximizes the activation the specified filter:
# 接下来,我们将上述代码片段放到一个 Python函数中,输入一个层的名称和一个过滤器索引,
# 它将返回一个有效的图像张量,表示能够将特定过滤器的激活最大化的模式。
# 代码清单 5-38 生成过滤器可视化的函数def generate_pattern(layer_name, filter_index, size=150):
    # Build a loss function that maximizes the activation
    # of the nth filter of the layer considered.
    
#     构建一个损失函数,将该层第 n 个过滤器的激活最大化
    layer_output = model.get_layer(layer_name).output
    loss = K.mean(layer_output[:, :, :, filter_index])# Compute the gradient of the input picture wrt this loss
#     计算这个损失相对于输入图像的梯度
    grads = K.gradients(loss, model.input)[0]# Normalization trick: we normalize the gradient
#     标准化技巧:将梯度标准化
    grads /= (K.sqrt(K.mean(K.square(grads))) + 1e-5)# This function returns the loss and grads given the input picture
#     返回给定输入图像的损失和梯度
    iterate = K.function([model.input], [loss, grads])
    
    # We start from a gray image with some noise
    input_img_data = np.random.random((1, size, size, 3)) * 20 + 128.# Run gradient ascent for 40 steps
    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)
Let's try this:

plt.imshow(generate_pattern('block3_conv1', 0))
plt.show()



    
#     这些过滤器可视化包含卷积神经网络的层如何观察世界的很多信息:卷积神经网络中每一
# 层都学习一组过滤器,以便将其输入表示为过滤器的组合。这类似于傅里叶变换将信号分解为一
# 组余弦函数的过程。随着层数的加深,卷积神经网络中的过滤器变得越来越复杂,越来越精细。
# ‰ 模型第一层(block1_conv1)的过滤器对应简单的方向边缘和颜色(还有一些是彩色
# 边缘)。
# ‰ block2_conv1 层的过滤器对应边缘和颜色组合而成的简单纹理。
# ‰ 更高层的过滤器类似于自然图像中的纹理:羽毛、眼睛、树叶等
# 看起来, block3_conv1 层第 0 个过滤器响应的是波尔卡点(polka-dot)图案。下面来
# 看有趣的部分:我们可以将每一层的每个过滤器都可视化。为了简单起见,我们只查看每一
# 层的前 64 个过滤器,并只查看每个卷积块的第一层(即 block1_conv1、 block2_conv1、
# block3_conv1、 block4_ conv1、 block5_conv1)。我们将输出放在一个 8×8 的网格中,
# 每个网格是一个 64 像素×64 像素的过滤器模式,两个过滤器模式之间留有一些黑边(见
# 图 5-30 ~ 图 5-33)。# 代码清单 5-39 生成某一层中所有过滤器响应模式组成的网格for layer_name in ['block1_conv1', 'block2_conv1', 'block3_conv1', 'block4_conv1']:
    size = 64
    margin = 5# This a empty (black) image where we will store our results.
#     空图像(全黑色),用于保存结果
    results = np.zeros((8 * size + 7 * margin, 8 * size + 7 * margin, 3))for i in range(8):  # iterate over the rows of our results grid
        for j in range(8):  # iterate over the columns of our results grid
#             遍历 results 网格的行
#             遍历 results 网格的列
            # Generate the pattern for filter `i + (j * 8)` in `layer_name`
            filter_img = generate_pattern(layer_name, i + (j * 8), size=size)# Put the result in the square `(i, j)` of the results grid
#             将结果放到 results 网格  第 (i, j) 个方块中
            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
​
    # Display the results grid
    plt.figure(figsize=(20, 20))
    plt.imshow(results)
    plt.show()
    
    
    
#     这些过滤器可视化包含卷积神经网络的层如何观察世界的很多信息:卷积神经网络中每一
# 层都学习一组过滤器,以便将其输入表示为过滤器的组合。这类似于傅里叶变换将信号分解为一
# 组余弦函数的过程。随着层数的加深,卷积神经网络中的过滤器变得越来越复杂,越来越精细。
# • 模型第一层(block1_conv1)的过滤器对应简单的方向边缘和颜色(还有一些是彩色
# 边缘)。
# • block2_conv1 层的过滤器对应边缘和颜色组合而成的简单纹理。
# • 更高层的过滤器类似于自然图像中的纹理:羽毛、眼睛、树叶等

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值