《机器学习实战》笔记02~K-近邻(KNN)算法(分类)

1.K-近邻算法的优缺点

  1. 采用测量特征值间的距离的方法进行分类
  2. 优点在于,精度高,无数据输入假定,对异常值不敏感
  3. 缺点在于,计算复杂度和空间复杂度高
  4. 适用于数值型,标称型

2.KNN工作原理

  1. 存在训练样本集,样本集中每个数据都有标签及所属分类
  2. 新数据输入时,选择样本数据集中前k个(通常k不大于20)距离最近(最相似)的数据
  3. 统计k个数据中的分类数量,选择次数最多的分类分配给新数据

3.构建一个测试用的分类器

  • 仅用于测试分类器在本机环境下是否可用,python3.6.5环境下pycharm2018.1.2
import operator
from numpy import tile, sqrt, array
def createDataSet():
    arr = [[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]]
    group = array(arr)
    labels = ['A', 'A', 'B', 'B']
    return group, labels
group, labels = createDataSet()
# print(group)
# print(labels)
# 参数依次为待分类的新数据,训练样本集,标签向量,k个取值
def classify0(inData, dataSet, labels, k):
    # 1 利用欧式距离公式计算向量间距离
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inData, (dataSetSize, 1)) - dataSet
    sqDiffMat = pow(diffMat, 2)
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqrt(sqDistances)
    # 按距离排序
    sortedDistIndicies = distances.argsort()
    classCount = {}
    # 2 选择距离最小的k个点
    for i in range(k):
        votelabel = labels[sortedDistIndicies[i]]
        classCount[votelabel] = classCount.get(votelabel, 0) + 1
    # 3 按分类出现次数排序,将字典分解成元组列表
    # itemgetter(1)按照第二个元素的次序对元组排序
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    # 返回次数最多的分类
    return sortedClassCount[0][0]
# 输入一个待分类的新数据[0,0]测试返回的分类结果
r = classify0([0.0], group, labels, 3)
print(r)

4.为避免各维度单位不同导致的权重不同,要进行归一化处理

from numpy import tile
# 新值=(旧值-最小值)/(最大值-最小值)
# 得到的值都在0-1之间
def autoNorm(dataSet):
    # 从列中选择最小值dataSet.min(0)
    minVals=dataSet.min(0)
    # 选择最大值
    maxVals = dataSet.max(0)
    ranges=maxVals-minVals
    # shape()函数用于读取参数矩阵各维度的长度
    # 用变量m表示参数矩阵第一列的长度
    m=dataSet.shape[0]
    # tile() 将变量内容复制成输入矩阵同样大小的矩阵,即生成一个列数与原矩阵相同,行数为1,各元素都为最小值的矩阵,进行运算
    normDataSet=dataSet-tile(minVals,(m,1))
    # 上式进行旧值-最小值,下式进行除法,得到均一化的矩阵
    normDataSet=normDataSet/tile(ranges,(m,1))
    # 函数返回值依次为均一化后的矩阵,最大与最小数据差值,最小值
    return normDataSet,ranges,minVals

5.从本地文件中读取训练样本集的数据,格式为每行四组数字,以\t隔开

from numpy.ma import zeros, array
from demo.归一化特征值 import autoNorm
def file2matrix(filename):
    # 1 按行读取本地文件,并返回行数
    fr = open(filename)
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    # 2 zeros()创建以0填充的矩阵,以元组作参数,规定行数为文件行数,列数为3
    returnMat = zeros((numberOfLines, 3))
    classLabelVector = []
    index = 0
    for line in arrayOLines:
        # 3 拆分文件内容到列表
        # strip()去除回车
        line = line.strip()
        # split()分割成元素列表
        listFromLine = line.split('\t')
        # 选取列表前3个元素存入特征值矩阵,行数与文件相同,内容为分割后的列表元素
        returnMat[index, :] = listFromLine[0:3]
        # 将列表最后一列以整数型存储进目的列表中
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    # 返回值依次为特征值矩阵,用作标记结果的元素列表
    return returnMat, classLabelVector

6.测试KNN算法的错误率,以90%作训练样本,10%作测试数据

