昇思25天学习打卡营第2天|快速入门

🤖 快速入门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_tuple_iterator 

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>
    >
  >

注释:

这是神经网络结构描述

  1. Flatten 层

    • 名称: flatten
    • 功能: 将输入的多维数据(如图像)展平为一维向量。
    • 输入维度: 输入的图像通常是 28x28 的二维数组,经过 Flatten 层后,被展平成长度为 784 的一维向量。

SequentialCell(顺序层)

重新编排神经网络模型结构的表格描述如下

层级序号层级类型输入维度输出维度是否有偏置
1Flatten    28x28x1    784      N/A
2Dense-ReLU784        512      True      
3Dense-ReLU512        512        True      
4Dense      512        10True      

在这个重新编排后的表格中:

- 第一层是展平层(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)需要实现以下三步:

  1. 正向计算:模型预测结果(logits),并与正确标签(label)求预测损失(loss)。

  2. 反向传播:利用自动微分机制,自动求模型参数(parameters)对于loss的梯度(gradients)。

  3. 参数优化:将梯度更新到参数上。

MindSpore使用函数式自动微分机制,因此针对上述步骤需要实现:

  1. 定义正向计算函数。

  2. 使用value_and_grad通过函数变换获得梯度计算函数。

  3. 定义训练函数,使用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的训练和测试输出,我们可以获得以下分析:

  1. 训练过程:每个Epoch中的训练损失(loss)从较高的值逐步降低,这表明模型在训练数据上逐渐学习到正确的预测。

  2. 测试过程:随着Epoch的增加,测试准确率从90.8%迅速上升到96.9%,说明模型随着训练的进行,能够更准确地分类新数据。

  3. 损失曲线:训练和测试损失都在每个Epoch中稳步下降,这表明模型在学习过程中逐渐减少了预测误差,能够更好地泛化到测试数据。

  4. 性能稳定性:最终几个Epoch的测试准确率保持在96.6%到96.9%之间,表明模型达到了比较高的分类性能,并且在不同的数据集上能够稳定地保持这一性能水平。

但仅凭这些结果我们无法确定此模型是否过拟合测试集数据。

更多细节详见模型训练

保存模型

模型训练完成后,需要将其参数进行保存。

# Save checkpoint
mindspore.save_checkpoint(model, "model.ckpt")
print("Saved Model to model.ckpt")
Saved Model to model.ckpt

加载模型

加载保存的权重分为两步:

  1. 重新实例化模型对象,构造模型。

  2. 加载模型参数,并将其加载至模型上。

# 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)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值