基于pyhton3.6-机器学习实战-kNN代码解释



本人是一名数学系研究生,于2017年底第一次接触python和机器学习,作为一名新手,欢迎与大家交流。

我主要给大家讲解代码,理论部分给大家推荐3本书:

《机器学习实战中文版》

《机器学习》周志华

《统计学习方法》李航

以上3本书,第一本是基于python2的代码实现;剩余两本主要作为第一本书理论省略部分的补充,理论大部分都讲得很细。

博客上关于机器学习实战理论解释都很多,参差不齐,好作品也大都借鉴了以上3本书,网上有很多电子版的书。

与其看看一些没用的博客,真心不如以上3本书有收获。

说实话,学习一定要静下心来,切忌浮躁。不懂可以每天看一点,每天你懂一点,天天积累就多了。

操作系统:windows8.1

python版本:python3.6

运行环境:spyder(anaconda)

# -*- coding: utf-8 -*-
"""
Created on Thu Nov 16 19:55:33 2017

@author: Lelouch_C.C
"""

from numpy import *  
import operator  
 
#创建一个简单的数据集createDataSet
def createDataSet():  
    group = array([[1.0, 0.9], [1.0, 1.0], [0.1, 0.2], [0.0, 0.1]])  
    labels = ['A', 'A', 'B', 'B']  
    return group, labels  
  
  
def classify0(inX, dataSet, labels, k):
    """
    函数说明:kNN算法分类器
    参数:用于分类的数据(测试集)inX 
         用于训练的数据(训练集)dataSet 
         类别标签labes 
         kNN算法参数,选择距离最小的k个点k  
    返回值:分类结果sortedClassCount[0][0] 
    """
    dataSetSize = dataSet.shape[0]          #shape[0]返回dataSet的样本个数
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet       #tile()用于复制
    #在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
    sqDiffMat = diffMat**2                  #二维特征相减后平方
    sqDistances = sqDiffMat.sum(axis=1)     
    #sum()所有元素相加,sum(0)列相加,sum(1)行相加
    distances = sqDistances**0.5            #开方,计算出距离
    sortedDistIndices = distances.argsort()
    #argsort()返回distances中元素从小到大排序后的索引值
    classCount = {}                         #初始化一个记录类别次数的空字典
    for i in range(k):                      #遍历前k个元素(距离最小的k个点),
        voteIlabel = labels[sortedDistIndices[i]]                 #取出它们的类别标签
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #计算类别次数
        #字典的get()方法,返回指定键的值,如果键不在字典中返回设定值0。
    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]           #返回次数最多的类别,即所要分类的类别(注意数据结构)

#准备数据:从文本文件中解析数据
def file2matrix(filename):       
    """
    函数说明:数据导入函数
    """
    fr = open(filename)
    arrayLines=fr.readlines()             #观察readlines()读取后的返回值/列表
    numberOfLines = len(arrayLines)       #读取文件的行数#
    returnMat = zeros((numberOfLines,3))  
    #初始化一个与文件行数相等,列数为3的零矩阵作为特征矩阵,来存储样本的3个特征
    classLabelVector = []                 #初始化一个空列表,来存储样本类别标签
    index = 0                             #index用来指定矩阵的索引值
    for line in arrayLines:
        line = line.strip()
        #strip()删除字符串line中开头、结尾处,位于括号内删除序列的字符,
        #若为空,默认删除所有的空白符(包括'\n','\r','\t',' ')
        listFromLine = line.split('\t')   
        #将以制表符(\t)作为分隔符将整行数据分割成一个元素列表
        returnMat[index,:] = listFromLine[0:3]   
        #将returnMat矩阵的第index个元素(其实是整行)用listFromLine的前3个元素组成的列表替换
        classLabelVector.append(int(listFromLine[-1]))
        #将listfromline最后一列的元素转化为整型(告知解释器为整型,否则会直接被当成字符串),
        #再加到classlabelvedtor中
        index += 1
    return returnMat,classLabelVector
"""
if __name__ == '__main__':
    dataSet, labels =createDataSet()
    inX = array([1.2, 1.0])  
    k = 3  
    outputLabel = classify0(inX, dataSet, labels, 3)  
    print ("Your input is:", inX, "and classified to class: ", outputLabel)  

    inX = array([0.1, 0.3]) 
    outputLabe1 = classify0(inX, dataSet, labels, 3)  
    print ("Your input is:", inX, "and classified to class: ", outputLabe1)
    #输出:
    #Your input is: [1.2 1. ] and classified to class:  A
    #Your input is: [0.1 0.3] and classified to class:  B
#"""
    
#分析数据:使用Matplotlib创建散点图
from matplotlib.font_manager import FontProperties
import matplotlib.lines as mlines
import matplotlib.pyplot as plt

