物体检测中小物体问题

摘自:3D视觉初学者

0.介绍

检测小物体是计算机视觉中最具挑战性和重要的问题之一。在这篇文章中,我们将讨论通过迭代数百种小物体检测模型在Roboflow上开发的一些策略。无人机在公共空中海上数据集中从上方看到的小物体

为了提高模型在小对象上的性能,我们建议以下技术入手:

  • 提高图像拍摄分辨率
  • 增加模型的输入分辨率
  • 平铺图片
  • 通过扩充生成更多数据
  • 自动学习模型
  • 过滤掉多余的类

小物件很难!例如,在EfficientDet中,小型对象的AP仅为12%,大型对象的AP为51%。那几乎是五倍的差异!那么,为什么很难检测小物体呢?一切都取决于模型。对象检测模型通过聚合卷积层中的像素来形成特征。

PP-YOLO中用于对象检测的特征聚合

如果地面物体本来就不大,而在进行训练时还会变小。因此,小物体最有可能出现数据标记错误,甚至可能会省略其标识。从经验和理论上讲,小物体都很难检测。

提高图像拍摄分辨率

分辨率,分辨率,分辨率……全都与分辨率有关。

很小的物体在边界框内可能只包含几个像素,这意味着增加图像的分辨率以增加检测器可以从该边界框提取信息的丰富度,这非常重要。因此,如果可能我们建议捕获尽可能高分辨率的图像。

增加模型的输入分辨率

获得更高分辨率的图像后,就可以扩大模型的输入分辨率。警告:这将导致大型模型需要花费较长的训练时间,并且在开始部署时将较慢地推断出来。您可能需要进行实验以找出速度与性能之间的权衡。

平铺图片

检测小图像的另一种很好的策略是将图像平铺作为预处理步骤。平铺可以有效地将检测器放大到小物体上,但可以保持所需的小输入分辨率,以便能够进行快速推理。

通过扩充生成更多数据

数据扩充会从基本数据集中生成新图像。这对于防止模型过度拟合训练集非常有用。对于小物体检测,一些特别有用的增强包括随机裁剪,随机旋转和镶嵌增强。自动学习模型的锚定框

锚定框是模型学习预测的原型边界框。也就是说,锚框可以预先设置,有时对于训练数据而言不是最佳的。自定义调整这些参数以适合我们需要完成的任务,这是很好的。YOLOv5模型架构会根据小伙伴的自定义数据自动为您完成此操作。我们要做的就只是是开始训练。

过滤掉多余的类

类管理是提高数据集质量的一项重要技术。如果有两个类明显重叠的类,则应从数据集中过滤一个。如果因为认为数据集中的小对象不值得检测,并希望将他们先去除。大家可以通过运行Roboflow Pro的高级数据集来快速识别所有这些小对象。可以通过Roboflow的管理工具来实现类遗漏和类重命名。

GAN系列之 StarGAN

StarGAN: Unified Generative Adversarial Networks for Multi-Domain Image-to-Image Translation

Authors

Yunjey Choi, Minje Choi, Munyoung Kim, Jung-Woo Ha, Sunghun Kim, Jaegul Choo

Abstract

Recent studies have shown remarkable success in image-to-image translation for two domains. However, existing approaches have limited scalability and robustness in handling more than two domains, since different models should be built independently for every pair of image domains. To address this limitation, we propose StarGAN, a novel and scalable approach that can perform image-to-image translations for multiple domains using only a single model. Such a unified model architecture of StarGAN allows simultaneous training of multiple datasets with different domains within a single network. This leads to StarGAN’s superior quality of translated images compared to existing models as well as the novel capability of flexibly translating an input image to any desired target domain. We empirically demonstrate the effectiveness of our approach on a facial attribute transfer and a facial expression synthesis tasks.

Pix2Pix模型解决了有Pair对数据的图像翻译问题;CycleGAN解决了Unpaired数据下的图像翻译问题。但无论是Pix2Pix还是CycleGAN,都是解决了一对一的问题,即一个领域到另一个领域的转换。当有很多领域要转换了,对于每一个领域转换,都需要重新训练一个模型去解决。这样的行为太低效了。本文所介绍的StarGAN就是将多领域转换用统一框架实现的算法。

下图是StarGAN的效果,在同一种模型下,可以做多个图像翻译任务,比如更换头发颜色,更换表情,更换年龄等。

StarGAN,顾名思义,就是星形网络结构,在StarGAN中,生成网络G被实现成星形。

1.CycleGAN 不能解决多领域迁移的问题。 只能两个领域的互相转化A->B,B-A。 但是实际场景中,我们可能遇到 多个数据集,或者多种属性的互相转化的要求。这样的话我们就需要O(n^2)的G model。(如下图)

2.有些属性(如人的表情),如果只取其中的两个属性(笑和不笑),那么就无法利用上其他训练数据(比如生气/恐惧等表情数据)。

1.作者提出了StarGAN 来处理多个domain之间互相generate图像 的问题。只用一个generator网络。

假如想实现四个域内图像风格的相互转换,要实现这个目标,通过cycleGAN需要创建12个生成器(如图a)。而starGAN的直观构造如图b,只需要一个生成器即可。

2. G的输入除了图片,还有domain的label,对应的把生成图片变到指定的domain。

starGAN的提出是为了解决多数据集在多域间图像转换的问题,starGAN可以接受多个不同域的训练数据,并且只需要训练一个生成器,就可以拟合所有可用域中的数据。

StarGAN的大致训练流程

