2-6节 k-近邻算法|优化约会网站的配对效果项目汇总|机器学习实战-学习笔记

文章原创,最近更新:2018-08-10

本章节的主要内容是:
重点介绍项目案例1: 优化约会网站的配对效果中的完整代码

1.KNN项目案例介绍:

项目案例1:

优化约会网站的配对效果

项目概述:

1)海伦使用约会网站寻找约会对象。经过一段时间之后,她发现曾交往过三种类型的人: 不喜欢的人、魅力一般的人、 极具魅力的人。
2)她希望: 1. 工作日与魅力一般的人约会 2. 周末与极具魅力的人约会 3. 不喜欢的人则直接排除掉。现在她收集到了一些约会网站未曾记录的数据信息,这更有助于匹配对象的归类。

开发流程:
  • 收集数据:提供文本文件
  • 准备数据:使用 Python 解析文本文件
  • 分析数据:使用 Matplotlib 画二维散点图
  • 训练算法:此步骤不适用于 k-近邻算法
  • 测试算法:使用海伦提供的部分数据作为测试样本。
    测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
  • 使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。
数据集介绍

海伦把这些约会对象的数据存放在文本文件 datingTestSet2.txt (数据来源于《机器学习实战》第二章 k邻近算法)中,总共有 1000 行。

本文使用的数据主要包含以下三种特征:每年获得的飞行常客里程数,玩视频游戏所耗时间百分比,每周消费的冰淇淋公升数。其中分类结果作为文件的第四列,并且只有3、2、1三种分类值。datingTestSet2.csv文件格式如下所示:

飞行里程数 游戏耗时百分比 冰淇淋公升数 分类结果
40920 8.326976 0.953952 3
14488 7.153469 1.673904 2
26052 1.441871 0.805124 1

数据在datingTestSet2.txt文件中的格式如下所示:


2.优化约会网站的配对效果项目

第二章是描述K近邻算法的,算法本质就是寻找距离最近的点,这个距离可以是欧式距离,也可以是其他,这本书采用的就是欧式距离了。K近邻算法主要是用来分类的,比如我新输入一个数据,要判断他属于哪个类别,用这个算法就很合适了,简单实用。

2.1准备工作

这里第一步,其实就是为了检验我们后续编写的的K近邻算法有没有效果。

首先我们创建一个名为kNN.py的文件,然后我们就创建一个函数,这个函数返回一个矩阵和标签列表,以方便我们后续对K近邻算法进行检验。

具体内容,可参见:2-1节 k-近邻算法-KNN算法|优化约会网站的配对效果项目|机器学习实战-学习笔记

import numpy as np
import operator

def createDataSet():
    """
    创建数据集和标签

     调用方式
     import kNN
     group, labels = kNN.createDataSet()
    """
    group = np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
    labels=["A","A","B","B"]
    return group,labels

2.2kNN算法

目的就是为了找最近的距离,采用的是欧氏距离.
具体内容,可参见:2-1节 k-近邻算法-KNN算法|优化约会网站的配对效果项目|机器学习实战-学习笔记

