K-近邻 python实现

KNN 工作原理(k-NearestNeighbor---主要是用来进行分类)

假设有一个带有标签的样本数据集(训练样本集),其中包含每条数据与所属分类的对应关系。
输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较。
计算新数据与样本数据集中每条数据的距离。
1.对求得的所有距离进行排序(从小到大,越小表示越相似)。
2.取前 k (k 一般小于等于 20 )个样本数据对应的分类标签。
3.求 k 个数据中出现次数最多的分类标签作为新数据的分类。
 

 

  1. 优点:精度高、对异常值不敏感、无数据输入假定

  2. 缺点:计算复杂度高、空间复杂度高

  3. 适用数据范围:数值型和标称型

标称型:一般在有限的数据中取,而且只存在‘是’和‘否’两种不同的结果(一般用于分类)

数值型:可以在无限的数据中取,而且数值比较具体化,例如4.02,6.23这种值(一般用于回归分析)

 

开发流程:(约会网站推荐案例)

收集数据:提供文本文件
准备数据:使用 Python 解析文本文件

将文本记录转换为 NumPy 的解析程序

def file2matrix(filename):
   """
   Desc:
       导入训练数据
   parameters:
       filename: 数据文件路径
   return: 
       数据矩阵 returnMat 和对应的类别 classLabelVector
   """
   fr = open(filename)
   # 获得文件中的数据行的行数
   numberOfLines = len(fr.readlines())

‘’‘

read([size])

从文件当前位置起读取size个字节,若无参数size,则表示读取至文件结束为止,它返回为字符串对象

readline()

该方法每次读出一行内容,所以,读取时占用内存小,比较适合大文件,该方法返回一个字符串对象

readlines()

读取整个文件所有行,保存在一个列表(list)变量中,每行作为一个元素,但读取大文件会比较占内存。

 

‘’’
   # 生成对应的空矩阵
   # 例如:zeros(2,3)就是生成一个 2*3的矩阵,各个位置上全是 0 
   returnMat = zeros((numberOfLines, 3))  # prepare matrix to return
   classLabelVector = []  # prepare labels return
   fr = open(filename)
   index = 0
   for line in fr.readlines():
       # str.strip([chars]) --返回  移除字符串头尾指定的字符后生成的新字符串
       line = line.strip()
       # 以 '\t' 切割字符串
       listFromLine = line.split('\t')
       # 每列的属性数据
       returnMat[index, :] = listFromLine[0:3]
       # 每列的类别数据,就是 label 标签数据
       classLabelVector.append(int(listFromLine[-1]))
       index += 1
   # 返回数据矩阵returnMat和对应的类别classLabelVector
   return returnMat, classLabelVector
 

 

 


分析数据:使用 Matplotlib 画二维散点图

import matplotlib
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0*array(datingLabels), 15.0*array(datingLabels))
plt.show()

scatter参数

https://blog.csdn.net/anneqiqi/article/details/64125186

归一化特征值,消除特征之间量级不同导致的影响

def autoNorm(dataSet):
    """
    Desc:
        归一化特征值,消除特征之间量级不同导致的影响
    parameter:
        dataSet: 数据集
    return:
        归一化后的数据集 normDataSet. ranges和minVals即最小值与范围,并没有用到

    归一化公式:
        Y = (X-Xmin)/(Xmax-Xmin)
        其中的 min 和 max 分别是数据集中的最小特征值和最大特征值。该函数可以自动将数字特征值转化为0到1的区间。
    """
    # 计算每种属性的最大值、最小值、范围
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    # 极差
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))
    m = dataSet.shape[0]
    # 生成与最小值之差组成的矩阵
    normDataSet = dataSet - tile(minVals, (m, 1))
    # 将最小值之差除以范围组成矩阵
    normDataSet = normDataSet / tile(ranges, (m, 1))  # element wise divide
    return normDataSet, ranges, minVals

 


shape[0]就是读取矩阵第一维度的长度

>>> c = array([[1,1],[1,2],[1,3],[1,4]])  #四行二列

>>> c.shape  

(4, 2)  

>>> c.shape[0]  

4  

>>> c.shape[1]  

2  

 

 

python tile() 函数简单介绍

格式:tile(A,reps) 
* A:array_like 输入的array 
* reps:array_like A沿各个维度重复的次数

for example:

A=[1,2]
tile(A,2)
Out[10]: array([1, 2, 1, 2])

tile(A,(2,2))
Out[11]:
array([[1, 2, 1, 2],
       [1, 2, 1, 2]])

