机器学习-第二章-K近邻算法

1.K近邻算法概述

1.1概述

简单的说,k-近邻算法采用测量不同特征值之间的距离方法进行分类。
优点:精度高,对异常值不敏感,无数据输入假定。
缺点:计算复杂度高,空间复杂度高。
使用数据范围:数值型和标称型。
原理:存在一个样本数据集,也称为训练样本集,并且样本数据集中都存在标签,即我们知道样本数据集中每一个数据所属的分类的对应关系。输入没有标签的数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本数据集中特征最为相似数据(最近邻)的分类标签。一般,选择k个最为相似数据中出现次数最多的分类,作为新数据的分类。那么相似是如何定义的?这里采用距离来度量相似度。

1.2准备:使用python导入数据

首先创建KNN.py,

'''
K近邻算法实现
参数
inX:待分类的新数据向量
dataSet:样本数据集
labels:样本集的标签
K:取前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()#对距离进行从小到大排序,返回值是排序后的数在原列表中的下标位置
    classcount={}#定义一个字典

    #选择距离最小的K个点
    for i in range(K):
        voteIlabel=labels[sortedDistIndicies[i]]#获取距离较小数据的标签
        classcount[voteIlabel]=classcount.get(voteIlabel,0)+1#统计各种标签出现的次数

     #对出现过的标签进行排序,选择出现最多次数的标签作为最终结果
    sortedClasscount=sorted(classcount.items(),key=operator.itemgetter(1),reverse=True)
    # python3中用items()替换python2中的iteritems()
    # key=operator.itemgetter(1)根据字典的值进行排序
    # key=operator.itemgetter(0)根据字典的键进行排序
    # reverse降序排序字典

    return sortedClasscount[0][0]

函数用法解释:
1.tile()函数:就是重复的意思,
eg:a=[0,1,2]
b=tile(a,2)
print(b) 结果是:array([0,1,2],[0,1,2])
我们也可以不输入数字,而改用元组,用元组来表示格式.

   下面我们的元组是(1,2) ,也就是1行2次

b=tile(a,(1,2))
结果是:array([0,1,2],[0,1,2])

我们也可以把元组改为(2,1),也就是2行1列
b=tile(a,(2,1))
结果是:array([0,1,2]
[0,1,2])
2.argsort()函数: x=np.array([1,4,3,-1,6,9]),y=x.argsort()
y=[3 0 2 1 4 5];即该函数是将x中的元素从小到大排列,提取其对应的index(索引),然后赋值给y;

距离计算公式:
1.若数据的特征是二维的,则数据间的距离度量可以用两点间的距离公式进行计算

2.当数据上升为高维时,则可采用欧式距离、曼哈顿距离,相关度等进行度量

2.K近邻算法实战-约会网站匹配改进

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

样本数据有3种特征和确定和类别标签
1.每年飞行的里程数
2.玩视频游戏所耗用的百分比
3.每周消费冰淇淋的公斤数
在KNN.py中创建名为fileread_matrix()的函数:用来处理文件数据,返回的是训练样本矩阵和类标签向量。

'''
处理txt文件函数
参数:文件名
返回训练样本矩阵和类标签向量
'''
def fileread_matrix(filename):
    fr=open(filename)
    arrayOLines=fr.readlines()#按行读取全部文件数据
    numberLines=len(arrayOLines)#统计行数
    returnMat=zeros((numberLines,3))#创建一个矩阵
    classLabels=[]#类别标签向量
    index=0
    for line in arrayOLines:
        line=line.strip() #去除空格
        listFormLine=line.split('\t')#每行按'\t'分割字符串
        returnMat[index,:]=listFormLine[0:3]
        classLabels.append(int(listFormLine[-1]))#将每行的最后一个数据加入到类别标签向量
        index+=1
    return returnMat,classLabels

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

'''
展示数据函数
参数:数据文件名
'''
def show_data(filename):
    # 设置汉字格式
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    datingDataMat,label=fileread_matrix(filename)

    # 将fig画布分隔成1行1列,不共享x轴和y轴,fig画布的大小为(13,8)
    # 当nrow=2,nclos=2时,代表fig画布被分为四个区域,axs[0][0]表示第一行第一个区域
    fig,axs=plt.subplots(nrows=2,ncols=2,sharex=False,sharey=False,figsize=(13,8))
    print(fig)
    numberofLabel=len(label)
    LabelsColors=[]
    for i in label:
        if i==1:
            LabelsColors.append('black')
        if i==2:
            LabelsColors.append('orange')
        if i==3:
            LabelsColors.append('red')
    #画出散点图,数据集矩阵第一列(飞行里数),第二列(玩游戏)数据画散点图,散点大小为15,透明度为0.5
    axs[0][0].scatter(x=datingDataMat[:,0],y=datingDataMat[:,1],color=LabelsColors,s=15,alpha=.5)
    #设置标题,x轴,y轴
    axs0_title_text = axs[0][0].set_title(u'每年获得的飞行常客里程数与玩视频游戏所消耗时间占比', FontProperties=font)
    axs0_xlabel_text = axs[0][0].set_xlabel(u'每年获得的飞行常客里程数', FontProperties=font)
    axs0_ylabel_text = axs[0][0].set_ylabel(u'玩视频游戏所消耗时间占', FontProperties=font)
    plt.setp(axs0_title_text, size=9, weight='bold', color='red')
    plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')

    # 画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
    axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=.5)
    # 设置标题,x轴label,y轴label
    axs1_title_text = axs[0][1].set_title(u'每年获得的飞行常客里程数与每周消费的冰激淋公升数', FontProperties=font)
    axs1_xlabel_text = axs[0][1].set_xlabel(u'每年获得的飞行常客里程数', FontProperties=font)
    axs1_ylabel_text = axs[0][1].set_ylabel(u'每周消费的冰激淋公升数', FontProperties=font)
    plt.setp(axs1_title_text, size=9, weight='bold', color='red')
    plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black')

    # 画出散点图,以datingDataMat矩阵的第二(玩游戏)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
    axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=LabelsColors, s=15, alpha=.5)
    # 设置标题,x轴label,y轴label
    axs2_title_text = axs[1][0].set_title(u'玩视频游戏所消耗时间占比与每周消费的冰激淋公升数', FontProperties=font)
    axs2_xlabel_text = axs[1][0].set_xlabel(u'玩视频游戏所消耗时间占比', FontProperties=font)
    axs2_ylabel_text = axs[1][0].set_ylabel(u'每周消费的冰激淋公升数', FontProperties=font)
    plt.setp(axs2_title_text, size=9, weight='bold', color='red')
    plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black')
    plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black')


    # 显示图片
    plt.show()

在这里插入图片描述
通过图可以分析出海伦喜欢生活有品质的男人,因为飞行数多但又不高,玩游戏但又不沉迷。图1所示。

2.3准备数据:归一化数字

若存在样本
序号 玩游戏百分比 飞行数 每周消费冰淇淋公斤数 样本分类
1 0.8 400 0.5 1
2 12 13400 0.9 3
3 0 20000 1.1 2
4 67 32000 0.1 2
样本3与样本4间的距离计算公式为:
在这里插入图片描述
这样明显,飞行里数会对结果产生较大的影响,而事实上这三种特征是同等重要的,因此作为等权重的,飞行里数不应该如此严重到计算结果。所以需要对数据进行归一化操作。
通常将取值范围处理为0到1或者-1到1,下面的公式可以将任何范围的值转化为0到1区间的值:
newvalue=(oldvalue-min)/(max-min)

'''
数值归一化操作
参数:数据集特征矩阵
'''
def autoNorm(dataset):
    min=dataset.min(0)#将每一列的最小值放在min
    max=dataset.max(0)#将每一列的最大值放在min
    ranges=max-min
    #normDataSet=zeros(shape(dataset))
    m=dataset.shape(0)#获取样本集中数据的个数
    normDataSet=dataset-tile(min,(m,1))
    normDataSet=normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,min

2.4测试算法:验证分类器

'''
验证算法的正确率:
无参数
'''
def check_classfy():
    ratio=0.1
    datingMat,labels=fileread_matrix('datingTestSet.txt')
    normMat,ranges,minval=autoNorm(datingMat)
    num=normMat.shape[0]
    num_test=int(num*ratio)
    errot_count=0.0
    for i in range(num_test):
        result=classify0(normMat[i,:],normMat[num_test:num,:],labels[num_test:num],3)
        print("预测结果:%d,真实结果:%d"%(result,labels[i]))
        if result!=labels[i]:
            errot_count+=1.0
    print("错误率为:%d"%(errot_count/float(num_test)))

3.K近邻算法实战-手写数字识别系统

3.1数据集

手写数字数据集TXT格式
在这里插入图片描述
在这里插入图片描述
https://download.csdn.net/download/qq_37937847/12502613

3.2代码实现

'''
时间:2020-6-6
地点:家中
内容:knn实现手写数字识别
训练集:machine_learning/KNN/trainingDigits
测试集:machine_learning/KNN/testDigits
'''


from numpy import *
from os import listdir
import operator



'''
函数名:img2vector(filename)
描述:将图像转换为算法所需的向量格式
参数:文件名
图片是txt文件,是图像文件二值化后转化为,01数据格式的txt文件。32*32大小
'''
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


'''

