KNN算法
核心思想
未标记的数据样本,通过距离其最近的k个数据样本进行投票
算法原理
便利所有的样本点,计算每个样本点与待分类数据的距离,找出k个距离最近的点,统计每个类别的个数,投票数据最多的类别即为样本点的类别。
优点
准确性高,对异常值和噪声的容忍度较高
缺点
计算量大,需要将所有数据在内存中计算。
算法参数
k,k越大,模型偏差越大,对噪声的容忍度越大,容易造成欠拟合,k越小,模型方差越大,容易过拟合
算法变种
- 增加邻近数据的权重,距离越近权重越高,通过weights设置
- 使用半径范围取代k个点,sklearn中,RadiusNeighborsClassifier类实现了该方法
使用KNN分类示例
使用KNeighborsClassifier方法
from sklearn.neighbors import KNeighborsClassifier # 导入knn算法类
from matplotlib import pyplot as plt
import numpy as np
centers = [[-2,2],[2,2],[0,4]]
X,y = make_blobs(n_samples=60,centers=centers,random_state=0,cluster_std=0.6) #
生成聚类使用的数据
plt.figure(figsize=(16,10),dpi=144)c = np.array(centers) # 将数据转为np array
plt.scatter(X[:,0],X[:,1],c=y,s=100,cmap="cool")
plt.scatter(c[:,0],c[:,1],s=100,marker="^",c="orange") # 画出散点图
k= 5 # 设置k参数为5
clf = KNeighborsClassifier(n_neighbors=k) # 定义knn的算法模型
clf.fit(X,y)
X_sample = np.array([0,2]).reshape(1, -1) # 新版本的sklearn中所有的输入必须为2D的数据,所以这里使用了np reshape
y_sample = clf.predict(X_sample) # 预测
neighbors = clf.kneighbors(X_sample,return_distance=False) # 取出最邻近的k个数据样本plt.scatter(X_sample[:,0],X_sample[:,1],marker="x",c=y_sample,s=100,cmap="cool")
for i in neighbors[0]:
plt.plot([X[i][0],X_sample[:,0]],[X[i][1],X_sample[:,1]],"k--",linewidth =0.6)
plt.show()
使用KNN回归示例
使用KNeighborsRegressor方法
n_dots =40
X = 5*np.random.rand(n_dots,1)
y = np.cos(X).ravel()
y+= 0.2*np.random.rand(n_dots)-1
k = 5
knn=KNeighborsRegressor(n_neighbors=k)
knn.fit(X,y)
T = np.linspace(0,5,500)[:,np.newaxis]
y_pred = knn.predict(T)
knn.score(X,y)
plt.figure(figsize=(16,10),dpi=144)plt.scatter(X,y,c="g",label="data",s=100)
plt.plot(T,y_pred,c="k",label="pred",lw=4)
plt.axis("tight")
plt.title("KNN")
plt.show()
使用三种方法做分类
- 原始knn
- 带权重的knn
- 指定半径的knn
data = pd.read_csv("diabetes.csv")
X = data.iloc[:,0:8]
Y = data.iloc[:,-1]
from sklearn.model_selection import train_test_split
X_train,X_test,Y_train,Y_test = train_test_split(X,Y,test_size=0.2) # 将数据集分为测试集和训练集models = []
models.append(("KNN",KNeighborsClassifier(n_neighbors=2)))
models.append(("KNN with
weights",KNeighborsClassifier(n_neighbors=2,weights="distance")))
models.append(("KNN
RadiusNeighborsClassifier",RadiusNeighborsClassifier(n_neighbors=2,radius=500)))
result = []
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
for name,model in models:
kflod = KFold(n_splits=10)
cv_result = cross_val_score(model,X,Y,cv=kflod) # model.fit(X_train,Y_train) result.append((name,cv_result))
for i in range(len(result)):
print("name:{} score:{}".format(result[i][0],result[i][1].mean()))
特征选择
选出相关度最高的两个特征,通过卡方值来判断
from sklearn.feature_selection import SelectKBest
selector = SelectKBest(k=2)
X_new = selector.fit_transform(X,Y)
KNN算法蛮力实现
类似于暴力破解法
KNN KD树实现原理
KD树的建立
从m个样本的n维特征中,计算每个维度的方差,然后使用方差最大的特征作为根节点,对于这个特征,选取中位值的样本点作为划分点,对于小于该特征的划分到左子树,大于的划分到右子树,递归生成KD树。
示例
有数据
(2,3),(5,4),(9,6),(4,7),(8,1),(7,2)
- 计算方差,第一维方差为6.97 ,第二维的方差为5.37,则选取第一维作为根节点,中位值为7,所以以(7,2)划分
- 分割超平面x=7将整个空间分为两部分:x<=7的部分为左子空间,包含3个节点={(2,3),(5,4),(4,7)};另一部分为右子空间,包含2个节点={(9,6),(8,1)}
- 用同样的办法划分左子树的节点{(2,3),(5,4),(4,7)}和右子树的节点{(9,6),(8,1)}。最终得到KD树。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JYJPZ1zB-1578235958718)(en-resource://database/724:0)]
KD树搜索
当我们生成KD树以后,就可以去预测测试集里面的样本目标点了。对于一个目标点,我们首先在KD树里面找到包含目标点的叶子节点。以目标点为圆心,以目标点到叶子节点样本实例的距离为半径,得到一个超球体,最近邻的点一定在这个超球体内部。然后返回叶子节点的父节点,检查另一个子节点包含的超矩形体是否和超球体相交,如果相交就到这个子节点寻找是否有更加近的近邻,有的话就更新最近邻。如果不相交那就简单了,我们直接返回父节点的父节点,在另一个子树继续搜索最近邻。当回溯到根节点时,算法结束,此时保存的最近邻节点就是最终的最近邻。
比如数据点(2,4.5)
先从根节点入手,2<7 ,进入根节点左子树,左子树根节点(5,4),根据y=4分割的,进入下一个节点(4,7),但是(2,4.5)到(4,7)的距离大于(5,4),换边,到(2,3)节点,计算(2,4.5)到(2,3)节点的距离,小于(5,4),ok,检索完毕,最近的是(2,3),找出了最近的点。
第二轮中,我们忽略上一轮中选择的点,每轮找出一个点,共找出k个,然后投票。
不过在处理分布不均匀的数据时,这种效率并不高。
sklearn参数详解
KNeighborsClassifier | KNeighborsRegressor | RadiusNeighborsClassifier | RadiusNeighborsRegressor | |
---|---|---|---|---|
n_neighbors | 选取最近样本数 | 选取最近样本数 | x | x |
radius | x | x | 样本半径 | 样本半径 |
weights | uniform 权重相同,distance 权重与距离成反比 | 同上 | 同上 | 同上 |
algorithm | ‘brute’对应第一种蛮力实现,‘kd_tree’对应第二种KD树实现,‘ball_tree’对应第三种的球树实现,‘auto’则会在上面三种算法中做权衡,选择一个拟合最好的最优算法 | 同上 | 同上 | 同上 |