机器学习实战—k近邻算法(kNN)02-改进约会网站的配对效果

28 篇文章 0 订阅
18 篇文章 0 订阅

示例:使用k-近邻算法改进约会网站的配对效果

在约会网站上使用k-近邻算法:
1.收集数据:提供文本文件。
2.准备数据:使用Python解析文本文件。
3.分析数据:使用matplotlib画二维扩散图。
4.训练算法:此步骤不适用于k-近邻算法
5.测试数据:使用提供的部分数据作为测试样本。
测试样本与非测试样本的区别在于,测试样本是已经完成分类的数据,若果预测分类与实际不同,则标记为一个错误。
6.使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。

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

kNN.py中创建名为file2matrix的函数,以此来处理输入格式问题。该函数的输入为文件名字符串输出为训练样本矩阵和类标签向量

# 将文本记录转换为NumPy的解析程序
def file2matrix(filename):
    fr = open(filename)
    #得到文件行数
    arrayOfLines = fr.readlines()
    numberOfLines = len(arrayOfLines)
    #创建返回的Numpy矩阵
    returnMat = zeros((numberOfLines,3))
    classLabelVector = []
    #解析文件数据到列表
    index = 0
    for line in arrayOfLines:
        line = line.strip() #注释1
        listFromLine = line.split('\t') #注释2
        returnMat[index,:] = listFromLine[0:3]
        classLabelVector.append(int(listFromLine[-1]))
        index +=1
    return returnMat,classLabelVector

注释:
1. Python strip() 方法用于移除字符串头尾指定的字符(默认为空格)
2. .split(‘\t’) 按照制表符切割字符串
split()通过指定分隔符对字符串进行切片,如果参数num 有指定值,则仅分隔 num 个子字符串
语法:
str.split(str=”“, num=string.count(str))

  • str – 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等。
  • num – 分割次数。

在终端输入:

In[13]: reload(kNN)  #之前必须先import过,不然会报错
Out[13]: 
<module 'kNN' from '/home/vickyleexy/PycharmProjects/handwriting_KNN/kNN.py'>
In[14]: datingDataMat,datingLabels = kNN.file2matrix('datingTestSet.txt')
In[15]: datingDataMat
Out[15]: 

array([[  4.09200000e+04,   8.32697600e+00,   9.53952000e-01],
       [  1.44880000e+04,   7.15346900e+00,   1.67390400e+00],
       [  2.60520000e+04,   1.44187100e+00,   8.05124000e-01],
       ..., 
       [  2.65750000e+04,   1.06501020e+01,   8.66627000e-01],
       [  4.81110000e+04,   9.13452800e+00,   7.28045000e-01],
       [  4.37570000e+04,   7.88260100e+00,   1.33244600e+00]])
In[16]: datingLabels[0:20]
Out[16]: 
[3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3]       
  • 在Python中,想要使用模块,例如numpy,要先用import numpy as np导入,但调用其中的方法的时候,例如要使用zeros创建零矩阵,要用np.zeros(),有时候这样感觉有些麻烦,可以直接from numpy import * ,这样调用方法的时候直接使用zeros()即可。

分析数据:使用Matplotlib创建散点图

在终端输入:

In[17]: import matplotlib
In[18]: import matplotlib.pyplot as plt
In[19]: zhfont = matplotlib.font_manager.FontProperties(fname='/usr/share/fonts/truetype/arphic/simsun.ttc')
In[19]: fig = plt.figure()   #创建新窗口
In[20]: ax = fig.add_subplot(111) #将画布分割成1行1列,图像画在从左到右从上到下的第1块
In[22]: ax.scatter(datingDataMat[:,1],datingDataMat[:,2]) # 散点图函数(横坐标数据,纵坐标数据)
Out[22]: 
<matplotlib.collections.PathCollection at 0x7fcbaf70a550>
In[23]: plt.xlabel(u'每年获取的飞行常客历程数',fontproperties=zhfont)
Out[23]: 
<matplotlib.text.Text at 0x7fcbadfc6b50>
In[24]: plt.ylabel(u'玩游戏所耗时间百分比',fontproperties=zhfont)
Out[24]: 
<matplotlib.text.Text at 0x7fcbaddc0950>