def classify0(inX,dataSet,labels,k):
    """
    inX:用于分类的输入向量
    dataSet:输入的训练样本集
    lables:标签向量
    k:表示用于选择最近邻居的数目
    
    预测数据所在分类可在输入下列命令
    kNN.classify0([0,0], group, labels, 3)
    """
    # array的shape函数返回指定维度的大小,如dataset为n*m的矩阵,
    # 则dataset.shape[0]返回n,dataset.shape[1]返回m,dataset.shape返回n,m
    dataSetSize = dataSet.shape[0]
    # tile函数简单的理解,它的功能是重复某个数组。比如tile(A,n),功能是将数组A重复n次,构成一个新的数组
    # 所以此处tile(inX,(dataSetSize,1))的作用是将inX重复复制dataSetSize次,以便与训练样本集的样本个数一致
    # 减去dataSet就是求出其差值,所以diffMat为一个差值矩阵
    diffmat=np.tile(inX,(dataSetSize,1))-dataSet
    #距离度量,度量公式为欧氏距离
    sqdiffmat=diffmat**2
    # 将矩阵的每一行相加,axis用于控制是行相加还是列相加
    sqdistances=sqdiffmat.sum(axis=1)
    #开方
    distances=sqdistances**0.5
    # 根据距离排序从小到大的排序,返回对应的索引位置
    sortedDistIndicies=distances.argsort()
    # 选择距离最小的k个点
    classcount={}
   
    for i in range(k):
        # 找到该样本标签的类型
        voteIlabel=labels[sortedDistIndicies[i]]
        # 字典的get方法,list.get(k,d) 其中 get相当于一条if...else...语句,参数k在字典中,字典将返回list[k];如果参数k不在字典中则返回参数d
        classcount[voteIlabel]=classcount.get(voteIlabel,0)+1
        # 字典的 items() 方法,以列表返回可遍历的(键,值)元组数组。
        # sorted 中的第2个参数 key=operator.itemgetter(1) 这个参数的意思是先比较第几个元素
        sortedClasscount = sorted(classcount.items(),key=operator.itemgetter(1),reverse=True)
        # 返回最符合的标签
        return sortedClasscount[0][0]

2.3准备数据。我们将数据转化为numpy解析程序

具体内容,可参见:2-2节 k-近邻算法-使用 Python 解析文本文件|优化约会网站的配对效果项目|机器学习实战-学习笔记

def file2matrix(filename):
    """
    导入训练数据
    filename: 数据文件路径
    return: 数据矩阵returnMat和对应的类别classLabelVector
    """
    # 打开文件
    fr=open(filename)
    # 读取每一行的信息
    array0Lines=fr.readlines()
    # 获得文件中的数据行的行数
    numberofLines=len(array0Lines)
    # 生成numberofLines行3列的零矩阵,这3列都是用来存放特征信息
    returnMat=np.zeros((numberofLines,3))
    # 存放类别是列表
    classLabelVector=[]
    index=0
    for line in array0Lines:
        # 去掉回车制表符,生成新的字符串
        line=line.strip()
        # 通过split以\t切割字符串,返回元素列表
        listFromLine=line.split('\t')
        # 选取前三个数据的特征值放进矩阵
        returnMat[index,:]=listFromLine[0:3]
        # 将listFromLine列表中的最后一列元素存储到classLabelVector
        # 这列的数据是类别数据
        classLabelVector.append(int(listFromLine[-1]))
        # 更新index
        index +=1
    #返回特征矩阵returnMat和类别矩阵classLabelVector
    return returnMat,classLabelVector

2.4使用Matplotlib创建散点图

具体内容,可参见:2-3节 k-近邻算法-使用 Matplotlib 画二维散点图|优化约会网站的配对效果项目|机器学习实战-学习笔记

简单版本

import numpy as np
import matplotlib
import matplotlib.pyplot as plt

fig=plt.figure()
ax=fig.add_subplot(1,1,1)
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*np.array(datingLabels),15.0*np.array(datingLabels))
plt.show()

详细版本
为了得到更好的效果,采用datingDataMat矩阵的属性列1和2展示数据,并以红色的'*'表示类标签1、蓝色的'o'表示表示类标签2、绿色的'+'表示类标签3

import kNN
import operator
import matplotlib
import matplotlib.pyplot  as plt
import numpy as np
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
group,labels=kNN.createDataSet()
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*np.array(datingLabels),15.0*np.array(datingLabels))
datingLabels = np.array(datingLabels)
idx_1 = np.where(datingLabels==1)
p1 = ax.scatter(datingDataMat[idx_1,0],datingDataMat[idx_1,1],marker = '*',color = 'r',label='1',s=10)
idx_2 = np.where(datingLabels==2)
p2 = ax.scatter(datingDataMat[idx_2,0],datingDataMat[idx_2,1],marker = 'o',color ='g',label='2',s=20)
idx_3 = np.where(datingLabels==3)
p3 = ax.scatter(datingDataMat[idx_3,0],datingDataMat[idx_3,1],marker = '+',color ='b',label='3',s=30)
plt.xlabel("每年获取的飞行里程数",fontproperties=font)
plt.ylabel("玩视频游戏所消耗的事件百分比",fontproperties=font)
ax.legend((p1,p2,p3),("不喜欢","魅力一般","极具魅力"),loc=2,prop=font)
plt.show()

