【ML学习笔记】21:kNN与剪辑近邻(随机二分方式)

简述

剪辑近邻能够除去kNN训练集中样本交错位置附近的样本,即从分类器中去除可能影响分类效果或造成overfitting的样本点,使分类面更平滑。

近邻法的渐进错误率上界是2倍贝叶斯错误率,剪辑近邻的渐进错误率近似等于贝叶斯错误率。

代码实现

#-*-coding:utf-8-*-
from numpy import *
import operator
import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
import operator
from mpl_toolkits.mplot3d import Axes3D
import random

#获取数据
def getData(str1,str0):
    fr1=open(str1)
    fr0=open(str0)
    arrayOLines1=fr1.readlines() #读取文件
    arrayOLines0=fr0.readlines()
    #特征矩阵
    dataMat1=[]
    dataMat0=[]
    #男同学
    for line in arrayOLines1:
        line=line.strip() #strip()去掉首尾空格
        listFromLine=line.split() #按空白字符分割成列表
        #string变float
        for i in range(len(listFromLine)):
            listFromLine[i]=float(listFromLine[i])
        #加入特征矩阵
        dataMat1.append(listFromLine)
    #女同学
    for line in arrayOLines0:
        line=line.strip() #strip()去掉首尾空格
        listFromLine=line.split() #按空白字符分割成列表
        #string变float
        for i in range(len(listFromLine)):
            listFromLine[i]=float(listFromLine[i])
        #加入特征矩阵
        dataMat0.append(listFromLine)
    return dataMat1,dataMat0

#不同的特征,范围可能不同,作差得到的值可能差别很大
#如果它们的重要程度一样,显然不应如此
#可以将不同取值范围的特征值数值归一化到0~1之间
def autoNorm(dataMat):
    #下面参数0表示从列中取最大最小
    minVals=dataMat.min(0) #每列的最小值存到minVals里
    maxVals=dataMat.max(0) #每列的最大值存到maxVals里
    ranges=maxVals-minVals #每列最大最小值之差,存到ranges里
    newDataMat=zeros(shape(dataMat)) #用来存归一化后的矩阵
    m=dataMat.shape[0] #取第0维即行数
    newDataMat=dataMat-tile(minVals,(m,1)) #把最小值重复m成m行,用原值减去
    newDataMat=newDataMat/tile(ranges,(m,1)) #把减完的每个偏差值除以自己列最大和最小的差值
    return newDataMat,ranges,minVals #返回归一化之后的矩阵,原范围,原最小值

#训练集交错混合,需要保证男女样本数基本差不多
def MyFix(dataMat1,dataMat0):
    i=j=0
    len1=len(dataMat1)
    len0=len(dataMat0)
    #不能像上面那样连续赋值了!否则会指向同一个列表
    dataMat=[0]*(len1+len0)
    labelVec=[0]*(len1+len0) #久违的标签向量
    #只要有一侧未结束就不能退出循环
    while(i<len1 or j<len0):
        if i<len1:
            dataMat[i+j]=dataMat1[i]
            labelVec[i+j]=1
            i+=1
        if j<len0:
            dataMat[i+j]=dataMat0[j]
            labelVec[i+j]=0
            j+=1
    return dataMat,labelVec

#用长为lnth的rdmLst列表打乱用dataMat,labelVec表征的同样数目的样本集
def MyUpset(dataMat,labelVec,rdmLst,lnth):
    for i in range(lnth): #对随机数表中每一项条目所表示的下标
        #做交换以打乱样本集
        dataMat[i],dataMat[rdmLst[i]]=dataMat[rdmLst[i]],dataMat[i]
        labelVec[i],labelVec[rdmLst[i]]=labelVec[rdmLst[i]],labelVec[i]
    return dataMat,labelVec

#对(考试集)中的某个样本向量做测试,返回分类结果
def Tst(tstVec,traMat,traLab):
    traMat=mat(traMat)
    rowNum=traMat.shape[0] #训练集行数
    #用tile()将输入的特征向量重复成和训练集特征向量一样多的行
    #变成2维,里面的维度重复1次,外面一层重复rowNum次
    diffMat=tile(tstVec,(rowNum,1))
    #相减得到偏差矩阵
    diffMat=diffMat-traMat
    #将减出来的偏差矩阵每个元素平方
    sqDiffMat=multiply(diffMat,diffMat)
    #对行求和,表示这个实例和这行对应的训练集实例的L2范数的平方
    sqDistances=sqDiffMat.sum(axis=1)
    ###print(sqDistances)
    #mat变列表
    sqDistances=[sqDistances[i,0] for i in range(len(sqDistances))]
    #再变成array对象(为了argsort())
    sqDistances=array(sqDistances)
    #为了方便就不开根号(**0.5)了
    #argsort()返回其从小到大排序的排序索引序列
    sortIndex=sqDistances.argsort()
    #找前k个距离最近的,也就是排序下标从0~k-1的
    Vote1=Vote0=0 #男女投票数都初始化为0
    for i in range(5):
        #第i近(从0计数)训练集实例的标签,男生是1女生是0
        if traLab[sortIndex[i]]==1:
            Vote1+=1
        elif traLab[sortIndex[i]]==0:
            Vote0+=1
    return 1 if Vote1>Vote0 else 0

