首页 > 编程语言 >0002.有监督学习之k-近邻算法

0002.有监督学习之k-近邻算法

时间:2023-06-02 20:34:24浏览次数:68  
标签:dist 0002 data 近邻 算法 train test iloc 数据

一、概述

k-近邻算法(k-Nearest Neighbour algorithm),又称为KNN算法,是数据挖掘技术中原理最简单的算法。KNN的工作原理:给定一个已知标签类别的训练数据集,输入没有标签的新数据后,在训练数据集中找到与新数据最邻近的k个实例,如果这k个实例的多数属于某个类别,那么新数据就属于这个类别。可以简单理解为:由那些离X最近的k个点来投票决定X归为哪一类。

 

 

上图中有红色三角和蓝色方块两种类别,我们现在需要判断绿色远点属于哪种类别

当k=3时,绿色圆点属于红色三角这种类型;

当k=5时,绿色圆点属于蓝色方块这种类别。

案例:分类一个电影是爱情片还是动作片,以虚构的打斗镜头和接吻镜头数量进行分类?

可以从散点图中大致推断,这个未知电影有可能是爱情片。k-近邻算法是用什么方法进行判断?没错,就是距离度量(欧氏距离)。

计算未知电影与其他所有电影的欧氏距离:

通过上述计算结果,我们可以知道绿点标记的电影到爱情片《后来的我们》距离最近,为29.1。如果仅仅根据这个结果,判定绿点电影的类别为爱情片,这个算法叫做最近邻算法,而非k-近邻算法。k-近邻算法步骤如下:

①计算已知类别数据集中的点与当前点之间的距离;

②按照距离递增次序排序;

③选取与当前点距离最小的k个点;

④确定当前k个点所在类别的出现频率;

⑤返回前k个点出现频率最高的类别作为当前点的预测类别。

比如,现在K=4,那么在这个电影例子中,把距离按照升序排列,距离绿点电影最近的前4个的电影分别是《后来的我们》、《前任3》、《无问西东》和《红海行动》,这四部电影的类别统计为爱情片:动作片=3:1,出现频率最高的类别为爱情片,所以在k=4时,绿点电影的类别为爱情片。这个判别过程就是k-近邻算法。

二、k-近邻算法的python实现

1. 算法实现

①构建已经分类号的原始数据集

import pandas as pd   #导入pandas数据库

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

movie_data = pd.DataFrame(rowdata)   # 将字典数据转换成DataFrame格式数据
movie_data

②计算已知类别数据集中的点与当前点之间的距离

new_data = [24, 67]
dist = list((((movie_data.iloc[:6, 1:3] - new_data)**2).sum(1))**0.5)
dist

③将距离升序排列,然后选取距离最小的k个点

dist_1 = pd.DataFrame({'dist': dist, 'labels': (movie_data.iloc[;6, 3])})
dr = dist_1.sort_values(by = 'dist')[:4]
dr

④确定前k个点所在类别的出现频率

re = dr.loc[:, 'labels'].value_counts()
re

⑤选择频率最高的类别作为当前点的预测类别

result = []
result.append(re.index[0])
result

2. 封装函数

一般在测试流程已实现后,会将这些步骤封装成函数,方便后续调用

import pandas as pd

def classify0(new_data, movie_data, k):
    """
        函数功能:KNN分类器
    
        参数说明:
            new_data: 需要预测分类的数据集
            movie_data:已知分类标签的数据集(训练集)
            k : k-近邻算法参数,选择距离最小的k个点
            result:返回的结果
    """
    result = []
    dist = list((((movie_data.iloc[:, 1:3]-new_data)**2).sum(1))**0.5)
  dist_l = pd.DataFrame({'dist':dist,'labels':(movie_data.iloc[:, 3])})
  dr = dist_l.sort_values(by = 'dist')[: k]
  re = dr.loc[:, 'labels'].value_counts()
  result.append(re.index[0])
  return result

测试函数运行结果

new_data = new_data
movie_data = movie_data
k  = 3
classify0(new_data, movie_data, k)

最终得出结果: ['爱情片']

