机器学习之——K近邻算法(KNN)

定义

K近邻法(k-NearestNeighbor)是一种很基本的机器学习方法,能做分类和回归任务

KNN的三个基本要素

  1. 欧式距离 判断类别远近
  2. k值,选择方式
  3. 决策方式

(1)距离度量

  • 在引例中所画的坐标系,可以叫做特征空间。特征空间中两个实例点的距离是两个实例点相似程度的反应(距离越近,相似度越高)。kNN模型使用的距离一般是欧氏距离,但也可以是其他距离如:曼哈顿距离

(2)k值的选择

  • k值的选择会对kNN模型的结果产生重大影响。选择较大的k值,相当于用较大邻域中的训练实例进行预测,模型会考虑过多的邻近点实例点,甚至会考虑到大量已经对预测结果没有影响的实例点,会让预测出错;选择较小的k值,相当于用较小邻域中的训练实例进行预测,会使模型变得敏感(如果邻近的实例点恰巧是噪声,预测就会出错)。
  • 在应用中,k值一般取一个比较小的数值。通常采用一些验证方法来选取最优的k值。

(3)决策规则

  • 分类:往往是多数表决,即由输入实例的k个邻近的训练实例中的多数类决定待测实例的类。或带权投票
  • 回归:取平均值。或带权取平均值

KNN的实现方法

(1)蛮力法(brute-force)

  • k近邻法最简单的实现方式是线性扫描,需要计算待测实例与每个实例的距离,在大数据上不可行。

(2)KD树(KDTree)

  • 为了提高k近邻搜索效率,考虑使用特殊的结构存储训练数据,以减少计算距离的次数,可以使用kd树(kd tree)方法。kd树分为两个过程——构造kd树(使用特殊结构存储训练集)、搜索kd树(减少搜索计算量)

(3)球树(BallTree)

  • 球树,顾名思义就是分割块都是超球体,而不是KD树里面的超矩形体。

KNN模型的优缺点

(1)优点

  • 思想简单,既可以做分类又可以做回归
  • 惰性学习,无需训练(蛮力法),KD树的话,则需要建树
  • 对异常点不敏感
  • 训练时间复杂度比支持向量机之类的算法低,仅为O(n)
  • 和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
  • 对类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合
  • 比较适用于样本比较大的类域的自动分类,而那些样本容量比较小的类域采用这种算法容易产生误分

(2)缺点

  • 计算量大、速度慢
  • 样本不平衡的时候,对稀有类别的预测准确率低
  • KD树,球树之类的模型建立需要大量的内存
  • 相比决策树模型,KNN模型可解释性不强
  • 使用懒惰的学习方法,基本不学习,导致预测时的速度比逻辑回归之类的算法慢

闵可夫斯基距离

1.假设有两个样本点x1,x2,它们两者间的闵可夫斯基距离Lp定义为
在这里插入图片描述

  • 当p=1时,称为曼哈顿距离(Manhattan distance),即
    在这里插入图片描述
  • 当p=2时,称为欧氏距离(Euclidean distance),即
    在这里插入图片描述
  • 当p=∞时,称为切比雪夫距离
    在这里插入图片描述

代码实现

(1)投票法[0,1]

#投票法做分类
#[x1, x2, y]
T = [[3, 104, 0],
    [2, 100, 0],
    [1, 81, 0],
    [101, 10, 1],
    [99, 5, 1],
    [98, 2, 1]]
    
#预测分类
x = [18, 90]

#用最近k个点做预测
K = 5

#记录所有样本点到预测点距离
dis = []

import math
for i in T:
    d = math.sqrt((x[0]-i[0])**2 + (x[1] - i[1])**2)
    
    #添加距离和标签
    dis.append([d,i[2]])
    
#按照距离排序
dis.sort(key=lambda x:x[0])
print(dis)

# box 距离最近的前k个标签点
print(1 if sum([i[1] for i in dis[:k]])>k/2 else 0)
[[18.867962264113206, 0], [19.235384061671343, 0], [20.518284528683193, 0], [115.27792503337315, 1], [117.41379816699569, 1], [118.92854997854805, 1]]
0

(2)投票法[-1,1]

#分类
#训练样本点
T = [[3, 104, -1],
    [2, 100, -1],
    [1, 81, -1],
    [101, 10, 1],
    [99, 5, 1],
    [98, 2, 1]]
#测试样本
x = [18, 90]

K = 5
dis = []

import numpy as np
for i in T:
    d = np.sqrt((x[0] - i[0])**2 + (x[1] - i[1])**2)
    dis.append([d,i[2]])

dis.sort(key=lambda x:x[0])
print(dis)


# 计算距离为正还是负, 如果是正,那么结果是+1
print(-1 if sum([1/i[0]*i[1] for i in dis[:K]])<0 else 1)
[[18.867962264113206, -1], [19.235384061671343, -1], [20.518284528683193, -1], [115.27792503337315, 1], [117.41379816699569, 1], [118.92854997854805, 1]]
-1

(3)回归[求均值]

