一、kNN基本介绍
KNN(K-Nearest Neighbor)
- 可以用于分类和回归
- 监督学习算法
二、kNN工作原理
如果一个样本在特征空间中的K个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
- 分类:在该样本点附近找K个最近的点进行投票,票数多的即测试点分类
- 回归:要预测的点的值通过求与它距离最近的K个点的值的平均值得到
分类前要归一化数值,使特征成为等同权重
三、手写kNN算法核心
"""
1.计算已知类别数据集中的点与当前点之间的距离
2.按照距离递增依次排序
3.选取与当前点距离最小的k个点
4.确定前k个点所在类别的出现频率
5.返回前k个点出现频率最高的类别作为当前点的预测分类
"""
def classify0(inX, dataSet, labels, k):
# inX: 测试点 dataSet: 样本集
# labels: 类别 k: 取多少个近邻点
dataSetSize = dataSet.shape[0] # 数据集有几行,即几个样本
diffMat = np.tile(inX, (dataSetSize,1)) - dataSet # tile(a,b)生成b个a的数组
sqDiffMat = diffMat**2 # 每个数都**2
sqDistances = sqDiffMat.sum(axis=1) # 横向相加,即每个样本的距离
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort() # 按距离从小到大排序,返回元素数组下标
classCount={}
for i in range(k):
votelabel = labels[sortedDistIndicies[i]]
classCount[votelabel] = classCount.get(votelabel, 0) + 1
# classCount --> {'B': 2, 'A': 1}
# classCount.items() --> dict_items([('B', 2), ('A', 1)])
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
# sortedClassCount --> [('B', 2), ('A', 1)]
return sortedClassCount[0][0]
应该先数据归一化处理
-
最值归一化:把所有数据映射到0-1之间
适用于分布有明显边界的情况,但受outlier影响较大def autoNorm(dataSet): # 最值归一化 示例代码 minVals = np.min(dataSet,axis=0) maxVals = np.max(dataSet,axis=0) diff = maxVals - minVals normDataSet = np.zeros(dataSet.shape) m = dataSet.shape[0] # 堆叠数组[1,2,3,4] 3行,1列表示竖着堆 normDataSet = dataSet - np.tile(minVals, (m,1)) # 原始值-最小值 normDataSet = normDataSet / np.tile(diff, (m, 1)) # 除以(max-min) return normDataSet, diff, minVals
-
均值方差归一化 (更常用):把所有数据归一到均值为0方差为1的分布中
适用于数据分布没有明显的边界的情况,但有可能存在极端数据值
四、sklearn实现KNN算法
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
# 分割测试数据集
X_train,X_test,y_train,y_test = train_test_split(X,y)
# 创建KNeighborsClassifier 对象
clf = KNeighborsClassifier()
# 拟合训练数据集,得出分类准确度
clf.fit(X_train,y_train)
clf.score(X_test,y_test)
# 使用模型预测测试集
y_predict = knn.predict(X_test)
KNeighborsClassifier() 构造函数的参数及默认值
clf = KNeighborsClassifier(n_neighbors=5,weights=’uniform’, algorithm=’auto’, leaf_size=30, p=2,
metric=’minkowski’,metric_params=None, n_jobs=1, **kwargs)
n_neighbors
:默认为5,就是k-NN的k的值,选取最近的k个点。weights
:默认是uniform,还可以是distance以考虑距离,可以解决“平票”问题algorithm
:快速k近邻搜索算法,默认参数为auto,还可以是ball_tree、kd_tree、bruteleaf_size
:默认是30,这个是构造的kd树和ball树的大小。metric
:用于距离度量,默认度量是minkowski,也就是p=2的欧氏距离p
:minkowski距离中的超参数metric_params
:距离公式的其他关键参数,这个可以不管n_jobs
:并行处理设置。默认为1。如果为-1,那么CPU的所有cores都用于并行工作。
应该先数据归一化处理
from sklearn.preprocessing import StandardScalar # StandardScalar默认采用均值方差归一化
standardScaler = StandardScalar()
standardScaler.fit(X_train) # 保存训练集的均值、方差等
X_train = standardScaler.transform(X_train) # 归一化后的训练集
X_test_standard = standardScaler.transform(X_test) # 归一化后的测试集(用训练集同样地归一化方式)
总结
优缺点
1. 优点
- 简单,精度高,理论成熟,既可以用来做分类也可以用来做回归
- 可用于数值型数据和离散型数据;
- 训练时间复杂度为O(n);
- 无数据输入假定;
- 对异常值不敏感
2. 缺点
-
计算复杂性高;空间复杂性高;
-
样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少);
-
一般样本数很大或维数很高的时候,计算量太大(因为对每一个待分类的测试点都要计算它到全体已知样本的距离,才能求得它的K个最邻近点)。但是样本又不能太少,否则容易发生误分。
-
最大的缺点是无法给出数据的内在含义,解释性不强
问题
- kNN是不需要训练过程的,即训练的时间复杂度为0,为什么有的资料说训练的时间复杂度为O(n)呢,是因为sklearn中的fit函数复杂度是O(n)吗?