KNN 常用来做机器学习中的分类任务 。
白话介绍
物以类聚,人以群分。 身边的朋友的收入加起来的平均 就是你的收入。KNN 的思想也就是这样。 k-NearestNeighbor 距离最近要预测的数据 最近的 K 个 数据 他们的标签 就是预测数据的标签。
图解:
比如: 上图 将历史数据 分成 w1 w2 w3 三个分类 来了新数据 Xu 怎么判断 属于哪个分类呢?
按照距离远近 求解出 5 个点 发现 4 个红色 1 个绿色 离他最近 ,所以我们就认为 Xu属于 红色分类。
手写板 实例介绍
手写一个数字猜测 人写的是哪个数字 0—9 。
大致步骤:
1, 准备数据为特定的格式
1,将预测的数据 和 全部的 历史数据样本 进行 求距离
2,按照距离算出 最近的K个距离点。
3,判断最近K个距离点的标签 最多的标记的 就是预测点的标签。
准备数据
存入数据
都知道图片是像素RGBA 组成的 。我们将手写的图片 转换成 灰度图 也就是只要一个通道 有手划过的地方 置为 1 没有的地方 都是 0 。 如下图: 展示了三种写法的 0 是怎么存入记事本的。
为了 方便计算 我们将每一张图片都转换成 32*32 这么一个大小的图片。写入到 记事本里面 。 0---- 9每一个数字 我们都写了 近 200 张 存入记事本 并且记事本的名字格式: 数字_顺序 .txt 那么我们就得到了 数字的符号 和 数字的 标签 (0—9)
这个过程 不需要我们准备 有很多这样的数据集 网上都可以下载到。
取出数据
也是为了 方便计算 我们都采用的是矩阵计算 。而且 numpy 这个专门计算矩阵的库效率非常高 。所以我们将数据 都转成 矩阵存起来。
3232= 1024 我们一共找到 1935个0–9历史的数据
所以 表示方式 这样 [ 1935 ,1024] 这么大一个矩阵 也就是这样将上图的图片3232 转换成一行 [ 01110------ 00001] 后面大部分的图片数据 都这么处理的。
还有一个 表示标签的矩阵 [ 0…0,1…1,2…2,3…3,…9…9] 1935 这么一个 1*1935的矩阵 记录标签 跟前面的[1935,1024] 一一 对应起来。
计算距离
计算距离的方式 有很多种:
看到这些公式不要害怕 ,只是告诉大家有这么多种方式 。 我们采用最简单的 欧氏距离。
我们有了 A= 【1935,1024】 这么大一个矩阵 都在内存中 还有一个标签矩阵 C1= 1935 这么 1*1935的 ,
来了一个新数据 [00111100…1111] 大小 1*1024 变成
复制 这一行的数据1934次 变成 B=【 1935 ,1024】 这么大一个矩阵 。
根据 欧式距离:
然后 D= (A - B )^2 E = 开根号 (D对 所有行 每一行分别 求 和) 算出
根据这个距离排序 挑选出 前 K 行 索引 。
然后根据 索引 和 C1 矩阵 判定 属于哪个分类 ,也就是少数服从多数原则。
代码实现
#coding=utf-8
import numpy as np
from numpy import *
import os ,sys
"""
将一个图片从32*32 变成 1*1024
"""
def TextToList(fileName):
tmpFile = open(fileName)
line =tmpFile.readline()
tmpCount=0
dataSet = []
while line:
tmpCount= tmpCount+1
for i in range(32):
dataSet.append(int(line[i]))
line = tmpFile.readline()
pass
tmpFile.close()
#print("tmpCount==",tmpCount)
#print(dataSet)
return dataSet
"""
读取所有的图片文件 变成 1935*1024 矩阵
"""
def ReadFiles(myPath):
fileList= os.listdir(myPath)
#print("fileList==",fileList)
m = len (fileList)
tranDataSet = np.zeros((m,1024))
trainLabels = np.zeros((1,m))
for i in range(m) :
tmpName = fileList[i][0]
trainLabels[0,i]= tmpName
#print(trainLabels[1,i])
oneFile = TextToList(myPath +"/"+fileList[i])
tranDataSet[i,] = oneFile
#print(tranDataSet[i,],len(tranDataSet[i,]))
#print(oneFile,len(oneFile))
return tranDataSet,trainLabels
"""
读取历史图片 变成 1935*1024 矩阵
"""
def CreateData():
myPath= "./trainingDigits"
return ReadFiles(myPath)
"""
读取 将要预测的图片
"""
def ReadTest():
myPath= "./testDigits"
return ReadFiles(myPath)
"""
将预测矩阵 变成 1935*1024 和历史数据 矩阵1934*1024
做欧式距离判断 并根据前K 个值判断 所属分类
"""
def Classify(inX,dataSet,labels,k):
tmpShape= dataSet.shape
print("shape ==",dataSet.shape,inX.shape)
differMat = np.tile(inX,(tmpShape[0],1))
print("differMat==",differMat.shape)
differMat = mat(differMat) - dataSet
#print("differMat22==",differMat)
differMat = square(differMat)
#multiply(differMat,differMat)
#print("differMat **2==",differMat)
sqDistance = differMat.sum(axis=1)
#print("sqDistance ==",sqDistance)
sqDistance = sqrt(sqDistance)
#print("sqDistance =22=",sqDistance)
sortDistance = np.array(sqDistance.argsort(axis=0))
print("sortDistance ==",sortDistance)
#print(sortDistance[2][0])
label= np.array(labels.T)
classCount= {}
for i in range(k):
tmpIndex = sortDistance[i][0]
voteLable = label[tmpIndex][0]
#print(tmpIndex, label[tmpIndex],voteLable)
if(not classCount.get(voteLable)):
classCount[voteLable] =0
classCount[voteLable] +=1
#print ("classCount ==",classCount)
sortedClass = sorted(classCount.items())
print ("sorted ==",sortedClass[0][0])
return sortedClass[0][0]
"""
所有的流程 和 计算错误率
"""
def Test():
trainData,trainLabels = CreateData()
testData,testLables= ReadTest()
realRate= {}
errorRate = {}
for x in range(len(testData)):
testOne =Classify(testData[x,],trainData,trainLabels,1)
if not realRate.get(testLables[0,x]):
realRate[testLables[0,x]] = 0
errorRate[testLables[0,x]] =0
if( testOne != testLables[0,x]):
errorRate[testLables[0,x]] +=1.0
realRate[testLables[0,x]] +=1.0
for key in realRate.keys():
tmpFloat = errorRate[key] / realRate[key]
print("number==",key,"errorRate ==",tmpFloat)
Test()
工程也可以从 git上获取:下载
还有不明白的 可以交流:
群群nub:八七四一三九四三六