KNN算法,也称K近邻算法,是一种监督学习的分类算法。
本篇文章主要由以下几个方面构成:
KNN算法,即在已知训练集数据所对应标签的情况下,去预测测试集数据所对应的标签,其算法核心就是要找到其训练集数据与其标签之间的对应关系。
伪代码:
优点:简单易懂,精度高,对异常值不敏感
缺点:计算复杂度高,空间复杂度高
项目概述:
该项目主要由三大块组成:
用到的Python模块:numpy、os模块中listdir函数、operator模块
首先导入模块:
import numpy as np
import operator
from os import listdir #从os模块中导入listdir函数,实现读取文件夹下的所有文件名功能
然后进行数据预处理:
#将32*32转换成1*1024
def img2vector(filename):
# 创建向量
returnVect = np.zeros((1, 1024))
# 打开数据文件,读取每行内容
fr = open(filename)
for i in range(32):
# 读取每一行
lineStr = fr.readline()
# 将每行前 32 字符转成 int 存入向量
for j in range(32):
returnVect[0, 32*i+j] = int(lineStr[j])
return returnVect
接着构建KNN分类器:
#KNN分类器
import operator
def classify0(inX, dataSet, labels, k):
"""
参数:
- inX: 需要预测分类的当前测试集数据
- dataSet: 输入的训练集数据
- labels: 训练集数据的标签向量
- k: 用于选择最近邻居的数目
"""
# 获取训练数据集的行数
dataSetSize = dataSet.shape[0]
# 矩阵运算,计算测试数据与每个样本数据对应数据项的差值
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
# sqDistances 上一步骤结果平方和
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
# 取平方根,得到距离向量
distances = sqDistances**0.5
# 按照距离从低到高排序
sortedDistIndicies = distances.argsort()
# 依次取出最近的样本数据
for i in range(k):
# 根据索引,找到该样本数据所属的标签
voteIlabel = labels[sortedDistIndicies[i]]
# 建立一个字典,用于存放标签出现的频次,统计标签出现的频次
classCount = {}
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
# 对标签出现的频次进行排序,从高到低
sortedClassCount = sorted(
classCount.items(), key=operator.itemgetter(1), reverse=True)
# 返回出现频次最高的类别
return sortedClassCount[0][0]
注意:
举例说明:建立一个2×4的单位矩阵e, e.shape为(2,4),表示2行4列,第一维的长度为2,第二维的长度为4。e.shape[0]为2,e.shape[1]为4。
tile函数的功能是对一个矩阵进行重复,得到新的矩阵,例如:b=np.tile(a,(3,1)),得到b=[a,a,a](将a重复3行)
argsort函数是返回一个数组升序排列的索引值,例如:x = np.array([3, 1, 2]),np.argsort(x),返回array([1, 2, 0])
operator.itemgetter()返回的是一个函数
operator.itemgetter(1)按照第二个元素的次序对元组进行排序,reverse=True是逆序,即按照从大到小的顺序排列
所以 sorted这里的意思是:
classCount.items()将classCount字典分解为元组列表
即由变成
并且按第二个元素进行从大到小的排列
最后return sortedClassCount[0][0],就是返回sortedClassCount的第一行第一列,即频数最高的那个对应的分类标签
最后是该项目的主程序,对所有测试集数据进行分类预测:
def handwritingClassTest():
# 样本数据的类标签列表
hwLabels = []
# 样本数据文件列表
trainingFileList = listdir(r'C:\Users\lenovo\Desktop\PYTHON\识别手写数字\trainingDigits')
m = len(trainingFileList)
# 初始化样本数据矩阵(M*1024)
trainingMat = np.zeros((m, 1024))
# 依次读取所有样本数据到数据矩阵
# fileNamestr里存放的是当前训练集文件名,提取文件名中的数字
for i in range(m):
fileNameStr = trainingFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
hwLabels.append(classNumStr)
# 将样本数据存入矩阵
trainingMat[i, :] = img2vector(r'C:\Users\lenovo\Desktop\PYTHON\识别手写数字\trainingDigits/%s' % fileNameStr)
# 循环读取测试数据
testFileList = listdir(r'C:\Users\lenovo\Desktop\PYTHON\识别手写数字\testDigits')
mTest = len(testFileList)
# 初始化错误率
errorCount = 0.0
# 循环测试每个测试数据文件
for i in range(mTest):
# fileNamestr里存放的是当前测试集文件名,提取文件名中的数字
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0]
classNumStr = int(fileStr.split('_')[0])
# 提取数据向量
vectorUnderTest = img2vector(r'C:\Users\lenovo\Desktop\PYTHON\识别手写数字\testDigits/%s' % fileNameStr)
# 对数据文件进行分类,K值选取为3
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
# 打印 K 近邻算法分类结果和真实的分类
print("测试样本 %d, 分类器预测: %d, 真实类别: %d" %
(i+1, classifierResult, classNumStr))
# 判断K 近邻算法结果是否准确
if (classifierResult != classNumStr):
errorCount += 1.0
# 打印错误率
print("\n错误分类计数: %d" % errorCount)
print("\n错误分类比例: %f" % (errorCount/float(mTest)))
注意:
最后的最后不要忘了运行主程序哦:
handwritingClassTest()
修改K值,分类结果的准确率不同:
K=2,错误计数13
K=3,错误计数10
K=4,错误计数11
K=5,错误计数17
K=6,错误计数17
最佳K值为3