声明:如需转载请先联系我。

最近学习了k近邻算法,在这里进行了总结。

KNN介绍

k近邻法(k-nearest neighbors)是由Cover和Hart于1968年提出的,它是懒惰学习(lazy learning)的著名代表。
它的工作机制比较简单:

  • 给定一个测试样本
  • 计算它到训练样本的距离
  • 取离测试样本最近的k个训练样本
  • “投票法”选出在这k个样本中出现最多的类别,就是预测的结果

距离衡量的标准有很多,常见的有:\(L_p\)距离、切比雪夫距离、马氏距离、巴氏距离、余弦值等。

什么意思呢?先来看这张图

我们对应上面的流程来说

  • 1.给定了红色和蓝色的训练样本,绿色为测试样本
  • 2.计算绿色点到其他点的距离
  • 3.选取离绿点最近的k个点
  • 4.选取k个点中,同种颜色最多的类。例如:k=1时,k个点全是蓝色,那预测结果就是Class 1;k=3时,k个点中两个红色一个蓝色,那预测结果就是Class 2

举例

这里用欧氏距离作为距离的衡量标准,用鸢尾花数据集举例说明。

鸢尾花数据集有三个类别,每个类有150个样本,每个样本有4个特征。

先来回顾一下欧氏距离的定义(摘自维基百科):

在欧几里得空间中,点 x = (x1,...,xn) 和 y = (y1,...,yn) 之间的欧氏距离为

\(d(x,y):={\sqrt {(x_{1}-y_{1})^{2}+(x_{2}-y_{2})^{2}+\cdots +(x_{n}-y_{n})^{2}}}={\sqrt {\sum _{{i=1}}^{n}(x_{i}-y_{i})^{2}}}\)

向量 \({\displaystyle {\vec {x}}}\)的自然长度,即该点到原点的距离为

\(\|{\vec {x}}\|_{2}={\sqrt {|x_{1}|^{2}+\cdots +|x_{n}|^{2}}}\)

它是一个纯数值。在欧几里得度量下,两点之间线段最短。

现在给出六个训练样本,分为三类,每个样本有4个特征,编号为7的名称是我们要预测的。

编号 花萼长度(cm) 花萼宽度(cm) 花瓣长度(cm) 花瓣宽度(cm) 名称
1 4.9 3.1 1.5 0.1 Iris setosa
2 5.4 3.7 1.5 0.2 Iris setosa
3 5.2 2.7 3.9 1.4 Iris versicolor
4 5.0 2.0 3.5 1.0 Iris versicolor
5 6.3 2.7 4.9 1.8 Iris virginica
6 6.7 3.3 5.7 2.1 Iris virginica
7 5.5 2.5 4.0 1.3 ?

按照之前说的步骤,我们来计算测试样本到各个训练样本的距离。例如到第一个样本的距离:
\(d_{1}=\sqrt{(4.9 - 5.5)^2 + (3.1 - 2.5)^2 + (1.5 - 4.0)^2 + (0.1 - 1.3)^2} = 2.9\)

写一个函数来执行这个操作吧

import numpy as np
def calc_distance(iA,iB):
temp = np.subtract(iA, iB) # 对应元素相减
temp = np.power(temp, 2) # 元素分别平方
distance = np.sqrt(temp.sum()) # 先求和再开方
return distance testSample = np.array([5.5, 2.5, 4.0, 1.3])
print("Distance to 1:", calc_distance(np.array([4.9, 3.1, 1.5, 0.1]), testSample))
print("Distance to 2:", calc_distance(np.array([5.4, 3.7, 1.5, 0.2]), testSample))
print("Distance to 3:", calc_distance(np.array([5.2, 2.7, 3.9, 1.4]), testSample))
print("Distance to 4:", calc_distance(np.array([5.0, 2.0, 3.5, 1.0]), testSample))
print("Distance to 5:", calc_distance(np.array([6.3, 2.7, 4.9, 1.8]), testSample))
print("Distance to 6:", calc_distance(np.array([6.7, 3.3, 5.7, 2.1]), testSample))

Distance to 1: 2.9

Distance to 2: 2.98496231132

Distance to 3: 0.387298334621

Distance to 4: 0.916515138991

Distance to 5: 1.31909059583

Distance to 6: 2.36854385647

如果我们把k定为3,那么离测试样本最近3个依次是:

编号 名称
3 Iris versicolor
4 Iris versicolor
5 Iris virginica

显然测试样本属于Iris versicolor类的“票数”多一点,事实上它的确属于这个类。

优/缺点

这里参考了CSDN芦金宇博客上的总结

优点

  • 简单好用,容易理解,精度高,理论成熟,既可以用来做分类也可以用来做回归;
  • 可用于数值型数据和离散型数据;
  • 训练时间复杂度为O(n);无数据输入假定;
  • 对异常值不敏感。

缺点

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

补充一点:由于它属于懒惰学习,因此需要大量的空间来存储训练实例,在预测时它还需要与已知所有实例进行比较,增大了计算量。

这里介绍一下,当样本不平衡时的影响。

