机器学习实战 第二章

1.算法概述

K-近邻算法的基本原理是基于每个数据点在数据空间中的分布,当有新数据点需要分类时,通过寻找与其最相似的K个数据点,并根据这K个数据点的多数投票或者加权投票来决定新数据点的分类,举个例子,假设我们有以下数据集:

  • 数据点1: (0.5, 0.8, 0)
  • 数据点2: (1.0, 0.7, 0)
  • 数据点3: (0.5, 0.6, 1)
  • 数据点4: (1.0, 0.9, 1)

同时,我们有一个新的数据点(0.7, 0.8)

首先,对于每个数据点,计算新数据点与它们的特征之间的距离,选取距离最小的那个数据点;然后,我们选择距离最小的K个数据点,即距离新数据点最近的K个数据点。对于上面的例子,距离新数据点最近的3个数据点分别是数据点1, 数据点4, 和数据点2;最后统计这K(3)个数据点中每个类别的数量,然后根据同类别数量多少来决定新数据点的分类。在上面的例子中,我们发现有1个数据点(数据点1)的类别是0,有2个数据点(数据点2和数据点4)的类别是1,所以新数据点的类别应该是1

以下是上述例子的示意图:
请添加图片描述

2.算法代码说明

先给出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
    sortedDistIndicies = distances.argsort()     
    classCount={}          
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
    sortedClassCount = sorted(classCount.iteritems(),
    key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

classify0函数有四个参数,分别是输入向量inX、训练样本dataSet、标签向量labels和选择数目k,分别用第一部分的概念表示:新的数据点(0.7, 0.8)、 四个初始数据点集合、训练数据集中每个数据点对应的标签0/1、k=3

整体算法可以分成三个部分:距离计算、选择距离最小的k个点、排序

  • 距离计算
# 获取数据集中的数据点数量
dataSetSize = dataSet.shape[0]
# 通过从数据集中的每个数据点中减去输入数据点创建一个名为diffMat的矩阵。tile函数用于将inX复制并匹配dataSet的形状
diffMat = tile(inX, (dataSetSize,1)) - dataSet
# 对diffMat中的每个元素进行平方,计算平方差值
sqDiffMat = diffMat**2
# 沿着轴1对平方差值进行求和,计算平方欧氏距离
sqDistances = sqDiffMat.sum(axis=1)
# 对每个平方距离取平方根,得到欧氏距离
distances = sqDistances**0.5
  • 选择k个点
# 获取将距离按升序排序的索引
sortedDistIndicies = distances.argsort()  
# 初始化一个空字典,用于计算每个类别标签的出现次数   
classCount={}       
# 遍历sortedDistIndicies的前k个元素   
for i in range(k):
    voteIlabel = labels[sortedDistIndicies[i]] # 获取相应索引处的标签
    classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #增加对应类别标签的计数
  • 排序
# 对类别计数进行排序,按照计数值降序排序
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True)
# 返回排序后的类别计数中的第一个类别
return sortedClassCount[0][0]

3.用KNN算法改进约会网站配对效果

以约会网站配对效果为例,训练样本dataSet包含三个特征:每年飞行常客里程数、玩游戏耗费百分比、每周消费的冰激凌数;标签向量labels为不喜欢、一般、喜欢三个

基本流程

  1. 收集数据:提供含有数据的文件,如txt、xlsx、csv等
  2. 准备数据:使用Python解析数据文件
  3. 分析数据:使用Matplotlib画二维扩散图
  4. 测试算法:使用数据中的部分数据作为测试样本
    测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误
  5. 使用算法:产生简单的命令行程序,然后可以输人一些特征数据以判斯对方是否为自己喜欢的类型

准备数据

将数据处理前需要将数据的格式进行转换,因此要定义一个file2matrix函数将输入的文件名字符串转换为样本矩阵和类标签向量

