K近邻算法实现红酒聚类-AI代码解析
本实验主要介绍使用MindSpore在部分wine数据集上进行KNN实验。
1、实验目的
- 了解KNN的基本概念;
- 了解如何使用MindSpore进行KNN实验。
2、K近邻算法原理介绍
K近邻算法(K-Nearest-Neighbor, KNN)是一种用于分类和回归的非参数统计方法,最初由 Cover 和 Hart 于 1968 年提出,是机器学习最基础的算法之一。它的基本思想是:要确定一个样本的类别,可以计算它与所有训练样本的距离,然后找出和该样本最接近的 k 个样本,统计出这些样本的类别并进行投票,票数最多的那个类就是分类的结果。
KNN的三个基本要素:
- K值:一个样本的分类是由 K 个邻居的"多数表决"确定的。K 值越小,容易受噪声影响,反之,会使类别之间的界限变得模糊。
- 距离度量:反映了特征空间中两个样本间的相似度,距离越小,越相似。常用的有 Lp 距离(p=2 时,即为欧式距离)、曼哈顿距离、海明距离等。
- 分类决策规则:通常是多数表决,或者基于距离加权的多数表决(权值与距离成反比)。
2.1 分类问题
预测算法(分类)的流程如下:
- 在训练样本集中找出距离待测样本 x_test 最近的 k 个样本,并保存至集合 N 中;
- 统计集合 N 中每一类样本的个数 (C_{i}, i=1,2,3,...,c);
- 最终的分类结果为 ( \text{argmax} C_{i} )(最大的对应的 (C_{i}))那个类。
在上述实现过程中,k 的取值尤为重要。它可以根据问题和数据特点来确定。在具体实现时,可以考虑样本的权重,即每个样本有不同的投票权重,这种方法称为带权重的 k 近邻算法,它是一种变种的 k 近邻算法。
2.2 回归问题
假设离测试样本最近的 k 个训练样本的标签值为 (y_{i}),则对样本的回归预测输出值为:
即为所有邻居的标签均值。
带样本权重的回归预测函数为:
其中 (w_{i}) 为第个 (i) 样本的权重。
2.3 距离的定义
KNN算法的实现依赖于样本之间的距离,其中最常用的距离函数就是欧氏距离(欧几里得距离)。( \mathbb{R}^{n} )空间中的两点 (x) 和 (y),它们之间的欧氏距离定义为:
需要特别注意的是,使用欧氏距离时,应将特征向量的每个分量归一化,以减少因为特征值的尺度范围不同所带来的干扰,否则数值小的特征分量会被数值大的特征分量淹没。
其它的距离计算方式还有 Mahalanobis 距离、Bhattacharyya 距离等。
3、实验环境
预备知识:
- 熟练使用 Python;
- 具备一定的机器学习理论知识,如 KNN、无监督学习、欧式距离等。
实验环境:
- MindSpore 2.0(MindSpore 版本会定期更新,本指导也会定期刷新,与版本配套);
- 本案例支持 win_x86 和 Linux 系统,CPU/GPU/Ascend 均可运行;
- 如果在本地运行此实验,请参考《MindSpore 环境搭建实验手册》在本地安装 MindSpore。
4、数据处理
4.1 数据准备
Wine 数据集是模式识别最著名的数据集之一,Wine 数据集的官网:Wine Data Set。这些数据是对来自意大利同一地区但来自三个不同品种的葡萄酒进行化学分析的结果。数据集分析了三种葡萄酒中每种所含 13 种成分的量。这些 13 种属性是:
- Alcohol,酒精
- Malic acid,苹果酸
- Ash,灰
- Alcalinity of ash,灰的碱度
- Magnesium,镁
- Total phenols,总酚
- Flavanoids,类黄酮
- Nonflavanoid phenols,非黄酮酚
- Proanthocyanins,原花青素
- Color intensity,色彩强度
- Hue,色调
- OD280/OD315 of diluted wines,稀释酒的OD280/OD315
- Proline,脯氨酸
- 方式一,从 Wine 数据集官网下载 wine.data 文件。
- 方式二,从华为云 OBS 中下载 wine.data 文件。
| Key | Value | Key | Value |
| --- | --- | --- | --- |
| Data Set Characteristics: | Multivariate | Number of Instances: | 178 |
| Attribute Characteristics: | Integer, Real | Number of Attributes: | 13 |
| Associated Tasks: | Classification | Missing Values? | No |
python
%%capture captured_output
# 实验环境已经预装了 mindspore==2.2.14,如需更换 mindspore 版本,可更改下面 mindspore 的版本号
!pip uninstall mindspore -y # 卸载当前的 mindspore 版本
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14 # 从指定的镜像源安装指定版本的 mindspore
# 查看当前 mindspore 版本
!pip show mindspore # 显示当前安装的 mindspore 包的信息
from download import download # 导入 download 函数用于下载文件
# 下载红酒数据集
url = "https://ascend-professional-construction-dataset.obs.cn-north-4.myhuaweicloud.com:443/MachineLearning/wine.zip"
path = download(url, "./", kind="zip", replace=True) # 下载红酒数据集压缩文件
解析:
%%capture captured_output
:这是一个 Jupyter Notebook 的魔法命令,用于捕获单元格的输出,防止输出在屏幕上显示。!pip uninstall mindspore -y
:使用pip
命令卸载当前安装的mindspore
包,-y
参数表示自动确认卸载,避免提示用户。!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14
:安装指定版本(2.2.14)的mindspore
包,-i
后面跟的是安装源的地址,这里使用的是中国科学技术大学的 PyPI 镜像。!pip show mindspore
:该命令用于显示当前安装的mindspore
包的详细信息,包括版本、位置等。from download import download
:导入download
函数,假设该函数用于处理文件下载。url = "https://ascend-professional-construction-dataset.obs.cn-north-4.myhuaweicloud.com:443/MachineLearning/wine.zip"
:定义一个变量url
,它包含了要下载的红酒数据集的链接。path = download(url, "./", kind="zip", replace=True)
:调用download
函数下载红酒数据集。参数说明:url
: 要下载的文件的地址;"./"
: 下载的目标路径,这里表示当前目录;kind="zip"
: 指定下载文件的类型为 zip 格式;replace=True
: 如果目标路径已存在同名文件,是否替换,True
表示替换。
4.2 数据读取与处理
导入 MindSpore 模块和辅助模块
在生成数据之前,导入需要的 Python 库。
目前使用到 os 库,为方便理解,其他需要的库,我们在具体使用到时再说明。
详细的 MindSpore 的模块说明,可以在 MindSpore API 页面中搜索查询。
可以通过 context.set_context
来配置运行需要的信息,例如运行模式、后端信息、硬件等信息。
导入 context 模块,配置运行需要的信息。
python
%matplotlib inline
# 使得在 Jupyter Notebook 中绘制的图形被嵌入在输出单元格中,而不是在新窗口中显示
import os # 导入 os 模块,用于与操作系统进行交互
import csv # 导入 csv 模块,用于处理 CSV 文件
import numpy as np # 导入 numpy,用于科学计算和数组操作
import matplotlib.pyplot as plt # 导入 matplotlib 的 pyplot 模块,用于绘制图形
import mindspore as ms # 导入 mindspore 库
from mindspore import nn, ops # 从 mindspore 中导入 nn(神经网络模块)和 ops(操作模块)
ms.set_context(device_target="CPU") # 设置 MindSpore 的运行环境为 CPU
解析:
%matplotlib inline
:这是一个 Jupyter Notebook 的魔法命令,它使得生成的图形可以直接嵌入在 Notebook 的输出单元格中,而不是在新窗口中弹出。import os
:导入os
模块,它提供了一些与操作系统交互的功能,比如文件和目录操作。import csv
:导入csv
模块,用于读取和写入 CSV 格式的数据文件。import numpy as np
:导入numpy
库,并将其简写为np
,它是用于数组计算和科学计算的基础库。import matplotlib.pyplot as plt
:导入matplotlib.pyplot
模块,并简写为plt
,该模块提供了绘制图形的功能。import mindspore as ms
:导入mindspore
库,并将其简写为ms
,它是华为开发的深度学习框架。from mindspore import nn, ops
:从mindspore
库中导入神经网络模块nn
和操作模块ops
,这两个模块包含了构建深度学习模型和执行各种运算的工具。ms.set_context(device_target="CPU")
:设置 MindSpore 的上下文环境,指定计算设备为 CPU。这意味着接下来的计算将在 CPU 上进行,而不是使用 GPU 或其他计算设备。
读取 Wine 数据集 wine.data
,并查看部分数据。
python
with open('wine.data') as csv_file:
# 以默认模式(只读)打开名为 'wine.data' 的 CSV 文件
data = list(csv.reader(csv_file, delimiter=','))
# 使用 csv.reader 读取文件内容,并将其转换为列表
# delimiter=',' 指定 CSV 文件的分隔符为逗号
print(data[56:62] + data[130:133])
# 打印数据列表中第 57 到 62 行(不包括第 62 行)以及第 131 到 133 行(不包括第 133 行)的内容
解析:
with open('wine.data') as csv_file:
:以上下文管理器的方式打开名为wine.data
的文件。这种方式确保文件在处理完成后能够自动关闭。data = list(csv.reader(csv_file, delimiter=','))
:csv.reader(csv_file, delimiter=',')
:使用csv
模块的reader
函数读取打开的文件csv_file
,并指定分隔符为逗号(,
),这适用于 CSV 格式的文件。list(...)
:将读取的结果转换为一个列表,data
变量将存储该列表,列表的每个元素都是一个表示 CSV 行的列表。
print(data[56:62] + data[130:133])
:data[56:62]
:切片操作,获取data
列表中索引为 56 到 61 的行(共 6 行)。data[130:133]
:切片操作,获取data
列表中索引为 130 到 132 的行(共 3 行)。+
:将这两个切片结果合并为一个新列表,并打印输出。
取三类样本(共 178 条),将数据集的 13 个属性作为自变量 (X)。将数据集的 3 个类别作为因变量 (Y)。
python
X = np.array([[float(x) for x in s[1:]] for s in data[:178]], np.float32)
# 创建一个二维 numpy 数组 X,包含前 178 行数据的特征部分
# 使用列表推导式,将每行数据中的第 1 个元素到最后一个元素转换为 float 类型
# np.float32 指定数组的数据类型为 32 位浮点数
Y = np.array([s[0] for s in data[:178]], np.int32)
# 创建一维 numpy 数组 Y,包含前 178 行数据的标签部分
# 使用列表推导式,提取每行数据的第 0 个元素(标签)并转换为 int 类型
# np.int32 指定数组的数据类型为 32 位整数
解析:
X = np.array([[float(x) for x in s[1:]] for s in data[:178]], np.float32)
:- 这行代码使用嵌套列表推导式,首先对
data[:178]
切片进行操作,提取前 178 行的数据。 for s in data[:178]
:遍历前 178 行的数据,s
是每一行的内容。[float(x) for x in s[1:]]
:对于每一行s
,提取从索引 1 开始到行末的所有元素,将其转换为浮点数(float
)。np.array(..., np.float32)
:将生成的二维列表转换为 NumPy 数组,并指定数据类型为 32 位浮点数(np.float32
)。
- 这行代码使用嵌套列表推导式,首先对
Y = np.array([s[0] for s in data[:178]], np.int32)
:- 这行代码使用列表推导式提取前 178 行数据的标签部分。
[s[0] for s in data[:178]]
:遍历前 178 行数据,提取每行的第 0 个元素(通常为标签或目标值)。np.array(..., np.int32)
:将生成的一维列表转换为 NumPy 数组,并指定数据类型为 32 位整数(np.int32
)。
取样本的某两个属性进行 2 维可视化,可以看到在某两个属性上样本的分布情况以及可分性。
python
attrs = ['Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash', 'Magnesium', 'Total phenols',
'Flavanoids', 'Nonflavanoid phenols', 'Proanthocyanins', 'Color intensity', 'Hue',
'OD280/OD315 of diluted wines', 'Proline']
# 定义一个列表 attrs,包含不同的葡萄酒属性名称
plt.figure(figsize=(10, 8))
# 创建一个新的图形,设定图形的大小为 10x8 英寸
for i in range(0, 4):
# 循环从 0 到 3,生成 4 个子图
plt.subplot(2, 2, i+1)
# 创建一个 2 行 2 列的子图,并选择第 i+1 个子图进行绘制
a1, a2 = 2 * i, 2 * i + 1
# 计算当前子图中要绘制的两个属性的索引 a1 和 a2
plt.scatter(X[:59, a1], X[:59, a2], label='1')
# 绘制前 59 个样本的属性 a1 和 a2 的散点图,标签为 '1'
plt.scatter(X[59:130, a1], X[59:130, a2], label='2')
# 绘制第 60 到 130 个样本的属性 a1 和 a2 的散点图,标签为 '2'
plt.scatter(X[130:, a1], X[130:, a2], label='3')
# 绘制第 131 个样本到最后的样本的属性 a1 和 a2 的散点图,标签为 '3'
plt.xlabel(attrs[a1])
# 设置 x 轴标签为对应属性的名称
plt.ylabel(attrs[a2])
# 设置 y 轴标签为对应属性的名称
plt.legend()
# 添加图例,以显示不同类别的样本标签
plt.show()
# 显示绘制的图形
解析:
attrs = [...]
:定义了一个包含葡萄酒特征名称的列表,这些特征将用于绘制图形的坐标轴标签。plt.figure(figsize=(10, 8))
:使用 Matplotlib 创建一个新图形,并设定其大小为 10x8 英寸。for i in range(0, 4):
:循环 4 次(i
从 0 到 3),用于创建 4 个子图。plt.subplot(2, 2, i+1)
:在 2 行 2 列的图形布局中选择第i+1
个子图。a1, a2 = 2 * i, 2 * i + 1
:计算要在当前子图中绘制的两个特征的索引。每个子图显示两个特征的散点图。plt.scatter(...)
:绘制散点图:X[:59, a1]
和X[:59, a2]
:表示前 59 个样本的第a1
和a2
列作为 x 和 y 轴的数据。X[59:130, a1]
和X[59:130, a2]
:表示第 60 到 130 个样本的数据。X[130:, a1]
和X[130:, a2]
:表示第 131 个样本到最后的样本的数据。
plt.xlabel(attrs[a1])
和plt.ylabel(attrs[a2])
:设置 x 轴和 y 轴的标签,显示对应的属性名称。plt.legend()
:添加图例,表示每个类别的样本。plt.show()
:显示最终绘制的图形。
将数据集按 128:50 划分为训练集(已知类别样本)和验证集(待验证样本):
python
train_idx = np.random.choice(178, 128, replace=False)
# 随机选择 128 个不重复的索引从 0 到 177(共 178 个样本),用于训练集
# np.random.choice 的参数为样本总数、选择的数量和是否允许重复选择
test_idx = np.array(list(set(range(178)) - set(train_idx)))
# 创建测试集的索引
# set(range(178)) 生成一个包含 0 到 177 的集合
# set(train_idx) 生成训练集索引的集合
# 通过集合的差集操作得到未被选择的索引,转化为 NumPy 数组
X_train, Y_train = X[train_idx], Y[train_idx]
# 根据训练集索引 train_idx 提取训练数据 X 和标签 Y 的相应部分
X_test, Y_test = X[test_idx], Y[test_idx]
# 根据测试集索引 test_idx 提取测试数据 X 和标签 Y 的相应部分
解析:
train_idx = np.random.choice(178, 128, replace=False)
:- 使用
np.random.choice
从 0 到 177 的范围内随机选择 128 个索引。 replace=False
表示不允许重复选择,即每个索引只能选择一次。
- 使用
test_idx = np.array(list(set(range(178)) - set(train_idx)))
:set(range(178))
创建一个包含 0 到 177 的集合。set(train_idx)
创建一个包含训练集索引的集合。- 通过集合的差集操作
-
得到未被选择的索引。 - 最后将结果转换为 NumPy 数组,存储为
test_idx
。
X_train, Y_train = X[train_idx], Y[train_idx]
:- 根据
train_idx
提取训练集的特征数据X
和对应的标签Y
。 X[train_idx]
获取训练样本的特征数据,Y[train_idx]
获取训练样本的标签。
- 根据
X_test, Y_test = X[test_idx], Y[test_idx]
:- 根据
test_idx
提取测试集的特征数据X
和对应的标签Y
。 X[test_idx]
获取测试样本的特征数据,Y[test_idx]
获取测试样本的标签。
- 根据
5、模型构建--计算距离
利用 MindSpore 提供的 tile, square, ReduceSum, sqrt, TopK
等算子,通过矩阵运算的方式同时计算输入样本 x 和已明确分类的其他样本 X_train 的距离,并计算出 top k 近邻。
python
class KnnNet(nn.Cell):
def __init__(self, k):
super(KnnNet, self).__init__()
self.k = k
# 初始化 KNN 网络,k 表示最近邻的数量
def construct(self, x, X_train):
# 平铺输入 x 以匹配 X_train 中的样本数
x_tile = ops.tile(x, (128, 1))
# 使用 ops.tile 将输入 x 复制 128 次,以便与训练样本 X_train 的形状匹配
square_diff = ops.square(x_tile - X_train)
# 计算 x_tile 和 X_train 之间的平方差
square_dist = ops.sum(square_diff, 1)
# 对每一行的平方差求和,以得到每个样本的平方距离
dist = ops.sqrt(square_dist)
# 计算每个样本的欧几里得距离
# -dist 表示值越大,样本就越接近
values, indices = ops.topk(-dist, self.k)
# 获取距离最近的 k 个样本的索引,-dist 确保距离越小的样本排在前面
return indices
# 返回 k 个最近邻的索引
def knn(knn_net, x, X_train, Y_train):
x, X_train = ms.Tensor(x), ms.Tensor(X_train)
# 将输入 x 和训练样本 X_train 转换为 Tensor 类型,适合后续计算
indices = knn_net(x, X_train)
# 使用 knn_net 模型对输入 x 和训练样本 X_train 进行预测,获取最近邻的索引
topk_cls = [0] * len(indices.asnumpy())
# 初始化一个计数器列表,用于记录每个类的出现次数
for idx in indices.asnumpy():
topk_cls[Y_train[idx]] += 1
# 根据最近邻的索引,增加对应类在 topk_cls 中的计数
cls = np.argmax(topk_cls)
# 找到计数最多的类作为最终的预测结果
return cls
# 返回预测的类标签
解析:
- KnnNet 类 :
class KnnNet(nn.Cell):
:定义一个 KNN 网络类,继承自nn.Cell
。def __init__(self, k):
:构造函数,初始化 KNN 中的 k 值。self.k = k
:保存 k 的值。
- construct 方法 :
def construct(self, x, X_train):
:定义前向传播的方法,接收输入x
和训练数据X_train
。x_tile = ops.tile(x, (128, 1))
:对输入样本x
进行平铺,以与X_train
的样本数匹配。square_diff = ops.square(x_tile - X_train)
:计算x
和每个训练样本之间的平方差。square_dist = ops.sum(square_diff, 1)
:计算每个样本的平方距离。dist = ops.sqrt(square_dist)
:计算每个样本的欧几里得距离。values, indices = ops.topk(-dist, self.k)
:获取距离最近的 k 个样本的索引(使用负值确保较小的距离优先)。
- knn 函数 :
def knn(knn_net, x, X_train, Y_train):
:定义 KNN 函数,执行预测。x, X_train = ms.Tensor(x), ms.Tensor(X_train)
:将输入数据转换为适合计算的 Tensor 格式。indices = knn_net(x, X_train)
:调用 KNN 网络进行预测,获取最近邻的索引。topk_cls = [0] * len(indices.asnumpy())
:初始化一个计数器列表,记录最近邻的类别。for idx in indices.asnumpy():
:遍历最近邻索引,更新计数器。cls = np.argmax(topk_cls)
:找到出现次数最多的类别作为预测结果。return cls
:返回最终的预测类别。
6、模型预测
在验证集上验证 KNN 算法的有效性,取 (k = 5),验证精度接近 80%,说明 KNN 算法在该 3 分类任务上有效,能根据酒的 13 种属性判断出酒的品种。
python
acc = 0
# 初始化准确率计数器 acc 为 0
knn_net = KnnNet(5)
# 创建一个 KNN 网络实例,指定最近邻数量 k 为 5
for x, y in zip(X_test, Y_test):
# 遍历测试集中的每个样本 x 和其对应的标签 y
pred = knn(knn_net, x, X_train, Y_train)
# 使用 knn 函数进行预测,得到预测结果 pred
acc += (pred == y)
# 如果预测结果 pred 与真实标签 y 相同,则 acc 加 1
print('label: %d, prediction: %s' % (y, pred))
# 打印每个样本的真实标签和预测结果
print('Validation accuracy is %f' % (acc / len(Y_test)))
# 计算并打印验证集的准确率,准确率为正确预测的数量除以测试样本总数
解析:
- 初始化准确率计数器 :
acc = 0
:初始化一个变量acc
作为正确预测的数量计数器,初始值为 0。
- 创建 KNN 网络实例 :
knn_net = KnnNet(5)
:实例化KnnNet
类,设置最近邻数k
为 5。
- 遍历测试集 :
for x, y in zip(X_test, Y_test):
:将测试数据X_test
和对应的标签Y_test
进行配对,逐一遍历。pred = knn(knn_net, x, X_train, Y_train)
:调用knn
函数进行预测,传入当前测试样本x
和训练数据,得到预测结果pred
。
- 更新准确率计数器 :
acc += (pred == y)
:检查预测结果pred
是否等于真实标签y
,如果相等则将acc
加 1,表示正确预测。
- 打印每个样本的结果 :
print('label: %d, prediction: %s' % (y, pred))
:输出每个测试样本的真实标签和预测结果,以便观察模型性能。
- 计算并打印验证集的准确率 :
print('Validation accuracy is %f' % (acc / len(Y_test)))
:计算验证集的准确率,将正确预测的数量acc
除以测试样本总数len(Y_test)
,并格式化输出。
实验小结
本实验使用 MindSpore 实现了 KNN 算法,用来解决 3 分类问题。取 wine 数据集上的 3 类样本,分为已知类别样本和待验证样本,从验证结果可以看出 KNN 算法在该任务上有效,能根据酒的 13 种属性判断出酒的品种。
整体代码
python
# K近邻算法实现红酒聚类
# 本实验主要介绍使用MindSpore在部分wine数据集上进行KNN实验。
## 1、实验目的
# - 了解KNN的基本概念;
# - 了解如何使用MindSpore进行KNN实验。
## 2、K近邻算法原理介绍
# K近邻算法(K-Nearest-Neighbor, KNN)是一种用于分类和回归的非参数统计方法,最初由 Cover和Hart于1968年提出。
# 它正是基于以上思想:要确定一个样本的类别,可以计算它与所有训练样本的距离,然后找出和该样本最接近的k个样本,
# 统计出这些样本的类别并进行投票,票数最多的那个类就是分类的结果。
# KNN的三个基本要素:
# - K值,一个样本的分类是由K个邻居的"多数表决"确定的。
# - 距离度量,反映了特征空间中两个样本间的相似度。
# - 分类决策规则,通常是多数表决,或者基于距离加权的多数表决。
### 2.1 分类问题
# 预测算法(分类)的流程如下:
# (1)在训练样本集中找出距离待测样本x_test最近的k个样本,并保存至集合N中;
# (2)统计集合N中每一类样本的个数C_{i}, i=1,2,3,...,c;
# (3)最终的分类结果为argmaxC_{i} (最大的对应的C_{i})那个类。
### 2.2 回归问题
# 假设离测试样本最近的k个训练样本的标签值为y_{i},则对样本的回归预测输出值为:
# $\hat y = (\sum_{i=1}^{n}{y_{i}})/k$
### 2.3 距离的定义
# KNN算法的实现依赖于样本之间的距离,其中最常用的距离函数就是欧氏距离。
## 3、实验环境
# 实验环境:
# - MindSpore 2.0;
# - 支持win_x86和Linux系统,CPU/GPU/Ascend均可运行。
## 4、数据处理
### 4.1 数据准备
# Wine数据集是模式识别最著名的数据集之一,分析了三种葡萄酒中每种所含13种成分的量。
# 方式一,从Wine数据集官网下载[wine.data文件](http://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data)。
# 实验环境已经预装了mindspore==2.2.14。
!pip uninstall mindspore -y
!pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore==2.2.14
# 查看当前 mindspore 版本
!pip show mindspore
from download import download
# 下载红酒数据集
url = "https://ascend-professional-construction-dataset.obs.cn-north-4.myhuaweicloud.com:443/MachineLearning/wine.zip"
path = download(url, "./", kind="zip", replace=True)
### 4.2 数据读取与处理
import os
import csv
import numpy as np
import matplotlib.pyplot as plt
import mindspore as ms
from mindspore import nn, ops
ms.set_context(device_target="CPU")
# 读取Wine数据集`wine.data`
with open('wine.data') as csv_file:
data = list(csv.reader(csv_file, delimiter=','))
print(data[56:62]+data[130:133])
# 取三类样本(共178条),将数据集的13个属性作为自变量X。将数据集的3个类别作为因变量Y。
X = np.array([[float(x) for x in s[1:]] for s in data[:178]], np.float32)
Y = np.array([s[0] for s in data[:178]], np.int32)
# 取样本的某两个属性进行2维可视化
attrs = ['Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash', 'Magnesium', 'Total phenols',
'Flavanoids', 'Nonflavanoid phenols', 'Proanthocyanins', 'Color intensity', 'Hue',
'OD280/OD315 of diluted wines', 'Proline']
plt.figure(figsize=(10, 8))
for i in range(0, 4):
plt.subplot(2, 2, i+1)
a1, a2 = 2 * i, 2 * i + 1
plt.scatter(X[:59, a1], X[:59, a2], label='1')
plt.scatter(X[59:130, a1], X[59:130, a2], label='2')
plt.scatter(X[130:, a1], X[130:, a2], label='3')
plt.xlabel(attrs[a1])
plt.ylabel(attrs[a2])
plt.legend()
plt.show()
# 将数据集按128:50划分为训练集和验证集
train_idx = np.random.choice(178, 128, replace=False)
test_idx = np.array(list(set(range(178)) - set(train_idx)))
X_train, Y_train = X[train_idx], Y[train_idx]
X_test, Y_test = X[test_idx], Y[test_idx]
## 5、模型构建
class KnnNet(nn.Cell):
def __init__(self, k):
super(KnnNet, self).__init__()
self.k = k
def construct(self, x, X_train):
# 平铺输入x以匹配X_train中的样本数
x_tile = ops.tile(x, (128, 1))
square_diff = ops.square(x_tile - X_train)
square_dist = ops.sum(square_diff, 1)
dist = ops.sqrt(square_dist)
# -dist表示值越大,样本就越接近
values, indices = ops.topk(-dist, self.k)
return indices
def knn(knn_net, x, X_train, Y_train):
x, X_train = ms.Tensor(x), ms.Tensor(X_train)
indices = knn_net(x, X_train)
topk_cls = [0]*len(indices.asnumpy())
for idx in indices.asnumpy():
topk_cls[Y_train[idx]] += 1
cls = np.argmax(topk_cls)
return cls
## 6、模型预测
acc = 0
knn_net = KnnNet(5)
for x, y in zip(X_test, Y_test):
pred = knn(knn_net, x, X_train, Y_train)
acc += (pred == y)
print('label: %d, prediction: %s' % (y, pred))
print('Validation accuracy is %f' % (acc/len(Y_test)))
## 实验小结
# 本实验使用MindSpore实现了KNN算法,用来解决3分类问题。
解析:
- 实验目的 :
- 学习 KNN 的基本概念和使用 MindSpore 实现 KNN 的方法。
- KNN 原理 :
- KNN 是一种基于距离的分类和回归算法,通过计算样本距离来判定样本的类别或输出值。
- 数据处理 :
- 下载和读取 Wine 数据集,并将数据分为特征和标签(X 和 Y)。
- 可视化数据集中的样本分布。
- 模型构建 :
- 定义 KNN 网络类
KnnNet
,实现距离计算和最近邻索引获取。 - 定义
knn
函数进行预测,包括分类计数和返回最终的预测结果。
- 定义 KNN 网络类
- 模型预测 :
- 在验证集上测试模型的准确性,输出每个样本的真实标签和预测结果,最后计算整体准确率。
- 实验小结 :
- 通过实验验证 KNN 算法在 Wine 数据集分类任务上的有效性。