第二章-K近邻算法

2.1K-近邻算法概述

        knn的基本思想:需要确定一个样本A的类别,可以计算出它与所有训练样本的距离,然后找出和该样本距离最小的k个样本,对这k个样本的类别进行统计,样本数最多的那个类别就是我们A的类别了。简单的说,K-近邻算法采用测量不同特征值之间的距离方法进行分类。

2.1.1K-近邻算法优缺点

优点:精度高、对异常值不敏感、无数据输入假定.

缺点:计算复杂度高、空间复杂度高。

适用数据范围:数值型和标称型。

2.1.2K-近邻算法工作原理

        存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前K个最相似的数据,这就是K-近邻算法的K的出处,通常K是不大于20的整数。最后,选择K个最相似数据中出现次数最多的分类,作为新数据的分类。

2.1.3K-近邻算法的一般流程

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

(2)准备数据:距离计算机所需要的数值,最好是结构化的数据格式。

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

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

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

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

2.2预测算法流程

         knn没有需要求解的参数,没有训练过程,参数k由人工指定。对于分类问题,给定n个训练样本(xi,yi),xi为特征向量,yi为标签值。设定合适的参数k,类别数为c,待分类的样本为x。算法的预测流程如下:
(1)在训练数据中找出离x最近的k个样本,假设这些样本的集合为N。
(2)统计集合N中每类样本的数量Ci,i=0,1,2…,c-1。
(3)最终分类结果为argmaxCi,即样本数最多的那个类别。
在实现的时候还可以考虑样本的权重,即每个样本带有不同的样本权重,比如说这个权重和每个类样本数在总样本数占比有关,这种方法成为带权重的k近邻算法。

2.3KNN算法的应用场景


KNN算法的优点包括简单易懂、无需训练过程、适用于多类别问题等。KNN算法在许多领域中都有广泛的应用,KNN算法常见的应用场景如下:

分类问题:KNN算法可以用于分类问题,如文本分类、图像分类、语音识别等。通过比较待分类数据点与已知数据点之间的相似性,KNN可以将新的数据点分配到最相似的类别中。

回归问题:KNN算法也可以用于回归问题,如房价预测、股票价格预测等。通过计算最近邻数据点的平均值或加权平均值,KNN可以预测待分类数据点的数值属性。

推荐系统:KNN算法可以应用于推荐系统,根据用户之间的相似性来推荐相似兴趣的物品。通过比较用户之间的行为模式或兴趣偏好,KNN可以找到与当前用户最相似的一组用户,并向其推荐相似的物品。

异常检测:KNN算法可以用于检测异常数据点,如信用卡欺诈、网络入侵等。通过计算数据点与其最近邻之间的距离,KNN可以识别与大多数数据点不同的异常数据点。

文本挖掘:KNN算法可以用于文本挖掘任务,如文本分类、情感分析等。通过比较文本之间的相似性,KNN可以将新的文本数据点归类到相应的类别中。

图像处理:KNN算法可以应用于图像处理领域,如图像识别、图像检索等。通过比较图像之间的像素值或特征向量,KNN可以识别和检索相似的图像。

2.4常用距离公式

2.4.1曼哈顿距离

d=\left | x_{1}-x{_2{}} \right |+\left | y_{1}-y{_2{}} \right |

2.4.2欧式距离

d=\sqrt{(x_{1}-x_{2})^{2}+(y_{1}-y_{2})^{2}}

2.5电影分类的例子

2.5.1数据的读取

data = pd.read_csv(r"./files/FBlocation/train.csv")
print(data.head())

2.5.2处理数据

 
    data.query("x > 1.0 & y < 1.25 & y > 2.5 & y < 2.75")
 
 
    time_value = pd.to_datetime(data["time"],unit="s") #秒
    print(time_value)
 
    time_value =  pd.DataFrame(time_value) #年月日时分秒等变为{"year":2019,"month":01} 这样的字典格式数据
 
 
    data["day"] = time_value.day
    data["hour"] = time_value.hour
    data["weekday"] = time_value.weekday
    #删除一些特征
    data = data.drop(["time"],axis=1) 
 
    place_count = data.groupby("place_id").count() #根据place_id分组,统计次数
    tf = place_count[place_count.row_id > 3].reset_index() 
    data = data[data["place_id"].isin(tf.place_id)] #如果place_id > 3的数据保存,小于则去掉
 
 
    y = data["place_id"]
    x = data.drop(["place_id"],axis=0.25)
 
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.25)

 