#找出考试集中考试错误的那些样本的下标
#[修正]传入labelVec以确定死亡样本
#[停用]停用参数tstLab
def FindErr(tstMat,tstLab,traMat,traLab,sword,labelVec):
    whoErr=[]
    #对于考试集中的每个样本(sword就等于len(tst*))
    for i in range(sword):
        #如果有考试资格(非-1,即不是被剔除掉的僵尸样本),且分类错误
        #[修正]tstLab改用labelVec
        if labelVec[i]!=-1 and Tst(tstMat[i],traMat,traLab)!=labelVec[i]:
            whoErr.append(i) #把错误者的下标i记录下来
        ###break
    return whoErr

def Go(a,b):
    #获取训练集
    dataMat1,dataMat0=getData('185Boy.txt','185Girl.txt')
    #print(dataMat0)
    #训练集交错混合
    dataMat,labelVec=MyFix(dataMat1,dataMat0)
    #归一化
    dataMat,ranges,minVals=autoNorm(mat(dataMat))
    #变回list并且按参数a,b的要求,降成2维
    dataMat=dataMat.tolist()
    dataMat=[[dataMat[r][a],dataMat[r][b]] for r in range(len(dataMat))]
    ###print(len(dataMat))
    ###print(dataMat,labelVec)

    #样本集的长度
    dtmtLnth=len(dataMat)
    #基于后面特殊的剪辑方法,这个值不需要变

    #划分样本集的阈值是lnth/2(前1/2做考试集)
    sword=int(dtmtLnth/2)
    #基于后面特殊的剪辑方法,这个值不需要变

    #剪辑近邻法,这里只剪15次
    for page in range(15):
        ###print(sword)
        ###return
        tstMat=dataMat[0:sword]
        ###print(tstMat)
        ###return
        tstLab=labelVec[0:sword]
        if page==0:
            #[修正]显示考试集,传入labelVec以确定死亡样本
            MyPic(tstMat,tstLab,"Clips 0 times",a,b,labelVec)
        traMat=dataMat[sword:]
        traLab=labelVec[sword:]
        #计算并返回考试集中考试错误的样本下标
        #[修正]传入labelVec以确定死亡样本
        whoErr=FindErr(tstMat,tstLab,traMat,traLab,sword,labelVec)
        #[添加]输出检验
        print("分类错误%d次"%len(whoErr))
        #把这些分类错误的样本的特征向量变成-1
        #在调用MyPic显示输出或者FindErr考试时,特判跳过它们即可
        #避免了对列表项的删除(那样很费时)
        for index in whoErr:
            #[原来的错误]tstLab[index]=-1
            #[修正]切片只能切得值,不能切得引用,另需在LabelVec里改
            labelVec[index]=-1
        #[修正]显示考试集,传入labelVec以确定死亡样本
        MyPic(tstMat,tstLab,"Clips %d times"%(page+1),a,b,labelVec)
        ###print(whoErr)
        #[修正]随机数表必须混合训练集和测试集
        rdmLst=[random.randint(0,dtmtLnth-1) for i in range(dtmtLnth)]
        #对刚刚分出来的训练集和测试集混合打乱,[修正]长度
        dataMat,labelVec=MyUpset(dataMat,labelVec,rdmLst,dtmtLnth)
        ###print(dataMat,labelVec)

#[修正]绘图,传入labelVec以确定死亡样本
#[停用]停用参数subLab
def MyPic(subMat,subLab,str,i,j,labelVec):
    manX=[]
    manY=[]
    girlX=[]
    girlY=[]
    if i==0:
        xlab='Height'
    elif i==1:
        xlab='Weight'
    elif i==2:
        xlab='Shoe Size'
    if j==0:
        ylab='Height'
    elif j==1:
        ylab='Weight'
    elif j==2:
        ylab='Show Size'
    #分离出男生女生,方便绘制
    for r in range(len(subLab)):
        if labelVec[r]==1:
            manX.append(subMat[r][0])
            manY.append(subMat[r][1])
        elif labelVec[r]==0:
            girlX.append(subMat[r][0])
            girlY.append(subMat[r][1])
    #绘制
    plt.scatter(manX,manY,c=u'b',marker='.')
    plt.scatter(girlX,girlY,c=u'r',marker='.')
    plt.title(str)
    plt.xlabel(xlab)
    plt.ylabel(ylab)
    plt.show()

运行

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
…略…
这里写图片描述
这里写图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值