Mindspore初学(三)

Mindspore初学(三)

1.创建网络

1.1定义模型

以构建LeNet-5网络为例,展示使用mindspore.nn建立神经网络模型
模型结构如下:
在这里插入图片描述
共含有一个输入层,2个卷积层(C),2个子采样层(S),3个全连接层(F)。
输入层:假设数据集是MNIST数据集,该数据集中的样本数据都是规格为32×32的灰度图,我们以1个样本图片为例。那么我们输入的图片规格就是1×1×32×32,表示一个通道输入1个32×32的数组。

C1层:C1层中数组规格为6×1×28×28,从1×1×32×32卷积得到。首先需要6个批次的卷积数组,每一个批次中都有1个规格为5×5的卷积数组,卷积步幅默认为1。即卷积数组规格为6×1×5×5。

该卷积层共有6+1×5×5×6=156个参数,其中6个偏置参数。这一层网络**有6×1×28×28=4704个节点,每个节点和当前层5×5=25个节点相连,所以本层卷积层共有6×(1×28×28)×(1×5×5+1)=122304个全连接。

S2层:S2层的数组规格为6×1×14×14,从1×1×28×28卷积得到。使用的是2×2,步幅为1的最大池化操作,所以并不改变批次数,只是将每一个输入数组从28×28降到14×14的输出数组。

该池化层共有6×2=12个可训练参数,以及6×(1×14×14)×(2×2+1)=5880个全连接。

C3层:C3层的数组规格为16×1×10×10,从6×1×14×14卷积得到。输出通道数数改变,所以卷积数组需要16批卷积数组,每一批中有6个卷积核与输入通道对应,每一个卷积数组规格都是5×5,步幅为1。即卷积数组规格为16×6×5×5。

该卷积层共有16+1×5×5×16=2416个参数,其中16个偏置参数。这一层网络**有16×1×10×10=1600个节点,每个节点和当前层5×5=25个节点相连,所以本层卷积层共有16×(1×10×10)×(1×5×5+1)=41600个全连接。

S4层:S4层的数组规格为16×1×5×5,这一层池化与S2层池化设置相同。所以输出数组只改变每一个数组的规格,不改变数量。

该池化层共有16×2=32个可训练参数,以及16×(1×5×5)×(2×2+1)=2000个全连接。

C5层:C5层是规格为120×1的一维向量,那么需要将S4层数组转换成一维向量,输入的数组规格是1×(16×1×5×)=1×400。使用全连接层将1×400转为1×120的向量。在全连接层中,每一个节点计算处结果后,都需要再经过激活函数计算,得出的值为输出的值。

该连接层共有5×5×16=400个输入节点,参数个数为5×5×16×120+120=48120个,输出节点120个。

F6层:F6层是规格为84×1的一维向量,与C5层计算相同,也是通过全连接层计算得到。为什么要转成84个神经元向量呢,如下图中所示,是所有字符标准格式,规格为12×7.所以有84个像素点,然后使用F6层的向量与这些标准图计算相似度。

该连接层共有120个输入节点,参数个数为120×84+84=10164个,输出节点84个。
MindSpore的Cell类是构建所有网络的基类,也是网络的基本单元。构建神经网络时,需要继承Cell类,并重写__init__方法和construct方法。
通过图片可以看出这个模型有很多层网络,每一层网络就是一行代码,就像搭积木一样将模型搭建出来。(init方法就是定义积木,而construct就是搭积木的过程)


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假设是32*32的图片
        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
model = LeNet5()##初始化模型
print(model)

输出结果如下:

LeNet5<
  (conv1): Conv2d<input_channels=1, output_channels=6, kernel_size=(5, 5), stride=(1, 1), pad_mode=valid, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=normal, bias_init=zeros, format=NCHW>
  (conv2): Conv2d<input_channels=6, output_channels=16, kernel_size=(5, 5), stride=(1, 1), pad_mode=valid, padding=0, dilation=(1, 1), group=1, has_bias=False, weight_init=normal, bias_init=zeros, format=NCHW>
  (fc1): Dense<input_channels=400, output_channels=120, has_bias=True>
  (fc2): Dense<input_channels=120, output_channels=84, has_bias=True>
  (fc3): Dense<input_channels=84, output_channels=10, has_bias=True>
  (relu): ReLU<>
  (max_pool2d): MaxPool2d<kernel_size=2, stride=2, pad_mode=VALID>
  (flatten): Flatten<>
  >

将LeNet5网络模型打印出来,第一个算子就是卷积,里面有很多参数,有些参数没有做设置时,会使用默认值,比如:weight_init默认使用高斯分布

