在深度学习中,训练数据量不足常常会影响分类算法的性能。我从这几年的相关工作经验感受得出,缺乏训练数据并不是例外而是一种规律,这就是为什么很多人会想出各种各样的数据增强方法。吴恩达也说过,scale drives machine learning progress,也是对在深度学习领域数量影响质量这一概念的一种诠释。
我们可以使用常规的数据增强手段,比如参考链接中提到的使用的例如旋转翻转,旋转,裁剪,变形,缩放等各类操作原始数据来生成新的训练数据,但这并不能给我们带来真正的新图像。
相反的,当我们处理稀少的训练数据时,GAN数据增强方法就显得更具优势。假设我们想要训练在工业生产中某些特定的缺陷如刮痕、脏污、磨损等,但常常会遇到这类希望出现的缺陷很少产生的现象,这就导致了我们可能只有一小部分显示典型缺陷的图像来训练网络。如果我们使用GAN,我们就可以为任何给定的缺陷类型生成额外的“真实的”图像。
这篇我简单地记录一下在我自定义的数据集上训练最近大火的模型“StyleGAN3”,对GAN感兴趣的,可以到B站找相关的资料,例如李沐大神的GAN精读,以及唐宇迪大神的GAN系列解读等。
我的环境:
- pytorch 1.10.0
- CUDA 11.5,cudnn 8.2
- VS2019
- Pycharm
可以参考StyleGAN3论文解读,模型关键的地方都有做了些许介绍。如果对StyleGAN3十分感兴趣 ,建议阅读StyleGAN3的论文。
解读部分更新于2022/3/22,还在持续更新中
最近重新看了StyleGAN3的论文,核心部分应该在第三段Practical application to generator network,这一段具体介绍了StyleGAN3相较于StyleGAN2在生成器方面的改进方法,这里面涉及了很多数字信号处理(复变函数)的处理手段,于是论文在附录(Appendices)中花了将近20多页介绍了这方面相关的理论知识。总而言之,整篇文章都在表现出作者是在用数字信号的概念来诠释网络的构建流程,光这个相对比较新奇的思路就让很多人阅读整篇论文都感觉很晦涩。为了防止被很多人冲,我这里大部分借鉴了某位大佬的解读,里面加入自己些许的理解,如果大家有疑问欢迎在评论区友善地提出。
1.1.1 整体逻辑
-
问题分析:发现目前的网络架构没有一个明确的机制来限制生成器以严格的分层方式合成图像。所谓分层,可以认为是因果的,粗糙特征对细微特征具有控制效果,比如在人脸图像中,转动脸部会带动鼻子的转动,鼻子的转动会进一步带动鼻子上毛孔的转动此外,由于特征图的频率不满足奈奎斯特-香农(Nyquisit-Shannon)采样定理的条件,会出现混叠现象。
-
解决方式:重新设计一个无混叠(alias-free)并严格遵循分层合成方式的网络。
-
分析:设计出来的无混叠生成器在平移或旋转时都是等变的,且这种生成器不会产生纹理沾粘(texture sticking)的现象。
1.1.2 动机
-
当前GAN网络的缺陷
作者发现目前流行的GAN结构都没有从一种自然的分层次方式(hierarchical manner ,由浅到深,由粗到细,由低到高频率合成特征)来合成图像,尽管这些GAN网络已经限制了各层特征图的分辨率,使得浅层的特征图不能代表高频信号,但不能保证各层操作产生的新频率小于对应的奈奎斯特频率。如果不满足了上述条件,就会出现混叠问题,使高频在频域中被表示为低频,污染整个信号。 -
主要贡献
作者设计一个网络体系结构,严格遵循理想的分层次方式。每一层都被限制在我们指定的范围内合成频率,因此,消除了混叠的问题。
1.1.3 方法
-
(4)低通滤波器的设计基于Kaiser窗函数(论文C.1 Kaiser low-pass filters),这里可以看它的直观作用。
-
等变和纹理沾粘
等变是指当输入平移/旋转时,输出也平移/旋转。(stylegan3/metrics/equivariance.py )
(1)平移等变(代码 equivariance.py[line 224] Integer translation (EQ-T))
根据上面的理论分析,如果我们将信号在整个网络的时域上视为无限连续信号,那么信号在时域上的平移实际上并不会改变信号在频域上的幅值。无论在时域上如何移动(上下左右)输入信号,网络每一层的输出都会跟着它移动,最终的输出信号肯定也会跟着它移动。作者定义了一个度量来计算平移等变方差:EQ-T(论文 公式3)。作者反映出峰值信噪比 (PSNR)以两组图像之间的分贝 (dB) 为单位,通过将合成网络的输入和输出平移某个随机量获得。
(2)旋转等变(代码 equivariance.py[line 243] Rotation (EQ-R))
对于旋转等变,需要对卷积和低通滤波器(LPF)做一些修改(论文 E.3 Rotation)。作者认为卷积核函数在时域是径向对称的,因为如果旋转输入信号,最直观和简单的方法是对Conv核执行同样的旋转,然而如果这样的话,两者之间就没有相对的运动,相当于原来的操作。对于低通滤波器的解释理论上和卷积同样的道理。EQ-R(论文 公式23)。
(3)纹理沾粘
等变网络不存在这种现象。这一现象的表现是高频和低频特征不会以相同的速度同时变换。但如果网络具有等变性,那么所有的特征必须以相同的速度变换在一起,这种现象自然不会发生。(论文 Figure 1) -
整体网络架构的详细设计
相较于stylegan2的生成器,除了基本操作的变化,网络架构也发生了变化,具体可参见论文Figure 3中带有**+**号的Configuration,接下来会一一介绍。(config B)
根据前面的分析,我们处理的输入本质上是一个无限的连续信号,所以作者在这里使用了傅里叶特征,它具有空间无限的特征即离散输入信号可以从连续表达式中采样。同时,由于存在一个实际的连续表达式,我们也可以很容易地对信号进行平移和旋转,然后对其进行采样并输入到网络中,这样我们就可以方便地计算EQ-T和EQ-R,这里可以详见代码networks_stylegan3.py[line 230~234]。networks_stylegan3.py[line 230~234]:
(config C),因为它们与自然转换层次结构的目标非常不一致,也就是说,每个特征的确定的亚像素位置完全继承自底层的粗糙特征。因此,作者减少了mapping network的深度(代码 train.py[c.G_kwargs.mapping_kwargs.num_layers], 由8变为2)并且去掉了 mixing regularization和path length regularization(代码 train.py[line 233~251],stylegan2与stylegan3-r的区别,stylegan3-r就没有这两项)。
*(config D)*在训练过程中σ^ 2=E[x^2],并将特征图用√σ ^2来划分(实际上使用卷积来划分以提高效率),详见代码networks_stylegan3.py[line 153~155];消除跳过连接,改为使用sigma的EMA归一化
(config E),一种非常直观的方法(临界采样)是将低通滤波器的截断频率Fc设置为采样率S的一半即S/2,将过渡带f频率设为fh = (√2 − 1)(s/2)。详见代码networks_stylegan3.py[line 436]。
(config F),使用当前深度学习框架中原始的构造来实现上样本👉leaky ReLU👉下样本这一序列其实并不有效,因此作者实现了一个自定义的CUDA内核(Appendix D),它结合了这些操作(Figure 4b),从而加快了10×的训练和相当大的内存节省。具体的代码可以看torch/unit/ops/filtered_lrelu开头的代码,尤其是cuda代码。
(config G),因为低通滤波器只是近似值,所以它不是频域中的理想矩形窗口,因此会有一些缺失的频率仍然可以通过临界点。为了抑制混叠,可以简单地将截止频率降低到fc=s/2−fh,从而确保所有混叠频率(在s/2以上)都在阻带内。由于信号现在包含的空间信息较少,作者修改了用于确定特征映射数量的触发方式,使其与fc成反比,而不是采样率s。networks_stylegan3.py [line 431]
(config H),networks_stylegan3.py [line 204~215],这里引入一个学习到的仿射层,它为输入的傅里叶特征输出全局平移和旋转参数,该层被初始化以执行身份转换,但会随着训练的时间推移而是使模型学习使用该机制。
(config T),虽然作者们发现他们对网络的改动已经大大提高了网路的等变性,但一些可见的伪影仍然存在,正如论文附带的视频所示。针对这一问题,作者们建议对每一层分别进行设计,他们希望在低分辨率层中有尽可能大的衰减,在高分辨率层中保留更多的高频特征。networks_stylegan3.py [class SynthesisNetwork]
在Figure 4c中展示了一个14层生成器中滤波器参数的示例,最后有两个严格采样的全分辨率层;
截止频率从第一层的fc=2几何增长到第一临界采样层的fc=sN/2;
最小可接受的阻带频率从f_{t,0} = 2·power(2.1) 开始,它虽然也是不停地增长但显然慢于截止频率。在作者的测试中,最后一层的阻带频率是ft=fc·2·power(0.3);
采样率s被设置为大于ft的2的最小倍数的两倍(但不超过最终的输出分辨率);
将过渡频率的半宽设置为fh = max(s/2, ft) -fc等(config R)。1.作者在所有层上用1×1替换3×3卷积,并通过增加两倍的特征映射数量来弥补减少的容量;networks_stylegan3.py [line 296]。 2.作者用一个径向对称的基于jinc的滤波器来代替基于sinc的降采样过滤器,我们使用相同的凯撒方案构造了该滤波器networks_stylegan3.py [line 376~384]
1.1.4 部分代码
training_loop.py
train.py
参考,首先克隆到本地,由于项目没有写requirements.txt,我把我环境中相关库导出在这里以供参考。
至此模型搭建基本完成,可以用如下命令进行单个图片/视频的生成简单体验一下。如果报错缺库,就自行pip安装。
- 数据打包:
训练的时候需要将一整个文件夹的数据转换成tfrecords的格式,可以通过👇这个命令生成对应的zip包 。
3.训练:
这里我说下需要着重考虑的参数,我参考了官方训练配置指南和一个StyleGAN2的训练教程。
-
–cfg:有StyleGAN3-T (等变平移)、 StyleGAN3-R (等变平移+旋转)、StyleGAN2这三项,优先选择前两项。
-
–batch:总批次大小,需要根据gpu的配置情况而选择合适的值。我这里只有一个3090,试了几次发现最高只能到24。
-
–gamma:R1正则化权重,根据作者的解释,值越大模型越稳定,值越小模型多样性越强。这个跟IS评价指标类似,涉及了一个熵值的问题。我这里选了参考StyleGAN2,选了10这个比较大的值。
-
–kimg:类似于iterations,一个总的迭代次数。默认值是25000,但作者说5000基本上效果就很好了。
-
–tick与–snap与:前者表示间隔多久打印一次训模信息,后者表示在tick*snap的基础上多久保存一个模型以及该模型的一张推理的结果。
-
–workers:windows设为0或1,懂得都懂。。
-
–metrics:用于在训练过程中评估生成的图像相较于我们自定义数据集的质量,如果不是为了写paper做研究性数据就设置为none,否则非常耗时。
- 图像生成:
借助搭建模型里的预测命令进行生成,注意seed配置。
-
command ‘[‘ninja’, ‘-v’]’ returned non-zero exit status 1
把环境中的site-packages/torch/utils/cpp_extension.py
[‘ninja’, ‘-v’]
改成
[‘ninja’, ‘–version’] -
Setting up PyTorch plugin “bias_act_plugin”… Failed!
Setting up PyTorch plugin “filtered_lrelu_plugin”… Failed!
Setting up PyTorch plugin “upfirdn2d_plugin”… Failed!以上三种情况的报错大概如下:
linux
importError: /root/path to .so file/bias_act_plugin.so: cannot open shared object file: No such file or directory
importError: /root/path to .so file/filtered_lrelu_plugin.so: cannot open shared object file: No such file or directory
importError: /root/path to .so file/upfirdn2d_plugin.so: cannot open shared object file: No such file or directorywindows
importError: DLL load failed while importing bias_act_plugin: 找不到指定的模块
….so的报错是在linux系统下,主要是因为不同的地方(没具体找)定义了两个不同的标准库选项:-std=c++14和/std:c++17,最直接的排查方法是
这样可以最直观的看到会报出c++: error: /std:c++17: No such file or directory这个错误,即可确定是上述所分析的错误。如果没有报错,而且后没有报错,代表你已成功手动编译了这个.so文件,即不需要再修改任何内容,直接可以运行代码。
还有一个原因是pytorch版本升级到了1.11以上,这样会导致linux和windows都出错,解决办法是将pytorch降低到1.11,或者像链接这里在torch_utils/ops/grid_sample_gradfix.py里面添加高版本pytorch支持(绿色行+号),这样就不用考虑将版本后torch和cuda对应支持的问题;建议不要用pytorch 2.0及以后的版本。
-
权重文件下载
链接 -
AttributeError: module ‘distutils’ has no attribute ‘version’
链接 -
RuntimeError: The size of tensor a (256) must match the size of tensor b (128) at non-singleton dimension 1
后面加了一个–cbase=16384 解决 -
模型
链接:https://pan.baidu.com/s/12-VlDG20AkCtUvVJQnKhyg
提取码:xzt0 -
RuntimeError: shape ‘[4, -1, 1, 512, 4, 4]’ is invalid for input of size 49152 (每个人的input of size可能不是49152)
networks_stylegan2.py(line 653)
将第653行G值改为 input of size/512/4/4, 我的是49152/512/4/4=6