这里写图片描述

由于上图没有使用样本分类的特征值,我们很难从图中看出任何有用的数据模式信息。
以下改进:
在终端输入:

In[27]:from numpy import *
In[29]: ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
Out[29]: 
<matplotlib.collections.PathCollection at 0x7fcbb2662e90>
plt.xlabel(u'每年获取的飞行常客历程数',fontproperties=font)

这里写图片描述

以下采用列1和列2的属性值却可以得到更好的效果:
另创建文件matplotlib_pic.py

# _*_ coding: utf-8 _*_
import kNN
reload(kNN)
import matplotlib
import matplotlib.pyplot as plt
datingDataMat, datingLabels = kNN.file2matrix('datingTestSet.txt')
print datingDataMat
print datingLabels
zhfont = matplotlib.font_manager.FontProperties(fname='/usr/share/fonts/truetype/arphic/uming.ttc')
plt.figure(figsize=(8, 5), dpi=80)
axes = plt.subplot(111)
# 将三类数据分别取出来
# x轴代表飞行的里程数
# y轴代表玩视频游戏的百分比
type1_x = []
type1_y = []
type2_x = []
type2_y = []
type3_x = []
type3_y = []
print 'range(len(datingLabels)):'
print range(len(datingLabels))
for i in range(len(datingLabels)):
    if datingLabels[i] == 1:  # 不喜欢
        type1_x.append(datingDataMat[i][0])
        type1_y.append(datingDataMat[i][1])

    if datingLabels[i] == 2:  # 魅力一般
        type2_x.append(datingDataMat[i][0])
        type2_y.append(datingDataMat[i][1])

    if datingLabels[i] == 3:  # 极具魅力
        print i, ':', datingLabels[i], ':', type(datingLabels[i])
        type3_x.append(datingDataMat[i][0])
        type3_y.append(datingDataMat[i][1])

type1 = axes.scatter(type1_x, type1_y, s=20, c='red')
type2 = axes.scatter(type2_x, type2_y, s=40, c='green')
type3 = axes.scatter(type3_x, type3_y, s=50, c='blue')
# plt.scatter(matrix[:, 0], matrix[:, 1], s=20 * numpy.array(labels),
#c=50 * numpy.array(labels), marker='o',label='test')
plt.xlabel(u'每年获取的飞行里程数', fontproperties=zhfont)
plt.ylabel(u'玩视频游戏所消耗的事件百分比', fontproperties=zhfont)
axes.legend((type1, type2, type3), (u'不喜欢', u'魅力一般', u'极具魅力'), loc=2, prop=zhfont)
plt.show()

这里写图片描述
- 字体下载:http://download.csdn.net/detail/u012150360/9835424
- 或:http://download.csdn.net/detail/u012150360/9835396

准备数据:归一化数值

由于‘每年获得的飞行常客里程数’对计算结果的影响远大于其他两个特征对计算结果的影响,特别是以距离作为计算标准的时候,所以要将数据进行归一化处理,统一将初始的数据的取值范围转化为0到1区间内的值(其他范围比如-1到1内也是可以的),如此才能使得它们的权重是相等的。

以下公式可以将任意取值范围的特征值转化为0到1区间的值。
newValue = (oldValue - min)/(max - min)

在文件kNN.py中增加一个新函数autoNorm(),该函数可以自动将数字特征值转化为0到1的区间。

# 归一化特征值
def autoNorm(dataSet):
    minVals = dataSet.min(0) #注释1
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals #是一个一行3列的数组
    normDataSet = zeros(shape(dataSet)) #创建一个与dataSet同大小的零矩阵
    m = dataSet.shape[0]           #数据行数
    normDataSet = dataSet - tile(minVals,(m,1)) #tile()函数将变量内容复制成输入矩阵同等的大小,注释2
    #特征值相除
    normDataSet = normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,minVals