1.2模型层

nn.Conv2d层,给网络中加入卷积函数,帮助神经网络提取特征。
加入nn.ReLU层,给网络中加入非线性的激活函数,帮助神经网络学习各种复杂的特征。
初始化nn.MaxPool2d层,将6×28×28的张量降采样为6×7×7的张量。

max_pool2d = nn.MaxPool2d(kernel_size=4, stride=4)
input_x = ms.Tensor(np.ones([1, 6, 28, 28]), ms.float32)

print(max_pool2d(input_x).shape)#(1, 6, 7, 7)

初始化nn.Flatten层,将1×16×5×5的四维张量转换为400个连续元素的二维张量。


flatten = nn.Flatten()
input_x = ms.Tensor(np.ones([1, 16, 5, 5]), ms.float32)
output = flatten(input_x)

print(output.shape)

初始化nn.Dense层,对输入矩阵进行线性变换。

dense = nn.Dense(400, 120, weight_init='normal')
input_x = ms.Tensor(np.ones([1, 400]), ms.float32)
output = dense(input_x)

print(output.shape)

1.3快速建立模型

通过调用接口来建立LeNet-5网络模型,lenet接口参数:
num_classes:分类的数量
pretrained:是否使用预训练模型进行训练

from mindvision.classification.models import lenet

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}")

2.自动微分

自动微分是深度学习框架的灵魂,有了写模型就只需要关注前向传播,将所有复杂的求导、反传过程都留给框架。一般而言,自动微分指一种自动求某个函数其导数的方法。在机器学习中,这些导数可以更新权重。在更广泛的自然科学中,这些导数也能用于各种后续计算。

2.1计算一阶导数方法

mindspore.ops.GradOperation (get_all=False, get_by_list=False, sens_param=False)

get_all : 决定着是否根据输出对输入进行求导。
get_by_list : 决定着是否对神经网络内的参数权重求导。
sens_param : 对网络的输出进行乘积运算后再求导。(通过对网络的输出值进行缩放后再进行求导)

import mindspore.ops as ops
from mindspore import Tensor
from mindspore import ParameterTuple, Parameter
from mindspore import dtype as mstype


class Net(nn.Cell):
    def __init__(self):
        super(Net, self).__init__()
        self.matmul = ops.MatMul()
        self.z = Parameter(Tensor(np.array([1.0, 1.0, 1.0], np.float32)), name='z')

    def construct(self, x, y):
        x = x * self.z
        out = self.matmul(x, y)
        return out
    

model = Net()
x = Tensor([[0.8, 0.6, 0.2], [1.8, 1.3, 1.1]], dtype=mstype.float32)
y = Tensor([[0.11, 3.3, 1.1], [1.1, 0.2, 1.4], [1.1, 2.2, 0.3]], dtype=mstype.float32)
result = model(x, y)
print(result)

如果想对所有的输入求梯度, ops.GradOperation(get_all=True)

x = Tensor([[0.8, 0.6, 0.2], [1.8, 1.3, 1.1]], dtype=mstype.float32)
y = Tensor([[0.11, 3.3, 1.1], [1.1, 0.2, 1.4], [1.1, 2.2, 0.3]], dtype=mstype.float32)
output = GradNetWrtX(Net())(x, y)

2.2对权重求一阶导

对权重求一阶导数其实与前面相比有两个地方要更改:

  1. 求导函数要写明对权重求导,即传入参数 get_by_list=True
    即:self.grad_op = ops.GradOperation(get_by_list=True)

  2. 具体求导时要传入具体待求导的参数(即,权重):self.params = ParameterTuple(net.trainable_params()) gradient_function = self.grad_op(self.net, self.params)
    需要知道的一点是如果我们设置了对权重求梯度,则默认不会再对输入求梯度


import mindspore as ms

class GradNet(nn.Cell):
    def __init__(self, net):
        super(GradNet, self).__init__()
        self.net = net
        self.params = ms.ParameterTuple(net.trainable_params())
        self.grad_op = ops.GradOperation(get_by_list=True)  # 设置对权重参数进行一阶求导

    def construct(self, x):
        gradient_function = self.grad_op(self.net, self.params)
        return gradient_function(x)


# 对函数进行求导计算
x = ms.Tensor([100], dtype=ms.float32)
fx = GradNet(Net())(x)

# 打印结果
print(f"wgrad: {fx[0]}\nbgrad: {fx[1]}")
#wgrad: [100.]
#bgrad: [1.]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值