感知器(PLA——Perceptron Learning Algorithm),也叫感知机,处理的是机器学习中的分类问题,通过学习得到感知器模型来对新实例进行预测,因此属于判别模型。感知器于1957年提出,是神经网络的基础。

模型

  以最简单的二分类为例,假设医院需要根据肿瘤患者的病患特征(x1肿瘤大小,x2肿瘤颜色),判断肿瘤是良性(+1)还是恶性(-1),那么所有数据集都可以在一个二维空间表示;如果能找到一条直线将所有1和-1分开,这个数据集就是线性可分的,否则就是线性不可分。将两个特征向量分别用x1, x2表示,θ是权重,分隔直线的函数是:

  也许用初中的知识更容易说明,常见的直线函数:

  感知器的假设函数(hypothesis function) hθ(x):

  θ称为感知器的模型参数。需要注意的是,上图中左右两侧的g(x)不是直线,它们的x1和x2都是具体的特征值,是一个具体的实数数值;直线是g(x) = θ0 + θ1x1 + θ2x2 = 0,它的x1和x2才是变量。

  我们需要做的就是根据训练集中的样本数据计算出恰当的θ,以使得直线f(x) 最终能够正确地分开数据集中的所有1和-1(对于线性不可分问题是找到最好的直线以划分尽可能多的1和-1)。

  感知器也可以扩展到多维数据,比如判断肿瘤时除了大小和颜色外还有发病时间、以前是否有过类似肿瘤等。对于三维空间,最终将找到一个平面;对于更多维的空间,最终将找到一个超平面。超平面是一个概念,可以将多维数据的正负例一分为二,但很难在平面媒体中呈现。

  现在,对于n维空间的一个样本,xn表示一个样本中的第n个特征向量,θn表示xn对应的权重,感知器的假设函数hθ(x):

  如果g(x)写成下面形式,则g(x)可以看作多维空间的“直线”:

  也可以写成矩阵的形式:

  后续文章将混用这两种表达。

学习策略

  我们的最终目标是找到最佳的θ,这就需要制定一个学习策略,即定义一个用来计算误分类的损失函数,使损失函数极小化,从而通过优化的手段求解θ。

  现在设J(θ)是损失函数,第一感觉很自然会将误分类数量作为损失函数:

  上式表示在样本集中对于特定的感知器参数,共有k个样本被误判,上标 i 表示误分类集中的第 i 个样本。然而这样做存在问题,该函数的结果可能是0,1,2,3……,是离散而非连续数据;因为J(θ)不是连续的,不连续的函数必定不可导,不可导的函数也不易优化,所以没有办法根据这样的J(θ) 制定算法。(关于导数和利用导数求极值的问题,可参考《单变量微积分》中的相关章节)。

  下面的图片由其他网友提供,忘记了来源,它很形象地说明了连续和可导的关系:

  现在需要将J(θ) 变成连续函数,以分类点到直线超平面的总距离作为损失函数,每个误分类点到超平面的距离是:

  关于点到平面的距离可参考《线性代数笔记5——平面方程与矩阵》。

  由于是误分类,所有正确结果是1的被判定为-1,-1的被判定为1,用y表示正确的分类结果(实际结果),可以用下面的方法将绝对值符号去掉:

  损失函数也就是所有误分类点的总距离:

  将上式中的S表示几何间隔,是点到平面距离。在感知机中,使用函数间隔(也就是去掉S的二阶范数)来近似地表示距离,从而简化问题:

  损失函数是非负的,如果没有误分类点,损失函数为0;误分类点越少,误分类点离超平面越近,损失函数越小,J(θ) 是关于θ 的连续可导函数。

  现在有一个问题,如下图所示:

在两条分隔线中,f1分错了一个样本,f2分错了两个,f1的准确率高于f2,但是根据损失函数,f2的误分类点距离之和小于f1,这样算法就会判断f2是最终结果。为了应对这类问题,喂给模型的训练数据需要经过一定的过滤从而去掉其中的噪声,就想我们预测房价时需要过滤掉一些明显的内部价格。

算法

原始形式

现在感知器学习问题转换为求解损失函数的最优化问题,也就是在训练样本线性不可分的情况下,通过优化算法使得对于全体训练样本来说J(θ) 最小。