i)如图a,训练判别器,将 real_img 和 fake_img 分别传递给判别器,判别器会判别图像的真假,同时它还会判别该图像来自哪个域(只对real_img 的label做判别)。
ii)如图b,训练生成器,与CGAN类似,这里除了输入图像外,还要输入该图像想转换的目标域,这个目标域类似于约束条件,它要求生成器尽可能去生成该目标域中的图像。
iii)如图c,表示循环一致性的过程,如果只是单纯的使用条件去控制生成器生成,那么生成器就会生成满足条件但可能与输入图像无关的数据,为了避免这种情况,便使用循环一致性的思想,即将生成的图像加上输入图像所在的域作为生成器的输入,希望获得的输出与原输入图像越接近越好。
iiii)如图d,表示训练生成器,即将生成器生成的图片交给判别器,让判别器判别图像的真假以及图像所在的域是否正确。

损失函数:

Adversarial loss 为 conditional gan常用的。(实际替换为WGAN的loss)

Reconstruction loss 为L1 Loss (和Cyclegan一样)

Domain classification loss(属性分类)就是传统分类log NLLloss。

类别损失,该损失被分成两个,训练D的时候,使用真实图像在原始领域进行,训练G的时候,使用生成的图像在目标领域进行。

多数据集训练
在多数据集下训练StarGAN存在一个问题,那就是数据集之间的类别可能是不相交的,但内容可能是相交的。比如CelebA数据集合RaFD数据集,前者拥有很多肤色,年龄之类的类别。而后者拥有的是表情的类别。但前者的图像很多也是有表情的,这就导致前一类的图像在后一类的标记是不可知的。

为了解决这个问题,在模型输入中加入了Mask,即如果来源于数据集B,那么将数据集A中的标记全部设为0.

GAN系列之 CycleGAN

摘自 https://zhuanlan.zhihu.com/p/306442363

pixtopix需要一对一,一个image对应一个image,训练集的两组图片一一对应才能训练

CycleGAN的介绍

1.CycleGAN的原理

CycleGAN,即循环生成对抗网络,出自发表于 ICCV17 的论文《Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks》,和它的兄长Pix2Pix(均为朱大神作品)一样,用于图像风格迁移任务。以前的GAN都是单向生成,CycleGAN为了突破Pix2Pix对数据集图片一一对应的限制,采用了双向循环生成的结构,因此得名CycleGAN。

首先,CycleGAN也是一个GAN模型,通过判别器和生成器的对抗训练,学习数据集图片的像素概率分布来生成图片。

要完成X域到Y域的图片风格迁移,就要求GAN网络既要拟合Y域图片的风格分布分布,又要保持X域图片对应的内容特征。打个比方,用草图风格的猫图片生成照片风格的猫图片时,要求生成的猫咪“即要活灵活现,又要姿势不变”。“拟合数据分布”本来就是GAN干的活儿,而“保持原图片特征”在Pix2Pix上是这么实现的:

因为Pix2Pix是一个CGAN,所以,我们通过用X域图片当约束条件来限制Pix2Pix的输出Y域风格图片时保有X域图片的特征。

而送入CycleGAN的两组(X域Y域)图片没有一一对应关系,即使我们将X域图片当成限制条件输入到一个CGAN中,也起不到限制模型输出保有X域图片特征的作用。因为,送入的两组图片完全是随机配在一起,CGAN学不到任何联系。因此,CycleGAN采取了一个绝妙的设计:通过添加“循环生成”并优化一致性损失(Consistency Loss)来代替CGAN中使用的约束条件来限制生成器保有原域图片特征。这样就不需要训练集图片一一对应了。

2.CycleGAN的流程

下面,我们就来看看循环生成网络(CycleGAN)到底是怎么“循环起来”的:

上图左半部分,将原域图片x送入(x2y方向)生成器G生成目标域图片y^,然后再将生成的目标域图片y^送入(y2x方向)生成器F反过来生成原域图片x^。生成x^的目的就是用它与输入的真图片x来算L1 Loss。我们知道Pix2Pix优化时除了使用GAN Loss(对抗损失)外,还加入了生成器输入图片和输出图片的L1 Loss来对齐生成图片与输入图片的宏观轮廓(所谓低频信息)。同样的逻辑,我们也能在CycleGAN中用L1 Loss来对齐“循环生成”的x^与输入的原图片x的内容自然,x生成的y^的轮廓也是和x对齐的了。这就达到了(原论文中的例子)“马变斑马,花纹变,姿势不变”的目的了。(我在网上看到的CycleGAN资料都没有点明这一点的,所以只好自行脑补,欢迎指正。)

在这个x->y^->x^的生成过程中,可以通过判别器Dy与生成器(x2y)G进行对抗训练。那么这个链条上的反向生成器(y2x)F怎么办?当然是加个判别器Dx与它进行对抗训练了。这样CycleGAN就有了两个方向相反的生成器,两个分别判别x域、y域图片的判别器。但要注意一个问题:就像GAN的生成器和判别器不能同时训练一样,Cyc1eGAN的两个生成器、两个判别器也只能一个一个训练,这就形成了CycleGAN训练的两条“环路”。

3.CycleGAN的结构

接下来,我们再看看这两对判别器、生成器怎么摆:

上半部份是生成器G和判别器Dy进行x2y的训练过程,下半部份是生成器F和判别器Dx进行y2x的训练过程。很像是两个风格迁移方向相反Pix2Pix模型,只是这两个GAN是普通GAN,不是Pix2Pix那样的CGAN。这一点,从生成器和判别器的输入就可以看出来,输入的只有原域图片并没有像Pix2Pix一样融合条件图片。

4.CycleGAN的loss函数

前面分析了CycleGAN的原理,我们已经知道了CycleGAN的loss由对抗损失(称为gan loss或adversarial loss)和循环一致性损失(consitency loss)组成,下面看看公式:

上面公式中:

​指的是x2y过程的对抗损失(adversarial loss)

​指的是y2x过程的对抗损失(adversarial loss)

​指的是生成器G和生成器F的循环一致性损失。

其中为循环一致性损失(consitency loss)的缩放系数,是一个超参数。

实际上,原论文的代码还加入了本体映射损失(identity loss),只是默认设置为关闭。CycleGAN正常训练时,生成器G输入x,生成y^。计算生成器G的本体映射损失(identity loss)时,生成器G输入y,生成y^,然后用y与y^的L1 loss作为G的identity loss。相应地,生成器F的identity loss则是输入的x与生成的x^的L1 loss。优化CycleGAN时,如果启用identity loss则将这两部分加到模型总loss中。与循环一致性损失(consistency loss)一样,也使用缩放系数超参控制其在总loss中所占比重。

论文中提到,CycleGAN使用identity loss的目的是在迁移的过程中保持原色调,下面是使用identity loss的对比效果:

上面图片最右边一列使用identity loss后果然纠正了生成器的色偏。

code:

https://github.com/eriklindernoren/PyTorch-GAN

GAN系列之pix2pix

也许是CycleGAN的光芒太过耀眼,Pix2Pix就像家中的次子,还没得宠多长时间,就被弟弟CycleGAN抢走了风头。这也怪不得它们的“爹滴”朱大神把“域风格迁移”的CycleGAN(下个项目介绍)造得太好用了,似乎完全能够代替“像素风格迁移”的Pix2Pix,以至于都来不及给Pix2Pix起个××GAN的名字~

其实,除了“白天照片变夜晚”、“图片着色”、“蓝图变街景”等它弟弟CycleGAN更容易玩儿的花样儿外,Pix2Pix是有着自己的独门绝技的。比如,用自然风景照片训练好的Pix2Pix模型,能实时将手绘的草图渲染成对应风景照片。如果训练集照片里包括老虎等动物,我们几笔在一个圆圈脑袋上画个王字,Pix2Pix模型就能生成一张活灵活现的大老虎,比《照相馆的故事》快多了~Pix2Pix的工作也启发了一些更具体的应用,比如专门手绘照片的SketchyGAN、手绘人脸的模型DeepFaceDrawing等。另外Pix2Pix->Pix2PixHD(高清渲染)->Vid2Vid(视频实时渲染)也是一条发展路线。试想,只需建好游戏人物和场景的结构模型,然后机器自动按训练的风格渲染人物和场景,游戏设计师们有没有感到点儿激动。

1. Pix2Pix的原理

发表在CVPR2017上的论文《Image-to-Image Translation with Conditional Adversarial Networks》是将GAN应用于有监督的图像到图像翻译的经典论文,提出的GAN模型被简称为Pix2Pix(不叫××GAN,很像是小名儿吧~)。为了解决图像到图像的翻译(也就是前面提到的那些上色、手绘草图的应用),我们需要建立一个模型实现图像到图像的映射。

以前曾经有过尝试搭建一个CNN网络进行映射,并用L1距离来度量、优化模型,结果发现效果很模糊(用L2距离更模糊),就像下面这样:

那么,既然GAN能够较好地生成图片的细节,我们何不拿来一用?显然,经典GAN是不行的,没法控制输出嘛。CGAN正好拿来一用。对此,朱大神在报告里曾经解释过:如果我们用经典GAN,判别器判别时会出现这样的问题。

这样的生成图片判别为真没问题

但是,这样的生成图片也判断为真就有问题了。显而易见,生成的猫图片与手绘的猫草图的形态完全不一致。但因为这也是一张猫图片,是符合训练集图片的像素概率分布的,所以会被经典GAN判别为真图片。

为了解决这一问题,我们将输入的猫草图作为“条件标签”和生成的猫图片一起送入判别器进行判断,如下图:

这看上去是不是有点儿CGAN的影子?没错,这个Pix2Pix就是个CGAN!

2.Pix2Pix的结构

我们将Pix2Pix的结构与上篇CGAN的结构对比一下:

上图的上半部份是普通CGAN的结构,下半部分是Pix2Pix的结构。对比发现,Pix2Pix与CGAN的结构有两点不同:

  1. 在Pix2Pix中,输入生成器的控制条件由“分类标签y”变成了A组(原风格)图片,因为这里我们要用A组(原风格)图片做为控制条件来生成B组(目标风格)图片。由于输入生成器的A组图片的维度(图片尺寸)与生成器输出的B组图片的维度相同,足以映射复杂分布,所以,我们不必再输入噪声z。细心的同学可能会发现:在刚才那张“对比普通CGAN和Pix2Pix结构”的图片中,我们对“条件y”的解释,与上一张“介绍给Pix2Pix加标签原因”的图片中的解释不一样。“对比结构”的图片中将生成器的输入解释为“条件y”,而“解释用CGAN原因”的图片中将生成器的输入解释为“输入x”。实际上这两种对生成器输入的解释都指的是A(原风格)组图片,不影响后面的推理。但个人觉得:将生成器的输入解释为“条件y”更容易帮助理解Pix2Pix的CGAN本质。我理解,Pix2Pix拟合的是训练集中B组(目标风格)图片的像素概率分布,A组(原风格)图片是作为“约束条件”来使用的。对比一下普通CGAN的结构就清楚了。
  2. 在Pix2Pix中,输入判别器的控制条件也由“分类标签y”变成了A组(原风格)图片。A组(原风格)图片作为“条件y”要和真B组(目标风格)图片或生成器生成的假B组图片(在图像通道维度上)拼接在一起送入判别器。这个很好理解,也说明了前面把生成器的输入解释为“条件y”更“工整”。

