allenlu2007

A great WordPress.com site

GCP Virtual Machine and ssh task execution

Ref1: http://orcaman.blogspot.tw/2013/08/google-compute-engine-keeping-your.html

Ref2: https://www.tecmint.com/keep-remote-ssh-sessions-running-after-disconnection/amp/

使用 GCP 主要使用 ssh command line window.  好處是在各種平台都可以運行。壞處就是缺乏 GUI, 需要用 VNC + gnome desktop 協助。

另外一個很大的問題:一旦 ssh disconnect, 正在運行的 task 也自動被結束。即使是用 background task ( command &) 也不行。

這是一個很大的困擾。

 

原因可參考 Ref2.  

when we ssh on to other user on some other system and run commands on that machine, it actually creates a pseudo-terminal and attaches it to the login shell of the user logged in.

When we log out of the session or the session times out after being idle for quite some time, the SIGHUP signal is send to the pseudo-terminal and all the jobs that have been run on that terminal, even the jobs that have their parent jobs being initiated on the pseudo-terminal are also sent the SIGHUP signal and are forced to terminate.

Only the jobs that have been configured to ignore this signal are the ones that survive the session termination. On Linux systems, we can have many ways to make these jobs running on the remote server or any machine even after user logout and session termination.

 

本文是討論解法。參考 Ref1.

So after we’ve ssh’ed into our GCE VM, we will need to:
1. install GNU screen: 

apt-get update
apt-get upgrade
apt-get install screen
 
2. type “screen”. this will open up a new screen – kind of similar in look & feel to what “clear” would result in. 
3. run the process (e.g.: ./init-dev.sh to fire up a ChicagoBoss erlang server)
4. type: Ctrl + A, and then Ctrl + D. This will detach your screen session but leave your processes running!
5. feel free to close the SSH terminal. whenever you feel like it, ssh back into your GCE VM, and type screen -r to resume your previously detached session.
6. to kill all detached screens, run: 

screen -ls | grep pts | cut -d. -f1 | awk '{print $1}' | xargs kill

 

百度 Deep Voice – TTS (Text To Sound)

Reference: https://www.leiphone.com/news/201703/P1OEbKjpB0pHvHDA.html

Reference: https://arxiv.org/pdf/1702.07825.pdf

Medium

NewImage 

 

 

NewImage

 

Deep voice 包含以上三個部分:Grapheme-to-Phoneme; F0 Prediction; Audio Synthesis

 

Step 1: Grapheme-to-Phoneme 語素轉音素

以英语为代表的语言不是语音语言(phonetic)。

(雷锋网AI科技评论按:语音语言指的是单词拼写与读音一致的语言,比如拉丁语就是一种典型的语音语言,即单词中没有不发音的字母,每个字母都有固定的发音。 )

例如以下单词(参考于linguisticslearner),都带后缀“ough”:

1.thoug (和 go 里面的 o 类似 )

2.through (和 too 里面的 oo 类似)

3.cough (和 offer 里面的 off 类似 )

4.rough (和 suffer 里面的的 uff 类似)

注意,即使它们有相同的拼写,但它们的发音却完全不同。如果我们的 TTS 系统使用拼写作为其主要输入,即使有相同的后缀,在接受为什么”thoug”和”rough”发音如此不同上,会不可避免地会陷入困境。 因此,我们需要使用稍微不同的表达方式,展示出更多的发音信息。

音素正是这样的一样东西。我们发出来的声音由不同音素单位组成。将因素组合在一起,我们几乎可以 重复发出任何单词的发音。这里有几个拆分成音素的词语(改编自CMU的音素字典):

· White Room – [ W, AY1, T, ., R, UW1, M,. ]

· Crossroads – [ K, R, AO1, S, R, OW2, D, Z, . ]

在音素旁边的1,2等数字表示应该发重音的位置。此外,句号表示音间停顿。

因此 Deep Voice 的第一步是,利用一个简单的音素字典,把每个句子直接转换为对应的音素。

我们的句子

处理我们句子的第一步, Deep Voice 将具有以下输入和输出。

· Input – “It was earky spring”

· Output – [IH1, T, ., W, AA1, Z, ., ER1, L, IY0, ., S, P, R, IH1, NG,. ]

在下一篇博文中我们将介绍如何训练这样的模型。

 

Step 2: Fundamental Frequency Prediction 基頻預測

为了让发音尽可能地接近人声,我们还想要预测出每个音素的音调和语调。这一点从多方面考量,对以汉语为代表的语言尤为重要。因为这些语言中,相同的声音,读出不同的音调和重音具有完全不同的含义。预测每个音素的基频有助于我们发好每一个音素,因为频率会告诉系统,什么音素该发什么音高和什么音调。

此外,一些音素并不完全都发浊音,这意味着发这些音不需要每次都震动声带。

例如,拿发音“ssss”和“zzzz”做例子,注意到前者是清音 (unvoiced),发音时声带没有振动,而后者是浊音 (voiced) ,发音时声带振动了。

我们的基本频率预测也将考虑到这一点,预测出何时应当发清音,何时应该发浊音。

我们的句子

在这一步我们的例句会变成以下形式:

· Input – [IH1, T, ., W, AA1, Z, ., ER1, L, IY0, ., S, P, ., R, Ih1, NG,.]

· Output – [IH1(140hz),T(142hz),. (Not voiced),…]

-> 我認為這部分有很多的改進空間!!!

 

Step 3: Audio Synthesis 語音合成

語音合成是用 WaveNet 相同的架構。主要是優化 procedure 加速執行速度。

WaveNet 生成原始波形,允许生成所有类型的声音,不同的口音、情绪、呼吸和人类语音的其他基本部分都能包含在内,这样的声音和人类的声音区别就非常小了。此外, WaveNet 甚至能在这一步之上生成音乐。

在发布的文章中,百度团队通过优化程序的执行能力,特别是优化执行生成高频输入的能力来改进 WaveNet 。因此, WaveNet 需要几分钟来生成一秒钟的新音频,百度修改后的 WaveNet 可能只需要几分之一秒完成同样的任务,如 Deep Voice 的作者所述:

Deep Voice 可以在几分之一秒内合成音频,并在合成速度和音频质量之间提供可调谐的权衡。相比之下,以前的 WaveNe 合成一秒钟的音频需要几分钟的运行时间。

深度學習的開山文章

Reference: https://baijia.baidu.com/s?id=1574411896360227&wfr=pc&fr=ch_lst

Excellent articles

 

自从2012年CNN首次登陆ImageNet挑战赛并一举夺取桂冠后,由CNN发展开来的深度学习一支在近5年间得到了飞速的发展。

 

今天,我们将带领大家一起阅读9篇为计算机视觉和卷积神经网络领域里带来重大发展的开山之作,为大家摘录每篇论文的主要思路、重点内容和贡献所在。

 

AlexNet (2012)  

https://papers.nips.cc/paper/4824-imagenet-classification-with-deep-convolutional-neural-networks.pdf

这篇论文可谓是CNN的开山鼻祖(当然,有些人也会说Yann LeCun 1998年发表的Gradient-Based Learning Applied to Document Recognition才是真正的开山之作)。该论文题为“ImageNet Classification with Deep Convolutional Networks”,目前已经被引用6184次,是该领域公认影响最为深远的论文之一。

在这篇论文中,Alex Krizhevsky, Ilya Sutskever, 和Geoffrey Hinton 共同创造了一个“大规模深度神经网络”,并用它赢得了2012 ImageNet挑战赛 (ImageNet Large-Scale Visual Recognition Challenge)的冠军。(注:ImageNet可被视作计算机视觉领域的奥林匹克大赛,每年,来自全世界的各个团队在这项竞赛中激烈竞争,不断发现用于分类、定位、探测等任务的最优计算机视觉模型。)

2012年,CNN首次登场便以15.4%的错误率拔得头筹,远远优于第二名的26.2%。CNN对计算机视觉领域的贡献可见一斑。自这场比赛后,CNN就变得家喻户晓了。

这篇论文里讨论了AlexNet神经网络的具体结构。相比现在流行的神经网络构架,它的结构其实非常简单:5个卷积层、最大池化层(max pooling layer),dropout层,和3个全连接层(fully connected layers)。但这个结构已经可以被用来分类1000种图片了。

AlexNet的结构(可能你会注意到,图上出现了两个分支,这是因为训练计算量太大了,以至于他们不得不把训练过程分到两个GPU上) 


划重点:

ReLu, Data augmentation, Dropout, batch SGD

训练集ImageNet data有超过1500万张标记图片,共22000个分类。 

解非线性方程用的是ReLu算法(因为发现ReLu的训练时间相比卷积tanh函数要快上数倍)。

-> 另外的好處是避免 gradient diminishing 的問題。

使用了包括图像平移(image translations),水平翻转(horizontal reflections),和图像块提取(patch extractions)等数据增强技术。

使用了dropout层来避免对训练数据的过度拟合。

使用了特定的动量(momentum)和权重衰减(weight decay)参数的批量随机梯度下降(batch stochastic gradient descent)算法来训练模型。

在两块GTX580GPU上训练了5到6天。 

意义总结: 

这个由Krizhevsky,Sutskever和Hinton在2012年共同提出的神经网络开始了CNN在计算机视觉领域的盛宴,因为这是有史以来第一个在ImageNet数据集上表现如此优异的模型。里面用到的一些技术,如数据增强和丢弃(data augmentation and dropout),至今仍然在被广泛使用。这篇论文详述了CNN的优势,而它在比赛上的惊艳表现也让它名留青史。 


ZF Net (2013) 

https://arxiv.org/pdf/1311.2901v3.pdf

AlexNet在2012年大放异彩之后,2013年的ImageNet大赛上出现了大量的CNN模型,而桂冠则由纽约大学的Matthew Zeiler和Rob Fergus摘得,折桂模型就是ZF Net。这一模型达到了11.2%的错误率。它好比是之前AlexNet的更精确调试版本,但它提出了如何提高准确率的关键点。此外,它花了大量篇幅解释了ConvNets背后的原理,并且正确地可视化了神经网络的过滤器(filter)和权重(weights)。

