TowardsDataScience 博客中文翻译 2019(二百零九)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

GANs 和缺失数据插补

原文:https://towardsdatascience.com/gans-and-missing-data-imputation-815a0cbc4ece?source=collection_archive---------12-----------------------

基于生成对抗网络的缺失数据填补新方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

https://www.flickr.com/photos/crdot/6212236687/

介绍

看看 GANs 令人印象深刻的表现,他们变得如此受欢迎也就不足为奇了。它们广泛用于编辑或生成图像、安全目的以及许多其他领域,优于大多数神经网络架构。它们也有一些不太重要的应用,比如将马变成斑马,或者将城市景观渲染成 GTA 风格。

Rendering cityscape into GTA style. [Source]

看来它们被用于输入缺失数据只是时间问题。使用 GANs 处理缺失数据的想法相对较新,主要存在于研究文献中。然而,这是一种非常有前途的方法,我们可能很快就会看到用于缺失数据插补的现成 GAN 模型。事实上,Github 上现有的实现很少,我将在文章的底部提到。在本文中,我将解释缺失数据插补最流行的 GAN 架构,同时摒弃科学术语,以更简单、直观的方式进行解释😉让我们开始吧!

开始之前

在我们研究处理缺失数据的不同 GAN 架构之前,我们需要确保基础知识是正确的。

在处理真实数据集时,缺失数据是一个常见问题。我们中的一些人只能在 Kaggle 上使用近乎理想且格式良好的数据。不幸的是,现实生活中并不是这样。数据经常会丢失,它可能以三种不同的方式发生:MCAR(完全随机丢失),马尔(随机丢失)和 NMAR(非随机丢失)。如果你想了解更多细节,可以看看这篇文章。幸运的是,有各种方法可以处理缺失数据。为了深入了解现有的插补算法(例如 KNN、老鼠、MissForest)和它们各自的 Python 包,我推荐阅读我以前的文章

gan 非常适合处理缺失数据。他们很好地学习隐藏的数据分布,并且发生器和鉴别器之间的反馈回路产生非常高精度的结果。如果你觉得你需要刷新一下关于甘斯的记忆,看看下面欧文·凯里在这篇惊人文章中写的一小段话。

一个称为生成器的神经网络生成新的数据实例,而另一个称为鉴别器的神经网络评估它们的真实性。

你可以把一个 GAN 想象成造假者(发生者)和警察(鉴别者)之间的猫捉老鼠的游戏。伪造者正在学习制造假币,而警察正在学习检测假币。两个人都在学习,都在提高。造假者在不断学习制造更好的假货,而警察在检测假货方面也在不断进步。最终结果是伪造者(制造者)现在被训练来创造超现实的货币!

GAN 架构

增加

GAIN 代表生成式对抗插补网。在撰写本文时,它似乎是处理缺失数据的最流行的 GAN 架构。其背后的思想很简单:生成器获取一些缺失值的真实数据的向量,并相应地对它们进行估算。估算的数据被反馈给鉴别器,鉴别器的工作是找出最初丢失的数据。下图更详细地解释了该架构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GAIN architecture [1]

有一个屏蔽矩阵,它告诉发生器哪些值缺失或存在。随机矩阵增加了估算值的随机性(所以它们每次都不一样)。还有提示向量,提供给鉴别器,确保鉴别器强制发生器学习。

增益插补的准确性与其他最先进的插补算法进行了比较,并证明其明显优于所有算法[1]。听起来是个不错的估算,是吧?

米斯甘

MisGAN 是另一个处理缺失数据的 GAN 框架。它的架构与 GAIN 略有不同,因为它有 2 个发生器和 2 个鉴别器[2]。下图展示了 MisGAN 架构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The overall structure of the MisGAN framework [2]

发生器 Gx 产生完整的数据,发生器 Gm 产生缺失数据的掩码(它是一个二进制矩阵,其中‘1’代表非缺失数据,而‘0’代表缺失数据)。然后在鉴别器 DxDm 中对其进行比较,以检查其是否可以与真实数据矩阵 x 和真实缺失值掩码矩阵m区分开来。这里需要注意的重要一点是,在成功训练 MisGAN 架构后,我们可以分别生成完整的数据和缺失值掩码。很酷吧。

对上面的架构有一个调整,可以直接创建一个缺失数据估算器。在这种情况下,我们有可以生成完整数据的预训练生成器 Gx 和充当估算器的生成器 Gi 。它被馈送到单个鉴别器 Di 并检查 Gi 是否从带有缺失值的数据中产生令人满意的结果。下图更详细地展示了该架构。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Architecture for MisGAN imputation [2]

维甘

VIGAN 以不同的方式处理缺失数据的问题。为了向你介绍这个问题,让我们想象我们有一个实验,在这个实验中,一家医院为某个样本的病人收集神经图像,为一个完全不同的样本的病人收集大脑信号。这导致了不成对的数据,从而导致多模态(,即以不同的方式表达同一事物;图像和文字描述就是一个很好的例子)数据分析。使用这两个数据集时,患者数据没有一对一的映射。我们不能简单地将它们结合起来,因为患者在数据集中是独一无二的。它需要不同的数学建模来解决这个问题,这通常是大数据中的一个常见问题[3]。通常,您甚至不能将不同来源的数据集组合在一起(所谓的多视图数据),因为它们实在太大了。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

The missing view problem extremely limits the cross-view collaborative learning [3]

这种 GAN 架构解决了多模态或多视图数据集中缺失数据的问题。作者声称他们的解决方案是高度可扩展的,并且是将域映射与缺失数据的跨视图插补相结合的第一种方法[3]。

其背后的思想比 GAIN 和 MisGAN 更复杂,解释其背后的数学概念需要很长时间。如果你想冒险,看看这里的论文。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

It can take a while to read — you’ve been warned! [Source]

科拉根

协作 GAN 处理稍微不同类型的丢失数据。它用于缺失图像数据插补。根据论文作者的观点,该架构在图像领域优于其他 GAN 架构的优势如下:

  • 缺失数据估计更加准确
  • 单发生器架构仍然存在,这使得它更节省内存

该框架的新颖之处在于,它“将图像插补问题转化为多域图像到图像的转换任务,使得单个生成器和鉴别器网络可以使用剩余的干净数据集成功地估计缺失数据。[4]"

在我个人看来,这篇论文的措辞有点模糊,我发现很难完全理解他们是如何实现他们的 GAN 架构的。如果你觉得这个想法很有趣,可以在这里看一下最初的论文。

Github 仓库

在 Github 中快速浏览之后,我发现这些 GAN 架构已经有了一些实现!为了方便起见,我在下面列出了它们。

增益

这个库似乎是 GAIN 中最受欢迎的,它使用了 Tensorflow。它在 Github 上有> 70 颗星,但是从 2019 年 3 月开始就没有更新过。

维甘

VIGAN 已经在 PyTorch 中实现了,但是它在拥有 20 颗星的情况下似乎不是很受欢迎。回购的链接是这里的

最后几句话要说

虽然用 GANs 输入缺失数据是一个相对较新的概念,但它显示了非常有前途的结果。在不久的将来,我们可能会看到更多的 GAN 架构以更高的精度处理缺失数据。

希望你觉得我的文章有用。如果你有任何问题,请在评论中告诉我,或者随时在 LinkedIn 上与我联系。

参考

[1]增益,https://arxiv.org/abs/1806.02920

[2]米斯根,https://openreview.net/pdf?id=S1lDV3RcKm

[3]维根,https://arxiv.org/abs/1708.06724

[4]科拉甘,https://arxiv.org/abs/1901.09764

甘斯与应用 ML @ ICLR 2019

原文:https://towardsdatascience.com/gans-applied-ml-iclr-2019-20d16cade735?source=collection_archive---------21-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Ian Goodfellow filled the whole Great Hall at New Orleans.

TL;大卫:在底部。

我刚刚从 2019 年在纽奥良举行的国际学习代表大会回来,这是甘论文硕果累累的一年。在第一部分中,我讨论了图像合成(BigGAN)、音频(WaveGAN)、特征选择(KnockoffGAN)、3D、文本&表格和许多其他主题的论文。然后,本文的第二部分重点关注更实际的 ML 考虑事项。

幸运偏爱有准备的人

在去 ICLR 之前,我列了一个清单,上面列出了所有我想学的东西。这意味着一个非常忙碌的星期一——在这一点上,四个有趣的研讨会同时进行(更多研讨会在“应用 ML”部分)。这也意味着一个繁忙的星期二,组织者在这一天投入了 37 篇 GAN 论文。这意味着海报会议开始得早,结束得晚。我用一个电子表格记录了这一切。

我包含了我提到的所有论文的链接,甚至还有到直播研讨会以及全体会议的链接,其中也包含了大量的 GANs

生成的对抗部分

在这里,我想探讨这些变化,特别是讨论生成性对抗网络。正如许多人所说,这是一项令人兴奋的新技术,与大多数其他 ML 不同,它出现的时间还不到 5 年。本着之前 ICML 2018 文章的精神,我与学术界人士进行了交谈,所以你不必这样做,但鉴于内容的量,不可能再浏览每篇论文,所以我只会挑选一些主题。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Brock et al. presented even better BigGAN-deep at ICLR ‘19.

主题 1:图像合成日趋成熟

Ian Goodfellow 经常谈论2012 年的深度学习革命如何实现了机器学习应用的“寒武纪大爆发”。这是因为在任何技术领域,首要任务是让一项技术可靠地工作,并使大量下游应用成为可能。

这种情况在图像合成中有所发生。现在 BigGAN 可以可靠地生成非常多样的高保真图像,我们可以开始考虑将其应用于其他用例。一个例子是使用 BigGAN 作为增加现有训练数据的方式(即,通过合成新的数据点来人工增加数据点的数量)。尽管有的另一篇论文在 ICLR 被接受,它显示了这项技术的局限性。在这种平衡数据集的情况下,GAN 数据增加似乎对下游任务的影响有限。但这是一个被认真研究的提议,这一事实似乎是一个好迹象,但仍有许多数据增强途径未被探索。

我们可能关心的另一个下游任务是使用较少标签的图像合成。在最初的 BigGAN 中,我们使用 ImageNet 中的所有标签来合成 1000 种类型的对象。然而在另一篇 ICLR 论文中,我们可以看到同样高质量的图片,只有 10%的标签,甚至比 BigGAN 只有 20%的标签使用自我和半监督学习的结果更好。

此外,ICLR 还展示了几篇 论文,它们提出了对生成的图像进行更精细控制的有趣建议。所以现在,你一直想在照片中出现的长颈鹿,而不是你的前任,可以出现在正确的位置了。

我只是惊讶于这个领域发展的如此之快,在不到 5 年的时间里,我们已经成功制作了 1000 类 512x512 的图像,这些图像足够逼真,可以在下游应用中使用。用 Károly Zsolnai-Fehér 的话说,活着是多么美好的时光啊!

主题 2:外来数据类型/应用。

今年 ICLR 的另一个重要主题是更多“奇异”数据类型和应用的出现。我只讲几个更有趣的例子。对我来说,这似乎再次表明了 GANs 作为一个领域的日益成熟。

  • WaveGAN :是一种使用扩展卷积和 DCGAN 类架构的 GAN 的音频条件合成。
  • TimbreTron :使用 CycleGAN 将音乐从一个乐器(域)传输到另一个(域)乐器。
  • PateGAN :生成合成数据的 GAN,具有不同的隐私保证。
  • 山寨版:是一种用山寨版的 GANs 进行健壮特征选择的方法。总的来说,这篇论文将会是比较复杂的论文之一。
  • LayoutGAN :通过在 2D 空间中合理地组合不同的 UI 元素,使用 GANs 生成 UI 线框的方法。
  • 合成 GAN :通过匹配不同的 3D 对象并合成它们以产生具有真实照明和遮挡的新场景来生成真实外观合成的方法。
  • 3D 点云生成蛋白质骨架生成 & 生成标签图:这些论文超出了我的专业领域,也超出了 ICML 2018 上展示的这一广泛领域的论文,但很高兴看到这项工作仍在继续。

主题 3:理论进展

一如既往,有许多论文涉及训练的某些方面(拒绝采样相对论甘变分鉴别器瓶颈)或生成模型的某些理论性质(例如潜在空间插值的可逆性)。

虽然学者们倾向于热爱这一领域,但在 ICML 大学 18 届毕业生中,结果却有些喜忧参半。我觉得许多论文引入了大量额外的复杂性来推导一些我不认为非常有趣或不期望它们以同样的方式成为事实上的标准的属性,例如 Wasserstein GAN 或 gradient penalties。

幸运的是,在 ICLR,情况并非如此。上面的三种技术加上训练中的平均技术看起来都很简单有效,很容易成为未来艺术的标准模式。

应用机器学习

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Many industry presentations at ICLR ’19.

作为一个经常不得不担心如何生产我正在建造的系统的人。令我非常惊喜的是,甚至来自 ICLR 的研讨会组织者也认为这很重要。因此,我试图从以下研讨会中捕捉所有有趣的内容:

  • ML 中的再现性:这最终成为一个非常有用的研讨会。顺便提一下,我在那里的时候只有 7 个人,所以我想知道这说明了我们领域的状况。一般来说,我认为再现性是一个非常重要的话题,因为再现性实际上是理解已部署的 ML 系统如何运行的 0 级水平。因此,如果我们不能正确对待这一点,所有这些关于公平偏见的谈论几乎毫无意义。
  • 调试 ML :这是一个非常有用的研讨会,但遗憾的是,许多演示要么没有发布代码,要么非常学术性。我肯定会尝试研究模型断言,因为这个想法对我来说很有意义。总的来说,再次调试对我们来说非常关键,以确保我们多少理解了模型是如何构建的。从对立的例子到神经网络能够适应随机分配的标签,一切都表明我们需要更多的工具来理解深度学习。
  • 从有限的标记数据中学习:这非常有趣,因为很少的数据是常见的商业现实。克里斯托弗·雷的参与让我很受鼓舞,但是,我并不觉得有什么特别重要的收获。
  • 生成高度结构化的数据:尽管本吉奥一开始的演讲很拥挤,但我并不觉得口头报告有什么用处,尽管我强烈建议查看已被接受的论文。

结论

总的来说,我一直对 ML 的进展速度感到惊讶,学术会议也有其缺点,但如果你相应地计划和准备,你会比我参加过的任何其他会议获得更多。

TL;博士:

  • 我们生成各种各样的逼真的 512x512,这导致了进一步的应用。
  • GANs 似乎在其他数据类型中得到更多的关注,但是成熟度大约是。影像在 2016 年的位置。
  • 甚至学者们现在也更多地考虑实际问题& ML 工具——尽管他们并不总是这样称呼它。

感谢丹尼尔·杜马博士和哈帕尔·辛格博士的精彩反馈。

除了甘

原文:https://towardsdatascience.com/gans-or-d0fb38ff8ddb?source=collection_archive---------35-----------------------

在这篇博文中,我将讨论更精确地使用 GANs 的替代方法。我建议读者阅读 GANs 来理解和比较下面解释的策略。让我们开始吧。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Image by Gerd Altmann from Pixabay

我希望到现在为止,你们都非常熟悉 GANs 的概念。但是,我们仍然不能用 GANs 达到很高的精确度。如果你看过我在 GANs 上的博客,下面是最后阶段的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

中间的图像是 GAN 预测的。虽然它有点类似于目标图像,但仍然不是可接受的图像,因为预测图像中猫的眼睛不亮,猫的爪子不清晰。如果我们开始根据特征来区分猫,这些小事情就很重要了。

我们可能会在更大程度上改进结果,但 GANs 仍然无法提供足够的特征间隙,因为我们没有以这种方式训练模型。甘不在乎猫的眼睛,也不在乎猫的爪子和其他东西。

我们能摆脱甘一家吗?

Fastai 想出了一些更好的东西,恢复图像的力量。现在,当我们谈论一个更好的模型时,首先想到的是改进 loff 函数。如果我们可以增强损失函数,那么我们的模型将训练得更好,从而得到更准确的结果。所以,我们的主要目的是创建一个更好的损失函数。现在,我们还可以提出更复杂的架构设计,但这无疑是最后一个要实施的选项。