tile(A,(2,1))
Out[12]:
array([[1, 2],
       [1, 2]

 

训练算法:此步骤不适用于 k-近邻算法

测试算法:计算错误率

例:

  1. 使用提供的部分数据作为测试样本。

  2. 测试样本和非测试样本的区别在于:

  3. 测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误

 

 

def datingClassTest():
    """
    Desc:
        对约会网站的测试方法
    parameters:
        none
    return:
        错误数
    """
    # 设置测试数据的的一个比例(训练数据集比例=1-hoRatio)
    hoRatio = 0.1  # 测试范围,一部分测试一部分作为样本
    # 从文件中加载数据
    datingDataMat, datingLabels = file2matrix('input/2.KNN/datingTestSet2.txt')  # load data setfrom file
    # 归一化数据
    normMat, ranges, minVals = autoNorm(datingDataMat)
    # m 表示数据的行数,即矩阵的第一维
    m = normMat.shape[0]
    # 设置测试的样本数量, numTestVecs:m表示训练样本的数量
    numTestVecs = int(m * hoRatio)
    print 'numTestVecs=', numTestVecs
    errorCount = 0.0
    for i in range(numTestVecs):
        # 对数据测试
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)
        print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i])
        if (classifierResult != datingLabels[i]): errorCount += 1.0
    print "the total error rate is: %f" % (errorCount / float(numTestVecs))
    print errorCount

#k近邻算法
def classify0(inX, dataSet, labels, k):    #输入向量,输入的训练样本集,标签向量,选择最近邻的数目
    dataSetSize = dataSet.shape[0]    #距离计算
    diffMat = tile(inX, (dataSetSize,1)) - dataSet   #重复数组 
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    sortedDistIndicies = distances.argsort()     #选择距离最小的k个点 (
返回从小到大排序后,原位置所对应的索引,而数组并没有排序
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1   
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)    #排序
    return sortedClassCount[0][0]

 

sorted(iterable,key=None,reverse=False),返回新列表

sort(key=None,reverse=False) 就地改变列表。

key 
接收一个key函数实现自定义排序 
key指定的函数将作用于list 上的每一个元组,并根据key函数返回的结果进行排序 

    print([12, -21, 4, 34])
    [-21, 4, 12, 34]
    print([12, -21, 4, 34], key = abs)
    [4, 12, -21, 34 ]

reverse 是一个布尔值, 当reverse = True时, 元素将被逆序排序

s = [1, 2, 3, 4, 5]
print(sorted(s, reverse = True))
[5, 4, 3, 2, 1]
 

 

 

1. python 自己的sum()

输入的参数首先是[]

>>> sum([0,1,2])
3
>>> sum([0,1,2],3)
6
>>> sum([0,1,2],[3,2,1])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can only concatenate list (not "int") to list


2.python的 numpy当中

现在对于数据的处理更多的还是numpy。没有axis参数表示全部相加,axis=0表示按列相加,axis=1表示按照行的方向相加

>>> import numpy as np
>>> a=np.sum([[0,1,2],[2,1,3]])
>>> a
9
>>> a.shape
()
>>> a=np.sum([[0,1,2],[2,1,3]],axis=0)
>>> a
array([2, 2, 5])
>>> a.shape
(3,)
>>> a=np.sum([[0,1,2],[2,1,3]],axis=1)
>>> a
array([3, 6])
>>> a.shape
(2,)

 


 

使用算法:输入样本数据和结构化的输出结果,然后运行 k-近邻算法判断输入数据分类属于哪个分类,最后对计算出的分类执行后续处理

def clasdifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    percentTats = float(raw_input("percentage of time spent playing video games ?"))
    ffMiles = float(raw_input("frequent filer miles earned per year?"))
    iceCream = float(raw_input("liters of ice cream consumed per year?"))
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = array([ffMils, percentTats, iceCream])
    classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels, 3)
    print "You will probably like this person: ", resultList[classifierResult - 1]

 


完整代码:https://github.com/apachecn/MachineLearning/blob/master/src/python/2.KNN/kNN.py

 

 

项目案例2: 手写数字识别系统
项目概述
构造一个能识别数字 0 到 9 的基于 KNN 分类器的手写数字识别系统。

需要识别的数字是存储在文本文件中的具有相同的色彩和大小:宽高是 32 像素 * 32 像素的黑白图像。

开发流程
收集数据:提供文本文件


准备数据:编写函数 img2vector(), 将图像格式转换为分类器使用的向量格式

将图像文本数据转换为向量

def img2vector(filename):
    returnVect = zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        lineStr = fr.readLine()
        for j in range(32):
            returnVect[0,32*i+j] = int(lineStr[j])
    return returnVect
 

分析数据:在 Python 命令提示符中检查数据,确保它符合要求
训练算法:此步骤不适用于 KNN
测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的
         区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,
         则标记为一个错误
使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取
         数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值