本文为稀土技能社区首发签约文章,14天内制止转载,14天后未获授权制止转载,侵权必究!
🍊作者简介:秃头小苏,致力于用最通俗的语言描绘问题
🍊专栏引荐:深度学习网络原理与实战
🍊近期方针:写好专栏的每一篇文章
🍊支撑小苏:点赞👍🏼、收藏⭐、留言📩
对立生成网络GAN系列——Spectral Normalization原理详解及源码解析
写在前面
Hello,咱们好,我是小苏🧒🏽🧒🏽🧒🏽
在前面的文章中,我现已介绍过挺多种GAN网络了,感兴趣的能够关注一下我的专栏:深度学习网络原理与实战 。现在专栏首要更新了GAN系列文章、Transformer系列和语义分割系列文章,都有理论详解和代码实战,文中的讲解都比较通俗易懂,假如你期望丰富这方面的常识,主张你阅览试试,信任你会有蛮不错的收成。🍸🍸🍸
在阅览本篇教程之前,你十分有必要阅览下面两篇文章:
- [1]对立生成网络GAN系列——DCGAN简介及人脸图画生成事例
- [2]对立生成网络GAN系列——WGAN原理及实战演练
其实啊,我信任咱们来看这篇文章的时分,必定是对上文提到的文章有所了解了,因而咱们要是觉得自己对GAN和WGAN了解的现已足够透彻了,那么完全没有必要再浪费时间阅览了。假如你还对它们有一些疑惑或许过了好久现已忘了期望回忆一下的话,那么文章[1]和文章[2]获取对你有所协助。
咱们预备好了嘛,咱们这就开端预备学习Spectral Normalization啦!🚖🚖🚖
Spectral Normalization原理详解
首先,让咱们简略的回忆一下WGAN。🌞🌞🌞因为原始GAN网络存在练习不稳定的现象,究其实质,是因为它的丢失函数实际上是JS散度,而JS散度不会跟着两个分布的间隔改动而改动(这句不谨慎,细节参考WGAN中的描绘),这就会导致生成器的梯度会一直不变,然后导致模型练习效果很差。WGAN为了解决原始GAN网络练习不稳定的现象,引入了EM distance代替原有的JS散度,这样的改动会使生成器梯度一直改变,然后使模型得到充分练习。可是WGAN的提出伴跟着一个难点,即怎样让判别器的参数矩阵满意Lipschitz接连条件。
怎样解决上述所说的难点呢?在WGAN中,咱们采用了一种简略粗犷的方式来满意这一条件,即直接对判别器的权重参数进行取舍,强制将权重约束在[-c,c]规模内。咱们能够动动咱们的小脑瓜想想这种权重取舍的方式有什么样的问题——(滴,揭晓答案🍍🍍🍍)假如权重取舍的参数c很大,那么任何权重或许都需要很长时间才能到达极限,然后使练习判别器到达最优变得更加困难;假如权重取舍的参数c很小,这又简略导致梯度消失。因而,怎样确认权重取舍参数c是重要的,同时这也是困难的。WGAN提出之后,又提出了WGAN-GP来完成Lipschitz 接连条件,其首要经过添加一个赏罚项来完成。【关于WGAN-GP我没有做相关教程,假如不理解的能够评论区留言】那么本文提出了一种归一化的手段Spectral Normalization来完成Lipschitz接连条件,这种归一化详细是怎样完成的呢,下面听我渐渐道来。🍻🍻🍻
咱们仍是来先回忆一下Lipschitz接连条件,如下:
∣f(x1)−f(x2)∣≤K∣x1−x2∣|f(x_1)-f(x_2)| \le K|x_1-x_2|
这个式子约束了函数f(⋅){\rm{f}}( \cdot )的导数,即其导数的绝对值小于K,∣f(x1)−f(x2)∣∣x1−x2∣≤K\frac{|f(x_1)-f(x_2)|}{|x_1-x_2|} \le K。 🍋🍋🍋
本文介绍的Spectral Normalization的K=1,让咱们一起来看看怎样完成的吧!!!
上文提到,WGAN的难点是怎样让判别器的参数矩阵满意Lipschitz接连条件。那么咱们就从判别器入手和咱们唠一唠。实际上,判别器也是由多层卷积神经网络构成的,咱们用下式表明第n层网络输出和第n-1层输入的联系:
Xn=an(Wn⋅Xn−1+bn)X_n=a_n(W_n \cdot X_{n-1}+b_n)
其间an(⋅)a_n(\cdot)表明激活函数,WnW_n表明权重参数矩阵。为了便利起见,咱们不设置偏置项bnb_n,即bn=0b_n=0。那么上式变为:
Xn=an(Wn⋅Xn−1)X_n=a_n(W_n \cdot X_{n-1})
再为了便利起见🤸🏽♂️🤸🏽♂️🤸🏽♂️,咱们设an(⋅)a_n(\cdot),即激活函数为Relu。Relu函数在大于0时为y=x,小于0时为y=0,函数图画如下图所示:
这样的话式Xn=an(Wn⋅Xn−1)X_n=a_n(W_n \cdot X_{n-1})能够写成Xn=Dn⋅Wn⋅Xn−1X_n=D_n \cdot W_n \cdot X_{n-1},其间DnD_n为对角矩阵。【咱们这里能否理解呢?假如咱们的输入为正数时,经过Relu函数值是不变的,那么此刻DnD_n对应的对角元素应该为1;假如咱们的输入为负数时,经过Relu函数值将变成0,那么此刻DnD_n对应的对角元素应该为0。也便是说咱们将XnX_n改写成Dn⋅Wn⋅Xn−1D_n \cdot W_n \cdot X_{n-1}形式是可行的。】
接着咱们做一些简略的推理,得到判别器第n层输出和原始输入的联系,如下图所示:
最终一层的输出XnX_n即为判别器的输出,接下来咱们用f(x)f(x)表明;原始输入数据x0x_0咱们接下来用xx表明。则判别器终究输入输出的联系式如下:
f(x)=Dn⋅Wn⋅Dn−1⋅Wn−1⋯D3⋅W3⋅D2⋅W2⋅D1⋅W1⋅xf(x) = {D_n} \cdot {W_n} \cdot {D_{n – 1}} \cdot {W_{n – 1}} \cdots {D_3} \cdot {W_3} \cdot {D_2} \cdot {W_2} \cdot {D_1} \cdot {W_1} \cdot x
上文提到Lipschitz接连条件实质上便是约束函数f(⋅){\rm{f}}( \cdot )的导数改变规模,其实便是对f(x)f(x)梯度提出约束,如下:
∣∣∇xf(x)∣∣2=∣∣Dn⋅Wn⋅Dn−1⋅Wn−1⋯D3⋅W3⋅D2⋅W2⋅D1⋅W1∣∣2≤∣∣Dn∣∣2⋅∣∣Wn∣∣2⋅∣∣Dn−1∣∣2⋅∣∣Wn−1∣∣2⋯∣∣D1∣∣2⋅∣∣W1∣∣2||{\nabla _x}f(x)|{|_2} = ||{D_n} \cdot {W_n} \cdot {D_{n – 1}} \cdot {W_{n – 1}} \cdots {D_3} \cdot {W_3} \cdot {D_2} \cdot {W_2} \cdot {D_1} \cdot {W_1}|{|_2} \le ||{D_n}|{|_2} \cdot ||{W_n}|{|_2} \cdot ||{D_{n – 1}}|{|_2} \cdot ||{W_{n – 1}}|{|_2} \cdots ||{D_1}|{|_2} \cdot ||{W_1}|{|_2}
其间∣∣A∣∣2||A||_2表明矩阵A的2范数,也叫谱范数,它的值为λ1\sqrt {{\lambda _1}},λ1{\lambda _1}为 AHA{{\rm{A}}^H}{\rm{A}}的最大特征值。λ1\sqrt {{\lambda _1}}又称作矩阵A的奇特值【注:奇特值是AHA{{\rm{A}}^H}{\rm{A}}的特征值的开根号,也便是说λ1\sqrt {{\lambda _1}} 为A的其间一个奇特值或谱范数是最大的奇特值】,这里咱们将谱范数,即最大的奇特值记作σ(A)=λ1\sigma {(A)} = \sqrt {{\lambda _1}}。因为D是对角矩阵且由0、1构成,其奇特值总是小于等于1,故有下式:
即∇xf(x)∣∣2=∣∣Dn∣∣2⋅∣∣Wn∣∣2⋯∣∣D1∣∣2⋅∣∣W1∣∣2≤Π1nσ(Wi){\nabla _x}f(x)|{|_2}= ||{D_n}|{|_2}\cdot ||{W_n}|{|_2} \cdots ||{D_1}|{|_2} \cdot ||{W_1}|{|_2} \le \mathop \Pi \limits_1^{\rm{n}} \sigma ({W_i})。为满意Lipschitz接连条件,咱们应该让∣∣∇xf(x)∣∣2≤K||{\nabla _x}f(x)|{|_2} \le K ,这里的K设置为1。那详细要怎样做呢,其实便是对上式做一个归一化处理,让每一层参数矩阵除以该层参数矩阵的谱范数,如下:
∣∣∇xf(x)∣∣2=∣Dn∣∣2⋅∣∣Wn∣∣2σ(Wn)⋯∣∣D1∣∣2⋅∣∣W1∣∣2σ(W1)≤Π1nσ(Wi)σ(Wi)=1||{\nabla _x}f(x)|{|_2} = |{D_n}|{|_2} \cdot \frac{{||{W_n}|{|_2}}}{{\sigma ({W_n})}} \cdots ||{D_1}|{|_2} \cdot \frac{{||{W_1}|{|_2}}}{{\sigma ({W_1})}} \le \mathop \Pi \limits_1^{\rm{n}} \frac{{\sigma ({W_i})}}{{\sigma ({W_i})}} = 1
这样,其实咱们的Spectral Normalization
原理就讲的差不多了,最终咱们要做的便是求得每层参数矩阵的谱范数,然后再进行归一化操作。要想求矩阵的谱范数,首先得求矩阵的奇特值,详细求法我放在附录部分。
可是依照正常求奇特值的办法会消耗大量的计算资源,因而论文中运用了一种近似求解谱范数的办法,伪代码如下图所示:
在代码的实战中咱们便是依照上图的伪代码求解谱范数的,届时咱们会为咱们介绍。🍄🍄🍄
注:咱们阅览这部分有没有什么难度呢,我觉得或许仍是挺难的,你需要一些矩阵剖析的常识,我现已尽或许把这个问题描绘的简略了,有的文章写的很好,公式推导的也很详尽,我会在参考链接中给出。可是会涉及到最优化的一些理论,估计这就让咱们更头疼了,所以咱们渐渐消化吧!!!🍚🍚🍚在最终的附录中,我会给出本节内容相关的矩阵剖析常识,是我上课时的一些笔记,笔记包括本节的常识点,但针对性或许不是很强,也便是说或许包括一些其它内容,咱们能够挑选疏忽,当然了,你也能够细细的研究研究每个常识点,说不定后面就用到了呢!!!🥝🥝🥝
Spectral Normalization源码解析
源码下载地址:Spectral Normalization📥📥📥
这个代码运用的是CIFAR10数据集,完成的是一般生成对立网络的图画生成使命。我不打算再对每一句代码进行详细的解说,有不理解的能够先去看看我专栏中的其它GAN网络的文章,都有源码解析,弄理解后再看这篇你会发现十分简略。那么这篇文章我首要来介绍一下Spectral Normalization
部分的内容,其相关内容在spectral_normalization.py
文件中,咱们理论部分提到Spectral Normalization
关键的一步是求解每个参数矩阵的谱范数,相关代码如下:
def _update_u_v(self):
u = getattr(self.module, self.name + "_u")
v = getattr(self.module, self.name + "_v")
w = getattr(self.module, self.name + "_bar")
height = w.data.shape[0]
for _ in range(self.power_iterations):
u.data = l2normalize(torch.mv(w.view(height, -1).data, v.data))
v.data = l2normalize(torch.mv(torch.t(w.view(height,-1).data), u.data))
sigma = u.dot(w.view(height, -1).mv(v))
setattr(self.module, self.name, w / sigma.expand_as(w))
def l2normalize(v, eps=1e-12):
return v / (v.norm() + eps)
对上述代码做必定的解说,6,7,8,9,10行做的便是理论部分伪代码的工作,最终会得到谱范数sigma。11行为运用参数矩阵除以谱范数sigma,以此完成归一化的作用。【torch.mv完成的是矩阵乘法的操作,里面或许还有些函数你没见过,咱们百度一下用法就知道了,十分简略】
其实关键的代码就这些,是不是发现特别简略呢🍸🍸🍸每次介绍代码时我都会强调自己着手调试的重要性,很多时分写文章介绍源码都觉得有些力不从心,一些想表达的点总是很难表述,总归,咱们要是有什么不理解的就纵情调试叭,或许评论区留言,我天天在线摸鱼滴喔。⭐⭐⭐后期我也打算出一些视频教学了,这样的话就能够带着咱们一起调试,我想这样介绍源码彼此都会轻松很多。🛩🛩🛩
小结
Spectral Normalization
确实是有必定难度的,我也有许多地方理解的也不是很清楚,对于这种难啃的问题我是这样以为的。咱们能够先对其有一个大致的了解,知道整个过程,知道代码怎样完成,能运用代码跑通一些模型,然后考虑能否将其用在自己或许需要运用的地方,假如参加的效果欠好,咱们就没必要深究原理了,假如发现效果好,这时分咱们再回来渐渐细嚼原理也不迟。最终,期望各位都能获取新常识,能够学有所成叭!!!🌹🌹🌹
参考链接
GAN — Spectral Normalization 🍁🍁🍁
Spectral Normalization for GAN🍁🍁🍁
详解GAN的谱归一化(Spectral Normalization)🍁🍁🍁
谱归一化(Spectral Normalization)的理解🍁🍁🍁
附录
这部分是我学习矩阵剖析这门课程时的笔记,截取一些包括此部分的内容,有需求的感兴趣的能够看一看。🌱🌱🌱
如若文章对你有所协助,那就🛴🛴🛴