Python:感知器 (五十七)

感知器技巧 - 计算机如何“学习”分类?

在上一部分,你使用你自己的逻辑和数学知识为某些最常见的逻辑运算符创建了感知器。 但是在现实生活中,除了这些非常简单的形式,我们人类是无法靠自己构建这些感知器函数,找到用于分类的曲线的。

下面的视频将告诉你,计算机如何根据我们人类给出的结果,来自己进行构建感知器函数。对于这一点,有一个非常棒的技巧能帮到我们。

如何然直线离点更靠近?

file

file

file

整个数据集中的每一个点都会把分类的结果提供给感知器(分类函数),并调整感知器。——这就是计算机在神经网络算法中,找寻最优感知器的原理。

感知器算法

掌握了感知器技巧后,我们就可以编写完整的感知器运算的算法了!

下面的视频将介绍感知器算法的伪代码,现在你还不需要担心什么是学习速率(learning rate),我们在之后的课程中会详细介绍为什么这里的伪代码中有学习率。

file

编写感知器算法

该编写代码了!在此练习中,你将实现感知器算法以分类下面的数据(位于文件 data.csv 中)。

file

感知器步骤如下所示。对于坐标轴为 (p,q) 的点,标签 y,以及等式\( \hat{y} = step(w_1x_1 + w_2x_2 + b) \)
给出的预测

  • 如果点分类正确,则什么也不做。
  • 如果点分类为正,但是标签为负,则分别减去 αp,αq, 和 α 至 \(w_1, w_2\) , 和 b
  • 如果点分类为负,但是标签为正,则分别将 αp,αq, 和 α 加到 w_1, w_2, w , 和 b 上。

然后点击测试运行绘出感知器算法给出的解决方案。它实际上会画出一组虚线,显示算法如何接近最佳解决方案(用黑色实线表示)。

请随意改动算法的参数(epoch 数量、学习速率,甚至随机化初始参数),看看初始条件对解决方案有何影响!

import numpy as np
# Setting the random seed, feel free to change it and see different solutions.
np.random.seed(42)

def stepFunction(t):
    if t >= 0:
        return 1
    return 0

def prediction(X, W, b):
    return stepFunction((np.matmul(X,W)+b)[0])

# TODO: Fill in the code below to implement the perceptron trick.
# The function should receive as inputs the data X, the labels y,
# the weights W (as an array), and the bias b,
# update the weights and bias W, b, according to the perceptron algorithm,
# and return W and b.
def perceptronStep(X, y, W, b, learn_rate = 0.01):
    for i in range(len(X)):
        y_hat = prediction(X[i],W,b)
        if y[i]-y_hat == 1:
            W[0] += X[i][0]*learn_rate
            W[1] += X[i][1]*learn_rate
            b += learn_rate
        elif y[i]-y_hat == -1:
            W[0] -= X[i][0]*learn_rate
            W[1] -= X[i][1]*learn_rate
            b -= learn_rate
    return W, b

# This function runs the perceptron algorithm repeatedly on the dataset,
# and returns a few of the boundary lines obtained in the iterations,
# for plotting purposes.
# Feel free to play with the learning rate and the num_epochs,
# and see your results plotted below.
def trainPerceptronAlgorithm(X, y, learn_rate = 0.01, num_epochs = 25):
    x_min, x_max = min(X.T[0]), max(X.T[0])
    y_min, y_max = min(X.T[1]), max(X.T[1])
    W = np.array(np.random.rand(2,1))
    b = np.random.rand(1)[0] + x_max
    # These are the solution lines that get plotted below.
    boundary_lines = []
    for i in range(num_epochs):
        # In each epoch, we apply the perceptron step.
        W, b = perceptronStep(X, y, W, b, learn_rate)
        boundary_lines.append((-W[0]/W[1], -b/W[1]))
    return boundary_lines

非线性界线

我们再看看这个录取或拒绝学生的模型

file

我们可以用曲线,圆,两条曲线来分隔红点和蓝点。

误差函数

刚刚的感知器算法实现告诉我们,获取正确分类的方式,就是通过每一个错误分类的点,评估错误点位置与我们期望位置之间的差异,来慢慢的修正我们分类函数。

因为误差暗示了如何进行正确的分类,因此误差的定义就变得尤为重要,这也被称为误差函数(error function)

误差函数可以告诉我们当前与正确答案之间的差别有多大
An error function is simply something that tells us how far we are from the solution.

误差函数与梯度下降

误差函数提供给我们的预测值与实际值之间的差异,但是这个差异如何指导我们权重的更新呢?我们的目标是找到最小的误差函数值来找到与实际值误差最小的预测值。

在简单的线性方程中,我们可以通过判断“预测值与实测值相比是大了还是小了”来决定权重是增加还是减少。但是在更为复杂的非线性环境中呢?复杂的数学问题,我们就直接来看看学者们的解决策略。

假设一维问题是一条直线,那么二维问题就是一个平面,而三维问题就是一个曲面。曲面可以理解为有山峰也有低谷的地面,误差最小的地方就是低谷处,我们希望计算机找到的就是这个低谷的值。为了找到这个低谷,学者们发明了梯度下降

我们的误差函数不能是离散(discrete)的,必须是连续的,误差之巅的高度是连续函数,因为位置上的轻微扰动会导致高度发生变化。实际上误差函数必须是可微分的。

file

我们要做的是构建一个连续误差函数,构建方法如下,这是六个点,其中四个分类正确,即两个蓝点和两个红点,另外两个分类错误,即最左侧的一个红点和最右侧的一个蓝点,误差函数将向这两个分类错误的点分配大的阀值,并为四个分配正确的点分配小的阀值,我们将阀值用点的大小表示,当点分类错误时,阀值约等于点离直线的距离,当点分类正确时,则约为 0, 稍后我们将学习这一误差公式。

file

Gradient Descent(梯度下降)

我们位于误差之巅,我们站的非常高,误差很大,可以看出误差是高度,即蓝点和红点之和,我们朝四周看去,看看哪个方向可以使我们下降最多,也就是说朝那个方向移动直线可以使误差降低最多。
然后朝着这个方向向前进一步,我们正确分类其中一个点,让误差减小了一些,然后再重复计算误差。

file

为者常成,行者常至