使用 python 成为视频分析专家
Courtesy: pixabay
如果告诉你这不是用机器学习或者深度学习生成的呢!
为任何运动生成精彩场面的任务都是相当乏味和困难的。它涉及许多技术和其他软件、硬件需求。你也需要投入大量的时间去做。如果你对这样的视频公司或部门感兴趣,那就没问题,但如果你是像我一样的普通用户,你就不会想这么做。我将使用 python 在几个步骤中完成这项工作。不会涉及任何外部软件。只是纯粹的 python,而且,我们不必依赖机器学习或深度学习技术来做到这一点。我们将使用语音分析来完成这项任务。
当比赛中发生令人兴奋的事情时,解说员的声音就会提高。让我们以板球为例。每当击球手击球或投球手击球时,解说员的声音就会提高。在那场比赛中,观众和解说员的音调都很高。我们可以利用音频的这些变化从视频中捕捉有趣的瞬间。
我们将遵循的流程补充如下:
- 输入视频文件
- 提取音频
- 将音频分成几块
- 计算每个块的短时能量
- 将每个块分类为兴奋或不兴奋(基于阈值)
- 合并所有精彩片段,形成视频集锦
- 生成最终的视频集锦
记住,我们不会在这里使用任何外部软件。
1。下载视频输入:https://bit.ly/2lKfVa6
你也可以选择你最喜欢的运动的任何其他视频。
2。提取音频:
为了提取音频和对视频文件进行其他操作,我们将使用一个名为 moviepy 的包。
a.安装软件包。
pip install moviepy
b.在此代码中输入文件路径。
# AudioExtract.pyimport moviepy.editor as mpclip = mp.VideoFileClip("filepath//videoplayback.mp4").subclip(1, 1380)
clip.audio.write_audiofile("filepath//audio.wav")
在上面的代码中, mp 视频文件剪辑是我们输入视频文件的地方。
子剪辑*,我们指定我们需要音频的时间限制。如果您想要完整的剪辑音频,则不需要*
audio.write_audiofile ,我们在这里指定输出路径和文件名。
就这样,我们通过两个简单的步骤提取了音频。
3。将音频分成几块:
我们将使用刚刚在步骤 2 中提取的音频。
librosa 是一个用于音乐和音频分析的 python 包。
import librosa
import IPython.display as ipdfilename = "filepath\\audio.wav"# loading the file with a sampling rate
x, sr = librosa.load(filename, sr=16000)# To get duration of the audio clip in minutes
int(librosa.get_duration(x, sr) / 60)# Dividing into chunks of 5 seconds
max_slice = 10
window_length = max_slice * sr# Playing the audio chunk
a = x[21 * window_length:22 * window_length]
ipd.Audio(a, rate=sr)
在这里,我将采样率设置为 16000,您可以根据自己的方便和需要进行设置。现在,我已经将音频分成每个 10 秒的块,因为我们想要找出特定的音频块是否包含音频声音的上升。您可以根据自己的选择设置时间限制,对不同的输出尝试不同的时间限制。此外,我们将播放音频块只是为了检查。
4。计算每个组块的短时能量:
import numpy as np
s_energy = np.array([sum(abs(x[i:i + window_length] ** 2)) for i in range(0, len(x), window_length)])
音频信号的能量或功率是指声音的响度。它是通过时域中音频信号幅度的平方和来计算的。当对整个音频信号的块计算能量时,它被称为短时能量。
让我们画出来:
import matplotlib.pyplot as plt
plt.hist(s_energy)
plt.show()
5。将每个组块分类为兴奋与否(基于阈值):
import pandas as pd
df = pd.DataFrame(columns=['energy', 'start', 'end'])
thresh = 180
row_index = 0
for i in range(len(s_energy)):
value = energy[i]
if value >= thresh:
i = np.where(s_energy == value)[0]
df.loc[row_index, 'energy'] = value
df.loc[row_index, 'start'] = i[0] * 10
df.loc[row_index, 'end'] = (i[0] + 1) * 10
row_index = row_index + 1
我正在根据能源数据创建一个数据框架。我选择的阈值是 180,你可以根据自己的选择来选择,也可以看上面的图表。然后将音频剪辑的连续时间间隔合并成一个:
temp = []
i = 0
j = 0
n = len(df) - 2
m = len(df) - 1
while (i <= n):
j = i + 1
while (j <= m):
if (df['end'][i] == df['start'][j]):
df.loc[i, 'end'] = df.loc[j, 'end']
temp.append(j)
j = j + 1
else:
i = j
break
df.drop(temp, axis=0, inplace=True)
print(df)
6。合并所有精彩片段,形成视频集锦
start = np.array(df['start'])
end = np.array(df['end'])
for i in range(len(df)):
if i != 0:
start_lim = start[i] - 10
else:
start_lim = start[i]
end_lim = end[i]
filename = "highlight" + str(i + 1) + ".mp4"
ffmpeg_extract_subclip("filepath//videofile.mp4", start_lim, end_lim, targetname=filename)
这段代码将根据振幅从每个块中生成突出显示,然后将这些文件保存到您的系统中。你还没有完成,你必须将所有的 14 文件合并成一个文件来创建高光。
7。生成最终的视频集锦
# Final_joined.pyfrom moviepy.editor import VideoFileClip, concatenate_videoclipsclip1 = VideoFileClip("filename\\highlight1.mp4")
clip2 = VideoFileClip("filename\\highlight2.mp4")
clip3 = VideoFileClip("filename\\highlight3.mp4")
clip4 = VideoFileClip("filename\\highlight5.mp4")
clip5 = VideoFileClip("filename\\highlight6.mp4")
clip6 = VideoFileClip("filename\\highlight7.mp4")
clip7 = VideoFileClip("filename\\highlight8.mp4")
clip8 = VideoFileClip("filename\\highlight9.mp4")
clip9 = VideoFileClip("filename\\highlight10.mp4")
clip10 = VideoFileClip("filename\\highlight11.mp4")
clip11= VideoFileClip("filename\\highlight12.mp4")
clip12 = VideoFileClip("filename\\highlight13.mp4")
clip13= VideoFileClip("filename\\highlight14.mp4")
clip14= VideoFileClip("filename\\highlight4.mp4")final_clip = concatenate_videoclips([clip1,clip2,clip3,clip4,clip5,clip6,clip7,clip8,clip9,clip9,clip10,clip11,clip12,clip13,clip14])
final_clip.write_videofile("filepath\\Final_Highlights.mp4")
我们使用 concatenate 将所有文件合并成一个文件。在此之后,将生成最终文件。
你可以在这里获得【https://bit.ly/2lDEd5V】视频演示:
如果您遇到任何错误或需要任何帮助,您可以随时在 LinkedIn 上评论或 ping 我。
LinkedIn:https://bit.ly/2u4YPoF
我希望这有助于增强你的知识基础:】
关注我了解更多!
感谢您的阅读和宝贵时间!
使用 stylecloud 生成现代时尚的 Wordcloud
几乎每个需要从文本语料库中寻找见解的文本分析项目都会包含一个词云。但正如你们中的许多人所记得的,在数据科学家的脑海中,单词云有一个非常无聊的图像和感知,我们总是试图美化它们——最终在这个过程中放弃——除了少数人会选择一些掩蔽图像,然后试图让单词云变成那个形状。这是我们大多数人使用单词云的最高水平。
但在内心深处,我们所有人都一直希望有现代时尚美丽的文字云。这个愿望在 Max Woolf(以[minimaxir](https://github.com/minimaxir/)
闻名)的新 python 包[stylecloud](https://github.com/minimaxir/stylecloud)
中实现了
78%的数据营折扣
关于 styelcloud
stylecloud
是一个 Python 包,它利用了流行的 word_cloud 包,添加了有用的功能来创建真正独特的单词云,包括渐变和图标形状。
stylecloud 安装
stylecloud
仅一个pip
之遥
pip3 install stylecloud
样式云—基础知识
stylecloud
提供了两种方式来生成 style-wordcloud:
- 作为一个 CLI 命令,可以从您的终端/Shell/Cmd 提示符调用来生成 wordcloud(快速而时尚)
- 导入
stylecloud
包并在代码中使用stylecloud()
创建 wordcloud 的典型 pythonic 方式
示例文本文件
对于本文,我们将考虑美国前总统巴拉克·奥巴马在 2008 年当选为总统时的标志性就职演说。
从这里下载文件2009–01–20-inaugural-address.txt
样式云— CLI
简单地说,打开你的Terminal
或Command Prompt
并尝试下面的命令stylecloud
指向我们上面下载的文件
stylecloud --file_path 2009-01-20-inaugural-address.txt
来自stylecloud
的这个简单命令产生了这个美丽的图(自动保存在与stylecloud.png
相同的当前目录中)
这是简单,快速和美丽的不是吗?⚑
样式云-在 Python 脚本中
CLI 中的 stylecloud 是针对普通人类的,而我们是喜欢用Python
编码的coders
。所以让我们用下面两行代码在 Python 中构建同样的东西。
import stylecloudstylecloud.gen_stylecloud(file_path = "2009-01-20-inaugural-address.txt")
样式云—定制
现在,假设我们不希望它是国旗的形状,而是 twitter 标志的形式。毕竟这是互联网时代,不是吗?代码中的一点点变化——只需一个新的参数来给特定的字体图标命名,就能让我们得到奥巴马演讲的 twitter 形状的风格云。
stylecloud.gen_stylecloud(file_path = "2009-01-20-inaugural-address.txt", icon_name= "fab fa-twitter")
现在,让我们改变一点调色板,也是一个黑暗的主题(这是每个人都喜欢这些天)
stylecloud.gen_stylecloud(file_path = "2009-01-20-inaugural-address.txt", icon_name= "fab fa-twitter", palette="cartocolors.diverging.TealRose_7", background_color="black")
**那是真正的黑暗!**如果你是 Linkedin(而不是 Twitter)的粉丝,不要离开你——这是你的 Linkedin 图标形状的风格——word cloud
摘要
感谢 Max Woolf ,我们被赋予了这个神奇的库 stylecloud 。我们很快就学会了如何使用这个时髦的 wordcloud 生成器,既可以作为 CLI 工具,也可以使用 Python 脚本。PNG 文件和笔记本可以在这里找到。
当您没有足够的数据时,生成更多的训练数据
找出可以用来增加你的训练数据的技巧。
Photo album (Photo by True Agency on Unsplash)
计算机在图像和物体识别方面胜过人类。
像谷歌和微软这样的大公司已经在图像识别方面击败了人类基准[1,2]。平均而言,人类在图像识别任务中犯错误的概率约为 5%。截至 2015 年,微软的图像识别软件达到了 4.94%的错误率,而大约在同一时间,谷歌宣布其软件实现了 4.8%的降低错误率[3]。
这怎么可能呢?
这是通过在包含数百个对象类别的 ImageNet 数据集的数百万个训练样本上训练深度卷积神经网络而实现的[1]。
一百万训练数据!
German 1 million mark stamp (Image by Hebi B. from Pixabay)
“例如,要教会一台计算机从多个角度识别一只猫,可能需要覆盖各种视角的数千张照片。”- 汤姆·西蒙尼特
Image of a cat (Photo by Mikhail Vasilyev on Unsplash)
为了成功地训练用于计算机视觉任务的深度卷积神经网络,需要大量的数据。这是因为这些神经网络有多个隐藏的处理层,随着层数的增加,它需要学习的样本数量也增加。如果没有足够的训练数据可用,模型往往会学习训练数据太好。这叫做过度拟合。如果一个模型过拟合,它的概括能力就很差,因此它对看不见的数据的表现就很差。
但是如果没有庞大的训练数据呢?
对于我们手头的所有图像识别任务来说,没有必要有数百万个训练样本。对于某些任务,收集成千上万的示例图像甚至是一个挑战。这通常是医学图像的情况,例如用于乳腺癌检测和定位的乳房 x 线照相术、用于肺癌检测的胸部 x 射线或用于定位脑瘤的 MRI 扫描。
归结为一个问题。
当我们只有有限的数据时,我们如何训练一个模型来很好地执行这些任务?
通过使用增强生成更多的训练数据
当我们只有少量的图像数据来训练深度卷积神经网络时,我们可以使用数据增强技术从我们已经拥有的数据中生成更多的训练数据。
Multiple images of a person
数据扩充是一种为原始图像生成多个图像的技术。有几种不同的数据增强技术,Mikolajczyk 和 Grochowski 在他们的论文[4]中将这些技术分为两个子类别:使用基本图像处理的数据增强和使用深度学习方法的数据增强。
Data augmentation techniques [4]
几何变换
诸如翻转、裁剪、旋转和平移等几何变换是一些常用的数据扩充技术。我们将在本帖中简要讨论它们。
轻弹
Original image of a dog on the left, horizontally flipped image about centre on the right [6]
翻转是拍摄任何给定图像的镜像。这是最简单的增强技术之一。图像可以水平或垂直翻转。然而,水平翻转在两者中更为常见。
种植
Orignal and random cropped image of a cat [7]
裁剪是一种数据扩充技术,通过裁剪边界像素来减小原始图像的大小。裁剪时不会保留空间维度。在这种类型的数据扩充中,不能保证转换后的图像与原始图像属于相同的输出标签。
在上面的图像中,通过从左和右裁剪像素,从原始图像生成了四个图像。裁剪后的图像尺寸从 256x256 缩小到 227x277。
旋转
Orignal and rotated image of a cat [8]
图像可以在 1 到 359 度之间的轴上向左或向右旋转。1 到 20 度之间的旋转被称为轻微旋转,并且可以是用于扩充原始图像的有用技术。随着旋转角度的增加,转换后的数据可能无法保留其原始标签。
翻译
Original and translated images of a tennis ball [8]
翻译是一种将图像向左、向右、向上或向下移动的技术。这是一种非常有用的转换技术,可以避免数据中的位置偏差。当图像被移动时,剩余的空间或者被 0、255 填充,或者被随机噪声填充,从而保持图像的原始大小。
基于 GAN 的数据增强
生成性对抗网络(GAN)也称为 GAN,是一种生成性建模技术,其中从数据集创建人工实例,其方式是保留原始集的相似特征[9]。
GAN 由两个相互竞争的人工神经网络(ann)组成,即生成器和鉴别器。第一个创建新的数据实例,而第二个评估它们的真实性[10]。
这是由 GAN 生成的人脸图像,它是在人脸上训练的。请注意,这些是合成的脸,而不是真人。
Synthetic faces generated by GAN trained on human images [10]
这些是为了从有限的数据集中生成更多的数据,从而可以训练更有效的卷积神经网络而通常使用的一些数据扩充技术。
Olaf 和他的团队使用显微图像上的移位、旋转和随机弹性变形等数据增强技术来训练 U-net 架构模型,同时只有有限的训练数据,并在这些类别中以较大优势赢得了 2015 年 ISBI 细胞追踪挑战赛[11]。
因此,下次你在训练卷积神经网络时对数据进行排序时,使用这些技术来创建更多的数据。
您使用过哪些数据增强技术?在下面的评论中分享你的想法。
来源:
[1]https://www.eetimes.com/document.asp?doc_id=1325712
[2]https://venturebeat . com/2017/12/08/6-人工神经网络胜过人类的领域/
[4] Mikolajczyk,a .,& Grochowski,M. (2018 年)。图像分类问题中改进深度学习的数据增强。 2018 国际跨学科博士研讨会(Iiphdw) 。doi:10.1109/iiphdw。56686.88868888666
[5]佩雷斯,l .,&王,J. (2017)。使用深度学习的图像分类中数据扩充的有效性。斯坦福大学研究报告。
https://snow.dog/blog/data-augmentation-for-small-datasets
https://www.learnopencv.com/understanding-alexnet/
[9]肖滕,c .,& Khoshgoftaar,T. (2019 年)。面向深度学习的图像数据增强综述。大数据期刊、 6 (1)。doi:10.1186/s 40537–019–0197–0
[10]亨里克,f .,&阿拉尼亚,C. (2019 年)。使用 GANs 的数据扩充。机器学习研究论文集。
[11] Ronneberger,o .,Fischer,p .,& Brox,T. (2015 年)。生物医学图像分割的卷积网络。
利用深度学习生成钢琴器乐
通过试验 Tensorflow v2.0 Alpha 逐步生成钢琴音乐
Photo by Marius Masalar on Unsplash
大家好!终于可以在我的介质上再次写作,有空闲时间做一些人工智能(AI)的实验了。这一次,我将写下并分享如何利用深度学习生成音符。与我的上一篇关于生成歌词的文章不同,这次我们将生成音乐的音符并生成文件(MIDI 格式)。
Photo by Malte Wingen on Unsplash
音乐的主题是钢琴。本文将使用递归神经网络(RNN)、门控递归单元(GRU)的变体,在自我注意的帮助下生成钢琴音符。这篇文章不仅将告诉如何生成音符,这篇文章还将告诉如何生成一个适当的 MIDI 文件,也可以在电脑上播放。
这篇文章的目标读者是对人工智能感兴趣的人,尤其是想练习使用深度学习的人。我希望通过发表这篇文章来提高我的写作技巧,并且内容对你有益😃。
如果您想了解完整的源代码,本文末尾有一个 Github 链接。现在,我将给出 python 笔记本和资源库中的协作链接。
下面是开场音乐
Sound 1 : Opening Piano 😃
(音乐是由我们将在本文中创建的模型生成的)
概述
- 介绍
- 技术和数据
- 管道
- 预处理 MIDI 文件
- 火车模型
- 推理并生成 MIDI 文件
- 结果
- 结论
- 编后记
介绍
当前人工智能领域的一个热门话题是如何仅使用数据(无监督的)来生成东西。在计算机视觉领域,有许多研究人员正在研究利用生成式 Advesarial Network (GAN)生成图像的先进技术。例如,NVIDIA 通过使用 GAN 创建了逼真的人脸生成器。还有一些利用甘生成音乐的研究。
Photo by Akshar Dave on Unsplash
如果我们谈论音乐生成器的值,它可以用来帮助音乐家创作他们的音乐。它能增强人的创造力。我认为在未来,如果在这个领域有很多高关注度,大多数音乐人将在人工智能的帮助下创作音乐。
这篇文章将集中在如何通过在音乐中生成连续的音符来生成音乐。我们将知道如何对数据进行预处理,并将其转换为神经网络的输入来生成音乐。
实验还将使用 Tensorflow v2.0 (仍在 alpha 阶段)作为深度学习框架。我想展示的是通过遵循他们的一些最佳实践来测试和使用 Tensorflow v2.0。Tensorflow v2.0 中我喜欢的一个特性是,通过使用他们的亲笔签名,它确实加速了模型的训练。可以通过使用@tf.function
定义我们的函数来使用。此外,不再有“tf.session ”,也没有全局初始化。这些特点是我从 Tensorflow 转到 PyTorch 的原因之一。Tensorflow 的可用性对我来说并不好。然而,在我看来 Tensorflow v2.0 改变了这一切,增加了它们的可用性,使做一些实验变得更舒适。
这个实验还使用了自我关注层。自我注意层会告诉我们,给定一个连续的实例(例如在音乐音符“C D E F G”中),每个记号会学习到那个记号对其他记号的影响有多大。以下是一些示例(针对 NLP 任务):
Image 1 : Visualization of attention. Taken from : http://jalammar.github.io/illustrated-transformer/
关于自我关注的进一步信息,尤其是关于变形金刚的,你可以看这篇牛逼的文章。
事不宜迟,让我们继续创作音乐
技术和数据
本实验将使用:
- Tensorflow v2.0:深度学习框架,Tensorflow 的新版本,仍处于 alpha 开发阶段。
- Python 3.7
- 合作实验室:免费的 Jupyter 笔记本环境,无需设置,完全在云中运行。有 GPU 特斯拉 K80 甚至 TPU!遗憾的是,在撰写本文时,Tensorflow v2.0 alpha 仍不支持 TPU。
- Python 库 pretty_midi :操作和创建 midi 文件的库
对于数据,我们使用 Magenta 的 MAESTRO (为同步轨道和组织编辑的 MIDI 和音频)作为数据集。该数据集仅包含钢琴乐器。我们将从大约 1000 首音乐中随机抽取 100 首,以加快我们的训练时间。
管道
下面是我们的音乐生成器将如何工作的管道:
Image 2 : Pipeline
我们将看到每一个过程。为简单起见,我们将每个流程划分如下:
- 预处理待输入神经网络的 MIDI 文件
- 培训过程
- 生成 MIDI 文件
预处理 MIDI 文件
在我们进入如何预处理 midi 文件之前,我们需要知道什么是 Midi 格式文件。
从pcmag中,MIDI 的定义:
乐器、合成器和计算机之间交换音乐信息的标准协议。开发 MIDI 是为了允许一个合成器的键盘演奏另一个合成器产生的音符。它定义了音符以及按钮、拨号盘和踏板调整的代码,MIDI 控制信息可以编排一系列合成器,每个合成器演奏乐谱的一部分。MIDI 版本 1.0 于 1983 年推出。
总之,MIDI 文件包含一系列带有音符的乐器。比如钢琴和吉他的结合。每种乐器通常有不同的音符来演奏。
对于 MIDI 文件的预处理,Python 中有一些库可以用来完成。其中一个就是[*pretty_midi*](https://github.com/craffel/pretty-midi)
。它可以操作 MIDI 文件,也可以创建一个新的。在本文中,我们将使用这个库。
对于pretty_midi
, midi 文件的格式如下:
Image 3 : PrettyMidi format
****开始是以秒为单位弹奏的音符的开始。结束是一个音符在一秒钟内演奏完毕。一次可以有多个音符重叠。 Pitch 是弹奏的音符的 MIDI 编号。力度是弹奏音符的力度。
关于 MIDI 编号和音符名称之间的关系,可以参考下图:
Image 4 : Midi Number with the Note Name. Taken from https://newt.phys.unsw.edu.au/jw/notes.html
读取 Midi 文件
我们将成批读取 midi 文件。这是我们如何使用 pretty_midi 读取它:
midi_pretty_format = pretty_midi.PrettyMIDI('song.mid')
我们会得到PrettyMidi
的对象。
钢琴卷帘窗阵列预处理
Image 5 : PrettyMidi to Piano Roll Array
对于本文,我们需要从乐器中提取音乐的所有音符。许多 MIDI 文件的音乐中有多种乐器。在我们的数据集中,MIDI 文件只包含一种乐器,即钢琴。我们将从钢琴乐器中提取音符。为了使它更容易,我们将提取每秒所需帧的笔记。pretty_midi
有一个方便的函数get_piano_roll
来获取(notes, time)
维数组中二进制 2D numpy.array 的注释。notes
长度为 128,time
跟随音乐的持续时间除以 FPS。
源代码我们是如何做到的:
midi_pretty_format = pretty_midi.PrettyMIDI(midi_file_name)
piano_midi = midi_pretty_format.instruments[0] # Get the piano channels
piano_roll = piano_midi.get_piano_roll(fs=fs)
对时间和笔记字典进行预处理
Image 6 : Piano Roll Array to Dictionary
当我们得到钢琴卷首的数组后,我们把它们转换成字典。字典将从弹奏音符的时间开始。例如,在上面的图片中,我们从 28 开始(如果我们转换到第二,假设我们以 5 fps 转换到 piano_roll,音乐在 5.6 秒开始播放它的音符,这可以通过 28 除以 5 得到)。
创建字典后,我们将把字典的值转换成字符串。例如:
array([49,68]) => '49,68'
为此,我们应该循环字典的所有键并更改其值:
for key in dict_note:
dict_note[key] = ','.join(dict_note[key])
对待输入音符列表和神经网络目标进行预处理
Image 7 : Dictionary to List of Sequences
在我们得到字典后,我们将把它转换成音符序列,作为神经网络的输入。然后我们得到下一个时间步长作为神经网络的输入目标。
Image 8 : Sliding window, taken from : https://towardsdatascience.com/generating-drake-rap-lyrics-using-language-models-and-lstms-8725d71b1b12
在本文中,序列表的长度为 50。这意味着如果我们的 fps 是 5,我们将得到一个包含 10 (50 / 5)秒播放时间的序列。****
列表中的“e”表示在这段时间内没有音符被演奏。因为有时在每个弹奏音符之间会有跳跃或者没有弹奏音符。在图 7** 的例子中,我们可以看到从 43 到 46 有一个跳跃。如果我们转换序列,序列列表将是:**
[ ... '61,77', '61,77', 'e', 'e', '73' , ...]
我们怎么做?我们将在一批音乐中处理音符。
我们使用一个 50 长的推拉窗。对于音乐中的第一个音符,我们将在列表中添加“e”49 次。然后将开始时间设置为字典中的第一个时间步长。在图 7** 中的例子中,是 28 。然后,我们添加音乐中的第一个音符(例如“77”)。**
然后,对于下一个实例,我们将窗口滑动一次,将“e”追加到列表中 48 次,追加在时间步长 28 中播放的音符,在时间步长 29 中追加音符,并重复,直到音乐结束。****
对于下一首音乐,我们重复上述过程。
这是源代码:
创建笔记标记器
在我们深入研究神经网络之前,我们必须创建标记化器,将顺序笔记转换为笔记的顺序索引。首先,我们应该将票据映射到一个表示票据 id 的索引中。
例如:
{
'61,77' : 1, # 61,77 will be identified as 1
'e' : 2,
'73' : 3,
.
.
}
因此,如果我们之前的输入如下:
[ ... , '61,77', '61,77', 'e', 'e', '73' , ...]
我们将其转换为:
[ ... 1, 1, 2, 2, 3 , ...]
这是我们的做法。
总结我们的预处理函数,下面是我们将使用的函数:
火车模型
在我们了解如何使用 Tensorflow v2.0 的新功能进行训练之前,我们将看到如下架构:
神经网络体系结构
Image 9 : Our Neural Network Architecture
因此,深度学习架构将使用 3 层门控递归单元(GRU,递归神经网络的一种变体)和一些自我关注层。使用丢弃是为了使神经网络不会过拟合得太快。
对于自我关注层,我们将使用这个存储库并稍加编辑,以便我们可以在 Tensorflow v2.0 上使用它。
代码:
培养
我们将通过迭代数据集中的一些音乐来更新模型的权重,并如上所述对数据进行预处理。然后,我们将一批实例中的一些实例作为神经网络的输入和目标。
我们将使用GradientTape
来更新神经网络的权重。首先,我们计算损耗,并使用apply_gradients
对其进行反向传播。如果你熟悉 PyTorch 的使用,这就是 Pytorch 训练神经网络模型的方法。
确保在功能上使用@tf.function
。这样可以把功能转换成亲笔签名,让我们的训练更快。tf.function
的一个缺点是不能使用不同大小的批次作为神经网络的输入。例如,我们的批量是 64。如果数据集的大小是 70,最后一批将包含 6 个实例。这将向程序抛出异常,因为图形将具有与初始图形不同大小的输入。也许它的工作原理是通过使用函数时看到第一个输入来创建占位符。
在本文中,我们将使用 16 个BATCH_SONG
和 96 个BATCH_NNET_SIZE
。这意味着我们将从所有音乐列表中选取 16 首音乐,然后提取其序列。然后对于神经网络中的每一步,我们从提取的序列实例中取 96 个序列作为神经网络的输入和目标。
代码如下所示:
推理并生成 MIDI 文件
Image 10 : Inference and Generate MIDI files
使用我们训练过的神经网络模型,有两种方法生成 MIDI 文件:
我们需要从一开始就做出选择:
- 我们随机生成 50 个音符作为音乐的开始。
- 我们使用 49 个空注释(’ e ‘),后跟我们选择的开始注释(例如’ 72 ',确保注释在 NoteTokenizer 中)。
Image 11 : Visualization on how the generator works
在我们选择了音乐生成器的种子之后,我们使用训练好的模型根据 50 个随机音符预测下一个音符。我们用预测值作为随机选择音符的概率分布。我们这样做,直到指定我们想要的最大序列长度。然后我们放下前 50 个音符。
在我们生成一个音符序列列表后,我们将再次将其转换为钢琴卷帘窗数组。然后将其转换为 PrettyMidi 对象。
之后,我们调整音乐的速度和节奏,最终生成 MIDI 文件。
代码:
这是如何从生成的音符编写 midi 文件:
结果
当我做的时候,训练花了 1 个小时 1 个纪元。当我这样做的时候,我决定运行 4 个纪元(4 小时)的训练。
根据经过 4 个时期训练的模型,以下是结果:
随机生成 50 张纸币
Sound 2
Sound 3
从一个音符生成
Sound 4
Sound 5
(注意,这些是从 MIDI 文件转换而来的 mp3 文件。我用在线转换器做到这一点。这份笔记似乎有点与原作的不符。如果你想听的话,我会上传原始的 MIDI。)
这些生成的音符之间存在明显的差异。如果我们从一个音符生成它,它将在演奏音符时有一个缓慢的速度开始。它不同于我们从 50 张随机纸币中产生的。它没有一个缓慢的开始。
这是选择从随机的 50 个音符开始的音乐的最后一个序列上的自我注意块的可视化:
首先关注
Image 12 : First Self Attention
第二注意
Image 13 : Second Self Attention
正如你所看到的,第一个自我注意块学习序列实例中的每个音符要关注什么音符。然而,对于第二个注意块应该关注什么还没有结果。我们还可以看出,如果其他音符的位置离当前音符很远,它将不会聚焦到当前音符(图像 12 和图像 13 中的黑色)。
结论
我们已经建立了一个工具来生成包含钢琴音乐的 MAESTRO 数据集的音乐。我们对它进行预处理,训练我们的神经网络模型,然后用它生成音乐。这些音乐是 MIDI 格式的。我们用 Tensorflow v2.0 来做。我认为 Tensorflow v2.0)的用户体验比之前的版本更好。
我们的模型生成的音乐也很连贯,很好听。它可以调整如何弹奏音符。例如:当发生器从一个音符(意味着它是音乐的开始)开始时,它以慢节奏开始。
我们可以为音乐生成器尝试一些东西。在本文中,我们对生成单个乐器进行了实验。如果音乐有多种乐器会怎样?需要有一个更好的架构来做这件事。我们可以尝试多种方法来试验音乐数据。
编后记
Photo by Alan Chen on Unsplash
这就是这篇关于生成钢琴音乐音符的文章。事实上,我写这篇文章的灵感来自于我的第一篇关于深度学习的文章,深度学习是生成音乐的歌词。“生成音乐笔记怎么样?”。我试验了一下,很好……它工作了。
对我来说,做这个实验有些困难。首先,我需要搜索什么样的文件格式便于预处理和作为神经网络的输入。我发现 MIDI 很简单,文件也很小。然后,我需要知道在 Python 中有没有可以预处理文件的库。我找到了两个,有music21
和pretty_midi
,它们的存储库没有过期。我选择pretty_midi
。也许是因为它的名字里有“漂亮”😝。最后,我需要思考如何对笔记进行预处理。谢天谢地,pretty_midi
有一个方便的功能get_piano_roll
让它变得更容易。
我也没看过多少关于音乐的研究论文。也许有研究论文可以被复制,可以在实验室里看到。
我很抱歉缺乏对自我关注层的可视化。
我欢迎任何可以提高我自己和这篇文章的反馈。我正在学习写作和深度学习。我感激能让我变得更好的反馈。确保以适当的方式给出反馈😄。
在我的下一篇文章中再见!
Source : https://cdn.pixabay.com/photo/2017/07/10/16/07/thank-you-2490552_1280.png
贮藏室ˌ仓库
** [## haryoa/note_music_generator
在 GitHub 上创建一个帐户,为 haryoa/note_music_generator 的开发做出贡献。
github.com](https://github.com/haryoa/note_music_generator)
来源
(乐器数字接口)一种标准协议,用于在音乐…
www.pcmag.com](https://www.pcmag.com/encyclopedia/term/47014/midi) [## 记下名称、MIDI 编号和频率
记下名称、MIDI 编号和频率
音符名称、MIDI 编号和频率 new . phys . UNSW . edu . au](https://newt.phys.unsw.edu.au/jw/notes.html) [## 图示的变压器
在之前的帖子中,我们研究了注意力——现代深度学习模型中普遍存在的方法。注意力是一种…
jalammar.github.io](http://jalammar.github.io/illustrated-transformer/) [## 斯库尔杜尔/古典钢琴作曲家
在 GitHub 上创建一个帐户,为 Skuldur/古典钢琴作曲家的发展做出贡献。
github.com](https://github.com/Skuldur/Classical-Piano-Composer) [## 品红
一个探索机器学习在艺术和音乐创作过程中的作用的研究项目。
magenta.tensorflow.org](https://magenta.tensorflow.org/)
https://cs224d.stanford.edu/reports/allenh.pdf
[## 有效张量流 2.0 |张量流|张量流
TensorFlow 1 中常见的使用模式。x 是“厨房水槽”策略,所有可能的计算…
www.tensorflow.org](https://www.tensorflow.org/alpha/guide/effective_tf2)**
利用数据科学生成体育排名
在本教程中,我们将介绍一种非常流行的算法,用于为运动队生成排名。我们将要学习的方法叫做科利评级。
在深入研究之前,让我们先了解为什么需要这些排名系统。简单的输赢比会不会不够?要回答这个,我们先来看一个例子。假设有一个有 4 支球队参加的锦标赛,以下是结果,以及到目前为止锦标赛的最终排名。
正如你所看到的,曼联赢得了他们的所有 6 场比赛,轻松地坐在首位,利物浦输掉了他们所有的比赛,躺在积分榜的底部。阿森纳和切尔西分享战利品,两轮都被曼联击败,两轮都击败了利物浦,然后他们平分秋色,阿森纳在第一轮获胜,切尔西在反向比赛中回敬。
现在想象我们开始第三轮比赛,前几场比赛的结果如下。第一场:阿森纳击败曼联第二场:切尔西击败利物浦
更新后的表格如下。
等等,切尔西和阿森纳真的应该得到同样的排名吗?这不是很明显吗?阿森纳击败了曼联,他们是目前为止在锦标赛中占主导地位的球队,而切尔西击败了利物浦,反正他们输掉了所有的比赛。毫无疑问,阿森纳应该得到更多的信任,排名应该高于切尔西。但是我们怎么做呢?如果我们只是用一个简单的输赢比,就没有办法区分它们。更进一步,如果你考虑净胜球之类的问题,考虑到所有比赛都是以 2 比 1 的比分获胜,我们再次陷入僵局。那么我们应该如何考虑阿森纳击败了一支比切尔西强大得多的球队,因此应该得到更高的排名。这就是排名系统发挥作用的地方。正如我们将看到的,科利排名能够理解强队被击败的事件,并相应地调整他们的排名。所以不要再拖延了,让我们开始吧。
科利排名
Wesley N. Colley 先生建议,不要像;收视率=[总胜场数]/[总游戏数],收视率应根据以下公式计算
评级= [1 +总胜场数] / [2 +总游戏数]。如果你看看这个公式,在任何锦标赛开始之前,所有球队都将以 0.5 的评分开始,因为“总胜场数”和“总游戏数”都将为零。记住这一点(事实上所有的团队都是从 0.5 分开始的),它以后会派上用场的。事实上,在所有时间内,所有团队的评分在科利算法中的平均值将始终保持在 0.5。
我们可以写出如下所示的总胜率。
仔细观察单个团队的* [总游戏数]项,我们可以说*[总游戏数] = * [1+1+1…1],其中“1”代表与每个对手进行的游戏。这反过来可以写成*[总游戏数] = [1/2 + +…].如果你还记得,就像开始时所说的,所有团队的评分都在 0.5 左右,因此所有团队的平均评分加起来也是 0.5。考虑到这一点,我们可以说[1/2 + +…]~ =[对手评分总和]。以此改写总胜率的方程式。
重写评分公式,
扩展总胜率并写下单个团队的评分,我们可以写为。
其中,rᵢ、wᵢ、lᵢ和 ti 代表感兴趣的球队的评分、胜场、负场和总场次,而∑rj 是对手评分的总和。这可以进一步重新安排为
现在,正如我的大多数其他教程一样,让我们使用一个玩具示例来更好地理解数学方程。考虑两个团队的情况。如上所述,在开始之前,它们都将具有 0.5 的评级。现在让我们假设他们互相打了一场比赛。让 rW 和 rL 分别代表赢队和输队的新评级。如果您为两个团队输入 tᵢ(等于 1),并考虑他们的 wᵢ和 lᵢ值,我们将从等式(a)获得以下结果
这代表一个简单的两变量线性系统,当求解时,我们得到新的额定值 rw = 5/8 和 rL = 3/8。对于 2 个团队,方程(A)导致两个变量的线性系统,对于 N 个团队,它将导致 N 个变量的线性系统。在编程世界中,每当我们想要解 N 个变量的线性方程时,我们总是把它们写成矩阵的形式。因此,将矩阵形式的等式(A)改写为
其中向量 r 表示所有评级 rᵢ的列向量,而向量 b 是等式(a)右侧的列向量。矩阵 C 被定义为科利矩阵,只是稍微复杂一些。想象一下,我们最喜欢的四支英超球队排成 4x4 的行列,如下图所示
科利矩阵中的对角线元素应由相应球队的数字= (2 +总游戏数)填充。因此,第一个蓝色单元格的值应该是 2 +曼联的比赛次数。同样的,切尔西、阿森纳和利物浦的比赛也将被填满。由绿色单元格表示的对角线外的元素应该具有由该单元格的行和列表示的团队之间进行的游戏次数的负值。因此,例如,与曼联行和切尔西列相交的单元格应该有一个等于曼联对切尔西比赛次数负值的数字。就这样,我们将填充科利矩阵中的剩余单元。一旦我们解了这个 N 变量线性方程 Cr = b,我们将得到’ r '的向量值,它代表每个单独团队的评级。
有了对理论背景的清晰理解,让我们继续编写一个 Python 代码来计算本文开头介绍的 toy example 的 Colley 排名。Python 代码文件以及一些支持文件可以在我的 github repo 这里找到。
在开始编码之前,让我解释一下两个支持文件的结构,即“分数”和“团队”,它们都是“逗号分隔值”格式。“团队”文件包含团队列表,旁边有一个“号码”。这个“号码”将用于在“分数”文件中代表该队。下面的截图显示了“团队”文件的内容。
所以从现在开始,无论何时我们想代表曼联,我们都会用 1 号代表他们。同样,对于切尔西、阿森纳和利物浦,我们将分别使用数字 2、3 和 4 来代表他们的分数。现在让我们在下面截图的帮助下看看分数的文件。
“分数”文件中的每一行对应一场比赛。我稍后会解释为什么这个文件有如此特殊的顺序。“分数”文件中的列表示= >列 1 =从 1/1/0000 开始玩这个特定游戏的天数。第 2 列=今天的日期。第 3 列=团队 1 号。第 4 列=球队 1‘主场’值(1 =主场,-1 =客场,0 =中立)。第 5 列=团队 1 得分。第 6 列=团队 2 号。第 7 列=球队 2‘主场’值(1 =主场,-1 =客场,0 =中立)。第 8 列=团队 2 得分。
因为在本练习中我们不会使用第 1、2、4 和 7 列的值,所以我为它们设置了虚拟数字。此外,为了方便起见,我将分数(第 5 列表示团队 1 的分数,第 8 列表示团队 2 的分数)分别记为 2 和 1,因为在本例中我们也不会查看分数。我们将用它来决定每场比赛的赢家。
让我们以第一排为例。表示比赛日期的列 1 和 2 分别具有虚拟值“1”。第 3 列的值为“1”。交叉检查我们的“团队”文件,我们可以看到值“1”是用来代表曼联。第 4 列有“-1”,表示这场比赛是曼联的客场比赛。第 5 列的值为“2 ”,表示曼联进了 2 个球。第 6 列数值代表第 2 队的号码,其中 2 号表示第 2 队是切尔西,这是一场曼联对切尔西的比赛。第 7 列的值为“1 ”,表示这是切尔西的主场比赛,最后一列的值为“1 ”,表示切尔西的进球数。
最后,让我们开始编码这个算法。编码 Colley 算法相对简单,并且相当容易理解。与任何 Python 文件一样,我们将导入所需的包,即本例中的 numpy 和 pandas。
# Importing packages
import numpy as np
import pandas as pd
然后我们去读“团队”和“分数”文件。
# Reading 'teams' and 'scores' data
teams = pd.read_csv('teams.txt', header = None)
num_of_teams = len(teams.index)data = pd.read_csv('scores.txt', header = None)
我们用所需维数中的所有 0 来初始化 Colley 矩阵和向量 b。
# Initializing Colley Matrix 'c'and vector 'b'
c = np.zeros([num_of_teams, num_of_teams])
b = np.zeros(num_of_teams)
然后,逐行迭代“分数”文件,我们根据哪个队在比赛以及每场比赛的获胜者来填充 Colley 矩阵和向量“b”。
# Iterating through rows and populating Colley matrix values
for index, row in data.iterrows():
t1 = row[2]
t2 = row[5]
c[(t1-1)][(t1-1)] = c[(t1-1)][(t1-1)] + 1 # Updating diagonal element
c[(t2-1)][(t2-1)] = c[(t2-1)][(t2-1)] + 1 # Updating diagonal element
c[(t1-1)][(t2-1)] = c[(t1-1)][(t2-1)] - 1 # Updating off - diagonal element
c[(t2-1)][(t1-1)] = c[(t2-1)][(t1-1)] - 1 # Updating off - diagonal element
# Updating vecotr b based on result of each game
if row[4] > row[7]:
b[(t1-1)] += 1
b[(t2-1)] -= 1
elif row[4] < row[7]:
b[(t1-1)] -= 1
b[(t2-1)] += 1
一旦完成,我们按照等式(A)进行调整,将 2 加到 Colley 矩阵的对角元素,并将 1 除以向量 b 的每个元素。
# Adding 2 to diagonal elements (total number of games) of Colley matrix
diag = c.diagonal() + 2
np.fill_diagonal(c, diag)# Dividing by 2 and adding one to vector b
for i, value in enumerate(b):
b[i] = b[i] / 2
b[i] += 1
然后,我们使用 numpy 的 linalg.solve()求解这个 N 变量线性方程。
# Solving N variable linear equation
r = np.linalg.solve(c, b)
一旦我们在“r”中得到结果,我们通过使用 argsort()方法显示前 4 个团队。
# Displaying ranking for top 4 teams
top_teams = r.argsort()[-4:][::-1]
for i in top_teams:
print (str(r[i]) + " " + str(teams.iloc[i][1]))
这样,如果您成功下载了 repo 并运行了这段代码,您应该会看到下面的输出。
现在,为了检查这个算法的真正实力,我们将通过插入下面几行来在我们的“分数”文件中增加两个游戏。
第一场:阿森纳击败曼联= > 1,1,1,-1,1,3,1,2
第二场:切尔西击败利物浦= > 1,1,2,-1,2,4,1,1
在‘scores’中做了上述修改后再次运行 Python 文件,会得到如下结果。
正如你所看到的,即使阿森纳和切尔西打了,赢了和输了同样多的比赛,科利算法也能够认识到这样一个事实,即阿森纳击败了比被切尔西击败的利物浦更强大的曼联队。
状态良好的团队
回到“分数”文件的格式,让我们再看一个用例。假设锦标赛有 3 轮,结果如下图所示。
曼联在第一轮赢得所有比赛,切尔西在第二轮赢得所有比赛,阿森纳在第三轮。尽管三支球队在胜利数量上打成平手,但任何有见识的体育人士都会告诉你,在第一轮赢得所有比赛后,曼联已经失败了。另一方面,阿森纳在最近一轮的比赛中连胜,切尔西位于两者之间。考虑到这一点,即使曼联,阿森纳和切尔西的总积分相同,一个好的排名系统应该提供一个区分旧表现和最近表现的条款。
为了将这种“动量”或“形式”因素考虑在内,引入了一个称为“权重”的新变量。借助一个简单的“if else”条件语句,我们给不同的游戏赋予不同的权重。
# Iterating through rows and populating Colley matrix values
for index, row in data.iterrows():
t1 = row[2]
t2 = row[5]
if row[0] <= 6: #For first round matches
weight = 0.9
elif row[0] > 6 and row[0] <=12: #For second round matches
weight = 1.0
else:
weight = 1.1 #For third round matches
c[(t1-1)][(t1-1)] = c[(t1-1)][(t1-1)] + 1 * weight # Updating diagonal element
c[(t2-1)][(t2-1)] = c[(t2-1)][(t2-1)] + 1 * weight # Updating diagonal element
c[(t1-1)][(t2-1)] = c[(t1-1)][(t2-1)] - 1 * weight # Updating off - diagonal element
c[(t2-1)][(t1-1)] = c[(t2-1)][(t1-1)] - 1 * weight # Updating off - diagonal element
# Updating vecotr b based on result of each game
if row[4] > row[7]:
b[(t1-1)] += 1 * weight
b[(t2-1)] -= 1 * weight
elif row[4] < row[7]:
b[(t1-1)] -= 1 * weight
b[(t2-1)] += 1 * weight
随着锦标赛的进行,旧游戏的比重越来越小。而与前两轮相比,最新一轮的装置被给予更高的权重。通过以上修改,如果我们再次运行我们的 Colley 算法,我们将得到以下结果。
不出所料,在最近的比赛中赢得所有比赛的阿森纳排名第一,而曼联排名第三,尽管他们与阿森纳和切尔西积分相同。
就这样,我将结束这篇文章。希望你这个体育迷和数据科学家能够欣赏科利方法的美丽和简单。
像往常一样,如果你喜欢这篇文章,在 Twitter 上关注,转发,或者鼓掌,媒体上的赞将鼓励我继续我的博客世界之旅。
直到下一次…干杯!!
用 MapBox GL 和 Python 生成 logo。
建筑足迹总是有一种令人愉悦的美感。视觉设计通常源于自然和人造的隐喻——这两种事物都包含在制图领域中。拓扑特征和水路为我们呈现了柔和的曲线特征,与道路设计的线性和对称形状形成了直接对比。
在这里,我们将建筑物的占地面积绘制成类似 Tron 的图像,有点像印刷电路板(PCB)。
第一步:选择你的位置
就我个人而言,我发现河流与城市结合的地方效果最好。这就是为什么我选择使用威斯敏斯特划船基地的位置,这是伦敦市中心的一个小型水上运动中心。
使用快速谷歌地图搜索,可以从 url 获得经度和纬度:
获取一个国家建筑物的形状文件有很多来源。美国的足迹可以在这里找到,英国的足迹可以通过英国国家测绘局的开放地图项目(链接如下)找到。
此页面包含为整个英国构建 shapefile 数据的链接,以及指向单个地理包的链接…
ajrae.staff.shef.ac.uk](http://ajrae.staff.shef.ac.uk/buildings/)
步骤 3:只提取你需要的数据
这个过程涉及到 Python 的使用,结合 geopandas 库pip install geopandas
。
我们用geopandas.read_file
读取文件,然后过滤掉任何不想要的结果。因为我们希望绘制的区域包括三个不同的行政区,所以我们只在NAME
列包含其中一个行政区名称的地方提取数据:
selection = all_data[ (all_data.NAME == 'Westminster') ^
(all_data.NAME == 'Wandsworth' ) ^
(all_data.NAME == 'Lambeth' ) ]
然后,我们确保我们的单位是经度和纬度的单位:
selection = selection.to_crs(epsg=4326).reset_index()
第四步:只获取我们位置一定半径范围内的建筑
现在我们有了所有的数据,我们希望过滤掉一定距离以外的所有建筑。
为此,我们可以使用 shapley 点几何图形并定义我们的位置。然后,我们可以遍历 GeoPandas 数据框中的所有项目,并且只追加距离所选点一定距离的项目。
point = Point(lon,lat)
thresh = 0.01
keep=[]**for** r **in** selection.iterrows(): dst = point.distance(r[1].geometry) **if** abs(dst) < thresh:
x,y = r[1].geometry.exterior.coords.xy
keep.append([list(x),list(y)])
最后,我们将所有数据写入一个文本文件:
**import** **json**
json.dump( [[list(i) **for** i **in** zip(x,y)] **for** x,y **in** keep] ,
open('keep_footprint.json','w')
可选:在 python 中预先绘制
fig = plt.figure()
ax = fig.add_subplot(111)
**for** poly **in** keep:
x,y = poly
ax.plot(x, y, color='red', alpha=0.7,
linewidth=1, solid_capstyle='round', zorder=2)
ax.set_title('test')
plt.show()
步骤 5:设计创建地图框样式
现在我们注册了一个免费的地图框账户,用提供的工作室应用【https://studio.mapbox.com 创建我们自己的定制风格
我个人是从“暗”布局开始的,把所有的街道都变暗,把所有的标签的不透明度设置为 0,去掉所有的标签。生产出来的款式可以在这里找到。
Mapbox Studio
步骤 6:简单的地图框布局
最后,我们可以使用带有我们的 lat 和 lon 值的自定义样式的 Mapbox 示例脚本来生成我们位置的预览。(下面链接中的代码)
使用自定义地图框托管样式。
docs.mapbox.com](https://docs.mapbox.com/mapbox-gl-js/example/custom-style-id/)
然后,我们通过添加一个椭圆形的剪辑路径来扩展它,样式为“map”元素:
<style>#map{
width:600px;
height:600px;
clip-path:ellipse(300px 300px at 300px 300px);
}</style>
为我们的位置添加一个标记:
// marker
new mapboxgl.Marker({"color":'#33ff00'})
.setLngLat([ -0.134605,51.484575])
.addTo(map);
步骤 6:加载我们的足迹数据
最后,剩下的就是在文本文件中读取。我发现使用 GitHub 的 raw 格式,结合 d3 库最简单:d3js.org
d3.json(“[https://raw.githubusercontent.com/wolfiex/GeoLogo/master/polyfoot.txt](https://raw.githubusercontent.com/wolfiex/GeoLogo/master/polyfoot.txt)", run)
现在,我们需要做的就是在加载的数据集中绘制每个建筑物的轮廓:
data.forEach((polygon, i) => {map.addLayer({
'id': 'poly' + i,
'type': 'line',
'source': {
'type': 'geojson',
'data': {
'type': 'Feature',
'geometry': {
'type': 'Polygon',
'coordinates': [polygon]
}
}
},
'layout': {},
'paint': {
'line-color': '#088',
'line-opacity': 0.8
}
});
}
})
如果我们希望每个形状都被填充而不是描边,我们可以将“类型”设置为fill
而不是line
,并相应地更改line-color
和line-opacity
。
第七步:享受!
我们有了它,一个围绕我们感兴趣的区域的漂亮标志。
注意:由于可能有许多多边形要绘制,此过程可能需要一段时间才能完成。明智地选择你的门槛距离!
*
用 Python 生成简历
我正沿着“自学”的道路向数据科学领域过渡。因此,当我坐下来开始写简历时,我努力思考如何在简历这种静态和传统的东西上表达我学到的新技能。老实说,我觉得写简历很吓人。
当我没有能让你眼前一亮的学历或经验时,我如何才能吸引招聘经理的目光呢?
在思考这个问题的时候,我想起了一件重要的事情。虽然数据科学对我来说是一个相对较新的尝试,但我非常精通通信和创造性故事的世界。因此,戴上我的创意帽子,我开始寻找一种新的方式来创建一份简历,它可以以一种自然而清晰的方式快速展示技术数据可视化技能。
而这就是结果…
这非常简单,但是我使用 Python 中的 matplotlib 库编译了整个简历。它基本上是一个 8.5 x 11 的图表,没有轴和信息,但有一些图形线条和许多注释。
它很容易编译,但是它展示了对 Python 的熟练程度和创造性交流的能力。或许更重要的是,它让写简历变得更有趣,也不那么令人生畏。
源代码在下面,或者你可以在我的 GitHub 库找到。希望你喜欢它,如果你有任何意见或建议让我知道!
# Text Variables
Header = '>>>This resume was generated entirely in Python. For full sourcecode, view my portfolio.'
Name = 'EDDIE KIRKLAND'
Title = 'Data Science & Analytics'
Contact = 'Atlanta, GA\n404-XXX-XXXX\nwekrklndATgmailDOTcom\nlinkedin.com/in/ekirkland\ngithub.com/e-kirkland'
ProjectsHeader = 'PROJECTS/PUBLICATIONS'
ProjectOneTitle = 'Increasing Kaggle Revenue'
ProjectOneDesc = '- Published by Towards Data Science\n- Analyzed user survey to recommend most profitable future revenue source\n- Cleaned/visualized data using pandas/matplotlib libraries in Python'
ProjectTwoTitle = 'NYC School Data Cleaning & Analysis'
ProjectTwoDesc = '- Cleaned and combined several tables using pandas library in Python\n- Used PDE and visualization to determine correlations for future study'
ProjectThreeTitle = 'Pandas Cleaning and Visualization'
ProjectThreeDesc = '- Cleaned data for analysis using pandas library in Python\n- Used pandas and matplotlib to explore which cars hold the most value over time'
Portfolio = 'Portfolio: rebrand.ly/ekirkland'
WorkHeader = 'EXPERIENCE'
WorkOneTitle = 'Example Company / Example Position'
WorkOneTime = '8/2013-Present'
WorkOneDesc = '- Raised $350k in startup funds, recruited/organized launch team\n- Coordinated branding and communication strategy\n- Led team of 80 volunteer and staff leaders'
WorkTwoTitle = 'Second Company / Second Position'
WorkTwoTime = '2/2007-8/2013'
WorkTwoDesc = '- Led team of over 100 full-time and contract staff\n- Helped create branding and messaging for weekly content\n- Created/directed musical elements at weekly events for up to 10,000 people'
WorkThreeTitle = 'Third Company / Third Position'
WorkThreeTime = '6/2004-2/2007'
WorkThreeDesc = '- Planned/Coordianted Toronto arena event and South Africa speaking tour\n- Oversaw research for published products'
EduHeader = 'EDUCATION'
EduOneTitle = 'Example University, Bachelor of Business Administration'
EduOneTime = '2000-2004'
EduOneDesc = '- Major: Management, Minor: Statistics'
EduTwoTitle = 'Example University, Master of Arts'
EduTwoTime = '2013-2017'
SkillsHeader = 'Skills'
SkillsDesc = '- Python\n- Pandas\n- NumPy\n- Data Visualization\n- Data Cleaning\n- Command Line\n- Git and Version Control\n- SQL\n- APIs\n- Probability/Statistics\n- Data Manipulation\n- Excel'
ExtrasTitle = 'DataQuest\nData Scientist Path'
ExtrasDesc = 'Learned popular data science\nlanguages, data cleaning and\nmanipulation, machine learning \nand statistical analysis'
CodeTitle = 'View Portfolio'# Setting style for bar graphs
import matplotlib.pyplot as plt
%matplotlib inline# set font
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = 'STIXGeneral'fig, ax = plt.subplots(figsize=(8.5, 11))# Decorative Lines
ax.axvline(x=.5, ymin=0, ymax=1, color='#007ACC', alpha=0.0, linewidth=50)
plt.axvline(x=.99, color='#000000', alpha=0.5, linewidth=300)
plt.axhline(y=.88, xmin=0, xmax=1, color='#ffffff', linewidth=3)# set background color
ax.set_facecolor('white')# remove axes
plt.axis('off')# add text
plt.annotate(Header, (.02,.98), weight='regular', fontsize=8, alpha=.75)
plt.annotate(Name, (.02,.94), weight='bold', fontsize=20)
plt.annotate(Title, (.02,.91), weight='regular', fontsize=14)
plt.annotate(Contact, (.7,.906), weight='regular', fontsize=8, color='#ffffff')
plt.annotate(ProjectsHeader, (.02,.86), weight='bold', fontsize=10, color='#58C1B2')
plt.annotate(ProjectOneTitle, (.02,.832), weight='bold', fontsize=10)
plt.annotate(ProjectOneDesc, (.04,.78), weight='regular', fontsize=9)
plt.annotate(ProjectTwoTitle, (.02,.745), weight='bold', fontsize=10)
plt.annotate(ProjectTwoDesc, (.04,.71), weight='regular', fontsize=9)
plt.annotate(ProjectThreeTitle, (.02,.672), weight='bold', fontsize=10)
plt.annotate(ProjectThreeDesc, (.04,.638), weight='regular', fontsize=9)
plt.annotate(Portfolio, (.02,.6), weight='bold', fontsize=10)
plt.annotate(WorkHeader, (.02,.54), weight='bold', fontsize=10, color='#58C1B2')
plt.annotate(WorkOneTitle, (.02,.508), weight='bold', fontsize=10)
plt.annotate(WorkOneTime, (.02,.493), weight='regular', fontsize=9, alpha=.6)
plt.annotate(WorkOneDesc, (.04,.445), weight='regular', fontsize=9)
plt.annotate(WorkTwoTitle, (.02,.4), weight='bold', fontsize=10)
plt.annotate(WorkTwoTime, (.02,.385), weight='regular', fontsize=9, alpha=.6)
plt.annotate(WorkTwoDesc, (.04,.337), weight='regular', fontsize=9)
plt.annotate(WorkThreeTitle, (.02,.295), weight='bold', fontsize=10)
plt.annotate(WorkThreeTime, (.02,.28), weight='regular', fontsize=9, alpha=.6)
plt.annotate(WorkThreeDesc, (.04,.247), weight='regular', fontsize=9)
plt.annotate(EduHeader, (.02,.185), weight='bold', fontsize=10, color='#58C1B2')
plt.annotate(EduOneTitle, (.02,.155), weight='bold', fontsize=10)
plt.annotate(EduOneTime, (.02,.14), weight='regular', fontsize=9, alpha=.6)
plt.annotate(EduOneDesc, (.04,.125), weight='regular', fontsize=9)
plt.annotate(EduTwoTitle, (.02,.08), weight='bold', fontsize=10)
plt.annotate(EduTwoTime, (.02,.065), weight='regular', fontsize=9, alpha=.6)
plt.annotate(SkillsHeader, (.7,.8), weight='bold', fontsize=10, color='#ffffff')
plt.annotate(SkillsDesc, (.7,.56), weight='regular', fontsize=10, color='#ffffff')
plt.annotate(ExtrasTitle, (.7,.43), weight='bold', fontsize=10, color='#ffffff')
plt.annotate(ExtrasDesc, (.7,.345), weight='regular', fontsize=10, color='#ffffff')
plt.annotate(CodeTitle, (.7,.2), weight='bold', fontsize=10, color='#ffffff')#add qr code
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
import matplotlib.image as mpimg
arr_code = mpimg.imread('ekresumecode.png')
imagebox = OffsetImage(arr_code, zoom=0.5)
ab = AnnotationBbox(imagebox, (0.84, 0.12))
ax.add_artist(ab)plt.savefig('resumeexample.png', dpi=300, bbox_inches='tight')
用机器学习生成披头士歌词
(source)
语言模型和 OpenAI 的 GPT-2 的高级入门
甲壳虫乐队是一个巨大的文化现象。他们永恒的音乐至今仍在年轻人和老年人中引起共鸣。我个人是个超级粉丝。依我拙见,他们是有史以来最伟大的乐队。他们的歌曲充满了有趣的歌词和深刻的思想。以这些酒吧为例:
当你超越自己时,你会发现内心的平静在那里等着你
强大的东西。然而,甲壳虫乐队的伟大之处在于他们的多才多艺。他们的一些歌曲深刻而有思想,而另一些则有趣而轻松。不出所料,贯穿他们歌词的最大主题是爱情。这里有一段这样的诗句:
我亲爱的小宝贝,你难道看不出,当你属于我时是多么美好。要是我能永远做你的爱人就好了,我永远得不到我的心,我得不到我的思想。
事实上,这些歌词不是你可能认识的任何一个甲壳虫乐队成员写的。不是列侬,或麦卡特尼,或哈里森,甚至,上帝保佑,林哥·斯塔尔(只是开玩笑,林戈是好的)。它们实际上是由一个机器学习模型生成的,即 OpenAI 的 GPT-2 [1]。虽然这使用了他们最小的模型,但结果是相当惊人的。
但是在我们走得太远之前,让我们后退一步,看看这一切是如何工作的。和往常一样,我的 Github 上有完整的工作代码。
语言建模
语言模型试图学习一种语言的结构(例如英语或披头士的歌词)。它们是使用监督学习训练的生成模型。像其他监督学习任务一样,语言模型试图预测给定一些特征的标签。然而,与大多数监督学习任务不同,没有明确的标签,而是语言本身既充当特征又充当标签。
在高层次上,语言模型试图做的是,在给定一系列前一个单词的情况下,预测下一个单词。例如,一个好的语言模型可能预测到*“milk”是短语“购买一加仑 ____”的逻辑结论*
通过尝试猜测下一个单词,我们真正做的是学习词汇的概率分布,这取决于我们到目前为止看到的单词。也就是说,我们想要学习
我们的词汇表中的单词在哪里?
因为我们显式地对这个分布建模,所以我们可以用它做一些很酷的事情,比如用它来生成我们以前没有见过的单词。我们可以通过重复地从这个分布中抽取下一个单词,然后当我们抽取下一个单词时,用它作为条件,以此类推。具体来说,让我们看看这在 Python 中可能是什么样子。如果我们有一个带有sample
方法的model
对象,那么我们可以通过如下方式生成新的样本:
How we might generate sentences from a language model.
当然,我跳过了一些细节,但希望随着我们的继续,这些会变得更加清晰。现在,让我们考虑世界上最简单的语言模型,unigram。
unigram 模型忽略任何条件作用,只是从训练数据中随机选择下一个单词。这相当于把我们的训练数据扔进搅拌机,在高空搅拌 10 分钟后把里面的东西洒出来。可以说,我们不会产生任何类似英语的东西(当然,除非我们有一万亿只猴子和一万亿台搅拌机,或者是打字机)。
二元模型
一元模型之上的一步是二元模型。正如你可能已经从名字中猜到的,二元模型学习一个分布,这个分布只取决于前一个单词,即
因为它非常简单,所以二元模型很容易在 Python 中实现,并且会让我们更深入地理解语言模型是如何工作的。
收集数据
在实现之前,我们首先需要一些数据。我们的最终目标是创作出让披头士感到自豪的歌曲,所以让我们从收集他们所有已知的歌词开始吧。
我发现这个网站把他们发行的每首歌的歌词分类。它也有一个有用的索引页面,上面有我们可以用来抓取网站的单首歌曲的链接。我编写了一个简单的脚本来迭代页面上的每个链接,解析它的 HTML 来提取歌词,并将歌词转储到一个文件中,每行一首歌。如果你打算继续下去,或者只是自己想要披头士的歌词,我强烈推荐使用它,因为抓取 HTML 是相当乏味和烦人的,即使使用像 Beautiful Soup 这样的工具。
一旦我们有了一个好的、干净的格式的数据,剩下的就容易了。但是不要只相信我的话,从这张图表中来看:
Data cleaning and organizing accounts for the biggest chunk of data science projects (source).
构建模型
如上所述,二元模型只是在前一个词的条件下对下一个词进行采样。我们可以做到这一点的一个简单方法是跟踪哪些单词跟随当前单词,以及出现的频率。也就是说,我们为训练数据中的每个单词current_word
保留一个字典,然后每当我们看到一个next_word
,我们就更新current_word[next_word] += 1
。然后,为了生成单词,我们只需在current_word
字典中查找所有单词和计数,并以与其计数成比例的概率对单词进行采样。下面是 Python 中完整模型的草图:
A sketch of the bigram language model.
最后要注意的是,我们可能想通过添加一些特殊的标记来表示歌词和歌曲的开始/结束,从而对歌词进行预处理。这是为了迫使我们的模型在生成新歌词时保持歌曲的一些结构,否则模型将会不断地吐出大块的文本。在我的代码中,我用XXSL, XXEL, XXSS,
和XXES
分别表示开始行、结束行、开始歌曲和结束歌曲。
最后,为了生成歌曲,我们可以从XXSS
标记开始,并一直调用model.predict()
,直到我们达到一个XXES
标记。
Generating a song with the bigram model.
理论上,一旦循环停止,我们将生成一首前所未见的披头士歌曲。但是这有什么好处吗?
一首前所未见的披头士歌曲
这是 bigram 模型生成的一首歌曲的一小段:
她是如此的我爱她的心。
他们很好;他们说我这么多,
她一点也不惊讶
当你是我的时候
悲伤和阿姆斯特丹的希尔顿
他们让我走,
是的我等一个男孩生下来就和一个有钱人在一起,
大家一起分享
生成的样本听起来像一个疯子的胡言乱语,只有在我们非常幸运的情况下才有意义。
我们可以继续扩展二元模型来考虑前面的两个词。这就是所谓的三元模型。你可能会发现三元模型实际上会产生更好听的歌词。这是因为少了三个单词组合,所以模型在每一步都有较少的选择,在某些步骤中只有一个选择。一般来说,我们可以通过考虑前面的 n 个单词来创建一个任意的 n-gram 模型。当 n 等于一首完整歌曲的长度时,你可能会发现这个模型非常适合生成披头士的歌曲。不幸的是,它生成的歌曲已经存在。
走向更好的模式
二元模型的一个最明显的问题是,它将只使用它在训练数据中看到的单词和短语。虽然我们希望生成听起来像是披头士写的歌词,但我们不想只局限于他们使用的词。例如,如果甲壳虫乐队从未使用过“游行”这个词,那么 bigram 模型将不会生成任何关于“游行”的歌曲。当然,由于我们只训练披头士的歌词,我们不可能指望我们的模型使用从未见过的词。我们需要的是在巨大的语料库上进行训练,比如维基百科或者 Reddit 。
然而,即使我们在所有的维基百科上训练,并且看到英语中的每一个单词,我们的二元模型仍然太死板了。比如说“高个子”这个短语。每一个对英语有基本了解的人都会意识到“高”只是“人”的修饰词,与“人”无关。相反,“高”可以用来修饰无数其他事物,如“女人”、“男孩”、“建筑物”、“长颈鹿”等。然而,我们的二元模型无法了解这一点,而是必须在使用“tall”之前至少看到它的一次使用。所以如果模特只见过“高个子”、“高个子男生”、“高个子女生”,而没有见过“高个子女生”,就它而言,“高个子女生”这个词组甚至不存在。
因此,我们想要的是一个具有更丰富的词汇表和对词汇表中单词之间关系的更深入理解的模型。幸运的是,聪明的研究人员已经发明了如此强大的模型,我们可以用它们来创作更好的歌曲。
GPT-2 模型
OpenAI 的 GPT-2 模型[1]最近因为“太危险而不能发布”而成为头条新闻该模型生成了如此令人信服的文本,以至于作者认为它可能被用于恶意 purposes⁴.相反,他们发布了两个较小的版本供人们玩和试验。我们将使用两者中最小的一个来生成披头士的歌词。
GPT-2 是一个基于变形金刚的模型,它是在数百个 GPU 小时的海量 Reddit 数据上训练的。在训练期间,它能够学习一种非常好的英语语言模型(或者至少是 Reddit 上使用的英语版本)。这意味着它能够理解“高”可以用于人类、建筑物或长颈鹿。此外,因为它在 Reddit 的很大一部分上进行了训练,所以它可能看到了英语中 99.9%的单词和短语。这对我们来说是个好消息,因为这正是我们想要的:丰富的词汇和对如何使用这些词汇的深刻理解。
然而,如果我们打开模型,让它生成一些东西,它极不可能得出类似披头士的歌词(即使r/披头士存在)。这是因为模型不知道我们关心的是生成披头士的歌词,毕竟这不是它被训练要做的。相反,我们需要推动模型做我们想让它做的事情。我们可以做到这一点的一个方法是通过迁移学习。
迁移学习
迁移学习是指我们可以通过做一件事来利用我们所学的信息,并应用它来解决一件相关的事情。例如,当你开始阅读这篇文章时,你不必重新学习什么是单词,哪些单词跟随哪些其他单词,或者它们如何组合在一起构成句子。想象一下那会有多乏味。相反,你利用花在阅读 AP 文学书籍上的所有时间来理解我现在在说什么(我猜哈克芬终究派上了用场)。
以类似的方式,我们可以利用 GPT 2 号通过数百小时阅读 Reddit 帖子学到的知识,并将其转移到我们生成披头士歌词的任务中。高层次的想法是采取预先训练的模型,并继续训练它一段时间。然而,代替 Reddit 的帖子,我们将只使用刮掉的披头士的歌词*。这将使模型严重偏向于生成类似披头士的歌曲。*
我将在这里跳过具体如何做,因为这将需要另一个类似长度的帖子来解释一切。相反,如果你对确切的细节感兴趣,我会让你参考[2]。这是一篇很棒的博文,一步一步地指导你如何将 GPT-2 模型应用到你关心的任何语言任务中。这也是我得到这里显示的结果所遵循的。
新甲壳虫乐队
像所有好的深度学习结果一样,我在简介中发布的歌词是经过精心挑选的。生成的歌曲并不都一样好,它们的质量取决于你在微调阶段的位置。当模型仍然严重不符合训练数据时,在对大约 100 个小批量进行微调后,您可能会得到以下结果:
我永远爱你,列侬,麦卡特尼,你永远爱我。你永远爱我,你也永远爱我。我永远都是
这样又持续了 10-15 行。至少比 Lil’ Pump 要好。
玩笑归玩笑,我最感兴趣的是前两行。在训练数据中,每首歌曲的第一行是标题,第二行是作者,后面几行是实际的歌词。即使在这个早期阶段,模型已经设法学习我们的数据结构:第一行和第二行是特殊的;在第二行中,可能出现的单词组合很少,最有可能是 Lennon & McCartney。
如果我们对大约 350 个小批量进行微调,该模型就会开始生成更加可信的歌词,比如介绍中的这首,或者这首:
黑衣女人
列侬&麦卡特尼
我要大吵大闹
如果你不想让我出现
你最好离我远点。
我濒临死亡,我恋爱了
不完美,但还不错。最后,如果我们长时间保持微调(大约 2800 分钟一批),就会发生这种情况:
回来(回来)
列侬&麦卡特尼
黄色潜水艇
列侬&麦卡特尼
在一个周六的夜晚,当太阳照在我身上的时候
太阳出来了,船帆清晰
太阳出来了,船帆清晰
哦——嘿
模型开始过度拟合,并且生成的样本是很可能出现在训练数据中的东西,例如重复的“Lennon & McCartney”线、“黄色潜水艇”等。我发现微调 300-500 步可以产生最好的歌词。
结论
希望您现在对语言模型如何工作以及我们如何利用最先进的模型来极大地改进下游任务有了更好的了解。
也就是说,GPT-2 模型还有很多值得探索的地方。我生成的样本使用了默认的(可能)次优超参数。如果花更多的时间进行微调,看看生成的歌词会有多好,这将是一件有趣的事情。我也只使用了最小的型号,因为我是用我的笔记本电脑来训练的。我相信更大的模型会产生更壮观的结果。最后,OpenAI 最近发布了 MuseNet ,它能够生成非常逼真的声音音乐。将 GPT-2 和 MuseNet 放在一起(它们基本上是同一个模型)并生成歌词和伴奏音乐会有多神奇?如果我有更多的时间、金钱或任何关于我在做什么的想法,我想用机器学习来创作一首成熟的歌曲,然后让真正有才华的人来演奏它。
感谢阅读!
Eugen Hotaj,
2019 年 6 月 30 日
附言:如果你喜欢这篇文章,那就关注我,让我通知你有新的帖子!一如既往,所有代码和数据都可以在 my GitHub 上获得。
脚注
尽管我认为最佳独唱音乐家必须是另一个 60 年代的传奇人物,鲍勃·迪伦。他的单曲像滚石,可能是有史以来写得最好的歌曲,这是不仅仅是我的看法。
*来自 无你之内 *,我最喜欢的披头士专辑里我最喜欢的披头士的歌。很奇怪,但又很好。
为了简洁起见,我跳过了一些次要的细节。你可以在我的 GitHub 上看到所有血淋淋的细节。
⁴有些人认为这只是一个巨大的宣传噱头,但那是无关紧要的。
参考
[1] A .拉德福德等人,语言模型是无监督的多任务学习器 (2019)
[2] S .托多洛夫,根据 Facebook Messenger 的数据微调 OpenAI 的 GPT-2,产生虚假对话 (2019)
使用异常检测生成关键场景
概观
假设你正在开发一辆自动驾驶汽车或软件(我同意这是一个相当大的假设),你的第一个原型已经准备好,你想彻底测试。一种方法是运行大量测试车,检查系统如何响应其环境,这就是所谓的系统验证。但这可能是非常资本密集型的,虽然你应该这样做,但有一种更容易或更便宜的方式来做,那就是通过模拟,可以非常快速地生成大量数据。在这两种情况下,你都有大量的场景可以测试你的系统,但让自动驾驶软件承受大量的常规条件有意义吗?我们需要在关键场景下测试软件,这是对自治系统的真正测试,但关键场景的生成率不到 1%。在这里,我提出了一种称为异常检测的技术,它可以用来从大量数据中过滤关键场景。
Anomaly Detection
首先,让我们从总体上讨论异常或异常值检测。根据维基百科的说法,异常检测(也称异常检测)是对罕见项目、事件或观察结果的识别,这些项目、事件或观察结果通过与大多数数据显著不同而引起怀疑。那么,如何在数据集上实现异常检测呢?有几种方法可以执行异常检测,例如基于密度的异常检测、基于聚类的异常检测和基于支持向量机的异常检测。这里,我们将讨论一种基于移动平均线的简单方法,该方法将超出移动平均线固定标准偏差的点检测为时间序列数据中的异常值。遍历时间序列数据的平均值并不简单,因为它不是静态的。您需要一个滚动窗口来计算数据点的平均值。从技术上来说,这被称为滚动平均线或移动平均线,它的目的是平滑短期波动,突出长期波动。数学上,n 周期简单移动平均线也可以定义为“低通滤波器”。这里你可以找到一个非常直观的解释。让我们来看看算法:
- 首先,我们定义一个函数,使用离散线性卷积计算固定窗口大小的时间序列数据的移动平均值。卷积是一种数学运算,可以描述为两个函数的乘积在一个函数反转和移位后的积分。在这种情况下,该操作计算每个滑动窗口的平均值。
- 计算移动平均值后,我们计算 y 值和相应平均值之间的差值,我们称之为残差。接下来,我们计算这些残差的标准差。
- 我们将异常值定义为相应残差大于移动平均值加三个标准差或小于移动平均值减三个标准差的点。我们将三个标准偏差作为任意选择,它可以根据数据的分布和我们的用例而变化。
让我们来看看 python 中的实现:
在这里,我试图在加速度与时间的样本数据中找到异常,这将给我们带来车辆加速度的异常行为。这可能意味着突然刹车或加速度增加,这可能是由于可能的障碍物、碰撞或急转弯等。以下结果是使用 20 的滚动窗口在样本数据集上获得的:
红点是异常值,绿线是移动窗口的平均值。现在,必须进一步分析这些异常值,以了解它们是否仅对应于我们感兴趣的噪声或关键事件。这种算法为我们提供了整个时间序列数据中的兴趣点,从而缩小了我们的搜索范围。我们可以从我们可能从客户/测试汽车中收集的大量数据中挖掘所有这些关键事件,并将它们输入到我们的自动驾驶汽车系统中,看看它们如何应对这些事件。这对于验证自动驾驶汽车非常有用,因为在我们收集的数据中,只有不到 1%的数据发生了关键事件。
干杯!
用可变自动编码器和张量流生成假的 FIFA 19 足球运动员
更新 25/01/2021:我会很快更新这些帖子,在 Twitter 上关注我以获得更多信息https://twitter.com/mmeendez8
这是我的第三篇关于可变自动编码器的文章。如果你想赶上数学,我推荐你查看我的第一篇文章。如果你想跳过这一部分,直接用 VAEs 做一些简单的实验,那就来看看我在的第二篇文章,在那里我展示了这些网络有多有用。如果你只是想看看神经网络如何创造足球运动员的假脸,那么你来对地方了!阅读更多 …
使用深度自回归模型生成高分辨率图像
超越 GANs,捕捉真实数据分布的多样性
Figure 1. High-resolution (256x256 pixels) 8-bit celebrity images generated using a deep autoregressive model trained on the CelebA-HQ dataset.
介绍
图 1 中的名人面孔并不存在。它们是使用深度自回归模型生成的,该模型是由谷歌 Deepmind 和谷歌大脑的研究人员在一篇题为“用亚尺度像素网络和多维尺度放大生成高保真图像”的论文中引入的。这项工作特别重要,因为它朝着生成高分辨率、高保真度的自然图像迈出了一大步,同时支持整个数据分布并保证模型的概化能力。不用担心这里介绍的不熟悉的术语。在这篇文章中,我将带你浏览这篇文章,并解释所有的术语。最近我也在 AISC 展示并讨论了这项工作。你可以在 YouTube 上找到完整的演示。
在我们深入阅读本文之前,让我先概述一下我们的目标:
- 文件概述:其目标和贡献;
- 图像生成的深度生成模型与该领域最新趋势的比较:
- 图像生成的深度自回归模型概述:它们是什么,为什么我们对它们感兴趣,以及以前方法的主要挑战;和
- 深入探究子尺度像素网络和多尺度放大:它们是什么,它们为什么工作,以及它们如何与以前的深度自回归模型进行比较。
本文的目标是构建一个深度自回归(AR)解码器,它可以无条件地生成高保真图像。保真度是指模型生成真实图像的程度,这项工作能够使用深度 AR 模型在高分辨率下生成高度逼真的图像。我们将讨论与使用深度 AR 模型生成高分辨率、高保真度图像相关的主要挑战,并且我们将解释在这项工作中引入的用于解决这些问题的解决方案。
这里将简单介绍的 pixelRNN、pixelCNN 等深度 AR 模型就是成功利用深度 AR 模型进行图像生成的例子;但是,它们只能生成分辨率相对较低的图像(32x32 和 64x64 像素图像)。使用深度 AR 模型生成高分辨率图像具有挑战性,因为网络的大小随着图像大小线性增加,这是不可持续的。然而,该论文生成最大尺寸为 256x256 的图像,并且网络的尺寸不依赖于图像尺寸。他们使用在受欢迎的 CelebA-HQ 数据集上训练的模型生成 256x256 张名人脸。他们还使用在著名的 ImageNet 数据集上训练的模型,生成各种类别的 32x32 到 256x256 图像。
图像的深层生成模型
生成模型旨在学习训练数据的经验分布,并通过对学习的分布进行采样来生成图像,在样本质量和样本多样性之间进行权衡。图像的深度生成模型可以分为三个主要类别:
- 可变自动编码器(VAEs),
- 生成对抗网络(GANs),以及
- 自回归(AR)模型。
变分推理模型(各种类型的变分自动编码器)逼近潜在空间,并通过对所学习的潜在空间进行采样来生成一组不同的图像;然而,生成的图像往往是模糊的。对抗图像生成模型(各种类型的生成对抗网络)生成清晰、高分辨率的图像;但是,不能保证这些模型以有意义的方式学习整个数据分布,并且生成的样本可能不会覆盖整个数据分布。最后,自回归模型捕捉整个数据分布,保证生成一组不同的样本;然而,AR 模型往往局限于低分辨率图像,因为内存和计算要求随着图像的大小而增长。这是本文试图解决的问题。
比较这些模式时要考虑的其他要点是它们的训练过程和有效采样,即快速生成新样本的能力。变分推理和对抗模型在采样时相对有效;然而,这是自回归模型研究的一个开放领域,因为它们在采样过程中往往效率低下。AR 方法是具有稳定训练过程的简单模型,而对抗性方法通常在训练期间不稳定并且难以训练。
图像深度生成建模的最新趋势
谷歌 Deepmind 的研究人员最近发表了许多突破性的研究论文,都专注于图像的深度生成模型(见图 2)。这些论文的中心主题是,可以生成高保真度和高分辨率的样本,而不会遭受 GAN 的缺点,如模式崩溃和缺乏多样性。这些结果非常重要,因为它们显示了在探索除 GANs 之外的方法中的价值,例如用于图像生成任务的变分推断方法和自回归方法。他们还引入了一个新的度量标准来衡量模型对数据分布的了解程度。这个想法是,如果一个模型已经学习了整个数据分布,那么它生成的样本可以成功地用于下游任务,如分类。他们表明,对抗方法比变分推断和自回归模型获得更低的分数,表明对抗方法不一定学习整个数据分布,尽管它们能够生成高保真图像。本文成功使用自回归模型生成高分辨率、高保真图像,同时保证学习整个数据分布。
Figure 2. Recent papers published by researchers at Google Deepmind with a common theme.
自回归模型
AR 模型将图像视为一系列像素,并将其概率表示为所有像素的条件概率的乘积。如下式所示,每个像素强度的概率取决于所有先前生成的像素。
换句话说,为了生成图 3 中的像素 xi ,我们需要之前生成的所有蓝色像素的亮度值。
Figure 3. Generating individual image pixels using an autoregressive model
对抗模型的挑战之一是没有内在的概括措施。在文献中已经提出了不同的度量,但是没有关于单个度量的共识,使得比较不同图像生成模型的性能具有挑战性。另一方面,AR 模型由于其目标函数而被迫捕捉整个数据分布。它们是基于似然的模型,通过最大似然估计来训练。这允许直接计算负对数似然(NLL)分数,这是对模型在保留数据上进行良好概括的能力的一种测量,并可用作测量生成样本的视觉保真度的代理。一个成功的生成模型能产生高保真的样本,并能很好地概括保留的数据。虽然对抗方法产生高保真图像,但不能保证捕捉到整个数据分布。
以前的尝试
在过去的几年中,人们已经做出了许多努力来使用深度 AR 模型来顺序预测图像中的像素。最著名的型号是 PixelRNN 和 PixelCNN 。PixelRNN 使用 LSTM 层从先前生成的像素中捕获信息,而 PixelCNN 对先前生成的像素使用掩蔽卷积。最近的门控 PixelCNN 架构通过对 LSTM 门使用掩蔽卷积,结合了 pixelRNN 和 PixelCNN 的思想,实现了与 PixelRNN 相当的性能,同时训练速度与 pixelCNN 一样快。最近, PixelSNAIL 架构结合了掩蔽卷积和自关注,利用卷积在有限上下文大小上的高带宽访问和自关注在无限大上下文上的访问来生成图像。本文中描述的基线解码器使用类似 PixelSNAIL 的架构。
尽管做出了这些努力,AR 模型仍然需要生成高分辨率的样本,并捕捉长程结构和语义一致性。此外,由于 AR 模型被迫支持整个数据分布,它们倾向于将容量用于与保真度无关的部分分布。
亚比例像素网络
本文中描述的论文旨在通过引入亚尺度像素网络(SPN)以及将大图像划分为一系列大小相等的切片的替代排序,来生成捕捉细节和全局一致性的高分辨率、高保真图像。如图 4 所示,从原始图像每隔 n 个像素对图像切片进行二次采样。这种架构将模型大小与图像分辨率/大小分离,从而保持内存和计算需求不变。它还可以轻松地对大图像中许多像素之间的长程相关性进行压缩编码。
Figure 4. Subsampling 4 image slices from the original image
SPN 是一种条件解码器,它将图像生成为一系列大小相等的子图像。这些子图像随后在尺寸和深度上增长,以在称为多维放大的过程中生成全分辨率、全深度图像。这个过程如图 5 所示,可以描述为引导模型首先关注分布中视觉上更显著的位,然后关注视觉上不太显著的位。该文件确定了两个视觉突出的子集:大小和深度。尺寸是指生成初始切片(子图像),然后通过以编码丰富空间结构的方式基于先前生成的切片一次生成一个切片来对原始图像进行上采样。深度是指用于分别生成每个 RGB 通道的最高和最低有效位的不同网络。
Figure 5. Three networks working together to generate the final full-size full-depth image
因此,训练了 3 个具有相似架构的独立网络:
(a)在小尺寸、低深度图像切片上训练的解码器;
(b)尺寸放大解码器,其生成以初始图像切片为条件的大尺寸、低深度图像,即小尺寸、低深度图像;和
©深度提升解码器,其以大尺寸、低深度图像为条件生成大尺寸、高深度图像。
图 6 (i)示出了网络(a)和(b)一起工作以生成全尺寸、低深度图像,而图 6 (ii)示出了用于生成大尺寸、高深度图像的所有三个网络。最初的小尺寸、低深度图像切片(白色像素)是使用网络(a)生成的。然后,网络(b)用于通过生成蓝色像素来生成大尺寸、低深度的图像。最后,网络©用于生成紫色像素,该紫色像素代表大尺寸图像的每个 RGB 通道的剩余位。
Figure 6. Distinct colors correspond to distinct neural networks used to generates various pixels of an image
SPN 架构如图 7 所示。该网络具有编码器或切片嵌入器和屏蔽解码器。编码器将先前生成的沿深度维度连接的切片作为输入,并将先前切片的上下文编码在单个切片形状张量中。该嵌入被传递到解码器,该解码器使用掩蔽卷积和自关注层来生成图像切片。
Figure 7. Subscale pixel network architecture
您可以在 YouTube 上观看完整的演示,了解 SPN 架构的详细解释以及围绕本文算法和结果的进一步讨论。
结论
在这篇文章中,我介绍了一种新颖的深度 AR 架构,它能够学习复杂域的分布,如名人图像,并从所得的学习分布中生成高保真样本。生成的样本显示了前所未有的语义一致性和细节的准确性,即使在高分辨率下也是如此,这令人印象深刻。
Figure 8. High-resolution (128x128 pixels) 8-bit images generated using a deep autoregressive model trained on the ImageNet dataset.
本文表明,学习复杂自然图像的分布并获得高样本分辨率和保真度是可能的。获得捕捉整个数据分布的准确数据表示对于生成各种高分辨率样本至关重要,本文介绍的想法是朝着这个方向迈出的重要一步。这对我们 Looka 来说尤其重要,因为我们的目标是创造各种高质量的设计资产,这对创造伟大的设计至关重要。Looka 的使命是让每个人都可以获得伟大的设计,我们使用深度学习来创建与图形设计师在线合作的体验。特别是,我们正在使用深度生成模型,如本文中介绍的模型,来自动生成令人惊叹的设计资产,如独特的字体和符号。
我希望你喜欢这篇文章。如果您有进一步的问题或意见,请随时联系我们。
我是 Looka 的一名数据科学家,在这里,我们利用深度学习让每个人都能接触到并喜欢伟大的设计。如果你有兴趣在 AI 和设计的交叉点工作,可以看看我们的 招聘页面 。
自动编码器综合介绍
在接下来的几周里,我将发布一系列教程,全面介绍使用神经网络进行无监督和自监督学习,以实现图像生成、图像增强和图像混合。这些主题包括:
- 可变自动编码器(VAEs)(本教程)
- 神经风格迁移学习
- 生成对抗网络
在本教程中,我们将重点关注一种特定类型的自动编码器,称为变分自动编码器。网上有几篇文章解释了如何使用自动编码器,但是没有一篇是特别全面的。在本文中,我计划提供为什么我们可能想要使用 VAEs 的动机,以及它们解决的问题的种类,给出这些神经架构如何工作的数学背景,以及一些使用 Keras 的真实世界实现。
这篇文章借用了哈佛大学 209b 讲座的内容,主要归功于哈佛大学 IACS 系的讲师 T2。
vae 可以说是最有用的自动编码器类型,但在我们尝试处理 vae 之前,有必要了解通常用于数据压缩或去噪的传统自动编码器。
不过,首先,我将通过查看几个示例来让您对 VAEs 可以做的事情感到兴奋。
VAEs 的力量
假设你正在开发一个视频游戏,你有一个开放世界的游戏,有非常复杂的场景。你雇佣了一个图形设计师团队来制作一堆植物和树木来装饰你的世界,但是一旦把它们放到游戏中,你发现这看起来很不自然,因为同一物种的所有植物看起来都完全一样,你能做些什么呢?
首先,你可能会建议使用一些参数化来尝试随机扭曲图像,但是多少个特征就足够了呢?这个变化应该有多大?一个重要的问题是,实现它的计算强度有多大?
这是使用可变自动编码器的理想情况。我们可以训练一个神经网络来学习关于植物的潜在特征,然后每当一个植物出现在我们的世界中时,我们可以从我们“学习”的特征中随机抽取一个样本,并生成一个独特的植物。事实上,这是多少开放世界游戏已经开始在他们的世界中产生风景的方面。
让我们来看一个更形象的例子。假设我们是一名建筑师,想要为任意形状的建筑生成平面图。我们可以让自动编码器网络学习给定任意建筑形状的数据生成分布,它将从我们的数据生成分布中抽取样本,并生成平面图。下面的动画展示了这个想法。
这些对于设计师的潜力可以说是最突出的。想象一下,我们为一家时尚公司工作,任务是创造新的服装风格,事实上,我们可以只训练一个关于“时尚”物品的自动编码器,并允许网络学习时尚服装的数据生成分布。随后,我们可以从这个低维的潜在分布中提取样本,并利用它来创造新的想法。
最后一个示例是我们将在本教程的最后一节学习时尚 MNIST 数据集时使用的示例。
自动编码器
传统自动编码器
自动编码器是非常简单的神经架构。它们基本上是一种压缩形式,类似于使用 MP3 压缩音频文件或使用 JPEG 压缩图像文件的方式。
自动编码器与主成分分析(PCA)密切相关。事实上,如果自动编码器中使用的激活函数在每一层中是线性的,则潜在变量出现在瓶颈(网络中最小的层,aka。代码)直接对应于来自 PCA 的主分量。通常,自动编码器中使用的激活函数是非线性的,典型的激活函数是 ReLU(校正线性单元)和 sigmoid。
网络背后的数学原理相当容易理解,所以我将简要介绍一下。本质上,我们将网络分为两个部分,编码器和解码器。
由ϕ表示的编码器功能将原始数据 x 映射到存在于瓶颈处的潜在空间 f。由ψ表示的解码器函数将瓶颈处的潜在空间 F 映射到输出。在这种情况下,输出与输入函数相同。因此,我们基本上是试图在一些广义的非线性压缩之后重建原始图像。
编码网络可以由通过激活函数的标准神经网络函数来表示,其中 z 是潜在维度。
类似地,解码网络可以以相同的方式表示,但是使用不同的权重、偏置和潜在的激活函数。
损失函数可以用这些网络函数来表示,我们将使用这个损失函数通过标准的反向传播过程来训练神经网络。
由于输入和输出是相同的图像,这不是真正的监督或无监督学习,所以我们通常称之为自监督学习。自动编码器的目的是以这样一种方式选择我们的编码器和解码器功能,即我们需要最少的信息来编码图像,以便它可以在另一侧重新生成。
如果我们在瓶颈层中使用的节点太少,我们重新创建图像的能力将会受到限制,并且我们将重新生成模糊或无法从原始图像中识别的图像。如果我们使用太多的节点,那么使用压缩就没有什么意义了。
压缩的例子很简单,例如,每当你在网飞上下载东西,发送给你的数据都是压缩的。一旦它到达你的电脑,它通过一个解压缩算法,然后显示在你的电脑上。这类似于 zip 文件的工作方式,只是它是通过流算法在幕后完成的。
去噪自动编码器
还有几种其他类型的自动编码器。最常用的一种是去噪自动编码器,它将在本教程的后面使用 Keras 进行分析。这些自动编码器在训练之前向数据添加一些白噪声,但是在训练时将误差与原始图像进行比较。这迫使网络不会过度适应图像中存在的任意噪声。我们稍后将使用它来消除文档扫描图像中的折痕和暗区域。
稀疏自动编码器
与直觉相反,稀疏自动编码器具有比输入或输出维度更大的潜在维度。然而,每次网络运行时,只有一小部分神经元激活,这意味着网络本质上是“稀疏的”。这类似于去噪自动编码器,因为它也是一种正则化形式,以减少网络过拟合的倾向。
收缩式自动编码器
收缩编码器与最后两个过程非常相似,但在这种情况下,我们不改变架构,只是在损失函数中添加一个正则项。这可以被认为是岭回归的一种神经形式。
所以现在我们了解了自动编码器是怎样的,我们需要了解它们不擅长什么。一些最大的挑战是:
- 潜在空间的缺口
- 潜在空间中的可分性
- 离散潜在空间
这些问题都可以在这张图表中得到说明。
Latent space representation for MNIST dataset.
这张图向我们展示了不同标记数字在潜在空间中的位置。我们可以看到,潜在空间包含间隙,我们不知道这些空间中的字符可能看起来像什么。这相当于在监督学习问题中缺乏数据,因为我们的网络没有针对潜在空间的这些情况进行训练。另一个问题是空格的可分性,在上图中有几个数字被很好地分开,但也有一些区域的标签是随机散布的,这使得很难分开字符的独特特征(在本例中是数字 0-9)。这里的另一个问题是无法研究连续的潜在空间,例如,我们没有一个为任意输入而训练的统计模型(即使我们封闭了潜在空间中的所有间隙也不会)。
传统自动编码器的这些问题意味着,在我们能够了解数据生成分布并产生新的数据和图像之前,我们还有一段路要走。
现在我们已经了解了传统的自动编码器是如何工作的,我们将继续讨论变化的自动编码器。这些方法稍微复杂一些,因为它们实现了一种来自贝叶斯统计的变分推理。我们将在下一节更深入地讨论这一点
可变自动编码器
VAEs 继承了传统自动编码器的架构,并使用它来学习数据生成分布,这允许我们从潜在空间中随机采样。然后,可以使用解码器网络对这些随机样本进行解码,以生成独特的图像,这些图像具有与网络被训练的图像相似的特征。
对于熟悉贝叶斯统计的人来说,编码器正在学习后验分布的近似值。这种分布通常难以解析,因为它没有封闭形式的解。这意味着我们可以执行计算量大的采样程序,如马尔可夫链蒙特卡罗(MCMC)方法,或者我们可以使用变分法。正如人们可能怀疑的那样,变分自动编码器使用变分推理来生成其对该后验分布的近似。
我们将详细讨论这个过程,但是对于深入的分析,我强烈推荐查看 Jaan Altosaar 的博客文章。变分推理是研究生机器学习或统计学课程的一个主题,但你不需要统计学学位就能理解其基本思想。感兴趣的人可以链接到 Jaan 的文章:
[## 教程-什么是可变自动编码器?—贾恩·阿尔托萨尔
从两个角度理解变分自动编码器:深度学习和图形模型。
jaan.io](https://jaan.io/what-is-variational-autoencoder-vae-tutorial/)
对于那些对基础数学不感兴趣的人,可以直接跳到 VAE 编码教程。
我们首先需要了解的是后验分布,以及为什么我们不能计算它。看看下面的等式,这是贝叶斯定理。这里的前提是,我们想知道如何学习如何从我们的潜变量 z 生成数据 x。这暗示了我们要学习 p(z|x)。不幸的是,我们不知道这个分布,但我们不需要知道,因为我们可以用贝叶斯定理重新表述这个概率。然而,这并不能解决我们所有的问题,因为分母,即证据,通常是难以处理的。然而,并没有失去一切,因为存在一个厚脸皮的解决方案,允许我们近似这个后验分布。事实证明,我们可以将这个推理问题转化为一个优化问题。
为了逼近后验分布,我们需要一种方法来评估建议分布与真实后验分布相比有多好。为了做到这一点,我们使用贝叶斯统计学家最好的朋友,库尔巴克-莱布勒分歧。KL 散度是两个概率分布相似程度的度量;如果相同,则散度为零,如果是正数,则两个分布不同。KL 散度严格来说是正的,虽然它在技术上不是距离,因为函数是不对称的。我们以下面的方式使用 KL 散度。
这个等式可能看起来吓人,但这里的想法很简单。我们提出了一系列可能的分布,这些分布可能就是我们的数据是如何生成的, Q ,并且我们希望找到最佳分布, q* ,它使我们提出的分布和实际分布之间的距离最小化,由于实际分布的困难性,我们试图对其进行近似。这个公式还有一个问题,即我们实际上不知道 p(z|x ),所以我们不能计算 KL 散度。我们如何解决这个问题?
这就是事情变得有点深奥的地方。我们可以做一些数学操作,用所谓的 ELBO(证据下限)和另一个涉及 p(x)的术语重写 KL 散度。
有趣的是,ELBO 是这个等式中唯一的变量,它取决于我们选择的分布。另一项不受我们选择的分布的影响,因为它不依赖于 q 。因此,我们可以通过最大化(因为它是负的)上式中的 ELBO 来最小化 KL 散度。这里的关键点是,我们实际上可以计算 ELBO,这意味着我们现在可以执行优化程序。
因此,我们现在需要做的就是为 Q 想出一个好的选择,然后对 ELBO 求微分,将其设置为零,瞧,我们就有了最佳分布。在这成为可能之前还有一些障碍,首先,我们必须决定选择什么样的发行版家族。
典型地,当定义 q 时,为了简单起见,进行平均场变分推断。这实质上是说,每个变分参数都是相互独立的。因此,对于每个数据点,我们有一个单独的 q ,我们可以将它们相乘,得到一个联合概率,从而得到“平均场”q
实际上,我们可以选择任意多的字段或集群。例如,在 MNIST 的情况下,我们可能选择 10 个分类,因为我们知道可能存在 10 个可能的数字。
我们需要做的第二件事是通常被称为重新参数化的技巧,借此我们将随机变量从导数中取出,因为对随机变量取导数会由于它们固有的随机性而给我们带来更大的误差。
重新参数化的技巧有点深奥,但它基本上是说,我可以将正态分布写成一个平均值加上一些标准差,乘以一些误差。这意味着当求导时,我们不求随机函数本身的导数,而只求它的参数。
如果这没有多大意义,这里有一篇很好的文章解释了这个技巧,以及为什么它比随机变量本身的导数表现得更好:
[## 重新参数化技巧
戈克尔·埃尔多安的个人网站。
gokererdogan.github.io](https://gokererdogan.github.io/2016/07/01/reparameterization-trick/)
这种方法没有一般的封闭解,所以我们在近似后验分布的能力上仍然受到一些限制。然而,事实上,指数分布族有一个封闭形式的解。这意味着我们可以使用标准分布,如正态分布、二项式分布、泊松分布、贝塔分布等。因此,虽然我们可能找不到真正的后验分布,但我们可以找到一个近似,它在给定指数分布族的情况下做得最好。
变分推理的艺术是选择我们的分布族, Q ,使其足够大,以获得后验概率的良好近似,但又不要太大,以至于计算时间过长。
既然我们已经对我们的网络如何被训练来学习我们数据的潜在分布有了一个不错的想法,我们可以看看我们如何使用这种分布来生成数据。
数据生成过程
看下图,我们可以认为我们对数据生成过程的近似决定了我们要生成数字“2”,所以它从潜在变量质心生成值 2。然而,我们可能不希望每次都生成看起来一样的“2”,就像我们的视频游戏中的植物示例一样,所以我们在潜在空间中向这个项目添加一些随机噪声,这是基于随机数和值“2”的分布的“学习”分布。我们把它通过我们的解码器网络,我们得到了一个看起来和原来不同的 2。
这是一个过于简化的版本,它抽象了实际自动编码器网络的架构。下面是在编码器和解码器网络中使用卷积层的实际变分自动编码器的架构表示。我们看到,我们正在分别学习潜在空间内数据生成分布的中心和分布,然后从这些分布中“采样”以生成本质上“虚假”的数据。
学习过程的固有性质意味着看起来相似的参数(刺激相同的网络神经元放电)在潜在空间中聚集在一起,而不是任意隔开。下图对此进行了说明。我们看到我们的值 2 开始聚集在一起,而值 3 逐渐被推开。这是有用的,因为这意味着网络不会任意地将字符放置在潜在空间中,使得值之间的转换不那么虚假。
整个网络架构的概述如下所示。希望,在这一点上,程序是有意义的。我们使用一组图像来训练自动编码器,以学习我们在潜在空间内的均值和标准差,这形成了我们的数据生成分布。接下来,当我们想要生成类似的图像时,我们从潜在空间中的一个质心进行采样,使用我们的标准偏差和一些随机误差稍微扭曲它,然后将它通过解码器网络。从这个例子中可以清楚地看出,最终输出看起来与输入图像相似,但并不相同。
VAE 编码教程
在本节中,我们将研究一个简单的去噪自动编码器,用于消除文档扫描图像上的折痕和标记,以及消除时尚 MNIST 数据集中的噪声。在 MNIST 数据集上训练网络之后,我们将使用 VAEs 来生成新的服装项目。
去噪自动编码器
时尚 MNIST
在第一个练习中,我们将向时尚 MNIST 数据集添加一些随机噪声(椒盐噪声),并尝试使用去噪自动编码器来消除这些噪声。首先,我们执行预处理:下载数据,缩放数据,然后添加噪声。
之后,我们为自动编码器网络创建架构。这涉及多层卷积神经网络、编码器网络上的最大池层和解码器网络上的升级层。
这个模型需要一段时间来运行,除非你有一个图形处理器,它可能需要大约 3-4 分钟的时间。我们的输入图像、带噪声的输入图像和输出图像如下所示。
Input images from fashion MNIST.
Input images with salt and pepper noise.
Output from denoising network
正如你所看到的,我们能够从嘈杂的图像中充分去除噪声,但我们失去了相当数量的服装细节的分辨率。这是我们为强大的网络付出的代价之一。可以调整网络,以便使最终输出更能代表输入图像。
文字清理
去噪自动编码器的第二个例子包括清理扫描图像的折痕和暗区。这是我们希望获得的输入和输出图像。
Input images of ‘noisy’ text data.
Cleaned text images.
这方面的数据预处理有点复杂,所以我不会在这里介绍,但它可以在我的 GitHub 存储库中找到,还有数据本身。网络架构如下。
变型自动编码器
最后,我们将尝试生成时装 MNIST 数据集中出现的服装项目的新图像。
这方面的神经架构稍微复杂一点,包含一个称为“Lambda”层的采样层。
这是架构,但我们仍然需要插入损失函数,并纳入 KL 散度。
我们现在可以查看重构的样本,看看我们的网络能够学习到什么。
我们可以清楚地看到鞋子、手袋以及服装之间的过渡。并非所有的潜在空间都绘制在这里,以帮助图像清晰。我们还可以查看时装 MNIST 数据集中的 10 件服装的潜在空间和颜色代码。
我们看到这些项目被分成不同的组。
最终点评
本教程是自动编码器、变分自动编码器和变分推理的速成课程。我希望读者对此感兴趣,并且现在对什么是自动编码器以及如何在实际应用中使用它们有了更好的理解。
在我的下一个教程中,我将着眼于使用生成式对抗网络制作名人和动漫人物的假图像,这是变型自动编码器的天然垫脚石。
利用生成性对抗网络生成现代艺术
趣味甘
创建一个生成性对抗网络来生成现代艺术,并在 Spell 平台的 GPU 上对其进行训练
Arts Generated By Our GAN
先决条件
你需要很好地理解:
- 图像处理
- Python 编程语言
- numpy——科学计算图书馆
- Keras —深度学习图书馆
和一些基本知识:
- 生成对抗网络
使用的数据集
介绍
在本教程中,我们将看一步一步的过程来创建一个生成性的对抗性网络,以生成现代艺术,并使用 Python 和 Keras 编写代码。
之后,为了训练模型,我们将使用一个强大的拼写平台的 GPU 实例。一切都将在途中解释,并提供进一步阅读的链接。
我们开始吧!
探索数据集
在开始之前,让我们看看我们的图像数据集。
WikiArt 收藏了大量不同风格的现代艺术作品。对于我们的特定项目,我们将使用 立体派 风格的图像。
你可以从 WikiArt.org 了解更多的艺术风格和现代艺术。
下载图像
你既可以从 WikiArt 下载你喜欢的图片,也可以通过 cs-chan 前往该库下载 26GB 的 WikiArt 图片。
因为它有所有不同类型的集合,我们将只选择立体主义并将它们存储在名为 dataset 的文件夹中。
处理图像数据
我们数据集中的图像大小不同,为了将它们输入到我们的生成性对抗性神经网络中,我们将把所有图像的大小调整为 128X128。
开始之前,在数据集文件夹所在的根目录下创建一个 python 文件。
现在,让我们编写一个小的 python 脚本,从文件夹中选择所有图像,并将其大小调整为 128X128,然后保存在 cubism_data.npy 文件中。
*## image_resizer.py*
*# Importing required libraries*
**import** os
**import** numpy **as** np
**from** PIL **import** Image
*# Defining an image size and image channel*
*# We are going to resize all our images to 128X128 size and since our images are colored images*
*# We are setting our image channels to 3 (RGB)*
IMAGE_SIZE = 128
IMAGE_CHANNELS = 3
IMAGE_DIR = 'dataset/'
*# Defining image dir path. Change this if you have different directory*
images_path = IMAGE_DIR
training_data = []
*# Iterating over the images inside the directory and resizing them using*
*# Pillow's resize method.*
print('resizing...')
**for** filename **in** os.listdir(images_path):
path = os.path.join(images_path, filename)
image = Image.open(path).resize((IMAGE_SIZE, IMAGE_SIZE), Image.ANTIALIAS)
training_data.append(np.asarray(image))
training_data = np.reshape(
training_data, (-1, IMAGE_SIZE, IMAGE_SIZE, IMAGE_CHANNELS))
training_data = training_data / 127.5 - 1
print('saving file...')
np.save('cubism_data.npy', training_data)
我们来分解一下。
在上面的代码块中,在前几行,我们已经导入了执行调整大小操作所需的所有库。
**import** os
**import** numpy **as** np
**from** PIL **import** Image
IMAGE_SIZE = 128
IMAGE_CHANNELS = 3
IMAGE_DIR = 'dataset/'
images_path = IMAGE_DIR
这里,我们使用 Pillow 将所有图像调整到我们想要的大小,并将它们作为 numpy 数组添加到一个列表中。
training_data = []**for** filename **in** os.listdir(images_path):
path = os.path.join(images_path, filename)
image = Image.open(path).resize((IMAGE_SIZE, IMAGE_SIZE), Image.ANTIALIAS)
training_data.append(np.asarray(image))
之后,我们使用 numpy 以合适的格式重塑数组,并规范化数据。
training_data = np.reshape(
training_data, (-1, IMAGE_SIZE, IMAGE_SIZE, IMAGE_CHANNELS))
training_data = training_data / 127.5–1
标准化之后,我们将图像数组保存在 npy 二进制文件中,这样我们就不必每次都遍历所有的图像。
np.save(‘cubism_data.npy’, training_data)
这就是处理我们的图像数据。
创建 GAN
现在是我们项目中最激动人心的部分了,从这里开始我们将为生成性对抗网络(GAN)编写代码。
我们将使用Keras——一个深度学习库来创建我们的 GAN。
开始之前,让我们简单了解一下什么是 GAN 及其结构。
甘是什么?
生成对抗网络(GANs)是机器学习领域一项令人兴奋的最新创新。伊恩·哥德费罗在他的论文 中首次提出了生成对抗网络 。
gan 是生成型模型:在给定一些训练数据后,它们可以创建看起来像你的训练数据的新数据实例。例如,GANs 可以创建看起来像人脸照片的图像,即使这些人脸不属于任何真实的人。
关于 GAN 的一个很好的例子,你可以访问,它是由 Nvidia 创造的。它生成了一个不存在的人的高质量图像。****
听起来很有趣,对吗?
它是如何工作的?
让我们了解它的结构和工作原理。
甘提出了两种模型:生成模型和判别模型。
生成模型负责生成不同种类的噪声数据,而鉴别模型负责鉴别给定数据是真的还是假的。
生成模型不断地训练自己,通过生成假噪声数据来欺骗判别模型,而判别模型从训练集中训练自己来分类数据是否来自数据集,并且不被生成模型欺骗。
GAN structure source
损失函数
GAN 中的鉴别器使用交叉熵损失,因为鉴别器的工作是分类;交叉熵损失是最好的方法。
这个公式表示 p:真实分布和 q:估计分布之间的交叉熵损失。
§和(q)是 m 维的,其中 m 是类的数量。
在 GAN 中,鉴别器是一个二元分类器。它需要区分数据是真的还是假的。也就是说 m = 2。真正的分布是一个仅包含 2 项的热向量。
对于 n 个样本,我们可以对损失求和。
上面所示的等式是二元交叉熵损失,其中 y 可以取两个值 0 和 1。
GAN 有一个潜在向量 z,图像 G(z)就神奇地从中生成。我们对真实图像 x 和生成的图像 G(z)应用鉴别器函数 D。
损失函数的目的是将真实图像的预测推向 1,将伪图像推向 0。我们用对数概率项来做。
注 : ~ 符号表示:分布为,这里的 Ex 表示期望值:由于我们不知道样本是如何被送入鉴别器的,所以我们将它们表示为期望值而不是总和。
如果我们观察联合损失函数,我们将最大化鉴别器项,这意味着 D(x)的 log 应该逐渐接近零,而 D(G(z))的 log 应该接近 1。在这里,生成器试图使 D(G(z))英寸更接近 1,而鉴别器试图做相反的事情。
GAN 的代码
现在,让我们毫不迟疑地写我们的 GAN。
我们将把我们的文件命名为 art_gan.py ,并将其存储在根目录中。这个文件将包含我们的生成器和鉴别器的所有超参数和函数。
让我们写一些代码:
****from** keras.layers **import** Input, Reshape, Dropout, Dense, Flatten, BatchNormalization, Activation, ZeroPadding2D
**from** keras.layers.advanced_activations **import** LeakyReLU
**from** keras.layers.convolutional **import** UpSampling2D, Conv2D
**from** keras.models **import** Sequential, Model, load_model
**from** keras.optimizers **import** Adam
**import** numpy **as** np
**from** PIL **import** Image
**import** os**
在这里,我们导入创建 GAN 所需的所有库和辅助函数。
所有的导入都是不言自明的。在这里,我们正在导入一些 keras 层来创建我们的模型。
现在让我们定义一些参数:
***# Preview image Frame
PREVIEW_ROWS = 4
PREVIEW_COLS = 7
PREVIEW_MARGIN = 4
SAVE_FREQ = 100**# Size vector to generate images from
NOISE_SIZE = 100**# Configuration
EPOCHS = 10000 # number of iterations
BATCH_SIZE = 32**GENERATE_RES = 3
IMAGE_SIZE = 128 # rows/cols*IMAGE_CHANNELS = 3**
在这里的前几行,我们已经定义了图像帧的大小和填充来保存我们生成的图像。
这里是生成我们的图像的潜在维度大小。
EPOCHS 是迭代次数:它定义了我们想要迭代训练图像的次数,而 BATCH_SIZE 是每次迭代中要输入的图像数量。
IMAGE_SIZE 是我们之前调整到 128X128 的图像尺寸,而 IMAGE_CHANNELS 是我们图像中的通道数;也就是 3。
注意:图像应始终为方形尺寸
让我们加载之前创建的 npy 数据文件。
**training_data = np.load(‘cubism_data.npy’)**
为了加载 npy 文件,我们使用 numpy 的 load 函数并将文件路径作为参数传递。
因为我们的数据文件在根目录中,所以不需要额外的路径参数。如果您已经将数据存储在其他地方,您可以使用以下代码来加载数据:
**training_data = np.load(os.path.join(‘dirname’, ‘filename.npy’))**
这就是加载我们的训练数据。
现在我们可以创建生成器和鉴别器函数了。
让我们看看代码:
****def** **build_discriminator**(image_shape): model = Sequential() model.add(Conv2D(32, kernel_size=3, strides=2,
input_shape=image_shape, padding=”same”))
model.add(LeakyReLU(alpha=0.2))
model.add(Dropout(0.25)) 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=0.2))
model.add(Dropout(0.25)) 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(0.25)) 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(0.25))
model.add(Conv2D(512, kernel_size=3, strides=1, padding=”same”))
model.add(BatchNormalization(momentum=0.8))
model.add(LeakyReLU(alpha=0.2)) model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(1, activation=’sigmoid’)) input_image = Input(shape=image_shape)
validity = model(input_image)
**return** Model(input_image, validity)**
分解一下:
如果您对 keras 有所了解,那么代码是不言自明的。
总的来说,我们正在定义一个以 image_shape 为参数的函数。
在这个函数中,我们从 keras 中初始化一个顺序模型,帮助我们创建线性层堆栈。
**model = Sequential()**
之后,我们在顺序模型中添加一些层。
我们的第一层是一个 32 形状的卷积层,其 kernel_size 为 3,我们的步幅值为 2,填充值相同。因为它是第一层,所以它保存输入形状。
要了解这是怎么回事,可以参考 keras 官方文档页面。
简单地说,我们在这里定义一个卷积层,它有一个大小为 3X3 的滤波器,该滤波器跨越我们的图像数据。我们有相同的衬垫,这意味着,没有额外的衬垫。它仍然和原来一样。
**model.add(Conv2D(32, kernel_size=3, strides=2,
input_shape=image_shape, padding=”same”))model.add(LeakyReLU(alpha=0.2))**
之后,我们添加了一个激活函数 LeakyRelu 层。
类似地,在其他块中,层被添加到顺序模型中,具有一些漏失和批量标准化,以防止过度拟合。
我们模型的最后一层是具有激活函数 sigmoid 的全连接层。
由于我们的鉴别器的工作是分类给定的图像是否是假的,这是一个二元分类任务,sigmoid 是一个将每个值压缩到 0 和 1 之间的激活。
**model.add(Flatten())
model.add(Dense(1, activation=’sigmoid’))**
现在,在初始化我们的鉴别器模型之后,让我们也创建一个生成模型。
****def** **build_generator**(noise_size, channels):
model = Sequential()
model.add(Dense(4 * 4 * 256, activation=”relu”, input_dim=noise_size))
model.add(Reshape((4, 4, 256))) model.add(UpSampling2D())
model.add(Conv2D(256, kernel_size=3, padding=”same”))
model.add(BatchNormalization(momentum=0.8))
model.add(Activation(“relu”)) model.add(UpSampling2D())
model.add(Conv2D(256, kernel_size=3, padding=”same”))
model.add(BatchNormalization(momentum=0.8))
model.add(Activation(“relu”)) **for** i **in** range(GENERATE_RES):
model.add(UpSampling2D())
model.add(Conv2D(256, kernel_size=3, padding=”same”))
model.add(BatchNormalization(momentum=0.8))
model.add(Activation(“relu”)) model.summary()
model.add(Conv2D(channels, kernel_size=3, padding=”same”))
model.add(Activation(“tanh”)) input = Input(shape=(noise_size,))
generated_image = model(input)
**return** Model(input, generated_image)**
分解一下:
这里我们定义了一个函数,它将 noise_size 和 channels 作为参数。
在函数内部,我们再次初始化了一个序列模型。
因为我们的生成器模型必须从噪声向量生成图像,所以我们的第一层是大小为 4096 (4 * 4 * 256)的完全连接的密集层,它将 noise_size 作为参数。
**model.add(Dense(4 * 4 * 256, activation=”relu”, input_dim=noise_size))**
注意:我们已经定义了它的大小为 4096,以便在 4X4X256 的图层中调整它的大小。
之后,我们使用整形图层将完全连接的图层整形为 4X4X256 的形状。
**model.add(Reshape((4, 4, 256)))**
这之后的层块只是一个具有批量归一化和激活函数 relu 的卷积层。
为了看到和理解它的样子,我们来看一下模型摘要:
从 4X4 的形状扩展到 128X128 的大小,这是我们的 training_data 形状。
我们的生成器模型将噪声作为输入,输出图像。
初始化生成器和鉴别器模型后,让我们编写一个助手函数,在一些迭代后保存图像。
****def** save_images(cnt, noise):
image_array = np.full((
PREVIEW_MARGIN + (PREVIEW_ROWS * (IMAGE_SIZE + PREVIEW_MARGIN)),
PREVIEW_MARGIN + (PREVIEW_COLS * (IMAGE_SIZE + PREVIEW_MARGIN)), 3),
255, dtype=np.uint8)generated_images = generator.predict(noise)generated_images = 0.5 * generated_images + 0.5image_count = 0
**for** row in range(PREVIEW_ROWS):
**for** col in range(PREVIEW_COLS):
r = row * (IMAGE_SIZE + PREVIEW_MARGIN) + PREVIEW_MARGIN
c = col * (IMAGE_SIZE + PREVIEW_MARGIN) + PREVIEW_MARGIN
image_array[r:r + IMAGE_SIZE, c:c +
IMAGE_SIZE] = generated_images[image_count] * 255
image_count += 1output_path = 'output'
if not os.path.exists(output_path):
os.makedirs(output_path)filename = os.path.join(output_path, f"trained-{cnt}.png")
im = Image.fromarray(image_array)
im.save(filename)**
我们的 save_images 函数将计数和噪声作为输入。
在函数内部,它根据我们上面定义的参数生成帧,并存储我们根据噪声输入生成的图像数组。
之后,它会将其保存为图像。
现在,是我们编译模型并训练它们的时候了。
让我们也为此编写一段代码:
**image_shape = (IMAGE_SIZE, IMAGE_SIZE, IMAGE_CHANNELS)optimizer = Adam(1.5e-4, 0.5)discriminator = build_discriminator(image_shape)
discriminator.compile(loss=”binary_crossentropy”,
optimizer=optimizer, metrics=[“accuracy”])
generator = build_generator(NOISE_SIZE, IMAGE_CHANNELS)random_input = Input(shape=(NOISE_SIZE,))generated_image = generator(random_input)discriminator.trainable = **False**validity = discriminator(generated_image)combined = Model(random_input, validity)
combined.compile(loss=”binary_crossentropy”,
optimizer=optimizer, metrics=[“accuracy”])y_real = np.ones((BATCH_SIZE, 1))
y_fake = np.zeros((BATCH_SIZE, 1))fixed_noise = np.random.normal(0, 1, (PREVIEW_ROWS * PREVIEW_COLS, NOISE_SIZE))cnt = 1
**for** epoch **in** range(EPOCHS):
idx = np.random.randint(0, training_data.shape[0], BATCH_SIZE)
x_real = training_data[idx]
noise= np.random.normal(0, 1, (BATCH_SIZE, NOISE_SIZE))
x_fake = generator.predict(noise)
discriminator_metric_real = discriminator.train_on_batch(x_real, y_real)discriminator_metric_generated = discriminator.train_on_batch(
x_fake, y_fake)
discriminator_metric = 0.5 * np.add(discriminator_metric_real, discriminator_metric_generated)generator_metric = combined.train_on_batch(noise, y_real)**if** epoch % SAVE_FREQ == 0:
save_images(cnt, fixed_noise)
cnt += 1
print(f”{epoch} epoch, Discriminator accuracy: {100* discriminator_metric[1]}, Generator accuracy: {100 * generator_metric[1]}”)**
分解一下:
在这里的前几行中,我们已经定义了我们的输入形状:128X128X3 (image_size,image_size,image_channel)。
之后,我们使用亚当作为我们的优化器。
注:所有参数均来源于论文[1]。
**image_shape = (IMAGE_SIZE, IMAGE_SIZE, IMAGE_CHANNELS)optimizer = Adam(1.5e-4, 0.5)discriminator = build_discriminator(image_shape)
discriminator.compile(loss=”binary_crossentropy”,
optimizer=optimizer, metrics=[“accuracy”])**
初始化优化器后,我们调用 build_discriminator 函数并传递图像形状,然后用损失函数和优化器对其进行编译。
因为它是一个分类模型,我们使用准确性作为它的性能度量。
类似地,在下一行中,我们调用 build_generator 函数并传递 random_input 噪声向量作为它的输入。
**generator = build_generator(NOISE_SIZE, IMAGE_CHANNELS)random_input = Input(shape=(NOISE_SIZE,))generated_image = generator(random_input)**
它将生成的图像作为输出返回。
现在,GAN 的一个重要部分是我们应该阻止我们的鉴别器进行训练。
**discriminator.trainable = **False**validity = discriminator(generated_image)combined = Model(random_input, validity)
combined.compile(loss=”binary_crossentropy”,
optimizer=optimizer, metrics=[“accuracy”])**
因为我们在这里只是训练生成器,所以我们不想调整鉴别器的权重。
这就是对抗性网络中“对抗性”的真正含义。
如果我们不设置这个,生成器会调整它的权重,这样它就能更好的愚弄鉴别器,它也会调整鉴别器的权重,使它更好的被愚弄。
我们不想这样。所以,我们要分开训练他们,互相对抗。
然后,我们用损失函数和优化器编译生成模型。
之后,我们将两个向量定义为 y_real 和 y_fake。
**y_real = np.ones((BATCH_SIZE, 1))
y_fake = np.zeros((BATCH_SIZE, 1))fixed_noise = np.random.normal(0, 1, (PREVIEW_ROWS * PREVIEW_COLS, NOISE_SIZE))**
这些向量由随机的 0 和 1 值组成。
之后,我们创建一个 fixed_noise:这将导致生成的图像被保存下来,我们可以看到它在每次迭代中变得更好。
之后,我们将使用我们定义的时期范围迭代我们的训练数据。
**cnt = 1
**for** epoch **in** range(EPOCHS):
idx = np.random.randint(0, training_data.shape[0], BATCH_SIZE)
x_real = training_data[idx]
noise= np.random.normal(0, 1, (BATCH_SIZE, NOISE_SIZE))
x_fake = generator.predict(noise)
discriminator_metric_real = discriminator.train_on_batch(x_real, y_real) discriminator_metric_generated = discriminator.train_on_batch(
x_fake, y_fake)
discriminator_metric = 0.5 * np.add(discriminator_metric_real, discriminator_metric_generated) generator_metric = combined.train_on_batch(noisse, y_real)
**if** epoch % SAVE_FREQ == 0:
save_images(cnt, fixed_noise)
cnt += 1
print(f”{epoch} epoch, Discriminator accuracy: {100* discriminator_metric[1]}, Generator accuracy: {100 * generator_metric[1]}”)**
在迭代过程中,我们从真实图像中提取样本,并将其放在 x_real 上。之后,我们定义一个噪声向量,并将其传递给我们的生成器模型,以在 x_fake 中生成一个假图像。
然后,我们分别在真实和虚假图像中训练我们的鉴别器模型。
**discriminator_metric_real = discriminator.train_on_batch(x_real, y_real)discriminator_metric_generated = discriminator.train_on_batch(
x_fake, y_fake)**
一些研究表明,分别训练他们可以让我们得到更好的结果。
训练后,我们从两个模型中提取指标并取平均值。
**discriminator_metric = 0.5 * np.add(discriminator_metric_real, discriminator_metric_generated)**
通过这种方式,我们获得了鉴别器模型的指标,现在,对于发生器模型,我们在噪声向量和 y_real:上训练它,y _ real:是 1 的向量。
在这里,我们试图训练发电机。超时生成器将从这些输入中变得更好,并且鉴别器将不能鉴别输入是假还是真。
这里需要注意的一点是,我们的组合模型是基于直接链接到鉴别器模型的生成器模型。这里我们的输入是发生器想要的输入:噪声,输出是鉴别器给我们的。
最后,我们有一个 if 语句来检查我们的检查点。
****if** epoch % SAVE_FREQ == 0:
save_images(cnt, fixed_noise)
cnt += 1
print(f”{epoch} epoch, Discriminator accuracy: {100* discriminator_metric[1]}, Generator accuracy: {100 * generator_metric[1]}”)**
如果到达检查点,则保存当前迭代噪声并打印生成器和鉴别器的当前精度。
这都是为了创建 GAN 的编码部分,但我们还没有完成。
我们刚刚为它编写了代码,现在我们必须实际训练这些模型,并查看它的输出表现如何。
训练甘
在普通的笔记本电脑上训练 GAN 是不可能的,因为它需要很高的计算能力。
普通 CPU 的普通笔记本电脑无法处理如此庞大的任务,所以我们准备用 拼 :这是机器学习和深度学习最快最强大的端到端平台。
为什么拼写?
魔咒是构建和管理机器学习项目的强大平台。魔咒负责基础设施,使机器学习项目更容易启动,更快获得结果,更有组织性,比自己管理基础设施更安全。
在每一个法术注册,你可以获得 10 元免费信贷!
简单地说,我们将把我们的数据文件上传到拼写平台,让它处理我们所有的训练任务。Spell 在他们强大的 GPU 中运行我们的任务,这样我们就什么都不用担心了。我们可以从他们的 Web GUI 监控我们的日志,并且我们所有的输出都被安全地保存。
培训过程
在拼写运行我们的项目之前,有几件事情要涉及。
首先,我们必须创建我们的法术账户。在他们的官方页面上有很好很容易上手的文档。
创建帐户后,我们可以使用 pip install 安装 Spell CLI:
**pip install spell**
这就把所有的法术能量都安装到了我们的笔记本电脑上。我们既可以使用 Web GUI,也可以轻松地登录到 spell 服务器,从 cmd 或 bash 执行命令。
为了上传我们的项目,我们将使用命令行工具。
让我们在项目文件夹的根目录中打开命令行终端,并使用拼写登录命令登录到服务器:
**spell login**
成功登录后,现在我们可以上传我们的训练数据文件,并在拼写服务器中运行我们的代码:
**Spell upload “filename”**
之后,我们的训练数据将被上传到服务器。
注意:在服务器运行代码之前,代码已经被推送到 github。
现在我们准备在拼写服务器中执行我们的代码。
在命令行中,让我们运行以下命令:
**Spell run python art_gan.py -t V100 -m uploads/art_gan/cubism_data.npy**
上面的命令在机器类型为 V100 的 Spell 服务器上运行我们的代码,这是一台 GPU 机器。最后一个参数挂载数据集目录,以便我们的代码可以访问它。
现在代码已经成功执行,您可以在控制台上看到日志。如果您想在 GUI 中进行监控,那么您可以登录到 Spell 的 Web GUI,查看“运行”部分。
如您所见,它保存了我们最近运行的所有信息。
就像我们写的代码一样。在每 100 次迭代中,我们生成的图像被保存在输出目录中,并打印带有精度度量的日志。
您可以在日志部分查看它们。
太棒了,不是吗?当它为你训练和保存输出时,你可以做你的其他工作。
输出
现在,训练完成后,Spell 会自动将我们的输出保存在 resources/runs 目录中。
之后,我们可以使用以下命令将 Spell 运行的输出下载到我们的本地机器上:
**spell cp [OPTIONS] SOURCE_PATH [LOCAL_DIR]**
对于这个项目,它将是:
**spell cp runs/44**
您只需输入 runs/ 即可下载该 runs 的内容。
就是这样!!现在,您已经在本地机器上的 Spell 的 GPU 机器上训练了 GAN 的输出。现在,您可以从输出图像中直观地看到 GAN 执行情况。
结论
GANs 是一个令人兴奋且快速变化的领域,它实现了生成模型的承诺,能够在一系列问题领域生成真实的例子。一夜之间理解 GAN 或者任何机器学习、深度学习领域都不是一件容易的事情。这需要耐心、大量的练习和理解。
在以前,对于像我们这样有抱负的 ML 爱好者来说,进行重复的练习来看看发生了什么是不可能的。但是现在,像 Spell 这样的平台帮助我们提供一个系统来运行和管理我们的项目,这样我们就可以运行和测试我们的模型。
我们所创造的只是 GAN 如何被创造和 GAN 能做什么的简单表示。还有更高级的调整有待执行。
为了更进一步,你可以调整参数,看看它如何不同地生成图像。
还有很多东西可以研究。
****如有任何疑问和讨论,你可以从这里加入拼写社区:【https://chat.spell.ml/ ****
参考
[1] 生成对抗网络,伊恩·古德菲勒,让·普盖-阿巴迪,迈赫迪·米尔扎,徐炳,大卫·沃德-法利,谢尔吉尔·奥泽尔,亚伦·库维尔,约舒阿·本吉奥,2014
[2] 生成性对抗网络(GANs)的温和介绍杰森·布朗利,2019【在线】https://machine learning mastery . com/what-are-Generative-Adversarial-Networks-GANs/
[3] 生成对抗网络(GANs)入门指南克里斯,2019【在线】https://skymind.ai/wiki/generative-adversarial-network-gan