使用随机梯度下降法求解模型参数θ(梯度下降可参考《ML(附录1)——梯度下降》),选择一个误分类样本(x(j), y(j)),初始的θ可以全部设置为0:

  反复迭代,每次随机选取第j个样本,达到阈值或迭代一定次数后停止迭代。需要注意的是,每次迭代后都将产生新的模型,该模型会得到新的误分类集,新一轮迭代选取的样本都是从新的误分类集合中选取的。

   对于任意θ,用矩阵的写法:

  所以感知器的原始形态可化简为:

  这种算法被称为感知器学习算法的原始形态,其基本思想是:当一个实例点被误分类,即位于分类超平面错误的一侧时,则调整θ,使分类超平面向该误分类点的一侧移动,以减少该误分类点与超平面的距离,直到超平面越过该误分类点使其被正确分类为止。这种感知机学习算法得到的模型参数不是唯一的,它会由于采用不同的参数初始值或选取不同的误分类点,而导致解不同。

  二分类训练集x中的每个样本都是n维向量,其分类结果是y = ±1,现在利用PLA训练数据,得到模型hθ(x) = sign(g(x)),原始形态的求解步骤如下:

  1)    初始化α、θ,令θ是0向量

  2)    利用hθ(x)从训练集x中选择误分类数据,形成误分类集errDatas

  3)    从errDatas中随机选取一个误分类样本x(j),用该样本重新计算θ: θ = θ + αy(j)x(j)

  4)    使用新θ,利用sign(hθ(x))检测是否还有误分类点,如果没有,计算结束,否则跳转到2;或判断是否达到指定阈值(或循环次数),如果是,计算结束,否则跳转到2

对偶形式

  简单的理解,可以将感知器的对偶形态看作感知器的另一种学习算法,是对原始形态的优化。

  在原始形态中:

  我们每次都随机选取误分类集中的第j个样本进行θ的迭代。如果这个被选中的样本x(j)在原始训练集中的序号是i,那么对于每次迭代能够建立这样的映射:

  i对应原始训练集的样本序号,j对误分类集的样本序号。需要注意的是,由于误分类集在每次迭代后都有所变化,但是误分类样本在原始训练集中的序号是不变的。比如对于原始集的样本x(5),i = 5,在其中一次迭代后被判做误分类,此时它是误分类集中的第4条数据,j = 4;在另一次迭代后仍然是误分类,此时变成了误分类集中第2条数据,j = 2;随后被分类正确,不在误分类集中。

  由此我们看到,对于多次被误分类的原始集中的第i号样本(靠近分隔线的样本),设它被选中(因为使用随机梯度下降)参与迭代的次数是mi,对于一个从未被误分的样本(远离分隔线的样本),mi = 0。对于每次迭代,参与计算的原始集的x(i)对应的mi += 1。如果令θ是0向量,对于含有n个样本的训练集:

  如果令βi = miα,则:

  现在,在误分类集中对于第j个误分类的判断变成了:

  注意JudgeFun的点积,如果我们事先用矩阵运算计出所有的样本之间的点积,那么在算法运行时就可以直接得到结果,这就使算法节省了计算g(x)的时间,也是对偶形态的关键。样本的点积矩阵称为Gram矩阵,记作 G = [x(i)∙x(j)]。

  为了便于理解,上式中j表示误分类集的第j个数据,i表示原始集的第i个数据。刚才我撒了谎,实际上Gram矩阵需要知道的是j在原始集中的位置k,即 G = [x(i)∙x(k)],这样才能有效缓存数据。

  二分类训练集x中的每个样本都是n维向量,其分类结果是y = ±1,现在利用PLA训练数据,得到模型hθ(x),对偶形态的求解步骤如下:

  1)    初始化α、θ,令θ是0向量

  2)    为所有样本添加x0 = 1,计算所有数据样本的点积,形成Gram矩阵

  3)    利用JudgeFun从训练集x中随机选出一个误分类样本x(j),j是原始集中的第i个数据。在误分类判断时使用Gram矩阵快速查询点积的值

  4)    更新βi,βi = α(mi + 1) = αmi + α,即βi += α

  5)    利用JudgeFun检测是否还有误分类点,如果没有,计算θ,算法结束,否则跳转到3;或判断是否达到指定阈值(或循环次数),如果是,计算θ,算法结束,否则跳转到3

代码实现

原始形式

 from __future__ import division
