KNN用于多类别图片分类-Mnist或自己的数据集-numpy底层原理实现

完整代码

import os.path
import numpy as np
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
from torchvision import transforms
from sklearn.model_selection import train_test_split

def LoadMyData(dataroot):
    transform = transforms.Compose([transforms.Resize((16,16)),transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
    dataset = ImageFolder(dataroot,transform)
    datafolder = DataLoader(dataset)
    classes = []
    labels = []
    for myclass,mylabel in datafolder:
        classes.append(myclass.view(-1).numpy())
        labels.append(mylabel.item())
    return classes,labels

def SplitData(data,labels):
    x_train,x_test,y_train,y_test = train_test_split(data,labels,test_size=0.3,random_state=0)
    return x_train,x_test,y_train,y_test

def PrepareData(dataroot):
    classes,labels = LoadMyData(dataroot)
    x_train,x_test,y_train,y_test = SplitData(classes,labels)
    return x_train,x_test,y_train,y_test


def cal_distance(model,img1,img2):
    if model == 'L1':
        dis = sum(np.abs(img1-img2))
    elif model == 'L2':
        dis = np.sqrt(sum(np.square(img1-img2)))
    return dis

def get_user_input():
    print("请输入对距离的度量,L1表示曼哈顿距离,L2表示欧式距离:")
    while(1):
        mydis = input()
        if mydis == 'L1':
            model = 'L1'
            return model
        elif mydis == 'L2':
            model = 'L2'
            return model
        else :
            print("输入错误!请重新输入:")

def main(k,mydataroot):
    model = get_user_input()
    print("开始计算,请稍后")
    dataroot = mydataroot
    x_train, x_test, y_train, y_test = PrepareData(dataroot)
    pred_y = np.empty((0,1))
    for i in range(len(x_test)):
        dis_list = np.empty((0,2))
        for j in range(len(x_train)):
            dis = cal_distance(model,x_test[i],x_train[j])
            img_item = [[dis,y_train[j]]]
            img_item = np.array(img_item)
            dis_list = np.concatenate((dis_list,img_item))
        distances = dis_list[:, 0]
        index = np.argsort(distances)
        sort_dis_list = dis_list[index]
        k_sort_label_list = sort_dis_list[:,1][:k]
        counts = np.bincount(k_sort_label_list.astype(int))
        pred_label = np.argmax(counts)
        pred_y = np.append(pred_y,pred_label).astype(int)
        acc = np.mean(pred_y == np.array(y_test[:(i+1)])) * 100
        print(f"共分类{i+1}张图像,当前准确率为{acc}%")
    acc = np.mean(pred_y == np.array(y_test))*100
    print(f"选择{k}个近邻的到的最终准确率为{acc}%")

if __name__ == "__main__":
    dataroot = './data'
    print("请输入选择的紧邻数量K值:")
    k = input()
    main(int(k),dataroot)

运行截图

在这里插入图片描述

代码解读

L1,L2距离

L1是曼哈顿距离,两个向量直接相减后取绝对值的加和。
L2是欧式距离,为两个向量相减后的方均根值

np.concatenate

np.concatenate是NumPy库中的一个函数,用于将两个或多个数组沿指定轴连接起来形成一个新的数组。它的语法如下:

numpy.concatenate((a1, a2, ...), axis=0, out=None)

(a1, a2, …):要连接的数组序列,可以是元组、列表或单独的数组。
axis:指定连接的轴。默认为0,表示沿着第一个轴连接,即在行方向上连接。如果指定为1,则表示沿着列方向连接。
out:可选参数,指定输出数组的存在位置。
np.concatenate的工作方式取决于指定的轴:

如果axis为0,则它将沿着行方向连接数组,即将数组垂直堆叠在一起,形成一个更大的数组。
如果axis为1,则它将沿着列方向连接数组,即将数组水平连接在一起,形成一个更宽的数组。
示例:

import numpy as np

# 连接两个一维数组
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.concatenate((a, b))
print(result)
# 输出: [1 2 3 4 5 6]

# 连接两个二维数组
c = np.array([[1, 2], [3, 4]])
d = np.array([[5, 6]])
result = np.concatenate((c, d), axis=0)
print(result)
# 输出:
# [[1 2]
#  [3 4]
#  [5 6]]

# 沿着列方向连接数组
e = np.array([[1, 2], [3, 4]])
f = np.array([[5, 6], [7, 8]])
result = np.concatenate((e, f), axis=1)
print(result)
# 输出:
# [[1 2 5 6]
#  [3 4 7 8]]

np.empty

np.empty是NumPy库中的一个函数,用于创建一个指定形状和数据类型的未初始化的数组。它的语法如下:

numpy.empty(shape, dtype=float, order='C')

参数说明:

shape:表示所创建数组的形状的整数或整数元组。例如,要创建一个2行3列的数组,可以传递(2, 3)作为形状。
dtype:可选参数,表示所创建数组的数据类型。默认为float类型。可以使用NumPy中定义的数据类型,如int、float、bool等,或者通过指定字符串来表示特定的数据类型,如int32、float64等。
order:可选参数,表示数组在内存中的存储顺序。默认为’C’,表示按行的C语言顺序存储。也可以设置为’F’,表示按列的Fortran语言顺序存储。
np.empty函数创建一个未初始化的数组,其元素的初始值取决于内存的状态。由于未初始化,数组的内容可能是随机的,可能包含之前存储在相同内存位置上的值。因此,在使用np.empty创建数组后,需要确保通过其他方式对数组进行初始化或填充。

下面是一些示例,以说明np.empty的用法:

import numpy as np

# 创建一个形状为(3, 3)的未初始化数组
a = np.empty((3, 3))
print(a)
# 输出:
# [[4.67296746e-307 1.69121096e-306 2.78145741e-307]
#  [1.89146896e-307 1.37961302e-306 6.23058028e-307]
#  [1.02359848e-306 1.33510679e-306 6.23039354e-307]]

# 创建一个形状为(2, 2)的未初始化整数数组
b = np.empty((2, 2), dtype=int)
print(b)
# 输出:
# [[                  0                   0]
#  [1072693248 1073741824]]

# 创建一个形状为(2, 3, 4)的未初始化数组
c = np.empty((2, 3, 4))
print(c)
# 输出:
# [[[6.23042070e-307 1.33511018e-306 6.23052935e-307 1.86921279e-306]
#   [1.60217812e-306 1.06810268e-306 1.42418172e-306 7.56601165e-307]
#   [8.90104239e-307 1.33511969e-306 2.22522597e-306 1.06809792e-306]]
# 
#  [[8.06612616e-308 1.60219035e-306 1.37961302e-306 1.02360558e-306]
#   [6.23061763e-307 1.11261502e-306

当创建空数组时,直接写为:

pred_y = np.empty((0,1))  #1列的空数组
dis_list = np.empty((0,2)) #2列的空数组(二维数组)

np.argsort

np.argsort是NumPy库中的一个函数,用于返回数组中元素排序后的索引值。它的语法如下:

numpy.argsort(a, axis=-1, kind=None, order=None)

参数说明:

a:要排序的数组。
axis:可选参数,表示沿着指定轴进行排序。默认为-1,表示沿着最后一个轴进行排序。
kind:可选参数,表示排序算法的种类。可以取值为’quicksort’、‘mergesort’、‘heapsort’。默认为None,表示使用默认的排序算法。
order:可选参数,表示要排序的字段。只有当数组是结构化数组时才有意义。
np.argsort函数返回一个数组,其中包含原始数组中每个元素排序后的索引值。这意味着返回的索引数组与原始数组具有相同的形状,但其值表示了原始数组中对应位置元素在排序后的位置。

下面是一些示例,以说明np.argsort的用法:

import numpy as np

# 一维数组的排序索引
a = np.array([3, 1, 2])
indices = np.argsort(a)
print(indices)
# 输出: [1 2 0]
# 原始数组为 [3, 1, 2],排序后为 [1, 2, 3],对应的索引为 [1, 2, 0]

# 二维数组按行排序的索引
b = np.array([[3, 1, 2], [6, 4, 5]])
row_indices = np.argsort(b, axis=1)
print(row_indices)
# 输出:
# [[1 2 0]
#  [1 2 0]]
# 原始数组的第一行为 [3, 1, 2],排序后为 [1, 2, 3],对应的索引为 [1, 2, 0]
# 原始数组的第二行为 [6, 4, 5],排序后为 [4, 5, 6],对应的索引为 [1, 2, 0]

# 二维数组按列排序的索引
c = np.array([[3, 1, 2], [6, 4, 5]])
column_indices = np.argsort(c, axis=0)
print(column_indices)
# 输出:
# [[0 0 0]
#  [1 1 1]]
# 原始数组的第一列为 [3, 6],排序后为 [3, 6],对应的索引为 [0, 1]
# 原始数组的第二列为 [1, 4],排序后为 [1, 4],对应的索引为 [0, 1]
# 原始数组的第三列为 [2, 5],排序后为 [2, 5],对应的索引为 [0, 1]

[:, k]和[:k]

1.[:, k]:这种形式的切片操作应用于二维数组(或矩阵)。:表示取所有行,k表示取第k列。例如,arr[:, 2]表示取所有行的第2列。

示例:

import numpy as np

arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
sliced = arr[:, 1]  # 取所有行的第1列
print(sliced)

[2 5 8]

2.[:k]:这种形式的切片操作应用于一维数组。:表示从开头到结尾,k表示切片的结束索引(不包含在切片中)。例如,arr[:5]表示取数组的前5个元素。

示例:

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
sliced = arr[:3]  # 取数组的前3个元素
print(sliced)

[1 2 3]

嵌套列表-[[dis,y_train[j]]]和[dis,y_train[j]]的区别

如果希望将 dis 和 y_train[j] 作为一个整体添加到二维数组中,应该使用嵌套列表的方式。

在二维数组中,每个元素都应该是一个具有相同长度的一维子数组。因此,如果要将 dis 和 y_train[j] 作为一个整体添加到二维数组中,需要使用一个包含两个元素的子列表。

以下是一个示例代码,展示了如何使用嵌套列表的方式向二维数组添加新的项:

import numpy as np

dis = 1.5
y_train_j = 2

# 创建空的二维数组
my_array = np.empty((0, 2))

# 将 [dis, y_train[j]] 添加到二维数组中
new_item = [[dis, y_train_j]]
my_array = np.append(my_array, new_item, axis=0)

print(my_array)
# 输出:
# [[1.5 2. ]]

在这个示例中,我们创建了一个空的二维数组 my_array,然后使用嵌套列表的方式创建了一个新的子列表 new_item,其中包含要添加的元素 [dis, y_train[j]]。然后,我们使用 np.append() 函数将 new_item 添加到 my_array 中,并指定 axis=0 参数以沿着行的方向添加。
在使用 np.append() 函数将新的项添加到二维数组时,需要确保新的项具有相同的维度和形状。直接使用 [dis, y_train_j] 创建的对象是一个一维列表,而不是一个二维列表。因此,它不能直接添加到二维数组中。

np.append() 函数期望传递具有相同形状的数组作为参数进行连接。当我们在二维数组中添加新的项时,我们需要确保新的项是一个二维列表(即包含一个子列表)。

在之前的示例代码中,我们使用了 [[dis, y_train_j]] 创建了一个嵌套的列表(二维列表),这个嵌套的列表作为新的项添加到二维数组中。这样,我们可以确保新的项的形状和维度与二维数组相匹配。

如果直接使用 [dis, y_train_j],则它是一个一维列表,无法直接添加到二维数组中。您可以尝试使用 np.reshape() 或 np.expand_dims() 函数来将一维列表转换为二维列表,然后再进行添加操作。

和列表append()的区别

在 NumPy 中,np.append() 函数在执行添加操作时,要求添加的元素和原始数组具有相同的形状(shape)或维度(dimension)。这意味着添加的元素需要与原始数组在维度和形状上匹配,以便正确地执行连接操作。

当使用 np.append() 函数添加元素时,它会创建一个新的数组,并将原始数组和要添加的元素连接在一起。要求连接的数组具有相同的形状或维度是为了确保数据的一致性。

在列表中添加元素时,我们可以直接使用 append 函数来添加单个元素,而不必使用 append 函数加上中括号的形式 append([元素])。

这是因为列表是 Python 中的内置数据结构,可以直接处理各种对象。append 函数旨在将指定的元素添加到列表的末尾,而不需要将元素放在另一个列表中。

np.astype

在 NumPy 中,np.astype() 是用于将数组的数据类型(dtype)进行转换的函数。它返回一个新的数组,其中的元素类型被转换为指定的数据类型。

astype() 函数可以接受一个数据类型作为参数,用于指定转换后的数据类型。这个数据类型可以是 Python 的内置数据类型(例如 int、float、str 等),也可以是 NumPy 定义的数据类型(例如 np.int32、np.float64 等)。以下是一个示例代码,演示了如何使用 astype() 函数进行数据类型转换:

import numpy as np

# 创建一个包含整数的数组
my_array = np.array([1, 2, 3, 4, 5])

# 将数组的数据类型转换为浮点数类型
float_array = my_array.astype(float)

print(float_array)
# 输出: [1. 2. 3. 4. 5.]
print(float_array.dtype)
# 输出: float64
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值