本文主要是介绍【机器学习实战】K-近邻算法之海伦约会,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
【机器学习实战】K-近邻算法之海伦约会
文章是阅读《机器学习实战》【美】Peter Harrington著 李锐李鹏 曲亚东 王斌 译 的读书笔记,具体可以去参考这本书。
1.准备数据:文本文件中解析数据
海伦将约会数据放在datingTestSet.txt中,每个样本数据占一行,总共有1000行。海伦的样本主要包含以下三种特征:
- 每年获得的飞行常客里程数
- 玩视频游戏所消耗时间百分比
- 每周消费的冰淇淋公升数
在上述数据输入到分类器之前,需要将待处理数据格式改变为分类器可以接受的格式。由csv文件构建相对应的numpy数组, 输入为文件名字字符串,输出为训练样本矩阵和类标签向量。
def file2matrix(filename):fr = open(filename)arrayOLines = fr.readlines()# 得到文件行数numberOfLines = len(arrayOLines)# 创建返回的numpy数组,因为是海伦约会数据集有3个特征returnMat = zeros((numberOfLines, 3))classLabelVector = []index = 0# 解析文本数据到列表for line in arrayOLines:# 截取掉所有的回车字符line = line.strip()# 使用'\tab'键将整行数据转换为列表listFromLine = line.split('\t')# 将海伦约会的每一行数据转换为要返回的numpy数据行returnMat[index, :] = listFromLine[0:3]classLabelVector.append((listFromLine[-1]))# 将字符标签转换为数字标签# if listFromLine[-1] == 'didntLike':# classLabelVector.append(1)# if listFromLine[-1] == 'smallDoses':# classLabelVector.append(2)# if listFromLine[-1] == 'largeDoses':# classLabelVector.append(3)index += 1return returnMat, classLabelVector
加载数据
2.标题分析数据
使用Matplotlib制作原始数据的散点图分析数据
博主陈多鱼这块代码写的很好,在这借鉴了一下,感兴趣可以去看原帖,感谢博主
https://blog.csdn.net/qq_42338771/article/details/108270830
代码及其解释
# 图像数据分析
def showData(returnMat, classLabelVector):fig, axs = plt.subplots(nrows=2, ncols=2, sharex=False, sharey=False, figsize=(13, 8))labelColors = []for i in datingLables:if i == 1:labelColors.append('black')if i == 2:labelColors.append('orange')if i == 3:labelColors.append('red')# 设置第一张图片,飞行里程数和游戏百分比, 散点大小为15, 透明度0.5axs[0][0].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=labelColors, s=15, alpha=0.5)# 设置标题和坐标轴备注axs0_title_text = axs[0][0].set_title('flight_play')axs0_xlabel_text = axs[0][0].set_xlabel('飞行里程')axs0_ylabel_text = axs[0][0].set_ylabel('游戏时间')# 中文标题正常显示plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.setp(axs0_title_text, size=9, weight='bold', color='red')plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')# 设置第二张图片, 飞行里程数和冰淇淋axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=labelColors, s=15, alpha=0.5)axs1_title_text = axs[0][1].set_title('flight_eat')axs1_xlabel_text = axs[0][1].set_xlabel('飞行里程')axs1_ylabel_text = axs[0][1].set_ylabel('冰淇淋量')plt.setp(axs1_title_text, size=9, weight='bold', color='red')plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black')plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black')# 设置第三张图片,游戏时间和冰淇淋量axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=labelColors, s=15, alpha=0.5)axs2_title_text = axs[1][0].set_title('play_eat')axs2_xlabel_text = axs[1][0].set_xlabel('游戏时间')axs2_ylabel_text = axs[1][0].set_ylabel('冰淇淋量')plt.setp(axs2_title_text, size=9, weight='bold', color='red')plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black')plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black')# 设置图例didntLike = mlines.Line2D([], [], color='black', marker='.',markersize=6, label='didntLike')smallDoses = mlines.Line2D([], [], color='orange', marker='.',markersize=6, label='smallDoses')largeDoses = mlines.Line2D([], [], color='red', marker='.',markersize=6, label='largeDoses')# 添加图例axs[0][0].legend(handles=[didntLike, smallDoses, largeDoses])axs[0][1].legend(handles=[didntLike, smallDoses, largeDoses])axs[1][0].legend(handles=[didntLike, smallDoses, largeDoses])# 画出图片plt.show()
数据分析图例
这三张图可以看出来,第一张图通过飞行里程数和游戏百分比,还是可以把三种约会类型比较清晰的分类出来,而后面两张图类别中互相混合,无法分类出来。
3.数据归一化
约会网站的原始数据,可以看出来如果使用欧氏距离的话,飞行里程数与另外两个类别的数差距比较大,影响过大,但是海伦认为这三种数据同等重要,处理不同范围的特征值的时候,一般将数值归一化,减小数级的影响,将取值范围处理到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 ) / ( m a x − m i n ) newvalue = (oldvalue - min)/(max-min) newvalue=(oldvalue−min)/(max−min)
数值归一化范围-1到1之间公式:
n e w v a l u e = 2 ∗ ( o l d v a l u e − m i n ) / ( m a x − m i n ) − 1 newvalue = 2*(oldvalue - min)/(max-min) - 1 newvalue=2∗(oldvalue−min)/(max−min)−1
max与min分别是数据集中的最小特征值和最大特征值,改变数值取值范围会增加分类器的复杂度,但是会使结果更加准确。
代码数值归一化0到1区间函数autoNorm()。
# 数值归一化函数
def autoNorm(dataSet):minValue = dataSet.min(0)maxValue = dataSet.max(0)ranges = maxValue - minValue# 同样规格的零矩阵记录数值归一化的结果normDataset = zeros(shape(dataSet))# 记录数据行数,即数据条数m = dataSet.shape(0)# 复制minvalue到同样大小,然后做数据相减操作normDataset = dataSet - tile(minValue, (m, 1))# 复制ranges到同样规格大小,然后做数据相除操作normDataset = normDataset/tile(ranges, (m, 1))# 返回归一化结果,范围,最小值return normDataset, ranges, minValue
这里面操作的是特征值(数值)相除,不是向量相除,在numpy库中,向量相除使用函数linalg.solve(matA, matB)。
4.分类算法:KNN算法实现
使用的是之前文章中使用的欧式距离计算的方法来判断分类,详细参考文章
https://blog.csdn.net/qq_43587460/article/details/135983033#comments_31185167
# 分类器,使用欧几里得距离
def classify0(inx, dataset, labels, k):''':param inx: 未知标签数据特征:param dataset: 样本数据:param labels: 样本标签:param k: k近邻取值:return: 判断标签值'''# 距离计算datasetSetSize = dataset.shape[0]diffMat = tile(inx, (datasetSetSize, 1)) - datasetsqDiffMat = diffMat ** 2sqDistances = sqDiffMat.sum(axis=1)distance = sqDistances ** 0.5# 距离排序sortedDistIndicies = distance.argsort()classCount = {}for i in range(k):voteIlabel = labels[sortedDistIndicies[i]]classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1sorteClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)return sorteClassCount[0][0]
5.测试算法:作为完整程序验证分类器
评估算法的正确率,通常我们用90%的数据来作为训练样本来训练分类器,用10%的数据去测试分类器,检测分类器的正确率。10%的测试数据应该是随机选择的,海伦约会数据没有根据特定的目的来排序,可以随意选取10%的数据而不影响随机性。
可以使用错误率来检测分类器的性能,错误率就是分类器给出错误结果的次数除以测试数据的总数,完美分类器的错误率为0,而错误率为1.0的分类器不会给出任何正确的分类结果。
代码:
def datingClassTest():# 测试数据比例hoRatio = 0.10datingDataMat, datingLables = file2matrix('datingTestSet.txt')normMat, ranges, minValues = autoNorm(datingDataMat)# 获取归一化后矩阵行数m = normMat.shape[0]# 测试数据数量numTestVecs = int(m * hoRatio)errorCount = 0.0for i in range(numTestVecs):# 使用分类器分类classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :],\datingLables[numTestVecs:m], 3)print("the classifier came back with:%d, the real answer is:%d" % (classifierResult, datingLables[i]))# 分类器分类数据与预测数据对比,最后计算错误率if(classifierResult != datingLables[i]): errorCount += 1.0print("the total error rate is:%f" % (errorCount/float(numTestVecs)))
测试分类器结果:
可以看出,构建的分类器的错误率是5%,效果还不错,可以改进检测数据的量(hoRatio)与近邻判断的数量(k的值),来检测错误率是否会发生变化。
6.使用算法,构建完整可用的系统
写了一个人工智能,最想的还是可以帮忙来预测数据,来使用自己的构建的机器学习算法,构建预测函数。
代码及其解释如下:
# 约会网站预测函数
def classifyPerson():# 预测结果是数字,对应成字符串resultList = ['not at all', 'in small doses', 'in large doses']# 输入飞行里程,冰淇淋量,游戏时间来进行预测percentTats = float(raw_input(\"percentage of time spent playing video games?"))ffMiles = float(raw_input("frequent filer miles earned per year?"))iceCream = float(raw_input("liters of ice cream consumed per year?"))# 数据读取datingDataMat, datingLables = file2matrix('datingTestSet.txt')# 数据归一化normMat, ranges, minValues = autoNorm(datingDataMat)# 输入的数据格式整合,适配机器学习算法inArr = array([ffMiles, percentTats, iceCream])# 对数据进行归一化处理,并进行预测classifierResult = classify0((inArr - minValues)/ranges, normMat, datingLables, 3)print("You will probably like this person:", resultList[classifierResult - 1])
使用效果:
7.完整代码
from numpy import *
import operator
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
from pip._vendor.distlib.compat import raw_inputdef file2matrix(filename):fr = open(filename)arrayOLines = fr.readlines()# 得到文件行数numberOfLines = len(arrayOLines)# 创建返回的numpy数组,因为是海伦约会数据集有3个特征returnMat = zeros((numberOfLines, 3))classLabelVector = []index = 0# 解析文本数据到列表for line in arrayOLines:# 截取掉所有的回车字符line = line.strip()# 使用'\tab'键将整行数据转换为列表listFromLine = line.split('\t')# 将海伦约会的每一行数据转换为要返回的numpy数据行returnMat[index, :] = listFromLine[0:3]# 文本标签收集# classLabelVector.append((listFromLine[-1]))# 将字符标签转换为数字标签if listFromLine[-1] == 'didntLike':classLabelVector.append(1)if listFromLine[-1] == 'smallDoses':classLabelVector.append(2)if listFromLine[-1] == 'largeDoses':classLabelVector.append(3)index += 1return returnMat, classLabelVector# 加载数据
datingDataMat, datingLables = file2matrix('datingTestSet.txt')
# print(datingDataMat)
# print('------------')
# print(datingLables[0:20])# 图像数据分析
def showData(returnMat, classLabelVector):fig, axs = plt.subplots(nrows=2, ncols=2, sharex=False, sharey=False, figsize=(13, 8))labelColors = []for i in datingLables:if i == 1:labelColors.append('black')if i == 2:labelColors.append('orange')if i == 3:labelColors.append('red')# 设置第一张图片,飞行里程数和游戏百分比, 散点大小为15, 透明度0.5axs[0][0].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=labelColors, s=15, alpha=0.5)# 设置标题和坐标轴备注axs0_title_text = axs[0][0].set_title('flight_play')axs0_xlabel_text = axs[0][0].set_xlabel('飞行里程')axs0_ylabel_text = axs[0][0].set_ylabel('游戏时间')# 中文标题正常显示plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']plt.setp(axs0_title_text, size=9, weight='bold', color='red')plt.setp(axs0_xlabel_text, size=7, weight='bold', color='black')plt.setp(axs0_ylabel_text, size=7, weight='bold', color='black')# 设置第二张图片, 飞行里程数和冰淇淋axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=labelColors, s=15, alpha=0.5)axs1_title_text = axs[0][1].set_title('flight_eat')axs1_xlabel_text = axs[0][1].set_xlabel('飞行里程')axs1_ylabel_text = axs[0][1].set_ylabel('冰淇淋量')plt.setp(axs1_title_text, size=9, weight='bold', color='red')plt.setp(axs1_xlabel_text, size=7, weight='bold', color='black')plt.setp(axs1_ylabel_text, size=7, weight='bold', color='black')# 设置第三张图片,游戏时间和冰淇淋量axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=labelColors, s=15, alpha=0.5)axs2_title_text = axs[1][0].set_title('play_eat')axs2_xlabel_text = axs[1][0].set_xlabel('游戏时间')axs2_ylabel_text = axs[1][0].set_ylabel('冰淇淋量')plt.setp(axs2_title_text, size=9, weight='bold', color='red')plt.setp(axs2_xlabel_text, size=7, weight='bold', color='black')plt.setp(axs2_ylabel_text, size=7, weight='bold', color='black')# 设置图例didntLike = mlines.Line2D([], [], color='black', marker='.',markersize=6, label='didntLike')smallDoses = mlines.Line2D([], [], color='orange', marker='.',markersize=6, label='smallDoses')largeDoses = mlines.Line2D([], [], color='red', marker='.',markersize=6, label='largeDoses')# 添加图例axs[0][0].legend(handles=[didntLike, smallDoses, largeDoses])axs[0][1].legend(handles=[didntLike, smallDoses, largeDoses])axs[1][0].legend(handles=[didntLike, smallDoses, largeDoses])# 画出图片plt.show()showData(datingDataMat, datingLables)# 分类器,使用欧几里得距离
def classify0(inx, dataset, labels, k):''':param inx: 未知标签数据特征:param dataset: 样本数据:param labels: 样本标签:param k: k近邻取值:return: 判断标签值'''# 距离计算datasetSetSize = dataset.shape[0]diffMat = tile(inx, (datasetSetSize, 1)) - datasetsqDiffMat = diffMat ** 2sqDistances = sqDiffMat.sum(axis=1)distance = sqDistances ** 0.5# 距离排序sortedDistIndicies = distance.argsort()classCount = {}for i in range(k):voteIlabel = labels[sortedDistIndicies[i]]classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1sorteClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)return sorteClassCount[0][0]# 数值归一化函数
def autoNorm(dataSet):minValue = dataSet.min(0)maxValue = dataSet.max(0)ranges = maxValue - minValue# 同样规格的零矩阵记录数值归一化的结果normDataset = zeros(shape(dataSet))# 记录数据行数,即数据条数m = dataSet.shape[0]# 复制minvalue到同样大小,然后做数据相减操作normDataset = dataSet - tile(minValue, (m, 1))# 复制ranges到同样规格大小,然后做数据相除操作normDataset = normDataset/tile(ranges, (m, 1))# 返回归一化结果,范围,最小值return normDataset, ranges, minValue# 归一化实例
normMat, ranges, minValues = autoNorm(datingDataMat)
# print(normMat)
# print('-----------')
# print(ranges)
# print('-------------')
# print(minValues)# 错误率测试代码
def datingClassTest():# 测试数据比例hoRatio = 0.10datingDataMat, datingLables = file2matrix('datingTestSet.txt')normMat, ranges, minValues = autoNorm(datingDataMat)# 获取归一化后矩阵行数m = normMat.shape[0]# 测试数据数量numTestVecs = int(m * hoRatio)errorCount = 0.0for i in range(numTestVecs):# 使用分类器分类classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :],\datingLables[numTestVecs:m], 3)print("the classifier came back with:%d, the real answer is:%d" % (classifierResult, datingLables[i]))# 分类器分类数据与预测数据对比,最后计算错误率if(classifierResult != datingLables[i]): errorCount += 1.0print("the total error rate is:%f" % (errorCount/float(numTestVecs)))# 测试分类器
# datingClassTest()# 约会网站预测函数
def classifyPerson():# 预测结果是数字,对应成字符串resultList = ['not at all', 'in small doses', 'in large doses']# 输入飞行里程,冰淇淋量,游戏时间来进行预测percentTats = float(raw_input(\"percentage of time spent playing video games?"))ffMiles = float(raw_input("frequent filer miles earned per year?"))iceCream = float(raw_input("liters of ice cream consumed per year?"))# 数据读取datingDataMat, datingLables = file2matrix('datingTestSet.txt')# 数据归一化normMat, ranges, minValues = autoNorm(datingDataMat)# 输入的数据格式整合,适配机器学习算法inArr = array([ffMiles, percentTats, iceCream])# 对数据进行归一化处理,并进行预测classifierResult = classify0((inArr - minValues)/ranges, normMat, datingLables, 3)print("You will probably like this person:", resultList[classifierResult - 1])if __name__ == '__main__':classifyPerson()
从简单的开始,一步步进步吧。如有错误,欢迎指正,积极学习。
这篇关于【机器学习实战】K-近邻算法之海伦约会的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!