K–近邻算法

K-近邻算法

K-近邻算法的直观理解:

给定一个训练集和, 对于新输入的实例,在这个集合中找k个与该实例最近的邻居,然后判断这k个邻居大多数归属某一类,于是这个新输入的实例就划分为这一类。

K-近邻算法的三个要素

K-近邻算法有三要素,k值的选取,邻居距离的度量和分类决策的制定。

k值的选取

k较小,分类算法的鲁棒性较差,也很容易发生过拟合现象。k较大,分类错误率很快回升。所以人们通常采取交叉验证。

邻居距离的度量

归一化:
为公平起见,样本的不同特征需要做归一化处理,即把特征值都映射在[0, 1]范围之内。
归一化的公式有很多,最简单的方法是:对于给定的特征,首先找到他的最大值和最小值,然后对于某个特征值x,归一化公式可表示为:
x = x − M I N M A X − M I N x = \frac{x - MIN}{MAX - MIN} x=MAXMINxMIN

计算距离公式:
计算样本中的距离公式有很多,其中最常用的是欧几里得距离:
L 2 = ∑ ( X i − X j ) 2 L2 = \sqrt{\sum{(X_i - X_j)^2}} L2=(XiXj)2

分类决策的制定:
一般使用加权投票原则:距离越近的邻居,权重越大。

K-近邻算法实战

我们使用鸢尾花会数据集进行实战

数据的获取

下载地址: 鸢尾花数据集下载.
获取本地数据的代码实现:

# 获得训练集和测试集
def loadDataset(filename, split, trainingSet=[], testSet=[]):
    with open(filename, 'r') as csvfile:
        lines = csv.reader(csvfile)
        dataset = list(lines)
        for x in range(len(dataset)):
            for y in range(4):
                dataset[x][y] = float(dataset[x][y])
            if random.random() < split:
                trainingSet.append(dataset[x])
            else:
                testSet.append(dataset[x])

计算相似性

我们使用欧几里得距离,代码如下:

# 欧几里得距离
def EuclidDist(instance1, instance2, length):
    # length是集合的长度
    distance = 0.0
    for x in range(length):
        distance += pow((instance1[x] - instance2[x]), 2)

    return math.sqrt(distance)

寻找最近的邻居

有了相似性的度量标准,下面我们就可以为一个给定的位置样本,找到它的k个最邻近的邻居。其过程是:对于这个未知的新样本,计算他与训练集合中的所有样本距离。然后再挑选k个最小的作为他的邻居。

# 寻找未知点的邻居
def getNeighbors(trainSet, testInstance, k):
    distances = []
    length = len(testInstance) # 因为其中包含种类信息,计算时不使用,所以长度需要-1
    for x in range(len(trainSet)):
        dist = EuclidDist(testInstance, trainSet[x], length-1)
        distances.append((trainSet[x], dist))

    distances.sort(key=operator.itemgetter(1))
    neighbors = []
    for x in range(k):
        neighbors.append(distances[x][0])

    return neighbors

分类

一旦确定了某个测试实例最近的k个邻居,那么我们就将依据“少数服从多数”的原则,决定该类别的所属分类。

# 判断分类
def getClass(neighbors):
    classVotes = {}
    for x in range(len(neighbors)):
        instance_class = neighbors[x][-1]
        if instance_class in classVotes:
            classVotes[instance_class] += 1
        else:
            classVotes[instance_class] = 1
    sortedVotes = sorted(classVotes.items(), key=operator.itemgetter(1), reverse=True)
    # classVotes.items()返回可用于迭代操作的字典元素,按照字典的value排序, reverse=True按照降序排列
    return sortedVotes[0][0]

评估

用正确预测的次数除以样本总量

# 评估函数
def getAccuracy(testSet, predictions):
    correct = 0
    for x in range(len(testSet)):
        if testSet[x][-1] == predictions[x]:
            correct += 1

    return (correct / float(len(testSet))) * 100.0

综合案例

import csv
import math
import random
import operator


# 获得训练集和测试集
def loadDataset(filename, split, trainingSet=[], testSet=[]):
    with open(filename, 'r') as csvfile:
        lines = csv.reader(csvfile)
        dataset = list(lines)
        for x in range(len(dataset)):
            for y in range(4):
                dataset[x][y] = float(dataset[x][y])
            if random.random() < split:
                trainingSet.append(dataset[x])
            else:
                testSet.append(dataset[x])


# 欧几里得距离
def EuclidDist(instance1, instance2, length):
    # length是集合的长度
    distance = 0.0
    for x in range(length):
        distance += pow((instance1[x] - instance2[x]), 2)

    return math.sqrt(distance)


# 寻找未知点的邻居
def getNeighbors(trainSet, testInstance, k):
    distances = []
    length = len(testInstance) # 因为其中包含种类信息,计算时不使用,所以长度需要-1
    for x in range(len(trainSet)):
        dist = EuclidDist(testInstance, trainSet[x], length-1)
        distances.append((trainSet[x], dist))

    distances.sort(key=operator.itemgetter(1))
    neighbors = []
    for x in range(k):
        neighbors.append(distances[x][0])

    return neighbors


# 判断分类
def getClass(neighbors):
    classVotes = {}
    for x in range(len(neighbors)):
        instance_class = neighbors[x][-1]
        if instance_class in classVotes:
            classVotes[instance_class] += 1
        else:
            classVotes[instance_class] = 1
    sortedVotes = sorted(classVotes.items(), key=operator.itemgetter(1), reverse=True)
    # classVotes.Items()返回可用于迭代操作的字典元素,按照字典的value排序, reverse=True按照降序排列
    return sortedVotes[0][0]


# 评估函数
def getAccuracy(testSet, predictions):
    correct = 0
    for x in range(len(testSet)):
        if testSet[x][-1] == predictions[x]:
            correct += 1

    return (correct / float(len(testSet))) * 100.0


if __name__ == '__main__':
    trainingSet = []
    testSet = []
    split = 0.70
    loadDataset('iris.data', split, trainingSet, testSet)
    print('训练结合样本数:' + repr(len(trainingSet)))
    print('测试集合样本数:' + repr(len(testSet)))
    predictions = []
    k = 1
    for x in range(len(testSet)):
        neighbors = getNeighbors(trainingSet, testSet[x], k)
        result = getClass(neighbors)
        predictions.append(result)
        print('>预测=' + repr(result) + ', 实际=' + repr(testSet[x][-1]))

    accuracy = getAccuracy(testSet, predictions)
    print('精确度为:' + repr(accuracy) + '%')

运行结果:

训练结合样本数:106
测试集合样本数:44
>预测='Iris-setosa', 实际='Iris-setosa'
>预测='Iris-setosa', 实际='Iris-setosa'
>预测='Iris-setosa', 实际='Iris-setosa'
……
>预测='Iris-virginica', 实际='Iris-virginica'
>预测='Iris-virginica', 实际='Iris-virginica'
>预测='Iris-virginica', 实际='Iris-virginica'
精确度为:93.18181818181817%

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值