import random
import numpy as np
import matplotlib.pyplot as plt # 使用随机梯度下降法训练模型
# trainDatas 训练数据
# iterateNum 迭代次数
# alpha 学习率
def train(trainDatas, iterateNum = 50, alpha = 0.01):
theta = [0, 0, 0] for i in range(iterateNum):
# 对误分类的点进行迭代
errDatas = [ex for ex in trainDatas if classify(theta, ex) != ex[2]]
# 检测是否还存在误分类点
if len(errDatas) == 0:
break x1, x2, y = random.choice(errDatas)
theta[0] += alpha * y
theta[1] += alpha * y * x1
theta[2] += alpha * y * x2 return theta def classify(theta, data):
return np.sign(theta[0] + theta[1] * data[0] + theta[2] * data[1]) def getGlobalFun(theta):
globalFun = 'g(x) = '
for i, t in enumerate(theta):
if t > 0:
globalFun += ' +{theta}x{xIdx}'.format(theta=round(float(t), 2), xIdx=i)
elif t < 0:
globalFun += ' {theta}x{xIdx}'.format(theta=round(float(t), 2), xIdx=i) globalFun = globalFun.replace('x0', '') return globalFun def plot_points(trainDatas,theta):
plt.figure() # 绘制分隔直线 g = θ0 + θ1x1 + θ2x2 = 0
x1 = np.linspace(0, 8, 2)
# θ0 + θ1x1 + θ2x2 = 0 => x2 = -(θ0 + θ1x1)/θ2
x2 = (-theta[0] - theta[1] * x1) / theta[2]
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('PLA: ' + getGlobalFun(theta))
plt.plot(x1, x2, color='b') # 绘制数据点
datasLen = len(trainDatas)
for i in range(datasLen):
if (trainDatas[i][-1] == 1):
plt.scatter(trainDatas[i][0], trainDatas[i][1], s=50, color='g')
else:
plt.scatter(trainDatas[i][0], trainDatas[i][1], marker='x', s=50, color='r')
plt.show() if __name__ == '__main__':
trainDatas = [[1, 3, 1], [2, 2, 1], [3, 4, 1], [2, 5, 1],[2, 1, -1], [4, 1, -1], [6, 2, -1], [5, 3, -1]]
theta = train(trainDatas)
plot_points(trainDatas, theta)

对偶形式

 from __future__ import division
import random
import numpy as np
import matplotlib.pyplot as plt def get_train_data():
# 构造100个2维正例点
m, n = 100, 2
x_positive = np.random.random((m, n))
y_positive = np.ones(m) # 构造100个2维负例点
x_negetive = np.random.random((m, n)) - 0.7
y_negetive = np.ones(m) * (-1) # 将所有正例点和负例点拼接在一起
x = np.vstack((x_positive, x_negetive))
y = np.hstack((y_positive, y_negetive)) return x, y # Gram矩阵的计算
def get_Gram(x):
(m, n) = x.shape
G = np.zeros((m, m))
for i in range(m):
for j in range(m):
G[i, j] = np.dot(x[i][:n], x[j][:n]) return G # 使用随机梯度下降法训练模型
# x 训练数据特征集
#y 训练数据结果集
# iterateNum 迭代次数
# alpha 学习率
def train(x, y, iterateNum=50, alpha=0.01):
theta = [0, 0, 0]
m, n = x.shape
# β记录每个数据出错的次数
beta = np.zeros(m)
# 记录误判数据的索引 # 将x0 = 1 添加到x
x = np.c_[np.ones(m), x]
# 计算Gram矩阵
G = get_Gram(x)
for t in range(iterateNum):
# 误分类数据集的序号
err_datas_idx = [j for j in range(m) if judge_fun(x, y, beta, j, G) <= 0]
if len(err_datas_idx) == 0:
break # 从误分类集随机选择一个数据,更新βj
j = random.choice(err_datas_idx)
beta[j] += alpha # 计算模型函数
for t in range(n + 1):
theta[t] = sum([beta[i] * y[i] * x[i, t] for i in range(m)]) return theta def judge_fun(x, y, beta, j, G):
return y[j] * sum([beta[i] * y[i] * G[j, i] for i in range(x.shape[0])]) # 数据可视化
def plot_points(x, y, theta):
m = x.shape[0] # 绘制分隔直线 g = θ0 + θ1x1 + θ2x2 = 0
x1 = np.linspace(-1, 1, 2)
# θ0 + θ1x1 + θ2x2 = 0 => x2 = -(θ0 + θ1x1)/θ2
x2 = (-theta[0] - theta[1] * x1) / theta[2]
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('PLA: ' + getGlobalFun(theta))
plt.plot(x1, x2) # 正例和反例
positive_x1 = [x[i, 0] for i in range(m) if y[i] == 1]
positive_x2 = [x[i, 1] for i in range(m) if y[i] == 1]
negetive_x1 = [x[i, 0] for i in range(m) if y[i] == -1]
negetive_x2 = [x[i, 1] for i in range(m) if y[i] == -1] # 绘制数据点
plt.scatter(positive_x1, positive_x2, c='green')
plt.scatter(negetive_x1, negetive_x2, marker='x', c='red')
plt.show() def getGlobalFun(theta):
globalFun = 'g(x) = '
for i, t in enumerate(theta):
if t > 0:
globalFun += ' +{theta}x{xIdx}'.format(theta=t, xIdx=i)
elif t < 0:
globalFun += ' {theta}x{xIdx}'.format(theta=t, xIdx=i) globalFun = globalFun.replace('x0', '') return globalFun def get_rate(x, y, threta):
m = x.shape[0]
right_num = len([i for i in range(m) if np.sign(threta[0] + np.dot(threta[1:], x[i])) == y[i]])
return right_num / m if __name__ == '__main__':
x, y = get_train_data()
threta = train(x, y)
plot_points(x, y, threta)

