KNN算法

KNN算法简单的来说就是计算两个数据之间的差异,就如同计算机视觉中,使用knn算法就是计算两张图片的像素差,在今天学习机器学习的过程中也是如此,用的是欧式距离公式。

首先是编写基本的函数。

from numpy import *
import operator

def createDataSet():
      group = array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]]) #可以看作4行2列的矩阵
      labels = ['A', 'A', 'B', 'B'] #每一行数据的标签
      return group, labels

kNN算法的伪代码:
1.计算拥有标签数据集的点与当前点的距离
2.按照距离递增的顺序排序
3.选取距离最小的前k个点
4.确定前k个点所在类别(也就是label)出现的频率
5.返回前k个点出现频率最高的类别作为当前点的预测分类

# inX是输入向量,dataSet是训练样本集,labels是标签向量,k是选择的邻居数
def classify0(inX, dataSet, labels, k):
      dataSetSize = dataSet.shape[0] #shape[0]的意思是训练集的行数,也就是有多少个向量
      diffMat = tile(inX, (dataSetSize, 1)) - dataSet #tile()是把inX向量向列复制一次,行复制dataSetSize次,就是做个和训练集样本一样的矩阵,得出差异矩阵
      sqDiffMat = diffMat ** 2 # 计算差值平方
      sqDistances = sqDiffMat.sum(axis=1)  #这里要记住的是axis=1表示计算行和
      Distances = sqDistances ** 0.5 #到此为止,输入向量和样本集之间的距离已经算完
      sortedDistIndices = Distances.argsort() #按距离递增排序,记录的是他们的位置号
      classCount = {}  #申请一个字典,好存放前k个标签和他们出现的频率关系
      for i in range(k):
            voteIlabel = labels[sortedDistIndices[i]] # 提取前k个的标签
            #理解sorted函数,第一个参数是可迭代对象,比较方法由key决定,operator.itemgetter(1)获取了迭代对象的下标为1的值,也就是字典里面的频率,reverse=True表示从大到小排
            #operator.itemgetter(1)是定义了一个函数,每次取迭代对象标为1的值
            sortedClassCount[voteIlabel] = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
      return sortedClassCount[0][0]  #返回出现频率最高的标签

运行代码

groups, labels = createDataSet()
label = classify0([0, 0], groups, labels, 3)
print(label)
# 结果为B

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

def file2matrix(filename):
    fr = open(filename) #打开文件,获得句柄
    arrayOfLines = fr.readlines() # 调用方法,将文本中的每一行放入一个列表中
    numberOfLines = len(arrayOfLines) # 计算文本中有多少行
    returnMat = zeros((numberOfLines, 3)) #创建一个返回矩阵,元素为0
    classLabelVector = []  # 创建一个标签列表
    index = 0
    for line in arrayOfLines:
        line = line.strip()  # 将每一行的空格去头去尾
        listFromLine = line.split('\t')  # 将每一行的数据划分开存入列表
        returnMat[index, :] = listFromLine[:3]  # 将每一行前三个数据存入返回矩阵
        labels = {'didntLike': 1, 'smallDoses': 2, 'largeDoses': 3} # 书上代码报错,故加入一个列表来使代码运行正确
        classLabelVector.append(labels[listFromLine[-1]])  # 将每一行数据的标签存入
        index += 1
    return returnMat, classLabelVector  

归一化数值
为啥要归一化数值?因为按照欧式公式来计算的话,不同属性上的差异很大,差异大的属性上计算的值几乎决定了结果。而假设不同属性的权重应该是同等重要的,所以应该要解决这个问题。(这里说属性是习惯了数据库的说法,反正意思差不多,是这个东西就行了)

#公式
newValue = (oldValue - min) / (max - min)
def autoNorm(dataSet):
    minVals = dataSet.min(0) #min(0)是按列返回最小值,也就是返回一个属性上的最小值
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals #每个属性上的最大值和最小值差
    normDataSet = zeros(shape(dataSet)) #创建一个和dataSet型号一样的矩阵,元素全为0
    m = dataSet.shape[0] # 返回dataSet有多少个数据
    normDataSet = dataSet - tile(minVals, (m, 1))  # 计算每个数据和最小值的差
    normDataSet = normDataSet / tile(ranges, (m, 1)) # 除(max - min)
    return normDataSet, ranges, minVals

针对约会网站的测试代码,计算错误率,因为海伦给的数据本身就是随机值,所以取前10%的数据为测试数据,后90%的数据为dataSet

def datingClassTest():
    hoRatio = 0.1 # 设置要测试的数据集
    datingDataMat, datingLabels = file2matrix('datingTestSet.txt')  # 文本中分析数据
    normMat, ranges, minVals = autoNorm(datingDataMat) # 数值归一化
    m = normMat.shape[0] # 返回数据的个数
    numTestVecs = int(m * hoRatio) # 要测试数据的个数
    errorCount = 0.0 # 初始化错误个数
    for i in range(numTestVecs): 
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)  # 调用classify0函数,返回分类结果,参数上面写了
        print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]))
        if (classifierResult != datingLabels[i]):  # 如果分类结果和标签不符,errorCount加1
            errorCount += 1.0
    print("the total error rate is: %f" % (errorCount / float(numTestVecs)))

约会网站预测函数

def classifyPerson():
    resultList = ['not at all', 'in small doese', '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('datingTestSet.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = array([ffMiles, percentTats, iceCream]) # 将输入的数据合并成一个向量
    # 调用classify0函数生成预测结果,这里我一开始没有想到的点是输入数据也要归一化
    classifyResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 3)
    print("You will probably like this person: ", resultList[classifyResult - 1])

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

# 将一幅32x32的图像转换成一个一维的向量
def img2vector(filename):
    returnVect = zeros((1, 1024)) # 申请一个一维向量
    fr = open(filename) 
    for i in range(32): # 遍历行
        lineStr = fr.readline() # 逐行读取,readline()的效果
        for j in range(32): # 遍历每一行,将字符串的数字转换为真正的数字
            returnVect[0, 32*i+j] = int(lineStr[j])
    return returnVect

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

def handwritingClassTest():
    hwLabels = []  # 记录标签列表
    trainingFileList = listdir('trainingDigits') # listdir()方法返回文件夹中文件或文件夹的名字,但不包括.和..
    m = len(trainingFileList) # 记录该文件夹的文件数
    trainingMat = zeros((m, 1024))
    for i in range(m):
        fileNameStr = trainingFileList[i] # 获取文件名的全称,包括.txt
        fileStr = fileNameStr.split('.')[0]  # 获取文件名,不包括.txt
        classNumStr = int(fileStr.split('_')[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]  # 获取文件名的全称,包括.txt
        fileStr = fileNameStr.split('.')[0]  # 获取文件名,不包括.txt
        classNumStr = int(fileStr.split('_')[0])  # 获取该图片的标签
        vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)
        # 调用函数classify0来获取结果
        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
    print("\nthe total number of errors is: %d" % errorCount)
    print("\nthe total error rate is: %f" % (errorCount / float(mTest)))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值