这样,Pix2Pix做了以上改动后,整个模型从“输入噪声、输出图片”的流程,变成了“输入A组图片、输出B组图片”的流程。

3.Pix2Pix的loss

在大神造Pix2Pix的过程中也试过各种“配方”。包括使用L1损失、使用CGAN损失和使用两者之和,测试结果如下:

观察结果发现:

  • 只用L1损失时,生成的图片比较模糊。
  • 只用CGAN损失时,生成的图片很清晰,但颜色风格与Ground Truth图片差别较大。
  • 使用L1+CGAN损失时,生成的图片又清晰,又保留了更多Ground Truth图片的特征。

所以,最后Pix2Pix使用了L1+CGAN损失。我们看下loss的构成。

先看L1损失:

L1损失的计算方法就是真B组(目标风格)图片与生成器生成的假B组图片逐像素求差的绝对值再求平均。公式中的x指A组(原风格)图片,y指B组(目标风格)图片,z指C输入给生成器的(一般是高斯分布的)噪声,代码中并未使用。

再来看看CGAN损失:

Pix2Pix的CGAN损失和普通CGAN损失一模一样

Pix2Pix总的损失是这两者之和:

GAN系列之CGAN(Conditional GAN)

GAN只是拟合原数据集的像素概率分布,生成的样本并没有提供新的信息以优化模型的分类边界。我理解,样本插值还能优化一下分类边界,原始GAN充其量只能添加一点噪声,或许能增强一点模型泛化能力吧(真做数据增强还得InforGAN、styleGAN这样的才好,能通过潜空间插值对图像做高级语义的增强,这是后话。)。

原始GAN用起来也不方便,为了分别生成0~9的数字,得将原数据集按标签分为10组,每组用一个模型训练,一共需要10个模型。训练时由于每组的数据量少到原来的十分之一,也会发生因样本太少导致模型无法拟合的现象。所以,意欲降伏GAN的大神给原始GAN装了个钮,让GAN乖乖要啥给啥。这个带按钮的改进版就是CGAN。

CGAN(Conditional GAN)介绍

1、CGAN的原理

CGAN的全称是Conditional Generative Adversarial Nets,即条件生成对抗网络。故名思议,就是通过添加限制条件,来控制GAN生成数据的特征(类别)。

当我第一次了解了CGAN原理,我惊诧于它给GAN“加按钮”的方法竟然如此简单粗暴,要做仅仅就是“把按钮加上去”——训练时将控制生成类别的标签连同噪声一起送进生成器的输入端,这样在预测时,生成器就会同样根据输入的标签生成指定类别的图片了判别器的处理也是一样,仅仅在输入加上类别标签就可以了。

那么,为什么加了标签,CGAN就乖乖听话、要啥给啥了呢?原理也是十分简单,我们知道GAN要干的就是拟合数据的概率分布,而CGAN拟合的就是条件下的概率分布。

GAN:

原生GAN中的概率全改成条件概率:

而上面CGAN公式中的条件y就是咱给GAN装的“钮”。加上了这个条件按钮,GAN优化的概率期望分布公式就变成了CGAN优化的条件概率期望分布公式。即CGAN优化的目标是:在条件Y下,在判别器最大化真实数据与生成数据差异的情况下,最小化这个差距。训练CGAN的生成器时要同时送入随机噪声z和和条件y(在本项目中y就是MNIST手写数字数据集的数字标签)。就是这么简单!

2、CGAN的结构

CGAN设计巧妙,而结构也十分简单、清晰,与经典GAN只有输入部分稍许不同。

我们看看原始GAN与CGAN的结构对比(包括生成器和判别器),上半部份的是经典GAN,下半部分是CGAN:

我们先回顾下经典GAN的结构流程(如上图上半部份所示):

  • 训练判别器。将噪声z送入生成器,输出fake_x;将fake_x送入判别器,在更新判别器参数时尝试拉近判别器的输出与真标签1的距离,即最小化判别器输出与真标签1的交叉熵损失。再将真图片送入判别器,更新判别器参数时尝试拉近判别器的输出与假标签0的距离,即最小化判别器输出与假标签0的交叉熵损失。这个过程中,用真、“假”图片训练判别器的顺序不必需固定,真、假标签取值0、1也无需固定(可相反,效果没有区别)。要注意的是,训练判别器的过程中,只更新判别器参数,不更新生成器参数。
  • 训练生成器。生成器训练的过程和判别器基本一样,只是将生成器输出的“假图片”送入判别器后,将判别器的输出与真标签(1)拉近。目的就是,使生成器参数更新的方向朝着“骗过判别器的目标”进行,也就是所谓“对抗过程”。当然判别器出掌(判别器更新参数)时,生成器不还手(生成器不更新参数),轮到生成器还手(生成器更新参数)时,判别器也得双手背后(判别器不更新参数)。不然就打成一团,谁也看不到招式(无法正确更新参数,提高生成能力)了

