机器学习——KNN算法实战

一、KNN算法介绍
1.1 KNN算法概述
KNN(K-Nearest Neighbor)算法是机器学习算法中最基础、最简单的算法之一,是一种分类和回归的统计方法,是监督学习。KNN通过测量不同特征值之间的距离来进行分类。所谓k近邻,就是k个最近的邻居的意思,说的是每个样本类别都可以用它最接近的k个邻居的类别来代表。 就比如:判断一个人的人品好坏,只需要观察与他来往最密切的几个人的人品好坏就可以得出,即“近朱者赤,近墨者黑"。

举个例子:

图中绿色的点是我们要预测的那个点,假设K=3。那么KNN算法就会找到与它距离最近的三个点,看看哪种类别多一些,例子中蓝色三角形有两个,红色圆有一个,那么新来的绿色点就归类到蓝三角了。

二、KNN算法原理
1、KNN算法的基本原理

       对于一个新的未知样本,通过计算该样本与训练集中所有样本之间的距离,找到与之距离最近的K个样本,然后根据这K个最近邻样本的类别或数值属性来判断新样本的类别或预测其数值。


2、距离度量方法

       KNN算法中常用的距离度量方法主要包括以下几种:

欧氏距离(Euclidean Distance):这是最常见的距离度量方式,用于在二维或多维空间中计算两个样本之间的直线距离。在二维空间中,欧氏距离的计算公式为:

其中,(x1, y1)和(x2, y2)分别表示两个样本的坐标。欧氏距离提供了一种直观的相似性度量,特别适用于处理连续的实值特征。


曼哈顿距离(Manhattan Distance):在二维空间中,曼哈顿距离的计算公式为:

这个距离可以看作是两个样本在坐标轴上的距离之和。


闵可夫斯基距离(Minkowski Distance):这是一种通用的距离度量方式,可以根据具体的情况调整为欧氏距离、曼哈顿距离或切比雪夫距离。在二维空间中,闵可夫斯基距离的计算公式为:

其中,p是一个可调的参数。

       除了上述三种常用的距离度量方法外,还有切比雪夫距离、马氏距离、巴氏距离等其他的距离度量方法。这些距离度量方法的选择取决于数据的特性和问题的需求。博主在以下分类器中使用的是欧式距离。


3、K值的选择

      KNN算法中的K值选择是一个核心且关键的问题,因为它直接影响到算法的性能和预测结果。K值决定了用于分类或预测的最近邻居的数量。

K值过小:较小的K值意味着算法在预测时会更加关注与输入实例较近或相似的训练实例,这样“学习”的近似误差会减小,但估计误差可能会增大。换句话说,模型会变得相对复杂,且容易发生过拟合,即模型对训练数据的拟合度过高,导致对未知数据的预测能力下降。

K值过大:而较大的K值则会使算法在预测时考虑更大范围内的训练实例,这有助于减少估计误差,但可能会导致近似误差增大。这是因为与输入实例较远或不相似的训练实例也可能对预测结果产生影响,从而增加误判的可能性。此时,模型会变得相对简单。

       因此,在选择K值时,需要权衡近似误差和估计误差。通常,K值的选择没有固定的规则,而是需要通过交叉验证等方法来确定。交叉验证将数据集划分为训练集和验证集,然后针对不同的K值进行模型训练和验证,选择在验证集上表现最好的K值作为最终的选择。

       在实际应用中,K值一般取一个比较小的数值,并通过交叉验证等方法进行优化。这样可以在保证模型复杂度的同时,尽可能提高预测精度。需要注意的是,K值的选择并不是一成不变的,它需要根据具体的数据集和问题进行调整。

三、KNN算法实例 ——使用K-近邻算法根据鸢尾花外形进行分类

2.1数据集介绍

鸢尾花数据集共收集了三类鸢尾花,即Setosa鸢尾花、Versicolour鸢尾花和Virginica鸢尾花,包括4个属性,分别为花萼的长、花萼的宽、花瓣的长和花瓣的宽。每个类别50个样本。

2.2代码介绍
2.2.1初始化数据集和数据集划分
读取数据集文件中的数据到列表中,因为数据集中单个数据特征是以,分隔开,所以按,分隔为一个样本数据再存到数组data_temp中

# 初始化数据集
def init(adder):
    f = open(adder)  # 打开数据文件文件
    lines = f.readlines()  # 把全部数据文件读到一个列表lines中
    data_test = []
    for line in lines:  # 把lines中的数据逐行读取出来
        data_test.append(line.rstrip('\n').split(','))
    data_temp = np.array(data_test)
    return data_temp
使用numpy库把数据打乱,再按照指定比例把数据划分为数据集和验证集,用在确定模型准确率的时候使用

