文章原创,最近更新:2018-08-10
本章节的主要内容是:
重点介绍项目案例1: 优化约会网站的配对效果中的完整代码
。
1.KNN项目案例介绍:
项目案例1:
优化约会网站的配对效果
项目概述:
1)海伦使用约会网站寻找约会对象。经过一段时间之后,她发现曾交往过三种类型的人: 不喜欢的人、魅力一般的人、 极具魅力的人。
2)她希望: 1. 工作日与魅力一般的人约会 2. 周末与极具魅力的人约会 3. 不喜欢的人则直接排除掉。现在她收集到了一些约会网站未曾记录的数据信息,这更有助于匹配对象的归类。
开发流程:
- 收集数据:提供文本文件
- 准备数据:使用 Python 解析文本文件
- 分析数据:使用 Matplotlib 画二维散点图
- 训练算法:此步骤不适用于 k-近邻算法
- 测试算法:使用海伦提供的部分数据作为测试样本。
测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。 - 使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。
数据集介绍
海伦把这些约会对象的数据存放在文本文件 datingTestSet2.txt (数据来源于《机器学习实战》第二章 k邻近算法)中,总共有 1000 行。
本文使用的数据主要包含以下三种特征:每年获得的飞行常客里程数,玩视频游戏所耗时间百分比,每周消费的冰淇淋公升数。其中分类结果作为文件的第四列,并且只有3、2、1三种分类值。datingTestSet2.csv文件格式如下所示:
飞行里程数 | 游戏耗时百分比 | 冰淇淋公升数 | 分类结果 |
---|---|---|---|
40920 | 8.326976 | 0.953952 | 3 |
14488 | 7.153469 | 1.673904 | 2 |
26052 | 1.441871 | 0.805124 | 1 |
数据在datingTestSet2.txt文件中的格式如下所示:
2.优化约会网站的配对效果项目
第二章是描述K近邻算法的,算法本质就是寻找距离最近的点,这个距离可以是欧式距离,也可以是其他,这本书采用的就是欧式距离了。K近邻算法主要是用来分类的,比如我新输入一个数据,要判断他属于哪个类别,用这个算法就很合适了,简单实用。
2.1准备工作
这里第一步,其实就是为了检验我们后续编写的的K近邻算法有没有效果。
首先我们创建一个名为kNN.py的文件,然后我们就创建一个函数,这个函数返回一个矩阵和标签列表,以方便我们后续对K近邻算法进行检验。
具体内容,可参见:2-1节 k-近邻算法-KNN算法|优化约会网站的配对效果项目|机器学习实战-学习笔记
import numpy as np
import operator
def createDataSet():
"""
创建数据集和标签
调用方式
import kNN
group, labels = kNN.createDataSet()
"""
group = np.array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels=["A","A","B","B"]
return group,labels
2.2kNN算法
目的就是为了找最近的距离,采用的是欧氏距离.
具体内容,可参见:2-1节 k-近邻算法-KNN算法|优化约会网站的配对效果项目|机器学习实战-学习笔记
def classify0(inX,dataSet,labels,k):
"""
inX:用于分类的输入向量
dataSet:输入的训练样本集
lables:标签向量
k:表示用于选择最近邻居的数目
预测数据所在分类可在输入下列命令
kNN.classify0([0,0], group, labels, 3)
"""
# array的shape函数返回指定维度的大小,如dataset为n*m的矩阵,
# 则dataset.shape[0]返回n,dataset.shape[1]返回m,dataset.shape返回n,m
dataSetSize = dataSet.shape[0]
# tile函数简单的理解,它的功能是重复某个数组。比如tile(A,n),功能是将数组A重复n次,构成一个新的数组
# 所以此处tile(inX,(dataSetSize,1))的作用是将inX重复复制dataSetSize次,以便与训练样本集的样本个数一致
# 减去dataSet就是求出其差值,所以diffMat为一个差值矩阵
diffmat=np.tile(inX,(dataSetSize,1))-dataSet
#距离度量,度量公式为欧氏距离
sqdiffmat=diffmat**2
# 将矩阵的每一行相加,axis用于控制是行相加还是列相加
sqdistances=sqdiffmat.sum(axis=1)
#开方
distances=sqdistances**0.5
# 根据距离排序从小到大的排序,返回对应的索引位置
sortedDistIndicies=distances.argsort()
# 选择距离最小的k个点
classcount={}
for i in range(k):
# 找到该样本标签的类型
voteIlabel=labels[sortedDistIndicies[i]]
# 字典的get方法,list.get(k,d) 其中 get相当于一条if...else...语句,参数k在字典中,字典将返回list[k];如果参数k不在字典中则返回参数d
classcount[voteIlabel]=classcount.get(voteIlabel,0)+1
# 字典的 items() 方法,以列表返回可遍历的(键,值)元组数组。
# sorted 中的第2个参数 key=operator.itemgetter(1) 这个参数的意思是先比较第几个元素
sortedClasscount = sorted(classcount.items(),key=operator.itemgetter(1),reverse=True)
# 返回最符合的标签
return sortedClasscount[0][0]
2.3准备数据。我们将数据转化为numpy解析程序
具体内容,可参见:2-2节 k-近邻算法-使用 Python 解析文本文件|优化约会网站的配对效果项目|机器学习实战-学习笔记
def file2matrix(filename):
"""
导入训练数据
filename: 数据文件路径
return: 数据矩阵returnMat和对应的类别classLabelVector
"""
# 打开文件
fr=open(filename)
# 读取每一行的信息
array0Lines=fr.readlines()
# 获得文件中的数据行的行数
numberofLines=len(array0Lines)
# 生成numberofLines行3列的零矩阵,这3列都是用来存放特征信息
returnMat=np.zeros((numberofLines,3))
# 存放类别是列表
classLabelVector=[]
index=0
for line in array0Lines:
# 去掉回车制表符,生成新的字符串
line=line.strip()
# 通过split以\t切割字符串,返回元素列表
listFromLine=line.split('\t')
# 选取前三个数据的特征值放进矩阵
returnMat[index,:]=listFromLine[0:3]
# 将listFromLine列表中的最后一列元素存储到classLabelVector
# 这列的数据是类别数据
classLabelVector.append(int(listFromLine[-1]))
# 更新index
index +=1
#返回特征矩阵returnMat和类别矩阵classLabelVector
return returnMat,classLabelVector
2.4使用Matplotlib创建散点图
具体内容,可参见:2-3节 k-近邻算法-使用 Matplotlib 画二维散点图|优化约会网站的配对效果项目|机器学习实战-学习笔记
简单版本
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*np.array(datingLabels),15.0*np.array(datingLabels))
plt.show()
详细版本
为了得到更好的效果,采用datingDataMat矩阵的属性列1和2展示数据,并以红色的'*'表示类标签1、蓝色的'o'表示表示类标签2、绿色的'+'表示类标签3
import kNN
import operator
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.font_manager import FontProperties
font = FontProperties(fname=r"c:\windows\fonts\SimSun.ttc", size=14)
group,labels=kNN.createDataSet()
fig=plt.figure()
ax=fig.add_subplot(1,1,1)
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
#ax.scatter(datingDataMat[:,1],datingDataMat[:,2],15.0*np.array(datingLabels),15.0*np.array(datingLabels))
datingLabels = np.array(datingLabels)
idx_1 = np.where(datingLabels==1)
p1 = ax.scatter(datingDataMat[idx_1,0],datingDataMat[idx_1,1],marker = '*',color = 'r',label='1',s=10)
idx_2 = np.where(datingLabels==2)
p2 = ax.scatter(datingDataMat[idx_2,0],datingDataMat[idx_2,1],marker = 'o',color ='g',label='2',s=20)
idx_3 = np.where(datingLabels==3)
p3 = ax.scatter(datingDataMat[idx_3,0],datingDataMat[idx_3,1],marker = '+',color ='b',label='3',s=30)
plt.xlabel("每年获取的飞行里程数",fontproperties=font)
plt.ylabel("玩视频游戏所消耗的事件百分比",fontproperties=font)
ax.legend((p1,p2,p3),("不喜欢","魅力一般","极具魅力"),loc=2,prop=font)
plt.show()
2.5归一化处理
归一化是为了让数据都处于[0,1]之间
具体内容,可参见:2-4节 k-近邻算法-归一化处理|优化约会网站的配对效果项目|机器学习实战-学习笔记
def autoNorm(dataSet):
"""
归一化特征值,消除属性之间量级不同导致的影响
dataSet: 数据集
return: 归一化后的数据集normDataSet,ranges和minVals即归一化的数据集,最小值与极差
归一化公式:
Y = (X-Xmin)/(Xmax-Xmin)
其中的 min 和 max 分别是数据集中的最小特征值和最大特征值。该函数可以自动将数字特征值转化为0到1的区间。
"""
# 找出样本集中每列属性的最小值
minVals= dataSet.min(0)
# 找出样本集中每列属性的最大值
maxVals= dataSet.max(0)
# 每列最大最小值之间的极差
ranges=maxVals-minVals
# 创建与样本集一样大小的零矩阵
normDataSet=np.zeros(np.shape(dataSet))
# 获得样本集的行的维度
m=dataSet.shape[0]
# 样本集中的元素与最小值组成的矩阵
normDataSet=dataSet-np.tile(minVals,(m,1))
# 将最小值之差除以范围组成矩阵
normDataSet=normDataSet/np.tile(ranges,(m,1))
return normDataSet, ranges, minVals
2.6测试算法
具体内容,可参见:2-5节 k-近邻算法-测试算法&使用算法|优化约会网站的配对效果项目|机器学习实战-学习笔记
def datingClassTest():
"""
对约会网站的测试方法
return:错误数
"""
# 设置测试数据的的一个比例(训练数据集比例=1-hoRatio)
hoRatio= 0.1 # 测试范围,一部分测试一部分作为样本
# 从文件中加载数据,获得特征和标签分开保存
datingDataMat,datingLabels =file2matrix('datingTestSet2.txt')
# 特征归一化,返回归一化特征,归一化范围集每个特征最小值
normMat,ranges,minVals=autoNorm(datingDataMat)
# m表示数据的行数,即矩阵的第一维
m =normMat.shape[0]
# 设置测试的样本数量,numTestVecs:m表示训练样本的数量
numTestVecs=int(m*hoRatio)
print("numTestVecs=",numTestVecs)
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)))
print(errorCount)
2.7使用算法:构建完整的约会网站预测函数
具体内容,可参见:2-5节 k-近邻算法-测试算法&使用算法|优化约会网站的配对效果项目|机器学习实战-学习笔记
def classifyPerson():
resultList=['not at all', 'in small doses', 'in large doses']
# 原文中用的raw_input,在python3中统统使用input
percentTats = float(input("Percentage of time spent playing vedio game?"))
ffMiles = float(input("frequent flier miles earned per years?"))
iceCream = float(input("liters of ice cream consumed per years?"))
datingDataMat,datingLabels=file2matrix("datingTestSet2.txt")
normMat,ranges,minVals =autoNorm(datingDataMat)
inArr=np.array([ffMiles, percentTats, iceCream])
# classifyerResut-1是因为分类结果是123,而resultlist中排序是012
classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3)
print("You will probably like this person: ",resultList[classifierResult-1])
网友评论