这就是我们使用k-近邻算法构建的一个分类器,根据我们的“经验”可以看出,分类器给的答案还是比较符合我们的预期的。

学习到这里,有人可能会问:”分类器何种情况下会出错?“或者”分类器给出的答案是否永远都正确?“答案一定是否定的,分类器并不会得到百分百正确的结果,我们可以使用很多种方法来验证分类器的准确率。此外,分类器的性能也会受到很多因素的影响,比如k的取值就在很大程度上影响了分类器的预测结果,还有分类器的设置、原始数据集等等。为了测试分类器的效果,我们可以把原始数据集分为两部分,一部分用来训练算法(称为训练集),一部分用来测试算法的准确率(称为测试集)。同时,我们不难发现,k-近邻算法没有进行数据的训练,直接使用未知的数据与已知的数据进行比较,得到结果。因此,可以说,k-近邻算法不具有显式的学习过程。

案例:k-近邻算法之约会网站配对效果判定

海伦一直使用在线约会网站寻找适合自己的约会对象,尽管约会网站会推荐不同的人选,但她并不是每一个都喜欢,经过一番总结,她发现曾经交往的对象可以分为三类:

  • 不喜欢的人
  • 魅力一般的人
  • 极具魅力得人

海伦收集约会数据已经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,其中各字段分别为:

  • 每年飞行常客里程
  • 玩游戏视频所占时间比
  • 每周消费冰淇淋公升数
#1. 准备数据
datingTest = pd.read_table('datingTestSet.txt',header=None)
datingTest.head()

 0:每年飞行常客里程;1:玩游戏视频所占时间比;2:每周消费冰淇淋公升数; 3:最后结果 y

datingTest.info()

 数据并无缺失值

#2. 分析数据
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt

#把不同标签用颜色区分
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()

3. 数据归一化

 数据归一化,也可以理解为中心标准化,简单的理解就是将数据标准化成0~1之间的数据集

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

将原始数据集带入函数,进行归一化处理

datingT = pd.concat([minmax(datingTest.iloc[:, :3]), datingTest.iloc[:,3]], axis=1)    
# 上述公式将原数据0~2列所有数据进行归一化,然后拼接原数据第3列 datingT.head()

4. 划分训练集和测试集

前面概述部分我们有提到,为了测试分类器的效果,我们可以把原始数据集分为训练集和测试集两部分,训练集用来训练模型,测试集用来验证模型准确率。

Scikit Learn官网上也有相应的函数比如model_selection 类中的train_test_split 函数也可以完成训练集和测试集的切分。通常来说,我们只提供已有数据的90%作为训练样本来训练模型,其余10%的数据用来测试模型。这里需要注意的10%的测试数据一定要是随机选择出来的,由于海伦提供的数据并没有按照特定的目的来排序,所以我们这里可以随意选择10%的数据而不影响其随机性。

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

带入原数据,即可得到训练集和测试集数据

train,test = randSplit(datingT)    #对归一化数据进行切分
train
test

三、案例:约会网站配对效果判定

至此,就可以来构建针对于这个约会网站数据的分类器了,上面已经将原始数据进行归一化处理,且也切分了训练集和测试集,所以我们的函数输入参数就是train,test,k值。

def datingClass(train,test,k):
    """
        函数功能:k-近邻算法分类器
        参数说明:
        train:训练集
        test:测试集
        k:k-近邻参数,即选择距离最小的k个点
        返回:预测好分类的测试集
    """
  n = train.shape[1] - 1         # 数据列3=4-1
  m = test.shape[0]              # 测试集行数100
  result = []          
  for i in range(m):
    dist = list((((train.iloc[:, :n] - test.iloc[i, :n]) ** 2).sum(1))**0.5)    # 计算欧氏距离
    dist_l = pd.DataFrame({'dist': dist, 'labels': (train.iloc[:, n])})   # 同样将欧氏距离与训练集第n列y值拼接
    dr = dist_l.sort_values(by = 'dist')[: k]    # 排序取前k个数据
    re = dr.loc[:, 'labels'].value_counts()   # 求labels三种情况的个数和
    result.append(re.index[0])    # 取比例最大的为第i项的评估结果
  result = pd.Series(result)      
  test['predict'] = result        # 将结果拼接至test测试集
  acc = (test.iloc[:,-1]==test.iloc[:,-2]).mean()    # 求测试集中原本的结果与模型预测结果进行比较后求和
  print(f'模型预测准确率为{acc}')
  return test

