深度学习
  • 前言
  • 第一章:经典网络
    • ImageNet Classification with Deep Convolutional Neural Network
    • Very Deep Convolutional Networks for Large-Scale Image Recognition
    • Going Deeper with Convolutions
    • Deep Residual Learning for Image Recognition
    • PolyNet: A Pursuit of Structural Diversity in Very Deep Networks
    • Squeeze-and-Excitation Networks
    • Densely Connected Convolutional Networks
    • SQUEEZENET: ALEXNET-LEVEL ACCURACY WITH 50X FEWER PARAMETERS AND <0.5MB MODEL SIZE
    • MobileNet v1 and MobileNet v2
    • Xception: Deep Learning with Depthwise Separable Convolutions
    • Aggregated Residual Transformations for Deep Neural Networks
    • ShuffleNet v1 and ShuffleNet v2
    • CondenseNet: An Efficient DenseNet using Learned Group Convolution
    • Neural Architecture Search with Reinforecement Learning
    • Learning Transferable Architectures for Scalable Image Recognition
    • Progressive Neural Architecture Search
    • Regularized Evolution for Image Classifier Architecture Search
    • 实例解析:12306验证码破解
  • 第二章:自然语言处理
    • Recurrent Neural Network based Language Model
    • Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation
    • Neural Machine Translation by Jointly Learning to Align and Translate
    • Hierarchical Attention Networks for Document Classification
    • Connectionist Temporal Classification : Labelling Unsegmented Sequence Data with Recurrent Neural Ne
    • About Long Short Term Memory
    • Attention Is All you Need
    • BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding
  • 第三章:语音识别
    • Speech Recognition with Deep Recurrent Neural Network
  • 第四章:物体检测
    • Rich feature hierarchies for accurate object detection and semantic segmentation
    • Spatial Pyramid Pooling in Deep Convolutional Networks for Visual Recognition
    • Fast R-CNN
    • Faster R-CNN: Towards Real-Time Object Detection with Region Proposal Networks
    • R-FCN: Object Detection via Region-based Fully Convolutuional Networks
    • Mask R-CNN
    • You Only Look Once: Unified, Real-Time Object Detection
    • SSD: Single Shot MultiBox Detector
    • YOLO9000: Better, Faster, Stronger
    • Focal Loss for Dense Object Detection
    • YOLOv3: An Incremental Improvement
    • Learning to Segment Every Thing
    • SNIPER: Efficient Multi-Scale Training
  • 第五章:光学字符识别
    • 场景文字检测
      • DeepText: A Unified Framework for Text Proposal Generation and Text Detection in Natural Images
      • Detecting Text in Natural Image with Connectionist Text Proposal Network
      • Scene Text Detection via Holistic, Multi-Channel Prediction
      • Arbitrary-Oriented Scene Text Detection via Rotation Proposals
      • PixelLink: Detecting Scene Text via Instance Segmentation
    • 文字识别
      • Spatial Transform Networks
      • Robust Scene Text Recognition with Automatic Rectification
      • Bidirectional Scene Text Recognition with a Single Decoder
      • multi-task learning for text recognition with joint CTC-attention
    • 端到端文字检测与识别
      • Reading Text in the Wild with Convolutional Neural Networks
      • Deep TextSpotter: An End-to-End Trainable Scene Text Localization and Recognition Framework
    • 实例解析:字符验证码破解
    • 二维信息识别
      • 基于Seq2Seq的公式识别引擎
      • Show and Tell: A Neural Image Caption Generator
      • Show, Attend and Tell: Neural Image Caption Generation with Visual Attention
  • 第六章:语义分割
    • U-Net: Convolutional Networks for Biomedical Image Segmentation
  • 第七章:人脸识别
    • 人脸检测
      • DenseBox: Unifying Landmark Localization with End to End Object Detection
      • UnitBox: An Advanced Object Detection Network
  • 第八章:网络优化
    • Batch Normalization
    • Layer Normalization
    • Weight Normalization
    • Instance Normalization
    • Group Normalization
    • Switchable Normalization
  • 第九章:生成对抗网络
    • Generative Adversarial Nets
  • 其它应用
    • Holistically-Nested Edge Detection
    • Image Style Transfer Using Convolutional Nerual Networks
    • Background Matting: The World is Your Green Screen
  • Tags
  • References