def file2matrix(filename):
    fr = open(filename) # 打开,目标文件
    numberOfLines = len(fr.readlines()) # 得到文件中的数据行数
    returnMat = zeros((numberOfLines,3)) # 准备零矩阵存储要返回的数据
    classLabelVector = [] # 用来存储标签
    fr = open(filename)
    index = 0
    for line in fr.readlines():
        line = line.strip() # 去除行两端的空白字符
        listFromLine = line.split('\t') # 将行按制表符\t进行分割
        returnMat[index,:] = listFromLine[0:3] # 将列表中前三个元素赋值给矩阵的当前行
        classLabelVector.append(int(listFromLine[-1]))# 将列表的最后一个标签转换为整数类型,并添加到标签向量
        index += 1
    return returnMat,classLabelVector # 返回结果

创建散点图

使用matplotlib库能使数据可视化,方便查看,以下是三个特征的相关可视化代码

import matplotlib
import matplotlib.pyplot as plt
# 绘图中文输出设置
plt.rc("font",family='SimHei')  # 对中文字体进行指定
plt.rc('axes',unicode_minus=False)  # 指定中文字体后正常显示负号
plt.tight_layout()  # 紧密布局,使标题、文本等完整显示

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(Data[:,1], Data[:,2],15.0*array(Labels),15.0*array(Labels))

plt.xlabel('玩游戏时间所占百分比')
plt.ylabel('每周消费的冰激凌公升数')
plt.show()

请添加图片描述
图上的三种不同颜色尺寸的数据点分别表示了三个样本的分布情况

归一化数据

使用当前值减去最小值,并除以取值范围

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  # 在测试样本中的占比,也就是10%
    # 从文件中加载数据集
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    # 对数据进行归一化处理
    normMat, ranges, minVals = autoNorm(datingDataMat)
    # 计算数据的总行数
    m = normMat.shape[0]
    # 计算测试样本的数量,即实际测试样本数量
    numTestVecs = int(m * hoRatio)
    # 初始化错误计数器,用于统计测试错误率
    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)

请添加图片描述
经过测试,错误率只有6.6%,算是不错的结果

使用算法

创建一个综合的使用函数,通过输入三个特征量来判断是否符合约会的概率

def classifyPerson():
	# 创建三个结果标签向量的列表
    resultList = ['不可能', '小概率', '大概率']
    percentTats = float(input(\"玩游戏耗费百分比?"))
    ffMiles = float(input("每年飞行常客里程数?"))
    iceCream = float(input("每年消费的冰激凌数?"))
    # 使用file2matrix函数得到数据和数据标签
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    # 使用autoNorm函数得到归一化的数据、取值范围和最小值
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = np.array([ffMiles, percentTats, iceCream, ])
    # 再使用classify0函数得到结果
    classifierResult = classify0((inArr - \minVals)/ranges, normMat, datingLabels, 3)
    print("你可能喜欢这个人的概率: %s" % resultList[classifierResult - 1])

结果如下图所示
请添加图片描述

4.总结

本章学习了KNN算法,它是一种基于实例的学习方法。这种方法在处理分类和回归问题时都表现良好。KNN的基本思想是根据待分类数据(测试数据)与已知数据的相似度(距离度量)来确定它的分类。在实际应用中,通常选择一个合适的“邻居”数量K来平衡计算复杂度和预测精度。以下是对KNN算法的总结:

  1. 计算距离:KNN算法依赖于距离度量来计算待分类数据与已知数据之间的相似度
  2. 选择邻居:确定距离待分类数据最近的K个已知数据,这些数据即为待分类数据的“邻居”
  3. 投票表决:对于每个邻居,根据其所属类别的数量对其进行“投票”。预测时,选择票数最多的类别作为待分类数据的分类结果
  4. 计算权重:为避免“少数服从多数”的情况,可以为每个邻居分配一个权重,以降低预测错误的影响
  5. 分类结果:根据权重加权后的邻居投票结果,计算预测类别的预测概率。预测概率最高的类别即为待分类数据的预测类别
  6. 预测精度:预测精度是一个评估KNN算法性能的关键指标。预测精度可以通过平均准确率、AUC-ROC曲线等方法进行评估,在本章中使用了错误率来表示
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值