从直观上可以看出X应该属于\(\omega_{1}\),这是理所应当的。对于Y看起来应该属于\(\omega_{1}\),但事实上在k范围内,更多的点属于\(\omega_{2}\),这就造成了错误分类。

一个结论

在周志华编著的《机器学习》中证明了最近邻学习器的泛化错误率不超过贝叶斯最优分类器的错误率的两倍,在原书的226页,这里就不摘抄了。

代码实现

知道KNN的原理后,应该可以很轻易的写出代码了,这里介绍一下在距离计算上的优化,在结尾给上完整代码(代码比较乱,知道思想就好)。

函数的输入:train_Xtest_X是numpy array,假设它们的shape分别为(n, 4)、(m, 4);要求输出的是它们两点间的距离矩阵,shape为(n, m)。

不就是计算两点之间的距离,再存起来吗,直接暴力上啊︿( ̄︶ ̄)︿,于是就有了下面的

双循环暴力实现


def euclideanDistance_two_loops(train_X, test_X):
num_test = test_X.shape[0]
num_train = train_X.shape[0]
dists = np.zeros((num_test, num_train))
for i in range(num_test):
for j in range(num_train):
test_line = test_X[i]
train_line = train_X[j]
temp = np.subtract(test_line, train_line)
temp = np.power(temp, 2)
dists[i][j] = np.sqrt(temp.sum())
return dists

不知道你有没有想过,这里的样本数只有100多个,所以时间上感觉没有等太久,但是当样本量非常大的时候呢,双循环计算的弊端就显露出来了。解决方案是把它转换为两个矩阵之间的运算,这样就能避免使用循环了。

转化为矩阵运算实现

L2 Distance

此处参考CSDNfrankzd的博客

记测试集矩阵P的大小为\(M*D\),训练集矩阵C的大小为\(N*D\)(测试集中共有M个点,每个点为D维特征向量。训练集中共有N个点,每个点为D维特征向量)

记\(P_{i}\)是P的第i行\(P_i = [ P_{i1}\quad P_{i2} \cdots P_{iD}]\),记\(C_{j}\)是C的第j行\(C_j = [ C_{j1} C_{j2} \cdots \quad C_{jD}]\)

  • 首先计算Pi和Cj之间的距离dist(i,j)

  • 我们可以推广到距离矩阵的第i行的计算公式

  • 继续将公式推广为整个距离矩阵(也就是完全平方公式)

知道距离矩阵怎么算出来的之后,在代码上只需要套公式带入就能实现了。

def euclideanDistance_no_loops(train_X, test_X):
num_test = test_X.shape[0]
num_train = train_X.shape[0]
sum_train = np.power(train_X, 2)
sum_train = sum_train.sum(axis=1)
sum_train = sum_train * np.ones((num_test, num_train))
sum_test = np.power(test_X, 2)
sum_test = sum_test.sum(axis=1)
sum_test = sum_test * np.ones((1, sum_train.shape[0]))
sum_test = sum_test.T
sum = sum_train + sum_test - 2 * np.dot(test_X, train_X.T)
dists = np.sqrt(sum)
return dists

是不是很简单,这里两种方法我们衡量两点间距离的标准是欧氏距离。如果想用其他的标准呢,比如\(L_{1}\)距离该怎么实现呢,这里我参照上面推导公式的思想得出了计算\(L_{1}\)距离的矩阵运算。

L1 Distance

记测试集矩阵P的大小为\(M*D\),训练集矩阵C的大小为\(N*D\)(测试集中共有M个点,每个点为D维特征向量。训练集中共有N个点,每个点为D维特征向量)

记\(P_{i}\)是P的第i行\(P_i = [ P_{i1}\quad P_{i2} \cdots P_{iD}]\),记\(C_{j}\)是C的第j行\(C_j = [ C_{j1} C_{j2} \cdots \quad C_{jD}]\)

  • 首先计算Pi和Cj之间的距离dist(i,j)

    \(d(P_{i}, C_{j}) = |P_{i1} - C_{j1}| + |P_{i2} - C_{j2}| + \cdots + |P_{iD} - C_{jD}|\)

  • 我们可以推广到距离矩阵的第i行的计算公式

    \(dist[i] = \left | [P_{i}\quad P_{i} \cdots P_{i}] - [C_{1}\quad C_{2}\cdots C_{N}] \right|=[|P_{i} - C_{1}|\quad |P_{i} - C_{2}| \cdots |P_{i} - C_{N}|]\)

  • 继续将公式推广为整个距离矩阵




    其中\(P_i = [ P_{i1}\quad P_{i2} \cdots P_{iD}]\)、\(C_j = [ C_{j1} C_{j2} \cdots \quad C_{jD}]\)

def l1_distance_no_loops(train_X, test_X):
num_test = test_X.shape[0]
num_train = train_X.shape[0]
test = np.tile(test_X, (num_train, 1, 1))
train = np.tile(train_X, (num_test, 1, 1))
train = np.transpose(train, axes=(1, 0, 2))
sum = np.subtract(test, train)
sum = np.abs(sum)
sum = np.sum(sum, axis=2)
dists = sum.T
return dists

由于测试集样本数量有限,两种距离衡量标准下的准确率分别是0.94和0.98。