由 GitBook 提供支持
在本页
  • 前言
  • 1. Image Style Transfer(IST)算法详解
  • 1.1 算法概览
  • 1.2 内容表示
  • 1.3 风格表示
  • 1.4 风格迁移
  • 2. 总结

这有帮助吗?

  1. 其它应用

Image Style Transfer Using Convolutional Nerual Networks

上一页Holistically-Nested Edge Detection下一页Background Matting: The World is Your Green Screen

最后更新于4年前

这有帮助吗?

tags: Nueral Style Transfer

前言

Leon A.Gatys是最早使用CNN做图像风格迁移的先驱之一,这篇文章还有另外一个版本,应该是它投到CVPR之前的预印版,两篇文章内容基本相同。

我们知道在训练CNN分类器时,接近输入层的Feature Map包含更多的图像的纹理等细节信息,而接近输出层的Feature Map则包含更多的内容信息。这个特征的原理可以通过我们在中介绍的数据处理不等式(DPI)解释:越接近输入层的Feature Map经过的处理(卷积和池化)越少,则这时候损失的图像信息还不会很多。随着网络层数的加深,图像经过的处理也会增多,根据DPI中每次处理信息会减少的原理,靠后的Feature Map则包含的输入图像的信息是不会多余其之前的Feature Map的;同理当我们使用标签值进行参数更新时,越接近损失层的Feature Map则会包含越多的图像标签(内容)信息,越远则包含越少的内容信息。这篇论文正是利用了CNN的天然特征实现的图像风格迁移的。

具体的讲,当我们要在图片p⃗\vec{p}p​(content)的内容之上应用图片a⃗\vec{a}a(style)的风格时,我们会使用梯度下降等算法更新目标图像x⃗\vec{x}x(target)的内容,使其在较浅的层有和图片a⃗\vec{a}a类似的响应值,同时在较深的层和p⃗\vec{p}p​也有类似的响应,这样就保证了x⃗\vec{x}x和a⃗\vec{a}a有类似的风格而且和p⃗\vec{p}p​有类似的内容,这样生成的图片x⃗\vec{x}x就是我们要得到的风格迁移的图片。如图1所示。

在Keras官方源码中,作者提供了神经风格迁移的,这里对算法的讲解将结合源码进行分析。

图1:图像风格迁移效果图

1. Image Style Transfer(IST)算法详解

1.1 算法概览

IST的原理基于上面提到的网络的不同层会响应不同的类型特征的特点实现的。给定一个训练好的网络,源码中使用的是 ,下面是源码第142-143行,因此在运行该源码时如果你之前没有下载过训练好的VGG19模型文件,第一次运行会有下载该文件的过程,文件名为'vgg19_weights_tf_dim_ordering_tf_kernels_notop.h5'。

142 model = vgg19.VGG19(input_tensor=input_tensor,
143                     weights='imagenet', include_top=False)

论文中有两点在源码中并没有体现,一个是对权值进行了归一化,使用的方法是我们之前介绍的,另外一个是使用平均池化代替最大池化,使用了这两点的话会有更快的收敛速度。

图2有三个部分,最左侧的输入是风格图片a⃗\vec{a}a,将其输入到训练好的VGG19中,会得到一批它对应的Feature Map;最右侧则是内容图片p⃗\vec{p}p​,它也会输入到这个网络中得到它对应的Feature Map;中间是目标图片x⃗\vec{x}x,它的初始值是白噪音图片,它的值会通过SGD进行更新,SGD的损失函数时通过x⃗\vec{x}x在这个网络中得到的Feature Map和a⃗\vec{a}a的Feature Map以及p⃗\vec{p}p​的Feature Map计算得到的。图2中所有的细节会在后面的章节中进行介绍。

图2:图像风格迁移算法流程图

传统的深度学习方法是根据输入数据更新网络的权值。而IST的算法是固定网络的参数,更新输入的数据。固定权值更新数据还有几个经典案例,例如材质学习,卷积核可视化等。

1.2 内容表示

136 input_tensor = K.concatenate([base_image,
137                               style_reference_image,
138                               combination_image], axis=0)

通过对142行的model的遍历我们可以得到每一层的Feature Map的名字以及内容,然后将其保存在字典中,见147行。

