![深度学习必学的十个问题:理论与实践](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=1738855214-IQxyXPKT7VfKJr6htjfBX0eC0KCylKuk-0-cec0b2982d790fa166ffc549cb94d3cd)
图1.3 从左到右,分别表示“与”,“非”,“或”和“异或”
我们使用numpy来完成一个感知器算法,使用均方误差作为损失函数。需要注意,均方误差是我故意使用的,因为很多人会记住交叉熵用于分类,均方误差用于回归。实际上正如我们在统计学习中所说的,均方误差也可以用于分类,只是不具有对错误率的一致性。而在这个问题中,均方误差作为损失也已经足够说明问题,代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P13_5671.jpg?sign=1738855214-l5T8wB1TYKxUel0MXgqwLkr58YY3xg41-0-ac5293e8776d9a54a08b09c4b0e9843d)
其中,我们定义了sigmoid函数和它的导函数,并且定义了Neural Network的类,并没有隐层,它包含了两个方法,一个是feedforward用来得到输出,另一个是backprop用来训练。backprop使用的是标准的误差反向传播算法(我们会在第2章讨论)。接下来,我们将数据设置为图1.3NAND的四个数据点,并且通过反向传播算法训练它:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P14_5673.jpg?sign=1738855214-tmRPdRkNsvChi3c2qUcjCZc2l5ZEe5lH-0-b9542b5b0b8c781ba0c4dbae1013c7d5)
如图1.4,感知器的损失随着迭代迅速减小,并且可以误差趋于零,表明通过训练可以把所有的数据分类正确,我们的训练是成功的。
训练完成以后,我们观察经过训练的感知器在数据上划分的决策边界,添加代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P14_5674.jpg?sign=1738855214-e2scI4WFtYp2d4KvAY0ZZtcoTtLPDHIS-0-9610edb0f753e256a95db809aca0ecae)
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P15_3537.jpg?sign=1738855214-hPDOAOjyzz0keIO5OxAjEmZpJlKFEs4e-0-42f1c645cb4a4f17ed84571db1cd6d39)
图1.4 感知器的训练误差与迭代次数的关系
如图1.5,可以看出经过100次的迭代优化,感知器的决策边界已经可以很好地将NAND数据分开。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P15_3541.jpg?sign=1738855214-zYkLz3QL9JgzebAAWTIKQFiWGCy5amh3-0-ff5717d6734dd0d37473dfc9b765d2da)
图1.5 上述的感知器在NAND数据的决策边界
那么这个感知器是否真的像理论所说的无法解决异或问题呢,我们对上述代码数据的类别改为异或问题:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_5675.jpg?sign=1738855214-9lZ8ebOH9I7lLxo6BkH8rGns1RDfzfJn-0-30c41d365de8e0d406dc1a3041e62a76)
我们仍然尝试感知器来解决异或问题,就会得到图1.6,可以看到训练误差虽然也随着迭代迅速减小,但并不会趋于零,而是趋近1,这代表着有一个数据是被错误分类的,而通过迭代来调节参数也无法完全正确分类。而决策边界的表现验证了这一点。
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_3548.jpg?sign=1738855214-QFxtvFOGNqMlCrECbPIqjwuwmiuMHhhW-0-7e1585f3f643a378acbda2074e755863)
图1.6 面对异或问题,(a)为感知器的决策边界,(b)为感知器的训练误差随迭代的变化
接着,我们来尝试使用多层感知器算法来解决异或问题,虽然我们已经定义好了一个类,给其增加隐层也会非常简单。但如果我们快速验证想法,可以使用sklearn中的多层感知器并简单调用它来解决异或问题,代码如下:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P16_5676.jpg?sign=1738855214-k5BZhFhScZdkzzmlcR07f8PEr8a3qdWf-0-e6889454eaa3f6a3d6c44b2a5cc25d1e)
其中,我们只是增加了一个隐层,将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=1738855214-FzuiiNKmyuaxqAt53PvLsCvmUxQASpQe-0-bbce9e0035ca0239a0ede85771cf00c5)
图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=1738855214-bgAfS74iIRVDW1fJJWzGvVFfbA9KYug5-0-64668f5edd7a6c0d3c4c97b4e6a783ee)
图1.8 多层感知器在异或问题上的决策边界
读者大概只需要花几分钟的时间就可以简单上手它。首先,一个神经网络必须有输入/输出,在keras中,我们要先定义好一个模型:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P18_5678.jpg?sign=1738855214-uCtCFAItH3qbNxRDEG2Hx9z8gzPMZ0qD-0-690ebfc1ee06824f213cf329c779e300)
在上段代码中,我们创建了一个输入有100维的模型,但注意到我们的输入和输出都是一样的,这意味着我们只是创建了一个没有任何用处的100个神经元,放在inputs层里。keras.layers类中提供了很多种层,我们接下来增加层,只需要:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P18_5679.jpg?sign=1738855214-y6zKvuLH2a8mGflnP5uaNHXjeRcrPa38-0-93d2ef6a663ded880bba17782a567597)
在上段代码中,我们使用Dense函数来得到全连接层。接下来,我们需要激活函数,需要激活函数来调整神经网络,我们使用了ReLU(我们会在第4章中讨论)作为激活函数,让其单独成为一层。并添加到原来的模型中:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5680.jpg?sign=1738855214-8aLWKctLTqTzt3QOHERnzZ325AHD2J7Z-0-acb71c05d0474139727e9364890e33e7)
通过这样的添加,我们已经获得了一个两层的神经网络,输出的值就为激活函数的处理之后的输出,准确地说,这只是一个输入为100维,输出为32维的感知机,我们要继续添加层使其能够处理非线性问题,紧接着,我们添加一个用于输出的层,并采用softmax函数作为激活函数,添加到上述模型中:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5681.jpg?sign=1738855214-U9euO7rADeiosGRwTAyUdI8ZjuoJP1JY-0-935a540b99957fb49f6e0b5d1760632b)
我们最终的模型就是处理100维特征的10分类数据。为了达到同样的效果,激活函数可以在层内事先指定好,就像我们上面做的那样,也可以把激活函数和层放在一起写作:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5682.jpg?sign=1738855214-9aHBO6jGYvbmlnbeCz7jzqSY8yvnbsRf-0-a39312f14607f214298f2c9d652aac56)
两种方法没有什么特别的区别。模型搭建完成后,我们完成了表示的任务.但在开始优化之前,我们需要初始化参数,keras提供了参数初始化类,我们在对一个网络进行优化的时候会使用到它,比如我们想进行正态分布的随机初始化:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5683.jpg?sign=1738855214-TKzQOlIipZ89FdeXQA5RAlNqYVpHO6qD-0-0225ddb057b8c4413a5ce47d266fa21e)
在上段代码中,我们设置好了一个均值为零,标准差为1。随机数种子为42的正态分布随机初始化器。在搭建网络中通过kernel_initializer传递初始化方法,并将上述的模型总结起来:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P19_5684.jpg?sign=1738855214-xFNfCi3Ws72HQfrqKtkltW2CCFqfUovu-0-2d0ef67261bb1b1e5c17e0b49644c5ce)
同时,我们需要知道损失函数、优化算法和评估标准,它们分别可以从以下代码获得:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5746.jpg?sign=1738855214-0mO4fmEYi8q3G81Q3gAVaUeIOwZQJJBI-0-debe7e99576771cf7f8a5f774fb87761)
其中,我们指定损失函数的交叉熵,优化算法为带有nesterov动量的随机梯度下降(我们会在第3章详细讨论),评估标准为准确率。就可以用损失函数、优化算法以及评估标准编译好我们的模型:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5747.jpg?sign=1738855214-sG6Iev8vleLBDDqHWdUzuqF3JdxruVSq-0-ca5b9439ec81a33ace79d43aa0bd95fd)
最后我们开始训练数据:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5748.jpg?sign=1738855214-7GtiP7kKEbbMuzNpeJvzSSlq9RqAZNQI-0-7f42af2b6fecbfc9187e986361a18ecf)
其中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=1738855214-0uh5gIQ2K4B1dA2zI29VtjcI4ElIX9Gf-0-d8b2563c8774e7548e95e2bcf13d9705)
而当我们在其他地方使用这个模型时,只需要:
![](https://epubservercos.yuewen.com/6D4BA3/23721527309452606/epubprivate/OEBPS/Images/Figure-P20_5750.jpg?sign=1738855214-tqsjpsjb64wuWYEoQSq203JtxfI9M3GF-0-8cea09d029c0e1b2940802ec4f0209a8)
以上就是Keras的基本使用方法,也是后面用神经网络来验证想法的主要工具。