k-近邻算法2——识别约会对象

本文主要内容来自Peter Harrington的《Machine Learning in Action》的中文版本《机器学习实战》

本文中数据和代码在这里

示例2

任务——海伦一直在约会网站上寻找合适的约会对象,但网站推荐的约会对象并不都合她心意。她把曾约会过的对象分为三个类型:毫无魅力、魅力一般、极具魅力,然后收集到一些有益数据,我们将根据她收集到的数据创建分类器,帮助她把网站推荐的约会对象划分到切确的类。

识别约会对象的k-近邻算法

  1. 收集数据:海伦提供的文本文件
  2. 准备数据:使用python解析文本文件
  3. 分析数据:使用Matplotlib画二维散点图
  4. 训练算法:此步骤不适合k-近邻算法
  5. 测试算法:利用留出法把海伦提供的数据,随机提取10%作为测试数据,并统计错误率。
  6. 使用算法:利用分类器,根据海伦输入的特征数据,对约会对象进行判断

收集数据

海伦收集的数据在datingTestSet2.txt中,每个样本数据占一行,总共1000行。海伦的样本主要包含以下三种特征:

  • 每年获得的飞行常客里程数
  • 玩视频游戏所耗时间百分比
  • 每周消费的冰淇淋公升数

准备数据

由于数据在文本文件datingTestSet2.txt中,我们需要解析文本文件,把数据转化为分类器可接受的格式。

##转换文本数据
def fileToMatrix(filename):
    file = open(filename) #读入文件
    lines = file.readlines() #按行读取
    numberOfLines = len(lines) #统计行数
    datingMatrix = zeros((numberOfLines, 3)) #约会对象特征矩阵
    datingLabels = [] #约会对象类型向量
    index = 0
    for line in lines:
        line = line.strip() #去除首尾空格
        listFromLine = line.split('\t')  #使用tab字符把整行数据分割为数据列表
        datingMatrix[index, :] = listFromLine[0:3] #把前三个数据存储到对象特征矩阵
        datingLabels.append(int(listFromLine[-1])) #最后一个数据强制转化为整型后,存储到约会对象类型向量中
        index += 1
    return datingMatrix, datingLabels

fileToMatrix把文本中的数据,转化输出到对象特征矩阵datingMatrix和约会对象类型向量datingLabels中。

我们写一个测试程序

import kNN

datingMatrix, datingLabel = kNN.fileToMatrix('datingTestSet2.txt')

print(datingMatrix)
print(datingLabel)

运行结果如下

[[4.0920000e+04 8.3269760e+00 9.5395200e-01]
 [1.4488000e+04 7.1534690e+00 1.6739040e+00]
 [2.6052000e+04 1.4418710e+00 8.0512400e-01]
 ...
 [2.6575000e+04 1.0650102e+01 8.6662700e-01]
 [4.8111000e+04 9.1345280e+00 7.2804500e-01]
 [4.3757000e+04 7.8826010e+00 1.3324460e+00]]
[3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, ....2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 1, 3, 3, 3]

分析数据

我们使用二维散点图来分析数据,其中黄色点表示极具魅力,蓝色表示魅力一般,紫色表示毫无魅力

通过三张散点图,我们发现前两个特征更容易区分数据点从属的类别。

数据归一化

由于三个特征数据之间数值相差很大,而海伦认为三者同等重要,所以我们需要进行归一化处理。
我们利用公式\(newValue = (oldValue-min)/(max-min)\),其中\(min\)\(max\)是最小特征值和最大特征值。

把约会对象的特征数据归一化

def normDatingData(dataSet):
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = zeros(shape(dataSet))
    rows = dataSet.shape[0]
    normDataSet = dataSet - tile(minVals, (rows, 1))
    normDataSet = normDataSet/tile(ranges, (rows, 1))
    return normDataSet, minVals, ranges

测试算法

利用留出法进行测试,并计算错误率

def datingClassTest():
    hoRatio = 0.10  # 留出法10%
    datingMatrix, datingLabels = fileToMatrix('datingTestSet2.txt')  # 读数据
    normMat = normDatingData(datingMatrix)
    rows = normMat.shape[0]
    numTestVecs = int(rows * hoRatio)
    errorCount = 0.0
    for i in range(numTestVecs):
        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:rows, :], datingLabels[numTestVecs:rows], 3)
        print("分类器返回结果: %d, 真正的结果: %d" % (classifierResult, datingLabels[i]))
        if (classifierResult != datingLabels[i]): errorCount += 1.0
    print("错误率: %f" % (errorCount / float(numTestVecs)))

运行结果:

分类器返回结果: 3, 真正的结果: 3
分类器返回结果: 2, 真正的结果: 2
分类器返回结果: 1, 真正的结果: 1
分类器返回结果: 1, 真正的结果: 1
分类器返回结果: 1, 真正的结果: 1
分类器返回结果: 1, 真正的结果: 1
 ...
分类器返回结果: 2, 真正的结果: 2
分类器返回结果: 1, 真正的结果: 1
分类器返回结果: 3, 真正的结果: 3
分类器返回结果: 3, 真正的结果: 3
分类器返回结果: 2, 真正的结果: 2
分类器返回结果: 1, 真正的结果: 1
分类器返回结果: 3, 真正的结果: 1
错误率: 0.050000

使用算法

对单个约会对象进行分类

def classifyPerson():
    resultList = ['毫无魅力', '魅力一般', '极具魅力']
    percentTats = float(input("玩视频游戏所耗时间百分比:"))
    ffMiles = float(input("每年获得的飞行常客里程数:"))
    iceCream = float(input("每周消费的冰淇淋公升数:"))
    datingMatrix, datingLabels = fileToMatrix('datingTestSet2.txt')
    normMat, minVals, ranges = normDatingData(datingMatrix)
    person = array([ffMiles, percentTats, iceCream])
    normPerson = (person-minVals)/ranges
    classifierResult = classify0(normPerson, normMat, datingLabels, 5)
    print("此人魅力值:", resultList[classifierResult-1])

运行结果

玩视频游戏所耗时间百分比:15
每年获得的飞行常客里程数:40000
每周消费的冰淇淋公升数:1
此人魅力值: 极具魅力
posted @ 2018-04-18 23:05  Liupeng_Wang  阅读(537)  评论(0编辑  收藏  举报