147 outputs_dict = dict([(layer.name, layer.output) for layer in model.layers])
208 layer_features = outputs_dict['block5_conv2']
209 base_image_features = layer_features[0, :, :, :]
210 combination_features = layer_features[2, :, :, :]
211 loss += content_weight * content_loss(base_image_features,
212                                       combination_features)

上式中的content_weight是内容损失函数的比重,源码中给出的值是0.025,内容损失函数的定义见185-186行:

185 def content_loss(base, combination):
186     return K.sum(K.square(combination - base))

1.3 风格表示

另外一点和内容表示不同的是,风格表示使用了每个block的第一个卷积来计算损失函数,作者认为这种方式得到的纹理特征更为光滑,因为仅仅使用底层Feature Map得到的图像较为精细但是比较粗糙,而高层得到的图像则含有更多的内容信息,损失了一些纹理信息,但他的材质更为光滑。所以,综合了所有层的样式表示的损失函数为:

上面的更新同样使用SGD。

下面我们继续来学习源码,从源码的214-223行我们可以看出样式表示使用了5个block的Feature Map:

214 feature_layers = ['block1_conv1', 'block2_conv1',
215                   'block3_conv1', 'block4_conv1',
216                   'block5_conv1']
217 for layer_name in feature_layers:
218     layer_features = outputs_dict[layer_name]
219     style_reference_features = layer_features[1, :, :, :]
220     combination_features = layer_features[2, :, :, :]
221     sl = style_loss(style_reference_features, combination_features)
222     loss += (style_weight / len(feature_layers)) * sl
223 loss += total_variation_weight * total_variation_loss(combination_image)

从上面的代码中我们可以看出,样式表示使用了feature_layers中所包含的Feature Map,并且最后loss的计算把它们进行了相加。第221行的style_loss的定义见源码的171-178行:

171 def style_loss(style, combination):
172     assert K.ndim(style) == 3
173     assert K.ndim(combination) == 3
174     S = gram_matrix(style)
175     C = gram_matrix(combination)
176     channels = 3
177     size = img_nrows * img_ncols
178     return K.sum(K.square(S - C)) / (4.0 * (channels ** 2) * (size ** 2))

从174-175行我们可以看出损失函数的计算使用的是两个Feature Map的Gram矩阵,Gram矩阵的定义见155-162行:

155 def gram_matrix(x):
156     assert K.ndim(x) == 3
157     if K.image_data_format() == 'channels_first':
158         features = K.batch_flatten(x)
159     else:
160         features = K.batch_flatten(K.permute_dimensions(x, (2, 0, 1)))
161     gram = K.dot(features, K.transpose(features))
162     return gram

第158或者160行的batch_flatten验证了Feature Map要先展开成向量,第161行则是Gram矩阵的计算公式。

还有一些超餐在配置文件中进行了指定,style_weight和total_variation_weight的默认值都是1。

1.4 风格迁移

下面继续学习这一部分的源码。在第287-288行的fmin_l_bfgs_b说明了计算梯度使用了L-BFGS算法,它是scipy提供:

287 x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
288                                  fprime=evaluator.grads, maxfun=20)

fmin_l_bfgs_b是scipy包中一个函数。第一个参数是定义的损失函数,第二个参数是输入数据,fprime通常用于计算第一个损失函数的梯度,maxfun是函数执行的次数。它的第一个返回值是更新之后的x的值,这里使用了递归的方式反复更新x,第二个返回值是损失值。

282 x = preprocess_image(base_image_path)

287行的损失函数定义在264-269行:

264 def loss(self, x):
265     assert self.loss_value is None
266     loss_value, grad_values = eval_loss_and_grads(x)
267     self.loss_value = loss_value
268     self.grad_values = grad_values
269     return self.loss_value

其中最重要的函数是eval_loss_and_grads()函数,它定义在了237-248行:

237 def eval_loss_and_grads(x):
238     if K.image_data_format() == 'channels_first':
239         x = x.reshape((1, 3, img_nrows, img_ncols))
240     else:
241         x = x.reshape((1, img_nrows, img_ncols, 3))
242     outs = f_outputs([x])
243     loss_value = outs[0]
244     if len(outs[1:]) == 1:
245         grad_values = outs[1].flatten().astype('float64')
246     else:
247         grad_values = np.array(outs[1:]).flatten().astype('float64')
248     return loss_value, grad_values
226 grads = K.gradients(loss, combination_image)
227 
228 outputs = [loss]
229 if isinstance(grads, (list, tuple)):
230     outputs += grads
231 else:
232     outputs.append(grads)
233 
234 f_outputs = K.function([combination_image], outputs)

