感知机:教程,实现和可视示例

做者|Dorian Lazar
编译|VK
来源|Towards Data Sciencepython

感知器是人工神经网络的组成部分,它是大脑中生物神经元的简化模型。感知器是最简单的神经网络,仅由一个神经元组成。感知器算法由Frank Rosenblatt于1958年发明。算法

如下是生物神经元的图示:数组

经由树突接收到神经元的大部分输入信号。其余神经元与这些树突造成约1,000至10,000个链接。来自链接的信号称为突触,经过树突传播到细胞体内。细胞体内的电位增长,一旦达到阈值,神经元就会沿着轴突发出一个尖峰,该轴突经过轴突末端链接到大约100个其余神经元。网络

感知器是真实神经元的简化模型,它尝试经过如下过程来模仿它:接收输入信号,将它们称为x1,x2,…,xn,计算这些输入的加权和z,而后将其传递阈值函数ϕ并输出结果。app

但将w0做为阈值和将w0做为偏置添加到和中并将阈值改成0是同样的。咱们考虑一个始终设置为1的附加输入信号x0。机器学习

下面是一个感知器:函数

要使用向量表示法,咱们能够将全部输入x0、x一、…、xn和全部权重w0、w一、…、wn放入向量x和w中,当它们的点积为正时输出1,不然输出-1。学习

如下是仅使用2个输入x1和x2的几何表示,以便咱们能够在2维中绘制:测试

如上所示,具备2个输入的感知器的决策边界是一条直线。若是有3个输入,则决策边界为二维平面。通常来讲,若是咱们有n个输入,决策边界将是一个称为n-1维的超平面,该超平面将咱们的n维特征空间分红两部分:一部分是将点分类为正的,另外一部分是将点分类为负的(按照惯例,咱们将认为刚好在决策边界上的点是负的)。所以,感知器是一个二元分类器,其权值是线性的。动画

在上面的图像中,w'表示没有偏移项w0的权重向量。w'垂直于决策边界并指向正分类点的性质。该向量决定了决策边界的斜率,而偏移项w0决定了决策边界沿w'轴的偏移。

到目前为止,咱们讨论了感知器如何根据输入信号及其权值作出决策。可是,感知者其实是如何学习的呢?如何找到合适的参数w0,w1,…,wn,以便进行良好的分类?

感知器算法是基于如下简单更新规则的迭代算法:

其中y是当前数据点x的标签(要么-1要么+1),w是权重向量。

咱们的更新规则怎么说?点积x⋅w只是感知器基于当前权重的预测(其符号与预测标签的符号相同)。表达式y(x⋅w)只能小于或等于0,前提是实际标签y不一样于预测标签φ(x⋅w)。所以,若是真标签和预测标签之间不匹配,那么咱们更新权重:w=w+yx;不然,咱们让它们保持原样。

那么,为何w=w+yx更新规则有效?由于它试图在if条件下将y(x⋅w)的值推向0的正边,从而正确地对x进行分类。若是数据集是线性可分的,经过对每一个点进行必定次数的迭代,权值最终会收敛到每一个点都被正确分类的状态。让咱们经过在更新后从新评估if条件来查看更新规则的效果:

也就是说,在特定数据点的权重更新以后,if条件中的表达式应该更接近于正数,从而正确分类。

伪代码中的完整感知器算法以下:

Python实现

咱们如今将在python中从头开始实现感知器算法,只使用numpy做为矩阵向量操做的外部库。咱们将把它做为一个类来实现,这个类的接口相似于Scikit-Learn这样的通用机器学习包中的其余分类器。咱们将为此类实现3个方法:.fit().predict().score()

.fit()方法将用于训练感知器。它指望第一个参数是2D numpy数组X。该数组的行是数据集的样本,列是特征。第二个参数y应该是1D的numpy数组,它包含X中每行数据的标签。第三个参数n_iter是咱们让算法运行的迭代次数。

def fit(self, X, y, n_iter=100):

    n_samples = X.shape[0]
    n_features = X.shape[1]

    # 偏置都加1
    self.weights = np.zeros((n_features+1,))

    X = np.concatenate([X, np.ones((n_samples, 1))], axis=1)

    for i in range(n_iter):
        for j in range(n_samples):
            if y[j]*np.dot(self.weights, X[j, :]) <= 0:
                self.weights += y[j]*X[j, :]

.predict()方法将用于预测新数据的标签。它首先检查weights对象属性是否存在,若是不存在,则表示感知器还没有训练,而后显示警告消息并返回。该方法须要一个与.fit()方法形状相同的参数X。而后咱们在X和权重之间作一个矩阵乘法,而后把它们映射到-1或+1。咱们使用np.vectorize()将此映射应用于矩阵乘法结果向量中的全部元素。