带入数据,确认输出结果:模型预测准确率为95%。

datingClass(train,test,5)

四、k-近邻算法之手写数字识别

已经经过图形处理软件处理后的手写数字。简单的说就是将手写数字处理成文本格式。

1.准备数据

观察数据集后,首先需要做的是将各个文本的信息进行读取并拼接。

import os

def get_train():
    """
        函数功能:得到标记好的训练集
    """
  path='digits/trainingDigits'   # 提供数据集相对路径
  trainingFileList = os.listdir(path)    #得到数据集list
  train = pd.DataFrame()    #  空数据集
  img = []     #  数据集项img为空列表
  labels =[]   #  数据集项labels为空列表
  for i in range(len(trainingFileList)):   
    filename = trainingFileList[i]  
    txt = pd.read_csv(f'digits/trainingDigits/{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)
  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(f'digits/testDigits/{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

生成训练集

train = get_train()

生成测试集

test = get_test()

2.分类器针对于手写数字的测试代码

① Levenshtein安装

进入Anaconda Prompt后,pip install levenshtein -i https://pypi.douban.com/simple

② 距离计算公式

汉明距离的计算公式:计算两个等长子串之间对应位置上不同字符的个数。也就是说要求输入的两个字符串必须长度一致。

from Levenshtein import hamming
#Levenshtein.hamming(str1, str2) #汉明距离格式
hamming('abc', 'aac')
# 1 hamming('0010', '1111')
# 3

通过结果,我们可以看出这两个字符串越相近汉明距离就越小。

③ 构建分类器

def handwritingClass(train, test, k):
    """
        函数功能:k-近邻算法实现手写数据分类
        参数说明:
            train:训练集
            test:测试集
            k: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]))
            dist.append(d)
        dist_1 = pd.DataFrame({'dist': dist, 'labels': (train,iloc[:, 1])})
        dr = dict_1.sort_values(by = 'dist')[: k]
        re = dr.loc[:, 'labels'].value_counts()
        resulit.append(re.index[0])
    result = pd.Series(result)
    test['predict'] = result
    acc = (test.iloc[:, -1] == test.iloc[:, -2]).mean()
    print(f'模型预测准确率为{acc}'}
    return test

带入数据,确认模型准确率97.99%。

handwritingClass(train, test, 3)

五、总结

k-近邻算法  
算法功能 分类(核心),回归
算法类型 有监督学习-惰性学习,距离类模型
数据输入

包含数据标签y,且特征空间中至少包含k个训练样本(k>=1)

特征空间中各个特征的量纲需统一,若不统一则需要进行归一化处理

自定义的超参数k(k>=1)

模型输出

在KNN分类中,输出是标签中的某个类别

在KNN回归中,输出是对象的属性值,该值是距离输入的数据最近的k个训练样本标签的平均值

1. 优点

  • 简单好用,容易理解,精度高,理论成熟,既可以用来做分类页可以用来做回归
  • 可用于数据性数据和离散性数据
  • 无数据输入假定
  • 适合对稀有事件进行分类

2. 缺点

  • 计算复杂性高;空间复杂性高
  • 计算量大,所以一般数值很大的时候不用这个,但是单个样本又不能太少,否则容易发生误分
  • 样本不平衡问题(即有些类别的样本数量很多,而其他样本的数量很少)
  • 可理解性比较差,无法给出数据的内在含义

标签:dist,0002,data,近邻,算法,train,test,iloc,数据
From: https://www.cnblogs.com/lxinghua/p/17451758.html

相关文章

  • 算法岗求职,如何做出让人眼前一亮的简历?
    在当今竞争激烈的求职市场中,一份优秀的简历无疑是敲开招聘企业大门的关键角色,其重要性不言而喻。对hr而言,简历就是你的「名片」。用得好就是「加分项」,能够给企业留下好的印象。用得不好,则是在给自己挖坑,不知不觉间与好工作失之交臂。而要想在无数的简历中脱颖而出,你最需要的是站在......
  • bzoj1001 [BeiJing2006]狼抓兔子(网络流dinic算法||最短路spfa)
    http://www.lydsy.com/JudgeOnline/problem.php?id=10011001:[BeiJing2006]狼抓兔子TimeLimit: 15Sec  MemoryLimit: 162MBSubmit: 24017  Solved: 6068[Submit][Status][Discuss]Description现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓......
  • 三个博弈-巴什博奕、威佐夫博弈、尼姆博弈。acm博弈算法笔记HDU 2149,1850,1527
    博弈论(一)、acm博弈基础算法BashGame,NimGame和WythoffGame(即巴什博奕、尼姆博弈、威佐夫博弈)Bash  Game: 同余理论Nim   Game: 异或理论WythoffGame: 黄金分割(二)、三个博弈。1、巴什博奕。只有一堆n个物品,两个人轮流从这堆物品中取物, 规定每次至少取一个,......
  • 基于Qt的A*算法可视化分析
    需求之前做过一个无人车需要自主寻找最佳路径,所以研究了相关的寻路算法,最终选择A算法,因为其简单易懂,是入门级的寻路算法。但是在验证的算法的时候,没有直观的感受,总是觉得会有什么问题,所以我就写了一个可视化的A算法验证,界面基于Qt开发。项目说明本项目主要分为2个部分,Qt绘制网格......
  • lucene底层数据结构——FST,针对field使用列存储,delta encode压缩doc ids数组,LZ4压缩算
    参考:http://www.slideshare.net/lucenerevolution/what-is-inaluceneagrandfinalhttp://www.slideshare.net/jpountz/how-does-lucene-store-your-data摘录一些重要的:看一下Lucene的倒排索引是怎么构成的。我们来看一个实际的例子,假设有如下的数据: docid年龄性别118女220女318男 ......
  • 谈谈一致性哈希算法
    一致性哈希算法是1997年由麻省理工的几位学者提出的用于解决分布式缓存中的热点问题。大家有没有发现,我们之前介绍的例如快排之类的算法是更早的六七十年代,此时分布式还没有发展起来,大家往往还在提高单机性能。但是九十年代开始,逐渐需要用分布式集群来解决大型问题,相应的算法研究......
  • lca算法
    #include<bits/stdc++.h>usingnamespacestd;intmain(){intn,m,s;scanf("%d%d%d",&n,&m,&s);intN=20;vector<vector<int>>adj(n+1);for(inti=1;i<n;i++){intu,v;scan......
  • 装饰器补充(算法)
    算法之二分法就是将一个列表或(其他容器)里面的数排列组合,将要找里面的数的时候从中间切分比较留半,然后再重复,最终至找到或者最后切分为空1x=[11,2,3,44,55,66,77,88,99,100,23,34,45,56,67]2x.sort()3defbijiao(l):4iflen(l)==0:5......
  • 算法刷题记录:日历中的数字
    题目链接https://ac.nowcoder.com/acm/contest/19859/B题目分析很简单的一道数位统计的题目其中年和月是乘法原理。(固定住年和月,枚举该月有几天,所以是乘法原理)当x=0并且month<10时,月需要特判一位数的情况,是加法原理日是加法原理AC代码//Problem:日历中的数字//Cont......
  • 强化学习基础篇【1】:基础知识点、马尔科夫决策过程、蒙特卡洛策略梯度定理、REINFORCE
    强化学习基础篇【1】:基础知识点、马尔科夫决策过程、蒙特卡洛策略梯度定理、REINFORCE算法1.强化学习基础知识点智能体(agent):智能体是强化学习算法的主体,它能够根据经验做出主观判断并执行动作,是整个智能系统的核心。环境(environment):智能体以外的一切统称为环境,环境在与智能体......