1. 保存与加载
在训练网络模型的过程中,实际上我们希望保存中间和最后的结果,用于微调(fine-tune)和后续的模型推理与部署,本章节我们将介绍如何保存与加载模型。
1.1 导入依赖
# 导入numpy库,并将其重命名为np,以便在代码中引用
import numpy as np
# 导入MindSpore库,这是华为推出的一个开源深度学习框架,用于构建和训练神经网络
import mindspore
# 从MindSpore库中导入nn模块,这个模块包含了构建神经网络所需的各种层和函数
from mindspore import nn
# 从MindSpore库中导入Tensor模块,Tensor是MindSpore中用于表示张量的类
from mindspore import Tensor
1.1定义神经网络模型
# 定义一个函数,该函数创建一个简单的全连接神经网络模型
def network():
# 使用nn.SequentialCell创建一个层序列,这是一个容器类,可以包含多个层
model = nn.SequentialCell(
# 第一个层是一个Flatten层,用于将输入的二维图像数据展平为一维向量
nn.Flatten(),
# 第二个层是一个全连接层,将28x28的输入节点映射到512个节点
nn.Dense(28*28, 512),
# 第三个层是一个ReLU激活函数,用于非线性变换
nn.ReLU(),
# 第四个层是一个全连接层,将512个节点映射到512个节点
nn.Dense(512, 512),
# 第五个层是一个ReLU激活函数,用于非线性变换
nn.ReLU(),
# 第六个层是一个全连接层,将512个节点映射到10个节点,对应于10个类别的输出
nn.Dense(512, 10)
)
# 返回创建好的模型
return model
1.2 保存和加载模型权重
1.2.1 保存模型
保存模型使用save_checkpoint
接口,传入网络和指定的保存路径:
# 创建一个神经网络模型实例
model = network()
# 使用MindSpore的save_checkpoint函数将模型的检查点保存到文件
# 第一个参数是模型对象
# 第二个参数是文件名,这里保存为"model.ckpt"
mindspore.save_checkpoint(model, "model.ckpt")
# 打印模型结构
print(model)
输出:
SequentialCell<
(0): Flatten<>
(1): Dense<input_channels=784, output_channels=512, has_bias=True>
(2): ReLU<>
(3): Dense<input_channels=512, output_channels=512, has_bias=True>
(4): ReLU<>
(5): Dense<input_channels=512, output_channels=10, has_bias=True>
>
模型大小估算:
model_capacity ≈ 模型参数 * 数据精度(默认是int32类型)大小 = [(784512+512) + (512512+512) + (512*10 +10)] *32bit/8(bit/Byte)= 669704 *4 = 2678824 Byte
可以看到,模型参数量约为67W,占用空间大小应约为2678824字节
实际该模型文件大小为2679017。可以说非常接近了,剩下的字节应该就是文件类型描述符加模型结构描述符之类的内容了。
所以当我们已知一个模型的参数量和参数精度后,实际就可以估算出模型占用的磁盘空间大小了。
1.2.2 加载模型
要加载模型权重,需要先创建相同模型的实例,然后使用load_checkpoint
和load_param_into_net
方法加载参数。
# 创建一个神经网络模型
model = network()
# 使用MindSpore的load_checkpoint函数从文件中加载模型的参数和优化器状态
# 参数是检查点的文件名,这里加载的文件名为"model.ckpt"
param_dict = mindspore.load_checkpoint("model.ckpt")
# 使用MindSpore的load_param_into_net函数将加载的参数字典加载到模型中
# 第一个参数是模型对象
# 第二个参数是参数字典
# 返回值是一个元组,第一个元素是未加载的参数列表,第二个元素是加载的参数列表
param_not_load, _ = mindspore.load_param_into_net(model, param_dict)
# 打印未加载的参数列表,如果加载成功,这个列表应该是空的
print(param_not_load)
输出:
[]
param_not_load
是未被加载的参数列表,为空时代表所有参数均加载成功。
1.3 保存和加载MindIR
除Checkpoint外,MindSpore提供了云侧(训练)和端侧(推理)统一的中间表示(Intermediate Representation,IR)。可使用export
接口直接将模型保存为MindIR。
# 创建网络模型
model = network()
# 创建一个Tensor对象,它包含一个大小为[1, 1, 28, 28]的矩阵,所有元素都是1,数据类型为float32
inputs = Tensor(np.ones([1, 1, 28, 28]).astype(np.float32))
# 使用mindspore.export函数将模型导出为MINDIR格式
# 第一个参数是模型对象
# 第二个参数是输入数据,这里使用了一个Tensor对象作为示例
# 第三个参数是文件名,这里导出的文件名为"model"
# 第四个参数是文件格式,这里设置为"MINDIR",表示导出的模型格式
mindspore.export(model, inputs, file_name="model", file_format="MINDIR")
MindIR同时保存了Checkpoint和模型结构,因此需要定义输入Tensor来获取输入shape。
已有的MindIR模型可以方便地通过load
接口加载,传入nn.GraphCell
即可进行推理。
nn.GraphCell
仅支持图模式。
# 设置MindSpore的执行模式为GRAPH_MODE
mindspore.set_context(mode=mindspore.GRAPH_MODE)
# 加载之前导出的MINDIR模型
graph = mindspore.load("model.mindir")
# 创建一个GraphCell对象,它将graph作为其成员
model = nn.GraphCell(graph)
# 使用模型对输入数据进行前向计算,得到输出
outputs = model(inputs)
# 打印输出的形状
print(outputs.shape)
输出:
2. 小结
本文主要介绍了模型的保存和加载,都包括检查点checkpoint
和统一中间表示MindIR
(Intermediate Representation)两种方法,还介绍了模型大小的估算方法。