KNN-k近邻算法


KNN(k-Nearest Neighbors)思想简单,应用的数学知识几乎为0,所以作为机器学习的入门非常实用、可以解释机器学习算法使用过程中的很多细节问题。能够更加完整地刻画机器学习应用的流程。

​ 首先大致介绍一下KNN的思想,假设我们现在有两类数据集,一类是红色的点表示,另一类用蓝色的点表示,这两类点就作为我们的训练数据集,当有一个新的数据绿色的点,那么我们该怎么给这个绿色的点进行分类呢?一般情况下,我们需要先指定一个k,当一个新的数据集来临时,我们首先计算这个新的数据跟训练集中的每一个数据的距离,一般使用欧氏距离。然后从中选出距离最近的k个点,这个k一般选取为奇数,方便后面投票决策。在k个点中根据最多的确定新的数据属于哪一类。

一、KNN基础

  1. 先创建好数据集x_train, y_train,和一个新的数据x_new, 并使用matplot将其可视化出来。
import numpy as np
import matplotlib.pyplot as plt raw_data_x = [[3.3935, 2.3313],
[3.1101, 1.7815],
[1.3438, 3.3684],
[3.5823, 4.6792],
[2.2804, 2.8670],
[7.4234, 4.6965],
[5.7451, 3.5340],
[9.1722, 2.5111],
[7.7928, 3.4241],
[7.9398, 0.7916]]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
x_train = np.array(raw_data_x)
y_train = np.array(raw_data_y) x_new = np.array([8.0936, 3.3657]) plt.scatter(x_train[y_train==0,0], x_train[y_train==0,1], color='g')
plt.scatter(x_train[y_train==1,0], x_train[y_train==1,1], color='r')
plt.scatter(x_new[0], x_new[1], color='b')
plt.show()
  1. knn过程
  • 计算距离
from math import sqrt
distance = []
for x in x_train:
d = sqrt(np.sum((x_train - x) ** 2))
distance.append(d) # 其实上面这些代码用一行就可以搞定
# distances = [sqrt(np.sum((x_train - x) ** 2)) for x in x_train]

输出结果:

[10.888422144185997,
11.825242797930196,
15.18734646375067,
11.660703691887552,
12.89974598548359,
12.707715895864213,
9.398411207752083,
15.62480440229573,
12.345673749536719,
14.394770082568183]
  • 将距离进行排序,返回的是排序之后的索引位置
nearsest = np.argsort(distances)

输出结果:array([6, 0, 3, 1, 8, 5, 4, 9, 2, 7], dtype=int64)

  • 取k个点,假设k=5
k = 5
topk_y = [y_train[i] for i in nearest[:k]]
topk_y

输出结果:[1, 0, 0, 0, 1]

根据输出结果我们可以发现,新来的数据距离最近的5个点,有三个点属于第一类,有两个点属于第二类,根据少数服从多数原则,新来的数据就属于第一类!

  • 投票
from collections import Counter
Counter(topk_y)

输出结果:Counter({1: 2, 0: 3})

votes = Counter(topk_y)
votes.most_common(1)
y_new = votes.most_common(1)[0][0]

输出结果:0

这样,我们就完成了一个基本的knn!

二、自己写一个knn函数

​ knn是一个不需要训练过程的机器学习算法。其数据集可以近似看成一个模型。

import numpy as np
from math import sqrt
from collections import Counter def kNN_classifier(k, x_train, y_train, x): assert 1 <= k <= x_train.shape[0], "k must be valid"
assert x_train.shape[0] == y_train.shape[0], "the size of x_train must be equal to the size of y_train"
assert x_train.shape[1] == x.shape[0], "the feature number of x must be equal to x_train" distances = [sqrt(np.sum((x_train - x) ** 2)) for x in x_train]
nearest = np.argsort(distances) topk_y = [y_train[i] for i in nearest[:k]]
votes = Counter(topk_y) return votes.most_common(1)[0][0]

测试一下:

