深度学习从理论到实践(2)—感知机(Perceptron)

536 阅读6分钟

今天要介绍的感知机是人类最早的人工智能模型,大概是 60、70 年前的模型了。 数十亿的神经元。神经元是人脑中相互连接的神经细胞,参与处理和传输化学和电信号。树突是接收其他神经元信息的分支。

细胞核或细胞体处理从树突收到的信号,轴突看做神经元用来发送信号的电缆。突触用于连接轴突和其他神经元树突。

001.png

受到

一个人的大脑有神经元启发 研究人员沃伦-麦卡洛克和沃尔特-皮茨在 1943 年发表了简化脑细胞的概念。这被称为麦卡洛克-皮茨(MCP)神经元。他们将这样的神经细胞描述为一个具有二进制输出的简单逻辑门。

多个信号到达树突,然后被整合到细胞体中,如果累积的信号超过某个阈值,就会产生一个输出信号,由轴突传递出去。

人工神经元

人工神经元是一个基于生物神经元模型的数学函数,每个神经元接受输入,分别对其进行称重,并将其相加,通过一个非线性函数来产生输出。

  • 神经元是在生物神经元上进行建模的数学模型
  • 人工神经网络的中基本单元
  • 各个输入之间的权重是相对独立的
  • 对于输入进行加权求和后,经过非线性激活函数作为输出
  • 每个神经元内部具有一个内部状态和激活信号
  • 每个神经元都可以接收到输入
  • 两层上神经元彼此连接,同层上神经元之间不存在连接

感知机

感知机Perceptron 是由 Frank Rosenblatt 在1957年提出的。他在最初的MCP神经元的基础上提出了一个Perceptron学习规则。感知机(Perceptron)属于监督学习,是二分类器的算法。这种算法使神经元能够一次学习和处理训练集中的元素,那么这里定义感知机是一种算法。

感知机的分类

  • 单层感知机: 单层感知器只能解决线性可分离的问题
  • 多层感知机: 多层感知器具有两层或多层的前馈神经网络具,因此可以解决非线性这样更复杂的问题

感知机主要学习到一个分类边界,那么监督学习是机器学习一个类别,通过学习带有标注的数据来学习出一个可未知数据进行预测的模型,接下来一段时间我们都会讨论属于监督学习的模型

感知机学习策略

感知器该算法将自动学习到最优的权重系数,然后,输入特征与这些权重相乘,以确定一个神经元是否启动。

感知器接收多个输入信号,输入信号加权求和超过某个阈值,就输出这个信号,否则不输出这个信号。在监督学习和分类的背景下,这就可以用来预测一个样本的类别。

感知机函数

003.png

y=1ifi=0nwixi0y=0ifi=0nwixi<0y=1 \, if \, \sum_{i=0}^n w_i x_i \ge 0\\ y=0 \, if \, \sum_{i=0}^n w_i x_i < 0

感知机输入是一个向量,权重也是一个向量而偏置 b 是一个标量,输出也是一个标量。

感知机激活函数

感知机解决 OR 问题

005.png

感知机计算过程

关于感知机会用到数据基础知识放在后面,如果大家需要可以去简单浏览一下。 接下来我们来解释一下整个计算过程,我们将样本数据集按 label=0 和 label=1 (这里 1 和 0 代表两个不同分类)划分为 P 和 N,这里 P 表示正例样本(label=1)相反 N 表示负例样本(label=0) 那么数据集可以表示为 PNP \cup N

我们首先随机生成一个权重 w,然后开始循环,每次循环会随机从 PNP \cup N 抽取一个样本,然后带入 wxwx

  • 如果这个样本是从 P 中抽取,而 wx<0wx < 0w=w+xw = w + x 结束循环再重新开始
  • 如果这个样本是从 N 中抽取,而 wx0wx \ge 0w=wxw = w - x 同样结束循环再从新开始

感知机训练停止条件是,感知机看过所有数据后,都能把所有数据正确分类才会停止,这是一个现在看来比较奇怪的终止条件了。

另一个角度来看感知计算过程

这里我们从另一个角度来考虑,例如我们假设 yi(1,1)y_i \in (1,-1) 那么如果 yi[w,xi+b]0y_i[w,x_i + b] \le 0 表示分类错误这更新参数 ww+yixiw \leftarrow w + y_ix_ibb+yjb \leftarrow b + y_j

可以采用梯度下降方法来优化参数,那么对于梯度下降来说他损失函数又是什么呢?他损失函数就是下面的样子,批量大小为 1 的梯度下降。