K近邻算法实现
函数名:classify0(inX,dataSet,labels,K)
参数说明
inX:待分类的新数据向量
dataSet:样本数据集
labels:样本集的标签
K:取前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()#对距离进行从小到大排序,返回值是排序后的数在原列表中的下标位置
    classcount={}#定义一个字典

    #选择距离最小的K个点
    for i in range(K):
        voteIlabel=labels[sortedDistIndicies[i]]#获取距离较小数据的标签
        classcount[voteIlabel]=classcount.get(voteIlabel,0)+1#统计各种标签出现的次数

     #对出现过的标签进行排序,选择出现最多次数的标签作为最终结果
    sortedClasscount=sorted(classcount.items(),key=operator.itemgetter(1),reverse=True)
    # python3中用items()替换python2中的iteritems()
    # key=operator.itemgetter(1)根据字典的值进行排序
    # key=operator.itemgetter(0)根据字典的键进行排序
    # reverse降序排序字典

    return sortedClasscount[0][0]


'''
函数名:handwritingClassTest()
描述:使用K近邻算法实现手写数字识别
无参数
'''
def handwritingClassTest():
    hwLabels=[]
    trainingFileList=listdir('trainingDigits')#获取目录内容,即文件夹中所有文件的名字
    m=len(trainingFileList)#计算文件夹中文件的总个数
    trainingMat=zeros((m,1024))#初始化特征矩阵
    for i in range(m):
        fileNameStr=trainingFileList[i]
        fileStr=fileNameStr.split('.')[0]#利用文件名,获得数据的标签分类
        calssNumStr=int(fileStr.split('_')[0])
        hwLabels.append(calssNumStr)
        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]#利用文件名获取数据的分类标签
        calssNumStr=int(fileStr.split('_')[0])

        vectorUnderTest=img2vector('testDigits/%s'%fileNameStr)
        class_result=classify0(vectorUnderTest,trainingMat,hwLabels,3)
        print('分类结果是:%d,真实结果是:%d'%(class_result,calssNumStr))
        if(class_result!=calssNumStr):
            errorcount+=1.0
    print('测试集数据个数共有:%d'%mTest)
    print('错了%d'%errorcount)
    print('错误率为%f'%(errorcount/float(mTest)))

