01-kNN算法实战-(机器学习实战)

最近在看机器学习实战这本书。刚开始看kNN算法,并写了些程序,分享下一些感悟和细节。

什么是kNN

kNN中文又称为k-近邻算法,其基本思想是通过计算输入样本点 和 训练集样本点之前的这两个向量之前的距离,距离越近的话,说明其特征越靠近,通过取出其k个距离最近的样本点,然后计算这k个样本点中类别占比最大的类比以此来预测输入样本点(被测样本点)的类别。

kNN的优势

  • kNN是ML里最简单,最基本的算法。
  • kNN不会受到差别特别大的样本中的特征元素的影响(对异常值不敏感)。因为采用了归一化技术
  • kNN的精度高

kNN的劣势

  • kNN算法时间复杂度较高,需要计算被测样本点和训练集中所有样本点的距离

kNN算法的实现

from numpy import *
import operator
# 该分类器模型,只需要输入向量, 训练数据集矩阵dataSet,每一行是一个样本。labels(每一行的样本标签)。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={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]
  • 这里的dataSet是numpy模块里的数组(也可以看成矩阵),不是python中内置的数组,shape属性会返回这个数组的纬度.(比如是2 * 3的二维数组,则会返回(2, 3)表示这是一个2纬数组,每个纬度的大小分别是2,3)
  • tile函数用来创建矩阵,其中(dataSetSize, 1)表示沿着inX向量的行方向(inX是一个行向量),赋值dataSetSize次,沿着列方向复制1次(既列不变)。
  • diffMat矩阵计算出了各个向量之前的距离的平方值。其中2表示平方,0.5表示开平方

使用kNN来改进婚恋网站的匹配

这里的数据集如下

image.png

datingTestSet.txt存放了该网站关于个人信息的集合。每一行代表一个人,一共有三个特征属性,和一个标签属性用来标识是哪一类人。
datingTestSet2.txt 存放的是处理过的datingTestSet数据,将类别标签处理成数字

  • 三个特征属性分别是: 每年航班的行程公里数,玩游戏的时间所占的时间百分比,每周消费的冰淇淋公升数
  • 人一共分为三类(不喜欢的、魅力一般、极具魅力)

两个文件的内容如下


image.png

目的

现在的需求是,我们必须根据提供的数据,来准确的划分出这三类人。才能精确的从数据中挑选出的人是用户感兴趣的。

分析

  • 根据之前总结的kNN分类器模型,我们需要将数据进行处理。分别分离出训练数据集、测试数据集、数据集对应的标签。其实最重要的是准本和分析数据集,然后进行建模,但是由于这里数据集已经是现有的,直接用就行。
  • 接着我们编写测试程序,将测试数据集、训练数据集、标签丢入改模型进行计算
  • 统计识别的错误率,如果错误率很低。那基本上可以使用。如果错误率高,那就要改进数据集,进行其他特征点的抽取。

步骤

处理数据(提取数据,归一化)

改函数用来读取训练集数据将其转化为矩阵,并提取出标签集合。具体代码看下面

from numpy import *
def file2matrix(filename):
    file = open(filename)
    arrayOfLines = file.readlines()
    numberOfLines = len(arrayOfLines)
    returnMat = zeros((numberOfLines,3))
    classLabelVector = []
    index = 0
    for line in arrayOfLines:
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))  # 这边要十分小心,必须强制转换为整形,不然编译器会当做字符串处理
        index += 1
    return returnMat, classLabelVector

将特征数据都归一化
因为航程特征的数字太大,对其他两个影响太大,但是又不能忽略,为了减少这种影响。将数据进行归一行,既处理层0到1之前的小数。利用了如下原理
newValue = oldValue - minValue/(maxValue - minValue)

# 数据归一化 newValue = oldValue - minValue/(maxValue - minValue)
def autoNorm(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normaMat = zeros(shape(dataSet))
    m = dataSet.shape[0]
    normaMat = dataSet - tile(minVals, (m, 1))
    normaMat = normaMat / tile(ranges, (m, 1))
    return normaMat, ranges, minVals
通过图形分析数据
  • (x轴为游戏时间占比,y轴为每周吃冰淇淋的公斤数)
# 列2和列1的比较
datingDataMat,datingDataLabels = file2matrix('/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/datingTestSet2.txt')
print datingDataLabels
import matplotlib
import matplotlib.pyplot as plt
from numpy import *
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0 * array(datingDataLabels), 15.0 * array(datingDataLabels))
plt.show()
image.png
  • (x轴为航班占比,y轴为游戏时间耗时占比)
