k-邻近算法

算法背景

假设我们要给一堆mp3音乐分类,我们可以分成摇滚,民谣,戏曲等等,摇滚的音乐激昂,节奏快。民谣舒缓节奏慢,但是摇滚中也有可能存在舒缓节奏慢点旋律, 同理民谣中也会有激昂,快的旋律。那么如何区分他们呢, 我们可以根据出现的频率来, 比如舒缓慢节奏的旋律多的是民谣, 激昂快多的旋律是摇滚。so这种基于某一特征出现的次数来区分事物的算法,我们使用k-邻近算法。

概述

k-邻近算法就是采用测量不同特征值之间的距离方法进行分类
优点: 精度高, 对异常值不敏感, 无数据输入假定
缺点: 计算复杂度高,空间复杂度高
适用范围: 数值型和表称行

原理

假设我们我们每个mp3时常 180秒
根据快慢节奏来做特征和一组已有数据集统计:

  • 编号 - 慢节奏(秒) - 快节奏(秒) - 分类 -
  • 1 - 100 - 80 - 民谣 -
  • 2 - 140 - 40 - 民谣 -
  • 3 - 20 - 160 - 摇滚 -
  • 4 - 110 - 70 - 民谣 -
  • 5 - 30 - 150 - 摇滚 -
    。。。。。。。。。

现在我们有一个未知分类的mp3序号为x
其慢节奏时长为 103 快节奏时长为77
我们可以根据某种方法算出x与样本数据其他mp3的距离得到如下:

  • 编号 - 与x的距离
  • 1 - 10.1
  • 2 - 20.5
  • 3 - 90.3
  • 4 - 15.7
  • 5 - 80.2

按照距离递增我们排序然后找到k个距离最近的样本, 假定k = 3,
这三个样本依次序号为: 1, 4, 2
然后我们分析这三个全部是民谣, 于此我们断定x的分类为民谣

k-邻近算法的流程

  1. 搜集数据
  2. 准备数据
  3. 分析数据
  4. 训练算法
  5. 测试算法
  6. 使用算法

实例:

创建kNN.py

# 科学技术包
from numpy import *
# 运算符模块
import Operator

# 创建数据集
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 

接下来我们终端进入python交互模式

import kNN
group, labels = kNN.createDataSet( )

group是拥有四组数据的数组, 每组数据拥有两个特征, labels是数据的标签。我们将以有数据画在平面直角坐标系中如图:
图片描述

分析数据

当拿到一组位置属性的数据时,我们需要一次做如下操作:

  1. 计算已有数据集中各个点与当前未知数据点的距离
  2. 按照距离递增排序
  3. 选取与未知点距离最近的k组数据
  4. 确定这k组数据的各标签出现的频率
  5. 返回这k组数据出现频率最高的标签作为未知点的标签

程序实例1:

# inX(需要预测分类的向量) dataSet(数据集) labels(标签集) k(邻近算法系数)
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistance = sqDiffMat.sum(axis=1)
    distance = sqDistance**0.5
    sortedDistIndicies = distance.argsort()
    classCount={}
    for i in range(k):
        voteIlable = labels[sortedDistIndicies[i]]
        classCount[voteIlable] = classCount.get(voteIlable, 0) + 1
    sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]    

我们逐句分析下:
dataSetSize = dataSet.shape[0] (获取数据集的维度)详细点击
diffMat = tile(inX, (dataSetSize, 1)) - dataSet() (在行方向上重复dataSetSize次, 列方向上重复1此, 然后举证相减)详细介绍
假设inx向量为(x, y), 此时相当于数学上的矩阵相减:

[ x, y]      [1.0, 1.1]     [x-1, y-1.1]
[ x, y]      [1.0, 1.0]     [x-1, y-1]
[ x, y]  -   [0, 0]      =  [x-0, y-0]
[ x, y]      [0, 0.1]       [x-0, y-0.1]      

sqDiffMat = diffMat**2 (将矩阵每个值平方) 相当于数学上的

[(x-10)², (y-1.1)²]
[(x-1)², (y-1)²]
[(x-0)², (y-0)²]
[(x-0)², (y-0.1)²] 

sqDistance = sqDiffMat.sum(axis=1) (将矩阵按照行的方向相加) 详细点击
次操作相当于数学上的:

[(x-10)² + (y-1.1)²]
[(x-1)² + (y-1)²]
[(x-0)² + (y-0)²]
[(x-0)² + (y-0.1)²]

distance = sqDistance**0.5 (将矩阵的每个元素开0.5次方也就是 开平方)
相当于数学的:

[√2((x-10)² + (y-1.1)²)]
[√2((x-1)² + (y-1)²)]
[√2((x-0)² + (y-0)²)]
[√2((x-0)² + (y-0.1)²)]

细心的朋友就会发现算到这里其实我们采用了初中所学习过两点之间球距离的公式
图片描述
sortedDistIndicies = distance.argsort() (将所得的距离进行排序)
classCount={} (新建一个字典)
for i in range(k): (以k邻近算法系数k循环)
voteIlable = labels[sortedDistIndicies[i]] (依次取出距离最近的k组数据对应的标签)
classCount[voteIlable] = classCount.get(voteIlable, 0) + 1 (以标签为健,以出现的次数为值)
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True) (将字典按照值的大小排序) 详细点击
return sortedClassCount[0][0] (最后返回出现次数最多的标签)
接下来我们实验一下,
我们进入终端
图片描述

下一节学习 k邻近算法应用实例(一)


小小六级了
28 声望6 粉丝