2.5.3我们先不对数进行标准化,直接进行算法预测

    knn = KNeighborsClassifier(n_neighbors=5)
    #fit,predict,score
    knn.fit(x_train,y_train)
    #得出预测结果
    y_predict= knn.predict(x_test)
    print("预测的目标签入位置为",y_predict)
    #得出正确率
    correctRate = knn.score(x_test,y_test)
    print(correctRate) 

 签入位置和预测正确率: 

很差 

2.5.4标准化后再进行算法预测

  
    std = StandardScaler()
 
    x_train_std = std.fit_transform(x_train)
    # y_train_std = std.fit_transform(y_train)#已经fit转换过了,可以直接transform()
    y_train_std = std.transform(y_train)
 
    knn = KNeighborsClassifier(n_neighbors=5)
    #fit,predict,score
    knn.fit(x_train_std,y_train_std)
 
    y_predict= knn.predict(x_test)
    print("预测的目标签入位置为",y_predict)
    #得出正确率
    correctRate = knn.score(x_test,y_test)
    print(correctRate) #

 签入位置和预测正确率:

正确率达到 40.5%  

2.6基于pytorch在MNIST数据集上实现数据分类

2.6.1获取MNIST数据集
import operator
import matplotlib.pyplot as plt
import numpy as np
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
 
 
batch_size = 100

train_dataset = datasets.MNIST(root='data',  # 选择数据的根目录
                            train=True,  # 选择训练集
                            transform=None,  # 不使用任何数据预处理
                            download=True)  # 从网络上下载图片
 test_dataset = datasets.MNIST(root='data',  # 选择数据的根目录
                           train=False,  # 选择测试集
                           transform=None,  # 不适用任何数据预处理
                           download=True)  # 从网络上下载图片

train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=True)

print("train_data:", train_dataset.data.size())
print("train_labels:", train_dataset.data.size())
print("test_data:", test_dataset.data.size())
print("test_labels:", test_dataset.data.size())

图片内容展示:

digit = train_loader.dataset.data[0] 
plt.imshow(digit, cmap=plt.cm.binary)
plt.show()
print(train_loader.dataset.targets[0])

 2.6.2准备数据


# 欧式顿距离计算
def e_distance(dataset_a, data_b):
    return np.sqrt(np.sum(((dataset_a - np.tile(data_b, (dataset_a.shape[0], 1))) ** 2), axis=1))
 
# 曼哈顿距离计算
def m_distance(dataset_a, data_b):
    return np.sum(np.abs(train_data - np.tile(test_data[i], (train_data.shape[0], 1))), axis=1)

2.6.3分析数据


def KNN_classify(k, dis_func, train_data, train_label, test_data):
    num_test = test_data.shape[0]  # 测试样本的数量
    label_list = []
    for idx in range(num_test):
        distances = dis_func(train_data, test_data[idx])
        nearest_k = np.argsort(distances)
        top_k = nearest_k[:k]  # 选取前k个距离
        class_count = {}
        for j in top_k:
            class_count[train_label[j]] = class_count.get(train_label[j], 0) + 1
        sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
        label_list.append(sorted_class_count[0][0])
 
    return np.array(label_list)
 
 
def get_mean(data):
    data = np.reshape(data, (data.shape[0], -1))
    mean_image = np.mean(data, axis=0)
    return mean_image
 
 
def centralized(data, mean_image):
    data = data.reshape((data.shape[0], -1))
    data = data.astype(np.float64)
    data -= mean_image  # 减去图像均值,实现领均值化
    return data
 
 
if __name__ == '__main__':

2.6.4训练数据

# 训练数据
    train_data = train_loader.dataset.data.numpy()
    train_data = train_data.reshape(train_data.shape[0], 28 * 28)
    
    # 归一化处理
    mean_image = get_mean(train_data)  # 计算所有图像均值
    train_data = centralized(train_data, mean_image)
    
    print('train_data shape:', train_data.shape)
    train_label = train_loader.dataset.targets.numpy()
    print('train_lable shape', train_label.shape)

2.6.5测试数据

