赞
踩
K最近邻(k-Nearest Neighbor,KNN)分类算法是最简单的机器学习算法。[dht1]
KNN算法的指导思想是“近朱者赤,近墨者黑”,由你的邻居来推断出你的类别。
本质上,KNN算法就是用距离来衡量样本之间的相似度
计算步骤如下:
1)算距离:给定测试对象,计算它与训练集中的每个对象的距离
2)找邻居:圈定距离最近的k个训练对象,作为测试对象的近邻
3)做分类:根据这k个近邻归属的主要类别,来对测试对象分类
但,距离不能代表一切,有些数据的相似度衡量并不适合用距离
(简单应用中,一般使用欧氏距离,但对于文本分类来说,使用余弦(cosine)来计算相似度就比欧式(Euclidean)距离更合适)
该方法比较适用于样本容量比较大的类域的分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。
<比如:计算地理位置的相似度>
……
有以下先验数据,使用knn算法对未知类别数据分类
属性1 | 属性2 | 类别 |
1.0 | 0.9 | A |
1.0 | 1.0 | A |
0.1 | 0.2 | B |
0.0 | 0.1 | B |
未知类别数据
属性1 | 属性2 | 类别 |
1.2 | 1.0 | ? |
0.1 | 0.3 | ? |
首先,我们新建一个kNN.py脚本文件,文件里面包含两个函数,一个用来生成小数据集,一个实现kNN分类算法。代码如下:
######################################### # kNN: k Nearest Neighbors
# 输入: newInput: (1xN)的待分类向量 # dataSet: (NxM)的训练数据集 # labels: 训练数据集的类别标签向量 # k: 近邻数
# 输出: 可能性最大的分类标签 #########################################
from numpy import * import operator
#创建一个数据集,包含2个类别共4个样本 def createDataSet(): # 生成一个矩阵,每行表示一个样本 group = array([[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]]) # 4个样本分别所属的类别 labels = ['A', 'A', 'B', 'B'] return group, labels
# KNN分类算法函数定义 def kNNClassify(newInput, dataSet, labels, k): numSamples = dataSet.shape[0] # shape[0]表示行数
## step 1: 计算距离[dht3] # tile(A, reps): 构造一个矩阵,通过A重复reps次得到 # the following copy numSamples rows for dataSet diff = tile(newInput, (numSamples, 1)) - dataSet # 按元素求差值 squaredDiff = diff ** 2 #将差值平方 squaredDist = sum(squaredDiff, axis = 1) # 按行累加 distance = squaredDist ** 0.5 #将差值平方和求开方,即得距离
## step 2: 对距离排序 # argsort() 返回排序后的索引值 sortedDistIndices = argsort(distance) classCount = {} # define a dictionary (can be append element) for i in xrange(k): ## step 3: 选择k个最近邻 voteLabel = labels[sortedDistIndices[i]]
## step 4: 计算k个最近邻中各类别出现的次数 # when the key voteLabel is not in dictionary classCount, get() # will return 0 classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
## step 5: 返回出现次数最多的类别标签 maxCount = 0 for key, value in classCount.items(): if value > maxCount: maxCount = value maxIndex = key
return maxIndex |
然后调用算法进行测试:
import kNN from numpy import * #生成数据集和类别标签 dataSet, labels = kNN.createDataSet() #定义一个未知类别的数据 testX = array([1.2, 1.0]) k = 3 #调用分类函数对未知数据分类 outputLabel = kNN.kNNClassify(testX, dataSet, labels, 3) print "Your input is:", testX, "and classified to class: ", outputLabel
testX = array([0.1, 0.3]) outputLabel = kNN.kNNClassify(testX, dataSet, labels, 3) print "Your input is:", testX, "and classified to class: ", outputLabel |
这时候会输出
Your input is: [ 1.2 1.0] and classified to class: A Your input is: [ 0.1 0.3] and classified to class: B |
利用一个手写数字“先验数据”集,使用knn算法来实现对手写数字的自动识别;
先验数据(训练数据)集:
数据集压缩包解压后有两个目录:
本案例看起来跟前一个案例几乎风马牛不相及,但是一样可以用KNN算法来实现。没错,这就是机器学习的魅力,不过,也是机器学习的难点:模型抽象能力!
思考:
新建一个kNN.py脚本文件,文件里面包含四个函数:
######################################### # kNN: k Nearest Neighbors
# 参数: inX: vector to compare to existing dataset (1xN) # dataSet: size m data set of known vectors (NxM) # labels: data set labels (1xM vector) # k: number of neighbors to use for comparison
# 输出: 多数类 #########################################
from numpy import * import operator import os
# KNN分类核心方法 def kNNClassify(newInput, dataSet, labels, k): numSamples = dataSet.shape[0] # shape[0]代表行数
## step 1: 计算欧式距离 # tile(A, reps): 将A重复reps次来构造一个矩阵 # the following copy numSamples rows for dataSet diff = tile(newInput, (numSamples, 1)) - dataSet # Subtract element-wise squaredDiff = diff ** 2 # squared for the subtract squaredDist = sum(squaredDiff, axis = 1) # sum is performed by row distance = squaredDist ** 0.5
## step 2: 对距离排序 # argsort()返回排序后的索引 sortedDistIndices = argsort(distance)
classCount = {} # 定义一个空的字典 for i in xrange(k): ## step 3: 选择k个最小距离 voteLabel = labels[sortedDistIndices[i]]
## step 4: 计算类别的出现次数 # when the key voteLabel is not in dictionary classCount, get() # will return 0 classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
## step 5: 返回出现次数最多的类别作为分类结果 maxCount = 0 for key, value in classCount.items(): if value > maxCount: maxCount = value maxIndex = key
return maxIndex
# 将图片转换为向量 def img2vector(filename): rows = 32 cols = 32 imgVector = zeros((1, rows * cols)) fileIn = open(filename) for row in xrange(rows): lineStr = fileIn.readline() for col in xrange(cols): imgVector[0, row * 32 + col] = int(lineStr[col])
return imgVector
# 加载数据集 def loadDataSet(): ## step 1: 读取训练数据集 print "---Getting training set..." dataSetDir = 'E:/Python/ml/knn/' trainingFileList = os.listdir(dataSetDir + 'trainingDigits') # 加载测试数据 numSamples = len(trainingFileList)
train_x = zeros((numSamples, 1024)) train_y = [] for i in xrange(numSamples): filename = trainingFileList[i]
# get train_x train_x[i, :] = img2vector(dataSetDir + 'trainingDigits/%s' % filename)
# get label from file name such as "1_18.txt" label = int(filename.split('_')[0]) # return 1 train_y.append(label)
## step 2:读取测试数据集 print "---Getting testing set..." testingFileList = os.listdir(dataSetDir + 'testDigits') # load the testing set numSamples = len(testingFileList) test_x = zeros((numSamples, 1024)) test_y = [] for i in xrange(numSamples): filename = testingFileList[i]
# get train_x test_x[i, :] = img2vector(dataSetDir + 'testDigits/%s' % filename)
# get label from file name such as "1_18.txt" label = int(filename.split('_')[0]) # return 1 test_y.append(label)
return train_x, train_y, test_x, test_y
# 手写识别主流程 def testHandWritingClass(): ## step 1: 加载数据 print "step 1: load data..." train_x, train_y, test_x, test_y = loadDataSet()
## step 2: 模型训练. print "step 2: training..." pass
## step 3: 测试 print "step 3: testing..." numTestSamples = test_x.shape[0] matchCount = 0 for i in xrange(numTestSamples): predict = kNNClassify(test_x[i], train_x, train_y, 3) if predict == test_y[i]: matchCount += 1 accuracy = float(matchCount) / numTestSamples
## step 4: 输出结果 print "step 4: show the result..." print 'The classify accuracy is: %.2f%%' % (accuracy * 100) |
测试非常简单,只需要在命令行中输入:
import kNN kNN.testHandWritingClass() |
输出结果如下:
step 1: load data... ---Getting training set... ---Getting testing set... step 2: training... step 3: testing... step 4: show the result... The classify accuracy is: 98.84% |
k太小,分类结果易受噪声点影响;k太大,近邻中又可能包含太多的其它类别的点。
(对距离加权,可以降低k值设定的影响)
k值通常是采用交叉检验来确定(以k=1为基准)
经验规则:k一般低于训练样本数的平方根
投票法没有考虑近邻的距离的远近,距离更近的近邻也许更应该决定最终的分类,所以加权投票法更恰当一些。而具体如何加权,需要根据具体的业务和数据特性来探索
高维度对距离衡量的影响:众所周知当变量数越多,欧式距离的区分能力就越差。
变量值域对距离的影响:值域越大的变量常常会在距离计算中占据主导作用,因此应先对变量进行标准化。
在训练集中,有些样本可能是更值得依赖的。
也可以说是样本数据质量的问题
可以给不同的样本施加不同的权重,加强依赖样本的权重,降低不可信赖样本的影响。
kNN是一种懒惰算法,平时不好好学习,考试(对测试样本分类)时才临阵磨枪(临时去找k个近邻)。
懒惰的后果:构造模型很简单,但在对测试样本分类地的系统开销大,因为要扫描全部训练样本并计算距离。
已经有一些方法提高计算的效率,例如压缩训练样本量[dht4] 等。
机器学习,算法本身不是最难的,最难的是:
这两个事都不是简单的事。算法反而是比较简单的事。
数据虽然是抽象的,但其实可以映射到任意具体业务上,比如:
1、根据已毕业学生各科成绩及其就业数据来预测或引导应届毕业生生就业方向
2、根据客户各属性及其购买行为,来预测新客户的购买行为
假如:
Newinput:[1,0,2]
Dataset:
[1,0,1]
[2,1,3]
[1,0,2]
计算过程即为:
[1,0,1] [1,0,2]
[2,1,3] -- [1,0,2]
[1,0,2] [1,0,2]
=
[0,0,-1]
[1,1,1]
[0,0,-1]
[0,0,1]
[1,1,1]
[0,0,1]
[1]
[3]
[1]
[1]
[1.73]
[1]
还有诸如:
浓缩技术(condensing)
编辑技术(editing)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。