![深度学习必学的十个问题:理论与实践](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=1739681739-x2GQb2UcdOEnBJZk1YtofGKk78DV8Tkz-0-8d550df22af4999190c37cebf7569918)
图1.3 从左到右,分别表示“与”,“非”,“或”和“异或”
我们使用numpy来完成一个感知器算法,使用均方误差作为损失函数。需要注意,均方误差是我故意使用的,因为很多人会记住交叉熵用于分类,均方误差用于回归。实际上正如我们在统计学习中所说的,均方误差也可以用于分类,只是不具有对错误率的一致性。而在这个问题中,均方误差作为损失也已经足够说明问题,代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P13_5671.jpg?sign=1739681739-NQJXfBDH6Thv4PdSPFUF6k8OFyJIMdWm-0-edea40004294e5fab8d644771284b3a1)
其中,我们定义了sigmoid函数和它的导函数,并且定义了Neural Network的类,并没有隐层,它包含了两个方法,一个是feedforward用来得到输出,另一个是backprop用来训练。backprop使用的是标准的误差反向传播算法(我们会在第2章讨论)。接下来,我们将数据设置为图1.3NAND的四个数据点,并且通过反向传播算法训练它:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P14_5673.jpg?sign=1739681739-AR02xK7E7WUgG7Be72kfFw3qbyDrUoL6-0-1165f57b3abfdbab94ac71ab5abc6d9e)
如图1.4,感知器的损失随着迭代迅速减小,并且可以误差趋于零,表明通过训练可以把所有的数据分类正确,我们的训练是成功的。
训练完成以后,我们观察经过训练的感知器在数据上划分的决策边界,添加代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P14_5674.jpg?sign=1739681739-c6NobllgIcwWzPktTRmxlfl41naN9YFU-0-35bd476d97f2b227d696546071084ae4)
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P15_3537.jpg?sign=1739681739-bGoUk8FHsGbFlwWFprI9t8idxHsgdGG6-0-902d9491fb7e43545bca0fab3bb8e814)
图1.4 感知器的训练误差与迭代次数的关系
如图1.5,可以看出经过100次的迭代优化,感知器的决策边界已经可以很好地将NAND数据分开。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P15_3541.jpg?sign=1739681739-eMNsnz5DiM3sy209gBvZ6wTJTuDAIpgF-0-1c80ac7aa6e5902ffd18d353e6c3bd48)
图1.5 上述的感知器在NAND数据的决策边界
那么这个感知器是否真的像理论所说的无法解决异或问题呢,我们对上述代码数据的类别改为异或问题:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_5675.jpg?sign=1739681739-KjDGVsVswvFgTKCej1KHA2K4UAQ3B4a8-0-a23e8ef7490d4cc8caed53d933cde8a8)
我们仍然尝试感知器来解决异或问题,就会得到图1.6,可以看到训练误差虽然也随着迭代迅速减小,但并不会趋于零,而是趋近1,这代表着有一个数据是被错误分类的,而通过迭代来调节参数也无法完全正确分类。而决策边界的表现验证了这一点。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_3548.jpg?sign=1739681739-03EOaImIDH9lEu622QwYn3ojs4Dux2kN-0-3eca7a5b97c1bbf4127b3a048325b056)
图1.6 面对异或问题,(a)为感知器的决策边界,(b)为感知器的训练误差随迭代的变化
接着,我们来尝试使用多层感知器算法来解决异或问题,虽然我们已经定义好了一个类,给其增加隐层也会非常简单。但如果我们快速验证想法,可以使用sklearn中的多层感知器并简单调用它来解决异或问题,代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_5676.jpg?sign=1739681739-bMGWReVKpr0fuehOQvUSs2brmfOUgH09-0-0043da33168ccc9bc9b25a78143b81cf)
其中,我们只是增加了一个隐层,将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=1739681739-Qi96DjnowoFppWEwhFjebTjU8W9t8XIg-0-831e16f4fbd655eb0d5a6a93dbf64059)
图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=1739681739-j3yAVbF3k3VJLNckERB8BEa2Py1SCxeO-0-d42231253030631308861aac868ae5fa)
图1.8 多层感知器在异或问题上的决策边界
读者大概只需要花几分钟的时间就可以简单上手它。首先,一个神经网络必须有输入/输出,在keras中,我们要先定义好一个模型:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P18_5678.jpg?sign=1739681739-u8gAoLrs8Iaf8gB5Wk9GvWEJGAmn1g7T-0-0fb07eb4cdf8bf3d51e310c55cc395fe)
在上段代码中,我们创建了一个输入有100维的模型,但注意到我们的输入和输出都是一样的,这意味着我们只是创建了一个没有任何用处的100个神经元,放在inputs层里。keras.layers类中提供了很多种层,我们接下来增加层,只需要:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P18_5679.jpg?sign=1739681739-p3YZmRbJcBpZVSv58jXYW9uO0B3283QI-0-56a71fac2544c93ecab54ee2ccb48780)
在上段代码中,我们使用Dense函数来得到全连接层。接下来,我们需要激活函数,需要激活函数来调整神经网络,我们使用了ReLU(我们会在第4章中讨论)作为激活函数,让其单独成为一层。并添加到原来的模型中:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5680.jpg?sign=1739681739-7IkN9NoyuhXUa3qyN9b7zcvG2vxfTxZe-0-6c12847c29a9d2c83ae70b809743863e)
通过这样的添加,我们已经获得了一个两层的神经网络,输出的值就为激活函数的处理之后的输出,准确地说,这只是一个输入为100维,输出为32维的感知机,我们要继续添加层使其能够处理非线性问题,紧接着,我们添加一个用于输出的层,并采用softmax函数作为激活函数,添加到上述模型中:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5681.jpg?sign=1739681739-m5SOPDeX7qTwQb3UQyBTdUtUqBiywzWV-0-549b3fc4c7a11aca409af01f753b762c)
我们最终的模型就是处理100维特征的10分类数据。为了达到同样的效果,激活函数可以在层内事先指定好,就像我们上面做的那样,也可以把激活函数和层放在一起写作:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5682.jpg?sign=1739681739-YTDq0g2Hrw2Hzkc5WqqDRxIbShxD7lnt-0-48167575c7ad45f0f6bb41fe2eb98669)
两种方法没有什么特别的区别。模型搭建完成后,我们完成了表示的任务.但在开始优化之前,我们需要初始化参数,keras提供了参数初始化类,我们在对一个网络进行优化的时候会使用到它,比如我们想进行正态分布的随机初始化:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5683.jpg?sign=1739681739-UPsn4ooyY1vjsvSld5umfSLZnfhiSCDO-0-c9aaa8e7980f4dd337f15b3b09a2697a)
在上段代码中,我们设置好了一个均值为零,标准差为1。随机数种子为42的正态分布随机初始化器。在搭建网络中通过kernel_initializer传递初始化方法,并将上述的模型总结起来:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5684.jpg?sign=1739681739-Skf9PGpnV7hjtjaQHaerLnjRUn4fuWRB-0-800867d657737cd79be57e15d259ef1e)
同时,我们需要知道损失函数、优化算法和评估标准,它们分别可以从以下代码获得:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5746.jpg?sign=1739681739-j8GrLCG97mtb7G1P9NnH3pRkomsLrLhT-0-4c36bebd049080166d0515b1cb103985)
其中,我们指定损失函数的交叉熵,优化算法为带有nesterov动量的随机梯度下降(我们会在第3章详细讨论),评估标准为准确率。就可以用损失函数、优化算法以及评估标准编译好我们的模型:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5747.jpg?sign=1739681739-iDN9hedSbvHUfImuimRa9Gdd1iTVs55I-0-a8c5bbfab57987d46e29b2a5797dc29a)
最后我们开始训练数据:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5748.jpg?sign=1739681739-jjpe5iXXAiX13Om0aUyqRePsUHuBhebb-0-785b53b2ae828d50ad3cb5f8a88c051f)
其中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=1739681739-7JN6TCa75n8rX3NXi4rxXE51s80j8ryh-0-751a67af06e277472035a81c69735d2a)
而当我们在其他地方使用这个模型时,只需要:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5750.jpg?sign=1739681739-1pZkaDvVUtvVAe5K6crZcaM3ZcFwr0rV-0-5e735fb70bb5f2d8615e41324754697c)
以上就是Keras的基本使用方法,也是后面用神经网络来验证想法的主要工具。