sklearn实现

 from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.linear_model import Perceptron def train(x, y, iterateNum=50, alpha=0.001):
# 定义感知机, Perceptron中的alpha默认值0.0001
clf = Perceptron(fit_intercept=False, n_iter=iterateNum, alpha=alpha, shuffle=False)
# 使用训练数据进行训练
clf.fit(x, y)
# 得到训练结果,权重矩阵[θ0, θ1,θ2]
#超平面的截距clf.intercept_, 权重clf.coef_
theta = [clf.intercept_[0], clf.coef_[0][0],clf.coef_[0][1]] return theta def plot_points(x, y,theta):
# 正例和反例
positive_x1 = [x[i, 0] for i in range(1000) if y[i] == 1]
positive_x2 = [x[i, 1] for i in range(1000) if y[i] == 1]
negetive_x1 = [x[i, 0] for i in range(1000) if y[i] == 0]
negetive_x2 = [x[i, 1] for i in range(1000) if y[i] == 0] # 绘制分隔直线 g = θ0 + θ1x1 + θ2x2 = 0
x1 = np.linspace(-4, 4, 2)
# θ0 + θ1x1 + θ2x2 = 0 => x2 = -(θ0 + θ1x1)/θ2
x2 = (-theta[0] - theta[1] * x1) / theta[2]
plt.xlabel('x1')
plt.ylabel('x2')
plt.title('PLA: ' + getGlobalFun(theta))
plt.plot(x1, x2, color='b') # 绘制数据点
plt.scatter(positive_x1, positive_x2, c='green')
plt.scatter(negetive_x1, negetive_x2, marker='x', c='red')
plt.show() def getGlobalFun(theta):
globalFun = 'g(x) = '
for i, t in enumerate(theta):
if t > 0:
globalFun += ' +{theta}x{xIdx}'.format(theta=float(t), xIdx=i)
elif t < 0:
globalFun += ' {theta}x{xIdx}'.format(theta=float(t), xIdx=i) globalFun = globalFun.replace('x0', '') return globalFun if __name__ == '__main__':
# n_samples:生成样本的数量
# n_features=2:生成样本的特征数,特征数=n_informative() + n_redundant + n_repeated
# n_informative:多信息特征的个数
# n_redundant:冗余信息,informative特征的随机线性组合
# n_clusters_per_class :某一个类别是由几个cluster构成的
x, y = make_classification(n_samples=1000, n_features=2, n_redundant=0, n_informative=1, n_clusters_per_class=1)
theta = train(x, y)
plot_points(x, y, theta)

参考:

1.《统计学习方法》李航

2. 吴恩达斯坦福公开课  http://open.163.com/movie/2008/1/B/O/M6SGF6VB4_M6SGHJ9BO.html


作者:我是8位的

出处:http://www.cnblogs.com/bigmonkey

本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途!

扫描二维码关注公众号“我是8位的”