我在这里关心的事情在实时风格转换和超分辨率的感知损失中有所解释。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Image source — research paper mentioned above

  • 图像转换网络——它基本上是 GANs 的 UNet 或生成器部分。它产生输出,即预测的图像。
  • 上述类型的模型被称为生成模型。在这些类型的模型中,我们有一个下采样部分,也称为编码器,还有一个上采样部分,称为解码器。
  • Ŷ是预测图像。Yc是我们想要得出的目标图像。
  • 现在,我们将预测图像和目标图像通过预先训练的图像分类模型,如 ResNet34、VGG-16 等。这些模型在许多类别的图像上被预先训练,并且它们对输入图像进行分类。
  • 通常,它的输出会告诉你,“嘿,这个生成的东西是狗,猫,飞机,还是别的什么。”但是在最终分类的过程中,它会经过许多不同的层次。在这种情况下,他们使用相同的格网大小对所有图层进行了颜色编码,并使用相同的颜色对要素地图进行了颜色编码。所以每次我们改变颜色,我们就改变了网格的大小。所以有一个跨步二卷积,或者在 VGG 的情况下,他们使用最大池层。
  • 现在,我们可以做的是,我们可以比较中间层的激活,然后找出预测图像层和目标图像层之间的像素损失,而不是比较最后几层。如果我们可以这样做,那么我们就可以引导模型的中间层变得更加具体和准确。
  • 让我们用一个例子来理解这个比较中间层的概念。

max-pooling 层之前的输出中的层的结构是 2162828。这意味着我们有 216 个通道,图像大小为 28*28。每个通道告诉我们图像中不同种类的特征。可能第 100 个频道比较了猫形象的眼球。如果我们可以比较预测图像和目标图像的第 100 层,那么我们的模型将为期望的结果准备得更好。

  • 在我们的例子中,特征图会说,“这里有眼球(在目标图像中),但在生成的版本中没有,所以请多训练,做得更好。制造更好的眼球。”这就是我们的想法。这就是 fastai 所说的特征损失或 Johnson 等人所说的感知损失。

这就是我们如何在不使用 GANs 的情况下改进图像恢复的方法。

让我们了解损失在实际中是如何起作用的。我正在使用 fastai 库的代码。

class class **FeatureLoss**(nn.Module):
    def __init__(self, m_feat, layer_ids, layer_wgts):
        super().__init__()
        **self.m_feat** = m_feat
        **self.loss_features** = [self.m_feat[i] for i in layer_ids]
        **self.hooks** = hook_outputs(self.loss_features, detach=False)
        **self.wgts** = layer_wgtsdef **make_features**(self, x, clone=False):
        **self.m_feat**(x)
        **return** [(o.clone() if clone else o) for o in self.hooks.stored]

    def **forward**(self, input, target):
        **out_feat** = self.make_features(target, clone=True)
        **in_feat** = self.make_features(input)
        **self.feat_losses** = [base_loss(input,target)]
        **self.feat_losses** += [base_loss(f_in, f_out)*w
                             for f_in, f_out, w in zip(in_feat, out_feat, self.wgts)]
        **self.feat_losses** += [base_loss(gram_matrix(f_in), gram_matrix(f_out))*w**2 * 5e3
                             for f_in, f_out, w in zip(in_feat, out_feat, self.wgts)]
       ** self.metrics** = dict(zip(self.metric_names, self.feat_losses))
        **return sum(self.feat_losses)**

    def __del__(self): self.hooks.remove()(nn.Module):
  • m_feat —是预训练的模型;在我们的例子中,我们使用的是 VGG。
  • layer_ids —这是变化前的图层 id 列表。这是我们在最大池层数之前的层数列表。毫不奇怪,所有这些都是 ReLU 的。这些是我们想要抓住一些特征的地方。只是一串身份证。
  • out_feat —它包括目标图像中 layer_ids 中提到的相关层的列表。
  • in_feat —由预测图像的 layer_ids 中指定的相关图层列表组成。
  • self.feat_losses —它是发生在 out_feat 和 in_feat 层的损失以及预测图像和目标图像之间的基本 MSE 损失的总和,也在 GANs 中定义。

这就是我们如何改进损失函数。在对模型进行了相当长时间的训练后,我们得到了下面的输出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在,我们预测图像,它与目标图像非常相似。目前,与目标图像相比,我们预测的猫具有更合理的特征。

值得注意的是,这就是我们如何使用不同于 GANs 的东西。如上所述,我建议用户更多地探索 fastai 库,以了解该方法背后的更多信息。

GANs 与自动编码器:深度生成模型的比较

原文:https://towardsdatascience.com/gans-vs-autoencoders-comparison-of-deep-generative-models-985cf15936ea?source=collection_archive---------0-----------------------

想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是你新的最好的朋友。

生成对抗网络是过去 10 年机器学习中最有趣的想法——扬·勒村,脸书人工智能研究中心主任

本教程的第 1 部分可以在这里找到:

[## 图灵学习和 GANs 简介

想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是…

towardsdatascience.com](/comprehensive-introduction-to-turing-learning-and-gans-part-1-81f6d02e644d)

本教程的第 2 部分可以在这里找到:

[## GANs 中的高级主题

想把马变成斑马?制作 DIY 动漫人物或名人?生成敌对网络是…

towardsdatascience.com](/comprehensive-introduction-to-turing-learning-and-gans-part-2-fd8e4a70775)

这些文章基于哈佛大学关于 AC209b 的讲座,主要归功于哈佛大学 IACS 系的讲师 Pavlos Protopapas

这是专门使用生成性对抗网络创建深度生成模型的三部分教程的第三部分。这是上一个关于变型自动编码器主题的自然延伸(在这里找到)。我们将看到,与可变自动编码器相比,GANs 作为深度生成模型通常更优越。然而,众所周知,它们很难使用,并且需要大量的数据和调整。我们还将研究一种称为 VAE-GAN 的混合 GAN 模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Taxonomy of deep generative models. This article’s focus is on GANs.

教程的这一部分将主要是变分自动编码器(VAEs)的编码实现,也将向读者展示如何制作 VAEs 甘。

  • CelebA 数据集的 VAE
  • 西里巴数据集的 DC-甘
  • 动漫数据集 DC-甘
  • 动漫数据集 VAE-甘

我强烈建议读者在进一步阅读之前,至少先阅读一下 GAN 教程的第 1 部分,以及我的自动编码器变化演练,否则,读者可能对实现没有太多的了解。

所有相关代码现在都可以在我的 GitHub 存储库中找到:

[## 龙熊先生/甘-教程

GitHub 是超过 5000 万开发人员的家园,他们一起工作来托管和审查代码、管理项目和构建…

github.com](https://github.com/mrdragonbear/GAN-Tutorial)

我们开始吧!

CelebA 数据集的 VAE

CelebFaces 属性数据集(CelebA)是一个大规模的人脸属性数据集,拥有超过 20 万张名人图像,每张图像都有 40 个属性注释。该数据集中的图像覆盖了较大的姿态变化和背景混乱。西里巴有很大的多样性,数量大,注释丰富,包括

  • 10177 个身份,
  • 202,599 个面部图像,以及
  • 5 个标志位置,每幅图像 40 个二元属性注释。

你可以从 Kaggle 这里下载数据集:

[## 名人面孔属性(CelebA)数据集

超过 200,000 张名人图片,带有 40 个二元属性注释

www.kaggle.com](https://www.kaggle.com/jessicali9530/celeba-dataset)

第一步是导入所有必要的函数并提取数据。

进口

import shutil
import errno
import zipfile
import os
import matplotlib.pyplot as plt

提取数据

# Only run once to unzip images
zip_ref = zipfile.ZipFile('img_align_celeba.zip','r')
zip_ref.extractall()
zip_ref.close()

自定义图像生成器

这一步可能是大多数读者以前没有用过的。由于我们的数据非常庞大,可能无法将数据集加载到 Jupyter 笔记本的内存中。在处理大型数据集时,这是一个很正常的问题。

解决这一问题的方法是使用流生成器,它将成批的数据(本例中是图像)按顺序流入内存,从而限制该函数所需的内存量。需要注意的是,理解和编写它们有点复杂,因为它们需要对计算机内存、GPU 架构等有合理的理解。

# data generator
# source from https://medium.com/@ensembledme/writing-custom-keras-generators-fe815d992c5a
from skimage.io import imread

def get_input(path):
    """get specific image from path"""
    img = imread(path)
    return img

def get_output(path, label_file = None):
    """get all the labels relative to the image of path"""
    img_id = path.split('/')[-1]
    labels = label_file.loc[img_id].values
    return labels

def preprocess_input(img):
    # convert between 0 and 1
    return img.astype('float32') / 127.5 -1

def image_generator(files, label_file, batch_size = 32):
    while True:

        batch_paths = np.random.choice(a = files, size = batch_size)
        batch_input = []
        batch_output = []

        for input_path in batch_paths:

            input = get_input(input_path)
            input = preprocess_input(input)
            output = get_output(input_path, label_file = label_file)
            batch_input += [input]
            batch_output += [output]
        batch_x = np.array(batch_input)
        batch_y = np.array(batch_output)

        yield batch_x, batch_y

def auto_encoder_generator(files, batch_size = 32):
    while True:
        batch_paths = np.random.choice(a = files, size = batch_size)
        batch_input = []
        batch_output = []

        for input_path in batch_paths:
            input = get_input(input_path)
            input = preprocess_input(input)
            output = input
            batch_input += [input]
            batch_output += [output]
        batch_x = np.array(batch_input)
        batch_y = np.array(batch_output)

        yield batch_x, batch_y

关于用 Keras 编写定制生成器的更多信息,我在上面的代码中引用了一篇很好的文章:

[## 编写定制的 Keras 生成器

使用 Keras 生成器背后的想法是在训练期间动态地获得成批的输入和相应的输出…

medium.com](https://medium.com/@ensembledme/writing-custom-keras-generators-fe815d992c5a)

加载属性数据

我们不仅有这个数据集的图像,而且每个图像还有一个与名人的各个方面相对应的属性列表。例如,有描述名人是否涂口红或戴帽子、他们是否年轻、他们是否有黑头发等的属性。

# now load attribute

# 1.A.2
import pandas as pd
attr = pd.read_csv('list_attr_celeba.csv')
attr = attr.set_index('image_id')

# check if attribute successful loaded
attr.describe()

完成发电机的制作

现在我们完成了发电机的制作。我们将图像名称长度设置为 6,因为我们的数据集中有 6 位数的图像。阅读定制 Keras 生成器文章后,这部分代码应该有意义。

import numpy as np
from sklearn.model_selection import train_test_splitIMG_NAME_LENGTH = 6file_path = "img_align_celeba/"
img_id = np.arange(1,len(attr.index)+1)
img_path = []
for i in range(len(img_id)):
    img_path.append(file_path + (IMG_NAME_LENGTH - len(str(img_id[i])))*'0' + str(img_id[i]) + '.jpg')# pick 80% as training set and 20% as validation set
train_path = img_path[:int((0.8)*len(img_path))]
val_path = img_path[int((0.8)*len(img_path)):]train_generator = auto_encoder_generator(train_path,32)
val_generator = auto_encoder_generator(val_path,32)

我们现在可以选择三个图像,并检查属性是否有意义。

fig, ax = plt.subplots(1, 3, figsize=(12, 4))
for i in range(3):    
    ax[i].imshow(get_input(img_path[i]))
    ax[i].axis('off')
    ax[i].set_title(img_path[i][-10:])
plt.show()

attr.iloc[:3]

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Three random images along with some of their attributes.

建立和训练一个 VAE 模型

首先,我们将为名人面孔数据集创建并编译一个卷积 VAE 模型(包括编码器和解码器)。

更多进口商品

from keras.models import Sequential, Model
from keras.layers import Dropout, Flatten, Dense, Conv2D, MaxPooling2D, Input, Reshape, UpSampling2D, InputLayer, Lambda, ZeroPadding2D, Cropping2D, Conv2DTranspose, BatchNormalization
from keras.utils import np_utils, to_categorical
from keras.losses import binary_crossentropy
from keras import backend as K,objectives
from keras.losses import mse, binary_crossentropy

模型架构

现在,我们可以创建并总结该模型。

b_size = 128
n_size = 512
def sampling(args):
    z_mean, z_log_sigma = args
    epsilon = K.random_normal(shape = (n_size,) , mean = 0, stddev = 1)
    return z_mean + K.exp(z_log_sigma/2) * epsilon

def build_conv_vae(input_shape, bottleneck_size, sampling, batch_size = 32):

    # ENCODER
    input = Input(shape=(input_shape[0],input_shape[1],input_shape[2]))
    x = Conv2D(32,(3,3),activation = 'relu', padding = 'same')(input)    
    x = BatchNormalization()(x)
    x = MaxPooling2D((2,2), padding ='same')(x)
    x = Conv2D(64,(3,3),activation = 'relu', padding = 'same')(x)
    x = BatchNormalization()(x)
    x = MaxPooling2D((2,2), padding ='same')(x)
    x = Conv2D(128,(3,3), activation = 'relu', padding = 'same')(x)
    x = BatchNormalization()(x)
    x = MaxPooling2D((2,2), padding ='same')(x)
    x = Conv2D(256,(3,3), activation = 'relu', padding = 'same')(x)
    x = BatchNormalization()(x)
    x = MaxPooling2D((2,2), padding ='same')(x)

    # Latent Variable Calculation
    shape = K.int_shape(x)
    flatten_1 = Flatten()(x)
    dense_1 = Dense(bottleneck_size, name='z_mean')(flatten_1)
    z_mean = BatchNormalization()(dense_1)
    flatten_2 = Flatten()(x)
    dense_2 = Dense(bottleneck_size, name ='z_log_sigma')(flatten_2)
    z_log_sigma = BatchNormalization()(dense_2)
    z = Lambda(sampling)([z_mean, z_log_sigma])
    encoder = Model(input, [z_mean, z_log_sigma, z], name = 'encoder')

    # DECODER
    latent_input = Input(shape=(bottleneck_size,), name = 'decoder_input')
    x = Dense(shape[1]*shape[2]*shape[3])(latent_input)
    x = Reshape((shape[1],shape[2],shape[3]))(x)
    x = UpSampling2D((2,2))(x)
    x = Cropping2D([[0,0],[0,1]])(x)
    x = Conv2DTranspose(256,(3,3), activation = 'relu', padding = 'same')(x)
    x = BatchNormalization()(x)
    x = UpSampling2D((2,2))(x)
    x = Cropping2D([[0,1],[0,1]])(x)
    x = Conv2DTranspose(128,(3,3), activation = 'relu', padding = 'same')(x)
    x = BatchNormalization()(x)
    x = UpSampling2D((2,2))(x)
    x = Cropping2D([[0,1],[0,1]])(x)
    x = Conv2DTranspose(64,(3,3), activation = 'relu', padding = 'same')(x)
    x = BatchNormalization()(x)
    x = UpSampling2D((2,2))(x)
    x = Conv2DTranspose(32,(3,3), activation = 'relu', padding = 'same')(x)
    x = BatchNormalization()(x)
    output = Conv2DTranspose(3,(3,3), activation = 'tanh', padding ='same')(x)
    decoder = Model(latent_input, output, name = 'decoder')

    output_2 = decoder(encoder(input)[2])
    vae = Model(input, output_2, name ='vae')
    return vae, encoder, decoder, z_mean, z_log_sigma

vae_2, encoder, decoder, z_mean, z_log_sigma = build_conv_vae(img_sample.shape, n_size, sampling, batch_size = b_size)
print("encoder summary:")
encoder.summary()
print("decoder summary:")
decoder.summary()
print("vae summary:")
vae_2.summary()

定义 VAE 损失

def vae_loss(input_img, output):
    # Compute error in reconstruction
    reconstruction_loss = mse(K.flatten(input_img) , K.flatten(output))

    # Compute the KL Divergence regularization term
    kl_loss = - 0.5 * K.sum(1 + z_log_sigma - K.square(z_mean) - K.exp(z_log_sigma), axis = -1)

    # Return the average loss over all images in batch
    total_loss = (reconstruction_loss + 0.0001 * kl_loss)    
    return total_loss

编译模型

vae_2.compile(optimizer='rmsprop', loss= vae_loss)
encoder.compile(optimizer = 'rmsprop', loss = vae_loss)
decoder.compile(optimizer = 'rmsprop', loss = vae_loss)

训练模型

vae_2.fit_generator(train_generator, steps_per_epoch = 4000, validation_data = val_generator, epochs=7, validation_steps= 500)

我们随机选择训练集的一些图像,通过编码器运行它们以参数化潜在代码,然后用解码器重建图像。

import random
x_test = []
for i in range(64):
    x_test.append(get_input(img_path[random.randint(0,len(img_id))]))
x_test = np.array(x_test)
figure_Decoded = vae_2.predict(x_test.astype('float32')/127.5 -1, batch_size = b_size)
figure_original = x_test[0]
figure_decoded = (figure_Decoded[0]+1)/2
for i in range(4):
    plt.axis('off')
    plt.subplot(2,4,1+i*2)
    plt.imshow(x_test[i])
    plt.axis('off')
    plt.subplot(2,4,2 + i*2)
    plt.imshow((figure_Decoded[i]+1)/2)
    plt.axis('off')
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Random samples from training set compared to their VAE reconstruction.

请注意,重建的图像与原始版本有相似之处。然而,新的图像有点模糊,这是一个已知的 VAEs 现象。这被假设是由于变分推理优化了似然性的下限,而不是实际的似然性本身。

潜在空间表征

我们可以选择两个不同属性的图像,并绘制它们的潜在空间表示。请注意,我们可以看到潜在代码之间的一些差异,我们可以假设解释原始图像之间的差异。

# Choose two images of different attributes, and plot the original and latent space of it

x_test1 = []
for i in range(64):
    x_test1.append(get_input(img_path[np.random.randint(0,len(img_id))]))
x_test1 = np.array(x_test)
x_test_encoded = np.array(encoder.predict(x_test1/127.5-1, batch_size = b_size))
figure_original_1 = x_test[0]
figure_original_2 = x_test[1]
Encoded1 = (x_test_encoded[0,0,:].reshape(32, 16,)+1)/2 
Encoded2 = (x_test_encoded[0,1,:].reshape(32, 16)+1)/2

plt.figure(figsize=(8, 8))
plt.subplot(2,2,1)
plt.imshow(figure_original_1)
plt.subplot(2,2,2)
plt.imshow(Encoded1)
plt.subplot(2,2,3)
plt.imshow(figure_original_2)
plt.subplot(2,2,4)
plt.imshow(Encoded2)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从潜空间取样

我们可以随机抽取 15 个潜在代码,解码后生成新的名人面孔。我们可以从这个表示中看到,由我们的模型生成的图像与我们训练集中的那些图像具有非常相似的风格,并且它也具有良好的真实性和变化性。

# We randomly generated 15 images from 15 series of noise informationn = 3
m = 5
digit_size1 = 218
digit_size2 = 178
figure = np.zeros((digit_size1 * n, digit_size2 * m,3))

for i in range(3):
    for j in range(5):
        z_sample = np.random.rand(1,512)
        x_decoded = decoder.predict([z_sample])
        figure[i * digit_size1: (i + 1) * digit_size1,
               j * digit_size2: (j + 1) * digit_size2,:] = (x_decoded[0]+1)/2 plt.figure(figsize=(10, 10))
plt.imshow(figure)
plt.show()

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

所以看起来我们的 VAE 模式并不是特别好。如果有更多的时间和更好地选择超参数等等,我们可能会取得比这更好的结果。

现在让我们将这个结果与相同数据集上的 DC-甘进行比较。

CelebA 数据集上的 DC-甘

因为我们已经设置了流生成器,所以没有太多的工作要做来启动和运行 DC-甘模型。

# Create and compile a DC-GAN model, and print the summary

from keras.utils import np_utils
from keras.models import Sequential, Model
from keras.layers import Input, Dense, Dropout, Activation, Flatten, LeakyReLU,\
      BatchNormalization, Conv2DTranspose, Conv2D, Reshape
from keras.layers.advanced_activations import LeakyReLU
from keras.optimizers import Adam, RMSprop
from keras.initializers import RandomNormal
import numpy as np
import matplotlib.pyplot as plt
import random
from tqdm import tqdm_notebook
from scipy.misc import imresize

def generator_model(latent_dim=100, leaky_alpha=0.2, init_stddev=0.02):

    g = Sequential()
    g.add(Dense(4*4*512, input_shape=(latent_dim,),
                kernel_initializer=RandomNormal(stddev=init_stddev)))
    g.add(Reshape(target_shape=(4, 4, 512)))
    g.add(BatchNormalization())
    g.add(Activation(LeakyReLU(alpha=leaky_alpha)))
    g.add(Conv2DTranspose(256, kernel_size=5, strides=2, padding='same',
                kernel_initializer=RandomNormal(stddev=init_stddev)))
    g.add(BatchNormalization())
    g.add(Activation(LeakyReLU(alpha=leaky_alpha)))
    g.add(Conv2DTranspose(128, kernel_size=5, strides=2, padding='same', 
                kernel_initializer=RandomNormal(stddev=init_stddev)))
    g.add(BatchNormalization())
    g.add(Activation(LeakyReLU(alpha=leaky_alpha)))
    g.add(Conv2DTranspose(3, kernel_size=4, strides=2, padding='same', 
                kernel_initializer=RandomNormal(stddev=init_stddev)))
    g.add(Activation('tanh'))
    g.summary()
    #g.compile(loss='binary_crossentropy', optimizer=Adam(lr=0.0001, beta_1=0.5), metrics=['accuracy'])
    return g

def discriminator_model(leaky_alpha=0.2, init_stddev=0.02):

    d = Sequential()
    d.add(Conv2D(64, kernel_size=5, strides=2, padding='same', 
               kernel_initializer=RandomNormal(stddev=init_stddev),
               input_shape=(32, 32, 3)))
    d.add(Activation(LeakyReLU(alpha=leaky_alpha)))
    d.add(Conv2D(128, kernel_size=5, strides=2, padding='same', 
               kernel_initializer=RandomNormal(stddev=init_stddev)))
    d.add(BatchNormalization())
    d.add(Activation(LeakyReLU(alpha=leaky_alpha)))
    d.add(Conv2D(256, kernel_size=5, strides=2, padding='same', 
               kernel_initializer=RandomNormal(stddev=init_stddev)))
    d.add(BatchNormalization())
    d.add(Activation(LeakyReLU(alpha=leaky_alpha)))
    d.add(Flatten())
    d.add(Dense(1, kernel_initializer=RandomNormal(stddev=init_stddev)))
    d.add(Activation('sigmoid'))
    d.summary()
    return d

def DCGAN(sample_size=100):
    # Generator
    g = generator_model(sample_size, 0.2, 0.02)

    # Discriminator
    d = discriminator_model(0.2, 0.02)
    d.compile(optimizer=Adam(lr=0.001, beta_1=0.5), loss='binary_crossentropy')
    d.trainable = False
    # GAN
    gan = Sequential([g, d])
    gan.compile(optimizer=Adam(lr=0.0001, beta_1=0.5), loss='binary_crossentropy')

    return gan, g, d

以上代码只是针对发生器和鉴别器网络的架构。将这种编码 GAN 的方法与我在第 2 部分中使用的方法进行比较是一个好主意,您可以看到这种方法不太清晰,并且我们没有定义全局参数,因此有许多地方我们可能会有潜在的错误。

现在,我们定义了一些函数来简化我们的工作,这些函数主要用于图像的预处理和绘图,以帮助我们分析网络输出。

def load_image(filename, size=(32, 32)):
    img = plt.imread(filename)
    # crop
    rows, cols = img.shape[:2]
    crop_r, crop_c = 150, 150
    start_row, start_col = (rows - crop_r) // 2, (cols - crop_c) // 2
    end_row, end_col = rows - start_row, cols - start_row
    img = img[start_row:end_row, start_col:end_col, :]
    # resize
    img = imresize(img, size)
    return img

def preprocess(x):
    return (x/255)*2-1

def deprocess(x):
    return np.uint8((x+1)/2*255)

def make_labels(size):
    return np.ones([size, 1]), np.zeros([size, 1])  

def show_losses(losses):
    losses = np.array(losses)

    fig, ax = plt.subplots()
    plt.plot(losses.T[0], label='Discriminator')
    plt.plot(losses.T[1], label='Generator')
    plt.title("Validation Losses")
    plt.legend()
    plt.show()

def show_images(generated_images):
    n_images = len(generated_images)
    cols = 5
    rows = n_images//cols

    plt.figure(figsize=(8, 6))
    for i in range(n_images):
        img = deprocess(generated_images[i])
        ax = plt.subplot(rows, cols, i+1)
        plt.imshow(img)
        plt.xticks([])
        plt.yticks([])
    plt.tight_layout()
    plt.show()

训练模型

我们现在定义训练函数。正如我们之前所做的,请注意,我们在将鉴别器设置为可训练和不可训练之间进行了切换(我们在第 2 部分中隐式地这样做了)。

def train(sample_size=100, epochs=3, batch_size=128, eval_size=16, smooth=0.1): batchCount=len(train_path)//batch_size
    y_train_real, y_train_fake = make_labels(batch_size)
    y_eval_real,  y_eval_fake  = make_labels(eval_size)

    # create a GAN, a generator and a discriminator
    gan, g, d = DCGAN(sample_size)

    losses = [] for e in range(epochs):
        print('-'*15, 'Epoch %d' % (e+1), '-'*15)
        for i in tqdm_notebook(range(batchCount)):

            path_batch = train_path[i*batch_size:(i+1)*batch_size]
            image_batch = np.array([preprocess(load_image(filename)) for filename in path_batch])

            noise = np.random.normal(0, 1, size=(batch_size, noise_dim))
            generated_images = g.predict_on_batch(noise) # Train discriminator on generated images
            d.trainable = True
            d.train_on_batch(image_batch, y_train_real*(1-smooth))
            d.train_on_batch(generated_images, y_train_fake) # Train generator
            d.trainable = False
            g_loss=gan.train_on_batch(noise, y_train_real)

        # evaluate
        test_path = np.array(val_path)[np.random.choice(len(val_path), eval_size, replace=False)]
        x_eval_real = np.array([preprocess(load_image(filename)) for filename in test_path]) noise = np.random.normal(loc=0, scale=1, size=(eval_size, sample_size))
        x_eval_fake = g.predict_on_batch(noise)

        d_loss  = d.test_on_batch(x_eval_real, y_eval_real)
        d_loss += d.test_on_batch(x_eval_fake, y_eval_fake)
        g_loss  = gan.test_on_batch(noise, y_eval_real)

        losses.append((d_loss/2, g_loss))

        print("Epoch: {:>3}/{} Discriminator Loss: {:>6.4f} Generator Loss: {:>6.4f}".format(
            e+1, epochs, d_loss, g_loss))  

        show_images(x_eval_fake[:10])

    # show the result
    show_losses(losses)
    show_images(g.predict(np.random.normal(loc=0, scale=1, size=(15, sample_size))))    
    return gnoise_dim=100
train()

该函数的输出将为我们提供每个时期的以下输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

它还将绘制鉴别器和发生器的验证损失。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

生成的图像看起来很合理。在这里,我们可以看到我们的模型表现得足够好,尽管图像质量不如训练集中的图像质量好(因为我们对图像进行了整形,使其变得更小,并使它们比原始图像更模糊)。但是,它们足够生动,可以创建有效的人脸,并且这些人脸足够接近现实。此外,与 VAE 制作的图像相比,这些图像更有创意,看起来更真实。

所以看起来 GAN 在这种情况下表现更好。现在,让我们尝试一个新的数据集,看看 GAN 与混合变体 VAE-GAN 相比表现如何。

动漫数据集

在这一节中,我们将使用 GAN 以及另一种特殊形式的 GAN,即 VAE-GAN,来生成与动画数据集风格相同的人脸。术语 VAE-甘首先由 Larsen 等人使用。al 在他们的论文“使用学习的相似性度量进行像素以外的自动编码”。VAE-甘模型与甘模型的区别在于它们的生成器是变异自动编码器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

VAE-GAN architecture. Source: https://arxiv.org/abs/1512.09300

首先,我们将重点放在 DC-甘。动漫数据集由超过 20K 张 64x64 图像形式的动漫头像组成。我们还需要创建另一个 Keras 定制数据生成器。该数据集的链接可在此处找到:

[## McKinsey 666/动漫人脸数据集

🖼收集了高质量的动漫面孔。为 Mckinsey666/Anime-Face-Dataset 开发做出贡献,创建一个…

github.com](https://github.com/Mckinsey666/Anime-Face-Dataset)

动漫数据集上的甘

我们需要做的第一件事是创建动漫目录并下载数据。这可以通过上面的链接来完成。在继续之前检查数据总是好的做法,所以我们现在就这样做。

from skimage import io
import matplotlib.pyplot as plt

filePath='anime-faces/data/'
imgSets=[]

for i in range(1,20001):
    imgName=filePath+str(i)+'.png'
    imgSets.append(io.imread(imgName))

plt.imshow(imgSets[1234])
plt.axis('off')
plt.show()

我们现在创建并编译我们的 DC-甘模型。

# Create and compile a DC-GAN modelfrom keras.models import Sequential, Model
from keras.layers import Input, Dense, Dropout, Activation, \
    Flatten, LeakyReLU, BatchNormalization, Conv2DTranspose, Conv2D, Reshape
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import UpSampling2D
from keras.optimizers import Adam, RMSprop,SGD
from keras.initializers import RandomNormalimport numpy as np
import matplotlib.pyplot as plt
import os, glob
from PIL import Image
from tqdm import tqdm_notebook image_shape = (64, 64, 3)
#noise_shape = (100,)
Noise_dim = 128
img_rows = 64
img_cols = 64
channels = 3def generator_model(latent_dim=100, leaky_alpha=0.2):
    model = Sequential()

    # layer1 (None,500)>>(None,128*16*16)
    model.add(Dense(128 * 16 * 16, activation="relu", input_shape=(Noise_dim,)))

    # (None,16*16*128)>>(None,16,16,128)
    model.add(Reshape((16, 16, 128)))

   # (None,16,16,128)>>(None,32,32,128)
    model.add(UpSampling2D())
    model.add(Conv2D(256, kernel_size=3, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Activation("relu")) #(None,32,32,128)>>(None,64,64,128)
    model.add(UpSampling2D())

    # (None,64,64,128)>>(None,64,64,64)
    model.add(Conv2D(128, kernel_size=3, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Activation("relu")) # (None,64,64,128)>>(None,64,64,32) model.add(Conv2D(32, kernel_size=3, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Activation("relu"))

    # (None,64,64,32)>>(None,64,64,3)
    model.add(Conv2D(channels, kernel_size=3, padding="same"))
    model.add(Activation("tanh")) model.summary()
    model.compile(loss='binary_crossentropy', optimizer=Adam(lr=0.0001, beta_1=0.5), metrics=['accuracy'])
    return model def discriminator_model(leaky_alpha=0.2, dropRate=0.3):
    model = Sequential()

    # layer1 (None,64,64,3)>>(None,32,32,32)
    model.add(Conv2D(32, kernel_size=3, strides=2, input_shape=image_shape, padding="same"))
    model.add(LeakyReLU(alpha=leaky_alpha))
    model.add(Dropout(dropRate)) # layer2 (None,32,32,32)>>(None,16,16,64)
    model.add(Conv2D(64, kernel_size=3, strides=2, padding="same")) # model.add(ZeroPadding2D(padding=((0, 1), (0, 1))))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=leaky_alpha))
    model.add(Dropout(dropRate)) # (None,16,16,64)>>(None,8,8,128)
    model.add(Conv2D(128, kernel_size=3, strides=2, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(dropRate)) # (None,8,8,128)>>(None,8,8,256)
    model.add(Conv2D(256, kernel_size=3, strides=1, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(dropRate)) # (None,8,8,256)>>(None,8,8,64)
    model.add(Conv2D(64, kernel_size=3, strides=1, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(dropRate))

    # (None,8,8,64)
    model.add(Flatten())
    model.add(Dense(1, activation='sigmoid')) model.summary() sgd=SGD(lr=0.0002)
    model.compile(loss='binary_crossentropy', optimizer=Adam(lr=0.0001, beta_1=0.5), metrics=['accuracy'])
    return model def DCGAN(sample_size=Noise_dim):
    # generator
    g = generator_model(sample_size, 0.2) # discriminator
    d = discriminator_model(0.2)
    d.trainable = False
    # GAN
    gan = Sequential([g, d])

    sgd=SGD()
    gan.compile(optimizer=Adam(lr=0.0001, beta_1=0.5), loss='binary_crossentropy')
    return gan, g, d def get_image(image_path, width, height, mode):
    image = Image.open(image_path)
    #print(image.size) return np.array(image.convert(mode)) def get_batch(image_files, width, height, mode):
    data_batch = np.array([get_image(sample_file, width, height, mode) \
                           for sample_file in image_files])
    return data_batch def show_imgs(generator,epoch):
    row=3
    col = 5
    noise = np.random.normal(0, 1, (row * col, Noise_dim))
    gen_imgs = generator.predict(noise) # Rescale images 0 - 1
    gen_imgs = 0.5 * gen_imgs + 0.5 fig, axs = plt.subplots(row, col)
    #fig.suptitle("DCGAN: Generated digits", fontsize=12)
    cnt = 0 for i in range(row):
        for j in range(col):
            axs[i, j].imshow(gen_imgs[cnt, :, :, :])
            axs[i, j].axis('off')
            cnt += 1 #plt.close()
    plt.show()

我们现在可以在动画数据集上训练模型。我们将以两种不同的方式来实现这一点,第一种方式将涉及以 1:1 的训练时间比例来训练鉴别器和生成器。

# Training the discriminator and generator with the 1:1 proportion of training timesdef train(epochs=30, batchSize=128):
    filePath = r'anime-faces/data/' X_train = get_batch(glob.glob(os.path.join(filePath, '*.png'))[:20000], 64, 64, 'RGB')
    X_train = (X_train.astype(np.float32) - 127.5) / 127.5 halfSize = int(batchSize / 2)
    batchCount=int(len(X_train)/batchSize) dLossReal = []
    dLossFake = []
    gLossLogs = [] gan, generator, discriminator = DCGAN(Noise_dim) for e in range(epochs):
        for i in tqdm_notebook(range(batchCount)):
            index = np.random.randint(0, X_train.shape[0], halfSize)
            images = X_train[index] noise = np.random.normal(0, 1, (halfSize, Noise_dim))
            genImages = generator.predict(noise) # one-sided labels
            discriminator.trainable = True
            dLossR = discriminator.train_on_batch(images, np.ones([halfSize, 1]))
            dLossF = discriminator.train_on_batch(genImages, np.zeros([halfSize, 1]))
            dLoss = np.add(dLossF, dLossR) * 0.5
            discriminator.trainable = False noise = np.random.normal(0, 1, (batchSize, Noise_dim))
            gLoss = gan.train_on_batch(noise, np.ones([batchSize, 1])) dLossReal.append([e, dLoss[0]])
        dLossFake.append([e, dLoss[1]])
        gLossLogs.append([e, gLoss]) dLossRealArr = np.array(dLossReal)
        dLossFakeArr = np.array(dLossFake)
        gLossLogsArr = np.array(gLossLogs)        # At the end of training plot the losses vs epochs
        show_imgs(generator, e) plt.plot(dLossRealArr[:, 0], dLossRealArr[:, 1], label="Discriminator Loss - Real")
    plt.plot(dLossFakeArr[:, 0], dLossFakeArr[:, 1], label="Discriminator Loss - Fake")
    plt.plot(gLossLogsArr[:, 0], gLossLogsArr[:, 1], label="Generator Loss")
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    plt.title('GAN')
    plt.grid(True)
    plt.show()

    return gan, generator, discriminator GAN,Generator,Discriminator=train(epochs=20, batchSize=128)  
train(epochs=1000, batchSize=128, plotInternal=200)

输出现在将开始打印一系列的动漫人物。它们起初非常粗糙,随着时间的推移逐渐变得越来越明显。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们还将得到发电机和鉴频器损耗函数的曲线图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

现在我们将做同样的事情,但是用不同的训练时间来训练鉴别器和生成器,看看效果如何。

在继续之前,最好将模型的权重保存在某个地方,这样您就不需要再次运行整个训练,而是可以将权重加载到网络中。

为了节省重量:

discriminator.save_weights('/content/gdrive/My Drive/discriminator_DCGAN_lr0.0001_deepgenerator+proportion2.h5')
gan.save_weights('/content/gdrive/My Drive/gan_DCGAN_lr0.0001_deepgenerator+proportion2.h5')
generator.save_weights('/content/gdrive/My Drive/generator_DCGAN_lr0.0001_deepgenerator+proportion2.h5')

要加载砝码:

discriminator.load_weights('/content/gdrive/My Drive/discriminator_DCGAN_lr0.0001_deepgenerator+proportion2.h5')
gan.load_weights('/content/gdrive/My Drive/gan_DCGAN_lr0.0001_deepgenerator+proportion2.h5')
generator.load_weights('/content/gdrive/My Drive/generator_DCGAN_lr0.0001_deepgenerator+proportion2.h5')

现在,我们转到第二个网络实施,而不用担心比之前的网络节省成本。

# Train the discriminator and generator separately and with different training timesdef train(epochs=300, batchSize=128, plotInternal=50):
    gLoss = 1
    filePath = r'anime-faces/data/'

    X_train = get_batch(glob.glob(os.path.join(filePath,'*.png'))[:20000],64,64,'RGB')
    X_train=(X_train.astype(np.float32)-127.5)/127.5
    halfSize= int (batchSize/2) dLossReal=[]
    dLossFake=[]
    gLossLogs=[]    for e in range(epochs):
        index=np.random.randint(0,X_train.shape[0],halfSize)
        images=X_train[index] noise=np.random.normal(0,1,(halfSize,Noise_dim))
        genImages=generator.predict(noise)

        if e < int(epochs*0.5):    
            #one-sided labels
            discriminator.trainable=True
            dLossR=discriminator.train_on_batch(images,np.ones([halfSize,1]))
            dLossF=discriminator.train_on_batch(genImages,np.zeros([halfSize,1]))
            dLoss=np.add(dLossF,dLossR)*0.5
            discriminator.trainable=False cnt = e while cnt > 3:
                cnt = cnt - 4 if cnt == 0:
                noise=np.random.normal(0,1,(batchSize,Noise_dim))
                gLoss=gan.train_on_batch(noise,np.ones([batchSize,1]))

        elif e>= int(epochs*0.5) :
            cnt = e while cnt > 3:
                cnt = cnt - 4 if cnt == 0:
                #one-sided labels
                discriminator.trainable=True
                dLossR=discriminator.train_on_batch(images,np.ones([halfSize,1]))
                dLossF=discriminator.train_on_batch(genImages,np.zeros([halfSize,1]))
                dLoss=np.add(dLossF,dLossR)*0.5
                discriminator.trainable=False noise=np.random.normal(0,1,(batchSize,Noise_dim))
            gLoss=gan.train_on_batch(noise,np.ones([batchSize,1])) if e % 20 == 0:
           print("epoch: %d [D loss: %f, acc.: %.2f%%] [G loss: %f]" % (e, dLoss[0], 100 * dLoss[1], gLoss)) dLossReal.append([e,dLoss[0]])
        dLossFake.append([e,dLoss[1]])
        gLossLogs.append([e,gLoss]) if e % plotInternal == 0 and e!=0:
            show_imgs(generator, e)

        dLossRealArr= np.array(dLossReal)
        dLossFakeArr = np.array(dLossFake)
        gLossLogsArr = np.array(gLossLogs)

        chk = e while chk > 50:
            chk = chk - 51 if chk == 0:
            discriminator.save_weights('/content/gdrive/My Drive/discriminator_DCGAN_lr=0.0001,proportion2,deepgenerator_Fake.h5')
            gan.save_weights('/content/gdrive/My Drive/gan_DCGAN_lr=0.0001,proportion2,deepgenerator_Fake.h5')
            generator.save_weights('/content/gdrive/My Drive/generator_DCGAN_lr=0.0001,proportion2,deepgenerator_Fake.h5')
        # At the end of training plot the losses vs epochs
    plt.plot(dLossRealArr[:, 0], dLossRealArr[:, 1], label="Discriminator Loss - Real")
    plt.plot(dLossFakeArr[:, 0], dLossFakeArr[:, 1], label="Discriminator Loss - Fake")
    plt.plot(gLossLogsArr[:, 0], gLossLogsArr[:, 1], label="Generator Loss")
    plt.xlabel('Epochs')
    plt.ylabel('Loss')
    plt.legend()
    plt.title('GAN')
    plt.grid(True)
    plt.show()

    return gan, generator, discriminatorgan, generator, discriminator = DCGAN(Noise_dim)
train(epochs=4000, batchSize=128, plotInternal=200)

让我们比较一下这两个网络的输出。通过运行该行:

show_imgs(Generator)

网络将从生成器输出一些图像(这是我们之前定义的函数之一)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Generated images from 1:1 training of discriminator vs. generator.

现在让我们检查第二个模型。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Generated images from the second network with different training times for the discriminator and generator.

我们可以看到,生成的图像的细节得到了改善,它们的纹理稍微更加详细。然而,与训练图像相比,它们仍然是不合格的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Training images from Anime dataset.

也许 VAE-甘会表现得更好?

动漫数据集上的甘

重申一下我之前说过的关于 VAE-甘的话,术语 VAE-甘首先是由 Larsen 等人使用的。al 在他们的论文“使用学习的相似性度量进行像素以外的自动编码”。VAE-甘模型与甘模型的区别在于它们的生成器是变异自动编码器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

VAE-GAN architecture. Source: https://arxiv.org/abs/1512.09300

首先,我们需要创建并编译 VAE-GAN,并对每个网络进行总结(这是简单检查架构的好方法)。

# Create and compile a VAE-GAN, and make a summary for themfrom keras.models import Sequential, Model
from keras.layers import Input, Dense, Dropout, Activation, \
    Flatten, LeakyReLU, BatchNormalization, Conv2DTranspose, Conv2D, Reshape,MaxPooling2D,UpSampling2D,InputLayer, Lambda
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import UpSampling2D
from keras.optimizers import Adam, RMSprop,SGD
from keras.initializers import RandomNormal
import numpy as np
import matplotlib.pyplot as plt
import os, glob
from PIL import Image
import pandas as pd
from scipy.stats import norm
import keras
from keras.utils import np_utils, to_categorical
from keras import backend as K
import random
from keras import metrics
from tqdm import tqdm # plotInternal
plotInternal = 50#######
latent_dim = 256
batch_size = 256
rows = 64
columns = 64
channel = 3
epochs = 4000
# datasize = len(dataset)# optimizers
SGDop = SGD(lr=0.0003)
ADAMop = Adam(lr=0.0002)
# filters
filter_of_dis = 16
filter_of_decgen = 16
filter_of_encoder = 16 def sampling(args):
    mean, logsigma = args
    epsilon = K.random_normal(shape=(K.shape(mean)[0], latent_dim), mean=0., stddev=1.0)
    return mean + K.exp(logsigma / 2) * epsilondef vae_loss(X , output , E_mean, E_logsigma):
	# compute the average MSE error, then scale it up, ie. simply sum on all axes
  reconstruction_loss = 2 * metrics.mse(K.flatten(X), K.flatten(output))

	# compute the KL loss
  kl_loss = - 0.5 * K.sum(1 + E_logsigma - K.square(E_mean) - K.exp(E_logsigma), axis=-1) total_loss = K.mean(reconstruction_loss + kl_loss)    

  return total_loss def encoder(kernel, filter, rows, columns, channel):
    X = Input(shape=(rows, columns, channel))
    model = Conv2D(filters=filter, kernel_size=kernel, strides=2, padding='same')(X)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = LeakyReLU(alpha=0.2)(model) model = Conv2D(filters=filter*2, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = LeakyReLU(alpha=0.2)(model) model = Conv2D(filters=filter*4, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = LeakyReLU(alpha=0.2)(model) model = Conv2D(filters=filter*8, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = LeakyReLU(alpha=0.2)(model) model = Flatten()(model) mean = Dense(latent_dim)(model)
    logsigma = Dense(latent_dim, activation='tanh')(model)
    latent = Lambda(sampling, output_shape=(latent_dim,))([mean, logsigma])
    meansigma = Model([X], [mean, logsigma, latent])
    meansigma.compile(optimizer=SGDop, loss='mse')
    return meansigma def decgen(kernel, filter, rows, columns, channel):
    X = Input(shape=(latent_dim,)) model = Dense(2*2*256)(X)
    model = Reshape((2, 2, 256))(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = Activation('relu')(model) model = Conv2DTranspose(filters=filter*8, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = Activation('relu')(model)

    model = Conv2DTranspose(filters=filter*4, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = Activation('relu')(model) model = Conv2DTranspose(filters=filter*2, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = Activation('relu')(model) model = Conv2DTranspose(filters=filter, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = Activation('relu')(model) model = Conv2DTranspose(filters=channel, kernel_size=kernel, strides=2, padding='same')(model)
    model = Activation('tanh')(model) model = Model(X, model)
    model.compile(loss='binary_crossentropy', optimizer=Adam(lr=0.0001, beta_1=0.5), metrics=['accuracy'])
    return model def discriminator(kernel, filter, rows, columns, channel):
    X = Input(shape=(rows, columns, channel)) model = Conv2D(filters=filter*2, kernel_size=kernel, strides=2, padding='same')(X)
    model = LeakyReLU(alpha=0.2)(model) model = Conv2D(filters=filter*4, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = LeakyReLU(alpha=0.2)(model) model = Conv2D(filters=filter*8, kernel_size=kernel, strides=2, padding='same')(model)
    model = BatchNormalization(epsilon=1e-5)(model)
    model = LeakyReLU(alpha=0.2)(model) model = Conv2D(filters=filter*8, kernel_size=kernel, strides=2, padding='same')(model) dec = BatchNormalization(epsilon=1e-5)(model)
    dec = LeakyReLU(alpha=0.2)(dec)
    dec = Flatten()(dec)
    dec = Dense(1, activation='sigmoid')(dec) output = Model(X, dec)
    output.compile(loss='binary_crossentropy', optimizer=Adam(lr=0.0002, beta_1=0.5), metrics=['accuracy'])

    return output def VAEGAN(decgen,discriminator):
    # generator
    g = decgen # discriminator
    d = discriminator
    d.trainable = False
    # GAN
    gan = Sequential([g, d])

#     sgd=SGD()
    gan.compile(optimizer=Adam(lr=0.0001, beta_1=0.5), loss='binary_crossentropy')
    return g, d, gan

我们再次定义了一些函数,这样我们就可以打印来自生成器的图像。

def get_image(image_path, width, height, mode):
    image = Image.open(image_path)
    #print(image.size)

    return np.array(image.convert(mode))

def show_imgs(generator):
    row=3
    col = 5
    noise = np.random.normal(0, 1, (row*col, latent_dim))
    gen_imgs = generator.predict(noise)

    # Rescale images 0 - 1
    gen_imgs = 0.5 * gen_imgs + 0.5

    fig, axs = plt.subplots(row, col)
    #fig.suptitle("DCGAN: Generated digits", fontsize=12)
    cnt = 0

    for i in range(row):
        for j in range(col):
            axs[i, j].imshow(gen_imgs[cnt, :, :, :])
            axs[i, j].axis('off')
            cnt += 1

    #plt.close()
    plt.show()

发生器的参数将受到 GAN 和 VAE 训练的影响。

# note: The parameters of the generator will be affected by both the GAN and VAE training

G, D, GAN = VAEGAN(decgen(5, filter_of_decgen, rows, columns, channel),discriminator(5, filter_of_dis, rows, columns, channel))

# encoder
E = encoder(5, filter_of_encoder, rows, columns, channel)
print("This is the summary for encoder:")
E.summary()

# generator/decoder
# G = decgen(5, filter_of_decgen, rows, columns, channel)
print("This is the summary for dencoder/generator:")
G.summary()

# discriminator
# D = discriminator(5, filter_of_dis, rows, columns, channel)
print("This is the summary for discriminator:")
D.summary()

D_fixed = discriminator(5, filter_of_dis, rows, columns, channel)
D_fixed.compile(optimizer=SGDop, loss='mse')

# gan
print("This is the summary for GAN:")
GAN.summary()

# VAE
X = Input(shape=(rows, columns, channel))

E_mean, E_logsigma, Z = E(X)

output = G(Z)
# G_dec = G(E_mean + E_logsigma)
# D_fake, F_fake = D(output)
# D_fromGen, F_fromGen = D(G_dec)
# D_true, F_true = D(X)

# print("type(E)",type(E))
# print("type(output)",type(output))
# print("type(D_fake)",type(D_fake))

VAE = Model(X, output)
VAE.add_loss(vae_loss(X, output, E_mean, E_logsigma))
VAE.compile(optimizer=SGDop)

print("This is the summary for vae:")
VAE.summary()

在下面的单元格中,我们开始训练我们的模型。注意,我们使用前面的方法来训练鉴频器以及 GAN 和 VAE 不同的时间长度。我们在训练过程的前半部分强调鉴别器的训练,在后半部分我们更多地训练发生器,因为我们想提高输出图像的质量。

# We train our model in this cell

dLoss=[]
gLoss=[]
GLoss = 1
GlossEnc = 1
GlossGen = 1
Eloss = 1

halfbatch_size = int(batch_size*0.5)

for epoch in tqdm(range(epochs)):
    if epoch < int(epochs*0.5):
        noise = np.random.normal(0, 1, (halfbatch_size, latent_dim))
        index = np.random.randint(0,dataset.shape[0], halfbatch_size)
        images = dataset[index]  

        latent_vect = E.predict(images)[0]
        encImg = G.predict(latent_vect)
        fakeImg = G.predict(noise)

        D.Trainable = True
        DlossTrue = D.train_on_batch(images, np.ones((halfbatch_size, 1)))
        DlossEnc = D.train_on_batch(encImg, np.ones((halfbatch_size, 1)))       
        DlossFake = D.train_on_batch(fakeImg, np.zeros((halfbatch_size, 1)))

#         DLoss=np.add(DlossTrue,DlossFake)*0.5

        DLoss=np.add(DlossTrue,DlossEnc)
        DLoss=np.add(DLoss,DlossFake)*0.33
        D.Trainable = False

        cnt = epoch

        while cnt > 3:
            cnt = cnt - 4

        if cnt == 0:
            noise = np.random.normal(0, 1, (batch_size, latent_dim))
            index = np.random.randint(0,dataset.shape[0], batch_size)
            images = dataset[index]  
            latent_vect = E.predict(images)[0]     

            GlossEnc = GAN.train_on_batch(latent_vect, np.ones((batch_size, 1)))
            GlossGen = GAN.train_on_batch(noise, np.ones((batch_size, 1)))
            Eloss = VAE.train_on_batch(images, None)   
            GLoss=np.add(GlossEnc,GlossGen)
            GLoss=np.add(GLoss,Eloss)*0.33
        dLoss.append([epoch,DLoss[0]]) 
        gLoss.append([epoch,GLoss])

    elif epoch >= int(epochs*0.5):
        cnt = epoch
        while cnt > 3:
            cnt = cnt - 4

        if cnt == 0:
            noise = np.random.normal(0, 1, (halfbatch_size, latent_dim))
            index = np.random.randint(0,dataset.shape[0], halfbatch_size)
            images = dataset[index]  

            latent_vect = E.predict(images)[0]
            encImg = G.predict(latent_vect)
            fakeImg = G.predict(noise)

            D.Trainable = True
            DlossTrue = D.train_on_batch(images, np.ones((halfbatch_size, 1)))
        #     DlossEnc = D.train_on_batch(encImg, np.ones((halfbatch_size, 1)))       
            DlossFake = D.train_on_batch(fakeImg, np.zeros((halfbatch_size, 1)))

            DLoss=np.add(DlossTrue,DlossFake)*0.5

#             DLoss=np.add(DlossTrue,DlossEnc)
#             DLoss=np.add(DLoss,DlossFake)*0.33
            D.Trainable = False

        noise = np.random.normal(0, 1, (batch_size, latent_dim))
        index = np.random.randint(0,dataset.shape[0], batch_size)
        images = dataset[index]  
        latent_vect = E.predict(images)[0]

        GlossEnc = GAN.train_on_batch(latent_vect, np.ones((batch_size, 1)))
        GlossGen = GAN.train_on_batch(noise, np.ones((batch_size, 1)))
        Eloss = VAE.train_on_batch(images, None)   
        GLoss=np.add(GlossEnc,GlossGen)
        GLoss=np.add(GLoss,Eloss)*0.33

        dLoss.append([epoch,DLoss[0]]) 
        gLoss.append([epoch,GLoss])

    if epoch % plotInternal == 0 and epoch!=0:
        show_imgs(G)

    dLossArr= np.array(dLoss)
    gLossArr = np.array(gLoss)

#     print("dLossArr.shape:",dLossArr.shape)
#     print("gLossArr.shape:",gLossArr.shape)

    chk = epoch

    while chk > 50:
        chk = chk - 51

    if chk == 0:
        D.save_weights('/content/gdrive/My Drive/VAE discriminator_kernalsize5_proportion_32.h5')
        G.save_weights('/content/gdrive/My Drive/VAE generator_kernalsize5_proportion_32.h5')
        E.save_weights('/content/gdrive/My Drive/VAE encoder_kernalsize5_proportion_32.h5')

    if epoch%20 == 0:    
        print("epoch:", epoch + 1,"  ", "DislossTrue loss:",DlossTrue[0],"D accuracy:",100* DlossTrue[1], "DlossFake loss:", DlossFake[0],"GlossEnc loss:",
          GlossEnc, "GlossGen loss:",GlossGen, "Eloss loss:",Eloss)
#     print("loss:")
#     print("D:", DlossTrue, DlossEnc, DlossFake)
#     print("G:", GlossEnc, GlossGen)
#     print("VAE:", Eloss)

print('Training done,saving weights')
D.save_weights('/content/gdrive/My Drive/VAE discriminator_kernalsize5_proportion_32.h5')
G.save_weights('/content/gdrive/My Drive/VAE generator_kernalsize5_proportion_32.h5')
E.save_weights('/content/gdrive/My Drive/VAE encoder_kernalsize5_proportion_32.h5')

print('painting losses')
# At the end of training plot the losses vs epochs
plt.plot(dLossArr[:, 0], dLossArr[:, 1], label="Discriminator Loss")
plt.plot(gLossArr[:, 0], gLossArr[:, 1], label="Generator Loss")
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.title('GAN')
plt.grid(True)
plt.show()
print('end')

如果你计划运行这个网络,请注意培训过程需要很长时间。我不会尝试这样做,除非你有一些强大的图形处理器,或者愿意运行一整天的模型。

现在我们的 VAE-GAN 训练已经完成,我们可以检查看看我们的输出图像看起来如何,并与我们以前的 GAN 进行比较。

# In this cell, we generate and visualize 15 images. 

show_imgs(G)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以看到,在 VAE-甘的实现中,我们得到了一个很好的模型,它可以生成清晰的图像,并且具有与原始图像相似的风格。我们的 VAE-甘可以创建更健壮的图像,这可以在没有额外的动画脸噪声的情况下完成。然而,我们模型的概括能力不是很好,它很少改变角色的方式或性别,所以这是我们可以尝试改进的一点。

最终点评

不一定清楚哪个模型比其他模型更好,而且这些方法都没有经过适当的优化,因此很难进行比较。

这仍然是一个活跃的研究领域,所以如果你感兴趣,我建议你投入进去,在你自己的工作中尝试使用 GANs,看看你能想出什么。

我希望你喜欢这个关于 GANs 的文章三部曲,并且现在对它们是什么、它们能做什么以及如何制作你自己的有了更好的了解。

感谢您的阅读!

时事通讯

关于新博客文章和额外内容的更新,请注册我的时事通讯。

[## 时事通讯订阅

丰富您的学术之旅,加入一个由科学家,研究人员和行业专业人士组成的社区,以获得…

mailchi.mp](https://mailchi.mp/6304809e49e7/matthew-stewart)

进一步阅读

在 COLAB 中运行 BigGAN:

更多代码帮助+示例:

有影响力的论文:

“GANs”vs“ODEs”:数学建模的终结?

原文:https://towardsdatascience.com/gans-vs-odes-the-end-of-mathematical-modeling-ec158f04acb9?source=collection_archive---------2-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Disentangling neural networks representations [source]

大家好!在这篇文章中,我想在我们在学校、大学学习的经典数学建模和机器学习之间建立一种联系,机器学习也以完全不同的方式对我们周围的对象和过程进行建模。虽然数学家基于他们的专业知识和对世界的理解创建模型,但机器学习算法以某种隐藏的方式描述世界,不完全可以理解,但在大多数情况下,甚至比人类专家开发的数学模型更准确。然而,在许多应用中(如医疗保健、金融、军事),我们需要清晰和可解释的决策,而机器学习算法,特别是深度学习模型,并没有设计来提供这些决策。

本博客将回顾我们期望从任何模型中获得的主要特征,以及“经典”数学建模和机器学习建模的优缺点,并将展示一个结合了这两个世界的候选者— 解开表征学习

此外,如果你想尝试在你自己的数据上应用解开的表示,请查看来自 Google Research 的我在 GitHub 上的实现这个库

深度学习有什么问题?

自从深度学习革命以来,我们试图将神经网络应用到各个领域。在许多重要的领域,它确实有意义,并有助于实现最先进的结果:在计算机视觉,自然语言处理,语音分析和信号处理。最终,所有这些深度学习宣传都是关于从复杂数据中自动提取特征,结合神经网络中的线性和非线性转换,以一些“向量”结束,我们也称之为“嵌入”,它表示关于输入对象的所有需要的信息,并允许对其进行分类或回归:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这些“嵌入”在特征提取和准确性方面确实非常好,但它们在许多方面都失败了:

  • 解释:大小为 N 的向量没有告诉我为什么要做出某个特定的决定,只有逆向工程方法可以突出输入数据中的“感兴趣的对象”。
  • 需要大量数据:深度学习实际上对 10–100 个样本不起作用。
  • 无监督学习:现在大部分应用都需要标注训练数据
  • 零触发重用:这是当今一个非常重要的问题:在一个数据集上训练的神经网络很少能够直接应用于另一个类似的数据集,而无需重新训练。
  • 对象生成:我能从这个嵌入中生成一个真实的对象吗?可能是和甘斯——是的。
  • 对象操作:我可以用这个嵌入操作输入对象的特定属性吗?不完全是。
  • 理论基础:嗯,我们得到了普适近似理论。不多。

看起来这些问题真的很难在现代机器学习框架内解决。但是我们最近都在处理它们!

数学建模有什么好?

关于上面提到的所有这些问题,20 年、50 年甚至 100 年前的大多数数学家根本没有遇到过。为什么?因为他们忙于数学建模,即使用数学抽象描述现实世界中的对象和过程,例如,分布、公式或微分方程(这就是为什么我们在标题中有“ODE”,即常微分方程)。让我们再检查一下“问题清单”,但是想想科学家们从零开始创造的数学模型。这里我仍将使用术语“嵌入”,但它将表示数学模型的参数,即微分方程中的一组自由度。

  • 解释:每一个数学模型都是基于科学家对物体的描述而创建的——带有明确的动机和理解。例如,为了描述物理运动,我们的嵌入将由物体质量、运动速度和坐标空间组成——没有抽象矢量!
  • 需要大量数据:今天的大多数科学突破都不是在“图像网络大小”的数据集上完成的。
  • 无监督学习:嗯,数学建模也不尽然:)
  • 零炮重用:比如说,几何布朗运动的同一个随机微分方程可以应用于金融、生物或物理——只需重命名参数名称。
  • 对象生成:开箱即用,只是采样参数。
  • 对象操纵:开箱即用,只是操纵参数。
  • 理论基础:几百年的科学。

那么为什么我们不把微分方程用于所有的事情呢?事实证明,对于大规模的复杂数据,它们的表现要差得多。这就是为什么我们今天正在驾驭深度学习的浪潮。但是,我们仍然希望从人类开发的模型中获得好的属性。

结合机器学习和基于人类的建模

如果我们仍然可以使用神经网络,在分析复杂数据时如此准确,而且还具有我们上面描述的属性,会怎么样?可解释性,生成和操纵对象的能力,无监督的特征学习和不同环境下的零拍重用,你在哪?例如,作为面部图像的特征提取器,我希望看到这样的内容:

Almost unsupervised disentanglement

它处理对于微分方程或其他模型来说过于复杂的图像,允许生成和操作对象,是可解释的,并且最有可能的是,也可以在另一个数据集上完成所有这些。这项工作的唯一问题是不能完全无人监督。操纵的另一个重要问题是,当我改变“胡子”特征时,它会自动使一张脸变得更有男子气概,这意味着,习得的特征虽然可以解释,但彼此相关,或者换句话说,纠缠在一起

β -VAE

然而,有一种方法可以帮助我们获得解开的表示,换句话说,一种嵌入,其中每个元素负责一个单独的因素,并且这种嵌入可以用于新数据的分类、生成或操作任务(以零触发的方式)。该算法由 DeepMind 实验室开发,基于变分自动编码器,但更强调潜在分布和选择的先验分布之间的 KL 散度,而不是恢复损失。要了解更多细节,我希望你参考下面的视频,它很好地解释了贝塔 VAE 背后的想法,它在监督学习和强化学习中的应用。

beta-VAE applications to machine learning and reinforcement learning

看完这个视频后,你可以看到,beta-VAEs 真的能够提取输入数据的变化因素:物理运动方向、对象大小、颜色和方向,它们能够在强化学习应用中分离感兴趣的对象和背景,以及在现实环境中的模拟中训练的代理的零触发重用。

我自己的实验

由于我经常处理医疗和金融应用,在这些应用中,分离可以真正解决许多与模型的可解释性、人工数据生成和零触发学习相关的实际问题,因此我尝试将 beta-VAEs 用于 ECG 数据和 BTC 价格数据。你可以在我的 GitHub 里找到训练模型的代码。首先,我将β-VAE(实际上非常简单的 MLP 网络)应用于来自 PTB 诊断数据集的心电图,该数据集实际上有三个变化因素:心电图的不同导联/形式、因人而异的脉搏、以及诊断,即梗塞或其缺失。我用瓶颈大小= 10,学习速率 5e-4,容量 C = 25 训练了 VAE 50 个纪元(详见本作)。输入总是一个心跳。正如我所料,我的模型了解到了数据集中变化的真实因素。在下面的图片上,你可以看到,我是如何操作输入(黑线)心跳的,将一个单一的特性从瓶颈的-3 变为 3,同时保持所有其他特性不变。你可以看到,第 5 个特征负责改变心跳的形式,第 8 个特征代表心脏状况(蓝色心电图有梗塞症状,而红色心电图试图“相反”),第 10 个特征轻微改变脉搏。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Disentangling ECG beats

关于财务数据,一切都不那么明朗(这并不奇怪)。训练参数相对相似,但输入是 2017 年收集的 180 分钟 BTC 价格样本。我期望从 beta-VAE 中学习一些“标准的”金融时间序列模型,如均值回复时间序列,但是解释获得的表示相对困难。好吧,我可以告诉你,特征 5 改变了输入时间序列的趋势,但是特征 2、4 和 6 在时间序列的不同部分添加/移除曲线,或者使其更“不稳定”。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Disentangling BTC close prices

多个对象解开

当几个物体出现在图像上,我们想为每个物体找到不同的因素时,该怎么办呢?同样,DeepMind 让我们对他们的结果感到满意。我不会深究细节,只需查看下面两张 gif,获得动力并阅读推文中相应的论文。值得:)

摘要

让我们像描述“正常的”深度学习和数学建模一样,得出描述贝塔-VAE 方法的结论。

  • 解释:完全可解释的特性,我们只需要验证每个特定的嵌入元素。
  • 需要大量数据:嗯,仍然如此,因为我们正在深度学习领域运营。
  • 无监督学习 : 100%无监督。
  • 零镜头重用:视频《为自己说话》中的强化学习实例
  • 对象生成:像一般 VAE 一样简单采样。
  • 物体操作:你想要的任何变化因素都很好很容易。
  • 理论基础:进行中的工作:/

我们几乎拥有数学建模的所有良好特性,同时具备深度学习能力,能够以高精度分析复杂的数据类型。因此,一个非常自然的问题出现了:如果我能以完全无监督的方式从复杂数据中学习如此好的表示,这是否意味着“经典”数学建模的终结?如果一个 ML 模型可以做到,我们真的需要考虑复杂的模型吗,我们只需要分析它的特征?由我们决定:)

P.S.
如果你觉得这个内容有用,有观点,可以在 Bitclout 上支持我。关注我还可以在脸书上看到太短的人工智能文章,在 Instagram 上看到个人资料,在 Linkedin 上看到!如果你想在可解释的人工智能应用或其他人工智能项目上合作,请联系我。

使用 GARCH & Monte-Carlo 模拟的波动性度量

原文:https://towardsdatascience.com/garch-processes-monte-carlo-simulations-for-analytical-forecast-27edf77b2787?source=collection_archive---------7-----------------------

风险度量是资金管理的关键

如何对波动性建模以衡量风险

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Image by author

风险措施是投资组合基金管理的主要因素。波动性和风险是相互关联的。高波动性的投资容易产生高风险,因此不利于短期风险。传统上,波动性在度量风险中起着重要的作用,然而,波动性并不跟随市场的变化。为了估计波动性,有必要开发一个考虑到时间序列中波动性变动的模型,例如非对称 Garch 模型,如 Tarch 和 Egarch 模型。

在这里,我们将探讨如何使用 GARCH、e GARCH 和 GJR-GARCH 模型结合蒙特卡罗模拟来建立 VaR 模型。金融时间序列的尖峰值、聚集波动性和杠杆效应特征证明了 GARCH 建模方法的正确性。时间序列的非线性特性将被用来检验布朗运动和研究时间演化模式。

因此,在 GARCH 建模方法之前,我们将采用涉及分形维数(FD)、重标极差和递归量化分析(RQA)的数据建模技术来总结数据的非线性动力学行为并实现研究目标。

方法:

赫斯特系数 (H) 是长程相关性的特征,与 FD ( FD + H = 2 )相关。重定标(R/S) 分析是分形数据建模的核心工具。经验研究(*1)表明,与该类别中的其他方法相比,如自相关分析、方差比和光谱分解,R/S 会带来更好的结果。它是表征时间序列散度的度量,定义为给定持续时间 (T) 的以平均值为中心的值的范围除以该持续时间的标准偏差[R/S = k * T(H)]; k 是依赖于时间序列的常数。h 测量时间序列的长期记忆,将其描述为均值回复、趋势或随机游走。

H < 0.5 indicating mean reversion

H > 0.5 表示趋势时间序列,以及

H = 0.5 表示随机游走。

递归量化分析(RQA)将用于通过计算 REC、DET、TT 和 LAM 等测量值对动态系统进行量化,从而从数据中提取有价值的见解。

我们将探讨如何使用 GARCH 模型进行风险评估。

GARCH 模型的一个关键限制是对其参数施加非负约束,以确保条件方差的正定性。这种限制会给估计 GARCH 模型带来困难。因此,非对称 GARCH 模型,俗称 GJR-GARCH 模型,可以用来处理对称 GARCH 模型的局限性。此外,指数 GARCH (EGARCH)将被引入,对传统 GARCH 模型进行潜在的改进(4)。

数据挖掘:

让我们看看数据中存储了什么。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在过去的几十年里,原油价格呈现大幅波动,尤其是在 2008 年前后。可以看出,随着多次断崖式的涨跌,价格保持在相对较低的水平。从自相关图中可以看到原始数据中显著的序列自相关的明显证据。QQ 和概率图的形状表明该过程接近正态,但尾部很重。

简单收益常用的形式是:r(t) = {p(t) — p(t-1)}/p(t-1)对数收益= ln(pt/p(t-1),pt 每日原油价格,r(t)是每日收益。

日志返回在这里被认为是每日返回。原始价格和对数收益的直观显示清楚地证明了使用接近常数均值的对数收益的合理性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

回归序列图显示了高和低可变性的周期。在图中可以看到一个以零为中心的随机过程。正负收益的大幅波动增加了风险投资和管理的难度。石油日收益率的均值基本上在零水平附近,具有明显的波动聚类,表明存在异方差。ACF 很小,串行不相关,但高度依赖。QQ 和概率图的形状没有明显变化。正负收益的大幅波动增加了风险投资和管理的难度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*sns.distplot(df.returns, color=’blue’) #density plot
plt.title(“2000–2019 Crude Oil return frequency”)
plt.xlabel(‘Possible range of data values’)
# Pull up summary statistics
print(df.returns.describe())*

收益的偏度(-0.119)和向右偏离表明正收益比负收益更强烈,峰度(7.042)反映了石油波动的大范围。

标准正态分布的偏度和峰度分别为 0 和 3。

Jarque-Bera 检验的值表明,传统的正态分布假设并不适合原油收益的真实分布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*ADF = ADF(df.returns)
print(ADF.summary().as_text())**kpss = KPSS(df.returns)
print(kpss.summary().as_text())**dfgls = DFGLS(df.returns)
print(dfgls.summary().as_text())**pp = PhillipsPerron(df.returns)
print(pp.summary().as_text())**za = ZivotAndrews(df.returns)
print(za.summary().as_text())**vr = VarianceRatio(df.returns, 12)
print(vr.summary().as_text())*

进行虚拟现实测试是为了测试收益序列是否是纯粹的随机游走,而不是具有一定的可预测性。我们在这里比较了 1 个月和 12 个月的回报。用负检验统计量 VA(-11.07)拒绝零表示时间序列中存在序列相关性。ADF、KPSS、DFGLS、PP 和 ZA 统计量的单位根和平稳性检验都显示出显著性,表明应用 GARCH 型模型拟合收益率序列是合适的。

自从 Mandelbrot 发表了他的关于 R/S 分析在时间序列的长记忆依赖性中的应用的著作(Mandelbrot,Wallis,1969;Mandelbrot,1972)和自从 Peters 提出了他的分形市场假说(Peters,1991)作为公认的有效市场假说的替代,这种方法正在关于金融时间序列被检查。

非线性动力学:

我们现在将使用 H 扩展对平稳性的研究。 H 提供了一种方法来衡量金融时间序列偏离随机游走的程度。

*closes_recent = df.Price[-2000:]
plot(closes_recent); show()
# calculate Hurst of recent prices
lag1 = 2
lags = range(lag1, 20)
tau = [sqrt(std(subtract(closes_recent[lag:], closes_recent[:-lag]))) for lag in lags]
plot(log(lags), log(tau)); show()
m = polyfit(log(lags), log(tau), 1)
hurst = m[0]*2
print (‘hurst = ‘,hurst)*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

H (0.531)表示具有长期相关性的随机运动时间序列。对数收益的长记忆性证明了本研究中给定系列的 GARCH 模型的合理性。

*time_series = TimeSeries(df.Price, embedding_dimension=2, time_delay=2)
settings = Settings(time_series, computing_type=ComputingType.Classic, neighbourhood=FixedRadius(0.65), similarity_measure=EuclideanMetric,
theiler_corrector=1)
computation = RQAComputation.create(settings, verbose=True)
result = computation.run()
result.min_diagonal_line_length = 2
result.min_vertical_line_length = 2
result.min_white_vertical_line_lelngth = 2
print(result)*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里,低 RR 表示较低程度的周期性和随机行为;此外,低的 DET 值表明较少的确定性。 LAM 值表示过程中的层次性,是明显周期性和混沌动力学的阶段交替。虽然来自 RQA 的发现也证明了分形建模的含义,但是,通过显示自动回归行为,非线性动力学拒绝了有效市场假说,证明了使用 GARCH 方法的合理性。

GARCH 模型:

在估计 GARCH 型模型之前,将回报率放大 100 倍总是一个好主意。这有助于优化器转换,因为波动率截距的范围更接近模型中其他参数的范围。

X = 100* df.returns

让我们拟合一个 ARCH 模型,并绘制平方残差来检验自相关性。

*gam = arch_model(Model.resid, p=2, o=0, q=2, dist=’StudentsT’)
gres = gam.fit(update_freq=5, disp=’off’)
print(gres.summary())**tsplot(gres.resid**2, lags=30)*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们可以在平方残差中看到自相关的明显证据。让我们拟合一个 GARCH 模型,看看它的表现如何。我们将应用如下程序:

  • 迭代 ARIMA(p,d,q)模型的组合,以最佳拟合时间序列。
  • 根据 AIC 最低的 ARIMA 模型选择 GARCH 模型订单。
  • 用 GARCH(p,q)模型拟合时间序列。
  • 检查自相关的模型残差和残差平方
*def _get_best_model(TS):
 best_aic = np.inf 
 best_order = None
 best_mdl = None
 pq_rng = range(5) # [0,1,2,3,4,5]
 d_rng = range(2) # [0,1]
 for i in pq_rng:
 for d in d_rng:
 for j in pq_rng:
 try:
 tmp_mdl = smt.ARIMA(TS, order=(i,d,j)).fit(
 method=’mle’, trend=’nc’
 )
 tmp_aic = tmp_mdl.aic
 if tmp_aic < best_aic:
 best_aic = tmp_aic
 best_order = (i, d, j)
 best_mdl = tmp_mdl
 except: continue
 print(‘aic: {:6.2f} | order: {}’.format(best_aic, best_order)) 
 return best_aic, best_order, best_mdl

TS = X
res_tup = _get_best_model(TS)*

#aic: 22462.01 |顺序:(2,0,2)

所以,我们在这里发现,最佳模型是 ARIMA(2,0,2)。现在我们绘制残差来决定它们是否拥有条件异方差行为的证据。

*am = arch_model(X, p=2, q=2, o=1,power=2.0, vol=’Garch’, dist=’StudentsT’)
res = am.fit(update_freq=5)
print(res.summary())*

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*am = arch_model(X, p=2, q=2, o=1,power=2.0, vol=’Garch’, dist=’StudentsT’)
res = am.fit(update_freq=5)
print(res.summary())**eam = arch_model(X, p=2,q=2, o=1, power=2.0, vol=’EGARCH’, dist=’StudentsT’)
eres = eam.fit(update_freq=5)
print(res.summary())**gjam = arch_model(X, p=2, o=1, q=2, power=2.0, dist=’StudentsT’)
gjres = gjam.fit(update_freq=5, disp=’off’)
print(gjres.summary())*

所有 3 个 GARCH 模型的输出都以表格形式显示。ω是白噪声,α和β是模型的参数。此外,α [1] + β [1] < 1 表示稳定模型。EGARCH 模型似乎是三个模型中最好的,然而,GARCH 和 EGARCH 之间的差别很小。

尽管我已经用所有可用的数据进行了实验;然而,在训练/测试中分割数据并获得 MSE/MAE/RMSE 结果来比较最佳模型拟合是一个好主意。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

标准化残差的计算方法是将残差除以条件波动率。

*std_resid = res_normal.resid / res_normal.conditional_volatility
unit_var_resid = res_normal.resid / res_normal.resid.std()*

标准化残差和条件波动率图显示了一些误差,但幅度不大。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*plt.xlim(-2, 2)
sns.kdeplot(squared_resid, shade=True)
sns.kdeplot(std_resid, shade=True)
sns.kdeplot(unit_var_resid, shade=True)
plt.legend([‘Squared Residual’, “Unit variance residual”, “Std Residual”], loc=’best’)
plt.show()*

标准化残差也与非标准化但成比例的残差一起绘制。平方残差在中心更突出,表明该分布比标准化残差的分布具有更重的尾部。让我们检查一下 ACF 图。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*plot_acf(std_resid)
plt.title(‘Standardized residuals’)
pyplot.show()*

看起来有些尖峰超出了阴影置信区域。让我们来看看残差的平方。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*plot_acf(squared_resid)
plt.title(‘Squared residuals’)
pyplot.show()*

残差平方显示数据点位于蓝色阴影置信区域(95%)内,表明模型拟合度良好。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*am = arch_model(X,mean=’HAR’,lags=[1,5,22],vol=’Constant’) 
sim_data = am.simulate([0.1,0.4,0.3,0.2,1.0], 250)
sim_data.index = pd.date_range(‘2019–01–01’,periods=250) 
am = arch_model(sim_data[‘data’],mean=’HAR’,lags=[1,5,22], vol=’Constant’) 
res = am.fit()
fig = res.hedgehog_plot(type=’mean’)*

刺猬图显示了 2019 年的预测方法。橙色线表示不同时间间隔的预测。

基于模拟的预测:

基于模拟的方法用于从这里模拟的 EGARCH 中获得预测波动率的置信区间。这个过程被重复了很多次,以获得波动性预测的集合。预测点通过对模拟进行平均来计算,95%置信区间分别使用模拟分布的 2.5%和 97.5%分位数来计算。考虑到平均收益输入(mu)为 0.0292,年波动率输入(vol)为(26.48) * sqrt 252 = 37.37%。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

*vol = df.returns.std()*sqrt(252)
print (“Annual Volatility =”, str(round(vol,4)*100)+”%”)**#Define Variables
S = df.Price[-1] #starting stock price (i.e. last available real stock price)
T = 252 #Number of trading days
mu = 0.0622 #Return
vol = 0.3737 #Volatility**daily_returns=np.random.normal((1+mu)**(1/T),vol/sqrt(T),T)**price_list = [S]
 price_list.append(price_list[-1]*x)**#Generate Plots — price series and histogram of daily returns
plt.plot(price_list)
plt.show()
plt.hist(daily_returns-1, 100) 
plt.show()*

最上面的图显示了一个交易年度(252 天)内潜在价格序列演变的单一模拟,基于遵循正态分布的随机每日回报。第二个图是一年中这些随机日收益率的直方图。然而,真正的洞察力可以从运行成千上万的模拟中获得,每次运行都基于相同的股票特征(mu 和 vol)产生不同系列的潜在价格演变。

*#set up empty list to hold our ending values for each simulated price series
result = []**S = df.Price[-1] #starting stock price (i.e. last available real stock price)
T = 252 #Number of trading days
mu = 0.0622 #Return
vol = 0.3737 #Volatility**#choose number of runs to simulate — I have chosen 10,000
for i in range(10000):
 #create list of daily returns using random normal distribution
 daily_returns= np.random.normal((1+mu)**(1/T),vol/sqrt(T),T)

 #set starting price and create price series generated by above random daily returns
 price_list = [S]

 for x in daily_returns:
 price_list.append(price_list[-1]*x)**#append the ending value of each simulated run to the empty list we created at the beginning
 result.append(price_list[-1])**plt.figure(figsize=(10,6))
plt.hist(result,bins= 100)
plt.axvline(np.percentile(result,5), color=’r’, linestyle=’dashed’, linewidth=2)
plt.axvline(np.percentile(result,95), color=’r’, linestyle=’dashed’, linewidth=2)
plt.figtext(0.6,0.8,s=”Start price: $%.2f” %S)
plt.figtext(0.6,0.7,”Mean final price: $58.44")
plt.figtext(0.6,0.6,”5% quantile: $29.72")
plt.figtext(0.15,0.6, “95% quantile: $101.75”)
plt.title(u”Final price distribution for Crude stock”, weight=’bold’, fontsize=12)
plt.show()*

这里的结果会略有不同,因为这些是模拟随机的每日收益抽取。由于每个模拟中包含的路径,平均值越倾向于“mu”输入中使用的平均回报。下面的直方图显示了潜在价格分布的几个分位数,以了解非常高或非常低的回报的可能性。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

很明显,原油价格有 5%的可能会低于 29.72 美元,有 5%的可能会高于 101.75 美元。

关键要点:

在这里,我们研究并提出了一个基于混合时变长记忆 GARCH 和模拟的波动模型。经验证据表明,具有布朗运动的原油数据往往在其时间动态中表现出一定程度的可预测性。

虽然风险值可以用分析方法和模拟方法来衡量;我们使用蒙特卡罗模拟来检验结果的稳健性。蒙特卡洛模拟的输出表明,即使在控制了外来因素之后,结果仍然是稳健的。因此,研究结果提供了一个很好的混合 EGARCH 和 Monte-Carlo 模拟模型,该模型考虑了诸如波动聚集性和不对称性、时变风险和重尾分布等波动特征来衡量原油价格。

可以到达 这里

参考文献:

(1)曼德尔布罗,b . b .&沃利斯,J. R. (1969)。分数高斯噪声的计算机实验:第 2 部分,重新标度的范围和光谱。水资源研究,5(1),242–259

(2)杜塔,A. (2014)。波动性建模:对称或非对称 garch 模型。统计学杂志:理论与应用进展,12(2),99–108。

(3) Glosten,l .,Jagannathan,r .和 Runkle,D. (1993)关于股票预期收益之间的关系,金融杂志,48,1779–801。

(4) Nelson,D. (1991)资产回报的条件异方差:一种新的方法,计量经济学,59,349–70。

(5)彼得斯,E. E. (1991),资本市场的混乱和秩序:周期、价格和市场波动的新观点,威利,纽约。

Gartner 2019 新兴技术炒作周期。人工智能领导者有什么好处?

原文:https://towardsdatascience.com/gartner-2019-hype-cycle-for-emerging-technologies-whats-in-it-for-ai-leaders-3d54ad6ffc53?source=collection_archive---------13-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

https://www.gartner.com/smarterwithgartner/5-trends-appear-on-the-gartner-hype-cycle-for-emerging-technologies-2019/

Gartner 2019 年对新兴技术的炒作周期已经结束,因此这是一个很好的时机来深入研究这份报告并反思我们作为一家公司的人工智能战略。你可以在这里找到完整报告的简要总结

首先,在详细介绍报告的内容及其对公司人工智能战略的影响之前,我想谈谈最近几天我在社交网络上看到的一个反复出现的评论。许多人惊讶地看到,某些技术尽管在前几年出现过,却完全从报告中消失了。正如 Gartner 在其研究中解释的那样,其炒作周期涵盖了非常广泛的主题,因此如果某项特定技术没有被报道,并不一定意味着它们不重要,恰恰相反一些技术从炒作周期中消失的一个原因可能是它们不再是“新兴的”,而是业务和 IT 的关键。

因此,让我们从实际回顾与人工智能相关的基础技术开始,这些技术已从今年的报告中排除,但对业务仍然至关重要:

  • 深度神经网络。事实上,Gartner 认为 DNNs 是炒作周期中许多其他新兴技术的基础。
  • 对话人工智能平台。 Gartner 不再考虑对话式人工智能平台新兴技术,而是坚持其业务相关性。
  • 虚拟助手。Gartner 不再认为虚拟助理是新兴技术,而是坚持其业务相关性。
  • 人工通用智能(AGI) 。在我看来,Gartner 的呼吁很好,主张围绕人工智能的务实愿景,远离炒作。正如 Gartner 提到的,AGI 几十年内都不会成熟。

**根据 Gartner 的说法,哪些应该是公司人工智能领导者的重点领域?**根据其 2019 年新兴技术优先矩阵,这些技术应该是:

  • 增强智能。Gartner 将这一新兴技术视为新业务解决方案设计方法的关键,平衡短期自动化与中长期方法,确保不仅通过自动化手段,而且通过放大人才来提高质量。
  • 边缘 AI 。在那些通信成本、延迟或大容量摄入可能是关键的场景中。当然,这意味着确保我们用例(如深度学习)的足够的人工智能开发和技术可用于我们想要部署的物联网基础设施,以及其他条件。

最后,2019 炒作周期中与 AI 相关的新兴技术有哪些:

  • **适应性 ML。**定义为可以在相同评分环境下再培训的一类 ML。
  • **情感 AI。**定义为使用计算机视觉或语音分析等人工智能技术来适应用户的情绪状态并改善整体体验。
  • **可解释的人工智能。**在确保道德和法规合规性的同时,可解释性是深度学习面临的主要挑战之一。可解释的人工智能包括不同的方法和技术,能够解释人工智能算法考虑基于提供的数据产生其输出的推理。
  • **生成对抗网络(GANs)。**最初由 Ian Goodfellow 和 al。,GANs 使用两种不同的神经网络(生成型和判别型)。虽然判别网络的目标是改善其对输入数据的分类方式,但生成网络试图通过创建旨在欺骗算法的合成数据来使其失败。虽然这种网络可以用来改善人工智能训练,但它们正被用来按照特定的模式或风格生成视频、音频或文本等合成内容。
  • **迁移学习。**定义为重用先前训练的人工智能模型作为在不同背景下训练它们的起点,一些人工智能专家声称迁移学习是未来实现人工一般智能的关键

综上所述,基于我的个人经验,人工智能领导者应该:

  • 拥抱像 DNN 这样的人工智能核心技术不是作为一项创新举措,而是作为公司核心战略的一部分。公司应该从实验室概念验证转向可扩展的人工智能方法,包括如何组织人工智能人才、应用方法以及在整个公司范围内实施通用平台。
  • 考虑人工智能如何帮助他们的公司与物联网或 5G 等其他创新技术一起解决业务挑战。虽然 Gartner 专注于边缘 AI 的影响,但 5G 也可以在不久的将来使解决有趣的用例变得可行。此外,区块链(顺便说一下,它也从炒作周期中消失了)和人工智能非常适合解决非常有趣的用例。
  • 最后,关注技术的人性一面。从如何让我们的数字人才从自动化中受益,到考虑人工智能对员工工作、客户体验和整个社会的影响。

如果你喜欢读这篇文章,请 考虑成为会员 以获得每个故事的完整访问权,同时支持我和媒体上的其他作者。

用矩阵解释门控循环单位:第 1 部分

原文:https://towardsdatascience.com/gate-recurrent-units-explained-using-matrices-part-1-3c781469fc18?source=collection_archive---------7-----------------------

由:闪耀的拉塞尔-普勒里道林-普勒里

很多时候,我们都在使用深度学习框架,这些框架执行构建模型所需的所有操作。然而,首先理解一些基本的矩阵运算是有价值的。在本教程中,我们将带您完成理解 GRU 工作原理所需的简单矩阵运算。

详细的笔记本可以在 https://github.com/sparalic/GRUs-internals-with-matrices或 https://github.com/DPuleriNY/GRUs-with-matrices找到

什么是门控循环单元(GRU)?

门控递归单元(如下图所示)是一种递归神经网络,它解决了长期依赖性问题,这可能导致梯度消失更大的香草 RNN 网络体验。GRUs 通过存储前一时间点的“记忆”来帮助通知网络未来的预测,从而解决了这个问题。乍一看,人们可能认为这个图相当复杂,但事实恰恰相反。本教程的目的是揭穿使用线性代数基础的 GRUs 的困难。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GRUs 的控制方程为:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Governing equations of a GRU

其中 z 和 r 分别代表更新门和复位门。而 h_tilde 和 h 分别表示中间存储器和输出。

GRUs vs 长期短期记忆(LSTM) RNNs

GRUs 和流行的lstm(由 Chris Olah 很好地解释)之间的主要区别是门的数量和单元状态的维护。与 GRUs 不同,LSTMs 有 3 个门(输入、遗忘、输出),并保持内部存储单元状态,这使其更加灵活,但在存储和时间方面效率较低。然而,由于这两种网络都擅长解决有效跟踪长期相关性所需的消失梯度问题。通常使用以下经验法则在它们之间做出选择。当在这两者之间做出决定时,建议您首先训练 LSTM,因为它有更多的参数,更灵活一些,然后是 GRU,如果两者的性能之间没有相当大的差异,那么使用更简单有效的 GRU

方法

为了进一步说明 RNNs 的优雅,我们将带您了解理解 GRU 内部工作所需的线性代数基础知识。为此,我们将使用一小段字母来说明我们认为理所当然的矩阵计算是如何使用预打包的包装函数创建许多常见的 DL 框架的。本教程的目的不是让我们倒退,而是帮助我们更深入地理解 rnn 如何使用线性代数工作。

示例使用以下示例字符串作为输入数据:

` text =数学数学数学数学’

然而,算法本质上是某种数学方程,因此我们的原始文本在提交给 GRU 层之前必须用数字形式表示。这在下面的预处理步骤中完成。

数据预处理

第一步,创建一个包含所有唯一字符的字典,将每个字母映射到一个唯一的整数:

字符字典:{‘h’: 0,’ a’: 1,’ t’: 2,’ M’: 3}

我们的编码输入现在变成:
数学数学= [3,1,2,0,3,1,2,0]

第一步:创建数据批次 这一步是通过用户指定我们想要创建多少批次(B),或者给定我们的词汇表(V)的序列长度(S)来实现的。下图演示了如何创建和编码批处理。

假设我们需要以下参数:
1。批量大小(B) = 2
2。序列长度(S) = 3
3。词汇(V) = 4
4。输出(O) = 4

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

那么什么是时间序列呢?
如果您对 RNN 进行基本搜索,您将会找到下图。这个图像是展开形式中发生的事情的概括视图。然而,x(t-1)、x(t)和 x(t+1)(以红色突出显示)在我们的批次中意味着什么?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Vanilla RNN architecture

在我们的小批量中,时间序列代表每个序列,信息从左到右流动,如下图所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Schematic of data flow for each one-hot encoded batch

数据集的维度

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Batch anatomy

步骤 1:用代码演示

整形后,如果你检查 X 的形状,你会发现你得到一个形状的秩为 3 的张量:3 x 3 x 2 x 4。这是什么意思?

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Dimensions of the dataset

会演示什么?

数据现在已经准备好进行建模了。然而,我们想强调本教程的流程。我们将演示为批次 1 中的第一个序列(以红色突出显示)执行的矩阵运算(如下所示)。这个想法是为了理解第一个序列的信息是如何传递给第二个序列的,以此类推。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Sequence used for walk through (Sequence 1 batch 1)

为此,我们需要首先回忆一下这些批次是如何被输入算法的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Schematic of batch one being ingested into the RNN

更具体地说,我们将遍历序列 1 在 GRU 单元中完成的所有矩阵运算,并将在该过程中计算结果输出 y_(t-1)和 h_t(如下所示):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

First time step of of batch 1

步骤 2:定义我们的权重矩阵和偏差向量

在这一步中,我们将带您完成用于计算 z 门的矩阵运算,因为其余三个方程的计算完全相同。为了帮助理解这一点,我们将通过将内部等式分解为三部分来遍历复位门 z 的点积,最后我们将对输出应用 sigmoid 激活函数,以挤压 0 和 1 之间的值:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Reset gate

但首先让我们定义网络参数:

什么是隐藏尺寸?

上面定义的隐藏大小是学习参数的数量,或者简单地说,是网络内存。该参数通常由用户根据手头的问题来定义,因为使用更多的单元可能会使训练数据过拟合。在我们的例子中,我们选择了隐藏大小 2,以便更容易说明这一点。这些值通常被初始化为来自正态分布的随机数,它们是可训练的,并且在我们执行反向传播时被更新。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Anatomy of the Weight matrix

我们体重的大小

我们将使用第一批遍历所有矩阵运算,因为对于所有其他批来说,这是完全相同的过程。然而,在我们开始任何上述矩阵运算之前,让我们讨论一个叫做广播的重要概念。如果我们看 batch 1 (3 x 2 x 4)的形状和 Wz (4 x 2)的形状,首先想到的可能是,我们将如何对这两个形状不同的张量执行元素式矩阵乘法?

答案是我们使用一个叫做“广播”的过程。广播被用来使这两个张量的形状兼容,这样我们可以执行我们的元素矩阵运算。这意味着 Wz 将被广播到一个非矩阵维度,在我们的例子中是我们的序列长度 3。这意味着更新等式 z 中的所有其他项也将被广播。因此,我们的最终等式将是这样的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation for z with weight matrices broadcasted

在我们执行实际的矩阵运算之前,让我们想象一下第一批中的序列 1 是什么样子的:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of matrix operations and dimensions for the first sequence in batch 1

更新门:z

更新门决定了过去的信息对当前状态的有用程度。这里,sigmoid 函数的使用导致更新门值在 0 和 1 之间。因此,该值越接近 1,我们包含的过去的信息就越多,而值越接近 0,则意味着只保留新信息。

现在让我们开始数学… 第一项:注意,当这两个矩阵用点积相乘时,我们是每行乘以每列。这里,第一个矩阵(x_t)的每一行(用黄色突出显示)都要乘以第二个矩阵(Wz)的每一列(用蓝色突出显示)。

术语 1:应用于输入的权重

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Dot product of the first term in the update gate equation

术语 2:隐藏权重

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Dot product of the second term in the update gate equation

术语 3:偏差向量

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Bias vector

将所有这些放在一起:z_inner

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Inner linear equation of the reset gate

然后,使用 sigmoid 激活函数将结果矩阵中的值压缩在 0 和 1 之间:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Sigmoid equation

复位门:r

重置门允许模型忽略可能与未来时间步不相关的过去信息。在每一批中,复位门将重新评估先前和新输入的综合性能,并根据新输入的需要进行复位。再次因为 sigmoid 激活函数,更接近 0 的值将意味着我们将继续忽略先前的隐藏状态,并且对于更接近 1 的值,情况相反。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Reset gate

中间内存:波形符

中间存储单元或候选隐藏状态将来自先前隐藏状态的信息与输入相结合。因为第一项和第三项所需的矩阵运算与我们在 z 中所做的相同,所以我们将只给出结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Intermediate/candidate hidden state

第二学期:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Second term matrix operations

将所有内容放在一起:颚化符

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Inner linear equation calculation

然后,使用 tanh 激活函数将结果矩阵中的值压缩在-1 和 1 之间:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Tanh activation function

最后:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Candidate hidden state output

在时间步长 t:h(t-1)输出隐藏层

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Hidden state for the first time step

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Resulting matrix for hidden state at time step 1

批次 1 中的第二个序列(时间步长 x_t)如何从这种隐藏状态中获取信息?

回想一下,h(t-n)首先被初始化为零(在本教程中使用)或随机噪声,以开始训练,之后网络将学习和适应。但是在第一次迭代之后,新的隐藏状态 h_t 现在将被用作我们的新的隐藏状态,并且在时间步长(x_t)对序列 2 重复上述计算。下图演示了这是如何做到的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Illustration of the new hidden state calculated in the above matrix operations

这个新的隐藏状态 h(t-1)将不用于计算批量中第二个时间步的输出(y(t+1))和隐藏状态 h(t),以此类推。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Passing of hidden states from sequence1 to sequence 2

下面我们演示如何使用新的隐藏状态 h(t-1)来计算后续的隐藏状态。这通常使用循环来完成。该循环迭代每个给定批次中的所有元素,以计算 h_(t-1)。

代码实现:第 1 批输出:h(t1)、h(t)和 h(t+1)

第二批的隐藏状态是什么?

如果你是一个视觉的人,它可以被看作是一个系列,在 h(t+1)的输出,然后将被送到下一批,整个过程再次开始。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Passing of hidden states across batches

步骤 3:计算每个时间步的输出预测

为了获得我们对每个时间步长的预测,我们首先必须使用线性层来转换我们的输出。回想一下隐藏状态下的列的维数 h(t+n)本质上是网络尺寸/隐藏尺寸的维数。然而,我们有 4 个唯一的输入,我们希望我们的输出也有 4 个。因此,我们使用所谓的密集层或全连接层将输出转换回所需的维度。然后,根据所需的输出,将这个完全连接的层传递给一个激活函数(对于本教程为 Softmax)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Fully connected/Linear layer

最后,我们应用 Softmax 激活函数将我们的输出归一化为一个概率分布,其总和为 1。Softmax 函数:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Softmax equation

根据教科书的不同,您可能会看到不同风格的 softmax,特别是使用 softmax max 技巧,它会减去整个数据集的最大值,以防止大型 y _ linear y/full _ connected 的值爆炸。在我们的情况下,这意味着我们的最大值 0.9021 将首先从 y_linear 中减去,然后应用于 softmax 方程。

让我们分解一下,请注意,我们不能像前面那样对序列进行子集划分,因为求和需要整批中的所有元素。

  1. 从完全连接的图层中的所有元素中减去整个数据集的最大值:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Applying the Max trick for Softmax equation

2.求指数矩阵中所有元素的和

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Sum of the exponents for each row

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Final Softmax output for the first sequence in batch 1

最后,训练我们的网络(仅向前)

这里,我们通过在网络中多次运行每个批次来训练输入批次的网络,这被称为一个时期。这允许网络多次学习序列。随后进行损失计算和反向传播,以最大限度地减少我们的损失。在本节中,我们将一次性实现上面显示的所有代码片段。鉴于输入尺寸较小,我们将仅演示正向传递,因为损失函数和反向传播的计算将在后续教程中详细介绍。

这个函数将向网络输入一系列字母,帮助创建一个初始状态,避免胡乱猜测。如下所示,生成的前几个字符串有点不稳定,但是经过几次处理后,至少接下来的两个字符是正确的。然而,考虑到词汇量很小,这个网络很可能会过度适应。

最后的话

本教程的目的是通过演示简单的矩阵运算如何组合成如此强大的算法,提供 GRUs 内部工作的一个演示。

接下来:用矩阵解释的门递归单元:第 2 部分训练和损失函数

参考资料:

  1. 递归神经网络的不合理有效性
  2. 使用 Pytorch 的 Udacity 深度学习
  3. 面向编码人员的 fastai 深度学习
  4. 深度学习——直接的兴奋剂
  5. 深度学习书籍
  6. http://colah.github.io/posts/2015-08-Understanding-LSTMs/

数据科学中的看门人和精英主义

原文:https://towardsdatascience.com/gatekeeping-and-elitism-in-data-science-74cf19cd5744?source=collection_archive---------11-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Photo by Court Prather on Unsplash

老实说,我经常觉得我需要证明我比其他人更优秀。当涉及到我所建立的身份,我引以为豪的事情,给我提供自我价值感的事情时,我可能会特别自私,不管这些事情多么肤浅。因此,当我在数据科学领域遇到精英主义和势利的知识分子守门人时,我完全理解。

当我听到那些大部分时间都在使用 Microsoft Excel 工作、对数学或统计学知之甚少的人将他们的工作称为数据科学或分析时,我经常感到愤怒。当我看到有人在网上论坛问“理解统计学对数据科学真的有必要吗?”或者“我正试图从社交媒体经理转型为数据科学家,从哪里开始学习线性代数最好?”,我有时会觉得自己被冒犯了。见鬼,即使人们习惯于在真实数据集上天真地运行 svm.fit()和 svm.predict(),而不试图理解凸优化的理论细微差别,我的不安全感也会迫使我嘲笑自己,就好像我是一个被农民邀请参加舞会的法国贵族妇女。

当然,这样的反应是幼稚和自恋的。试图阻止有抱负的学习者,并假设一些错误的优越感或道德制高点,不仅是不成熟和自私的。不积极鼓励其他人参与数据科学领域,不赞扬在线教育资源的扩展,就会成为数据科学领域进步的阻碍。许多人对人类知识所能做出的最重要的贡献是激励和鼓励他人。

充当数据科学的看门人是对求知欲的背叛,是对可获取知识、技术颠覆以及数据和经验证据的民主本质的新范式的冒犯。

另一方面,我也相信在某种程度上,这种守门本能至少是部分有效的反应。尽管自我和饥荒思维——认为在数据科学中只有固定数量的机会,因此一个人的成功对另一个人是有害的——可以解释大量的这些反应,但我确实相信这里还有其他因素在起作用。

在试图对这种精英主义、排外主义的心态提供一种宽容的理解之前,我想澄清一下我所说的数据科学中的守门人是什么意思。我所指的守门人通常采取既定的数据科学家和其他“内部人士”的形式,试图劝阻或阻止人们(通常是来自非常规或非技术学术和职业背景的人)追求他们的神圣领域。一种类似的精英主义倾向贯穿于许多清晰区分“真正的数据科学家”和假货的尝试中,以澄清“商业/报告分析师”不是真正在做分析,或者声称一个人“必须拥有 STEM 领域的研究生学位”才能成为一名有价值的数据科学家。

现在,我想谈谈我们应该同情看门人的几个原因。为了理解他们的观点,我们需要对其中的一些问题进行批判性的审视。最终,这让我们更好地理解了鼓励来自许多不同背景的不同人群进行合作、组建社区以及在所有数据科学领域寻求各种机会的必要性。

在为 techcrunch.com 撰写的题为“软件开发人员日益增长的精英主义问题”的文章中,卡兰·夏普指出,推动程序员精英主义的因素之一是年轻一代的新兴程序员有机会获得老一辈没有的机会和捷径。老一代的守卫者感到被这种感知的不公平所欺骗,并被新程序员所拥有的巨大潜力所威胁。我感觉这种相同的动态在数据科学中很普遍。

想象一下,你是几十年来一直使用统计编程和构建模型的职业统计学家之一,或者你是第一批行业数据科学家中的一员。在过去十年左右围绕数据科学的炒作爆发之前,拥有研究生学位和定量领域正式教育经验的人成为数据科学家可能更常见。物理学、计算机科学、生物学和其他 STEM 领域的博士可能在应用科学研究的背景下学习编程和数据分析,然后过渡到工业界作为数据科学家工作。

不难看出,拥有 STEM 研究生学位的早期数据科学家和如今的新兴数据科学家之间存在巨大差异。后者将获得精心策划的在线数据科学项目,可以在世界任何地方免费访问,以及特定的职业指导和新的分析软件。这位 2000 年代中期的资深数据科学家可能会觉得受到了欺骗,因为他错过了“Python 中的数据科学入门”或“R 编程入门”的所有在线课程,以及 Kaggle 等在线数据科学社区,此外还有专门提供数据科学和机器学习方面的本科生和研究生课程的大学。

让我们假定我们的看门人是无辜的。让我们假设他们的反应不仅仅是觉得被新一代人享有的额外优势欺骗了。让我们假设他们的担忧是更真实的:他们认为这一代人的教育水平在下降。此外,假设他们真的担心越来越多的人仅仅为了诱人的薪水和社会声望而追求数据科学。最后,让我们补充一个事实,即不得不应对“数据科学家”这一宽泛且往往无益的标签是可以理解的恼人。在某些方面,也许精英主义仅仅是为了拒绝媒体围绕数据科学的讨论。

这一论点,即使是以最慈善的形式,也远远不能证明精英主义是正当的。哀叹教育质量的下降并不是阻止他人获取知识并声称自己是真理的唯一来源的理由。如果有什么不同的话,劣质教育内容的普及应该激励人们传播更高质量的教育内容来取代它。

寻租的涌入可能是试图保护该领域免受其腐败影响的更有力的理由。然而,守门人的回应似乎实际上依赖于这样一种心态,即新来者将侵占老一辈人本应享有的利润。这不是一场零和游戏,像数据科学这样基础而广泛的领域本身不会腐败,就像数学不会腐败一样。

在数据科学和一般技术领域,定义松散的术语肯定存在问题,但将如此多的重量放在一个标题上,并让它偏离炒作背后非常真实的实质,这是非常琐碎的。

随着社会变革开始影响各种行业、学科、艺术家、音乐流派等。试图保护自己的区域不受外界影响是人类非常自然的反应。在历史上,臭名昭著的勒德分子对纺织机械的抵制,摇滚乐会撕裂社会道德结构的呼声,以及对伽利略提出的日心说的暴力反对,都可以看到这样的例子。然而,在数据科学的情况下,更合适的做法是,该领域开始避免这些情绪冲动,转而支持更理性、更有计算能力的评估,这种评估自然会否定精英主义,拥抱颠覆。

高斯混合模型(GMM)

原文:https://towardsdatascience.com/gaussian-mixture-modelling-gmm-833c88587c7f?source=collection_archive---------0-----------------------

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

内部 AI

使用无监督学习理解文本数据

在之前的帖子中,我讨论了 k-means 聚类作为总结文本数据的一种方式。我还谈到了 k-means 的一些局限性,以及在什么情况下它可能不是最合适的解决方案。可能最大的限制是每个聚类都有相同的对角协方差矩阵。这就产生了球形集群,就它们能够建模的分布类型而言,球形集群是相当不灵活的。在这篇文章中,我想解决其中的一些限制,并特别谈谈一种可以避免这些问题的方法,【GMM】。这篇文章的格式将与上一篇非常相似,在上一篇文章中,我解释了 GMM 背后的理论以及它是如何工作的。然后,我想深入研究用 Python 编写算法,我们可以看到结果与 k-means 有何不同,以及为什么使用 GMM 可能是一个很好的替代方案。

GMM 变得简单了

最简单地说,GMM 也是一种聚类算法。顾名思义,每个聚类都是根据不同的高斯分布建模的。这种灵活的概率数据建模方法意味着,我们有软分配,而不是像 k-means 那样将硬分配到聚类中。这意味着每个数据点可能是由具有相应概率的任何分布生成的。实际上,每个分布对于生成特定的数据点都有一些“责任”。

我们如何评估这种类型的模型?嗯,我们可以做的一件事是为每个数据点引入一个 潜变量 𝛾 (伽马)。这假设每个数据点都是通过使用潜在变量 𝛾 的一些信息生成的。换句话说,它告诉我们哪个高斯函数生成了一个特定的数据点。然而,在实践中,我们没有观察到这些潜在的变量,所以我们需要估计它们。我们该怎么做?对我们来说幸运的是,已经有一种算法可以在这种情况下使用,即 期望最大化(EM)算法 ,这是我们接下来要讨论的。

EM 算法

EM 算法由两个步骤组成,E 步骤或期望步骤和 M 步骤或最大化步骤。假设我们有一些潜在变量 𝛾 (它们未被观察到,用下面的向量 z 表示)和我们的数据点 X 。我们的目标是在给定参数的情况下最大化 X 的边际可能性(由向量 θ 表示)。本质上,我们可以找到作为 X 和 Z 的结合点的边际分布,并对所有 Z 求和(概率求和规则)。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 1: Marginal Likelihood with Latent variables

上述等式通常会产生一个难以最大化的复杂函数。在这种情况下我们能做的就是用 简森斯不等式构造一个下界函数这样就更容易优化了。如果我们通过最小化两个分布之间的 KL 散度(间隙)来优化这一点,我们可以近似原始函数。这个过程如下图 1 所示。我也在上面提供了一个视频链接,展示了 KL 散度的推导,给那些想要更严格的数学解释的人。**

为了从本质上评估我们的模型,我们只需要执行两个步骤。在第一步(E-step)中,我们希望根据我们的权重(π)均值()和高斯分布的协方差(σ)来估计我们的潜在变量𝛾的后验分布。参数向量在图 1 中表示为θ。估计 E-step 需要首先初始化这些值,我们可以用 k-means 来完成,这通常是一个很好的起点(在下面的代码中有更多的介绍)。然后,我们可以进入第二步(m 步),使用𝛾使参数θ的可能性最大化。重复这个过程,直到算法收敛(损失函数不变)。

可视化 EM 算法

为什么我们不试着用图 1 来形象化这个过程呢?我们在第一步中计算𝛾的后验分布,它相当于我们通过最小化两个分布之间的 KL 散度得到的值。然后,我们将后验概率设为 q(我知道这是一个令人困惑的符号,但这只是𝛾),并最大化关于参数θ的函数。从图中我们可以看到,当我们迭代和执行这些计算时,我们向最优(或者至少是局部最优)移动。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Note: Theta is a vector of all parameters, Source: Bayesian Methods for Machine Learning

GMM 的 EM 算法

电子步骤

好了,现在我们已经看到了 EM 算法在做什么,我想概述并解释一下我们需要在 E 步和 M 步中计算的方程。在编写代码的时候,这些真的很重要。我们可以将高斯混合分布写成权重等于π的高斯分布的组合,如下所示。其中 K 是我们要建模的高斯数。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 2: Gaussian Mixture Distribution

采用上述结果,我们可以使用下面的公式计算每个高斯函数对于每个数据点的责任的后验分布。这个等式就是贝叶斯规则,其中π是先验权重,并且似然性是正态的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 3: Posterior Responsibilities using Bayes Rule

M 步

在计算后验概率之后,我们需要做的就是得到由下面的等式定义的每个高斯参数的估计,然后评估对数似然。然后重复这两个步骤,直到收敛。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 4: Mean of the Gaussians

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 5: Covariance of the Gaussians

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 6: weights

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 7: Sum of responsibilities in each Gaussian k

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 8: Marginal Likelihood: This is what we want to maximise

请记住,我们已经以这样的方式设置了问题,我们可以最大化下限(或最小化分布之间的距离),这将近似于上面的等式 8。我们可以把我们的下界写成如下,其中 z 是我们的潜在变量。请注意,我们的求和现在出现在对数之外,而不是对数之内,这导致表达式比等式 8 简单得多。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Equation 9: Variational Lower Bound, Source: Bishop equation 9.74

Python 代码

既然我们已经解释了建模背后的理论,我想用 Python 来编写这个算法。像我之前的帖子一样,我将使用相同的数据集,这样我们可以比较 k-means 和 GMM 的结果。预处理步骤与前一篇文章中的步骤完全相同,我在这篇文章的结尾提供了完整代码的链接。

k 均值估计

正如我之前提到的,为了开始算法(执行第一步),我们需要参数的初始值。与其随机设置这些值,不如使用 k-means 来估计它们。这通常会给我们一个好的起点,并可以帮助我们的模型更快地收敛。在我们估计 GMM 之前,让我们快速看一下 k-means 给出了什么样的聚类。

sklearn k-means

使用来自 sklearn 的估计,我们可以创建一个很好的集群可视化(图 2)。请注意,这些集群都是球形的,大小相同。球形聚类似乎没有很好地模拟数据的分布,这表明在这种特定情况下 k-means 可能不是最佳方法。这说明了 k 均值的局限性之一,因为所有协方差矩阵都是单位方差的对角矩阵。这个限制意味着该模型不是特别灵活。记住这一点,让我们试试 GMM,看看会给我们带来什么样的结果。

Source: Python for Data Science Handbook

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 2: k-means spherical Gaussians

GMM 估计

下面的图 3 展示了 GMM 正在做的事情。它清楚地显示了由三种不同的高斯分布建模的三个集群。我在这里用了一个玩具数据集来清楚地说明这一点,因为安然的数据集不太清楚。如你所见,与图 2 中使用球状星团建模相比,GMM 更加灵活,使我们能够生成更好的拟合分布。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 3: GMM example: simple data set: Full Covariance

GMM Python 类

好了,现在我们将直接用 Python 来编写我们的 GMM 类。和往常一样,我们从一个 init 方法开始。我在这里初始化的唯一的东西是我们想要运行我们的算法的次数和我们想要建模的集群的数量。这段代码中最有趣的方法是calculate _ mean _ 协方差*** 。这有助于我们计算初始参数的值。它接受我们的数据以及来自 k-means 的预测,并计算每个聚类的权重、均值和协方差矩阵。***

下一段代码实现了我们的 initialise _ parameters 方法,该方法使用 sklearn 库中的 k-means 来计算我们的聚类。注意,这个函数实际上调用了上面定义的 calculate _ mean _ 协方差方法。我们可能已经使用了一种方法来计算我们的集群和初始参数,但是如果每种方法只执行一个特定的任务,通常会更容易调试和避免错误。

是时候进入我们班上最重要的方法了。算法的 E-step 在下面定义,并接受我们的参数和数据,这对于我们上面定义的方程来说是非常有意义的。记住,这一步的目的是计算我们责任的后验分布(𝞬).)这里要注意的主要问题是,我们循环遍历每个 C 高斯函数(在我们的例子中是 3 个),并使用 scipy 中的函数计算后验概率,以计算多元正态 pdf。

**from scipy.stats import multivariate_normal as mvn**

在我们为每个高斯计算了这个值之后,我们只需要归一化伽马(𝞬),对应于等式 3 中的分母。这是为了确保我们的伽玛是有效概率。如果我们对每个数据点的聚类值求和,它们应该等于 1。

在我们计算出职责(𝞬)的值后,我们可以将这些值输入到 M-step 中。 同样,M 步的目的是使用 E 步的结果计算我们的新参数值,对应于等式 4、5 和 6。为了使调试更容易,我在下面的代码中分离了 m_step 方法和compute _ loss _ function方法。compute_loss_function 正如其名称所暗示的那样。它接受 E-step 和 M-step 返回的责任和参数,并使用这些来计算等式 9 中定义的下限损失函数。

我们所有最重要的方法现在都被编码了。与 sklearn 保持一致,我将定义一个 fit 方法,它将调用我们刚刚定义的方法。特别是,我们从初始化参数值开始。在此之后,我们执行 EM 算法中概述的步骤,以选择迭代次数。请注意,实际上并不需要大量的迭代来收敛,特别是当您使用 k-means 来获得初始参数的值时(我认为我的算法在大约 30 次迭代中就收敛了)。

因为我们可能也对使用这个模型来预测高斯新数据可能属于什么感兴趣,所以我们可以实现一个 预测预测 _proba 方法。predict_proba 方法将接受新的数据点,并预测每个高斯函数的责任。换句话说,这个数据点来自每个分布的概率。这就是我在文章开头提到的软任务的本质。predict 方法基本上做同样的事情,但是使用 np.argmax 来分配具有最高概率的聚类。

符合我们的模型

解释完之后,我认为是时候评估我们的模型了,看看我们会得到什么。希望上面的 GMM 视觉化提供了一个关于模型正在做什么的很好的直觉。我们将对我们的安然数据集做完全相同的事情。下面的代码只是使用 3 种不同的高斯模型在我们的数据集上估计我们的 GMM 模型。为了绘图的目的,我还计算了每个分布的最高密度点,对应于中心,这有助于可视化。最后,我们还使用模型参数来绘制图 4 中每个分布的形状。

该图的主要结论是,分布显然不再是球形的。GMM 允许我们放松对协方差矩阵的限制,使分布更好地符合数据。考虑到我们的数据显然不是球形的,这一点特别有用。现在,这可能不是一个完美的解决方案,有一些数据点不适合任何分布,但它是对 k 均值的改进。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 4: GMM with Full covariance

GMM sklearn 实施

现在,为了确保我们没有在代码中做任何完全疯狂的事情,我将使用 sklearn 重新做这个估计,看看我的解决方案是否相同。下面的代码与上面的代码几乎完全相同,所以我不会详细介绍。看起来我们和 sklearn 的结果非常相似。唯一不同的是,我们的一个集群中心似乎是不同的。在 sklearn 实现中,中心接近 0.4,而在我们的实现中,中心接近 0.6。或许这是由于 sklearn 中的初始化略有不同?

sklearn GMM

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Figure 5: GMM sklearn

好了,伙计们,这是这篇文章。我希望这是对高斯混合模型的一个有用且非常直观的解释。如果你们中的任何人想要更深入地理解这些材料,我推荐 Coursera 课程 机器学习的贝叶斯方法 我从本课程中获取了大量资料,我认为它对我在这里提出的概念进行了非常好和深入的解释。我还要推荐主教**模式识别与机器学习这本书。这本书是你在机器学习中会遇到的大多数经典算法的绝佳参考。下面我提供了帖子中概述的 GMM 类的完整代码,以及一个到 Kaggle 内核的链接,我在那里做了所有的分析。一如既往,欢迎反馈。

GMM 类的完整 Python 代码

链接完整代码:https://www.kaggle.com/dfoly1/gaussian-mixture-model

来源:Christopher m . Bishop 2006,模式识别与机器学习

来源: 机器学习的贝叶斯方法:Coursera 课程

来源:Python for Data Science Handbook

请注意,上面的一些链接是附属链接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值