Deep Learning从0到0.1

以下大部分是根据我自己的学习过程来的,可以算是一种人生经验,也有可能是只适合我的。有好多人问我怎么入门,我自己都没入,所以是不想公开,能不看就不要看了。(陈老师微博转了一下,班门弄斧羞愧难当。小小服务器微卡,请见谅。)

前期准备

  1. 个人认为Linux远远优于Windows(mac 不清楚,貌似也比Windows方便一点),大二之后我没有在Windows上写过跑过代码,不清楚常用的deep learning框架能否顺利在Windows上安装。但我知道在Linux上安装theano就一个命令,GPU cuda 设置也很方便。当然,这是一个安利,可以不吃。
  2. 可以上Google,会用英文搜索。最好少看中文资料,或者粗略看中文资料有个大概印象之后仔细看英文资料。
  3. 基本的优化知识,像梯度下降法,以及随机的梯度下降法(SGD)。另外还有一些加速方法,可以看这篇博文,形象科学。
  4. 基本的机器学习知识,最起码的logistic regression。这个非常重要,可以看作是最最基本的单层神经网络,相当于平房跟金茂的关系。理解里面的sigmoid也很重要,对以后理解为什么要用tanh,relu有帮助。这个最好能自己实现,matlab也行,了解原理就好了。可以使用libsvm的数据集测试分类效果。可以参考coursera上的机器学习不过他的作业貌似封装了优化算法,最好优化算法也自己写。这可能是最艰难无趣的一步,实在不行先跳过。
  5. 一点矩阵知识,感觉知道就行,离真正用到还很远。
  6. 概率知识,只要不搞生成模型啥的如VAE这种就可以先不管。像CNN基本用不到,只要知道softmax是个什么就行了。softmax(敲黑板!)就是多类logistic regression,简单讲就是模型判定输入是属于哪个类的概率。理解了logistic regression就不难了。
  7. 最好能看一本书,能够系统的学习,Ian Goodfellow and Yoshua Bengio and Aaron Courville写的deep learning就不错。

准备这些我觉得需要2周左右,推荐几个框架,边准备边试,看看喜欢哪一个框架。


初步了解

神经网络最初的是多层感知机(MLP),就是多层的Logistic regression,coursera上的机器学习那课里也有讲到。可以跑一下thenao的代码。为什么纯MLP很少用呢?主要因为参数冗余且不符合原理。还有很多其他原因,可以在接触CNN等其他结构的网络之后慢慢理解。当然这个MLP也是很重要的,虽然这里不多谈。

最重要的是尝试,否则就是夸夸其谈不明细节。当然得选择有趣简单的东西玩。deep learning中RBM这类可能有点晦涩而且现在并不流行。我就分别举CNN,RNN,GAN中简单可行的例子。可能现在不知道这些分别是什么,后面慢慢理解。

CNN

CNN的开山鼻祖LeNetAlexNet算是最初级的CNN可以一看,毕竟里面有很多东西都过时了。但它引入的relu,pooling这些精华还一直在,所以务必看一下AlexNet的论文。这篇博文还有一些比较。简单来说,CNN就是下面的架构,很好理解,相比MLP就多了卷积pooling操作。看MLP就出现了,在这里cnn就是在提取特征,而MLP就是在提取的特征上做判别。

simple cnn
From deeplearning.net

知道这些并不有趣,要玩点直观的。我觉得最有趣的肯定是neural style,最近流行的Prisma也基于差不多的原理,如果你都懂原理你就是high level。我们在有限的计算资源下训练一个大型CNN显得非常不划算,但是用别人训练好的CNN做一些有趣的事也算是另辟蹊径来理解什么是CNN。他的paper中介绍的还算清楚。工业界是要让代码跑起来,入门的第一件事就是能跑别人的代码,管他原理不原理。如果不清楚,先跑起来再说,生成一张自己照片的风格图也很值啊,然后再慢慢了解原理。


如果了解了原理,看了源码,可以尝试修改,比如合成两张图片啥的。能做到就算完全了解了neural style的原理了。这算是入门的第二步,修改别人的代码,同时可以学习一个。牛b之后还可以想想能否通过Autoencoder来加快速度。



好像有点跳,不虚慢慢来。当了解CNN工作模式还得靠其他的东西。

还可以看看deep dream,不过看着好像有点恶心。或者看看其他流行的项目。最重要的是阅读源代码,代码是绝对正确的。书本上paper里的都是大概,很少有细节。我没怎么训练过CNN,所以没法给出更多训练时的细节。

RNN

RNN就难多了,理解LSTM花了我很久。下面两个系列帮我了很大的忙。看完绝对能理解RNN,至少知道是RNN个什么,至少知道为什么要用LSTM,GRU这些变种。看不懂可以先看个大概,跑跑代码再说,然后再看,然后再研究代码。我就这么recurrent的入门的。

  1. Recurrent Neural Networks tutorials
  2. Understanding LSTM Networks

