《机器学习实战》-02 k-近邻算法

说明:

作业的所有代码都要基于Python3
学习大纲:https://blog.csdn.net/qq_34243930/article/details/84669684
(所有计划均在学习大纲里)

02 k-近邻算法(第一周)

2.1 k-近邻算法概述
2.2 示例:使用 k-近邻算法改进网站的配对效果
2.3 示例:手写识别系统

第一节学习内容
学习时间: 12/4—12/7
任务1题目: 书籍阅读
任务详解: 阅读《机器学习实战》书籍第二章2.1、2.2、2.3章节
参考资料: 李航《统计学习方法》第3章

作业1: 简要概括 k-近邻算法的原理,优缺点。
作业2: 将本章中“使用 k­近邻算法改进网站的配对效果”完整代码键入,并添加详细注释。
作业3: 将本章中“手写识别系统”完整代码键入,并添加详细注释。

第一节学习内容笔记

《第2章-k-近邻算法》

在这里插入图片描述
本章第一节基于电影中出现的亲吻、打斗出现的次数,使用k-近邻算法构造程序,自动划分电影的题材类型。我们首先使用电影分类讲解k-近邻算法的基本概念,然后学习如何在其他系统上使用k-近邻算法。
本章介绍第一个机器学习算法:k-近邻算法,它非常有效而且易于掌握。首先,我们将探讨k-近邻算法的基本理论,以及如何使用距离测量的方法分类物品;其次我们将使用Python从文本文件中导入并解析数据;再次,本书讨论了当存在许多数据来源时,如何避免计算距离时可能碰到的一些常见错误;最后,利用实际的例子讲解如何使用k-近邻算法改进约会网站和手写数字识别系统。

2.1 k-近邻算法概述

简单地说,k-近邻算法采用测量不同特征值之间的距离方法进行分类。
在这里插入图片描述
优点:
1、精度高
2、对异常数据不敏感(你的类别是由邻居中的大多数决定的,一个异常邻居并不能影响太大)
3、无数据输入假定
4、算法简单,容易理解,无复杂机器学习算法。
缺点:
1、计算复杂度高(需要计算新的数据点与样本集中每个数据的“距离”,以判断是否是前k个邻居)
2、空间复杂度高(巨大的矩阵)。

k-近邻算法(kNN),它的工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
本章主要讲解如何在实际环境中应用k-近邻算法,同时涉及如何使用Python工具和相关的机器学习术语。按照1.5节开发机器学习应用的通用步骤,我们使用Python语言开发k-近邻算法的简单应用,以检验算法使用的正确性。
在这里插入图片描述

2.1.1 准备:使用 Python 导入数据

首先,创建名为kNN.py的Python模块。在构造完整的k-近邻算法之前,我
们还需要编写一些基本的通用函数,在kNN.py文件中增加下面的代码:

import numpy as np
import operator


def createDataSet():
    """
    创建数据集和标签
    """
    group = np.array([1.0, 1.1], [1.0, 1.0], [0, 0], [0,0.1])
    labels = ['A', 'A', 'B', 'B']
    return group, labels

在上面的代码中,我们导入了两个模块:第一个是科学计算包NumPy;第二个是运算符模块,k-近邻算法执行排序操作时将使用这个模块提供的函数。
这里有4组数据,每组数据有两个我们已知的属性或者特征值。上面的 group 矩阵每行包含一个不同的数据,我们可以把它想象为某个日志文件中不同的测量点或者入口。由于人类大脑的限制,我们通常只能可视化处理三维以下的事务。因此为了简单地实现数据可视化,对于每个数据点我们通常只使用两个特征。
向量 labels 包含了每个数据点的标签信息, labels 包含的元素个数等于 group 矩阵行数。
在这里插入图片描述

2.1.2 实施 kNN 算法

k-近邻算法的伪代码
该函数的功能是使用k-近邻算法将每组数据划分到某个类中,其伪代码如下:
对未知类别属性的数据集中的每个点依次执行以下操作:
(1) 计算已知类别数据集中的点与当前点之间的距离;
(2) 按照距离递增次序排序;
(3) 选取与当前点距离最小的k个点;
(4) 确定前k个点所在类别的出现频率;
(5) 返回前k个点出现频率最高的类别作为当前点的预测分类。
Python函数 classify0()
在这里插入图片描述
classify0() 函数有4个输入参数:用于分类的输入向量是inX,输入的训练样本集为dataSet,标签向量为 labels ,最后的参数 k 表示用于选择最近邻居的数目,其中标签向量的元素数目和矩阵 dataSet 的行数相同。