from demo.归一化特征值 import autoNorm
from demo.读取文件 import file2matrix
from demo.KNN import classify0
def datingClassTest():
    # 规定10%作测试数据
    hoRatio=0.1
    # 读取数据返回特征值矩阵,标签列表
    datamat,labels=file2matrix('demo.txt')
    # 归一化,返回归一化后矩阵
    normMat, ranges, minVals = autoNorm(datamat)
    # 计算向量数量,决定哪些测试,哪些做训练样本
    m=normMat.shape[0]
    # m为行数,以0.1*m后取整作为测试数据
    numTestVecs=int(m*hoRatio)
    errorCount=0.0
    for i in range(numTestVecs):
        # 利用自定义函数classify0(),依次传入第0至测试行数行数据,测试的行数至总行数用作训练样本,同前参的标签列,取最相似的3个向量作投票
        # 返回的参数为猜测的分类结果
        classResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],labels[numTestVecs:m],3)
        # classResult测试结果,labels真正的结果
        print('测试:{},实际:{}'.format(classResult,labels[i]))
        if classResult != labels[i]:
            # 二者不同,错误数+1
            errorCount += 1
    # 错误率=错误数/测试总数
    print('错误率:{}'.format(errorCount/float(numTestVecs)))

7.实际使用KNN算法,输入任意3组数字,返回预测的分类结果

from numpy.ma import array
from demo.读取文件 import file2matrix
from demo.归一化特征值 import autoNorm
from demo.KNN import classify0
def classifyPerson():
    resultList = ['讨厌','一般','喜欢']
    data1 = float(input('输入第一列数据'))
    data2 = float(input('输入第二列数据'))
    data3 = float(input('输入第三列数据'))
    datamat,labels=file2matrix('demo.txt')
    normmat,ranges,minvals = autoNorm(datamat)
    inarr = array([data1,data2,data3])
    classresult=classify0((inarr-minvals)/ranges,normmat,labels,3)
    print('可能的结果是{}'.format(resultList[classresult-1]))

8.示例2:图像识别手写数字0~9,添加一个方法读取本地生成好的txt,进行图像识别错误率的测试

# 将图像转换为1*1024的向量
def img2vecotr(filename):
    # 以0占位,构建1*1024的空数组
    returnVect = zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        # 读取32行数据
        lineStr = fr.readline()
        for j in range(32):
            # 每行前32个字存入数组
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect

9.利用作者提供的测试集和训练集测试KNN算法的错误率

def handTest():
    hwLabels = []
    # listdir('文件夹'),获取文件夹下所有文件
    trainList = listdir('E:\machinelearninginaction\Ch02\digits\\trainingDigits')
    m = len(trainList)
    trainMat = zeros((m,1024))
    for i in range(m):
        # 将文件名拆分后存入列表
        filenameStr = trainList[i]
        fileStr = filenameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        # 文件名开头的数字是给定的实际结果,存入一个列表备用
        hwLabels.append(classNumStr)
        trainMat[i,:] = img2vecotr(f'E:\machinelearninginaction\Ch02\digits\\trainingDigits\{filenameStr}')
    testList = listdir('E:\machinelearninginaction\Ch02\digits\\trainingDigits')
    errorcount=0.0
    mTest = len(testList)
    for i in range(mTest):
        filenameStr = trainList[i]
        fileStr = filenameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        vecotrTest = img2vecotr(f'E:\machinelearninginaction\Ch02\digits\\trainingDigits\{filenameStr}')
        # 利用classify0()方法以k=3生成测试结果
        ifierResult = classify0(vecotrTest,trainMat,hwLabels,3)
        print(f'测试:{ifierResult},实际:{classNumStr}')
        if (ifierResult != classNumStr):
            errorcount += 1
    print('错误率为:{}'.format(errorcount/float(mTest)))

10.KNN算法的局限性

  1. 执行效率低,在图像识别时,要对每个测试向量做2000次的欧氏距离计算,每次包含1024个维度的浮点运行,总计900次才能得到测试结果
  2. KNN为基于实例的学习,必须存在接近实际数据的训练样本数据
  3. 必须保存全部数据集,存储空间消耗大
  4. 无法给出数据的基础结构信息,无法获得样本的特征

猜你喜欢

转载自blog.csdn.net/wxfghy/article/details/80267169