k-NN 算法(k-Nearest Neighbor),也叫k 近邻算法。
学会k-NN 算法,只需要三步:
k-近邻算法,它的本质是通过距离判断两个样本是否相似,如果距离够近就认为他们足够相似属于同一类别。
当然只对比一个样本是不够的,误差会很大,我们需要找到离其最近的k个样本,并将这些样本称之为「近邻」(nearest neighbor)。对这k个近邻,查看它们的都属于何种类别(这些类别我们称作「标签」(labels))。
然后根据“少数服从多数,一点算一票”原则进行判断,数量最多的的标签类别就是新样本的标签类别。其中涉及到的原理是“越相近越相似”,这也是KNN的基本假设。
可以看到k-近邻算法就是通过距离来解决分类问题。这里我们解决的二分类问题,整个算法结构如下:
给定测试对象????,计算它与训练集中每个对象的距离。
依据公式计算???? 与?1,?2,…,?j之间的相似度,得到???(????,?1) , ???(????,?2) , …, ??? (????,?j).
k-NN模型的表示形式是整个数据集。除了对整个数据集进行存储之外,k-NN没有其他模型。因此,k-NN不具有显式的学习过程,在做「分类」时,对新的实例,根据其k 个最近邻的训练实例的类别,通过多数表决等方式进行预测。k-近邻法实际上利用了训练数据集对特征向量空间进行划分,并作为其分类的"模型" 。
该算法的「距离」在二维坐标轴就表示两点之间的距离,计算距离的公式有很多,我们常用欧拉公式,即“欧氏距离”。
在n维空间中,有两个点A 和B,它们的坐标分别为:
?(?&1, ?(1, ?21,……?31), ?(?&6, ?(6, ?26,……?36)
则A和B两点之间的欧氏距离的基本计算公式如下:
在机器学习中,坐标轴上的值?&, ?(,…… ?3 正是我们样本数据上的n个特征。
圈定距离最近的训练对象,作为测试对象的近邻。
将???(????,?1) , ???(????,?2) , …, ??? (????,?j)排序,若是超过相似度阈值?,则放入邻居集合??.
根据这k个近邻归属的主要类别,来对测试对象进行分类。
自邻居集合??中取出前k名,查看它们的标签,对这k个点的标签求和,以多数决,就得到了????可能的类别。
下面,以葡萄酒分类预测为例,用python代码及其机器学习库sklearn自带的K-NN算法模块实现:
#导包
import numpy as np
import pandas as pd
#为方便验证,使用字典dict格式构建数据集,然后转为DataFrame查看
rowdata = {'颜色深度':[14.23,13.2,13.16,14.37,13.24,12.07,12.43,11.79,12.37,12.04],
'酒精浓度':[5.64,4.38,5.68,4.80,4.32,2.76,3.94,3. ,2.12,2.6 ],
'品种':[0,0,0,0,0,1,1,1,1,1]}
# 0 代表 “黑皮诺”,1 代表 “赤霞珠”
wine_data = pd.DataFrame(rowdata)
wine_data
运行结果
颜色深度 酒精浓度 品种
0 14.23 5.64 0
1 13.20 4.38 0
2 13.16 5.68 0
3 14.37 4.80 0
4 13.24 4.32 0
5 12.07 2.76 1
6 12.43 3.94 1
7 11.79 3.00 1
8 12.37 2.12 1
9 12.04 2.60 1
#查看DataFrame数据信息:dtypes: float64(2), int64(1)
wine_data.info()
运行结果
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 3 columns):
颜色深度 10 non-null float64
酒精浓度 10 non-null float64
品种 10 non-null int64
dtypes: float64(2), int64(1)
memory usage: 320.0 bytes
#scikit-learn实现,导入KNN分类器KNeighborsClassifier
from sklearn.neighbors import KNeighborsClassifier
# 实例化:KNeighborsClassifier()分类器应用
clf = KNeighborsClassifier(n_neighbors=3)
# 训练模型:.fit()拟合
clf = clf.fit(wine_data.iloc[:,0:2], wine_data.iloc[:,-1])
# 预测输出,.predict()返回预测的标签
result = clf.predict([[12.8,4.1]])
result
#predict是训练后返回预测结果,是标签值。
运行结果
array([0], dtype=int64)
# 返回预测的概率.predict_proba()
'''predict_proba返回的是一个n行k列的数组,
第i行第j列上的数值是模型预测第i个预测样本为某个标签的概率,
且每行的概率和为1。'''
clf.predict_proba([[12.8,4.1]])
运行结果
array([[0.66666667, 0.33333333]])
饮马江湖,仗剑天涯,鲜衣怒马,君仍少年