def classify0(inX, dataSet, labels, k):
    """
        k-近邻算法
        :param inX: 用于分类的输入向量
        :param dataSet:输入的训练样本集
        :param labels:标签向量
        :param k:用于选择最近邻居的数目
    """
    dataSetSize = dataSet.shape[0]                          # dataSet行数
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet      # 对应项相减
    sqDiffMat = diffMat**2                                  # 平方
    sqDistances = sqDiffMat.sum(axis=1)                     # axis=1:按行,相加
    distances = sqDistances**0.5                            # 开根号
    sortedDistIndicies = distances.argsort()                # 排序(返回的是数组值从小到大的索引值)
    classCount = {}                                         # 创建空字典
    for i in range(k):                                      # 找出最近的k个
        voteIlabel = labels[sortedDistIndicies[i]]          # 选出来的标签
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1  # dict. get()函数返回指定键的值,如果值不在字典中则返回0
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)   # 倒序排序
    return sortedClassCount[0][0]

【1】距离计算
程序清单2-1使用欧氏距离公式,计算两个向量点xA和xB之间的距离:
在这里插入图片描述
【2】排序,选择距离最小的k个点
计算完所有点之间的距离后,可以对数据按照从小到大的次序排序。然后,确定前k个距离最小元素所在的主要分类,输入k总是正整数。
【3】排序,返回发生频率最高的元素标签
最后,将classCount字典分解为元组列表,然后使用程序第二行导入运算符模块的 itemgetter 方法,按照第二个元素的次序对元组进行排序 。此处的排序为逆序,即按照从最大到最小次序排序,最后返回发生频率最高的元素标签。

2.1.3 如何测试分类器

为了测试分类器的效果,我们可以使用已知答案的数据,当然答案不能告诉分类器,检验分类器给出的结果是否符合预期结果。通过大量的测试数据,我们可以得到分类器的错误率——分类器给出错误结果的次数除以测试执行的总数。错误率是常用的评估方法,主要用于评估分类器在某个数据集上的执行效果。完美分类器的错误率为0,最差分类器的错误率是1.0。

2.1 Python 语法解析

1、numpy array数组

一个[]代表一行,[]里面元素个数,代表列数
在这里插入图片描述

2、numpy tile铺地砖
# 在列方向上重复[0,0]5次,默认行1次
# (后面是一个数就铺不成地板了 直接在后面延长)
np.tile([0,0],5)
Out[10]: array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 

# 在列方向上重复[0,0]1次,行1次
# (有两个中括号 外面那个代表整个地板 里面那个代表一块地砖)
np.tile([0, 0], (1, 1))
Out[11]: array([[0, 0]])

# 在列方向上重复[0,0]1次,行2次
np.tile([0,0],(2,1))
Out[12]: 
array([[0, 0],
       [0, 0]])
       
# 在列方向上重复[0,0]1次,行3次
np.tile([0,0],(3,1))
Out[13]: 
array([[0, 0],
       [0, 0],
       [0, 0]])

# 在列方向上重复[0,0]3次,行1次
np.tile([0, 0], (1, 3))
Out[14]: array([[0, 0, 0, 0, 0, 0]])

#在列方向上重复[0,0]3次,行2次
np.tile([0, 0], (2, 3))
Out[15]: 
array([[0, 0, 0, 0, 0, 0],
       [0, 0, 0, 0, 0, 0]])

在这里插入图片描述

3、两个乘号:x的n次方

在这里插入图片描述

4、numpy.argsort()

numpy.argsort() 函数返回的是数组值从小到大的索引值。
返回的是索引值!!!
在这里插入图片描述
axis: 沿着它排序数组的轴,如果没有数组会被展开,沿着最后的轴排序, axis=0 按列排序,axis=1 按行排序
在这里插入图片描述

5、创建空字典

在这里插入图片描述

6、字典(Dictionary) get()方法

dict. get() 函数返回指定键的值,如果值不在字典中返回默认值。
在这里插入图片描述

7、sorted() 函数

sorted() 函数对所有可迭代的对象进行排序操作。
sorted(iterable, key=None, reverse=False)
iterable – 可迭代对象。
key – 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
reverse – 排序规则,reverse = True 降序 , reverse = False 升序(默认)。

