zoukankan      html  css  js  c++  java
  • 机器学习:KNN(K 近邻)分类算法

    kNN 算法不需要经过算法训练,属于懒惰学习,需要训练的属于急切学习
    kNN 是最简单的分类算法

    优点:精度高、对异常值不敏感、无数据输入假定
    缺点:计算复杂度高、空间复杂度高

    工作原理:

    • 输入没有标签的新数据后,将新数据的每个特征与样本集中每个数据对应的特征进行比较,然后算法提取样本集中 k 个特征最相似的数据(最近邻)的分类标签
    • 通过距离矩阵计算相似度(距离):比如 (1, 2, 0, 1) 与 (7, 6, 9, 4) 的距离
        (small d = sqrt{(7-1)^{2} + (6-2)^{2} + (9-0)^{2} + (4-1)^{2}})
    • 通常 k 是不大于 20 的整数,选择 k 个最相似数据中出现次数最多的分类,作为新数据的分类
    # coding=utf-8
    import numpy as np
    import operator as op
    
    
    # KNN 分类算法
    def classify(inX, dataSet, labels, k):
        """
        inX     - 要预测的特征值矩阵,是一个 (1, n) 的 numpy 矩阵,n 是特征的数量
        dataSet - 样本的特征值矩阵,是一个 (r, n) 的 numpy 矩阵,r 是样本数量,n 是特征的数量
        labels  - 样本的标签,是一个长度为 r 的列表
        k       - 取最近的 k 个样本
        """
    
        # 取样本的数量
        dataSetSize = dataSet.shape[0]
    
        # tile 将 inX 按 (dataSetSize, 1) 平铺,结果是 (r, n) 矩阵,矩阵的每列都是 inX,然后和样本矩阵相减
        # 得到 diffMat 是 inX 的每个特征值和所有样本的差
        diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    
        # sum(axis=1) 指按第二个下标求和,即
        #     sqDiffMat[0][0] + sqDiffMat[0][1] + sqDiffMat[0][2] + ...... + sqDiffMat[0][n-1]
        #     sqDiffMat[1][0] + sqDiffMat[1][1] + sqDiffMat[0][2] + ...... + sqDiffMat[1][n-1]
        # 得到一个 (r, 1) 矩阵
        # 再开根,得到 inX 和每个样本的距离
        sqDiffMat = diffMat**2
        sqDistances = sqDiffMat.sum(axis=1)
        distances = sqDistances**0.5
    
        # 对距离按从小到大排序,并返回索引值,即 distances[sortedDistIndex[0]] 是 distances 的最小值
        sortedDistIndex = distances.argsort()
    
        classCount = {}
        # 取距离最近的 k 个样本
        for i in range(k):
            # 取样本的标签值
            votedLabel = labels[sortedDistIndex[i]]
            # 统计每个标签值的数量
            classCount[votedLabel] = classCount.get(votedLabel, 0) + 1
    
        # classCount.iteritems 得到为迭代器
        # op.itemgetter(1) 取每个数据的第二个域的值,即 label 的次数
        # 从大到小排序
        sortedClassCount = sorted(classCount.iteritems(), key=op.itemgetter(1), reverse=True)
    
        # 返回距离最近的 k 个样本中出现次数最多的标签值
        return sortedClassCount[0][0]
    
    
    # 从文件中取样本数据
    def readDataFromFile(filename, featureNumber):
        """
        文件的每一行是一条数据,最后一列是标签,其余列是特征值,共 fieldNumber 个特征
        """
        numberOfLines = 0
        with open(filename) as f:
            # 统计行数,数据量太大时 len(f.readlines()) 可能无法工作,所以采用循环的方式
            while True:
                tempBuffer = f.read(8192 * 1024)
                if not tempBuffer:
                    numberOfLines = numberOfLines + 1
                    break
                numberOfLines += tempBuffer.count('
    ')
    
        # 特征值矩阵
        featureMat = np.zeros((numberOfLines, featureNumber))
        # 标签列表
        classLabelVector = []
    
        with open(filename) as f:
            index = 0
            while True:
                line = f.readline()
                if line:
                    line = line.strip()
                    fields = line.split('	')
                    featureMat[index, :] = fields[0:featureNumber]
                    classLabelVector.append(int(fields[featureNumber]))
                    index += 1
                else:
                    break
    
        return featureMat, classLabelVector
    
    
    # 归一化数据
    def normalize(dataSet):
        # dataSet.min(0) 按第一个下标求最小值,即求
        #   min(dataSet[0][0], dataSet[1][0], dataSet[2][0], ..., dataSet[m][0])
        #   min(dataSet[0][1], dataSet[1][1], dataSet[2][1], ..., dataSet[m][1])
        # 结果是求每列,即每个特征值的最大和最小值
        minVals = dataSet.min(0)
        maxVals = dataSet.max(0)
    
        # 最大值和最小值的差
        ranges = maxVals - minVals
    
        # np.tile(minVals, (m, 1)) 复制 m 个 minVals,方便矩阵的计算
        # 归一化 newValue = (oldValue - min)/(max - min) 将所有数据转换到 (0, 1)
        m = dataSet.shape[0]
        normDataSet = dataSet - np.tile(minVals, (m, 1))
        normDataSet = normDataSet/np.tile(ranges, (m, 1))
    
        return normDataSet
    
    
    # 测试
    def classifyTest():
        # 10% 的数据作测试集,90% 的数据作样本集
        ratio = 0.10
    
        # 读数据
        featureNumber = 10
        featureMat, classLabelVector = readDataFromFile('kNN.txt', featureNumber)
    
        # 归一化
        normDataSet = normalize(featureMat)
    
        # 数据量
        m = normDataSet.shape[0]
    
        # 测试集大小
        numTest = int(m * ratio)
    
        errorCount = 0
        for i in range(numTest):
            result = classify(normDataSet[i, :], normDataSet[numTest:m, :], classLabelVector[numTest:], featureNumber)
            if result != classLabelVector[i]:
                errorCount += 1
    
            print "predict result: %d, real answer is: %d" % (result, classLabelVector[i])
    
        print "error count: %d, total: %d" % (errorCount, numTest)
        print "the total error rate is: %f" % (errorCount / float(numTest))
    
    


  • 相关阅读:
    CSLA.Net 3.0.5 项目管理示例 业务基类 Project.cs
    为什么我要写博客
    LINQ 标准的查询操作符 过滤 where、index1、OfType
    LINQ 概述和演变
    sl中几个简单变量的获取
    Rails存储库从SVN转向Git
    showcase测试界面
    Silverlight读取xml
    向silverlight传递自定义参数
    RadRails1.0降临——增加Profiler、CallGraph Analyzer和Rails Shell等新特性
  • 原文地址:https://www.cnblogs.com/moonlight-lin/p/12297625.html
Copyright © 2011-2022 走看看