# 测试数据
    test_data = test_loader.dataset.data[:1000].numpy()
    test_data = centralized(test_data, mean_image)
    test_data = test_data.reshape(test_data.shape[0], 28 * 28)
    print('test_data shape', test_data.shape)
    test_label = test_loader.dataset.targets[:1000].numpy()
    print('test_label shape', test_label.shape)

2.6.6训练得到准确率


    # 训练
    test_label_pred = KNN_classify(5, e_distance, train_data, train_label, test_data)
 
    # 得到训练准确率
    num_test = test_data.shape[0]
    num_correct = np.sum(test_label == test_label_pred)
    print(num_correct)
    accuracy = float(num_correct) / num_test
    print('Got %d / %d correct => accuracy: %f' % (num_correct, num_test, accuracy))

输出:

train_data: torch.Size([60000, 28, 28])
train_labels: torch.Size([60000, 28, 28])
test_data: torch.Size([10000, 28, 28])
test_labels: torch.Size([10000, 28, 28])
train_data shape: (60000, 784)
train_lable shape (60000,)
test_data shape (1000, 784)
test_label shape (1000,)
963
Got 963 / 1000 correct => accuracy: 0.963000

这个例子没有调参,所以咱们再看一个例子

2.7对鸢尾花iris数据集的k近邻算法分类及预测

2.7.1数据集简单介绍

· Iris (/ˈaɪrɪs/) 数据集是机器学习任务中常用的分类实验数据集,由Fisher在1936年整理。

· Iris :Anderson’s Iris data set, 中文名称:安德森鸢尾花数据集

· Iris 数据集一共包含150个样本,分3类,每类50个数据,每个数据包含4个特征。4个特征分别为: Sepal.Length(花萼长度)、Sepal.Width(花萼宽度)、Petal.Length(花瓣长度)、Petal.Width(花瓣宽度),特征值都为正浮点数,单位为厘米。根据4个特征预测鸢尾花属于 Iris Setosa(山鸢尾)、Iris Versicolour(杂色鸢尾),Iris Virginica(维吉尼亚鸢尾)
数据集结构与数据大致如下:

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
 
iris = load_iris()
print(iris)

其中前部分为特征: Sepal.Length(花萼长度)、Sepal.Width(花萼宽度)、Petal.Length(花瓣长度)、Petal.Width(花瓣宽度)后部分为标签:Iris Setosa(山鸢尾)、Iris Versicolour(杂色鸢尾),Iris Virginica(维吉尼亚鸢尾)(分别利用0,1,2代替原有标签名)。


2.7.2引入库,加载数据集,定义区分特征与标记

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
import numpy as np

iris = load_iris()
#date为特征数据集
data = iris.get("data")
#target为标记数据集
target = iris.get("target")

2.7.3 划分测试集与训练集,定义k值

#划分测试集占20%
x_train, x_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=0)
#定义k值
KNN = KNeighborsClassifier(n_neighbors=5)

2.7.4评价模型的准确率,使用模型预测未知种类样本

test_score = KNN.score(x_test, y_test)
print("模型的准确率:", test_score)
#定义三个测试数据
X1 = np.array([[1.9, 2.8, 4.7, 1.1], [5.8, 2.7, 4.1, 1.5], [3.6, 2.5, 3.1, 2.1]])
prediction = KNN.predict(X1)
#根据预测值找出对应花名
a = iris.get("target_names")[prediction]
print("第一朵花的种类为:", a[0])
print("第二朵花的种类为:", a[1])
print("第三朵花的种类为:", a[2])

结果:

*k近邻算法对样本的分类及其依赖于k的取值。k值不可取过大或过小。

  • k值越大,模型的偏差越大,对噪声数据越不敏感,当k值很大时,可能造成欠拟合;
  • k值越小,模型的方差就会越大,当k值太小,就会造成过拟合。 

  • *k的取值因满足以下两个条件

    1.近邻点要有相同/相近的类别

    2.特征维度的尺度(范围)要具备一致性

现在来调参,改变K的值:

当K=120时:

当K=100时:

当K=50时:

分析:

当k值越来越大至接近样本总数150时它的准确率越来越低,这是k值过大造成的欠拟合现象。

当k值越来越小至接近50时它的准确率越来越高,如果k值在继续变小的话可能会造成的过拟合现象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值