8、字典(Dictionary) items()方法

Python 字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
dict.items()

9、operator.itemgetter函数

operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为一些序号。
在这里插入图片描述

2.2 示例:使用 k-近邻算法改进约会网站的配对效果

在这里插入图片描述

2.2.1 准备数据:从文本文件中解析数据

在这里插入图片描述
在这里插入图片描述
在将上述特征数据输入到分类器之前,必须将待处理数据的格式改变为分类器可以接受的格式。
在kNN.py中创建名为 file2matrix 的函数,以此来处理输入格式问题。该函数的输入为文件名字符串,输出为训练样本矩阵和类标签向量。
将下面的代码增加到kNN.py中。
在这里插入图片描述

def file2matrix(filename):
    """
    输入文件格式转换
    :param filename:待处理文件
    """
    love_dictionary = {'largeDoses': 3, 'smallDoses': 2, 'didntLike': 1}
    fr = open(filename)                       # 打开文件操作
    arrayOLines = fr.readlines()              # 读取所有行并返回一个列表(每一行成为列表中的一个元素)
    numberOfLines = len(arrayOLines)          # 返回列表元素个数
    returnMat = np.zeros((numberOfLines, 3))  # 初始化零填充的矩阵
    classLabelVector = []                     # prepare labels return
    index = 0
    for line in arrayOLines:
        line = line.strip()
        listFromLine = line.split('\t')       # 字符串分割
        returnMat[index, :] = listFromLine[0:3]
        if (listFromLine[-1].isdigit()):      # 若文件中是以数字方式存储喜好类别
            classLabelVector.append(int(listFromLine[-1]))
        else:                                 # 若文件中是以英文存储喜好类别
            classLabelVector.append(love_dictionary.get(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector

【1】得到文件行数
首先我们需要知道文本文件包含多少行。打开文件,得到文件的行数 。
【2】创建返回的NumPy矩阵
然后创建以零填充的矩阵NumPy (实际上,NumPy是一个二维数组,这里暂时不用考虑其用途)。为了简化处理,我们将该矩阵的另一维度设置为固定值 3 ,你可以按照自己的实际需求增加相应的代码以适应变化的输入值。
【3】解析文件数据到列表
循环处理文件中的每行数据 ,首先使用函数 line.strip() 截取掉所有的回车字符,然后使用tab字符 \t 将上一步得到的整行数据分割成一个元素列表。(Why?使用tab字符 \t 。因为文本里数据就是用Tab字符间距的)
接着,我们选取前3个元素,将它们存储到特征矩阵中。Python语言可以使用索引值-1表示列表中的最后一列元素,利用这种负索引,我们可以很方便地将列表的最后一列存储到向量 classLabelVector 中。
需要注意的是,我们必须明确地通知解释器,告诉它列表中存储的元素值为整型,否则Python语言会将这些元素当作字符串处理。
在这里插入图片描述
若kNNTest.py不在kNN.py所属工程文件中(但工程文件与kNNTest.py在同一目录下):
在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

from KNN import kNN

datingDataMat, datingLabels = kNN.file2matrix('datingTestSet.txt')
print(datingDataMat)
print(datingLabels[0:20])

在这里插入图片描述
若kNNTest.py在kNN.py所属工程文件中:
在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

from kNN import *

datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
print(datingDataMat)
print(datingLabels[0:20])

在这里插入图片描述
或者:
在这里插入图片描述
现在已经从文本文件中导入了数据,并将其格式化为想要的格式,接着我们需要了解数据的真实含义。当然我们可以直接浏览文本文件,但是这种方法非常不友好,一般来说,我们会采用图形化的方式直观地展示数据。下面就用Python工具来图形化展示数据内容,以便辨识出一些数据模式。

2.2.2 分析数据:使用 Matplotlib 创建散点图

Matplotlib 创建散点图绘制方法参考:https://blog.csdn.net/qq_34243930/article/details/84557429
在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

import kNN
import matplotlib.pyplot as plt

datingDataMat, datingLabels = kNN.file2matrix('datingTestSet.txt')

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2])
plt.show()