然后是搞点有意思的。我觉得对于初学者来说,自动生成Shakespeare风格的文章或者Linux代码算有意思了吧。可以参考这篇博文和这篇paper。Github上有很多代码,如char-rnnblocks-char-rnn。原理非常非常简单,就是根据前一个字符预测下一个字符。如输入一句话”我tm怎么这么diǎo”,输出就是“tm怎么这么diǎo。”,用RNN来拟合这样一个输入输出就行了。就是以下的结构。

from colah.github.io

这篇文章比较n-gram和RNN的结果,可以看出RNN是多么diǎo。简单来说,RNN能捕获前后之间的联系,比如c代码,RNN能生成匹配的括号,而n-gram是做不到的。

这是RNN能做到的最简单的,而且能在自己电脑上轻松跑起来的例子。还有简单的就是曲线拟合,时序分析。如下就是用RNN拟合的,可以自己尝试用RNN来拟合sin(x)



另外还有结合CNN和LSTM生成图片描述音乐合成,很多能玩的例子,而且玩着玩着就懂了。如果没有好奇心,那可能玩了之后还是不懂。反正我玩了之后,就会想:“tmd,怎么做到的?!”,然后就开始学习一个。

GAN

Generative Adversarial Networks算高级一点的内容,也是算简单的很有意思很有前途的一块,看字面意思就知道是生成模型。比其他生成模型VAE,RBM什么的简单多了,而且原理非常简单。总的来说就是一个生成网络,一个判别网络,目标是让生成网络生成判别网络无法区分是生成的还是真实的数据,这是个对抗(adversarial)的训练过程。应用于图片可能会有点问题,因此出了Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks(DCGAN),效果好很多,见github

GAN比较简单,网上没有满意的代码,随便写个mnist的GAN代码,稍微能看到数字。



要玩还是试试DCGAN比较好,毕竟GAN有点不完善。生成数字可能不那么有冲击力,生成人脸够diǎo了吧,详见github。更有冲击力的是结合VAE和GAN的生成网络VAE/GAN

其他

如reinforce learning, deep-Q 还没有接触。以后再谈。


略微高级

我只能说说我熟悉的deep learning的领域。

框架开发

在知道deep learning的基本知识之后就应该回馈社区。参与一些流行的年轻的框架的开发。如mxnetkerasblocks。首先可以锻炼自己阅读代码和写代码的能力,还可以了解实现细节,这对以后实现自己的奇思妙想很有帮助。个人推荐keras,文档完善,社区广泛,同时支持theano和tensorflow。这里的关系是,theano和tensorflow是两种不同类型的砖头,而keras用这两种砖头做成一些通用的部件如墙壁地板对应于deep learning中的如MLP,LSTM网络等。搞deep learning,熟悉一种框架是必须的,可以的话,需要了解其他框架,毕竟要看源码。或者更形象一点,CUDA相当于汇编语言,theano和tensorflow是c语言,keras和blocks这些框架就像Python。

研究方向

deep learning里还有很多可研究的东西。我们不关注性能,毕竟这超出能力范围。我们可以想想deep learning能干嘛。机器翻译diǎo不diǎo,我一头就栽进去了,毫不犹豫,因为这个太tm有意义太tm有意思了。哥以后不用学外语,也能精通八国语言,想想就exciting。不像CNN,机器翻译的性能还没有那么好,始终缺少一点东西,等待开发,可以看看acl的tutorial

生成模型也是非常热门的发展方向。最近出的Pixel Recurrent Neural NetworksDRAW都是很美妙的,可惜没有时间跟进。但了解一下还是可以的。

最后是大牛的最新研究进展,随时可以跟进。


总结

入门实在有点难以定义。我认为能自己写个网络就算入门了,所以要看教程、跑代码、看文档、看代码、改代码反复来。其中跑代码是稍微轻松一点,也能激发兴趣。要尝试简单的,慢慢就会复杂的了,想想现在看的英文文章也是从一个个单词积累的。
所以要尝试啊,别光看光点赞光收藏啊。

最后我们需要帮助:我们正在翻译deep learning这本书,直译版已全部翻完正在校正中,大概2周后公开。希望多多提提意见,帮忙修改修改,目前处于私密状态,在github上。

Generative Adversarial Networks

生成对抗网络(Generative Adversarial Networks)越来越火,最开始是用于MLP的生成对抗网络,就是Ian J. Goodfellow论文中提出的。后来出现了CNN架构的,效果确实提高了。现在还有SeqGAN,我不敢做评论。既然是Simplified DeepLearning系列的,自然尝试最简单的。


Generative Adversarial Networks原理

GAN的原理其实很简单。首先我们要学习的是生成器G关于数据x的分布p_g,问题从哪生成呢,这就需要我们定义一个噪声先验p_z(z),如一个均匀分布。那么生成的样本就是G(z)。然后我们需要一个判别器D(y),判断y是来自G还是x,也就是D要判断出输入是伪造的还是真实的。这就是一个对抗学习的过程:G尽量生成逼近真实的数据,使D不能分辨真伪。D要足够厉害,能够分辨真伪。形象的图如下:

gan
图自slideshare

形式的代价函数如下:

    \[\underset{G}{\min}~ \underset{D}{\max} ~V(D,G) = \mathbb{E}_{x \sim p_{data}(x)}[\log D(x)] +\mathbb{E}_{z \sim p_{z}(z)}[\log(1 - D(G(z)))]\]

理解这个很重要,可以看看这个。这里其实是将两个loss拼起来了,而且省去了一部分。有了目标函数,训练则是交替的训练,论文中有详细描述。简单的说,就是一步固定G,训练D,然后固定D,训练G。作者提到,为了防止”the Helvetica scenario”,要训练D多步,然后训练G一步。


一维高斯分布

看一个简单的例子,也是论文中的例子,我觉得很多人没理解论文中那个用均匀分布生成高斯分布的例子。为了深刻理解,保证自己没有理解错误,我必须再现一下。网络结构很简单,没几行如下。

然后就是训练,具体见github。怎么知道我是对的呢。可以看以下训练过程。

init
初始状态
m1

训练好的判别器
m2

交替训练几步之后
m2

最后结果

 

我还能说什么,完全符合理论D^*(y) = \frac{p_{data}(y)}{p_{data}(y) + p_{g}(y)}以及最后D(y) = \frac{1}{2}。在强调一下,这是一维的情况,也就是从真实数据给出一个数字和从G中生成一个数字如4,判别器无法判断4来自真实数据还是伪造的,因为这两个分布产生这个数字的概率是一样的,如最后一张图。如果真实数据产生4的概率大一点,判别器就能稍微判断一下,如最后第二张图。


MNIST测试

又得用到果蝇MNIST了,上面的例子太简单,可能不能令人信服。代码其实差不多,稍微加了个dropout,见github。训练中间结果如下,注意这是一张只循环一次的gif,你可能要刷新一下。

mnist gan
mnist gan (gif)

可能注意到怎么只生成两个数字,这就是所谓的”the Helvetica scenario”,我还不知道哪里出了问题。反正能生成数字了,够了。


实践出真知,show me the code!

国庆终于把坑填完了,DOTA还拿了暴走,完成了千年辅助的梦想!3天没出宿舍楼,欢度国庆,开心!


链接

神经网络近似函数

神经网络的强大在于其能在有限区间内近似任意函数,更精确的说是MLP(多层感知机)是万能的函数近似机,证明由Cybenko给出。注意是在有限区间内,论文中也是在单位超立方体(the unit hypercube)内讨论。如果不是有限区间,可能得靠RNN,比如某些具有周期性质的函数,就能使用RNN近似。


MLP近似爱心函数

看到微博上推的使用MLP来近似函数的博文,我觉得少点东西,做点补充。第一步就挑一个有趣点的函数吧,随便挑个心形函数x^2+(y-\sqrt[3]{x^2})^2。然后用keras很快就能搭个MLP,具体见github

训练之后就能得到对这个函数的近似,画成图如下:

heart
近似爱心函数

还有个哥们居然搞了个penis的函数,无法直视,有兴趣的可以尝试。


RNN近似周期函数

超出训练的范围,MLP的近似就无效了,想想也知道,泛化能力不可能那么强。而RNN在一定程度上可以弥补一下,最好是有点周期性的函数。为了简单起见,就先用\sin(x)测试好了。同样用keras快速搭个RNN,具体见github

关键是训练数据如何构造,也就是输入输出是怎样的。跟MLP不同,这里用函数值预测函数值,而不是输入x。因为在如时间序列预测的情况下,输入x是未知的,只能使用之前已知的函数值。我用相邻 feature_length个点组成一步的输入,总共 seq_length 步,其中每个点间隔 interval 。那预测就是后面的点,也可以只预测一个,我测试是预测 feature_length个,其中一半重合。比如0到32个点预测16到48个点(这样的做法似乎也能使用在MLP预测时间序列中,Quantitative Finance论文)。说不清楚看下图或者看代码。

rnn predict
RNN输入和输出

之后就是训练了,\sin(x)可以说是完美近似,后面因为累积误差导致不能近似的很好也是正常的。我又测试了\sin(x) * \sqrt[10]{x},有一个递增项,效果就没有\sin(x)好了,但短期内效果还是好的。注意我画得图的区间不在测试区间内,也就是网络没看到过的数据,正因为RNN具有一定泛化能力才能有如此表现。

sinx
sin(x)
sinxx
sin(x)*x^0.1

累积误差比较蛋疼,如下的情况,就算是测试数据的范围内也会有问题。可以用beamsearch,或者其他目标函数做,比如Q-learning。比较复杂,这里只讨论最简单的情况。

误差
累积误差

总结

MLP还是很厉害的,只要参数足够多,就能在一定区间内近似任意函数。而RNN相当于图灵机,之前也写过使用RNN做时间序列预测的博文,由于数据不能放出,所以也没有把代码放出。这次的代码与上次是类似的,改改也能试试做时间序列预测,就我的经验来看,没有传统方法好。