2. 总结

图像风格迁移是一个非常好玩但是无法对齐效果量化的算法,我们可以得到和一些著名画家风格看起来非常类似的画作,但是很难从数学的角度去衡量一个画作的风格,得出的结论是非常主观的。但是算法的设计动机是出于CNN的底层Feature Map接近图像纹理而高层Feature Map接近图像内容的天然特性,也是对神经网络这个黑盒子从另外一个角度给与了解释。IST产生的结果非常有趣,由此诞生了一批商用的软件,例如Prisma等。

IST如果能迁移到音频领域也许会有帮助,例如在TTS中如果可以将合成的语音的内容应用到真实人类语音的风格上,这样也许可以得到更为平滑的语音。或者如果我们将音频内容应用到某个人说话的风格中,也许我们可以得到和这个人说话风格非常类似的音频输出。

算法另外一个缺点是对噪音比较敏感,尤其是当参与合成的风格图片和内容图片都是真实照片的时候。

内容表示是图2中右侧的两个分支所示的过程。我们先看最右侧,p⃗\vec{p}p​输入VGG19中,我们提取其在第四个block中第二层的Feature Map,表示为conv42(源码中提取的是conv5_2)。假设其层数为lll,NlN_lNl​是Feature Map的数量,也就是通道数,MlM_lMl​是Feature Map的像素点的个数。那么我们得到Feature Map FlF^lFl可以表示为Fl∈RNl×MlF^l \in \mathcal{R}^{N_l \times M_l}Fl∈RNl​×Ml​,$$F^l{ij}则是第则是第则是第l层的第层的第层的第i个FeatureMap在位置个Feature Map在位置个FeatureMap在位置j处的像素点的值。根据同样的定义,我们可以得到处的像素点的值。根据同样的定义,我们可以得到处的像素点的值。根据同样的定义,我们可以得到\vec{x}在conv4_2处的FeatureMap在conv4\_2处的Feature Map在conv4_2处的FeatureMapP^l$$。

如果x⃗\vec{x}x的FlF_lFl​和p⃗\vec{p}p​的PlP^lPl非常接近,那么我们可以认为x⃗\vec{x}x和p⃗\vec{p}p​在内容上比较接近,因为越接近输出的层包含有越多的内容信息。这里我们可以定义IST的内容损失函数为:

Lcontent(p⃗,x⃗,l)=12∑i,j(Fi,jl−Pi,jl)2\mathcal{L}_{\text{content}}(\vec{p},\vec{x},l)=\frac{1}{2}\sum_{i,j}(F_{i,j}^l - P_{i,j}^l)^2Lcontent​(p​,x,l)=21​i,j∑​(Fi,jl​−Pi,jl​)2

下面我们来看一下源码,上面142行的input_tensor的是由p⃗,a⃗,x⃗\vec{p}, \vec{a}, \vec{x}p​,a,x一次拼接而成的,见136-138行。

这样我们可以根据关键字提取我们想要的Feature Map,例如我们提取两个图像在conv5_2处的Feature Map PlP^lPl(源码中的base_image_features)和FlF^lFl源码中的combination_features),然后使用这两个Feature Map计算损失值,见208-212行:

有了损失函数的定义之后,我们便可以根据损失函数的值计算其关于Fi,jF_{i,j}Fi,j​的梯度值,从而实现从后向前的梯度更新。