2.5归一化处理

归一化是为了让数据都处于[0,1]之间
具体内容,可参见:2-4节 k-近邻算法-归一化处理|优化约会网站的配对效果项目|机器学习实战-学习笔记

def autoNorm(dataSet):
    """
    归一化特征值,消除属性之间量级不同导致的影响
    dataSet: 数据集
    return: 归一化后的数据集normDataSet,ranges和minVals即归一化的数据集,最小值与极差
    归一化公式:
        Y = (X-Xmin)/(Xmax-Xmin)
        其中的 min 和 max 分别是数据集中的最小特征值和最大特征值。该函数可以自动将数字特征值转化为0到1的区间。
    """
    # 找出样本集中每列属性的最小值
    minVals= dataSet.min(0)
    # 找出样本集中每列属性的最大值
    maxVals= dataSet.max(0)
    # 每列最大最小值之间的极差
    ranges=maxVals-minVals
    # 创建与样本集一样大小的零矩阵
    normDataSet=np.zeros(np.shape(dataSet))
    # 获得样本集的行的维度
    m=dataSet.shape[0]
    # 样本集中的元素与最小值组成的矩阵
    normDataSet=dataSet-np.tile(minVals,(m,1))
    # 将最小值之差除以范围组成矩阵
    normDataSet=normDataSet/np.tile(ranges,(m,1))
    return normDataSet, ranges, minVals

2.6测试算法

具体内容,可参见:2-5节 k-近邻算法-测试算法&使用算法|优化约会网站的配对效果项目|机器学习实战-学习笔记

def datingClassTest():
    """
    对约会网站的测试方法
    return:错误数
    """
    # 设置测试数据的的一个比例(训练数据集比例=1-hoRatio)
    hoRatio= 0.1 # 测试范围,一部分测试一部分作为样本
    # 从文件中加载数据,获得特征和标签分开保存
    datingDataMat,datingLabels =file2matrix('datingTestSet2.txt')
    # 特征归一化,返回归一化特征,归一化范围集每个特征最小值
    normMat,ranges,minVals=autoNorm(datingDataMat)
    # m表示数据的行数,即矩阵的第一维
    m =normMat.shape[0]
    # 设置测试的样本数量,numTestVecs:m表示训练样本的数量
    numTestVecs=int(m*hoRatio)
    print("numTestVecs=",numTestVecs)
    errorCount= 0.0 
    for i in range(numTestVecs):#遍历测试集数据确定错误分类百分比
        # 对数据进行测试
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs: m], datingLabels[numTestVecs:m], 3)
        # 显示分类结果和实际标签
        print("the classifier came back with: %d, the real answer is: %d" % (classifierResult, datingLabels[i]))
        # 对比分类结果统计错误分类数量
        if (classifierResult != datingLabels[i]): errorCount += 1.0
    # 显示错分比例
    print("the total error rate is: %f" % (errorCount / float(numTestVecs)))
    print(errorCount)

2.7使用算法:构建完整的约会网站预测函数

具体内容,可参见:2-5节 k-近邻算法-测试算法&使用算法|优化约会网站的配对效果项目|机器学习实战-学习笔记

def classifyPerson():
    resultList=['not at all', 'in small doses', 'in large doses']
    # 原文中用的raw_input,在python3中统统使用input
    percentTats = float(input("Percentage of time spent playing vedio game?"))
    ffMiles = float(input("frequent flier miles earned per years?"))
    iceCream = float(input("liters of ice cream consumed per years?"))
    datingDataMat,datingLabels=file2matrix("datingTestSet2.txt")
    normMat,ranges,minVals =autoNorm(datingDataMat)
    
    inArr=np.array([ffMiles, percentTats, iceCream])
    # classifyerResut-1是因为分类结果是123,而resultlist中排序是012
    classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
    print("You will probably like this person: ",resultList[classifierResult-1])

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

推荐阅读更多精彩内容