机器学习算法(一):k-近邻理论与python实现+经典应用(约会网站匹配、手写识别系统)

一份算法学习笔记~
亲爱的朋友,恭喜你点开神秘之门,哈哈哈哈!从这里开始,我们一起学习机器学习的经典算法吧。
这一次的主要内容是机器学习入门算法:k-近邻。理论相当好理解,下面我们正式开始啦!

算法简介

k-近邻算法是1968年,由Cover和Hart提出,主要用来对数据进行分类。其核心思想易于理解,已知数据集中所有样本的特征和标签,当输入新的样本a时,通过计算样本a与数据集中其他样本在特征空间的距离,选取最近的k个样本,由这k个样本投票决定样本a的标签,即这k个样本中,占比最高的类别,就作为样本a的标签。

主要步骤:
1、 依次计算已知样本点到待分类点的距离。
2、 对计算出的距离进行递增排序
3、 选取与待分类点最近的k个点。
4、 统计k个点对应的类别出现频率,返回出现频率最高的类别作为待分类点的类别。

常见的距离度量:LP距离、欧式距离、曼哈顿距离等。对概念不清楚的伙伴可以查阅百度百科,在此就不一一介绍了。

算法优点
1、简单好用、理论成熟。
2、精度高
3、对异常值不敏感

算法缺点:
1、计算量大,相应的对内存的需求也较大
2、样本不平衡问题,当数据集中某一类样本数量过多,对结果准确性有较大影响。
3、无法给出数据的内在含义
计算量大,相应的时间消耗就会增加,这一点在手写识别系统中可以感受到。

k值选取
k值是k-近邻算法的关键参数,k值取多少需要参考数据而定。当k值越大时,相应的模型的偏差会越大,对异常值就越不敏感。K值很大时,容易造成模型欠拟合。相反,当k值越小时,模型的方差就会越大,k值很小时,容易造成模型过拟合。

程序实现

假设数据集中样本有两个特征x和y,特征空间为二维,对应两种类别,A和B
数据集:四个样本 (1.0,0) (8.3,8) (1.2,3) (9.2,7)
类别:对应四个标签为 A,B,A,B
待分类样本:(10,9)

from numpy import *
import os 
import operator
import numpy

########构建数据集######################
''' 
假设数据集中样本有两个特征x和y,特征空间为二维.对应两种类别,A和B
'''
def createDataSet():
    dataSet=array([[1.0,0],[8.3,8],[1.2,3],[9.2,7]])
    label=['A','B','A','B']
    return dataSet,label
dataSet,label=createDataSet() 
#######knn算法#########################
'''
距离度量:欧氏距离
X_example:待分类点
dataSet:数据集
label:dataSet中样本的标签
k:算法参数
'''
def knn(X_example,dataSet,label,k):
    dataSize=len(dataSet)   ##计算数据集长度
    minus=tile(X_example,(dataSize,1))-dataSet  #重复X_example,行方向datasize次,列方向一次,得出的数组与数据集大小一致,再与数据集相减
    minus2=minus**2   #相减结果平方
    D_distance=(minus2.sum(axis=1))**0.5 #结果按行求和。
    index=D_distance.argsort()  #排序,返回的是D_distance中元素从小到大对应的索引值,数组[]

    classCount={}  #创建一个字典统计选择的k个点对应的标签出现次数
    for i in range(k):
        votelabel=label[index[i]]  #获取前k个近点的标签
        classCount[votelabel]=classCount.get(votelabel,0)+1 #首次出现则赋值0,加1
    sortedResult=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)  ##返回一个list, k行两列的   [('B', 2), ('A', 1)]
    return sortedResult[0][0]
    
print('样本类别为:%s' %(knn([10,9],dataSet,label,2)))  #输入带分类点[10,9],,此处k值取2,执行knn算法

程序输出:

样本类别为:B

以上就是算法的基本实现,清晰简单。

经典应用:改进约会网站配对效果