raw_data_x = [[3.3935, 2.3313],
[3.1101, 1.7815],
[1.3438, 3.3684],
[3.5823, 4.6792],
[2.2804, 2.8670],
[7.4234, 4.6965],
[5.7451, 3.5340],
[9.1722, 2.5111],
[7.7928, 3.4241],
[7.9398, 0.7916]]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
x_train = np.array(raw_data_x)
y_train = np.array(raw_data_y) x_new = np.array([8.0936, 3.3657]) y_new = kNN_classifier(5, x_train, y_train, x_new)
print(y_new)

三、使用sklearn中的KNN

from sklearn.neighbors import KNeighborsClassifier
import numpy as np raw_data_x = [[3.3935, 2.3313],
[3.1101, 1.7815],
[1.3438, 3.3684],
[3.5823, 4.6792],
[2.2804, 2.8670],
[7.4234, 4.6965],
[5.7451, 3.5340],
[9.1722, 2.5111],
[7.7928, 3.4241],
[7.9398, 0.7916]]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
x_train = np.array(raw_data_x)
y_train = np.array(raw_data_y) x_new = np.array([8.0936, 3.3657]) knn_classifier = KNeighborsClassifier(n_neighbors=5)
knn_classifier.fit(x_train, y_train) y_new = knn_classifier.predict(x_new.reshape(1, -1))
print(y_new[0])

四、自己写一个面向对象的KNN

import numpy as np
from math import sqrt
from collections import Counter class KNNClassifier(): def __init__(self, k):
assert 1 <= k, "k must be valid"
self.k = k
self._x_train = None
self._y_train = None def fit(self, x_train, y_train):
assert x_train.shape[0] == y_train.shape[0], \
"the size of x_train must be equal to the size of y_train"
assert self.k <= x_train.shape[0], \
"the size of x_train must be at least k" self._x_train = x_train
self._y_train = y_train
return self def predict(self, x_new):
x_new = x_new.reshape(1, -1)
assert self._x_train is not None and self._y_train is not None, \
"must fit before predict"
assert x_new.shape[1] == self._x_train.shape[1], \
"the feature number of x must be equal to x_train" y_new = [self._predict(x) for x in x_new]
return np.array(y_new) def _predict(self, x):
assert x.shape[0] == self._x_train.shape[1], \
"the feature number of x must be equal to x_train" distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in self._x_train]
nearest = np.argsort(distances) topk_y = [self._y_train[i] for i in nearest[:self.k]]
votes = Counter(topk_y) return votes.most_common(1)[0][0] def __repr__(self):
return "KNN(k=%d)" % self.k

​ 测试一下:

raw_data_x = [[3.3935, 2.3313],
[3.1101, 1.7815],
[1.3438, 3.3684],
[3.5823, 4.6792],
[2.2804, 2.8670],
[7.4234, 4.6965],
[5.7451, 3.5340],
[9.1722, 2.5111],
[7.7928, 3.4241],
[7.9398, 0.7916]]
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
x_train = np.array(raw_data_x)
y_train = np.array(raw_data_y) x_new = np.array([8.0936, 3.3657]) knn_clf = KNNClassifier(6)
knn_clf.fit(x_train, y_train)
y_new = knn_clf.predict(x_new)
print(y_new[0])

五、分割数据集

import numpy as np
from sklearn import datasets def train_test_split(x, y, test_ratio=0.2, seed=None): assert x.shape[0] == y.shape[0], "the size of x must be equal to the size of y"
assert 0.0 <= test_ratio <= 1.0, "test_ratio must be valid" if seed:
np.random.seed(seed) shuffle_idx = np.random.permutation(len(x)) test_size = int(len(x) * test_ratio)
test_idx = shuffle_idx[:test_size]
train_idx = shuffle_idx[test_size:] x_train = x[train_idx]
y_train = y[train_idx] x_test = x[test_idx]
y_test = y[test_idx] return x_train, y_train, x_test, y_test

六、使用sklearn中的鸢尾花数据测试KNN