def showdatas(datingDataMat, datingLabels):
    """
    函数说明:画散点图
    """
    font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14) #设置汉字格式
    fig, axs = plt.subplots(nrows=2, ncols=2,figsize=(13,8))
    #将fig画布分成2行2列,不共享x轴和y轴,fig画布的大小为(13,8)
    #当nrow=2,nclos=2时,代表fig画布被分为四个区域,axs[0][0]表示第一行第一个区域,以此类推
    numberOfLabels = len(datingLabels)
    LabelsColors = []
    for i in datingLabels:
        if i == 1:
            LabelsColors.append('black')
        if i == 2:
            LabelsColors.append('orange')
        if i == 3:
            LabelsColors.append('red')
    axs[0][0].scatter(x=datingDataMat[:,0], y=datingDataMat[:,1], color=LabelsColors,s=15, alpha=.5)
    #画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第二列(玩游戏)数据画散点数据,散点大小为15,透明度为0.5
    
    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')
    #设置标题,x轴label,y轴label
    
    axs[0][1].scatter(x=datingDataMat[:,0], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5)
    #画出散点图,以datingDataMat矩阵的第一(飞行常客例程)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
    
    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')
    #设置标题,x轴label,y轴label
    
    axs[1][0].scatter(x=datingDataMat[:,1], y=datingDataMat[:,2], color=LabelsColors,s=15, alpha=.5)
    #画出散点图,以datingDataMat矩阵的第二(玩游戏)、第三列(冰激凌)数据画散点数据,散点大小为15,透明度为0.5
    
    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')
    #设置标题,x轴label,y轴label
    
    didntLike = mlines.Line2D([], [], color='black', marker='.',
                      markersize=6, label='didntLike')                #设置图例
    smallDoses = mlines.Line2D([], [], color='orange', marker='.',
                      markersize=6, label='smallDoses')               #设置图例
    largeDoses = mlines.Line2D([], [], color='red', marker='.',
                      markersize=6, label='largeDoses')               #设置图例
    
    axs[0][0].legend(handles=[didntLike,smallDoses,largeDoses])#添加图例
    axs[0][1].legend(handles=[didntLike,smallDoses,largeDoses])#添加图例
    axs[1][0].legend(handles=[didntLike,smallDoses,largeDoses])#添加图例
    
    plt.show()         #显示图片
"""
if __name__ == '__main__':
    datingDataMat, datingLabels=file2matrix('datingTestSet.txt')
    showdatas(datingDataMat, datingLabels)
#"""
    
#准备数据
def autoNorm(dataSet):
    """
    函数说明:归一化特征值
    """
    minVals = dataSet.min(0)   #获得dataSet每列的最小值
    maxVals = dataSet.max(0)   #获得dataSet每列的最大值
    ranges = maxVals - minVals #最大值和最小值的范围 
    normDataSet = zeros(shape(dataSet)) #shape(dataSet)返回dataSet的矩阵行列数
    m = dataSet.shape[0]       #返回dataSet的行数
    normDataSet = dataSet - tile(minVals, (m, 1))            #原始值减去最小值
    normDataSet = normDataSet / tile(ranges, (m, 1))
    #除以最大和最小值的差,得到归一化数据
    return normDataSet, ranges, minVals     #返回归一化数据结果,数据范围,最小值
"""
if __name__ == '__main__':
    normMat,ranges,minVals=autoNorm(datingDataMat)
    print ('normMat=',normMat)
    print ('ranges=',ranges)
    print ('minVals=',minVals)
    #输出:
    #normMat= [[0.44832535 0.39805139 0.56233353]
              #[0.15873259 0.34195467 0.98724416]
              #[0.28542943 0.06892523 0.47449629]
              #...
              #[0.29115949 0.50910294 0.51079493]
              #[0.52711097 0.43665451 0.4290048 ]
              #[0.47940793 0.3768091  0.78571804]]
    #ranges= [9.1273000e+04 2.0919349e+01 1.6943610e+00]
    #minVals= [0.       0.       0.001156]
#"""

#测试算法    
#分类器针对约会网站的测试代码
def datingClassTest():
    datingDataMat, datingLabels = file2matrix("datingTestSet.txt")
    hoRatio = 0.10            #取所有数据的百分之十
    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], 4)
        #前numTestVecs个数据作为测试集,后m-numTestVecs个数据作为训练集
        print("分类结果:%d\t真实类别:%d" % (classifierResult, datingLabels[i]))
        if classifierResult != datingLabels[i]:
            errorCount += 1.0
    print("错误率:%f%%" %(errorCount/float(numTestVecs)*100)) 
    #%f 匹配一个浮点数,后面的%%输出一个%,因为%是作为格式符号了,因此需要2个表示1个%字符
"""
if __name__ == '__main__':
    datingClassTest()
    #输出:
    #错误率:4.000000%
#"""
    
