k-Nearest Neighbors 笔记

KNN(k-Nearest Neighbors, k-近邻算法) 属于监督学习算法, 它采用测量不同特征值之间的距离方法进行分类, 取最近距离的 K 个样本中出现次数最多的标签(label)作为测试样本的标签(label).

优点 精度高, 对异常值不敏感, 无数据输入假定
缺点 计算复杂度高, 空间复杂度高, 无法给出数据的内在含义
适用数据类型 数值型, 标称型

算法描述
1. 有一个训练集, 其中的每个样本都有几个属性值, 及唯一的标签(label)
2. 对新加入的没有标签(label)的样本, 计算其与训练集中所有样本的欧拉距离 
3. 按距离从小到大取出k个训练集中的样本
4. 统计这些样本中的标签(label)出现在次数, 出现次数最多标签(label)即为新加入样本的标签(label)
注: 由算法可知, KNN 算法只有训练数据, 但是没有训练过程; 即一个新样本进来, 都要与训练集中的所有样本进行欧拉距离计算, 从而导致较高的计算复杂度.

算法流程图
k-Nearest Neighbors 笔记_第1张图片

代码

# -*- coding: utf-8 -* 
from numpy import*
import operator
import matplotlib
import matplotlib.pyplot as plt

# 从文件读取, 取前 3 列作为输入, 最后一列为分类标签 
def file2Matrix(filename):
    fr = open(filename)
    lines = fr.readlines()
    numLines = len(lines)

    left3Mat = zeros((numLines, 3)) # numLines行3列, 全部初始化为 0
    classLabel = []
    index = 0

    for line in lines:
        line = line.strip()                         # 除去后面空白字符
        lineSplited = line.split('\t')
        left3Mat[index, :] = lineSplited[0 : 3]
        classLabel.append(int(lineSplited[-1]))     # -1 表示倒数第一列
        index += 1

    return left3Mat, classLabel


# 归一化特征值
def autoNorm(dataSet):
    minVals = dataSet.min(0)            # 按第一维取最小值, 即按行取, 1行3列
    maxVals = dataSet.max(0)
    ranges  = maxVals - minVals

    normDataSet = zeros(shape(dataSet))  # shape 返回矩阵的 行, 列
    r = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (r, 1))   # 1行3列扩为 r行3列
    normDataSet = normDataSet / tile(ranges, (r, 1))

    return normDataSet, ranges, minVals

# 分类, 找出离输入向量 testVect 最近的 k 个邻居中出现频率最高的标签
def classify(testVect, dataSet, labels, k):
    numRows = dataSet.shape[0]

    # 欧拉公式计算距离
    diffMat = tile(testVect, (numRows, 1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distance = sqDistances**0.5           # 开根号

    # 统计距离最近的 k 个向量的 label 出现的次数
    sortedDistance = distance.argsort()
    classCount = {}
    for i in range(k):
        label = labels[sortedDistance[i]]
        classCount[label] = classCount.get(label, 0) + 1

    # 根据 classCount 的 value(即 label 的次数) 进行排序(逆序, 由大到小)
    # sorted 函数 http://docs.python.org/2/howto/sorting.html
    sortedClassCount = sorted(classCount.iteritems(), 
                              key = operator.itemgetter(1), 
                              reverse = True)
    return sortedClassCount[0][0]


# 读取 dataFile 中的数据, 按百分比取前 testRatio 的数据进行测试
def datingClassTest(dataFile, testRatio, k):
    # 读入数据, 并进行归一化               
    datingDataMat, datingLabels = file2Matrix(dataFile)
    normMat, ranges, minVals = autoNorm(datingDataMat)

    # 测试数据数
    totalRow = normMat.shape[0]
    numTestVects = int(totalRow*testRatio)

    # 分类并统计成功的个数
    succCount = 0.0
    for i in range(numTestVects):
        label = classify(normMat[i, :], 
                         normMat[numTestVects : totalRow, :],
                         datingLabels[numTestVects : totalRow], 
                         k)

        if (label == datingLabels[i]): 
            succCount += 1.0
      # print "分类结果: %d, 实际结果: %d" % (label, datingLabels[i])

    print "校验成功比例为: %f" % (succCount / float(numTestVects))



if __name__ == "__main__":
    datingClassTest("datingTestSet2.txt", 0.10, 4)
    # fig = plt.figure()
    # ax  = fig.add_subplot(111)
    # ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 
    #           15.0*array(datingLabel), 15.0*array(datingLabel))
    # plt.show()

说明
本文为《Machine Leaning in Action》第二章(Classsifying with k-Nearest Neighbors)读书笔记, 代码稍作修改及注释.


你可能感兴趣的:(knn,机器学习实战)