我们再看下CGAN给GAN加的“料”(如上图下半部份所示):

  • 先看判别器。如图,无论是给判别器送入真图片还是生成器生成的假图片时,都要加上个“条件y”,也就是分类标签。判别器输出没有变化仍然只是判断输入图片的真假。老实说,当时我曾想:既然咱都conditional GAN了,这个判别器是不是要输出分类标签y来训练Condition那部分?但转念一想,不行,判别器还是得判别真假,不然没法和生成器对抗了。BUT,后来我发现还真有走这个路线的GAN,叫InfoGAN。这个InfoGAN给生成器配了两个判别器,一个判真假,一个分类别。
  • 再看生成器。生成器的输入除了随机噪声z外,也加入了“条件y”。到这儿,我又想:既然有了条件标签,就不用输入噪声z了吧~。答案当然是,不行!因为,噪声z的维度是和生成器输出图片的尺寸、复杂度相关的。本项目中输出图片尺寸是28×28=784。按理说模型进行映射的输入、输出尺寸应该是相等的。但是输出图片只是手写数字,规律比较简单,输入的尺寸可以进行一定程度的压缩。一般噪声z的维度为几十到一百就能生成比较理想的图片细节,如果太低会导致生成器拟合能力不足,生成图片质量低下。条件z只是一个取值0~9的维度为一的向量,模型拟合像素概率分布的效果可想而知。后面我们介绍的Pix2Pix模型的输入是一张和输出尺寸相同的图片,就不再输入噪声z了。

CGAN需要注意的一点是:输入的条件标签y不但要在输入时与噪声z融合在一起,在生成器和判别器的每一层输入里都要与特征图相融合,才能让模型“学好条件y”。不然,标签可能不灵~

code https://github.com/eriklindernoren/PyTorch-GAN

GAN系列之经典GAN(一)

reference:

https://zhuanlan.zhihu.com/p/78777020

https://zhuanlan.zhihu.com/p/28853704

GAN全称:Generative Adversarial Network 即生成对抗网络,由Ian J. Goodfellow等人于2014年10月发表在NIPS大会上的论文《Generative Adversarial Nets》中提出。此后各种花式变体Pix2Pix、CYCLEGAN、STARGAN、StyleGAN等层出不穷,在“换脸”、“换衣”、“换天地”等应用场景下生成的图像、视频以假乱真,好不热闹。前段时间PaddleGAN实现的First Order Motion表情迁移模型,能用一张照片生成一段唱歌视频。各种搞笑鬼畜视频火遍全网。用的就是一种GAN模型哦。深度学习三巨神之一的LeCun也对GAN大加赞赏,称“adversarial training is the coolest thing since sliced bread”。

对抗生成模型GAN首先是一个生成模型,和大家比较熟悉的、用于分类的判别模型不同。

判别模型的数学表示是y=f(x),也可以表示为条件概率分布p(y|x)。当输入一张训练集图片x时,判别模型输出分类标签y。模型学习的是输入图片x与输出的类别标签的映射关系。即学习的目的是在输入图片x的条件下,尽量增大模型输出分类标签y的概率。

而生成模型的数学表示是概率分布p(x)。没有约束条件的生成模型是无监督模型,将给定的简单先验分布π(z)(通常是高斯分布),映射为训练集图片的像素概率分布p(x),即输出一张服从p(x)分布的具有训练集特征的图片。模型学习的是先验分布π(z)与训练集像素概率分布p(x)的映射关系。

生成对抗网络一般由一个生成器(生成网络),和一个判别器(判别网络)组成。生成器的作用是,通过学习训练集数据的特征,在判别器的指导下,将随机噪声分布尽量拟合为训练数据的真实分布,从而生成具有训练集特征的相似数据。而判别器则负责区分输入的数据是真实的还是生成器生成的假数据,并反馈给生成器。两个网络交替训练,能力同步提高,直到生成网络生成的数据能够以假乱真,并与与判别网络的能力达到一定均衡。

GAN的本质

其实GAN模型以及所有的生成模型都一样,做的事情只有一件:拟合训练数据的分布。对图片生成任务来说就是拟合训练集图片的像素概率分布。下面我们从原理的角度演示一下GAN的训练过程:

上图中: 黑色点线为训练集数据分布曲线 蓝色点线为判别器输出的分布曲线 绿色实线为生成器输出的分布曲线 z展示的是生成器映射前的简单概率分布(一般是高斯分布)的范围和密度 x展示的是生成器映射后学到的训练集的概率分布的范围和密度 (a)判别器与生成器均未训练呈随机分布 (b)判别器经过训练,输出的分布在靠近训练集“真”数据分布的区间趋近于1(真),在靠近生成器生成的“假”数据分布的区间趋近于0(假) (c)生成器根据判别器输出的(真假)分布,更新参数,使自己的输出分布趋近于训练集“真”数据的分布。 经过(b)(c)(b)(c)…步骤的循环交替。判别器的输出分布随着生成器输出的分布与训练集分布的接近而更加平缓;生成器输出的分布则在判别器输出分布的指引下逐渐趋近于训练集“真”数据的分布。 (d)训练完成时,生成器输出的分布完美拟合了训练集数据的分布,判别器的输出由于生成器的完美拟合而无法判别生成器输出的真伪而呈一条取值约为0.5(真假之间)的直线。

GAN的组成

  1. 解读GAN的loss函数

GAN网络的训练优化目标就是如下公式:

公式出自Goodfellow在2014年发表的论文Generative Adversarial Nets。这里简单介绍下公式的含义和如何应用到代码中。上式中等号左边的部分: V(D,G)表示的是生成样本和真实样本的差异度,可以使用二分类(真、假两个类别)的交叉熵损失。

maxV(D, G)表示在生成器固定的情况下,通过最大化交叉熵损失V(D,G)来更新判别器D的参数。

min maxV(D, G)表示生成器要在判别器最大化真、假图片交叉熵损失V(D,G)的情况下,最小化这个交叉熵损失

首先固定G训练D :

1)训练D的目的是希望这个式子的值越大越好。真实数据希望被D分成1,生成数据希望被分成0。

第一项,如果有一个真实数据被分错,那么log(D(x))<<0,期望会变成负无穷大。

第二项,如果被分错成1的话,第二项也会是负无穷大。

