模型创建

内置模块mindspore.nn

一般来说,神经网络模型由多个数据操作层组成,mindspore.nn提供了各种网络基础模块。在这里封装好了各种方法,卷积层通过Conv2d传参创建,采样层中的平均池化AvgPool2d和最大池化MaxPool2d都有对应的方法,激活函数提供了ReLUSigmoidTanhLeakyReLUPReLU等方法,优化器有SGDMomentumAdagradRMSPropAdam等方法,除此之外还有FlattenDenseSoftmaxCrossEntropyWithLogits等其他特别方便的方法供我们调用。

MindSpore的Cell类是构建所有网络的基类,也是网络的基本单元。构建神经网络时,需要继承Cell类,并重写__init__方法和construct方法。

LeNet-5网络模型的创建示例

import mindspore.nn as nn

class LeNet5(nn.Cell):
    """
    LeNet-5网络结构
    """
    def __init__(self, num_class=10, num_channel=1):
        super(LeNet5, self).__init__()
        # 卷积层,输入的通道数为num_channel,输出的通道数为6,卷积核大小为5*5
        self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode='valid')
        # 卷积层,输入的通道数为6,输出的通道数为16,卷积核大小为5*5
        self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid')
        # 全连接层,输入个数为16*5*5,输出个数为120
        self.fc1 = nn.Dense(16 * 5 * 5, 120)
        # 全连接层,输入个数为120,输出个数为84
        self.fc2 = nn.Dense(120, 84)
        # 全连接层,输入个数为84,分类的个数为num_class
        self.fc3 = nn.Dense(84, num_class)
        # ReLU激活函数
        self.relu = nn.ReLU()
        # 池化层
        self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2)
        # 多维数组展平为一维数组
        self.flatten = nn.Flatten()

    def construct(self, x):
        # 使用定义好的运算构建前向网络
        x = self.conv1(x)
        x = self.relu(x)
        x = self.max_pool2d(x)
        x = self.conv2(x)
        x = self.relu(x)
        x = self.max_pool2d(x)
        x = self.flatten(x)
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        x = self.relu(x)
        x = self.fc3(x)
        return x

在MindSpore里面是怎样创建网络模型的?

构建神经网络就像搭积木一样,在init中定义好了我们的积木,那么construct就是搭积木的过程。

首先编译使用到的算子,设定好当前一个卷积的kernels的大小,通道数和pad_mode,注意这个参数有一般有valid和same选择。定义完算子就到了construct模块,每一层网络其实就是一行代码,就像是搭积木,一层一层,一行一行的堆叠起来,比如LeNet-5网络模型,有卷积层、池化层、全连接层,按照一定的顺序搭建好,最后返回网络模型的结果,网络模型就创建好了。

pad_mode参数的选择?
因为卷积的特殊计算方式,在卷积核进行滤波操作的时候,图像最边缘的地方因为卷积核没办法去覆盖,所以最终计算的时候就会去掉边缘的值。比如我们输入图像为32 * 32,那么输出图像的大小希望是28*28,那么就需要选择valid模式;如果我们需要输入图像的大小和输出图像的大小相同,那么就选择same模式,因为这个模式会在卷积之前自动对边缘值进行补充,所以卷积的输入和输出大小是一样的。

模型的参数怎样查看?

网络内部的卷积层和全连接层等实例化后,即具有权重参数和偏置参数,这些参数会在训练过程中不断进行优化,在训练过程中可通过 get_parameters() 来查看网络各层的名字、形状、数据类型和是否反向计算等信息。

from mindvision.classification.models import lenet

# num_classes表示分类的数量,pretrained表示是否使用与训练模型进行训练
model = lenet(num_classes=10, pretrained=False)

for m in model.get_parameters():
    print(f"layer:{m.name}, shape:{m.shape}, dtype:{m.dtype}, requeires_grad:{m.requires_grad}")

为什么要停止梯度计算?

在神经网络构建中,有一个神经或者卷积进行了梯度计算,另一个分支想要控制它不要进行梯度计算,或者在网络模型中插入其他图像处理操作,不需要对图像处理操作的算子进行求导。为了更加灵活的控制整个系统的梯度什么时候进行求导,什么时候停止求导,Ops.stop_gradient停止计算梯度。

模型训练

模型训练一般分为四个步骤:

  1. 构建数据集。
  2. 定义神经网络。
  3. 定义超参、损失函数及优化器。
  4. 输入训练轮次和数据集进行训练。

超参(Hyper-parametric):超参是可以调整的参数,可以控制模型训练优化的过程,不同的超参数值可能会影响模型训练效果和收敛速度。一般超参有训练轮次(epoch)、批次大小(batch_size)、学习率(learning_rate)等。可以借助MindInsight工具,在训练时对参数进行可视化的监控,便于调试和调优。

损失函数:损失函数是用来评价模型的预测值和目标值之间的误差。mindspore.nn.loss也提供了许多其他常用的损失函数,如SoftmaxCrossEntropyWithLogitsMSELossSmoothL1Loss等。

优化器函数:有时候我们的网络模型训练不收敛,精度不达标,不一定是我们的网络模型构建出问题了,还有可能是优化器没有选择好,或者超参没有设置好。mindspore.nn提供了许多常用的优化器函数,这些在前文已经提到过了,就不过多赘述。

训练过程中会打印loss值,loss值会波动,但总体来说loss值会逐步减小,精度逐步提高。每个人运行的loss值有一定随机性,不一定完全相同。

训练:

模型训练的逻辑其实很简单,在模型训练之前,设置好网络模型的超参,定义好损失函数、优化器,然后用模型调用train方法把相关对象传入,就正式开始训练了。通过LossMonitor函数,可以在训练过程中打印相关的信息,包括epoch、step、loss值、每个step的时间time。

在训练过程中,一般打印的loss值会波动,但总体来说loss值会逐步减小,精度逐步提高。每次运行的loss值有一定随机性,对于成功的训练,通过loss的平均值可以看出是逐渐变小的,也就是精度逐步提高。

Logo

昇腾计算产业是基于昇腾系列(HUAWEI Ascend)处理器和基础软件构建的全栈 AI计算基础设施、行业应用及服务,https://devpress.csdn.net/organization/setting/general/146749包括昇腾系列处理器、系列硬件、CANN、AI计算框架、应用使能、开发工具链、管理运维工具、行业应用及服务等全产业链

更多推荐