理解内容损失背后的直觉(神经风格迁移)

人工智能 神经网络 机器学习 卷积神经网络 张量流 喀拉斯
2021-10-19 22:38:47

我试图了解如何在神经风格转移中计算内容损失背后的直觉。我正在阅读一篇文章:https ://medium.com/mlreview/making-ai-art-with-style-transfer-using-keras-8bb5fa44b216 ,它从内容丢失功能解释了神经风格转移的实现: 在此处输入图像描述

文章解释说:

  • F 和 P 是行数等于 N 且列数等于 M 的矩阵。

  • N 是第 l 层中的过滤器数量,M 是第 l 层的特征图中空间元素的数量(高度乘以宽度)。

从下面用于从特定 Conv 层获取特征/内容表示的代码中,我不太了解它是如何工作的。基本上我打印了每一行代码的输出以试图使它更容易,但它仍然留下了一些问题要问,我在代码下面列出了这些问题:

def get_feature_reps(x, layer_names, model):
    """
    Get feature representations of input x for one or more layers in a given model.
    """
    featMatrices = []
    for ln in layer_names:
        selectedLayer = model.get_layer(ln)
        featRaw = selectedLayer.output
        featRawShape = K.shape(featRaw).eval(session=tf_session)
        N_l = featRawShape[-1]
        M_l = featRawShape[1]*featRawShape[2]
        featMatrix = K.reshape(featRaw, (M_l, N_l))
        featMatrix = K.transpose(featMatrix)
        featMatrices.append(featMatrix)
    return featMatrices

def get_content_loss(F, P):
    cLoss = 0.5*K.sum(K.square(F - P))
    return cLoss

1-对于线featRaw = selectedLayer.output,当我打印时featRaw,我得到输出: Tensor("block4_conv2/Relu:0", shape=(1, 64, 64, 512), dtype=float32)

  • A-Relu:0这是否意味着RELU激活还未被应用?

  • b-我还假设我们正在输出特征图输出block4_conv2,而不是过滤器/内核本身,对吗?

  • c- 为什么一开始的轴是 1?我对 Conv 层的理解是,它们只是由应用于输入的过滤器/内核的数量(具有形状高度、宽度、深度)组成。

  • d- 是selectedLayer.output简单地输出 Conv 层的形状,还是输出对象还包含其他信息,例如来自该层的输出特征图的像素值?

2-使用行:featMatrix = K.reshape(featRaw, (M_l, N_l)打印featMatrix将输出的位置:Tensor("Reshape:0", shape=(4096, 512), dtype=float32)

  • a-这是我最困惑的地方。因此,为了获得图像的特定 Conv 层的特征/内容表示,我们只需创建一个二维矩阵,第一个是过滤器的数量,另一个是过滤器/内核的面积(高度 * 宽度)。那没有意义!我们如何从中获得图像的独特特征?!我们不会从特征图中检索任何像素值。我们只是获取过滤器/内核的区域大小和过滤器的数量,而不是检索任何内容(像素值)本身!

  • b- 最终featMatrix也是转置的 - 即featMatrix = K.transpose(featMatrix)与输出Tensor("transpose:0", shape=(512, 4096), dtype=float32)为什么会这样(即为什么要反转轴)?

3 - 最后我想知道,一旦我们检索到内容表示,我怎样才能将它输出为一个 numpy 数组并将其保存为图像?

任何帮助将非常感激。

1个回答
  1. 对于 line featRaw = selectedLayer.output,当我 print 时featRaw,我得到输出:Tensor("block4_conv2/Relu:0", shape=(1, 64, 64, 512), dtype=float32).

    • a)Relu:0这是否意味着尚未应用 Relu 激活?

它已被应用。

  • b)另外我假设我们输出的是特征图输出block4_conv2,而不是过滤器/内核本身,对吗?

是的。

  • c) 为什么一开始的轴是 1?我对 Conv 层的理解是,它们只是由应用于输入的过滤器/内核的数量(具有形状高度、宽度、深度)组成。

keras 中的第一个维度用于表示batch维度。训练模型时,您不是逐个传递图像,而是分批传递。在模型计算其损失并进行更新之前并行处理的图像数量称为batch_size. 但是,在计算内容损失时,您有一个要对其执行样式转换的图像,因此batch_size=1. 这就是为什么你会在张量形状的开头看到单位。

  • d) 是selectedLayer.output简单地输出 Conv 层的形状,还是输出对象还包含其他信息,例如来自该层的输出特征图的像素值?

它输出包含从该层输出的所有像素值的张量。

  1. 使用行:featMatrix = K.reshape(featRaw, (M_l, N_l)打印featMatrix将输出的位置:Tensor("Reshape:0", shape=(4096, 512), dtype=float32)

    • a)这是我最困惑的地方。因此,为了获得图像的特定 Conv 层的特征/内容表示,我们只需创建一个二维矩阵,第一个是过滤器的数量,另一个是过滤器/内核的面积(高度 * 宽度)。那没有意义!我们如何从中获得图像的独特特征?!我们不会从特征图中检索任何像素值。我们只是获取过滤器/内核的区域大小和过滤器的数量,而不是检索任何内容(像素值)本身!

我觉得你有点混淆了。正在获取像素值,只是形状不同而不是将它们堆叠在一个64×64×515数组,您将它们堆叠在一个1096×515大批。完全相同的信息以不同的形式存储。把它想象成改变一个10×2矩阵为5×4一。它包含相同的信息,但形状不同。

  • b)最后featMatrix也是转置的 - 即featMatrix = K.transpose(featMatrix)与输出Tensor("transpose:0", shape=(512, 4096), dtype=float32)为什么会这样(即为什么要反转轴)?

我不确定他到底为什么这样做,但从我看到的代码来看,我认为这与样式丢失(而不是内容丢失)有关。可能某些操作的尺寸与某些操作的尺寸不匹配,也许是矩阵乘法......

  1. 最后我想知道,一旦我们检索到内容表示,我怎样才能将它输出为一个 numpy 数组并将其保存为图像?

如果您将输出图像作为张量,假设y. 您可以通过以下方式将其转换为 numpy keras.backend.eval

import keras.backend as K

y_arr = K.eval(y)

保存图片的方法有很多,包括scikit-imagescipyopencvPILmatplotlib等。我会用最后一个:

import matplotlib.pyplot as plt

plt.imsave('my_image.png', y_arr)

请注意,为了工作y_arr应该具有(height, width, 3)(如果它是 RGB 图像)或(height, width)(如果它是灰度图像)的形状。