机器学习实战—— K-邻近算法(KNN)
Posted JS_MY
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机器学习实战—— K-邻近算法(KNN)相关的知识,希望对你有一定的参考价值。
K-邻近算法
K-邻近算法概述
KNN采用测量不同特征值之间的距离方法进行分类。
-
优点:精度高、对异常值不敏感、无数据输入假定
-
缺点:计算复杂度高、空间复杂度高
-
适用数据范围:数值型和标称型
-
工作原理:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
手写KNN算法
-
创建数据集并观察特征
import numpy as np from matplotlib import pyplot as plt def createDataSet(): group = np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]]) label = ['A','A','B','B'] return group,label ## 创建数据集 group,label = createDataSet() ## 绘制四个点 plt.scatter(group[0][0], group[0][1],c='b',label='A') plt.scatter(group[1][0], group[1][1],c='b') plt.scatter(group[2][0], group[2][1],c='r',label='B') plt.scatter(group[3][0], group[3][1],c='r') ## 绘制X轴,Y轴 plt.xlabel('X') plt.ylabel('Y') ## 添加图例 plt.legend() ## 添加标题 plt.title("KNN-TEST") ## 图片展示 plt.show()
- 实施KNN算法
import numpy as np
import operator
## 创建数据集
def createDataSet():
group = np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
label = ['A','A','B','B']
return group,label
## 构建分类器
def classify0(inX,dataSet,labels,k):
'''
Parameters
----------
inX : 输入向量
dataSet : 训练样本集
labels : 标签向量
k : 选择最近邻居的数目
-------
'''
# 求数据集中一共多少组数据
dataSet_size = dataSet.shape[0]
## tile函数表示将inX在行方向上重复dataSet_size次,在列方向上重复1次
# 求输入向量与数据集中点横纵坐标之差
diffMat = np.tile(inX,(dataSet_size,1))-dataSet
# 求输入向量与数据集中点横纵坐标之差的平方
sqDiffMat = diffMat ** 2
# 求横坐标之差平方与纵坐标之差平方和
sqDistances = sqDiffMat.sum(axis=1)
# 求距离
distances = sqDistances**0.5
## argsort函数返回一个列表,为原列表从小到大排序后各元素的索引
## 比方说第一个元素为原列表中第二小的元素,argsort函数在新列表中1位置写1
## 第三小,就在该位置写2,以此类推
# 求distances数组中元素从小到大排序后各元素的索引
sortedDistIndices = distances.argsort()
classCount = {}
for i in range(k):
# 依次寻找最邻近点的标签
voteIlabel = labels[sortedDistIndices[i]]
# 在字典中寻找有无标签,没有就添加,有就+1
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
# 将字典拆解为列表
# 遍历classCount字典的键值对,并按value进行排序,升序排列
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
group,label = createDataSet()
category = classify0([-1,-1], group, label, 3)
print(category)
输出结果为B,表示分类器将[-1,-1]这个点分类到了B类别
示例:使用K-邻近算法改进约会网站的配对效果
import numpy as np
import operator
## 将文件内容转为numpy
def file2matrix(filename):
'''
Parameters
----------
filename : 数据文件名
-------
'''
# 将标签映射为数字
love_dictionary = {'largeDoses':3, 'smallDoses':2, 'didntLike':1}
# 打开文件
fr = open(filename)
# 读取每一行
arrayOLines = fr.readlines()
# 计算行数
numberOfLines = len(arrayOLines)
# 创建零初始矩阵
returnMat = np.zeros((numberOfLines, 3))
classLabelVector = []
index = 0
for line in arrayOLines:
# 截取掉所有的回车字符
line = line.strip()
# 将截取后的每行数据分割成一个列表
listFromLine = line.split('\\t')
# 取前三个特征,存储到特征矩阵中
returnMat[index, :] = listFromLine[0:3]
# 如果标签是数字就添加数字,如果不是数字,将映射的数字添加到列表中
if(listFromLine[-1].isdigit()):
classLabelVector.append(int(listFromLine[-1]))
else:
classLabelVector.append(love_dictionary.get(listFromLine[-1]))
index += 1
return returnMat, classLabelVector
## 归一化特征值
def autoNorm(dataSet):
'''
Parameters
----------
dataSet : 数据集
-------
'''
# 返回数据集中每一列的最小值,minVals是一个列表
minVals = dataSet.min(0)
# 返回数据集中每一列的最大值,maxVals是一个列表
maxVals = dataSet.max(0)
# 求每一列特征的变化范围,为归一化做准备
ranges = maxVals - minVals
# 创建与数据集相同size的零矩阵
normDataSet = np.zeros(np.shape(dataSet))
# 计算数据集的行数
m = dataSet.shape[0]
## 线性函数归一化,等比例缩放原始数据
# 将minVals扩展到与数据集相同size,并与数据集做减法
normDataSet = dataSet - np.tile(minVals, (m, 1))
normDataSet = normDataSet / np.tile(ranges, (m, 1))
return normDataSet, ranges, minVals
## 构建分类器
def classify0(inX,dataSet,labels,k):
'''
Parameters
----------
inX : 输入向量
dataSet : 训练样本集
labels : 标签向量
k : 选择最近邻居的数目
-------
'''
# 求数据集中一共多少组数据
dataSet_size = dataSet.shape[0]
## tile函数表示将inX在行方向上重复dataSet_size次,在列方向上重复1次
# 求输入向量与数据集中点横纵坐标之差
diffMat = np.tile(inX,(dataSet_size,1))-dataSet
# 求输入向量与数据集中点横纵坐标之差的平方
sqDiffMat = diffMat ** 2
# 求横坐标之差平方与纵坐标之差平方和
sqDistances = sqDiffMat.sum(axis=1)
# 求距离
distances = sqDistances**0.5
## argsort函数返回一个列表,为原列表从小到大排序后各元素的索引
## 比方说第一个元素为原列表中第二小的元素,argsort函数在新列表中1位置写1
## 第三小,就在该位置写2,以此类推
# 求distances数组中元素从小到大排序后各元素的索引
sortedDistIndices = distances.argsort()
classCount = {}
for i in range(k):
# 依次寻找最邻近点的标签
voteIlabel = labels[sortedDistIndices[i]]
# 在字典中寻找有无标签,没有就添加,有就+1
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
# 将字典拆解为列表
# 遍历classCount字典的键值对,并按value进行排序,升序排列
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
## 测试分类器
def datingClassTest():
# 定义测试集所占数据集的比例
hoRatio = 0.30
# 将文本记录转换为Numpy
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
# 归一化特征值
normMat, ranges, minVals = autoNorm(datingDataMat)
print(normMat.shape)
# 计算数据集行数
m = normMat.shape[0]
# 获取测试集行数
numTestVecs = int(m*hoRatio)
print(numTestVecs)
# 定义初始的误差个数为0
errorCount = 0.0
for i in range(numTestVecs):
# normMat[i,:]表示遍历测试集中每一行数据
# normMat[numTestVecs:m,:]表示训练集中的数据
# datingLabels[numTestVecs:m]表示训练集中的标签
classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)
# 如果分类结果与预计的标签不相同,则初始误差+1
if (classifierResult != datingLabels[i]): errorCount += 1.0
print("the total error rate is: %f" % (errorCount / float(numTestVecs)))
print(errorCount)
if __name__ == '__main__':
datingClassTest()
示例:手写识别系统
import operator
import numpy as np
from os import listdir
## 准备数据:将图像转换为测试向量
def img2vector(filename):
# 32*32像素的图片,创建1*1024的零矩阵
returnVect = np.zeros((1, 1024))
# 打开文件
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
# 将32*32像素图片展开成1*1024
returnVect[0, 32*i+j] = int(lineStr[j])
return returnVect
## 构建分类器
def classify0(inX,dataSet,labels,k):
'''
Parameters
----------
inX : 输入向量
dataSet : 训练样本集
labels : 标签向量
k : 选择最近邻居的数目
-------
'''
# 求数据集中一共多少组数据
dataSet_size = dataSet.shape[0]
## tile函数表示将inX在行方向上重复dataSet_size次,在列方向上重复1次
# 求输入向量与数据集中点横纵坐标之差
diffMat = np.tile(inX,(dataSet_size,1))-dataSet
# 求输入向量与数据集中点横纵坐标之差的平方
sqDiffMat = diffMat ** 2
# 求横坐标之差平方与纵坐标之差平方和
sqDistances = sqDiffMat.sum(axis=1)
# 求距离
distances = sqDistances**0.5
## argsort函数返回一个列表,为原列表从小到大排序后各元素的索引
## 比方说第一个元素为原列表中第二小的元素,argsort函数在新列表中1位置写1
## 第三小,就在该位置写2,以此类推
# 求distances数组中元素从小到大排序后各元素的索引
sortedDistIndices = distances.argsort()
classCount = {}
for i in range(k):
# 依次寻找最邻近点的标签
voteIlabel = labels[sortedDistIndices[i]]
# 在字典中寻找有无标签,没有就添加,有就+1
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
# 将字典拆解为列表
# 遍历classCount字典的键值对,并按value进行排序,升序排列
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
## 手写数字识别系统测试
def handwritingClassTest():
hwLabels = []
# 获取训练集中的文件
trainingFileList = listdir('trainingDigits')
# 计算测试集中文件的数量
m = len(trainingFileList)
# 构建m*1024的零矩阵
trainingMat = np.zeros((m, 1024))
# 通过循环把零矩阵构建为测试向量矩阵
for i in range(m):
# 遍历获取训练集中的文件名(包含后缀)
fileNameStr = trainingFileList[i]
# 获取文件名(不包含后缀)
fileStr = fileNameStr.split('.')[0]
# 获取文件名表示的数字
classNumStr = int(fileStr.split('_')[0])
# 在手写标签列表中添加数字标签
hwLabels.append(classNumStr)
# 将测试向量叠加为训练矩阵
trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)
# 获取测试集中的文件
testFileList = listdir('testDigits')
# 定义初试误差为0.0
errorCount = 0.0
# 计算测试集中文件数量
mTest = len(testFileList)
for i in range(mTest):
# 遍历获取训练集中的文件名(包含后缀)
fileNameStr = testFileList[i]
# 获取训练集中的文件名(不包含后缀)
fileStr = fileNameStr.split('.')[0]
# 获取文件名表示的数字
classNumStr = int(fileStr.split('_')[0])
# 将文件内容转换为测试向量
vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
# 对其进行KNN分类
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))
# 只要有一个分类错误就在初始误差上+1
if (classifierResult != classNumStr): errorCount += 1.0
print("\\nthe total number of errors is: %d" % errorCount)
print("\\nthe total error rate is: %f" % (errorCount/float(mTest)))
handwritingClassTest()
关于两个示例的数据集以及整个工程文件,我放在百度网盘中
链接:https://pan.baidu.com/s/1Ndfw89xPGgzwGLV08AA8OQ
提取码:ua7c
说明:以上代码主体均出自于《机器学习实战》一书,博主根据自己的理解添加了大部分注释,浅显易懂,适合小白入门。
欢迎大家一起评论交流~
以上是关于机器学习实战—— K-邻近算法(KNN)的主要内容,如果未能解决你的问题,请参考以下文章