K-近邻算法
优点:精度高、对异常值不敏感、无数据输入假定 缺点:计算复杂度高、空间复杂度高 适用数据范围:数值型和标称型
解释: ??标称型:标称型目标变量的结果只在有限目标集中取值,比如真与假(标称型目标变量主要用于分类) ??数值型(连续型):数值型目标变量则可以从无限的数值集合中取值
工作原理
??存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。
K-近邻算法的一般流程
- 收集数据:可以使用任何方法
- 准备数据:距离计算所需要的数值,最好是结构化的数据格式
- 分析数据:可以使用任何方法
- 训练算法:此步骤不适用于k-近邻算法
- 测试算法:计算错误
- 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理
1、准备:使用Python导入数据
2、实施KNN分类算法
k-近邻算法将每组数据划分到某个类中,主要操作如下: (1)计算已知类别数据集中的点与当前点之间的距离 (2)按照距离递增次序排序 (3)选取与当前点距离最小的k个点 (4)确定k个点所在类别出现的频率 (5)返回前k个点出现的频率最高的类别作为当前的预测分类
3、如何测试分类器
我们可以使用已知答案的数据,检验分类器给出的结构是否符合预期。
错误率是分析器给出错误的结果的次数除以测试执行的总数。 完美的分类器的错误率为0,最差的分类器的错误率是1.0
4、KNN示例代码:
from numpy import *
import operator
import numpy
def createDataSet():
group = numpy.array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
labels = ['A', 'A', 'B', 'B']
return group, labels
def classify0(inx, dataSet, labels, k):
dataSetsize = dataSet.shape[0]
diffMat = tile(inx, (dataSetsize, 1)) - dataSet
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances ** 0.5
sortedDistIndices = distances.argsort()
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndices[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
print(classCount[voteIlabel], classCount)
sortedClassCount = sorted(
classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
group, labels = createDataSet()
print(group, "\n", labels)
test = classify0([0, 0], group, labels, 3)
print(test)
提示: 可以在不懂的地方将结果打印出来,看看每一个变量是怎么样的
示例:使用K-近邻算法改进约会网站的配对效果
使用K-近邻算法的具体流程
- 收集数据:提供文本文件
- 准备数据:使用Python解析文本文件
- 分析数据:使用Matplotlib画二维扩展图
- 训练算法:此步骤不适用于k-近邻算法
- 测试算法:使用提供的部分数据作为测试样本
测试样本与非测式样本的区别在于:测试样本是已经完成分类的数据,如果测试分类与实际类别不同,则标记为一个错误 - 使用算法:产生简单的命令行数据,然后输入一些特征数据以判断是否为想要的结果
1、准备数据:从文本文件中解析数据
??在将特征数据输入到分类器之前,必须将数据的格式转变为分类器可以接受的格式。
2、分析数据:使用Matplotlib创建散点图
??可以使用不同的颜色或标记来区分不同的样本,以便更好的理解数据信息。可以多试几组相关的信息,来形成不同样本分类分区。
3、准备数据:归一化数值
??在处理不同范围的特征值时,我们常采用的方法是将数值归一化,如将取值范围处理为0到1或者是-1到1之间。具体公式如下,特征值转化到0-1之间:
newValue = (oldValue - min) / (max - min)
4、测试算法:作为完整程序验证分类器
??机器学习算法一个很重要的工作就是评估算法的正确率,通常我们只提供90%的数据作为训练样本分类器,而使用其余的10%数据去测试分类器,检测分类器的正确性。
提示: 10%的数据是随机选择的
5、使用算法:构建完整可用的系统
??就是将复杂的数据展示给用户可以容易理解的形式展示出来。
6、约会网站的配对的示例代码
import operator
from os import error
import numpy as np
import matplotlib.pyplot as plt
from numpy.core.defchararray import array
from numpy import *
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
def classify0(inx, dataSet, labels, k):
dataSetsize = dataSet.shape[0]
diffMat = tile(inx, (dataSetsize, 1)) - dataSet
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances ** 0.5
sortedDistIndices = distances.argsort()
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndices[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
sortedClassCount = sorted(
classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
def file2matrix(filename):
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]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat, classLabelVector
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals-minVals
normDataSet = np.zeros(shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet-tile(minVals, (m, 1))
normDataSet = normDataSet/tile(ranges, (m, 1))
return normDataSet, ranges, minVals
def datingClassTest():
hoRatio = 0.10
datingDataMat, datingLables = file2matrix('datingTestSet.txt')
norMat, ranges, minVals = autoNorm(datingDataMat)
m = norMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(
norMat[i, :], norMat[numTestVecs:m, :], datingLables[numTestVecs:m], 3)
print("The classifier came back with: %d, the real answer is: %d" %
(classifierResult, datingLables[i]))
if (classifierResult != datingLables[i]):
errorCount += 1.0
print("The total error rate is: %f" % (errorCount/float(numTestVecs)))
def classifyPerson():
resultList = ['Not at all', 'in small doses', 'in large']
percentTats = float(input("Percentage of time spent playing video games?"))
ffMiles = float(input("Frequent flier miles earned per year?"))
iceCream = float(input("Liters of ice cream consumed per year?"))
datingDataMat, datingLables = file2matrix('datingTestSet.txt')
norMat, ranges, minVals = autoNorm(datingDataMat)
inArr = np.array([ffMiles, percentTats, iceCream])
classifierResult = classify0(
(inArr-minVals)/ranges, norMat, datingLables, 3)
print("You will probaby like this person:",
resultList[classifierResult-1])
classifyPerson()
datingDataMat, datingLables = file2matrix('datingTestSet.txt')
norMat, ranges, minVals = autoNorm(datingDataMat)
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 0], datingDataMat[:, 1],
15.0*np.array(datingLables), 15.0*np.array(datingLables))
plt.xlabel('每年获取的飞行常客里的程数')
plt.ylabel('玩视频游戏所耗时间的百分比')
plt.show()
示例:手写识别系统
提示: 此示例只能识别0-9
使用K-近邻算法的具体流程
- 收集数据:提供文本文件
- 准备数据:编写函数img2vector(),将图像格式转化为分类器使用的向量格式
- 分析数据:检查数据,确保它符合要求
- 训练算法:此步骤不适用于k-近邻算法
- 测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本得区别在于测试样本是已经完成了分类的数据,若预测分类与实际类别不同,则标记为一个错误
- 使用算法:本例没有完成此步骤
1、准备数据:将图像转化为测试向量
??为了使用前面例子中的分类器,必须将图像格式化处理为一个向量。将32 * 32的二进制矩阵转化为 1 * 1024的向量,这样就可以使用前面的分类器处理数字图像信息了。
2、测试数据:使用K-近邻算法识别手写数字
??上面已经将数据处理成分类器可以识别的格式了,我们再将数据输入到分类器中,检测分类的执行效果。
3、识别手写数字的示例代码
import numpy as np
import operator
import os
from numpy.lib.shape_base import tile
def classify0(inx, dataSet, labels, k):
dataSetsize = dataSet.shape[0]
diffMat = tile(inx, (dataSetsize, 1)) - dataSet
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances ** 0.5
sortedDistIndices = distances.argsort()
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndices[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
sortedClassCount = sorted(
classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
def img2vector(filename):
resultVect = np.zeros((1, 1024))
fr = open(filename)
for i in range(32):
linesStr = fr.readline()
for j in range(32):
resultVect[0, 32*i+j] = int(linesStr[j])
return resultVect
def handWritingClassTest():
hwLabels = []
trainingFileList = os.listdir('trainingDigits')
m = len(trainingFileList)
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 = os.listdir('testDigits')
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)
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
print("The classifier came back with: %d, the real answer is: %d" %
(classifierResult, classNumStr))
if (classifierResult != classNumStr):
errorCount += 1.0
print("\nThe totle number of error is: %d" % errorCount)
print("\nThe totle error rate is: %f" % (errorCount/float(mTest)))
handWritingClassTest()
注意: 改变变量K值、修改函数handWritingClassTest随机训练样本、改变训练样本数目,都会对K-近邻算法的错误产生影响。实际使用这个算法时执行的效率并不高
小结
??k-近邻算法是分类数据最简单有效的算法。k-近邻是基于实例的学习,使用算法时必须有接近的训练样本数据。k-近邻算法必须保存全部的数据集,若训练数据集很大,必须使用大量的存储空间;此外由于必须对数据集中的每个数据集中的每个数据计算距离值,实际使用时间可能非常耗时。
??k-近邻算法另一个缺点是它无法给出任何数据的基础结构信息,因此也无法得出平均实例样本和典型实例样本具有什么特征。
|