目录
一、KNN算法介绍
1.1概述
KNN(k-近邻算法)是一种基于实例的学习,或者说基于标签的数据分类方法。这种方法会预测一个新的观察对象的类别,该类别是该对象最接近的已知对象的k个邻居中大多数出现的那一个。即k-近邻算法采用测量不同特征值之间的距离方法进行分类。
1.2特点
KNN的主要特点如下:
1. 它是一种基于统计的分类方法,依赖于已经标注的训练数据。
2. KNN的决策过程基于距离,距离近的点被认为更有可能是同一类别的点。
3. KNN方法是一种简单且直观的算法,易于理解和实现。
1.3优缺点
优点:精度高、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高。
1.4k-近邻算法的一般流程
收集数据 | 可以使用任何方法 |
准备数据 | 距离计算所需要的数值,最好是结构化的数据格式 |
分析数据 | 可以使用任何方法 |
训练算法 | 此步骤不适用于k-近邻算法 |
测试算法 | 计算错误率 |
使用算法 | 首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理 |
二、距离的度量方式
从第一部分的介绍中我们可以得知,得出未知样本与训练样本之间的距离在KNN中是特别重要的(这里的距离大小实际上反映的是两个实例点之间的相似度)。对此,笔者给出以下两个比较常用的计算公式:
1.欧式距离
2.曼哈顿距离
三、代码实现
3.1数据收集
这里的实例用的数据集是葡萄酒的品种预测,对不同特点的数据的类别分为黑皮诺和赤珠霞。其样本特征为:颜色深度、酒精浓度。
代码:
import numpy as np
import pandas as pd
rowdata = {'颜色深度':[14.23,13.2,13.16,14.37,13.24,12.07,12.43,11.79,12.37,12.04],
'酒精浓度':[5.64,4.38,5.68,4.80,4.32,2.76,3.94,3. ,2.12,2.6 ],
'品种':[0,0,0,0,0,1,1,1,1,1]}
wine_data = pd.DataFrame(rowdata)
X = np.array(wine_data.iloc[:,:2]) #特征(颜色深度/酒精浓度)
y = np.array(wine_data.iloc[:,-1]) #标签 (品种:0黑皮诺,1赤霞珠)
print(wine_data)
运行结果:
3.2数据分析
代码:
import matplotlib.pyplot as plt # 设置中文显示,windows系统
plt.rcParams['font.sans-serif'] = 'SimHei' #设置中文显示,mac系统
plt.rcParams['axes.unicode_minus'] = False
plt.style.use('ggplot')
plt.figure(figsize = (9,6),dpi=200) #设置窗口的宽和高,以及分辨率
#new_data = np.array([12.8,4.1])
plt.scatter(X[y==0,1],X[y==0,0],color = 'purple',label = '黑皮诺')
plt.scatter(X[y==1,1],X[y==1,0],color = 'red',label = '赤霞珠')
#plt.scatter(new_data[1],new_data[0],color = 'yellow')
plt.xlabel('酒精浓度')
plt.ylabel('颜色深度')
plt.legend(loc='lower right')
plt.show()
运行结果:
3.3归一化数值
数据归一化是一种将数据值映射到特定范围内的方法,通常是将数据值映射到[0,1]区间。它是一种常见的预处理步骤,旨在使数据更易于处理和比较。数据归一化的具体方法包括最大-最小归一化、Z-score标准化等,可以根据数据的特点和算法的需求选择适当的方法。在本实例中,采用的是最大-最小归一化处理方法。
代码:
def autoNorm(dataSet):
minVals =dataSet.min(0)
maxVals = dataSet.max(0)
ranges =maxVals-minVals #最大值与最小值的差值
normDataSet=np.zeros(np.shape(dataSet)) #创建一个全零矩阵 normDataSet
m= dataSet.shape[0]
normDataSet =dataSet-np.tile(minVals,(m,1))#原始值减去最小值
normDataSet =normDataSet/np.tile(ranges,(m,1)) #对数据进行归一化
return normDataSet,ranges,minVals #返回数据集、各特征值的最大最小差值及其最小值
wine_data_after = autoNorm(wine_data)
print(wine_data_after)
运行结果:
分析:从运行结果可以看出,数据集中的数据都已经完成了归一化处理。
3.4分类
本实例中,分类器计算距离的度量方式为欧拉距离公式。
代码:
def KNN(inX,dataset,k):
'''
函数功能:KNN分类器
参数说明:
inX: 需要预测分类的数据集
dataSet: 已知分类标签的数据集
k: k-近邻算法参数,选择距离最小的k个点
return:
result: 分类结果
'''
from math import sqrt
from collections import Counter
import numpy as np
import pandas as pd
result=[]
distance = [sqrt(np.sum((x-inX)**2)) for x in np.array(dataSet.iloc[:,0:2])]
sort_dist = np.argsort(distance) #先升序排序,返回排序后的索引
topK = [dataSet.iloc[:,-1][i] for i in sort_dist[:k]] #返回最近k个点的y值
result.append(Counter(topK).most_common(1)[0][0])
return result
#测试
new_data = np.array([12.8,4.1])
inX = new_data
dataSet = wine_data
k = 3
print('输出[0]为黑皮诺,[1]为赤霞珠:')
result = KNN(inX,dataSet,k)
print(result)
运行结果:
分类器测试:
#对占样本中百分之四十进行验证检验
hoRatio=0.40
#数据集的行数
m =dataSet.shape[0]
#测试的行数
numTestVecs =int(m*hoRatio)
#错误次数统计
errorCount=0.0
#分类
for i in range(numTestVecs):
classifierResult= KNN(X[i],dataSet,3)
print ("分类结果: %d, 真实: %d"%(classifierResult[0],y[i]))
if(classifierResult[0] != y[i]):
errorCount += 1.0
print ("the total error rate is:%f"%(errorCount/float(numTestVecs)))
运行结果:
分析:此分类器可以将某个例子划分类别,其错误率为0,但由于数据集中的样本案例不够充分,所以可能存在一定的偏差。
四、模型评估介绍
4.1概述
模型评估(model assessment)是指对于一种具体方法输出的最终模型,使用一些指标和方法来评价它的泛化能力。模型评估一般可以分为回归、分类、聚类的任务,不同任务有不同评价指标。根据想要得到的目标值,可以把模型评估分为分类模型评估和回归模型评估。
4.2过拟合与欠拟合
过拟合:通俗来说,过拟合就是模型在训练数据中学得太好了,以至于它不仅学习了训练数据中的基本模式,还学习了训练数据中的噪声或异常值,把训练集的数据的特点当作所有样本的一般性质,以至于在未见过的测试数据上表现很差。
欠拟合:欠拟合是指模型对训练样本的一般性质学得不够,导致模型在训练集和测试集上表现都很差。
4.3模型泛化能力
是指机器学习算法对新鲜样本的适应能力。学习的目的是学到隐含在数据背后的规律,对具有同一规律的学习集以外的数据,经过训练的网络也能给出合适的输出,该能力称为泛化能力 。
五、常见分类模型评估
准确率、精确率、召回率、F1_score,混淆矩阵,AUC,PR等等。
5.1混淆矩阵
混淆矩阵也称误差矩阵,是表示精度评价的一种标准格式,是监督学习中的一种可视化工具,主要用于模型的分类结果和实例的真实信息的比较 ,用n行n列的矩阵形式来表示。具体评价指标有总体精度、制图精度、用户精度等,这些精度指标从不同的侧面反映了图像分类的精度。其中,矩阵中的每一行代表实例的预测类别,每一列代表实例的真实类别。
真实值是positive,模型认为是positive的数量(True Positive=TP);
真实值是positive,模型认为是negative的数量(False Negative=FN):这就是统计学上的第一类错误(Type I Error);
真实值是negative,模型认为是positive的数量(False Positive=FP):这就是统计学上的第二类错误(Type II Error);
真实值是negative,模型认为是negative的数量(True Negative=TN)
混淆矩阵 | 真实值 | ||
Positive | Negative | ||
预测值 | Positive | TP | FP(Type II) |
Negative | FN(Type I) | TN |
5.2准确率、精确率、召回率、F1_score
5.2.1准确率
对于给定的测试集,分类模型正确分类的样本数与总样本数之比;
Accuracy = (TP+TN)/(TP+FN+FP+TN)
5.2.2精确度
对于给定测试集的某一个类别,分类模型预测正确的比例,或者说:分类模型预测的正样本中有多少是真正的正样本;
Precision = TP/(TP+FP)
5.2.3召回率
对于给定测试集的某一个类别,样本中的正类有多少被分类模型预测正确召回率的定义为:对于给定测试集的某一个类别,样本中的正类有多少被分类模型预测正确;
Recall = TP/(TP+FN)
5.2.4F1——score
F值是精确度和召回率的调和值。主要用于评估模型的稳健性。
2/F1 = 1/Precision + 1/Recall
5.3AUC
AUC(Area Under Curve)被定义为ROC曲线下的面积(ROC的积分),通常大于0.5小于1。随机挑选一个正样本以及一个负样本,分类器判定正样本的值高于负样本的概率就是 AUC 值。AUC值(面积)越大的分类器,性能越好。
5.4P-R曲线
PR曲线的横坐标是精确率P,纵坐标是召回率R。前面提到的F1分数就是PR曲线的平衡点。
六、ROC曲线
6.1ROC曲线的优势
ROC曲线(Receiver Operating Characteristic curve)和PR曲线(Precision-Recall curve)都是用于评估分类模型性能的可视化工具,但它们关注的指标不同。
ROC曲线全称为接收者操作特征曲线,是一种用于评估二分类模型性能的工具。是以False Positive Rate(FPR,假阳性率)为横轴,True Positive Rate(TPR,真阳性率)为纵轴绘制的曲线。它主要用于评估模型在不同阈值下对正负样本的区分能力。ROC曲线下的面积(AUC,Area Under Curve)越大,说明模型的性能越好。ROC曲线适用于正负样本不平衡的情况,因为它关注的是整体的区分能力,而不是某一类样本的区分能力。
P-R曲线是以False Positive Rate(FPR,假阳性率)为横轴,Precision(精确率)为纵轴绘制的曲线。它主要用于评估模型在特定阈值下对正样本的召回能力。PR曲线越靠近左上角,说明模型的性能越好。PR曲线适用于正负样本平衡的情况,因为它关注的是正样本的召回能力。
相比P-R曲线,ROC曲线有个很好的特性:当测试集中的正负样本的分布发生变化的时候,ROC曲线能够保持稳定。在实际应用中,正例和负例的数量往往存在较大的差异。ROC曲线可以很好地处理这种不平衡情况,因为它关注的是整体的TPR和FPR,而不是某一类别的数量。此外, ROC曲线能够综合考虑模型的敏感性和特异性。通过观察ROC曲线的形状,我们可以了解到在不同阈值下模型的TPR和FPR的变化情况,从而选择最佳的阈值来平衡模型的准确性和误报率。
6.2与P-R曲线的差异
1. 关注指标不同:ROC曲线关注的是整体的区分能力,而PR曲线关注的是正样本的召回能力。
2. 适用场景不同:ROC曲线适用于正负样本不平衡的情况,而PR曲线适用于正负样本平衡的情况。
3. 阈值选择不同:ROC曲线需要选择一个固定的阈值来绘制曲线,而PR曲线可以针对不同的阈值绘制多条曲线。
4. 对异常值敏感程度不同:ROC曲线对异常值较为敏感,因为异常值可能导致TPR和FPR的变化;而PR曲线对异常值相对不敏感,因为精确率主要受正样本的影响。
七、ROC曲线绘制
7.1数据扩充
由于原始数据集数据过少,呈现的曲线效果不好,因此,先对数据集进行一个数据扩充。
# 扩充数据集
rowdata['颜色深度'].extend(np.random.rand(150) * 15)
rowdata['酒精浓度'].extend(np.random.rand(150) * 7)
rowdata['品种'].extend([0] * 149 + [1])
# 将数据集转换为csv文件
df = pd.DataFrame(rowdata)
df.to_csv('rowdata.csv', index=False)
7.2绘制曲线
#ROC曲线绘制
from sklearn.metrics import roc_auc_score,roc_curve,auc,precision_recall_curve, average_precision_score
from sklearn import metrics
from sklearn.model_selection import train_test_split
from matplotlib.font_manager import FontProperties
from sklearn.neighbors import KNeighborsClassifier
# 划分特征和标签
X = df[['颜色深度', '酒精浓度']]
y = df['品种']
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)
# kNN模型训练和预测
k_values = [3, 5, 6,7]
for k in k_values:
knn = KNeighborsClassifier(n_neighbors=k)
knn.fit(X_train, y_train)
y_pred = knn.predict_proba(X_test)[:, 1]
# 计算ROC曲线和AUC
fpr, tpr, _ = roc_curve(y_test, y_pred)
roc_auc = auc(fpr, tpr)
# 绘制ROC曲线
plt.figure()
plt.plot(fpr, tpr, label='KNN (k=%d), AUC = %.2f' % (k, roc_auc))
plt.plot([0, 1], [0, 1], 'k--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('Receiver Operating Characteristic')
plt.legend(loc="lower right")
plt.show()
7.3运行结果
7.4结果分析
通过运行上述代码,我们可以得到不同k值的ROC曲线。从图中可以看出,随着k值的增加,ROC曲线下的面积(AUC)逐渐增大。在k值为5时,AUC最大,说明模型的整体性能更好。这说明在分类问题中,增加k值可以提高模型的性能。然而,当k值过大时,可能会导致过拟合,从而降低模型的泛化能力。因此,选择合适的k值对于提高模型性能至关重要。不过,由于该数据集的数据是随机生成的,虽然笔者有进行部分数据调整,但最后曲线呈现的效果也不是很好。建议直接去网上找别人分享的数据集,不要自己写T-T。
八、总结
通过本次学习,我理解了KNN的基本概念和工作原理,了解数据归一化的重要性,学会了如何选择和调整k值和距离度量方式。其中k值的选择会对分类结果有较大影响。k值太小容易过拟合,k值太大容易欠拟合,因此要根据实际情况选择合适的k值。通过实践应用KNN算法,可以更好地掌握KNN的应用场景和注意事项,提高预测精度和稳定性。
模型评估是确保模型性能的关键步骤。学习ROC曲线,我深感它的重要性在于: 1. 理解模型的分类能力:ROC曲线越接近左上角,说明模型的分类性能越好,假阳性率和假阴性率都低。 2. 选择最优阈值:曲线下的面积(AUC)越大,模型整体性能越好。通过ROC曲线,可以直观地选择最合适的阈值来平衡敏感性和特异性。 3. 模型比较:不同模型的ROC曲线可以直观比较,曲线之上表示性能优于曲线之下。 通过学习ROC曲线,我更加认识到模型评估的全面性和模型选择的灵活性,这对于优化模型并在实际应用中取得最佳效果至关重要。