案例二.:使用K-近邻算法改进约会网站的配对效果
案例分析:
海伦收集的数据集有三类特征,分别是每年获得的飞行常客里程数、玩视频游戏所耗时间百分比、
每周消费的冰淇淋公升数。我们需要将新数据的每个新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
流程:在约会网站上使用K近邻算法
(1)收集数据:提供文本文件。
(2)_准备数据: 使用Python解析文本文件。
(3 )分析数据:使用Matplotlib画二维扩散图。
(4)训练算法:此步驟不适用于k近邻算法。
(5)测试算法:使用海伦提供的部分数据作为测试样本。测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
(6)使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。
数据集样式
从文件读取数据集转换为二维数组和类别向量
代码:
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(' ')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index+=1
return returnMat,classLabelVector
读取数据文件后显示截图:
绘制散点图,散点图使用datingMat矩阵的第二、第三列数据,分别表示特征
值 “玩视频游戏所耗时间百分比”和 “每周所消费的冰淇淋公升数” 。
代码:
from numpy import *
from imp import reload
import matplotlib
import matplotlib.pyplot as plt
import kNN
datingMat,labelVector = kNN.file2matrix('datingTestSet2.txt')
plt.figure(figsize = (8,5), dpi = 80)
axes = plt.subplot(111)
type1_x = []
type1_y = []
type2_x = []
type2_y = []
type3_x = []
type3_y = []
for i in range(len(labelVector)):
#不喜欢
if labelVector[i] == 1:
type1_x.append(datingMat[i][0])
type1_y.append(datingMat[i][1])
#魅力一般
elif labelVector[i] == 2:
type2_x.append(datingMat[i][0])
type2_y.append(datingMat[i][1])
#魅力超群
else:
type3_x.append(datingMat[i][0])
type3_y.append(datingMat[i][1])
typeFirst = axes.scatter(type1_x, type1_y, s=20, c='red')
typeSecond = axes.scatter(type2_x, type2_y, s=40, c='green')
typeThird = axes.scatter(type3_x, type3_y, s=60, c='blue')
plt.xlabel(u'每年获取的飞行里程数')
plt.ylabel(u'玩视频游戏所消耗的事件百分比')
axes.legend((typeFirst,typeSecond,typeThird),(u'不喜欢',u'魅力一般',u'魅力超群'),loc = 2)
plt.show()
显示图片:
归一化数据
如果我们仔细观察数据集会发现,每年获取的飞行常客里程数对于计算结果的影响将远远大于数据集中其他两个特征—
玩视频游戏的和每周消费冰洪淋公升数—
的影响。而产生这种现象的唯一原因,仅仅是因为飞行常客里程数远大于其他特征值。但海伦认为这三种特征是同等重要的,因此作为三个等权重的特征之一,飞行常客里程数并不应该如此严重地影响到计算结果。
在处理这种不同取值范围的特征值时,我们通常采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。下面的公式可以将任意取值范围的特征值转化为0到1区间内的值:
n e w V a l u e = { o l d V a l u e - m i n ) / (max-min)
其中min和max分别是数据集中的最小特征值和最大特征值。虽然改变数值取值范围增加了
分类器的复杂度,但为了得到准确结果,我们必须这样做。我们需要在文件kNN.py中增加一个新函数autoNorm() , 该函数可以自动将数字特征值转化为0到1的区间。
代码:
def autoNorm(dataSet):
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
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
归一化效果截图:
测试算法
机器学习算法一个很重要的工作就是评估算法的正确率,通常我们只提供已有数据的90%作为训练样本来训练分类器
,而使用其余的10%数据去测试分类器,检测分类器的正确率。需要注意的是,10%的测试数据应该是随机选择的,由于海伦提供的数据并没有按照特定目的来排序,所以我们可以随意选择10%数据而不影响其随机性。
对于分类器来说,错误率就是分类器给出错误结果的次数除以测试数据的总数,完美分类器的错误率为0
,而错误率为1.0的分类器不会给出任何正确的分类结果。代码里我们定义一个计数器变量,每次分类器错误地分类数据,计数器就加1,
程序执行完成之后计数器的结果除以数据点总数即是错误率。
代码:
def datingClassTest():
hoRatio = 0.10
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)))
测试截图:
约会网站预测函数
def classifyPerson():
resultList = ['not at all','in small doses','in large doses']
percentTats = float(input("percentage of time spent playing video games?"))
ffMiles = float(input("frequent flier miles earned per year"))
iceCream = float(input("liters of ice cream consumed per year?"))
datingDataMat,datingLabels = file2matrix('datingTestSet2.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: %s" % resultList[classifierResult-1])
效果截图: