第三章-KNN(分类和回归算法模型)
上上一章已经学习了感知机模型、策略和算法,感知机对于分类任务有着其优点,但是该模型是在具有强假设的条件下——训练数据集必须是线性可分的,但是如果数据集是呈现无规则的分布,那么此时如果要做分类任务,还可以考虑k近邻(KNN),这是一种基本的分类和回归方法,既可以做简单的二分类也可以做复杂的多分类任务,还可以做回归任务。
KNN模型
KNN模型实际上对应于对特征空间的划分,虽然没有具体的数学抽象语言描述,但是仍然存在其三要素:距离度量、K值的选择、分类决策规则。
距离度量
K值的选择
除了距离度量外,还有K值的选择对KNN算法的结果也会产生重大影响。
- 如果选择较小的k值,就相当于用较小的领域中的训练实例进行预测,“学习”的近似误差会减小,只有与输入实例较近的实例才会对预测结果起到作用,但缺点就是学习的估计误差就会增大,预测结果就会近邻的实例点非常敏感;
- 如果选择较大的值,学习的误差估计会减小,但是与此同时,近似误差就会增大,这时会出现对于距离比较远的实例点起不到预测作用,使得预测结果错误。
分类决策规则
KNN中的决策规则通常就是“投票选举”——少数服从多数的方式。
如果损失函数是0-1损失函数,那么分类函数就是:
对于相邻k个训练实例点构成集合N,误分类率是:
要使误分类率最小,那么就是要求正确的概率最大,所以少数服从多数的规则正好可以满足经验风险最小化。
KNN算法
算法描述
实现KNN时,主要是考虑的问题时如何对训练数据进行快速K近邻搜索,如果特征空间的维数大或者训练数据容量大时,那么数据存储就是一个大问题。KNN最简单的实现方法是线性扫描,这时当数据集很大,计算就非常地耗时。为了提高这种搜索效率,使用特殊地结构进行存储训练数据——kd树(kd tree)。kd树是一种对k维空间中的点进行存储以便于对其进行快速搜索的树形数据结构。实质上,kd树是一种二叉树,表示对k维空间的一个划分。
代码实现
自编程实现
class KNN:
"""
使用自编程实现KNN算法
@author cecilia
"""
def __init__(self,X_train,y_train,k=3):
# 所需参数初始化
self.k=k # 所取k值
self.X_train=X_train
self.y_train=y_train
def predict(self,X_new):
# 计算欧氏距离
dist_list=[(np.linalg.norm(X_new-self.X_train[i],ord=2),self.y_train[i])
for i in range(self.X_train.shape[0])]
#[(d0,-1),(d1,1)...]
# 对所有距离进行排序
dist_list.sort(key=lambda x: x[0])
# 取前k个最小距离对应的类别(也就是y值)
y_list=[dist_list[i][-1] for i in range(self.k)]
# [-1,1,1,-1...]
# 对上述k个点的分类进行统计
y_count=Counter(y_list).most_common()
# [(-1, 3), (1, 2)]
return y_count[0][0]
def main():
# 初始化数据
X_train=np.array([[5,4],
[9,6],
[4,7],
[2,3],
[8,1],
[7,2]])
y_train=np.array([1,1,1,-1,-1,-1])
# 测试数据
X_new = np.array([[5, 3]])
# 不同的k(取奇数)对分类结果的影响
for k in range(1,6,2):
#构建KNN实例
clf=KNN(X_train,y_train,k=k)
#对测试数据进行分类预测
y_predict=clf.predict(X_new)
print("k={},class label is:{}".format(k,y_predict))
Sklearn库
from sklearn.neighbors import KNeighborsClassifier
def sklearn_knn():
"""
使用sklearn库实现KNN算法
@author cecilia
"""
X_train=np.array([[5,4],
[9,6],
[4,7],
[2,3],
[8,1],
[7,2]])
y_train=np.array([1,1,1,-1,-1,-1])
# 待预测数据
X_new = np.array([[5, 3]])
# 不同k值对结果的影响
for k in range(1,6,2):
# 构建实例
clf = KNeighborsClassifier(n_neighbors=k,n_jobs=-1)
# 选择合适算法
clf.fit(X_train, y_train)
# print(clf.kneighbors(X_new))
# 预测
y_predict=clf.predict(X_new)
#print(clf.predict_proba(X_new))
print("accuracy:{:.0%}".format(clf.score([[5,3]],[[1]])))
print("k={},label lcass is:{}".format(k,y_predict))
结果显示:
思考
KNN算法模型的复杂度主要是体现在哪儿?什么情况下会造成过拟合?
k临近算法的模型复杂度体现在k值上;k值较小容易造成过拟合,k值较大容易造成欠拟合。