机器学习算法——k-近邻算法

前言

        K近邻算法是一种简单而有效的算法,它不需要对数据进行显式的训练,因此适用于小规模数据集和非线性问题。是学习和理解机器学习基础知识的重要算法之一,也可以作为其他机器学习算法的基准进行比较和评估。

一、k-近邻算法     

1、介绍        

        k-近邻算法(k-Nearest Neighbour algorithm),又称为KNN算法。KNN的工作原理:给定一个已知标签类别的训练数据集,输入没有标签的新数据后,在训练数据集中找到与新数据最邻近的k个实例,如果这k个实例的多数属于某个类别,那么新数据就属于这个类别。

可以简单理解为:由那些离X最近的k个点来投票决定X归为哪一类

        下面我们通过一个简单的小例子来了解一下k-近邻算法:

                                       

图1-1

        图1-1是一个很基础的knn算法模型。有两类不同的样本数据,分别用蓝色的小正方形和红色的小三角形表示,而图正中间的那个绿色的国所标示的数据则是待分类的数据。

        问题:图中的绿色的圆属于哪一类?

        如果K=3,绿色圆点的最近的3个邻居是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
        如果K=5,绿色国点的最近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。

2、距离公式

        在实现k-近邻算法中,我们经常要算出未知类别与样本中其它已知类别的距离,将它们进行排序,然后根据k的取值来判断未知样本的类别。

        距离公式有很多个,本篇博客着重使用欧式距离公式。

欧式距离公式(Euclidean Distance)

        (1)、二维平面:

假设二维平面有两个点:a(x_{1},y_{1}),b(x_{2},y_{2})

                               

        (2)、三维平面:

假设三维平面有两个店:a(x_{1},y_{1},z_{1}),b(x_{2},y_{2},z_{1})

     

        (3)、n维平面:

n维空间有两个点:a(x_{11},x_{12},...x_{1n}),b(x_{21},x_{22},...x_{2n})

        接下来通过一个小例子来使用距离公式解决问题:

电影名称打斗镜头接吻镜头电影类型
泰坦尼克号1101爱情片
后来的我们589爱情片
前任31297爱情片
战狼1129动作片
战狼21085动作片
唐人街探案1158动作片
2467

        我们将表中已有数据作为样本数据,打斗镜头和接吻镜头作为数据特征。我们用图像的方式显示出来:

图1-2

        现在我们计算未知电影与样本集中其它电影的距离,计算结果位于下面表格:

电影名称与未知电影的距离
泰坦尼克号20.5
后来的我们18.7
前任319.8
战狼115.3
战狼2117.4
唐人街探案118.9

        现在我们得到了样本集中所有电影与位置电影的距离,按照距离递增排序,可以找到k个距离最近的电影。

        比如说我们假定k=3,则最近的电影分别为:泰坦尼克号、后来的我们、前任3。因此未知电影属于爱情片。因此k的取值在k-近邻算法中是非常重要的,它间接影响到了未知样本类别的判断,所以我们对于k的取值一定要谨慎。

3、k-近邻算法的一般流程

流程:

(1)、收集数据:可以使用任何方法

(2)、准备数据:距离计算所需要的数值

(3)、分析数据:可以使用任何方法

(4)、训练算法:此步骤不适用于k-近邻算法

(5)、测试算法:计算错误率

(6)、使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入的数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。

二、k-近邻算法案例——医用判断良性恶性肿瘤

        接下来我们通过一个简单案例来深入学习k-近邻算法

问题背景

       医学上判断肿瘤需要通过radius (半径), texture(纹理), perimeter(周长), area(面积), smoothness(光滑度), symmetry(对称性), compactness(紧凑度), fractal_dimension(分形维度)等数据来判断肿瘤是否为恶性,现在有一组数据,请用knn方法来进行分析肿瘤的良性恶性(良性肿瘤用“B”,恶性肿瘤用“M”表示)

数据展示

iddiagnosis_resultradiustextureperimeterareasmoothnesscompactnesssymmetryfractal_dimension
1M23121519540.1430.2780.2420.079
2B91313313260.1430.0790.1810.057
3M212713012030.1250.160.2070.06
4M1416783860.070.2840.260.097

数据处理

# 读取csv文件,将每行数据存储为一个字典,并将所有字典存储到列表中
with open('Prostate_Cancer.csv') as file:
    reader = csv.DictReader(file)
    datas = [row for row in reader]

# 对数据进行随机排序
random.shuffle(datas)

