感知机:教程,实现和可视示例(感知机定义)
ztj100 2024-11-11 15:14 19 浏览 0 评论
感知器是人工神经网络的组成部分,它是大脑中生物神经元的简化模型。感知器是最简单的神经网络,仅由一个神经元组成。感知器算法由Frank Rosenblatt于1958年发明。
以下是生物神经元的图示:
经由树突接收到神经元的大部分输入信号。其他神经元与这些树突形成约1,000至10,000个连接。来自连接的信号称为突触,通过树突传播到细胞体内。细胞体内的电位增加,一旦达到阈值,神经元就会沿着轴突发出一个尖峰,该轴突通过轴突末端连接到大约100个其他神经元。
感知器是真实神经元的简化模型,它尝试通过以下过程来模仿它:接收输入信号,将它们称为x1,x2,…,xn,计算这些输入的加权和z,然后将其传递阈值函数?并输出结果。
但将w0作为阈值和将w0作为偏置添加到和中并将阈值改为0是一样的。我们考虑一个始终设置为1的附加输入信号x0。
下面是一个感知器:
要使用向量表示法,我们可以将所有输入x0、x1、…、xn和所有权重w0、w1、…、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次多项式项来扩充它,会发生什么。幸运的是,这个问题可以通过使用核函数来避免。但这是另一篇文章的主题,我不想把这篇文章写得太长。
相关推荐
- 这个 JavaScript Api 已被废弃!请慎用!
-
在开发过程中,我们可能会不自觉地使用一些已经被标记为废弃的JavaScriptAPI。这些...
- JavaScript中10个“过时”的API,你的代码里还在用吗?
-
JavaScript作为一门不断发展的语言,其API也在持续进化。新的、更安全、更高效的API不断涌现,而一些旧的API则因为各种原因(如安全问题、性能瓶颈、设计缺陷或有了更好的替代品)被标记为“废...
- 几大开源免费的 JavaScript 富文本编辑器测评
-
MarkDown编辑器用的时间长了,发现发现富文本编辑器用起来是真的舒服。...
- 比较好的网页里面的 html 编辑器 推荐
-
如果您正在寻找嵌入到网页中的HTML编辑器,以便用户可以直接在网页上编辑HTML内容,以下是几个备受推荐的:CKEditor:CKEditor是一个功能强大的、开源的富文本编辑器,可以嵌入到...
- Luckysheet 实现excel多人在线协同编辑
-
前言前些天看到Luckysheet支持协同编辑Excel,正符合我们协同项目的一部分,故而想进一步完善协同文章,但是遇到了一下困难,特此做声明哈,若侵权,请联系我删除文章!若侵犯版权、个人隐私,请联系...
- 从 Element UI 源码的构建流程来看前端 UI 库设计
-
作者:前端森林转发链接:https://mp.weixin.qq.com/s/ziDMLDJcvx07aM6xoEyWHQ引言...
- 手把手教你如何用 Decorator 装饰你的 Typescript?「实践」
-
作者:Nealyang转发连接:https://mp.weixin.qq.com/s/PFgc8xD7gT40-9qXNTpk7A...
- 推荐五个优秀的富文本编辑器
-
富文本编辑器是一种可嵌入浏览器网页中,所见即所得的文本编辑器。对于许多从事前端开发的小伙伴来说并不算陌生,它的应用场景非常广泛,平时发个评论、写篇博客文章等都能见到它的身影。...
- 基于vue + element的后台管理系统解决方案
-
作者:林鑫转发链接:https://github.com/lin-xin前言该方案作为一套多功能的后台框架模板,适用于绝大部分的后台管理系统(WebManagementSystem)开发。基于v...
- 开源富文本编辑器Quill 2.0重磅发布
-
开源富文本编辑器Quill正式发布2.0版本。官方TypeScript声明...
- Python之Web开发框架学习 Django-表单处理
-
在Django中创建表单实际上类似于创建模型。同样,我们只需要从Django类继承,则类属性将是表单字段。让我们在myapp文件夹中添加一个forms.py文件以包含我们的应用程序表单。我们将创建一个...
- Django测试入门:打造坚实代码基础的钥匙
-
这一篇说一下django框架的自动化测试,...
- Django ORM vs SQLAlchemy:到底谁更香?从入门到上头的选择指南
-
阅读文章前辛苦您点下“关注”,方便讨论和分享,为了回馈您的支持,我将每日更新优质内容。...
- 超详细的Django 框架介绍,它来了!
-
时光荏苒,一晃小编的Tornado框架系列也结束了。这个框架虽然没有之前的FastAPI高流量,但是,它也是小编的心血呀。总共16篇博文,从入门到进阶,包含了框架的方方面面。虽然小编有些方面介绍得不是...
- 20《Nginx 入门教程》使用 Nginx 部署 Python 项目
-
今天的目标是完成一个PythonWeb项目的线上部署,我们使用最新的Django项目搭建一个简易的Web工程,然后基于Nginx服务部署该PythonWeb项目。1.前期准备...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)
- vmware17pro最新密钥 (34)
- mysql单表最大数据量 (35)