一、K近邻算法简介
K近邻算法(K-Nearest Neighbor)简称KNN算法,是最简单的预测模型之一,它没有多少数学上的假设,也不要求任何复杂的处理,它所要求的只有以下两点
1.某种距离计算概念
2.彼此接近的点具有相似的性质
即对于一个新样本,算法在已有数据中寻找与它最相似的K个数据,或者说“离它最近”的K个数据,如果这K个数据大多数属于某个类别,则该样本也属于这个类别。
KNN算法只依赖待预测节点附近的少量节点,有意的忽略了数据集中的大量样本,同时该算法也不能帮助我们理解事物现象背后的机制和原理;
预测策略通常采用多数表决的投票法;也就是将k个样本中出现最多的分类作为预测结果;计算公式如下,里边的v是样本的分类标签,yi是第i个样本的分类标签,I是指示函数,如果预测结果属于某个分类就返回1,否则返回0,则最终取出现次数最多的分类作为预测结果;
\[y = argmax_{v} \sum_{(x_{i},y_{i})\in D_{z}} I(v=y_{i}) \]相对来说,K近邻学习算法没有显示的训练过程,只有收到待测试样本才进行处理,这是一种典型的基于实例的惰性学习;
只选择最近的k个邻居的算法,很容易受到数据集分布偏斜的影响,通过不同的k值,最终会覆盖到不同的样本,最终也会得到不同的预测结果;
采用数据节点相等的少数服从多数很容易出现群体暴政,从而产生对新样本分类结果的误判;我们可以根据距离远近赋予节点不同的区中,从而提高预测结果的准确性;
距离也有不同的算法欧氏距离、马氏距离、海明距离等,不同的算法会影响对最近的k个节点的选择,从而最终影响分类结果;
二、影响算法性能的因素
影响k-近邻算法性能的因素有很多,其中比较重要的有四个:k值的选取、特征数据的归一化、邻居距离的度量及分类原则;
k值的选择
如果k值的选取比较小,则覆盖的邻域的训练样本比较小,从而导致预测结果对训练样本比较敏感,很容易发生过拟合现象;
如果k值的选取比较大,则覆盖的邻域的训练样本会比较多,分类准确率会提高,但是随着k值的增大,则样本携带的噪声就会导致分类错误绿上升,所以对于k值的选取过犹不及;
通常可以采用交叉验证的方式来选择最优的k值,即对于每一个k值都做若干次交叉验证,然后计算出他们各自的平均误差,最后选择误差最小的k值使用;
特征数据归一化
计算样本之间的距离,需要考虑不同特征的取值范围,不同特征对距离的计算影响可谓大相径庭;归一化是特征值预处理的常见处理方法,它会把所有的特征值映射为[0,1]之间;
常用的一种归一化方法,首先找到它的最大值和最小值,然后按照如下公式计算
\[x^{'} = \frac{x-Min} {Max-Min} \]邻居距离的度量
在特征向量空间中,两个点之间的距离也是他们之间相似度的度量,计算方式的不同,也会显著的影响最终得到的最近距离,从而影响分类的结果;
一般情况下,经过归一化处理之后,可以使用欧式距离或者马氏距离来计算两个未知样本集的相似度方法;在文本分类等非连续变量的分类中,更多的采用马氏距离来衡量字符串之间的编辑距离;
分类原则
k近邻算法通常有两类分类原则,一类是平等投票表决原则,一类是加权投票原则;
三、红酒数据集情况
接下来我们使用knn算法对红酒进行分类;通过一下代码,我们可以看到红酒样本数据的特征字段名称、分类标签、样本数据量等维度的数据;
from sklearn.datasets import load_wine
wine = load_wine()
print('wine features:')
print(wine.feature_names)
print('wine target names:')
print(wine.target_names)
print('wine data shape:')
print(wine.data.shape)
print('one wine data:')
print(wine.data[0])
print('wine target:')
print(wine.target)
wine features:
['alcohol', 'malic_acid', 'ash', 'alcalinity_of_ash', 'magnesium', 'total_phenols', 'flavanoids', 'nonflavanoid_phenols', 'proanthocyanins', 'color_intensity', 'hue', 'od280/od315_of_diluted_wines', 'proline']
wine target names:
['class_0' 'class_1' 'class_2']
wine data shape:
(178, 13)
one wine data:
[1.423e+01 1.710e+00 2.430e+00 1.560e+01 1.270e+02 2.800e+00 3.060e+00
2.800e-01 2.290e+00 5.640e+00 1.040e+00 3.920e+00 1.065e+03]
wine target:
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2]
四、使用knn进行红酒分类
from sklearn.datasets import load_wine
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import MinMaxScaler
wine = load_wine()
X = wine.data
y = wine.target
# 归一化
scaler = MinMaxScaler()
scaler.fit(X)
X = scaler.transform(X)
X_train,X_test,y_train,y_test = train_test_split(X,y, test_size=0.3, random_state=123)
knn = KNeighborsClassifier(n_neighbors=3)
knn.fit(X, y)
y_predict_train = knn.predict(X_train)
y_predict_test = knn.predict(X_test)
acc_train = accuracy_score(y_train, y_predict_train)
acc_test = accuracy_score(y_test, y_predict_test)
print("train set accuracy {:.2f}".format(100*acc_train))
print("test set accuracy {:.2f}".format(100*acc_test))
train set accuracy 96.77
test set accuracy 98.15