机器学习实战笔记一:K-近邻算法在约会网站上的应用

K-近邻算法概述

简单的说,K-近邻算法采用不同特征值之间的距离方法进行分类

 K-近邻算法

优点:精度高、对异常值不敏感、无数据输入假定。

缺点:计算复杂度高、空间复杂度高。

适用范围:数值型和标称型。

 

k-近邻算法的一般流程

  1. 收集数据:可使用任何方法
  2. 准备数据:距离计算所需要的数值,最好是结构化的数据格式。
  3. 分析数据:可以使用任何方法。
  4. 训练算法:此步骤不适用于K-近邻算法
  5. 使用算法:首先需要输入样本数据和节后话的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理
#kNN分类器
def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]  #得到数据总量
    diffMat = tile(inX,(dataSetSize,1)) - dataSet #将输入数据扩充成与数据集同样大小的矩阵并作差
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1) #axis = 1 参数是维度参数等于1在此处表示将一个矩阵的每一行向量相加
    distances = sqDistances** 0.5
    sortedDistancesIndicies = distances .argsort() #将列表值进行对比返回一个按照数值升序的下标值
    classCount={}
    for i in range(k):
        voteIlabel = labels[sortedDistancesIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0)+1
        #dict.get("key") 返回value  dict.get("key",default= None)如果能找到就返回对应的value找不到返回默认值
    sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse=True)
    #sorted 返回一个list  operator.itemgetter(x,y)表示根据x+1维度的第y+1维度
    return sortedClassCount[0][0]

 

K-近邻算法在约会网站改进的应用

在约会网站上使用K-近邻算法

(1)收集数据:提供文本文件。

(2)准备数据:使用python解析文本文件。

(3)分析数据:使用Matplotlib绘画二维扩展图。

(4)训练算法:此步骤不适用于K-近邻算法。

(5)测试算法:使用提供的部分数据作为测试样本。

         测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际不同,则标记为一个错误。

(6)使用方法:产生简单的命令行程序,然后可以输入一些特征数据以判断对方是否为自己喜欢的类型

 

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

提供的文本文件datingTestSet2.txt中,每个样本数据占一行,总共有1000行。主要包括以下特征:

1.每年获得的飞行常客里程数

2.玩视频游戏所耗时间百分比

3.每周消费的冰淇淋公升数

将上述特征数据输入到分类器前,必须将待处理数据的格式改变为分类器可以接受的格式。创建名为file2matrix的函数,以此来处理格式问题。函数的输入为文件名,输出为训练样本和类标签向量。

# Author:Thomas Wang
from numpy import *
def file2matrix(filename):
    with open(filename,'r') as fr:
        arrayOLines = fr.readlines()
        numberOfLines = len(arrayOLines)#存储文件数据数目
        returnMat = zeros((numberOfLines,3))#准备接收数据的numpy数组
        classLabelVector = []#准备接收标签向量的数组python列表
        index = 0
        for line in arrayOLines:
            line = line.strip() #截去每行的回车
            listFromLine = line.split('\t')#以‘\t’为分隔符将字符串截取成字符串数组
            returnMat[index,:] = listFromLine[0:3]  #[index,:] = [index][:] 将字符串数组赋值给numpy数组自动转换为浮点型
            classLabelVector.append(int(listFromLine[-1]))
            index +=1
        return returnMat,classLabelVector

需要注意的是:

1、numpy数组中[index,:] 与python 列表中[index][:] 作用相同

2、numpy函数库可以自动解决变量值问题,而python并不可以必须明确告诉解释器才可以处理

 

通过以下命令查看数据内容

datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
print(datingDataMat)
print(datingLabels[0:20])
[[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, 1, 1, 1, 1, 1, 2, 3]

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

 我们借助Matplotlib可以让我们对我们得到的数据更加直接的展示在我们的面前(具体的Matplotlib可以根据需要进行了解学习)

import matplotlib
import  matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,0],datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
plt.show()

 机器学习实战笔记一:K-近邻算法在约会网站上的应用

 

准备数据:归一化数值

约会网站原始数据改进之后的样本数据
  玩视频游戏所耗时间百分比 每年获得的飞行常客里程数 每周消费的冰激凌公升数 样本分类
1 0.8 400 0.5 1
2 12 134000 0.9 3
3 0 20000 1.1 2
4 67 32000 0.1 2