在这里插入图片描述
在这里插入图片描述
由于没有使用样本分类的特征值,我们很难从图2-3中看到任何有用的数据模式信息。
一般来说,我们会采用色彩或其他的记号来标记不同样本分类,以便更好地理解数据信息。Matplotlib库提供的 scatter 函数支持个性化标记散点图上的点。
在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

import kNN
import matplotlib.pyplot as plt
from numpy import *

datingDataMat, datingLabels = kNN.file2matrix('datingTestSet.txt')

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

在这里插入图片描述
上述代码利用变量datingLabels存储的类标签属性,在散点图上绘制了色彩不等、尺寸不同的点。
在这里插入图片描述
带有样本分类标签的约会数据散点图。虽然能够比较容易地区分数据点从属类别,但依然很难根据这张图得出结论性信息。
但采用列1datingDataMat[:, 0]和2datingDataMat[:, 1]的属性值却可以得到更好的效果,图中清晰地标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同:
在这里插入图片描述

2.2.3 准备数据:归一化数值

在这里插入图片描述
在处理这种不同取值范围的特征值时,我们通常采用的方法是将数值归一化,如将取值范围处理为0到1或者1到1之间。下面的公式可以将任意取值范围的特征值转化为0到1区间内的值:
在这里插入图片描述
其中 min 和 max 分别是数据集中的最小特征值和最大特征值。
我们需要在文件kNN.py中增加一个新函数 autoNorm() ,该函数可以自动将数字特征值转化为0到1的区间。
在这里插入图片描述

def autoNorm(dataSet):
    """
    归一化特征值
    :param dataSet: 原数据集
    """
    minVals = dataSet.min(0)   # min(0)返回该矩阵中每一列的最小值
    maxVals = dataSet.max(0)   # max(0)返回该矩阵中每一列的最大值
    ranges = maxVals - minVals
    normDataSet = np.zeros(np.shape(dataSet))   # 创建与dataSet维度相同的全0矩阵
    m = dataSet.shape[0]       # dataSet行数
    normDataSet = dataSet - np.tile(minVals, (m, 1))    # tile铺地砖:1000行1列,(相减)
    normDataSet = normDataSet/np.tile(ranges, (m, 1))   # 相除
    return normDataSet, ranges, minVals

在函数 autoNorm() 中,我们将每列的最小值放在变量 minVals 中,将最大值放在变量maxVals 中,其中 dataSet.min(0) 中的参数0使得函数可以从列中选取最小值,而不是选取当前行的最小值。然后,函数计算可能的取值范围,并创建新的返回矩阵。正如前面给出的公式,为了归一化特征值,我们必须使用当前值减去最小值,然后除以取值范围。需要注意的是,特征值矩阵有1000×3个值,而 minVals 和 range 的值都为1×3。为了解决这个问题,我们使用NumPy库中 tile() 函数将变量内容复制成输入矩阵同样大小的矩阵。
注意这是具体特征值相除。【而对于某些数值处理软件包, / 可能意味着矩阵除法,但在NumPy库中,矩阵除法需要使用函数linalg.solve(matA,matB) 。】
在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

import kNN

datingDataMat, datingLabels = kNN.file2matrix('datingTestSet.txt')

normMat, ranges, minVals = kNN.autoNorm(datingDataMat)

print(normMat)
print(ranges)
print(minVals)

在这里插入图片描述

2.2.4 测试算法:作为完整程序验证分类器

机器学习算法一个很重要的工作就是评估算法的正确率,通常我们只提供已有数据的90%作为训练样本来训练分类器,而使用其余的10%数据去测试分类器,检测分类器的正确率。本书后续章节还会介绍一些高级方法完成同样的任务,这里我们还是采用最原始的做法。
需要注意的是,10%的测试数据应该是随机选择的,由于海伦提供的数据并没有按照特定目的来排序,所以我们可以随意选择10%数据而不影响其随机性。
代码里我们定义一个计数器变量,每次分类器错误地分类数据,计数器就加1,程序执行完成之后计数器的结果除以数据点总数即是错误率。
为了测试分类器效果,在kNN.py文件中创建函数 datingClassTest :
在这里插入图片描述

def datingClassTest():
    """
    测试分类器
    :return: error rate错误率
    """
    hoRatio = 0.10                    # 拿出10%作为测试test数据
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')  # 输入文件格式转换
    normMat, ranges, minVals = autoNorm(datingDataMat)               # 归一化特征值
    m = normMat.shape[0]              # normMat归一化特征值后的数据集,行数
    numTestVecs = int(m*hoRatio)      # 取整
    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)))