注释:
1..min(0)中的参数0,使得函数可以从每列中选取最小值。输出结果:
In[107]: minVals
Out[107]:
array([ 0. , 0. , 0.001156])
若.min(1)应该是从每行中选取最小值。
2. tile(minVals,(m,1))
tile函数是将minVals在行上复制m次,在列上复制1次。

在终端输入,以测试autoNorm函数:

In[123]: reload(kNN)
Out[123]: 
<module 'kNN' from '/home/vickyleexy/PycharmProjects/handwriting_KNN/kNN.py'>
In[124]: normMat,ranges,minVals = kNN.autoNorm(datingDataMat)
In[125]: normMat
Out[125]: 

array([[ 0.44832535,  0.39805139,  0.56233353],
       [ 0.15873259,  0.34195467,  0.98724416],
       [ 0.28542943,  0.06892523,  0.47449629],
       ..., 
       [ 0.29115949,  0.50910294,  0.51079493],
       [ 0.52711097,  0.43665451,  0.4290048 ],
       [ 0.47940793,  0.3768091 ,  0.78571804]])
In[126]: ranges
Out[126]: 
array([  9.12730000e+04,   2.09193490e+01,   1.69436100e+00])
In[127]: minVals
Out[127]: 
array([ 0.      ,  0.      ,  0.001156])

测试算法:作为完成程序验证分类器

  机器学习一个很重要的工作就是评估算法的正确率,通常我们只提供已有数据的90%作为训练样本来训练分类器,而使用其余的10%数据去测试分类器,检测分类器的正确率。
  代码里我们定义一个计数器变量,每次分类器错误地分类数据,计数器就加1,程序执行完之后计数器的结果除以数据点总数即是错误率。
  为了测试分类器的效果,在kNN.py文件中创建函数datingClassTest,该函数是自包含的,可以在任何时候在Python运行环境中使用该函数测试分类器效果。

#分类器针对约会网站的测试代码
def datingClassTest():
    hoRatio = 0.10      #测试样本的比例
    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],4)
        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))

在终端输入:

In[128]: reload(kNN)
Out[128]: 
<module 'kNN' from '/home/vickyleexy/PycharmProjects/handwriting_KNN/kNN.py'>
In[129]: kNN.datingClassTest()
the classifier came back with: 3, the real answer is :3
the classifier came back with: 2, the real answer is :2
the classifier came back with: 1, the real answer is :1
the classifier came back with: 1, the real answer is :1
……
the classifier came back with: 2, the real answer is :2
the classifier came back with: 1, the real answer is :1
the classifier came back with: 1, the real answer is :1
the total error rate is: 0.030000

可以看出错误率为3%,可以改变datingClassTest内变量hoRatio和变量k的值,检测错误率是否变化。

使用算法:构建完整可用系统

  给海伦一段小程序,通过该程序,海伦可以在约会网站上找到某个人并输入他的信息,程序会给出她对对方喜欢程度的预测值。
  将下列代码加入到KNN.py文件中,并重新载入KNN

#约会网站预测函数
def classifyPerson():
    resultList = ['not at all','in small doses','in large doses']
    percentTats = float(raw_input("percentage of time spent playing video games?"))
    ffMiles = float(raw_input("frequent flier miles earned per year?"))
    iceCream = float(raw_input("liters of ice cream consumed per year?"))
    datingDataMat,datingLabels = file2matrix('datingTestSet.txt')
    normMat,ranges,minVals = autoNorm(datingDataMat)
    inArr = array([ffMiles,percentTats,iceCream])
    classifierResult = classify0((inArr - minVals)/ranges,normMat,datingLabels,3)
    print "You will probably like this person:",resultList[classifierResult - 1]

在终端输入以下,以了解程序的实际运行效果:

In[149]: reload(kNN)
Out[149]: 
<module 'kNN' from '/home/vickyleexy/PycharmProjects/handwriting_KNN/kNN.py'>
In[150]: kNN.classifyPerson()
percentage of time spent playing video games?>? 10
frequent flier miles earned per year?>? 10000
liters of ice cream consumed per year?>? 0.5
You will probably like this person: in small doses
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值