AI 技术讲座精选:数学不好,也可以学好人工智能(五)——深度学习和卷积神经网络

【AI100 导读】欢迎阅读《数学不好,也可以学好人工智能》系列的第五篇文章。如果你错过了之前的四部分,一定记得把它们找出来看一下!本文主要介绍了深度学习架构——深度卷积神经网络(DCNN),以及作者用 Python 写的图像识别程序。

今天我们要介绍的是我们自己用 Python 写出来的图像识别程序。

开始写之前,我们先来研究一下这个非常强大的深度学习架构——深度卷积神经网络(DCNN)。

卷积神经网络(convnet)是计算机视觉的驱动力。从自动驾驶汽车到 Google 的图像查找,都是运用的卷积神经网络。在2017 TensorFlow 峰会上,一个研究者曾向大家展示如何像皮肤科医生一样利用卷积神经网络侦测皮肤癌的过程(https://www.youtube.com/watch?v=toK1OSLep3s),而他们利用的就只是一部智能手机而已。

那么,神经网络为什么有如此强大的能力?其中一个关键因素就是:

他们做自动模式识别。

那什么是模式识别,为什么我们会关注是否为自动模式识别?

模式的形式有很多,我们先看两个典型的例子:

  • 定义一个物理形式的特征
  • 做一个任务的步骤

计算机视觉

我们将图像处理中的模式识别称为特征抽取。

当你在看一幅图或者是真实世界中的某个东西的时候,你会有选择性的提取出其中的关键特征,这些关键特征会让你明白所看东西的含义。这是无意识的动作。

当你看到我家猫 Dove 的图片的时候,你可能会认为它是一只“猫”或者是“其他动物”,虽然你并不知道你是怎么做出这个判断的,但是你确实做到了。

你不知道你是怎么做到的,因为它是自动发生的,并且是无意识的。

对你来说,这似乎很简单,因为你每天都在做这样的事情,错综复杂的处理过程是被隐藏了的。

你的大脑相当于一个黑盒子,一个生来就没有操作手册的黑盒。

然而,如果你停下来想一下,你刚才做的那一小部分实际上涉及了大量的步骤。表面上看来是令人难以置信的简单过程,但实际上却非常复杂。

  • 你移动你的视线。
  • 你接收到光线,并且处理由光线组成的部件发送到你脑中的信号。
  • 之后,你的大脑开始工作,把光转化为电化学信号。
  • 这些信号经由你构建的神经网络发送出去,激活不同的部分,包括记忆、联想和感觉等。
  • 在这些最基本的层面上,大脑强调低水平模式(耳朵、胡须、尾巴等),在此基础上合并成高水平模式(动物)。
  • 最后,你做了一个分类,这意味着你把它转换成了一个词,这就是现实生活中事物的符号化表达,这个例子中的符号化表达为“猫”。

这一切都发生在眨眼之间。

如果让你去教一台计算机来做这些,你要从哪开始呢?

  • 你能告诉它如何侦测耳朵吗?
  • 什么是耳朵?
  • 你怎么去描述耳朵?
  • 为什么猫的耳朵与人类或者蝙蝠(蝙蝠侠)的耳朵是不同的?
  • 从不同的角度看去,耳朵分别是什么样的?
  • 所有的猫的耳朵都一样吗(不,看一下苏格兰折耳就知道了)?

问题远不止这些。

在如何教计算机实现上述过程的问题上,如果你运用了 C++ 或 Python,可能会找不到好的解决方案。但是别沮丧,因为这个问题已经困扰计算机科学家长达50年了!

你再自然不过的一个动作,却是深度学习神经网络的一个关键应用——“分类器”,在上述猫咪的例子中即为图片分类器。

刚开始,人工智能研究者尝试利用像我们刚才所说的步骤那样练习,尝试人工定义所有的步骤。比如,当想到自然语言处理(NLP)的时候,他们把最好的语言学家集合到一起,然后说:“把所有的规则都用语言写出来”。他们把这些称为早期的人工智能“专家系统”。

语言学家坐到一起,苦苦思索出一套令人眼花缭乱的组合,包含各种 if,unless 和 except:

  • 是鸟么? 是

如果不是,它是:

  • 死了
  • 伤了
  • 一个像企鹅一样不能飞的鸟
  • 翅膀不见了

这个表单所包含的规则和例外的情况可以说是无穷无尽的。不幸的是,在各种各样的错误面前,这些规则同样非常脆弱且容易出错。他们耗费了大量的时间来创建、讨论其中的规则和例外情况,但是却很难解决这个问题。

深度神经网络称得上是真正的突破,它使得你不再需要为所有的步骤找到解决方案,而是让机器来自动抽取猫的关键特征。

“自动化”是基础,因为我们会忽略掉在我们做复杂活动,尝试找到数千或数百万隐藏的步骤是不可能解决的问题。

让计算机自动处理这个过程!

无尽的步骤

让我们来看一下第二个例子:算出一个任务的步骤数量。

手动处理上述过程并为计算机定义步骤的做法叫做编程。假设你想找到硬盘上所有的照片,然后把它们都转移到新的文件夹下。

对大多数任务来说,编程者就是神经网络。他充满智慧,在研究任务的同时将任务分解成小的步骤,接着又为计算机定义了所有的小步骤。他把这一切以形式化向量的形式输入到计算机中,这就是我们所说的机器编程语言。

以下是一个 Python 语言的实例,来自于 Stack Exchange 上的“JollyJumper”(http://stackoverflow.com/questions/11903037/copy-all-jpg-file-in-a-directory-to-another-directory-in-python):

import glob import shutil

import os

src_dir = “your/source/dir”

dst_dir = “your/destination/dir”

for jpgfile in glob.iglob(os.path.join(src_dir, “*.jpg”)):

shutil.move(jpgfile, dst_dir)

Jolly Jumper 算出了所有的步骤,然后翻译给了计算机,例如:

  • 我们需要知道源目录
  • 我们也需要一个目标目录
  • 我们需要一个方法来区分我们想要的文件类型,在这个例子中是“jpg”文件
  • 最后我们去目录下,找到jpg文件,然后把他们从源目录移到目标目录下

这个方法适用于简单或中等复杂程度的问题。操作系统是现今最复杂的软件,包含数亿行代码。每行代码不但明确的介绍了计算机是如何工作的(例如:在屏幕上画东西、存储或升级信息),而且也介绍了人是如何完成工作的(复制文件、输入文本、发邮件、看照片、与其他人聊天等)。

但是我们一直处于进步的过程中,不断尝试解决更具挑战性的问题,于是便遇到了人工操作过程所不能解决的问题。

例如,我们如何定义开车?

要向完成此类极为复杂的任务,需要有数以亿计的小步骤的参与。我们不得不:

  • 站成一条线
  • 知道线是什么,然后识别它
  • 行驶于两地之间
  • 识别出类似于墙,人,碎片等障碍物
  • 对有用的目标(街区指示)或有威胁的目标(行人在绿灯时穿过马路)进行分类
  • 评估我们周围不断行驶过的汽车
  • 瞬间做出决定

在机器学习领域,这就是有名的决策问题。复杂的决策问题示例如下:

  • 机器人导航和感知
  • 语言翻译系统
  • 自动驾驶汽车
  • 股票交易系统

神经网络的内部秘密

让我们看一下深度学习是如何利用自动特征抽取来帮助我们解决现实世界里极度复杂的问题的!

如果你曾经读过 V. Anton Spraul 的杰作“Think Like a Programmer”(你应该读一下),你就会明白编程是为了解决问题。程序员把问题拆解成若干的小问题,然后为其制定解决方案,最终用代码将它们一一实现。

深度学习为我们解决问题,但人工智能当下仍然需要人的参与(谢天谢地)去设计和测试它的架构(至少现在还需要)。因此,让我们把神经网络拆散成小块儿,然后编程去识别图片——我的 Dove 是只猫。

深度学习的深度

深度学习是机器学习的子领域。深度学习的名字源于这样一个想法:把不同的层堆叠在一起,以便学习越来越有意义的数据向量。

每一个层都是一层神经网络,每一个层都包含着一定数量的与人工神经元的连接点。

在使用强大的 GPU 来为我们做计算之前,我们只能构建一些很小的神经元网络。而且这些神经元网络的作用非常有限。现在我们已经能把很多层堆叠在一起了,因此也就产生了深度学习的深度

神经网络的灵感来源与19世纪50年代医学对人脑的研究成果。研究者创建了一个神经元的数学表达式,如下图所示(感谢 Stanford 关于卷积神经网络的公开课和维基百科,http://cs231n.github.io/neural-networks-1/):

现在,忘记那些复杂的数据公式吧!因为根本就不需要。

基础相当容易。用 x0 来表示的数据在神经元之间传送,连接的强度用其权重来表示(w0x0,w1x1等)。如果信号够强,它能通过其“激活功能”激活神经元。

以下是一个三层深度神经网络的例子:

通过激活其中的一些神经元,加强一些神经元之间的连接,系统学习到世界上什么是重要的,什么不重要。

构建并训练一个神经网络

让我们从更深入些的层次看一下深度学习,像之前一样写点代码。所有的代码我都已经上传到了我的 Github 上https://github.com/the-laughing-monkey/learning-ai-if-you-suck-at-math/tree/master/Deep%20Learning%20Examples)。

系统的基本特征如下:

  • 训练
  • 输入数据
  • 权重
  • 目的
  • 损失函数
  • 优化函数
  • 预测

训练

训练过程就是教神经网络学习那些我们想让其学习的东西。它遵循以下五个简单的步骤:

  1. 创建训练数据集,我们把他命名为 x,然后加载它的标签命名为目标 y
  2. 为 x 供给数据,向前通过神经网络形成的结果是预测目标 y
  3. 计算出网络损失,就是预测 y 与正确结果 y’ 之间的不同
  4. 计算出损失梯度(l),损失梯度表示我们接近或偏离正确目标有多快。
  5. 在梯度方向的反方向调整网络权重,从第二步开始再试一下。

输入数据

在示例中,DCNN 的输入数据为一堆图片,而且图片数量越多越好。和人不一样,计算机需要利用大量的实例来学习如何区分这些图片。人工智能研究者正致力于研发用相对教少的数据来进行学习,但那仍然是一个前沿的问题。

一个著名的例子就是 ImageNet 数据集,这个数据集包含大量的手工标注的图片。换句话说,他们众包了一批人,利用他们已经构建好的神经网络去浏览所有的图片,赋予数据某些意义。人们在上传图片同时为图片打上了各种不同的标签,如“狗”或者是具体的“小猎犬”。

对网络来说,这些标签就代表准确的预测。网络开始匹配手工打标签的数据(y)和自身的预测结果(y’),y 与 y’ 越接近,网络越精确。

数据被分成两部分:训练数据集和测试数据集。训练数据集是输入,也就是我们要“喂给”神经网络的那一部分。它会学习各种各样目标的关键特征,然后我们测试在测试图像集中能否准确的找到那些随机目标。

在我们的程序中,我们用到了知名的数据集——CIFAR10 数据集,这个数据集是由加拿大高等研究所开发的(CanadianInstitute for Advanced Research)。

CIFAR10 包含10个类,含有60,000张32*32色的图片,也就是每个类里有6,000张图片。我们用其中的50,000张图片作为训练数据集,另外的10,000张作为测试数据集。

在我一开始用 CIFAR 的时候,我错误的假设了相对于使用 ImageNet 这种大图的挑战来说,使用 CIFAR 的挑战会比较小。但结果却是 CIFAR 更具挑战性,原因是图片太小而且少了很多,所以对于我们的神经网络来说图片的显著特征太少,很难锁定。

以我的经验,类似于 ResNet 这样的最大且最坏的模型在使用 ImageNet 的时候准确率也可以达到97%,而使用 CIFAR-10 时准确率仅仅能达到87%。当下 CIFAR-10 上最先进的是 DenseNet,拥有超大的250层和1500万个参数,准确率可以达到约95%!我把这些框架附在了文章的底部,以便未来进行研究。但是在专研如此复杂的系统之前,我们最好从一些简单的系统开始。

理论已经足够了,开始写代码吧。

如果你 Python 用的还不够熟练,我强烈、强烈而又强烈得向你推荐 Fabrizio Romano 所著的“Learning Python”这本书,书中的每一个细节都写得非常好。关于 Python,我从没见过比这更好的书。我有很多其他的关于 Python 的书,但对我的帮助都没有这本大。

我的 DCNN 的代码是基于 Github 上 Keras 的样例代码而编写的:

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

你能在这找到我的修订版:

https://github.com/the-laughing-monkey/learning-ai-if-you-suck-at-math/tree/master/Deep%20Learning%20Examples

我已经调整了其中的架构和参数并且增加了TensorBoard,以帮助实现网络的可视化。

让我们开始 Python 编程吧,输入数据集和各种各样的类,我们还需要创建我们的 DCNN。幸运的是,Keras 已经知道如何自动获取数据集,所以我们省了很多力气。

from __future__ importprint_function

import numpy as np

from keras.datasets importcifar10

from keras.callbacks importTensorBoard

from keras.models importSequential

from keras.layers importDense, Dropout, Activation, Flatten

from keras.layers importConvolution2D, MaxPooling2D

from keras.utils importnp_utils

from keras import backend asK

我们的神经网络开始于某个随机的配置。从哪里开始都很好,我们不应一味期望这个开端会非常明智。其次,随机配置可能会完全偶然的给我们带来一个神奇的结果,因此我们设定随机权重,以确保我们得到的结果不是源于纯粹的运气。

np.random.seed(1337) # Very l33t

现在我们需要增加一些层了。

很多神经网络用的是全连接层,这就意味着已经把所有的神经元都连接在了一起。

全连接层对于解决各种各样的问题都堪称完美。但不幸的是,在图像识别上它表现的不尽人意。

因此,我们选择用卷积层来构建我们的系统。由于系统并没有把所有的神经元都联系到一起,所以它是独一无二的。

让我们看一下 Stanford 课程上计算机视觉关于卷积神经网络扩展是怎么说的:

在 CIFAR10 中,图像的大小仅仅为 32*32*3(32位、32位高、3个颜色通道),因此一个全连接神经在一个规则神经网络的第一隐藏层中的权重应该是 32*32*3=3072。这个大小看起来仍然是可控的,但很明显这个全连接架构不能扩展为较大的图片。例如,像 200*200*3这样一张更大的图片,它会导致神经元的权重变成 200*200*3=120000。与此同时,我们几乎可以肯定,一定会需要一些这样的神经元,所以参数增加的速度会相当的快!很明显,这种全连接非常浪费,海量的参数会很快便会导致系统过度拟合。

所谓过度拟合,就是在网络训练得很好的情况下,会对训练集中的数据全权精通,但是一旦出现没有见过的图片便无计可施了。换句话说,它在识别现实世界里的图片的时候并没有什么卵用。

就好像你玩同一个游戏,玩了一次、一次又一次直到你完美的记住了它。然后有人在现实游戏里做了一个不同的动作,你就不知道怎么办了。我们稍后会讨论一下过度拟合。

以下是数据通过 DCNN 的过程。它只看到了数据中的一个很小的子集,将这个子集捕获出来用于各种模式。随后又把这些观察到的数据构建成了更高层次的理解方式。

注意一下最初的几个层,观察它们模式简单的原因,如边缘、颜色和基本形状等信息。

随着信息流不断通过各个层,系统会发现模式会越来越复杂,如质地信息。最终,它会归纳出各种各样的目标类。

这个想法是基于猫的视觉做的一个实验,该实验表明不同的细胞仅对某些特定的刺激有反应,如边缘或特定的颜色。

人类也是一样的。我们的视觉细胞仅对特定的特征有反应。

这是一个典型的 DCNN 架构算法:

你将注意到这里有三个不同种类的层,一个池化层。关于具体的细节,你可以在 Oxford 和 Standford 的课程里统统找到。不管怎样,我会跳过大量的细节描写,因为这些细节会让很多人会感到困惑。我清楚的知道在我第一次想搞清楚它的含义的时候,我也感到很混乱。

现在你需要知道的是池化层。池化层的目的非常简单,就是做二次抽样。换句话说,它们能够缩小输入的图片,这样可以减少计算加载量和内存使用率。需要处理的信息越少,我们对图片要做的工作就越简单。

在训练集异常的情况下,它们也能帮我们减少网络零的第二类过度拟合情况,但是真的与挑选狗、鸟或者猫的图片一点关系都没有。例如,在一堆图片里可能会出现像素混乱的情况或者镜头闪烁的情况,当它们认为小行星和婴儿喃喃自语比较相近的时候,那么网络可能会决定把镜头闪烁和狗归到一起。

最后,很多 DCNN 增加了一些密集型连接,我们将其称为全连接层,来处理在前面一些层里侦测到的特征图谱,并且进行预测。

因此,给我的卷积网络加几个层吧。

首先,我们为卷积神经网络的层增加一些变量。

# Defines how many images wewill process at once batch_size = 128

# Defines how many types ofobjects we can detect in this set. Since CIFAR 10 only detects 10 kinds of objects,we set this to 10.

nb_classes = 10

# The epoch defines how lonewe train the system. Longer is not always better.

After a period of time wereach the point of diminishing returns. Adjust this as necessary.

nb_epoch = 45

# Here we put in the imagedimensions. We know the images are 32 x 32. They are already preprocessed forus to be nicely uniform to work with at this point.

img_rows, img_cols = 32, 32

# Here we set the number ofconvolutional filters to use

nb_filters = 32

# size of pooling area formax pooling

pool_size = (2, 2)

# convolution kernel size

kernel_size = (3, 3)

核心池化大小定义了卷积网络是如何忽略图片其他部分而直接寻找其特征的。最小核心的大小为1*1,也就是关键特征仅为1个像素。确认特征有效性的典型核心大小应该是一次大于3个像素,然后池化这些特征缩小到2*2栅格。

从图片中抽取2*2栅格的特征,然后像交易卡片一样把它们堆叠起来。这便使其与图片上特定的点上脱离开来,允许系统在图片任意位置按直线或螺旋线查询,而不单单只是在发现它们的第一个位置。

大多数教程将其描述成处理“翻译不变性”(“translation invariance”)。

这到底是什么意思?好问题!

再看一下这张图:

在不把特征拉出来的情况下,就像你在第一层和第二层中看到的那样,系统可能会把发现猫鼻子的那个圆形区域认定为是图片中间最重要的位置。

让我们看一下是如何识别出我的猫 Dove。如果系统最开始发现了她眼睛中的一个圆,那么它就可能错误的去假设图片中的这个位置与侦测猫有关系。

相反,系统应该主动去寻找图片中的圆,不管这些圆处于图片中的哪个位置,就像我们下面看到的一样。

在我们增加层之前,我们首先需要加载并处理数据。

# This splits the data into training and test sets and loads the data. Cifar10 is a standard test data set for Keras so it can download it automatically. It's about 186MB expanded.

(X_train, y_train), (X_test, y_test) = cifar10.load_data()

# Unfortunately, TensorFlow and Theano want their tenor parameters in a different order, so we check for the backend from the json initialization file and set them accordingly.

if K.image_dim_ordering() == 'th':

X_train = X_train.reshape(X_train.shape[0], 3, img_rows, img_cols) X_test = X_test.reshape(X_test.shape[0], 3, img_rows, img_cols) input_shape = (1, img_rows, img_cols) else: X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 3) X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 3)

input_shape = (img_rows, img_cols, 3)

X_train = X_train.astype('float32')

X_test = X_test.astype('float32') X_train /= 255 X_test /= 255 print('X_train shape:', X_train.shape) print(X_train.shape[0], 'train samples')

print(X_test.shape[0], 'test samples')

# convert class vectors to binary class matrices

Y_train = np_utils.to_categorical(y_train, nb_classes)

Y_test = np_utils.to_categorical(y_test, nb_classes)

OK,我们现在终于准备好往我们的程序里增加层了:

model = Sequential()

model.add(Convolution2D(nb_filters, kernel_size[0], kernel_size[1],

border_mode='valid', input_shape=input_shape)) model.add(Activation('relu')) model.add(Convolution2D(nb_filters, kernel_size[0], kernel_size[1])) model.add(Activation('relu')) model.add(MaxPooling2D(pool_size=pool_size)) model.add(Dropout(0.25))

层堆叠需要遵循:

  • 卷积
  • 激活值
  • 卷积
  • 激活值
  • 池化
  • 中途退出

除了中途退出激活值以外,上述层的类型我们基本都讨论过了。

中途退出是最容易懂的。基本上,它指的是模型随机消失的概率。这与 Netflix 使用 Chaos Monkey 的方式很相似,都运用脚本随机关闭网络上的服务器,以确保网络可以通过内建的韧性和冗余而存活下来。这里也是如此。我们希望能够确保网络不过分依赖于任何一个特征。

激活值层是决定神经是否激活或被激活的方法。在激活层上存在许多激活功能,RELU 是其中最成功的一个,因为其计算效率高。这个表单是所有可以在 Keras 获取到的不同种类的激活功能。

我们同时也增加了第二个卷积层,目的是作第一层的镜像。如果为了提高效率而重写程序的话,我们应该创建一个模型生成器,然后做一个for循环来创建我们想堆叠的层数。但在这个例子中,我们只需要从上面剪切和黏贴层,为了方便而违背了 Python 的 zen 规则(http://wiki.c2.com/?PythonPhilosophy)。

model.add(Convolution2D(nb_filters,kernel_size[0], kernel_size[1]))

model.add(Activation('relu'))

model.add(Convolution2D(nb_filters,kernel_size[0], kernel_size[1]))

model.add(Activation('relu'))

model.add(MaxPooling2D(pool_size=pool_size))

model.add(Dropout(0.25))

最后,我们增加了密集层以及更多的中途退出层,然后把所有的特征图平铺。

model.add(Flatten())

model.add(Dense(256))

model.add(Activation('relu'))

model.add(Dropout(0.5))

model.add(Dense(nb_classes))

model.add(Activation('softmax'))

我们在最后一层使用名叫 Softmax 的激活值,因为它定义类以外的概率分布。

权重

之前我们曾简单的说了一下什么是权重,但是现在,我们会更深层次的探讨一下。

权重是各种神经元之间的连接强度。

在我们的大脑中,也有类似权重的存在。在人类大脑中存在一系列的生物神经元,这些神经元相互连接,会有电信号或化学信号从中流过。

但这些连接不是静态的。随着时间的流逝,有些连接变强了,而有些连接则变弱了。

两个生物神经元中流过的电化学信号越多,连接就变得越强。本质上,当你的大脑有新体验的时候,它会不断的给自己发送信息。通过加强神经元之间的链接,为你的种种体验的记忆、感觉和想法编码。

基于神经网络的计算机同样受到了生物个体的启发。我们可以把他们叫做人工神经网络(Artificial Neural Networks)或简单的叫 ANN。通常当我们说“神经网络”的时候,我们真正想表达的其实是 ANN。ANN 不能像生物个体功能那样准确,所以,不要错误的理解 ANN 是一种模拟人脑。这是不对的。例如在生物神经网络(BNN)中,并不是所有的神经元都与其他神经元连接在一起,而 ANN 某一层中的神经元通常是与下一层的神经元相连接的。

下图是 BNN 的图像,显示了各种神经元之间的连接情况。注意:不是所有神经元都有与之连接的神经元。

尽管有很多不同,在 BNN 和 ANN 间仍然有很多相似的地方。

就像你脑中的神经元会形成强连接或弱连接,在人工神经网络中权重决定着神经元间的连接强度。每个神经元仅了解世界的一小部分。当把它们集合到一起的时候,串在一起的神经元会拥有了一个更为综合而又全面的世界观。我们认为,相对较强的连接是解决问题时更为重要的部分。

让我们看一些关于神经网络游乐场的截图,一个可视化的 TensorFlow 会帮我们理解得更容易。

第一个网络展示的是一个简单的六层系统。这个网络要做的是在图片最右侧位置将橘色的点和蓝色的点清清楚楚的分开。它正在寻找一种最好的模式,以高度准确的将两种颜色的点分离开来。

我还没有开始对系统进行训练。因为我们明白,神经元间的权重基本相同。细虚线之间是弱连接,粗虚线之间是强连接。在开始的时候,利用随机权重对网络进行初始化。

现在看一下我们训练过的网络。

首先我们看一下最右边的图片。现在蓝色点都集中在了中间的区域,橘色点环绕在图片的其他位置。正如你所看到的,分离情况高度准确,非常好。这大概花费80个“纪元”或者训练周期。

同时请注意,很多权重在各式神经元中存在诸多蓝色虚线。权重增加了,现在系统被训练出来了,已经准备好接纳世界了。

训练我们的神经网并优化它

现在让我们的模型尝试一下数字吧。为了能实现目标,我们对模型进行了编译并赋予了它优化功能

model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

为了能够理解优化功能,我花费了很长时间,因为我发现在很多解释中,在“什么”后面都没有问“为什么”。

换句话说,到底为什么我们会需要优化功能?

记得网络中有个目标预测 y,经过若干轮的训练之后,它生成了新的预测 y’。系统在测试数据集中对这些预测进行了随机样本测试,来检验系统的验证准确性。最终,系统在其训练数据集里的准确性能够达到99%的准确性,但是在测试图片里仅能达到50%或70%,所以这个游戏真正的名字叫验证准确性,而不是准确性。

优化器会在考虑到模型权重的情况下,计算误差函数的梯度(用数学术语来说,也叫偏导数)。

那意味着什么?你可以通过 3D 丘陵景观图来想象一下权重分布情况(就像你下图看到的),也就是下图的“errorlandscape”。景观的坐标代表不同的权重配置(像地图上的坐标),而景观的高度代表不同权重配置的总错误或总成本。

优化器提供了一个重要的功能。它能计算出调整权重的方法以使错误降到最低概率。这一切是通过微积分来实现的。

什么是微积分?翻开任意一本数学课本,你都会发现一些关于微积分的超级没用的解释,全是关于计算导数和微分的。但是,微积分到底意味着什么?

我一直都不知道,直到有一天我读到了 Kalid Azad 在“Calculus Better Explained”一书中对微积分解释。

下面就是KalidAzad在书中对微积分的解释。

微积分做两件事:

  • 把事情分解成更小的部分,就如同把一个大圆分解成多个小环。
  • 计算出变化率。

换句话说,如果我把一个圆切割成环:

我能把环打开,然后做一些简单的数学计算。

骗子!

在我们的示例中,我们做了一大堆的测试并且调整了网络权重,但是我们实际上真的接近解决问题的最好的解决方案了吗?优化器将会帮我们回答这个问题!

你可以读一下梯度下降,访问网址获取海量的细节(http://sebastianruder.com/optimizing-gradient-descent/),也可以在 Stanford 课程上找,但你最终的找寻结果很可能与我一样,发现这些课程擅长在细节和关键问题上寻找原因。

本质上,你要努力去做的就是将错误率降到最低,有点像在大雾中开车。在这篇博客的相对较早的几期中,我把梯度下降描述为寻找最优解决方案的方法。但事实上,关于我们是否有一个“最佳”的解决方案,真的是无从知晓的。如果我们知道最佳方案是什么的话,我们只管去做就好了。相反,我们只是寻找一个比当下好一点的解决方案,有点像进化的过程。我们发现这一过程遵循适者生存的规律,但这不意味着我们创造出了 Einstein!

像儿时玩 MarcoPolo 一样思考梯度下降。

你闭上双眼,你所有的朋友都在泳池里飘着。你喊“Marco”,然后你所有的小伙伴都回答“Polo”。你用的你耳朵来判断,你是更加接近了,还是更加远离了。如果离得远了,你就会调整或者尝试一下其他的路径。如果更加接近了,你就继续在那个方向上前进。现在我们正在计算如何最好的去调整网络权重,来帮它们更好地理解这个世界。

我们选择了“adam”优化器,具体描述请访问此链接(https://arxiv.org/abs/1412.6980)。我发现通过蛮力来改变我的程序,看起来产生了最好的结果。这就是数据科学的艺术。没有算法为其设定规则。如果我改变了网络的架构,可能我会找到其他表现更好的优化器。

这个列表是Keras上提供的各种各样的优化器(https://keras.io/optimizers/)。

下一步是启动 TensorBoard,以使网络工作变得可视化。

# Set up TensorBoard tb = TensorBoard(log_dir='./logs')

之前我们所做的是创建出了一个日志目录。现在我们将训练模型,并在 TensorBoard 上显示日志。

model.fit(X_train, Y_train, batch_size=batch_size, nb_epoch=nb_epoch, verbose=1, validation_data=(X_test, Y_test), callbacks=[tb])

score = model.evaluate(X_test, Y_test, verbose=0)

print('Test score:', score[0])

print("Accuracy: %.2f%%" % (score[1]*100))

好吧,让我先找到那个小屁孩儿,看看他是怎么做的!

50000/50000 [==============================] - 3s - loss: 0.4894 - acc: 0.8253 - val_loss: 0.6288 - val_acc: 0.7908 Epoch 89/100 50000/50000 [==============================] - 3s - loss: 0.4834 - acc: 0.8269 - val_loss: 0.6286 - val_acc: 0.7911 Epoch 90/100 50000/50000 [==============================] - 3s - loss: 0.4908 - acc: 0.8224 - val_loss: 0.6169 - val_acc: 0.7951 Epoch 91/100 50000/50000 [==============================] - 4s - loss: 0.4817 - acc: 0.8238 - val_loss: 0.6052 - val_acc: 0.7952 Epoch 92/100 50000/50000 [==============================] - 4s - loss: 0.4863 - acc: 0.8228 - val_loss: 0.6151 - val_acc: 0.7930 Epoch 93/100 50000/50000 [==============================] - 3s - loss: 0.4837 - acc: 0.8255 - val_loss: 0.6209 - val_acc: 0.7964 Epoch 94/100 50000/50000 [==============================] - 4s - loss: 0.4874 - acc: 0.8260 - val_loss: 0.6086 - val_acc: 0.7967 Epoch 95/100 50000/50000 [==============================] - 3s - loss: 0.4849 - acc: 0.8248 - val_loss: 0.6206 - val_acc: 0.7919 Epoch 96/100 50000/50000 [==============================] - 4s - loss: 0.4812 - acc: 0.8256 - val_loss: 0.6088 - val_acc: 0.7994 Epoch 97/100 50000/50000 [==============================] - 3s - loss: 0.4885 - acc: 0.8246 - val_loss: 0.6119 - val_acc: 0.7929 Epoch 98/100 50000/50000 [==============================] - 3s - loss: 0.4773 - acc: 0.8282 - val_loss: 0.6243 - val_acc: 0.7918 Epoch 99/100 50000/50000 [==============================] - 3s - loss: 0.4811 - acc: 0.8271 - val_loss: 0.6201 - val_acc: 0.7975 Epoch 100/100 50000/50000 [==============================] - 3s - loss: 0.4752 - acc: 0.8299 - val_loss: 0.6140 - val_acc: 0.7935 Test score: 0.613968349266 Accuracy: 79.35%

在经过了100次的训练后,准确率达到了79%。相对于这么少的代码来说,结果还不错。现在你可能认为79%还不够好,但是请注意那可是在2011年,已经比当时最顶级的 Imagenet 还要好了,Imagenet 足足花费了10年才达到这个程度。与此同时,我们调整了 Keras Github 上的一些样例代码,并做了某些微调。

你会注意到,在2012年中各种新的想法逐渐显露出来。

人工智能研究者 Alex Krizhevsky、Ilya Sutskever 和 Geoffrey Hinton 所设计的 AlexNet 是最早的测橙色点的程序,标志着深度学习开始复兴。在接下来的一年中,大家纷纷都在用深度学习。到2014年,在图像识别方面,那些比较成功的架构的性能已经超过了人类。

虽然如此,这些架构经常与特定类型的问题联系到一起。当下某些非常流行的架构,像 ResNet 和 Google 的 Inception V3 在小 CIFAR10 图像上准确性仅做到88%,在更大的 CIFAR100 上则表现得更差。

现在顶级的架构是 DenseNet,它在2016的 ImageNet 比赛上脱颖而出。它碾压式的通过了 CIFAR10,达到了杀手级的94.81%的准确性,用了近乎疯狂的250层,包含1530万个连接!这是一个绝对的野兽。在单用一个 Nvidia1080GTX 显卡的情况下,如果你要运行一个40*12的模型同时要让准确率达到93%的话,你可以参见下表。这将会花费你一个月的时间。天哪!

即便如此,我还是鼓励你去深入研究这些模型,看看你能从中学到什么。

我做了一些实验,设法通过蛮力破解某个奇怪的架构,结果只构建了 Keras 层和无自定义层就使模型达到了81.40%的准确度。点此链接可以在 Github 上找到我的代码。

Epoch 70/75 50000/50000 [==============================] - 10s - loss: 0.3503 - acc: 0.8761 - val_loss: 0.6229 - val_acc: 0.8070 Epoch 71/75 50000/50000 [==============================] - 10s - loss: 0.3602 - acc: 0.8740 - val_loss: 0.6039 - val_acc: 0.8085 Epoch 72/75 50000/50000 [==============================] - 10s - loss: 0.3543 - acc: 0.8753 - val_loss: 0.5986 - val_acc: 0.8094 Epoch 73/75 50000/50000 [==============================] - 10s - loss: 0.3461 - acc: 0.8780 - val_loss: 0.6052 - val_acc: 0.8147 Epoch 74/75 50000/50000 [==============================] - 10s - loss: 0.3418 - acc: 0.8775 - val_loss: 0.6457 - val_acc: 0.8019 Epoch 75/75 50000/50000 [==============================] - 10s - loss: 0.3440 - acc: 0.8776 - val_loss: 0.5992 - val_acc: 0.8140 Test score: 0.599217191744 Accuracy: 81.40%

你也可以加载 TensorBoard 查看我们是如何做的。

tensorboard --logdir=./logs

现在打开一个浏览器并输入如下的 URL:

127.0.1.1:6006

这个截图是不同时间的训练结果。

你会发现,我们开始很快便通过了受益递减点——大概是训练35次的时候,相应的准确度为79%。而其他的时间都花费在了要达到81.4%的准确度上,在训练超过75次之后很可能出现了过度拟合。

所以,你要怎么做来改进模型?

  • 这里有一些策略:
  • 执行你的自定义层
  • 做图像增强方面的工作,如翻转图像、放大图像、曲翘图像或克隆图像等
  • 更深入一些
  • 更改这些层的设定
  • 看一下那些成功架构的介绍,用类似的特性来堆叠你的模型

用自己的达到去理解模型,同时人工建模进一步理解模型。这样一来,你就已经达到了数据科学领域的最高水平。或许你深入研究了 CIFAR10,并且也注意到了增强图片的对比度能使图片脱颖而出。做吧!

不要害怕把图片加载到 Photoshop 中,尝试各种各样的过滤器吧!看看图片最终是不是变得更棱角分明,或者更加清晰了。看看你可以是否可以做与Keras图像处理功能相同的事情。

深度学习并不是什么灵丹妙药,需要耐心和奉献精神。

它能做令人难以置信的事情,但你也可能会发现自己粘在电脑前盯着数字直到凌晨两点,却没有得到任何结果。

但接下来你会实现一个突破。

神经网络的测试过程是一个反复实验、反复纠错的过程。有些尝试会让我们越来越接近答案,而有些尝试却使我们离答案越来越远。

我正在研究怎么使用基因算法来实现神经网的自我进化。在前面已经做了蛮多的工作了,但是还远远不够。

最后,我们会达到这样一个时刻:存在很多成熟的架构,载入某些库和预训练的权重文件后便可轻松实现其功能。但这应该是 IT 企业几年以后的事情了。

这个领域仍在飞速发展,新想法层出不穷。幸运的是,我们仍处于不断上升的浪潮中。所以,放轻松,开始玩耍你自己的模型吧。

研究。实验。学习。

做吧,错不了!

本文由 AI100 编译,转载需得到本公众号同意。


编译:AI100

原文链接:https://hackernoon.com/learning-ai-if-you-suck-at-math-p5-deep-learning-and-convolutional-neural-nets-in-plain-english-cda79679bbe3


本文分享自微信公众号 - AI科技大本营(rgznai100)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-03-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏积累沉淀

数据挖掘算法之深入朴素贝叶斯分类

写在前面的话:   我现在大四,毕业设计是做一个基于大数据的用户画像研究分析。所以开始学习数据挖掘的相关技术。这是我学习的一个新技术领域,学习难度比我以往学过的...

37580
来自专栏机器之心

只需十四步:从零开始掌握Python机器学习(附资源)

选自kdnuggets 作者:Matthew Mayo 机器之心编译 参与:黄小天、吴攀、晏奇、蒋思源 Python 可以说是现在最流行的机器学习语言,而且你也...

473110
来自专栏量子位

轻叩次元壁——谈谈真人头像的漫画化

在这篇自带萌点的文章中,作者提出了一种新型模型TwinGAN,可以将真人头像转化成漫画风的卡通头像。打通二次元和三次元的世界的方法,都在这里面了~

21820
来自专栏达观数据

达观数据搜索引擎排序实践(下篇)

机器学习排序 机器学习排序(Machine Learning to rank, 简称MLR) 机器学习排序系统框架 机器学习排序系统一般分为离线学习系统和在线预...

589100
来自专栏数据派THU

【资源】只需十四步:从零开始掌握Python机器学习

来源:机器之心 作者:Matthew Mayo 校对:丁楠雅 编辑:胡蝶 原文可参阅:http://suo.im/KUWgl 和 http://suo.im/9...

23160
来自专栏量子位

传送门!ICML2017(国际机器学习大会)最佳论文(附下载)

允中 整理编译 量子位 报道 | 公众号 QbitAI ? 8月6日,第34届国际机器学习大会(ICML 2017)已在悉尼拉开帷幕。 其中最受关注的论文奖项已...

435140
来自专栏新智元

【AI玩跳一跳终极奥义】首个端到端神经网络,看AI在玩游戏时注意什么

作者:Flood Sung 编辑:费欣欣 【新智元导读】不用传统外挂,训练纯深度学习AI来玩跳一跳,结果会如何?本文作者使用模仿学习,训练了一个端到端的神经网络...

41970
来自专栏量子位

Google全新AI实战课发布:从原理到代码,手把手带你入门机器学习

16410
来自专栏机器之心

教程 | 如何使用TensorFlow构建、训练和改进循环神经网络

选自SVDS 作者:Matthew Rubashkin、Matt Mollison 机器之心编译 参与:李泽南、吴攀 来自 Silicon Valley Dat...

36290
来自专栏AI科技评论

论文|可用于实时应用的启发式搜索

摘要 现有的启发式搜索算法不能在找到完整的解决方案之前采取行动,所以它们不适用于实时应用。因此我们提出了一种极大极小前向搜索(minimax lookahead...

39270

扫码关注云+社区

领取腾讯云代金券