作者Zeiler 和 Fergus在这篇题为“Visualizing and Understanding Convolutional Neural Networks”的论文开篇就点出了CNN能够再次兴起的原因:规模越来越大的图片训练集,和GPU带来的越来越高的计算能力。同时他们也点出,很多研究者其实对于这些CNN模型的内在原理认知并不深刻,使得“模型优化过程只不过是不断试错”,而这一现象在3年后的今天依旧广泛存在于研究者当中,尽管我们对CNN有了更加深入的理解!论文的主要贡献在于细述了对AlexNet的优化细节,及以用一种巧妙的方式可视化了特征图。

ZF Net 结构

 

划重点: 

除了一些细小的修改,ZF Net模型与AlexNet构架很相似。

AlexNet使用了1500万张图片做训练,ZF Net只用了130万张。

AlexNet在第一层用了11×11的过滤器,而ZF Net用了减小了步长(stride)的7×7过滤器,原因是第一层卷积层用小一点的过滤器可以保留更多原始数据的像素信息。用11×11的过滤器会跳过很多有用信息,尤其在第一个卷积层。

过滤器的数目随着神经网络的增长而增多。

激活函数用了ReLu,误差函数用了交叉熵损失(cross-entropy loss),训练使用批量随机梯度下降方法。

在一块GTX580 GPU上训练了12天。

开发了一种被称为Deconvolutional Network(解卷积网络)的可视化技术,能够检测不同特征激活与对应输入空间(input space)间的关系。之所以称之为“解卷积网”(deconvnet),是因为它能把特征映射回对应的像素点上(正好和卷积层做的事相反)。

这个网络结构背后的思路就是,在训练CNN的每一层都加上一个“deconvnet”,从而可以回溯到对应的像素点。一般的前向传播过程是给CNN输入一个图片,然后每一层计算出一个激活函数值。假设现在我们想检测第四层某一特征的激活,我们就把除了此特征之外的其他特征激活值设为0,再把此时的特征映射矩阵输入给deconvnet。Deconvnet与原始的CNN有相同的过滤器结构,再针对前三层网络进行一系列反池化过程(unpool,maxpooling的逆过程)、激活(rectify)和过滤操作,直到回到最初的输入空间。

这样做是为了找出图像中刺激生成某一特征的结构所在。我们来看看对第一层和第二层的可视化结果:

前两层的可视化结果:每一层演示了两个结果,第一个是过滤器的可视化,另外一个是图片中被过滤器激活最明显的部分。例如图上Layer2部分展示了16个过滤器

 

我们在CNN入门手册(上)里说过,ConvNet的第一层永远做的是一些最基础的特征检测,像是简单的边缘和这里的颜色特征。我们能看到,到了第二层,更多的环形结构特征就显示出来了。再看看第三、四、五层:

3,4,5层的可视化结果

 

