代码来自于《机器学习实战》
问题背景
小红经常在约会网站寻找适合自己的约会对象。经过自己的总结,发现曾经交往过三种类型的人:
不喜欢的人
魅力一般的人
极具魅力的人
发现了上述规律,小红还是无法将约会网站推荐的匹配对象归到恰当的类别。她希望在周一到周五约会那些魅力一般的人,周末想那些极具魅力的人为伴(世界一直围绕着小红在转啊转......)。我们怎么帮助她?
1. 数据处理
数据保存在datingTestSet2.txt中,每个样本占据一行,共有1000行(哇,人才)。样本主要包含以下3个特征:
每月工资
玩游戏所消耗时间百分比
每周消费的冰淇淋公升数
些许样例示意图A 处理数据:将文本中的数据变成分类器能接受的格式。
导入需要的工具包
from numpy import *
定义函数file2Matrix:将文本数据转换成矩阵形式
def file2Matrix(filename):
fr= open(filename)
arrayOLines= fr.readlines()#直接将数据全部读取进来,形成列表,每一行为一个元素
numberOfLines= len(arrayOLines)#行数
returnMatrix= zeros((numberOfLines,3))
classLabelVector= []
index= 0
for linein arrayOLines:
line= line.strip()
listFromLine= line.split('\t')
returnMatrix[index,:] = listFromLine[0:3]#赋值方式很厉害!
classLabelVector.append(listFromLine[-1])
index+= 1
return returnMatrix,classLabelVector
B 数据归一化处理
数据中特征“每月工资”,取值范围太大,计算距离时,产生的影响过大。但我们认为3个特征的重要性是一样的。所以,有必要进行数据归一化处理。将数据归一化到 0~1 之间。
公式
newValue = (oldValue-min)/(max-min)
定义归一化函数autoNorm;返回归一化的数据,每个特征的最小值以及范围(方便对新数据进行归一化处理)。
def autoNorm(dataSet):
minVals= dataSet.min(0)#a.min() :全部的最小值;;a.min(axis=0):每列中的最小值;a.min(axis=1):每行中的最小值
maxVals= dataSet.max(0)
ranges= maxVals- minVals
m= dataSet.shape[0]#行数
normDataSet= dataSet- tile(minVals,(m,1))#(m,n)重复次数:行方向上重复m次,列方向上重复1次
normDataSet= normDataSet/tile(ranges,(m,1))
return normDataSet,ranges, minVals
其中,调用的函数
dataSet.min(0)
dataSet:python numpy array类型
array.min():求数组中的最小值
array.min(axis=0):对数组中的每列求最小值
array.min(axis=1):对数组中的每行求最小值
numpy.tile(A, reps):
Construct an array by repeating A the number of times given by reps.通过重复A ,reps次构建一个新的数组。
tile(minVals, (m, 1)):重复minVals 构造一个m*1的copy
2. 分类算法KNN
伪代码:
对未知类别属性的数据集中的每个点依次执行以下操作:
(1)计算已知类别数据集中的点与当前点之间的距离;
(2)按照距离递增次序排序;
(3)选取与当前点距离最近的k个点;
(4)确定前k个点所在类别的出现频率;
(5)返回前k个点出现频率最高的类别作为当前点的预测分类。
其实是计算两个数据的相似性:有距离相似性度量和角度相似性度量【knn算法介绍】。
这里采用距离相似性度量,欧式距离。
欧式距离计算公式分类器算法实现:
def classify0(inX, dataSet, labels, k):
dataSetSize= dataSet.shape[0]
diffMat= tile(inX,(dataSetSize,1)) - dataSet
sqDiffMat= diffMat** 2
sqDiffMat= sqDiffMat.sum(axis=1)#按列求和
distances= sqDiffMat** 0.5
sortedDistIndicies= distances.argsort()#argsort函数返回的是数组值从小到大的索引值
classCount= {}
for iin range(k):
voteLabel= labels[sortedDistIndicies[i]]
classCount[voteLabel] = classCount.get(voteLabel,0) + 1#在字典classCount中通过 键 查找相应的值,如果字典中没有键,值为0;有获取其值;最后加1
sortedClassCount= sorted(classCount.items(),key=lambda a:a[1],reverse=True)#根据出现次数排序,降序排序
return sortedClassCount[0][0]#返回概率最大(出现次数最多的键,即类别)
其中,调用函数:
numpy.argsort:
Returns the indices that would sort an array. 返回数组排序后的下标(默认升序排序)。
eg:[10, 1, 51]
调用函数,返回的是:[1, 0, 2] ;1是数据1的下标。
3. 测试算法
数据指标:误分率。
计算方法: 错误分类的记录数/测试数据的总数;
def datingClassTest():
"""
使用测试数据测试分类器效果
:return: 误分率"""
hoRatio= 0.10#测试数据所占的比例---将数据集按照1:9的比例划分,1是测试集;9是训练集
datingDataMat,datingLabels= file2Matrix('datingTestSet2.txt')
normMat,ranges,minVals= autoNorm(datingDataMat)
m= normMat.shape[0]#数据集总数
numTestVecs= int(m*hoRatio)#测试集数目
errorCount= 0
for i in range(numTestVecs):
classifierResult= classify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
print ("the classifier came back with: %s,the real answer is: %s" % (classifierResult, datingLabels[i]))
if (classifierResult!= datingLabels[i]):
errorCount+= 1
print ("the total error rate is: %f" %(errorCount/float(numTestVecs)))
运行结果:
测试算法运行结果错误率:5%
4. 使用算法
重头戏:使用knn分类算法,方便小红约会哦!
def classifyPerson1():
resultList= ["不喜欢","魅力一般","极具魅力(韩国欧巴)"]
percentTats= float(raw_input(\
"花费在游戏、视频上的时间百分比?"))#raw_input:读取控制台输入数据,赋值给变量,等效于C++里的cin>>a;
ffMiles= 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,k=3)#对输入数据进行归一化,然后再分类
print ("你将约会的人很可能是: %s" % resultList[int(classifierResult)-1])
应用:
小红将约会韩国欧巴(安排在周末) 小红遇到不喜欢的类型(pass)5. 小结
A。 矩阵运算----能加快运算速度;
B。knn算法分类数据简单有效,不用进行训练,是基于实例的学习,使用的数据必须接近实际数据的训练样本数据;必须保存所有数据,如果数据集过大,需要耗费巨大的存储空间;必须对数据中的所有样例点进行计算距离值,非常耗时(即使使用矩阵进行运算)。
C。人生苦短,我用Python!
完整代码及数据集:github
网友评论