问题描述
海伦一直师兄在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的人,但是并不是每一个她都喜欢。经过一番总结,她发现自己曾交往过三种类型的人:1、不喜欢的人 ; 2、魅力一般的人;3、极具魅力的人。
尽管发现了上述规律,但海伦依然无法将约会网站推荐的匹配对象归入恰当的类别。海伦希望分类软件可以更好地帮助她将匹配对象划分到确切的分类中,此外海伦收集了一些约会网站未记录的数据信息,她认为这些数据有助于匹配对象的归类。

文本数据集:
数据集内有一千个样本,每个样本各占一行,样本中主要包括三种特征:
1、每年获得的飞行常客里程数 (第一列数据)
2、玩视频游戏所消耗的时间百分比 (第二列数据)
3、每周消费的冰淇淋公升数 (第三列数据)
最后一列为样本标签,对应三种类型的人。
在这里插入图片描述

数据预处理:从文件中解析数据,并绘制散点图、进行归一化

import matplotlib.pyplot as plt

###############从文本文件中解析数据#########
'''
filename:存储数据集的文件
returnMat:从文件中解析出的数据特征
label:从文件中解析出的样本标签
'''
def file2matrix(filename):
    fr=open(filename) #打开文件
    AllLine=fr.readlines()  #读取所有行
    NumLine=len(AllLine)  #统计行数
    label=[]            #标签列表
    returnMat=zeros((NumLine,3))   #创建数组存储文件中数据
    index=0    
    for line in AllLine:
        line=line.strip()  #去除每一行后面的回车
        listfromLine=line.split('\t')  #取出的一行内容使用split函数分开,对应四个元素,存在列表listfromLine中
        returnMat[index,:]=listfromLine[0:3]  ##按行复制前三个元素,listfromLine[0]至listfromLine[2]
        index+=1   ##行标+1
        label.append(int(listfromLine[-1]))  #列表listfromLine最后一个元素对应样本标签
    return returnMat,label   

##########散点图展示样本分布##############
'''
取前两个特征
x轴:每年飞行里程数
y轴:玩视频游戏所耗时间百分比
'''
DatasetMat,datingLabels=file2matrix('datingTestSet2.txt')
fig=plt.figure()
ax=fig.add_subplot(111)
ax.scatter(DatasetMat[:,0],DatasetMat[:,1],10.0*array(datingLabels),90.0*array(datingLabels))
plt.xlabel(u'flying ') 
plt.ylabel(u'playing game')
plt.title(u"KNN")
plt.legend(loc="upper left")
plt.show()

###############归一化#####################
'''
datasetMat:数据特征
normMat:datasetMat归一化后的值
归一化:
newValue=(oldValue-min)/max-min
'''
def autoNorm(datasetMat): 
    maxnum=datasetMat.max(0) #每一列最大值
    minNum=datasetMat.min(0) #每一列最小值
    range=maxnum-minNum   #每一个特征对应范围
    normMat=zeros(shape(datasetMat))
    minmat=tile(minNum,(normMat.shape[0],1))
    rangemat=tile(range,(normMat.shape[0],1))
    normMat=(datasetMat-minmat)/rangemat
    return normMat,range,minNum

散点图绘制结果,x轴为每年飞行里程数,y轴为玩视频游戏所耗时间百分比,如图,展现的是三种类型的点在这两种特征下的分布情况。
在这里插入图片描述

算法测试

############算法测试#########################
'''
将normDataSet中的数据按比例划分为测试数据集和训练数据集
testData_num:测试数据集数量
normDataSet[i,:]:测试数据集样本
normDataSet[testData_num:m,:]:训练数据集样本
errorCount:分类错误数量统计
'''
def datingclasstest():
    dataset,label=file2matrix('datingTestSet2.txt')
    normDataSet,ranges,min=autoNorm(dataset)
    testData_num=int(normDataSet.shape[0]*0.1)
    m=normDataSet.shape[0]   
    errorCount=0  
    for i in range(testData_num):
        classResult=knn(normDataSet[i,:],normDataSet[testData_num:m,:],label[testData_num:m],3)
        if classResult!=label[i]:
            errorCount+=1
    print(errorCount/float(testData_num))
