KNN 算法实验(原理详见 ML学习笔记模块)
在开始做实验之前,我们应该对需要用到的数学工具有充分的认识:在本节实验里面,没有用到很多复杂的数学函数,但是我们需要建立对 tile 函数最基本的认识,请看以下实验:
# 我们先来做个实验,首先理解下面的函数
# tile 函数在 numpy 中所以先要导入 numpy
>>> import numpy
>>> # 下面是 tile 函数的实验
>>> numpy.tile([2,0,1,6],2)
array([2, 0, 1, 6, 2, 0, 1, 6])
>>> numpy.tile([2,0,1,6],(2,2))
array([[2, 0, 1, 6, 2, 0, 1, 6],
[2, 0, 1, 6, 2, 0, 1, 6]])
>>> numpy.tile([2,0,1,6],(2,2,2))
array([[[2, 0, 1, 6, 2, 0, 1, 6],
[2, 0, 1, 6, 2, 0, 1, 6]],
[[2, 0, 1, 6, 2, 0, 1, 6],
[2, 0, 1, 6, 2, 0, 1, 6]]])
>>> # tile 很容易理解 字面意思不是平铺吗
>>> # 或者更直接点把上面的第二个函数理解
>>> # 为第一个结果的 numpy.tile(a_result,2)
>>> # 同理可以解释第三个函数
现在大家应该明白 tile 函数的使用方法以及使用场合了吧.
实验 1 :(对二维坐标轴上简单的点进行分类)
# 下面开始第一个简单的实验
# 需要用到的模型
from numpy import *
import operator
def createDataSet():
# 此处每个分类使用了两个点,个人在做实验的时候完全可
# 以加入分别以 (0,0)和 (1,1)为中心的点的其它点
group = array([
[1.1, 1.1],
[1.0, 1.0],
[0.1 , 0.],
[0. , 0.1]
])
labels = ['A', 'A', 'B', 'B']
return group, labels
def classifyKNN(inX, dataset, labels, k):
"""
inX 是输入的测试样本,是一个[x, y]样式的
dataset 是训练样本集
labels 是训练样本标签
k 是top k最相近的
"""
dataSetSize = dataset.shape[0]
# 与样本的差分矩阵,用到了 tile 函数
diffMat = tile(inX, (dataSetSize, 1)) - dataset
# 差分矩阵求平方
sqDiffMat = diffMat ** 2
# 对平方矩阵进行累加
sqDistance = sqDiffMat.sum(axis=1)
# 取欧式距离作为度量
distance = sqDistance ** 0.5
# 对得到的测试数据与样本的距离进行排序,返回排序前索引
sortedDistIndicies = distance.argsort()
# 存放最终结果的字典
classCount = {}
# 统计前 k 个距离最近的样本的分类
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
# 导入数据,开始试验
if __name__== "__main__":
dataset, labels = createDataSet()
inX = [0.2, 0.2]
className = classifyKNN(inX, dataset, labels, 3)
print 'the class of test sample is %s' %className
总结:实验本身很简单,都是最基本的原理与操作,但是我们需要明确 KNN 算法有一点就是 K的选取可能影响最终结果,联系本例中的4 个点我们可以发现,当数据在他们两类的垂直平分线上时,算法不能给出正确的预测结果。
实验 2 :(对数据进行归一化)
#! __*__ coding:utf-8 __*__
from numpy import *
import operator
from os import listdir
def createDataSet():
group = array([
[1.0, 1.1],
[1.1, 1.0],
[0.1 , 0. ],
[0. , 0.1]
])
labels = ['A', 'A', 'B', 'B']
return group, labels
# 建立分类器
def classifyKNN(inX, dataset, labels, k):
"""
inX 是输入的测试样本,是一个[x, y]样式的
dataset 是训练样本集
labels 是训练样本标签
k 是top k最相近的
"""
dataSetSize = dataset.shape[0]
# 与样本的差分矩阵,用到了 tile 函数
diffMat = tile(inX, (dataSetSize, 1)) - dataset
# 差分矩阵求平方
sqDiffMat = diffMat ** 2
# 对平方矩阵进行累加
sqDistance = sqDiffMat.sum(axis=1)
# 取欧式距离作为度量
distance = sqDistance ** 0.5
# 对得到的测试数据与样本的距离进行排序,返回排序前索引
sortedDistIndicies = distance.argsort()
# 存放最终结果的字典
classCount = {}
# 统计前 k 个距离最近的样本的分类
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
# 把问件中的数据转化成矩阵
def file2matrix(filename):
"""
从文件中读入训练数据,并存储为矩阵
"""
fr = open(filename)
#获取 n=样本的行数
numberOfLines = len(fr.readlines())
#创建一个2维矩阵用于存放训练样本数据,一共有n行,每一行存放3个数据
returnMat = zeros((numberOfLines,3))
#创建一个1维数组用于存放训练样本标签
classLabelVector = []
fr = open(filename)
index = 0
for line in fr.readlines():
# 把回车符号给去掉
line = line.strip()
# 把每一行数据用\t分割
listFromLine = line.split('\t')
# 把分割好的数据放至数据集,其中index是该样本数据的下标,就是放到第几行
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
# 创建一个与dataSet同shape的全0矩阵,用于存放归一化后的数据
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0]
# 减去最小值
normDataSet = dataSet - tile(minVals, (m,1))
# 把最大最小差值扩充为dataSet同shape,然后作商,是指对应元素进行除法运算,而不是矩阵除法。
# 矩阵除法在numpy中要用linalg.solve(A,B)
# 进行归一化
normDataSet = normDataSet/tile(ranges, (m,1))
return normDataSet, ranges, minVals
# 导入数据,开始试验
def datingClassTest():
# 将数据集中10%的数据留作测试用,其余的90%用于训练
hoRatio = 0.10
#load data setfrom file
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classifyKNN(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
print "the classifier came back with: %d, the real answer is: %d, result is :%s" % (classifierResult,<span style="font-family: Arial, Helvetica, sans-serif;">datingLabels[i]</span><span style="font-family: Arial, Helvetica, sans-serif;">,</span>
classifierResult==datingLabels[i])
if (classifierResult != datingLabels[i]): errorCount += 1.0
print "the total error rate is: %f" % (errorCount/float(numTestVecs))
print errorCount
if __name__=="__main__":
datingClassTest()
总结:对数据的归一化可以有效的减少因为各个特征的量纲不同或是相差很大对结果造成干扰,当然通常意义上的 KNN 算法是只有归一化的,但现实生活中更普遍的是需要通过多个特征,且这些特征每一个都对最终判决的结果有
不同的影响因子,比如预测天气情况,需要对前两天的天气情况进行统计,同时可能也需要综合前一年的同期数据对最终天进行预测,但是最近这两天的天气情况应该在最终预测中起较大的作用。
PS: 不太熟悉 CSDN 的编辑器,所以还有一半的有图的内容没有加上,带完善……希望大家可以学到一点知识,因个人水平有限,在调试过程中遇到诸多的问题,只有一点一点的解决,后来者要是有什么实在调不通的地方或者库的
安装方面的问题可以发邮件问我,有时候真的距成功只有一步之遥---NEVER GIVE UP !
我的邮箱:chunfengshili@126.com
本文主要参考的人民邮电出版社的机器学习实战以及关于本书的几篇读书笔记:
CSDNBLOG:http://blog.csdn.net/niuwei22007/article/details/49703719
CSDNBLOG:http://blog.csdn.net/qq_26898461/article/details/51012610
在此向他们致谢,我不需要每一篇都写上谢谢我的 C.LUO 师兄,但是第一篇一定要写上:衷心感谢 C.LUO 师兄在学习上对我的帮助。