import numpy as np
from sklearn import datasets
from knn_clf import KNNClassifier iris = datasets.load_iris()
x = iris.data
y = iris.target x_train, y_train, x_test, y_test = train_test_split(x, y)
my_knn_clf = KNNClassifier(k=3)
my_knn_clf.fit(x_train, y_train) y_predict = my_knn_clf.predict(x_test)
print(sum(y_predict == y_test))
print(sum(y_predict == y_test) / len(y_test))
# 也可以使用sklearn中自带的数据集拆分方法
from sklearn.model_selection import train_test_split
import numpy as np
from sklearn import datasets
from knn_clf import KNNClassifier iris = datasets.load_iris()
x = iris.data
y = iris.target
x_train, y_train, x_test, y_test = train_test_split(x, y, \
test_size=0.2, random_state=666)
my_knn_clf = KNNClassifier(k=3)
my_knn_clf.fit(x_train, y_train)
y_predict = my_knn_clf.predict(x_test)
print(sum(y_predict == y_test))
print(sum(y_predict == y_test) / len(y_test))

七、使用sklearn中的手写数字数据集测试KNN

​ 首先,先来了解一下手写数字数据集。

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets digits = datasets.load_digits()
digits.keys()
print(digits.DESCR)
y.shape
digits.target_names
y[:100]
x[:10]
some_digit = x[666]
y[666]
some_digit_image = some_digit.reshape(8, 8)
plt.imshow(some_digit_image, cmap=plt.cm.binary)
plt.show()

​ 接下来,就开始动手试试。

from sklearn import datasets
from shuffle_dataset import train_test_split
from knn_clf import KNNClassifier digits = datasets.load_digits()
x = digits.data
y = digits.target x_train, y_train, x_test, y_test = train_test_split(x, y, test_ratio=0.2)
my_knn_clf = KNNClassifier(k=3)
my_knn_clf.fit(x_train, y_train)
y_predict = my_knn_clf.predict(x_test) print(sum(y_predict == y_test) / len(y_test))

​ 把求acc封装成一个函数,方便调用。

def accuracy_score(y_true, y_predict):
assert y_true.shape[0] == y_predict.shape[0], \
"the size of y_true must be equal to the size of y_predict" return sum(y_true == y_predict) / len(y_true)

​ 接下来把它封装到KNNClassifier的类中。

import numpy as np
from math import sqrt
from collections import Counter
from metrics import accuracy_score class KNNClassifier(): def __init__(self, k):
assert 1 <= k, "k must be valid"
self.k = k
self._x_train = None
self._y_train = None def fit(self, x_train, y_train):
assert x_train.shape[0] == y_train.shape[0], \
"the size of x_train must be equal to the size of y_train"
assert self.k <= x_train.shape[0], \
"the size of x_train must be at least k" self._x_train = x_train
self._y_train = y_train
return self def predict(self, x_new):
# x_new = x_new.reshape(1, -1)
assert self._x_train is not None and self._y_train is not None, \
"must fit before predict"
assert x_new.shape[1] == self._x_train.shape[1], \
"the feature number of x must be equal to x_train" y_new = [self._predict(x) for x in x_new]
return np.array(y_new) def _predict(self, x):
assert x.shape[0] == self._x_train.shape[1], \
"the feature number of x must be equal to x_train" distances = [sqrt(np.sum((x_train - x) ** 2)) for x_train in self._x_train]
nearest = np.argsort(distances) topk_y = [self._y_train[i] for i in nearest[:self.k]]
votes = Counter(topk_y) return votes.most_common(1)[0][0] def score(self, x_test, y_test):
y_predict = self.predict(x_test)
return accuracy_score(y_test, y_predict) def __repr__(self):
return "KNN(k=%d)" % self.k

​ 其实,在sklearn中这些都已经封装好了。

from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier digits = datasets.load_digits()
x = digits.data
y = digits.target x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
knn_classifier = KNeighborsClassifier(n_neighbors=3)
knn_classifier.fit(x_train, y_train)
knn_classifier.score(x_test, y_test)

八、超参数

  • k

​ 在knn中的超参数k何时最优?

from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier digits = datasets.load_digits()
x = digits.data
y = digits.target x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) best_score = 0.0
best_k = -1
for k in range(1, 11):
knn_clf = KNeighborsClassifier(n_neighbors=k)
knn_clf.fit(x_train, y_train)
score = knn_clf.score(x_test, y_test)
if score > best_score:
best_k = k
best_score = score
print("best k=", best_k)
print("best score=", best_score)
  • 投票方式