l(y,x,w)=max(0,y(w,x))l(y,x,w) = \max(0,-y(w,x))

感知机的局限性

通常对于感知机能够处理问题都是线性可分的,那么对于 XOR 问题,感知机也显得束手无措。

x1x_1x2x_2yy
101
011
110
110

006.png

Minsky 在 1969 于感知机无法解决 XOR 问题,导致大家认为这么复杂的模型却无法解决这么简单问题,这直接导致感知机进入第一个寒冬。

008.jpeg

感知机的收敛

数据在半径为 rr 区域内,余量 ρ\rho 分类二分类 y(xTw+b)ρy(x^Tw + b) \ge \rho 分类正确且有一些余量,对于 w2+b21||w||^2 + b^2 \le 1 感知机保证在 r2+1ρ2\frac{r^2 + 1}{\rho^2} 步后收敛。

代码实现

import numpy as np
from sklearn.datasets import make_classification
%pylab inline

这里生成数据集为

separable = False
while not separable:
    samples = make_classification(n_samples=100, n_features=2, n_redundant=0, n_informative=1, n_clusters_per_class=1, flip_y=-1)
    positive = samples[0][samples[1] == 0]
    negative = samples[0][samples[1] == 1]
    separable = any([positive[:, k].max() < negative[:, k].min() or positive[:, k].min() > negative[:, k].max() for k in range(2)])
plot(positive[:, 0], positive[:, 1], 'r.')
plot(negative[:, 0], negative[:, 1], 'b.')
len(negative)#500

正例和负例样本个 50 个,接下来我们要做的工作就是将这些数据变为 [x1,x2,y][x_1,x_2,y] 这种形式然后再进行一次打乱排序。

positive_dataset = np.ones(shape=(len(positive),3))
positive_dataset[:,:2] = positive

negative_dataset = np.zeros(shape=(len(negative),3))
negative_dataset[:,:2] = negative

dataset = np.concatenate((positive_dataset, negative_dataset), axis=0)
np.random.shuffle(dataset)
print(dataset[:10])
[[-0.23285037 -0.84531416 1. ] 
[-1.55552102 1.00510574 0. ] 
[-0.05428806 -0.88247394 1. ]
[-0.68219944 1.00128174 0. ] 
[ 1.86808647 -0.61659097 1. ]
[ 0.0335378 -0.66812554 1. ]
[ 0.03028771 1.00533141 0. ] 
[ 1.49430288 1.00160369 0. ] 
[ 0.28867377 1.00009757 0. ] 
[-0.23721778 0.99877196 0. ]]

def predict(sample, weights):
    activation = weights[0]
    for i in range(len(sample)-1):
            activation += weights[i + 1] * sample[i]
    return 1.0 if activation >= 0.0 else 0.0

activation 激活值,这里好处是包括偏置值在内 activation=wixi+activationactivation = w_ix_i + activation

def train_weights(train, l_rate, n_epoch):
    weights = [0.0 for i in range(len(train[0]))]
    for epoch in range(n_epoch):
            sum_error = 0.0
            for row in train:
                prediction = predict(row, weights)
                error = row[-1] - prediction
                sum_error += error**2
                weights[0] = weights[0] + l_rate * error
                for i in range(len(row)-1):
                    weights[i + 1] = weights[i + 1] + l_rate * error * row[i]
            print('>epoch=%d, lrate=%.3f, error=%.3f' % (epoch, l_rate, sum_error))
    return weights
    

weights

l_rate = 0.1
n_epoch = 5
weights = train_weights(dataset, l_rate, n_epoch)
print(weights)
>epoch=0, lrate=0.100, error=2.000 >epoch=1, lrate=0.100, error=0.000 >epoch=2, lrate=0.100, error=0.000 >epoch=3, lrate=0.100, error=0.000 >epoch=4, lrate=0.100, error=0.000 [0.0, -0.20158537856854422, 0.040592962030502784]
weights = [0.0, -0.20158537856854422, 0.040592962030502784]
for row in dataset[:10]:
    prediction = predict(row, weights)
    print("Expected=%d, Predicted=%d" % (row[-1], prediction))
Expected=1, Predicted=1 
Expected=0, Predicted=0 
Expected=1, Predicted=1 
Expected=1, Predicted=1 
Expected=1, Predicted=1 
Expected=1, Predicted=1 
Expected=1, Predicted=1 
Expected=0, Predicted=0 
Expected=0, Predicted=0 
Expected=1, Predicted=1

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