∂Lcontent∂Fi,jl={(Fl−Pl)i,jif Fi,j>00if Fi,j<0\frac{\partial \mathcal{L}_{content}}{\partial F_{i,j}^l} = \left\{ \begin{array}{} (F^l - P^l)_{i,j} & \text{if } F_{i,j} > 0\\ 0 & \text{if } F_{i,j} < 0 \end{array} \right.∂Fi,jl​∂Lcontent​​={(Fl−Pl)i,j​0​if Fi,j​>0if Fi,j​<0​

如果损失函数只包含内容损失,当模型收敛时,我们得到的x⃗′\vec{x}'x′应该非常接近p⃗\vec{p}p​的内容。但是它很难还原到和p⃗\vec{p}p​一模一样,因为即使损失值为0时,我们得到的x⃗′\vec{x}'x′值也有多种的形式。

为什么说x⃗′\vec{x}'x′具有p⃗\vec{p}p​的内容呢,因为当x⃗′\vec{x}'x′经过VGG19的处理后,它的conv5_2层的输出了p⃗\vec{p}p​几乎一样,而较深的层具有较高的内容信息,这也就说明了x⃗′\vec{x}'x′和p⃗\vec{p}p​具有非常类似的内容信息。

风格表示的计算过程是图2的左侧和中间两个分支。和计算FlF^lFl相同,我们将a⃗\vec{a}a输入到模型中便可得到它对应的Feature Map SlS^lSl。不同于内容表示的直接运算,风格表示使用的是Feature Map展开成1维向量的Gram矩阵的形式。使用Gram矩阵的原因是因为考虑到纹理特征是和图像的具体位置没有关系的,所以通过打乱纹理的位置信息来保证这个特征,Gram矩阵的定义如下:

Gi,jl=∑kFi,klFj,klG_{i,j}^l = \sum_k F_{i,k}^l F_{j,k}^lGi,jl​=k∑​Fi,kl​Fj,kl​
Lstyle=∑lwlElL_{style} = \sum_l w_l E_lLstyle​=l∑​wl​El​

其中ElE_lEl​是SlS^lSl的Gram矩阵AlA^lAl和FlF^lFl的Gram矩阵GlG^lGl的均方误差:

El=14Nl2Ml2∑i,j(Gi,jl−Ai,jl)2E_l = \frac{1}{4N_l^2M_l^2}\sum_{i,j}(G_{i,j}^l - A_{i,j}^l)^2El​=4Nl2​Ml2​1​i,j∑​(Gi,jl​−Ai,jl​)2

它关于Fi,jlF_{i,j}^lFi,jl​的梯度的计算方式为:

∂El∂Fi,jl={1Nl2Ml2((Fl)T(Gl−Al))jiif Fi,jl>00if Fi,jl<0\frac{\partial E_l}{\partial F_{i,j}^l} = \left\{ \begin{array}{} \frac{1}{N_l^2M_l^2}((F^l)^T(G^l-A^l))_{ji} & \text{if } F_{i,j}^l > 0 \\ 0 & \text{if } F_{i,j}^l < 0 \end{array} \right.∂Fi,jl​∂El​​={Nl2​Ml2​1​((Fl)T(Gl−Al))ji​0​if Fi,jl​>0if Fi,jl​<0​

明白了如何计算内容损失函数Lcontent\mathcal{L}_{content}Lcontent​和风格损失函数Lstyle\mathcal{L}_{style}Lstyle​之后,整个风格迁移任务的损失函数就是两个损失值得加权和:

Ltotal(p⃗,a⃗,x⃗)=αLcontent(p⃗,x⃗)+βLstyle(a⃗,x⃗)\mathcal{L}_{\text{total}}(\vec{p},\vec{a},\vec{x}) = \alpha \mathcal{L}_{\text{content}}(\vec{p}, \vec{x}) + \beta \mathcal{L}_{\text{style}}(\vec{a}, \vec{x})Ltotal​(p​,a,x)=αLcontent​(p​,x)+βLstyle​(a,x)

其中α\alphaα和β\betaβ就是我们在1.2节和1.3节介绍的content_weight和total_variation_weight。通过调整这两个超参数的值我们可以设置生成的图像更偏向于p⃗\vec{p}p​的内容还是a⃗\vec{a}a的风格。∂Ltotal∂x⃗\frac{\partial \mathcal{L}_{total}}{\partial \vec{x}}∂x∂Ltotal​​的值用来更新输入图像x⃗\vec{x}x的内容,作者推荐使用L-BFGS更新梯度。

另外对于x⃗\vec{x}x的初始化,论文中推荐使用白噪音进行初始化,这样虽然计算的时间要更长一些,但是得到的图像的样式具有更强的随机性。而论文使用的是使用p⃗\vec{p}p​初始化x⃗\vec{x}x,这样得到的生成图像更加稳定。

其中x的初始化使用的是内容图片p⃗\vec{p}p​:

其中f_outputs()是实例化的Keras函数,作用是使用梯度更新x⃗\vec{x}x的内容,见226-234行:

残差网络
源码
VGG19
Weight Normalization
图1:图像风格迁移效果图
图2:图像风格迁移算法流程图