问题简述:我有一组数据,数据量是五百多,维度是30,分类有两种,要使用knn算法对数据进行预测。
一、伪代码
(1)整个过程的伪代码
Begin:
Input: 数据集datasets, k值数组;
Do:
对datasets进行归一化,并将其按照7:3的比例划分成训练集和测试集
利用训练集的数据,根据k折交叉验证,获取最优k值
确定k值后使用knn算法对测试集的数据进行预测
Output: 测试集中各个数据所属的类别。
End
(2)knn算法伪代码
Begin:
Input: 数据集a,待测数据b,k
Do:
测量b与a中所有点的欧氏距离,得到从根据距离从近到远排列的列表list,返回其中前k个元素
对k个元素的标签进行分析,得到各种标签的数量,根据投票法,数量最大的标签就是b的标签
Output:b的标签
二、knn算法
工作原理:一个样本数据集合,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类对应的关系。输入没有标签的数据后,将新数据中的每个特征与样本集中数据对应的特征进行比较,提取出样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k近邻算法中k的出处,通常k是不大于20的整数。最后选择k个最相似数据中出现次数最多的分类作为新数据的分类。Knn算法属于懒惰学习,不会预先进行训练,而是在使用时才会训练。
三、数据的处理
1、数据的预处理
(1)数据归一化
当数据存在多个特征时,其中某些特征数量级较大,其他较小时最后的分类结果会被该特征所主导,而弱化了其他特征的影响,这是因为各个特征的量纲不同所致,需要将数据进行归一化。我采用的是离差标准化,是对原始数据的线性变换,使结果落到[0,1]区间。
(2)数据的随机化
原始数据的不合理的排序有可能导致最后出现过拟合的问题,因此,在训练和测试之前,必须进行随机化,我使用的是random.shuffle()方法进行随机化。
四、交叉验证得到k的最优值
K值在本实验中至关重要,k的取值直接回影响到预测的准确性。那么如何取最优k值?
我采用了k折交叉验证法,将数据集等分成几部分,每一部分轮流作为测试集和训练集。每个k值都会进行多次预测,预测结果的正确率取平均值就基本上代表了采用这个k值的的预测效率。
五、进行预测
每一次预测的准确率不一定相同,但波动并不大,这是正常现象。
六、思考
在编码过程中,我想到了几个问题或者是需要改进的地方:一是数据的降维问题,维数太多可能出现维度灾难,那么在维度达到多少的时候需要降维(这次我没有降维)。二是投票法的问题,当两种标签的训练数据量差别较大时,单纯使用投票法很可能得到错误的标签,应该考虑加权。第三点跟第二点有一定联系,就是两种标签的数据量差距很大的时候,怎样避免过拟合的情况发生。
主要模块,包括了各种需要调用的方法:
from re import sub
from os import listdir
from collections import Counter
from itertools import chain
import jieba
import PIL.Image as im
from sklearn.naive_bayes import MultinomialNB
from wordcloud import WordCloud
from scipy.misc import imread
import numpy as np
import matplotlib.pyplot as plt
#存放所有文件中的词
#每个元素是一个子列表,其中存放一个文件中的词
allWords = []
def getWordsFromFile(filePath):
words = []
with open(filePath,encoding='utf8') as f:
for line in f:
#移除字符串头尾指定的字符(默认为空格)或字符序列。
line = line.strip()
#过滤干扰字符或无效字符
line = sub(r'[.【】0-9、-。,!~\*]', '', line)
#分词
line = jieba.cut(line)
#过滤长度为1的词
line = filter(lambda word:len(word)>1,line)
#将拆分吧的词组添加到words数组中,此处不能使用append
words.extend(line)
return words
def getTopNWords(topN):
# 按文件编号顺序处理当前文件夹中所有记事本文件
#将所有文件的名称作为txtfiles的元素
txtfiles = [str(i)+'.txt' for i in range(151)]
for txtFile in txtfiles:
allWords.append(getWordsFromFile('C:/Users/gh/Desktop/mails/'+txtFile))
#获取全部单词
#将各个词组进行计数
freq = Counter(chain(*allWords))
#freq.most_common(topN)返回freq中出现次数最多的前topN个
print(freq.most_common(topN))
return [w[0] for w in freq.most_common(topN)]
#全部训练集集中出现次数最多的前400个单词
topWords = getTopNWords(400)
#创建贝叶斯模型,使用已有数据进行训练
#获取特征向量,前400个单词的每个单词在邮件中出现的频率
vector = []
for words in allWords:
temp = list(map(lambda x:words.count(x), topWords))
vector.append(temp)
vector = np.array(vector)
#邮件标签,1表示垃圾邮件,0表示正常邮件
labels = np.array([1]*127 + [0]*24)
#创建模型,使用已知训练集进行训练
model = MultinomialNB()
model.fit(vector, labels)
def predict(txtFlie):
#获取指定邮件文件内容,返回分类结果
words = getWordsFromFile(txtFlie)
currentVector = np.array(tuple(map(lambda x:words.count(x),topWords)))
result = model.predict(currentVector.reshape(1,-1))
return '垃圾邮件' if result == 1 else '正常邮件'
pic=np.array(im.open(r'C:/Users/gh/Desktop/map.jpg'))
# 根据词频生成词云图
wc = WordCloud(font_path=r'C:/Windows/Fonts/STZHONGS.TTF',mask = pic ,background_color="white",max_words=1000,max_font_size=300,
width=3000,height=3000).generate_from_frequencies(Counter(chain(*allWords)))
##使用matplotlib的pyplot来进行最后的渲染出图.
# plt.show(wc)
##目标文件另存为这个名录下
wc.to_file('C:/Users/gh/Desktop/wordsCloud.png')
print('此邮件为'+predict('C:/Users/gh/Desktop/mails/151.txt'))
方法的调用:
import knn
import numpy as np
crudeData = r'C:/Users/gh/Desktop/wbcd.txt'
prediction = []
right = 0
#数据归一化
datasets = knn.normalize(crudeData)
#将数据分成测试数据和训练数据
dataset = datasets[0:len(datasets)*7//10]
predictDatas = datasets[len(dataset):]
#求最优k值
results = knn.compareK([3, 5, 7, 9,11], datasets)
print(results)
#得到的结果是k等于3时准确率最高
# 进行预测
for i in range(len(predictDatas)):
neighbor = knn.findKNeighbors(3, predictDatas[i], dataset)
prediction = knn.getLabel(neighbor)
if prediction == predictDatas[i][1]:
right +=1
print("ID为", int(predictDatas[i][0]), "的数据预测结果为:", chr(int(prediction)), "实际标签是:", chr(int(predictDatas[i][1])))
print("预测数据共:", len(predictDatas), "组,正确率为:", right/len(predictDatas))