![深度学习必学的十个问题:理论与实践](https://wfqqreader-1252317822.image.myqcloud.com/cover/930/44509930/b_44509930.jpg)
1.3 使用keras
首先我们尝试解决著名的异或(XOR)问题,如图1.3,以逻辑关系“与”为例,它代表着坐标中只要一个为零,那么就整体为零,所以红色点出现在(1,1)位置。从图中可以看出,除了异或关系,其余的样本分布均可以用一条线来简单划分,也就是说,异或是一个线性不可分的问题。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P13_3491.jpg?sign=1739347624-9xVOKosywKKJdoXEDgDigeEAO2Ui2kuZ-0-85e459e0967a3f181a6564dd719da992)
图1.3 从左到右,分别表示“与”,“非”,“或”和“异或”
我们使用numpy来完成一个感知器算法,使用均方误差作为损失函数。需要注意,均方误差是我故意使用的,因为很多人会记住交叉熵用于分类,均方误差用于回归。实际上正如我们在统计学习中所说的,均方误差也可以用于分类,只是不具有对错误率的一致性。而在这个问题中,均方误差作为损失也已经足够说明问题,代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P13_5671.jpg?sign=1739347624-MH8Y5SInewePaEE1sur0nIYY711RqiuG-0-6cce097f36024eb2f3afef625f79c9fe)
其中,我们定义了sigmoid函数和它的导函数,并且定义了Neural Network的类,并没有隐层,它包含了两个方法,一个是feedforward用来得到输出,另一个是backprop用来训练。backprop使用的是标准的误差反向传播算法(我们会在第2章讨论)。接下来,我们将数据设置为图1.3NAND的四个数据点,并且通过反向传播算法训练它:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P14_5673.jpg?sign=1739347624-pBfXTywbCOYz6Z2BBNy1nCzQsZdYwm6v-0-c2f95266713970a37eef83655c1703dd)
如图1.4,感知器的损失随着迭代迅速减小,并且可以误差趋于零,表明通过训练可以把所有的数据分类正确,我们的训练是成功的。
训练完成以后,我们观察经过训练的感知器在数据上划分的决策边界,添加代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P14_5674.jpg?sign=1739347624-sQ1mqcR68i0YyHgXT20ypIqiyKHIpC4P-0-10d8c6b4d03d579d44f756bda0a9a545)
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P15_3537.jpg?sign=1739347624-3yTuN8bGYF6pBDy4JzgTRts4PGrxQptU-0-e2d0f0a0deaa4b9f12e49518d9cf9189)
图1.4 感知器的训练误差与迭代次数的关系
如图1.5,可以看出经过100次的迭代优化,感知器的决策边界已经可以很好地将NAND数据分开。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P15_3541.jpg?sign=1739347624-KprVdb0jmMYHE3my6rBgytQiXjae6vYd-0-08aadf591fafa01124e1e7592eb200ce)
图1.5 上述的感知器在NAND数据的决策边界
那么这个感知器是否真的像理论所说的无法解决异或问题呢,我们对上述代码数据的类别改为异或问题:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_5675.jpg?sign=1739347624-e8ifmO0v3SoI4kXtkd33WIlMzfsVF1hf-0-18f400480cc81f903241c1f74384f525)
我们仍然尝试感知器来解决异或问题,就会得到图1.6,可以看到训练误差虽然也随着迭代迅速减小,但并不会趋于零,而是趋近1,这代表着有一个数据是被错误分类的,而通过迭代来调节参数也无法完全正确分类。而决策边界的表现验证了这一点。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_3548.jpg?sign=1739347624-A0kx04QWjKL93axPuJdyR4sCQTGuMuCh-0-0a3fc2739e6a36f0d0c84ae1726ce366)
图1.6 面对异或问题,(a)为感知器的决策边界,(b)为感知器的训练误差随迭代的变化
接着,我们来尝试使用多层感知器算法来解决异或问题,虽然我们已经定义好了一个类,给其增加隐层也会非常简单。但如果我们快速验证想法,可以使用sklearn中的多层感知器并简单调用它来解决异或问题,代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_5676.jpg?sign=1739347624-nuWyscmPpGocS6tzwriRk25jEj5NK5bE-0-c44701b2d85b5131e63d3010809a6d00)
其中,我们只是增加了一个隐层,将hidden_layer_sizes设置为2,表示隐藏单元的数目为2,将activation设置为‘logistic’,表示激活函数使用sigmoid函数,将solver设置为‘sgd’,表示优化算法使用随机梯度下降(我们会在第3章详细讨论)。如图1.7,可以看到多层感知器的决策边界将数据正确分成了两部分,表明它可以解决异或问题。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P17_3604.jpg?sign=1739347624-UPlyE3ONaLWPjczCC8Ut6jto74fgi0j0-0-eba6eba1b2c566d2abc292f8e742461f)
图1.7 多层感知器在异或问题上的决策边界
多层感知器作为机器学习模型的一种,模型的容量会随着隐层单元的数目增加而增加,如果将隐层单元的数目调节到50,训练就会得到图1.8,可以发现决策边界从原来的直线变得弯曲,在不存在数据的点的区域,决策边界试图变得闭合,这表明发生了微弱的过拟合。
在使用完numpy搭建的感知器和sklearn搭建的多层感知器,我们接下来使用本书代码示例真正的主角——Keras,目前有很多流行的神经网络框架,它的共同点是让我们搭建神经网络更加容易,目前流行的有pytorch,tensorflow和MXNet,还有Keras。这里选择Keras的理由是它实在是太容易入门了,对于初学者来说拿代码作为工具快速验证想法是非常重要的,而且keras并非那么的不灵活,如果我们熟练地掌握理论,更改keras的后端也会是一件很简单的事情。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P18_3610.jpg?sign=1739347624-hOrS8DldReCYDlYlqGU7e2ksAeLI9A18-0-50ac8676f17cfb834bee3620778472c2)
图1.8 多层感知器在异或问题上的决策边界
读者大概只需要花几分钟的时间就可以简单上手它。首先,一个神经网络必须有输入/输出,在keras中,我们要先定义好一个模型:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P18_5678.jpg?sign=1739347624-AKk4OGxGn7DU86qgx9mpvX3TC1C18b4e-0-882c3f77c6c75683b2c2431145a5cbf8)
在上段代码中,我们创建了一个输入有100维的模型,但注意到我们的输入和输出都是一样的,这意味着我们只是创建了一个没有任何用处的100个神经元,放在inputs层里。keras.layers类中提供了很多种层,我们接下来增加层,只需要:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P18_5679.jpg?sign=1739347624-Uj2ExaChrqQUVgBcW0Ywo3691p1JDd9x-0-d98827fa058e09a9edd5813821a3a875)
在上段代码中,我们使用Dense函数来得到全连接层。接下来,我们需要激活函数,需要激活函数来调整神经网络,我们使用了ReLU(我们会在第4章中讨论)作为激活函数,让其单独成为一层。并添加到原来的模型中:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5680.jpg?sign=1739347624-VB7uFz4O4p2ZQpPqGCbOJtvsslDY7V5C-0-6b1294d833ef58ff5cb60a84d731f529)
通过这样的添加,我们已经获得了一个两层的神经网络,输出的值就为激活函数的处理之后的输出,准确地说,这只是一个输入为100维,输出为32维的感知机,我们要继续添加层使其能够处理非线性问题,紧接着,我们添加一个用于输出的层,并采用softmax函数作为激活函数,添加到上述模型中:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5681.jpg?sign=1739347624-7lKY4ep2tNRgj3gtZyA5XjbJqun9PRPC-0-c2efb6676b84cf432615323ce6c0c94b)
我们最终的模型就是处理100维特征的10分类数据。为了达到同样的效果,激活函数可以在层内事先指定好,就像我们上面做的那样,也可以把激活函数和层放在一起写作:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5682.jpg?sign=1739347624-nETJmUK5ENxKDT1vY2Unu8Ut8W42oYC0-0-6391e04615f620be8d4d889ae33dd4b0)
两种方法没有什么特别的区别。模型搭建完成后,我们完成了表示的任务.但在开始优化之前,我们需要初始化参数,keras提供了参数初始化类,我们在对一个网络进行优化的时候会使用到它,比如我们想进行正态分布的随机初始化:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5683.jpg?sign=1739347624-jweVWpDFJkpaefGJL3C3Uh58v3PsabOa-0-6f3305459c6299b301dd9758ab1487f3)
在上段代码中,我们设置好了一个均值为零,标准差为1。随机数种子为42的正态分布随机初始化器。在搭建网络中通过kernel_initializer传递初始化方法,并将上述的模型总结起来:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5684.jpg?sign=1739347624-edWJz9fewcVIZupnSom8SLWcjETSBIi0-0-de23325cd13b34d1e2730b6716596e1c)
同时,我们需要知道损失函数、优化算法和评估标准,它们分别可以从以下代码获得:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5746.jpg?sign=1739347624-OFJPKBehhoIV7sVXeucxzyE0AFmIWki4-0-2879fe1290e80039ebc91fe15fd6c4c9)
其中,我们指定损失函数的交叉熵,优化算法为带有nesterov动量的随机梯度下降(我们会在第3章详细讨论),评估标准为准确率。就可以用损失函数、优化算法以及评估标准编译好我们的模型:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5747.jpg?sign=1739347624-ZtMLibM9PU6Rc39DCOnf9jUuz5y9nbZ1-0-415c7268008d5c70ccdae826a16a8171)
最后我们开始训练数据:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5748.jpg?sign=1739347624-NN6TjK5FnPOZwWFlEMR4hvB7ImLWetqW-0-52bd9e1c1489f3e4b0cf3c42d97341ea)
其中X,Y是我们的数据,特别需要注意的是,batch_size就是指每次用于梯度更新的样本数量,epochs指整体数据被迭代的次数,与iteration不同,iteration是指进行的梯度更新的次数。verbose是一个显示日志的开关,如果设置为1,在训练过程中,会出现一个萌萌的进度条。训练完成后,我们可以方便地将keras模型保存为HDF5文件(需要安装python库:h5py):
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5749.jpg?sign=1739347624-F66E9StpGgAngD4al1WWMCvbUHrjKJJR-0-4c58b3cb829ee3b3dca0856f65221c78)
而当我们在其他地方使用这个模型时,只需要:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5750.jpg?sign=1739347624-xoiOP5k2TXzrj9F5ej5z4kbj6K8gUy9J-0-23b8297d2f88ddcdb904e90ccf94bfb2)
以上就是Keras的基本使用方法,也是后面用神经网络来验证想法的主要工具。