handwritingClassTest()

4.利用Sklearn中的knn函数库实现-手写数字识别系统

Sklearn简介,是机器学习领域出名的python模块之一,包含了很多机器学习算法,
1.Classification
2.Regression回归
3.Clustering非监督分类
4.Dimensionality reduction 数据降维
5.ModelSelection 模型选择
6.Preprocessing 数据处理

这里我们将会使用到函数:sklearn.neighbors.KNeighborsClassifier

class sklearn.neighbors.KNeighborsClassifier(n_neighbors=5, *, weights=‘uniform’, algorithm=‘auto’, leaf_size=30, p=2, metric=‘minkowski’, metric_params=None, n_jobs=None, **kwargs)

这个函数会用很多参数:

1.n_neighbors:默认为5,就是Knn中的K值
2.weights:默认是uniform,参数可以是unifrom,distance,也可以是用户自己定义的函数,uniform是均等的权重,就说所有的邻近点的权重都是相等的。distance是不均等的权重,距离近的点比距离远的点的影响大。用户自定义的函数,接收距离的数组,返回一组维数相同的权重。
3.algorithm:快速k近邻搜索算法,默认参数为auto,可以理解为算法自己决定合适的搜索算法。除此之外,用户也可以自己指定搜索算法ball_tree、kd_tree、brute方法进行搜索,brute是蛮力搜索,也就是线性扫描,当训练集很大时,计算非常耗时。kd_tree,构造kd树存储数据以便对其进行快速检索的树形数据结构,kd树也就是数据结构中的二叉树。以中值切分构造的树,每个结点是一个超矩形,在维数小于20时效率高。ball tree是为了克服kd树高纬失效而发明的,其构造过程是以质心C和半径r分割样本空间,每个节点是一个超球体。
4.leaf_size:默认是30,这个是构造的kd树和ball树的大小。这个值的设置会影响树构建的速度和搜索速度,同样也影响着存储树所需的内存大小。需要根据问题的性质选择最优的大小。
5.metric:用于距离度量,默认度量是minkowski,也就是p=2的欧氏距离(欧几里德度量)。
6.metric_params:距离公式的其他关键参数,这个可以不管,使用默认的None即可
7.并行处理设置。默认为1,临近点搜索并行工作数。如果为-1,那么CPU的所有cores都用于并行工作。