# 将数据随机分为训练集和测试集
def split_data(data, train_ratio):
    np.random.shuffle(data)#打乱数据
    train_size = int(len(data) * train_ratio)
    train_data = data[:train_size]#按照比例划分
    test_data = data[train_size:]
    return train_data, test_data
2.2.2计算待测样本和数据集距离,并从距离最近的K个数据中来预测数据类别
通过数据特征来计算待测样本和数据集中每个数据的欧氏距离并存储,然后从存储的数据数组中按距离排序,再从最近的K个数据中找出出现次数最多的数据类别作为预测结果返回。

# 计算两个样本之间的距离
def distance_out(need_judge, source):
    #类型转化
    source = source.astype(float)
    x = need_judge[0] - source[0]
    y = need_judge[1] - source[1]
    z = need_judge[2] - source[2]
    r = need_judge[3] - source[3]
    #计算欧氏距离
    distance = math.sqrt((x ** 2 + y ** 2 + z ** 2 + r ** 2))
    return distance
# 计算待测样本与训练样本的距离
def argue(need_judge, data_temp):
    # 获取训练样本数量
    times = data_temp.shape[0]
    # 初始化一个数组来存储距离和类别信息
    distance_all = np.zeros((1, 2))
    for time in range(times):
        #遍历样本,计算距离并存储距离和样本的类别
        temp = data_temp[time]
        temp_temp = temp[0:4]
        distance_temp = distance_out(need_judge, temp_temp)
        new_row = np.array((distance_temp, temp[4]), dtype=str)
        new_row = np.array(new_row).reshape((1, 2))
        #把包含距离和数据类别信息的新数组作为新的一行添加到distance_all中
        distance_all = np.r_[distance_all, new_row]
    return distance_all
# 根据K近邻算法找到合适的类别
def find_suitable(distance_all, K):
    # 从距离数组中去除第一行(因为它是零行)
    new_distance_all = distance_all[1:]
    # 按距离对样本进行排序并获取索引
    sorted_indices = np.argsort(new_distance_all[:, 0])
    # 获取前K个最近的样本
    min_k_rows = new_distance_all[sorted_indices[:K], :]
    # 获取这些样本的类别
    min_name = min_k_rows[:, 1]
    # 统计类别出现的次数
    counts = Counter(min_name.tolist())
    # 找到出现次数最多的类别
    most_common_str = max(counts, key=counts.get)
    # 返回出现次数最多的类别
    return most_common_str

2.2.3预测结果和使用验证集计算准确率
通过计算待测样本和数据样本之间的距离并找到最合适的类别,以及把数据集划分为训练集和验证集来计算准确率

# 预测结果
def train(need_judge, K, data):
    # 计算待测样本与训练样本的距离并找到最合适的类别
    distance_all = argue(need_judge, data)
    kinds = find_suitable(distance_all, K)
    return kinds
 
# 从样本数据中获取特征值
def get_point(data):
    #print("Data:", data)
    #print("Data size:", len(data))
    return data[0], data[1], data[2], data[3]
 
# 计算模型的准确率
def ua(K, train_ratio):
    # 初始化数据集
    data = init(data_address)
    # 划分训练集和测试集
    train_data, test_data = split_data(data, train_ratio)
    # 获取测试集的数量
    data_number = test_data.shape[0]
    right = 0
    faid = 0
    count = 0
    # 对每个测试样本进行预测
    for temp in test_data:
        count += 1
        print("正在进行第", count, "次验证")
        # 获取特征值
        x, y, z, r = get_point(temp)
        need_judge = np.array([x, y, z, r], dtype=float)
        # 预测样本类别
        judge = train(need_judge, K, train_data)
        # 统计预测正确和错误的样本数量
        if judge == temp[4]:
            right += 1
        else:
            faid += 1
    # 计算准确率
    right_faid = (float)((right) / (test_data.shape[0]))
    print("当前模型的成功率是百分之", right_faid * 100)

2.2.4交互界面
写了一点简单交互,让用户选择是进行预测还是进行计算准确率以及是否退出,在进行预测和准确率计算时都有让用户输入K值,并且在计算准确率时还有让用户指定训练集和验证集的比例。

if __name__ == '__main__':
    while True:
        choose = input("1.查看准确率\n"
                       "2.开始预测\n"
                       "3.退出\n")
 
        if choose == '1':
            K = int(input("输入K: "))
            train_ratio = float(input("输入训练集比例(例如0.8表示80%): "))
            ua(K, train_ratio)
        elif choose == '2':
            K = int(input("输入K: "))
            x = float(input("输入特征值1: "))
            y = float(input("输入特征值2: "))
            z = float(input("输入特征值3: "))
            r = float(input("输入特征值4: "))
            need_judge = np.array([x, y, z, r], dtype=float)
            data = init(data_address)
            kinds = train(need_judge, K, data)
            print("根据您输入的预测值,模型猜测结果是", kinds)
        elif choose == '3':
            break
        else:
            print("输入错误")
    print("欢迎下次使用")