​ 上面这张图,绿色的球最近的三颗球分别是红色的1号,紫色的3号和蓝色的4号。如果只考虑绿色的k个近邻中多数服从少数,目前来说就是平票。即使不是平票,红色也是距离绿色最近。此时我们就可以考虑给他们加个权重。一般使用距离的倒数作为权重。假设距离分别为1、 3、 4

​ 红球:1 紫+蓝:1/3 + 1/4 = 7/12

​ 这两者加起来都没有红色的权重大,因此最终将这颗绿球归为红色类别。这样能有效解决平票问题。 因此,这也算knn的一个超参数。

​ 其实这个在sklearn封装的knn中已经考虑到了这个问题。在KNeighborsClassifier(n_neighbors=k,weights=?)还有一个参数weights,一般有两种:uniform、distance。

from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier digits = datasets.load_digits()
x = digits.data
y = digits.target x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) best_method = ""
best_score = 0.0
best_k = -1
for method in["uniform", "distance"]:
for k in range(1, 11):
knn_clf = KNeighborsClassifier(n_neighbors=k)
knn_clf.fit(x_train, y_train)
score = knn_clf.score(x_test, y_test)
if score > best_score:
best_method = method
best_k = k
best_score = score
print("best_method=", best_method)
print("best k=", best_k)
print("best score=", best_score)
  • p

    ​ 如果使用距离,那么有很多种距离可以使用,欧氏距离、曼哈顿距离、明可夫斯基距离。

from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier digits = datasets.load_digits()
x = digits.data
y = digits.target x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2) best_p = -1
best_score = 0.0
best_k = -1
for p in range(1, 6):
for k in range(1, 11):
knn_clf = KNeighborsClassifier(n_neighbors=k, weights="distance", p=p)
knn_clf.fit(x_train, y_train)
score = knn_clf.score(x_test, y_test)
if score > best_score:
best_p = p
best_k = k
best_score = score
print("best_p=", best_p)
print("best k=", best_k)
print("best score=", best_score)

​ 关于knn更多的超参寻优,后续待更~

九、更多关于k近邻的思考

​ 其实使用k近邻除了可以解决分类问题还可以解决回归问题,比如下面,这个绿色的球可以按照k个近邻取平均或者进行加权平均,sklearn中也封装了k近邻的回归部分,详情

http://scikit-learn.rog/stable/modules/generated/sklearn.neighbors.KNeighborsRegressor.html

​ 缺点1:k近邻最大的缺点就是,效率低下,因为如果训练集有m个样本,n个特征,则预测每一个新的数据需要的时间复杂度O(m*n),关于k近邻的优化问题,可以使用KD-Tree,Ball-Tree。

​ 缺点2:高度数据相关。在数据集中异常样本对于预测的影响很大。

​ 缺点3:预测结果具有不可解释性

其实还有一个最大的问题,对于k近邻算法而言就是维度灾难

维度灾难:随着维度的增加,"看似相近"的两个点之间的距离越来越大。

解决的办法:就是降维!后续会对降维进行一个整理!

我是尾巴

​ 如果有人问我:那些艰难的岁月你是怎么熬过来的?

我想我只有一句话回答:我有一种强大的精神力量支撑着我,这种力量名字叫“想死又不敢”。

本次推荐:

xmind:xmind

间接性踌躇满志,持续性混吃等死!

