【实现】利用KNN约会分类

代码来自于《机器学习实战》

问题背景

小红经常在约会网站寻找适合自己的约会对象。经过自己的总结,发现曾经交往过三种类型的人:

不喜欢的人

魅力一般的人

极具魅力的人

发现了上述规律,小红还是无法将约会网站推荐的匹配对象归到恰当的类别。她希望在周一到周五约会那些魅力一般的人,周末想那些极具魅力的人为伴(世界一直围绕着小红在转啊转......)。我们怎么帮助她?

1. 数据处理

数据保存在datingTestSet2.txt中,每个样本占据一行,共有1000行(哇,人才)。样本主要包含以下3个特征:

每月工资

玩游戏所消耗时间百分比

每周消费的冰淇淋公升数

些许样例示意图

A 处理数据:将文本中的数据变成分类器能接受的格式。

导入需要的工具包

from numpy import *

定义函数file2Matrix:将文本数据转换成矩阵形式

def file2Matrix(filename):

    fr= open(filename)

    arrayOLines= fr.readlines()#直接将数据全部读取进来,形成列表,每一行为一个元素

    numberOfLines= len(arrayOLines)#行数

    returnMatrix= zeros((numberOfLines,3))

    classLabelVector= []

    index= 0

    for linein arrayOLines:

        line= line.strip()

        listFromLine= line.split('\t')

        returnMatrix[index,:] = listFromLine[0:3]#赋值方式很厉害!

        classLabelVector.append(listFromLine[-1])

        index+= 1

    return returnMatrix,classLabelVector

B 数据归一化处理

数据中特征“每月工资”,取值范围太大,计算距离时,产生的影响过大。但我们认为3个特征的重要性是一样的。所以,有必要进行数据归一化处理。将数据归一化到 0~1 之间。

公式

newValue = (oldValue-min)/(max-min)

定义归一化函数autoNorm;返回归一化的数据,每个特征的最小值以及范围(方便对新数据进行归一化处理)。

def autoNorm(dataSet):

    minVals= dataSet.min(0)#a.min() :全部的最小值;;a.min(axis=0):每列中的最小值;a.min(axis=1):每行中的最小值

    maxVals= dataSet.max(0)

    ranges= maxVals- minVals

    m= dataSet.shape[0]#行数

    normDataSet= dataSet- tile(minVals,(m,1))#(m,n)重复次数:行方向上重复m次,列方向上重复1次

    normDataSet= normDataSet/tile(ranges,(m,1))

    return normDataSet,ranges, minVals

其中,调用的函数

dataSet.min(0)

dataSet:python numpy array类型

array.min():求数组中的最小值

array.min(axis=0):对数组中的每列求最小值

array.min(axis=1):对数组中的每行求最小值

numpy.tile(A, reps):

Construct an array by repeating A the number of times given by reps.通过重复A ,reps次构建一个新的数组。

tile(minVals, (m, 1)):重复minVals 构造一个m*1的copy

2. 分类算法KNN

伪代码:

对未知类别属性的数据集中的每个点依次执行以下操作:

(1)计算已知类别数据集中的点与当前点之间的距离;

(2)按照距离递增次序排序;

(3)选取与当前点距离最近的k个点;

(4)确定前k个点所在类别的出现频率;

(5)返回前k个点出现频率最高的类别作为当前点的预测分类。

其实是计算两个数据的相似性:有距离相似性度量和角度相似性度量【knn算法介绍】。

这里采用距离相似性度量,欧式距离。

欧式距离计算公式

分类器算法实现:

def classify0(inX, dataSet, labels, k):

    dataSetSize= dataSet.shape[0]

    diffMat= tile(inX,(dataSetSize,1)) - dataSet

    sqDiffMat= diffMat** 2

    sqDiffMat= sqDiffMat.sum(axis=1)#按列求和

    distances= sqDiffMat** 0.5

    sortedDistIndicies= distances.argsort()#argsort函数返回的是数组值从小到大的索引值

    classCount= {}

    for iin range(k):

        voteLabel= labels[sortedDistIndicies[i]]

        classCount[voteLabel] = classCount.get(voteLabel,0) + 1#在字典classCount中通过 键 查找相应的值,如果字典中没有键,值为0;有获取其值;最后加1

    sortedClassCount= sorted(classCount.items(),key=lambda a:a[1],reverse=True)#根据出现次数排序,降序排序

    return sortedClassCount[0][0]#返回概率最大(出现次数最多的键,即类别)

其中,调用函数:

numpy.argsort:

Returns the indices that would sort an array. 返回数组排序后的下标(默认升序排序)。

eg:[10, 1, 51]

调用函数,返回的是:[1, 0, 2]  ;1是数据1的下标。

3. 测试算法

数据指标:误分率。

计算方法: 错误分类的记录数/测试数据的总数;

def datingClassTest():

    """

    使用测试数据测试分类器效果

    :return: 误分率"""

    hoRatio= 0.10#测试数据所占的比例---将数据集按照1:9的比例划分,1是测试集;9是训练集

    datingDataMat,datingLabels= file2Matrix('datingTestSet2.txt')

    normMat,ranges,minVals= autoNorm(datingDataMat)

    m= normMat.shape[0]#数据集总数

    numTestVecs= int(m*hoRatio)#测试集数目

    errorCount= 0

    for i  in range(numTestVecs):

        classifierResult= classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)

        print ("the classifier came back with: %s,the real answer is: %s"  % (classifierResult, datingLabels[i]))

        if (classifierResult!= datingLabels[i]):

            errorCount+= 1

    print ("the total error rate is: %f" %(errorCount/float(numTestVecs)))

运行结果:

测试算法运行结果

错误率:5%

4. 使用算法

重头戏:使用knn分类算法,方便小红约会哦!

def classifyPerson1():

    resultList= ["不喜欢","魅力一般","极具魅力(韩国欧巴)"]

    percentTats= float(raw_input(\

"花费在游戏、视频上的时间百分比?"))#raw_input:读取控制台输入数据,赋值给变量,等效于C++里的cin>>a;

    ffMiles= float(raw_input("每月工资有多少?"))

    iceCream= float(raw_input("每年消耗的冰淇淋有多少升?"))

    datingDataMat,datingLabels= file2Matrix("datingTestSet2.txt")

    normMat,ranges,minVals= autoNorm(datingDataMat)

    inArr= array([ffMiles,percentTats,iceCream])

    classifierResult= classify0((inArr-minVals)/ranges,normMat,datingLabels,k=3)#对输入数据进行归一化,然后再分类

    print ("你将约会的人很可能是: %s" % resultList[int(classifierResult)-1])

应用:

小红将约会韩国欧巴(安排在周末)


小红遇到不喜欢的类型(pass)

5. 小结

A。 矩阵运算----能加快运算速度;

B。knn算法分类数据简单有效,不用进行训练,是基于实例的学习,使用的数据必须接近实际数据的训练样本数据;必须保存所有数据,如果数据集过大,需要耗费巨大的存储空间;必须对数据中的所有样例点进行计算距离值,非常耗时(即使使用矩阵进行运算)。

C。人生苦短,我用Python!

完整代码及数据集:github

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