# 选取1/3的数据作为测试集,其余作为训练集
n = len(datas)//3
test_set = datas[0:n]
train_set = datas[n:]

计算距离

# 返回两个数据点之间的欧几里得距离
def distance(d1,d2):
    res = 0
    for key in ("radius","texture","perimeter","area",
                "smoothness","compactness","symmetry","fractal_dimension"):
        res += (float(d1[key])-float(d2[key])) ** 2
    return res ** 0.5

knn分类器实现

# 设定k值
k = 4

# KNN分类器
def knn(data):
    # 计算测试数据和每个训练数据之间的距离,并将结果存储到一个列表中
    res = [
        {"result":train["diagnosis_result"],"distance":distance(data,train)}
        for train in train_set
    ]
    
    # 将结果按照距离从小到大排序
    res = sorted(res, key=lambda item:item['distance'])
    # 取前K个距离最近的数据
    res2 = res[0:k]
    # 加权平均,将离测试数据更近的数据点所属类别的贡献权重更高
    result = {'B':0,'M':0}
    # 计算所有数据点到测试数据的距离之和
    sum_dist = 0
    for r1 in res2:
        sum_dist += r1['distance']
    
    # 逐个分类累加贡献权重(结果为'B'或'M')
    for r2 in res2:
        result[r2["result"]] += 1-r2["distance"]/sum_dist
    
    if result['B'] > result['M']:
        return 'B'
    else:
        return 'M'

测试集预测

# 求正确率
correct = 0
for test in test_set:
    result = test['diagnosis_result']
    result2 = knn(test)
    # 如果预测结果正确,计数器加1
    if result == result2:
        correct = correct + 1;
        
print("正确率为:" + str(correct/len(test_set)))

预测结果

图2-1

        多次改变k值可以发现,k取5的时候正确率最高。

完整代码

import csv
import random

# 读取csv文件,将每行数据存储为一个字典,并将所有字典存储到列表中
with open('Prostate_Cancer.csv') as file:
    reader = csv.DictReader(file)
    datas = [row for row in reader]

# 对数据进行随机排序
random.shuffle(datas)

# 选取1/3的数据作为测试集,其余作为训练集
n = len(datas)//3
test_set = datas[0:n]
train_set = datas[n:]

# 返回两个数据点之间的欧几里得距离
def distance(d1,d2):
    res = 0
    for key in ("radius","texture","perimeter","area",
                "smoothness","compactness","symmetry","fractal_dimension"):
        res += (float(d1[key])-float(d2[key])) ** 2
    return res ** 0.5

# 设定k值
k = 5

# KNN分类器
def knn(data):
    # 计算测试数据和每个训练数据之间的距离,并将结果存储到一个列表中
    res = [
        {"result":train["diagnosis_result"],"distance":distance(data,train)}
        for train in train_set
    ]
    
    # 将结果按照距离从小到大排序
    res = sorted(res, key=lambda item:item['distance'])
    # 取前K个距离最近的数据
    res2 = res[0:k]
    # 加权平均,将离测试数据更近的数据点所属类别的贡献权重更高
    result = {'B':0,'M':0}
    # 计算所有数据点到测试数据的距离之和
    sum_dist = 0
    for r1 in res2:
        sum_dist += r1['distance']
    
    # 逐个分类累加贡献权重(结果为'B'或'M')
    for r2 in res2:
        result[r2["result"]] += 1-r2["distance"]/sum_dist
    
    if result['B'] > result['M']:
        return 'B'
    else:
        return 'M'
        
# 求正确率
correct = 0
for test in test_set:
    result = test['diagnosis_result']
    result2 = knn(test)
    # 如果预测结果正确,计数器加1
    if result == result2:
        correct = correct + 1;
        
print("正确率为:" + str(correct/len(test_set)))

三、总结

       KNN算法是一种简单但有效的分类算法,尤其适用于样本数据不平衡、数据分布不规则的情况。它为初学者提供了一个很好的入门点,并且在一些小规模的问题中具有良好的效果。对于分类问题,通过投票的方式确定待分类样本所属的类别;对于回归问题,采用平均值等方法来预测待分类样本的数值。K近邻算法的优点在于简单易懂,且对于非线性、非参数化模型适用。但是它的缺点是需要大量的计算和存储空间,在处理高维度数据时效果较差,并且对于样本分布不均匀及噪声比较大的数据集容易受到干扰,因此在使用过程中需要多加注意。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿K还阔以

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值