在这里插入图片描述

'''
时间:2020-6-6
地点:家中
内容:调用sklearn中的KNN算法实现手写数字识别

'''
from sklearn.neighbors import KNeighborsClassifier as kNN
import operator
from os import listdir
import numpy as np

'''
函数名:img2vector(filename)
描述:将图像转换为算法所需的向量格式
参数:文件名
图片是txt文件,是图像文件二值化后转化为,01数据格式的txt文件。32*32大小
'''
def img2vector(filename):
    returnVect=np.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



'''
函数说明:调用Sklearn中的Knn函数实现手写数字识别
函数名:handwritingClassTest()
参数:无参
'''

def handwritingClassTest():
    errorcount=1.0
    hwlabels=[]#训练集的标签
    trainingFileList=listdir('trainingDigits')#得到训练集文件夹下的所有文件名
    m=len(trainingFileList)
    trainingMat=np.zeros((m,1024))#初始化训练集的特征矩阵
    for i in  range(m):
        fileNameStr=trainingFileList[i]
        classNumber=int(fileNameStr.split('_')[0])
        hwlabels.append(classNumber)
        #将每一个文件的1*1024数据存储到trainingMat矩阵中
        trainingMat[i,:]=img2vector('trainingDigits/%s'%(fileNameStr))

    #构建Knn分类器
    sklearn_knn=kNN(n_neighbors=3, algorithm='auto')
    #传入数据进行拟合模型
    sklearn_knn.fit(trainingMat,hwlabels)

    testFileList=listdir('testDigits')
    mtest=len(testFileList)
    for i in range(mtest):
        fileNameStr=testFileList[i]
        classNumber=int(fileNameStr.split('_')[0])
        vectorUndertest=img2vector('testDigits/%s'%(fileNameStr))
        classResult=sklearn_knn.predict(vectorUndertest)
        #test=sklearn_knn.predict_proba(vectorUndertest)#返回的待测数据是各个类别的概率
        #print('test:',test)
        print('预测结果是:%d\t真实结果是:%d'%(classResult,classNumber))
        if(classResult!=classNumber):
            errorcount+=1.0
    print("总共错了%d个数据\n错误率是:%f"%(errorcount,errorcount/mtest))
handwritingClassTest()





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值