DTeam 技术日志

Doer、Delivery、Dream

面向程序员的深度学习

胡键 Posted at — Apr 28, 2019 阅读

本文系我在上周 TFUG 西安活动中分享话题的文字版,旨在对一段不算短的深度学习自学过程的总结,并以程序员的角度给出深度学习的简单介绍,帮助有志于成为深度学习开发工程师的小伙伴迅速把握整体大局,理清进入深度学习的道路。

整个话题主要由以下四部分组成:

先从分工谈起

机器学习工程师在公司中到底是一个怎样的存在?他的职责到底是什么?估计有不少开发者会有这样的疑惑。从机器学习的书中,从招工简章中,似乎都隐隐约约告诉大家:数学、算法……。然而,这个岗位中不是还有“工程师”三个字么?

直到这篇文章的出现才让一切变得清晰起来。先来看看机器学习工程师要构建的系统是什么样子: image.png

诚如图中展示的,机器学习相关功能虽然是系统的核心,但它只是整个系统的一小部分,其他的功能(如安全权限、数据导入和处理、可视化等)并没有超出大多数普通开发者需要掌握的知识范畴。并且由上图,也很自然地导出了机器学习工程师需要掌握的知识技能: image.png

从图中可以看出,“机器学习工程师”相当于“掌握了机器学习技能的工程师”:

那么,作为普通工程师来讲,我们该如何开始去学习机器学习(或者说本文更关注的深度学习)呢?结合个人的学习过程,我建议将整个学习过程分为 3 个阶段。

阶段 1:找感觉

这一步是最难的。

假如你还没有被书中那些数学公式和推导过程吓到,仍然坚定地要入手专研机器学习,请给自己一个大大的 👍🏻!不过,在这个阶段,我建议不要把理论学习作为重点。相反地,我更建议大家先去找上一两个当前流行好用的机器学习框架,直接照着例子开始动手实验。这便是所谓的“找感觉”。

这个阶段的主要目的就是破除大多数开发者对于机器学习的神秘感和畏惧感,形成对机器学习的感性认识,进而找到自己的“感觉”。若是一开始就去专研机器学习理论,除非你天赋异禀,很容易陷入一大堆数学公式爬不出来,学了大半年却讲不清楚机器学习到底是什么,遇到实际问题也不知如何入手,其挫败感可想而知。

以上也是我自己的亲身体会。因此,为了避免上面的问题,我推荐大家一开始先不要去看理论背景很强的机器学习书籍,尤其是那些大学教材和算法类数据。而是先从一些实战类的书籍看起(比如【参考】部分的Python 深度学习),以例子驱动的方式去学习。在形成对机器学习的感性认识之后,再去着手学习理论,以增强作为机器学习工程师的内涵。

同时,对于深度学习而言,为了更清楚地了解其背后的实现机理,不妨自己动手去实现一个简单地深度网络。对于这个任务,【参考】部分的深度学习入门:基于 Python 的理论与实现Python 神经网络编程为大家提供了支持。

阶段 2:找队友

一个人学习总是孤单的,假如有同伴的话,情况就不一样了。不仅可以相互扶持和鼓励,也有助于形成一种竞争和合作的良好氛围。对于那些已经找到感觉的开发者,我建议可以去Kaggle找同好,顺便练练级,赚点外快(竞赛奖金)。作为国际知名的竞赛平台,Kaggle 的排名含金量非常高,并且还有什么比起在实战中提高能力更好的途径吗?

在练级的过程中,你很快就会发现自己的知识短板,此时,只需跟随你的感觉,缺啥补啥就好了。

一段时间之后,你已经具备了一定的理论知识和实战技巧。这时,不妨可以考虑将自己的所学和经验向社区(如自己的博客和 TFUG)输出,树立自己的个人品牌并检验自己的实际掌握程度。因为“你只能讲清楚你理解的东西”,假如你发现自己下笔困难或无法清晰地向他人讲清楚“机器学习是何物”,那么很可能是哪些地方还没有完全弄明白。

阶段 3:找方案

机器学习是门实践性很强的课程,不断有新的理论、方法和模型诞生。为了保持跟得上技术的发展,你还得去抽时间去搜集模型、论文和案例,好让这些能成为你未来武器库中的一部分。并且,作为开发者,为了更好地发挥你所用框架的潜力,对于框架的源码和机理需要做必要的了解,这样才有可能去进一步扩展和定制。

最后,在结束本节之前,我再来说说需要避免的学习误区:

深度学习基础概念

在了解深度学习的基础概念之前,先来看看典型的机器学习分类:

讲深度学习,不能不提神经网络,而且既然有“深”自然就有“浅”,下图给出了两种网络的示意图,左边为“浅层”网络,右边为“深度”网络。