ML(2)——感知器的更多相关文章

  1. [置顶] 局部加权回归、最小二乘的概率解释、逻辑斯蒂回归、感知器算法——斯坦福ML公开课笔记3

    转载请注明:http://blog.csdn.net/xinzhangyanxiang/article/details/9113681 最近在看Ng的机器学习公开课,Ng的讲法循循善诱,感觉提高了不少 ...

  2. Spark Multilayer perceptron classifier (MLPC)多层感知器分类器

    多层感知器分类器(MLPC)是基于前馈人工神经网络(ANN)的分类器. MLPC由多个节点层组成. 每个层完全连接到网络中的下一层. 输入层中的节点表示输入数据. 所有其他节点,通过输入与节点的权重w ...

  3. 感知器及其Python实现

    感知器是由美国计算机科学家罗森布拉特(F.Roseblatt)于1957年提出的.感知器可谓是最早的人工神经网络.单层感知器是一个具有一层神经元.采用阈值激活函数的前向网络.通过对网络权值的训练,可以 ...

  4. python机器学习——感知器

    最近在看机器学习相关的书籍,顺便把每天阅读的部分写出来和大家分享,共同学习探讨一起进步!作为机器学习的第一篇博客,我准备从感知器开始,之后会慢慢更新其他内容. 在实现感知器算法前,我们需要先了解一下神 ...

  5. 感知器、逻辑回归和SVM的求解

    这篇文章将介绍感知器.逻辑回归的求解和SVM的部分求解,包含部分的证明.本文章涉及的一些基础知识,已经在<梯度下降.牛顿法和拉格朗日对偶性>中指出,而这里要解决的问题,来自<从感知器 ...

  6. 从感知器到SVM

    这篇文章主要是分析感知器和SVM处理分类问题的原理,不涉及求解 感知器: 感知器要解决的是这样的一个二分类问题:给定了一个线性可分的数据集,我们需要找到一个超平面,将该数据集分开.这个超平面的描述如下 ...

  7. Stanford大学机器学习公开课(三):局部加权回归、最小二乘的概率解释、逻辑回归、感知器算法

    (一)局部加权回归 通常情况下的线性拟合不能很好地预测所有的值,因为它容易导致欠拟合(under fitting).如下图的左图.而多项式拟合能拟合所有数据,但是在预测新样本的时候又会变得很糟糕,因为 ...

  8. 第三集 欠拟合与过拟合的概念、局部加权回归、logistic回归、感知器算法

    课程大纲 欠拟合的概念(非正式):数据中某些非常明显的模式没有成功的被拟合出来.如图所示,更适合这组数据的应该是而不是一条直线. 过拟合的概念(非正式):算法拟合出的结果仅仅反映了所给的特定数据的特质 ...

  9. 设计一个简单的,低耗的能够区分红酒和白酒的感知器(sensor)

    学习using weka in your javacode 主要学习两个部分的代码:1.过滤数据集 2 使用J48决策树进行分类.下面的例子没有对数据集进行分割,完全使用训练集作为测试集,所以不符合数 ...

随机推荐

  1. js重写trim()方法

    最近项目中出现去空格的需求,我本地是IE11,使用trim()函数的时候,是无法识别的.因此重写String的trim()方法. 1.要求 可以去除首位全角,半角空格. 2.对   能去除所有 Str ...

  2. mvc Model验证总结及常用正则表达式

    本文属转载,来源: http://www.byywee.com/page/M0/S868/868615.html 关于Model验证官方资料: http://msdn.microsoft.com/zh ...

  3. 18-10-18 Python 思维导图 很棒的

    赠送 14 张 Python 知识点思维导图   来源 | Python学习联盟 本文主要涵盖了 Python 编程的核心知识(暂不包括标准库及第三方库). 按顺序依次展示了以下内容的一系列思维导图: ...

  4. 关于 数据库 my_slq的 安装及其卸载

    安装的时候 注意事项 自定后 根据电脑的系统版本 选择32  或者64  然后选择→方向键 密码默认是123456  或者  123123 查看装的数据库是否安装好了 如何完全卸载 mysql 数据库 ...

  5. Delphi和OutputDebugString

    曾经想要实时监控您的Delphi应用程序,并能够查看日志消息吗?当然,您始终可以在RAD Studio IDE中以完全调试模式运行.另一种方法是输出日志消息,例如输出到文本文件.您还可以使用Outpu ...

  6. mysql创建用户并授予权限

    MySQL创建数据库与创建用户以及授权   1.create schema [数据库名称] default character set utf8 collate utf8_general_ci;--创 ...

  7. python--selenium简单模拟百度搜索点击器

    python--selenium简单模拟百度搜索点击器 发布时间:2018-02-28 来源:网络 上传者:用户 关键字: selenium 模拟 简单 点击 搜索 百度 发表文章摘要:用途:简单模拟 ...

  8. 6--Python入门--Python基本运算符

    算数运算符 运算符 描述 示例 + 相加 1+1→2 - 相减 1-1→0 * 相乘 1*2→2 / 相除 1/2→0.5 % 取余数 3%2→1 ** 幂运算 2**2→4 // 取商 7//2→3 ...

  9. 【Python】UI自动化-1

    一.安装selenium和环境配置 1 pip install selenium 2 三个驱动文件放到d:盘根目录 3 安装火狐版本33 4 安装插件:selenium ide\firebug\fir ...

  10. java ip 正则表达式

    private static boolean isBoolIp(String ipAddress) { String ip = "(?:(?:25[0-5]|2[0-4][0-9]|[01] ...