机器学习实战(第二章:k-近邻算法)
今天学习了第二章,在此就我理解做一下简单的总结,算是加深我的理解和用我自己的语言描述出这个算法吧。
距离计算
基于向量空间的欧几里得距离的计算。(L2距离)
特别情况下可采用Lp距离(明氏距离) L1距离。
简单点来说就是 在一个具有大量样本集中,每一个实例都具有3个或以上的特征属性,其中有一个属性必然是分类属性,其余属性为数值型属性(即使是标称型属性,也可以通过 某些方法转变过来),每一个实例都是由属性特征值组成的一个向量,一个样本集就是多个向量组成。
例如下面这个例子
身高 | 体重 | 年龄 | 性别 |
170 | 140 | 22 | 男 |
160 | 100 | 21 | 女 |
“”性别“”可以看成是一个分类属性,然后其他看特征属性 ,组成一个实例向量就为 [170,140,22]和[160,100,21]
一、算法步骤:
1、计算已知类别数据集中的点与当前点之间的距离;
2、按照距离递增次序排序;
3、选取与当前点距离最小的k个点;
4、确定k个点所在类别的出现频率;
(K用于选择最近邻的数目,K的选择非常敏感。K值越小意味着模型复杂度越高,从而容易产生过拟合;K值越大则 意味着整体的模型变得简单,学习的近似误差会增大,在实际的应用中,一般采用一个比较小的K值,用交叉验证的 方法,选取一个最优的K值。)
5、返回前k个点出现频率最高的类别作为当前点的预测分类
二、例子说明(python)
首先按照算法编写一个knn算法类'''
inX:用于分类的输入向量
dataSet:训练样本集
labels:标签向量
k:用于选择最近邻的数目
'''
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
sortedDistIndicies = distances.argsort() #按从小到大的排序后的索引值
#选择距离最小的k个点
classCount = {}
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]
举一个实例说明(手写识别数据)
数据说明:
我们需要把每一个样本数据转变成向量的格式,由于图片是32X32的txt文本格式存在,我们转变成1X1024的向量,并把所有的训练样本保存为一个矩阵,对应的每一行就是每一个实例。
#把一个32X32的变成1X1024的向量
def img2vector(filename):
returnVec = zeros((1,1024))
fr = open(filename)
for i in range(32):
lineStr = fr.readline()
for j in range(32):
returnVec[0,32*i+j] = int(lineStr[j])
return returnVec
运用我们事先写好的knn算法类,按照格式代进去训练并且计算错误率。
def handwritingClassTest():
hwLabels = []
#获取训练集
trainingFileList = os.listdir('digits//trainingDigits') #获取该目录下所有文件名 类型:list
m = len(trainingFileList)
trainingMat = zeros((m,1024))
for i in range(m):
fileNameStr = trainingFileList[i] #7_173.txt
fileStr = fileNameStr.split('.')[0] #'7_173' 'txt'
classNumStr = int(fileStr.split('_')[0]) #7 173
hwLabels.append(classNumStr)
trainingMat[i,:] = img2vector("digits//trainingDigits//"+fileNameStr)
#获取测试集
testFileList = os.listdir("digits//testDigits")
errorCount = 0
mTest = len(testFileList)
for i in range(mTest):
fileNameStr = testFileList[i]
fileStr = fileNameStr.split('.')[0] # '7_173' 'txt'
classNumStr = int(fileStr.split('_')[0]) # 7 173
vectorUnderTest = img2vector("digits//testDigits//"+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
print "the total number of errors is:%d" %errorCount
print "the total error rate is:%f" %(errorCount/float(mTest))
总结:
优点
1.精度高
2.对异常值不敏感
3.没有对数据的分布假设
缺点
1、knn算法不像其他算法有一个训练的过程
2、knn算法针对那些分类不均匀的分类训练样本可能误差较大
3、计算量太大,每一个待测测试样本都要遍历一遍训练样本来计算距离
4、我们无法知晓实例样本和典型实例样本具有什么特征,无法给出任何数据的基础结构信息