《机器学习实战》第二章:k-近邻算法(2)约会对象分类

来源:互联网 发布:c语言编99乘法表 编辑:程序博客网 时间:2024/05/21 06:59

这是KNN一个新例子。

在一个约会网站里,每个约会对象有三个特征:

(1)每年获得的飞行常客里程数(额...这个用来判断你是不是成功人士?)

(2)玩视频游戏所耗时间百分比(额...这个用来判断你是不是肥宅?)

(3)每周消费的冰激凌公升数(额...这个是何用意我真不知道了...)

然后给贴标签。标签种类有三种:

(1)根本不喜欢

(2)有一点喜欢

(3)喜欢得不得了

------------------------------------------------------------------------------------------------------------------

我们首先要从数据文件里面把训练集读入内存。

数据文件大概长这样:



第一列是飞行里程数的特征值,第二列是游戏百分比,第三列是冰淇淋公升数。最后一列是标签,1~3表示喜欢程度。

接下来是代码,file2matrix 函数将文件中的特征数据和标签读到矩阵和list里面

def file2matrix(filename):    fr = open(filename)    arrayOLines = fr.readlines()    numberOfLines = len(arrayOLines)    returnMat = zeros((numberOfLines,3))    classLabelVector = []    index = 0    for line in arrayOLines:        line = line.strip()        listFromLine = line.split('\t')        returnMat[index,:] = listFromLine[0:3]        classLabelVector.append(int(listFromLine[-1]))        index += 1    return returnMat, classLabelVector

值得注意的是这个 zeros 函数,构造一个n×n的零矩阵。

另外第12行,偷懒的话可以这样写:

returnMat[index] = listFromLine

------------------------------------------------------------------------------------------------------------------

然后作者介绍了通过散点图来分析数据的方法,用的是metplotlib这个包。

import matplotlib.pyplot as pltdef matrix2plot(dataSet):    fig = plt.figure()    ax = fig.add_subplot(111)    ax.scatter(dataSet[:, 0], dataSet[:, 1], 15 * array(datingLabels), array(datingLabels))    plt.show()

figure()用来创建一个图的对象。

add_subplot(x,y,z) 是这么个意思:将画布划分为x行y列,图像画在从左到右从上到下的第z块。

scatter() 用来画出散点。这里它接收了4个参数:

(1)横轴数据。这里是dataSet[:, 0],也就是数据集的第1个特征(飞行里程数)

(2)纵轴数据。这里是dataSet[:, 1],也就是数据集的第2个特征(游戏百分比)

(3)每个散点的标度(scalar),从实际上看是散点的半径,或者说有多“粗”。这里是15 * array(datingLabels)。什么意思呢?每个散点对应一个标签datingLabel,要么1,要么2,要么3。把这个值乘以15,变成15,30,45,意思就是标签为1的散点的“粗度”是15,以此类推。其实就是为了在图上面好看出散点的标签。
(4)每个散点的颜色。这里是array(datingLabels)。意思是不同的散点,按照他的标签(1或2或3)给他分配一种颜色。目的也是为了在图上面好看出散点的标签。


效果如下:



横坐标是飞行里程数,纵坐标是游戏百分比。绿色点标签是1(根本不喜欢),黄色点标签是2(有一点不喜欢),紫色点标签是3( 喜欢得不得了)。


------------------------------------------------------------------------------------------------------------------
我们现在有个问题。不是要算欧氏距离吗,那么比如(20000, 0, 0.1)和(32000, 67, 0.1)这两组数据,如果要算欧氏距离的话,那么显然第一个特征的贡献率是最大的,因为其他两个特征的数量级和他没法比啊。

这就需要做一个预处理,叫做“数值归一化”,把任意取值范围的特征值转化为0到1区间内的值。公式如下:
newValue = (oldVlue - min) / (max - min)

代码很容易理解:

def autoNorm(dataSet):    minVals = dataSet.min(0) # 每列的最小值    maxVals = dataSet.max(0) # 每列的最大值    ranges = maxVals - minVals #取值范围    normDataSet = zeros(shape(dataSet)) # 按数据的行数、列数构造0    m = dataSet.shape[0] # 数据的条数    normDataSet = dataSet - tile(minVals, (m,1))    # tile:构造每一行都是minVals的数据    normDataSet = normDataSet / tile(ranges, (m,1)) # tile:构造每一行都是ranges的数据    return normDataSet, ranges, minVals

------------------------------------------------------------------------------------------------------------------

现在来测试一下分类器的效果。大概过程是:

(1)从文件中读取数据,并归一化特征值;

(2)抽出一部分来用作分类器的训练样本,剩下的用于测试。

(3)开始训练+测试,并统计错误率

def datingClassTest():    hoRatio = 0.50    datingDataMat, datingLabels = file2matrix('datingTestSet2.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],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))

normMat 是特征值归一化后的矩阵。m是数据的条数。hoRatio是测试样本的比例,这里是50%,所以有一半的样本拿去训练,另一半做测试。numTestVecs是测试样本数量。

第9行调用classify0(源码在上篇博客),逐条地将测试样本(前numTestVecs条数据)进行分类,然后验证准确与否。normMat[numTestVecs:m,:] 就是训练集(下标从numTestVecsm-1的数据)。

测试结果:


错误率为5.0%。还...不错

------------------------------------------------------------------------------------------------------------------

好。现在我们将这个分类器“产品化”,就是可以接受用户输入,然后帮忙分类与预测。

def classifyPerson():    resultList = ['压根不喜欢', '有一点喜欢', '喜欢得不得了']    ffMiles = float(raw_input("飞行里程?"))    percentTats = 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, 3)    print "你喜欢这个人的程度很可能是:", resultList[classifierResult - 1];

resultList枚举了标签的种类。inArr是待预测数据,在传入classify0前进行了归一化。

运行结果:



0 0
原创粉丝点击