# 列2和列1的比较
datingDataMat,datingDataLabels = file2matrix('/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/datingTestSet2.txt')
print datingDataLabels
import matplotlib
import matplotlib.pyplot as plt
from numpy import *
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0 * array(datingDataLabels), 15.0 * array(datingDataLabels))
plt.show()
image.png

通过上面的分析,可以很明显的发现,使用前两个特征,我们就可以将这三类人比较精确的分离出来。但如果只使用第二个和第三个特征难以分离出该三类。

编写测试用例,测试kNN分类器的效果
# 针对约会网站的测试代码,测试分类器的效果
def datingClassTest():
    hoRatio = 0.10
    datingDataMat, datingDataLabels = file2matrix('/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]   # 获取训练集行数
    numTestVecs = int(m * hoRatio)  # 取10%的行数作为测试集  
    errorCount = 0.0
    for i in range(numTestVecs):  
        classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,], datingDataLabels[numTestVecs:m], 3)  # 取从小到的前三个
        print "分类的结果是: %d, 目标结果是: %d" % (int(classifierResult), int(datingDataLabels[i]))
        if (classifierResult != datingDataLabels[i]): errorCount += 1.0
    print "总的错误率为: %f%%" %(errorCount / float(numTestVecs) * 100.0)

运行datingClassTest()方法我们可以看到如下结果(其中1,2,3分别代表三类人的标签映射。),改分类器的错误率为5%,也就是说95%的情况下匹配都是准确的,算是还不错的分类器。

image.png

使用KNN实现识别手写数字

具体思路和上面的例子一样。这边有个比较不一样的步骤是我们需要对图片进行处理,这里我们统一对图片做了以下处理。

  • 将图片的大小处理层一样的黑白图。
  • 对于每张图片,我们使用 正确的对应数字_样本索引.txt来命名。(之所以处理成文本是为了在这里比较直观)


    image.png

    image.png

分析

1.为了使用kNN模型,我们需要将图片转化为一个行向量。由于图片大小事32*32,我们需要一个1024大小的行向量即可存储。

from numpy import *
def img2vector(filename):
    fr = open(filename)
    returnVect = zeros((1, 1024))
    for i in range(32):
        lineStr = fr.readline()
        for j in range(32):
            returnVec[0, 32 * i + j] = int(lineStr[j])
    return returnVect

2.使用kNN模型进行测试

from os import listdir
from numpy import *
def handwritingClassTest():
# 遍历训练数据集,将数据集装载进矩阵。
    trainingFilesPath = "/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/digits/trainingDigits/"
    arrayOfTrainingFiles = listdir(trainingFilesPath)
    m = len(arrayOfTrainingFiles)
    trainingMat = zeros((m, 1024))
    classLabels = []
    for i in range(m):
        fileNameStr = arrayOfTrainingFiles[i]
        fileStr = fileNameStr.split('.')[0]
        classNum = int(fileStr.split('_')[0])
        classLabels.append(classNum)
        trainingMat[i,:] = img2vector(trainingFilesPath + fileNameStr)
    
    testFilePath = "/Users/sixleaves/Dropbox/DeepLearning/machinelearninginaction/Ch02/digits/testDigits/"
    arrayOfTestFiles =  listdir(testFilePath)
    mTest = len(arrayOfTestFiles)
    errorCount = 0.0;
    for j in range(mTest):
        testFileNameStr = arrayOfTestFiles[j]
        testFile = testFileNameStr.split('.')[0]
        testNum = testFile.split('_')[0]
        testImageVec = img2vector(testFilePath + testFileNameStr)
        classifierResult = classify0(testImageVec, trainingMat, classLabels, 3)
        print "识别结果为: %d, 正确结果为: %d" % (int(classifierResult), int(testNum))
        if classifierResult != int(testNum): errorCount += 1.0
    print "识别错误个数为: %d" % (int(errorCount))
    print "识别错误率为: %f%%" % (errorCount / float(mTest) * 100.0)    
image.png

效果还是相当不错,基本达到了99%识别效率

总结:

  • 一般机器学习解决问题需要以下步骤(准备数据分析数据训练算法(kNN不适用,kNN无需训练)测试算法,使用算法)。
  • 对于kNN算法模型来说,分析数据过程由于重要,只有有价值的数据使用kNN才能有精确的结果。
  • kNN算法比较简单,稳定。但是效率低。其思想主要是计算相似度(通过计算向量距离),并使用概率来得出分类结果。

by sixleaves 20170726 FuZhou

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容