KNN基本思想
KNN是通过测量不同特征值之间的距离进行分类。它的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别,其中K通常是不大于20的整数。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
使用图来说话比较方便:
原始数据的散点图如下:
如果增加一个数据点(增加的数据点用绿色来表示)如下:
那么新增的数据点归为哪一类呢?
如果我们测量3个点分别到绿色点的距离,那么很显然归为蓝色这一类。
如此便可以很形象的说,knn,就是选择k个点与新增加的点测量距离,距离最近点最多的意味着得到的票数越多,便归为那一类。
使用python进行编程:
# -*- coding: utf-8 -*-
import numpy as np
from math import sqrt
from collections import Counter
class KNN_Classifier:
def __init__(self,k):
"""构造函数"""
#给定k值
self.k=k
self.X_train=None
self.y_train=None
def fit(self,X_train,y_train):
"""训练模型"""
#knn的训练模型实际上就是把训练数据集进行保存
assert self.k<=X_train.shape[0],\
"k的值必须小于等于X_train的第二个维度"
self._X_train=X_train
self._y_train=y_train
return self
def predicty(self,x):
"""对矩阵中的元素进行预测"""
assert self._X_train is not None and self._y_train is not None,\
"X_train and y_train must be fit"
assert self._X_train.shape[1]==x.shape[1],\
"X_train和x的维度应该相等"
y_predict=[self._predict(m) for m in x]
return np.array(y_predict)
def _predict(self,m):
"""预测每个点的函数"""
#求距离
distances=np.array(np.array([sqrt(np.sum((x-m)**2)) for x in self._X_train]))
#得到值从小到大后排序的索引值
top_y=distances.argsort()
#取出最近的k个值
nearest=self._y_train[top_y[:self.k]]
#得到预测值
predict_y=Counter(nearest).most_common(1)[0][0]
return predict_y
sklearn中的KNN
import numpy as np
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
raw_data_X = [
[3.393533211, 2.331273381],
[3.110073483, 1.781539638],
[1.343808831, 3.368360954],
[3.582294042, 4.679179110],
[2.280362439, 2.866990263],
[7.423436942, 4.696522875],
[5.745051997, 3.533989803],
[9.172168622, 2.511101045],
[7.792783481, 3.424088941],
[7.939820817, 0.791637231]
] # 两列分别代表肿瘤的长和宽 X是大写
raw_data_y = [0, 0, 0, 0, 0, 1, 1, 1, 1, 1] # 分为0,1两个类别
#x训练集
X_train=np.array(raw_data_X)
y_train=np.array(raw_data_y)
#看一下原始数据点的分布
plt.figure(1)
plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1],color='red',marker='o',label='y==0')
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1],color='blue',marker='x',label='y==1')
plt.legend()
plt.show()
X_new = np.array([8.093623049, 3.365732334])#增加一个新的数据
#用散点图直观观察一下新增数据之后新的数据的分布
plt.figure(2)
plt.scatter(X_train[y_train==0,0],X_train[y_train==0,1],color='red',marker='o',label='y==0')
plt.scatter(X_train[y_train==1,0],X_train[y_train==1,1],color='blue',marker='x',label='y==1')
plt.legend()
plt.scatter(X_new[0],X_new[1],marker='+',color='green',s=100)
plt.show()
"""sklearn中的KNN"""
#创建knn类
knn=KNeighborsClassifier()
#训练knn
knn.fit(X_train,y_train)
#将X_new变成两列的
X_new=X_new.reshape(-1,2)
#输出类别
print(knn.predict(X_new))
使用knn对sklearn中的数据进行训练和预测
import numpy as np
from sklearn import datasets
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
#加载 sklearn 中的 datasets 数据中的digits(手写字体数据)
digits=datasets.load_digits()
x=digits.data
y=digits.target
#使用 train_test_split 进行模型建立和测试的分离,加zhiqian是因为没有进行
#归一化处理
X_train_zhiqian,X_test_zhiqian,y_train,y_test=train_test_split(x,y,test_size=0.2)
#使用 StandardScaler进行归一化处理
"""为什么使用X_train_zhiqian进行归一化处理的均值和方差进行对X_test的归一化呢?
是因为要服从现实情况,如果只有一个数据进行预测,就没有办法得出均值和方差
"""
s=StandardScaler()
s.fit(X_train_zhiqian)
X_train=s.transform(X_train_zhiqian)
X_test=s.transform(X_test_zhiqian)
knn_clf=KNeighborsClassifier()
#使用网格搜索的方法进行对超参数的选取
#超参数是指在算法进行时对不确定的参数进行调整以使得算法的预测准确率最高
params=[
{"weights":["uniform"],
"n_neighbors":[k for k in range(1,11)]
},
{"weights":["distance"],
"n_neighbors":[k for k in range(1,11)],
"p":[k for k in range(1,6)]
}
]
#n_jobs 这个参数是使用计算机的几个核,赋值为-1表示全都使用
gridsearch=GridSearchCV(knn_clf,params,n_jobs=-1)
gridsearch.fit(X_train,y_train)
print("best_score",gridsearch.best_score_)
print("best_params",gridsearch.best_params_)
#将最好的赋值给knn_clf
knn_clf=gridsearch.best_estimator_
print(knn_clf.score(X_test,y_test))