KNN-k近邻算法的更多相关文章

  1. 基本分类方法——KNN(K近邻)算法

    在这篇文章 http://www.cnblogs.com/charlesblc/p/6193867.html 讲SVM的过程中,提到了KNN算法.有点熟悉,上网一查,居然就是K近邻算法,机器学习的入门 ...

  2. 第四十六篇 入门机器学习——kNN - k近邻算法(k-Nearest Neighbors)

    No.1. k-近邻算法的特点 No.2. 准备工作,导入类库,准备测试数据 No.3. 构建训练集 No.4. 简单查看一下训练数据集大概是什么样子,借助散点图 No.5. kNN算法的目的是,假如 ...

  3. KNN K~近邻算法笔记

    K~近邻算法是最简单的机器学习算法.工作原理就是:将新数据的每一个特征与样本集中数据相应的特征进行比較.然后算法提取样本集中特征最相似的数据的分类标签.一般来说.仅仅提取样本数据集中前K个最相似的数据 ...

  4. KNN (K近邻算法) - 识别手写数字

    KNN项目实战——手写数字识别 1. 介绍 k近邻法(k-nearest neighbor, k-NN)是1967年由Cover T和Hart P提出的一种基本分类与回归方法.它的工作原理是:存在一个 ...

  5. kNN(k近邻)算法代码实现

    目标:预测未知数据(或测试数据)X的分类y 批量kNN算法 1.输入一个待预测的X(一维或多维)给训练数据集,计算出训练集X_train中的每一个样本与其的距离 2.找到前k个距离该数据最近的样本-- ...

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

    机器学习之K近邻算法(KNN) 标签: python 算法 KNN 机械学习 苛求真理的欲望让我想要了解算法的本质,于是我开始了机械学习的算法之旅 from numpy import * import ...

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

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

  8. k近邻算法(KNN)

    k近邻算法(KNN) 定义:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别. from sklearn.model_selection ...

  9. 1. K近邻算法(KNN)

    1. K近邻算法(KNN) 2. KNN和KdTree算法实现 1. 前言 K近邻法(k-nearest neighbors,KNN)是一种很基本的机器学习方法了,在我们平常的生活中也会不自主的应用, ...

  10. 机器学习:k-NN算法(也叫k近邻算法)

    一.kNN算法基础 # kNN:k-Nearest Neighboors # 多用于解决分裂问题 1)特点: 是机器学习中唯一一个不需要训练过程的算法,可以别认为是没有模型的算法,也可以认为训练数据集 ...

随机推荐

  1. [原创]App弱网测试方法介绍

    [原创]App弱网测试方法介绍 1 什么是弱网? 弱网就是在非正常网络状态下,用户在访问网络时遭遇到网络延迟或是丢包,造成使用产品时用户体验不佳或反感的场景. 2   为什么要进行弱网测试 简而方之, ...

  2. [Beta阶段]第四次Scrum Meeting

    Scrum Meeting博客目录 [Beta阶段]第四次Scrum Meeting 基本信息 名称 时间 地点 时长 第四次Scrum Meeting 19/05/06 大运村寝室6楼 30min ...

  3. DICOM worklist工作原理

    一.关于Worklist 在RIS与PACS的系统集成中.Wordlist的连接为其主要工作之一.Wordlist成像设备工作列表,它是DICOM协议中众多服务类别中的一个.它的功能是实现设备操作台与 ...

  4. 剑指offer:把数组排成最小的数

    题目描述: 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323. 思路分析: ...

  5. Element + Vue I18n动态import加载国际化语言包翻译文件

    需求 项目为多页应用,包含产品a.b.c.d.e,每个产品都有自己的翻译文件.一次加载所有翻译文件是极度不合理的.于是考虑动态加载. 实现 参考官方文档:延迟加载翻译 项目结构 │ ├── dist ...

  6. SyntaxError: expected expression, got ")" void() : 1: 5

    这个错误的意思是: 本来希望得到 一个 表达式, 缺得到了 ), 凡是 这样的错误, 就是 函数 在当前位置, 需要一个参数! 参数没有给, 就 输入 ) 右括号了! 错误位置 1: 5, 就是 指 ...

  7. vue中axios使用一:axios做拦截器

    转载请注明出处: 项目中用到了单点登录,依赖的公司通用的jar包,且项目为前后端分离的方式,为了管理系统的所有请求和 超时管理,用到了axios,做前端请求拦截,并做管理. 其有以下特点: axios ...

  8. CefSharp中文帮助文档

    https://github.com/cefsharp/CefSharp/wiki/CefSharp%E4%B8%AD%E6%96%87%E5%B8%AE%E5%8A%A9%E6%96%87%E6%A ...

  9. 上传下载execl

    ajax上传execl + easyexecl解析execl <!DOCTYPE html> <html> <head> <meta charset=&quo ...

  10. svg轻松实现文字水印

    1. 水印图片生成采用svg,这样可以运行时生成名字或其他信息的图片 svg模板 <svg xmlns="http://www.w3.org/2000/svg" xmlns: ...