TensorFlow是一个用于神经网络和深度学习的库。它由谷歌大脑团队开发,该团队专门针对可扩展性对其进行了设计。TensorFlow可以在多种平台上运行,无论是桌面设备还是移动设备,甚至是计算机集群。如今,TensorFlow已经成为最流行的机器学习库之一,且在实际应用软件中有着非常广泛的应用。例如,很多如今使用的在线服务,其背后的AI系统是由TensorFlow驱动的,其中包括图像搜索、语言识别、推荐引擎。TensorFlow已经变成了很多AI应用的幕后英雄,即使我们并没有注意到。

Keras是构建在TensorFlow上的高级应用程序接口(API)。为什么要用Keras呢?为什么我们需要另外一个函数库来作为TensorFlow的接口?简单来讲,Keras消除了构建神经网络的复杂性,可以快速构建模型进行实验和测试,而不需要让用户考虑底层的实现细节。Keras基于TensorFlow提供了简单且符合直觉的API以用于构建神经网络。它的设计准则是模块化和可扩展性。我们后面会看到,通过组合调用Keras API可以非常轻松地构建神经网络,你可以把它看作通过堆积乐高模块来构建大型结构。这一对新手友好的特性使Keras成为了最流行的Python机器学习库之一。本书将使用Keras作为构建神经网络项目的首要机器学习库。

Keras中的基础构建单元是层(layer)。通过将多个层线性地堆叠起来,可以构建出神经网络模型。我们使用优化器(optimizer)对模型进行训练并选择损失函数作为评估标准。回忆一下,之前我们从头构建神经网络时,需要编写代码来实现这些模块。我们把这些模块称为Keras中的基本结构单元,因为可以基于这些基本结构单元构建任意的神经网络。

Keras基本构建单元之间的关系如图1-22所示。

图1-22

1.层——Keras中构建神经网络的基本元素

你可以把Keras中的层看作原子,因为它们是神经网络中最基本的单元。每层可以接收输入并进行数学运算,然后将输出结果传递给下一层。Keras中的核心层包括致密层(dense layer),激活层(activation layer)和Dropout层。还有另外的一些更复杂的层,包括卷积层(convolutional layer)和池化层(pooling layer)。在本书中,这些不同种类的层都会在项目中出现。

就目前来讲,让我们仔细研究一下Dense层,这是我们目前在Keras中最常用到的层。Dense层也就是我们所说的全连接层(fully-connected layer)。Dense层是全连接的,因为它在其实现的数学函数中使用了全部的输入(与之相对的是仅使用部分输入)。

一个Dense层实现了如下的函数:

是输出,是激活函数,x是输入,而Wb分别是权重和偏差。

这个公式对你来说应该很眼熟吧。我们在手动编写神经网络时用过全连接层。

2.模型——层的集合

如果层可以被看作原子,那么模型就可以被看作Keras中的分子。一个模型就是一些层的集合,在Keras中最常用的模型是顺序模型(sequential model)。顺序模型允许我们将层线性地堆叠起来。这使得我们可以轻松地构建模型结构而不需要操心其背后的数学原理。在后面的章节我们会看到,要想让连续的层之间具有互相兼容的维度需要耗费很大精力,而这些工作Keras已经默默地替我们完成了。

一旦定义好了模型,就需要开始定义训练流程了,在Keras中可以通过compile方法完成。compile有很多参数,其中需要定义的最重要的参数是损失函数和优化器。

3.损失函数——神经网络训练误差评估标准

在前面的章节中,我们定义损失函数作为评价预测好坏的标准。问题的特点应该作为选择损失函数的依据。Keras中有很多损失函数,常用的有mean_squared_error、
categorical_crossentropy和binary_crossentropy。

对于如何选择损失函数,这里有一些经验法则:

如果是回归问题选择均方差函数(mean_squared_error);

如果是多类别分类问题选择分类交叉熵(categorical_crossentropy);