image.png

就大的结构而言,它们都由:输入层、隐藏层和输出层构成。所谓深浅之别,只不过在于:浅层网络的层数不会超过 3 层,反之则为深度网络。这个区分是不是有些意外地令人失望 😄?

根据神经网络理论,只需要输入输出两层全连接网络就可以“创造出世界”(只要不断加入神经元就好了,即胖网络),但为何又要引入“隐藏层”,并且后来又不断加深“隐藏层”的深度呢?原因就在于:

同时,“深”网络相比起“胖”网络,在计算上可以用现在的硬件得以实现。

了解完深度网络的概念,让我们再来看看深度网络的使用。整个使用过程分为两个阶段:

请注意,推理阶段的模型来源可以有两种:

下图展示了作为神经网络基本构成的神经元的组织结构:

image.png

其中:

然而,单个神经元并不能翻起多大风浪,必须将神经元按层组织,形成网络之后才有实际意义。对于网络中的层而言:

组建好了网络,自然需要对它进行训练。就训练过程而言,整个流程如下:

image.png

其中:

前面也说过,深度网络对于输入数据的要求是数值性,然而现实世界中的数据则不止于此,因此就必然涉及到对于输入数据预处理的问题。常见的预处理有:

image.png

此外,关于训练,你还需要了解下面的常见术语,有时候你会在一些书籍和代码中看到它们:

同时,在训练模型时,你需要考虑:

在实际过程中,常常将训练数据集划分为 3 部分:

验证集存在的价值在于提早发现问题,终止无用的训练。因为训练本身是一个耗时耗力的过程,若训练了半天发现效果很差,那么还不如提早结束。利用验证集,可以很早就发现过拟合现象。

这三类数据集的比例通常如下(训练集、验证集、测试集):

为了最大化利用这些来之不易的数据的价值,在实际部署之前,往往会将整个训练数据集合为一体,输入模型,完成对模型上线前的最终训练。

在本节最后,看看深度模型的部署方式。还记得吗,在本文中一直强调“模型即类库”。那么在部署方式上,其实也非常类似:

在实际过程中需要注意:

深度学习进阶概念

上一节中提到,将层视为不同的数据处理器,不同类型的层完成不同类型的工作。Keras 框架中典型的层有:

因此,构建网络的过程就是使用不同层进行组合的过程,本质上等同于使用编程语句构建程序。同时需要注意,一般情况下,输出层都为 Dense。这里可以理解为,通过隐藏层不断加强数据的关联性和缩小范围之后,最终通过暴力穷举解决问题。

常见的网络结构如下,具体介绍请参见【参考】中的MIT Deep Learning Basics: Introduction and Overview with TensorFlow,限于篇幅这里就点到为止。

image.png

前文已经出现了“过拟合”一词,现在我们来讨论一下关于拟合的两个概念:欠拟合和过拟合。

如果将网络类比为“内存”就很容易理解上面的两个概念。前一种相当于内存不足以容纳全部数据,从而无法学习;后者则相当于内存远超数据量,导致对现有数据通过死记硬背的方式就能蒙混过关。一般在实际工作中,欠拟合很容易发现,因此这里也不做过多强调。过拟合,则是从业者经常与之斗争的“顽疾”。这也是为何要引入“验证集”,提前发现过拟合现象,及时终止训练,避免浪费时间。

另一个需要了解的进阶概念就是“鞍点”,如下图:

image.png

有一定高数背景的读者应该对于通过“求导求极值”的做法并不陌生,深度学习中的优化方法本质上仍是如此。由上图可见,在鞍点附近,导数(斜率)几乎为 0。这样造成的负面后果就是:

为了应对这种情况,一般采用:

最后一个需要掌握的进阶概念就是“超参数”,它本质上相当于模型的“元数据”,无法通过训练过程得到。典型的超参数有:

并且超参数的调优过程是一个不断实验的体力活,一般的做法有:人工、Grid Search、随机优化、贝叶斯优化。随着技术的发展,目前也有类似 AutoML 的的端到端解决方案出现,相信未来会更美好。

深度学习实战

关于深度学习用到的框架和工具,以及实验环境的搭建,请参见我写的这篇文章这里就不再赘述。下面的代码来自【参考】部分Python 深度学习一书,大家可以对于使用 Keras 编写深度网络有一个感性认识:

from keras import layers
from keras import models

model = models.Sequential()
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5))
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))

简单吧,😄。

总结

讲到这里,我希望这次分享能够消除大家对于深度学习的神秘感和恐慌,真正动手去做做练习,在实践中成长。最后总结一下:

参考


相关文章