这篇笔记主要用来记录模式识别课程教授的第一个具体算法:感知机算法及在简单数据集上的编码实现.希望通过博客的方式,能将学习中遇到的问题和想法记录下来,供之后参考以及回顾.学习笔记中我尽量不看老师ppt和各种资料,尝试用自己的理解整理内容,并贴上自己编写的程序.python个人非常不熟练,代码美观度也很差,希望通过课程的学习不断提高.
section1.感知机算法简要介绍.
在本课程的学习中,感知机算法主要用来处理二分类问题.在样本空间中有若干个点,各带有标签1或-1,表示他属于哪一类.任务是学习得到一个分类面,(或求出权向量w),使得该分类面(或权向量w)对所有样本均能够正确分类,(构造了正确的分类器).
对于某一个权向量w和一个样本向量Xn及其标签Yn,我们有:
h(x)=dot(w,Xn)-threshold
作为感知机的输出.
我们可对w与Xn进行增广化.将-threshold放插入w向量开头,1插入Xn向量开头,就可用两个向量的乘积来表示感知机的输出.在上面所提到的二分类问题中,一般情况下threshold为0.
初始化权向量后,我们对数据集进行遍历.发现有分类错误的样本后,我们根据样本和他的标签值对权向量进行更新.更新公式为:
w(t+1)=w(t)+yn*xn
然后再检查下一个样本是否分类正确,以此类推,知道所有样本都分类正确,分类器学习结束.此时的 Wt+1即为最终权向量.
section2:感知机的缺陷和pocket算法
感知机算法有一个重要前提,也是其对数据集的重要要求,即数据集在样本空间上是线性可分的.存在一个分类面,能将两类样本完全分开.若不存在这个分类面,则该算法将陷入死循环.
pocket算法是在数据集线性不可分情况下做出的一种让步.既然数据集线性不可分,那我们就不在求一个能够完全分类正确的权向量,而是只需要让分错的样本数尽可能少就行.我们人为规定最大迭代次数,在迭代完这么多次之后不在迭代,算法中止.
除了初始权向量w,我们另外设置一个权向量w_hat,用来存放迭代得到的最佳分类权向量,作为最后算法的输出.每当w更新一次,我们就用更新后的w对数据集进行一次遍历,统计出错的数量,并从出错的样本当中随机取一个备用.
如果这个向量分类错的数量小于"最小分类错误数",即更新该"最小分类错误数",并用当前权向量替换w_hat.完成以上工作后,用之前随机选出的错误向量更新w(注意不是w_hat).最后返回分类错误最少的向量w_hat以及最少错分类数.下面以一个简单的例子进行示例,给定十个向量组成的数据集,对其使pocket算法求w_hat及最小错分数.
section3.pocket算法简单实现:
import numpy as np
import random
iterations = 20
x1 = np.array([1, 0.2, 0.7, 1])
x2 = np.array([1, 0.3, 0.3, 1])
x3 = np.array([1, 0.4, 0.5, 1])
x4 = np.array([1, 0.6, 0.5, 1])
x5 = np.array([1, 0.1, 0.4, 1])
x6 = np.array([1, 0.4, 0.6, -1])
x7 = np.array([1, 0.6, 0.2, -1])
x8 = np.array([1, 0.7, 0.4, -1])
x9 = np.array([1, 0.8, 0.6, -1])
x10 = np.array([1, 0.7, 0.5, -1])
dataset = [
x1, x2, x3, x4, x5, x6, x7, x8, x9, x10,
]
w = np.array([0, 0, 0])
w_hat = np.array([0, 0, 0])
least_err_amount = len(dataset)
cnt=10
def counterror(dataset, w):
errset = []
count = 0
for i in range(0, len(dataset)):
x=dataset[i]
if np.dot(w, dataset[i][:-1]) * dataset[i][-1] <= 0:
errset.append(dataset[i])
count += 1
if(len(dataset)==0):
rand_choice=np.zeros(4,1)
else:
rand_choice = random.choice(errset)
return count, rand_choice
for i in range(0, iterations):
for j in range(0, len(dataset) ):
if np.dot(w, dataset[j][:-1]) * dataset[j][-1] <= 0: # 分类错误
w = w + dataset[j][-1] * dataset[j][:-1] # 更新权向量
count, choice = counterror(dataset=dataset,w= w)
cnt=count
if count < least_err_amount:
w_hat=w
least_err_amount = count
w = w + choice[-1] * choice[:-1]
print(w,cnt)
print(w_hat)
print(least_err_amount)
多次运行可以得到多个权向量,这也是算法行进的随机性造成的.以上代码展现了pocket算法的主要过程,对于之后随机数做数据集等一系列任务,也可以使用该代码框架,只需对数据集和循环边界做出调整.
编写程序过程中间也因为对python不熟练出现了数个很低级的失误,在排查错误的过程中也渐渐对python熟悉起来.