如果是二元分类问题选择二元交叉熵(binary_crossentropy)。

在某些情况下,你可能会发现Keras默认的损失函数并不适用于你的问题。在这种情况下你可以通过使用Python中定义函数的方法定义你自己的损失函数,然后把这个自定义函数传递给Keras的compile方法。

4.优化器——神经网络训练算法

优化器是一种用于在神经网络训练过程中更新权重的算法。Keras中的优化器基于梯度下降算法,该算法在前面的章节我们已经有所涉及。

尽管并没有涉及不同种类优化器的细节,但是需要注意的是,我们对优化器的选择需要基于待解决问题的特点。通常来讲,研究人员发现Adam优化器对深度神经网络来讲是最佳的,而SGD优化器则更适合浅层神经网络(shallow neural network)。Adagrad优化器同样也是一个很流行的选择,它根据权重被更新的频率自适应地选择学习速率,这种方法的主要优点是可以避免手动调节学习速率这一超参数,而调参在机器学习工作流中是非常耗时的一步。

让我们看看如何使用Keras创建一个之前介绍过的两层神经网络吧。为了构建一组线性层,首先在Keras中声明一个顺序模型:

from keras.models import Sequential
model = Sequential()

上述代码会创建一个空的顺序模型以便我们可以向其中添加层。在Keras中向模型添加层非常简单,和一层一层搭乐高积木一样。我们先从左面开始添加层(最靠近输入的一层):

from keras.layers import Dense
# Layer 1
model.add(Dense(units=4, activation='sigmoid', input_dim=3)) 
# 输出层
model.add(Dense(units=1, activation='sigmoid'))

在Keras中添加层非常简单,只需要调用model.add()命令即可。注意我们必须为每一层定义节点个数,节点个数越多模型也就越复杂,因为这意味着要训练的权重也越多。对于第一层,我们需要定义input_dim,它将数据集中特征的数量(列数)告知Keras。同时要注意我们使用了Dense层。Dense层就是全连接层(fully connected layer),在后续的章节里面我们会向你介绍各种类型的层,它们适用于不同类型的问题。

可以调用model.summary函数来验证模型结构:

print(model.summary())

输出结果如图1-23所示。

图1-23

params的数量指的是在我们刚刚定义的模型中,需要训练的权重和偏差的个数。

当对模型结构感到满意之后,让我们编译模型并开始训练吧:

from keras import optimizers
sgd = optimizers.SGD(lr=1)
model.compile(loss='mean_squared_error', optimizer=sgd)

注意我们定义了SGD优化器的学习速率为1.0(lr=1)。学习速率是神经网络的一种超参数,需要根据问题小心地调优。我们会在后面的章节中仔细讲解超参数的调优。

Keras中的均方差(mean_squared_error)损失函数和先前定义的平方和损失函数类似。我们使用SGD优化器来训练模型。回忆一下,我们用梯度下降法更新权重和偏差,将其向损失函数关于权重和偏差的导数减小的方向去调节。

让我们使用之前用于训练神经网络的数据来训练这个神经网络。这样就可以将Keras构建的神经网络预测结果与之前我们徒手打造的神经网络预测结果进行比较。

定义一个x数组和y数组,分别对应特征和目标变量:

import numpy as np
np.random.seed(9)
X = np.array([[0,0,1],
              [0,1,1],
              [1,0,1],
              [1,1,1]])
y = np.array([[0],[1],[1],[0]])

对训练模型进行1500轮训练:

model.fit(X, y, epochs=1500, verbose=False)

使用model.predict()命令来获取预测结果:

print(model.predict(X))

预测结果如图1-24所示。

图1-24

与之前获得的预测结果进行对比,可以看到两个预测非常接近。使用Keras最大的优势在于构建神经网络时不需要像之前一样,操心底层的实现和数学原理。实际上,我们不需要做任何数学计算。只需要调用一系列的API就可以构建出神经网络,这样我们就可以专注于更高层次的细节,并进行快速实验。