一、前言
前面的一篇文章介绍了KNN算法的基本思想,接下来我们就根据B站UP主【abilityjh】老师的节奏,做一个关于KNN算法运用于“约会网站配对”的算法实现。当然,这个实践的代码是一样的,但是理解的话,我是用自己的话来阐述自己敲完这个算法后的理解,可能更多的是一些大白话,大家也可以去看这个老师的原视频,会有很多收获哦!
二、案例介绍
文本数据:
案例描述:我们需要通过这个DataText.txt文档中的数据来测试我们KNN的准确性,该文档有1000条数据,我们把其中一部分拿来做训练集,另一部分拿来做测试集,结合我们的KNN算法就可以来测试KNN算法的准确性。
三、KNN算法实践
(1)实践思路
如图所示:我们先用将我们txt中的文本数据,转化为对应信息的矩阵或者列表,然后我们通过txt中的数据发现“里程数”的数据比其他两项的数据大了很多,如果这样来求未知点和训练数据中点的距离的话,后两项数据太小基本,没有作用,所以这里我们通过0-1标准化对里程数据做归一化处理;然后我们把之前写的KNN算法封装成一个函数调用,再测试KNN准确性即可。(PS:虽然流程简单,但是代码实现过程中还是有很多坑!)
(2)代码实现
①实现文件数据读取
def file2matrix(filename):
fr = open(filename)
numberOfLines = len(fr.readlines())
# 定义一个numberOfLines行,3列的零矩阵
# 一定要加两个括号
returnMat = np.zeros((numberOfLines, 3))
# 标签向量
classLabelVector = []
fr.close()
fr = open(filename)
index = 0
for line in fr.readlines():
# 去掉每一行的前后空格
line = line.strip()
# 根据tab将数据切割出来,listFromLine就成了一个字符串列表
listFromLine = line.split("\t")
# listFromLine[0:3]将listFromLine的前三列赋值给returnMat[]数组,因为returnMat有numberOfLines行,就会执行numberOfLines次操作
returnMat[index, :] = listFromLine[0:3]
# listFromLine[-1]表示最后一列
if listFromLine[-1] == 'didntLike':
classLabelVector.append(1)
elif listFromLine[-1] == 'smallDoses':
classLabelVector.append(2)
elif listFromLine[-1] == 'largeDoses':
classLabelVector.append(3)
index += 1
fr.close()
return returnMat, classLabelVector
datingDataMat, datingLabels = file2matrix("D:/python工具/素材处/KNN(二)/DataText.txt")
print(datingDataMat)
print(datingLabels)
在这段代码中,我觉得最重要的几个点就是:文件读取、对每行数据处理(确保能分割成矩阵或者列表!)。代码的理解,我都打上了注释,大家可以仔细看看。
效果:
②数据归一化处理
从上面的数据,可以看出里程数的数值是很大的,然后我们用0-1标椎化做处理,将里程数的每个数据缩小在0~1之间。
# 数据归一化处理
# 0-1标注化
def autoNorm(dataSet):
# min(0)按特征的列选取,min(1)按照特征的行选取
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
# shape记录了dataSet的行和列
normDataSet = np.zeros(dataSet.shape)
normDataSet = (dataSet - minVals) / (maxVals - minVals)
return normDataSet
dataSet = autoNorm(datingDataMat)
print(dataSet)
print(datingLabels)
效果:
③对KNN代码封装
代码原理上一篇博客已经介绍
# inX输入数据,dataSet测试数据,labels标签,k最近的点的个数
def knn(inX, dataSet, labels, k):
dist = (((dataSet - inX) ** 2).sum(1)) ** 0.5
sortdDist = dist.argsort()
classCount = {}
for i in range(k):
voteLabel = labels[sortdDist[i]]
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
maxType = 0
maxCount = -1
for key, value in classCount.items():
if value > maxCount:
maxType = key
maxCount = value
return maxType
④调用KNN算法进行测试
m = 0.8
# shape[0]表示行、shape[1]表示列
dataSize = dataSet.shape[0]
print("数据集的总行数:", dataSize)
# 80%作为训练 20%作为测试
trainSize = int(m * dataSize)
testSize = int((1 - m) * dataSize)
k = 5
result = []
error = 0
# 调用封装好的KNN函数
for i in range(testSize):
# dataSet[trainSize + i - 1, :]表示dataSet矩阵中的第trainSize + i - 1行元素(包含对应的所有列)
# dataSet[0:trainSize, :]表示一个从0~trainSize行,包含对应所有列的矩阵
# datingLabels[0:trainSize]表示截取列表datingLabels中的0~trainSize个元素
result = K.knn(dataSet[trainSize + i - 1, :], dataSet[0:trainSize, :], datingLabels[0:trainSize], k)
# result != datingLabels[trainSize + i - 1]表示通过KNN算法得到的result是否与原来的datingLabels中对应下标为trainSize + i - 1是否相等
if result != datingLabels[trainSize + i - 1]:
error += 1
print('错误率:', error / testSize)
这段代码中,我觉得对新手来说最不好理解的就是for循环中的dataSet矩阵的切片操作,我自己也是下来梳理了一下,才通顺起来,相应代码的注解放在代码中了,大家自己取用。
效果:
可见,我们1000条数据,用80%做训练集,20%做测试集,K选用为5用使用KNN算法的出错率,只有5.52%,其实还是可以的。(大家也可以改变数据,再测试一下)
PS:当然KNN算法的优缺点以及使用场景,就不止这些,大家可以下来好好探索一番,我这里就主要梳理一下代码啦!
四、总结
博主作为一个新手,才开始学习机器学习而言,我觉得这篇关于KNN算法的实践,对自己帮助挺大的,让我学到了以下很多知识:
①关于文件数据的读取;
②数据的处理、归一化;
③函数的封装调用;
④矩阵和列表的一些切片操作;
最后,如果这篇文章对大家有所帮助,别忘了点赞、关注支持博主一波哦~,你们的助力,就是我持续更新的动力!
PS:如果有需要这个DataText.txt数据集的小伙伴,直接私信我,即可发送。