函数 datingClassTest 如程序清单2-4所示,它
【1】首先使用了 file2matrix ()和 autoNorm() 函数从文件中读取数据并将其转换为归一化特征值。
【2】接着计算测试向量的数量,此步决定了normMat 向量中哪些数据用于测试,哪些数据用于分类器的训练样本;
【3】然后将这两部分数据输入到原始kNN分类器函数 classify0 。
【4】最后,函数计算错误率并输出结果。
注意此处我们使用原始分类器,本章花费了大量的篇幅在讲解如何处理数据,如何将数据改造为分类器可以使用的特征值。得到可靠的数据同样重要,本书后续的章节将介绍这个主题。
在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

import kNN
kNN.datingClassTest()

在这里插入图片描述
我们可以改变函数datingClassTest 内变量 hoRatio 和变量 k 的值,检测错误率是否随着变量值的变化而增加。依赖于分类算法、数据集和程序设置,分类器的输出结果可能有很大的不同。
这个例子表明我们可以正确地预测分类.

2.2.5 使用算法:构建完整可用系统

上面我们已经在数据上对分类器进行了测试,现在终于可以使用这个分类器为海伦来对人们分类。我们会给海伦一小段程序,通过该程序海伦会在约会网站上找到某个人并输入他的信息。程序会给出她对对方喜欢程度的预测值。
在这里插入图片描述

def classifyPerson():
    """
    预测函数
    """
    resultList = ['not at all', 'in small doses', 'in large doses']

    percentTats = float(input("percentage of time spent playing video games?"))
    ffMiles = float(input("frequent flier miles earned per year?"))
    iceCream = float(input("liters of ice cream consumed per year?"))

    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)

    inArr = np.array([ffMiles, percentTats, iceCream])    # 用于分类的输入向量(也需要归一化:(inArr - minVals)/ranges)
    classifierResult = classify0((inArr - minVals)/ranges, normMat, datingLabels, 3)
    print("You will probably like this person: %s" % resultList[classifierResult - 1])

在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

import kNN

datingDataMat, datingLabels = kNN.file2matrix('datingTestSet.txt')
normMat, ranges, minVals = kNN.autoNorm(datingDataMat)
kNN.classifyPerson()

在这里插入图片描述

2.2 Python 语法解析

1、File readlines() 方法

readlines() 方法用于读取所有行(直到结束符 EOF)并返回列表
例子见:https://blog.csdn.net/qq_34243930/article/details/83748085#t32 中文件读操作部分

2、List len()方法

len(list)返回列表元素个数。
在这里插入图片描述

3、字符串分割str.split()

str.split()函数
()里放的是要以什么进行分割
输出的是一个列表list
例子见:https://blog.csdn.net/qq_34243930/article/details/83748085#t32 中字符串分割部分

4、[0:3]指第0,1,2个元素

正向索引:从0开始标!
反向索引:从-1开始标!
区间索引【A:B】注意不包含位置B!!!
正向索引区间从0开始,可以省略0。比如:S[0:3]等价于s[:3]
反向索引:以-1结尾,省略-1表示可以取到最后一位了。
但是这里S[-4:-1]不等价于s[-4:]!!!
例子见:https://blog.csdn.net/qq_34243930/article/details/83748085#t32 中字符串部分

5、 isdigit()方法

isdigit() 方法检测字符串是否只由数字组成。
str.isdigit()
如果字符串只包含数字则返回 True 否则返回 False。

6、List append()方法

append() 方法用于在列表末尾添加新的对象。
list.append(obj)
obj – 添加到列表末尾的对象。
在这里插入图片描述

7、plt.figure() :自定义画布大小

figure(num=None, figsize=None, dpi=None, facecolor=None, edgecolor=None, frameon=True)
num:图像编号或名称,数字为编号 ,字符串为名称
figsize:指定figure的宽和高,单位为英寸;
dpi参数指定绘图对象的分辨率,即每英寸多少个像素,缺省值为80
1英寸等于2.5cm,A4纸是 21*30cm的纸张
facecolor:背景颜色
edgecolor:边框颜色
frameon:是否显示边框
在这里插入图片描述

8、plt.subplot() :设置画布划分以及图像在画布上输出的位置