完整代码

近期文章

K-近邻算法介绍与代码实现的更多相关文章

  1. 从K近邻算法谈到KD树、SIFT+BBF算法

    转自 http://blog.csdn.net/v_july_v/article/details/8203674 ,感谢july的辛勤劳动 前言 前两日,在微博上说:“到今天为止,我至少亏欠了3篇文章 ...

  2. 机器学习03:K近邻算法

    本文来自同步博客. P.S. 不知道怎么显示数学公式以及排版文章.所以如果觉得文章下面格式乱的话请自行跳转到上述链接.后续我将不再对数学公式进行截图,毕竟行内公式截图的话排版会很乱.看原博客地址会有更 ...

  3. <转>从K近邻算法、距离度量谈到KD树、SIFT+BBF算法

    转自 http://blog.csdn.net/likika2012/article/details/39619687 前两日,在微博上说:“到今天为止,我至少亏欠了3篇文章待写:1.KD树:2.神经 ...

  4. 用Python从零开始实现K近邻算法

    KNN算法的定义: KNN通过测量不同样本的特征值之间的距离进行分类.它的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别.K通 ...

  5. 从K近邻算法、距离度量谈到KD树、SIFT+BBF算法

    转载自:http://blog.csdn.net/v_july_v/article/details/8203674/ 从K近邻算法.距离度量谈到KD树.SIFT+BBF算法 前言 前两日,在微博上说: ...

  6. 1.K近邻算法

    (一)K近邻算法基础 K近邻(KNN)算法优点 思想极度简单 应用数学知识少(近乎为0) 效果好 可以解释机器学习算法使用过程中的很多细节问题 更完整的刻画机器学习应用的流程 图解K近邻算法 上图是以 ...

  7. 数据挖掘入门系列教程(三)之scikit-learn框架基本使用(以K近邻算法为例)

    数据挖掘入门系列教程(三)之scikit-learn框架基本使用(以K近邻算法为例) 简介 scikit-learn 估计器 加载数据集 进行fit训练 设置参数 预处理 流水线 结尾 数据挖掘入门系 ...

  8. 机器学习——KNN算法(k近邻算法)

    一 KNN算法 1. KNN算法简介 KNN(K-Nearest Neighbor)工作原理:存在一个样本数据集合,也称为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分 ...

  9. 机器学习实战 - python3 学习笔记(一) - k近邻算法

    一. 使用k近邻算法改进约会网站的配对效果 k-近邻算法的一般流程: 收集数据:可以使用爬虫进行数据的收集,也可以使用第三方提供的免费或收费的数据.一般来讲,数据放在txt文本文件中,按照一定的格式进 ...

随机推荐

  1. C# WinForm 文件上传下载

    /// <summary> /// WebClient上传文件至服务器 /// </summary> /// <param name="fileNamePath ...

  2. C++ string的那些坑,C++ string功能补充(类型互转,分割,合并,瘦身) ,c++ string的内存本质(简单明了的一个测试)

    1. size_type find_first_of( const basic_string &str, size_type index = 0 ); 查找在字符串中第一个与str中的某个字符 ...

  3. Android blueZ HCI(一个):hciconfig实施和经常使用

    关键词:hciconfighcitool  hcidump笔者:xubin341719(欢迎转载,请明确说明,请尊重版权,谢谢.)欢迎指正错误,共同学习.共同进步! . Android blueZ H ...

  4. Google CFO 辞职信

    Google CFO 辞职信   After nearly 7 years as CFO, I will be retiring from Google to spend more time with ...

  5. Emgu-WPF 激光雷达研究-移动物体跟踪2

    原文:Emgu-WPF 激光雷达研究-移动物体跟踪2 初步实现了去燥跟踪,并用圆点标注障碍物 https://blog.csdn.net/u013224722/article/details/8078 ...

  6. 静态资源(StaticResource)和动态资源(DynamicResource)

    一.文章概述 本演示介绍了WPF的静态资源和动态资源的基本使用,并对两者做了简单的比较. 二.定义并使用资源 <Window x:Class="Demo010.MainWindow&q ...

  7. WPF中任意Object的XAML代码格式化输出

    原文:WPF中任意Object的XAML代码格式化输出 有时候,我们需要将WPF中的控件自身的XAML代码输出成文本,那么,我们可以使用System.Windows.Markup.XamlWriter ...

  8. wcf 代理实例

    通过过代理调用 wcf服务 using Microsoft.Extensions.Options; using System; using System.Collections.Generic; us ...

  9. QTcpServer与QTcpSocket通讯

    TCP        TCP是一个基于流的协议.对于应用程序,数据表现为一个长长的流,而不是一个大大的平面文件.基于TCP的高层协议通常是基于行的或者基于块的.          ●.基于行的协议把数 ...

  10. 深入理解Delphi的消息机制(别人写的,简明扼要,用来复习)

    永远记住,无论你是用 SDK 还是借用 VCL 来创建窗口,都要遵循 Windows 的游戏规则,即先注册窗口类,然后再创建窗口实例,在消息循环中写实现代码.你还要知道 Windows 已经为了我们预 ...