深度学习必学的十个问题:理论与实践
上QQ阅读APP看书,第一时间看更新

1.3 使用keras

首先我们尝试解决著名的异或(XOR)问题,如图1.3,以逻辑关系“与”为例,它代表着坐标中只要一个为零,那么就整体为零,所以红色点出现在(1,1)位置。从图中可以看出,除了异或关系,其余的样本分布均可以用一条线来简单划分,也就是说,异或是一个线性不可分的问题。

图1.3 从左到右,分别表示“与”,“非”,“或”和“异或”

我们使用numpy来完成一个感知器算法,使用均方误差作为损失函数。需要注意,均方误差是我故意使用的,因为很多人会记住交叉熵用于分类,均方误差用于回归。实际上正如我们在统计学习中所说的,均方误差也可以用于分类,只是不具有对错误率的一致性。而在这个问题中,均方误差作为损失也已经足够说明问题,代码如下:

其中,我们定义了sigmoid函数和它的导函数,并且定义了Neural Network的类,并没有隐层,它包含了两个方法,一个是feedforward用来得到输出,另一个是backprop用来训练。backprop使用的是标准的误差反向传播算法(我们会在第2章讨论)。接下来,我们将数据设置为图1.3NAND的四个数据点,并且通过反向传播算法训练它:

如图1.4,感知器的损失随着迭代迅速减小,并且可以误差趋于零,表明通过训练可以把所有的数据分类正确,我们的训练是成功的。

训练完成以后,我们观察经过训练的感知器在数据上划分的决策边界,添加代码如下:

图1.4 感知器的训练误差与迭代次数的关系

如图1.5,可以看出经过100次的迭代优化,感知器的决策边界已经可以很好地将NAND数据分开。

图1.5 上述的感知器在NAND数据的决策边界

那么这个感知器是否真的像理论所说的无法解决异或问题呢,我们对上述代码数据的类别改为异或问题:

我们仍然尝试感知器来解决异或问题,就会得到图1.6,可以看到训练误差虽然也随着迭代迅速减小,但并不会趋于零,而是趋近1,这代表着有一个数据是被错误分类的,而通过迭代来调节参数也无法完全正确分类。而决策边界的表现验证了这一点。

图1.6 面对异或问题,(a)为感知器的决策边界,(b)为感知器的训练误差随迭代的变化

接着,我们来尝试使用多层感知器算法来解决异或问题,虽然我们已经定义好了一个类,给其增加隐层也会非常简单。但如果我们快速验证想法,可以使用sklearn中的多层感知器并简单调用它来解决异或问题,代码如下:

其中,我们只是增加了一个隐层,将hidden_layer_sizes设置为2,表示隐藏单元的数目为2,将activation设置为‘logistic’,表示激活函数使用sigmoid函数,将solver设置为‘sgd’,表示优化算法使用随机梯度下降(我们会在第3章详细讨论)。如图1.7,可以看到多层感知器的决策边界将数据正确分成了两部分,表明它可以解决异或问题。

图1.7 多层感知器在异或问题上的决策边界

多层感知器作为机器学习模型的一种,模型的容量会随着隐层单元的数目增加而增加,如果将隐层单元的数目调节到50,训练就会得到图1.8,可以发现决策边界从原来的直线变得弯曲,在不存在数据的点的区域,决策边界试图变得闭合,这表明发生了微弱的过拟合。

在使用完numpy搭建的感知器和sklearn搭建的多层感知器,我们接下来使用本书代码示例真正的主角——Keras,目前有很多流行的神经网络框架,它的共同点是让我们搭建神经网络更加容易,目前流行的有pytorch,tensorflow和MXNet,还有Keras。这里选择Keras的理由是它实在是太容易入门了,对于初学者来说拿代码作为工具快速验证想法是非常重要的,而且keras并非那么的不灵活,如果我们熟练地掌握理论,更改keras的后端也会是一件很简单的事情。

图1.8 多层感知器在异或问题上的决策边界

读者大概只需要花几分钟的时间就可以简单上手它。首先,一个神经网络必须有输入/输出,在keras中,我们要先定义好一个模型:

在上段代码中,我们创建了一个输入有100维的模型,但注意到我们的输入和输出都是一样的,这意味着我们只是创建了一个没有任何用处的100个神经元,放在inputs层里。keras.layers类中提供了很多种层,我们接下来增加层,只需要:

在上段代码中,我们使用Dense函数来得到全连接层。接下来,我们需要激活函数,需要激活函数来调整神经网络,我们使用了ReLU(我们会在第4章中讨论)作为激活函数,让其单独成为一层。并添加到原来的模型中:

通过这样的添加,我们已经获得了一个两层的神经网络,输出的值就为激活函数的处理之后的输出,准确地说,这只是一个输入为100维,输出为32维的感知机,我们要继续添加层使其能够处理非线性问题,紧接着,我们添加一个用于输出的层,并采用softmax函数作为激活函数,添加到上述模型中:

我们最终的模型就是处理100维特征的10分类数据。为了达到同样的效果,激活函数可以在层内事先指定好,就像我们上面做的那样,也可以把激活函数和层放在一起写作:

两种方法没有什么特别的区别。模型搭建完成后,我们完成了表示的任务.但在开始优化之前,我们需要初始化参数,keras提供了参数初始化类,我们在对一个网络进行优化的时候会使用到它,比如我们想进行正态分布的随机初始化:

在上段代码中,我们设置好了一个均值为零,标准差为1。随机数种子为42的正态分布随机初始化器。在搭建网络中通过kernel_initializer传递初始化方法,并将上述的模型总结起来:

同时,我们需要知道损失函数、优化算法和评估标准,它们分别可以从以下代码获得:

其中,我们指定损失函数的交叉熵,优化算法为带有nesterov动量的随机梯度下降(我们会在第3章详细讨论),评估标准为准确率。就可以用损失函数、优化算法以及评估标准编译好我们的模型:

最后我们开始训练数据:

其中X,Y是我们的数据,特别需要注意的是,batch_size就是指每次用于梯度更新的样本数量,epochs指整体数据被迭代的次数,与iteration不同,iteration是指进行的梯度更新的次数。verbose是一个显示日志的开关,如果设置为1,在训练过程中,会出现一个萌萌的进度条。训练完成后,我们可以方便地将keras模型保存为HDF5文件(需要安装python库:h5py):

而当我们在其他地方使用这个模型时,只需要:

以上就是Keras的基本使用方法,也是后面用神经网络来验证想法的主要工具。