很多被分错的话,就会出现很多负无穷,那样可以优化的空间还有很多。可以修正参数,使V的数值增大。

2)训练G ,它是希望V的值越小越好,让D分不开真假数据。

因为目标函数的第一项不包含G,是常数,所以可以直接忽略 不受影响。

对于G来说 它希望D在划分他的时候能够越大越好,他希望被D划分1(真实数据)。

第二个式子和第一个式子等价。在训练的时候,第二个式子训练效果比较好 常用第二个式子的形式。

证明V是可以收敛导最佳解的。

(1)global optimum 存在

(2)global optimum训练过程收敛

全局优化首先固定G优化D,D的最佳情况为:

1、证明D*G(x)是最优解

由于V是连续的所以可以写成积分的形式来表示期望:

通过假设x=G(z)可逆进行了变量替换,整理式子后得到:

然后对V(G,D)进行最大化:对D进行优化令V取最大

取极值,对V进行求导并令导数等于0.求解出来可得D的最佳解D*G(x)结果一样。

2、假设我们已经知道D*G(x)是最佳解了,这种情况下G想要得到最佳解的情况是:G产生出来的分布要和真实分布一致,即:

在这个条件下,D*G(x)=1/2。

接下来看G的最优解是什么,因为D的这时已经找到最优解了,所以只需要调整G ,令

对于D的最优解我们已经知道了,D*G(x),可以直接把它带进来 并去掉前面的Max

然后对 log里面的式子分子分母都同除以2,分母不动,两个分子在log里面除以2 相当于在log外面 -log(4) 可以直接提出来:

结果可以整理成两个KL散度-log(4)

KL散度是大于等于零的,所以C的最小值是 -log(4)

当且仅当

所以证明了 当G产生的数据和真实数据是一样的时候,C取得最小值也就是最佳解。

如上图所示GAN由一个判别器(Discriminator)和一个生成器(Generator)两个网络组成。

训练时先训练判别器:将训练集数据(Training Set)打上真标签(1)和生成器(Generator)生成的假图片(Fake image)打上假标签(0)一同组成batch送入判别器(Discriminator),对判别器进行训练。计算loss时使判别器对真数据(Training Set)输入的判别趋近于真(1),对生成器(Generator)生成的假图片(Fake image)的判别趋近于假(0)。此过程中只更新判别器(Discriminator)的参数,不更新生成器(Generator)的参数。

然后再训练生成器:将高斯分布的噪声z(Random noise)送入生成器(Generator),然后将生成器(Generator)生成的假图片(Fake image)打上真标签(1)送入判别器(Discriminator)。计算loss时使判别器对生成器(Generator)生成的假图片(Fake image)的判别趋近于真(1)。此过程中只更新生成器(Generator)的参数,不更新判别器(Discriminator)的参数。

判别器结构:

生成器结构:

代码实现:http://139.9.1.231/index.php/2021/12/29/gan/

数据降维方法汇总

网上关于各种降维算法的资料参差不齐,同时大部分不提供源代码。这里有个 GitHub 项目整理了使用 Python 实现了 11 种经典的数据抽取(数据降维)算法,包括:PCA、LDA、MDS、LLE、TSNE 等,并附有相关资料、展示效果。

所谓降维,即用一组个数为 d 的向量 Zi 来代表个数为 D 的向量 Xi 所包含的有用信息,其中 d<D;通俗来讲,即将高维度下降至低维度;将高维数据下降为低维数据。

降维算法资料链接代码展示
PCA资料链接1 资料链接2 资料链接3PCA
KPCA资料链接1 资料链接2 资料链接3KPCA
LDA资料链接1 资料链接2LDA
MDS资料链接1MDS 
ISOMAP资料链接1 资料链接2ISOMAP
LLE资料链接1 资料链接2LLE
TSNE资料链接1TSNE
AutoEncoder无 
FastICA资料链接1FastICA
SVD资料链接1 资料链接2SVD
LE资料链接1资料链接2LE
LPP资料链接1 资料链接2LPP

环境: python3.6 ubuntu18.04(windows10) 需要的库: numpy sklearn tensorflow matplotlib

github:https://github.com/heucoder/dimensionality_reduction_alo_codes 

YOLO系列(二):yolov1

YOLOv1属于一阶段、anchor-free 目标检测

整体来看,Yolo算法采用一个单独的CNN模型实现end-to-end的目标检测,整个系统如图5所示:首先将输入图片resize到448×448,然后送入CNN网络,最后处理网络预测结果得到检测的目标。相比R-CNN算法,其是一个统一的框架,其速度更快,而且Yolo的训练过程也是end-to-end的。