def predict(self, X):
    if not hasattr(self, 'weights'):
        print('The model is not trained yet!')
        return

    n_samples = X.shape[0]
    X = np.concatenate([X, np.ones((n_samples, 1))], axis=1)
    y = np.matmul(X, self.weights)
    y = np.vectorize(lambda val: 1 if val > 0 else -1)(y)

    return y

score()方法计算并返回预测的准确性。它指望输入矩阵X和标签向量y做为参数。

def score(self, X, y):
    pred_y = self.predict(X)

    return np.mean(y == pred_y)

几个例子

我如今要作的是展现几个可视化的例子,说明决策边界是如何收敛到一个解的。

为了作到这一点,我将使用ScikitLearn的datasets.make_classification()datasets.make_circles()函数建立几个由200个样本组成的2特征分类数据集。这是用于建立下两个数据集的代码:

X, y = make_classification(
    n_features=2,
    n_classes=2,
    n_samples=200,
    n_redundant=0,
    n_clusters_per_class=1
)

还有一个数据集:

X, y = make_circles(n_samples=200, noise=0.03, factor=0.7)

对于每一个示例,我将把数据分红150个用于训练,50个用于测试。左边显示训练集,右边显示测试集。当决策边界收敛到一个解决方案时,决策边界将在两边显示。可是决策边界将仅根据左边的数据(训练集)进行更新。

例1 线性可分的

我要展现的第一个数据集是线性可分的。下面是完整数据集的图像:

这是一个简单的数据集,咱们的感知器算法在通过训练集的两次迭代后就会收敛到一个解。所以,每一个数据点的动画帧都会改变。绿点是目前在算法中测试的那个。

在该数据集上,算法对训练样本和测试样本进行了正确分类。

例2 噪声数据集

若是数据集不是线性可分的呢?若是正反两个例子像下图同样混淆在一块儿呢?

好吧,感知器算法将不能正确分类全部的例子,但它将试图找到一条线,最好的分开他们。在这个例子中,咱们的感知器获得了88%的测试精度。下面的动画帧在每次迭代后都会经过全部训练示例进行更新。

例3 非线性数据集

下面的数据集如何呢?

它是可分离的,但显然不是线性的。因此你可能会认为一个感知器不适合这个任务。但感知器的问题是,它的决策边界是线性的,就权重而言,不必定就输入而言。咱们能够扩充输入向量x,使其包含原始输入的非线性函数。例如,除了原始输入x1和x2以外,咱们还能够将项x1平方、x1乘以x2和x2平方相加。

下面的 polynomial_features(X, p)(X,p)函数可以将输入矩阵X转换成一个矩阵,该矩阵包含p次多项式的全部项做为特征。它使用polynom()函数计算表示要相乘列的索引列表,以得到p阶项。

def polynom(indices_list, indices, a, b, p):
    indices = [*indices]
    if p == 0:
        indices_list.append(indices)
        return
    for i in range(a, b):
        indices.append(i)
        polynom(indices_list, indices, i, b, p-1)
        indices = indices[0:-1]

def polynomial_features(X, p):
    n, d = X.shape
    features = []
    for i in range(1, p+1):
        l = []
        polynom(l, [], 0, d, i)
        for indices in l:
            x = np.ones((n,))
            for idx in indices:
                x = x * X[:, idx]
            features.append(x)
    return np.stack(features, axis=1)

在咱们的例子中,咱们将在X矩阵中添加2级项做为新特征。

X = polynomial_features(X, 2)

如今,让咱们看看使用这个转换后的数据集进行训练时会发生什么:

注意,对于绘图,咱们只使用原始输入来保持它的二维性。决策边界在扩展特征空间中仍然是线性的,如今是5D。可是,当咱们绘制投影到原始特征空间的决策边界时,它具备非线性形状。

经过这种方法,咱们的感知器算法可以在不修改算法自己的状况下正确地分类训练和测试实例。咱们只改变了数据集。

经过这种特征加强方法,咱们可使用线性算法在数据中建模很是复杂的模式。

可是,这种方法不是颇有效。想象一下,若是咱们有1000个输入特征,而且咱们想用最多10次多项式项来扩充它,会发生什么。幸运的是,这个问题能够经过使用核函数来避免。但这是另外一篇文章的主题,我不想把这篇文章写得太长。

原文连接:https://towardsdatascience.com/perceptron-explanation-implementation-and-a-visual-example-3c8e76b4e2d1

欢迎关注磐创AI博客站:
http://panchuang.net/

sklearn机器学习中文官方文档:
http://sklearn123.com/

欢迎关注磐创博客资源汇总站:
http://docs.panchuang.net/

相关文章
相关标签/搜索