{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 构建网络\n", "\n", "[![下载Notebook](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.8/resource/_static/logo_notebook.png)](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/r1.8/tutorials/zh_cn/advanced/network/mindspore_forward.ipynb) [![下载样例代码](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.8/resource/_static/logo_download_code.png)](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/r1.8/tutorials/zh_cn/advanced/network/mindspore_forward.py) [![查看源文件](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.8/resource/_static/logo_source.png)](https://gitee.com/mindspore/docs/blob/r1.8/tutorials/source_zh_cn/advanced/network/forward.ipynb)\n", "\n", "MindSpore的`Cell`类是构建所有网络的基类,也是网络的基本单元。自定义网络时,需要继承`Cell`类,本章主要介绍网络基本单元`Cell`和自定义前向网络。\n", "\n", "本章主要介绍前向网络模型的构建和网络模型的基本单元,因为不涉及到训练,因此没有反向传播和反向图。\n", "\n", "![learningrate.png](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r1.8/tutorials/source_zh_cn/advanced/network/images/introduction3.png)\n", "\n", "## 网络基本单元 Cell\n", "\n", "当用户需要自定义网络时,需要继承`Cell`类,并重写`__init__`方法和`construct`方法。损失函数、优化器和模型层等本质上也属于网络结构,也需要继承`Cell`类才能实现功能,同样用户也可以根据业务需求自定义这部分内容。\n", "\n", "下面介绍`Cell`的关键成员函数。\n", "\n", "### construct方法\n", "\n", "`Cell`类重写了`__call__`方法,在`Cell`类的实例被调用时,会执行`construct`方法。网络结构在`construct`方法里面定义。\n", "\n", "如下样例中,构建了一个简单的卷积网络,卷积网络在`__init__`中定义,在`construct`方法传入输入数据`x`执行卷积计算,并返回计算结果。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from mindspore import nn\n", "\n", "\n", "class Net(nn.Cell):\n", " def __init__(self):\n", " super(Net, self).__init__()\n", " self.conv = nn.Conv2d(10, 20, 3, has_bias=True, weight_init='normal')\n", "\n", " def construct(self, x):\n", " out = self.conv(x)\n", " return out" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 获取网络参数\n", "\n", "`nn.Cell`中返回参数的方法有`parameters_dict`、`get_parameters`和`trainable_params`。\n", "\n", "- `parameters_dict`:获取网络结构中所有参数,返回一个以key为参数名,value为参数值的OrderedDict。\n", "- `get_parameters`:获取网络结构中的所有参数,返回Cell中Parameter的迭代器。\n", "- `trainable_params`:获取Parameter中`requires_grad`为True的属性,返回可训参数的列表。\n", "\n", "如下示例分别使用上述方法获取网络参数并打印。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "parameters_dict of result:\n", " OrderedDict([('conv.weight', Parameter (name=conv.weight, shape=(20, 10, 3, 3), dtype=Float32, requires_grad=True)), ('conv.bias', Parameter (name=conv.bias, shape=(20,), dtype=Float32, requires_grad=True))])\n", "\n", "get_parameters of result:\n", "Parameter (name=conv.weight, shape=(20, 10, 3, 3), dtype=Float32, requires_grad=True)\n", "Parameter (name=conv.bias, shape=(20,), dtype=Float32, requires_grad=True)\n", "\n", "trainable_params of result:\n", " [Parameter (name=conv.weight, shape=(20, 10, 3, 3), dtype=Float32, requires_grad=True), Parameter (name=conv.bias, shape=(20,), dtype=Float32, requires_grad=True)]\n" ] } ], "source": [ "net = Net()\n", "\n", "# 获取网络结构中的所有参数\n", "result = net.parameters_dict()\n", "print(\"parameters_dict of result:\\n\", result)\n", "\n", "# 获取网络结构中的所有参数\n", "print(\"\\nget_parameters of result:\")\n", "for m in net.get_parameters():\n", " print(m)\n", "\n", "# 获取可训练参数列表\n", "result = net.trainable_params()\n", "print(\"\\ntrainable_params of result:\\n\", result)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 相关属性\n", "\n", "1. cells_and_names\n", "\n", "`cells_and_names`方法是一个迭代器,返回网络中每个`Cell`的名字和它的内容本身。代码样例如下:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('', Net<\n", " (conv): Conv2d\n", " >)\n", "('conv', Conv2d)\n" ] } ], "source": [ "net = Net()\n", "for m in net.cells_and_names():\n", " print(m)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. set_grad\n", "\n", "`set_grad`用于指定网络是否需要计算梯度。在不传入参数调用时,默认设置`requires_grad`为True,在执行前向网络时将会构建用于计算梯度的反向网络。`TrainOneStepCell`和`GradOperation`接口,无需使用`set_grad`,其内部已实现。若用户需要自定义此类训练功能的接口,需要在其内部或者外部设置`set_grad`。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "class CustomTrainOneStepCell(nn.Cell):\n", " def __init__(self, network, optimizer, sens=1.0):\n", " \"\"\"入参有三个:训练网络,优化器和反向传播缩放比例\"\"\"\n", " super(CustomTrainOneStepCell, self).__init__(auto_prefix=False)\n", " self.network = network # 前向网络\n", " self.network.set_grad() # 构建计算梯度的反向网络\n", " self.optimizer = optimizer # 优化器" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`CustomTrainOneStepCell`代码详细内容可参见[自定义训练与评估网络](https://www.mindspore.cn/tutorials/zh-CN/r1.8/advanced/train/train_eval.html#自定义训练网络)\n", "\n", "3. set_train\n", "\n", "`set_train`接口指定模型是否为训练模式,在不传入参数调用时,默认设置的`mode`属性为True。\n", "\n", "在实现训练和推理结构不同的网络时可以通过`training`属性区分训练和推理场景,当`mode`设置为True时,为训练场景;当`mode`设置为False时,为推理场景。\n", "\n", "MindSpore中的`nn.Dropout`算子,根据`Cell`的`mode`属性区分了两种执行逻辑,`mode`为False时直接返回输入,`mode`为True时执行算子。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "training result:\n", " [[[1.4285715 1.4285715 1.4285715]\n", " [1.4285715 0. 0. ]]\n", "\n", " [[1.4285715 1.4285715 1.4285715]\n", " [1.4285715 1.4285715 1.4285715]]]\n", "\n", "infer result:\n", " [[[1. 1. 1.]\n", " [1. 1. 1.]]\n", "\n", " [[1. 1. 1.]\n", " [1. 1. 1.]]]\n" ] } ], "source": [ "import numpy as np\n", "import mindspore as ms\n", "\n", "x = ms.Tensor(np.ones([2, 2, 3]), ms.float32)\n", "net = nn.Dropout(keep_prob=0.7)\n", "\n", "# 执行训练\n", "net.set_train()\n", "output = net(x)\n", "print(\"training result:\\n\", output)\n", "\n", "# 执行推理\n", "net.set_train(mode=False)\n", "output = net(x)\n", "print(\"\\ninfer result:\\n\", output)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "4. to_float\n", "\n", "`to_float`接口递归地在配置了当前`Cell`和所有子`Cell`的强制转换类型,以使当前网络结构以使用特定的Float类型运行,通常在混合精度场景使用。\n", "\n", "如下示例分别对`nn.dense`层使用float32类型和float16类型进行运算,并打印输出结果的数据类型。" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Float32\n", "Float16\n" ] } ], "source": [ "import numpy as np\n", "from mindspore import nn\n", "import mindspore as ms\n", "\n", "# float32进行计算\n", "x = ms.Tensor(np.ones([2, 2, 3]), ms.float32)\n", "net = nn.Dense(3, 2)\n", "output = net(x)\n", "print(output.dtype)\n", "\n", "# float16进行计算\n", "net1 = nn.Dense(3, 2)\n", "net1.to_float(ms.float16)\n", "output = net1(x)\n", "print(output.dtype)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 构建网络\n", "\n", "构建网络时,可以继承`nn.Cell`类,在`__init__`构造函数中申明各个层的定义,在`construct`中实现层之间的连接关系,完成神经网络的前向构造。\n", "\n", "`mindspore.ops`模块提供了基础算子的实现,如神经网络算子、数组算子和数学算子等。\n", "\n", "`mindspore.nn`模块实现了对基础算子的进一步封装,用户可以根据需要,灵活使用不同的算子。\n", "\n", "同时,为了更好地构建和管理复杂的网络,`mindspore.nn`提供了两种容器对网络中的子模块或模型层进行管理,分别为`nn.CellList`和`nn.SequentialCell`两种方式。\n", "\n", "### Ops算子构建网络\n", "\n", "[mindspore.ops](https://www.mindspore.cn/docs/zh-CN/r1.8/api_python/mindspore.ops.html)模块提供了基础算子的实现,如神经网络算子、数组算子和数学算子等。\n", "\n", "用户可使用`mindspore.ops`中的算子来构建一个简单的算法 $f(x)=x^2+w$,示例如下:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 3. 6. 11.]\n" ] } ], "source": [ "import numpy as np\n", "import mindspore as ms\n", "from mindspore import nn, ops\n", "\n", "class Net(nn.Cell):\n", " def __init__(self):\n", " super(Net, self).__init__()\n", " self.mul = ops.Mul()\n", " self.add = ops.Add()\n", " self.weight = ms.Parameter(ms.Tensor(np.array([2, 2, 2]), ms.float32))\n", "\n", " def construct(self, x):\n", " return self.add(self.mul(x, x), self.weight)\n", "\n", "net = Net()\n", "input = ms.Tensor(np.array([1, 2, 3]), ms.float32)\n", "output = net(input)\n", "\n", "print(output)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### nn层构建网络\n", "\n", "尽管`mindspore.ops`模块提供的多样算子可以基本满足网络构建的诉求,但为了在复杂的深度网络中提供更方便易用的接口,[mindspore.nn](https://www.mindspore.cn/docs/zh-CN/r1.8/api_python/mindspore.nn.html#)对`mindspore.ops`算子进行了进一步的封装。\n", "\n", "`mindspore.nn`模块主要包括神经网络(neural network)中常用的卷积层(如`nn.Conv2d`)、池化层(`nn.MaxPool2d`)、非线性激活函数(如`nn.ReLU`)、损失函数(如`nn.LossBase`)、优化器(如`nn.Momentum`)等,为用户的使用提供了便利。\n", "\n", "下面示例代码中,使用`mindspore.nn`模块构建一个Conv + Batch Normalization + ReLu模型网络。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "ConvBNReLU<\n", " (conv): Conv2d\n", " (bn): BatchNorm2d\n", " (relu): ReLU<>\n", " >\n" ] } ], "source": [ "import numpy as np\n", "from mindspore import nn\n", "\n", "class ConvBNReLU(nn.Cell):\n", " def __init__(self):\n", " super(ConvBNReLU, self).__init__()\n", " self.conv = nn.Conv2d(3, 64, 3)\n", " self.bn = nn.BatchNorm2d(64)\n", " self.relu = nn.ReLU()\n", "\n", " def construct(self, x):\n", " x = self.conv(x)\n", " x = self.bn(x)\n", " out = self.relu(x)\n", " return out\n", "\n", "net = ConvBNReLU()\n", "print(net)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 容器构建网络\n", "\n", "为了便于管理和组成更复杂的网络,`mindspore.nn`提供了容器对网络中的子模型块或模型层进行管理,有`nn.CellList`和`nn.SequentialCell`两种方式。\n", "\n", "1. CellList构建网络\n", "\n", "使用`nn.CellList`构造的Cell既可以是模型层,也可以是构建的网络子块。`nn.CellList`支持`append`、`extend`方法和`insert`方法三种方法。\n", "\n", "在运行网络时,可以在construct方法里,使用for循环,运行输出结果。\n", "\n", "- `append(cell)`:在列表末尾添加一个cell。\n", "- `extend(cells)`:将cells添加至列表末尾。\n", "- `insert(index, cell)`:在列表给定的索引之前插入给定的cell。\n", "\n", "如下使用`nn.CellList`构建并执行一个网络,依次包含一个之前定义的模型子块ConvBNReLU、一个Conv2d层、一个BatchNorm2d层和一个ReLU层:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "MyNet<\n", " (build_block): CellList<\n", " (0): ConvBNReLU<\n", " (conv): Conv2d\n", " (bn): BatchNorm2d\n", " (relu): ReLU<>\n", " >\n", " (1): Conv2d\n", " (2): BatchNorm2d\n", " (3): ReLU<>\n", " >\n", " >\n" ] } ], "source": [ "import numpy as np\n", "import mindspore as ms\n", "from mindspore import nn\n", "\n", "class MyNet(nn.Cell):\n", "\n", " def __init__(self):\n", " super(MyNet, self).__init__()\n", " layers = [ConvBNReLU()]\n", " # 使用CellList对网络进行管理\n", " self.build_block = nn.CellList(layers)\n", "\n", " # 使用append方法添加Conv2d层和ReLU层\n", " self.build_block.append(nn.Conv2d(64, 4, 4))\n", " self.build_block.append(nn.ReLU())\n", "\n", " # 使用insert方法在Conv2d层和ReLU层中间插入BatchNorm2d\n", " self.build_block.insert(-1, nn.BatchNorm2d(4))\n", "\n", " def construct(self, x):\n", " # for循环执行网络\n", " for layer in self.build_block:\n", " x = layer(x)\n", " return x\n", "\n", "net = MyNet()\n", "print(net)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "把数据输入到网络模型中:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1, 4, 64, 32)\n" ] } ], "source": [ "input = ms.Tensor(np.ones([1, 3, 64, 32]), ms.float32)\n", "output = net(input)\n", "print(output.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "2. SequentialCell构建网络\n", "\n", "使用`nn.SequentialCell`构造Cell顺序容器,支持子模块以List或OrderedDict格式作为输入。\n", "\n", "不同于`nn.CellList`的是,`nn.SequentialCell`类内部实现了`construct`方法,可以直接输出结果。\n", "\n", "如下示例使用`nn.SequentialCell`构建一个网络,输入为List,网络结构依次包含一个之前定义的模型子块ConvBNReLU、一个Conv2d层、一个BatchNorm2d层和一个ReLU层:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "MyNet<\n", " (build_block): SequentialCell<\n", " (0): ConvBNReLU<\n", " (conv): Conv2d\n", " (bn): BatchNorm2d\n", " (relu): ReLU<>\n", " >\n", " (1): Conv2d\n", " (2): BatchNorm2d\n", " (3): ReLU<>\n", " >\n", " >\n" ] } ], "source": [ "import numpy as np\n", "import mindspore as ms\n", "from mindspore import nn\n", "\n", "class MyNet(nn.Cell):\n", "\n", " def __init__(self):\n", " super(MyNet, self).__init__()\n", "\n", " layers = [ConvBNReLU()]\n", " layers.extend([nn.Conv2d(64, 4, 4),\n", " nn.BatchNorm2d(4),\n", " nn.ReLU()])\n", " self.build_block = nn.SequentialCell(layers) # 使用SequentialCell对网络进行管理\n", "\n", " def construct(self, x):\n", " return self.build_block(x)\n", "\n", "net = MyNet()\n", "print(net)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "把数据输入到网络模型中:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(1, 4, 64, 32)\n" ] } ], "source": [ "input = ms.Tensor(np.ones([1, 3, 64, 32]), ms.float32)\n", "output = net(input)\n", "print(output.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "如下示例使用`nn.SequentialCell`构建一个网络,输入为OrderedDict:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "MyNet<\n", " (build_block): SequentialCell<\n", " (ConvBNReLU): ConvBNReLU<\n", " (conv): Conv2d\n", " (bn): BatchNorm2d\n", " (relu): ReLU<>\n", " >\n", " (conv): Conv2d\n", " (norm): BatchNorm2d\n", " (relu): ReLU<>\n", " >\n", " >\n", "(1, 4, 64, 32)\n" ] } ], "source": [ "import numpy as np\n", "import mindspore as ms\n", "from mindspore import nn\n", "from collections import OrderedDict\n", "\n", "class MyNet(nn.Cell):\n", "\n", " def __init__(self):\n", " super(MyNet, self).__init__()\n", " layers = OrderedDict()\n", "\n", " # 将cells加入字典\n", " layers[\"ConvBNReLU\"] = ConvBNReLU()\n", " layers[\"conv\"] = nn.Conv2d(64, 4, 4)\n", " layers[\"norm\"] = nn.BatchNorm2d(4)\n", " layers[\"relu\"] = nn.ReLU()\n", "\n", " # 使用SequentialCell对网络进行管理\n", " self.build_block = nn.SequentialCell(layers)\n", "\n", " def construct(self, x):\n", " return self.build_block(x)\n", "\n", "net = MyNet()\n", "print(net)\n", "\n", "input = ms.Tensor(np.ones([1, 3, 64, 32]), ms.float32)\n", "output = net(input)\n", "print(output.shape)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## nn与ops关系\n", "\n", "`mindspore.nn`模块是Python实现的模型组件,对低阶API的封装,主要包括神经网络模型相关的各种模型层、损失函数、优化器等。\n", "\n", "同时`mindspore.nn`也提供了部分与`mindspore.ops`算子同名的接口,主要作用是对`mindspore.ops`算子进行进一步封装,为用户提供更友好的API。用户也可使用`mindspore.ops`算子根据实际场景实现自定义的网络。\n", "\n", "如下示例使用`mindspore.ops.Conv2D`算子实现卷积计算功能,即`nn.Conv2d`算子功能。" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "import mindspore.nn as nn\n", "import mindspore.ops as ops\n", "import mindspore as ms\n", "from mindspore.common.initializer import initializer\n", "\n", "\n", "class Net(nn.Cell):\n", " def __init__(self, in_channels=10, out_channels=20, kernel_size=3):\n", " super(Net, self).__init__()\n", " self.conv2d = ops.Conv2D(out_channels, kernel_size)\n", " self.bias_add = ops.BiasAdd()\n", " self.weight = ms.Parameter(\n", " initializer('normal', [out_channels, in_channels, kernel_size, kernel_size]),\n", " name='conv.weight')\n", " self.bias = ms.Parameter(initializer('normal', [out_channels]), name='conv.bias')\n", "\n", " def construct(self, x):\n", " \"\"\"输入数据x\"\"\"\n", " output = self.conv2d(x, self.weight)\n", " output = self.bias_add(output, self.bias)\n", " return output" ] } ], "metadata": { "kernelspec": { "display_name": "MindSpore", "language": "python", "name": "mindspore" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 }