plt.subplot(221):
将figure设置的画布大小分成几个部分,参数‘221’表示2(row)x2(colu),即将画布分成2x2,两行两列的4块区域,1表示选择图形输出的区域在第一块,图形输出区域参数必须在“行x列”范围(此处必须在1和2之间选择)
plt.subplot(111):
如果参数设置为subplot(111),则表示画布整个输出,不分割成小块区域,图形直接输出在整块画布上

9、add_subplot 和subplot区别
10、plt.scatter(x,y)

在这里插入图片描述

11、min(),max()的使用

min(0)返回该矩阵中每一列的最小值
min(1)返回该矩阵中每一行的最小值
max(0)返回该矩阵中每一列的最大值
max(1)返回该矩阵中每一行的最大值

12、np.array()、np.shape()、np.ones()、np.zeros()

采用np.array()创建时需要几个维度就要用几个[ ]括起来,这种创建方式要给定数据;
采用np.ones()或np.zeros()创建分别产生全1或全0的数据,
用a.shape会输出你创建时的输入,创建时输入了几个维度输出就会用几个[ ]括起来,shape的返回值是一个元组,里面每个数字表示每一维的长度
在这里插入图片描述

13、格式化输出的方法

.format与%
在这里插入图片描述
目前为止,我们已经看到如何在数据上构建分类器。这里所有的数据让人看起来都很容易,但是如何在人不太容易看懂的数据上使用分类器呢?从下一节的例子中,我们会看到如何在二进制存储的图像数据上使用kNN。

2.3 示例:手写识别系统

本节我们一步步地构造使用k-近邻分类器的手写识别系统。为了简单起见,这里构造的系统只能识别数字0到9,参见图2-6。需要识别的数字已经使用图形处理软件,处理成具有相同的色彩和大小:宽高是32像素×32像素的黑白图像。尽管采用文本格式存储图像不能有效地利用内存空间,但是为了方便理解,我们还是将图像转换为文本格式。
在这里插入图片描述

2.3.1 准备数据:将图像转换为测试向量

实际图像存储在第2章源代码的两个子目录内:目录trainingDigits中包含了大约2000个例子,每个例子的内容如图2-6所示,每个数字大约有200个样本;目录testDigits中包含了大约900个测试数据。我们使用目录trainingDigits中的数据训练分类器,使用目录testDigits中的数据测试分类器的效果。两组数据没有重叠,你可以检查一下这些文件夹的文件是否符合要求。
在这里插入图片描述
在这里插入图片描述
为了使用前面两个例子的分类器,我们必须将图像格式化处理为一个向量。我们将把一个32×32的二进制图像矩阵转换为1×1024的向量,这样前两节使用的分类器就可以处理数字图像信息了。
我们首先编写一段函数 img2vector ,将图像转换为向量:该函数创建1×1024的NumPy数组,然后打开给定的文件,循环读出文件的前32行,并将每行的头32个字符值存储在NumPy数组中,最后返回数组。
在这里插入图片描述

def img2vector(filename):
    """
    将图像转换为向量
    """
    returnVect = np.zeros((1, 1024))     # 创建1×1024的NumPy数组
    fr = open(filename)                  # 打开文件
    for i in range(32):                  # 循环读出文件32行
        lineStr = fr.readline()          # 一行一行读
        for j in range(32):              # 将每行的头32个字符值存储在NumPy数组
            returnVect[0, 32*i+j] = int(lineStr[j])
    return returnVect

在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

import kNN

testVector = kNN.img2vector('testDigits/0_13.txt')
print(testVector[0, 0:31])
print(testVector[0, 32:63])

注意:文件夹应该放到工程文件夹内
在这里插入图片描述
在这里插入图片描述

2.3.2 测试算法:使用 k-近邻算法识别手写数字

上节我们已经将数据处理成分类器可以识别的格式,本节我们将这些数据输入到分类器,检测分类器的执行效果。
程序清单2-6所示的函数 handwritingClassTest() 是测试分类器的代码,将其写入kNN.py文件中。
在写入这些代码之前,我们必须确保将 from os import listdir 写入文件的起始部分,这段代码的主要功能是从os模块中导入函数 listdir ,它可以列出给定目录的文件名。
在这里插入图片描述