这些层已经能显示出更高级特征,像是小狗和花朵。别忘了在第一层卷积之后,我们一般会有一个池化层减少图片的采样(例如从32x32x3减少到16x16x3)。这样做能让第二层对原始图片有一个更广阔的视角。如欲获取更多关于deconvnet和这篇论文的信息,可以看看Zeiler他本人这篇演讲(https://www.youtube.com/watch?v=ghEmQSxT6tw)。

 

意义总结: 

ZF Net的意义不仅在于夺得了2013年比赛的第一名,更重要的是它直观展示了CNN如何工作,并且提出了更多提高CNN表现的方法。这些可视化技术不仅描述了CNN内部的工作原理,同时对神经网络结构的优化提供了深刻的见解。精妙的deconv可视化展示和里面提到的闭塞实验(occlusion experiments)都让其成为我最喜欢的论文之一。

 

VGG Net (2014)  

https://arxiv.org/pdf/1409.1556v6.pdf 

简单、深度,就是这个2014年模型的理念。它由牛津大学的Karen Simonyan 和Andrew Zisserman提出,在ImageNet大赛中达到了7.3%的错误率(不过不是当年的第一)。它是一个19层的CNN模型,每一层都由步长和填充(pad)为1的3×3 过滤器,和步长为2的2×2 maxpooling层组成,是不是很简单呢?

 

划重点: 与AlexNet的11×11过滤器和ZF Net7x7的过滤器非常不同,作者解释了为什么选择用只有3×3的结构:两个3×3的卷积与一个5×5的卷积效果相同,这样我们可以用很小的过滤器模拟出较大的过滤器的效果。一个直接的好处就是减少了参数数量,同时用两个卷积层就能用到两次ReLu

 

3个卷积层与一个7×7的过滤器感受效果相同

 

输入空间随着层数增多而减少(因为卷积和池化),但随着卷积和过滤器的增多,卷积深度不断加深

 

值得注意的是每次maxpool之后过滤器的数量都翻倍了。这也再次实践了减少空间维度但加深网络深度的理念

 

在图像分类和定位上都表现很好,作者将定位用在了回归中(论文第10页)

 

使用Caffe建立模型

 

利用抖动(scale jittering)作为训练时数据增强的手段

 

每个卷积层后都有一个ReLu,用批量梯度下降来训练

 

在4个英伟达Titan Black GPU上训练了2到3周

 

意义总结:

我认为VGG Net算得上最具影响力的论作之一,因为它强调了卷积神经网络需要用到深度网络结构才能把图像数据的层次表达出来。

 

GoogLeNet (2015) 

http://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Szegedy_Going_Deeper_With_2015_CVPR_paper.pdf

 

还记得我们刚刚谈到的网络架构简单化的想法吗?谷歌就把这个想法引入了他们的Inception模块。 GoogLeNet是一个22层CNN,它凭着6.7%的错误率杀入五强并成为2014年ImageNet大赛的优胜者。 据我所知,这是第一个真正不同于原有的简单叠加顺序卷基层和池化层的构建方法的CNN架构之一。 这篇文章的作者还强调这个新模式在内存和功耗方面进行了重要的改进。(这是一个重要注意事项:堆叠层次并添加大量的过滤器都将产生巨大的计算和内存开销,也更容易出现过度拟合。)

GoogLeNet架构示意图之一

(原图为动图,请戳原文链接浏览)

 

GoogLeNet架构示意图之二

 

Inception模块

 

让我们来看一下GoogLeNet的结构。我们首先要注意到一点:并不是所有的过程都按顺序发生。如上图所示,GoogLeNet里有并行进行的网络结构。 

绿框内显示GoogLeNet的并行区

上图绿框里的结构叫做Inception模块。下面让我们来仔细看一下它的构成。

展开的Inception模块

上图中,底部的绿色框是我们的输入,而顶部的绿色框是模型的输出。(将这张图片右转90度, 与前一张GoogLeNet全景图联系起来一起看,可以看出完整网络的模型。 )基本上,在传统ConvNet的每一层里,你都必须选择是否进行池化操作或卷积操作,以及过滤器的尺寸。 一个Inception模块允许你并行执行所有这些操作。 事实上这正是作者最初提出的一个“天真”的想法。

关于Inception模块的一个“天真”的想法

 

那么,为什么这样的设计并不可行?因为这将导致太多太多的输出,使得我们最终因大输出量而停留在一个非常深的信道(channel)。 为了解决这个问题,作者在3×3和5×5层之前添加1×1卷积操作。 1×1卷积,又叫作网络层中的网络,提供了一种降低维数的方法。 例如,假设您的输入量为100x100x60(不一定是图像的尺寸,只是网络中任意一层的输入尺寸), 应用1×1卷积的20个过滤器可以将输入降低到100x100x20,从而3×3和5×5卷积的处理量不会太大。 这可以被认为是一种“特征池化”(pooling of features),因为我们减小了输入量的深度,这类似于我们通过标准的max-pooling层降低高度和宽度的维度。 另一个值得注意的点是,这些1×1卷积层之后的ReLU单元 的功能, 不会因降维而受到的损害。(参见Aaditya Prakash的博客http://iamaaditya.github.io/2016/03/one-by-one-convolution/以了解更多1×1卷积的效果等信息。) 你可以在这个视频中看到最终过滤器级联的可视化https://www.youtube.com/watch?v=_XF7N6rp9Jw。 )

你可能有这样的疑问“这个架构为什么好用”?这个架构提供了一个模块,它包括一个网络层的网络、一个中等大小的卷积过滤器 、一个大型的卷积过滤器和一个池化操作。 网络层的网络的卷积能够提取关于输入的非常细节的信息,而5×5滤镜能够覆盖输入的较大接收场,因此也能够提取其信息。 

你还可以用一个池化操作来减少占用空间大小,并防止过度拟合。 此外,每个卷积层之后都进行ReLUs,这有助于提高网络的非线性。大体来讲,这样的网络能够做到执行各类操作的同时仍保持计算性。 这篇论文还提出了一个高层次的推理,涉及稀疏性(sparsity)和密集连接(dense connections)等主题。(参见论文

http://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Szegedy_Going_Deeper_With_2015_CVPR_paper.pdf的第3段和第4段。我并不很理解这方面的内容,但很乐意听到你们在评论中发表自己的见解。) 

 

划重点: 

在整个架构中使用了9个inception模块,共有100多层。现在看来很有深度…

 

不使用完全连接的层 !他们使用平均池化代替,将7x7x1024的输入量转换为1x1x1024的输入量。 这节省了大量的参数。

 

使用比AlexNet少12倍的参数。

 

在测试期间,创建了同一图像的多个版本,输入到神经网络中,并且用softmax概率的平均值给出最终解决方案。

 

在检测模型中使用了R-CNN的概念(随后的一篇论文中将讨论) 。

 

Inception模块现在有多个更新版本(版本6和7)。

 

在一周内使用“几个高端GPU”训练模型。

 

意义总结:

GoogLeNet是率先引入“CNN层并不需要按顺序堆叠”这一概念的模型之一。随着 Inception模块的提出, 作者认为有创意的层次结构可以提高性能和计算效率。 这篇文章为接下来几年出现的一些惊人架构奠定了基础。

 

微软的ResNet(2015)

 

https://arxiv.org/pdf/1512.03385v1.pdf

让我们来想像一个深度CNN架构,对其层数加倍,然后再增加几层。但是这个CNN结构可能还是比不上微软亚洲研究院2015年底提出的ResNet体系结构那么深。ResNet是一个新的152层网络架构,它以一个令人难以置信的架构在分类、检测和定位等方面创造了新的记录。 除了层次上的新纪录之外,ResNet还以令人难以置信的3.6%的错误率赢得了2015年ImageNet大赛的桂冠。其他参赛者的错误率一般徘徊在5-10%左右。(参见Andrej Karpathy的博客http://karpathy.github.io/2014/09/02/what-i-learned-from-competing-against-a-convnet-on-imagenet/,这篇文章说明了其在ImageNet挑战赛与竞争对手ConvNets竞争的经历。)

上图ResNet架构示意图

Residual Block(残差块)

 

Residual Block背后的想法是把输入x通过conv-relu-conv序列输出。 这将先输出一些F(x),再把这些结果添加到原始输入x 即H(x)= F(x) + x。 而在传统的CNN中,H(x)直接等于F(x)。 所以,在ResNet中,我们不仅计算这个简单转换(从x到F(x)),还将F(x)加到输入x中。 在下图所示的迷你模块计算中,原输入x被做轻微变化,从而得到一个轻微改变的输出 。(当我们考虑传统CNN时,从x到F(x)是一个全新的表达,不保留关于原始x的任何信息。 作者认为“优化残差映射比优化原始映射更容易。”)

残差块的结构

Residual Block之所以有效的另一个原因是,在反向传播过程中,由于我们对加法运算分布了梯度,梯度能够很容易地流过图。

划重点: 

“超级深” – Yann LeCun。

 

152层…

 

有趣的是,在仅仅前两层之后,空间大小从224×224的输入量压缩到56×56。

 

作者声称,在普通网络中简单增加层次,将导致较高的训练和测试误差(论文里的图1)

 

该小组尝试了一个1202层网络,但可能由于过拟合而导致测试精度较低。

 

在8个GPU机器上训练了两到三周 。

 

意义总结: 

3.6%的错误率就足以说服你。 ResNet模型是我们目前拥有的最好的CNN架构,是残差学习思想的一大创新。 自2012年以来,ResNet模型的错误率逐年下降。我相信我们已经到了“堆叠很多层的神经网络也不能带来性能提升”的阶段了 。接下来肯定会有创新的新架构,就像过去2年一些新尝试的出现。 

推荐阅读:

ResNets内的ResNets

http://arxiv.org/pdf/1608.02908.pdf。

 

基于区域的CNN 

R-CNN – 2013:https://translate.googleusercontent.com/translate_c?act=url&depth=1&hl=en&ie=UTF8&prev=_t&rurl=translate.google.com&sl=en&sp=nmt4&tl=zh-CN&u=https://arxiv.org/pdf/1311.2524v5.pdf&usg=ALkJrhgwBa8jBFOgSDEuZFOmg5CeahQVSw

 

Fast R-CNN – 2015:https://translate.googleusercontent.com/translate_c?act=url&depth=1&hl=en&ie=UTF8&prev=_t&rurl=translate.google.com&sl=en&sp=nmt4&tl=zh-CN&u=https://arxiv.org/pdf/1504.08083.pdf&usg=ALkJrhiMQQ68G9lTdUV9DYnKPAUOb6PDaA

 

Faster R-CNN – 2015:https://translate.googleusercontent.com/translate_c?act=url&depth=1&hl=en&ie=UTF8&prev=_t&rurl=translate.google.com&sl=en&sp=nmt4&tl=zh-CN&u=http://arxiv.org/pdf/1506.01497v3.pdf&usg=ALkJrhjfeXGyTfxi4AmeFDqjODJ4qoHN8w

 

有些人可能会认为,这篇首次描述R-CNN的论文比之前所有创新网络架构论文都更有影响力。 随着第一个R-CNN论文被引用超过1600次,Ross Girshick和他在加州大学伯克利分校领导的研究组,实现了计算机视觉领域中最有影响力的进步之一。 他们的标题清楚地表明:Fast R-CNN和Faster R-CNN能使模型更快和更适配的用于当前的目标检测任务。

R-CNN的目的是解决目标检测问题。 对给定图像,我们希望能为图像里的全部物体绘制边界框。 这个过程可以分为两个步骤:区域提取和分类。

作者指出,任何类别不可知的区域提取方法都能用于R-CNN。 其中选择性搜索(https://ivi.fnwi.uva.nl/isis/publications/2013/UijlingsIJCV2013/UijlingsIJCV2013.pdf)特别适用于R-CNN。选择性搜索先执行生成 2000个概率最高的含有物体的不同区域的函数。随后这些被建议的区域都被“扭曲”(warp)成为一个个合适尺寸大小的图像, 输入到一个训练好的CNN(文章里用的是AlexNet)。这个CNN将为每个区域提取出一个特征向量,然后这些向量被输入到一组针对每类物体分别进行训练的线性SVM,从而输出分类结果。 同时这些向量也被输入给训练好的边界框回归器,以获得最准确的坐标。

R-CNN流程图 

接下来R-CNN使用非最大值抑制(Non-maxima suppression)来处理明显重叠的物体边界框。

Fast R-CNN:

 

针对三个主要问题,他们对原始模型进行了改进。 模型训练被分成几个阶段(ConvNets到SVM到边界框回归),计算量极其大, 而且非常慢(RCNN在每个图像上花费 53秒)。 Fast R-CNN基本上通过共享不同方案之间的转换层计算、交换生成区域提案的顺序、和运行CNN,从而解决了速度问题。

在该模型中,图像首先进入ConvNet, 从ConvNet的最后一个特征图中获取用于区域提取的特征(更多详细信息请参阅该论文的2.1部分),最后还有完全连接层、回归、和分类开始。

Faster R-CNN可以用于解决R-CNN和Fast R-CNN所使用的复杂的训练管线 。 作者在最后一个卷积层之后插入了一个区域候选网络(region proposal network,RPN)。 该网络能够在仅看到最后的卷积特征图后就从中产生区域建议。 从那个阶段开始,与R-CNN相同的管道就被使用了(感兴趣区域池层 ROI pooling,全连接层FC, 分类classification,回归regression… )。

Faster R-CNN工作流程

 

意义总结:

 

能够确定特定对象在图像中是一回事,但是能够确定对象的确切位置是另外一回事,那是计算机知识的巨大跳跃。 Faster R-CNN已经成为今天对象检测程序的标准。

 

生成对抗网络(2014)

https://arxiv.org/pdf/1406.2661v1.pdf

 

据Yann LeCun介绍,这些网络可能是下一个大发展。 在谈论这篇文章之前,让我们谈谈一些对抗范例。 例如,让我们考虑一个在ImageNet数据上运行良好的CNN。 我们先来看一个示例图,再用一个干扰或一个微小的修改使预测误差最大化,预测的对象类别结果因此而改变,尽管扰动后的图像相比愿图像本身看起来相同。 从高层视野 来看,对抗组示例基本上是愚弄ConvNets的图像。

对抗的例子(《神经网络中的有趣属性》)肯定让很多研究人员感到惊讶,并迅速成为一个令人感兴趣的话题。现在我们来谈一谈生成对抗网络。让我们来看两个模型,一个生成模型和一个判别模型。鉴别模型的任务是,确定一个给定的图像是否看起来自然(来自数据集的图像)或者看起来像人造的。生成器的任务是创建图像,使鉴别器得到训练并产生正确的输出。这可以被认为是一个双玩家间的零和博弈(zero-sum)或最大最小策略(minimax)。

该论文中使用的类比是:生成模式就像“一批冒牌者试图生产和使用假货”,而判别模式就像“警察试图检测假冒货币”。生成器试图愚弄判别器,而判别器试图不被生成器愚弄。随着模型的训练,两种方法都得到了改进,直到“假冒品与正品无法区分”为止。

 

意义总结:

 

听起来很简单,但为什么我们关心这些网络呢?正如Yann LeCun 所说,判别器现在知道“数据的内部表示”是因为它已理解来自数据集的真实图像和人为创建的之间的差异。 因此,你可以把它作为一个特征提取器用于CNN中 。 此外,您可以创建一些非常酷的人造图像,而且这些图像在我看来是很自然的(The Eyescream Project) 。

 

Generating Image Descriptions (2014)

 

https://arxiv.org/pdf/1412.2306v2.pdf

当你把CNN与RNN组合起来时会发生什么?不好意思,您不会得到R-CNN的。但是你会得到一个非常棒的应用程序。 作者Andrej Karpathy(我个人最喜欢的作者之一)和Fei-Fei Li一起撰写了这篇论文, 研究了CNN和双向RNN (Recurrent Neural Networks)的组合,以生成不同图像区域的自然语言描述。大体来讲,该模型能够拍摄一张图像并输出如下图片:

这很不可思议!No ROI —> where is bounding box coming from?

我们来看看这与正常的CNN相比如何。 对于传统的CNN,训练数据中每个图像都有一个明确的标签。 本论文中所描述的模型,其每个训练实例都具有一个与各个图像相关联的句子或标题 。 这种类型的标签被称为弱标签,其中句子的成分指的是图像的(未知)部分。 使用这个训练数据,一个深度神经网络“推断出句子的各个部分和他们描述的区域之间的潜在对应”(引自论文)。 另一个神经网络将图像作为输入,并生成文本描述。 让我们来看看这两个组件:对准和生成。

 

对准模型(Alignment Model):

 

这一部分模型的目标是,对齐视觉数据和文本数据(图像及其句子描述)。 该模型通过接受图像和句子作为输入,输出它们的匹配 程度得分。

现在让我们考虑将这些图像表现出来。 第一步是将图像送到R-CNN中,以便检测各个物体。 该R-CNN已对ImageNet数据进行了训练。 最先的19个(加上原始图像)对象区域被嵌入到500维的空间中。 现在我们有20个不同的500维向量(在论文中由v表示)。 我们有了关于图像的信息,现在我们想要有关句子的信息。 我们通过使用双向循环神经网络,将单词嵌入到同一个多模态空间中。 从最高层次来说,这是用来说明给定句子中单词的上下文信息的。 由于关于图片和句子的信息都在相同的空间中,我们可以通过计算它们的内积来得到它们的相似度。

 

生成模型(Generation Model):

 

对准模型的主要目的是创建一个数据集,其中包括一组图像区域(由RCNN找到)和其相应的文本(由BRNN找到)。 现在,生成模型将从该数据集中学习,以生成给定图像的描述。 该模型接收图像并交给CNN处理。最软层(softmax layer)被忽略,因为完全连接层的输出将成为另一个RNN的输入。 RNN的功能是形成一个句子中不同单词的概率分布(RNN也需要像CNN那样训练)。

免责声明:这绝对是本章节中诘屈聱牙的论文之一,所以如果有任何更正或其他解释,我很乐意听到他们的意见。

意义总结:

 

组合使用这些看似不同的RNN和CNN模型来创建一个非常有用的应用程序,对我来说是个有趣的想法,这是结合计算机视觉和自然语言处理领域的一种方式。 这开启了处理跨领域的任务时实现计算机和模型更加智能化的新思路。

 

Spatial Transformer Networks (2015) 

https://arxiv.org/pdf/1506.02025.pdf

最后,让我们走近一篇最近发表的论文。这篇文章是由Google Deepmind的一个小组在一年多前撰写的,主要贡献是引入空间变换模块。这个模块的基本思想是, 以一种方式转换输入图像,使得随后的图层更容易进行分类。作者担心的是在将图像在输入特定的卷积层之前出现的更改,而不是对CNN主体架构本身进行的更改 。这个模块希望纠正的两件事情是,形状正则化(对象被倾斜或缩放)和空间注意力(引起对拥挤图像中的正确对象的关注)。对于传统的CNN,如果你想使模型对不同尺度和旋转的图像保持不变性,你需要大量的训练示例。让我们来详细了解这个变换模块是如何帮助解决这个问题。

传统CNN模型中,处理空间不变性的实体是最大分池层[1] ( maxpooling layer)[2] 。 这一层背后的直观原因是,一旦我们知道某一个特定的特征位于原始输入空间内(任何有高激活值的地方),它的确切位置就不像其他特征的相对位置那样重要了。 这种新的空间变换器是动态的,它将为每个输入图像产生不同的行为(不同的扭曲或变换)。 它不仅仅是一个传统的maxpool那么简单和预定义。 我们来看看这个变换模型的工作原理。 该模块包括:

 

 

一个本地化网络,应用参数有输入量和空间变换输出。 对于仿射变换(affine transformation),参数 θ可以是6维的。

 

创建一个采样网格。这是通过在本地化网络中用创建的仿射变换theta来扭曲常规网格的结果。

 

一个采样器,其目的是对输入特征图做扭曲变换。

 

该模块可以随时投入CNN,并且会帮助网络学习如何以在训练中以最小化成本函数的方式来转换特征图。

 

意义总结: 

这篇论文之所以引起了我的注意,主要是因为它告诉我们CNN的改进不一定要来自网络架构的巨大变化,我们不需要创建下一个ResNet或Inception模块。 这篇论文实现了对输入图像进行仿射变换的简单思想,使得模型对转换、缩放、旋转操作变得更加稳定。

我们的《CNN入门手册》系列就正式结束了,希望大家通过阅读这一系列对卷积神经网络有了较为深入的了解。如果你想学习到更多关于卷积神经网络的知识,我再次强烈推荐斯坦福CS 231n课程视频。

WaveNet 源碼

Reference: 谷歌WaveNet 源码详解

繁體版:https://zhuanlan.zhihu.com/p/24568596


WaveNet是谷歌deepmind最新推出基於深度學習的語音生成模型。該模型可以直接對原始語音數據進行建模,在 text-to-speech和語音生成任務中效果非常好(詳情請參見:谷歌WaveNet如何通過深度學習方法來生成聲音?)。本文將對WaveNet的tensorflow實現的源碼進行詳解(本文解析的源代碼為github上的ibab發佈的採用tensorflow實現的WaveNet,github鏈接:ibab發佈的wavenet源碼)。

本文的結構如下:一,wavenet結構介紹;二,源代碼詳解;三,總結

 

1,wavenet結構介紹

wavenet採用了擴大卷積和因果卷積的方法,讓感受野隨著網絡深度增加而成倍增加,可以對原始語音數據進行建模。(詳情請參見:谷歌WaveNet如何通過深度學習方法來生成聲音?

 

2,源碼詳解

2.1 概況

github下載下來的文件夾如圖所示:

其中,關鍵的文件為train.py,generate.py和wavenet文件夾。train.py為訓練代碼,generate.py為生成代碼。wavenet文件夾包括了所需的模型,語音讀取,以及其它功能類和方法。wavenet文件夾包含文件如圖所示:

2.2 train.py解析

讓我們正式開始wavenet之旅把。首先看看train.py。train.py包括了一系列參數,模型保存(save())/加載(load())方法以及main()方法。

2.2.1 一系列參數

BATCH_SIZE = 1 #batchsize
DATA_DIRECTORY = './VCTK-Corpus' #訓練數據路徑
LOGDIR_ROOT = './logdir'#log路徑
CHECKPOINT_EVERY = 50#每個多少輪check
NUM_STEPS = 4000#每一輪訓練步數
LEARNING_RATE = 0.02#學習速率
WAVENET_PARAMS = './wavenet_params.json'#模型參數
STARTED_DATESTRING = "{0:%Y-%m-%dT%H-%M-%S}".format(datetime.now())#初始生成種子
SAMPLE_SIZE = 100000#樣本大小
L2_REGULARIZATION_STRENGTH = 0#l2 正則強度
SILENCE_THRESHOLD = 0.3
EPSILON = 0.001
ADAM_OPTIMIZER = 'adam'#adam優化器
SGD_OPTIMIZER = 'sgd'#sgd優化器
SGD_MOMENTUM = 0.9#學習動量

2.2.2 模型保存/加載方法

該部分代碼很簡單,其中關鍵函數為tensorflow的保存和加載模型函數。

saver.save(sess, checkpoint_path, global_step=step)#保存模型參數
saver.restore(sess, ckpt.model_checkpoint_path)#加載模型

2.2.3 main方法

main()包含了訓練的主要內容:一,讀取wavenet模型參數;二,建立tensorflow的coordinator;三, 從VCTK 數據集生成input;四,建模wavenet模型;五,訓練並保存模型

def main():
   #獲取參數
   args = get_arguments()
   …
   #略過部分不重要的代碼

   #讀取wavenet模型參數
   with open(args.wavenet_params, ‘r’) as f:
      wavenet_params = json.load(f)

   # 建立coordinator.
   coord = tf.train.Coordinator()

   # 從VCTK 數據集生成input.
   with tf.name_scope(‘create_inputs’):
       # Allow silence trimming to be skipped by specifying a threshold near
       # zero.
       silence_threshold = args.silence_threshold if args.silence_threshold > \
                                                                                 EPSILON else None
      #此處採用了audio_reader.py中的AudioReader類,後面將對該類進行詳解
      reader = AudioReader(
             args.data_dir,
             coord,
             sample_rate=wavenet_params[‘sample_rate’],
             sample_size=args.sample_size,
             silence_threshold=args.silence_threshold)
        audio_batch = reader.dequeue(args.batch_size)#傳入batch數據

   #建立網絡,使用model.py的WaveNetModel類.後面將對該類進行詳解。
   net = WaveNetModel(
         batch_size=args.batch_size,
         dilations=wavenet_params[“dilations”],
         filter_width=wavenet_params[“filter_width”],
         residual_channels=wavenet_params[“residual_channels”],
         dilation_channels=wavenet_params[“dilation_channels”],
         skip_channels=wavenet_params[“skip_channels”],
         quantization_channels=wavenet_params[“quantization_channels”],
         use_biases=wavenet_params[“use_biases”],
         scalar_input=wavenet_params[“scalar_input”],
         initial_filter_width=wavenet_params[“initial_filter_width”])
   #是否使用L2正則
   if args.l2_regularization_strength == 0:
        args.l2_regularization_strength = None
   #計算loss
   loss = net.loss(audio_batch, args.l2_regularization_strength)
   #選擇使用sgd還是adam優化器
   if args.optimizer == ADAM_OPTIMIZER:
      optimizer = tf.train.AdamOptimizer(learning_rate=args.learning_rate)
   elif args.optimizer == SGD_OPTIMIZER:
      optimizer = tf.train.MomentumOptimizer(learning_rate=args.learning_rate,
                           momentum=args.sgd_momentum)
   else:
       # This shouldn’t happen, given the choices specified in argument
       # specification.
       raise RuntimeError(‘Invalid optimizer option.’)
   trainable = tf.trainable_variables()
   optim = optimizer.minimize(loss, var_list=trainable)#優化,最小化loss函數

   # 將日誌數據寫入 TensorBoard.
   writer = tf.train.SummaryWriter(logdir)
   writer.add_graph(tf.get_default_graph())
   run_metadata = tf.RunMetadata()
   summaries = tf.merge_all_summaries()

   #開始 session
   sess = tf.Session(config=tf.ConfigProto(log_device_placement=False))
   init = tf.initialize_all_variables()
   sess.run(init)

   # Saver for storing checkpoints of the model.
   #保存模型
   saver = tf.train.Saver(var_list=tf.trainable_variables())
 
   try:
      saved_global_step = load(saver, sess, restore_from)
       if is_overwritten_training or saved_global_step is None:
            # The first training step will be saved_global_step + 1,
            # therefore we put -1 here for new or overwritten trainings.
            saved_global_step = 1

   except:
         print(“Something went wrong while restoring checkpoint. “
                  “We will terminate training to avoid accidentally overwriting “
                  “the previous model.”)
         raise

   #此處採用了tensorflow的線程和隊列的方法
   threads = tf.train.start_queue_runners(sess=sess, coord=coord)
   reader.start_threads(sess)

    …
    …
   #此處略過部分代碼
   finally:
       if step > last_saved_step:
            save(saver, sess, logdir, step)
      coord.request_stop()
      coord.join(threads)

main方法中使用了audio_reader.py和model.py中的類,讓我們進一步探究。

2.3 audio_reader.py解析

audio_reader.py包含了四個方法(find_files(),load_generic_audio(),load_vctk_audio(),trim_silence())和一個類 AudioReader()。

四個方法中,需要關注一下的是trim_silence()方法。該方法是去除音頻數據開始和結尾的空白段。

#去除音頻數據開始和結尾的空白段。
def trim_silence(audio, threshold):
   '''Removes silence at the beginning and end of a sample.'''
   energy = librosa.feature.rmse(audio)#獲取音頻energy(能量)
   frames = np.nonzero(energy > threshold)#大於閾值
   indices = librosa.core.frames_to_samples(frames)[1]
 
   # Note: indices can be an empty array, if the whole audio was silence.
   return audio[indices[0]:indices[-1]] if indices.size else audio[0:0]

讓我們再看一下 AudioReader()類。該類包含四個方法,功能是將預處理好的音頻數據打包成tensorflow queue。

class AudioReader(object):
  '''Generic background audio reader that preprocesses audio files
  and enqueues them into a TensorFlow queue.'''

  def __init__(self,
             audio_dir,
             coord,
             sample_rate,
             sample_size=None,
             silence_threshold=None,
             queue_size=256):
     self.audio_dir = audio_dir#訓練文件路徑
     self.sample_rate = sample_rate#採樣率
     self.coord = coord
     self.sample_size = sample_size#樣本大小
     self.silence_threshold = silence_threshold#閾值,低於多少就為零         
     self.threads = []#線程
     self.sample_placeholder = tf.placeholder(dtype=tf.float32, shape=None)

     #隊列初始化 self.queue = tf.PaddingFIFOQueue(queue_size,
                                            ['float32'],      
                                            shapes=[(None, 1)])

     #隊列入棧
     self.enqueue = self.queue.enqueue([self.sample_placeholder])

  #隊列出站
  def dequeue(self, num_elements):
      output = self.queue.dequeue_many(num_elements)
      return output

  #主線程
  def thread_main(self, sess):
     buffer_ = np.array([])#緩衝
     stop = False
     # Go through the dataset multiple times
     while not stop:
       iterator = load_generic_audio(self.audio_dir, self.sample_rate)#加載音頻數據
       for audio, filename in iterator:
         if self.coord.should_stop():
           stop = True
           break
         if self.silence_threshold is not None:
           # Remove silence
           audio = trim_silence(audio[:, 0],self.silence_threshold)#去除音頻開始和結尾的空白
           if audio.size == 0:
             print("Warning: {} was ignored as it contains only "
                   "silence. Consider decreasing trim_silence "
                   "threshold, or adjust volume of the audio."
                   .format(filename))
     
         if self.sample_size:
           # Cut samples into fixed size pieces
           buffer_ = np.append(buffer_, audio)
           while len(buffer_) > self.sample_size:
             piece = np.reshape(buffer_[:self.sample_size], [-1, 1])
             sess.run(self.enqueue,
                feed_dict={self.sample_placeholder: piece})
             buffer_ = buffer_[self.sample_size:]
         else:
            sess.run(self.enqueue,
              feed_dict={self.sample_placeholder: audio})#將讀取的音頻數據壓入隊列

  #開始線程
  def start_threads(self, sess, n_threads=1):
     for _ in range(n_threads):
       thread = threading.Thread(target=self.thread_main, args=(sess,))
       thread.daemon = True # Thread will close when parent quits.
       thread.start()
       self.threads.append(thread)
     return self.threads

這部分有參考價值的是tensorflow的線程和隊列的使用(詳情請參見tensorflow官方中文文檔)。隊列的使用包括幾個步驟:一,建立隊列;二,初始化隊列;三,隊列的入棧和出棧

(此圖摘自tensorflow官方中文文檔

線程的使用步驟:一,建立Coordinator對象;二,建立線程;將線程加入Coordinator運行。

# 線程體:循環執行,直到`Coordinator`收到了停止請求。
# 如果某些條件為真,請求`Coordinator`去停止其他線程。
def MyLoop(coord):
  while not coord.should_stop():
    ...do something...
    if ...some condition...:
      coord.request_stop()

# 建立Coordinator
coord = Coordinator()

# Create 10 threads that run 'MyLoop()'
#建立線程
threads = [threading.Thread(target=MyLoop, args=(coord)) for i in xrange(10)]

# Start the threads and wait for all of them to stop.
#開始線程
for t in threads: t.start()
  coord.join(threads)
 
(以上代碼摘自tensorflow官方中文文檔


2.4 model.py解析

該部分是源碼最精華的部分,包括了建立網絡模型和語音生成器相關函數。因為內容較多,同時生成器相關函數和建立網絡模型相關函數大同小異,本文只詳解網絡模型建立相關函數。

model.py包含兩個函數一個類。其中,create_variable()和create_bias_variable()功能分別為創建/初始化權值和bias的函數,很簡單。

WaveNetModel類中關鍵函數有:創建變量函數_create_variables(),創建因果卷積函數_create_causal_layer(),創建擴大卷積函數_create_dilation_layer(),建立網絡模型函數_create_network()以及loss()函數。本文對一些函數進行了測試,以便觀察函數是如何實現相關功能的。下文代碼註釋中的’測試中’,是表示在單獨測試該函數功能時設定的值。

2.4.1 創建變量函數_create_variables()

該函數創建了模型建立所需的所有變量(因果/擴大卷積層以及後處理層所需的變量),並存為字典待使用。

2.4.2 因果卷積函數_create_causal_layer()

該函數功能是建立因果卷積。函數調用了ops.py 文件中的causal_conv()函數。讓我們看看causal_conv()函數到底幹了什麼事情。下文代碼註釋中的’測試中’,是表示在單獨測試該函數功能時設定的值。從測試可以看出來,該因果卷積的實現方法是採用將輸出偏移幾步來實現,具體採用的是tf.pad()方法來實現偏移。


def time_to_batch(value, dilation, name=None):
  with tf.name_scope('time_to_batch'): #測試中,傳入的value的shape為(1,9,1)    
  #dilation=4
  shape = tf.shape(value)
  #pad_elements計算為3
  pad_elements = dilation - 1 - (shape[1] + dilation - 1) % dilation
  #padded後的shape為(1,12,1)即在第二個維度後加3個零
  padded = tf.pad(value, [[0, 0], [0, pad_elements], [0, 0]])
  #reshape後的shape為(3,4,1)
  reshaped = tf.reshape(padded, [-1, dilation, shape[2]])
  #轉置後的shape為(4,3,1)
  transposed = tf.transpose(reshaped, perm=[1, 0, 2])
  #最後返回的shape為(4,3,1)
  return tf.reshape(transposed, [shape[0] * dilation, -1, shape[2]])


def batch_to_time(value, dilation, name=None):
  with tf.name_scope('batch_to_time'):
    shape = tf.shape(value)
    prepared = tf.reshape(value, [dilation, -1, shape[2]])
    transposed = tf.transpose(prepared, perm=[1, 0, 2])
    #最後返回的是前面time_to_batch的最初輸入數值的shape
    #測試中,shape為(1,9,1)
    return tf.reshape(transposed,
        [tf.div(shape[0], dilation), -1, shape[2]])


def causal_conv(value, filter_, dilation, name='causal_conv'):
  with tf.name_scope(name):
  # Pad beforehand to preserve causality.
  #測試中,filter_width=2
  filter_width = tf.shape(filter_)[0]
  #測試中,dilation設定為4
  #因此,padding為padding=[[0, 0], [4, 0], [0, 0]]
  padding = [[0, 0], [(filter_width - 1) * dilation, 0], [0, 0]]
  #測試中,value的shape為(1,5,1)
  #測試中,padding為在value的第二維度前面加4個零
  #padded的shape變為(1,9,1)
  padded = tf.pad(value, padding)
  if dilation > 1:
    #見time_to_batch函數測試
    #測試中,最後返回來的shape為(4,3,1)
    transformed = time_to_batch(padded, dilation)

    conv = tf.nn.conv1d(transformed, filter_, stride=1, padding='SAME')
    #最後返回最開始的shape形式,詳情見op測試
    restored = batch_to_time(conv, dilation)
  else:
    restored = tf.nn.conv1d(padded, filter_, stride=1, padding='SAME')
  # Remove excess elements at the end.
  result = tf.slice(restored,
               [0, 0, 0],
               [-1, tf.shape(value)[1], -1])
  #最後返回的結果形式和padding後的即padded數據shape一樣
  return result

2.4.3 創建擴大卷積_create_dilation_layer()

該函數實現擴大卷積層,同時在該層創建了residual 和skip connection,讓模型更快收斂。該層的網絡結構如註釋中所示。

def _create_dilation_layer(self, input_batch, layer_index, dilation):
  '''Creates a single causal dilated convolution layer.

  The layer contains a gated filter that connects to dense output
  and to a skip connection:

    |-> [gate] -| |-> 1x1 conv -> skip output
    | |-> (*) -|
  input -|-> [filter] -| |-> 1x1 conv -|
    | |-> (+) -> dense output
    |------------------------------------|

  Where `[gate]` and `[filter]` are causal convolutions with a
  non-linear activation at the output.
  '''
  variables = self.variables['dilated_stack'][layer_index]

  weights_filter = variables['filter']
  weights_gate = variables['gate']

  #filter卷積
  conv_filter = causal_conv(input_batch, weights_filter, dilation)
  #gate卷積
  conv_gate = causal_conv(input_batch, weights_gate, dilation)

  #是否使用bias
  if self.use_biases:
    filter_bias = variables['filter_bias']
    gate_bias = variables['gate_bias']
    conv_filter = tf.add(conv_filter, filter_bias)
    conv_gate = tf.add(conv_gate, gate_bias)

  #gate和filter共同輸出
  out = tf.tanh(conv_filter) * tf.sigmoid(conv_gate)

  # The 1x1 conv to produce the residual output
  #採用1×1卷積實現殘差輸出
  weights_dense = variables['dense']
  transformed = tf.nn.conv1d(
      out, weights_dense, stride=1, padding="SAME", name="dense")

  # The 1x1 conv to produce the skip output
  #採用1×1卷積實現skip輸出
  weights_skip = variables['skip']
  #skip output
  skip_contribution = tf.nn.conv1d(
    out, weights_skip, stride=1, padding="SAME", name="skip")

  if self.use_biases:
    dense_bias = variables['dense_bias']
    skip_bias = variables['skip_bias']
    transformed = transformed + dense_bias
    skip_contribution = skip_contribution + skip_bias

  layer = 'layer{}'.format(layer_index)
  #加入summary
  tf.histogram_summary(layer + '_filter', weights_filter)
  tf.histogram_summary(layer + '_gate', weights_gate)
  tf.histogram_summary(layer + '_dense', weights_dense)
  tf.histogram_summary(layer + '_skip', weights_skip)
  if self.use_biases:
    tf.histogram_summary(layer + '_biases_filter', filter_bias)
    tf.histogram_summary(layer + '_biases_gate', gate_bias)
    tf.histogram_summary(layer + '_biases_dense', dense_bias)
    tf.histogram_summary(layer + '_biases_skip', skip_bias)

  #返回skip output和(殘差+input)
  return skip_contribution, input_batch + transformed

 

2.4.4 建立網絡模型函數_create_network()

該函數採用前面的_create_dilation_layer()建立網絡模型。在因果卷積後面,加上了後續處理層(postprocessing layer)。後續處理層結構為:Perform (+) -> ReLU -> 1×1 conv -> ReLU -> 1×1 conv。

#建立模型
def _create_network(self, input_batch):
  '''Construct the WaveNet network.'''
  outputs = []
  current_layer = input_batch

  # Pre-process the input with a regular convolution
  if self.scalar_input:
    initial_channels = 1
  else:
    initial_channels = self.quantization_channels

  #初始層
  current_layer = self._create_causal_layer(current_layer)

  # Add all defined dilation layers.
  #建立dilated層,總共18層
  with tf.name_scope('dilated_stack'):
    for layer_index, dilation in enumerate(self.dilations):
      with tf.name_scope('layer{}'.format(layer_index)):
        output, current_layer = self._create_dilation_layer(
          current_layer, layer_index, dilation)
        outputs.append(output)

  #postprocess層
  with tf.name_scope('postprocessing'):
    # Perform (+) -> ReLU -> 1x1 conv -> ReLU -> 1x1 conv to
    # postprocess the output.
    #創建後續處理層變量
    w1 = self.variables['postprocessing']['postprocess1']
    w2 = self.variables['postprocessing']['postprocess2']
    if self.use_biases:
      b1 = self.variables['postprocessing']['postprocess1_bias']
      b2 = self.variables['postprocessing']['postprocess2_bias']

    tf.histogram_summary('postprocess1_weights', w1)
    tf.histogram_summary('postprocess2_weights', w2)
    if self.use_biases:
      tf.histogram_summary('postprocess1_biases', b1)
      tf.histogram_summary('postprocess2_biases', b2)
  
  # We skip connections from the outputs of each layer, adding them
  # all up here.
  # 將每一層的skip connection輸出累加
  total = sum(outputs)
  transformed1 = tf.nn.relu(total)
  conv1 = tf.nn.conv1d(transformed1, w1, stride=1, padding="SAME")
  if self.use_biases:
    conv1 = tf.add(conv1, b1)
  transformed2 = tf.nn.relu(conv1)
  conv2 = tf.nn.conv1d(transformed2, w2, stride=1, padding="SAME")
  if self.use_biases:
    conv2 = tf.add(conv2, b2)

return conv2

 

2.4.5 loss()函數

該函數首先將輸入語音數據進行\mu -law編碼(mu_law_encode())後再使用one-hot編碼。loss函數採用的是tf.nn.softmax_cross_entropy_with_logits()。

#損失函數
def loss(self,
     input_batch,
     l2_regularization_strength=None,
     name='wavenet'):
  ”’Creates a WaveNet network and returns the autoencoding loss.

  The variables are all scoped to the given name.
  ”’

  with tf.name_scope(name):

    #使用mu-law編碼
    input_batch = mu_law_encode(input_batch,
                                self.quantization_channels)

    #再使用one hot編碼
    encoded = self._one_hot(input_batch)
    #如果使用標量輸入,則轉換成標量
    if self.scalar_input:
      network_input = tf.reshape( tf.cast(input_batch, tf.float32), [self.batch_size, 1, 1])
    else:
      network_input = encoded

    #網絡預測輸出
    raw_output = self._create_network(network_input)

    with tf.name_scope(‘loss’):
      # Shift original input left by one sample, which means that
      # each output sample has to predict the next input sample.
      #向左偏移一位,即減去第一位,保證每次是預測下一個輸出。
      #測試中,encoded的shape為(1,9,1),比如[0,0,0,0,1~5]
      #shifted後的shape為(1,8,1),比如[0,0,0,1~5]
      shifted = tf.slice(encoded, [0, 1, 0], [1, tf.shape(encoded)[1] 1, 1])
     
      #加零後,shape重新變為(1,9,1),比如比如[0,0,0,1~5,0]
      shifted = tf.pad(shifted, [[0, 0], [0, 1], [0, 0]])

      #將模型預測轉換shape為prediction
      prediction = tf.reshape(raw_output, [1, self.quantization_channels])

      #loss函數
      loss = tf.nn.softmax_cross_entropy_with_logits(
           prediction,
           tf.reshape(shifted, [1, self.quantization_channels]))
      reduced_loss = tf.reduce_mean(loss)

      tf.scalar_summary(‘loss’, reduced_loss)

      if l2_regularization_strength is None:
        return reduced_loss
      else:
        # L2 regularization for all trainable parameters
        l2_loss = tf.add_n([tf.nn.l2_loss(v)
                                  for v in tf.trainable_variables()
                                  if not(‘bias’ in v.name)])

        # Add the regularization term to the loss
        total_loss = (reduced_loss + l2_regularization_strength * l2_loss)
        tf.scalar_summary(‘l2_loss’, l2_loss)
        tf.scalar_summary(‘total_loss’, total_loss)

         return total_loss


2.5 generate.py解析

這部分代碼用於模型語音生成。有了前面的解析,代碼就相對比較簡單,略過。github上還有Fast Wavenet,解決了wavenet原文中的語音生成方法的問題是語音生成太慢,有興趣可以參考。

3,總結

WaveNet結合了因果卷積和擴展卷積方法,讓感受野隨著模型深度增加而成倍增加。該神經網絡模型不僅適用於原始語音數據的生成,也適用於文字生成(tex-wavenet),圖像生成(image-wavenet)等,是值得深入研究的一個神經網絡模型。

 

深度學習的語音生成

Reference: https://zhuanlan.zhihu.com/p/24317897

Reference: wavenet

 

A generative model for raw Audio

简介:本文提出了一种深度神经网络模型,名叫Wavenet。该模型可以生成原始语音。文章主要内容有几点:一,文章中,通过该模型进行语音生成任务,结果很接近真人发出的声音。二,Wavenet还可以抓取不同说话者的特征,有高保真度。三,使用音乐文件来训练该模型,可以生成新的高保真度的音乐片段。四,还可以加入判别模型,使之完成语音识别任务。

先說幾個 key words: dilated, causal, convolutions.

Dilated 是擴大 input text (TTS) range? 

Causal 是因果律,適用在 voice, 但不適合在 image.  Voice 也可以有 prediction?  是因為 voice synthesis 不需要?

不需要 RNN?  是因為 voice synthesis 不需要記憶? Pro: training fast.

 

1,前言 

通过神经网络方法,由所有像素或者单词联合概率生成的条件分布在内容生成任务方面取得了最好的成绩。这些模型通过对大量的随机变量建模来完成任务。本文要探索的是能否对原始语音进行建模来生成高保真的语音。

 

文章对卷积神经网络的改良很好,模型的设计和应用很好,很值得参考。

本文提出了类似于PixelCNN 的语音生成模型:WaveNet。本文主要成果如下:
一,WaveNet可以生成类似真人发音的语音,比以前的模型都好;
二,为了在语音生成中处理长跨度时间依赖问题,我们设计了一个新的扩大连接的卷积框架;该卷积框架有非常大的感受野。
三,一个模型可以生成不同类型的声音。
四,该模型可以用于语音识别以及音乐生成。


2,WaveNet



类似于PixelCNN,条件分布概率是通过多层卷积层来建模的。网络中没有池化层。Why?  Synthesis 應該是 unpooling layer?

但已經用 dilated convolution, 所以不用 unpooling layer?



2.1 DILATED CAUSAL CONVOLUTIONS(扩大的卷积)
(动态图可以参考deepmind链接:WaveNet: A Generative Model for Raw Audio | DeepMind


我们在WaveNet中采用因果卷积(causal convolutions)保证p(x_{t_1+1}^{}|x_{1},...x_{t}   )不包含x_{t+1}, x_{t+2},... x_{T}中的信息。对于图像处理任务中,类似的是采用masked convolution。对于1-D数据,秩序将输出偏移几步就行。(详情参见原文和代码,原文链接:arxiv.org/pdf/1609.0349

因为模型只有因果卷积,而没有遞歸連接 (recurrent connection),模型训练速度快于RNN,特别对于很长的序列。对于因果卷积,存在的一个问题是需要很多层或者很大的filter来增加卷积的感受野。本文中,我们通过大小排列来的扩大卷积来增加感受野。扩大卷积(dilated convolution)是通过跳过部分输入来使filter可以应用于大于filter本身长度的区域。等同于通过增加零来从原始filter中生成更大的filter。示意图如下:


在示意图中,卷积感受野扩大了1,2,4,8倍。扩大卷积(dilated convolution)可以使模型在层数不大的情况下有非常大的感受野。

2.2 softmax distribution
因为原始音频是按照16-bit(one per timestep)的整数值序列储存的,每个timestep,softmax层需要输出65536个概率值。为了便于运算,我们应用了\mu -law companding transformation进行转换,将输出概率数目降低为256个。公式如下:f(x_{t} )=sign(x_{t} )\frac{ln(1+\mu \left| x_{t}  \right| )}{ln(1+\mu )} ,其中-1<x_{t}<1    and \mu =255


2.3 gated activation units
文章中采用了类似于PixelCNN中的gated activation 模块:
同时,发现在声音信号建模中,非线性比线性模型效果更好。



2.4 residual and skip connections

文章中使用了residual 和skip connection技术来使模型更快收敛,并且使梯度能传到到更深层模型。


2.5 Conditional WaveNet
接受额外的输入h,WaveNet可以对条件分布(p(x|h))进行建模。p(x|h)=\prod_{t=1}^{T} p(x_{t}|x_{1},...,x_{t-1},h   )
通过条件分布建模,我们可以指导WaveNet生成具有目标特点的音频。比如,在多人对话的条件下,我们可以采取一位说话人的声音作为额外的输入。再比如,在TTS(text to speech)任务中,text信息也是作为额外的输入。文章中有两种条件建模的方法: global conditioning(全局方法) and local conditioning(局部方法)。
全局方法是接受单额外输入h,该额外输入在所有的时间点上影响模型输出。z=tanh(W_{f,k}\ast X+V_{f,k}^{T}h  )\odot \sigma (W_{g,k}\ast X+V_{g,k}^{T}h )
其中,V_{\ast ,k} 为线性投影,V_{*,k}^{T}h 在所有时间节点上进行传播。
局部条件建模方法:我们有第二种时间序列h_{t} ,可以通过对原始数据的低采样率获得(比如TTS模型中的线性特征)。我们首先通过transposed convolutional network(上采样)(y=f(h))将时间序列转换成和语音序列一样分辨率的新的时间序列。然后将其用于激活单元:z=tanh(W_{f,k}\ast X+V_{f,k}^{T}y  )\odot \sigma (W_{g,k}\ast X+V_{g,k}^{T}y ),其中,V_{f,k}^{}*y是1*1卷积。如果采用transposed convolutional network,也可以直接使用V_{f,k}^{}*h
,然后每隔一段时间重复。但是,在我们的试验中,我们发现这样效果没有前一种方法好。

 

2.6 context stacks 

我们提出了多种增加wavenet感受野的方法:一,提高扩大卷积的数目;二,增加层数;三,采用更大的filter;四,更大的扩大因子。

(这里英文原文不好翻译,但是是实际操作中很关键的地方。)

A complementary approach is to use a separate, smaller context stack that processes a long part of the audio signal and locally conditions a larger WaveNet that processes only a smaller part of the audio signal (cropped at the end). One can use multiple context stacks with varying lengths and numbers of hidden units. Stacks with larger receptive fields have fewer units per layer. Context stacks can also have pooling layers to run at a lower frequency. This keeps the computational requirements at a reasonable level and is consistent with the intuition that less capacity is required to model temporal correlations at longer timescales.

3,实验

本文中,我们在三个不同的任务上(多人语音生成(multi-speaker speech generation ),TTS(text to speech),音乐建模(music audio modelling)),对wavenet进行了评价。生成的样本见链接:deepmind.com/blog/waven.

3.1 multi-speaker speech generation(多人语音生成)

首先实验了自由语音生成(不基于文本)文中采用了VCTK数据集中的英文多人语料。数据集总共包含109为发言者的44小时语音数据。wavenet条件建模的条件是发言者的id(采用one hot 编码)。

模型不是基于文本,因此模型可以生成不存在的类似于人类发音的语音。这和生成模型生成图像一样,咋看很真实,仔细看存在很多不自然的地方。模型生成的语音缺乏长时间上的连续性,部分的原因是模型感受野所限(大约300毫秒),使模型只能记住2-3个音素。

单个wavenet模型可以基于任何一位编码的发言者进行条件建模。单个模型有足够的能力抓取所有109位发言者的特征。同时,我们观察到,相对于单个发言者数据,增加发言者可以于使验证集表现提高。这个现象提示:This suggests that WaveNet’s internal representation was shared among multiple speakers.

我们还发现,模型还能抓取除语音之外的特征,比如录音质量以及呼吸和嘴动的声音。

3.2 text to speech

第二个任务是TTS。我们采用了Google’s North American English and Mandarin Chinese TTS systems的语音数据集。前者包含 24.6小时的语音数据,后者包含34.8小时的语音数据。两种数据的发言者都是女性。

TTS任务中,wavenet条件建模的条件是输入文本的语言学特征。同时,我们也实验了同时基于logF_{0} 值和文本语言学特征进行建模。对每一种语言,我们也训练外部模型基于语言学特征预测logF_{0} 值和语音饱和度。WaveNets的感受野为240毫秒。同时,我们还建立了example based and model based speech synthesis baselines, hidden Markov model (HMM)-driven unit selection concatenative and long short-term memory recurrent neural network (LSTM-RNN)-based statistical parametric语音合成模型。使用同样的数据集以及语言学特征来训练这些模型,比较它们和wavenet的性能。

subjective paired comparison tests和mean opinion score (MOS) tests用于评价wavenet性能。(即让人员听生成的语音,再评分)结果如图 


3.3 音乐建模

我们采用了两种音乐数据库:一,the Magna TagATune dataset,包含200小时的音乐数据,每29秒添加了标签(总共188种标签)来表示类型,乐器,音量和情绪。二,the YouTube piano dataset。包含60小时的钢琴独奏。

音乐建模难以量化的评价,可以定性的评价。我们发现,增加感受野对音乐建模非常必要。即使感受野增加到几秒钟,模型也难以长时间段保持连续。结果是每一秒钟的音乐类型,乐器,音量,情绪以及音质都有差异。不过,即使是非条件建模生成的音乐听起来还是不错的,而且经常是和声。

我们希望可以采用条件建模,让模型生成特定标签(类型,乐器,情绪等)的声音。我们采用the Magna TagATune dataset数据集来训练该模型。数据集附带的标签有很多噪声和遗漏。 我们对标签进行了清洗(对标签合并类似标签,去除过少的标签)。模型训练后生成的音乐还是不错的。

3.4 语音识别

wavenet属于生成模型结构,但是也可以直接修改为判别模型来完成判别任务:语音识别。

传统语音识别着重于依赖 梅尔频率倒谱系数(MFCCs)。近期的研究开始转向直接对原始语音数据进行建模。

我们采用了TIMIT语音数据集来进行语音识别实验。我们在dilated convolution层后面增加了mean-pooling层,让输出聚集成10毫秒的带宽(160\times downsampling)。pooling层后面接了几层非因果卷积层。我们采用两类loss来训练wavenet。一类loss为预测下一个样本;另外一类loss为对该frame进行分类。 两种loss同时训练的结果好于只采用一种loss的结果,结果我们在测试集上获得了直接从原始语音数据建模的最好结果:18:8 PER。

4 总结

本文作者提出了wavenet模型,该模型可以直接对原始语音数据进行建模。wavenet结合了因果卷积和扩展卷积,让感受野随着模型深度增加而成倍增加。感受野的增加对原始语音建模中的长时间依赖非常重要。




 

深度學習各式卷積介紹

Reference: 

View story at Medium.com

https://po.baidu.com/feed/share?isBdboxShare=1&context=%7B%22nid%22%3A%22news_2849076689852113814%22%2C%22sourceFrom%22%3A%22bjh%22%7D

 

卷積神經網絡作为深度學習的典型網路,在圖像處理和计算機视覺等多个领域都取得了很好的效果。

Paul-Louis 在Medium上通过这篇文章快速地介绍了不同类型的卷積结構(Convolution)及优勢。为了简单起见,本文僅探讨二维卷積结構。
 

Convolution

首先定義卷積层的参数。
 
 卷積核为3、步幅为1和带有边界扩充的二维卷積结構
卷積核大小(Kernel Size):定義了卷積操作的感受域。在二维卷積中,通常设置为3,即卷積核大小为3×3。我認為應該要和 image spatial frequency 有關。
步幅(Stride):定義了卷積核遍历圖像时的步幅大小。其默认值通常设置为1,也可将步幅设置为2 后对圖像进行下采样,这种方式与最大池化类似。
边界扩充(Padding):定義了網路层處理样本边界的方式。当卷積核大于1且不进行边界扩充,输出尺寸将相应缩小;当卷積核以标准方式进行边界扩充,则输出数据的空间尺寸将与输入相等。
输入与输出通道(Channels):構建卷積层时需定義输入通道 I,并由此确定输出通道O。这样,可算出每个網路层的参数量为 I×O×K,其中K为卷積核的参数个数。例,某个網路层有64个大小为3×3的卷積核,则对应K值为 3×3 =9。
此處輸入和輸出通道和 image size 完全無關。而是輸入和輸出特徵 filter 的數目。 
 

Delated Convolution

空洞卷積(atrous convolutions)又名扩张卷積(dilated convolutions),向卷積层引入了一个称为 “扩张率(dilation rate)”的新参数,该参数定義了卷積核處理数据时各值的间距。
 
 卷積核为3、扩张率为2和无边界扩充的二维空洞卷積
一个扩张率为2的3×3卷積核,感受与5×5的卷積核相同,而且僅需要9个参数。你可以把它想象成一个5×5的卷積核,每隔一行或一列删除一行或一列。
在相同的计算条件下,空洞卷積提供了更大的感受域。空洞卷積经常用在实时圖像分割中。当網路层需要较大的感受,但计算资源有限而无法提高卷積核数量或大小时,可以考虑空洞卷積。
空洞卷積是補零嗎? 補零效果應該不好。似乎該用 near_neighbor 填補。 
 

Transposed Convolution

轉置卷積(transposed Convolutions)又名反卷積(deconvolution)或是分数步长卷積(fractially straced convolutions)。
反卷積(deconvolutions)这种叫法是不合适的,因为它不符合反卷積的概念。在深度學習中,反卷積确实存在,但是并不常用。实际上,反卷積是卷積操作的逆过程。你可以这么理解这个过程,将某个圖像输入到单个卷積层,取卷積层的输出传递到一个黑盒子中,这个黑盒子输出了原始圖像。那么可以说,这个黑盒子完成了一个反卷積操作,也就是卷積操作的数学逆过程。主要是用在 generative model (e.g. Autoencoder, GAN).
 
轉置卷積与真正的反卷積有点相似,因为两者产生了相同的空间分辨率。然而,这两种卷積对输入数据执行的实际数学运算是不同的。轉置卷積层只执行了常规的卷積操作,但是恢复了其空间分辨率。
 
 卷積核为3、步幅为2和无边界扩充的二维卷積结構
举个例子,假如将一张5×5大小的圖像输入到卷積层,其中步幅为2,卷積核为3×3,无边界扩充。则卷積层会输出2×2的圖像。
若要实现其逆过程,需要相应的数学逆运算,能根据每个输入像素来生成对应的9个值。然后,将步幅设为2,遍历输出圖像,这就是反卷積操作。
 
 卷積核为3×3、步幅为2和无边界扩充的二维轉置卷積
轉置卷積和反卷積的唯一共同点在于两者输出都为5×5大小的圖像,不过轉置卷積执行的仍是常规的卷積操作。为了实现扩充目的,需要对输入以某种方式进行填充。
你可以理解成,至少在数值方面上,轉置卷積不能实现卷積操作的逆过程。
轉置卷積只是为了重建先前的空间分辨率,执行了卷積操作。这不是卷積的数学逆过程,但是用于编码器-解码器结構中,效果仍然很好。这样,轉置卷積可以同时实现圖像的粗粒化和卷積操作,而不是通过两个单独过程来完成。
 

Separable Convolution (2D convolution is separated into 2 1D convolution)

在可分离卷積(separable convolution)中,可将卷積核操作拆分成多个步骤。卷積操作用y=conv(x, k)来表示,其中输出圖像为y,输入圖像为x,卷積核为k。接着,假设k可以由下式计算得出:k=k1.dot(k2)。这就实现了一个可分离卷積操作,因为不用k执行二维卷積操作,而是通过k1和k2分别实现两次一维卷積来取得相同效果。

 X、Y方向上的 Sobel 滤波器
Sobel算子通常被用于圖像處理中,这里以它为例。你可以分别乘以向量[1,0,-1]和[1,2,1]的轉置向量后得到相同的滤波器。完成这个操作,只需要6个参数,而不是二维卷積中的9个参数。
这个例子说明了什么叫做空间可分离卷積,这种方法并不应用在深度學習中,只是用来帮你理解这种结構。
在神經網絡中,我们通常会使用深度可分离卷積结構(depthwise separable convolution)。
这种方法在保持通道分离的前提下,接上一个深度卷積结構,即可实现空间卷積。
 
接下来通过一个例子让大家更好地理解。
假设有一个3×3大小的卷積层,其输入通道为16、输出通道为32。具体为,32个3×3大小的卷積核会遍历16个通道中的每个数据,从而产生16×32=512个特征圖谱。进而通过叠加每个输入通道对应的特征圖谱后融合得到1个特征圖谱。最后可得到所需的32个输出通道。
针对这个例子应用深度可分离卷積,用1个3×3大小的卷積核遍历16通道的数据,得到了16个特征圖谱。在融合操作之前,接着用32个1×1大小的卷積核遍历这16个特征圖谱,进行相加融合。这个过程使用了16×3×3+16×32×1×1=656个参数,远少于上面的16×32×3×3=4608个参数。
这个例子就是深度可分离卷積的具体操作,其中上面的深度乘数(depth multiplier)设为1,这也是目前这类網路层的通用参数。
这么做是为了对空间信息和深度信息进行去耦。从Xception模型的效果可以看出,这种方法是比较有效的。由于能够有效利用参数,因此深度可分离卷積也可以用于移动设备中。

MNIST Transfer Learning in Keras

Reference: https://github.com/fchollet/keras/tree/master/examples

 

Transfer learning 的好處是不用 learning from scratch!

(1) 需要大量的 labelled data;  (2) 需要大量的 training resource (GPU and time)!

 

其實 transfer learning 是蠻神奇的一件事!

試想一般 feature base 的分類器。如果 input data/image 改變,原來的分類方式可能要重新訓練。

但是 deep learning train 的 feature 似乎更普適。可以只要微調就可以達到很好的效果!

注意所謂微調不是 perturbation of neural network weights!  而只是調整最後的分類器。

 

基本上的想法就是拿已經 train 好的 network,做一些微調就可以用來處理新的問題。

如何作微調?

Deep learning network 大多是:  Deep neural network + classifier (softmax or SVM).

一般只需少量的 labelled data for transfer learning, 同時只需要重 train 分類器即可。

因此可以避免上述 (1) and (2) 的問題。

 

Reference 中 example 恰好有 transfer learning on MNIST.  

1- Train a simple convnet on the MNIST dataset the first 5 digits [0..4].                                          
2- Freeze convolutional layers and fine-tune dense layers                                                          
   for the classification of digits [5..9].     

1- Train a simple convnet on the MNIST dataset the first 5 digits [0..4].

2- Freeze convolutional layers and fine-tune dense layers for the classification of digits [5..9].  

Keras Backend – TensorFlow or Theano

Keras 可以 support 幾個 backend – TensorFlow, Theano, CNTK

Input data format: channel_first, or channel_last

 

這些可以在 ~/.keras/keras.json 設定。

注意先 install c++ 當使用 theano.

> sudo apt-get install c++

> conda install -c anaconda theano

Sparse Autoencoder in Keras

Reference: https://blog.keras.io/building-autoencoders-in-keras.html

 

在 reference 只有一段話。沒有源代碼。

 

Adding a sparsity constraint on the encoded representations

In the previous example, the representations were only constrained by the size of the hidden layer (32). In such a situation, what typically happens is that the hidden layer is learning an approximation of PCA (principal component analysis). But another way to constrain the representations to be compact is to add a sparsity contraint on the activity of the hidden representations, so fewer units would “fire” at a given time. In Keras, this can be done by adding an activity_regularizer to our Dense layer:

from keras import regularizers

encoding_dim = 32

input_img = Input(shape=(784,))
# add a Dense layer with a L1 activity regularizer
encoded = Dense(encoding_dim, activation='relu',
                activity_regularizer=regularizers.l1(10e-5))(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)

autoencoder = Model(input_img, decoded)

Let’s train this model for 100 epochs (with the added regularization the model is less likely to overfit and can be trained longer). The models ends with a train loss of 0.11 and test loss of 0.10. The difference between the two is mostly due to the regularization term being added to the loss during training (worth about 0.01).

Here’s a visualization of our new results:

sparse autoencoder

They look pretty similar to the previous model, the only significant difference being the sparsity of the encoded representations. encoded_imgs.mean() yields a value 3.33 (over our 10,000 test images), whereas with the previous model the same quantity was 7.30. So our new model yields encoded representations that are twice sparser.

 

我把 simplest autoencoder Keras code template 改成 sparse autoencoder 如下:

Adding a Sparsity Constraint on the Encoder

from keras.layers import Input, Dense
from keras.models import Model

from keras import regularizers

encoding_dim = 32

input_img = Input(shape=(784,))
# add a Dense layer with a L1 activity regularizer
encoded = Dense(encoding_dim, activation='relu',
                activity_regularizer=regularizers.l1(10e-9))(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)

autoencoder = Model(input_img, decoded)
Using TensorFlow backend.
# this model maps an input to its encoded representation
encoder = Model(input_img, encoded)
# create a placeholder for an encoded (32-dimensional) input
encoded_input = Input(shape=(encoding_dim,))
# retrieve the last layer of the autoencoder model
decoder_layer = autoencoder.layers[-1]
# create the decoder model
decoder = Model(encoded_input, decoder_layer(encoded_input))
#autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
from keras.datasets import mnist
import numpy as np
(x_train, _), (x_test, _) = mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))
print x_train.shape
print x_test.shape
(60000, 784)
(10000, 784)
autoencoder.fit(x_train, x_train,
                epochs=50,
                batch_size=256,
                shuffle=True,
                validation_data=(x_test, x_test))
Train on 60000 samples, validate on 10000 samples
Epoch 1/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0922
Epoch 2/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0922
Epoch 3/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0921
Epoch 4/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0922
Epoch 5/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0922
Epoch 6/50
60000/60000 [==============================] - 8s - loss: 0.0933 - val_loss: 0.0921
Epoch 7/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0922
Epoch 8/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 9/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 10/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 11/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0922
Epoch 12/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 13/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0920
Epoch 14/50
60000/60000 [==============================] - 8s - loss: 0.0932 - val_loss: 0.0921
Epoch 15/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0921
Epoch 16/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 17/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0921
Epoch 18/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 19/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 20/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 21/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 22/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 23/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 24/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 25/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 26/50
60000/60000 [==============================] - 8s - loss: 0.0931 - val_loss: 0.0920
Epoch 27/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0920
Epoch 28/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 29/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0920
Epoch 30/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0920
Epoch 31/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0920
Epoch 32/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 33/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 34/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 35/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 36/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 37/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 38/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 39/50
60000/60000 [==============================] - 8s - loss: 0.0930 - val_loss: 0.0919
Epoch 40/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 41/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 42/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 43/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 44/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 45/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0918
Epoch 46/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0918
Epoch 47/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 48/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0918
Epoch 49/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
Epoch 50/50
60000/60000 [==============================] - 8s - loss: 0.0929 - val_loss: 0.0919
<keras.callbacks.History at 0x7fcbd81c8450>
# encode and decode some digits
# note that we take them from the *test* set
encoded_imgs = encoder.predict(x_test)
decoded_imgs = decoder.predict(encoded_imgs)
# use Matplotlib (don't ask)
import matplotlib.pyplot as plt

n = 10  # how many digits we will display
plt.figure(figsize=(20, 4))
for i in range(n):
    # display original
    ax = plt.subplot(2, n, i + 1)
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)

    # display reconstruction
    ax = plt.subplot(2, n, i + 1 + n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()

如果修改這一個參數,結果就完全錯了。

encoded = Dense(encoding_dim, activation='relu',
                activity_regularizer=regularizers.l1(10e-5))(input_img)

   
10e-9 —> 10e-5  即使 epoches=100 仍然一樣。注意 optimizer 是用 adam 而非 adadelta

 10-9 —> 1e-6 OK

NewImage

 

Autoencoder in Keras

Reference: https://blog.keras.io/building-autoencoders-in-keras.html

前(數)文討論各類 autoencoder (fully connected autoencoder, sparse autoencoder, multi-layer fully connected autoencoder, convolutional autoencoder, denoise autoencoder, variational autoencoder).

剛好 reference 用 Keras 重新整理以上的 autoencoders.

  • a simple autoencoder based on a fully-connected layer
  • a sparse autoencoder
  • a deep fully-connected autoencoder
  • a deep convolutional autoencoder
  • an image denoising model
  • a sequence-to-sequence autoencoder
  • a variational autoencoder

What are autoencoders?

Autoencoder: schema

“Autoencoding” is a data compression algorithm where the compression and decompression functions are 1) data-specific, 2) lossy, and 3) learned automatically from examples rather than engineered by a human. Additionally, in almost all contexts where the term “autoencoder” is used, the compression and decompression functions are implemented with neural networks.

本文主要是從 compression 角度出發。先說結論:autoencoder 不適合做一般普適性 compression algorithm (like JPEG/MPEG etc.).  

1. Pretrain deep learning network

原先 autoencoder 似乎沒有太實際的用途。在 2012 (Hinton?) 發現 autoencoder 非常適合 deep convolutional neural network 的 greedy layer-wise pretraining!  

前文說到 deep neural network 如何避免陷入 local minimum.  Greedy layer-wise pretraining 似乎可以避免 local minimum. 

不過之後發現 random initial weights 似乎效果也不錯。因此後來也不用 autoencoder 做 pretraining.  2014 batch normalization; 2015 一般開始用 residual learning to train deep networks from scratch.

 

2. Dimension reduction for visualization

 t-SNE 算是目前最好的 2D visualization algorithm.  但是 t-SNE 一般需要低維的 input data.  一個好的策略是 autoencoder + t-SNE.

先用 autoencoder 把高維的 data 壓縮到低維 (e.g. 32 dimensional), 然後再用 t-SNE map 壓縮 data 到 2D plane.  

3. Denoise