具体来说,Yolo的CNN网络将输入的图片分割成 \(S \times S\) 网格,然后每个单元格负责去检测那些 中心点落在该格子内的目标,如图6所示,可以看到狗这个目标的中心落在左下角一个单元格内, 那么该单元格负责预测这个狗。每个单元格会预测 \(B\) 个边界框 (bounding box) 以及边界框的 置信度 (confidence score) 。所谓置信度其实包含两个方面,一是这个边界框含有目标的可能性 大小,二是这个边界框的准确度。前者记为 \(\operatorname{Pr}(object)\) ,当该边界框是背景时 (即不包含目 标),此时 \(\operatorname{Pr}(object)=0\) 。而当该边界框包含目标时, \(\operatorname{Pr}(object)=1\) 。边界框的准 确度可以用预测框与实际框 (ground truth) 的IOU (intersection over union,交并比) 来表 征,记为 \(\mathrm{IOU}{\text {pred }}^{\text {truth }}\) 。因此置信度可以定义为 \(\operatorname{Pr}(object) * \mathrm{IOU}{\text {pred }}^{\text {truth }}\) 。很多人可能将Yolo 的置信度看成边界框是否含有目标的概率,但是其实它是两个因子的乘积,预测框的准确度也反映 在里面。边界框的大小与位置可以用4个值来表征: (x, y, w, h),其中 (x, y) 是边界框的中 心坐标,而 w和 h 是边界框的宽与高。还有一点要注意,中心坐标的预测值 (x, y) 是相对于 每个单元格左上角坐标点的偏移值,并且单位是相对于单元格大小的,单元格的坐标定义如图6所 示。而边界框的 \(w\) 和 \(h\) 预测值是相对于整个图片的宽与高的比例,这样理论上4个元素的大小 应该在 \([0,1]\) 范围。这样,每个边界框的预测值实际上包含 5 个元素: \((x, y, w, h, c)\) ,其中 \((x, y)\) 是边界框的中 心坐标,而 \(w\) 和 \(h\) 是边界框的宽与高。还有一点要注意,中心坐标的预测值 \((x, y)\) 是相对于 每个单元格左上角坐标点的偏移值,并且单位是相对于单元格大小的,单元格的坐标定义如图所示。而边界框的\(w\) 和 \(h\) 预测值是相对于整个图片的宽与高的比例,这样理论上4个元素的大小 应该在 \([0,1]\) 范围。这样,每个边界框的预测值实际上包含 5 个元素: \((x, y, w, h, c)\) ,其中 前 4 个表征边界框的大小与位置,而最后一个值是置信度。

还有分类问题,对于每一个单元格其还要给出预测出 C个类别概率值,其表征的是由该单元格负 责预测的边界框其目标属于各个类别的概率。但是这些概率值其实是在各个边界框置信度下的条件 概率,即 \(\operatorname{Pr}\left(\right. class _{i} \mid object )\) 。值得注意的是,不管一个单元格预测多少个边界框,其只预测 一组类别概率值,这是Yolo算法的一个缺点,在后来的改进版本中,Yolo9000是把类别概率预测 值与边界框是绑定在一起的。同时,我们可以计算出各个边界框类别置信度(class-specific confidence scores):


边界框类别置信度表征的是该边界框中目标属于各个类别的可能性大小以及边界框匹配目标的好 坏。后面会说,一般会根据类别置信度来过滤网络的预测框。
总结一下,每个单元格需要预测 \((B * 5+C)\) 个值。如果将输入图片划分为 \(S \times S\) 网格,那 么最终预测值为 \(S \times S \times(B * 5+C)\) 大小的张量。整个模型的预测值结构如下图所示。对 于PASCAL VOC数据,其共有20个类别,如果使用 \(S=7, B=2\) ,那么最终的预测结果就是 \(7 \times 7 \times 30\) 大小的张量。在下面的网络结构中我们会详细讲述每个单元格的预测值的分布位 置。

Yolo采用卷积网络来提取特征,然后使用全连接层来得到预测值。网络结构参考GooLeNet模型,包含24个卷积层和2个全连接层,如图8所示。对于卷积层,主要使用1×1卷积来做channle reduction,然后紧跟3×3卷积。对于卷积层和全连接层,采用Leaky ReLU激活函数:max(x, 0.1x) 。但是最后一层却采用线性激活函数。

损失函数计算如下:

其中第一项是边界框中心坐标的误差项, \(1_{i j}^{obj}\) 指的是第 \(i\) 个单元格存在目标,且该单元格中的第 \(j\) 个边界框负责预测该目标。第二项是边界框的高与宽的误差项。第三项是包含目标的边界框 的置信度误差项。第四项是不包含目标的边界框的置信度误差项。而最后一项是包含目标的单元格 的分类误差项, \(1_{i}^{\text {obj }}\) 指的是第 \(i\) 个单元格存在目标。这里特别说一下置信度的target值 \(C_{i}\) , 如果是不存在目标,此时由于 \(\operatorname{Pr}( object )=0\) ,那么 \(C_{i}=0\) 。如果存在目标,
\(\operatorname{Pr}( object )=1\) ,此时需要确定 \(\mathrm{IOU}{\text {pred }}^{\text {truth }}\) ,当然你希望最好的话,可以将IOU取 1 ,这样 \(C{i}=1\) ,但是在 YOLO实现中,使用了一个控制参数 rescore (默认为 1 ),当其为 1 时,IOU不 是设置为 1 ,而就是计算truth和pred之间的真实 IOU

网络预测: 基于非极大值抑制算法

这个算法不单单是针对Yolo算法的,而是所有的检测算法中都会用到。NMS算法主要解决的是一个目标被多次检测的问题,如图11中人脸检测,可以看到人脸被多次检测,但是其实我们希望最后仅仅输出其中一个最好的预测框,比如对于美女,只想要红色那个检测结果。那么可以采用NMS算法来实现这样的效果:首先从所有的检测框中找到置信度最大的那个框,然后挨个计算其与剩余框的IOU,如果其值大于一定阈值(重合度过高),那么就将该框剔除;然后对剩余的检测框重复上述过程,直到处理完所有的检测框。Yolo预测过程也需要用到NMS算法。

下面就来分析Yolo的预测过程,这里我们不考虑batch,认为只是预测一张输入图片。根据前面的分析,最终的网络输出是 7×7×30 ,但是我们可以将其分割成三个部分:类别概率部分为 [7,7,20] ,置信度部分为 [7,7,2] ,而边界框部分为 [7,7,2,4] (对于这部分不要忘记根据原始图片计算出其真实值)。然后将前两项相乘(矩阵 [7,7,20] 乘以 [7,7,2] 可以各补一个维度来完成 [7,7,1,20]×[7,7,2,1] )可以得到类别置信度值为 [7,7,2,20] ,这里总共预测了 7∗7∗2=98 个边界框。