根据上表中样本3和样本4的数据,计算样本3和样本4之间的距离:

\[\sqrt {{{(0 - 67)}^2} + {{(20000 - 32000)}^2} + {{(1.1 - 0.1)}^2}} \]

我们可以发现,上面方程中数字差值最大的属性对计算结果的影响最大,其中飞行常客里程数对计算结果的影响将远远大于其他两个特征值。所以,作为三个等权重的特征值之一,飞行常客里程数不应该

如此严重的影响计算结果。

解决这种去不同范围的特征值是,我们常常采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。我们可以用下式将任意取值范围的特征值转化到0到1区间内的值:

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


其中min和max是数据集中最小特征值和最大特征值。我们可以通过构造函数autoNorm()自动将数字特征函数转化到0到1之间。

def autoNorm(dataSet):
    minVals = dataSet.min(0) #Return the minimum along a given axis.
    maxVals = dataSet.max(0) #Return the maximum along a given axis.
    ranges = maxVals - minVals #取到特征值最大最小值之间的范围
    normDataSet = zeros(shape(dataSet)) 
    m = dataSet.shape[0] #取到数据量
    normDataSet = dataSet - tile(minVals,(m,1))
    normDataSet = normDataSet/tile(ranges,(m,1))
    return normDataSet,ranges,minVals

 

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

使用错误率来检测分类器的性能,对于分类器来说,错误率就是分类器给出错误结果的次数初一测试数据的总数数值处于0到1之间。代码中需要定义一个计数器变量,每次分类器错误的分类数据,计数器

就加1,程序执行完成之后计数器的结果初一数据点总数即是错误率。需要注意的是训练数据和测试数据都是按比例随机取得。


#完整程序验证分类器
def datingClassTest():
hoRatio = 0.1 #测试数据比例
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt') #文本文件转换成可以处理的矩阵
normDataSet, ranges, minVals = autoNorm(datingDataMat) #将数据进行归一化
m = normDataSet.shape[0] #获取数据总量
numTestVecs = int(m * hoRatio) #获取测试数据数量
errorCount = 0.0 #错误数据个数计数器
for i in range(numTestVecs):
#对每个测试数据利用KNN算法进行分类
classifierResult = classify0(normDataSet[i,:],normDataSet[numTestVecs:m,:],datingLabels[numTestVecs:m],4)
#打印预测结果与实际结果
print("分类器结果:%s,真实结果:%s"%(classifierResult,datingLabels[i]))
#计算总错误个数并计算错误率
if (classifierResult != datingLabels[i]): errorCount+=1.0
print("the total error rate is: %f"%(errorCount/float(numTestVecs)))
datingClassTest()
分类器结果:3,真实结果:3
分类器结果:2,真实结果:2
分类器结果:1,真实结果:1
......
分类器结果:2,真实结果:2
分类器结果:2,真实结果:1
分类器结果:1,真实结果:1
the total error rate is: 0.040000

 我们可以改变datingClassTest内变量hoRatio和变量K的值,洁厕错误率是否会随着变量的增加而增加。取决于分类算法、数据集和程序设计,分类器的输出结果可能会有很大的不同。

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

数据已经在分类器上进行了测试,我们将给用户一段小程序,通过用户输入的信息。程序会给出符合用户的预测值。

#完整可用系统
def classifyPerson():
    resultList = ['not at all','in small does','in large does']
    percetTats = float(input("玩视频游戏所耗时间百分比:"))
    ffMiles = float(input("每年获取的飞行常客里程数:"))
    iceCream = float(input("每年消耗冰淇淋的公升数:"))
    datingDataMat,datingLabels = file2matrix("datingTestSet2.txt")
    normMat,ranges,minVals = autoNorm(datingDataMat)
    inArr = array([ffMiles,percetTats,iceCream])
    classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels,4)
    print("You probably like this person:",resultList[classifierResult-1])

这样一个简单可用的针对约会网站的完整可用系统就完成了,代码理解并不是很困难。

总结

第一篇博客内容不会很详尽,希望大家留言指导改进。本篇博客通过一个简单的约会网站预测实例入手,论述了一个机器学习算法实践从无到有的过程:准备数据--->分析数据--->准备数据:归一化数值--->

测试算法--->使用算法。