《机器学习实战》第二章 k-近邻算法 代码与注释

 

kNN.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri May 31 09:23:18 2019

@author: guiyuyang
"""
'''
k-近邻算法
优点:精度高、对异常值不敏感、无数据输入假定
缺点:计算复杂度高、空间复杂度高
适用数据类型:数值型和标称型
'''
'''
k-近邻算法的一般流程:
    (1) 收集数据:可以使用任何方法。
    (2) 准备数据:距离计算所需要的数值,最好是结构化的数据格式。
    (3) 分析数据:可以使用任何方法。
    (4) 训练算法:此步骤不适用于k-近邻算法。
    (5) 测试算法:计算错误率。
    (6) 使用算法:首先需要输入样本数据和结构化的输出结果,然后运行K-近邻算法判定
        输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。
'''
'''
k-近邻算法 伪代码:
    对未知类别属性的数据集中的每个点依次执行以下操作:
    (1) 计算已知类别数据集中的点与当前点之间的距离
    (2) 按照距离递增次序排序
    (3) 选取与当前点距离最小的k个点
    (4) 确定前k个点所在的类别的出现概率
    (5) 返回前K个点出现概率最高的类别作为当前的预测分类
'''

'''
    (1) 本代码实现了一个通过3个特征值训练并预测的约会程序代码
    (2) 本代码实现了一个简单的手写识别系统
    (3) 代码思路及来源参见Perer Harrington《机器学习实战》
'''

from numpy import *#导入科学计算包
import operator#导入运算符模块
import matplotlib
import matplotlib.pyplot as plt#绘图
from os import listdir#用于列出给定目录的文件名

#2.1.1 准备:使用Python导入数据
def createDataSet():#定义一个函数,创建数据集和标签
    group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels=['A','A','B','B']
    return group,labels

#2.1.2 实施kNN分类算法
'''
k-近邻算法
分类输入量:inX,输入训练样本集:dataSet,向量标签:labels,最近邻数目:k
'''
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]#表明有几个数据集
    diffMat = tile(inX, (dataSetSize,1))-dataSet#tile(A,n),是将数组A重复n次
    sqDiffMat = diffMat**2#diffMat数组中每个数进行平方
    sqDistances = sqDiffMat.sum(axis=1)#axis=1 就是将矩阵每一行向量相加
    distances = sqDistances**0.5#开根号
    sortedDistIndicies = distances.argsort()#srgsort函数返回的是数组值由小到大的索引值
    classCount = {}#字典数据类型
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]#按照距离先后顺序,通过第i个索引值找到对应的标签
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1#计算标签在在字典中出现的次数,默认为0,每出现一次加1
    sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)#按照第二个元素的顺序对元组进行排序,发生频率最高的就是结果
    return sortedClassCount[0][0]#频率最高的元素标签就是所分类的结果

#2.2.1 准备数据:从文本文件中解析数据
'''从txt文件导入数据'''
def file2matrix(filename):
    fr=open(filename)
    arrayOLines = fr.readlines()#列矩阵
    numberOfLines = len(arrayOLines)#得到文件行数
    returnMat = zeros((numberOfLines,3))#列数为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]))#表明列表中储存的是int型
        index += 1
    return returnMat,classLabelVector

#2.2.3 准备数据:归一化数值
'''归一化特征值'''
def autoNorm(dataSet):
    minVals = dataSet.min(0)#取出每列最小值  参数0使得函数从列中选取最小值,而不是选取当前行的最小值
    maxVals = dataSet.max(0)#取出每列最大值  
    ranges = maxVals-minVals#maxVals和minVals均为1*3矩阵
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]#dataSet为1000*3矩阵
    normDataSet = dataSet-tile(minVals,(m,1))#tile(...)将minVals复制为1000*3大小的矩阵
    normDataSet = normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,minVals

#2.2.4 测试算法:作为完整程序验证分类器
'''测试算法有效性'''
def datingClassTest():
    hoRatio = 0.10#用于测试的数据占总数据的10%
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')#导入全部数据 含有90%训练数据和10%测试数据
    normMat,ranges,minVals = autoNorm(datingDataMat)#归一化训练和测试数据
    m = normMat.shape[0]#normMat 总数据行数
    numTestVecs = int(m*hoRatio)#normMat 中参与测试的数据行数
    errorCount = 0.0
    for i in range(numTestVecs):#取前numTestVecs行数据进行测试,剩余数据作为训练样本
        classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:], datingLabels[numTestVecs:m], 3)#调用分类函数
        print("分类结果是:%d,真实结果是:%d"% (classifierResult,datingLabels[i]))
        if(classifierResult != datingLabels[i]): errorCount += 1.0#错误时计数
    print("总错误率为:%f"%(errorCount/float(numTestVecs)))#计算错误率
    
#2.2.5 使用算法:构建完整可用系统
'''
实际使用
用户输入:3个特征值
返回:大概率喜欢 或 小概率喜欢 或 不喜欢
'''
def classifyPerson():
    resultLists = ['不喜欢','小概率喜欢','大概率喜欢']
    percentTats = float(input("玩游戏时间的百分比:"))#用户输入
    ffMiles= float(input("每年飞行里程数:"))#用户输入
    iceCream = float(input("每周吃冰淇淋公升数:"))#用户输入
    
    datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')#导入全部数据 用于训练
    normMat,ranges,minVals = autoNorm(datingDataMat)#归一化训练数据
    
    inArr = array([ffMiles,percentTats,iceCream])
    
    mormDataIn = ((inArr-minVals)/ranges)#归一化
    classifierResult = classify0(mormDataIn, normMat, datingLabels, 3)#调用分类函数
    print("你可能会对这个人:%s"%resultLists[classifierResult-1])
    
#2.3 示例:手写识别系统
#2.3.1 准备数据:将图像转换为测试向量
'''
将图像转换为向量
输入:文件名
返回:向量
'''    
def img2vector(filename):
    returnVect = zeros((1,1024))#长度为2014的向量
    fr = open(filename)
    for i in range(32):#行数
        lineStr = fr.readline()#每次读入一行数据
        for j in range(32):#列数
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect

#2.3.2 测试算法:使用k-近邻算法识别手写数字
'''
手写识别
输入:文件名
返回:向量
使用的训练和测试数据 文件名格式为M_N.txt  其中M为手写数字值,N为该值的第几个数据样本
''' 
'''
    该算法执行效率不高。算法需要对每个测试向量做2000次距离运算,每个距离计算又包括了1024个维度的浮点运算
总计要执行900次 此外还需要为测试向量准备2MB的存储空间
    可以优化为k决策树,它是 k-近邻算法 的优化版本
'''
def handwritingClassTest():
    hwLabels = []#文件标签
    trainingFileList = listdir('trainingDigits')#获取目录
    m = len(trainingFileList)#统计训练文件总个数
    trainingMat = zeros((m,1024))#训练矩阵
    for i in range(m):
        fileNameStr = trainingFileList[i]#获取每个训练文件的文件名
        fileStr = fileNameStr.split('.')[0]#去掉字符串中的‘.’,取前面一段
        classNumStr = int(fileStr.split('_')[0])#去掉字符串中的‘_’,取前面一段  并转换类型为int
        hwLabels.append(classNumStr)#将转换后的 文件标签存入 hwLabels
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)#将数据存入 trainingMat
    
    testFileList = listdir('testDigits')#获取目录
    errorCount = 0.0
    n = len(testFileList)#统计测试文件总个数
    for i in range(n):
        fileNameStr = testFileList[i]#获取每个训练文件的文件名
        fileStr = fileNameStr.split('.')[0]#去掉字符串中的‘.’,取前面一段
        classNumStr = int(fileStr.split('_')[0])#去掉字符串中的‘_’,取前面一段  并转换类型为int
        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)#将数据存入 vectorUnderTest
        classifierResult = classify0(vectorUnderTest,trainingMat,hwLabels,3)
        print("分类器输出: %d, 真实结果是: %d" % (classifierResult,classNumStr))
        if(classifierResult != classNumStr):#分类错误时计数
            errorCount = errorCount+1
            
    print("分类错误总数:%d" % errorCount)
    print("错误率:%f" % (errorCount/float(n)))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值