def handwritingClassTest():
    hwLabels = []
    trainingFileList = listdir('trainingDigits')  # 获取训练集目录内容
    m = len(trainingFileList)                     # 目录中文件个数
    trainingMat = np.zeros((m, 1024))             # 创建一个m行1024列的训练矩阵
    for i in range(m):
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]       # 以'.'分割fileNameStr并获取第一个数据。即0_110.txt的0_110
        classNumStr = int(fileStr.split('_')[0])  # 以'_'分割fileStr并获取第一个数据。即0_110的0
        hwLabels.append(classNumStr)
        trainingMat[i, :] = img2vector('trainingDigits/%s' % fileNameStr)

    testFileList = listdir('testDigits')          # 获取测试集目录内容
    errorCount = 0.0                              # 初始化错误计数值
    mTest = len(testFileList)                     # 目录中文件个数
    for i in range(mTest):
        fileNameStr = testFileList[i]
        fileStr = fileNameStr.split('.')[0]
        classNumStr = int(fileStr.split('_')[0])
        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)

        classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr))
        if classifierResult != classNumStr:
            errorCount += 1.0
    print("\nthe total number of errors is: %d" % errorCount)
    print("\nthe total error rate is: %f" % (errorCount/float(mTest)))

【1】将trainingDigits目录中的文件内容存储在列表中 ,然后可以得到目录中有多少文件,并将其存储在变量 m 中。
【2】接着,代码创建一个 m 行1024列的训练矩阵,该矩阵的每行数
据存储一个图像。我们可以从文件名中解析出分类数字 。该目录下的文件按照规则命名,如文件9_45.txt的分类是9,它是数字9的第45个实例。然后我们可以将类代码存储在 hwLabels 向量中,使用前面讨论的 img2vector 函数载入图像。
在下一步中,我们对testDigits目录中的文件执行相似的操作,不同之处是我们并不将这个目录下的文件载入矩阵中,而是使用 classify0() 函数测试该目录下的每个文件。由于文件中的值已经在0和1之间,本节并不需要使用2.2节的 autoNorm() 函数。
在这里插入图片描述

"""
Created on Dec 7, 2018
@author: xpt
"""

import kNN

kNN.handwritingClassTest()

在这里插入图片描述
改变变量 k 的值、修改函数 handwriting-ClassTest 随机选取训练样本、改变训练样本的数目,都会对k-近邻算法的错误率产生影响。
实际使用这个算法时,算法的执行效率并不高。因为算法需要为每个测试向量做2000次距离计算,每个距离计算包括了1024个维度浮点运算,总计要执行900次,此外,我们还需要为测试向量准备2MB的存储空间。是否存在一种算法减少存储空间和计算时间的开销呢?
k决策树就是k-近邻算法的优化版,可以节省大量的计算开销。

2.3 Python 语法解析

1、File readline() 方法

readline() 方法用于从文件读取整行,包括 “\n” 字符。如果指定了一个非负数的参数,则返回指定大小的字节数,包括 “\n” 字符
readline(): 一行一行读
例子见 https://blog.csdn.net/qq_34243930/article/details/83748085#t32 中文件读操作部分

2、from os import listdir

主要功能是从os模块中导入函数 listdir ,它可以列出给定目录的文件名。
在这里插入图片描述

2.4 本章小结

k-近邻算法是分类数据最简单最有效的算法,本章通过两个例子讲述了如何使用k-近邻算法构造分类器。
k-近邻算法是基于实例的学习,使用算法时我们必须有接近实际数据的训练样本数据。k-近邻算法必须保存全部数据集,如果训练数据集的很大,必须使用大量的存储空间。此外,由于必须对数据集中的每个数据计算距离值,实际使用时可能非常耗时。
k-近邻算法的另一个缺陷是它无法给出任何数据的基础结构信息,因此我们也无法知晓平均实例样本和典型实例样本具有什么特征。下一章我们将使用概率测量方法处理分类问题,该算法可以解决这个问题。

Python:variable in function(argument、function) name should be lowercase 处理方式

用pyCharm时,常会出现警告信息:
function name should be lowercase --函数名应该是小写 字母
argument name should be lowercase --参数名应该是小写字母
variable in function should be lowercase --变量应该是小写字母

全是小写字母,可能与以往的习惯不大一样,将这样的警告忽略的方法如下:
File →Settings→Editor→Inspections→Python→PEP 8 naming convention violation
右下角有个"Ignored errors",增加:
N802
N803
N806
分别对应上面的三种提示。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏普通

谢谢打赏~普通在此谢过

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值