【机器学习实战】K-近邻算法之海伦约会

2024-03-10 13:40

本文主要是介绍【机器学习实战】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=(oldvaluemin)/(maxmin)
数值归一化范围-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(oldvaluemin)/(maxmin)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
可以看出,构建的分类器的错误率是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-近邻算法之海伦约会的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/794407

相关文章

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

Spring Security基于数据库的ABAC属性权限模型实战开发教程

《SpringSecurity基于数据库的ABAC属性权限模型实战开发教程》:本文主要介绍SpringSecurity基于数据库的ABAC属性权限模型实战开发教程,本文给大家介绍的非常详细,对大... 目录1. 前言2. 权限决策依据RBACABAC综合对比3. 数据库表结构说明4. 实战开始5. MyBA

springboot+dubbo实现时间轮算法

《springboot+dubbo实现时间轮算法》时间轮是一种高效利用线程资源进行批量化调度的算法,本文主要介绍了springboot+dubbo实现时间轮算法,文中通过示例代码介绍的非常详细,对大家... 目录前言一、参数说明二、具体实现1、HashedwheelTimer2、createWheel3、n

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Pandas使用SQLite3实战

《Pandas使用SQLite3实战》本文主要介绍了Pandas使用SQLite3实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学... 目录1 环境准备2 从 SQLite3VlfrWQzgt 读取数据到 DataFrame基础用法:读

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Python实战之屏幕录制功能的实现

《Python实战之屏幕录制功能的实现》屏幕录制,即屏幕捕获,是指将计算机屏幕上的活动记录下来,生成视频文件,本文主要为大家介绍了如何使用Python实现这一功能,希望对大家有所帮助... 目录屏幕录制原理图像捕获音频捕获编码压缩输出保存完整的屏幕录制工具高级功能实时预览增加水印多平台支持屏幕录制原理屏幕

最新Spring Security实战教程之Spring Security安全框架指南

《最新SpringSecurity实战教程之SpringSecurity安全框架指南》SpringSecurity是Spring生态系统中的核心组件,提供认证、授权和防护机制,以保护应用免受各种安... 目录前言什么是Spring Security?同类框架对比Spring Security典型应用场景传统