声明
本文参考了《机器学习实战》书中代码,结合该书讲解,并加之自己的理解和阐述
机器学习实战系列博文
- 机器学习实战--k近邻算法改进约会网站的配对效果
- 机器学习实战--决策树的构建、画图与实例:预测隐形眼镜类型
- 机器学习实战--朴素贝叶斯算法应用及实例:利用贝叶斯分类器过滤垃圾邮件
- 机器学习实战--Logistic回归与实例:从疝病症预测病马的死亡率
问题描述
朋友海伦一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的人选,但她并不是喜欢每一个人。经过一番总结,她发现曾交往过三种类型的人:
- 不喜欢的人
- 魅力一般的人
- 极具魅力的人
为了让软件更好的给海伦推荐人选,海伦收集很多约会数据,她把这些数据存放在文本文件datingTestSet.txt中,每个样本数据占据一行,总共有1000行。海伦的样本主要包含以下3种特征:
- 每年获得的飞行常客里程数
- 玩视频游戏所耗时间百分比
- 每周消费的冰淇淋公升数
接下来的任务就是判定一个人对海伦来说是否有魅力
K近邻算法
该算法就是在一堆数据中找到与当前点最相似的K个点,然后由这K个点决定当前点属于哪一类,对未知类别属性的数据集中的每个点依次执行以下操作:
- 计算已知类别数据集中的点与当前点之间的距离;
- 按照距离递增次序排序;
- 选取与当前点距离最小的k个点;
- 确定前k个点所在类别的出现频率;
- 返回前k个点出现频率最高的类别作为当前点的预测分类。
代码实现
def classify0(inX,dataSet,labels,k):
dataSetSize = dataSet.shape[0]
#当前点与数据集做差
diffMat = tile(inX,(dataSetSize,1))-dataSet
# 求距离
sqDiffMat = diffMat**2
sqDistances = sqDiffMat.sum(axis=1)
distances = sqDistances**0.5
sortedDistIndices = distances.argsort()
classCount = {}
#选取最近的K个点
for i in range(k):
voteIlabel = labels[sortedDistIndices[i]]
classCount[voteIlabel] = classCount.get(voteIlabel,0)+1
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
#返回相似度最高的标签
return sortedClassCount[0][0]
优化约会网站
首先要读取海伦收集到的信息
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
然后我们会可视化的看一下三个维度的关系
returnMat,classLabelVector = file2matrix("datingTestSet.txt")
fig = plt.figure()
ax = fig.add_subplot(111)
#第1维与第2维
ax.scatter(returnMat[:,1],returnMat[:,2],15.0*array(classLabelVector),15.0*array(classLabelVector))
plt.show()
然后会看到下图所示的图像,很明显竖直上没有区分信息,水平上有,那说明第一维信息有可能是有区分度的
再看一下第0维和第1维的图像如下,发现区分情况较好
当然我们还可以再看一看第0维和第2维的
所以从图像上边也可以看出来,第2维的区分度确实很小,当然这些判断只是我们人为做出的数据分析,其实实际算法是可以不考虑这些的,因为不相干的信息并不影响最后结果。
下边要对数据做出归一化
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
然后进行验证
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix("datingTestSet.txt")
normMat,ranges,minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
#选取前numTestVecs做测试,训练集就是numTestVecs+1到最后的数据
numTestVecs = int(m*hoRatio)
errorCount = 0.0
for i in range(numTestVecs):
classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],
datingLabels[numTestVecs:m],7)
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)))
结果展示
这里k=7,原文中使用k=3失误率只有2.4%,而我发现只有当k取7时错误率最低只能降到4%