🤖 快速入门MindSpore AI:打造你的智能助手
基本介绍 || 快速入门 || 张量 Tensor || 数据集 Dataset || 数据变换 Transforms || 网络构建 || 函数式自动微分 || 模型训练 || 保存与加载 || 使用静态图加速
快速入门
本节通过MindSpore的API来快速实现一个简单的深度学习模型。若想要深入了解MindSpore的使用方法,请参阅各节最后提供的参考链接。
import mindspore
from mindspore import nn
from mindspore.dataset import vision, transforms
from mindspore.dataset import MnistDataset
处理数据集
MindSpore提供基于Pipeline的数据引擎,通过数据集(Dataset)和数据变换(Transforms)实现高效的数据预处理。在本教程中,我们使用Mnist数据集,自动下载完成后,使用mindspore.dataset
提供的数据变换进行预处理。
本章节中的示例代码依赖
download
,可使用命令pip install download
安装。如本文档以Notebook运行时,完成安装后需要重启kernel才能执行后续代码。
# Download data from open datasets
from download import download
url = "https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/" \
"notebook/datasets/MNIST_Data.zip"
path = download(url, "./", kind="zip", replace=True)
Downloading data from https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/MNIST_Data.zip (10.3 MB) file_sizes: 100%|██████████████████████████| 10.8M/10.8M [00:01<00:00, 6.73MB/s] Extracting zip file... Successfully downloaded / unzipped to ./
MNIST数据集目录结构如下:
MNIST_Data └── train ├── train-images-idx3-ubyte (60000个训练图片) ├── train-labels-idx1-ubyte (60000个训练标签) └── test ├── t10k-images-idx3-ubyte (10000个测试图片) ├── t10k-labels-idx1-ubyte (10000个测试标签)
数据下载完成后,获得数据集对象。
train_dataset = MnistDataset('MNIST_Data/train')
test_dataset = MnistDataset('MNIST_Data/test')
打印数据集中包含的数据列名,用于dataset的预处理。
print(train_dataset.get_col_names())
['image', 'label']
MindSpore的dataset使用数据处理流水线(Data Processing Pipeline),需指定map、batch、shuffle等操作。这里我们使用map对图像数据及标签进行变换处理,将输入的图像缩放为1/255,根据均值0.1307和标准差值0.3081进行归一化处理,然后将处理好的数据集打包为大小为64的batch。
def datapipe(dataset, batch_size):
image_transforms = [
vision.Rescale(1.0 / 255.0, 0), # 将像素值重新缩放到 [0, 1]
vision.Normalize(mean=(0.1307,), std=(0.3081,)), # 使用给定的均值和标准差进行归一化
vision.HWC2CHW() # 将图像从高度-宽度-通道(HWC)格式转换为通道-高度-宽度(CHW)格式
]
label_transform = transforms.TypeCast(mindspore.int32) # 将标签转换为整数类型(mindspore.int32)
dataset = dataset.map(image_transforms, 'image')
dataset = dataset.map(label_transform, 'label')
dataset = dataset.batch(batch_size) # 把数据分成batch_size=64个小批次
return dataset
# Map vision transforms and batch dataset
train_dataset = datapipe(train_dataset, 64)
test_dataset = datapipe(test_dataset, 64)
或create_dict_iterator对数据集进行迭代访问,查看数据和标签的shape和datatype。
for image, label in test_dataset.create_tuple_iterator():
print(f"Shape of image [N, C, H, W]: {image.shape} {image.dtype}")
print(f"Shape of label: {label.shape} {label.dtype}")
break
Shape of image [N, C, H, W]: (64, 1, 28, 28) Float32
Shape of label: (64,) Int32
注释:
-
Image(图像):
- 形状为
(64, 1, 28, 28)
的浮点数数组。 64
表示批次大小,即这个批次中有64张图像。1
表示通道数,这里为1,通常表示灰度图像。28
表示图像的高度(高度为28个像素)。28
表示图像的宽度(宽度为28个像素)。
- 形状为
-
Label(标签):
- 形状为
(64,)
的整数数组。 - 包含了对应批次中每张图像的类别标签。
- 在这个例子中,标签是整数,通常用来表示图像对应的类别或者类别的索引。
- 形状为
for data in test_dataset.create_dict_iterator():
print(f"Shape of image [N, C, H, W]: {data['image'].shape} {data['image'].dtype}")
print(f"Shape of label: {data['label'].shape} {data['label'].dtype}")
break
Shape of image [N, C, H, W]: (64, 1, 28, 28) Float32
Shape of label: (64,) Int32
更多细节详见数据集 Dataset与数据变换 Transforms。
网络构建
mindspore.nn
类是构建所有网络的基类,也是网络的基本单元。当用户需要自定义网络时,可以继承nn.Cell
类,并重写__init__
方法和construct
方法。__init__
包含所有网络层的定义,construct
中包含数据(Tensor)的变换过程。
# Define model
class Network(nn.Cell):
def __init__(self):
super().__init__()
self.flatten = nn.Flatten()
self.dense_relu_sequential = nn.SequentialCell(
nn.Dense(28*28, 512),
nn.ReLU(),
nn.Dense(512, 512),
nn.ReLU(),
nn.Dense(512, 10)
)
def construct(self, x):
x = self.flatten(x)
logits = self.dense_relu_sequential(x)
return logits
model = Network()
print(model)
Network< (flatten): Flatten<> (dense_relu_sequential): SequentialCell< (0): Dense<input_channels=784, output_channels=512, has_bias=True> (1): ReLU<> (2): Dense<input_channels=512, output_channels=512, has_bias=True> (3): ReLU<> (4): Dense<input_channels=512, output_channels=10, has_bias=True> > >
注释:
这是神经网络结构描述
-
Flatten 层
- 名称:
flatten
- 功能: 将输入的多维数据(如图像)展平为一维向量。
- 输入维度: 输入的图像通常是 28x28 的二维数组,经过 Flatten 层后,被展平成长度为 784 的一维向量。
- 名称:
SequentialCell(顺序层)
重新编排神经网络模型结构的表格描述如下
层级序号 | 层级类型 | 输入维度 | 输出维度 | 是否有偏置 |
1 | Flatten | 28x28x1 | 784 | N/A |
2 | Dense-ReLU | 784 | 512 | True |
3 | Dense-ReLU | 512 | 512 | True |
4 | Dense | 512 | 10 | True |
在这个重新编排后的表格中:
- 第一层是展平层(Flatten),将输入的二维图像数据展平成一维向量。
- 后续的三个层级分别是 Dense 层和 ReLU 激活函数的组合,用于逐步提取和转换特征。
- 最后一层是一个输出层,将512维的特征空间映射到一个10维的输出空间,适用于多类别分类任务。
解释
-
Flatten 层:将输入的二维图像数据(形状为 (N, 1, 28, 28))展平为一维向量(形状为 (N, 784),其中 N 是批次大小)
-
SequentialCell:这是一个顺序的神经网络层容器,它依次包含三个全连接层(Dense)和它们的激活函数 ReLU。这种顺序结构典型于多层感知机(MLP)或深度神经网络。
-
Dense 层:全连接层,每个神经元与上一层的所有神经元相连,每层包含权重和可选的偏置项。这里的第三层 Dense 输出了 10 个通道,通常用于分类任务,其中每个通道对应一个类别(如果是分类任务,则可能要在最后加 softmax 激活函数)。
这段描述展示了一个典型的图像分类神经网络结构,通过 Flatten 层将图像展平,然后经过多个全连接层和非线性激活函数 ReLU,最终输出一个大小为 10 的向量(分别为0,1,2,3,4,5,6,7,8,9 共10个向量),表示对输入图像的类别预测或特征向量。
更多细节详见网络构建。
模型训练
在模型训练中,一个完整的训练过程(step)需要实现以下三步:
-
正向计算:模型预测结果(logits),并与正确标签(label)求预测损失(loss)。
-
反向传播:利用自动微分机制,自动求模型参数(parameters)对于loss的梯度(gradients)。
-
参数优化:将梯度更新到参数上。
MindSpore使用函数式自动微分机制,因此针对上述步骤需要实现:
-
定义正向计算函数。
-
使用value_and_grad通过函数变换获得梯度计算函数。
-
定义训练函数,使用set_train设置为训练模式,执行正向计算、反向传播和参数优化。
# Instantiate loss function and optimizer
loss_fn = nn.CrossEntropyLoss()
optimizer = nn.SGD(model.trainable_params(), 1e-2)
# 1. Define forward function
def forward_fn(data, label):
logits = model(data)
loss = loss_fn(logits, label)
return loss, logits
# 2. Get gradient function
grad_fn = mindspore.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=True)
# 3. Define function of one-step training
def train_step(data, label):
(loss, _), grads = grad_fn(data, label)
optimizer(grads)
return loss
def train(model, dataset):
size = dataset.get_dataset_size()
model.set_train()
for batch, (data, label) in enumerate(dataset.create_tuple_iterator()):
loss = train_step(data, label)
if batch % 100 == 0:
loss, current = loss.asnumpy(), batch
print(f"loss: {loss:>7f} [{current:>3d}/{size:>3d}]")
除训练外,我们定义测试函数,用来评估模型的性能。
def test(model, dataset, loss_fn):
num_batches = dataset.get_dataset_size()
model.set_train(False)
total, test_loss, correct = 0, 0, 0
for data, label in dataset.create_tuple_iterator():
pred = model(data)
total += len(data)
test_loss += loss_fn(pred, label).asnumpy()
correct += (pred.argmax(1) == label).asnumpy().sum()
test_loss /= num_batches
correct /= total
print(f"Test: \n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
训练过程需多次迭代数据集,一次完整的迭代称为一轮(epoch)。在每一轮,遍历训练集进行训练,结束后使用测试集进行预测。打印每一轮的loss值和预测准确率(Accuracy),可以看到loss在不断下降,Accuracy在不断提高。
epochs = 10
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
train(model, train_dataset)
test(model, test_dataset, loss_fn)
print("Done!")
Epoch 1 ------------------------------- loss: 2.314631 [ 0/938] loss: 1.649031 [100/938] loss: 0.898700 [200/938] loss: 0.848945 [300/938] loss: 0.496686 [400/938] loss: 0.414066 [500/938] loss: 0.370191 [600/938] loss: 0.301758 [700/938] loss: 0.215070 [800/938] loss: 0.456123 [900/938] Test: Accuracy: 90.8%, Avg loss: 0.316470 Epoch 2 ------------------------------- loss: 0.295787 [ 0/938] loss: 0.260117 [100/938] loss: 0.373872 [200/938] loss: 0.192878 [300/938] loss: 0.291981 [400/938] loss: 0.244807 [500/938] loss: 0.273400 [600/938] loss: 0.387756 [700/938] loss: 0.279873 [800/938] loss: 0.197472 [900/938] Test: Accuracy: 92.9%, Avg loss: 0.247251 Epoch 3 ------------------------------- loss: 0.301901 [ 0/938] loss: 0.137954 [100/938] loss: 0.323864 [200/938] loss: 0.163535 [300/938] loss: 0.274175 [400/938] loss: 0.168002 [500/938] loss: 0.307604 [600/938] loss: 0.420769 [700/938] loss: 0.284337 [800/938] loss: 0.242866 [900/938] Test: Accuracy: 94.0%, Avg loss: 0.207698 Epoch 4 ------------------------------- loss: 0.240945 [ 0/938] loss: 0.150102 [100/938] loss: 0.240543 [200/938] loss: 0.339016 [300/938] loss: 0.223620 [400/938] loss: 0.158127 [500/938] loss: 0.080091 [600/938] loss: 0.243693 [700/938] loss: 0.269532 [800/938] loss: 0.147567 [900/938] Test: Accuracy: 94.8%, Avg loss: 0.176856 Epoch 5 ------------------------------- loss: 0.207482 [ 0/938] loss: 0.127693 [100/938] loss: 0.131531 [200/938] loss: 0.070764 [300/938] loss: 0.209297 [400/938] loss: 0.193722 [500/938] loss: 0.248587 [600/938] loss: 0.145101 [700/938] loss: 0.166148 [800/938] loss: 0.124098 [900/938] Test: Accuracy: 95.3%, Avg loss: 0.160972 Epoch 6 ------------------------------- loss: 0.165120 [ 0/938] loss: 0.206427 [100/938] loss: 0.315757 [200/938] loss: 0.082879 [300/938] loss: 0.202446 [400/938] loss: 0.047018 [500/938] loss: 0.137566 [600/938] loss: 0.063785 [700/938] loss: 0.134142 [800/938] loss: 0.063492 [900/938] Test: Accuracy: 95.8%, Avg loss: 0.140456 Epoch 7 ------------------------------- loss: 0.135110 [ 0/938] loss: 0.147678 [100/938] loss: 0.147549 [200/938] loss: 0.098771 [300/938] loss: 0.111349 [400/938] loss: 0.065946 [500/938] loss: 0.134679 [600/938] loss: 0.075286 [700/938] loss: 0.144322 [800/938] loss: 0.139084 [900/938] Test: Accuracy: 96.3%, Avg loss: 0.127183 Epoch 8 ------------------------------- loss: 0.098997 [ 0/938] loss: 0.053554 [100/938] loss: 0.031550 [200/938] loss: 0.087523 [300/938] loss: 0.051703 [400/938] loss: 0.063892 [500/938] loss: 0.093435 [600/938] loss: 0.152330 [700/938] loss: 0.137969 [800/938] loss: 0.110145 [900/938] Test: Accuracy: 96.6%, Avg loss: 0.117406 Epoch 9 ------------------------------- loss: 0.109173 [ 0/938] loss: 0.079318 [100/938] loss: 0.065355 [200/938] loss: 0.123426 [300/938] loss: 0.041408 [400/938] loss: 0.281024 [500/938] loss: 0.047559 [600/938] loss: 0.057589 [700/938] loss: 0.072500 [800/938] loss: 0.085417 [900/938] Test: Accuracy: 96.7%, Avg loss: 0.109177 Epoch 10 ------------------------------- loss: 0.082384 [ 0/938] loss: 0.129326 [100/938] loss: 0.114787 [200/938] loss: 0.174314 [300/938] loss: 0.039538 [400/938] loss: 0.091571 [500/938] loss: 0.165758 [600/938] loss: 0.034662 [700/938] loss: 0.093723 [800/938] loss: 0.055408 [900/938] Test: Accuracy: 96.9%, Avg loss: 0.099216
注释:
根据10次epoch的训练和测试输出,我们可以获得以下分析:
-
训练过程:每个Epoch中的训练损失(loss)从较高的值逐步降低,这表明模型在训练数据上逐渐学习到正确的预测。
-
测试过程:随着Epoch的增加,测试准确率从90.8%迅速上升到96.9%,说明模型随着训练的进行,能够更准确地分类新数据。
-
损失曲线:训练和测试损失都在每个Epoch中稳步下降,这表明模型在学习过程中逐渐减少了预测误差,能够更好地泛化到测试数据。
-
性能稳定性:最终几个Epoch的测试准确率保持在96.6%到96.9%之间,表明模型达到了比较高的分类性能,并且在不同的数据集上能够稳定地保持这一性能水平。
但仅凭这些结果我们无法确定此模型是否过拟合测试集数据。
更多细节详见模型训练。
保存模型
模型训练完成后,需要将其参数进行保存。
# Save checkpoint
mindspore.save_checkpoint(model, "model.ckpt")
print("Saved Model to model.ckpt")
Saved Model to model.ckpt
加载模型
加载保存的权重分为两步:
-
重新实例化模型对象,构造模型。
-
加载模型参数,并将其加载至模型上。
# Instantiate a random initialized model
model = Network()
# Load checkpoint and load parameter to model
param_dict = mindspore.load_checkpoint("model.ckpt")
param_not_load, _ = mindspore.load_param_into_net(model, param_dict)
print(param_not_load)
[]
param_not_load
是未被加载的参数列表,为空时代表所有参数均加载成功。
加载后的模型可以直接用于预测推理。
model.set_train(False)
for data, label in test_dataset:
pred = model(data)
predicted = pred.argmax(1)
print(f'Predicted: "{predicted[:10]}", Actual: "{label[:10]}"')
break
Predicted: "[3 9 6 1 6 7 4 5 2 2]", Actual: "[3 9 6 1 6 7 4 5 2 2]"
更多细节详见保存与加载。
后续:
展示与推理不一致的5张图片
import matplotlib.pyplot as plt
import numpy as np
model.set_train(False)
misclassified_examples = []
for data, label in test_dataset:
pred = model(data)
predicted = pred.argmax(1)
for i in range(len(label)):
if predicted[i] != label[i]:
misclassified_examples.append((data[i], predicted[i], label[i]))
num_images_to_display = min(len(misclassified_examples), 5) # Limit to 5 images
fig, axes = plt.subplots(1, num_images_to_display, figsize=(15, 3))
for i in range(num_images_to_display):
image, predicted_label, actual_label = misclassified_examples[i]
image = np.squeeze(image.numpy())
axes[i].imshow(image, cmap='gray') # Display image in grayscale
axes[i].set_title(f'Predicted: {predicted_label}, Actual: {actual_label}')
axes[i].axis('off')
plt.tight_layout()
plt.show()
总结:根据显示的图像,我们可以看到五个被错误分类的例子(预测为4,而实际标签是2)。