knn既能用于回归,也能用于分类,属于有监督算法的一种,但严格意义上并没有学习过程(最终是计算样本点之间的距离)。
knn分类的核心思想:每次在预测一个样本x时,通过k值确定与其最近的k个邻居,并依次算这几个邻居到样本x的距离,按从小到大排序然后根据最多数邻居的类别作为样本x的预测值。(回归的话是邻居的值做平均赋给样本x,但和分类的核心思想一致均为找出k近邻)
最关键的参数选取:k和距离
(1)k需要交叉验证来找到最合适的值。k值不能过大,过大虽然可以减小噪声,但样本太多导致类别界限不清晰;过小则预测错误概率变大,容易过拟合。
(2)距离:曼哈顿距离(p=1特例),欧氏距离(p=2特例)以及闵可夫斯基距离(p=n)
实现思路:
(1)给出训练集和测试集
(2)计算测试集中每一个数据和训练集中的数据的距离
(3)排序距离结果,找出前k个值
(4)统计k个值中最多出现的标签值作为预测标签
代码:
import math
from sklearn import neighbors
class knnClass(object):
def __init__(self,k,p,train_dataset,test_data,train_dataset_labels,):
self.k = k
self.p = p
self.train_dataset = train_dataset
self.test_dataset = test_data
self.train_dataset_labels = train_dataset_labels
def knn_main(self):
if self.p <= 0:
return
"""计算距离"""
distance = []
for train_sample in self.train_dataset:
dis = 0
for vec in range(0,len(train_sample)):
dis += math.pow((train_sample[vec] - self.test_dataset[vec]),self.p)
dis = pow(dis,1/self.p)
distance.append(dis)
"""计算最邻近的k个值"""
index_list = []
for i in range(0,self.k):
index_i = distance.index(min(distance))
index_list.append(index_i)
distance[index_i] = float('inf')
"""取出最近邻k个值的索引并找到对应标签"""
k_dis_label = []
for j in index_list:
k_dis_label.append(self.train_dataset_labels[j])
"""取出出现最多的标签"""
predict_label = max(k_dis_label,key=k_dis_label.count)
return predict_label
def train(k,train_data,test_data,train_data_labels):
knn = neighbors.KNeighborsClassifier(n_neighbors=k,)
knn.fit(train_data,train_data_labels)
return knn.predict(test_data)
if __name__ == '__main__':
train_data = [[2,9],[3,12],[6,15],[1,5],[2,7],[4,13],[18,6],[15,4],[11,8],[17,3],[12,11],[19,10],[8,19]]
train_data_labels = [1,1,1,1,1,1,0,0,0,0,0,0,1]
test_data = [[5,5]]
test_data_label = 2
knn_obj = knnClass(1,2,train_data,[5,5],train_data_labels)
predict_label = knn_obj.knn_main()
print(predict_label)
print(train(1,train_data,test_data,train_data_labels))
优点:
1.可以用于非线性分类
2.训练时间复杂度比支持向量机之类的算法低(O(n),n为样本数)
3.和朴素贝叶斯之类的算法比,对数据没有假设,准确度高,对异常点不敏感
4. 天然适合多分类
5.由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属的类别,因此对于类域的交叉或重叠较多的待分类样本集来说,KNN方法较其他方法更为适合
缺点:
1.计算量大,尤其在特征数非常多的时候(维数灾难);另外,树之类的模型建立需要大量的内存
2.不能很好的解决数据不平衡问题
3.慵懒学习方法,几乎不学习,预测时速度比起逻辑回归之类的算法慢
4.依赖训练数据,如果数据依赖性过大无法准确分类(如距离太近)
优化的考虑:
(1)k值
(2)距离
(3)暴力计算、KD树等
(4)邻居加权