所有的准备数据已经得到了,那么我们先说第一种策略来得到检测框的结果,我认为这是最正常与自然的处理。首先,对于每个预测框根据类别置信度选取置信度最大的那个类别作为其预测标签,经过这层处理我们得到各个预测框的预测类别及对应的置信度值,其大小都是 [7,7,2] 。一般情况下,会设置置信度阈值,就是将置信度小于该阈值的box过滤掉,所以经过这层处理,剩余的是置信度比较高的预测框。最后再对这些预测框使用NMS算法,最后留下来的就是检测结果。一个值得注意的点是NMS是对所有预测框一视同仁,还是区分每个类别,分别使用NMS。Ng在deeplearning.ai中讲应该区分每个类别分别使用NMS,但是看了很多实现,其实还是同等对待所有的框,我觉得可能是不同类别的目标出现在相同位置这种概率很低吧。

上面的预测方法应该非常简单明了,但是对于Yolo算法,其却采用了另外一个不同的处理思路(至少从C源码看是这样的),其区别就是先使用NMS,然后再确定各个box的类别。其基本过程如图12所示。对于98个boxes,首先将小于置信度阈值的值归0,然后分类别地对置信度值采用NMS,这里NMS处理结果不是剔除,而是将其置信度值归为0。最后才是确定各个box的类别,当其置信度值不为0时才做出检测结果输出。这个策略不是很直接,但是貌似Yolo源码就是这样做的。Yolo论文里面说NMS算法对Yolo的性能是影响很大的,所以可能这种策略对Yolo更好。但是我测试了普通的图片检测,两种策略结果是一样的。

YOLO系列(五)yolov4-tiny

YOLOv4-tiny结构是YOLOv4的精简版,属于轻量化模型,参数只有600万相当于原来的十分之一,这使得检测速度提升很大。整体网络结构共有38层,使用了三个残差单元,激活函数使用了LeakyReLU,目标的分类与回归改为使用两个特征层,合并有效特征层时使用了特征金字塔(FPN)网络。其同样使用了CSPnet结构,并对特征提取网络进行通道分割,将经过3×3卷积后输出的特征层通道划分为两部分,并取第二部分。在COCO数据集上得到了40.2%的AP50、371FPS,相较于其他版本的轻量化模型性能优势显著。其结构图如下图所示。

YOLOv4-tiny具有多任务、端到端、注意力机制和多尺度的特点。多任务即同时完成目标的分类与回归,实现参数共享,避免过拟合;端到端即模型接收图像数据后直接给出分类与回归的预测信息;注意力机制是重点关注目标区域特征进行详细处理,提高处理速度;多尺度的特点是将经过下采样和上采样的数据相互融合,其作用是能够分割出多种尺度大小的目标。在对模型进行训练时可以使用Mosaic数据增强、标签平滑、学习率余弦退火衰减等方法来提升模型的训练速度和检测精度。

YOLO系列(四):yolov3

yolov3属于一阶段、anchor-based 目标检测

FPN :

原来多数的object detection算法都是只采用顶层特征做预测,但我们知道低层的特征语义信息比较少,但是目标位置准确;高层的特征语义信息比较丰富,但是目标位置比较粗略。另外虽然也有些算法采用多尺度特征融合的方式,但是一般是采用融合后的特征做预测,而本文不一样的地方在于预测是在不同特征层独立进行的。

FPN(Feature Pyramid Network)算法可以同时利用低层特征高分辨率和高层特征的高语义信息,通过融合这些不同层的特征达到很好的预测效果。此外,和其他的特征融合方式不同的是本文中的预测是在每个融合后的特征层上单独进行的。(对不同特征层单独预测)

网络结构解析:

  1. Yolov3中,只有卷积层,通过调节卷积步长控制输出特征图的尺寸。所以对于输入图片尺寸没有特别限制。流程图中,输入图片以256*256作为样例。
  2. Yolov3借鉴了金字塔特征图思想,小尺寸特征图用于检测大尺寸物体,而大尺寸特征图检测小尺寸物体。特征图的输出维度为 [公式] , [公式] 为输出特征图格点数,一共3个Anchor框,每个框有4维预测框数值 [公式] ,1维预测框置信度,80维物体类别数。所以第一层特征图的输出维度为 [公式] 。
  3. Yolov3总共输出3个特征图,第一个特征图下采样32倍,第二个特征图下采样16倍,第三个下采样8倍。输入图像经过Darknet-53(无全连接层),再经过Yoloblock生成的特征图被当作两用,第一用为经过3*3卷积层、1*1卷积之后生成特征图一,第二用为经过1*1卷积层加上采样层,与Darnet-53网络的中间层输出结果进行拼接,产生特征图二。同样的循环之后产生特征图三。
  4. concat操作与加和操作的区别:加和操作来源于ResNet思想,将输入的特征图,与输出特征图对应维度进行相加,即 [公式] ;而concat操作源于DenseNet网络的设计思路,将特征图按照通道维度直接进行拼接,例如8*8*16的特征图与8*8*16的特征图拼接后生成8*8*32的特征图。
  5. 上采样层(upsample):作用是将小尺寸特征图通过插值等方法,生成大尺寸图像。例如使用最近邻插值算法,将8*8的图像变换为16*16。上采样层不改变特征图的通道数。

Yolo的整个网络,吸取了Resnet、Densenet、FPN的精髓,可以说是融合了目标检测当前业界最有效的全部技巧。

YOLOv3网络结构示意图(VOC数据集)
YOLOv3所用的Darknet-53模型