datingclasstest()

错误率输出结果:

0.05

构建完整应用,进行预测

###############构建完整可用系统#########################
'''
game、mile、icecream:待分类样本对应的特征
result:算法执行结果对应的文字标签
'''
def datingClassPerson():
    listlabel=['不喜欢的人','魅力一般的人','极具魅力的人']
    game=float(input('玩视频游戏所耗时间百分比:'))
    mile=float(input('每年飞行里程数:'))
    icecream=float(input("每周消费的冰淇淋公升数:"))
    inx=array([mile,game,icecream])
    dataset,datalabel=file2matrix('datingTestSet2.txt')  #读取文件,解析数据
    normMat,range,min=autoNorm(dataset)             #归一化
    classResult=knn(((inx-min)/range),normMat,datalabel,3)  #调用算法
    result=listlabel[classResult-1]
    print("分类结果为:%s" %(result))
datingClassPerson()

输入

玩视频游戏所耗时间百分比:90
每年飞行里程数:400
每周消费的冰淇淋公升数:3

输出

分类结果为:不喜欢的人

经典应用:手写识别系统

需要识别的数字已经过图形软件处理,长宽均为32x32像素的黑白图像。图示为三个数字0,6,9,均为32x32像素大小。
在这里插入图片描述
将图像转换为文本格式,以下每一个文本文件对应一个图像(篇幅限制,下图中仅截取部分文件)
在这里插入图片描述
程序

###############图片转向量##################################
'''
returnMat:返回分类器可接受的向量格式,存储图片中的01
'''
def img2Vector(filename):
    returnMat=zeros((1,1024))  #构建1*1024数组,存储一个数字对应的0 1内容
    fr=open(filename)
    for i in range(32):
        line=fr.readline()
        for j in range(32):
            returnMat[0,i*32+j]=int(line[j])
    return returnMat        
#############构建手写识别系统############################
def handwrite():
    ##处理训练集
    tr_labels=[]
    tr_data=os.listdir('trainingDigits')
    tr_Mat=zeros((len(tr_data),1024))
    for i in range(len(tr_data)):
        file_name=tr_data[i]
        file_str=file_name.split('.')[0]
        digits=int(file_str.split('_')[0]) #从文件名称中解析得到数字的标签
        tr_labels.append(digits)    #训练集的标签
        tr_Mat[i,:]=img2Vector('trainingDigits/%s' %(file_name))  #将训练集的文件依次转换为向量,存储到数组中

    ##处理测试集
    errorcount=0
    test_data=os.listdir('testDigits')
    for i in range(len(test_data)):
        test_file_name=test_data[i]
        tfile_str=test_file_name.split('.')[0]
        test_digits=int(tfile_str.split('_')[0])
        test_vector=img2Vector('testDigits/%s' %(test_file_name))
        result=knn(test_vector,tr_Mat,tr_labels,3)  
        #print('the answer is %d ,the really is %d' %(result,test_digits))
        if result!=test_digits:
            errorcount+=1
    print('Total number of test cases is %d, the number of wrong classification cases is %d'  %(len(test_data),errorcount) )
    print('Classification error rate is :%f' %(errorcount/float(len(test_data))))
    
handwrite()

输出:

Total number of test cases is 946, the number of wrong classification cases is 10
Classification error rate is :0.010571

以上就是k-近邻算法的主要内容,眼过千遍不如手过一遍,建议小伙伴们有时间的话动手实践一下。

独学无朋,则孤陋而难成,如果有不理解的地方,欢迎与我交流,我们可以共同学习!以上若有纰漏,欢迎道友指正,感激不尽

本文参考学习资料:《机器学习实战》(peter Harrington 著)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

且听风吟~

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值