李文哲老师详细的讲解了KNN模型,讲的非常详细,直播时因为内容多,额外占用了老师很多的时间,感谢李文哲老师辛苦的付出,感谢幕后做录制上传视频的老师和助教老师们。因为直播时内容太多,当时听的很多不明白,也不知问什么,这篇笔记是看回播写的。
老师介绍KNN是一个AI领域的 The "hello world" Algorithm ,类似于编程入门的经典hello world
KNN算法称:K-Nearest Neighbors
KNN算法即可以作为分类器也可以作为回归模型来使用,今天主要做分类器来演示
KNN是最容易理解的算法也是最容易实现的算法(自己写代码实现)
KNN使用最近的距离来为目标元素分类
KNN中K表示距离目标最近的K个元素,使用K个元素来投票确定K的分类(K为奇数,防止平手,当类别大于2时也会出现平手这种情况时无法避免平手,平手时可以随机返回或用特殊方法来处理避免平手)
K如何影响算法的行为?
通过经典的iris三分类示例讲解KNN的分类使用
# 读取相应的库
from sklearn import datasets #使用sklearn自带的数据
from sklearn.model_selection import train_test_split #把数据分成训练和测试数据集
from sklearn.neighbors import KNeighborsClassifier #从包中引入KNeighborsClassifier算法,包中会有其他的算法
import numpy as np
# 读取数据 X, y
iris = datasets.load_iris() #加载数据,可到UCI dataset上查看数据描述,这个数据集是一个三分类问题
X = iris.data #特征,矩阵类型:N*d N:samples 样本的个数 d:dimension 维度
y = iris.target #label:标签 (这里是0,1,2),向量
print (X, y)
# 把数据分成训练数据和测试数据
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=2003)#指定随机种子,抽取时使用,保证每次一致
# 构建KNN模型, K值为3、 并做训练
clf = KNeighborsClassifier(n_neighbors=3) #这个值就是K
clf.fit(X_train, y_train) #训练 (KNN训练时没有训练的过程,只做了数据的内存分配)
# 计算准确率
from sklearn.metrics import accuracy_score
correct = np.count_nonzero((clf.predict(X_test)==y_test)==True)#准确率的计算
print ("Accuracy is: %.3f" %(correct/len(X_test)))#准确率的计算
#print(accuracy_score(y_test, clf.predict(X_test))) #这里是使用sklearn自定义的函数计算准确率
参数有两大类
- 模型参数:通过训练数据来学习的(KNN里面没有模型参数)
- 超参数:不属于模型的参数,模型外面的参数,指导模型训练(类似开关模型按超参数指定的开关进行训练,用不同的开关学习的模型是不一样的,KNN的超参数就是K,调优是调试超参数)
欧式距离以及KNN实现
- 实现KNN算法需要考虑的几大问题
- 把一个物体表示成向量或矩阵(特征工程,使用特征描述物体,模型的第一步就是做特征工程,不同的业务需求使用或关注的特征不一样)
- 标记号每个物体的标签(i.e., offer/no offer,KNN需要提前标记标签,很多图像问题使用人工打标记)
- 计算两个物体之间的距离/相似度(最重要的问题,最简单的距离计算:欧式距离 )
- 选择合适的K (KNN中最重要的话题,见后面)
- 从零开始自己写一个KNN算法
from sklearn import datasets
from collections import Counter # 为了做投票
from sklearn.model_selection import train_test_split
import numpy as np
# 导入iris数据
iris = datasets.load_iris()
X = iris.data
y = iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=2003)
#欧式距离计算,两维
def euc_dis(instance1, instance2):
"""
计算两个样本instance1和instance2之间的欧式距离
instance1: 第一个样本, array型
instance2: 第二个样本, array型
"""
# TODO
dist = np.sqrt(sum((instance1 - instance2)**2))
return dist
#knn核心函数
def knn_classify(X, y, testInstance, k):
"""
给定一个测试数据testInstance, 通过KNN算法来预测它的标签。
X: 训练数据的特征
y: 训练数据的标签
testInstance: 测试数据,这里假定一个测试数据 array型
k: 选择多少个neighbors?
"""
# TODO 返回testInstance的预测标签 = {0,1,2}
#时间复杂度:O(N) N:样本个数
distances = [euc_dis(x, testInstance) for x in X]
#时间复杂度:O(NlogN) 优化:使用 priority queue(优先级队列)->O(NlogK)
kneighbors = np.argsort(distances)[:k]
count = Counter(y[kneighbors])
print(count)#展示投票结果
return count.most_common()[0][0] #选取出现次数最大的那一项
# 预测结果。
predictions = [knn_classify(X_train, y_train, data, 3) for data in X_test]
correct = np.count_nonzero((predictions==y_test)==True)
#accuracy_score(y_test, clf.predict(X_test))
print ("Accuracy is: %.3f" %(correct/len(X_test)))
- 选择合适的K
理解K对算法的影响,决策边界非常重要
怎么寻找KNN的决策边界?随着K的增加,决策边界会越来越平滑。模型训练出的决策边界越陡峭,说明模型是不太稳定的;模型训练出的决策边界越平滑,有可能是好事,也有可能是模型的准确率不高,各有利弊。如何选K涉及到超参数的选择,交叉验证的过程可以计算出最好的K的值。可以看示例代码来学习
TODO 明天后续学习
通过交叉验证选择K
问题:随着K值的增加,会怎么变化? 决策边界会变得平滑,稳定性很高, 但可能准确率会降低.需要做交叉验证(CrossValidation,也叫CV) ,通常我们也把它归类为"调参"
交叉验证,测试数据不能用来做训练,需要在训练数据拆分出验证数据
对K使用交叉验证遍历出准确率最高的K,这个K就是最终答案,这种交叉检验称之为K-fold Cross,这里的K指的是K折,与KNN的K无关.一般使用5或10,使用10比较多,也可以使用100或200. !!!千万不能用测试数据来调参!!! 数据量少的时候可以适当增加折数
特征缩放(KNN核心之一)
欧式距离计算的时候如果量纲差异太大会让 数值小的特征权重会降低或失去作用,解决方案是:
1. 线性归一化(Min-max Normalization),把特征映射到 0-1 之间
2.标准差标准化(Z-score Normalization) ,把特征映射到 N(0,1) 的高斯分布
具体使用哪种?需要实际测试,哪个方法更好就使用哪个
二手车股价案例(KNN的回归使用)
KNN如何用在回归问题上?
执行老师的例子
下面这张图片是老师在代码中间注释的一段,也执行看了一下
KNN的延伸内容
1.时间的复杂度:KNN的时间复杂度很高,因为新的点需要与所有点计算距离,不适合实时系统
解决方案:
1) 使用KD-Tree 使用各个点来构建一颗树,让相邻的点落在同一个节点上(课后查看KD-Tree)
2) 类似哈希算法– Locality Sensitivity Hashing (LSH) 可解决高纬度问题,但它是一个近似的算法,不是一个精确的算法; 不能保证最好,只是近似; 损失了精确率得到了快速搜索, LSH在搜索领域非常常见
2.如何处理特征的相关性
3.怎么处理样本的重要性
4. 能不能利用Kernel Trick
总结
老师答疑的时候看到了自己的留言^_^
低纬空间里,KNN真的很好用,KNN可以作为搭建AI机器学习框架时的一个baseline的基准
同学提问:大类吞小类问题(数据的不平衡)
感谢李文哲老师多花费自己的时间来详细讲解KNN和KNN的延伸知识.