#
def classifyPerson():
    """
    函数说明:约会网站预测函数
    """
    resultList = ['讨厌','有些喜欢','非常喜欢']            #输出结果
    precentTats = float(input("玩视频游戏所耗时间百分比:"))
    ffMiles = float(input("每年获得的飞行常客里程数:"))
    iceCream = float(input("每周消费的冰激淋公升数:"))
    datingDataMat, datingLabels = file2matrix("datingTestSet.txt") #生成训练数据集
    normMat, ranges, minVals = autoNorm(datingDataMat)   #训练集归一化
    inArr = array([precentTats, ffMiles, iceCream])      #生成测试集
    norminArr = (inArr - minVals) / ranges               #测试集归一化
    classifierResult = classify0(norminArr, normMat, datingLabels, 3)#返回分类结果
    print("你可能%s这个人" % (resultList[classifierResult-1]))        #打印结果
    
"""
if __name__ == '__main__':
    classifyPerson()
    #输出:
    #玩视频游戏所耗时间百分比:10
    #每年获得的飞行常客里程数:10000
    #每周消费的冰激淋公升数:0.5
    #你可能讨厌这个人
#"""
    
#示例:手写识别系统
    
#准备数据:将图像转换成测试向量    
def img2vector(filename):           
    """
    函数说明:将32*32的二进制图像转换成1*1024的向量
    """
    returnVect = zeros((1,1024))    #初始化一个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])
            #将每行32个字符转化为整型存入returnVect对应的位置
    return returnVect

from os import listdir

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]        #去掉 .txt
        #文件名是按一定规则命名的,切分文件名并取第一部分为文件名字符串,这些事先知道
        classNumStr = int(fileStr.split('_')[0])   #取首字符为图像的实际数字
        hwLabels.append(classNumStr)               #类别标签依次存储在该向量中
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) 
        #将每个图像转化为向量后依次储存在特征矩阵trainingMat中
    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 (u"分类器返回: %d, 真实类别: %d" % (classifierResult, classNumStr))
        if (classifierResult != classNumStr): errorCount += 1.0
    print (u"分类错误总数: %d" % errorCount)
    print (u"总错误率: %f%%" % (errorCount/float(mTest)))
"""
if __name__ =='__main__':
    handwritingClassTest()
    #输出:
    #错误总数: 10
    #总的错误率: 0.010571%
#"""
    
"""
Scikit-learn 也简称sklearn,是机器学习领域当中最知名的python模块之一。
sklearn包含了很多机器学习的方式:
•Classification 分类
•Regression 回归
•Clustering 非监督分类
•Dimensionality reduction 数据降维
•Model Selection 模型选择
•Preprocessing 数据与处理

使用sklearn可以很方便地让我们实现一个机器学习算法。
一个复杂度算法的实现,使用sklearn可能只需要调用几行API即可。
所以学习sklearn,可以有效减少我们特定任务的实现周期。
"""    
    
from sklearn.neighbors import KNeighborsClassifier as kNN

def handwritingClassTest1():
    hwLabels = []                   #初始化一个空列表来储存手写数字数据集的类别标签
    trainingFileList = listdir('trainingDigits')     #获取目录内容
    #列出给定目录的文件名存入列表中,将这些文件名对应的文件作为训练集
    m = len(trainingFileList)       #文件名个数,也就是训练样本个数
    trainingMat = zeros((m,1024))   #初始化训练特征矩阵,每行数据存储一个图像
    for i in range(m):              #从文件名解析分类数字
        fileNameStr = trainingFileList[i]
        fileStr = fileNameStr.split('.')[0]        #去掉 .txt
        #文件名是按一定规则命名的,切分文件名并取第一部分为文件名字符串,这些事先知道
        classNumStr = int(fileStr.split('_')[0])   #取首字符为图像的实际数字
        hwLabels.append(classNumStr)               #类别标签依次存储在该向量中
        trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) 
        #将每个图像转化为向量后依次储存在特征矩阵trainingMat中
    
    neigh = kNN(n_neighbors = 3, algorithm = 'auto')  #构建kNN分类器
    neigh.fit(trainingMat, hwLabels)
    #拟合模型, trainingMat为测试矩阵,hwLabels为对应的标签
    testFileList = listdir('testDigits')           #返回testDigits目录下的文件列表
    errorCount = 0.0                               #错误检测计数
    mTest = len(testFileList)                      #测试数据的数量
    for i in range(mTest):
        fileNameStr = testFileList[i]              #获得文件的名字
        classNumber = int(fileNameStr.split('_')[0])          #获得分类的数字
        vectorUnderTest = img2vector('testDigits/%s' % (fileNameStr))
        #获得测试集的1x1024向量,用于训练
        classifierResult = neigh.predict(vectorUnderTest)     #获得预测结果
        #对比classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
        print("分类器返回:%d\t真实结果:%d" % (classifierResult, classNumber))
        if(classifierResult != classNumber):
            errorCount += 1.0
    print("分类错误总数:%d\t总错误率:%f%%" % (errorCount, errorCount/mTest * 100))
#"""
if __name__ =='__main__':
    handwritingClassTest1()
    #输出:
    #分类错误总数:12       总错误率:1.268499%
#"""

    
参考: https://blog.csdn.net/c406495762/article/details/75172850

你可能感兴趣的:(机器学习,机器学习)