【机器学习】单层感知器
感知器介绍
感知机(英语:Perceptron)是Frank Rosenblatt在1957年就职于Cornell航空实验室(Cornell Aeronautical Laboratory)时所发明的一种人工神经网络。它可以被视为一种最简单形式的前馈式人工神经网络,是一种二元线性分类器。
Frank Rosenblatt给出了相应的感知机学习算法,常用的有感知机学习、最小二乘法和梯度下降法。譬如,感知机利用梯度下降法对损失函数进行极小化,求出可将训练数据进行线性划分的分离超平面,从而求得感知机模型。
在人工神经网络领域中,感知机也被指为单层的人工神经网络,以区别于较复杂的多层感知机(Multilayer Perceptron)。 作为一种线性分类器,(单层)感知机可说是最简单的前向人工神经网络形式。尽管结构简单,感知机能够学习并解决相当复杂的问题。感知机主要的本质缺陷是它不能处理线性不可分问题。
线性分类器的第一个迭代算法是1956年由Frank Rosenblatt提出的。这个算法被提出后,受到了很大的关注。感知器在神经网络发展的历史上占据着特殊的位置:它是第一个从算法上完整描述的神经网络。在20世纪60年代和70年代,受感知器的启发,工程师、物理学家以及数学家们纷纷投身于神经网络不同方面的研究。这个算法在今天看来依然是有效的。
感知器结构与算法步骤
设有n维(特征数)输入的单个感知机(如下图所示),X1至X2为n维输入向量的各个分量,W1至W2为各个输入分量连接到感知机的权量(或称权值),W0为偏置,激活函数(又曰激励函数或传递函数),Z为标量输出(也称为净输入)。
第一步
这里z称为净输入(net input),它的值等于一个样本的每个维度值x与维度对应的权重值w相乘后的和。
第二步
计算结果Z是一个连续的值,我们需要将结果转换为离散的分类值,因此,这里,我们使用一个转换函数,该函数称为激励函数(激活函数),这里θ就是阈值。
第三步
更新权重值
感知器是一个自学习算法,即可以根据输入的数据(样本),不断调整权重的更新,最终完成分类。对于权重的更新公式如下:
- η:学习速率(一个介于0.0到1.0之间的常数)
- y(i):是第i个样本的真实类标(即真实值)
- y^(i):是第i个样本的预测类标(预测值)。需要注意的是,权重向量中的所有权重值是同时更新的,这意味着在所有的权重 ΔwjΔwj 更新前,我们无法重新计算y^(i)。
- 这里的i和j怎么理解?每次计算z是同一个样本的维度值和权重值相乘之和,每次更新权重值会对每个权重值进行更新。
- 类标是什么?类标就是分类的标签,在这里类标就是1或者0。
更新原则:感知器的权重更新依据是:如果预测准确,则权重不进行更新,否则,增加权重,使其更趋向于正确的类别。
Python算法实现
1.对权重进行初始化。(初始化为0或者很小的数值。)
2.对训练集中每一个样本进行迭代,计算输出值y。
- 根据输出值y与真实值,更新权重。
- 循环步骤2。直到达到指定的次数(或者完全收敛)。
说明:
如果两个类别线性可分,则感知器一定会收敛。
如果两个类别线性不可分,则感知器一定不会收敛。
感知器收敛的前提是两个类别必须是线性可分的,且学习速率足够小。
如果两个类别无法通过一个线性决策边界进行划分,可以为模型在训练数据集上的学习迭代次数设置一个最大值, 或者设置一个允许错误分类样本数量的阈值,否则,感知器训练算法将永远不停的更新权值。
import pandas as pd import numpy as np import matplotlib.pyplot as plt class Perceptron(object): """Perceptron classifier. 参数: eta (学习率): float,取值范围0.0-1.0 n_iter(在训练集进行迭代的次数) : int random_state (随机数产生器的种子): int 属性: w_ (权重): ,np一维数组 errors_ (存储每轮训练集判断错误的次数): list """ def __init__(self, eta=0.01, n_iter=50, random_state=1): self.eta = eta self.n_iter = n_iter self.random_state = random_state def fit(self, X, y): """Fit training data. Parameters ---------- X : 二维np数组,形式:[[样本1维度值1,样本1维度值2...],[样本2维度值1,样本2维度值2...],...] y : 一维np数组,形式:[样本1的类标],样本2的类标,...] Returns ------- self : object """ #设置随机数种子 rgen = np.random.RandomState(self.random_state) #生成正态分布的随机数,权重w self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]) self.errors_ = [] for _ in range(self.n_iter): # 迭代所有样本,并根据感知器规则来更新权重 errors = 0 for xi, target in zip(X, y): # print(xi,target) update = self.eta * (target - self.predict(xi))
#更新权重值 self.w_[0] += update self.w_[1:] += update * xi #预测错误:update如果不为0,则表示判断错误 errors += int(update != 0.0) self.errors_.append(errors) return self #计算z的函数 def net_input(self, X): """Calculate net input""" # ϕ(z) = w0 * 1 + w1∗x1 + w2∗x2 + ... + wm∗xm z = self.w_[0] * 1 + np.dot(X, self.w_[1:]) return z #阈值函数 def predict(self, X): """Return class label after unit step""" return np.where(self.net_input(X) >= 0.0, 1, -1) df = pd.read_csv('https://archive.ics.uci.edu/ml/' 'machine-learning-databases/iris/iris.data', header=None) print(df.tail()) # select setosa and versicolor #选择0-100行的第5列数据 y = df.iloc[0:100, 4].values #0-100行中,选择第5列的列名为Iris-setosa的数据做处理,如果 y = np.where(y == 'Iris-setosa', -1, 1) # [1,1,1,1,1,...-1,-1,-1] # extract sepal length and petal length X = df.iloc[0:100, [0, 2]].values ppn = Perceptron(eta=0.1, n_iter=10) #训练数据 ppn.fit(X, y) #迭代次数与每次迭代时预测错误的次数作图 plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o') plt.xlabel('Epochs') plt.ylabel('Number of updates') plt.savefig('images/02_07.png', dpi=300) # plt.show()
怎么判断感知器是否收敛
import pandas as pd import numpy as np import matplotlib.pyplot as plt class Perceptron(object): """Perceptron classifier. 参数: eta (学习率): float,取值范围0.0-1.0 n_iter(在训练集进行迭代的次数) : int random_state (随机数产生器的种子): int 属性: w_ (权重): ,np一维数组 errors_ (存储每轮训练集判断错误的次数): list """ def __init__(self, eta=0.01, n_iter=50, random_state=1): self.eta = eta self.n_iter = n_iter self.random_state = random_state def fit(self, X, y): """Fit training data. Parameters ---------- X : 二维np数组,形式:[[样本1维度值1,样本1维度值2...],[样本2维度值1,样本2维度值2...],...] y : 一维np数组,形式:[样本1的类标],样本2的类标,...] Returns ------- self : object """ #设置随机数种子 rgen = np.random.RandomState(self.random_state) #生成正态分布的随机数,权重w self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]) self.errors_ = [] for _ in range(self.n_iter): # 迭代所有样本,并根据感知器规则来更新权重 errors = 0 for xi, target in zip(X, y): # print(xi,target) update = self.eta * (target - self.predict(xi)) self.w_[0] += update self.w_[1:] += update * xi #预测错误:update如果不为0,则表示判断错误 errors += int(update != 0.0) self.errors_.append(errors) return self #计算z的函数 def net_input(self, X): """Calculate net input""" # ϕ(z) = w0 * 1 + w1∗x1 + w2∗x2 + ... + wm∗xm z = self.w_[0] * 1 + np.dot(X, self.w_[1:]) return z #阈值函数 def predict(self, X): """Return class label after unit step""" return np.where(self.net_input(X) >= 0.0, 1, -1) df = pd.read_csv('https://archive.ics.uci.edu/ml/' 'machine-learning-databases/iris/iris.data', header=None) # print(df.tail()) # select setosa and versicolor y = df.iloc[0:100, 4].values y = np.where(y == 'Iris-setosa', -1, 1) # extract sepal length and petal length X = df.iloc[0:100, [0, 2]].values # plot data ppn = Perceptron(eta=0.1, n_iter=10) ppn.fit(X, y) plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o') plt.xlabel('Epochs') plt.ylabel('Number of updates') plt.savefig('images/02_07.png', dpi=300) # plt.show()
怎么判断两个类别是否线性可分
使用散点图显示两个类别的两个维度
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('https://archive.ics.uci.edu/ml/' 'machine-learning-databases/iris/iris.data', header=None) print(df.tail()) # extract sepal length and petal length X = df.iloc[0:100, [0, 2]].values # plot data plt.scatter(X[:50, 0], X[:50, 1], color='red', marker='o', label='setosa') plt.scatter(X[50:100, 0], X[50:100, 1], color='blue', marker='x', label='versicolor') plt.xlabel('sepal length [cm]') plt.ylabel('petal length [cm]') plt.legend(loc='upper left') # plt.savefig('images/02_06.png', dpi=300) plt.show()
参考:
https://blog.csdn.net/qq_42442369/article/details/87613450
https://blog.csdn.net/u012806787/article/details/80116098
https://blog.csdn.net/xylin1012/article/details/71931900
https://blog.csdn.net/yawdeep/article/details/78827088