机器学习 K近邻算法笔记

算法原理

给定一个已知标签类别的训练数据集,输入没有标签的新数据后,在训练数据集中找到与新数据最邻近的k个实例,如果这k个实例的多数属于这个类别,那么新数据就属于这个类别。
k-近邻算法步骤如下:

计算已知类别数据集中的点与当前点之间的距离;
按照距离递增次序排序;
选取与当前点距离最小的k个点;
确定前k个点所在类别的出现频率;
返回前k个点所出现频率最高的类别作为当前点的预测分类。

k近邻法中,当训练集距离度量(如欧氏距离),k值的选择分类决策规则(如多数表决)确定后,队友任何一个新的输入实例,它所属的类唯一的确定。这相当于根据上述要素将特征空间划分为一些子空间,确定子控件中的每个点所述的类。

距离度量

在这里插入图片描述

  • 欧式距离(P=2)
  • 曼哈顿距离(P=1)
  • 由不同的距离度量所确定的最近邻点是不同的

K值的选择

  • 选择较小的k值,相当于用较小的邻域中的训练实例进行预测,学习的近似误差会减小,只有与输入实例较近的训练实例才对预测结果起作用,缺点是估计误差会增大,对邻近的点非常敏感,k越小,模型越复杂,容易过拟合。
  • 如果k=N,那么无论输入实例是什么,都将简单的预测它属于在训练集中最多的类,这时的模型过于简单,完全忽视了训练实例中大量有用的信息
  • 在应用中,k值一般去较小的值,通常采用交叉验证法来确定

K近邻的实现方法

  • 线性扫描:计算输入实例与每一个训练实例的距离,当训练集很大时,计算非常耗时
  • kd树方法:使用特征的结构存储训练数据,以减少计算距离的次数,提高k近邻搜索的效率。kd树是二叉树,表示对k维空间的一个划分,其每个结点对应于k为空间划分中的一个超矩形区域。

示例:根据接吻镜头数和打斗镜头数预测电影类型

import pandas as pd
"""

函数功能:KNN分类器-预测电影类型
参数说明:
    inx:需要预测分类的数据集
    dataSet:训练数据集
    k:选择距离最小的k个点
返回:result:预测结果
日期:2019-7-30

"""
#构建数据集
rowdata={'接吻镜头':[101,89,97,5,9,8],
         '打斗镜头':[1,5,12,108,112,115],
         '电影名称':['无问西东','后来的我们','前任3','红海行动','唐人街探案','战狼2'],
         '电影类型':['爱情片','爱情片','爱情片','动作片','动作片','动作片']}
movie_data = pd.DataFrame(rowdata)

new_data = [24, 67]
dataSet = movie_data
k=4


def classify0(new_data, dataSet, k):
    result = []
    #dist = list((((dataSet.iloc[:6, 0:2] - new_data) ** 2).sum(1)) ** 0.5) 
     #loc按列或行名字索引;iloc按行或列序号索引
    dist = list((((dataSet.loc[:6, ['打斗镜头','接吻镜头']] - new_data) ** 2).sum(1)) ** 0.5) 
    #sum()默认为0按行求和,sum(1)为按列求和 列表list中元素的类型可以不同
    dist_l = pd.DataFrame({'dist': dist, 'labels': (dataSet.iloc[:, 3])})
    dr = dist_l.sort_values(by='dist')[:k]  
    #by:axis轴上的某个索引或索引列表
    re = dr.loc[:, 'labels'].value_counts()
    result.append(re.index[0])
    return result

print(classify0(new_data,dataSet,k))

示例:约会网站匹配效果预测

#准备数据
datingTest = pd.read_table('datingTestSet.txt',header=None)
#把不同标签用颜色区分
Colors = []
for i in range(datingTest.shape[0]):
    m = datingTest.iloc[i,-1]
    if m=='didntLike':
        Colors.append('black')
    if m=='smallDoses':
        Colors.append('orange')
    if m=='largeDoses':
        Colors.append('red')

#绘制两两特征之间的散点图分析数据
plt.rcParams['font.sans-serif']=['Simhei'] #图中字体设置为黑体
pl=plt.figure(figsize=(12,8))

fig1=pl.add_subplot(221)
plt.scatter(datingTest.iloc[:,1],datingTest.iloc[:,2],marker='.',c=Colors)
plt.xlabel('玩游戏视频所占时间比')
plt.ylabel('每周消费冰淇淋公升数')

fig2=pl.add_subplot(222)
plt.scatter(datingTest.iloc[:,0],datingTest.iloc[:,1],marker='.',c=Colors)
plt.xlabel('每年飞行常客里程')
plt.ylabel('玩游戏视频所占时间比')

fig3=pl.add_subplot(223)
plt.scatter(datingTest.iloc[:,0],datingTest.iloc[:,2],marker='.',c=Colors)
plt.xlabel('每年飞行常客里程')
plt.ylabel('每周消费冰淇淋公升数')
plt.show()

可视化效果
在这里插入图片描述

"""
函数功能:归一化
参数说明:
dataSet:原始数据集
返回:0-1标准化之后的数据集
"""
def minmax(dataSet):
    minDf = dataSet.min()
    maxDf = dataSet.max()
    normSet = (dataSet - minDf )/(maxDf - minDf)
    return normSet

"""
函数功能:切分训练集和测试集
参数说明:
dataSet:原始数据集
rate:训练集所占比例
返回:切分好的训练集和测试集
"""
def randSplit(dataSet,rate=0.9):
    n = dataSet.shape[0]
    m = int(n*rate)
    train = dataSet.iloc[:m,:]
    test = dataSet.iloc[m:,:]
    test.index = range(test.shape[0])    #重置index
    return train,test

#分类器针对于约会网站的测试代码
def datingClass(train,test,k):
    n = train.shape[1] - 1
    m = test.shape[0]
    result = []
    for i in range(m):
        dist = list((((train.iloc[:, :n] - test.iloc[i, :n]) ** 2).sum(1))**5)
        dist_l = pd.DataFrame({'dist': dist, 'labels': (train.iloc[:, n])})
        dr = dist_l.sort_values(by = 'dist')[: k]
        re = dr.loc[:, 'labels'].value_counts()
        result.append(re.index[0])
    result = pd.Series(result)
    test.insert(loc=0, column='predict',value=result) #经过调试发现把预测结果插入到了0列
    acc = (test.iloc[:,-1]==test.iloc[:,0]).mean()
    print('模型预测准确率为',acc)
    return test

#将归一化后的数据与label按列拼接
datingT = pd.concat([minmax(datingTest.iloc[:, :3]), datingTest.iloc[:,3]], axis=1)
#将数据集分为训练集和测试集
train,test = randSplit(datingT,0.9)
#使用分类器测试
datingClass(train,test,5)

示例:手写数字识别

import os
import pandas as pd
from Levenshtein import hamming
"""
函数功能:KNN分类器-手写数字识别
"""

#准备数据
"""
函数功能:得到标记好的训练集
"""
def get_train():
    path='digits/trainingDigits'
    trainingFileList = os.listdir(path) #得到path下的所有文件名组成个list
    train = pd.DataFrame()
    img = []
    labels =[]
    for i in range(len(trainingFileList)):
        filename = trainingFileList[i]
        txt = pd.read_csv('digits/trainingDigits/%s'%filename,header=None)  #读取filename文件
        num = ''
        for i in range(txt.shape[0]):   #将32*32D的数据转换为1*1024D的数据
            num += txt.iloc[i,:]
        img.append(num[0])
        filelabel = filename.split('_')[0]  #用_对文件名进行切分,获取到第0个字符串即为标签
        labels.append(filelabel)
    train['img'] = img
    train['labels'] = labels
    return train

"""
函数功能:得到标记好的测试集
"""
def get_test():
    path='digits/testDigits'
    testFileList = os.listdir(path)
    test=pd.DataFrame()
    img = []
    labels =[]
    for i in range(len(testFileList)):
        filename = testFileList[i]
        txt = pd.read_csv('digits/testDigits/%s'%filename,header=None)   #获取目录时有修改:%
        num = ''
        for i in range(txt.shape[0]):
            num += txt.iloc[i,:]
        img.append(num[0])
        filelabel = filename.split('_')[0]
        labels.append(filelabel)
    test['img'] = img
    test['labels'] = labels
    return test

"""
函数功能:k-近邻算法实现手写数字分类
参数说明:
train:训练集
test:测试集
k:k-近邻参数,即选择距离最小的k个点
返回:预测好分类的测试集
"""
def handwritingClass(train,test,k):
    n = train.shape[0]
    m = test.shape[0]
    result = []
    for i in range(m):
        dist = []
        for j in range(n):
            d =str(hamming(train.iloc[j,0], test.iloc[i,0]))   #hamming 海明距离,计算两个等长字符串之间相异字符的个数
            dist.append(d)
        dist_l = pd.DataFrame({'dist': dist, 'labels': (train.iloc[:, 1])})
        dr = dist_l.sort_values(by = 'dist')[: k]
        re = dr.loc[:, 'labels'].value_counts()
        result.append(re.index[0])
    result = pd.Series(result)
    test['predict'] = result
    acc = (test.iloc[:,-1]==test.iloc[:,-2]).mean()
    print(f'模型预测准确率为{acc}')
    return test

train = get_train()
test = get_test()
handwritingClass(train,test,3)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值