前言:上一篇已经初步介绍了机器学习相关知识,简短介绍了机器学习的分类等等,本篇介绍其中监督学习中的分类领域下的感知器算法。
本篇将循序渐进的实现一个感知器,并且通过训练使其具备对鸢尾花数据集中数据进行分类的能力。
在详细讨论感知器和相关算法之前,先大体了解一下早期机器学习的起源。
为了理解大脑的工作原理以涉及人工智能系统,沃伦*麦卡洛克和沃尔特*皮茨在1943年
神经元是大脑中相互连接的神经细胞,他可以处理和传递化学和电信号。
麦卡洛克-皮茨将神经细胞描述为一个具备二进制输出的逻辑门。树突接收多个输入信号,如果累加的信号超过某一阈(yu)值,经细胞体的整合就会生成一个输出信号,
并通过轴突进行传递。
正是基于以上MCP模型,感知器学习法则被提出来。
MCP模型出现几年后,弗兰克*罗森布拉特提出了第一个感知器学习法则。
在此感知器规则中,罗森布拉特提出了一个自学习算法,此算法可以自动
在监督学习与分类中,类似算法可用于预测样本所属的类别。
更严谨的讲,我们可以把这个问题看作一个二值分类,为了简单起见,把两类分别记为1(正类别)和-1(父类别)。
定义一个激活函数(activation function),
它以特定的输入值x与相应的权值向量w的线性组合作为输入。
此时,对于一个特定样本 xi x i 的激活,如果其值大于预设的阈值a,我们将其划为1类,否则为-1类。在感知器算法中,激活函数(如下公式)
这里的输出值是指通过前面定义的单位阶跃函数预测得出的类标,而每次对权重向量中每一权重w的更新方式为:
对于用于更新权重的值可以通过感知器学习规则计算获得:
具体的,对于一个二维数据集,可通过下世进行更新:
以下介绍感知器的内核(推导过程),体验一下感知器规则的简洁之美。
解释:假定
被错误的分类到-1类别中,我们应更大幅度的移动决策边界,以保证下次遇到此样本时能正确分类。
注意:感知器收敛的前提是两个类别必须是线性可分的,且学习速率足够小。
如果两个类别无法通过一个线性决策边界进行划分,可以为模型在训练数据集上的学习迭代次数设置一个最大值,
或者设置一个允许错误分类样本数量的阈值,否则,感知器训练算法将永远不停的更新权值
上图说明了感知器如何接收样本x的输入,并将其与权值w进行加权以计算净输入(net_input),进而净输入被传递到激活函数(在此为单位阶跃函数),然后生成值为+1或者-1的二值输出,
并以其作为样本的预测类标。在学习阶段,此输出用来计算预测的误差并更新权重。
上述已经深入讲解感知器的规则,下面我们用代码实现它。
我们封装一个感知器类,对外提供训练和预测接口。
import numpy as np
class Perceptron(object):
"""
Perceptron:感知器
感知器收敛的前提是:两个类别必须是线性可分的,且学习速率必须足够小,否则感知器算法会永远不停的更新权值
"""
def __init__(self, eta=0.01, n_iter=10):
"""
初始化感知器对象
:param eta: float 学习速率
:param n_iter: int 在训练集进行迭代的次数
"""
self.eta = eta
self.n_iter = n_iter
def net_input(self, xi):
"""
计算净输入
:param xi: list[np.array] 一维数组数据集
:return: 计算向量的点积
向量点积的概念:
{1,2,3} * {4,5,6} = 1*4+2*5+3*6 = 32
description:
sum(i*j for i, j in zip(x, self.w_[1:])) python计算点积
"""
print(xi, end=" ")
print(self.w_[:], end=" ")
x_dot = np.dot(xi, self.w_[1:]) + self.w_[0]
print("的点积是:%d" % x_dot, end=" ")
return x_dot
""" 计算类标 """
def predict(self, xi):
"""
预测方法
:param xi: list[np.array] 一维数组数据集
:return:
"""
target_pred = np.where(self.net_input(xi) >= 0.0, 1, -1)
print("预测值:%d" % target_pred, end="; ")
return target_pred
def fit(self, x, y):
"""
学习、训练方法
:param x: list[np.array] 一维数组数据集
:param y: 被训练的数据集的实际结果
:return:
权值,初始化为一个零向量R的(m+1)次方,m代表数据集中纬度(特征)的数量
x.shape[1] = (100,2) 一百行2列:表示数据集中的列数即特征数
np.zeros(count) 将指定数量count初始化成元素均为0的数组 self.w_ = [ 0. 0. 0.]
"""
"""
按照python开发惯例,对于那些并非在初始化对象时创建但是又被对象中其他方法调用的属性,可以在后面添加一个下划线.
将权值初始化为一个零向量,x.shape[1] 是特征的维度数量,如鸢尾花数据 (150, 5) self.w_ = [0,0,0,0,0,0]
w_[0]是初始权重值0 w_[1:] 每次更新的权重值
"""
self.w_ = np.zeros(1 + x.shape[1])
print(self.w_)
# 收集每轮迭代过程中错误分类样本的数量,以便后续对感知器在训练中表现的好坏做出判定
self.errors_ = []
for _ in range(self.n_iter):
errors = 0
"""
迭代所有样本,并根据感知器规则来更新权重
"""
for x_element, target in zip(x, y):
""" 如果预测值(self.predict(x_element))和实际值(target)一致,则update为0 """
update = self.eta * (target - self.predict(x_element))
print("真实值:%d" % target)
self.w_[1:] += update * x_element
self.w_[0] += update
errors += int(update != 0.0)
self.errors_.append(errors)
return self