2.2.5遇到的问题
在写train函数时,考虑的是在预测时的使用,所以一开始写的下面这个样子

# 预测结果
def train(need_judge, K):
    # 初始化数据集
    data = init(data_address)
    # 计算待测样本与训练样本的距离并找到最合适的类别
    distance_all = argue(need_judge, data)
    kinds = find_suitable(distance_all, K)
    return kinds
导致在计算准确率中的验证时会有冲突,所以最后使用这个版本,而在预测时选择重新初始化一次数据来传参。

# 预测结果
def train(need_judge, K, data):
    # 计算待测样本与训练样本的距离并找到最合适的类别
    distance_all = argue(need_judge, data)
    kinds = find_suitable(distance_all, K)
    return kinds
2.3结果截图展示

四、KNN算法优缺点及应用场景
 1、KNN算法的优点

简单易懂:KNN算法的原理相对直观和简单,容易理解。它基于实例的学习,不需要建立复杂的模型,只需通过计算不同样本之间的距离来进行分类。

适用于多分类问题:KNN算法能够处理多分类问题,而不仅仅是二分类。对于具有多个类别的数据集,KNN算法能够有效地进行分类。

对异常值不敏感:由于KNN算法是基于实例的学习,它通常不会因为数据集中的个别异常值而受到太大影响。这是因为算法在分类时会考虑多个邻近样本的投票结果,从而减少了单个异常值对分类结果的影响。

无需特征提取:KNN算法可以直接在原始数据上进行操作,无需进行复杂的特征提取或转换。这使得它在某些情况下能够更直接地利用数据的原始信息。

灵活性强:KNN算法中的K值是一个可以调整的参数,通过调整K值,你可以控制算法的复杂度和分类的精度。此外,你还可以使用不同的距离度量方式(如欧氏距离、曼哈顿距离等)来适应不同的数据特性。

2、KNN算法的缺点

计算量大:KNN算法在分类时需要计算待分类样本与所有训练样本之间的距离,因此当训练集很大时,计算量会非常大,导致分类速度变慢。这在大规模数据集上尤为明显,可能会导致实时分类变得不可行。

对样本分布敏感:KNN算法基于样本之间的距离进行分类,因此样本的分布情况对分类结果有很大影响。如果样本分布不均匀,或者存在噪声和异常值,可能会导致分类效果不佳。

k值选择困难:KNN算法中的k值是一个重要参数,它决定了用于分类的邻近样本的数量。选择合适的k值是一个挑战,因为不同的k值可能导致不同的分类结果。如果k值太小,模型容易受到噪声和异常值的影响;如果k值太大,模型可能过于平滑,忽略了一些重要的局部信息。

对特征尺度敏感:KNN算法使用距离度量来判断样本之间的相似性,因此特征的尺度对分类结果有很大影响。如果不同特征之间的尺度差异很大,可能会导致距离计算不准确,从而影响分类效果。在实际应用中,通常需要对特征进行标准化或归一化处理。

不适用于高维数据:在高维空间中,样本之间的距离度量可能变得不再有效,因为所有样本之间的距离可能都变得很接近。这会导致KNN算法在高维数据上的性能下降,分类效果可能不如其他算法。

存储空间需求大:KNN算法需要存储整个训练数据集,以便在分类时计算距离。因此,当训练集很大时,存储空间的需求也会很大。

3、KNN算法的应用场景

      KNN算法(K-最近邻算法)是一种基于实例的学习算法,具有广泛的应用场景。以下是KNN算法的一些主要应用场景:

图像分类与识别:KNN算法在图像分类和识别中表现出色。例如,它可以用于识别图像中的人脸、车牌等,或者对图像进行自动分类。通过计算图像之间的距离,将待识别图像归类到训练集中与之最相似的类别,实现图像识别。


文本分类:在文本处理中,KNN算法可以用于文本分类任务,如垃圾邮件过滤、情感分析等。算法可以将文本数据转化为高维向量空间,并通过计算文档向量之间的相似度来实现分类。

推荐系统:KNN算法在推荐系统中具有广泛应用,特别是在用户协同过滤方面。通过计算用户之间的相似度,可以找到与目标用户最相似的K个用户,并将这些用户喜欢的物品推荐给目标用户。这种方法有助于提供个性化的推荐,提高用户满意度。

这次参与机器学习的实验标志着我的一个新的开始,尽管许多机器学习的基础概念对我来说还显得模糊不清,对于Python编程语言的掌握也不算特别精通,整个实验过程对我而言充满了挑战。尽管如此,我还是坚持将所有的代码一一输入,并在此过程中获得了大量的学习与成长。我期待着下一次的实验,不仅希望能够更加顺利地完成任务,还希望能深入学习更多机器学习的知识,同时也增强自己的实践操作能力,让自己在机器学习的道路上更进一步。

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值