赞
踩
K最近邻算法是一种监督学习算法。K最近邻指的是:每个样本都可以由离它最近的K个邻居来代表。KNN和SVM算法都是一种距离测度进行分类的算法。思路是:物以类聚。如果样本周围的K个样本都属于某一类,那么样本也应该属于同一类别。K近邻中所选择的邻居都是已经正确分类的对象。因此,KNN的分类结果只取决于和他临近的几个样本的类别。
K的选择结果不同会直接影响到分类结果,以上图为例,绿色圆点代表测试点。三角形代表蠢人,方形代表聪明人。想要知道绿色圆点是哪一类人。当选择K=3时,它周围的邻居大部分都是蠢人,那么分类的结果就是蠢人。当K=9时,它周围人大部分都是聪明人,那么分类的结果就会是聪明人。
K的取值决定了分类结果。当K取值过小时,噪声成分对整体结果影响较大。
当K取值过大时,就意味着,即使离样本点距离较远的点也对样本产生了一定影响。
我们可以想象两个极端,当K取值为1时,模型整体会非常复杂,一旦最近的点是个噪声点,就意味着分类错误。当K取整个样本数时,那么分类的结果是一种先验概率,并不具有参考性。因此,K要选择恰当的取值。
直观上看,KNN只是根据K个邻居中,多数样本点所属类别来进行分类。
具体展开,KNN分类其实是一种距离测度:计算平面上两个点的直线距离,关于距离的度量方法,常用的有:欧几里得距离、余弦值(cos), 相关度 (correlation), 曼哈顿距离 (Manhattan distance)等。
Euclidean Distance 定义:#
两个点或元组P1=(x1,y1)和P2=(x2,y2)的欧几里得距离:
这个距离是两点间的直线距离,解法平方和后开根号。
距离公式为:(多个维度的时候是多个维度各自求差)
算法具体执行如下:
1.计算测试数据与各个训练数据之间的距离
2.按照距离递增关系进行排序
2.选取距离最近的K个点
4.确定各个点所在类别出现的频率
5.返回K个点中出现频率最高的类别作为测试结果
实验语料1是从新浪微博搜集的600条地震灾情信息,按有效和无效标记为正类和负类。属于中文短文本二分类。
实验语料2是从THUCNews文本中抽取的14种不类别的新闻,每种新闻选择200条,共2800条。属于中文长文本多分类。
同时选取SVM作为外部评价指标,从多角度评估KNN分类器。
短文本数据处理较为简单,学术活动月已经说明,这里只介绍长文本的处理。
主要思路:
1.加载实验数据:(14个类别2800篇文章)
2.分词,去停用词:(载入哈工大停用词表,如果文中词语在停用词表则不存储)
3.返回值:分词后的2800篇文章,2800个类别标签
def text_processor(text_path): data_list = ' ' floder_list = os.listdir(text_path)#THUCNEWS\\科技,经济,军事:类别列表 list_all = [] label = [] for floder in floder_list: new_floder_path = os.path.join(text_path,floder)# 这句是获得文件地址,否则只是个文件名,无法读取 files = random.sample(os.listdir(new_floder_path),200)#遍历类别列表,每个列表选择100篇文章 # todo:对于每一篇文章,最终的结果应该是每篇文章一个字符串 for file in files: with open(os.path.join(new_floder_path,file),mode="r",encoding="utf-8") as fp: Word = fp.read() list = [] word_1 = Word.replace("\n"," ") seglist = jieba.cut(word_1) for word in seglist: word = word.strip() if word not in stopword: list.append(word) a = ' '.join(list) list_all.append(a) label.append(floder.encode("utf-8"))#floder是每一个类别的列表 #print(new_word) return list_all,label list_all,label = text_processor(text_path)#获取数据,标签
list_all为一个长度为2800的列表,列表中每一个元素对应一篇文章,每一篇文章以字符串形式存储,
label为每个文章的对应的类别,共14类。
主要思路:
1.CountVectorizer()函数用于统计词频
2.TfidfTransformer()函数用于训练词向量
上一个模块中返回的单词是按文章出现顺序就行排列的,需要先对这次词语进行词频排序,在用TFIDF转化成词向量矩阵。
# TODO:训练词向量
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
vec=CountVectorizer() # 使用前必须实例化!!!
tf = TfidfTransformer()
x = []
X = vec.fit_transform(list_all)
TFIDF = tf.fit_transform(X) # 文本语料转化为词向量矩阵
思路:训练集:测试集=3:1
#todo:划分训练集,测试集
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(TFIDF,label,test_size=0.33,random_state=1)
因为文章是按类别排序的,不能粗暴的分成7:3(这样会导致测试集中的类别样本,训练集中完全没有出现过),最开始是把标签和文章zip到一起,再随机抽取样本,zip和rezip后,原本的数据是列表形式,rezip后则变为了元组,处理起来比较棘手。后来查到了sklearn中train_test_split函数,两行代码搞定。
思路:在分类过程测试不同的K值:1-200
# TODO:KNN文本分类 import matplotlib.pyplot as plt import matplotlib from sklearn.neighbors import KNeighborsClassifier zhfont = matplotlib.font_manager.FontProperties(fname='C:\Windows\Fonts\simsun.ttc') for i in range(1, 201,1): kn_clf = KNeighborsClassifier(n_neighbors=i) # 返回各自文本的所被分配到的类索引 kn_clf.fit(X_train, y_train) print("-------------------------------------设置K为%d时的分类情况-------------------------------------------" % i) kn_y = kn_clf.predict(X_test) #print("模型预测测试集分类为:", kn_y) #print("测试集原本分类为:", y_test) accuracy_knn = (kn_y == y_test).astype(float).mean() print('分类准确率为:', accuracy_knn) knn_distortions.append(accuracy_knn)
svm对照实验
# todo:svm模型作为对照实验
from sklearn.calibration import CalibratedClassifierCV
from sklearn.svm import LinearSVC
svm_distortions = []
clf = LinearSVC()
svm = CalibratedClassifierCV(clf)
classifier = svm.fit(X_train, y_train)
svm_y = svm.predict(X_test)
accuracy_svm = (svm_y == y_test).astype(float).mean()
print('svm分类准确率为:', accuracy_svm)
svm_distortions.append(accuracy_svm)
短文本中训练集有421条,K取值从1到20,结果如下:
当K取1时,训练集样本测试准确率为1,但测试集测试结果约仅好于扔硬币。这说明,K取值过小,对于训练集过度拟合,因此鲁棒性差。随着K的增大,准确率逐渐上升,***当K=14时,KNN分类结果达到最大值0.86。SVM模型,其分类准确率达到了0.94。***与SVM对比结果如下:
其中,黄色线段描述的是SVM分类结果。蓝色折线是KNN随着不同取值所得到的分类结果,X轴描述的是K值。
观察KNN实验结果可以发现,K取值为奇数时的准确率要高于取偶数时的准确率,其准确率呈现波动上升趋势,查阅了一下网上相关资料:K的取值尽量要取奇数,以保证在计算结果最后会产生一个较多的类别,如果取偶数可能会产生相等的情况,不利于预测。但取偶数具体会产生什么样的分类结果,是随机赋值吗?并没有观测到。
K取1-421,即最大K值取全部测试样本数,随着K逐渐增大,其最终结果约等于扔硬币。
长文本K取值为1-1900,1900为全部测试样本数,实验结果如下:
**在该测试集中,SVM分类准确率为0.895。当K=23时,KNN分类结果达到最大值为: 0.849。**随着K增大到等于训练样本点个数,其分类准确率为: 0.06277056277056277。这个结果甚至低于随机猜测。
KNN是一种快速高效的分类算法,其逻辑简单,易于理解。但当训练数据集很大时,需要大量的存储空寂,而且需要计算训练样本和测试样本的距离,计算量较大。
从小型短文本二分类数据集和大型长文本多分类数据集的测试结果来看,虽然都是依赖于距离度量决定分类结果,但其在中文文本分类中的性能低于SVM。**
**分析:个人认为,这是由于KNN是一种懒惰的学习法,简单的认为“物以类聚,人以群分”,但实际上,无论是人际交往还是数据事实,可能并不完全符合这个逻辑。(机器学习中的蕴含的哲理简直美妙!)而SVM追求类别超平面边界距离最大化过程中,是基于严格的数学推导,因而更具有可靠性,所以取得了更好的分类结果。但我们不能简单的定义一个SVM要优于KNN,要结合具体问题去具体分析。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。