1.数据预处理
-
- 批量化:
dataset.batch(batch_size)
:将数据分批,每批batch_size
个样本。
- 应用变换:
dataset.map(image_transforms, 'image')
:对图像应用上述image_transforms
变换。dataset.map(label_transform, 'label')
:对标签应用label_transform
变换。
- 加载数据集:
MnistDataset(path)
:从指定路径加载MNIST数据集。
- 定义标签变换:
transforms.TypeCast(mindspore.int32)
:将标签转换为mindspore.int32
类型。
- 定义图像变换:
vision.Rescale(1.0 / 255.0, 0)
:将像素值从0-255缩放到0-1。vision.Normalize(mean=(0.1307,), std=(0.3081,))
:对图像进行归一化,减去均值,除以标准差。vision.HWC2CHW()
:将图像从HWC(高度、宽度、通道)格式转换为CHW(通道、高度、宽度)格式,这是MindSpore模型所需的输入格式。
datapipe
函数:是核心函数,用于构建数据处理管道,包括以下步骤:- 使用下载好的数据集对数据进行预处理包括:图像/标签变换、批量化等常见的数据处理步骤。
-
2.定义网络模型
__init__(self)
: 构造函数,初始化网络层。self.flatten = nn.Flatten()
: 创建一个Flatten层,将输入图像展平为一维向量。self.dense_relu_sequential = nn.SequentialCell(...)
: 创建一个SequentialCell容器,按顺序包含以下层:nn.Dense(28*28, 512)
: 全连接层,输入维度是 28x28 (MNIST图像尺寸),输出维度是 512。nn.ReLU()
: ReLU激活函数。nn.Dense(512, 512)
: 全连接层,输入维度和输出维度都是 512。nn.ReLU()
: ReLU激活函数。nn.Dense(512, 10)
: 全连接层,输入维度是 512,输出维度是 10 (MNIST的类别数)。
construct(self, x)
: 定义模型的前向传播过程。x = self.flatten(x)
: 将输入图像展平。logits = self.dense_relu_sequential(x)
: 将展平后的图像输入到SequentialCell中,得到模型的输出logits
(未经softmax处理的分类分数)。return logits
: 返回模型的输出。
-
3. 定义超参、损失函数和优化器
- epochs = 3
batch_size = 64
learning_rate = 1e-2
loss_fn = nn.CrossEntropyLoss()
optimizer = nn.SGD(model.trainable_params(), learning_rate=learning_rate) - 超参
- 超参(Hyperparameters)是可以调整的参数,可以控制模型训练优化的过程,不同的超参数值可能会影响模型训练和收敛速度。
- 训练轮次(epoch):训练时遍历数据集的次数。
- 批次大小(batch size):数据集进行分批读取训练,设定每个批次数据的大小。batch size过小,花费时间多,同时梯度震荡严重,不利于收敛;batch size过大,不同batch的梯度方向没有任何变化,容易陷入局部极小值,因此需要选择合适的batch size,可以有效提高模型精度、全局收敛。
- 学习率(learning rate):如果学习率偏小,会导致收敛的速度变慢,如果学习率偏大,则可能会导致训练不收敛等不可预测的结果。梯度下降法被广泛应用在最小化模型误差的参数优化算法上。梯度下降法通过多次迭代,并在每一步中最小化损失函数来预估模型的参数。学习率就是在迭代过程中,会控制模型的学习进度。
- 损失函数
- 损失函数(loss function)用于评估模型的预测值(logits)和目标值(targets)之间的误差。训练模型时,随机初始化的神经网络模型开始时会预测出错误的结果。损失函数会评估预测结果与目标值的相异程度,模型训练的目标即为降低损失函数求得的误差。
- 优化器
- 模型优化(Optimization)是在每个训练步骤中调整模型参数以减少模型误差的过程。MindSpore提供多种优化算法的实现,称之为优化器(Optimizer)。优化器内部定义了模型的参数优化过程(即梯度如何更新至模型参数),所有优化逻辑都封装在优化器对象中。在这里,我们使用SGD(Stochastic Gradient Descent)优化器。
- epochs = 3
-
4.训练与评估
- 设置了超参、损失函数和优化器后,我们就可以循环输入数据来训练模型。一次数据集的完整迭代循环称为一轮(epoch)。每轮执行训练时包括两个步骤:
训练:迭代训练数据集,并尝试收敛到最佳参数。验证/测试:迭代测试数据集,以检查模型性能是否提升。
- 使用函数式自动微分,需先定义正向函数
forward_fn
,使用value_and_grad获得微分函数grad_fn
。然后,我们将微分函数和优化器的执行封装为train_step
函数,接下来循环迭代数据集进行训练即可 - 定义前向传播函数 (
forward_fn
)- 接受
data
(输入数据)和label
(标签)作为输入。 - 将数据传入模型 (
model(data)
) 得到输出logits
。 - 使用损失函数 (
loss_fn
) 计算logits
和label
之间的损失loss
。 - 返回
loss
和logits
。
- 接受
-
获取梯度函数 (grad_fn
)mindspore.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=True)
forward_fn
: 前向传播函数。None
: 不计算任何参数的梯度,因为我们只关注优化器的参数。optimizer.parameters
: 要计算梯度的参数列表,通常是模型的参数。has_aux=True
: 表示forward_fn
返回多个值(这里是loss
和logits
)。- 返回一个函数
grad_fn
,它接受data
和label
,返回(loss, logits)
和对应参数的梯度。
-
定义单步训练函数 (train_step
)- 接受
data
和label
。 - 调用
grad_fn(data, label)
获取(loss, logits)
和梯度grads
。 - 使用优化器 (
optimizer
) 更新模型参数。 - 返回
loss
。
- 接受
-
定义训练循环 (train_loop
)- 接受
model
(模型)和dataset
(数据集)。 size = dataset.get_dataset_size()
: 获取数据集大小。model.set_train()
: 将模型设置为训练模式。
- 接受
- 遍历数据集:
for batch, (data, label) in enumerate(dataset.create_tuple_iterator()):
- 获取当前批次的
data
和label
。
- 获取当前批次的
loss = train_step(data, label)
: 执行单步训练。- 打印训练进度(每100个批次打印一次)。
- 定义测试循环函数 (
test_loop
)- 接受
model
(模型)、dataset
(数据集)和loss_fn
(损失函数)作为输入。 num_batches = dataset.get_dataset_size()
: 获取测试集中的批次数。model.set_train(False)
: 将模型设置为评估模式(关闭 Dropout 等)。- 初始化
total
(样本总数)、test_loss
(总损失)和correct
(正确预测数)为0。
- 接受
- 遍历数据集:
for data, label in dataset.create_tuple_iterator()
: 获取每个批次的data
和label
。pred = model(data)
: 模型对数据进行预测。total += len(data)
: 累加样本总数。test_loss += loss_fn(pred, label).asnumpy()
: 累加损失值。correct += (pred.argmax(1) == label).asnumpy().sum()
: 累加正确预测数。pred.argmax(1)
: 获取预测结果中概率最大的类别索引。(pred.argmax(1) == label)
: 判断预测是否正确。.asnumpy().sum()
: 转换为NumPy数组并求和。
test_loss /= num_batches
: 计算平均损失。correct /= total
: 计算准确率。- 打印测试结果:准确率和平均损失。
- 定义损失函数和优化器
loss_fn = nn.CrossEntropyLoss()
: 使用交叉熵损失函数,适用于多分类问题。optimizer = nn.SGD(model.trainable_params(), learning_rate=learning_rate)
: 使用随机梯度下降(SGD)优化器,传入模型的可训练参数和学习率。
- 训练和测试循环
for t in range(epochs)
: 迭代指定的轮数(epochs
)。- 打印当前轮数。
train_loop(model, train_dataset)
: 调用之前的train_loop
函数进行训练。test_loop(model, test_dataset, loss_fn)
: 调用test_loop
函数进行测试。
- 打印 "Done!"。
- 设置了超参、损失函数和优化器后,我们就可以循环输入数据来训练模型。一次数据集的完整迭代循环称为一轮(epoch)。每轮执行训练时包括两个步骤:
- 批量化: