最近在学习CS20课程——一门讲述tensorflow应用的实践性课程,正好Assignment2讲到了Style Transfer这个东西,这里把我的理解总结一下(代码基于Tensorflow)。

代码:
chiphuyen/stanford-tensorflow-tutorials/assignments/02_style_transfer/
论文:Bringing Impressionism to Life with Neural Style Transfer in Come Swim https://arxiv.org/pdf/1701.04928v1.pdf


简介

这是一个使用两个图片(以A和B表示)作为输入(图A作为内容输入(content input),图B作为风格输入(style input)。最终的目的是得到一张具有图B的风格和图A的内容的图像。

  • style picture
    这里写图片描述
  • content picture
    这里写图片描述
  • what we got
    这里写图片描述

    用一个简单的方程可以表示为:style + content = picture of highly abstracted

步骤简述

  • 使用一个训练好的CNN结构(一般来说做Image classification的就可以,比如VGG, GoogLeNet等等)
  • 我们要有一个概念:CNN中低层特征图保留的内容比较多,而高层更多的是纹理,正是利用这一点,我们将组合内容信息和纹理信息并将两者相结合得到一个全新的图片。
  • 对content和style分别定义损失函数,并将其组合起来作为整个结构的Loss function。

  • 值得注意的是,我们输入时候使用的结构使用的是一样的结构(无论是style pic、content pic还是trianable input),为了避免重复的子计算图装配(To save us from having to assemble the same subgraph multiple times, we will use one variable for all three of them.),需要使用tf.variable_scope(‘input’)。
    这里写图片描述

  • 跟之前的CNN中通过反向传播调整网络结构参数如w和b不同的是,这里的目标是对一个输入的content pic加入白噪声后的图片进行不断调整,给其加入内容和纹理信息。

损失函数说明

Ltotal(p,a,x)=αLcontent(p,x)+βLstyle(a,x) L t o t a l ( p → , a → , x → ) = α L c o n t e n t ( p → , x → ) + β L s t y l e ( a → , x → )
<script type="math/tex; mode=display" id="MathJax-Element-1">L_{total}(\overrightarrow p,\overrightarrow a,\overrightarrow x) = \alpha L_{content}(\overrightarrow p,\overrightarrow x)+\beta L_{style}(\overrightarrow a,\overrightarrow x)</script>

其中α和β是权重,论文指出α/β=0.001或者0.0001,这个比例是根据内容和风格的比例来调整的,当你想要风格更浓烈,就提高α/β的值,反之则降低α/β。

1. content loss

content loss比较容易理解,论文里定义如下:

Lcontent(p,x,l)=12i,j(Fli,jPli,j)2 L c o n t e n t ( p → , x → , l ) = 1 2 ∑ i , j ( F i , j l − P i , j l ) 2
<script type="math/tex; mode=display" id="MathJax-Element-2">L_{content}(\overrightarrow p,\overrightarrow x, l) = \frac {1}{2} \sum _ {i,j} (F_{i,j}^l - P_{i,j}^l)^2</script>

假设某一层 l l <script type="math/tex" id="MathJax-Element-3">l</script>得到的响应是

FlRNlMl
<script type="math/tex; mode=display" id="MathJax-Element-4">F^l\in R^{N^l*M^l}</script>其中 Nl N l <script type="math/tex" id="MathJax-Element-5">N^l</script>为l层filter的个数, Ml M l <script type="math/tex" id="MathJax-Element-6">M^l</script>为filter的大小( heightwidth h e i g h t ∗ w i d t h <script type="math/tex" id="MathJax-Element-7">height * width</script>)。 Fli,j F i , j l <script type="math/tex" id="MathJax-Element-8">F^{l}_{i,j}</script>表示的是第 l l <script type="math/tex" id="MathJax-Element-9">l</script>层第i<script type="math/tex" id="MathJax-Element-10">i</script>个filter在位置 j j <script type="math/tex" id="MathJax-Element-11">j</script>的输出。

这里的Fi,jl<script type="math/tex" id="MathJax-Element-12">F_{i,j}^l</script>是层 l l <script type="math/tex" id="MathJax-Element-13">l</script>生成图像的特征表示(content representation of the generated image),Pi,jl<script type="math/tex" id="MathJax-Element-14">P_{i,j}^l</script>是层 l l <script type="math/tex" id="MathJax-Element-15">l</script>内容图像的特征表示(content representation of the content image)。实际上就是关于F<script type="math/tex" id="MathJax-Element-16">F</script>和 P P <script type="math/tex" id="MathJax-Element-17">P</script>的均方误差。论文中建议使用的layer是conv4_2

下面翻译自CS20 Assignment2 的note

然而在实践中发现,这个content loss收敛的很慢。所以这里可以将前面的系数1/2换成1/(4s)。s为P<script type="math/tex" id="MathJax-Element-18">P</script>的所有维度的乘积,比如 P P <script type="math/tex" id="MathJax-Element-19">P</script>为[5, 5, 3]。那么s = 5*5*3=75。

2. style loss

style loss有些复杂,在论文中,它有三步计算得来。首先让我们来看style loss的定义:

El=14Nl2Ml2i,j(Gi,jlAi,jl)2
<script type="math/tex; mode=display" id="MathJax-Element-20">E_l = \frac {1} {4N_l^2M_l^2} \sum_{i,j} (G_{i,j}^l - A_{i,j}^l)^2</script>

Lstyle(a,x)=l=0LwlEl L s t y l e ( a → , x → ) = ∑ l = 0 L w l E l
<script type="math/tex; mode=display" id="MathJax-Element-21"> L_{style}(\overrightarrow a,\overrightarrow x) = \sum_{l=0}^L w_lE_l</script>

这里 Nl N l <script type="math/tex" id="MathJax-Element-22">N_l</script>是层 l l <script type="math/tex" id="MathJax-Element-23">l</script>的卷积核(filter)个数,Ml<script type="math/tex" id="MathJax-Element-24">M_l</script>为特征图的高和宽的乘积。这里, A A <script type="math/tex" id="MathJax-Element-25">A</script>是原图的Gram矩阵,G<script type="math/tex" id="MathJax-Element-26">G</script>为生成图像的Gram矩阵。

Gram矩阵的计算方式是: Gli,j=kFli,kFlj,k G i , j l = ∑ k F i , k l ∗ F j , k l <script type="math/tex" id="MathJax-Element-27">G^{l}_{i,j}=\sum_k F^{l}_{i,k}*F^{l}_{j,k}</script>。即为同一层 l l <script type="math/tex" id="MathJax-Element-28">l</script>各个不同的特征图的偏心协方差矩阵。在feature map中,每一个数字都来自于一个特定滤波器在特定位置的卷积,因此每个数字就代表一个特征的强度,而Gram计算的实际上是特征之间的相关性——“哪两个特征是同时出现的,哪两个是此消彼长”。

以层l<script type="math/tex" id="MathJax-Element-29">l</script>为例,当很多feature map的同一个位置上的数值越小,那么我们得到的Gram中此位置的值就越小;当很多feature map的同一个位置上的数值越大,那么我们得到的Gram中此位置的值就越大。

所以Gram矩阵这里的作用是:在能够在保证内容的情况下,进行风格的渲染

这里的 l l <script type="math/tex" id="MathJax-Element-30">l</script>表示了我们想要吸收进生成图像style的特征图,论文中建议取:
这里写图片描述

代码中对应的权值为:
这里写图片描述

试验

定义完毕之后,就回到最开始的Total loss:

Ltotal(p,a,x)=αLcontent(p,x)+βLstyle(a,x)
<script type="math/tex; mode=display" id="MathJax-Element-46">L_{total}(\overrightarrow p,\overrightarrow a,\overrightarrow x) = \alpha L_{content}(\overrightarrow p,\overrightarrow x)+\beta L_{style}(\overrightarrow a,\overrightarrow x)</script>

这里再次强调,我们训练的参数不是VGG或者GoogLenet里面的 W W <script type="math/tex" id="MathJax-Element-47">W</script>和b<script type="math/tex" id="MathJax-Element-48">b</script>,而是输入的content pic + noise的内容(为其加上content和style信息)

  • content pic
    这里写图片描述

  • style pic
    这里写图片描述

  • 加噪声的intial pic

这里写图片描述

  • 使其变为
    这里写图片描述

参考资料(Reference)

[1]Bringing Impressionism to Life with Neural Style Transfer in Come Swim
[2]Gram Matrices理解
[3]如何用简单易懂的例子解释格拉姆矩阵/Gram matrix?
[4]tensorflow学习笔记(七):TensorFLow实战之style_transfer(风格转换)

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