
2.1 感知器
和我们想象的一样,感知器的概念受到生物神经元启发,其主要功能是决定阻止或允许信号通过。神经元接收一组由电信号创建的二进制输入。如果总信号超过某个阈值,则神经元将触发输出。
感知器的工作原理也是如此,详见图2-1。
它可以接收多个输入,将该输入乘以一组权重。然后,加权信号的总和将通过一个激活函数(这里为一个阶跃函数)。如果总信号大于某个阈值,则感知器将让信号通过或不让信号通过。我们可以用以下公式来进行数学表示:

图 2-1

这就是神经元的数学模型,表示为显式求和与矩阵运算。WTx是公式的向量化表示,其中W是权重矩阵,该矩阵首先被转置,然后乘以输入向量x。
为了得到完整的数学描述,我们应该添加一个常数b,称为偏置项:

现在,我们有了线性方程的通用表达式,这就是阶跃函数之前的整个过程。
接下来,输入和权重z的线性组合通过激活函数,该函数将确定感知器是否会让信号通过。
最简单的激活函数就是阶跃函数。神经元的输出可以通过阶跃函数来近似,该阶跃函数可以用以下方程式表示:

图2-2是其可视化表示。

图2-2 阶跃函数
激活函数的类型很多,我们会在后面详细讲解。
实现感知器
现在,我们来看一下如何从头开始构建感知器,以确保我们理解这些概念,因为我们将使用它们来构建复杂网络。
单层感知器只能学习线性可分的模式。学习部分就是找到使输出误差最小的权重的过程。
首先,让我们创建一个数据集。为此,我们将从创建的两个不同的正态分布中采样,并根据分布对数据进行标记。之后,我们将训练感知器来区分它们:

为了将上面的代码可视化,我们可以在设置了%matplotlib inline选项的Jupyter Notebook中运行,得到图2-3。

图2-3 通过不同的颜色标记两种分布(彩图见本书下载文件)
如图2-3所示,两个分布是线性可分的,所以这个任务很适合我们的模型。
现在,让我们创建一个简单的类来实现这个感知器。我们知道有两个输入数据(即图中的两个坐标)和一个二进制输出(即数据点的类型),数据点的类型用不同的颜色区分:

我们需要两个权重,每个输入各一个权重,再加上一个额外的偏置项。我们将偏置项表示为始终接收等于1的输入的权重。这样更容易进行优化。
现在,需要将计算预测的方法添加到我们的类中,即实现数学公式的部分。当然,在一开始,我们不知道权重是多少(这实际上就是我们训练模型的原因),但是我们需要一些值来开始,因此我们将它们初始化为任意值。
我们将使用阶跃函数作为这个人工神经元的激活函数,它将作为决定信号是否通过的过滤器:

然后将输入乘以权重并求和,因此我们需要实现一个方法来接收两个输入并返回其加权和。偏置项由self.w0项表示,该值始终乘以1:

现在,我们需要实现predict函数,它会使用前面的代码块中定义的函数来计算神经元的输出:

在本书后面你会发现最好选择易于求导的激活函数,因为通过梯度下降能够更便捷地训练网络。
计算权重的训练阶段是一个简单的过程,可通过以下拟合方法实现。我们需要为该方法提供输入、输出和另外两个参数(epoch数和步长)。
一个epoch是模型训练的一个阶段,每次当所有训练样本都用于更新权重后,一个epoch就结束了。对于DNN,通常需要训练数百甚至更多个epoch,但是在我们的示例中,一个epoch就够了。
步长(或学习率)是一个用来控制更新对当前权重的效应的参数。感知器收敛定理表明,如果类是线性可分的,则感知器将收敛,而与学习率无关。此外,对于神经网络,学习率非常重要。使用梯度下降时,它决定了收敛速度,并可能会决定你能够获得的误差函数的值与最小值的接近程度。较大的步长可能会使训练在局部最小值附近跳跃,而较小的步长会使训练太慢。
在下面的代码块中,可以找到我们需要添加到感知器的类中进行训练的方法代码:


训练过程通过将步长(或学习率)乘以实际输出与预测之间的差来计算权重更新。然后将此加权误差乘以每个输入,并加到相应的权重上。这种简单的更新策略让我们可以将区域分为两部分从而对数据进行分类。这种学习策略称为感知器学习规则(Perceptron Learning Rule,PLR),并且可以证明,如果问题是线性可分的,则感知器学习规则将可以在有限的迭代中找到一组权重解决这个问题。
我们还添加了一些错误日志功能,因此可以用更多的epoch来进行测试,并查看错误是如何受到影响的。
现在学完了感知器类。我们需要创建训练集和测试集来训练网络并验证其结果。最佳做法是再加上一个验证集,但在此示例中,我们将跳过它,专注于训练过程。使用交叉验证也是一种好习惯,但是为了简单起见,我们也将跳过,只使用一个训练集和一个测试集:

现在已经准备好了训练所需的一切,我们将权重初始化为接近零的数字并执行训练:

为了检查算法的性能,我们可以使用混淆矩阵,显示所有正确预测和错误分类。由于这是一项二元分类任务,因此结果将有三种可能:真阳性、假阳性或假阴性:

上述程序代码会产生下列输出。

我们可以通过在输入空间绘制线性决策边界可视化这些结果。为此,需要在感知器类中添加下述代码:

要找到边界,我们需要找到满足下列等式的点:

现在可以使用以下代码绘制决策线和数据:

结果如图2-4所示。
除了二元分类,还可以计算连续输出,只需要使用连续激活函数(例如逻辑函数)就可以了。通过这种方法,我们的感知器变成了逻辑回归模型。

图 2-4