#回归
T = [[3, 104, 98],
    [2, 100, 93],
    [1, 81, 95],
    [101, 10, 16],
    [99, 5, 8],
    [98, 2, 7]]

x = [18, 90]
K = 5
dis = []

from math import sqrt
for i in T:
    d = sqrt((x[0]-i[0])**2 + (x[1]-i[1])**2)
    dis.append([d, i[2]])
dis.sort(key=lambda x:x[0])

from numpy import mean
#求平均值
print(mean([i[1] for i in dis[0:K]]))
62.0

(4)带权回归

#带权回归
T = [
    [3, 104, 98],
    [2, 100, 93],
    [1, 81, 95],
    [101, 10, 16],
    [99, 5, 8],
    [98, 2, 7]
]
x = [18, 90]
K = 4
dis = []

import math
for i in T:
    d = (x[0]-i[0])**2 + (x[1]-i[1])**2
    #pow方法   幂函数
    dis.append([pow(d,1/2), i[2]])
dis.sort(key=lambda x:x[0])

dis = [[1/i[0],i[1]] for i in dis][0:K]
a = 1 / sum([i[0] for i in dis])

print(dis)

res = sum([i[0]*i[1] for i in dis])
print(res*a)
[[0.052999894000318, 93], [0.05198752449100364, 95], [0.04873701788285793, 98], [0.008674687714152543, 16]]
91.02775527644329

导库求预测

from sklearn.neighbors import KNeighborsClassifier
#  编写一个kNN算法函数,并通过数据来验证算法的正确性。
# 1、四个样本数据点:[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]
x = [1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]

# 2、四个样本数据点所属分类:['A', 'A', 'B', 'B']
y = ['A', 'A', 'B', 'B']

print(x,y)
model = KNeighborsClassifier(n_neighbors=2)
model.fit(x,y)

# 3、通过调用kNN算法,分别检验两个点[1.2, 1.0][0.1, 0.3]的分类
s = [1.2, 1.0], [0.1, 0.3]
print(model.predict(s))

([1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]) ['A', 'A', 'B', 'B']
['A' 'B']

sklearn.neighbors.KNeighborsClassifier 使用

sklearn.neighbors.KNeighborsClassifier(n_neighbors = 5,weights =‘uniform’,algorithm =‘auto’,leaf_size = 30,p = 2,metric =‘minkowski’,metric_params = None,n_jobs = None,kwargs

参数

  • n_neighbors : int,optional(default = 5)默认情况下kneighbors查询使用的邻居数。
  • weights: str或callable,可选(default = ‘uniform’)用于预测的权重函数。可能的值:
    ’uniform’: 均匀的重量。每个社区的所有积分均等。
    ’distance’: 重量点距离的倒数。在这种情况下,查询点的较近邻居将比远离的邻居具有更大的影响力。
    [callable]: 一个用户定义的函数,它接受一个距离数组,并返回一个包含权重的相同形状的数组。
  • algorithm : {‘auto’,‘ball_tree’,‘kd_tree’,‘brute’},可选
    用于计算最近邻居的算法:
    'ball_tree’将使用 BallTree
    'kd_tree’将使用 KDTree
    'brute’将使用蛮力搜索。
    'auto’将尝试根据传递给fit方法的值来确定最合适的算法。
    注意:在稀疏输入上拟合将使用强力来覆盖此参数的设置。
  • leaf_size : int,optional(default = 30)
    叶子大小传递给BallTree或KDTree。这可能会影响构造和查询的速度,以及存储树所需的内存。最佳值取决于问题的性质。
  • p : 整数,可选(default = 2)
    Minkowski指标的功率参数。当p = 1时,这相当于使用manhattan_distance(l1),并且对于p = 2使用euclidean_distance(l2)。对于任意p,使用minkowski_distance(l_p)。
  • metric : 字符串或可调用,默认为’minkowski’
    用于树的距离度量。默认度量标准是minkowski,p = 2等于标准欧几里德度量标准。有关可用指标的列表,请参阅DistanceMetric类的文档。
  • metric_params : dict,optional(默认=无)
    度量函数的其他关键字参数。
  • n_jobs : int或None,可选(默认=无)
    为邻居搜索运行的并行作业数。 None除非在joblib.parallel_backend上下文中,否则表示1 。 -1表示使用所有处理器。有关 详细信息,请参阅词汇表。不影响fit方法。

方法

  • fit(self, X, y) 使用X作为训练数据并使用y作为目标值来拟合模型
  • get_params(self[, deep]) 获取此估算工具的参数。
  • kneighbors(self [,X,n_neighbors,…]) 找到一个点的K邻居。
  • kneighbors_graph(self [,X,n_neighbors,mode]) 计算X中点的k-邻居的(加权)图
  • predict(self, X) 预测所提供数据的类标签
  • predict_proba(self, X) 测试数据X的返回概率估计。
  • score(self,X,y [,sample_weight]) 返回给定测试数据和标签的平均精度。
  • set_params(self, **params) 设置此估算器的参数。
    详情(https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html#sklearn.neighbors.KNeighborsClassifier)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值