diff --git a/tutorials/source_zh_cn/advanced/dataset.rst b/tutorials/source_zh_cn/advanced/dataset.rst
new file mode 100644
index 0000000000000000000000000000000000000000..78314b6cacf8a8a38919c2ced7faa6179c20c08d
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset.rst
@@ -0,0 +1,14 @@
+数据处理
+===================
+
+.. toctree::
+ :maxdepth: 1
+
+ dataset/sampler
+ dataset/transform
+ dataset/iterator
+ dataset/record
+ dataset/custom
+ dataset/enhanced_image_data
+ dataset/enhanced_text_data
+ dataset/enhanced_graph_data
diff --git a/tutorials/source_zh_cn/advanced/dataset/custom.ipynb b/tutorials/source_zh_cn/advanced/dataset/custom.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..3f527fd4ce0e2b8adc59d765fca66b07bcdaa338
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset/custom.ipynb
@@ -0,0 +1,300 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 自定义数据集\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_custom.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_custom.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/dataset/custom.ipynb)\n",
+ "\n",
+ "[mindspore.dataset](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.dataset.html)提供了部分常用数据集和标准格式数据集的加载接口。对于MindSpore暂不支持直接加载的数据集,可以通过构造自定义数据集类或自定义数据集生成函数的方式来生成数据集,然后通过[mindspore.dataset.GeneratorDataset](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset/mindspore.dataset.GeneratorDataset.html#mindspore.dataset.GeneratorDataset)接口实现自定义方式的数据集加载。\n",
+ "\n",
+ "通过**自定义数据集类**和**自定义数据集**生成函数两种方式生成的数据集,都可以完成加载、迭代等操作。由于在自定义数据集类中定义了随机访问函数和获取数据集大小函数,因此当需要随机访问数据集中某条数据或获取数据集大小时,使用自定义数据集类生成的数据集可以快速完成这些操作,而通过自定义数据集生成函数的方式生成的数据集需要对数据逐条遍历方可完成这些操作。\n",
+ "\n",
+ "一般情况下,当数据量较小时使用两种生成自定义数据集的方式中的任一种都可以,而当数据量过大时,优先使用自定义数据集类的方式生成数据集。\n",
+ "\n",
+ "本篇我们分别介绍**自定义数据集类**和**自定义数据集**两种生成自定义数据集的方式。\n",
+ "\n",
+ "## 自定义数据集类\n",
+ "\n",
+ "在用户自定义数据集类中须要自定义的类函数如下:\n",
+ "\n",
+ "- `__init__`:定义数据初始化等操作,在实例化数据集对象时被调用。\n",
+ "- `__getitem__`:定义该函数后可使其支持随机访问,能够根据给定的索引值`index`,获取数据集中的数据并返回。数据返回值类型是由NumPy数组组成的Tuple。\n",
+ "- `__len__`:返回数据集的样本数量。\n",
+ "\n",
+ "在完成自定义数据集类之后,可以通过`GeneratorDataset`接口按照用户定义的方式加载并访问数据集样本。下面我们通过两段示例代码来说明使用自定义数据集类的方式生成单标签数据集和多标签数据集的方法。\n",
+ "\n",
+ "### 生成单标签数据集\n",
+ "\n",
+ "通过自定义数据集类`MyDataset`生成五组数据,每组数据由两个随机数组成且只有一个标签。示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "data:[0.41702, 0.72032], label:[0.41919]\n",
+ "data:[0.00011, 0.30233], label:[0.68522]\n",
+ "data:[0.14676, 0.09234], label:[0.20445]\n",
+ "data:[0.18626, 0.34556], label:[0.87812]\n",
+ "data:[0.39677, 0.53882], label:[0.02739]\n",
+ "data size: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "np.random.seed(1)\n",
+ "\n",
+ "class MyDataset:\n",
+ " \"\"\"自定义数据集类\"\"\"\n",
+ "\n",
+ " def __init__(self):\n",
+ " \"\"\"自定义初始化操作\"\"\"\n",
+ " self.data = np.random.sample((5, 2)) # 自定义数据\n",
+ " self.label = np.random.sample((5, 1)) # 自定义标签\n",
+ "\n",
+ " def __getitem__(self, index):\n",
+ " \"\"\"自定义随机访问函数\"\"\"\n",
+ " return self.data[index], self.label[index]\n",
+ "\n",
+ " def __len__(self):\n",
+ " \"\"\"自定义获取样本数据量函数\"\"\"\n",
+ " return len(self.data)\n",
+ "\n",
+ "# 实例化数据集类\n",
+ "dataset_generator = MyDataset()\n",
+ "dataset = ds.GeneratorDataset(dataset_generator, [\"data\", \"label\"], shuffle=False)\n",
+ "\n",
+ "# 迭代访问数据集\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " data1 = data['data'].asnumpy()\n",
+ " label1 = data['label'].asnumpy()\n",
+ " print(f'data:[{data1[0]:7.5f}, {data1[1]:7.5f}], label:[{label1[0]:7.5f}]')\n",
+ "\n",
+ "# 打印数据条数\n",
+ "print(\"data size:\", dataset.get_dataset_size())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印可以看出,通过自定义数据集类生成的数据集一共有五组,每组数据有一个标签。\n",
+ "\n",
+ "### 生成多标签数据集\n",
+ "\n",
+ "通过自定义数据集类`MyDataset`生成五组数据,每组数据由两个随机数组成且有两个标签`label1`和`lable2`。示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "data:[0.41702, 0.72032] label1:[0.41919] label2:[0.67047]\n",
+ "data:[0.00011, 0.30233] label1:[0.68522] label2:[0.41730]\n",
+ "data:[0.14676, 0.09234] label1:[0.20445] label2:[0.55869]\n",
+ "data:[0.18626, 0.34556] label1:[0.87812] label2:[0.14039]\n",
+ "data:[0.39677, 0.53882] label1:[0.02739] label2:[0.19810]\n",
+ "data size: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "np.random.seed(1)\n",
+ "\n",
+ "class MyDataset:\n",
+ " \"\"\"自定义数据集类\"\"\"\n",
+ " def __init__(self):\n",
+ " \"\"\"自定义初始化操作\"\"\"\n",
+ " self.data = np.random.sample((5, 2)) # 自定义数据\n",
+ " self.label1 = np.random.sample((5, 1)) # 自定义标签1\n",
+ " self.label2 = np.random.sample((5, 1)) # 自定义标签2\n",
+ "\n",
+ " def __getitem__(self, index):\n",
+ " \"\"\"自定义随机访问函数\"\"\"\n",
+ " return self.data[index], self.label1[index], self.label2[index]\n",
+ "\n",
+ " def __len__(self):\n",
+ " \"\"\"自定义获取样本数据量函数\"\"\"\n",
+ " return len(self.data)\n",
+ "\n",
+ "# 实例化数据集类\n",
+ "dataset_generator = MyDataset()\n",
+ "# 加载数据集\n",
+ "dataset = ds.GeneratorDataset(dataset_generator, [\"data\", \"label1\", \"label2\"], shuffle=False)\n",
+ "\n",
+ "# 迭代访问数据集\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(\"data:[{:7.5f},\".format(data['data'].asnumpy()[0]),\n",
+ " \"{:7.5f}] \".format(data['data'].asnumpy()[1]),\n",
+ " \"label1:[{:7.5f}]\".format(data['label1'].asnumpy()[0]),\n",
+ " \"label2:[{:7.5f}]\".format(data['label2'].asnumpy()[0]))\n",
+ "\n",
+ "# 打印数据条数\n",
+ "print(\"data size:\", dataset.get_dataset_size())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印可以看出,通过自定义数据集类生成的数据集一共有五组,每组数据有两个标签。\n",
+ "\n",
+ "## 自定义数据集生成函数\n",
+ "\n",
+ "我们也可以通过使用自定义数据集生成函数的方式生成数据集,之后使用`GeneratorDataset`接口按照用户定义的方式加载并访问数据集样本。\n",
+ "\n",
+ "下面我们通过两段示例代码来说明如何使用自定义数据集生成函数的方式来分别生成单标签数据集和多标签数据集。\n",
+ "\n",
+ "### 生成单标签数据集\n",
+ "\n",
+ "通过自定义数据集生成函数`get_singlelabel_data`生成五组数据,每组数据由一个随机数组成且只有一个标签。示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "data:[ -3.73152] label:[ 0.38465] \n",
+ "data:[ -6.60339] label:[ 0.75629] \n",
+ "data:[ 6.01489] label:[ 0.93652] \n",
+ "data:[ -8.29912] label:[ -0.92189] \n",
+ "data:[ 7.52778] label:[ 0.78921] \n",
+ "data size: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import dataset as ds\n",
+ "\n",
+ "def get_multilabel_data(num):\n",
+ " \"\"\"定义生成多标签数据集函数\"\"\"\n",
+ " for _ in range(num):\n",
+ " data = np.random.uniform(-10.0, 10.0) # 自定义数据\n",
+ " label = np.random.uniform(-1.0, 1.0) # 自定义标签\n",
+ " yield np.array([data]).astype(np.float32), np.array([label]).astype(np.float32)\n",
+ "\n",
+ "# 定义数据集\n",
+ "dataset = ds.GeneratorDataset(list(get_multilabel_data(5)), column_names=['data', 'label'])\n",
+ "\n",
+ "# 打印数据集\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(\"data:[{:9.5f}] \".format(data['data'].asnumpy()[0]),\n",
+ " \"label:[{:9.5f}] \".format(data['label'].asnumpy()[0]))\n",
+ "\n",
+ "# 打印数据条数\n",
+ "print(\"data size:\", dataset.get_dataset_size())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印可以看出,通过自定义数据集生成函数`get_singlelabel_data`生成的数据集一共有五组,每组数据只有一个标签。\n",
+ "\n",
+ "### 生成多标签数据集\n",
+ "\n",
+ "下面我们通过自定义数据集生成函数`get_multilabel_data`来生成一个多标签数据集,每组数据由一个随机数组成且有两个标签。\n",
+ "\n",
+ "示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "data:[ 3.73002] label1:[ 0.66925] label2:[ 1.01829]\n",
+ "data:[ 0.66331] label1:[ 0.38375] label2:[ 1.31552]\n",
+ "data:[ 5.00289] label1:[ 0.97772] label2:[ 1.74817]\n",
+ "data:[ -4.39112] label1:[ 0.57856] label2:[ 1.10323]\n",
+ "data:[ -8.03306] label1:[ -0.15778] label2:[ 1.95789]\n",
+ "data size: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import dataset as ds\n",
+ "\n",
+ "def get_multilabel_data(num, w=2.0, b=3.0):\n",
+ " \"\"\"定义生成多标签数据集函数\"\"\"\n",
+ " for _ in range(num):\n",
+ " data = np.random.uniform(-10.0, 10.0) # 自定义数据\n",
+ " label1 = np.random.uniform(-1.0, 1.0) # 自定义标签1\n",
+ " label2 = np.random.uniform(1.0, 2.0) # 自定义标签2\n",
+ " yield np.array([data]).astype(np.float32), np.array([label1]).astype(np.float32), np.array([label2]).astype(np.float32)\n",
+ "\n",
+ "# 定义数据集\n",
+ "dataset = ds.GeneratorDataset(list(get_multilabel_data(5)), column_names=['data', 'label1', 'label2'])\n",
+ "\n",
+ "# 打印数据集\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(\"data:[{:9.5f}] \".format(data['data'].asnumpy()[0]),\n",
+ " \"label1:[{:9.5f}] \".format(data['label1'].asnumpy()[0]),\n",
+ " \"label2:[{:9.5f}]\".format(data['label2'].asnumpy()[0]))\n",
+ "\n",
+ "# 打印数据条数\n",
+ "print(\"data size:\", dataset.get_dataset_size())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印可以看出,通过自定义数据集生成函数生成的数据集共五组,每组数据有两个标签。"
+ ]
+ }
+ ],
+ "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
+}
diff --git a/tutorials/source_zh_cn/advanced/dataset/enhanced_graph_data.ipynb b/tutorials/source_zh_cn/advanced/dataset/enhanced_graph_data.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..bcc595be53186adcf2c8bbb54b2a0b854c34585c
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset/enhanced_graph_data.ipynb
@@ -0,0 +1,498 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 图数据集加载与处理\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_enhanced_graph_data.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_enhanced_graph_data.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/dataset/enhanced_graph_data.ipynb)\n",
+ "\n",
+ "MindSpore提供的`mindspore.dataset`模块可以帮助用户构建数据集对象,分批次地读取文本数据。同时,在各个数据集类中还内置了数据处理和数据分词算子,使得数据在训练过程中能够像经过pipeline管道的水一样源源不断地流向训练系统,提升数据训练效果。\n",
+ "\n",
+ "本章将简要演示如何使用MindSpore加载和处理图数据。"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 图的概念\n",
+ "\n",
+ "在介绍图数据的读取及增强之前,先介绍图的基本概念进行,有助于后续内容更好地理解。通常一个图(graph) `G`是由一系列的节点(vertices) `V`以及边(eage)`E`组成的,每条边都连接着图中的两个节点,用公式可表述为:\n",
+ "\n",
+ "$$G = F(V, E)$$\n",
+ "\n",
+ "简单的图如下所示。\n",
+ "\n",
+ "\n",
+ "\n",
+ "图中包含节点V = {a, b, c, d},和边E = {(a, b), (b, c), (c, d), (d, b)},针对图中的连接关系通常需借助数学的方式进行描述,如常用的基于邻接矩阵的方式,用于描述上述图连接关系的矩阵C如下,其中a、 b、c、d对应为第1、2、 3、4个节点。\n",
+ "\n",
+ "$$C=\\begin{bmatrix}\n",
+ "1&1&0&0\\\\\n",
+ "1&1&1&1\\\\\n",
+ "0&1&1&1\\\\\n",
+ "0&1&1&1\\\\\n",
+ "\\end{bmatrix}$$"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 数据集准备环节"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "1. 数据集介绍\n",
+ "\n",
+ "常用的图数据集包含Cora、Citeseer、PubMed等,在本文中我们基于Cora数据集进行介绍。\n",
+ "\n",
+ "> 原始数据集可以从[ucsc网站](https://linqs-data.soe.ucsc.edu/public/lbc/cora.tgz)进行下载,本文采用kimiyoung提供的[预处理后的版本](https://github.com/kimiyoung/planetoid)[[1]](#参考文献)。\n",
+ "\n",
+ "其中,Cora数据集主体部分(`cora.content`)包含2708条样本,每条样本描述1篇科学论文的信息,论文都属于7个类别中的一个。每条样本数据包含三部分,依次为论文编号、论文的词向量(一个1433位的二进制)、论文的类别;引用数据集部分(`cora.cites`)包含5429行,每行包含两个论文编号,表示第二篇论文对第一篇论文进行了引用。"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. 数据集下载\n",
+ "\n",
+ "下载预处理后的cora数据集目录如下所示。\n",
+ "\n",
+ "```text\n",
+ ".\n",
+ "└── cora\n",
+ " ├── ind.cora.allx\n",
+ " ├── ind.cora.ally\n",
+ " ├── ind.cora.graph\n",
+ " ├── ind.cora.test.index\n",
+ " ├── ind.cora.tx\n",
+ " ├── ind.cora.ty\n",
+ " ├── ind.cora.x\n",
+ " ├── ind.cora.y\n",
+ " ├── trans.cora.graph\n",
+ " ├── trans.cora.tx\n",
+ " ├── trans.cora.ty\n",
+ " ├── trans.cora.x\n",
+ " └── trans.cora.y\n",
+ "```\n",
+ "\n",
+ "以下示例代码将cora数据集下载并解压到指定位置:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Cloning into 'planetoid'...\n",
+ "remote: Enumerating objects: 86, done.\u001b[K\n",
+ "remote: Total 86 (delta 0), reused 0 (delta 0), pack-reused 86\u001b[K\n",
+ "Unpacking objects: 100% (86/86), done.\n"
+ ]
+ }
+ ],
+ "source": [
+ "!mkdir -p ./cora\n",
+ "!git clone https://github.com/kimiyoung/planetoid\n",
+ "!cp planetoid/data/*.cora.* ./cora\n",
+ "!rm -rf planetoid"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "3. 数据集格式转换\n",
+ "\n",
+ "将数据集转换为MindSpore Record格式,可借助models仓库提供的转换脚本进行转换,生成的MindSpore Record文件在`./cora_mindrecord`路径下。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Cloning into 'models'...\n",
+ "remote: Enumerating objects: 3475, done.\u001b[K\n",
+ "remote: Counting objects: 100% (3475/3475), done.\u001b[K\n",
+ "remote: Compressing objects: 100% (1276/1276), done.\u001b[K\n",
+ "remote: Total 16325 (delta 2612), reused 2429 (delta 2195), pack-reused 12850\u001b[K\n",
+ "Receiving objects: 100% (16325/16325), 79.56 MiB | 6.66 MiB/s, done.\n",
+ "Resolving deltas: 100% (9993/9993), done.\n",
+ "Checking out files: 100% (7152/7152), done.\n",
+ "Namespace(graph_api_args='./cora', mindrecord_file='./cora_mindrecord/cora_mr', mindrecord_header_size_by_bit=18, mindrecord_page_size_by_bit=20, mindrecord_partitions=1, mindrecord_script='cora', mindrecord_workers=8, num_edge_tasks=1, num_node_tasks=1)\n",
+ "Init writer ...\n",
+ "exec task 0, parallel: False ...\n",
+ "Node task is 0\n",
+ "transformed 512 record...\n",
+ "transformed 1024 record...\n",
+ "transformed 1536 record...\n",
+ "transformed 2048 record...\n",
+ "transformed 2560 record...\n",
+ "Processed 2708 lines for nodes.\n",
+ "transformed 2708 record...\n",
+ "exec task 0, parallel: False ...\n",
+ "Edge task is 0\n",
+ "transformed 512 record...\n",
+ "transformed 1024 record...\n",
+ "transformed 1536 record...\n",
+ "transformed 2048 record...\n",
+ "transformed 2560 record...\n",
+ "transformed 3072 record...\n",
+ "transformed 3584 record...\n",
+ "transformed 4096 record...\n",
+ "transformed 4608 record...\n",
+ "transformed 5120 record...\n",
+ "transformed 5632 record...\n",
+ "transformed 6144 record...\n",
+ "transformed 6656 record...\n",
+ "transformed 7168 record...\n",
+ "transformed 7680 record...\n",
+ "transformed 8192 record...\n",
+ "transformed 8704 record...\n",
+ "transformed 9216 record...\n",
+ "transformed 9728 record...\n",
+ "transformed 10240 record...\n",
+ "transformed 10752 record...\n",
+ "Processed 10858 lines for edges.\n",
+ "transformed 10858 record...\n",
+ "--------------------------------------------\n",
+ "END. Total time: 4.108929634094238\n",
+ "--------------------------------------------\n"
+ ]
+ }
+ ],
+ "source": [
+ "!git clone https://gitee.com/mindspore/models.git\n",
+ "\n",
+ "SRC_PATH = \"./cora\"\n",
+ "MINDRECORD_PATH = \"./cora_mindrecord\"\n",
+ "\n",
+ "!rm -rf $MINDRECORD_PATH\n",
+ "!mkdir $MINDRECORD_PATH\n",
+ "\n",
+ "!python models/utils/graph_to_mindrecord/writer.py --mindrecord_script cora --mindrecord_file \"$MINDRECORD_PATH/cora_mr\" --mindrecord_partitions 1 --mindrecord_header_size_by_bit 18 --mindrecord_page_size_by_bit 20 --graph_api_args \"$SRC_PATH\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 加载数据集\n",
+ "\n",
+ "MindSpore目前支持加载文本领域常用的经典数据集和多种数据存储格式下的数据集,用户也可以通过构建自定义数据集类实现自定义方式的数据加载。\n",
+ "\n",
+ "下面演示使用`MindSpore.dataset`模块中的`MindDataset`类加载上述已转换成MindSpore Record格式的cora数据集。\n",
+ "\n",
+ "1. 配置数据集目录,创建数据集对象。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import numpy as np\n",
+ "\n",
+ "data_file = \"./cora_mindrecord/cora_mr\"\n",
+ "dataset = ds.GraphData(data_file)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. 访问对应的接口,获取图信息及特性、标签内容。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "graph info: {'node_type': [0], 'edge_type': [0], 'node_num': {0: 2708}, 'edge_num': {0: 10858}, 'node_feature_type': [1, 2], 'edge_feature_type': []}\n",
+ "node shape: 2708\n",
+ "features shape: (2708, 1433)\n",
+ "labels shape: (2708,)\n",
+ "labels: [3 4 4 ... 3 3 3]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 查看图中结构信息\n",
+ "graph = dataset.graph_info()\n",
+ "print(\"graph info:\", graph)\n",
+ "\n",
+ "# 获取所有的节点信息\n",
+ "nodes = dataset.get_all_nodes(0)\n",
+ "nodes_list = nodes.tolist()\n",
+ "print(\"node shape:\", len(nodes_list))\n",
+ "\n",
+ "# 获取特征和标签信息,总共2708条数据\n",
+ "# 每条数据中特征信息是用于描述论文i,长度为1433的二进制表示,标签信息指的是论文所属的种类\n",
+ "raw_tensor = dataset.get_node_feature(nodes_list, [1, 2])\n",
+ "features, labels = raw_tensor[0], raw_tensor[1]\n",
+ "\n",
+ "print(\"features shape:\", features.shape)\n",
+ "print(\"labels shape:\", labels.shape)\n",
+ "print(\"labels:\", labels)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 数据处理"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "下面演示构建pipeline,对节点进行采样等操作。\n",
+ "\n",
+ "1. 获取节点的邻居节点,构造邻接矩阵。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "neighbor:\n",
+ " [[ 0 633 1862 ... -1 -1 -1]\n",
+ " [ 1 2 652 ... -1 -1 -1]\n",
+ " [ 2 1986 332 ... -1 -1 -1]\n",
+ " ...\n",
+ " [2705 287 -1 ... -1 -1 -1]\n",
+ " [2706 165 2707 ... -1 -1 -1]\n",
+ " [2707 598 165 ... -1 -1 -1]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "neighbor = dataset.get_all_neighbors(nodes_list, 0)\n",
+ "\n",
+ "# neighbor的第一列是node_id,第二列到最后一列存储的是第一列的邻居节点,如果不存在这么多,则用-1补齐。\n",
+ "print(\"neighbor:\\n\", neighbor)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. 依据节点的邻居节点信息,构造邻接矩阵。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "adj:\n",
+ " [[0. 0. 0. ... 0. 0. 0.]\n",
+ " [0. 0. 1. ... 0. 0. 0.]\n",
+ " [0. 1. 0. ... 0. 0. 0.]\n",
+ " ...\n",
+ " [0. 0. 0. ... 0. 0. 0.]\n",
+ " [0. 0. 0. ... 0. 0. 1.]\n",
+ " [0. 0. 0. ... 0. 1. 0.]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "nodes_num = labels.shape[0]\n",
+ "node_map = {node_id: index for index, node_id in enumerate(nodes_list)}\n",
+ "adj = np.zeros([nodes_num, nodes_num], dtype=np.float32)\n",
+ "\n",
+ "for index, value in np.ndenumerate(neighbor):\n",
+ " # neighbor的第一列是node_id,第二列到最后一列存储的是第一列的邻居节点,如果不存在这么多,则用-1补齐。\n",
+ " if value >= 0 and index[1] > 0:\n",
+ " adj[node_map[neighbor[index[0], 0]], node_map[value]] = 1\n",
+ "\n",
+ "print(\"adj:\\n\", adj)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "3. 节点采样,支持常见的多次跳跃采样与随机游走采样方法等。\n",
+ "- 多跳邻接点采样如(a)图所示,当次采样的节点将作为下次采样的起始点;随机游走方式如(b)图所示,随机选择一条路径依次遍历相邻的节点,对应图中则选择了从Vi到Vj的游走路径。\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "neighbor:\n",
+ " [[ 0 633 2582]\n",
+ " [ 1 654 2]\n",
+ " [ 2 1 1986]\n",
+ " [ 3 2544 2544]\n",
+ " [ 4 1256 2175]\n",
+ " [ 5 1629 2546]\n",
+ " [ 6 1042 1602]\n",
+ " [ 7 208 208]\n",
+ " [ 8 1996 269]\n",
+ " [ 9 723 2614]\n",
+ " [ 10 2545 476]\n",
+ " [ 11 1839 1655]\n",
+ " [ 12 2661 1001]\n",
+ " [ 13 1810 1701]\n",
+ " [ 14 2034 2075]\n",
+ " [ 15 1093 1090]\n",
+ " [ 16 2642 2444]\n",
+ " [ 17 1316 927]\n",
+ " [ 18 2082 2145]\n",
+ " [ 19 1939 1939]\n",
+ " [ 20 2269 2374]]\n",
+ "walks:\n",
+ " [[ 0 2582]\n",
+ " [ 1 2]\n",
+ " [ 2 1454]\n",
+ " [ 3 2544]\n",
+ " [ 4 1016]\n",
+ " [ 5 1629]\n",
+ " [ 6 1042]\n",
+ " [ 7 208]\n",
+ " [ 8 1996]\n",
+ " [ 9 723]\n",
+ " [ 10 476]\n",
+ " [ 11 1839]\n",
+ " [ 12 1318]\n",
+ " [ 13 1701]\n",
+ " [ 14 158]\n",
+ " [ 15 1093]\n",
+ " [ 16 2642]\n",
+ " [ 17 1315]\n",
+ " [ 18 1560]\n",
+ " [ 19 1939]\n",
+ " [ 20 2374]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 基于多次跳跃进行节点采样\n",
+ "neighbor = dataset.get_sampled_neighbors(nodes_list[0:21], [2], [0])\n",
+ "print(\"neighbor:\\n\", neighbor)\n",
+ "\n",
+ "# 基于随机游走进行节点采样\n",
+ "meta_path = [0]\n",
+ "walks = dataset.random_walk(nodes_list[0:21], meta_path)\n",
+ "print(\"walks:\\n\", walks)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "4. 通过节点获取边/通过边获取节点。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "part edges: [0 1 2 3 4 5 6 7 8 9]\n",
+ "nodes: [[ 0 633]\n",
+ " [ 0 1862]\n",
+ " [ 0 2582]\n",
+ " [ 1 2]\n",
+ " [ 1 652]\n",
+ " [ 1 654]\n",
+ " [ 2 1986]\n",
+ " [ 2 332]\n",
+ " [ 2 1666]\n",
+ " [ 2 1]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 通过边获取节点\n",
+ "part_edges = dataset.get_all_edges(0)[:10]\n",
+ "nodes = dataset.get_nodes_from_edges(part_edges)\n",
+ "print(\"part edges:\", part_edges)\n",
+ "print(\"nodes:\", nodes)\n",
+ "\n",
+ "# 通过节点获取边\n",
+ "nodes_pair_list = [(0, 1), (1, 2), (1, 3), (1, 4)]\n",
+ "edges = dataset.get_edges_from_nodes(nodes_pair_list)\n",
+ "print(\"edges:\", edges)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 参考文献\n",
+ "\n",
+ "[1] Yang Z, Cohen W, Salakhudinov R. [Revisiting semi-supervised learning with graph embeddings](http://proceedings.mlr.press/v48/yanga16.pdf)."
+ ]
+ }
+ ],
+ "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.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/dataset/enhanced_image_data.ipynb b/tutorials/source_zh_cn/advanced/dataset/enhanced_image_data.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..7c79d8544e670636f2237232ea4741213e63a577
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset/enhanced_image_data.ipynb
@@ -0,0 +1,568 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 图像数据加载与增强\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_enhanced_image_data.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_enhanced_image_data.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/dataset/enhanced_image_data.ipynb)\n",
+ "\n",
+ "在计算机视觉任务中,数据量过小、样本场景单一等问题都会影响模型的训练效果,用户可以通过数据增强操作对图像进行预处理,从而提升模型的泛化性。MindSpore提供了`c_transforms`模块和`py_transforms`模块供用户进行多种数据增强操作,二者的区别如下,用户也可以自定义函数或者算子进行数据增强。\n",
+ "\n",
+ "- `c_transforms`:基于C++的OpenCV实现,提供了多种图像增强功能,具有较高的性能;\n",
+ "\n",
+ "- `py_transforms`:基于Python的PIL实现,提供了多种图像增强功能,并提供了PIL Image和NumPy数组之间的传输方法。\n",
+ "\n",
+ "下面将以CIFAR-10数据集和MNIST数据集为例,简要介绍这两种图像数据加载的方式和几种常用的`c_transforms`模块和`py_transforms`模块数据增强算子的使用方法,更多图像类型的数据集加载方式可参考[API文档](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.dataset.html#id1),更多图像数据增强算子相关信息可参考[API文档](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.dataset.vision.html)。\n",
+ "\n",
+ "## 加载图像数据\n",
+ "\n",
+ "> 本章节中的示例代码依赖第三方支持包`matplotlib`,可使用命令`pip install matplotlib`安装。如本文档以Notebook运行时,完成安装后需要重启kernel才能执行后续代码。\n",
+ "\n",
+ "1. 以下示例代码分别将CIFAR-10数据集和MNIST数据集下载并解压到指定位置,网络状况良好的情况下此段代码预计需执行三至五分钟。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "from mindvision.dataset import DownLoad\n",
+ "\n",
+ "dl_path_cifar10 = \"./datasets\"\n",
+ "dl_url_cifar10 = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz\"\n",
+ "\n",
+ "dl = DownLoad()\n",
+ "\n",
+ "# 下载CIFAR-10数据集并解压\n",
+ "dl.download_and_extract_archive(url=dl_url_cifar10, download_path=dl_path_cifar10)\n",
+ "\n",
+ "# MNIST数据集保存路径\n",
+ "dl_path_mnist = \"./mnist\"\n",
+ "dl_url_mnist_labels = \"http://yann.lecun.com/exdb/mnist/train-labels-idx1-ubyte.gz\"\n",
+ "dl_url_mnist_images = \"http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz\"\n",
+ "\n",
+ "# 下载MNIST数据集并解压\n",
+ "dl.download_and_extract_archive(url=dl_url_mnist_labels, download_path=dl_path_mnist)\n",
+ "dl.download_and_extract_archive(url=dl_url_mnist_images, download_path=dl_path_mnist)\n",
+ "\n",
+ "image_gz = \"./mnist/train-images-idx3-ubyte.gz\"\n",
+ "label_gz = \"./mnist/train-labels-idx1-ubyte.gz\"\n",
+ "\n",
+ "# 删除压缩文件\n",
+ "if os.path.exists(image_gz):\n",
+ " os.remove(image_gz)\n",
+ "if os.path.exists(label_gz):\n",
+ " os.remove(label_gz)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. 使用[mindspore.dataset.Cifar10Dataset](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset/mindspore.dataset.Cifar10Dataset.html#mindspore.dataset.Cifar10Dataset)接口加载CIFAR-10数据集,使用[mindspore.dataset.MnistDataset](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset/mindspore.dataset.MnistDataset.html#mindspore.dataset.MnistDataset)接口加载MNIST数据集。示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "CIFAR-10 shape: (32, 32, 3) label: 4\n",
+ "CIFAR-10 shape: (32, 32, 3) label: 5\n",
+ "CIFAR-10 shape: (32, 32, 3) label: 7\n",
+ "CIFAR-10 shape: (32, 32, 3) label: 2\n",
+ "MNIST shape: (28, 28, 1) label: 0\n",
+ "MNIST shape: (28, 28, 1) label: 3\n",
+ "MNIST shape: (28, 28, 1) label: 8\n",
+ "MNIST shape: (28, 28, 1) label: 9\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import mindspore.dataset as ds\n",
+ "%matplotlib inline\n",
+ "\n",
+ "DATA_DIR_MNIST = \"./mnist/\"\n",
+ "DATA_DIR_CIFAR10 = \"./datasets/cifar-10-batches-bin/\"\n",
+ "\n",
+ "# 加载数据集,选取4张图片\n",
+ "dataset_cifar10 = ds.Cifar10Dataset(DATA_DIR_CIFAR10, num_samples=4)\n",
+ "dataset_mnist = ds.MnistDataset(DATA_DIR_MNIST, num_samples=4)\n",
+ "\n",
+ "def printDataset(dataset_list, name_list):\n",
+ " \"\"\"显示数据集\"\"\"\n",
+ " dataset_sizes = []\n",
+ " for dataset in dataset_list:\n",
+ " dataset_sizes.append(dataset.get_dataset_size())\n",
+ " row = len(dataset_list) # 画布行数\n",
+ " column = max(dataset_sizes) # 画布列数\n",
+ " pos = 1\n",
+ " for i in range(row):\n",
+ " for data in dataset_list[i].create_dict_iterator(output_numpy=True):\n",
+ " plt.subplot(row, column, pos) # 显示位置\n",
+ " plt.imshow(data['image'].squeeze(), cmap=plt.cm.gray) # 显示内容\n",
+ " plt.title(data['label']) # 显示标题\n",
+ " print(name_list[i], \" shape:\", data['image'].shape, \"label:\", data['label'])\n",
+ " pos = pos + 1\n",
+ " pos = column * (i + 1) + 1\n",
+ "\n",
+ "printDataset([dataset_cifar10, dataset_mnist], [\"CIFAR-10\", \"MNIST\"])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## c_transforms模块\n",
+ "\n",
+ "`c_transforms`是基于C++的OpenCV实现,提供了多种图像增强功能,具有较高的性能。\n",
+ "\n",
+ "### RandomCrop\n",
+ "\n",
+ "`RandomCrop`操作对输入图像进行在随机位置的裁剪。\n",
+ "\n",
+ "参数说明:\n",
+ "\n",
+ "- `size`:裁剪图像的尺寸。\n",
+ "\n",
+ "- `padding`:填充的像素数量。\n",
+ "\n",
+ "- `pad_if_needed`:原图小于裁剪尺寸时,是否需要填充。\n",
+ "\n",
+ "- `fill_value`:在常量填充模式时使用的填充值。\n",
+ "\n",
+ "- `padding_mode`:填充模式。\n",
+ "\n",
+ "下面的样例首先使用顺序采样器加载CIFAR-10数据集,然后对已加载的图片进行长宽均为10的随机裁剪,最后输出裁剪前后的图片形状及对应标签,并对图片进行了展示。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Source image shape: (32, 32, 3) label: 6\n",
+ "Source image shape: (32, 32, 3) label: 9\n",
+ "Source image shape: (32, 32, 3) label: 9\n",
+ "Cropped image shape: (10, 10, 3) label: 6\n",
+ "Cropped image shape: (10, 10, 3) label: 9\n",
+ "Cropped image shape: (10, 10, 3) label: 9\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.vision.c_transforms as c_trans\n",
+ "\n",
+ "ds.config.set_seed(1)\n",
+ "\n",
+ "# CIFAR-10数据集加载路径\n",
+ "DATA_DIR = \"./datasets/cifar-10-batches-bin/\"\n",
+ "\n",
+ "# 使用SequentialSampler采样器选取3张图片\n",
+ "sampler = ds.SequentialSampler(num_samples=3)\n",
+ "dataset1 = ds.Cifar10Dataset(DATA_DIR, sampler=sampler)\n",
+ "\n",
+ "# 使用RandomCrop对原图进行10*10随机裁剪操作\n",
+ "random_crop = c_trans.RandomCrop([10, 10])\n",
+ "dataset2 = dataset1.map(operations=random_crop, input_columns=[\"image\"])\n",
+ "\n",
+ "printDataset([dataset1, dataset2], [\"Source image\", \"Cropped image\"])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印和图片显示结果可以看出,图片随机裁剪前后,标签不变,形状发生了变化。裁剪前的图片分辨率为32×32,裁剪后为10×10。\n",
+ "\n",
+ "### RandomHorizontalFlip\n",
+ "\n",
+ "`RandomHorizontalFlip`操作对输入图像进行随机水平翻转。\n",
+ "\n",
+ "参数说明:\n",
+ "\n",
+ "- `prob`: 单张图片发生翻转的概率。\n",
+ "\n",
+ "下面的样例首先使用随机采样器加载CIFAR-10数据集,然后对已加载的图片进行概率为0.8的随机水平翻转,最后输出翻转前后的图片形状及对应标签,并对图片进行了展示。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Source image shape: (32, 32, 3) label: 7\n",
+ "Source image shape: (32, 32, 3) label: 8\n",
+ "Source image shape: (32, 32, 3) label: 2\n",
+ "Source image shape: (32, 32, 3) label: 9\n",
+ "Flipped image shape: (32, 32, 3) label: 7\n",
+ "Flipped image shape: (32, 32, 3) label: 8\n",
+ "Flipped image shape: (32, 32, 3) label: 2\n",
+ "Flipped image shape: (32, 32, 3) label: 9\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.vision.c_transforms as c_trans\n",
+ "\n",
+ "ds.config.set_seed(1)\n",
+ "\n",
+ "# CIFAR-10数据集加载路径\n",
+ "DATA_DIR = \"./datasets/cifar-10-batches-bin/\"\n",
+ "\n",
+ "# 使用RandomSampler采样器随机选取4张图片\n",
+ "sampler = ds.RandomSampler(num_samples=4)\n",
+ "dataset1 = ds.Cifar10Dataset(DATA_DIR, sampler=sampler)\n",
+ "\n",
+ "# 使用RandomHorizontalFlip对原图进行随机水平翻转\n",
+ "random_horizontal_flip = c_trans.RandomHorizontalFlip(prob=0.8)\n",
+ "dataset2 = dataset1.map(operations=random_horizontal_flip, input_columns=[\"image\"])\n",
+ "\n",
+ "printDataset([dataset1, dataset2], [\"Source image\", \"Flipped image\"])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印和图片显示结果可以看出,经过随机水平翻转操作后,图像的形状、标签均未发生变化,部分图片被水平翻转。\n",
+ "\n",
+ "### Resize\n",
+ "\n",
+ "`Resize`操作对输入图像进行缩放。\n",
+ "\n",
+ "参数说明:\n",
+ "\n",
+ "- `size`:缩放的目标大小。\n",
+ "- `interpolation`:缩放时采用的插值方式。\n",
+ "\n",
+ "下面的样例首先加载[MNIST数据集[2]](#参考文献),然后将已加载的图片缩放至(101, 101)大小,最后输出缩放前后的图片形状及对应标签,并对图片进行了展示。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Source image shape: (28, 28, 1) label: 5\n",
+ "Source image shape: (28, 28, 1) label: 0\n",
+ "Source image shape: (28, 28, 1) label: 4\n",
+ "Source image shape: (28, 28, 1) label: 1\n",
+ "Resized image shape: (101, 101, 1) label: 5\n",
+ "Resized image shape: (101, 101, 1) label: 0\n",
+ "Resized image shape: (101, 101, 1) label: 4\n",
+ "Resized image shape: (101, 101, 1) label: 1\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.vision.c_transforms as c_trans\n",
+ "\n",
+ "# MNIST数据集加载路径\n",
+ "DATA_DIR = \"./mnist/\"\n",
+ "\n",
+ "# 加载MNIST数据集,选取4张图片\n",
+ "dataset1 = ds.MnistDataset(DATA_DIR, num_samples=4, shuffle=False)\n",
+ "\n",
+ "# 使用Resize操作对图像进行101×101缩放\n",
+ "resize = c_trans.Resize(size=[101, 101])\n",
+ "dataset2 = dataset1.map(operations=resize, input_columns=[\"image\"])\n",
+ "\n",
+ "printDataset([dataset1, dataset2], [\"Source image\", \"Resized image\"])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印和图片显示结果可以看出,缩放前后,图片的形状发生了变化,标签未变。缩放前图片分辨率为28×28,缩放后,图片分辨率为101×101。\n",
+ "\n",
+ "### Invert\n",
+ "\n",
+ "`Invert`操作对输入图像进行反相处理。\n",
+ "\n",
+ "下面的样例首先加载CIFAR-10数据集,然后定义反相操作并作用于已加载的图片,最后输出反相前后的图片形状和标签,并对图片进行展示。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Source image shape: (32, 32, 3) label: 8\n",
+ "Source image shape: (32, 32, 3) label: 1\n",
+ "Source image shape: (32, 32, 3) label: 9\n",
+ "Source image shape: (32, 32, 3) label: 7\n",
+ "Inverted image shape: (32, 32, 3) label: 8\n",
+ "Inverted image shape: (32, 32, 3) label: 1\n",
+ "Inverted image shape: (32, 32, 3) label: 9\n",
+ "Inverted image shape: (32, 32, 3) label: 7\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.vision.c_transforms as c_trans\n",
+ "\n",
+ "ds.config.set_seed(18)\n",
+ "\n",
+ "# CIFAR-10数据集加载路径\n",
+ "DATA_DIR = \"./datasets/cifar-10-batches-bin/\"\n",
+ "\n",
+ "# 加载CIFAR-10数据集,选取4张图片\n",
+ "dataset1 = ds.Cifar10Dataset(DATA_DIR, num_samples=4, shuffle=True)\n",
+ "\n",
+ "# 对图片进行反相操作\n",
+ "invert = c_trans.Invert()\n",
+ "dataset2 = dataset1.map(operations=invert, input_columns=[\"image\"])\n",
+ "\n",
+ "printDataset([dataset1, dataset2], [\"Source image\", \"Inverted image\"])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印和图片显示结果可以看出,反相操作前后,图片的形状和标签未变,颜色发生了变化。\n",
+ "\n",
+ "## py_transforms模块\n",
+ "\n",
+ "### Compose\n",
+ "\n",
+ "`Compose`操作接收一个`transforms`列表,将列表中的数据增强操作依次作用于数据集图片。\n",
+ "\n",
+ "下面的样例首先加载[CIFAR-10数据集[1]](#参考文献),然后同时定义解码、缩放和数据类型转换操作,并作用于已加载的图片,最后输出处理后的图片形状及对应标签,并对图片进行展示。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Transformed image Shape: (3, 200, 200) , Transformed label: 4\n",
+ "Transformed image Shape: (3, 200, 200) , Transformed label: 9\n",
+ "Transformed image Shape: (3, 200, 200) , Transformed label: 6\n",
+ "Transformed image Shape: (3, 200, 200) , Transformed label: 5\n",
+ "Transformed image Shape: (3, 200, 200) , Transformed label: 7\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.vision.py_transforms as py_trans\n",
+ "from mindspore.dataset.transforms.py_transforms import Compose\n",
+ "from PIL import Image\n",
+ "%matplotlib inline\n",
+ "\n",
+ "ds.config.set_seed(8)\n",
+ "\n",
+ "# CIFAR-10数据集加载路径\n",
+ "DATA_DIR = \"./datasets/cifar-10-batches-bin/\"\n",
+ "\n",
+ "# 加载CIFAR-10数据集,选取5张图片\n",
+ "dataset1 = ds.Cifar10Dataset(DATA_DIR, num_samples=5, shuffle=True)\n",
+ "\n",
+ "def decode(image):\n",
+ " \"\"\"定义解码函数\"\"\"\n",
+ " return Image.fromarray(image)\n",
+ "\n",
+ "# 定义transforms列表\n",
+ "transforms_list = [\n",
+ " decode,\n",
+ " py_trans.Resize(size=(200, 200)),\n",
+ " py_trans.ToTensor()\n",
+ "]\n",
+ "\n",
+ "# 通过Compose操作将transforms列表中函数作用于数据集图片\n",
+ "compose_trans = Compose(transforms_list)\n",
+ "dataset2 = dataset1.map(operations=compose_trans, input_columns=[\"image\"])\n",
+ "\n",
+ "# 打印数据增强操作后图片的形状、标签\n",
+ "image_list, label_list = [], []\n",
+ "for data in dataset2.create_dict_iterator():\n",
+ " image_list.append(data['image'])\n",
+ " label_list.append(data['label'])\n",
+ " print(\"Transformed image Shape:\", data['image'].shape, \", Transformed label:\", data['label'])\n",
+ "\n",
+ "num_samples = len(image_list)\n",
+ "for i in range(num_samples):\n",
+ " plt.subplot(1, len(image_list), i + 1)\n",
+ " plt.imshow(image_list[i].asnumpy().transpose(1, 2, 0))\n",
+ " plt.title(label_list[i].asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印和图片显示结果可以看出,经过transforms列表中的数据增强操作后,图片标签未变,形状发生了变化,分辨率缩放为200×200。\n",
+ "\n",
+ "## 注意事项\n",
+ "\n",
+ "在数据管道处理模式中,请谨慎混用`c_transforms`与`py_transforms`,因为两者在数据的计算管道(即Pipeline)中运行的方式存在差异。\n",
+ "\n",
+ "混用会引发C++与Python切换的成本,从而降低处理性能,因此建议尽量不要过度混用两个模块的算子。\n",
+ "\n",
+ "> 注:Eager模式混用`c_transforms`与`py_transforms`不受运行方式差异影响\n",
+ "\n",
+ "### 推荐的使用方式\n",
+ "\n",
+ "1. 优先推荐单独使用`py_transform`或`c_transform`。如下图所示,不存在Python层和C++层切换使用的情况。\n",
+ "\n",
+ " \n",
+ "\n",
+ "2. 先使用`py_transform`,再使用`c_transform`。如下图所示,先完成Python层的操作后,再完成C++层的操作。\n",
+ "\n",
+ " \n",
+ "\n",
+ "3. 先使用`c_transform`,再使用`py_transform`。如下图所示,先完成C++层的操作后,再完成Python层的操作。\n",
+ "\n",
+ " \n",
+ "\n",
+ "### 不推荐的使用方式\n",
+ "\n",
+ "在两种transform之间频繁切换。如下图所示,这种在Python层和C++层来回切换的使用方式是极不推荐的。\n",
+ "\n",
+ "\n",
+ "\n",
+ "## 参考文献\n",
+ "\n",
+ "[1] Alex Krizhevsky. [Learning_Multiple Layers of Features from Tiny Images](http://www.cs.toronto.edu/~kriz/learning-features-2009-TR.pdf).\n",
+ "\n",
+ "[2] Y. LeCun, L. Bottou, Y. Bengio, and P. Haffner. [Gradient-based learning applied to document recognition](http://yann.lecun.com/exdb/publis/pdf/lecun-98.pdf)."
+ ]
+ }
+ ],
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/dataset/enhanced_text_data.ipynb b/tutorials/source_zh_cn/advanced/dataset/enhanced_text_data.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..bc3ab99929bcfb013c3bf7f5eab38544497dfd4f
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset/enhanced_text_data.ipynb
@@ -0,0 +1,814 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 文本数据加载与增强\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_enhanced_text_data.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_enhanced_text_data.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/dataset/enhanced_text_data.ipynb)\n",
+ "\n",
+ "随着可获得的文本数据逐步增多,对文本数据进行预处理,以便获得可用于网络训练所需干净数据的诉求也更为迫切。文本数据集预处理通常包括文本数据集加载与数据增强两部分。\n",
+ "\n",
+ "文本数据加载通常包含以下三种方式:\n",
+ "\n",
+ "1. 通过文本读取的Dataset接口如[ClueDataset](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset/mindspore.dataset.CLUEDataset.html#mindspore.dataset.CLUEDataset)、[TextFileDataset](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset/mindspore.dataset.TextFileDataset.html#mindspore.dataset.TextFileDataset)进行读取。\n",
+ "2. 将数据集转成标准格式(如MindRecord格式),再通过对应接口(如[MindDataset](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset/mindspore.dataset.MindDataset.html#mindspore.dataset.MindDataset))进行读取。\n",
+ "3. 通过GeneratorDataset接口,接收用户自定义的数据集加载函数,进行数据加载,用法可参考[自定义数据集加载](https://www.mindspore.cn/tutorials/zh-CN/master/advanced/dataset/custom.html)章节。\n",
+ "\n",
+ "## 加载文本数据\n",
+ "\n",
+ "下面我们以从TXT文件中读取数据为例,介绍`TextFileDataset`的使用方式,更多文本数据集加载相关信息可参考[API文档](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.dataset.html#id2)。\n",
+ "\n",
+ "1. 准备文本数据,内容如下:\n",
+ "\n",
+ "```text\n",
+ "Welcome to Beijing\n",
+ "北京欢迎您!\n",
+ "我喜欢China!\n",
+ "```\n",
+ "\n",
+ "2. 创建`tokenizer.txt`文件并复制文本数据到该文件中,将该文件存放在./datasets路径下。执行如下代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "\n",
+ "if not os.path.exists('./datasets'):\n",
+ " os.mkdir('./datasets')\n",
+ "\n",
+ "# 把上面的文本数据写入文件tokenizer.txt\n",
+ "file_handle = open('./datasets/tokenizer.txt', mode='w')\n",
+ "file_handle.write('Welcome to Beijing \\n北京欢迎您! \\n我喜欢China! \\n')\n",
+ "file_handle.close()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面的代码执行完后,数据集结构为:\n",
+ "\n",
+ "```text\n",
+ "./datasets\n",
+ "└── tokenizer.txt\n",
+ "```\n",
+ "\n",
+ "3. 从TXT文件中加载数据集并打印。代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Welcome to Beijing \n",
+ "北京欢迎您! \n",
+ "我喜欢China! \n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.text as text\n",
+ "\n",
+ "# 定义文本数据集的加载路径\n",
+ "DATA_FILE = './datasets/tokenizer.txt'\n",
+ "\n",
+ "# 从tokenizer.txt中加载数据集\n",
+ "dataset = ds.TextFileDataset(DATA_FILE, shuffle=False)\n",
+ "\n",
+ "for data in dataset.create_dict_iterator(output_numpy=True):\n",
+ " print(text.to_str(data['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 文本数据增强\n",
+ "\n",
+ "针对文本数据增强,常用操作包含文本分词、词汇表查找等:\n",
+ "\n",
+ "- 文本分词:将原始一长串句子分割成多个基本的词汇。\n",
+ "- 词汇表查找:查找分割后各词汇对应的id,并将句子中包含的id组成词向量传入网络进行训练。\n",
+ "\n",
+ "下面对数据增强过程中用到的分词功能、词汇表查找等功能进行介绍,更多关于文本处理API的使用说明,可以参考[API文档](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.dataset.text.html)。\n",
+ "\n",
+ "### 构造与使用词汇表\n",
+ "\n",
+ "词汇表提供了单词与id对应的映射关系,通过词汇表,输入单词能找到对应的单词id,反之依据单词id也能获取对应的单词。\n",
+ "\n",
+ "MindSpore提供了多种构造词汇表(Vocab)的方法,可以从字典、文件、列表以及Dataset对象中获取原始数据,以便构造词汇表,对应的接口为:[from_dict](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset_text/mindspore.dataset.text.Vocab.html#mindspore.dataset.text.Vocab.from_dict)、[from_file](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset_text/mindspore.dataset.text.Vocab.html#mindspore.dataset.text.Vocab.from_file)、[from_list](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset_text/mindspore.dataset.text.Vocab.html#mindspore.dataset.text.Vocab.from_list)、[from_dataset](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset_text/mindspore.dataset.text.Vocab.html#mindspore.dataset.text.Vocab.from_dataset)。\n",
+ "\n",
+ "以from_dict为例,构造Vocab的方式如下,传入的dict中包含多组单词和id对。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore.dataset import text\n",
+ "\n",
+ "# 构造词汇表\n",
+ "vocab = text.Vocab.from_dict({\"home\": 3, \"behind\": 2, \"the\": 4, \"world\": 5, \"\": 6})"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Vocab提供了单词与id之间相互查询的方法,即:[tokens_to_ids](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset_text/mindspore.dataset.text.Vocab.html#mindspore.dataset.text.Vocab.tokens_to_ids)和[ids_to_tokens](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset_text/mindspore.dataset.text.Vocab.html#mindspore.dataset.text.Vocab.ids_to_tokens)方法,用法如下所示:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "ids: [3, 5]\n",
+ "tokens: ['behind', 'world']\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 根据单词查找id\n",
+ "ids = vocab.tokens_to_ids([\"home\", \"world\"])\n",
+ "print(\"ids: \", ids)\n",
+ "\n",
+ "# 根据id查找单词\n",
+ "tokens = vocab.ids_to_tokens([2, 5])\n",
+ "print(\"tokens: \", tokens)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面打印的结果可以看出:\n",
+ "\n",
+ "- 单词`\"home\"`和`\"world\"`的id分别为`3`和`5`;\n",
+ "- id为`2`的单词为`\"behind\"`,id为`5`的单词为`\"world\"`;\n",
+ "\n",
+ "这一结果也与词汇表一致。此外Vocab也是多种分词器(如WordpieceTokenizer)的必要入参,分词时会将句子中存在于词汇表的单词,前后分割开,变成单独的一个词汇,之后通过查找词汇表能够获取对应的词汇id。\n",
+ "\n",
+ "### 分词器\n",
+ "\n",
+ "分词就是将连续的字序列按照一定的规范划分成词序列的过程,合理的分词有助于语义理解。\n",
+ "\n",
+ "MindSpore提供了多种不同用途的分词器,如BasicTokenizer、BertTokenizer、JiebaTokenizer等,能够帮助用户高性能地处理文本。用户可以构建自己的字典,使用适当的标记器将句子拆分为不同的标记,并通过查找操作获取字典中标记的索引。此外,用户也可以根据需要实现自定义的分词器。\n",
+ "\n",
+ "> 下面介绍几种常用分词器的使用方法,更多分词器相关信息请参考[API文档](https://mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.dataset.text.html#mindspore-dataset-text-transforms)。\n",
+ "\n",
+ "#### BertTokenizer\n",
+ "\n",
+ "`BertTokenizer`操作是通过调用`BasicTokenizer`和`WordpieceTokenizer`来进行分词的。\n",
+ "\n",
+ "下面的样例首先构建了一个文本数据集和字符串列表,然后通过`BertTokenizer`对数据集进行分词,并展示了分词前后的文本结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------before tokenization----------------------------\n",
+ "床前明月光\n",
+ "疑是地上霜\n",
+ "举头望明月\n",
+ "低头思故乡\n",
+ "I am making small mistakes during working hours\n",
+ "😀嘿嘿😃哈哈😄大笑😁嘻嘻\n",
+ "繁體字\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.text as text\n",
+ "\n",
+ "# 构造待分词数据\n",
+ "input_list = [\"床前明月光\", \"疑是地上霜\", \"举头望明月\", \"低头思故乡\", \"I am making small mistakes during working hours\",\n",
+ " \"😀嘿嘿😃哈哈😄大笑😁嘻嘻\", \"繁體字\"]\n",
+ "\n",
+ "# 加载文本数据集\n",
+ "dataset = ds.NumpySlicesDataset(input_list, column_names=[\"text\"], shuffle=False)\n",
+ "\n",
+ "print(\"------------------------before tokenization----------------------------\")\n",
+ "for data in dataset.create_dict_iterator(output_numpy=True):\n",
+ " print(text.to_str(data['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面为数据集未被分词前的数据打印情况,下面使用`BertTokenizer`分词器对数据集进行分词。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------after tokenization-----------------------------\n",
+ "['床' '前' '明' '月' '光']\n",
+ "['疑' '是' '地' '上' '霜']\n",
+ "['举' '头' '望' '明' '月']\n",
+ "['低' '头' '思' '故' '乡']\n",
+ "['I' 'am' 'mak' '##ing' 'small' 'mistake' '##s' 'during' 'work' '##ing'\n",
+ " 'hour' '##s']\n",
+ "['😀' '嘿' '嘿' '😃' '哈' '哈' '😄' '大' '笑' '😁' '嘻' '嘻']\n",
+ "['繁' '體' '字']\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 构建词汇表\n",
+ "vocab_list = [\n",
+ " \"床\", \"前\", \"明\", \"月\", \"光\", \"疑\", \"是\", \"地\", \"上\", \"霜\", \"举\", \"头\", \"望\", \"低\", \"思\", \"故\", \"乡\",\n",
+ " \"繁\", \"體\", \"字\", \"嘿\", \"哈\", \"大\", \"笑\", \"嘻\", \"i\", \"am\", \"mak\", \"make\", \"small\", \"mistake\",\n",
+ " \"##s\", \"during\", \"work\", \"##ing\", \"hour\", \"😀\", \"😃\", \"😄\", \"😁\", \"+\", \"/\", \"-\", \"=\", \"12\",\n",
+ " \"28\", \"40\", \"16\", \" \", \"I\", \"[CLS]\", \"[SEP]\", \"[UNK]\", \"[PAD]\", \"[MASK]\", \"[unused1]\", \"[unused10]\"]\n",
+ "\n",
+ "# 加载词汇表\n",
+ "vocab = text.Vocab.from_list(vocab_list)\n",
+ "\n",
+ "# 使用BertTokenizer分词器对文本数据集进行分词操作\n",
+ "tokenizer_op = text.BertTokenizer(vocab=vocab)\n",
+ "dataset = dataset.map(operations=tokenizer_op)\n",
+ "\n",
+ "print(\"------------------------after tokenization-----------------------------\")\n",
+ "for i in dataset.create_dict_iterator(num_epochs=1, output_numpy=True):\n",
+ " print(text.to_str(i['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面两次的打印结果可以看出,数据集中的句子、词语和表情符号等都被`BertTokenizer`分词器以词汇表中的词汇为最小单元进行了分割,“故乡”被分割成了‘故’和‘乡’,“明月”被分割成了‘明’和‘月’。值得注意的是,“mistakes”被分割成了‘mistake’和‘##s’。\n",
+ "\n",
+ "#### JiebaTokenizer\n",
+ "\n",
+ "`JiebaTokenizer`操作是基于jieba的中文分词。\n",
+ "\n",
+ "以下示例代码完成下载字典文件`hmm_model.utf8`和`jieba.dict.utf8`,并将其放到指定位置。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindvision.dataset import DownLoad\n",
+ "\n",
+ "# 字典文件存放路径\n",
+ "dl_path = \"./dictionary\"\n",
+ "\n",
+ "# 获取字典文件源\n",
+ "dl_url_hmm = \"https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/datasets/hmm_model.utf8\"\n",
+ "dl_url_jieba = \"https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/datasets/jieba.dict.utf8\"\n",
+ "\n",
+ "# 下载字典文件\n",
+ "dl = DownLoad()\n",
+ "dl.download_url(url=dl_url_hmm, path=dl_path)\n",
+ "dl.download_url(url=dl_url_jieba, path=dl_path)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "下载的文件放置的目录结构如下:\n",
+ "\n",
+ "```text\n",
+ "./dictionary/\n",
+ "├── hmm_model.utf8\n",
+ "└── jieba.dict.utf8\n",
+ "```\n",
+ "\n",
+ "下面的样例首先构建了一个文本数据集,然后使用HMM与MP字典文件创建`JiebaTokenizer`对象,并对数据集进行分词,最后展示了分词前后的文本结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------before tokenization----------------------------\n",
+ "明天天气太好了我们一起去外面玩吧\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.text as text\n",
+ "\n",
+ "# 构造待分词数据\n",
+ "input_list = [\"明天天气太好了我们一起去外面玩吧\"]\n",
+ "\n",
+ "# 加载数据集\n",
+ "dataset = ds.NumpySlicesDataset(input_list, column_names=[\"text\"], shuffle=False)\n",
+ "\n",
+ "print(\"------------------------before tokenization----------------------------\")\n",
+ "for data in dataset.create_dict_iterator(output_numpy=True):\n",
+ " print(text.to_str(data['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面为数据集未被分词前的数据打印情况,下面使用`JiebaTokenizer`分词器对数据集进行分词。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------after tokenization-----------------------------\n",
+ "['明天' '天气' '太好了' '我们' '一起' '去' '外面' '玩吧']\n"
+ ]
+ }
+ ],
+ "source": [
+ "HMM_FILE = \"./dictionary/hmm_model.utf8\"\n",
+ "MP_FILE = \"./dictionary/jieba.dict.utf8\"\n",
+ "\n",
+ "# 使用JiebaTokenizer分词器对数据集进行分词\n",
+ "jieba_op = text.JiebaTokenizer(HMM_FILE, MP_FILE)\n",
+ "dataset = dataset.map(operations=jieba_op, input_columns=[\"text\"], num_parallel_workers=1)\n",
+ "\n",
+ "print(\"------------------------after tokenization-----------------------------\")\n",
+ "for data in dataset.create_dict_iterator(num_epochs=1, output_numpy=True):\n",
+ " print(text.to_str(data['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面两次打印结果来看,数据集中的句子被`JiebaTokenizer`分词器以词语为最小单元进行了划分。\n",
+ "\n",
+ "#### SentencePieceTokenizer\n",
+ "\n",
+ "`SentencePieceTokenizer`操作是基于开源自然语言处理工具包[SentencePiece](https://github.com/google/sentencepiece)封装的分词器。\n",
+ "\n",
+ "以下示例代码将下载文本数据集文件`botchan.txt`,并将其放置到指定位置。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# 数据集存放位置\n",
+ "dl_path = \"./datasets\"\n",
+ "\n",
+ "# 获取语料数据源\n",
+ "dl_url_botchan = \"https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/datasets/botchan.txt\"\n",
+ "\n",
+ "# 下载语料数据\n",
+ "dl.download_url(url=dl_url_botchan, path=dl_path)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "下载的文件放置的目录结构如下:\n",
+ "\n",
+ "```text\n",
+ "./datasets/\n",
+ "└── botchan.txt\n",
+ "```\n",
+ "\n",
+ "下面的样例首先构建了一个文本数据集,然后从`vocab_file`文件中构建一个`vocab`对象,再通过`SentencePieceTokenizer`对数据集进行分词,并展示了分词前后的文本结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------before tokenization----------------------------\n",
+ "Nothing in the world is difficult for one who sets his mind on it.\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.text as text\n",
+ "from mindspore.dataset.text import SentencePieceModel, SPieceTokenizerOutType\n",
+ "\n",
+ "# 构造待分词数据\n",
+ "input_list = [\"Nothing in the world is difficult for one who sets his mind on it.\"]\n",
+ "\n",
+ "# 加载数据集\n",
+ "dataset = ds.NumpySlicesDataset(input_list, column_names=[\"text\"], shuffle=False)\n",
+ "\n",
+ "print(\"------------------------before tokenization----------------------------\")\n",
+ "for data in dataset.create_dict_iterator(output_numpy=True):\n",
+ " print(text.to_str(data['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面为数据集未被分词前的数据打印情况,下面使用`SentencePieceTokenizer`分词器对数据集进行分词。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------after tokenization-----------------------------\n",
+ "['▁Nothing' '▁in' '▁the' '▁world' '▁is' '▁difficult' '▁for' '▁one' '▁who'\n",
+ " '▁sets' '▁his' '▁mind' '▁on' '▁it.']\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 语料数据文件存放路径\n",
+ "vocab_file = \"./datasets/botchan.txt\"\n",
+ "\n",
+ "# 从语料数据中学习构建词汇表\n",
+ "vocab = text.SentencePieceVocab.from_file([vocab_file], 5000, 0.9995, SentencePieceModel.WORD, {})\n",
+ "\n",
+ "# 使用SentencePieceTokenizer分词器对数据集进行分词\n",
+ "tokenizer_op = text.SentencePieceTokenizer(vocab, out_type=SPieceTokenizerOutType.STRING)\n",
+ "dataset = dataset.map(operations=tokenizer_op)\n",
+ "\n",
+ "print(\"------------------------after tokenization-----------------------------\")\n",
+ "for i in dataset.create_dict_iterator(num_epochs=1, output_numpy=True):\n",
+ " print(text.to_str(i['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面两次打印结果来看,数据集中的句子被`SentencePieceTokenizer`分词器以词语为最小单元进行了划分。在`SentencePieceTokenizer`分词器的处理过程中,空格作为普通符号处理,并使用下划线标记空格。\n",
+ "\n",
+ "#### UnicodeCharTokenizer\n",
+ "\n",
+ "`UnicodeCharTokenizer`操作是根据Unicode字符集来分词的。\n",
+ "\n",
+ "下面的样例首先构建了一个文本数据集,然后通过`UnicodeCharTokenizer`对数据集进行分词,并展示了分词前后的文本结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------before tokenization----------------------------\n",
+ "Welcome to Beijing!\n",
+ "北京欢迎您!\n",
+ "我喜欢China!\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.text as text\n",
+ "\n",
+ "# 构造待分词数据\n",
+ "input_list = [\"Welcome to Beijing!\", \"北京欢迎您!\", \"我喜欢China!\"]\n",
+ "\n",
+ "# 加载数据集\n",
+ "dataset = ds.NumpySlicesDataset(input_list, column_names=[\"text\"], shuffle=False)\n",
+ "\n",
+ "print(\"------------------------before tokenization----------------------------\")\n",
+ "for data in dataset.create_dict_iterator(output_numpy=True):\n",
+ " print(text.to_str(data['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面为数据集未被分词前的数据打印情况,下面使用`UnicodeCharTokenizer`分词器对数据集进行分词。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------after tokenization-----------------------------\n",
+ "['W', 'e', 'l', 'c', 'o', 'm', 'e', ' ', 't', 'o', ' ', 'B', 'e', 'i', 'j', 'i', 'n', 'g', '!']\n",
+ "['北', '京', '欢', '迎', '您', '!']\n",
+ "['我', '喜', '欢', 'C', 'h', 'i', 'n', 'a', '!']\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 使用UnicodeCharTokenizer分词器对数据集进行分词\n",
+ "tokenizer_op = text.UnicodeCharTokenizer()\n",
+ "dataset = dataset.map(operations=tokenizer_op)\n",
+ "\n",
+ "print(\"------------------------after tokenization-----------------------------\")\n",
+ "for data in dataset.create_dict_iterator(num_epochs=1, output_numpy=True):\n",
+ " print(text.to_str(data['text']).tolist())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面两次打印结果可以看出,数据集中的句子被`UnicodeCharTokenizer`分词器进行分割,中文以单个汉字为最小单元,英文以单个字母为最小单元。\n",
+ "\n",
+ "#### WhitespaceTokenizer\n",
+ "\n",
+ "`WhitespaceTokenizer`操作是根据空格来进行分词的。\n",
+ "\n",
+ "下面的样例首先构建了一个文本数据集,然后通过`WhitespaceTokenizer`对数据集进行分词,并展示了分词前后的文本结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------before tokenization----------------------------\n",
+ "Welcome to Beijing!\n",
+ "北京欢迎您!\n",
+ "我喜欢China!\n",
+ "床前明月光,疑是地上霜。\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.text as text\n",
+ "\n",
+ "# 构造待分词数据\n",
+ "input_list = [\"Welcome to Beijing!\", \"北京欢迎您!\", \"我喜欢China!\", \"床前明月光,疑是地上霜。\"]\n",
+ "\n",
+ "# 加载数据集\n",
+ "dataset = ds.NumpySlicesDataset(input_list, column_names=[\"text\"], shuffle=False)\n",
+ "\n",
+ "print(\"------------------------before tokenization----------------------------\")\n",
+ "for data in dataset.create_dict_iterator(output_numpy=True):\n",
+ " print(text.to_str(data['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面为数据集未被分词前的数据打印情况,下面使用`WhitespaceTokenizer`分词器对数据集进行分词。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------after tokenization-----------------------------\n",
+ "['Welcome', 'to', 'Beijing!']\n",
+ "['北京欢迎您!']\n",
+ "['我喜欢China!']\n",
+ "['床前明月光,疑是地上霜。']\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 使用WhitespaceTokenizer分词器对数据集进行分词\n",
+ "tokenizer_op = text.WhitespaceTokenizer()\n",
+ "dataset = dataset.map(operations=tokenizer_op)\n",
+ "\n",
+ "print(\"------------------------after tokenization-----------------------------\")\n",
+ "for i in dataset.create_dict_iterator(num_epochs=1, output_numpy=True):\n",
+ " print(text.to_str(i['text']).tolist())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面两次打印结果可以看出,数据集中的句子被`WhitespaceTokenizer`分词器以空格为分隔符进行分割。\n",
+ "\n",
+ "#### WordpieceTokenizer\n",
+ "\n",
+ "`WordpieceTokenizer`操作是基于单词集来进行划分的,划分依据可以是单词集中的单个单词,或者多个单词的组合形式。\n",
+ "\n",
+ "下面的样例首先构建了一个文本数据集,然后从单词列表中构建`vocab`对象,通过`WordpieceTokenizer`对数据集进行分词,并展示了分词前后的文本结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------before tokenization----------------------------\n",
+ "My\n",
+ "favorite\n",
+ "book\n",
+ "is\n",
+ "love\n",
+ "during\n",
+ "the\n",
+ "cholera\n",
+ "era\n",
+ ".\n",
+ "what\n",
+ "我\n",
+ "最\n",
+ "喜\n",
+ "欢\n",
+ "的\n",
+ "书\n",
+ "是\n",
+ "霍\n",
+ "乱\n",
+ "时\n",
+ "期\n",
+ "的\n",
+ "爱\n",
+ "情\n",
+ "。\n",
+ "好\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.text as text\n",
+ "\n",
+ "# 构造待分词数据\n",
+ "input_list = [\"My\", \"favorite\", \"book\", \"is\", \"love\", \"during\", \"the\", \"cholera\", \"era\", \".\", \"what\",\n",
+ " \"我\", \"最\", \"喜\", \"欢\", \"的\", \"书\", \"是\", \"霍\", \"乱\", \"时\", \"期\", \"的\", \"爱\", \"情\", \"。\", \"好\"]\n",
+ "\n",
+ "# 构造英文词汇表\n",
+ "vocab_english = [\"book\", \"cholera\", \"era\", \"favor\", \"##ite\", \"My\", \"is\", \"love\", \"dur\", \"##ing\", \"the\", \".\"]\n",
+ "\n",
+ "# 构造中文词汇表\n",
+ "vocab_chinese = ['我', '最', '喜', '欢', '的', '书', '是', '霍', '乱', '时', '期', '爱', '情', '。']\n",
+ "\n",
+ "# 加载数据集\n",
+ "dataset = ds.NumpySlicesDataset(input_list, column_names=[\"text\"], shuffle=False)\n",
+ "\n",
+ "print(\"------------------------before tokenization----------------------------\")\n",
+ "for data in dataset.create_dict_iterator(output_numpy=True):\n",
+ " print(text.to_str(data['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面为数据集未被分词前的数据打印情况,此处特意构造了词汇表中没有的单词“what”和“好”,下面使用`WordpieceTokenizer`分词器对数据集进行分词。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------------------------after tokenization-----------------------------\n",
+ "['My']\n",
+ "['favor' '##ite']\n",
+ "['book']\n",
+ "['is']\n",
+ "['love']\n",
+ "['dur' '##ing']\n",
+ "['the']\n",
+ "['cholera']\n",
+ "['era']\n",
+ "['.']\n",
+ "['[UNK]']\n",
+ "['我']\n",
+ "['最']\n",
+ "['喜']\n",
+ "['欢']\n",
+ "['的']\n",
+ "['书']\n",
+ "['是']\n",
+ "['霍']\n",
+ "['乱']\n",
+ "['时']\n",
+ "['期']\n",
+ "['的']\n",
+ "['爱']\n",
+ "['情']\n",
+ "['。']\n",
+ "['[UNK]']\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 使用WordpieceTokenizer分词器对数据集进行分词\n",
+ "vocab = text.Vocab.from_list(vocab_english+vocab_chinese)\n",
+ "tokenizer_op = text.WordpieceTokenizer(vocab=vocab)\n",
+ "dataset = dataset.map(operations=tokenizer_op)\n",
+ "\n",
+ "print(\"------------------------after tokenization-----------------------------\")\n",
+ "for i in dataset.create_dict_iterator(num_epochs=1, output_numpy=True):\n",
+ " print(text.to_str(i['text']))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面两次打印结果可以看出,数据集中的词语被`WordpieceTokenizer`分词器以构造的词汇表进行分词,“My”仍然被分为“My”,“love”仍然被分为“love”。值得注意的是,“favorite”被分为了“favor”和“##ite”,由于“word”和“好”在词汇表中未找到,所以使用\\[UNK\\]表示。"
+ ]
+ }
+ ],
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/basic_graph.graffle b/tutorials/source_zh_cn/advanced/dataset/images/basic_graph.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..06e29462afcb425217bcb25833626019f7fa1ed1
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/basic_graph.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/basic_graph.png b/tutorials/source_zh_cn/advanced/dataset/images/basic_graph.png
new file mode 100644
index 0000000000000000000000000000000000000000..def29ced54ed9ef7123c3f4ef06d6ef121060b75
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/basic_graph.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/cifar10.jpg b/tutorials/source_zh_cn/advanced/dataset/images/cifar10.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..950221430b5c5133b4cda31fe5b09ae96f08eb6e
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/cifar10.jpg differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/data_conversion_concept.graffle b/tutorials/source_zh_cn/advanced/dataset/images/data_conversion_concept.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..83dbddbc4b2c87efe0496681c93f669fc0020712
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/data_conversion_concept.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/data_conversion_concept.png b/tutorials/source_zh_cn/advanced/dataset/images/data_conversion_concept.png
new file mode 100644
index 0000000000000000000000000000000000000000..74a0198b1e8f633b577604fd7c066b44bf9acdcb
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/data_conversion_concept.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/dataset_process.graffle b/tutorials/source_zh_cn/advanced/dataset/images/dataset_process.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..a21dd9fd0901858d6af5a1c02f18ae2ebf7088d4
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/dataset_process.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/dataset_process.png b/tutorials/source_zh_cn/advanced/dataset/images/dataset_process.png
new file mode 100644
index 0000000000000000000000000000000000000000..c0b0cb3d32591eed32eefee7dc248dd8db15d96d
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/dataset_process.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/graph_sample.graffle b/tutorials/source_zh_cn/advanced/dataset/images/graph_sample.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..d5090ad80c2c523d18ffe46de1fe34093de7d324
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/graph_sample.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/graph_sample.png b/tutorials/source_zh_cn/advanced/dataset/images/graph_sample.png
new file mode 100644
index 0000000000000000000000000000000000000000..a4310c6111629102e0ab2fc9fea76aefd1f3f083
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/graph_sample.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/mindrecord.graffle b/tutorials/source_zh_cn/advanced/dataset/images/mindrecord.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..1f47a56c6d97c53e47f35c162cefd92e2bbb8767
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/mindrecord.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/mindrecord.png b/tutorials/source_zh_cn/advanced/dataset/images/mindrecord.png
new file mode 100644
index 0000000000000000000000000000000000000000..8dc15b9b1de8557c783206898d7c737d34c9d38b
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/mindrecord.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_batch.graffle b/tutorials/source_zh_cn/advanced/dataset/images/op_batch.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..ad3a59471180ab58fc83f73b3dcf75c3abc1de06
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_batch.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_batch.png b/tutorials/source_zh_cn/advanced/dataset/images/op_batch.png
new file mode 100644
index 0000000000000000000000000000000000000000..3847cc60775ad656c2f6f3743f3a918ec70d1c7a
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_batch.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_concat.graffle b/tutorials/source_zh_cn/advanced/dataset/images/op_concat.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..b6f4609f57cf76ab44a53bbdb77db447c2963995
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_concat.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_concat.png b/tutorials/source_zh_cn/advanced/dataset/images/op_concat.png
new file mode 100644
index 0000000000000000000000000000000000000000..7f0895fc9bfbcd09475e69d38a4b43f4e0e4ba37
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_concat.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_map.graffle b/tutorials/source_zh_cn/advanced/dataset/images/op_map.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..a707133f193cc14f814c2109b86e6f1f84e77c33
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_map.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_map.png b/tutorials/source_zh_cn/advanced/dataset/images/op_map.png
new file mode 100644
index 0000000000000000000000000000000000000000..6eae5c56323739715c8aa51374b47e89b556c855
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_map.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_repeat.graffle b/tutorials/source_zh_cn/advanced/dataset/images/op_repeat.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..f29c374c814c39e0d28cc170663e8169a3a565ee
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_repeat.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_repeat.png b/tutorials/source_zh_cn/advanced/dataset/images/op_repeat.png
new file mode 100644
index 0000000000000000000000000000000000000000..d24a1d718b2894f9165e0f74bd93f87b2d516286
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_repeat.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_shuffle.graffle b/tutorials/source_zh_cn/advanced/dataset/images/op_shuffle.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..145549582f9e99eab51948c5444c16475ad1393b
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_shuffle.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_shuffle.png b/tutorials/source_zh_cn/advanced/dataset/images/op_shuffle.png
new file mode 100644
index 0000000000000000000000000000000000000000..8b1a788e66effb87329bd2c697a06d09a587dab6
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_shuffle.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_zip.graffle b/tutorials/source_zh_cn/advanced/dataset/images/op_zip.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..e61a2df9f74160bfbf4843d773014f134f5e2349
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_zip.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/op_zip.png b/tutorials/source_zh_cn/advanced/dataset/images/op_zip.png
new file mode 100644
index 0000000000000000000000000000000000000000..f33511cd17e2185ec26e13d4d409c5124d009cfd
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/op_zip.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/transform_not_recommended.graffle b/tutorials/source_zh_cn/advanced/dataset/images/transform_not_recommended.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..b77cb0196b10066a0b59b72d8a4d4da01c0164ff
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/transform_not_recommended.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/transform_not_recommended.png b/tutorials/source_zh_cn/advanced/dataset/images/transform_not_recommended.png
new file mode 100644
index 0000000000000000000000000000000000000000..4d55f671be08b3b38ef5ed5098e8c6a1911a96c0
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/transform_not_recommended.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_1.graffle b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_1.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..5abb22a3e7d7e26cf5f531e3cada031a1454c7b8
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_1.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_1.png b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_1.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d8ab9c122f56746f063507524f0ad4f4f0bada0
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_1.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_2.graffle b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_2.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..174b791db3783da49bfbe9f68dd91d17d26c6897
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_2.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_2.png b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..95540999f926333afd00917c35f3c6d118314df3
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_2.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_3.graffle b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_3.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..7c663679b0a43ed0c7cbcf2f2b6ef8c1e25bc1dc
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_3.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_3.png b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_3.png
new file mode 100644
index 0000000000000000000000000000000000000000..d5443a60d3d646a1c829a979dc1e3dfb45ced2ca
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/dataset/images/transform_recommended_3.png differ
diff --git a/tutorials/source_zh_cn/advanced/dataset/iterator.ipynb b/tutorials/source_zh_cn/advanced/dataset/iterator.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..7253242497020583027d8274c660e377e5d941d2
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset/iterator.ipynb
@@ -0,0 +1,498 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 数据迭代\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_iterator.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_iterator.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/dataset/iterator.ipynb)\n",
+ "\n",
+ "原始数据集通过数据集加载接口读取到内存,再通过数据增强操作进行数据变换,最后得到的数据集对象有两种常规的数据迭代方法:\n",
+ "\n",
+ "1. 创建`iterator`迭代器进行数据迭代。\n",
+ "2. 数据直接传入网络模型的Model接口(如`model.train`、`model.eval`等)进行迭代训练或推理。\n",
+ "\n",
+ "## 创建迭代器\n",
+ "\n",
+ "数据集对象通常可以创建两种不同的迭代器来遍历数据,分别为:\n",
+ "\n",
+ "1. **元组迭代器**。创建元组迭代器的接口为`create_tuple_iterator`,通常用于`Model.train`内部使用,其迭代出来的数据可以直接用于训练。\n",
+ "2. **字典迭代器**。创建字典迭代器的接口为`create_dict_iterator`,自定义`train`训练模式下,用户可以根据字典中的`key`进行进一步的数据处理操作,再输入到网络中,使用较为灵活。\n",
+ "\n",
+ "下面通过示例介绍两种迭代器的使用方式:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "# 数据集\n",
+ "np_data = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]\n",
+ "\n",
+ "# 加载数据\n",
+ "dataset = ds.NumpySlicesDataset(np_data, column_names=[\"data\"], shuffle=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "然后使用`create_tuple_iterator`或者`create_dict_iterator`创建数据迭代器。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-09-13T09:01:55.937317Z",
+ "start_time": "2021-09-13T09:01:53.924910Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ " create tuple iterator\n",
+ "item:\n",
+ " [[1 2]\n",
+ " [3 4]]\n",
+ "item:\n",
+ " [[5 6]\n",
+ " [7 8]]\n",
+ "\n",
+ " create dict iterator\n",
+ "item:\n",
+ " [[1 2]\n",
+ " [3 4]]\n",
+ "item:\n",
+ " [[5 6]\n",
+ " [7 8]]\n",
+ "\n",
+ " iterate dataset object directly\n",
+ "item:\n",
+ " [[1 2]\n",
+ " [3 4]]\n",
+ "item:\n",
+ " [[5 6]\n",
+ " [7 8]]\n",
+ "\n",
+ " iterate dataset using enumerate\n",
+ "index: 0, item:\n",
+ " [[1 2]\n",
+ " [3 4]]\n",
+ "index: 1, item:\n",
+ " [[5 6]\n",
+ " [7 8]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 创建元组迭代器\n",
+ "print(\"\\n create tuple iterator\")\n",
+ "for item in dataset.create_tuple_iterator():\n",
+ " print(\"item:\\n\", item[0])\n",
+ "\n",
+ "# 创建字典迭代器\n",
+ "print(\"\\n create dict iterator\")\n",
+ "for item in dataset.create_dict_iterator():\n",
+ " print(\"item:\\n\", item[\"data\"])\n",
+ "\n",
+ "# 直接遍历数据集对象(等同于创建元组迭代器)\n",
+ "print(\"\\n iterate dataset object directly\")\n",
+ "for item in dataset:\n",
+ " print(\"item:\\n\", item[0])\n",
+ "\n",
+ "# 使用enumerate方式遍历(等同于创建元组迭代器)\n",
+ "print(\"\\n iterate dataset using enumerate\")\n",
+ "for index, item in enumerate(dataset):\n",
+ " print(\"index: {}, item:\\n {}\".format(index, item[0]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "如果需要产生多个epoch的数据,可以相应地调整入参`num_epochs`的取值。相比于多次调用迭代器接口,直接设置epoch数可以提高数据迭代的性能。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-09-13T09:01:55.951495Z",
+ "start_time": "2021-09-13T09:01:55.938705Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "epoch: 0\n",
+ "item:\n",
+ " [[1 2]\n",
+ " [3 4]]\n",
+ "item:\n",
+ " [[5 6]\n",
+ " [7 8]]\n",
+ "epoch: 1\n",
+ "item:\n",
+ " [[1 2]\n",
+ " [3 4]]\n",
+ "item:\n",
+ " [[5 6]\n",
+ " [7 8]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "epoch = 2 # 创建元组迭代器产生2个epoch的数据\n",
+ "\n",
+ "iterator = dataset.create_tuple_iterator(num_epochs=epoch)\n",
+ "\n",
+ "for i in range(epoch):\n",
+ " print(\"epoch: \", i)\n",
+ " for item in iterator:\n",
+ " print(\"item:\\n\", item[0])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "迭代器默认输出的数据类型为`mindspore.Tensor`,如果希望得到`numpy.ndarray`类型的数据,可以设置入参`output_numpy=True`。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "dtype: \n",
+ "item:\n",
+ " [[1 2]\n",
+ " [3 4]]\n",
+ "dtype: \n",
+ "item:\n",
+ " [[5 6]\n",
+ " [7 8]]\n",
+ "dtype: \n",
+ "item:\n",
+ " [[1 2]\n",
+ " [3 4]]\n",
+ "dtype: \n",
+ "item:\n",
+ " [[5 6]\n",
+ " [7 8]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 默认输出类型为mindspore.Tensor\n",
+ "for item in dataset.create_tuple_iterator():\n",
+ " print(\"dtype: \", type(item[0]), \"\\nitem:\\n\", item[0])\n",
+ "\n",
+ "# 设置输出类型为numpy.ndarray\n",
+ "for item in dataset.create_tuple_iterator(output_numpy=True):\n",
+ " print(\"dtype: \", type(item[0]), \"\\nitem:\\n\", item[0])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 在训练网络时使用迭代器\n",
+ "\n",
+ "下面我们通过一个拟合线性函数的场景,介绍在训练网络时如何使用数据迭代器,线性函数表达式为:\n",
+ "\n",
+ "$$output = {x_0}\\times1 + {x_1}\\times2 + {x_2}\\times3 + ··· + {x_7}\\times8$$\n",
+ "\n",
+ "其函数定义如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def func(x):\n",
+ " \"\"\"定义线性函数表达式\"\"\"\n",
+ " result = []\n",
+ " for sample in x:\n",
+ " total = 0\n",
+ " for i, e in enumerate(sample):\n",
+ " total += (i+1) * e\n",
+ " result.append(total)\n",
+ " return result"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "使用上面的线性函数构造自定义训练数据集和验证数据集。在构造自定义训练数据集时需要注意,上述线性函数表达式有8个未知数,把训练数据集的数据带入上述线性函数得出8个线性无关方程,通过解方程即可得出未知数的值。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "class MyTrainData:\n",
+ " \"\"\"自定义训练用数据集类\"\"\"\n",
+ " def __init__(self):\n",
+ " \"\"\"初始化操作\"\"\"\n",
+ " self.__data = np.array([[[1, 1, 1, 1, 1, 1, 1, 1]],\n",
+ " [[1, 1, 1, 1, 1, 1, 1, 0]],\n",
+ " [[1, 1, 1, 1, 1, 1, 0, 0]],\n",
+ " [[1, 1, 1, 1, 1, 0, 0, 0]],\n",
+ " [[1, 1, 1, 1, 0, 0, 0, 0]],\n",
+ " [[1, 1, 1, 0, 0, 0, 0, 0]],\n",
+ " [[1, 1, 0, 0, 0, 0, 0, 0]],\n",
+ " [[1, 0, 0, 0, 0, 0, 0, 0]]]).astype(np.float32)\n",
+ " self.__label = np.array([func(x) for x in self.__data]).astype(np.float32)\n",
+ "\n",
+ " def __getitem__(self, index):\n",
+ " \"\"\"定义随机访问函数\"\"\"\n",
+ " return self.__data[index], self.__label[index]\n",
+ "\n",
+ " def __len__(self):\n",
+ " \"\"\"定义获取数据集大小函数\"\"\"\n",
+ " return len(self.__data)\n",
+ "\n",
+ "class MyEvalData:\n",
+ " \"\"\"自定义验证用数据集类\"\"\"\n",
+ " def __init__(self):\n",
+ " \"\"\"初始化操作\"\"\"\n",
+ " self.__data = np.array([[[1, 2, 3, 4, 5, 6, 7, 8]],\n",
+ " [[1, 1, 1, 1, 1, 1, 1, 1]],\n",
+ " [[8, 7, 6, 5, 4, 3, 2, 1]]]).astype(np.float32)\n",
+ " self.__label = np.array([func(x) for x in self.__data]).astype(np.float32)\n",
+ "\n",
+ " def __getitem__(self, index):\n",
+ " \"\"\"定义随机访问函数\"\"\"\n",
+ " return self.__data[index], self.__label[index]\n",
+ "\n",
+ " def __len__(self):\n",
+ " \"\"\"定义获取数据集大小函数\"\"\"\n",
+ " return len(self.__data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "下面我们使用[mindspore.nn.Dense](https://mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.Dense.html?highlight=dense#mindspore.nn.Dense)创建自定义网络,网络的输入为8×1的矩阵。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import mindspore.nn as nn\n",
+ "from mindspore.common.initializer import Normal\n",
+ "\n",
+ "class MyNet(nn.Cell):\n",
+ " \"\"\"自定义网络\"\"\"\n",
+ " def __init__(self):\n",
+ " super(MyNet, self).__init__()\n",
+ " self.fc = nn.Dense(8, 1, weight_init=Normal(0.02))\n",
+ "\n",
+ " def construct(self, x):\n",
+ " x = self.fc(x)\n",
+ " return x"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "自定义网络训练,代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "--------- Train ---------\n",
+ "epoch:0, loss: 91.81532\n",
+ "epoch:5, loss: 1.5533581\n",
+ "epoch:10, loss: 0.6733132\n",
+ "epoch:15, loss: 0.00088715553\n",
+ "epoch:20, loss: 0.005654335\n",
+ "epoch:25, loss: 0.09283447\n",
+ "epoch:30, loss: 0.00046157837\n",
+ "epoch:35, loss: 0.00390625\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import amp\n",
+ "\n",
+ "def train(dataset, net, optimizer, loss, epoch):\n",
+ " \"\"\"自定义训练过程\"\"\"\n",
+ " print(\"--------- Train ---------\")\n",
+ " train_network = amp.build_train_network(net, optimizer, loss, level=\"O3\")\n",
+ " for i in range(epoch):\n",
+ " for item in dataset.create_dict_iterator(): # 此处使用数据迭代器获取数据\n",
+ " data = item[\"data\"]\n",
+ " label = item[\"label\"]\n",
+ " loss = train_network(data, label)\n",
+ " if i % 5 == 0: # 每5个epoch打印一次\n",
+ " print(\"epoch:{}, loss: {}\".format(i, loss))\n",
+ "\n",
+ "dataset = ds.GeneratorDataset(MyTrainData(), [\"data\", \"label\"], shuffle=True) # 定义数据集\n",
+ "net = MyNet() # 定义网络\n",
+ "optimizer = nn.Momentum(net.trainable_params(), 0.01, 0.9) # 定义优化器\n",
+ "loss = nn.MSELoss(reduction=\"mean\") # 定义损失函数\n",
+ "epoch = 40 # 定义训练轮次\n",
+ "\n",
+ "# 开始训练\n",
+ "train(dataset, net, optimizer, loss, epoch)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,随着训练次数逐渐增多,损失值趋于收敛。接下来我们使用上面训练好的网络进行推理,并打印预测值与目标值。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "--------- Eval ---------\n",
+ "predict: 203.875, label: 204.000\n",
+ "predict: 36.000, label: 36.000\n",
+ "predict: 116.625, label: 120.000\n"
+ ]
+ }
+ ],
+ "source": [
+ "def eval(net, data):\n",
+ " \"\"\"自定义推理过程\"\"\"\n",
+ " print(\"--------- Eval ---------\")\n",
+ " for item in data:\n",
+ " predict = net(Tensor(item[0]))[0]\n",
+ " print(\"predict: {:7.3f}, label: {:7.3f}\".format(predict.asnumpy()[0], item[1][0]))\n",
+ "\n",
+ "# 开始推理\n",
+ "eval(net, MyEvalData())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,推理结果较为准确。\n",
+ "\n",
+ "> 更多关于数据迭代器的使用说明,请参考[create_tuple_iterator](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset/mindspore.dataset.NumpySlicesDataset.html#mindspore.dataset.NumpySlicesDataset.create_tuple_iterator) 和[create_dict_iterator](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/dataset/mindspore.dataset.NumpySlicesDataset.html#mindspore.dataset.NumpySlicesDataset.create_dict_iterator)的API文档。\n",
+ "\n",
+ "## 数据迭代训练\n",
+ "\n",
+ "数据集对象创建后,可通过传入`Model`接口,由接口内部进行数据迭代,并送入网络执行训练或推理。实例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-09-13T09:01:56.002002Z",
+ "start_time": "2021-09-13T09:01:55.953018Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import ms_function\n",
+ "from mindspore import nn, Model\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.ops as ops\n",
+ "\n",
+ "def create_dataset():\n",
+ " \"\"\"创建自定义数据集\"\"\"\n",
+ " np_data = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]\n",
+ " np_data = np.array(np_data, dtype=np.float16)\n",
+ " dataset = ds.NumpySlicesDataset(np_data, column_names=[\"data\"], shuffle=False)\n",
+ " return dataset\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " \"\"\"创建一个神经网络\"\"\"\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.relu = ops.ReLU()\n",
+ " self.print = ops.Print()\n",
+ "\n",
+ " @ms_function\n",
+ " def construct(self, x):\n",
+ " self.print(x)\n",
+ " return self.relu(x)\n",
+ "\n",
+ "if __name__ == \"__main__\":\n",
+ " dataset = create_dataset()\n",
+ "\n",
+ " network = Net()\n",
+ " model = Model(network)\n",
+ "\n",
+ " # 数据集传入model中,train接口进行数据迭代处理\n",
+ " model.train(epoch=1, train_dataset=dataset)"
+ ]
+ }
+ ],
+ "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.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/dataset/record.ipynb b/tutorials/source_zh_cn/advanced/dataset/record.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..b7fe2f966e09613c0507aa91799bab46534e4187
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset/record.ipynb
@@ -0,0 +1,770 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 格式转换\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_record.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_record.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/dataset/record.ipynb)\n",
+ "\n",
+ "MindSpore中可以把用于训练网络模型的数据集,转换为MindSpore特定的格式数据(MindSpore Record格式),从而更加方便地保存和加载数据。其目标是归一化用户的数据集,并进一步通过`MindDataset`接口实现数据的读取,并用于训练过程。\n",
+ "\n",
+ "\n",
+ "\n",
+ "此外,MindSpore还针对部分数据场景进行了性能优化,使用MindSpore Record数据格式可以减少磁盘IO、网络IO开销,从而获得更好的使用体验。\n",
+ "\n",
+ "MindSpore Record数据格式具备的特征如下:\n",
+ "\n",
+ "1. 实现数据统一存储、访问,使得训练时数据读取更加简便。\n",
+ "2. 数据聚合存储、高效读取,使得训练时数据方便管理和移动。\n",
+ "3. 高效的数据编解码操作,使得用户可以对数据操作无感知。\n",
+ "4. 可以灵活控制数据切分的分区大小,实现分布式数据处理。\n",
+ "\n",
+ "## Record文件结构\n",
+ "\n",
+ "如下图所示,MindSpore Record文件由数据文件和索引文件组成。\n",
+ "\n",
+ "\n",
+ "\n",
+ "其中数据文件包含文件头、标量数据页、块数据页,用于存储用户归一化后的训练数据,且单个MindSpore Record文件建议小于20G,用户可将大数据集进行分片存储为多个MindSpore Record文件。\n",
+ "\n",
+ "而索引文件则包含基于标量数据(如图像Label、图像文件名等)生成的索引信息,用于方便的检索、统计数据集信息。\n",
+ "\n",
+ "数据文件中的文件头、标量数据页、块数据页的具体用途如下所示:\n",
+ "\n",
+ "- **文件头**:是MindSpore Record文件的元信息,主要用来存储文件头大小、标量数据页大小、块数据页大小、Schema信息、索引字段、统计信息、文件分区信息、标量数据与块数据对应关系等。\n",
+ "- **标量数据页**:主要用来存储整型、字符串、浮点型数据,如图像的Label、图像的文件名、图像的长宽等信息,即适合用标量来存储的信息会保存在这里。\n",
+ "- **块数据页**:主要用来存储二进制串、NumPy数组等数据,如二进制图像文件本身、文本转换成的字典等。\n",
+ "\n",
+ "> 值得注意的是,数据文件和索引文件均暂不支持重命名操作。\n",
+ "\n",
+ "## 转换成Record格式\n",
+ "\n",
+ "下面主要介绍如何将CV类数据和NLP类数据转换为MindSpore Record文件格式,并通过`MindDataset`接口,实现MindSpore Record文件的读取。\n",
+ "\n",
+ "### 转换CV类数据集\n",
+ "\n",
+ "本示例主要以包含100条记录的CV数据集并将其转换为MindSpore Record格式为例子,介绍如何将CV类数据集转换成MindSpore Record文件格式,并使用`MindDataset`接口读取。\n",
+ "\n",
+ "首先,需要创建100张图片的数据集并对齐进行保存,其样本包含`file_name`(字符串)、`label`(整型)、 `data`(二进制)三个字段,然后使用`MindDataset`接口读取该MindSpore Record文件。\n",
+ "\n",
+ "1. 导入相关模块。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-02-22T10:33:34.444561Z",
+ "start_time": "2021-02-22T10:33:34.441434Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from io import BytesIO\n",
+ "import os\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.mindrecord as record\n",
+ "import mindspore.dataset.vision.c_transforms as vision\n",
+ "from PIL import Image"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. 生成100张图像,并转换成MindSpore Record文件格式。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-02-22T10:34:03.889515Z",
+ "start_time": "2021-02-22T10:34:02.950207Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 2,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# 输出的MindSpore Record文件完整路径\n",
+ "MINDRECORD_FILE = \"test.mindrecord\"\n",
+ "\n",
+ "if os.path.exists(MINDRECORD_FILE):\n",
+ " os.remove(MINDRECORD_FILE)\n",
+ " os.remove(MINDRECORD_FILE + \".db\")\n",
+ "\n",
+ "# 定义包含的字段\n",
+ "cv_schema = {\"file_name\": {\"type\": \"string\"}, \"label\": {\"type\": \"int32\"}, \"data\": {\"type\": \"bytes\"}}\n",
+ "\n",
+ "# 声明MindSpore Record文件格式\n",
+ "writer = record.FileWriter(file_name=MINDRECORD_FILE, shard_num=1)\n",
+ "writer.add_schema(cv_schema, \"it is a cv dataset\")\n",
+ "writer.add_index([\"file_name\", \"label\"])\n",
+ "\n",
+ "# 创建数据集\n",
+ "data = []\n",
+ "for i in range(100):\n",
+ " i += 1\n",
+ " sample = {}\n",
+ " white_io = BytesIO()\n",
+ " Image.new('RGB', (i*10, i*10), (255, 255, 255)).save(white_io, 'JPEG')\n",
+ " image_bytes = white_io.getvalue()\n",
+ " sample['file_name'] = str(i) + \".jpg\"\n",
+ " sample['label'] = i\n",
+ " sample['data'] = white_io.getvalue()\n",
+ "\n",
+ " data.append(sample)\n",
+ " if i % 10 == 0:\n",
+ " writer.write_raw_data(data)\n",
+ " data = []\n",
+ "\n",
+ "if data:\n",
+ " writer.write_raw_data(data)\n",
+ "\n",
+ "writer.commit()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果`MSRStatus.SUCCESS`的值为`0`可以看出,数据集转换成功。在本篇后续的例子中如果数据集转换成功均可看到此打印结果。\n",
+ "\n",
+ "3. 通过`MindDataset`接口读取MindSpore Record文件格式。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-02-22T10:34:07.729322Z",
+ "start_time": "2021-02-22T10:34:07.575711Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Got 100 samples\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 读取MindSpore Record文件格式\n",
+ "data_set = ds.MindDataset(dataset_files=MINDRECORD_FILE)\n",
+ "decode_op = vision.Decode()\n",
+ "data_set = data_set.map(operations=decode_op, input_columns=[\"data\"], num_parallel_workers=2)\n",
+ "\n",
+ "# 样本计数\n",
+ "print(\"Got {} samples\".format(data_set.get_dataset_size()))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 转换NLP类数据集\n",
+ "\n",
+ "本示例首先创建一个包含100条记录的MindSpore Record文件格式,其样本包含八个字段,均为整型数组,然后使用`MindDataset`接口读取该MindSpore Record文件。\n",
+ "\n",
+ "> 为了方便展示,此处略去了将文本转换成字典序的预处理过程。\n",
+ "\n",
+ "1. 导入相关模块。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-02-22T10:34:21.606147Z",
+ "start_time": "2021-02-22T10:34:21.603094Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.mindrecord as record"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. 生成100条文本数据,并转换成MindSpore Record文件格式。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-02-22T10:34:23.883130Z",
+ "start_time": "2021-02-22T10:34:23.660213Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "# 输出的MindSpore Record文件完整路径\n",
+ "MINDRECORD_FILE = \"test.mindrecord\"\n",
+ "\n",
+ "if os.path.exists(MINDRECORD_FILE):\n",
+ " os.remove(MINDRECORD_FILE)\n",
+ " os.remove(MINDRECORD_FILE + \".db\")\n",
+ "\n",
+ "# 定义样本数据包含的字段\n",
+ "nlp_schema = {\"source_sos_ids\": {\"type\": \"int64\", \"shape\": [-1]},\n",
+ " \"source_sos_mask\": {\"type\": \"int64\", \"shape\": [-1]},\n",
+ " \"source_eos_ids\": {\"type\": \"int64\", \"shape\": [-1]},\n",
+ " \"source_eos_mask\": {\"type\": \"int64\", \"shape\": [-1]},\n",
+ " \"target_sos_ids\": {\"type\": \"int64\", \"shape\": [-1]},\n",
+ " \"target_sos_mask\": {\"type\": \"int64\", \"shape\": [-1]},\n",
+ " \"target_eos_ids\": {\"type\": \"int64\", \"shape\": [-1]},\n",
+ " \"target_eos_mask\": {\"type\": \"int64\", \"shape\": [-1]}}\n",
+ "\n",
+ "# 声明MindSpore Record文件格式\n",
+ "writer = record.FileWriter(file_name=MINDRECORD_FILE, shard_num=1)\n",
+ "writer.add_schema(nlp_schema, \"Preprocessed nlp dataset.\")\n",
+ "\n",
+ "# 创建虚拟数据集\n",
+ "data = []\n",
+ "for i in range(100):\n",
+ " i += 1\n",
+ " sample = {\"source_sos_ids\": np.array([i, i + 1, i + 2, i + 3, i + 4], dtype=np.int64),\n",
+ " \"source_sos_mask\": np.array([i * 1, i * 2, i * 3, i * 4, i * 5, i * 6, i * 7], dtype=np.int64),\n",
+ " \"source_eos_ids\": np.array([i + 5, i + 6, i + 7, i + 8, i + 9, i + 10], dtype=np.int64),\n",
+ " \"source_eos_mask\": np.array([19, 20, 21, 22, 23, 24, 25, 26, 27], dtype=np.int64),\n",
+ " \"target_sos_ids\": np.array([28, 29, 30, 31, 32], dtype=np.int64),\n",
+ " \"target_sos_mask\": np.array([33, 34, 35, 36, 37, 38], dtype=np.int64),\n",
+ " \"target_eos_ids\": np.array([39, 40, 41, 42, 43, 44, 45, 46, 47], dtype=np.int64),\n",
+ " \"target_eos_mask\": np.array([48, 49, 50, 51], dtype=np.int64)}\n",
+ " data.append(sample)\n",
+ "\n",
+ " if i % 10 == 0:\n",
+ " writer.write_raw_data(data)\n",
+ " data = []\n",
+ "\n",
+ "if data:\n",
+ " writer.write_raw_data(data)\n",
+ "\n",
+ "writer.commit()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "3. 通过`MindDataset`接口读取MindSpore Record格式文件。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-02-22T10:34:27.133717Z",
+ "start_time": "2021-02-22T10:34:27.083785Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Got 100 samples\n",
+ "source_sos_ids: [63 64 65 66 67]\n",
+ "source_sos_ids: [73 74 75 76 77]\n",
+ "source_sos_ids: [36 37 38 39 40]\n",
+ "source_sos_ids: [ 98 99 100 101 102]\n",
+ "source_sos_ids: [33 34 35 36 37]\n",
+ "source_sos_ids: [1 2 3 4 5]\n",
+ "source_sos_ids: [ 6 7 8 9 10]\n",
+ "source_sos_ids: [18 19 20 21 22]\n",
+ "source_sos_ids: [88 89 90 91 92]\n",
+ "source_sos_ids: [4 5 6 7 8]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 读取MindSpore Record文件格式\n",
+ "data_set = ds.MindDataset(dataset_files=MINDRECORD_FILE)\n",
+ "\n",
+ "# 样本计数\n",
+ "print(\"Got {} samples\".format(data_set.get_dataset_size()))\n",
+ "\n",
+ "# 打印部分数据\n",
+ "count = 0\n",
+ "for item in data_set.create_dict_iterator():\n",
+ " print(\"source_sos_ids:\", item[\"source_sos_ids\"])\n",
+ " count += 1\n",
+ " if count == 10:\n",
+ " break"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 其他数据集转换\n",
+ "\n",
+ "MindSpore提供转换常用数据集的工具类,能够将常用的数据集转换为MindSpore Record文件格式。\n",
+ "\n",
+ "> 更多数据集转换的详细说明参考[API文档](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.mindrecord.html)。\n",
+ "\n",
+ "### 转换CIFAR-10数据集\n",
+ "\n",
+ "用户可以通过`Cifar10ToMR`类,将CIFAR-10原始数据转换为MindSpore Record,并使用`MindDataset`接口读取。\n",
+ "\n",
+ "1. 下载[CIFAR-10数据集](https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz)并解压到指定目录,以下示例代码将数据集下载并解压到指定位置。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindvision import dataset\n",
+ "\n",
+ "# 声明数据集下载地址和数据集存储路径\n",
+ "dl_path = \"./datasets\"\n",
+ "dl_url = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-python.tar.gz\"\n",
+ "\n",
+ "# 下载和解压数据集\n",
+ "dl = dataset.DownLoad()\n",
+ "dl.download_and_extract_archive(url=dl_url, download_path=dl_path)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "解压后数据集文件的目录结构如下所示:\n",
+ "\n",
+ "```text\n",
+ "./datasets/cifar-10-batches-py\n",
+ "├── batches.meta\n",
+ "├── data_batch_1\n",
+ "├── data_batch_2\n",
+ "├── data_batch_3\n",
+ "├── data_batch_4\n",
+ "├── data_batch_5\n",
+ "├── readme.html\n",
+ "└── test_batch\n",
+ "```\n",
+ "\n",
+ "2. 导入相关模块。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-02-18T02:27:04.856761Z",
+ "start_time": "2021-02-18T02:26:46.536793Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import os\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.vision.c_transforms as vision\n",
+ "from mindspore.mindrecord import Cifar10ToMR"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "3. 创建`Cifar10ToMR`对象,调用`transform`接口,将CIFAR-10数据集转换为MindSpore Record文件格式。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 35,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "ds_target_path = \"./datasets/mindspore_dataset_conversion/\"\n",
+ "\n",
+ "os.system(\"rm -f {}*\".format(ds_target_path))\n",
+ "os.system(\"mkdir -p {}\".format(ds_target_path))\n",
+ "\n",
+ "# CIFAR-10数据集路径\n",
+ "CIFAR10_DIR = \"./datasets/cifar-10-batches-py\"\n",
+ "# 输出的MindSpore Record文件路径\n",
+ "MINDRECORD_FILE = \"./datasets/mindspore_dataset_conversion/cifar10.mindrecord\"\n",
+ "\n",
+ "cifar10_transformer = Cifar10ToMR(CIFAR10_DIR, MINDRECORD_FILE)\n",
+ "cifar10_transformer.transform(['label'])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "4. 通过`MindDataset`接口读取MindSpore Record文件格式。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 36,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Got 50000 samples\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 读取MindSpore Record文件格式\n",
+ "data_set = ds.MindDataset(dataset_files=MINDRECORD_FILE)\n",
+ "decode_op = vision.Decode()\n",
+ "data_set = data_set.map(operations=decode_op, input_columns=[\"data\"], num_parallel_workers=2)\n",
+ "\n",
+ "# 样本计数\n",
+ "print(\"Got {} samples\".format(data_set.get_dataset_size()))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 转换CSV数据集\n",
+ "\n",
+ "本示例首先创建一个包含5条记录的CSV文件,然后通过`CsvToMR`工具类将CSV文件转换为MindSpore Record文件格式,并最终通过`MindDataset`接口将其读取出来。\n",
+ "\n",
+ "> 本示例依赖第三方支持包`pandas`,可使用命令`pip install pandas`安装。如本文档以Notebook运行时,完成安装后需要重启kernel才能执行后续代码。\n",
+ "\n",
+ "1. 导入相关模块。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import csv\n",
+ "import os\n",
+ "import mindspore.dataset as ds\n",
+ "from mindspore import mindrecord as record"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. 生成CSV文件,并转换成MindSpore Record。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# CSV文件的路径\n",
+ "CSV_FILE = \"test.csv\"\n",
+ "# 输出的MindSpore Record文件路径\n",
+ "MINDRECORD_FILE = \"test.mindrecord\"\n",
+ "\n",
+ "if os.path.exists(MINDRECORD_FILE):\n",
+ " os.remove(MINDRECORD_FILE)\n",
+ " os.remove(MINDRECORD_FILE + \".db\")\n",
+ "\n",
+ "def generate_csv():\n",
+ " \"\"\"生成csv格式文件数据\"\"\"\n",
+ " headers = [\"id\", \"name\", \"math\", \"english\"]\n",
+ " rows = [(1, \"Lily\", 78.5, 90),\n",
+ " (2, \"Lucy\", 99, 85.2),\n",
+ " (3, \"Mike\", 65, 71),\n",
+ " (4, \"Tom\", 95, 99),\n",
+ " (5, \"Jeff\", 85, 78.5)]\n",
+ " with open(CSV_FILE, 'w', encoding='utf-8') as f:\n",
+ " writer = csv.writer(f)\n",
+ " writer.writerow(headers)\n",
+ " writer.writerows(rows)\n",
+ "\n",
+ "# 生成csv格式文件数据\n",
+ "generate_csv()\n",
+ "\n",
+ "# 转换csv格式文件\n",
+ "csv_transformer = record.CsvToMR(CSV_FILE, MINDRECORD_FILE, partition_number=1)\n",
+ "csv_transformer.transform()\n",
+ "\n",
+ "assert os.path.exists(MINDRECORD_FILE)\n",
+ "assert os.path.exists(MINDRECORD_FILE + \".db\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "3. 通过`MindDataset`接口读取MindSpore Record。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Got 5 samples\n"
+ ]
+ }
+ ],
+ "source": [
+ "data_set = ds.MindDataset(dataset_files=MINDRECORD_FILE)\n",
+ "\n",
+ "# 样本计数\n",
+ "print(\"Got {} samples\".format(data_set.get_dataset_size()))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 转换TFRecord数据集\n",
+ "\n",
+ "> 本示例需提前安装TensorFlow,目前只支持TensorFlow 1.13.0-rc1及以上版本。如本文档以Notebook运行时,完成安装后需要重启kernel才能执行后续代码。\n",
+ "\n",
+ "本示例首先通过TensorFlow创建一个TFRecord文件,然后通过`TFRecordToMR`工具类将TFRecord文件转换为MindSpore Record格式文件,最后通过`MindDataset`接口将其读取出来,并使用`Decode`函数对`image_bytes`字段进行解码。\n",
+ "\n",
+ "1. 导入相关模块。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import collections\n",
+ "from io import BytesIO\n",
+ "import os\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.mindrecord as record\n",
+ "import mindspore.dataset.vision.c_transforms as vision\n",
+ "from PIL import Image\n",
+ "import tensorflow as tf\n",
+ "\n",
+ "os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. 生成TFRecord文件。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 41,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Write 10 rows in tfrecord.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# TFRecord文件的路径\n",
+ "TFRECORD_FILE = \"test.tfrecord\"\n",
+ "# 输出的MindSpore Record文件路径\n",
+ "MINDRECORD_FILE = \"test.mindrecord\"\n",
+ "\n",
+ "def generate_tfrecord():\n",
+ " def create_int_feature(values):\n",
+ " if isinstance(values, list):\n",
+ " feature = tf.train.Feature(int64_list=tf.train.Int64List(value=list(values)))\n",
+ " else:\n",
+ " feature = tf.train.Feature(int64_list=tf.train.Int64List(value=[values]))\n",
+ " return feature\n",
+ "\n",
+ " def create_float_feature(values):\n",
+ " if isinstance(values, list):\n",
+ " feature = tf.train.Feature(float_list=tf.train.FloatList(value=list(values)))\n",
+ " else:\n",
+ " feature = tf.train.Feature(float_list=tf.train.FloatList(value=[values]))\n",
+ " return feature\n",
+ "\n",
+ " def create_bytes_feature(values):\n",
+ " if isinstance(values, bytes):\n",
+ " white_io = BytesIO()\n",
+ " Image.new('RGB', (10, 10), (255, 255, 255)).save(white_io, 'JPEG')\n",
+ " image_bytes = white_io.getvalue()\n",
+ " feature = tf.train.Feature(bytes_list=tf.train.BytesList(value=[image_bytes]))\n",
+ " else:\n",
+ " feature = tf.train.Feature(bytes_list=tf.train.BytesList(value=[bytes(values, encoding='utf-8')]))\n",
+ " return feature\n",
+ "\n",
+ " writer = tf.io.TFRecordWriter(TFRECORD_FILE)\n",
+ "\n",
+ " example_count = 0\n",
+ " for i in range(10):\n",
+ " # 随机创建Tensorflow样本数据\n",
+ " file_name = \"000\" + str(i) + \".jpg\"\n",
+ " image_bytes = bytes(str(\"aaaabbbbcccc\" + str(i)), encoding=\"utf-8\")\n",
+ " int64_scalar = i\n",
+ " float_scalar = float(i)\n",
+ " int64_list = [i, i+1, i+2, i+3, i+4, i+1234567890]\n",
+ " float_list = [float(i), float(i+1), float(i+2.8), float(i+3.2),\n",
+ " float(i+4.4), float(i+123456.9), float(i+98765432.1)]\n",
+ "\n",
+ " # 把数据存入TFRecord文件格式中\n",
+ " features = collections.OrderedDict()\n",
+ " features[\"file_name\"] = create_bytes_feature(file_name)\n",
+ " features[\"image_bytes\"] = create_bytes_feature(image_bytes)\n",
+ " features[\"int64_scalar\"] = create_int_feature(int64_scalar)\n",
+ " features[\"float_scalar\"] = create_float_feature(float_scalar)\n",
+ " features[\"int64_list\"] = create_int_feature(int64_list)\n",
+ " features[\"float_list\"] = create_float_feature(float_list)\n",
+ "\n",
+ " tf_example = tf.train.Example(features=tf.train.Features(feature=features))\n",
+ " writer.write(tf_example.SerializeToString())\n",
+ " example_count += 1\n",
+ "\n",
+ " writer.close()\n",
+ " print(\"Write {} rows in tfrecord.\".format(example_count))\n",
+ "\n",
+ "generate_tfrecord()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "3. 将TFRecord转换成MindSpore Record。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "feature_dict = {\"file_name\": tf.io.FixedLenFeature([], tf.string),\n",
+ " \"image_bytes\": tf.io.FixedLenFeature([], tf.string),\n",
+ " \"int64_scalar\": tf.io.FixedLenFeature([], tf.int64),\n",
+ " \"float_scalar\": tf.io.FixedLenFeature([], tf.float32),\n",
+ " \"int64_list\": tf.io.FixedLenFeature([6], tf.int64),\n",
+ " \"float_list\": tf.io.FixedLenFeature([7], tf.float32),\n",
+ " }\n",
+ "\n",
+ "if os.path.exists(MINDRECORD_FILE):\n",
+ " os.remove(MINDRECORD_FILE)\n",
+ " os.remove(MINDRECORD_FILE + \".db\")\n",
+ "\n",
+ "tfrecord_transformer = record.TFRecordToMR(TFRECORD_FILE, MINDRECORD_FILE, feature_dict, [\"image_bytes\"])\n",
+ "tfrecord_transformer.transform()\n",
+ "\n",
+ "assert os.path.exists(MINDRECORD_FILE)\n",
+ "assert os.path.exists(MINDRECORD_FILE + \".db\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "4. 通过`MindDataset`接口读取MindSpore Record。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 43,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Got 10 samples\n"
+ ]
+ }
+ ],
+ "source": [
+ "data_set = ds.MindDataset(dataset_files=MINDRECORD_FILE)\n",
+ "decode_op = vision.Decode()\n",
+ "data_set = data_set.map(operations=decode_op, input_columns=[\"image_bytes\"], num_parallel_workers=2)\n",
+ "\n",
+ "# 样本计数\n",
+ "print(\"Got {} samples\".format(data_set.get_dataset_size()))"
+ ]
+ }
+ ],
+ "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.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/dataset/sampler.ipynb b/tutorials/source_zh_cn/advanced/dataset/sampler.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..0153649b4192ccbd4673f9ba76adcd95d8f9f00e
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset/sampler.ipynb
@@ -0,0 +1,417 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 数据采样\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_sampler.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_sampler.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/dataset/sampler.ipynb)\n",
+ "\n",
+ "为满足训练需求,解决诸如数据集过大或样本类别分布不均等问题,MindSpore提供了多种不同用途的采样器(Sampler),帮助用户对数据集进行不同形式的采样。用户只需在加载数据集时传入采样器对象,即可实现数据的采样。\n",
+ "\n",
+ "MindSpore目前提供了如`RandomSampler`、`WeightedRandomSampler`、`SubsetRandomSampler`等多种采样器。此外,用户也可以根据需要实现自定义的采样器类。\n",
+ "\n",
+ "> 更多采样器的使用方法参见[采样器API文档](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.dataset.html)。\n",
+ "\n",
+ "## 采样器\n",
+ "\n",
+ "下面主要以CIFAR-10数据集为例,介绍几种常用MindSpore采样器的使用方法。\n",
+ "\n",
+ "\n",
+ "\n",
+ "在介绍采样器之前,我们首先把示例中用到的数据集通过MindSpore Vision套件提供的接口下载下来,并解压到指定位置。\n",
+ "\n",
+ "> 本章节中的示例代码依赖第三方支持包`matplotlib`,可使用命令`pip install matplotlib`安装。如本文档以Notebook运行时,完成安装后需要重启kernel才能执行后续代码。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindvision import dataset\n",
+ "\n",
+ "dl_path = \"./datasets\"\n",
+ "data_dir = \"./datasets/cifar-10-batches-bin/\"\n",
+ "dl_url = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/cifar-10-binary.tar.gz\"\n",
+ "\n",
+ "dl = dataset.DownLoad() # 下载CIFAR-10数据集\n",
+ "dl.download_and_extract_archive(url=dl_url, download_path=dl_path)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "解压后数据集文件的目录结构如下:\n",
+ "\n",
+ "```text\n",
+ ".\n",
+ "└── cifar-10-batches-bin\n",
+ " ├── batches.meta.txt\n",
+ " ├── data_batch_1.bin\n",
+ " ├── data_batch_2.bin\n",
+ " ├── data_batch_3.bin\n",
+ " ├── data_batch_4.bin\n",
+ " ├── data_batch_5.bin\n",
+ " ├── readme.html\n",
+ " └── test_batch.bin\n",
+ "```\n",
+ "\n",
+ "### RandomSampler\n",
+ "\n",
+ "从索引序列中随机采样指定数目的数据。\n",
+ "\n",
+ "下面的样例使用随机采样器,分别从数据集中有放回和无放回地随机采样5个数据,并打印展示。为了便于观察有放回与无放回的效果,这里自定义了一个数据量较小的数据集。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "With Replacement: 5 5 6 7 5 \n",
+ "Without Replacement: 1 3 2 6 4 "
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "ds.config.set_seed(0)\n",
+ "\n",
+ "np_data = [1, 2, 3, 4, 5, 6, 7, 8] # 数据集\n",
+ "\n",
+ "# 定义有放回采样器,采样5条数据\n",
+ "sampler1 = ds.RandomSampler(replacement=True, num_samples=5)\n",
+ "dataset1 = ds.NumpySlicesDataset(np_data, column_names=[\"data\"], sampler=sampler1)\n",
+ "\n",
+ "print(\"With Replacement: \", end='')\n",
+ "for data in dataset1.create_tuple_iterator():\n",
+ " print(data[0], end=' ')\n",
+ "\n",
+ "# 定义无放回采样器,采样5条数据\n",
+ "sampler2 = ds.RandomSampler(replacement=False, num_samples=5)\n",
+ "dataset2 = ds.NumpySlicesDataset(np_data, column_names=[\"data\"], sampler=sampler2)\n",
+ "\n",
+ "print(\"\\nWithout Replacement: \", end='')\n",
+ "for data in dataset2.create_tuple_iterator():\n",
+ " print(data[0], end=' ')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,使用有放回采样器时,同一条数据可能会被多次获取;使用无放回采样器时,同一条数据只能被获取一次。\n",
+ "\n",
+ "### WeightedRandomSampler\n",
+ "\n",
+ "指定长度为N的采样概率列表,按照概率在前N个样本中随机采样指定数目的数据。\n",
+ "\n",
+ "下面的样例使用带权随机采样器从CIFAR-10数据集的前10个样本中按概率获取6个样本,并展示已读取数据的形状和标签。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Image shape: (32, 32, 3) , Label: 9\n",
+ "Image shape: (32, 32, 3) , Label: 9\n",
+ "Image shape: (32, 32, 3) , Label: 6\n",
+ "Image shape: (32, 32, 3) , Label: 9\n",
+ "Image shape: (32, 32, 3) , Label: 6\n",
+ "Image shape: (32, 32, 3) , Label: 6\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import math\n",
+ "import matplotlib.pyplot as plt\n",
+ "import mindspore.dataset as ds\n",
+ "%matplotlib inline\n",
+ "\n",
+ "ds.config.set_seed(1) # 设置随机数种子\n",
+ "\n",
+ "DATA_DIR = \"./datasets/cifar-10-batches-bin/\"\n",
+ "\n",
+ "# 指定前10个样本的采样概率并进行采样\n",
+ "weights = [0.8, 0.5, 0, 0, 0, 0, 0, 0, 0, 0]\n",
+ "sampler = ds.WeightedRandomSampler(weights, num_samples=6)\n",
+ "dataset = ds.Cifar10Dataset(DATA_DIR, sampler=sampler) # 加载数据\n",
+ "\n",
+ "def plt_result(dataset, row):\n",
+ " \"\"\"显示采样结果\"\"\"\n",
+ " num = 1\n",
+ " for data in dataset.create_dict_iterator():\n",
+ " print(\"Image shape:\", data['image'].shape, \", Label:\", data['label'])\n",
+ " plt.subplot(row, math.ceil(dataset.get_dataset_size() / row), num)\n",
+ " image = data['image'].asnumpy()\n",
+ " plt.imshow(image, interpolation=\"None\")\n",
+ " num += 1\n",
+ "\n",
+ "plt_result(dataset, 2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,本次再前一共在前10个样本中随机采样了6条数据,只有前面两个采样概率不为0的样本才有机会被采样。\n",
+ "\n",
+ "### SubsetRandomSampler\n",
+ "\n",
+ "从指定样本索引子序列中随机采样指定数目的样本数据。\n",
+ "\n",
+ "下面的样例使用子序列随机采样器从CIFAR-10数据集的指定子序列中抽样3个样本,并展示已读取数据的形状和标签。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Image shape: (32, 32, 3) , Label: 1\n",
+ "Image shape: (32, 32, 3) , Label: 6\n",
+ "Image shape: (32, 32, 3) , Label: 4\n",
+ "Image shape: (32, 32, 3) , Label: 9\n",
+ "Image shape: (32, 32, 3) , Label: 9\n",
+ "Image shape: (32, 32, 3) , Label: 1\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "ds.config.set_seed(2) # 设置随机数种子\n",
+ "\n",
+ "DATA_DIR = \"./datasets/cifar-10-batches-bin/\" # CIFAR-10数据集存放路径\n",
+ "\n",
+ "# 指定样本索引序列\n",
+ "indices = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
+ "sampler = ds.SubsetRandomSampler(indices, num_samples=6)\n",
+ "# 加载数据\n",
+ "dataset = ds.Cifar10Dataset(DATA_DIR, sampler=sampler)\n",
+ "\n",
+ "plt_result(dataset, 2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看到,采样器从索引序列中随机采样了6个样本。\n",
+ "\n",
+ "### PKSampler\n",
+ "\n",
+ "在指定的数据集类别P中,每种类别各采样K条数据。\n",
+ "\n",
+ "下面的样例使用PK采样器从CIFAR-10数据集中每种类别抽样2个样本,最多20个样本,并展示已读取数据的形状和标签。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Image shape: (32, 32, 3) , Label: 0\n",
+ "Image shape: (32, 32, 3) , Label: 0\n",
+ "Image shape: (32, 32, 3) , Label: 1\n",
+ "Image shape: (32, 32, 3) , Label: 1\n",
+ "Image shape: (32, 32, 3) , Label: 2\n",
+ "Image shape: (32, 32, 3) , Label: 2\n",
+ "Image shape: (32, 32, 3) , Label: 3\n",
+ "Image shape: (32, 32, 3) , Label: 3\n",
+ "Image shape: (32, 32, 3) , Label: 4\n",
+ "Image shape: (32, 32, 3) , Label: 4\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "ds.config.set_seed(3) # 设置随机数种子\n",
+ "DATA_DIR = \"./datasets/cifar-10-batches-bin/\" # CIFAR-10数据集存放路径\n",
+ "\n",
+ "# 每种类别抽样2个样本,最多10个样本\n",
+ "sampler = ds.PKSampler(num_val=2, class_column='label', num_samples=10)\n",
+ "dataset = ds.Cifar10Dataset(DATA_DIR, sampler=sampler)\n",
+ "\n",
+ "plt_result(dataset, 3)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,采样器对数据集中的每种标签都采样了2个样本,一共10个样本。\n",
+ "\n",
+ "### DistributedSampler\n",
+ "\n",
+ "在分布式训练中,对数据集分片进行采样。\n",
+ "\n",
+ "下面的样例使用分布式采样器将构建的数据集分为4片,在分片抽取一个样本,共中采样3个样本,并展示已读取的数据。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'data': Tensor(shape=[], dtype=Int64, value= 0)}\n",
+ "{'data': Tensor(shape=[], dtype=Int64, value= 4)}\n",
+ "{'data': Tensor(shape=[], dtype=Int64, value= 8)}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "# 自定义数据集\n",
+ "data_source = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]\n",
+ "\n",
+ "# 构建的数据集分为4片,共采样3个数据样本\n",
+ "sampler = ds.DistributedSampler(num_shards=4, shard_id=0, shuffle=False, num_samples=3)\n",
+ "dataset = ds.NumpySlicesDataset(data_source, column_names=[\"data\"], sampler=sampler)\n",
+ "\n",
+ "# 打印数据集\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,数据集被分成了4片,每片有3个样本,本次获取的是id为0的片中的样本。\n",
+ "\n",
+ "## 自定义采样器\n",
+ "\n",
+ "用户可以继承`Sampler`基类,通过实现`__iter__`方法来自定义采样器的采样方式。\n",
+ "\n",
+ "下面的样例定义了一个从下标0至下标9间隔为2采样的采样器,将其作用于自定义数据集,并展示已读取数据。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "a c e g i "
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "# 自定义采样器\n",
+ "class MySampler(ds.Sampler):\n",
+ " def __iter__(self):\n",
+ " for i in range(0, 10, 2):\n",
+ " yield i\n",
+ "\n",
+ "# 自定义数据集\n",
+ "np_data = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']\n",
+ "\n",
+ "# 加载数据\n",
+ "sampler = ds.IterSampler(sampler=MySampler())\n",
+ "dataset = ds.NumpySlicesDataset(np_data, column_names=[\"data\"], sampler=sampler)\n",
+ "for data in dataset.create_tuple_iterator():\n",
+ " print(data[0], end=' ')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印可以看出,自定义的采样器读取了下标为0、2、4、6、8的样本数据,这与自定义采样器的采样目的一致。"
+ ]
+ }
+ ],
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/dataset/transform.ipynb b/tutorials/source_zh_cn/advanced/dataset/transform.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..ae87f22966fb7404f7808823eb022c964ba786e3
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/dataset/transform.ipynb
@@ -0,0 +1,472 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 数据处理\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_transform.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/dataset/mindspore_transform.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/dataset/transform.ipynb)\n",
+ "\n",
+ "数据是深度学习的基础,良好的数据输入可以对整个深度神经网络训练起到非常积极的作用。在训练前对已加载的数据集进行数据处理,可以解决诸如数据量过大、样本分布不均等问题,从而获得对训练结果更有利的数据输入。\n",
+ "\n",
+ "MindSpore的各个数据集类都为用户提供了多种数据处理操作,用户可以通过构建数据处理的流水线(pipeline)来定义需要使用的数据处理操作,在训练过程中,数据即可像水一样源源不断地经过数据处理pipeline流向训练系统。\n",
+ "\n",
+ "MindSpore目前支持如数据清洗`shuffle`、数据分批`batch`、数据重复`repeat`、数据拼接`concat`等常用数据处理操作。\n",
+ "\n",
+ "> 更多数据处理操作参见[API文档](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.dataset.html)。\n",
+ "\n",
+ "## 数据处理操作\n",
+ "\n",
+ "### shuffle\n",
+ "\n",
+ "shuffle操作会随机打乱数据顺序,对数据集进行混洗。\n",
+ "\n",
+ "设定的`buffer_size`越大,数据混洗程度越大,同时所消耗的时间、计算资源也更大。\n",
+ "\n",
+ "\n",
+ "\n",
+ "下面的样例先构建了一个随机数据集,然后对其进行混洗操作,最后展示了数据混洗前后的结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])}\n",
+ "------ after processing ------\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "ds.config.set_seed(0)\n",
+ "\n",
+ "def generator_func():\n",
+ " \"\"\"定义生成数据集函数\"\"\"\n",
+ " for i in range(5):\n",
+ " yield (np.array([i, i+1, i+2]),)\n",
+ "\n",
+ "# 生成数据集\n",
+ "dataset = ds.GeneratorDataset(generator_func, [\"data\"])\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "print(\"------ after processing ------\")\n",
+ "\n",
+ "# 执行数据清洗操作\n",
+ "dataset = dataset.shuffle(buffer_size=2)\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,经过`shuffle`操作之后,数据顺序被打乱了。\n",
+ "\n",
+ "### batch\n",
+ "\n",
+ "batch操作将数据集分批,分别输入到训练系统中进行训练,可以减少训练轮次,达到加速训练过程的目的。\n",
+ "\n",
+ "\n",
+ "\n",
+ "下面的样例先构建了一个数据集,然后分别展示了丢弃多余数据与否的数据集分批结果,其中批大小为2。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])}\n",
+ "------not drop remainder ------\n",
+ "{'data': Tensor(shape=[2, 3], dtype=Int64, value=\n",
+ "[[0, 1, 2],\n",
+ " [1, 2, 3]])}\n",
+ "{'data': Tensor(shape=[2, 3], dtype=Int64, value=\n",
+ "[[2, 3, 4],\n",
+ " [3, 4, 5]])}\n",
+ "{'data': Tensor(shape=[1, 3], dtype=Int64, value=\n",
+ "[[4, 5, 6]])}\n",
+ "------ drop remainder ------\n",
+ "{'data': Tensor(shape=[2, 3], dtype=Int64, value=\n",
+ "[[0, 1, 2],\n",
+ " [1, 2, 3]])}\n",
+ "{'data': Tensor(shape=[2, 3], dtype=Int64, value=\n",
+ "[[2, 3, 4],\n",
+ " [3, 4, 5]])}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "def generator_func():\n",
+ " \"\"\"定义生成数据集函数\"\"\"\n",
+ " for i in range(5):\n",
+ " yield (np.array([i, i+1, i+2]),)\n",
+ "\n",
+ "dataset = ds.GeneratorDataset(generator_func, [\"data\"])\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "# 采用不丢弃多余数据的方式对数据集进行分批\n",
+ "dataset = ds.GeneratorDataset(generator_func, [\"data\"])\n",
+ "dataset = dataset.batch(batch_size=2, drop_remainder=False)\n",
+ "print(\"------not drop remainder ------\")\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "# 采用丢弃多余数据的方式对数据集进行分批\n",
+ "dataset = ds.GeneratorDataset(generator_func, [\"data\"])\n",
+ "dataset = dataset.batch(batch_size=2, drop_remainder=True)\n",
+ "print(\"------ drop remainder ------\")\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,数据集大小为5,每2个分一组,不丢弃多余数据时分为3组,丢弃多余数据时分为2组,最后一条数据被丢弃。\n",
+ "\n",
+ "### repeat\n",
+ "\n",
+ "repeat操作对数据集进行重复,达到扩充数据量的目的。`repeat`和`batch`操作的先后顺序会影响训练batch的数量,建议将`repeat`置于`batch`之后。\n",
+ "\n",
+ "\n",
+ "\n",
+ "下面的样例先构建了一个随机数据集,然后将其重复2次,最后展示了重复后的数据结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 40,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])}\n",
+ "------ after processing ------\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "def generator_func():\n",
+ " \"\"\"定义生成数据集函数\"\"\"\n",
+ " for i in range(5):\n",
+ " yield (np.array([i, i+1, i+2]),)\n",
+ "\n",
+ "# 生成数据集\n",
+ "dataset = ds.GeneratorDataset(generator_func, [\"data\"])\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "print(\"------ after processing ------\")\n",
+ "\n",
+ "# 对数据进行数据重复操作\n",
+ "dataset = dataset.repeat(count=2)\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,数据集被拷贝了之后扩充到原数据集后面。\n",
+ "\n",
+ "### zip\n",
+ "\n",
+ "zip操作实现两个数据集的列拼接,将其合并为一个数据集。使用时需要注意以下两点:\n",
+ "\n",
+ "1. 如果两个数据集的列名相同,则不会合并,请注意列的命名。\n",
+ "2. 如果两个数据集的行数不同,合并后的行数将和较小行数保持一致。\n",
+ "\n",
+ "\n",
+ "\n",
+ "下面的样例先构建了两个不同样本数的随机数据集,然后将其进行列拼接,最后展示了拼接后的数据结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "------ data1 ------\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [5, 6, 7])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [6, 7, 8])}\n",
+ "------ data2 ------\n",
+ "{'data2': Tensor(shape=[2], dtype=Int64, value= [1, 2])}\n",
+ "{'data2': Tensor(shape=[2], dtype=Int64, value= [1, 2])}\n",
+ "{'data2': Tensor(shape=[2], dtype=Int64, value= [1, 2])}\n",
+ "{'data2': Tensor(shape=[2], dtype=Int64, value= [1, 2])}\n",
+ "------ data3 ------\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2]), 'data2': Tensor(shape=[2], dtype=Int64, value= [1, 2])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3]), 'data2': Tensor(shape=[2], dtype=Int64, value= [1, 2])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4]), 'data2': Tensor(shape=[2], dtype=Int64, value= [1, 2])}\n",
+ "{'data1': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5]), 'data2': Tensor(shape=[2], dtype=Int64, value= [1, 2])}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "def generator_func():\n",
+ " \"\"\"定义生成数据集函数1\"\"\"\n",
+ " for i in range(7):\n",
+ " yield (np.array([i, i+1, i+2]),)\n",
+ "\n",
+ "def generator_func2():\n",
+ " \"\"\"定义生成数据集函数2\"\"\"\n",
+ " for _ in range(4):\n",
+ " yield (np.array([1, 2]),)\n",
+ "\n",
+ "print(\"------ data1 ------\")\n",
+ "dataset1 = ds.GeneratorDataset(generator_func, [\"data1\"])\n",
+ "for data in dataset1.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "print(\"------ data2 ------\")\n",
+ "dataset2 = ds.GeneratorDataset(generator_func2, [\"data2\"])\n",
+ "for data in dataset2.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "print(\"------ data3 ------\")\n",
+ "\n",
+ "# 对数据集1和数据集2做zip操作,生成数据集3\n",
+ "dataset3 = ds.zip((dataset1, dataset2))\n",
+ "for data in dataset3.create_dict_iterator():\n",
+ " print(data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,数据集3由数据集1和数据集2列拼接得到,其列数为后两者之和,其行数与后两者中最小行数(数据集2行数)保持一致,数据集1中后面多余的行数被丢弃。\n",
+ "\n",
+ "### concat\n",
+ "\n",
+ "concat实现两个数据集的行拼接,并将其合并为一个数据集。使用时需要注意:输入数据集中的列名、列数据类型和列数据的排列应相同。\n",
+ "\n",
+ "\n",
+ "\n",
+ "下面的样例先构建了两个随机数据集,然后将其做行拼接,最后展示了拼接后的数据结果。值得一提的是,使用`+`运算符也能达到同样的效果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 42,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "data1:\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 0, 0])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 0, 0])}\n",
+ "data2:\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "data3:\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 0, 0])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 0, 0])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "def generator_func():\n",
+ " \"\"\"定义生成数据集函数1\"\"\"\n",
+ " for _ in range(2):\n",
+ " yield (np.array([0, 0, 0]),)\n",
+ "\n",
+ "def generator_func2():\n",
+ " \"\"\"定义生成数据集函数2\"\"\"\n",
+ " for _ in range(2):\n",
+ " yield (np.array([1, 2, 3]),)\n",
+ "\n",
+ "# 生成数据集1\n",
+ "dataset1 = ds.GeneratorDataset(generator_func, [\"data\"])\n",
+ "print(\"data1:\")\n",
+ "for data in dataset1.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "# 生成数据集2\n",
+ "dataset2 = ds.GeneratorDataset(generator_func2, [\"data\"])\n",
+ "print(\"data2:\")\n",
+ "for data in dataset2.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "# 在数据集1上concat数据集2,生成数据集3\n",
+ "dataset3 = dataset1.concat(dataset2)\n",
+ "print(\"data3:\")\n",
+ "for data in dataset3.create_dict_iterator():\n",
+ " print(data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,数据集3由数据集1和数据集2行拼接得到,其列数与后两者保持一致,其行数为后两者之和。\n",
+ "\n",
+ "### map\n",
+ "\n",
+ "map操作将指定的函数作用于数据集的指定列数据,实现数据映射操作。\n",
+ "\n",
+ "用户可以自定义映射函数,也可以直接使用`c_transforms`或`py_transforms`中的函数针对图像、文本数据进行数据增强。\n",
+ "\n",
+ "\n",
+ "\n",
+ "下面的样例先构建了一个随机数据集,然后定义了数据翻倍的映射函数并将其作用于数据集,最后对比展示了映射前后的数据结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 1, 2])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [1, 2, 3])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [2, 3, 4])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [3, 4, 5])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [4, 5, 6])}\n",
+ "------ after processing ------\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [0, 2, 4])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [2, 4, 6])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [4, 6, 8])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [ 6, 8, 10])}\n",
+ "{'data': Tensor(shape=[3], dtype=Int64, value= [ 8, 10, 12])}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "\n",
+ "def generator_func():\n",
+ " \"\"\"定义生成数据集函数\"\"\"\n",
+ " for i in range(5):\n",
+ " yield (np.array([i, i+1, i+2]),)\n",
+ "\n",
+ "def pyfunc(x):\n",
+ " \"\"\"定义对数据的操作\"\"\"\n",
+ " return x*2\n",
+ "\n",
+ "# 生成数据集\n",
+ "dataset = ds.GeneratorDataset(generator_func, [\"data\"])\n",
+ "\n",
+ "# 显示上述生成的数据集\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)\n",
+ "\n",
+ "print(\"------ after processing ------\")\n",
+ "\n",
+ "# 对数据集做map操作,操作函数为pyfunc\n",
+ "dataset = dataset.map(operations=pyfunc, input_columns=[\"data\"])\n",
+ "\n",
+ "# 显示map操作后的数据集\n",
+ "for data in dataset.create_dict_iterator():\n",
+ " print(data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,经过map操作,将函数`pyfunc`作用到数据集后,数据集中每个数据都被乘2。"
+ ]
+ }
+ ],
+ "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
+}
diff --git a/tutorials/source_zh_cn/advanced/handout/advance.pptx b/tutorials/source_zh_cn/advanced/handout/advance.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..062e3c8743d5c920afde9a8182e8921e858d370c
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/advance.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/dataset/custom.pptx b/tutorials/source_zh_cn/advanced/handout/dataset/custom.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..f0c5953869019ee25ffa7579f10297b8bae569ee
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/dataset/custom.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_graph_data.pptx b/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_graph_data.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..15ce6c7673bad01a9ec9e55988a0f5852ab34e0e
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_graph_data.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_image_data.pptx b/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_image_data.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..3475c10554fdfb57b48ab3139e35443c872a9b0c
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_image_data.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_text_data.pptx b/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_text_data.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..91b5e5adda86c5b24a4aa0e6d47f2a7ac0fdeba5
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/dataset/enhanced_text_data.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/dataset/iterator.pptx b/tutorials/source_zh_cn/advanced/handout/dataset/iterator.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..ef5a2afa87f9f9c616af2d9083028191520efac5
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/dataset/iterator.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/dataset/record.pptx b/tutorials/source_zh_cn/advanced/handout/dataset/record.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..ca5d9c8fc7172f279d90066c4a4459748666f52f
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/dataset/record.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/dataset/sampler.pptx b/tutorials/source_zh_cn/advanced/handout/dataset/sampler.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..bb2e671993e326941520b922e1497527d77a39bd
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/dataset/sampler.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/dataset/transform.pptx b/tutorials/source_zh_cn/advanced/handout/dataset/transform.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..f33ec38914c1d9c188804a791e1e8e2dc15e0e59
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/dataset/transform.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/handout/linear_fitting.pptx b/tutorials/source_zh_cn/advanced/handout/linear_fitting.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..68fb073948d9361e70d678d48d94e018bc06048d
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/handout/linear_fitting.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/linear_fitting.ipynb b/tutorials/source_zh_cn/advanced/linear_fitting.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..b0927d1729fdd5ef47f68263264a93f14de1b5d0
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/linear_fitting.ipynb
@@ -0,0 +1,762 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 进阶案例:线性拟合\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/mindspore_linear_fitting.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/mindspore_linear_fitting.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/linear_fitting.ipynb)\n",
+ "\n",
+ "MindSpore向用户提供了高阶、中阶和低阶3个不同层次的API,详细内容参见[基本介绍-层次结构内容章节](https://www.mindspore.cn/tutorials/zh-CN/master/beginner/introduction.html#层次结构)。\n",
+ "\n",
+ "为方便控制网络的执行流程,MindSpore提供了高阶的训练和推理接口`mindspore.Model`,通过指定要训练的神经网络模型和常见的训练设置,调用`train`和`eval`方法对网络进行训练和推理。同时,用户如果想要对特定模块进行个性化设置,也可以调用对应的中低阶接口自行定义网络的训练流程。\n",
+ "\n",
+ "本章将使用MindSpore提供的中低阶API拟合线性函数:\n",
+ "\n",
+ "$$f(x) = 2x + 3 \\tag {1}$$\n",
+ "\n",
+ "初始化网络之前,需要配置`context`参数,用于控制程序执行的策略,如配置静态图或动态图模式,配置网络运行的硬件环境等。\n",
+ "\n",
+ "本章将会介绍配置信息和使用MindSpore提供的中低阶API,实现自定义损失函数、优化器、训练流程、Metric、自定义验证流程模块。\n",
+ "\n",
+ "## 配置信息\n",
+ "\n",
+ "初始化网络之前,需要配置`context`参数,用于控制程序执行的策略,如配置静态图或动态图模式,配置网络运行的硬件环境等。初始化网络之前要配置`context`参数,用于控制程序执行的策略,本节主要介绍执行模式管理和硬件管理。\n",
+ "\n",
+ "### 执行模式\n",
+ "\n",
+ "MindSpore支持Graph和PyNative两种运行模式。Graph模式是MindSpore的默认模式,而PyNative模式用于调试等用途。\n",
+ "\n",
+ "- Graph模式(静态图模式):将神经网络模型编译成一整张图,然后下发到硬件执行。该模式利用图优化等技术提高运行性能,同时有助于规模部署和跨平台运行。\n",
+ "\n",
+ "- PyNative模式(动态图模式):将神经网络中的各个算子逐一下发到硬件中执行,该模式方便用户编写代码和调试神经网络模型。\n",
+ "\n",
+ "MindSpore提供了静态图和动态图统一的编码方式,大大增加了静态图和动态图的可兼容性,用户无需开发多套代码,仅变更一行代码便可切换静态图/动态图模式。模式切换时,请留意目标模式的[约束](https://www.mindspore.cn/docs/note/zh-CN/master/static_graph_syntax_support.html)。\n",
+ "\n",
+ "设置运行模式为动态图模式:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore import context\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "同样,MindSpore处于动态图模式时,可以通过`context.set_context(mode=context.GRAPH_MODE)`切换为静态图模式:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "context.set_context(mode=context.GRAPH_MODE)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 硬件管理\n",
+ "\n",
+ "硬件管理部分主要包括`device_target`和`device_id`两个参数。\n",
+ "\n",
+ "- `device_target`: 待运行的目标设备,支持`Ascend`、`GPU`和`CPU`,可以根据实际环境情况设置,或者使用系统默认配置。\n",
+ "\n",
+ "- `device_id`: 表示目标设备ID,其值在[0, `device_num_per_host` - 1]范围内,`device_num_per_host`表示服务器的总设备数量,`device_num_per_host`的值不能超过4096,`device_id`默认为0。\n",
+ "\n",
+ "> 在非分布式模式执行的情况下,为了避免设备的使用冲突,可以通过设置`device_id`决定程序执行的设备ID。\n",
+ "\n",
+ "代码样例如下:\n",
+ "\n",
+ "```Python\n",
+ "from mindspore import context\n",
+ "\n",
+ "context.set_context(device_target=\"Ascend\", device_id=6)\n",
+ "```"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 处理数据集\n",
+ "\n",
+ "### 生成数据集\n",
+ "\n",
+ "定义数据集生成函数 `get_data` ,生成训练数据集和测试数据集。\n",
+ "\n",
+ "由于拟合的是线性数据,假定要拟合的目标函数为:$f(x)=2x+3$,那么我们需要的训练数据集应随机分布于函数周边,这里采用了$f(x)=2x+3+noise$的方式生成,其中`noise`为遵循标准正态分布规律的随机数值。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "def get_data(num, w=2.0, b=3.0):\n",
+ " for _ in range(num):\n",
+ " x = np.random.uniform(-10.0, 10.0)\n",
+ " noise = np.random.normal(0, 1)\n",
+ " y = x * w + b + noise\n",
+ " yield np.array([x]).astype(np.float32), np.array([y]).astype(np.float32)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "使用get_data生成50组验证数据,并可视化。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "train_data = list(get_data(50))\n",
+ "x_target_label = np.array([-10, 10, 0.1])\n",
+ "y_target_label = x_target_label * 2 + 3\n",
+ "x_eval_label, y_eval_label = zip(*train_data)\n",
+ "\n",
+ "plt.scatter(x_eval_label, y_eval_label, color=\"red\", s=5)\n",
+ "plt.plot(x_target_label, y_target_label, color=\"green\")\n",
+ "plt.title(\"Eval data\")\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上图中绿色线条部分为目标函数,红点部分为验证数据`train_data`。\n",
+ "\n",
+ "### 加载数据集\n",
+ "\n",
+ "加载`get_data`函数所产生的数据集到系统内存里面,并对进行基本的数据处理操作。\n",
+ "\n",
+ "- `ds.GeneratorDataset`:将生成的数据转换为MindSpore的数据集,并且将生成的数据的x,y值存入到`data`和`label`的数组中。\n",
+ "- `batch`:将`batch_size`个数据组合成一个batch。\n",
+ "- `repeat`:将数据集数量倍增。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore import dataset as ds\n",
+ "\n",
+ "def create_dataset(num_data, batch_size=16, repeat_size=1):\n",
+ " input_data = ds.GeneratorDataset(list(get_data(num_data)), column_names=['data', 'label'])\n",
+ " input_data = input_data.batch(batch_size, drop_remainder=True)\n",
+ " input_data = input_data.repeat(repeat_size)\n",
+ " return input_data"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "使用数据集增强函数生成训练数据,通过定义的`create_dataset`将生成的1600个数据增强为100组shape为16x1的数据集。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "The dataset size of ds_train: 100\n",
+ "dict_keys(['data', 'label'])\n",
+ "The x label value shape: (16, 1)\n",
+ "The y label value shape: (16, 1)\n"
+ ]
+ }
+ ],
+ "source": [
+ "data_number = 1600\n",
+ "batch_number = 16\n",
+ "repeat_number = 1\n",
+ "\n",
+ "ds_train = create_dataset(data_number, batch_size=batch_number, repeat_size=repeat_number)\n",
+ "print(\"The dataset size of ds_train:\", ds_train.get_dataset_size())\n",
+ "step_size = ds_train.get_dataset_size()\n",
+ "dict_datasets = next(ds_train.create_dict_iterator())\n",
+ "\n",
+ "print(dict_datasets.keys())\n",
+ "print(\"The x label value shape:\", dict_datasets[\"data\"].shape)\n",
+ "print(\"The y label value shape:\", dict_datasets[\"label\"].shape)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 定义网络模型\n",
+ "\n",
+ "`mindspore.nn`类是构建所有网络的基类,也是网络的基本单元。当用户需要自定义网络时,可以继承`nn.Cell`类,并重写`__init__`方法和`construct`方法。\n",
+ "\n",
+ "`mindspore.ops`模块提供了基础算子的实现,`nn.Cell`模块实现了对基础算子的进一步封装,用户可以根据需要,灵活使用不同的算子。\n",
+ "\n",
+ "如下示例使用`nn.Cell`构建一个简单的全连接网络,用于后续自定义内容的示例片段代码。在MindSpore中使用`nn.Dense`生成单个数据输入,单个数据输出的线性函数模型:\n",
+ "\n",
+ "$$f(x)=wx+b\\tag{2}$$\n",
+ "\n",
+ "并使用Normal算子随机初始化公式 (2) 中的参数$w$和$b$。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore import nn\n",
+ "from mindspore.common.initializer import Normal\n",
+ "\n",
+ "class LinearNet(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(LinearNet, self).__init__()\n",
+ " self.fc = nn.Dense(1, 1, Normal(0.02), Normal(0.02))\n",
+ "\n",
+ " def construct(self, x):\n",
+ " fx = self.fc(x)\n",
+ " return fx"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "初始化网络模型后,接下来将初始化的网络函数和训练数据集进行可视化,了解拟合前的模型函数情况。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from mindspore import Tensor\n",
+ "\n",
+ "net = LinearNet() # 初始化线性回归网络\n",
+ "\n",
+ "model_params = net.trainable_params() # 获取训练前的网络参数 w 和 b\n",
+ "\n",
+ "x_model_label = np.array([-10, 10, 0.1])\n",
+ "y_model_label = (x_model_label * model_params[0].asnumpy()[0] + model_params[1].asnumpy()[0])\n",
+ "\n",
+ "plt.axis([-10, 10, -20, 25])\n",
+ "plt.scatter(x_eval_label, y_eval_label, color=\"red\", s=5)\n",
+ "plt.plot(x_model_label, y_model_label, color=\"blue\")\n",
+ "plt.plot(x_target_label, y_target_label, color=\"green\")\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 自定义损失函数\n",
+ "\n",
+ "损失函数(Loss Function)用于衡量预测值与真实值差异的程度。深度学习中,模型训练就是通过不停地迭代来缩小损失函数值的过程,因此在模型训练过程中损失函数的选择非常重要,定义一个好的损失函数可以帮助损失函数值更快收敛,达到更好的精度。\n",
+ "\n",
+ "[mindspore.nn](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id13)提供了许多通用损失函数供用户选择, 也支持用户根据需要自定义损失函数。\n",
+ "\n",
+ "自定义损失函数类时,既可以继承网络的基类`nn.Cell`,也可以继承损失函数的基类`nn.LossBase`。`nn.LossBase`在`nn.Cell`的基础上,提供了`get_loss`方法,利用`reduction`参数对损失值求和或求均值,输出一个标量。下面将使用继承LossBase的方法来定义平均绝对误差损失函数(Mean Absolute Error,MAE),MAE算法的公式如下所示:\n",
+ "\n",
+ "$$ loss= \\frac{1}{m}\\sum_{i=1}^m\\lvert y_i-f(x_i) \\rvert \\tag{3}$$\n",
+ "\n",
+ "上式中$f(x)$为预测值,$y$为样本真实值,$loss$为预测值与真实值之间距离的平均值。\n",
+ "\n",
+ "使用继承LossBase的方法来自定义损失函数时,需要重写`__init__`方法和`construct`方法,使用`get_loss`方法计算损失。示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore import nn, ops\n",
+ "\n",
+ "class MyMAELoss(nn.LossBase):\n",
+ " \"\"\"定义损失\"\"\"\n",
+ " def __init__(self):\n",
+ " super(MyMAELoss, self).__init__()\n",
+ " self.abs = ops.Abs()\n",
+ "\n",
+ " def construct(self, predict, target):\n",
+ " x = self.abs(target - predict)\n",
+ " return self.get_loss(x)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 自定义优化器\n",
+ "\n",
+ "优化器在模型训练过程中,用于计算和更新网络参数,合适的优化器可以有效减少训练时间,提高模型性能。\n",
+ "\n",
+ "[mindspore.nn](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id14)提供了许多通用的优化器供用户选择,同时也支持用户根据需要自定义优化器。\n",
+ "\n",
+ "自定义优化器时可以继承优化器基类`nn.Optimizer`,重写`__init__`方法和`construct`方法实现参数的更新。\n",
+ "\n",
+ "如下示例实现自定义优化器Momentum(带动量的SGD算法):\n",
+ "\n",
+ "$$ v_{t+1} = v_t × u+grad \\tag{4}$$\n",
+ "\n",
+ "$$p_{t+1} = p_t - lr × v_{t+1} \\tag{5}$$\n",
+ "\n",
+ "其中,$grad$ 、$lr$ 、$p$ 、$v$ 和 $u$ 分别表示梯度、学习率、权重参数、动量参数(Momentum)和初始速度。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-22T06:58:20.992077Z",
+ "start_time": "2021-12-22T06:58:20.933414Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "from mindspore import Tensor, Parameter\n",
+ "from mindspore import nn, ops\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "class MyMomentum(nn.Optimizer):\n",
+ " \"\"\"定义优化器\"\"\"\n",
+ "\n",
+ " def __init__(self, params, learning_rate, momentum=0.9):\n",
+ " super(MyMomentum, self).__init__(learning_rate, params)\n",
+ " self.moment = Parameter(Tensor(momentum, mstype.float32), name=\"moment\")\n",
+ " self.momentum = self.parameters.clone(prefix=\"momentum\", init=\"zeros\")\n",
+ " self.assign = ops.Assign()\n",
+ "\n",
+ " def construct(self, gradients):\n",
+ " \"\"\"construct输入为梯度,在训练中自动传入梯度gradients\"\"\"\n",
+ " lr = self.get_lr()\n",
+ " params = self.parameters # 待更新的权重参数\n",
+ " for i in range(len(params)):\n",
+ " self.assign(self.momentum[i], self.momentum[i] * self.moment + gradients[i])\n",
+ " update = params[i] - self.momentum[i] * lr # 带有动量的SGD算法\n",
+ " self.assign(params[i], update)\n",
+ " return params"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 自定义训练流程\n",
+ "\n",
+ "`mindspore.Model`提供了`train`和`eval`的接口方便用户在训练过程中使用,但此接口无法适用于所有场景,比如多数据多标签场景,在这些场景下用户需自行定义训练过程。\n",
+ "\n",
+ "本节主要使用线性回归的例子来简单介绍自定义训练流程。首先定义损失网络,将前向网络与损失函数连接起来;然后定义训练流程,训练流程一般继承`nn.TrainOneStepCell`,`nn.TrainOneStepCell`封装了损失网络和优化器,用来实现反向传播网络以更新权重参数。\n",
+ "\n",
+ "### 定义损失网络\n",
+ "\n",
+ "定义损失网络`MyWithLossCell`,将前向网络与损失函数连接起来。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-22T06:58:21.023142Z",
+ "start_time": "2021-12-22T06:58:21.008460Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "class MyWithLossCell(nn.Cell):\n",
+ " \"\"\"定义损失网络\"\"\"\n",
+ "\n",
+ " def __init__(self, backbone, loss_fn):\n",
+ " \"\"\"实例化时传入前向网络和损失函数作为参数\"\"\"\n",
+ " super(MyWithLossCell, self).__init__(auto_prefix=False)\n",
+ " self.backbone = backbone\n",
+ " self.loss_fn = loss_fn\n",
+ "\n",
+ " def construct(self, data, label):\n",
+ " \"\"\"连接前向网络和损失函数\"\"\"\n",
+ " out = self.backbone(data)\n",
+ " return self.loss_fn(out, label)\n",
+ "\n",
+ " def backbone_network(self):\n",
+ " \"\"\"要封装的骨干网络\"\"\"\n",
+ " return self.backbone"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 定义训练流程\n",
+ "\n",
+ "定义训练流程`MyTrainStep`,该类继承`nn.TrainOneStepCell`,`nn.TrainOneStepCell`封装了损失网络和优化器,在执行训练时通过`ops.GradOperation`算子来进行梯度的获取,通过优化器来实现权重的更新。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-22T06:58:21.040020Z",
+ "start_time": "2021-12-22T06:58:21.025185Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "class MyTrainStep(nn.TrainOneStepCell):\n",
+ " \"\"\"定义训练流程\"\"\"\n",
+ "\n",
+ " def __init__(self, network, optimizer):\n",
+ " \"\"\"参数初始化\"\"\"\n",
+ " super(MyTrainStep, self).__init__(network, optimizer)\n",
+ " self.grad = ops.GradOperation(get_by_list=True)\n",
+ "\n",
+ " def construct(self, data, label):\n",
+ " \"\"\"构建训练过程\"\"\"\n",
+ " weights = self.weights\n",
+ " loss = self.network(data, label)\n",
+ " grads = self.grad(self.network, weights)(data, label)\n",
+ " return loss, self.optimizer(grads)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "下面定义绘图函数`plot_model_and_datasets`来绘制测试数据、目标函数和网络模型拟合函数,并查看损失值。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import matplotlib.pyplot as plt\n",
+ "import time\n",
+ "\n",
+ "def plot_model_and_datasets(net, data, loss):\n",
+ " weight = net.trainable_params()[0]\n",
+ " bias = net.trainable_params()[1]\n",
+ " x = np.arange(-10, 10, 0.1)\n",
+ " y = x * Tensor(weight).asnumpy()[0][0] + Tensor(bias).asnumpy()[0]\n",
+ " x1, y1 = zip(*data)\n",
+ " x_target = x\n",
+ " y_target = x_target * 2 + 3\n",
+ "\n",
+ " plt.axis([-11, 11, -20, 25])\n",
+ " plt.scatter(x1, y1, color=\"red\", s=5) # 原始数据\n",
+ " plt.plot(x, y, color=\"blue\") # 预测数据\n",
+ " plt.plot(x_target, y_target, color=\"green\") # 拟合函数\n",
+ " plt.title(f\"Loss:{loss}\") # 打印损失值\n",
+ "\n",
+ " plt.show()\n",
+ " time.sleep(0.2)\n",
+ " display.clear_output(wait=True)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 执行训练\n",
+ "\n",
+ "使用训练数据`ds_train`对训练网络`train_net`进行训练,并可视化训练过程。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-22T06:58:22.681175Z",
+ "start_time": "2021-12-22T06:58:21.041577Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "from IPython import display\n",
+ "\n",
+ "loss_func = MyMAELoss() # 损失函数\n",
+ "opt = MyMomentum(net.trainable_params(), 0.01) # 优化器\n",
+ "\n",
+ "net_with_criterion = MyWithLossCell(net, loss_func) # 构建损失网络\n",
+ "train_net = MyTrainStep(net_with_criterion, opt) # 构建训练网络\n",
+ "\n",
+ "for data in ds_train.create_dict_iterator():\n",
+ " train_net(data['data'], data['label']) # 执行训练,并更新权重\n",
+ " loss = net_with_criterion(data['data'], data['label']) # 计算损失值\n",
+ " plot_model_and_datasets(train_net, train_data, loss) # 可视化训练过程"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 自定义评价指标\n",
+ "\n",
+ "当训练任务结束,常常需要评价指标(Metrics)评估函数来评估模型的好坏。常用的评价指标有混淆矩阵、准确率 Accuracy、精确率 Precision、召回率 Recall等。\n",
+ "\n",
+ "[mindspore.nn](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id16)模块提供了常见的评估函数,用户也可以根据需要自行定义评估指标。自定义Metrics函数需要继承`nn.Metric`父类,并重新实现父类中的`clear`方法、`update`方法和`eval`方法。平均绝对误差(MAE)算法如下式所示,下面以简单的MAE为例,介绍这三个函数及其使用方法。\n",
+ "\n",
+ "$$ MAE=\\frac{1}{n}\\sum_{i=1}^n\\lvert y\\_pred_i - y_i \\rvert \\tag{6}$$\n",
+ "\n",
+ "- `clear`:初始化相关的内部参数。\n",
+ "- `update`:接收网络预测输出和标签,计算误差,并更新内部评估结果。一般在每个step后进行计算,并更新统计值。\n",
+ "- `eval`:计算最终评估结果,一般在一个epoch结束后计算最终的评估结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-22T06:58:22.707013Z",
+ "start_time": "2021-12-22T06:58:22.683370Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "class MyMAE(nn.Metric):\n",
+ " \"\"\"定义metric\"\"\"\n",
+ "\n",
+ " def __init__(self):\n",
+ " super(MyMAE, self).__init__()\n",
+ " self.clear()\n",
+ "\n",
+ " def clear(self):\n",
+ " \"\"\"初始化变量abs_error_sum和samples_num\"\"\"\n",
+ " self.abs_error_sum = 0\n",
+ " self.samples_num = 0\n",
+ "\n",
+ " def update(self, *inputs):\n",
+ " \"\"\"更新abs_error_sum和samples_num\"\"\"\n",
+ " y_pred = inputs[0].asnumpy()\n",
+ " y = inputs[1].asnumpy()\n",
+ "\n",
+ " # 计算预测值与真实值的绝对误差\n",
+ " error_abs = np.abs(y.reshape(y_pred.shape) - y_pred)\n",
+ " self.abs_error_sum += error_abs.sum()\n",
+ " self.samples_num += y.shape[0] # 样本的总数\n",
+ "\n",
+ " def eval(self):\n",
+ " \"\"\"计算最终评估结果\"\"\"\n",
+ " return self.abs_error_sum / self.samples_num"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 自定义验证流程\n",
+ "\n",
+ "mindspore.nn模块提供了评估网络包装函数[nn.WithEvalCell](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.WithEvalCell.html#mindspore.nn.WithEvalCell),由于`nn.WithEvalCell`只有两个输入`data`和`label`,不适用于多数据或多标签的场景,所以需要自定义评估网络。多标签场景下自定义评估网络可参考[自定义评估与训练章节](https://www.mindspore.cn/tutorials/zh-CN/master/advanced/train/train_eval.html#自定义评估网络)。\n",
+ "\n",
+ "如下示例实现简单的自定义评估网络`MyWithEvalCell`,输入传入数据`data`和标签`label`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-22T06:58:22.726360Z",
+ "start_time": "2021-12-22T06:58:22.709549Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "class MyWithEvalCell(nn.Cell):\n",
+ " \"\"\"定义验证流程\"\"\"\n",
+ "\n",
+ " def __init__(self, network):\n",
+ " super(MyWithEvalCell, self).__init__(auto_prefix=False)\n",
+ " self.network = network\n",
+ "\n",
+ " def construct(self, data, label):\n",
+ " outputs = self.network(data)\n",
+ " return outputs, label"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "执行推理并评估:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-22T06:58:22.935251Z",
+ "start_time": "2021-12-22T06:58:22.729350Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "MAE: 0.9605088472366333\n"
+ ]
+ }
+ ],
+ "source": [
+ "data_number = 160\n",
+ "batch_number = 16\n",
+ "repeat_number = 1\n",
+ "\n",
+ "# 获取验证数据\n",
+ "ds_eval = create_dataset(data_number, batch_size=batch_number, repeat_size=repeat_number)\n",
+ "\n",
+ "eval_net = MyWithEvalCell(net) # 定义评估网络\n",
+ "eval_net.set_train(False)\n",
+ "mae = MyMAE()\n",
+ "\n",
+ "# 执行推理过程\n",
+ "for data in ds_eval.create_dict_iterator():\n",
+ " output, eval_y = eval_net(data['data'], data['label'])\n",
+ " mae.update(output, eval_y)\n",
+ "\n",
+ "mae_result = mae.eval()\n",
+ "print(\"MAE: \", mae_result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "输出评估误差,MAE与模型在训练集上效果大致相同。"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 保存及导出模型\n",
+ "\n",
+ "将上述训练好的模型参数保存到CheckPoint(简称ckpt)文件中,然后将CheckPoint文件导出为MindIR格式文件用于跨平台推理使用。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "fc.weight : [[1.894384]]\n",
+ "fc.bias : [3.0015702]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import save_checkpoint, load_checkpoint, export\n",
+ "\n",
+ "save_checkpoint(net, \"./linear.ckpt\") # 将模型参数保存在ckpt文件\n",
+ "param_dict = load_checkpoint(\"./linear.ckpt\") # 将模型参数存入param_dict字典中\n",
+ "\n",
+ "# 查看模型参数\n",
+ "for param in param_dict:\n",
+ " print(param, \":\", param_dict[param].asnumpy())\n",
+ "\n",
+ "net1 = LinearNet()\n",
+ "input_np = np.random.uniform(0.0, 1.0, size=[1, 1]).astype(np.float32)\n",
+ "export(net1, Tensor(input_np), file_name='linear', file_format='MINDIR')"
+ ]
+ }
+ ],
+ "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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/network.rst b/tutorials/source_zh_cn/advanced/network.rst
new file mode 100644
index 0000000000000000000000000000000000000000..9664131c52e6312cce41098d65a58c78432d7f80
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/network.rst
@@ -0,0 +1,12 @@
+网络构建
+===================
+
+.. toctree::
+ :maxdepth: 1
+
+ network/parameter
+ network/loss
+ network/optim
+ network/forward
+ network/derivation
+ network/control_flow
diff --git a/tutorials/source_zh_cn/advanced/network/control_flow.ipynb b/tutorials/source_zh_cn/advanced/network/control_flow.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..6f5bc73a2a1d01272896ae8ff679df1ea51283b2
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/network/control_flow.ipynb
@@ -0,0 +1,548 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 流程控制语句\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_control_flow.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_control_flow.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/network/control_flow.ipynb)\n",
+ "\n",
+ "目前主流的深度学习框架的执行模式有两种,分别为静态图模式`GRAPH_MODE`和动态图`PYNATIVE_MODE`模式。\n",
+ "\n",
+ "在`PYNATIVE_MODE`模式下,MindSpore完全支持Python原生语法的流程控制语句。`GRAPH_MODE`模式下,MindSpore在编译时做了性能优化,因此,在定义网络时使用流程控制语句时会有部分特殊约束,其他部分仍和Python原生语法保持一致。\n",
+ "\n",
+ "运行模式从动态图切换到静态图时,请留意[静态图语法支持](https://www.mindspore.cn/docs/note/zh-CN/master/static_graph_syntax_support.html#%E9%9D%99%E6%80%81%E5%9B%BE%E8%AF%AD%E6%B3%95%E6%94%AF%E6%8C%81)。下面我们详细介绍在`GRAPH_MODE`模式下定义网络时流程控制语句的使用方式。\n",
+ "\n",
+ "## 常量与变量条件\n",
+ "\n",
+ "在`GRAPH_MODE`模式下定义网络,MindSpore将流程控制语句中的条件表达式分为两类:即常量条件和变量条件。在图编译时可以确定结果为True或False的条件表达式为常量条件,在图编译时不能确定结果为True或False的条件表达式为变量条件。只有当条件表达式为变量条件时,MindSpore才会在网络中生成控制流算子。\n",
+ "\n",
+ "需要注意的是,当网络中存在控制流算子时,网络会被切分成多个执行子图,子图间进行流程跳转和数据传递会产生一定的性能损耗。\n",
+ "\n",
+ "### 常量条件\n",
+ "\n",
+ "判断方式:\n",
+ "\n",
+ "- 条件表达式中不存在Tensor类型,且也不存在元素为Tensor类型的List、Tuple、Dict。\n",
+ "- 条件表达式中存在Tensor类型,或者元素为Tensor类型的List、Tuple、Dict,但是表达式结果不受Tensor的值影响。\n",
+ "\n",
+ "举例:\n",
+ "\n",
+ "- `for i in range(0,10)`,`i`为标量:潜在的条件表达式`i < 10`在图编译时可以确定结果,因此为常量条件;\n",
+ "\n",
+ "- `self.flag`,`self.flag`为标量:此处`self.flag`为一个bool类型标量,其值在构建Cell对象时已确定;\n",
+ "\n",
+ "- `x + 1 < 10`,`x`为标量:此处`x + 1`的值在构建Cell对象时是不确定的,但是在图编译时MindSpore会计算所有标量表达式的结果,因此该表达式的值也是在编译期确定的。\n",
+ "\n",
+ "- `len(my_list) < 10`,`my_list`为元素是Tensor类型的List对象:该条件表达式包含Tensor,但是表达式结果不受Tensor的值影响,只与`my_list`中Tensor的数量有关;\n",
+ "\n",
+ "### 变量条件\n",
+ "\n",
+ "判断方式:\n",
+ "\n",
+ "- 条件表达式中存在Tensor类型或者元素为Tensor类型的List、Tuple、Dict,并且条件表达式的结果受Tensor的值影响。\n",
+ "\n",
+ "举例:\n",
+ "\n",
+ "- `x < y`,`x`和`y`为算子输出。\n",
+ "\n",
+ "- `x in list`,`x`为算子输出。\n",
+ "\n",
+ "由于算子输出是图在各个step执行时才能确定,因此上面两个都属于变量条件。\n",
+ "\n",
+ "## if语句\n",
+ "\n",
+ "在`GRAPH_MODE`模式下定义网络时,使用`if`语句需要注意:**在条件表达式为变量条件时,在不同分支的同一变量应被赋予相同的数据类型**。\n",
+ "\n",
+ "### 变量条件的if语句\n",
+ "\n",
+ "在下面代码中,虽然在`if`和`else`分支中,变量`out`被赋予的数据类型(`int`和`list`)并不相同,但是由于其不是在定义网络的过程中,所以遵守的是Python原生语法的规则,仍然可以成功执行,其输出结果为`1`。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "output: 1\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore import dtype as ms\n",
+ "\n",
+ "def my_fun(x, y, z):\n",
+ " if x < y:\n",
+ " out = x\n",
+ " else:\n",
+ " out = z\n",
+ " out = out + 1\n",
+ " return out\n",
+ "\n",
+ "x = Tensor(np.array(0), dtype=ms.int32)\n",
+ "y = Tensor(np.array(1), dtype=ms.int32)\n",
+ "z = Tensor(np.array([1, 2]), dtype=ms.int32)\n",
+ "\n",
+ "output = my_fun(x, y, z)\n",
+ "print(\"output:\", output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "接下来,我们把上面代码中的`my_fun`函数放到网络模型的`construct`过程中,看一下结果如何。\n",
+ "\n",
+ "```python\n",
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore import dtype as ms\n",
+ "\n",
+ "class SingleIfNet(nn.Cell):\n",
+ "\n",
+ " def construct(self, x, y, z):\n",
+ " # 构造条件表达式为变量条件的if语句\n",
+ " if x < y:\n",
+ " out = x\n",
+ " else:\n",
+ " out = z\n",
+ " out = out + 1\n",
+ " return out\n",
+ "\n",
+ "forward_net = SingleIfNet()\n",
+ "\n",
+ "x = Tensor(np.array(0), dtype=ms.int32)\n",
+ "y = Tensor(np.array(1), dtype=ms.int32)\n",
+ "z = Tensor(np.array([1, 2]), dtype=ms.int32)\n",
+ "\n",
+ "output = forward_net(x, y, z)\n",
+ "```\n",
+ "\n",
+ "执行上面的代码,报错信息如下:\n",
+ "\n",
+ "```text\n",
+ "ValueError: mindspore/ccsrc/pipeline/jit/static_analysis/static_analysis.cc:800 ProcessEvalResults] Cannot join the return values of different branches, perhaps you need to make them equal.\n",
+ "Shape Join Failed: shape1 = (), shape2 = (2).\n",
+ "```\n",
+ "\n",
+ "由于在`GRAPH_MODE`模式下定义网络时,变量`out`在`if`和`else`分支中被赋予的数据类型(`int`和`list`)并不相同,所以代码执行失败。\n",
+ "\n",
+ "### 常量条件的if语句\n",
+ "\n",
+ "当`if`语句中的条件表达式为常量条件时,其使用方式与Python原生语法保持一致,并无额外的约束。如下代码中的`if`语句条件表达式`x < y + 1`为常量条件,因此变量`out`可以在不同分支被赋予不同的数据类型,其输出结果为`1`。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-07T03:35:40.804471Z",
+ "start_time": "2022-01-07T03:35:39.226569Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "output: 1\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore import dtype as ms\n",
+ "\n",
+ "class SingleIfNet(nn.Cell):\n",
+ "\n",
+ " def construct(self, z):\n",
+ " x = 0\n",
+ " y = 1\n",
+ "\n",
+ " # 构造条件表达式为常量条件的if语句\n",
+ " if x < y + 1:\n",
+ " out = x\n",
+ " else:\n",
+ " out = z\n",
+ " out = out + 1\n",
+ "\n",
+ " return out\n",
+ "\n",
+ "z = Tensor(np.array([0, 1]), dtype=ms.int32)\n",
+ "forward_net = SingleIfNet()\n",
+ "\n",
+ "output = forward_net(z)\n",
+ "print(\"output:\", output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## for语句\n",
+ "\n",
+ "`for`语句会将循环体展开,因此使用`for`语句的网络的子图数量、算子数量取决于`for`语句的循环次数和循环体中变量条件个数,算子数量过多或者子图过多会消耗更多的硬件资源。\n",
+ "\n",
+ "下面的示例代码中,`for`语句中的循环体会被执行3次,输出结果为`5`。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-07T03:35:40.922574Z",
+ "start_time": "2022-01-07T03:35:40.806485Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "output: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore import dtype as ms\n",
+ "\n",
+ "class IfInForNet(nn.Cell):\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " out = 0\n",
+ "\n",
+ " # 构造条件表达式为常量条件的for语句\n",
+ " for i in range(0, 3):\n",
+ " # 构造条件表达式为变量条件的if语句\n",
+ " if x + i < y:\n",
+ " out = out + x\n",
+ " else:\n",
+ " out = out + y\n",
+ " out = out + 1\n",
+ "\n",
+ " return out\n",
+ "\n",
+ "forward_net = IfInForNet()\n",
+ "\n",
+ "x = Tensor(np.array(0), dtype=ms.int32)\n",
+ "y = Tensor(np.array(1), dtype=ms.int32)\n",
+ "\n",
+ "output = forward_net(x, y)\n",
+ "print(\"output:\", output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "由于`for`语句会展开循环体内容,所以上面的代码和下面的代码等价:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-07T03:35:40.985953Z",
+ "start_time": "2022-01-07T03:35:40.923594Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "output: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore import dtype as ms\n",
+ "\n",
+ "class IfInForNet(nn.Cell):\n",
+ " def construct(self, x, y):\n",
+ " out = 0\n",
+ "\n",
+ " # 循环: 0\n",
+ " if x + 0 < y:\n",
+ " out = out + x\n",
+ " else:\n",
+ " out = out + y\n",
+ " out = out + 1\n",
+ " # 循环: 1\n",
+ " if x + 1 < y:\n",
+ " out = out + x\n",
+ " else:\n",
+ " out = out + y\n",
+ " out = out + 1\n",
+ "\n",
+ " # 循环: 2\n",
+ " if x + 2 < y:\n",
+ " out = out + x\n",
+ " else:\n",
+ " out = out + y\n",
+ " out = out + 1\n",
+ "\n",
+ " return out\n",
+ "\n",
+ "forward_net = IfInForNet()\n",
+ "\n",
+ "x = Tensor(np.array(0), dtype=ms.int32)\n",
+ "y = Tensor(np.array(1), dtype=ms.int32)\n",
+ "\n",
+ "output = forward_net(x, y)\n",
+ "print(\"output:\", output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面两段示例代码我们可以看出,在部分场景下,使用`for`语句会导致出现子图过多的问题时。为了节约硬件资源开销,提升网络性能,可尝试将`for`语句等价转换为条件表达式是变量条件的`while`语句。\n",
+ "\n",
+ "## while语句\n",
+ "\n",
+ "`while`语句相比`for`语句更为灵活。当`while`的条件为常量时,`while`对循环体的处理和`for`类似,会展开循环体内容。\n",
+ "\n",
+ "当`while`的条件表达式是变量条件时,`while`语句则不会展开循环体内容,而是在执行图中产生控制流算子,因此可以避免`for`循环带来的子图过多的问题。\n",
+ "\n",
+ "### 常量条件的while语句\n",
+ "\n",
+ "下面的示例代码中,`for`语句中的循环体会被执行3次,输出结果为`5`,和上面介绍`for`语句中的示例代码本质上是一样的。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-07T03:35:41.051756Z",
+ "start_time": "2022-01-07T03:35:40.988065Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "output: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore import dtype as ms\n",
+ "\n",
+ "class IfInWhileNet(nn.Cell):\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " i = 0\n",
+ " out = x\n",
+ " # 构造条件表达式为常量条件的while语句\n",
+ " while i < 3:\n",
+ " # 构造条件表达式为变量条件的if语句\n",
+ " if x + i < y:\n",
+ " out = out + x\n",
+ " else:\n",
+ " out = out + y\n",
+ " out = out + 1\n",
+ " i = i + 1\n",
+ " return out\n",
+ "\n",
+ "forward_net = IfInWhileNet()\n",
+ "x = Tensor(np.array(0), dtype=ms.int32)\n",
+ "y = Tensor(np.array(1), dtype=ms.int32)\n",
+ "\n",
+ "output = forward_net(x, y)\n",
+ "print(\"output:\", output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 变量条件的while语句\n",
+ "\n",
+ "1. 约束一:**当while语句中的条件表达式是变量条件时,while循环体内部不能出现标量、List、Tuple等非Tensor类型的计算操作**。\n",
+ "\n",
+ "为了避免产生过多的控制流算子,我们可以尝试使用条件表达式为变量条件的`while`语句重写上面的代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "output: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore import dtype as ms\n",
+ "\n",
+ "class IfInWhileNet(nn.Cell):\n",
+ "\n",
+ " def construct(self, x, y, i):\n",
+ " out = x\n",
+ " # 构造条件表达式为变量条件的while语句\n",
+ " while i < 3:\n",
+ " # 构造条件表达式为变量条件的if语句\n",
+ " if x + i < y:\n",
+ " out = out + x\n",
+ " else:\n",
+ " out = out + y\n",
+ " out = out + 1\n",
+ " i = i + 1\n",
+ " return out\n",
+ "\n",
+ "forward_net = IfInWhileNet()\n",
+ "i = Tensor(np.array(0), dtype=ms.int32)\n",
+ "x = Tensor(np.array(0), dtype=ms.int32)\n",
+ "y = Tensor(np.array(1), dtype=ms.int32)\n",
+ "\n",
+ "output = forward_net(x, y, i)\n",
+ "print(\"output:\", output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "需要注意的是,在上面的代码中,`while`语句的条件表达式为变量条件,`while`循环体不会被展开,`while`循环体内的表达式都是在各个step运行时计算,同时也产生了如下约束:\n",
+ "\n",
+ "> 当`while`语句中的条件表达式是变量条件时,`while`循环体内部不能出现标量、List、Tuple等非Tensor类型的计算操作。\n",
+ "\n",
+ "因为这些类型的计算操作是在图编译时期完成的,这与`while`循环体在执行期进行计算的机制是矛盾的。下面我们通过示例代码说明:\n",
+ "\n",
+ "```Python\n",
+ "class IfInWhileNet(nn.Cell):\n",
+ "\n",
+ " def __init__(self):\n",
+ " super().__init__()\n",
+ " self.nums = [1, 2, 3]\n",
+ "\n",
+ " def construct(self, x, y, i):\n",
+ " j = 0\n",
+ " out = x\n",
+ "\n",
+ " # 构造条件表达式为变量条件的while语句\n",
+ " while i < 3:\n",
+ " if x + i < y:\n",
+ " out = out + x\n",
+ " else:\n",
+ " out = out + y\n",
+ " out = out + self.nums[j]\n",
+ " i = i + 1\n",
+ " # 在条件表达式为变量条件的while语句循环体内构造标量计算\n",
+ " j = j + 1\n",
+ "\n",
+ " return out\n",
+ "\n",
+ "forward_net = IfInWhileNet()\n",
+ "i = Tensor(np.array(0), dtype=ms.int32)\n",
+ "x = Tensor(np.array(0), dtype=ms.int32)\n",
+ "y = Tensor(np.array(1), dtype=ms.int32)\n",
+ "\n",
+ "output = forward_net(x, y, i)\n",
+ "```\n",
+ "\n",
+ "上面的代码中,条件表达式`i < 3`为变量条件的`while`循环体内部存在标量计算`j = j + 1`,因此会导致图编译出错。代码在执行时报错信息如下:\n",
+ "\n",
+ "```text\n",
+ "IndexError: mindspore/core/abstract/prim_structures.cc:127 InferTupleOrListGetItem] list_getitem evaluator index should be in range[-3, 3), but got 3.\n",
+ "```\n",
+ "\n",
+ "2. 约束二:**当while语句中的条件表达式是变量条件时,循环体内部不能更改算子的输入shape。**\n",
+ "\n",
+ "MindSpore要求网络的同一个算子的输入shape在图编译时是确定的,而在`while`的循环体内部改变算子输入shape的操作是在图执行时生效,两者是矛盾的。\n",
+ "\n",
+ "下面我们通过示例代码来说明:\n",
+ "\n",
+ "```Python\n",
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore.common import dtype as ms\n",
+ "from mindspore import ops\n",
+ "\n",
+ "class IfInWhileNet(nn.Cell):\n",
+ "\n",
+ " def __init__(self):\n",
+ " super().__init__()\n",
+ " self.expand_dims = ops.ExpandDims()\n",
+ "\n",
+ " def construct(self, x, y, i):\n",
+ " out = x\n",
+ " # 构造条件表达式为变量条件的while语句\n",
+ " while i < 3:\n",
+ " if x + i < y:\n",
+ " out = out + x\n",
+ " else:\n",
+ " out = out + y\n",
+ " out = out + 1\n",
+ " # 更改算子的输入shape\n",
+ " out = self.expand_dims(out, -1)\n",
+ " i = i + 1\n",
+ " return out\n",
+ "\n",
+ "forward_net = IfInWhileNet()\n",
+ "i = Tensor(np.array(0), dtype=ms.int32)\n",
+ "x = Tensor(np.array(0), dtype=ms.int32)\n",
+ "y = Tensor(np.array(1), dtype=ms.int32)\n",
+ "\n",
+ "output = forward_net(x, y, i)\n",
+ "```\n",
+ "\n",
+ "上面的代码中,条件表达式`i < 3`为变量条件的`while`循环体内部的`ExpandDims`算子会改变表达式`out = out + 1`在下一轮循环的输入shape,因此会导致图编译出错。代码在执行时报错信息如下:\n",
+ "\n",
+ "```text\n",
+ "ValueError: mindspore/ccsrc/pipeline/jit/static_analysis/static_analysis.cc:800 ProcessEvalResults] Cannot join the return values of different branches, perhaps you need to make them equal.\n",
+ "Shape Join Failed: shape1 = (1), shape2 = (1, 1).\n",
+ "```"
+ ]
+ }
+ ],
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/network/derivation.ipynb b/tutorials/source_zh_cn/advanced/network/derivation.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..d432f4794005f1a22bdd637def57964abf6e7a8f
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/network/derivation.ipynb
@@ -0,0 +1,474 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 自动求导\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_derivation.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_derivation.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/network/derivation.ipynb)\n",
+ "\n",
+ "`mindspore.ops`模块提供的`GradOperation`接口可以生成网络模型的梯度。本文主要介绍如何使用`GradOperation`接口进行一阶、二阶求导,以及如何停止计算梯度。\n",
+ "\n",
+ "> 由于不同计算平台的精度可能存在差异,因此本章节中的代码在不同平台上的执行结果会存在微小的差别。更多求导接口相关信息可参考[API文档](https://mindspore.cn/docs/api/zh-CN/master/api_python/ops/mindspore.ops.GradOperation.html#mindspore.ops.GradOperation)。\n",
+ "\n",
+ "## 一阶求导\n",
+ "\n",
+ "计算一阶导数方法:`mindspore.ops.GradOperation (get_all=False, get_by_list=False, sens_param=False)`,其中参数使用方式为:\n",
+ "\n",
+ "- `get_all`:为`False`时,只会对第一个输入求导;为`True`时,会对所有输入求导。\n",
+ "- `get_by_list:`为`False`时,不会对权重求导;为`True`时,会对权重求导。\n",
+ "- `sens_param`:对网络的输出值做缩放以改变最终梯度,故其维度与输出维度保持一致;\n",
+ "\n",
+ "下面我们先使用[MatMul](https://mindspore.cn/docs/api/zh-CN/master/api_python/ops/mindspore.ops.MatMul.html#mindspore.ops.MatMul)算子构建自定义网络模型`Net`,再对其进行一阶求导,通过这样一个例子对`GradOperation`接口的使用方式做简单介绍,即公式:\n",
+ "\n",
+ "$$f(x, y)=(x * z) * y \\tag{1}$$\n",
+ "\n",
+ "首先我们要定义网络模型`Net`、输入`x`和输入`y`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import ParameterTuple, Parameter\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "# 定义输出x和y\n",
+ "x = Tensor([[0.8, 0.6, 0.2], [1.8, 1.3, 1.1]], dtype=mstype.float32)\n",
+ "y = Tensor([[0.11, 3.3, 1.1], [1.1, 0.2, 1.4], [1.1, 2.2, 0.3]], dtype=mstype.float32)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " \"\"\"定义矩阵相乘网络Net\"\"\"\n",
+ "\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.matmul = ops.MatMul() # 矩阵相乘\n",
+ " self.z = Parameter(Tensor(np.array([1.0], np.float32)), name='z') # 定义权重\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " x = x * self.z\n",
+ " out = self.matmul(x, y)\n",
+ " return out"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 对输入进行求导\n",
+ "\n",
+ "对输入值进行求导,代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[4.5099998 2.7 3.6000001]\n",
+ " [4.5099998 2.7 3.6000001]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "class GradNetWrtX(nn.Cell):\n",
+ " \"\"\"定义网络输入的一阶求导\"\"\"\n",
+ "\n",
+ " def __init__(self, net):\n",
+ " super(GradNetWrtX, self).__init__()\n",
+ " self.net = net\n",
+ " self.grad_op = ops.GradOperation()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " gradient_function = self.grad_op(self.net)\n",
+ " return gradient_function(x, y)\n",
+ "\n",
+ "output = GradNetWrtX(Net())(x, y)\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "接下来我们对上面的结果做一个解释。为便于分析,我们把上面的输入`x`、`y`以及权重`z`表示成如下形式:\n",
+ "\n",
+ "```text\n",
+ "x = Tensor([[x1, x2, x3], [x4, x5, x6]])\n",
+ "y = Tensor([[y1, y2, y3], [y4, y5, y6], [y7, y8, y9]])\n",
+ "z = Tensor([z])\n",
+ "```\n",
+ "\n",
+ "根据MatMul算子定义可得前向结果:\n",
+ "\n",
+ "$$output = [[(x_1 \\cdot y_1 + x_2 \\cdot y_4 + x_3 \\cdot y_7) \\cdot z, (x_1 \\cdot y_2 + x_2 \\cdot y_5 + x_3 \\cdot y_8) \\cdot z, (x_1 \\cdot y_3 + x_2 \\cdot y_6 + x_3 \\cdot y_9) \\cdot z],$$\n",
+ "\n",
+ "$$[(x_4 \\cdot y_1 + x_5 \\cdot y_4 + x_6 \\cdot y_7) \\cdot z, (x_4 \\cdot y_2 + x_5 \\cdot y_5 + x_6 \\cdot y_8) \\cdot z, (x_4 \\cdot y_3 + x_5 \\cdot y_6 + x_6 \\cdot y_9) \\cdot z]] \\tag{2}$$\n",
+ "\n",
+ "梯度计算时由于MindSpore采用的是Reverse自动微分机制,会对输出结果求和后再对输入`x`求导:\n",
+ "\n",
+ "1. 求和公式:\n",
+ "\n",
+ "$$\\sum{output} = [(x_1 \\cdot y_1 + x_2 \\cdot y_4 + x_3 \\cdot y_7) + (x_1 \\cdot y_2 + x_2 \\cdot y_5 + x_3 \\cdot y_8) + (x_1 \\cdot y_3 + x_2 \\cdot y_6 + x_3 \\cdot y_9)$$\n",
+ "\n",
+ "$$+ (x_4 \\cdot y_1 + x_5 \\cdot y_4 + x_6 \\cdot y_7) + (x_4 \\cdot y_2 + x_5 \\cdot y_5 + x_6 \\cdot y_8) + (x_4 \\cdot y_3 + x_5 \\cdot y_6 + x_6 \\cdot y_9)] \\cdot z \\tag{3}$$\n",
+ "\n",
+ "2. 求导公式:\n",
+ "\n",
+ "$$\\frac{\\mathrm{d}(\\sum{output})}{\\mathrm{d}x} = [[(y_1 + y_2 + y_3) \\cdot z, (y_4 + y_5 + y_6) \\cdot z, (y_7 + y_8 + y_9) \\cdot z],$$\n",
+ "\n",
+ "$$[(y_1 + y_2 + y_3) \\cdot z, (y_4 + y_5 + y_6) \\cdot z, (y_7 + y_8 + y_9) \\cdot z]] \\tag{4}$$\n",
+ "\n",
+ "3. 计算结果:\n",
+ "\n",
+ "$$\\frac{\\mathrm{d}(\\sum{output})}{\\mathrm{d}x} = [[4.51 \\quad 2.7 \\quad 3.6] [4.51 \\quad 2.7 \\quad 3.6]] \\tag{5}$$\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> 若考虑对`x`、`y`输入求导,只需在`GradNetWrtX`中设置`self.grad_op = GradOperation(get_all=True)`。\n",
+ "\n",
+ "### 对权重进行求导\n",
+ "\n",
+ "对权重进行求导,示例代码如下:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[21.536]\n"
+ ]
+ }
+ ],
+ "source": [
+ "class GradNetWrtZ(nn.Cell):\n",
+ " \"\"\"定义网络权重的一阶求导\"\"\"\n",
+ "\n",
+ " def __init__(self, net):\n",
+ " super(GradNetWrtZ, self).__init__()\n",
+ " self.net = net\n",
+ " self.params = ParameterTuple(net.trainable_params())\n",
+ " self.grad_op = ops.GradOperation(get_by_list=True)\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " gradient_function = self.grad_op(self.net, self.params)\n",
+ " return gradient_function(x, y)\n",
+ "\n",
+ "output = GradNetWrtZ(Net())(x, y)\n",
+ "print(output[0])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "下面我们通过公式对上面的结果做一个解释。对权重的求导公式为:\n",
+ "\n",
+ "$$\\frac{\\mathrm{d}(\\sum{output})}{\\mathrm{d}z} = (x_1 \\cdot y_1 + x_2 \\cdot y_4 + x_3 \\cdot y_7) + (x_1 \\cdot y_2 + x_2 \\cdot y_5 + x_3 \\cdot y_8) + (x_1 \\cdot y_3 + x_2 \\cdot y_6 + x_3 \\cdot y_9)$$\n",
+ "\n",
+ "$$+ (x_4 \\cdot y_1 + x_5 \\cdot y_4 + x_6 \\cdot y_7) + (x_4 \\cdot y_2 + x_5 \\cdot y_5 + x_6 \\cdot y_8) + (x_4 \\cdot y_3 + x_5 \\cdot y_6 + x_6 \\cdot y_9) \\tag{6}$$\n",
+ "\n",
+ "计算结果:\n",
+ "\n",
+ "$$\\frac{\\mathrm{d}(\\sum{output})}{\\mathrm{d}z} = [2.1536e+01] \\tag{7}$$\n",
+ "\n",
+ "### 梯度值缩放\n",
+ "\n",
+ "可以通过`sens_param`参数控制梯度值的缩放:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[2.211 0.51 1.49 ]\n",
+ " [5.588 2.68 4.07 ]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "class GradNetWrtN(nn.Cell):\n",
+ " \"\"\"定义网络的一阶求导,控制梯度值缩放\"\"\"\n",
+ " def __init__(self, net):\n",
+ " super(GradNetWrtN, self).__init__()\n",
+ " self.net = net\n",
+ " self.grad_op = ops.GradOperation(sens_param=True)\n",
+ "\n",
+ " # 定义梯度值缩放\n",
+ " self.grad_wrt_output = Tensor([[0.1, 0.6, 0.2], [0.8, 1.3, 1.1]], dtype=mstype.float32)\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " gradient_function = self.grad_op(self.net)\n",
+ " return gradient_function(x, y, self.grad_wrt_output)\n",
+ "\n",
+ "output = GradNetWrtN(Net())(x, y)\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "为了方便对上面的结果进行解释,我们把`self.grad_wrt_output`记作如下形式:\n",
+ "\n",
+ "```text\n",
+ "self.grad_wrt_output = Tensor([[s1, s2, s3], [s4, s5, s6]])\n",
+ "```\n",
+ "\n",
+ "缩放后的输出值为原输出值与`self.grad_wrt_output`对应元素的乘积,公式为:\n",
+ "\n",
+ "$$output = [[(x_1 \\cdot y_1 + x_2 \\cdot y_4 + x_3 \\cdot y_7) \\cdot z \\cdot s_1, (x_1 \\cdot y_2 + x_2 \\cdot y_5 + x_3 \\cdot y_8) \\cdot z \\cdot s_2, (x_1 \\cdot y_3 + x_2 \\cdot y_6 + x_3 \\cdot y_9) \\cdot z \\cdot s_3], $$\n",
+ "\n",
+ "$$[(x_4 \\cdot y_1 + x_5 \\cdot y_4 + x_6 \\cdot y_7) \\cdot z \\cdot s_4, (x_4 \\cdot y_2 + x_5 \\cdot y_5 + x_6 \\cdot y_8) \\cdot z \\cdot s_5, (x_4 \\cdot y_3 + x_5 \\cdot y_6 + x_6 \\cdot y_9) \\cdot z \\cdot s_6]] \\tag{8}$$\n",
+ "\n",
+ "求导公式变为输出值总和对`x`的每个元素求导:\n",
+ "\n",
+ "$$\\frac{\\mathrm{d}(\\sum{output})}{\\mathrm{d}x} = [[(s_1 \\cdot y_1 + s_2 \\cdot y_2 + s_3 \\cdot y_3) \\cdot z, (s_1 \\cdot y_4 + s_2 \\cdot y_5 + s_3 \\cdot y_6) \\cdot z, (s_1 \\cdot y_7 + s_2 \\cdot y_8 + s_3 \\cdot y_9) \\cdot z],$$\n",
+ "\n",
+ "$$[(s_4 \\cdot y_1 + s_5 \\cdot y_2 + s_6 \\cdot y_3) \\cdot z, (s_4 \\cdot y_4 + s_5 \\cdot y_5 + s_6 \\cdot y_6) \\cdot z, (s_4 \\cdot y_7 + s_5 \\cdot y_8 + s_6 \\cdot y_9) \\cdot z]] \\tag{9}$$\n",
+ "\n",
+ "计算结果:\n",
+ "\n",
+ "$$\\frac{\\mathrm{d}(\\sum{output})}{\\mathrm{d}z} = [[2.211 \\quad 0.51 \\quad 1.49][5.588 \\quad 2.68 \\quad 4.07]] \\tag{10}$$\n",
+ "\n",
+ "## 停止计算梯度\n",
+ "\n",
+ "我们可以使用`stop_gradient`来停止计算指定算子的梯度,从而消除该算子对梯度的影响。\n",
+ "\n",
+ "在上面一阶求导使用的矩阵相乘网络模型的基础上,我们再增加一个算子`out2`并禁止计算其梯度,得到自定义网络`Net2`,然后看一下对输入的求导结果情况。\n",
+ "\n",
+ "示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[4.5099998 2.7 3.6000001]\n",
+ " [4.5099998 2.7 3.6000001]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore.ops import stop_gradient\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ "\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.matmul = ops.MatMul()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " out1 = self.matmul(x, y)\n",
+ " out2 = self.matmul(x, y)\n",
+ " out2 = stop_gradient(out2) # 停止计算out2算子的梯度\n",
+ " out = out1 + out2\n",
+ " return out\n",
+ "\n",
+ "class GradNetWrtX(nn.Cell):\n",
+ "\n",
+ " def __init__(self, net):\n",
+ " super(GradNetWrtX, self).__init__()\n",
+ " self.net = net\n",
+ " self.grad_op = ops.GradOperation()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " gradient_function = self.grad_op(self.net)\n",
+ " return gradient_function(x, y)\n",
+ "\n",
+ "output = GradNetWrtX(Net())(x, y)\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印可以看出,由于对`out2`设置了`stop_gradient`, 所以`out2`没有对梯度计算有任何的贡献,其输出结果与未加`out2`算子时一致。\n",
+ "\n",
+ "下面我们删除`out2 = stop_gradient(out2)`,再来看一下输出结果。示例代码为:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[9.0199995 5.4 7.2000003]\n",
+ " [9.0199995 5.4 7.2000003]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.matmul = ops.MatMul()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " out1 = self.matmul(x, y)\n",
+ " out2 = self.matmul(x, y)\n",
+ " # out2 = stop_gradient(out2)\n",
+ " out = out1 + out2\n",
+ " return out\n",
+ "\n",
+ "class GradNetWrtX(nn.Cell):\n",
+ " def __init__(self, net):\n",
+ " super(GradNetWrtX, self).__init__()\n",
+ " self.net = net\n",
+ " self.grad_op = ops.GradOperation()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " gradient_function = self.grad_op(self.net)\n",
+ " return gradient_function(x, y)\n",
+ "\n",
+ "output = GradNetWrtX(Net())(x, y)\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "打印结果可以看出,在我们把`out2`算子的梯度也计算进去之后,由于`out2`和`out1`算子完全相同,因此它们产生的梯度也完全相同,所以我们可以看到,结果中每一项的值都变为了原来的两倍(存在精度误差)。\n",
+ "\n",
+ "## 高阶求导\n",
+ "\n",
+ "高阶微分在AI支持科学计算、二阶优化等领域均有应用。如分子动力学模拟中,利用神经网络训练势能时,损失函数中需计算神经网络输出对输入的导数,则反向传播便存在损失函数对输入、权重的二阶交叉导数。\n",
+ "\n",
+ "此外,AI求解微分方程(如PINNs方法)还会存在输出对输入的二阶导数。又如二阶优化中,为了能够让神经网络快速收敛,牛顿法等需计算损失函数对权重的二阶导数。\n",
+ "\n",
+ "MindSpore可通过多次求导的方式支持高阶导数,下面通过几类例子展开阐述。\n",
+ "\n",
+ "### 单输入单输出高阶导数\n",
+ "\n",
+ "例如Sin算子,其二阶导数(-Sin)实现如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[-5.35897932e-08]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import Tensor\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " \"\"\"定义基于Sin算子的网络模型\"\"\"\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.sin = ops.Sin()\n",
+ "\n",
+ " def construct(self, x):\n",
+ " out = self.sin(x)\n",
+ " return out\n",
+ "\n",
+ "class Grad(nn.Cell):\n",
+ " \"\"\"一阶求导\"\"\"\n",
+ " def __init__(self, network):\n",
+ " super(Grad, self).__init__()\n",
+ " self.grad = ops.GradOperation()\n",
+ " self.network = network\n",
+ "\n",
+ " def construct(self, x):\n",
+ " gout = self.grad(self.network)(x)\n",
+ " return gout\n",
+ "\n",
+ "class GradSec(nn.Cell):\n",
+ " \"\"\"二阶求导\"\"\"\n",
+ " def __init__(self, network):\n",
+ " super(GradSec, self).__init__()\n",
+ " self.grad = ops.GradOperation()\n",
+ " self.network = network\n",
+ "\n",
+ " def construct(self, x):\n",
+ " gout = self.grad(self.network)(x)\n",
+ " return gout\n",
+ "\n",
+ "x_train = Tensor(np.array([3.1415926]), dtype=mstype.float32)\n",
+ "\n",
+ "net = Net()\n",
+ "firstgrad = Grad(net)\n",
+ "secondgrad = GradSec(firstgrad)\n",
+ "output = secondgrad(x_train)\n",
+ "\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,`-sin(3.1415926)`的值接近于`0`。"
+ ]
+ }
+ ],
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/tutorials/source_zh_cn/advanced/network/forward.ipynb b/tutorials/source_zh_cn/advanced/network/forward.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..449646c9bfb7c59d5d33c917d5ea5c05cf6fac90
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/network/forward.ipynb
@@ -0,0 +1,676 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 构建网络\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_forward.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_forward.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/network/forward.ipynb)\n",
+ "\n",
+ "MindSpore的`Cell`类是构建所有网络的基类,也是网络的基本单元。自定义网络时,需要继承`Cell`类,本章主要介绍网络基本单元`Cell`和自定义前向网络。\n",
+ "\n",
+ "本章主要介绍前向网络模型的构建和网络模型的基本单元,因为不涉及到训练,因此没有反向传播和反向图。\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`。\n",
+ "\n",
+ "```Python\n",
+ "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 # 优化器\n",
+ "```\n",
+ "\n",
+ "`CustomTrainOneStepCell`代码详细内容可参见[自定义训练与评估网络](https://www.mindspore.cn/tutorials/zh-CN/master/advanced/network/train_eval.html#自定义训练网络)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "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": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "training result:\n",
+ " [[[1.4285715 1.4285715 1.4285715]\n",
+ " [0. 1.4285715 1.4285715]]\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",
+ "from mindspore import Tensor\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "x = Tensor(np.ones([2, 2, 3]), mstype.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": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Float32\n",
+ "Float16\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, nn\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "# float32进行计算\n",
+ "x = Tensor(np.ones([2, 2, 3]), mstype.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(mstype.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/api/zh-CN/master/api_python/mindspore.ops.html)模块提供了基础算子的实现,如神经网络算子、数组算子和数学算子等。\n",
+ "\n",
+ "用户可使用`mindspore.ops`中的算子来构建一个简单的算法 $f(x)=x^2+w$,示例如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "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 Parameter, Tensor, 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 = Parameter(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 = 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/api/zh-CN/master/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": 7,
+ "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 Tensor, 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": 8,
+ "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\n",
+ "from mindspore import Tensor, 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": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "(1, 4, 64, 32)\n"
+ ]
+ }
+ ],
+ "source": [
+ "input = Tensor(np.ones([1, 3, 64, 32]), mindspore.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": 10,
+ "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\n",
+ "from mindspore import Tensor, 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": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "(1, 4, 64, 32)\n"
+ ]
+ }
+ ],
+ "source": [
+ "input = Tensor(np.ones([1, 3, 64, 32]), mindspore.float32)\n",
+ "output = net(input)\n",
+ "print(output.shape)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "如下示例使用`nn.SequentialCell`构建一个网络,输入为OrderedDict:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "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\n",
+ "from mindspore import Tensor, 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 = Tensor(np.ones([1, 3, 64, 32]), mindspore.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": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import mindspore.nn as nn\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import Parameter\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 = Parameter(\n",
+ " initializer('normal', [out_channels, in_channels, kernel_size, kernel_size]),\n",
+ " name='conv.weight')\n",
+ " self.bias = 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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/network/images/learning_rate.png b/tutorials/source_zh_cn/advanced/network/images/learning_rate.png
new file mode 100644
index 0000000000000000000000000000000000000000..dc8008798c288a8e7bc4b020fe5e85bc8524a0ab
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/network/images/learning_rate.png differ
diff --git a/tutorials/source_zh_cn/advanced/network/images/loss_function.png b/tutorials/source_zh_cn/advanced/network/images/loss_function.png
new file mode 100644
index 0000000000000000000000000000000000000000..60d698b1269e6ba9a91a2ec2d69b872366a2caaf
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/network/images/loss_function.png differ
diff --git a/tutorials/source_zh_cn/advanced/network/images/parameter.graffle b/tutorials/source_zh_cn/advanced/network/images/parameter.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..63936dbacafcf5931326b734771893a86e32208b
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/network/images/parameter.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/network/images/parameter.png b/tutorials/source_zh_cn/advanced/network/images/parameter.png
new file mode 100644
index 0000000000000000000000000000000000000000..8229469c378abd0d02851c53bd871deb594bc8b2
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/network/images/parameter.png differ
diff --git a/tutorials/source_zh_cn/advanced/network/loss.ipynb b/tutorials/source_zh_cn/advanced/network/loss.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..f9ddcf63058d6a398a30681b80f8cbf5a77f9259
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/network/loss.ipynb
@@ -0,0 +1,482 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 损失函数\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_loss.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_loss.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/network/loss.ipynb)\n",
+ "\n",
+ "损失函数,又叫目标函数,用于衡量预测值与真实值差异的程度。\n",
+ "\n",
+ "在深度学习中,模型训练就是通过不停地迭代来缩小损失函数值的过程,因此,在模型训练过程中损失函数的选择非常重要,定义一个好的损失函数,可以有效提高模型的性能。\n",
+ "\n",
+ "`mindspore.nn`模块中提供了许多[通用损失函数](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id12),但这些通用损失函数并不适用于所有场景,很多情况需要用户自定义所需的损失函数。因此,本教程介绍如何自定义损失函数。\n",
+ "\n",
+ "\n",
+ "\n",
+ "## 内置损失函数\n",
+ "\n",
+ "`mindspore.nn`模块中提供了许多[通用损失函数](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id12)。\n",
+ "\n",
+ "如下示例以`nn.L1Loss`为例,计算预测值和目标值之间的平均绝对误差:\n",
+ "\n",
+ "$$\\ell(x, y) = L = \\{l_1,\\dots,l_N\\}^\\top, \\quad \\text{with } l_n = \\left| x_n - y_n \\right|$$\n",
+ "\n",
+ "其中N为数据集中的batch size大小。如果参数 `reduction`置位空,则默认:\n",
+ "\n",
+ "$$\\ell(x, y) =\n",
+ " \\begin{cases}\n",
+ " \\operatorname{mean}(L), & \\text{if reduction} = \\text{'mean';}\\\\\n",
+ " \\operatorname{sum}(L), & \\text{if reduction} = \\text{'sum'.}\n",
+ " \\end{cases}$$\n",
+ "\n",
+ "`nn.L1Loss`中的参数`reduction`取值可为`mean`,`sum`,或`none`。如果 `reduction` 为`mean`或`sum`,则输出一个标量Tensor;如果`reduction`为`none`,则输出Tensor的shape为广播后的shape。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-29T03:42:22.717822Z",
+ "start_time": "2021-12-29T03:42:20.636585Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "loss: 1.5\n",
+ "loss_sum: 9.0\n",
+ "loss_none:\n",
+ " [[1. 0. 2.]\n",
+ " [1. 2. 3.]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "\n",
+ "# 输出loss均值\n",
+ "loss = nn.L1Loss()\n",
+ "# 输出loss和\n",
+ "loss_sum = nn.L1Loss(reduction='sum')\n",
+ "# 输出loss原值\n",
+ "loss_none = nn.L1Loss(reduction='none')\n",
+ "\n",
+ "input_data = Tensor(np.array([[1, 2, 3], [2, 3, 4]]).astype(np.float32))\n",
+ "target_data = Tensor(np.array([[0, 2, 5], [3, 1, 1]]).astype(np.float32))\n",
+ "\n",
+ "print(\"loss:\", loss(input_data, target_data))\n",
+ "print(\"loss_sum:\", loss_sum(input_data, target_data))\n",
+ "print(\"loss_none:\\n\", loss_none(input_data, target_data))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 自定义损失函数\n",
+ "\n",
+ "自定义损失函数的方法有两种,一种是通过继承网络的基类`nn.Cell`来定义损失函数,另一种是通过继承损失函数的基类`nn.LossBase`来定义损失函数。`nn.LossBase`在`nn.Cell`的基础上,提供了`get_loss`方法,利用`reduction`参数对损失值求和或求均值,输出一个标量。\n",
+ "\n",
+ "下面将分别使用继承`Cell`和继承`LossBase`的方法,来定义平均绝对误差损失函数(Mean Absolute Error,MAE),MAE算法的公式如下所示:\n",
+ "\n",
+ "$$ loss= \\frac{1}{m}\\sum_{i=1}^m\\lvert y_i-f(x_i) \\rvert$$\n",
+ "\n",
+ "上式中$f(x)$为预测值,$y$为样本真实值,$loss$为预测值与真实值之间距离的平均值。\n",
+ "\n",
+ "### 继承Cell的损失函数\n",
+ "\n",
+ "`nn.Cell`是MindSpore的基类,可以用于构建网络,也可以用于定义损失函数。使用`nn.Cell`定义损失函数的方法与定义一个普通的网络相同,差别在于,其执行逻辑用于计算前向网络输出与真实值之间的误差。\n",
+ "\n",
+ "下面通过继承`nn.Cell`方法来定义损失函数`MAELoss`的方法如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-29T03:42:22.729232Z",
+ "start_time": "2021-12-29T03:42:22.723517Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0.033333335\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor\n",
+ "import mindspore.nn as nn\n",
+ "import mindspore.ops as ops\n",
+ "\n",
+ "class MAELoss(nn.Cell):\n",
+ " \"\"\"自定义损失函数MAELoss\"\"\"\n",
+ "\n",
+ " def __init__(self):\n",
+ " \"\"\"初始化\"\"\"\n",
+ " super(MAELoss, self).__init__()\n",
+ " self.abs = ops.Abs()\n",
+ " self.reduce_mean = ops.ReduceMean()\n",
+ "\n",
+ " def construct(self, base, target):\n",
+ " \"\"\"调用算子\"\"\"\n",
+ " x = self.abs(base - target)\n",
+ " return self.reduce_mean(x)\n",
+ "\n",
+ "loss = MAELoss()\n",
+ "\n",
+ "input_data = Tensor(np.array([0.1, 0.2, 0.3]).astype(np.float32)) # 生成预测值\n",
+ "target_data = Tensor(np.array([0.1, 0.2, 0.2]).astype(np.float32)) # 生成真实值\n",
+ "\n",
+ "output = loss(input_data, target_data)\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 继承LossBase的损失函数\n",
+ "\n",
+ "通过继承[nn.LossBase](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.LossBase.html#mindspore.nn.LossBase)来定义损失函数`MAELoss`,与`nn.Cell`类似,都要重写`__init__`方法和`construct`方法。\n",
+ "\n",
+ "`nn.LossBase`可使用方法`get_loss`将`reduction`应用于损失计算。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-29T03:42:22.766767Z",
+ "start_time": "2021-12-29T03:42:22.759510Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "0.033333335\n"
+ ]
+ }
+ ],
+ "source": [
+ "class MAELoss(nn.LossBase):\n",
+ " \"\"\"自定义损失函数MAELoss\"\"\"\n",
+ "\n",
+ " def __init__(self, reduction=\"mean\"):\n",
+ " \"\"\"初始化并求loss均值\"\"\"\n",
+ " super(MAELoss, self).__init__(reduction)\n",
+ " self.abs = ops.Abs() # 求绝对值算子\n",
+ "\n",
+ " def construct(self, base, target):\n",
+ " x = self.abs(base - target)\n",
+ " return self.get_loss(x) # 返回loss均值\n",
+ "\n",
+ "loss = MAELoss()\n",
+ "\n",
+ "input_data = Tensor(np.array([0.1, 0.2, 0.3]).astype(np.float32)) # 生成预测值\n",
+ "target_data = Tensor(np.array([0.1, 0.2, 0.2]).astype(np.float32)) # 生成真实值\n",
+ "\n",
+ "output = loss(input_data, target_data)\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 损失函数与模型训练\n",
+ "\n",
+ "自定义的损失函数`MAELoss`完成后,可使用MindSpore的接口[Model](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore/mindspore.Model.html#mindspore.Model)中`train`接口进行模型训练,定义`Model`时需要指定前向网络、损失函数和优化器,`Model`内部会将它们关联起来,组成一张可用于训练的网络模型。\n",
+ "\n",
+ "在`Model`中,前向网络和损失函数是通过[nn.WithLossCell](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.WithLossCell.html#mindspore.nn.WithLossCell)关联起来的,`nn.WithLossCell`支持两个输入,分别为`data`和`label`。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-29T03:42:23.488075Z",
+ "start_time": "2021-12-29T03:42:23.312491Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch:[ 0/ 1], step:[ 1/ 10], loss:[ 9.128/ 9.128], time:129.267, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 2/ 10], loss:[ 9.771/ 9.450], time:0.578, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 3/ 10], loss:[10.412/ 9.770], time:0.623, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 4/ 10], loss:[12.535/10.461], time:2.648, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 5/ 10], loss:[ 8.608/10.091], time:0.844, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 6/ 10], loss:[11.945/10.400], time:4.566, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 7/ 10], loss:[ 7.768/10.024], time:0.850, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 8/ 10], loss:[ 7.319/ 9.686], time:0.877, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 9/ 10], loss:[ 7.356/ 9.427], time:0.914, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 10/ 10], loss:[ 5.024/ 8.987], time:0.777, lr:0.00500.\n",
+ "Epoch time: 155.354,per step time: 15.535,avg loss: 8.987\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore import Model\n",
+ "from mindspore import dataset as ds\n",
+ "from mindspore.nn import LossBase\n",
+ "from mindspore.common.initializer import Normal\n",
+ "from mindvision.engine.callback import LossMonitor\n",
+ "\n",
+ "def get_data(num, w=2.0, b=3.0):\n",
+ " \"\"\"生成数据及对应标签\"\"\"\n",
+ " for _ in range(num):\n",
+ " x = np.random.uniform(-10.0, 10.0)\n",
+ " noise = np.random.normal(0, 1)\n",
+ " y = x * w + b + noise\n",
+ " yield np.array([x]).astype(np.float32), np.array([y]).astype(np.float32)\n",
+ "\n",
+ "def create_dataset(num_data, batch_size=16):\n",
+ " \"\"\"加载数据集\"\"\"\n",
+ " dataset = ds.GeneratorDataset(list(get_data(num_data)), column_names=['data', 'label'])\n",
+ " dataset = dataset.batch(batch_size)\n",
+ " return dataset\n",
+ "\n",
+ "class LinearNet(nn.Cell):\n",
+ " \"\"\"定义线性回归网络\"\"\"\n",
+ " def __init__(self):\n",
+ " super(LinearNet, self).__init__()\n",
+ " self.fc = nn.Dense(1, 1, Normal(0.02), Normal(0.02))\n",
+ "\n",
+ " def construct(self, x):\n",
+ " return self.fc(x)\n",
+ "\n",
+ "ds_train = create_dataset(num_data=160)\n",
+ "net = LinearNet()\n",
+ "loss = MAELoss()\n",
+ "opt = nn.Momentum(net.trainable_params(), learning_rate=0.005, momentum=0.9)\n",
+ "\n",
+ "# 使用model接口将网络、损失函数和优化器关联起来\n",
+ "model = Model(net, loss, opt)\n",
+ "model.train(epoch=1, train_dataset=ds_train, callbacks=[LossMonitor(0.005)])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 多标签损失函数与模型训练\n",
+ "\n",
+ "上述定义了一个简单的平均绝对误差损失函数`MAELoss`,但许多深度学习应用的数据集较复杂,例如目标检测网络Faster R-CNN的数据中就包含多个标签,而不是简单的一个数据对应一个标签,这时候损失函数的定义和使用略有不同。\n",
+ "\n",
+ "本节介绍在多标签数据集场景下,如何定义多标签损失函数(Multi label loss function),并使用Model进行模型训练。\n",
+ "\n",
+ "### 多标签数据集\n",
+ "\n",
+ "如下示例通过`get_multilabel_data`函数拟合两组线性数据$y1$和$y2$,拟合的目标函数为:\n",
+ "\n",
+ "$$f(x)=2x+3$$\n",
+ "\n",
+ "由于最终的数据集应该随机分布于函数周边,这里按以下公式的方式生成,其中`noise`为遵循标准正态分布规律的随机数值。`get_multilabel_data`函数返回数据$x$、$y1$和$y2$:\n",
+ "\n",
+ "$$f(x)=2x+3+noise$$\n",
+ "\n",
+ "通过`create_multilabel_dataset`生成多标签数据集,并将`GeneratorDataset`中的`column_names`参数设置为['data', 'label1', 'label2'],最终返回的数据集就有一个数据`data`对应两个标签`label1`和`label2`。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import dataset as ds\n",
+ "\n",
+ "def get_multilabel_data(num, w=2.0, b=3.0):\n",
+ " for _ in range(num):\n",
+ " x = np.random.uniform(-10.0, 10.0)\n",
+ " noise1 = np.random.normal(0, 1)\n",
+ " noise2 = np.random.normal(-1, 1)\n",
+ " y1 = x * w + b + noise1\n",
+ " y2 = x * w + b + noise2\n",
+ " yield np.array([x]).astype(np.float32), np.array([y1]).astype(np.float32), np.array([y2]).astype(np.float32)\n",
+ "\n",
+ "def create_multilabel_dataset(num_data, batch_size=16):\n",
+ " dataset = ds.GeneratorDataset(list(get_multilabel_data(num_data)), column_names=['data', 'label1', 'label2'])\n",
+ " dataset = dataset.batch(batch_size) # 每个batch有16个数据\n",
+ " return dataset"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 多标签损失函数\n",
+ "\n",
+ "针对上一步创建的多标签数据集,定义多标签损失函数`MAELossForMultiLabel`。\n",
+ "\n",
+ "$$ loss1= \\frac{1}{m}\\sum_{i=1}^m\\lvert y1_i-f(x_i) \\rvert$$\n",
+ "\n",
+ "$$ loss2= \\frac{1}{m}\\sum_{i=1}^m\\lvert y2_i-f(x_i) \\rvert$$\n",
+ "\n",
+ "$$ loss = \\frac{(loss1 + loss2)}{2}$$\n",
+ "\n",
+ "上式中,$f(x)$ 为预测值,$y1$ 和 $y2$ 为样本真实值,$loss1$ 为预测值与样本真实值 $y1$ 之间距离的平均值,$loss2$ 为预测值与样本真实值 $y2$ 之间距离的平均值 ,$loss$ 为损失值 $loss1$ 与损失值 $loss2$ 平均值。\n",
+ "\n",
+ "在`MAELossForMultiLabel`中的`construct`方法的输入有三个,预测值`base`,真实值`target1`和`target2`,在`construct`中分别计算预测值与真实值`target1`,预测值与真实值`target2`之间的误差,将这两个误差的均值作为最终的损失函数值.\n",
+ "\n",
+ "示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class MAELossForMultiLabel(LossBase):\n",
+ " def __init__(self, reduction=\"mean\"):\n",
+ " super(MAELossForMultiLabel, self).__init__(reduction)\n",
+ " self.abs = ops.Abs()\n",
+ "\n",
+ " def construct(self, base, target1, target2):\n",
+ " x1 = self.abs(base - target1)\n",
+ " x2 = self.abs(base - target2)\n",
+ " return self.get_loss(x1)/2 + self.get_loss(x2)/2"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 多标签模型训练\n",
+ "\n",
+ "使用`Model`关联指定的前向网络、损失函数和优化器时,由于`Model`默认使用的`nn.WithLossCell`只有两个输入:`data`和`label`,不适用于多标签的场景。\n",
+ "\n",
+ "在多标签场景下,如果想使用`Model`进行模型训练就需要将前向网络与多标签损失函数连接起来,需要自定义损失网络,将前向网络和自定义多标签损失函数关联起来。\n",
+ "\n",
+ "- 定义损失网络\n",
+ "\n",
+ "定义损失网络`CustomWithLossCell`,其中`__init__`方法的输入分别为前向网络`backbone`和损失函数`loss_fn`,`construct`方法的输入分别为数据`data`、`label1`和`label2`,将数据部分`data`传给前向网络`backend`,将预测值和两个标签传给损失函数`loss_fn`。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class CustomWithLossCell(nn.Cell):\n",
+ " def __init__(self, backbone, loss_fn):\n",
+ " super(CustomWithLossCell, self).__init__(auto_prefix=False)\n",
+ " self._backbone = backbone\n",
+ " self._loss_fn = loss_fn\n",
+ "\n",
+ " def construct(self, data, label1, label2):\n",
+ " output = self._backbone(data)\n",
+ " return self._loss_fn(output, label1, label2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- 定义网络模型并训练\n",
+ "\n",
+ "使用Model连接前向网络、多标签损失函数和优化器时,`Model`的网络`network`指定为自定义的损失网络`loss_net`,损失函数`loss_fn`不指定,优化器仍使用`Momentum`。\n",
+ "\n",
+ "由于未指定`loss_fn`,`Model`则认为`network`内部已经实现了损失函数的逻辑,不会用`nn.WithLossCell`对前向函数和损失函数进行封装。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-29T03:42:24.079033Z",
+ "start_time": "2021-12-29T03:42:23.851418Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch:[ 0/ 1], step:[ 1/ 10], loss:[ 9.128/ 9.128], time:129.267, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 2/ 10], loss:[ 9.771/ 9.450], time:0.578, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 3/ 10], loss:[10.412/ 9.770], time:0.623, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 4/ 10], loss:[12.535/10.461], time:2.648, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 5/ 10], loss:[ 8.608/10.091], time:0.844, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 6/ 10], loss:[11.945/10.400], time:4.566, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 7/ 10], loss:[ 7.768/10.024], time:0.850, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 8/ 10], loss:[ 7.319/ 9.686], time:0.877, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 9/ 10], loss:[ 7.356/ 9.427], time:0.914, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 10/ 10], loss:[ 5.024/ 8.987], time:0.777, lr:0.00500.\n",
+ "Epoch time: 155.354,per step time: 15.535,avg loss: 8.987\n"
+ ]
+ }
+ ],
+ "source": [
+ "ds_train = create_multilabel_dataset(num_data=160)\n",
+ "net = LinearNet()\n",
+ "\n",
+ "# 定义多标签损失函数\n",
+ "loss = MAELossForMultiLabel()\n",
+ "\n",
+ "# 定义损失网络,连接前向网络和多标签损失函数\n",
+ "loss_net = CustomWithLossCell(net, loss)\n",
+ "\n",
+ "# 定义优化器\n",
+ "opt = nn.Momentum(net.trainable_params(), learning_rate=0.005, momentum=0.9)\n",
+ "\n",
+ "# 定义Model,多标签场景下Model无需指定损失函数\n",
+ "model = Model(network=loss_net, optimizer=opt)\n",
+ "\n",
+ "model.train(epoch=1, train_dataset=ds_train, callbacks=[LossMonitor(0.005)])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "本章节简单讲解了多标签数据集场景下,如何定义损失函数并使用Model进行模型训练。在很多其他场景中,也可以采用此类方法进行模型训练。"
+ ]
+ }
+ ],
+ "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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/network/optim.ipynb b/tutorials/source_zh_cn/advanced/network/optim.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..4d352b2c73dd5b01fea822c8bbf66f7aed540c51
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/network/optim.ipynb
@@ -0,0 +1,434 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 优化器\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_optim.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_optim.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/network/optim.ipynb)\n",
+ "\n",
+ "模型训练过程中,使用优化器计算梯度并更新网络参数,合适的优化器可以有效减少训练时间,提高模型性能。\n",
+ "\n",
+ "最基本的优化器是随机梯度下降算法(SGD),很多优化器在SGD的基础上进行了改进,以实现目标函数能更快速更有效地收敛到全局最优点。MindSpore中的`nn`模块提供了常用的优化器,如`nn.SGD`、`nn.Adam`、`nn.Momentum`等。本章主要介绍如何配置MindSpore提供的优化器以及如何自定义优化器。\n",
+ "\n",
+ "\n",
+ "\n",
+ "> MindSpore提供的优化器详细内容参见[优化器API](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id13)。\n",
+ "\n",
+ "## 配置优化器\n",
+ "\n",
+ "使用MindSpore提供的优化器时,首先需要指定待优化的网络参数`params`,然后设置优化器的其他主要参数,如学习率`learning_rate`和权重衰减`weight_decay`等。\n",
+ "\n",
+ "如果要为不同网络参数单独设置选项,如对卷积参数和非卷积参数设置不同的学习率,可使用参数分组的方法来设置优化器。\n",
+ "\n",
+ "### 参数配置\n",
+ "\n",
+ "在构建优化器实例时,需要通过优化器参数`params`配置模型网络中要训练和更新的权重,常见的参数配置方法有以下两种。\n",
+ "\n",
+ "- 使用`trainable_params`配置参数\n",
+ "\n",
+ "变量`Parameter`中包含了一个`requires_grad`的布尔型的类属性,表示模型中的网络参数是否需要进行更新。MindSpore中的`trainable_params`方法会屏蔽掉`Parameter`中`requires_grad`为False的属性,默认在网络模型训练的时候自动更新所有`Parameter`中的参数。\n",
+ "\n",
+ "在构建优化器时,可使用`trainable_params`方法来指定需要优化和更新的网络参数。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[Parameter (name=param, shape=(1,), dtype=Float32, requires_grad=True), Parameter (name=conv.weight, shape=(6, 1, 5, 5), dtype=Float32, requires_grad=True)]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import nn, Tensor, Parameter\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.matmul = ops.MatMul()\n",
+ " self.conv = nn.Conv2d(1, 6, 5, pad_mode=\"valid\")\n",
+ " self.param = Parameter(Tensor(np.array([1.0], np.float32)))\n",
+ "\n",
+ " def construct(self, x):\n",
+ " x = self.conv(x)\n",
+ " x = x * self.param\n",
+ " out = self.matmul(x, x)\n",
+ " return out\n",
+ "\n",
+ "net = Net()\n",
+ "\n",
+ "# 配置优化器需要更新的参数\n",
+ "optim = nn.Adam(params=net.trainable_params())\n",
+ "print(net.trainable_params())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- 自定义筛选参数\n",
+ "\n",
+ "用户也可以设定筛选条件,在使用`get_parameters`获取到网络全部参数后,通过限定参数名字等方法,自定义`filter`来决定哪些参数需要更新。\n",
+ "\n",
+ "例如下面的例子,训练过程中将只对非卷积参数进行更新。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[Parameter (name=param, shape=(1,), dtype=Float32, requires_grad=True)]\n"
+ ]
+ }
+ ],
+ "source": [
+ "params_all = net.get_parameters()\n",
+ "no_conv_params = list(filter(lambda x: 'conv' not in x.name, params_all))\n",
+ "\n",
+ "optim = nn.Adam(params=no_conv_params)\n",
+ "print(no_conv_params)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 学习率\n",
+ "\n",
+ "学习率作为机器学习及深度学习中常见的超参,对目标函数能否收敛到局部最小值及何时收敛到最小值有重要影响。学习率过大容易导致目标函数波动较大,难以收敛到最优值,太小则会导致收敛过程耗时过长。除了设置固定学习率,MindSpore还支持设置动态学习率,这些方法在深度学习网络中能明显提升收敛效率。\n",
+ "\n",
+ "#### 固定学习率\n",
+ "\n",
+ "使用固定学习率时,优化器传入的`learning_rate`为浮点类型或标量Tensor。\n",
+ "\n",
+ "以`nn.Momentum`为例,固定学习率为0.01,示例如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# 设置学习率为0.01\n",
+ "optim = nn.Momentum(params=net.trainable_params(), learning_rate=0.01, momentum=0.9)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "#### 动态学习率\n",
+ "\n",
+ "`mindspore.nn`提供了动态学习率的模块,分为Dynamic LR函数和LearningRateSchedule类。其中Dynamic LR函数会预先生成长度为`total_step`的学习率列表,将列表传入优化器中使用,训练过程中,第i步使用第i个学习率的值作为当前step的学习率,其中`total_step`的设置值不能小于训练的总步数;LearningRateSchedule类将实例传递给优化器,优化器根据当前step计算得到当前的学习率。\n",
+ "\n",
+ "- Dynamic LR函数\n",
+ "\n",
+ "[Dynamic LR](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#dynamic-lr)函数目前有基于余弦衰减函数计算学习率(`nn.cosine_decay_lr`)、基于指数衰减函数计算学习率(`nn.exponential_decay_lr`)、基于逆时衰减函数计算学习率(`nn.inverse_decay_lr`)、基于自然指数衰减函数计算学习率(`nn.natural_exp_decay_lr`)、获取分段常量学习率(`nn.piecewise_constant_lr`)、基于多项式衰减函数计算学习率(`nn.polynomial_decay_lr`)和预热学习率(`nn.warmup_lr`)。\n",
+ "\n",
+ "如下示例以分段常量学习率`nn.piecewise_constant_lr`为例:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[0.1, 0.05, 0.05, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01]\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore import nn\n",
+ "\n",
+ "milestone = [1, 3, 10]\n",
+ "learning_rates = [0.1, 0.05, 0.01]\n",
+ "lr = nn.piecewise_constant_lr(milestone, learning_rates)\n",
+ "# 打印学习率\n",
+ "print(lr)\n",
+ "\n",
+ "net = Net()\n",
+ "# 优化器设置待优化的网络参数和分段常量学习率\n",
+ "optim = nn.SGD(net.trainable_params(), learning_rate=lr)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- LearningRateSchedule类\n",
+ "\n",
+ "[LearningRateSchedule类](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#learningrateschedule)目前有基于余弦衰减函数计算学习率(`nn.CosineDecayLR`)、基于指数衰减函数计算学习率(`nn.ExponentialDecayLR`)、基于逆时衰减函数计算学习率(`nn.InverseDecayLR`)、基于自然指数衰减函数计算学习率(`nn.NaturalExpDecayLR`)、基于多项式衰减函数计算学习率(`nn.PolynomialDecayLR`)和预热学习率(`nn.WarmUpLR`)。\n",
+ "\n",
+ "如下示例基于指数衰减函数计算学习率`nn.ExponentialDecayLR`为例:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "step1, lr:0.1\n",
+ "step2, lr:0.097400375\n",
+ "step3, lr:0.094868325\n",
+ "step4, lr:0.09240211\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore import dtype\n",
+ "from mindspore import Tensor\n",
+ "\n",
+ "learning_rate = 0.1 # 学习率的初始值\n",
+ "decay_rate = 0.9 # 衰减率\n",
+ "decay_steps = 4 # 衰减的step数\n",
+ "step_per_epoch = 2\n",
+ "\n",
+ "exponential_decay_lr = nn.ExponentialDecayLR(learning_rate, decay_rate, decay_steps)\n",
+ "\n",
+ "for i in range(decay_steps):\n",
+ " step = Tensor(i, dtype.int32)\n",
+ " result = exponential_decay_lr(step)\n",
+ " print(f\"step{i+1}, lr:{result}\")\n",
+ "\n",
+ "net = Net()\n",
+ "\n",
+ "# 优化器设置学习率为基于指数衰减函数计算学习率\n",
+ "optim = nn.Momentum(net.trainable_params(), learning_rate=exponential_decay_lr, momentum=0.9)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 权重衰减\n",
+ "\n",
+ "权重衰减(weight decay),通常也被称为L2正则化,是一种减少深度学习神经网络模型过拟合的方法。\n",
+ "\n",
+ "一般情况下,`weight_decay`取值范围为$[0, 1)$,其默认值为0.0,此时不使用权重衰减策略。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "net = Net()\n",
+ "optimizer = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.9, weight_decay=0.9)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "2185b86c",
+ "metadata": {},
+ "source": [
+ "此外,MindSpore还支持动态weight decay。此时weight_decay是用户自定义的一个Cell,称之为weight_decay_schedule。在训练过程中,优化器调内部会用该Cell的实例,传入global_step计算当前step的weight_decay值。其中global_step是内部维护的变量,每训练一个step,global_step会自加1。注意,自定义的weight_decay_schedule的construct仅接收一个输入。如下是weight_decay在训练过程中进行指数衰减的一个示例。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "net = Net()\n",
+ "\n",
+ "# 指数衰减的weight decay\n",
+ "from mindspore.nn import Cell\n",
+ "from mindspore import ops\n",
+ "import mindspore.common.dtype as mstype\n",
+ "class ExponentialWeightDecay(Cell):\n",
+ " def __init__(self, weight_decay, decay_rate, decay_steps):\n",
+ " super(ExponentialWeightDecay, self).__init__()\n",
+ " self.weight_decay = weight_decay\n",
+ " self.decay_rate = decay_rate\n",
+ " self.decay_steps = decay_steps\n",
+ " self.pow = ops.Pow()\n",
+ " self.cast = ops.Cast()\n",
+ "# construct只能有一个输入,训练过程中,会自动传入global step进行计算\n",
+ " def construct(self, global_step):\n",
+ " p = self.cast(global_step, mstype.float32) / self.decay_steps\n",
+ " return self.learning_rate * self.pow(self.decay_rate, p)\n",
+ "\n",
+ "weight_decay_schedule = ExponentialWeightDecay(weight_decay=0.0001, decay_rate=0.1, decay_steps=10000)\n",
+ "optimizer = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.9, weight_decay=weight_decay_schedule)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 超参分组\n",
+ "\n",
+ "优化器也支持为不同参数单独设置选项,此时不直接传入变量,而是传入一个字典的列表,每个字典定义一个参数组别的设置值,key可以为`params`、`lr`、`weight_decay`和`grad_centralizaiton`,value为对应的设定值。\n",
+ "\n",
+ "其中`params`必须配置,其余参数可以选择配置,未配置的参数项,将采用定义优化器时设置的参数值。分组时,学习率可以使用固定学习率,也可以使用动态学习率。`weight_decay`可以使用固定值。\n",
+ "\n",
+ "如下示例分别对卷积参数和非卷积参数设置不同的学习率和权重衰减参数。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "net = Net()\n",
+ "\n",
+ "# 卷积参数\n",
+ "conv_params = list(filter(lambda x: 'conv' in x.name, net.trainable_params()))\n",
+ "# 非卷积参数\n",
+ "no_conv_params = list(filter(lambda x: 'conv' not in x.name, net.trainable_params()))\n",
+ "\n",
+ "# 固定学习率\n",
+ "fix_lr = 0.01\n",
+ "\n",
+ "# 基于多项式衰减函数计算学习率\n",
+ "polynomial_decay_lr = nn.PolynomialDecayLR(learning_rate=0.1, # 学习率初始值\n",
+ " end_learning_rate=0.01, # 学习率最终值\n",
+ " decay_steps=4, # 衰减的step数\n",
+ " power=0.5) # 多项式幂\n",
+ "\n",
+ "# 卷积参数使用固定学习率0.001,权重衰减为0.01\n",
+ "# 非卷积参数使用动态学习率,权重衰减为0.0\n",
+ "group_params = [{'params': conv_params, 'weight_decay': 0.01, 'lr': fix_lr},\n",
+ " {'params': no_conv_params, 'lr': polynomial_decay_lr}]\n",
+ "\n",
+ "optim = nn.Momentum(group_params, learning_rate=0.1, momentum=0.9, weight_decay=0.0)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> 当前MindSpore除个别优化器外(例如AdaFactor,FTRL),均支持对学习率进行分组,详情参考[优化器API](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id13)。\n",
+ "\n",
+ "## 自定义优化器\n",
+ "\n",
+ "除使用MindSpore提供的优化器外,用户可以自定义优化器。\n",
+ "\n",
+ "自定义优化器时需要继承优化器基类[nn.Optimizer](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.Optimizer.html#mindspore.nn.Optimizer),并重写`__init__`方法和`construct`方法实现参数的更新。\n",
+ "\n",
+ "如下示例实现自定义优化器Momentum(带有动量的SGD算法):\n",
+ "\n",
+ "$$ v_{t+1} = v_t×u+grad $$\n",
+ "\n",
+ "$$p_{t+1} = p_t - lr*v_{t+1}$$\n",
+ "\n",
+ "其中,$grad$ 、$lr$ 、$p$ 、$v$ 和 $u$ 分别表示梯度、学习率、权重参数、动量参数(Momentum)和初始速度。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore import Tensor, Parameter\n",
+ "from mindspore import nn, ops\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "class Momentum(nn.Optimizer):\n",
+ " \"\"\"定义优化器\"\"\"\n",
+ " def __init__(self, params, learning_rate, momentum=0.9):\n",
+ " super(Momentum, self).__init__(learning_rate, params)\n",
+ " self.momentum = Parameter(Tensor(momentum, mstype.float32), name=\"momentum\")\n",
+ " self.moments = self.parameters.clone(prefix=\"moments\", init=\"zeros\")\n",
+ " self.assign = ops.Assign()\n",
+ "\n",
+ " def construct(self, gradients):\n",
+ " \"\"\"construct输入为梯度,在训练中自动传入梯度gradients\"\"\"\n",
+ " lr = self.get_lr()\n",
+ " params = self.parameters # 待更新的权重参数\n",
+ " for i in range(len(params)):\n",
+ " # 更新moments值\n",
+ " self.assign(self.moments[i], self.moments[i] * self.momentum + gradients[i])\n",
+ " update = params[i] - self.moments[i] * lr #带有动量的SGD算法\n",
+ " self.assign(params[i], update)\n",
+ " return params\n",
+ "\n",
+ "net = Net()\n",
+ "# 设置优化器待优化的参数和学习率为0.01\n",
+ "opt = Momentum(net.trainable_params(), 0.01)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`mindSpore.ops`也封装了优化器算子供用户自行定义优化器,如`ops.ApplyCenteredRMSProp`、 `ops.ApplyMomentum`和`ops.ApplyRMSProp`等。如下示例使用`ApplyMomentum`算子自定义优化器Momentum:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Momentum(nn.Optimizer):\n",
+ " \"\"\"定义优化器\"\"\"\n",
+ " def __init__(self, params, learning_rate, momentum=0.9):\n",
+ " super(Momentum, self).__init__(learning_rate, params)\n",
+ " self.moments = self.parameters.clone(prefix=\"moments\", init=\"zeros\")\n",
+ " self.momentum = momentum\n",
+ " self.opt = ops.ApplyMomentum()\n",
+ "\n",
+ " def construct(self, gradients):\n",
+ " # 待更新的权重参数\n",
+ " params = self.parameters\n",
+ " success = None\n",
+ " for param, mom, grad in zip(params, self.moments, gradients):\n",
+ " success = self.opt(param, mom, self.learning_rate, grad, self.momentum)\n",
+ " return success\n",
+ "\n",
+ "net = Net()\n",
+ "# 设置优化器待优化的参数和学习率为0.01\n",
+ "opt = Momentum(net.trainable_params(), 0.01)"
+ ]
+ }
+ ],
+ "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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/tutorials/source_zh_cn/advanced/network/parameter.ipynb b/tutorials/source_zh_cn/advanced/network/parameter.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..bf545d4aa1b4d072fcde4f6c16de26c3a3ec4c81
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/network/parameter.ipynb
@@ -0,0 +1,533 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 网络参数\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_parameter.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/network/mindspore_parameter.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/network/parameter.ipynb)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "MindSpore提供了关于变量、网络相关参数的初始化模块,用户可以通过封装算子来调用字符串、Initializer子类或自定义Tensor等方式完成对网络参数进行初始化。\n",
+ "\n",
+ "下面图中蓝色表示具体的执行算子,绿色的表示张量Tensor,张量作为神经网络模型中的数据在网络中不断流动,主要包括网络模型的数据输入,算子的输入输出数据等;红色的为变量Parameter,作为网络模型或者模型中算子的属性,及其反向图中产生的中间变量和临时变量。\n",
+ "\n",
+ "\n",
+ "\n",
+ "本章主要介绍数据类型`dtype`、变量`Parameter`、变量元组`ParameterTuple`、网络的初始化方法和网络参数更新。\n",
+ "\n",
+ "## 数据类型 dtype\n",
+ "\n",
+ "MindSpore张量支持不同的数据类型`dtype`,包含int8、int16、int32、int64、uint8、uint16、uint32、uint64、float16、float32、float64、bool_,与NumPy的数据类型一一对应。详细的数据类型支持情况请参考:[mindspore.dtype](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore/mindspore.dtype.html#mindspore.dtype)。\n",
+ "\n",
+ "在MindSpore的运算处理流程中,Python中的int数会被转换为定义的int64类型,float数会被转换为定义的float32类型。\n",
+ "\n",
+ "以下代码,打印MindSpore的数据类型int32。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Int32\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "data_type = mstype.int32\n",
+ "print(data_type)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 数据类型转换接口\n",
+ "\n",
+ "MindSpore提供了以下几个接口,实现与NumPy数据类型和Python内置的数据类型间的转换。\n",
+ "\n",
+ "- `dtype_to_nptype`:将MindSpore的数据类型转换为NumPy对应的数据类型。\n",
+ "- `dtype_to_pytype`:将MindSpore的数据类型转换为Python对应的内置数据类型。\n",
+ "- `pytype_to_dtype`:将Python内置的数据类型转换为MindSpore对应的数据类型。\n",
+ "\n",
+ "以下代码实现了不同数据类型间的转换,并打印转换后的类型。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "Int64\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "np_type = mstype.dtype_to_nptype(mstype.int32)\n",
+ "ms_type = mstype.pytype_to_dtype(int)\n",
+ "py_type = mstype.dtype_to_pytype(mstype.float64)\n",
+ "\n",
+ "print(np_type)\n",
+ "print(ms_type)\n",
+ "print(py_type)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 变量 Parameter\n",
+ "\n",
+ "MindSpore的变量([Parameter](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore/mindspore.Parameter.html#mindspore.Parameter))表示在网络训练时,需要被更新的参数。例如,在前行计算的时候最常见的`nn.conv`算子的变量有权重`weight`和偏置`bias`;在构建反向图和反向传播计算的时候,会产生很多中间变量,用于暂存一阶梯度信息、中间输出值等。\n",
+ "\n",
+ "### 变量初始化\n",
+ "\n",
+ "变量`Parameter`的初始化方法有很多种,可以接收`Tensor`、`Initializer`等不同的数据类型。\n",
+ "\n",
+ "- `default_input`:为输入数据,支持传入`Tensor`、`Initializer`、`int`和`float`四种数据类型;\n",
+ "- `name`:可设置变量的名称,用于在网络中区别于其他变量;\n",
+ "- `requires_grad`:表示在网络训练过程,是否需要计算参数梯度,如果不需要计算参数梯度,将`requires_grad`设置为`False`。\n",
+ "\n",
+ "下面的示例代码中,使用`int`或`float`数据类型直接创建Parameter:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "Parameter (name=x, shape=(), dtype=Float32, requires_grad=True) value: 2.0\n",
+ "Parameter (name=y, shape=(), dtype=Float32, requires_grad=True) value: 5.0\n",
+ "Parameter (name=z, shape=(), dtype=Int32, requires_grad=False) value: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore import Parameter\n",
+ "\n",
+ "x = Parameter(default_input=2.0, name='x')\n",
+ "y = Parameter(default_input=5.0, name='y')\n",
+ "z = Parameter(default_input=5, name='z', requires_grad=False)\n",
+ "\n",
+ "print(type(x))\n",
+ "print(x, \"value:\", x.asnumpy())\n",
+ "print(y, \"value:\", y.asnumpy())\n",
+ "print(z, \"value:\", z.asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "下面示例代码中,使用MindSpore的张量`Tensor`创建Parameter:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Parameter (name=tensor, shape=(2, 3), dtype=Int64, requires_grad=True)\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor\n",
+ "\n",
+ "my_tensor = Tensor(np.arange(2 * 3).reshape((2, 3)))\n",
+ "x = Parameter(default_input=my_tensor, name=\"tensor\")\n",
+ "\n",
+ "print(x)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "下面示例代码中,使用`Initializer`创建Parameter:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Parameter (name=x, shape=(1, 2, 3), dtype=Float32, requires_grad=True)\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore.common.initializer import initializer as init\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "x = Parameter(default_input=init('ones', [1, 2, 3], mstype.float32), name='x')\n",
+ "print(x)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 属性\n",
+ "\n",
+ "变量`Parameter`的默认属性有变量名称`name`、形状`shape`、数据类型`dtype`和是否需要进行求导`requires_grad`。\n",
+ "\n",
+ "下例通过`Tensor`初始化一个变量`Parameter`,并获取变量`Parameter`的相关属性。示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "x: Parameter (name=x, shape=(2, 3), dtype=Int64, requires_grad=True)\n",
+ "x.data: Parameter (name=x, shape=(2, 3), dtype=Int64, requires_grad=True)\n"
+ ]
+ }
+ ],
+ "source": [
+ "my_tensor = Tensor(np.arange(2 * 3).reshape((2, 3)))\n",
+ "x = Parameter(default_input=my_tensor, name=\"x\")\n",
+ "\n",
+ "print(\"x: \", x)\n",
+ "print(\"x.data: \", x.data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 变量操作\n",
+ "\n",
+ "1. `clone`:克隆变量张量`Parameter`,克隆完成后可以给新的变量`Parameter`指定新的名称。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Parameter (name=Parameter, shape=(1, 2, 3), dtype=Float32, requires_grad=True)\n",
+ "Parameter (name=x_clone, shape=(1, 2, 3), dtype=Float32, requires_grad=True)\n"
+ ]
+ }
+ ],
+ "source": [
+ "x = Parameter(default_input=init('ones', [1, 2, 3], mstype.float32))\n",
+ "x_clone = x.clone()\n",
+ "x_clone.name = \"x_clone\"\n",
+ "\n",
+ "print(x)\n",
+ "print(x_clone)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. `set_data`:修改变量`Parameter`的数据或形状`shape`。\n",
+ "\n",
+ "其中,`set_data`方法有`data`和`slice_shape`两种入参。`data`表示变量`Parameter`新传入的数据;`slice_shape`表示是否修改变量`Parameter`的形状`shape`,默认为False。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Parameter (name=x, shape=(1, 2), dtype=Float32, requires_grad=True) [[1. 1.]]\n",
+ "Parameter (name=x, shape=(1, 2), dtype=Float32, requires_grad=True) [[0. 0.]]\n",
+ "Parameter (name=x, shape=(1, 4), dtype=Float32, requires_grad=True) [[1. 1. 1. 1.]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "x = Parameter(Tensor(np.ones((1, 2)), mstype.float32), name=\"x\", requires_grad=True)\n",
+ "print(x, x.asnumpy())\n",
+ "\n",
+ "y = x.set_data(Tensor(np.zeros((1, 2)), mstype.float32))\n",
+ "print(y, y.asnumpy())\n",
+ "\n",
+ "z = x.set_data(Tensor(np.ones((1, 4)), mstype.float32), slice_shape=True)\n",
+ "print(z, z.asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "3. `init_data`:并行场景下存在参数的形状发生变化的情况,用户可以调用`Parameter`的`init_data`方法得到原始数据。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Parameter (name=x, shape=(1, 2), dtype=Float32, requires_grad=True) [[1. 1.]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "x = Parameter(Tensor(np.ones((1, 2)), mstype.float32), name=\"x\", requires_grad=True)\n",
+ "\n",
+ "print(x.init_data(), x.init_data().asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 变量元组 Parameter Tuple\n",
+ "\n",
+ "变量元组[ParameterTuple](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore/mindspore.ParameterTuple.html#mindspore.ParameterTuple),用于保存多个`Parameter`,继承于元组`tuple`,提供克隆功能。\n",
+ "\n",
+ "如下示例提供`ParameterTuple`创建方法:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "(Parameter (name=x, shape=(2, 3), dtype=Int64, requires_grad=True), Parameter (name=y, shape=(1, 2, 3), dtype=Float32, requires_grad=True), Parameter (name=z, shape=(), dtype=Float32, requires_grad=True))\n",
+ "(Parameter (name=params_copy.x, shape=(2, 3), dtype=Int64, requires_grad=True), Parameter (name=params_copy.y, shape=(1, 2, 3), dtype=Float32, requires_grad=True), Parameter (name=params_copy.z, shape=(), dtype=Float32, requires_grad=True))\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, Parameter, ParameterTuple\n",
+ "from mindspore import dtype as mstype\n",
+ "from mindspore.common.initializer import initializer\n",
+ "\n",
+ "# 创建\n",
+ "x = Parameter(default_input=Tensor(np.arange(2 * 3).reshape((2, 3))), name=\"x\")\n",
+ "y = Parameter(default_input=initializer('ones', [1, 2, 3], mstype.float32), name='y')\n",
+ "z = Parameter(default_input=2.0, name='z')\n",
+ "params = ParameterTuple((x, y, z))\n",
+ "\n",
+ "# 从params克隆并修改名称为\"params_copy\"\n",
+ "params_copy = params.clone(\"params_copy\")\n",
+ "\n",
+ "print(params)\n",
+ "print(params_copy)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 网络参数初始化\n",
+ "\n",
+ "MindSpore提供了多种网络参数初始化的方式,并在部分算子中封装了参数初始化的功能。本节以`Conv2d`算子为例,分别介绍使用`Initializer`子类,字符串和自定义`Tensor`等方式对网络中的参数进行初始化。\n",
+ "\n",
+ "### Initializer初始化\n",
+ "\n",
+ "使用`Initializer`对网络参数进行初始化,示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import set_seed\n",
+ "from mindspore.common import initializer as init\n",
+ "\n",
+ "set_seed(1)\n",
+ "\n",
+ "input_data = Tensor(np.ones([1, 3, 16, 50], dtype=np.float32))\n",
+ "# 卷积层,输入通道为3,输出通道为64,卷积核大小为3*3,权重参数使用正态分布生成的随机数\n",
+ "net = nn.Conv2d(3, 64, 3, weight_init=init.Normal(0.2))\n",
+ "# 网络输出\n",
+ "output = net(input_data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 字符串初始化\n",
+ "\n",
+ "使用字符串对网络参数进行初始化,字符串的内容需要与`Initializer`的名称保持一致(字母不区分大小写),使用字符串方式进行初始化将使用`Initializer`类中的默认参数,例如使用字符串`Normal`等同于使用`Initializer`的`Normal()`,示例如下:\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import set_seed\n",
+ "\n",
+ "set_seed(1)\n",
+ "\n",
+ "input_data = Tensor(np.ones([1, 3, 16, 50], dtype=np.float32))\n",
+ "net = nn.Conv2d(3, 64, 3, weight_init='Normal')\n",
+ "output = net(input_data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 张量初始化\n",
+ "\n",
+ "用户也可以通过自定义`Tensor`的方式,来对网络模型中算子的参数进行初始化,示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "init_data = Tensor(np.ones([64, 3, 3, 3]), dtype=mstype.float32)\n",
+ "input_data = Tensor(np.ones([1, 3, 16, 50], dtype=np.float32))\n",
+ "\n",
+ "net = nn.Conv2d(3, 64, 3, weight_init=init_data)\n",
+ "output = net(input_data)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 网络参数更新\n",
+ "\n",
+ "MindSpore提供了网络参数更新功能,使用`nn.ParameterUpdate`可对网络参数进行更新,其输入的参数类型必须为张量,且张量`shape`需要与原网络参数`shape`保持一致。\n",
+ "\n",
+ "更新网络的权重参数示例如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Parameter:\n",
+ " [[-0.0164615 -0.01204428 -0.00813806]\n",
+ " [-0.00270927 -0.0113328 -0.01384139]\n",
+ " [ 0.00849093 0.00351116 0.00989969]\n",
+ " [ 0.00233028 0.00649209 -0.0021333 ]]\n",
+ "Parameter update:\n",
+ " [[ 0. 1. 2.]\n",
+ " [ 3. 4. 5.]\n",
+ " [ 6. 7. 8.]\n",
+ " [ 9. 10. 11.]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import dtype as mstype\n",
+ "from mindspore import nn, Tensor\n",
+ "\n",
+ "# 构建网络\n",
+ "network = nn.Dense(3, 4)\n",
+ "\n",
+ "# 获取网络的权重参数\n",
+ "param = network.parameters_dict()['weight']\n",
+ "print(\"Parameter:\\n\", param.asnumpy())\n",
+ "\n",
+ "# 更新权重参数\n",
+ "update = nn.ParameterUpdate(param)\n",
+ "weight = Tensor(np.arange(12).reshape((4, 3)), mstype.float32)\n",
+ "output = update(weight)\n",
+ "print(\"Parameter update:\\n\", 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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph.rst b/tutorials/source_zh_cn/advanced/pynative_graph.rst
new file mode 100644
index 0000000000000000000000000000000000000000..9bc4272ac3a3b7e185f739f2554a524fd782d2ce
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/pynative_graph.rst
@@ -0,0 +1,9 @@
+动态图与静态图
+===================
+
+.. toctree::
+ :maxdepth: 1
+
+ pynative_graph/mode
+ pynative_graph/combine
+ pynative_graph/pynative
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/combine.ipynb b/tutorials/source_zh_cn/advanced/pynative_graph/combine.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..ef377ea0560580e59960e5b90b6c45824da52880
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/pynative_graph/combine.ipynb
@@ -0,0 +1,471 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 动静结合\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/pynative_graph/mindspore_combine.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/pynative_graph/mindspore_combine.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/pynative_graph/combine.ipynb)\n",
+ "\n",
+ "当前在业界支持动态图和静态图两种模式,动态图通过解释执行,具有动态语法亲和性,表达灵活;静态图使用JIT(just in time)编译优化执行,偏静态语法,在语法上有较多限制。动态图和静态图的编译流程不一致,导致语法约束也不一致。\n",
+ "\n",
+ "MindSpore针对动态图和静态图模式,首先统一API表达,在两种模式下使用相同的API;其次统一动态图和静态图的底层微分机制。\n",
+ "\n",
+ "\n",
+ "\n",
+ "## 实现原理\n",
+ "\n",
+ "MindSpore支持使用`ms_function`装饰器来修饰需要用静态图执行的对象,从而实现动静结合的目的。下面我们通过一个简单的动静结合的示例来介绍其实现原理。示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "init x:\n",
+ " [[1. 1. 1.]\n",
+ " [1. 1. 1.]\n",
+ " [1. 1. 1.]]\n",
+ "\n",
+ "x:\n",
+ " [[8. 8. 8.]\n",
+ " [8. 8. 8.]\n",
+ " [8. 8. 8.]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import context, Tensor, ms_function\n",
+ "\n",
+ "class Add(nn.Cell):\n",
+ " \"\"\"自定义类实现x自身相加\"\"\"\n",
+ " def construct(self, x):\n",
+ " x = x + x\n",
+ " return x\n",
+ "\n",
+ "class Mul(nn.Cell):\n",
+ " \"\"\"自定义类实现x自身相乘\"\"\"\n",
+ " @ms_function # 使用ms_function修饰,此函数以静态图方式执行\n",
+ " def construct(self, x):\n",
+ " x = x * x\n",
+ " return x\n",
+ "\n",
+ "class Test(nn.Cell):\n",
+ " \"\"\"自定义类实现x先Add(x),后Mul(x),再Add(x)\"\"\"\n",
+ " def __init__(self):\n",
+ " super(Test, self).__init__()\n",
+ " self.add = Add()\n",
+ " self.mul = Mul()\n",
+ "\n",
+ " def construct(self, x):\n",
+ " x = self.add(x)\n",
+ " x = self.mul(x)\n",
+ " x = self.add(x)\n",
+ " return x\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "x = Tensor(np.ones([3, 3], dtype=np.float32))\n",
+ "print(\"init x:\\n\", x)\n",
+ "net = Test()\n",
+ "x = net(x)\n",
+ "print(\"\\nx:\\n\", x)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,经过Test运算后,x最终值为每个元素都是8的3\\*3矩阵。该用例按照执行序,编译的方式如下图所示:\n",
+ "\n",
+ "\n",
+ "\n",
+ "被`ms_function`修饰的函数将会按照静态图的方式进行编译和执行。如果网络涉及到反向求导,被`ms_function`修饰的部分也将以整图的形式来生成反向图,并与前后单个算子的反向图连成一个整体后被下发执行。其中,缓存的策略与静态图的缓存策略一致,相同的函数对象在输入Shape和Type信息一致时,编译的图结构将会被缓存。\n",
+ "\n",
+ "## `ms_function`装饰器\n",
+ "\n",
+ "### 使用方式\n",
+ "\n",
+ "MindSpore支持在动态图下使用静态编译的方式来进行混合执行,通过使用`ms_function`装饰符来修饰需要用静态图来执行的函数对象,即可实现动态图和静态图的混合执行。\n",
+ "\n",
+ "#### 1. 修饰独立函数\n",
+ "\n",
+ "使用`ms_function`装饰器时,可以对独立定义的函数进行修饰,使其在Graph模式下运行,示例如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[5. 7. 9.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import context, Tensor, ms_function\n",
+ "\n",
+ "# 设置运行模式为动态图模式\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "# 使用装饰器,指定静态图模式下执行\n",
+ "@ms_function\n",
+ "def add_func(x, y):\n",
+ " return ops.add(x, y)\n",
+ "\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "y = Tensor(np.array([4.0, 5.0, 6.0]).astype(np.float32))\n",
+ "\n",
+ "out = add_func(x, y)\n",
+ "print(out)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "在上面的示例代码中,虽然一开始设置了运行模式为动态图模式,但是由于使用了`ms_function`装饰器对函数`add_func(x, y)`进行了修饰,所以函数`add_func(x, y)`仍然是以静态图模式运行。\n",
+ "\n",
+ "#### 2. 修饰Cell的成员函数\n",
+ "\n",
+ "使用`ms_function`装饰器时,可以对`Cell`的成员函数进行修饰,示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Infer result:\n",
+ " [5. 7. 9.]\n",
+ "Gradient result:\n",
+ "Grad x Tensor1:\n",
+ " [1. 1. 1.]\n",
+ "Grad y Tensor2:\n",
+ " [1. 1. 1.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import context, Tensor, ms_function\n",
+ "\n",
+ "# 设置运行模式为动态图模式\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "class Add(nn.Cell):\n",
+ "\n",
+ " @ms_function # 使用装饰器,指定静态图模式下执行\n",
+ " def construct(self, x, y):\n",
+ " out = x + y\n",
+ " return out\n",
+ "\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "y = Tensor(np.array([4.0, 5.0, 6.0]).astype(np.float32))\n",
+ "\n",
+ "grad_ops = ops.GradOperation(get_all=True) # 定义求导操作\n",
+ "net = Add()\n",
+ "grad_out = grad_ops(net)(x, y)\n",
+ "\n",
+ "print(\"Infer result:\\n\", net(x, y))\n",
+ "\n",
+ "print(\"Gradient result:\")\n",
+ "print(\"Grad x Tensor1:\\n\", grad_out[0]) # 对x求导\n",
+ "print(\"Grad y Tensor2:\\n\", grad_out[1]) # 对y求导"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,x与y相加的结果为\\[5, 7, 9\\], 对x求导的结果和对y求导的结果相同,都为\\[1, 1, 1\\]。\n",
+ "\n",
+ "### 注意事项\n",
+ "\n",
+ "在使用ms_function来修饰函数,加速执行效率时,请注意以下几点:\n",
+ "\n",
+ "1. ms_function修饰的函数须在静态图编译支持的语法范围内,包括但不限于数据类型等。\n",
+ "\n",
+ "2. ms_function修饰的函数所支持的控制流语法,与静态图保持一致。其中,仅对固定循环次数或者分支条件的控制流结构具有加速效果。\n",
+ "\n",
+ "3. 在PyNative模式下使用ms_function功能时,非ms_function修饰的部分支持断点调试;被ms_function修饰的部分由于是以静态图的方式编译,不支持断点调试。\n",
+ "\n",
+ "4. 由于ms_function修饰的函数将按照静态图的方式编译执行,因此ms_function不支持修饰的函数中含有Hook算子,也不支持修饰自定义Bprop函数。\n",
+ "\n",
+ "5. ms_function修饰的函数会受到静态图函数副作用的影响。函数副作用指:当调用函数时,除了函数返回值之外,还对主调用函数产生的附加影响,例如修改全局变量(函数外的变量),修改函数的参数等。\n",
+ "\n",
+ "场景1:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "5\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import context, Tensor, ms_function\n",
+ "\n",
+ "# pylint: disable=W0612\n",
+ "\n",
+ "value = 5\n",
+ "\n",
+ "@ms_function\n",
+ "def func(x, y):\n",
+ " out = x + y\n",
+ " value = 1\n",
+ " return out\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "y = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "func(x, y)\n",
+ "print(value)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "该场景下,`value`是全局变量且在`func`函数中被修改。此时,如果用`ms_function`修饰`func`函数,全局变量`value`的值将不会被修改。原因是:**静态图编译时,会优化掉与返回值无关的语句**。\n",
+ "\n",
+ "场景2:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "out1: [6. 7. 8.]\n",
+ "out2: [6. 7. 8.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import context, Tensor, ms_function\n",
+ "\n",
+ "class Func(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Func, self).__init__()\n",
+ " self.value = 5\n",
+ "\n",
+ " @ms_function\n",
+ " def construct(self, x):\n",
+ " out = self.value + x\n",
+ " return out\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "func = Func()\n",
+ "print(\"out1:\", func(x))\n",
+ "func.value = 1\n",
+ "print(\"out2:\", func(x))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印可以看出,在修改了Func类的成员变量value的值为1之后,对成员函数construct的操作并无影响。这是因为在此场景下,用ms_function修饰了`Func`对象的`construct`成员函数,执行`Func`时将会以静态图的方式编译执行。由于静态图会缓存编译结果,第二次调用`Func`时,对`value`的修改不会生效。\n",
+ "\n",
+ "6. 加装了`ms_function`装饰器的函数中,如果包含不需要进行参数训练的算子(如`MatMul`、`Add`等算子),则这些算子可以在被装饰的函数中直接调用;如果被装饰的函数中包含了需要进行参数训练的算子(如`Conv2D`、`BatchNorm`等算子),则这些算子必须在被装饰的函数之外完成实例化操作。下面我们通过示例代码对这两种场景进行说明。\n",
+ "\n",
+ "场景1:在被装饰的函数中直接调用不需要进行参数训练的算子(示例中为`mindspore.ops.Add`)。示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "x: [1. 2. 3.] \n",
+ "y: [4. 5. 6.] \n",
+ "z: [5. 7. 9.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import context, Tensor\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import ms_function\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "add = ops.Add()\n",
+ "\n",
+ "@ms_function\n",
+ "def add_fn(x, y):\n",
+ " res = add(x, y)\n",
+ " return res\n",
+ "\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "y = Tensor(np.array([4.0, 5.0, 6.0]).astype(np.float32))\n",
+ "z = add_fn(x, y)\n",
+ "\n",
+ "print(\"x:\", x.asnumpy(), \"\\ny:\", y.asnumpy(), \"\\nz:\", z.asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "场景2:需要进行参数训练的算子(示例中为`mindspore.nn.Conv2d`),必须在被装饰的函数之外完成实例化操作,示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[[[ 0.00829158 -0.02994147]\n",
+ " [-0.09116832 -0.00181637]]\n",
+ "\n",
+ " [[-0.00519348 -0.02172063]\n",
+ " [-0.04015012 -0.02083161]]\n",
+ "\n",
+ " [[ 0.00608188 -0.01443425]\n",
+ " [-0.01468289 0.01200477]]\n",
+ "\n",
+ " [[ 0.00845292 0.00044869]\n",
+ " [-0.00361492 0.01993337]]]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import context, Tensor\n",
+ "from mindspore import ms_function\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE, device_target=\"GPU\")\n",
+ "\n",
+ "# 对函数conv_fn中的算子conv_obj完成实例化操作\n",
+ "conv_obj = nn.Conv2d(in_channels=3, out_channels=4, kernel_size=3, stride=2, padding=0)\n",
+ "conv_obj.init_parameters_data()\n",
+ "\n",
+ "@ms_function\n",
+ "def conv_fn(x):\n",
+ " res = conv_obj(x)\n",
+ " return res\n",
+ "\n",
+ "input_data = np.random.randn(1, 3, 3, 3).astype(np.float32)\n",
+ "z = conv_fn(Tensor(input_data))\n",
+ "print(z.asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## JIT Fallback\n",
+ "\n",
+ "MindSpore默认使用静态图模式,用户编写程序时需要遵循MindSpore[静态图语法支持](https://www.mindspore.cn/docs/note/zh-CN/master/static_graph_syntax_support.html),语法使用存在约束限制。虽然用户可以通过[set_context](https://mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.context.html#mindspore.context.set_context)来实现动静态图的一键式切换,由于两种模式的语法约束不同,因此存在一些动态图无法转到静态图的场景。对此,MindSpore推出了JIT fallback的特性,可以使得静态图支持尽量多的动态图语法,使得静态图提供接近动态图的语法使用体验,在MindSpore1.6版本先实现编译推导期的fallback。\n",
+ "\n",
+ "传统的JIT编译经常会通过profiling信息,对函数进行多态选择、value推导、分支调度等优化,同时设置guard条件,一旦guard条件发现情况有变,可以去JIT优化,回到原来未优化的函数进行解释执行。MindSpore的JIT Fallback特性,借鉴了传统JIT编译的fallback的思路,从静态图的角度出发考虑静态图和动态图的统一,其实现原理为:**在静态图编译的时候(一般JIT fallback是基于ast based的静态图),如果发现是编译器不支持的Python语法,可以把相关语句保留下来,生成解释节点,然后在后面的处理中,fallback到Python去执行相关的语句,从而实现相关语法的支持**。为了实现这一特性,Mindpore需要解决如下两个问题:\n",
+ "\n",
+ "1. 识别Python不支持的语法。\n",
+ "2. 确定解释节点的推导和执行时机。解释节点的推导和执行有两个时机:程序的编译阶段和程序的运行阶段,解释节点优先在程序的编译阶段推导和执行。\n",
+ "\n",
+ "当前JIT Fallback支持静态图模式的部分常量场景,包括在construct/ms_function中调用第三方库、创建及使用Tensor、调用Python的print打印等,更多JIT Fallback的说明和使用,请参考[JIT Fallback文档](https://www.mindspore.cn/docs/api/zh-CN/master/design/jit_fallback.html)。\n",
+ "\n",
+ "在下面的示例代码中,MindSpore静态图模式不支持在construct中调用NumPy第三方库和创建Tensor对象,因此用例中的`x = np.array([1, 2, 3])`和`y = Tensor(x)`将会通过JIT Fallback特性使用Python解释器进行解释执行,从而实现对这些语法的支持。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[1 2 3]\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import context, Tensor\n",
+ "\n",
+ "context.set_context(mode=context.GRAPH_MODE)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def construct(self):\n",
+ " x = np.array([1, 2, 3])\n",
+ " y = Tensor(x)\n",
+ " return y\n",
+ "\n",
+ "net = Net()\n",
+ "print(net())"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "MindSpore",
+ "language": "python",
+ "name": "python3"
+ },
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/images/forward_backward.graffle b/tutorials/source_zh_cn/advanced/pynative_graph/images/forward_backward.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..dab38dd61a239b6b3e4d11fd5541177c65b6dbdf
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/pynative_graph/images/forward_backward.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/images/forward_backward.png b/tutorials/source_zh_cn/advanced/pynative_graph/images/forward_backward.png
new file mode 100644
index 0000000000000000000000000000000000000000..6ef5194c370630599e1dc8b815a6c4735c5d0bb8
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/pynative_graph/images/forward_backward.png differ
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/images/framework1.png b/tutorials/source_zh_cn/advanced/pynative_graph/images/framework1.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ae42818042d20c898840dad65421be1dd33de01
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/pynative_graph/images/framework1.png differ
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/images/framework1.pptx b/tutorials/source_zh_cn/advanced/pynative_graph/images/framework1.pptx
new file mode 100644
index 0000000000000000000000000000000000000000..05edb2ee0576c10c13fa3354f35721f2710d8d3f
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/pynative_graph/images/framework1.pptx differ
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/images/framework2.graffle b/tutorials/source_zh_cn/advanced/pynative_graph/images/framework2.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..79daa81105ff5707172cbe68fe9a1d6eaf062b01
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/pynative_graph/images/framework2.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/images/framework2.png b/tutorials/source_zh_cn/advanced/pynative_graph/images/framework2.png
new file mode 100644
index 0000000000000000000000000000000000000000..a961c2f661836bd7c96b99d2ac25c12488819948
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/pynative_graph/images/framework2.png differ
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/images/ms_function.png b/tutorials/source_zh_cn/advanced/pynative_graph/images/ms_function.png
new file mode 100644
index 0000000000000000000000000000000000000000..e17b1edc0d8c04bf4d5be6d1c56d19cb7592d508
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/pynative_graph/images/ms_function.png differ
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/mode.ipynb b/tutorials/source_zh_cn/advanced/pynative_graph/mode.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..40f9b9e5c49901d53107697cebc4bee2bd37d339
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/pynative_graph/mode.ipynb
@@ -0,0 +1,419 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 动静态图\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/pynative_graph/mindspore_mode.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/pynative_graph/mindspore_mode.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/pynative_graph/mode.ipynb)\n",
+ "\n",
+ "目前主流的深度学习框架有静态图(Graph)和动态图(PyNative)两种执行模式。\n",
+ "\n",
+ "- 静态图模式下,程序在编译执行时,首先生成神经网络的图结构,然后再执行图中涉及的计算操作。因此,在静态图模式下,编译器可以通过使用图优化等技术来获得更好的执行性能,有助于规模部署和跨平台运行。\n",
+ "\n",
+ "- 动态图模式下,程序按照代码的编写顺序逐行执行,在执行正向过程中根据反向传播的原理,动态生成反向执行图。这种模式下,编译器将神经网络中的各个算子逐一下发到设备进行计算操作,方便用户编写和调试神经网络模型。\n",
+ "\n",
+ "## 动静态图介绍\n",
+ "\n",
+ "MindSpore提供了静态图和动态图统一的编码方式,大大增加了静态图和动态图的可兼容性,用户无需开发多套代码,仅变更一行代码便可切换静态图/动态图模式。静态图模式是MindSpore的默认模式,而动态图模式用于调试等用途。\n",
+ "\n",
+ "> 当运行模式从动态图切换到静态图时,请留意[静态图语法支持](https://www.mindspore.cn/docs/note/zh-CN/master/static_graph_syntax_support.html)。\n",
+ "\n",
+ "### 模式选择\n",
+ "\n",
+ "通过配置context参数可以控制程序运行的模式,动态图和静态图两种模式的区别主要有:\n",
+ "\n",
+ "- **适用场景**:静态图需要一开始就构建好网络结构,然后框架做整图优化和执行,比较适合网络固定没有变化,且需要高性能的场景。动态图逐行执行算子,支持执行单算子、普通函数和网络,以及单独求梯度的操作。\n",
+ "\n",
+ "- **网络执行**:静态图模式和动态图模式在执行相同的网络和算子时,精度效果一致。由于静态图模式运用了图优化、计算图整图下沉等技术,静态图模式执行网络的性能和效率更高,动态图模式更便于调试调优。\n",
+ "\n",
+ "- **代码调试**:在脚本开发和网络流程调试中,推荐使用动态图模式进行调试。在动态图模式下,可以方便地设置断点,获取网络执行的中间结果,也可以通过pdb的方式对网络进行调试。而静态图模式无法设置断点,只能先指定算子进行打印,然后在网络执行完成后查看输出结果。\n",
+ "\n",
+ "### 模式切换\n",
+ "\n",
+ "模式切换时,需要设置context中的运行模式。首先定义网络模型`MyNet`和后续代码片段用到的数据,用于后续的动静态图模式的切换和展示:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import Tensor, Parameter\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "class MyNet(nn.Cell):\n",
+ " \"\"\"自定义网络,实现两个张量的加法\"\"\"\n",
+ " def __init__(self):\n",
+ " super(MyNet, self).__init__()\n",
+ " self.add = ops.Add()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " return self.add(x, y)\n",
+ "\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "y = Tensor(np.array([4.0, 5.0, 6.0]).astype(np.float32))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "设置运行模式为静态图模式:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[5. 7. 9.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore import context\n",
+ "\n",
+ "context.set_context(mode=context.GRAPH_MODE)\n",
+ "\n",
+ "net = MyNet()\n",
+ "print(net(x, y))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "MindSpore处于静态图模式时,可以通过`mode=context.PYNATIVE_MODE`切换为动态图模式;同样,MindSpore处于动态图模式时,可以通过`mode=context.GRAPH_MODE`切换为静态图模式,请留意[静态图语法支持](https://www.mindspore.cn/docs/note/zh-CN/master/static_graph_syntax_support.html)。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[5. 7. 9.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "net = MyNet()\n",
+ "print(net(x, y))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 静态图\n",
+ "\n",
+ "在MindSpore中,静态图模式又被称为Graph模式,比较适合网络固定且需要高性能的场景,可以通过`context.set_context`接口中,参数`mode`入参为`context.GRAPH_MODE`来设置成静态图模式。\n",
+ "\n",
+ "在静态图模式下,基于图优化、计算图整图下沉等技术,编译器可以针对图进行全局的优化,因此在静态图模式下执行时可以获得较好的性能。但是,执行图是从源码转换而来,因此在静态图模式下不是所有的Python语法都能支持,会有一些特殊的约束,其支持情况的详细信息可参考[静态图语法支持](https://www.mindspore.cn/docs/note/zh-CN/master/static_graph_syntax_support.html)。\n",
+ "\n",
+ "### 静态图模式执行原理\n",
+ "\n",
+ "在静态图模式下,MindSpore通过源码转换的方式,将Python的源码转换成中间表达形式,也就是IR(Intermediate Representation),并在此基础上对IR图进行优化,最终在硬件设备上执行优化后的图。\n",
+ "\n",
+ "MindSpore使用的是一种基于图表示的函数式IR,称为MindIR。静态图模式就是基于MindIR进行编译优化,使用静态图模式时,需要使用[nn.Cell](https://mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell)类并且在`construct`函数中编写执行代码。\n",
+ "\n",
+ "### 静态图模式代码示例\n",
+ "\n",
+ "静态图模式的代码用例如下所示,神经网络模型实现 $f(x, y)=x*y$ 的计算操作:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T10:51:23.282871Z",
+ "start_time": "2022-01-04T10:51:21.743620Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[ 4. 10. 18.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 设置运行模式为静态图模式\n",
+ "context.set_context(mode=context.GRAPH_MODE)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " \"\"\"自定义网络,实现两个张量的乘法\"\"\"\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.mul = ops.Mul()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " \"\"\"定义执行代码\"\"\"\n",
+ " return self.mul(x, y)\n",
+ "\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "y = Tensor(np.array([4.0, 5.0, 6.0]).astype(np.float32))\n",
+ "\n",
+ "net = Net()\n",
+ "\n",
+ "print(net(x, y))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 动态图\n",
+ "\n",
+ "在MindSpore中,动态图模式又被称为PyNative模式,可以通过`context.set_context`接口中,参数`mode`入参为`context.PYNATIVE_MODE`来设置成动态图模式。\n",
+ "\n",
+ "在脚本开发和网络流程调试中,推荐使用动态图模式进行调试,其支持执行单算子、普通函数和网络、以及单独求梯度的操作。\n",
+ "\n",
+ "### 动态图模式执行原理\n",
+ "\n",
+ "在动态图模式下,用户可以使用完整的Python API,此外针对使用MindSpore提供的API时,框架会根据用户选择的不同硬件平台(Ascend/GPU/CPU)或环境信息,将算子API的操作在对应的硬件平台上执行,并返回相应的结果。\n",
+ "\n",
+ "框架整体的执行过程如下:\n",
+ "\n",
+ "\n",
+ "\n",
+ "通过前端的Python API,调用到框架层,最终到相应的硬件设备上进行计算。\n",
+ "\n",
+ "下面我们通过`ops.mul`算子,直接代替静态图模式下需要定义网络模型,实现 $f(x, y)=x*y$ 的计算操作:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T10:51:23.292278Z",
+ "start_time": "2022-01-04T10:51:23.284465Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[ 4. 10. 18.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 设置运行模式为动态图模式\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "y = Tensor(np.array([4.0, 5.0, 6.0]).astype(np.float32))\n",
+ "\n",
+ "output = ops.mul(x, y)\n",
+ "\n",
+ "print(output.asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "在上面的示例代码中,当调用到API接口`ops.mul(x, y)`时,会将MindSpore表达层Python API接口的调用,通过[Pybind11](https://pybind11.readthedocs.io/en/stable/basics.html)调用到MindSpore框架的C++层,转换成C++的接口调用。接着框架会根据MindSpore的安装环境信息,自动选择对应的硬件设备,在该硬件设备上执行add操作。\n",
+ "\n",
+ "从上述原理可以看到,PyNative模式下,Python脚本代码会根据Python的语法进行执行,而执行过程中涉及到MindSpore表达层的Python API,会根据用户的设置在不同的硬件上执行,从而进行性能加速。\n",
+ "\n",
+ "因此,在动态图模式下,用户可以随意使用Python的语法以及调试方法。\n",
+ "\n",
+ "### 动态图模式自动微分原理\n",
+ "\n",
+ "在动态图下,执行正向过程完全是按照Python的语法执行的,而反向传播过程是基于Tensor实现的。\n",
+ "\n",
+ "因此,我们在执行正向过程中,将所有应用于Tensor的操作记录下来,并针对每个计算操作求取其反向,然后将所有反向过程串联起来形成整体反向传播图,最终将反向图在设备上执行并计算出梯度。\n",
+ "\n",
+ "下面通过一段简单的示例代码说明动态图模式自动微分原理。对矩阵x乘上固定参数z,然后与y进行矩阵乘法:\n",
+ "\n",
+ "$$f(x, y)=(x * z) * y \\tag{1}$$\n",
+ "\n",
+ "代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T10:51:23.439867Z",
+ "start_time": "2022-01-04T10:51:23.293334Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[4.5099998 2.7 3.6000001]\n",
+ " [4.5099998 2.7 3.6000001]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 设置运行模式为动态图模式\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " \"\"\"自定义网络\"\"\"\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.matmul = ops.MatMul()\n",
+ " self.z = Parameter(Tensor(np.array([1.0], np.float32)), name='z')\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " x = x * self.z\n",
+ " x = self.matmul(x, y)\n",
+ " return x\n",
+ "\n",
+ "class GradNetWrtX(nn.Cell):\n",
+ " \"\"\"定义对x的求导\"\"\"\n",
+ " def __init__(self, net):\n",
+ " super(GradNetWrtX, self).__init__()\n",
+ "\n",
+ " self.net = net\n",
+ " self.grad_op = ops.GradOperation()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " gradient_function = self.grad_op(self.net)\n",
+ " return gradient_function(x, y)\n",
+ "\n",
+ "x = Tensor([[0.8, 0.6, 0.2], [1.8, 1.3, 1.1]], dtype=mstype.float32)\n",
+ "y = Tensor([[0.11, 3.3, 1.1], [1.1, 0.2, 1.4], [1.1, 2.2, 0.3]], dtype=mstype.float32)\n",
+ "\n",
+ "output = GradNetWrtX(Net())(x, y)\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> 由于不同计算平台的精度可能存在差异,因此上面的代码在不同平台上的执行结果会存在微小的差别。求导公式的推导和上述打印结果的解释,可参考[自动求导](https://www.mindspore.cn/tutorials/zh-CN/master/advanced/network/derivation.html#%E4%B8%80%E9%98%B6%E6%B1%82%E5%AF%BC)章节。\n",
+ "\n",
+ "\n",
+ "\n",
+ "根据上述动态图模式下构图原理可以看到,在正向传播过程中,MindSpore记录了Mul的计算过程,根据Mul对应的反向bprop的定义,得到了反向的MulGrad算子。\n",
+ "\n",
+ "根据Mul算子的bprop定义,如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore.ops._grad.grad_base import bprop_getters\n",
+ "\n",
+ "@bprop_getters.register(ops.Mul)\n",
+ "def get_bprop_mul(self):\n",
+ " \"\"\"Grad definition for `Mul` operation.\"\"\"\n",
+ " mul_func = P.Mul()\n",
+ "\n",
+ " def bprop(x, y, out, dout):\n",
+ " bc_dx = mul_func(y, dout)\n",
+ " bc_dy = mul_func(x, dout)\n",
+ " return binop_grad_common(x, y, bc_dx, bc_dy)\n",
+ "\n",
+ " return bprop"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "可以看到对Mul的输入求反向,需要两个输入和输出的反向传播梯度值,此时根据实际的输入值,可以将z连接到MulGrad。以此类推,对下一个算子Matmul,相应的得到MatmulGrad信息,再根据bprop的输入输出,将上下文梯度传播连接起来。\n",
+ "\n",
+ "同理对于输入y求导,可以使用同样的过程进行推导。\n",
+ "\n",
+ "### 动态图模式下的控制流\n",
+ "\n",
+ "在MindSpore中,针对控制流语法并没有做特殊处理,直接按照Python的语法展开执行,进而对展开的执行算子进行自动微分操作。\n",
+ "\n",
+ "例如,对于for循环,在动态图下会首先执行Python的源码,然后根据具体的循环次数,不断的执行for循环中的语句,并对其算子进行自动微分操作。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[4. 5. 6.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 设置运行模式为动态图模式\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " \"\"\"自定义网络\"\"\"\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.matmul = ops.MatMul()\n",
+ " self.z = Parameter(Tensor(np.array([1.0], np.float32)), name='z')\n",
+ "\n",
+ " def construct(self, x):\n",
+ " for _ in range(3):\n",
+ " x = x + self.z\n",
+ " return x\n",
+ "\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "net = Net()\n",
+ "output = net(x)\n",
+ "\n",
+ "print(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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/pynative_graph/pynative.ipynb b/tutorials/source_zh_cn/advanced/pynative_graph/pynative.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..7cf6bcc4a1d29cd15e98d821b2ad3727e6584bf6
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/pynative_graph/pynative.ipynb
@@ -0,0 +1,797 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-03-02T09:06:23.745016Z",
+ "start_time": "2022-03-02T09:06:21.533915Z"
+ }
+ },
+ "source": [
+ "# 动态图模式应用\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/pynative_graph/mindspore_pynative.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/pynative_graph/mindspore_pynative.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/pynative_graph/pynative.ipynb)\n",
+ "\n",
+ "## 概述\n",
+ "\n",
+ "MindSpore的针对动态图和静态图两种模式在调试和运行方面做了不同的优化:\n",
+ "\n",
+ "- 动态图模式:也称PyNative模式,将神经网络中的各个算子逐一下发执行,方便用户编写和调试神经网络模型。\n",
+ "- 静态图模式:也称Graph模式或者图模式,将神经网络模型编译成一整张图,然后下发执行。该模式利用图优化等技术提高运行性能,同时有助于规模部署和跨平台运行。\n",
+ "\n",
+ "在动态图模式下,MindSpore支持执行单算子、普通函数和网络,以及单独求梯度的操作,下面我们将通过示例代码详细介绍这几种操作的使用方法和注意事项。\n",
+ "\n",
+ "## 动态图模式下的操作\n",
+ "\n",
+ "首先,我们导入相关依赖,并设置运行模式为动态图模式:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-03-02T09:15:13.240496Z",
+ "start_time": "2022-03-02T09:15:13.237903Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.ops as ops\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor, context\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 执行单算子\n",
+ "\n",
+ "下面为执行加法算子[mindspore.ops.Add](https://mindspore.cn/docs/api/zh-CN/master/api_python/ops/mindspore.ops.Add.html#mindspore.ops.Add)的示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-03-02T09:08:29.337515Z",
+ "start_time": "2022-03-02T09:08:29.322592Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "x: [1. 2.] \n",
+ "y: [3. 5.] \n",
+ "z: [4. 7.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "add = ops.Add()\n",
+ "x = Tensor(np.array([1, 2]).astype(np.float32))\n",
+ "y = Tensor(np.array([3, 5]).astype(np.float32))\n",
+ "z = add(x, y)\n",
+ "print(\"x:\", x.asnumpy(), \"\\ny:\", y.asnumpy(), \"\\nz:\", z.asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 执行函数\n",
+ "\n",
+ "执行自定义函数`add_func`,示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-03-02T09:08:53.065585Z",
+ "start_time": "2022-03-02T09:08:53.058016Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "x: [1. 2.] \n",
+ "y: [3. 5.] \n",
+ "z: [5. 9.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "add = ops.Add()\n",
+ "\n",
+ "def add_func(x, y):\n",
+ " z = add(x, y)\n",
+ " z = add(z, x)\n",
+ " return z\n",
+ "\n",
+ "x = Tensor(np.array([1, 2]).astype(np.float32))\n",
+ "y = Tensor(np.array([3, 5]).astype(np.float32))\n",
+ "z = add_func(x, y)\n",
+ "print(\"x:\", x.asnumpy(), \"\\ny:\", y.asnumpy(), \"\\nz:\", z.asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 执行网络\n",
+ "\n",
+ "执行自定义网络`Net`,在construct中定义网络结构,示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-03-02T09:09:16.498705Z",
+ "start_time": "2022-03-02T09:09:16.490549Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "x: [1. 2. 3.] \n",
+ "y: [4. 5. 6.] \n",
+ "z: [ 4. 10. 18.]\n"
+ ]
+ }
+ ],
+ "source": [
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.mul = ops.Mul()\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " return self.mul(x, y)\n",
+ "\n",
+ "net = Net()\n",
+ "x = Tensor(np.array([1.0, 2.0, 3.0]).astype(np.float32))\n",
+ "y = Tensor(np.array([4.0, 5.0, 6.0]).astype(np.float32))\n",
+ "z = net(x, y)\n",
+ "\n",
+ "print(\"x:\", x.asnumpy(), \"\\ny:\", y.asnumpy(), \"\\nz:\", z.asnumpy())"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 提升动态图模式性能\n",
+ "\n",
+ "为了提高动态图模式下的前向计算任务执行速度,MindSpore提供了`ms_function`装饰器,可以通过修饰Python函数或者Python类的成员函数使其被编译成计算图,通过图优化等技术提高运行速度。`ms_function`装饰器的使用方式及注意事项已在[动静结合](https://www.mindspore.cn/tutorials/zh-CN/master/advanced/pynative_graph/combine.html#ms-function%E8%A3%85%E9%A5%B0%E5%99%A8)章节中说明,此处不再赘述。\n",
+ "\n",
+ "## 动态图模式下同步执行\n",
+ "\n",
+ "在动态图模式下,为了提升性能,算子在device上使用了异步执行方式,因此在算子执行错误的时候,错误信息可能会在程序执行到最后才显示。针对这种情况,MindSpore增加了一个pynative_synchronize的设置来控制算子device上是否使用异步执行。\n",
+ "\n",
+ "动态图模式下算子默认为异步执行,可以通过设置context来控制是否异步执行。当算子执行失败时,可以方便地通过调用栈看到出错的代码位置。示例代码如下:\n",
+ "\n",
+ "```python\n",
+ "import numpy as np\n",
+ "import mindspore.context as context\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import dtype as mstype\n",
+ "import mindspore.ops as ops\n",
+ "\n",
+ "# 通过设置pynative_synchronize来使算子同步执行\n",
+ "context.set_context(mode=context.PYNATIVE_MODE, pynative_synchronize=True)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.get_next = ops.GetNext([mstype.float32], [(1, 1)], 1, \"test\")\n",
+ "\n",
+ " def construct(self, x1,):\n",
+ " x = self.get_next()\n",
+ " x = x + x1\n",
+ " return x\n",
+ "\n",
+ "context.set_context()\n",
+ "x1 = np.random.randn(1, 1).astype(np.float32)\n",
+ "net = Net()\n",
+ "output = net(Tensor(x1))\n",
+ "print(output.asnumpy())\n",
+ "```\n",
+ "\n",
+ "输出:此时算子为同步执行,当算子执行错误时,可以看到完整的调用栈,找到出错的代码行。\n",
+ "\n",
+ "```text\n",
+ "Traceback (most recent call last):\n",
+ " File \"test.py\", line 24, in \n",
+ " output = net(Tensor(x1))\n",
+ " File \".../mindspore/nn/cell.py\", line 602, in __call__\n",
+ " raise err\n",
+ " File \".../mindspore/nn/cell.py\", line 599, in __call__\n",
+ " output = self._run_construct(cast_inputs, kwargs)\n",
+ " File \".../mindspore/nn/cell.py\", line 429, in _run_construct\n",
+ " output = self.construct(*cast_inputs, **kwargs)\n",
+ " File \"test.py\", line 17, in construct\n",
+ " x = self.get_next()\n",
+ " File \".../mindspore/ops/primitive.py\", line 294, in __call__\n",
+ " return _run_op(self, self.name, args)\n",
+ " File \".../mindspore/common/api.py\", line 90, in wrapper\n",
+ " results = fn(*arg, **kwargs)\n",
+ " File \".../mindspore/ops/primitive.py\", line 754, in _run_op\n",
+ " output = real_run_op(obj, op_name, args)\n",
+ "RuntimeError: mindspore/ccsrc/plugin/device/gpu/kernel/data/dataset_iterator_kernel.cc:139 Launch] For 'GetNext', gpu Queue(test) Open Failed: 2\n",
+ "```\n",
+ "\n",
+ "## Hook功能\n",
+ "\n",
+ "调试深度学习网络是每一个深度学习领域的从业者需要面对且投入精力较大的工作。由于深度学习网络隐藏了中间层算子的输入、输出数据以及反向梯度,只提供网络输入数据(特征量、权重)的梯度,导致无法准确地感知中间层算子的数据变化,从而降低了调试效率。为了方便用户准确、快速地对深度学习网络进行调试,MindSpore在动态图模式下设计了Hook功能,**使用Hook功能可以捕获中间层算子的输入、输出数据以及反向梯度**。\n",
+ "\n",
+ "目前,动态图模式下提供了四种形式的Hook功能,分别是:HookBackward算子和在Cell对象上进行注册的register_forward_pre_hook、register_forward_hook、register_backward_hook功能。\n",
+ "\n",
+ "### HookBackward算子\n",
+ "\n",
+ "HookBackward将Hook功能以算子的形式实现。用户初始化一个HookBackward算子,将其安插到深度学习网络中需要捕获梯度的位置。在网络正向执行时,HookBackward算子将输入数据不做任何修改后原样输出;在网络反向传播梯度时,在HookBackward上注册的Hook函数将会捕获反向传播至此的梯度。用户可以在Hook函数中自定义对梯度的操作,比如打印梯度,或者返回新的梯度。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-03-02T09:13:38.660885Z",
+ "start_time": "2022-03-02T09:13:38.645597Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "hook_fn print grad_out: (Tensor(shape=[], dtype=Float32, value= 2),)\n",
+ "output: (Tensor(shape=[], dtype=Float32, value= 4), Tensor(shape=[], dtype=Float32, value= 4))\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore\n",
+ "from mindspore import ops\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import context\n",
+ "from mindspore.ops import GradOperation\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "def hook_fn(grad_out):\n",
+ " \"\"\"打印梯度\"\"\"\n",
+ " print(\"hook_fn print grad_out:\", grad_out)\n",
+ "\n",
+ "grad_all = GradOperation(get_all=True)\n",
+ "hook = ops.HookBackward(hook_fn)\n",
+ "def hook_test(x, y):\n",
+ " z = x * y\n",
+ " z = hook(z)\n",
+ " z = z * y\n",
+ " return z\n",
+ "\n",
+ "def net(x, y):\n",
+ " return grad_all(hook_test)(x, y)\n",
+ "\n",
+ "output = net(Tensor(1, mindspore.float32), Tensor(2, mindspore.float32))\n",
+ "print(\"output:\", output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "更多HookBackward算子的说明可以参考[API文档](https://mindspore.cn/docs/api/zh-CN/master/api_python/ops/mindspore.ops.HookBackward.html)。\n",
+ "\n",
+ "### Cell对象的register_forward_pre_hook功能\n",
+ "\n",
+ "用户可以在Cell对象上使用`register_forward_pre_hook`函数来注册一个自定义的Hook函数,用来捕获正向传入该Cell对象的数据。该功能在静态图模式下和在使用`ms_function`修饰的Cell对象上不起作用。`register_forward_pre_hook`函数接收Hook函数作为入参,并返回一个与Hook函数一一对应的`handle`对象。用户可以通过调用`handle`对象的`remove()`函数来删除与之对应的Hook函数。每一次调用`register_forward_pre_hook`函数,都会返回一个不同的`handle`对象。Hook函数应该按照以下的方式进行定义。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def forward_pre_hook_fn(cell_id, inputs):\n",
+ " print(\"forward inputs: \", inputs)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "这里的cell_id是Cell对象的名称以及ID信息,inputs是正向传入到Cell对象的数据。因此,用户可以使用register_forward_pre_hook函数来捕获网络中某一个Cell对象的正向输入数据。用户可以在Hook函数中自定义对输入数据的操作,比如查看、打印数据,或者返回新的输入数据给当前的Cell对象。如果在Hook函数中对Cell对象的原始输入数据进行计算操作后,再作为新的输入数据返回,这些新增的计算操作将会同时作用于梯度的反向传播。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "forward inputs: (Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]),)\n",
+ "[2.]\n",
+ "forward inputs: (Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]),)\n",
+ "(Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]), Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]))\n",
+ "(Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]), Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]))\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore\n",
+ "import mindspore.nn as nn\n",
+ "import mindspore.ops as ops\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import context\n",
+ "from mindspore.ops import GradOperation\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "def forward_pre_hook_fn(cell_id, inputs):\n",
+ " print(\"forward inputs: \", inputs)\n",
+ " input_x = inputs[0]\n",
+ " return input_x\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.relu = nn.ReLU()\n",
+ " self.handle = self.relu.register_forward_pre_hook(forward_pre_hook_fn)\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " x = x + y\n",
+ " x = self.relu(x)\n",
+ " return x\n",
+ "\n",
+ "grad = GradOperation(get_all=True)\n",
+ "net = Net()\n",
+ "\n",
+ "x = Tensor(np.ones([1]).astype(np.float32))\n",
+ "y = Tensor(np.ones([1]).astype(np.float32))\n",
+ "\n",
+ "output = net(x, y)\n",
+ "print(output)\n",
+ "gradient = grad(net)(x, y)\n",
+ "print(gradient)\n",
+ "net.handle.remove()\n",
+ "gradient = grad(net)(x, y)\n",
+ "print(gradient)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "用户如果在Hook函数中直接返回新创建的数据,而不是返回由原始输入数据经过计算后得到的数据,那么梯度的反向传播将会在该Cell对象上截止。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "forward inputs: (Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]),)\n",
+ "(Tensor(shape=[1], dtype=Float32, value= [ 0.00000000e+00]), Tensor(shape=[1], dtype=Float32, value= [ 0.00000000e+00]))\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import context\n",
+ "from mindspore.ops import GradOperation\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "def forward_pre_hook_fn(cell_id, inputs):\n",
+ " print(\"forward inputs: \", inputs)\n",
+ " return Tensor(np.ones([1]).astype(np.float32))\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.relu = nn.ReLU()\n",
+ " self.handle = self.relu.register_forward_pre_hook(forward_pre_hook_fn)\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " x = x + y\n",
+ " x = self.relu(x)\n",
+ " return x\n",
+ "\n",
+ "grad = GradOperation(get_all=True)\n",
+ "net = Net()\n",
+ "\n",
+ "x = Tensor(np.ones([1]).astype(np.float32))\n",
+ "y = Tensor(np.ones([1]).astype(np.float32))\n",
+ "\n",
+ "gradient = grad(net)(x, y)\n",
+ "print(gradient)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "为了避免脚本在切换到图模式时运行失败,不建议在Cell对象的 `construct` 函数中调用 `register_forward_pre_hook` 函数和 `handle` 对象的 `remove()` 函数。在动态图模式下,如果在Cell对象的 `construct` 函数中调用 `register_forward_pre_hook` 函数,那么Cell对象每次运行都将新注册一个Hook函数。\n",
+ "\n",
+ "更多关于Cell对象的 `register_forward_pre_hook` 功能的说明可以参考[API文档](https://mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell.register_forward_pre_hook)。\n",
+ "\n",
+ "### Cell对象的register_forward_hook功能\n",
+ "\n",
+ "用户可以在Cell对象上使用`register_forward_hook`函数来注册一个自定义的Hook函数,用来捕获正向传入Cell对象的数据和Cell对象的输出数据。该功能在静态图模式下和在使用`ms_function`修饰的Cell对象上不起作用。`register_forward_hook`函数接收Hook函数作为入参,并返回一个与Hook函数一一对应的`handle`对象。用户可以通过调用`handle`对象的`remove()`函数来删除与之对应的Hook函数。每一次调用`register_forward_hook`函数,都会返回一个不同的`handle`对象。Hook函数应该按照以下的方式进行定义。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def forward_hook_fn(cell_id, inputs, outputs):\n",
+ " print(\"forward inputs: \", inputs)\n",
+ " print(\"forward outputs: \", outputs)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "这里的`cell_id`是Cell对象的名称以及ID信息,`inputs`是正向传入到Cell对象的数据,`outputs`是Cell对象的正向输出数据。因此,用户可以使用`register_forward_hook`函数来捕获网络中某一个Cell对象的正向输入数据和输出数据。用户可以在Hook函数中自定义对输入、输出数据的操作,比如查看、打印数据,或者返回新的输出数据。如果在Hook函数中对Cell对象的原始输出数据进行计算操作后,再作为新的输出数据返回,这些新增的计算操作将会同时作用于梯度的反向传播。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "forward inputs: (Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]),)\n",
+ "forward outputs: [2.]\n",
+ "(Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]), Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]))\n",
+ "(Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]), Tensor(shape=[1], dtype=Float32, value= [ 1.00000000e+00]))\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import context\n",
+ "from mindspore.ops import GradOperation\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "def forward_hook_fn(cell_id, inputs, outputs):\n",
+ " print(\"forward inputs: \", inputs)\n",
+ " print(\"forward outputs: \", outputs)\n",
+ " outputs = outputs + outputs\n",
+ " return outputs\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.relu = nn.ReLU()\n",
+ " self.handle = self.relu.register_forward_hook(forward_hook_fn)\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " x = x + y\n",
+ " x = self.relu(x)\n",
+ " return x\n",
+ "\n",
+ "grad = GradOperation(get_all=True)\n",
+ "net = Net()\n",
+ "\n",
+ "x = Tensor(np.ones([1]).astype(np.float32))\n",
+ "y = Tensor(np.ones([1]).astype(np.float32))\n",
+ "\n",
+ "gradient = grad(net)(x, y)\n",
+ "print(gradient)\n",
+ "net.handle.remove()\n",
+ "gradient = grad(net)(x, y)\n",
+ "print(gradient)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "用户如果在Hook函数中直接返回新创建的数据,而不是将原始的输出数据经过计算后,将得到的新输出数据返回,那么梯度的反向传播将会在该Cell对象上截止。该现象可以参考`register_forward_pre_hook`函数的用例说明。\n",
+ "为了避免脚本在切换到图模式时运行失败,不建议在Cell对象的`construct`函数中调用`register_forward_hook`函数和`handle`对象的`remove()`函数。在动态图模式下,如果在Cell对象的`construct`函数中调用`register_forward_hook`函数,那么Cell对象每次运行都将新注册一个Hook函数。\n",
+ "\n",
+ "更多关于Cell对象的`register_forward_hook`功能的说明可以参考[API文档](https://mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell.register_forward_hook)。\n",
+ "\n",
+ "### Cell对象的register_backward_hook功能\n",
+ "\n",
+ "用户可以在Cell对象上使用`register_backward_hook`函数来注册一个自定义的Hook函数,用来捕获网络反向传播时与Cell对象相关联的梯度。该功能在图模式下或者在使用`ms_function`修饰的Cell对象上不起作用。`register_backward_hook`函数接收Hook函数作为入参,并返回一个与Hook函数一一对应的`handle`对象。用户可以通过调用`handle`对象的`remove()`函数来删除与之对应的Hook函数。每一次调用`register_backward_hook`函数,都会返回一个不同的`handle`对象。\n",
+ "\n",
+ "与HookBackward算子所使用的自定义Hook函数有所不同,`register_backward_hook`使用的Hook函数的入参中,包含了表示Cell对象名称与id信息的`cell_id`、反向传入到Cell对象的梯度、以及Cell对象的反向输出的梯度。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def backward_hook_function(cell_id, grad_input, grad_output):\n",
+ " print(grad_input)\n",
+ " print(grad_output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "这里的`cell_id`是Cell对象的名称以及ID信息,`grad_input`是网络反向传播时,传入到Cell对象的梯度,它对应于正向过程中下一个算子的反向输出梯度;`grad_output`是Cell对象反向输出的梯度。因此,用户可以使用`register_backward_hook`函数来捕获网络中某一个Cell对象的反向传入和反向输出梯度。用户可以在Hook函数中自定义对梯度的操作,比如查看、打印梯度,或者返回新的输出梯度。如果需要在Hook函数中返回新的输出梯度时,返回值必须是`tuple`的形式。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-03-02T09:14:26.523389Z",
+ "start_time": "2022-03-02T09:14:26.506784Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "(Tensor(shape=[1, 2, 1, 1], dtype=Float32, value=\n",
+ "[[[[ 1.00000000e+00]],\n",
+ " [[ 1.00000000e+00]]]]),)\n",
+ "(Tensor(shape=[1, 2, 1, 1], dtype=Float32, value=\n",
+ "[[[[ 9.99994993e-01]],\n",
+ " [[ 9.99994993e-01]]]]),)\n",
+ "(Tensor(shape=[1, 1, 2, 2], dtype=Float32, value=\n",
+ "[[[[ 1.99998999e+00, 1.99998999e+00],\n",
+ " [ 1.99998999e+00, 1.99998999e+00]]]]),)\n",
+ "-------------\n",
+ " (Tensor(shape=[1, 1, 2, 2], dtype=Float32, value=\n",
+ "[[[[ 1.99998999e+00, 1.99998999e+00],\n",
+ " [ 1.99998999e+00, 1.99998999e+00]]]]),)\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import context\n",
+ "from mindspore.ops import GradOperation\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "def backward_hook_function(cell_id, grad_input, grad_output):\n",
+ " print(grad_input)\n",
+ " print(grad_output)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.conv = nn.Conv2d(1, 2, kernel_size=2, stride=1, padding=0, weight_init=\"ones\", pad_mode=\"valid\")\n",
+ " self.bn = nn.BatchNorm2d(2, momentum=0.99, eps=0.00001, gamma_init=\"ones\")\n",
+ " self.handle = self.bn.register_backward_hook(backward_hook_function)\n",
+ " self.relu = nn.ReLU()\n",
+ "\n",
+ " def construct(self, x):\n",
+ " x = self.conv(x)\n",
+ " x = self.bn(x)\n",
+ " x = self.relu(x)\n",
+ " return x\n",
+ "\n",
+ "net = Net()\n",
+ "grad_all = GradOperation(get_all=True)\n",
+ "output = grad_all(net)(Tensor(np.ones([1, 1, 2, 2]).astype(np.float32)))\n",
+ "print(output)\n",
+ "net.handle.remove()\n",
+ "output = grad_all(net)(Tensor(np.ones([1, 1, 2, 2]).astype(np.float32)))\n",
+ "print(\"-------------\\n\", output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "当 `register_backward_hook` 函数和 `register_forward_pre_hook` 函数、 `register_forward_hook` 函数同时作用于同一Cell对象时,如果 `register_forward_pre_hook` 和 `register_forward_hook` 函数中有添加其他算子进行数据处理,这些新增算子会在Cell对象执行前或者执行后参与数据的正向计算,但是这些新增算子的反向梯度不在 `register_backward_hook` 函数的捕获范围内。 `register_backward_hook` 中注册的Hook函数仅捕获原始Cell对象的输入、输出梯度。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "forward inputs: (Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]),)\n",
+ "forward inputs: (Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]),)\n",
+ "forward outputs: [2.]\n",
+ "grad input: (Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]),)\n",
+ "grad output: (Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]),)\n",
+ "(Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]), Tensor(shape=[1], dtype=Float32, value= [ 2.00000000e+00]))\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import context\n",
+ "from mindspore.ops import GradOperation\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "def forward_pre_hook_fn(cell_id, inputs):\n",
+ " print(\"forward inputs: \", inputs)\n",
+ " input_x = inputs[0]\n",
+ " return input_x\n",
+ "\n",
+ "def forward_hook_fn(cell_id, inputs, outputs):\n",
+ " print(\"forward inputs: \", inputs)\n",
+ " print(\"forward outputs: \", outputs)\n",
+ " outputs = outputs + outputs\n",
+ " return outputs\n",
+ "\n",
+ "def backward_hook_fn(cell_id, grad_input, grad_output):\n",
+ " print(\"grad input: \", grad_input)\n",
+ " print(\"grad output: \", grad_output)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super(Net, self).__init__()\n",
+ " self.relu = nn.ReLU()\n",
+ " self.handle = self.relu.register_forward_pre_hook(forward_pre_hook_fn)\n",
+ " self.handle2 = self.relu.register_forward_hook(forward_hook_fn)\n",
+ " self.handle3 = self.relu.register_backward_hook(backward_hook_fn)\n",
+ "\n",
+ " def construct(self, x, y):\n",
+ " x = x + y\n",
+ " x = self.relu(x)\n",
+ " return x\n",
+ "\n",
+ "net = Net()\n",
+ "grad = GradOperation(get_all=True)\n",
+ "gradient = grad(net)(Tensor(np.ones([1]).astype(np.float32)), Tensor(np.ones([1]).astype(np.float32)))\n",
+ "print(gradient)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "这里的 `grad_input` 是梯度反向传播时传入`self.relu`的梯度,而不是传入 `forward_hook_fn` 函数中,新增的 `Add` 算子的梯度。这里的 `grad_output` 是梯度反向传播时 `self.relu` 反向输出的梯度,而不是 `forward_pre_hook_fn` 函数中新增 `Add` 算子的反向输出梯度。 `register_forward_pre_hook` 函数和 `register_forward_hook` 函数是在Cell对象执行前后起作用,不会影响Cell对象上反向Hook函数的梯度捕获范围。\n",
+ "为了避免脚本在切换到图模式时运行失败,不建议在Cell对象的 `construct` 函数中调用 `register_backward_hook` 函数和 `handle` 对象的 `remove()` 函数。在PyNative模式下,如果在Cell对象的 `construct` 函数中调用 `register_backward_hook` 函数,那么Cell对象每次运行都将新注册一个Hook函数。\n",
+ "\n",
+ "更多关于Cell对象的 `register_backward_hook` 功能的说明可以参考[API文档](https://mindspore.cn/docs/api/zh-CN/master/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell.register_backward_hook)。\n",
+ "\n",
+ "## 自定义bprop功能\n",
+ "\n",
+ "用户可以自定义nn.Cell对象的反向传播(计算)函数,从而控制nn.Cell对象梯度计算的过程,定位梯度问题。自定义bprop函数的使用方法是:在定义的nn.Cell对象里面增加一个用户自定义的bprop函数。训练的过程中会使用用户自定义的bprop函数来生成反向图。\n",
+ "\n",
+ "示例代码:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-03-02T09:14:55.896896Z",
+ "start_time": "2022-03-02T09:14:55.881233Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "(Tensor(shape=[], dtype=Float32, value= 3), Tensor(shape=[], dtype=Float32, value= 2))\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Tensor\n",
+ "from mindspore import context\n",
+ "from mindspore.ops import GradOperation\n",
+ "\n",
+ "context.set_context(mode=context.PYNATIVE_MODE)\n",
+ "\n",
+ "class Net(nn.Cell):\n",
+ " def construct(self, x, y):\n",
+ " z = x * y\n",
+ " z = z * y\n",
+ " return z\n",
+ "\n",
+ " def bprop(self, x, y, out, dout):\n",
+ " x_dout = x + y\n",
+ " y_dout = x * y\n",
+ " return x_dout, y_dout\n",
+ "\n",
+ "grad_all = GradOperation(get_all=True)\n",
+ "output = grad_all(Net())(Tensor(1, mindspore.float32), Tensor(2, mindspore.float32))\n",
+ "print(output)"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "MindSpore",
+ "language": "python",
+ "name": "python3"
+ },
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/tutorials/source_zh_cn/advanced/train.rst b/tutorials/source_zh_cn/advanced/train.rst
new file mode 100644
index 0000000000000000000000000000000000000000..034fed9a1103c264f52f776d637d1c75ba1a22d3
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/train.rst
@@ -0,0 +1,11 @@
+训练与评估
+===================
+
+.. toctree::
+ :maxdepth: 1
+
+ train/metric
+ train/train_eval
+ train/model
+ train/callback
+ train/save
diff --git a/tutorials/source_zh_cn/advanced/train/callback.ipynb b/tutorials/source_zh_cn/advanced/train/callback.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..ce9ab4928dad5fb4aa9d978bde605c4bfe20ed7b
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/train/callback.ipynb
@@ -0,0 +1,465 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 回调机制 Callback\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_callback.ipynb) \n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_callback.py) \n",
+ "[](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/train/callback.ipynb)\n",
+ "\n",
+ "在深度学习训练过程中,为及时掌握网络模型的训练状态、实时观察网络模型各参数的变化情况和实现训练过程中用户自定义的一些操作,MindSpore提供了回调机制(Callback)来实现上述功能。\n",
+ "\n",
+ "Callback回调机制一般用在网络模型训练过程`Model.train`中,MindSpore的`Model`会按照Callback列表`callbacks`顺序执行回调函数,用户可以通过设置不同的回调类来实现在训练过程中或者训练后执行的功能。\n",
+ "\n",
+ "## Callback介绍\n",
+ "\n",
+ "当聊到回调Callback的时候,大部分用户都会觉得很难理解,是不是需要堆栈或者特殊的调度方式,实际上我们简单的理解回调:假设函数A有一个参数,这个参数是个函数B,当函数A执行完以后执行函数B,那么这个过程就叫回调。\n",
+ "\n",
+ "`Callback`是回调的意思,MindSpore中的回调函数实际上不是一个函数而是一个类,用户可以使用回调机制来**观察训练过程中网络内部的状态和相关信息,或在特定时期执行特定动作**。\n",
+ "\n",
+ "例如监控损失函数Loss、保存模型参数ckpt、动态调整参数lr、提前终止训练任务等。\n",
+ "\n",
+ "### MindSpore的Callback能力\n",
+ "\n",
+ "下面以基于MNIST数据集训练LeNet-5网络模型为例,介绍几种常用的MindSpore内置回调类。\n",
+ "\n",
+ "> 更多内置回调类的信息及使用方式请参考[API文档](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.train.html#mindspore-train-callback)。\n",
+ "\n",
+ "首先需要下载并处理MNIST数据,构建LeNet-5网络模型,示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import mindspore.nn as nn\n",
+ "from mindspore.train import Model\n",
+ "from mindvision.classification.dataset import Mnist\n",
+ "from mindvision.classification.models import lenet\n",
+ "\n",
+ "download_train = Mnist(path=\"./mnist\", split=\"train\", download=True)\n",
+ "dataset_train = download_train.run()\n",
+ "\n",
+ "network = lenet(num_classes=10, pretrained=False)\n",
+ "net_loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')\n",
+ "net_opt = nn.Momentum(network.trainable_params(), learning_rate=0.01, momentum=0.9)\n",
+ "\n",
+ "# 定义网络模型\n",
+ "model = Model(network, loss_fn=net_loss, optimizer=net_opt, metrics={\"Accuracy\": nn.Accuracy()})"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "回调机制的使用方法,在`model.train`方法中传入`Callback`对象,它可以是一个`Callback`列表,示例代码如下,其中[ModelCheckpoint](https://mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.train.html#mindspore.train.callback.ModelCheckpoint)和[LossMonitor](https://mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.train.html#mindspore.train.callback.LossMonitor)是MindSpore提供的回调类。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "epoch: 1 step: 1875, loss is 0.257398396730423\n",
+ "epoch: 2 step: 1875, loss is 0.04801357910037041\n",
+ "epoch: 3 step: 1875, loss is 0.028765171766281128\n",
+ "epoch: 4 step: 1875, loss is 0.008372672833502293\n",
+ "epoch: 5 step: 1875, loss is 0.0016194271156564355\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore.train.callback import ModelCheckpoint, LossMonitor\n",
+ "\n",
+ "# 定义回调类\n",
+ "ckpt_cb = ModelCheckpoint()\n",
+ "loss_cb = LossMonitor(1875)\n",
+ "\n",
+ "model.train(5, dataset_train, callbacks=[ckpt_cb, loss_cb])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 常用的内置回调函数\n",
+ "\n",
+ "MindSpore提供`Callback`能力,支持用户在训练/推理的特定阶段,插入自定义的操作。\n",
+ "\n",
+ "## ModelCheckpoint\n",
+ "\n",
+ "为了保存训练后的网络模型和参数,方便进行再推理或再训练,MindSpore提供了[ModelCheckpoint](https://mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.train.html?highlight=modelcheckpoint#mindspore.train.callback.ModelCheckpoint)接口,一般与配置保存信息接口[CheckpointConfig](https://mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.train.html?highlight=modelcheckpoint#mindspore.train.callback.CheckpointConfig)配合使用。\n",
+ "\n",
+ "下面我们通过一段示例代码来说明如何保存训练后的网络模型和参数:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore.train.callback import ModelCheckpoint, CheckpointConfig\n",
+ "\n",
+ "# 设置保存模型的配置信息\n",
+ "config_ck = CheckpointConfig(save_checkpoint_steps=1875, keep_checkpoint_max=10)\n",
+ "# 实例化保存模型回调接口,定义保存路径和前缀名\n",
+ "ckpoint = ModelCheckpoint(prefix=\"lenet\", directory=\"./lenet\", config=config_ck)\n",
+ "\n",
+ "# 开始训练,加载保存模型和参数回调函数\n",
+ "model.train(1, dataset_train, callbacks=[ckpoint])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面代码运行后,生成的Checkpoint文件目录结构如下:\n",
+ "\n",
+ "```text\n",
+ "./lenet/\n",
+ "├── lenet-1_1875.ckpt # 保存参数文件\n",
+ "└── lenet-graph.meta # 编译后的计算图\n",
+ "```\n",
+ "\n",
+ "## LossMonitor\n",
+ "\n",
+ "为了监控训练过程中的损失函数值Loss变化情况,观察训练过程中每个epoch、每个step的运行时间,MindSpore Vision提供了`LossMonitor`接口(与MindSpore提供的`LossMonitor`接口有区别)。\n",
+ "\n",
+ "下面我们通过示例代码说明:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch:[ 0/ 5], step:[ 375/ 1875], loss:[0.041/0.023], time:0.670 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 5], step:[ 750/ 1875], loss:[0.002/0.023], time:0.723 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 5], step:[ 1125/ 1875], loss:[0.006/0.023], time:0.662 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 5], step:[ 1500/ 1875], loss:[0.000/0.024], time:0.664 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 5], step:[ 1875/ 1875], loss:[0.009/0.024], time:0.661 ms, lr:0.01000\n",
+ "Epoch time: 1759.622 ms, per step time: 0.938 ms, avg loss: 0.024\n",
+ "Epoch:[ 1/ 5], step:[ 375/ 1875], loss:[0.001/0.020], time:0.658 ms, lr:0.01000\n",
+ "Epoch:[ 1/ 5], step:[ 750/ 1875], loss:[0.002/0.021], time:0.661 ms, lr:0.01000\n",
+ "Epoch:[ 1/ 5], step:[ 1125/ 1875], loss:[0.000/0.021], time:0.663 ms, lr:0.01000\n",
+ "Epoch:[ 1/ 5], step:[ 1500/ 1875], loss:[0.048/0.022], time:0.655 ms, lr:0.01000\n",
+ "Epoch:[ 1/ 5], step:[ 1875/ 1875], loss:[0.018/0.022], time:0.646 ms, lr:0.01000\n",
+ "Epoch time: 1551.506 ms, per step time: 0.827 ms, avg loss: 0.022\n",
+ "Epoch:[ 2/ 5], step:[ 375/ 1875], loss:[0.001/0.017], time:0.674 ms, lr:0.01000\n",
+ "Epoch:[ 2/ 5], step:[ 750/ 1875], loss:[0.001/0.018], time:0.669 ms, lr:0.01000\n",
+ "Epoch:[ 2/ 5], step:[ 1125/ 1875], loss:[0.004/0.019], time:0.683 ms, lr:0.01000\n",
+ "Epoch:[ 2/ 5], step:[ 1500/ 1875], loss:[0.003/0.020], time:0.657 ms, lr:0.01000\n",
+ "Epoch:[ 2/ 5], step:[ 1875/ 1875], loss:[0.041/0.019], time:1.447 ms, lr:0.01000\n",
+ "Epoch time: 1616.589 ms, per step time: 0.862 ms, avg loss: 0.019\n",
+ "Epoch:[ 3/ 5], step:[ 375/ 1875], loss:[0.000/0.011], time:0.672 ms, lr:0.01000\n",
+ "Epoch:[ 3/ 5], step:[ 750/ 1875], loss:[0.001/0.013], time:0.687 ms, lr:0.01000\n",
+ "Epoch:[ 3/ 5], step:[ 1125/ 1875], loss:[0.016/0.014], time:0.665 ms, lr:0.01000\n",
+ "Epoch:[ 3/ 5], step:[ 1500/ 1875], loss:[0.001/0.015], time:0.674 ms, lr:0.01000\n",
+ "Epoch:[ 3/ 5], step:[ 1875/ 1875], loss:[0.001/0.015], time:0.666 ms, lr:0.01000\n",
+ "Epoch time: 1586.809 ms, per step time: 0.846 ms, avg loss: 0.015\n",
+ "Epoch:[ 4/ 5], step:[ 375/ 1875], loss:[0.000/0.008], time:0.671 ms, lr:0.01000\n",
+ "Epoch:[ 4/ 5], step:[ 750/ 1875], loss:[0.000/0.013], time:0.701 ms, lr:0.01000\n",
+ "Epoch:[ 4/ 5], step:[ 1125/ 1875], loss:[0.009/0.015], time:0.666 ms, lr:0.01000\n",
+ "Epoch:[ 4/ 5], step:[ 1500/ 1875], loss:[0.008/0.015], time:0.941 ms, lr:0.01000\n",
+ "Epoch:[ 4/ 5], step:[ 1875/ 1875], loss:[0.008/0.015], time:0.661 ms, lr:0.01000\n",
+ "Epoch time: 1584.785 ms, per step time: 0.845 ms, avg loss: 0.015\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindvision.engine.callback import LossMonitor\n",
+ "\n",
+ "# 开始训练,加载保存模型和参数回调函数,LossMonitor的入参0.01为学习率,375为步长\n",
+ "model.train(5, dataset_train, callbacks=[LossMonitor(0.01, 375)], dataset_sink_mode=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,MindSpore Vision套件提供的`LossMonitor`接口打印信息更加详细。由于步长设置的是375,所以每375个step会打印一条,loss值会波动,但总体来说loss值会逐步减小,精度逐步提高。\n",
+ "\n",
+ "## ValAccMonitor\n",
+ "\n",
+ "为了在训练过程中保存精度最优的网络模型和参数,需要边训练边验证,MindSpore Vision提供了`ValAccMonitor`接口。\n",
+ "\n",
+ "下面我们通过一段示例来介绍:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "--------------------\n",
+ "Epoch: [ 1 / 1], Train Loss: [0.000], Accuracy: 0.988\n",
+ "================================================================================\n",
+ "End of validation the best Accuracy is: 0.988, save the best ckpt file in ./best.ckpt\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindvision.engine.callback import ValAccMonitor\n",
+ "\n",
+ "download_eval = Mnist(path=\"./mnist\", split=\"test\", download=True)\n",
+ "dataset_eval = download_eval.run()\n",
+ "\n",
+ "# 开始训练,加载保存模型和参数回调函数\n",
+ "model.train(1, dataset_train, callbacks=[ValAccMonitor(model, dataset_eval, num_epochs=1)])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上面代码执行后,精度最优的网络模型和参数会被保存在当前目录下,文件名为\"best.ckpt\"。\n",
+ "\n",
+ "## 自定义回调\n",
+ "\n",
+ "MindSpore不仅有功能强大的内置回调函数,当用户有自己的特殊需求时,还可以基于`Callback`基类自定义回调类。\n",
+ "\n",
+ "用户可以基于`Callback`基类,根据自身的需求,实现自定义`Callback`。`Callback`基类定义如下所示:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class Callback():\n",
+ " \"\"\"Callback base class\"\"\"\n",
+ " def begin(self, run_context):\n",
+ " \"\"\"Called once before the network executing.\"\"\"\n",
+ " pass # pylint: disable=W0107\n",
+ "\n",
+ " def epoch_begin(self, run_context):\n",
+ " \"\"\"Called before each epoch beginning.\"\"\"\n",
+ " pass # pylint: disable=W0107\n",
+ "\n",
+ " def epoch_end(self, run_context):\n",
+ " \"\"\"Called after each epoch finished.\"\"\"\n",
+ " pass # pylint: disable=W0107\n",
+ "\n",
+ " def step_begin(self, run_context):\n",
+ " \"\"\"Called before each step beginning.\"\"\"\n",
+ " pass # pylint: disable=W0107\n",
+ "\n",
+ " def step_end(self, run_context):\n",
+ " \"\"\"Called after each step finished.\"\"\"\n",
+ " pass # pylint: disable=W0107\n",
+ "\n",
+ " def end(self, run_context):\n",
+ " \"\"\"Called once after network training.\"\"\"\n",
+ " pass # pylint: disable=W0107"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "回调机制可以把训练过程中的重要信息记录下来,通过把一个字典类型变量`RunContext.original_args()`,传递给Callback对象,使得用户可以在各个自定义的Callback中获取到相关属性,执行自定义操作,也可以自定义其他变量传递给`RunContext.original_args()`对象。\n",
+ "\n",
+ "`RunContext.original_args()`中的常用属性有:\n",
+ "\n",
+ "- epoch_num:训练的epoch的数量\n",
+ "- batch_num:一个epoch中step的数量\n",
+ "- cur_epoch_num:当前的epoch数\n",
+ "- cur_step_num:当前的step数\n",
+ "\n",
+ "- loss_fn:损失函数\n",
+ "- optimizer:优化器\n",
+ "- train_network:训练的网络\n",
+ "- train_dataset:训练的数据集\n",
+ "- net_outputs:网络的输出结果\n",
+ "\n",
+ "- parallel_mode:并行模式\n",
+ "- list_callback:所有的Callback函数\n",
+ "\n",
+ "通过下面两个场景,我们可以增加对自定义Callback回调机制功能的了解。\n",
+ "\n",
+ "### 自定义终止训练\n",
+ "\n",
+ "实现在规定时间内终止训练功能。用户可以设定时间阈值,当训练时间达到这个阈值后就终止训练过程。\n",
+ "\n",
+ "下面代码中,通过`run_context.original_args`方法可以获取到`cb_params`字典,字典里会包含前文描述的主要属性信息。\n",
+ "\n",
+ "同时可以对字典内的值进行修改和添加,在`begin`函数中定义一个`init_time`对象传递给`cb_params`字典。每个数据迭代结束`step_end`之后会进行判断,当训练时间大于设置的时间阈值时,会向run_context传递终止训练的信号,提前终止训练,并打印当前的epoch、step、loss的值。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Begin training, time is: 1648452437.2004516\n",
+ "Epoch:[ 0/ 5], step:[ 1875/ 1875], loss:[0.011/0.012], time:0.678 ms, lr:0.01000\n",
+ "Epoch time: 1603.104 ms, per step time: 0.855 ms, avg loss: 0.012\n",
+ "Epoch:[ 1/ 5], step:[ 1875/ 1875], loss:[0.000/0.011], time:0.688 ms, lr:0.01000\n",
+ "Epoch time: 1602.716 ms, per step time: 0.855 ms, avg loss: 0.011\n",
+ "End training, time: 1648452441.20081 ,epoch: 3 ,step: 4673 ,loss: 0.014888153\n",
+ "Epoch time: 792.901 ms, per step time: 0.423 ms, avg loss: 0.010\n"
+ ]
+ }
+ ],
+ "source": [
+ "import time\n",
+ "from mindspore.train.callback import Callback\n",
+ "\n",
+ "class StopTimeMonitor(Callback):\n",
+ "\n",
+ " def __init__(self, run_time):\n",
+ " \"\"\"定义初始化过程\"\"\"\n",
+ " super(StopTimeMonitor, self).__init__()\n",
+ " self.run_time = run_time # 定义执行时间\n",
+ "\n",
+ " def begin(self, run_context):\n",
+ " \"\"\"开始训练时的操作\"\"\"\n",
+ " cb_params = run_context.original_args()\n",
+ " cb_params.init_time = time.time() # 获取当前时间戳作为开始训练时间\n",
+ " print(\"Begin training, time is:\", cb_params.init_time)\n",
+ "\n",
+ " def step_end(self, run_context):\n",
+ " \"\"\"每个step结束后执行的操作\"\"\"\n",
+ " cb_params = run_context.original_args()\n",
+ " epoch_num = cb_params.cur_epoch_num # 获取epoch值\n",
+ " step_num = cb_params.cur_step_num # 获取step值\n",
+ " loss = cb_params.net_outputs # 获取损失值loss\n",
+ " cur_time = time.time() # 获取当前时间戳\n",
+ "\n",
+ " if (cur_time - cb_params.init_time) > self.run_time:\n",
+ " print(\"End training, time:\", cur_time, \",epoch:\", epoch_num, \",step:\", step_num, \",loss:\", loss)\n",
+ " run_context.request_stop() # 停止训练\n",
+ "\n",
+ "model.train(5, dataset_train, callbacks=[LossMonitor(0.01, 1875), StopTimeMonitor(4)], dataset_sink_mode=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "从上面的打印结果可以看出,当第3个epoch的第4673个step执行完时,运行时间到达了阈值并结束了训练。\n",
+ "\n",
+ "### 设置阈值保存模型\n",
+ "\n",
+ "该回调机制实现当loss小于设定的阈值时,保存网络模型权重ckpt文件。\n",
+ "\n",
+ "示例代码如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Saved checkpoint, loss:0.0000001, current step num: 253.\n",
+ "Saved checkpoint, loss:0.0000005, current step num: 258.\n",
+ "Saved checkpoint, loss:0.0000001, current step num: 265.\n",
+ "Saved checkpoint, loss:0.0000000, current step num: 332.\n",
+ "Saved checkpoint, loss:0.0000003, current step num: 358.\n",
+ "Saved checkpoint, loss:0.0000003, current step num: 380.\n",
+ "Saved checkpoint, loss:0.0000003, current step num: 395.\n",
+ "Saved checkpoint, loss:0.0000005, current step num:1151.\n",
+ "Saved checkpoint, loss:0.0000005, current step num:1358.\n",
+ "Saved checkpoint, loss:0.0000002, current step num:1524.\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindspore import save_checkpoint\n",
+ "from mindspore.train.callback import Callback\n",
+ "\n",
+ "# 定义保存ckpt文件的回调接口\n",
+ "class SaveCkptMonitor(Callback):\n",
+ " \"\"\"定义初始化过程\"\"\"\n",
+ " def __init__(self, loss):\n",
+ " super(SaveCkptMonitor, self).__init__()\n",
+ " self.loss = loss # 定义损失值阈值\n",
+ "\n",
+ " def step_end(self, run_context):\n",
+ " \"\"\"定义step结束时的执行操作\"\"\"\n",
+ " cb_params = run_context.original_args()\n",
+ " cur_loss = cb_params.net_outputs.asnumpy() # 获取当前损失值\n",
+ "\n",
+ " # 如果当前损失值小于设定的阈值就停止训练\n",
+ " if cur_loss < self.loss:\n",
+ " # 自定义保存文件名\n",
+ " file_name = str(cb_params.cur_epoch_num) + \"_\" + str(cb_params.cur_step_num) + \".ckpt\"\n",
+ " # 保存网络模型\n",
+ " save_checkpoint(save_obj=cb_params.train_network, ckpt_file_name=file_name)\n",
+ "\n",
+ " print(\"Saved checkpoint, loss:{:8.7f}, current step num:{:4}.\".format(cur_loss, cb_params.cur_step_num))\n",
+ "\n",
+ "model.train(1, dataset_train, callbacks=[SaveCkptMonitor(5e-7)], dataset_sink_mode=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "保存目录结构如下:\n",
+ "\n",
+ "```text\n",
+ "./\n",
+ "├── 1_253.ckpt\n",
+ "├── 1_258.ckpt\n",
+ "├── 1_265.ckpt\n",
+ "├── 1_332.ckpt\n",
+ "├── 1_358.ckpt\n",
+ "├── 1_380.ckpt\n",
+ "├── 1_395.ckpt\n",
+ "├── 1_1151.ckpt\n",
+ "├── 1_1358.ckpt\n",
+ "├── 1_1524.ckpt\n",
+ "```"
+ ]
+ }
+ ],
+ "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.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/tutorials/source_zh_cn/advanced/train/images/model.graffle b/tutorials/source_zh_cn/advanced/train/images/model.graffle
new file mode 100644
index 0000000000000000000000000000000000000000..9c1f0156516fefd3681319a8cbe6f93d2dfcac90
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/train/images/model.graffle differ
diff --git a/tutorials/source_zh_cn/advanced/train/images/model.png b/tutorials/source_zh_cn/advanced/train/images/model.png
new file mode 100644
index 0000000000000000000000000000000000000000..94c13801ee61dff03bf314289df7d2551ae05fb1
Binary files /dev/null and b/tutorials/source_zh_cn/advanced/train/images/model.png differ
diff --git a/tutorials/source_zh_cn/advanced/train/metric.ipynb b/tutorials/source_zh_cn/advanced/train/metric.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..e67848955de41b8bdfc033873633340b4b592091
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/train/metric.ipynb
@@ -0,0 +1,281 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 评价指标\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_metric.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_metric.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/train/metric.ipynb)\n",
+ "\n",
+ "当训练任务结束,常常需要评价函数(Metrics)来评估模型的好坏。不同的训练任务往往需要不同的Metrics函数。例如,对于二分类问题,常用的评价指标有precision(准确率)、recall(召回率)等,而对于多分类任务,可使用宏平均(Macro)和微平均(Micro)来评估。\n",
+ "\n",
+ "MindSpore提供了大部分常见任务的评价函数,如`nn.Accuracy`、`nn.Pecision`、`nn.MAE`和`nn.MSE`等,由于MindSpore提供的评价函数无法满足所有任务的需求,很多情况下用户需要针对具体的任务自定义Metrics来评估训练的模型。\n",
+ "\n",
+ "本章主要介绍如何自定义Metrics以及如何在`nn.Model`中使用Metrics。\n",
+ "\n",
+ "> 详情可参考[评价指标](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id14)。\n",
+ "\n",
+ "## 自定义Metrics\n",
+ "\n",
+ "自定义Metrics函数需要继承`nn.Metric`父类,并重新实现父类中的`clear`方法、`update`方法和`eval`方法。\n",
+ "\n",
+ "- `clear`:初始化相关的内部参数。\n",
+ "- `update`:接收网络预测输出和标签,计算误差,每次step后并更新内部评估结果。\n",
+ "- `eval`:计算最终评估结果,在没次epoch结束后计算最终的评估结果。\n",
+ "\n",
+ "值得注意的是,`update`中如果用户评估网络有多个输出,但只用两个输出进行评估,此时可以使用`set_indexes`方法重排`update`的输入用于计算评估指标。使用`set_indexes`方法,需要用装饰器`nn.rearrange_inputs`修饰`update`方法,否则使用set_indexes配置的输入不生效。\n",
+ "\n",
+ "平均绝对误差(MAE)算法如式(1)所示:\n",
+ "\n",
+ "$$ MAE=\\frac{1}{n}\\sum_{i=1}^n\\lvert ypred_i - y_i \\rvert \\tag{1}$$\n",
+ "\n",
+ "下面以简单的MAE算法为例,介绍`clear`、`update`和`eval`三个函数及其使用方法。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T11:04:40.488396Z",
+ "start_time": "2022-01-04T11:04:38.090036Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "output(x,y): 0.1499999612569809\n",
+ "output(x,z): 0.24999992549419403\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore as ms\n",
+ "from mindspore import Tensor\n",
+ "import mindspore.nn as nn\n",
+ "\n",
+ "class MAE(nn.Metric):\n",
+ " def __init__(self):\n",
+ " super(MAE, self).__init__()\n",
+ " self.clear()\n",
+ "\n",
+ " def clear(self):\n",
+ " \"\"\"初始化变量_abs_error_sum和_samples_num\"\"\"\n",
+ " self._abs_error_sum = 0 # 保存误差和\n",
+ " self._samples_num = 0 # 累计数据量\n",
+ "\n",
+ " @nn.rearrange_inputs\n",
+ " def update(self, *inputs):\n",
+ " \"\"\"更新_abs_error_sum和_samples_num\"\"\"\n",
+ " y_pred = inputs[0].asnumpy()\n",
+ " y = inputs[1].asnumpy()\n",
+ "\n",
+ " # 计算预测值与真实值的绝对误差\n",
+ " abs_error_sum = np.abs(y.reshape(y_pred.shape) - y_pred)\n",
+ " self._abs_error_sum += abs_error_sum.sum()\n",
+ "\n",
+ " # 样本的总数\n",
+ " self._samples_num += y.shape[0]\n",
+ "\n",
+ " def eval(self):\n",
+ " \"\"\"计算最终评估结果\"\"\"\n",
+ " return self._abs_error_sum / self._samples_num\n",
+ "\n",
+ "# 网络有两个输出:x,y\n",
+ "x = Tensor(np.array([[0.1, 0.2, 0.6, 0.9], [0.1, 0.2, 0.6, 0.9]]), ms.float32)\n",
+ "y = Tensor(np.array([[0.1, 0.25, 0.7, 0.9], [0.1, 0.25, 0.7, 0.9]]), ms.float32)\n",
+ "error = MAE()\n",
+ "error.clear()\n",
+ "error.update(x, y)\n",
+ "result = error.eval()\n",
+ "print(\"output(x,y):\", result)\n",
+ "\n",
+ "# 网络有三个输出:x,y,z\n",
+ "z = Tensor(np.array([[0.1, 0.25, 0.7, 0.8], [0.1, 0.25, 0.7, 0.8]]), ms.float32)\n",
+ "\n",
+ "# 设置使用x,z进行评估\n",
+ "error = MAE().set_indexes([0, 2])\n",
+ "error.clear()\n",
+ "error.update(x, y, z)\n",
+ "result = error.eval()\n",
+ "print(\"output(x,z):\", result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 模型训练中使用Metrics\n",
+ "\n",
+ "[mindspore.Model](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore/mindspore.Model.html#mindspore.Model)是用于训练和评估的高层API,可以将自定义或MindSpore已有的Metrics作为参数传入,Model能够自动调用传入的Metrics进行评估。\n",
+ "\n",
+ "在网络模型训练后,需要使用评价指标,来评估网络模型的训练效果,因此在演示具体代码之前首先简单拟定数据集,对数据集进行加载和定义一个简单的线性回归网络模型:\n",
+ "\n",
+ "$$f(x)=w*x+b \\tag{2}$$"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Model\n",
+ "from mindspore import dataset as ds\n",
+ "from mindspore.common.initializer import Normal\n",
+ "\n",
+ "def get_data(num, w=2.0, b=3.0):\n",
+ " \"\"\"生成数据及对应标签\"\"\"\n",
+ " for _ in range(num):\n",
+ " x = np.random.uniform(-10.0, 10.0)\n",
+ " noise = np.random.normal(0, 1)\n",
+ " y = x * w + b + noise\n",
+ " yield np.array([x]).astype(np.float32), np.array([y]).astype(np.float32)\n",
+ "\n",
+ "def create_dataset(num_data, batch_size=16):\n",
+ " \"\"\"加载数据集\"\"\"\n",
+ " dataset = ds.GeneratorDataset(list(get_data(num_data)), column_names=['data', 'label'])\n",
+ " dataset = dataset.batch(batch_size)\n",
+ " return dataset\n",
+ "\n",
+ "class LinearNet(nn.Cell):\n",
+ " \"\"\"定义线性回归网络\"\"\"\n",
+ " def __init__(self):\n",
+ " super(LinearNet, self).__init__()\n",
+ " self.fc = nn.Dense(1, 1, Normal(0.02), Normal(0.02))\n",
+ "\n",
+ " def construct(self, x):\n",
+ " return self.fc(x)\n",
+ "\n",
+ "loss = nn.L1Loss()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Model使用内置评价指标\n",
+ "\n",
+ "使用MindSpore内置的Metrics作为参数传入Model时,Metrics可以定义为一个字典类型,字典的key值为字符串类型,字典的value值为MindSpore内置的[评价指标](https://www.mindspore.cn/docs/api/zh-CN/master/api_python/mindspore.nn.html#id14),如下示例使用`nn.Accuracy`计算分类的准确率。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch:[ 0/ 1], step:[ 1/ 10], loss:[10.545/10.545], time:125.129 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 2/ 10], loss:[10.139/10.342], time:0.344 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 3/ 10], loss:[10.932/10.539], time:0.268 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 4/ 10], loss:[9.591/10.302], time:0.280 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 5/ 10], loss:[9.971/10.236], time:0.248 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 6/ 10], loss:[7.491/9.778], time:0.275 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 7/ 10], loss:[8.217/9.555], time:0.238 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 8/ 10], loss:[10.609/9.687], time:0.209 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 9/ 10], loss:[7.562/9.451], time:0.216 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 10/ 10], loss:[6.351/9.141], time:0.201 ms, lr:0.01000\n",
+ "Epoch time: 133.117 ms, per step time: 13.312 ms, avg loss: 9.141\n",
+ "{'MAE': 5.60994963645935}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.nn as nn\n",
+ "from mindspore import Model\n",
+ "from mindvision.engine.callback import LossMonitor\n",
+ "\n",
+ "ds_train = create_dataset(num_data=160)\n",
+ "net = LinearNet()\n",
+ "opt = nn.Momentum(net.trainable_params(), learning_rate=0.005, momentum=0.9)\n",
+ "# 定义model,使用内置的Accuracy函数\n",
+ "model = Model(net, loss, opt, metrics={\"MAE\": nn.MAE()})\n",
+ "model.train(epoch=1, train_dataset=ds_train, callbacks=LossMonitor(0.01))\n",
+ "\n",
+ "# 模型评估\n",
+ "ds_eval = create_dataset(num_data=160)\n",
+ "output = model.eval(ds_eval)\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### Model使用自定义评价指标\n",
+ "\n",
+ "如下示例在`Model`中传入上述自定义的评估指标`MAE()`,将验证数据集传入`model.eval()`接口进行验证。\n",
+ "\n",
+ "验证结果为一个字典类型,验证结果的key值与`metrics`的key值相同,验证结果的value值为预测值与实际值的平均绝对误差。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch:[ 0/ 1], step:[ 1/ 10], loss:[11.125/11.125], time:62.274 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 2/ 10], loss:[11.778/11.452], time:0.385 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 3/ 10], loss:[9.193/10.699], time:0.278 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 4/ 10], loss:[8.846/10.235], time:0.271 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 5/ 10], loss:[5.526/9.293], time:0.258 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 6/ 10], loss:[10.185/9.442], time:0.314 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 7/ 10], loss:[6.545/9.028], time:0.250 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 8/ 10], loss:[6.508/8.713], time:0.310 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 9/ 10], loss:[5.936/8.405], time:0.320 ms, lr:0.01000\n",
+ "Epoch:[ 0/ 1], step:[ 10/ 10], loss:[6.866/8.251], time:0.361 ms, lr:0.01000\n",
+ "Epoch time: 71.295 ms, per step time: 7.129 ms, avg loss: 8.251\n",
+ "{'MAE': 5.989781427383423}\n"
+ ]
+ }
+ ],
+ "source": [
+ "ds_train = create_dataset(num_data=160)\n",
+ "net1 = LinearNet()\n",
+ "opt = nn.Momentum(net1.trainable_params(), learning_rate=0.005, momentum=0.9)\n",
+ "# 定义model,将自定义metrics函数MAE传入Model中\n",
+ "model1 = Model(net1, loss, opt, metrics={\"MAE\": MAE()})\n",
+ "model1.train(epoch=1, train_dataset=ds_train, callbacks=LossMonitor(0.01))\n",
+ "\n",
+ "# 模型评估\n",
+ "ds_eval = create_dataset(num_data=160)\n",
+ "output = model1.eval(ds_eval)\n",
+ "print(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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/train/model.ipynb b/tutorials/source_zh_cn/advanced/train/model.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..ba1ae646f31ccc23908114f52dad355fe6895c4d
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/train/model.ipynb
@@ -0,0 +1,565 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Model基本使用\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_model.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_model.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/train/model.ipynb)\n",
+ "\n",
+ "通常情况下,定义训练和评估网络并直接运行,已经可以满足基本需求。\n",
+ "\n",
+ "一方面,`Model`可以在一定程度上简化代码。例如:无需手动遍历数据集;在不需要自定义`nn.TrainOneStepCell`的场景下,可以借助`Model`自动构建训练网络;可以使用`Model`的`eval`接口进行模型评估,直接输出评估结果,无需手动调用评价指标的`clear`、`update`、`eval`函数等。\n",
+ "\n",
+ "另一方面,`Model`提供了很多高阶功能,如数据下沉、混合精度等,在不借助`Model`的情况下,使用这些功能需要花费较多的时间仿照`Model`进行自定义。\n",
+ "\n",
+ "本文档首先对MindSpore的Model进行基本介绍,然后重点讲解如何使用`Model`进行模型训练、评估和推理。\n",
+ "\n",
+ "\n",
+ "\n",
+ "## Model基本介绍\n",
+ "\n",
+ "[Model](https://mindspore.cn/docs/api/zh-CN/master/api_python/mindspore/mindspore.Model.html#mindspore.Model)是MindSpore提供的高阶API,可以进行模型训练、评估和推理。其接口的常用参数如下:\n",
+ "\n",
+ "- `network`:用于训练或推理的神经网络。\n",
+ "- `loss_fn`:所使用的损失函数。\n",
+ "- `optimizer`:所使用的优化器。\n",
+ "- `metrics`:用于模型评估的评价函数。\n",
+ "- `eval_network`:模型评估所使用的网络,未定义情况下,`Model`会使用`networ`和`loss_fn`进行封装。\n",
+ "\n",
+ "`Model`提供了以下接口用于模型训练、评估和推理:\n",
+ "\n",
+ "- `train`:用于在训练集上进行模型训练。\n",
+ "- `eval`:用于在验证集上进行模型评估。\n",
+ "- `predict`:用于对输入的一组数据进行推理,输出预测结果。\n",
+ "\n",
+ "### 使用Model接口\n",
+ "\n",
+ "对于简单场景的神经网络,可以在定义`Model`时指定前向网络`network`、损失函数`loss_fn`、优化器`optimizer`和评价函数`metrics`。\n",
+ "\n",
+ "此时,`Model`会使用`network`作为前向网络,并使用`nn.WithLossCell`和`nn.TrainOneStepCell`构建训练网络,使用`nn.WithEvalCell`构建评估网络。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T06:43:30.392367Z",
+ "start_time": "2022-01-04T06:43:28.436687Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Model\n",
+ "from mindspore.common.initializer import Normal\n",
+ "\n",
+ "def get_data(num, w=2.0, b=3.0):\n",
+ " \"\"\"生成样本数据及对应的标签\"\"\"\n",
+ " for _ in range(num):\n",
+ " x = np.random.uniform(-10.0, 10.0)\n",
+ " noise = np.random.normal(0, 1)\n",
+ " y = x * w + b + noise\n",
+ " yield np.array([x]).astype(np.float32), np.array([y]).astype(np.float32)\n",
+ "\n",
+ "def create_dataset(num_data, batch_size=16):\n",
+ " \"\"\"生成数据集\"\"\"\n",
+ " dataset = ds.GeneratorDataset(list(get_data(num_data)), column_names=['data', 'label'])\n",
+ " dataset = dataset.batch(batch_size)\n",
+ " return dataset\n",
+ "\n",
+ "class LinearNet(nn.Cell):\n",
+ " \"\"\"定义线性回归网络\"\"\"\n",
+ " def __init__(self):\n",
+ " super().__init__()\n",
+ " self.fc = nn.Dense(1, 1, Normal(0.02), Normal(0.02))\n",
+ "\n",
+ " def construct(self, x):\n",
+ " return self.fc(x)\n",
+ "\n",
+ "train_dataset = create_dataset(num_data=160)\n",
+ "net = LinearNet()\n",
+ "crit = nn.MSELoss()\n",
+ "opt = nn.Momentum(net.trainable_params(), learning_rate=0.005, momentum=0.9)\n",
+ "\n",
+ "# 使用Model构建训练网络\n",
+ "model = Model(network=net, loss_fn=crit, optimizer=opt, metrics={\"mae\"})"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 模型训练\n",
+ "\n",
+ "使用`train`接口执行模型训练,`train`接口的常用参数如下:\n",
+ "\n",
+ "- `epoch`:训练执行轮次,通常每个epoch都会使用全量数据集进行训练。\n",
+ "- `train_dataset`:一个训练数据集迭代器。\n",
+ "- `callbacks`:训练过程中需要执行的回调对象或者回调对象列表。\n",
+ "\n",
+ "有意思的是,如果网络模型定义了`loss_fn`,则数据和标签会被分别传给`network`和`loss_fn`,此时数据集需要返回一个元组(data, label)。如果数据集中有多个数据或者标签,可以设置`loss_fn`为None,并在`network`中实现自定义损失函数,此时数据集返回的所有数据组成的元组(data1, data2, data3, …)会传给`network`。\n",
+ "\n",
+ "如下示例使用`train`接口执行模型训练,通过`LossMonitor`回调函数查看在训练过程中的损失函数值。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T06:43:30.952190Z",
+ "start_time": "2022-01-04T06:43:30.525149Z"
+ },
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch:[ 0/ 1], step:[ 2/ 10], loss:[115.741/110.412], time:0.421 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 3/ 10], loss:[9.262/76.695], time:0.536 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 4/ 10], loss:[22.231/63.079], time:0.702 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 5/ 10], loss:[97.495/69.963], time:0.830 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 6/ 10], loss:[165.809/85.937], time:0.609 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 7/ 10], loss:[58.898/82.074], time:0.430 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 8/ 10], loss:[9.044/72.946], time:5.970 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 9/ 10], loss:[11.369/66.104], time:0.981 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 10/ 10], loss:[104.045/69.898], time:0.494 ms, lr:0.00500.\n",
+ "Epoch time: 181.851 ms, per step time: 18.185 ms, avg loss: 69.898\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindvision.engine.callback import LossMonitor\n",
+ "\n",
+ "# 模型训练,LossMonitor的入参0.005为学习率\n",
+ "model.train(1, train_dataset, callbacks=[LossMonitor(0.005)], dataset_sink_mode=False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 模型评估\n",
+ "\n",
+ "使用`eval`接口进行评估,`eval`接口参数如下:\n",
+ "\n",
+ "- `valid_dataset`:评估模型的数据集。\n",
+ "- `callbacks`:评估过程中需要执行的回调对象或回调对象列表。\n",
+ "- `dataset_sink_mode`:数据是否直接下沉至处理器进行处理。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T06:43:31.009605Z",
+ "start_time": "2022-01-04T06:43:30.954322Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'mae': 9.021200180053711}\n"
+ ]
+ }
+ ],
+ "source": [
+ "eval_dataset = create_dataset(num_data=80) # 创建评估数据集\n",
+ "eval_result = model.eval(eval_dataset) # 执行模型评估\n",
+ "print(eval_result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 模型推理\n",
+ "\n",
+ "使用`predict`接口进行推理,`predict`接口参数如下:\n",
+ "\n",
+ "- `predict_data`:预测样本,数据可以是单个张量、张量列表或张量元组。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T06:43:31.042129Z",
+ "start_time": "2022-01-04T06:43:31.011659Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[-3.8758864 ]\n",
+ " [ 0.2055009 ]\n",
+ " [-1.1084781 ]\n",
+ " [ 4.781383 ]\n",
+ " [-2.9034617 ]\n",
+ " [ 3.240841 ]\n",
+ " [ 0.23157847]\n",
+ " [-1.4979365 ]\n",
+ " [ 1.0967402 ]\n",
+ " [ 5.702408 ]\n",
+ " [ 3.8641698 ]\n",
+ " [ 5.4022083 ]\n",
+ " [ 0.4161299 ]\n",
+ " [-1.1233463 ]\n",
+ " [-2.5322387 ]\n",
+ " [ 6.712381 ]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "eval_data = eval_dataset.create_dict_iterator()\n",
+ "data = next(eval_data)\n",
+ "# 执行模型预测\n",
+ "output = model.predict(data[\"data\"])\n",
+ "print(output)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "一般情况下需要对推理结果进行后处理才能得到比较直观的推理结果。\n",
+ "\n",
+ "## 自定义场景\n",
+ "\n",
+ "MindSpore提供的网络封装函数`nn.WithLossCell`、`nn.TrainOneStepCell`和`nn.WithEvalCell`并不适用于所有场景,实际场景中常常需要自定义网络的封装函数,这种情况下`Model`使用这些封装函数自动地进行封装显然是不合理的。\n",
+ "\n",
+ "接下来介绍在自定义网络封装函数时如何正确地使用`Model`。"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 自定义损失网络\n",
+ "\n",
+ "在有多个数据或者多个标签的场景下,可以使用自定义损失网络将前向网络和自定义的损失函数链接起来作为`Model`的`network`,`loss_fn`使用默认值`None`,此时`Model`内部不会经过`nn.WithLossCell`,而会直接使用`nn.TrainOneStepCell`将`network`与`optimizer`组成训练网络。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T06:43:31.051039Z",
+ "start_time": "2022-01-04T06:43:31.043718Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch:[ 0/ 1], step:[ 1/ 10], loss:[8.382/8.382], time:119.369 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 2/ 10], loss:[8.710/8.546], time:0.521 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 3/ 10], loss:[6.140/7.744], time:6.315 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 4/ 10], loss:[9.578/8.202], time:1.420 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 5/ 10], loss:[10.786/8.719], time:0.958 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 6/ 10], loss:[9.874/8.912], time:1.501 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 7/ 10], loss:[4.433/8.272], time:9.982 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 8/ 10], loss:[6.723/8.078], time:2.540 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 9/ 10], loss:[8.435/8.118], time:1.258 ms, lr:0.00500.\n",
+ "Epoch:[ 0/ 1], step:[ 10/ 10], loss:[7.085/8.015], time:1.419 ms, lr:0.00500.\n",
+ "Epoch time: 153.744 ms, per step time: 15.374 ms, avg loss: 8.015\n"
+ ]
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.dataset as ds\n",
+ "import mindspore.ops as ops\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore import Model\n",
+ "from mindspore.nn import LossBase\n",
+ "from mindvision.engine.callback import LossMonitor\n",
+ "\n",
+ "def get_multilabel_data(num, w=2.0, b=3.0):\n",
+ " \"\"\"生成多标签数据,产生一组数据x对应两个标签y1和y2\"\"\"\n",
+ " for _ in range(num):\n",
+ " x = np.random.uniform(-10.0, 10.0)\n",
+ " noise1 = np.random.normal(0, 1)\n",
+ " noise2 = np.random.normal(-1, 1)\n",
+ " y1 = x * w + b + noise1\n",
+ " y2 = x * w + b + noise2\n",
+ " yield np.array([x]).astype(np.float32), np.array([y1]).astype(np.float32), np.array([y2]).astype(np.float32)\n",
+ "\n",
+ "def create_multilabel_dataset(num_data, batch_size=16):\n",
+ " \"\"\"生成多标签数据集,一个数据data对应两个标签label1和label2\"\"\"\n",
+ " dataset = ds.GeneratorDataset(list(get_multilabel_data(num_data)), column_names=['data', 'label1', 'label2'])\n",
+ " dataset = dataset.batch(batch_size)\n",
+ " return dataset\n",
+ "\n",
+ "class L1LossForMultiLabel(LossBase):\n",
+ " \"\"\"自定义多标签损失函数\"\"\"\n",
+ "\n",
+ " def __init__(self, reduction=\"mean\"):\n",
+ " super(L1LossForMultiLabel, self).__init__(reduction)\n",
+ " self.abs = ops.Abs()\n",
+ "\n",
+ " def construct(self, base, target1, target2):\n",
+ " \"\"\"输入有三个,分别为预测值base,真实值target1和target2\"\"\"\n",
+ " x1 = self.abs(base - target1)\n",
+ " x2 = self.abs(base - target2)\n",
+ " return self.get_loss(x1) / 2 + self.get_loss(x2) / 2\n",
+ "\n",
+ "class CustomWithLossCell(nn.Cell):\n",
+ " \"\"\"连接前向网络和损失函数\"\"\"\n",
+ "\n",
+ " def __init__(self, backbone, loss_fn):\n",
+ " \"\"\"输入有两个,前向网络backbone和损失函数loss_fn\"\"\"\n",
+ " super(CustomWithLossCell, self).__init__(auto_prefix=False)\n",
+ " self._backbone = backbone\n",
+ " self._loss_fn = loss_fn\n",
+ "\n",
+ " def construct(self, data, label1, label2):\n",
+ " output = self._backbone(data) # 前向计算得到网络输出\n",
+ " return self._loss_fn(output, label1, label2) # 得到多标签损失值\n",
+ "\n",
+ "multi_train_dataset = create_multilabel_dataset(num_data=160)\n",
+ "\n",
+ "# 构建线性回归网络\n",
+ "net = LinearNet()\n",
+ "# 多标签损失函数\n",
+ "loss = L1LossForMultiLabel()\n",
+ "\n",
+ "# 连接线性回归网络和多标签损失函数\n",
+ "loss_net = CustomWithLossCell(net, loss)\n",
+ "opt = nn.Momentum(net.trainable_params(), learning_rate=0.005, momentum=0.9)\n",
+ "\n",
+ "# 使用Model连接网络和优化器,此时Model内部不经过nn.WithLossCell\n",
+ "model = Model(network=loss_net, optimizer=opt)\n",
+ "# 使用train接口进行模型训练\n",
+ "model.train(epoch=1, train_dataset=multi_train_dataset, callbacks=[LossMonitor(0.005)])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 自定义训练网络\n",
+ "\n",
+ "在自定义训练网络时,需要手动构建训练网络作为`Model`的`network`,`loss_fn`和`optimizer`均使用默认值`None`,此时`Model`会使用`network`作为训练网络,而不会进行任何封装。\n",
+ "\n",
+ "如下示例自定义训练网络`CustomTrainOneStepCell`,然后通过`Model`接口构建训练网络。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch:[ 0/ 1], step:[ 1/ 10], loss:[6.055/6.055], time:111.966, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 2/ 10], loss:[3.870/4.963], time:2.944, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 3/ 10], loss:[2.912/4.279], time:2.560, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 4/ 10], loss:[2.473/3.828], time:1.305, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 5/ 10], loss:[3.031/3.668], time:1.549, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 6/ 10], loss:[4.278/3.770], time:0.837, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 7/ 10], loss:[4.222/3.834], time:0.680, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 8/ 10], loss:[3.250/3.761], time:0.720, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 9/ 10], loss:[3.961/3.784], time:0.719, lr:0.01000.\n",
+ "Epoch:[ 0/ 1], step:[ 10/ 10], loss:[3.874/3.793], time:6.948, lr:0.01000.\n",
+ "Epoch time: 138.377, per step time: 13.838, avg loss: 3.793\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.ops as ops\n",
+ "from mindspore import Model\n",
+ "from mindvision.engine.callback import LossMonitor\n",
+ "\n",
+ "class CustomTrainOneStepCell(nn.Cell):\n",
+ " \"\"\"自定义训练网络\"\"\"\n",
+ "\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 # 定义优化器\n",
+ " self.weights = self.optimizer.parameters # 待更新参数\n",
+ " self.grad = ops.GradOperation(get_by_list=True, sens_param=True) # 反向传播获取梯度\n",
+ "\n",
+ " def construct(self, *inputs):\n",
+ " loss = self.network(*inputs) # 执行前向网络,计算当前输入的损失函数值\n",
+ " grads = self.grad(self.network, self.weights)(*inputs, loss) # 进行反向传播,计算梯度\n",
+ " loss = ops.depend(loss, self.optimizer(grads)) # 使用优化器更新梯度\n",
+ " return loss\n",
+ "\n",
+ "multi_train_ds = create_multilabel_dataset(num_data=160)\n",
+ "\n",
+ "# 手动构建训练网络\n",
+ "train_net = CustomTrainOneStepCell(loss_net, opt)\n",
+ "# 构建训练网络\n",
+ "model = Model(train_net)\n",
+ "# 执行模型训练\n",
+ "model.train(epoch=1, train_dataset=multi_train_ds, callbacks=[LossMonitor(0.01)])"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 自定义评估网络\n",
+ "\n",
+ "`Model`默认使用`nn.WithEvalCell`构建评估网络,在不满足需求的情况下需要手动构建评估网络,如多数据和多标签场景下。\n",
+ "\n",
+ "如下示例自定义评估网络`CustomWithEvalCell`,然后使用`Model`接口构建评估网络。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T06:43:31.373188Z",
+ "start_time": "2022-01-04T06:43:31.369046Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "{'mae1': 5.182377433776855, 'mae2': 4.988385105133057}\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.nn as nn\n",
+ "from mindspore.train.callback import LossMonitor\n",
+ "from mindspore import Model\n",
+ "\n",
+ "\n",
+ "class CustomWithEvalCell(nn.Cell):\n",
+ " \"\"\"自定义多标签评估网络\"\"\"\n",
+ "\n",
+ " def __init__(self, network):\n",
+ " super(CustomWithEvalCell, self).__init__(auto_prefix=False)\n",
+ " self.network = network\n",
+ "\n",
+ " def construct(self, data, label1, label2):\n",
+ " output = self.network(data)\n",
+ " return output, label1, label2\n",
+ "\n",
+ "# 构建多标签评估数据集\n",
+ "multi_eval_dataset = create_multilabel_dataset(num_data=80)\n",
+ "\n",
+ "# 构建评估网络\n",
+ "eval_net = CustomWithEvalCell(net)\n",
+ "\n",
+ "# 评估函数\n",
+ "mae1 = nn.MAE()\n",
+ "mae2 = nn.MAE()\n",
+ "mae1.set_indexes([0, 1])\n",
+ "mae2.set_indexes([0, 2])\n",
+ "\n",
+ "# 使用Model构建评估网络\n",
+ "model = Model(network=loss_net, optimizer=opt, eval_network=eval_net, metrics={\"mae1\": mae1, \"mae2\": mae2})\n",
+ "result = model.eval(multi_eval_dataset)\n",
+ "print(result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "上述代码在进行模型评估时,评估网络的输出会透传给评估指标的`update`函数,`update`函数将接收到三个输入,分别为`logits`、`label1`和`label2`。\n",
+ "\n",
+ "`nn.MAE`仅允许在两个输入上计算评价指标,因此使用`set_indexes`指定`mae1`使用下标为0和1的输入,也就是`logits`和`label1`,计算评估结果;指定`mae2`使用下标为0和2的输入,也就是`logits`和`label2`,计算评估结果。"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 网络推理\n",
+ "\n",
+ " `Model`没有提供用于指定自定义推理网络的参数,此时可以直接运行前向网络获得推理结果。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2022-01-04T06:43:31.481326Z",
+ "start_time": "2022-01-04T06:43:31.449243Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "[[-21.598358 ]\n",
+ " [ -1.0123782]\n",
+ " [ 10.457726 ]\n",
+ " [ 12.409237 ]\n",
+ " [ 19.666183 ]\n",
+ " [ -5.846529 ]\n",
+ " [ 9.387393 ]\n",
+ " [ 2.6558673]\n",
+ " [-15.15129 ]\n",
+ " [-14.876989 ]\n",
+ " [ 19.112661 ]\n",
+ " [ 22.647848 ]\n",
+ " [ 4.9035554]\n",
+ " [ 20.119627 ]\n",
+ " [ -8.339532 ]\n",
+ " [ -2.7513359]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "for d in multi_eval_dataset.create_dict_iterator():\n",
+ " data = d[\"data\"]\n",
+ " break\n",
+ "\n",
+ "output = net(data)\n",
+ "print(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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/advanced/train/save.ipynb b/tutorials/source_zh_cn/advanced/train/save.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..9572dca1030080aa2aedd8d3f1b17da771713814
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/train/save.ipynb
@@ -0,0 +1,374 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "\n",
+ "# 模型保存与导出\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_save.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_save.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/train/save.ipynb)\n",
+ "\n",
+ "在模型训练过程中,可以添加检查点(CheckPoint)用于保存模型的参数,以便执行推理及再训练使用。如果想继续在不同硬件平台上做推理,可通过网络和CheckPoint格式文件生成对应的MindIR、AIR和ONNX格式文件。\n",
+ "\n",
+ "- **CheckPoint**:采用了Protocol Buffers机制,存储了网络中的所有的参数值。一般用于训练任务中断后恢复训练,或训练后的微调(Fine Tune)任务中。\n",
+ "- **MindIR**:全称MindSpore IR,是MindSpore的一种基于图表示的函数式IR,定义了可扩展的图结构以及算子的IR表示,同时存储了网络结构和权重参数值。它消除了不同后端的模型差异,一般用于跨硬件平台执行推理任务,比如把在Ascend 910训练好的模型,放在Ascend 310、GPU以及MindSpore Lite端侧上执行推理。\n",
+ "- **AIR**:全称Ascend Intermediate Representation,是华为定义的针对机器学习所设计的开放式的文件格式,同时存储了网络结构和权重参数值,能更好地适配Ascend AI处理器。一般用于Ascend 310上执行推理任务。\n",
+ "- **ONNX**:全称Open Neural Network Exchange,是一种针对机器学习所设计的开放式的文件格式,同时存储了网络结构和权重参数值。一般用于不同框架间的模型迁移或在推理引擎(TensorRT)上使用。\n",
+ "\n",
+ "本章主要介绍如何保存CheckPoint格式文件和导出MindIR、AIR和ONNX格式文件的方法。\n",
+ "\n",
+ "## 保存模型\n",
+ "\n",
+ "初级教程的[保存与加载章节](https://mindspore.cn/tutorials/zh-CN/master/beginner/save_load.html)已经介绍了使用`save_checkpoint`直接保存模型参数和使用Callback机制在训练过程中保存模型参数方法。本节将进一步介绍在训练过程中保存模型参数和使用`save_checkpoint`直接保存模型参数的方法。\n",
+ "\n",
+ "### 训练过程保存模型\n",
+ "\n",
+ "在训练过程中保存模型参数,MindSpore提供了两种保存策略,迭代策略和时间策略,可以通过创建`CheckpointConfig`对象设置相应策略。迭代策略和时间策略不能同时使用,其中迭代策略优先级高于时间策略,当同时设置时,只有迭代策略可以生效。当参数显示设置为None时,表示放弃该策略。另外,当训练过程中发生异常时,MindSpore也提供了断点续训功能,即在异常发生时系统会自动保存异常发生时的CheckPoint文件。\n",
+ "\n",
+ "1. 迭代策略\n",
+ "\n",
+ "`CheckpointConfig`中可根据迭代的次数进行配置,配置迭代策略的参数如下:\n",
+ "\n",
+ "- `save_checkpoint_steps`:表示每隔多少个step保存一个CheckPoint文件,默认值为1。\n",
+ "- `keep_checkpoint_max`:表示最多保存多少个CheckPoint文件,默认值为5。\n",
+ "\n",
+ "```Python\n",
+ "from mindspore.train.callback import CheckpointConfig\n",
+ "\n",
+ "# 每隔32个step保存一个CheckPoint文件,且最多保存10个CheckPoint文件\n",
+ "config_ck = CheckpointConfig(save_checkpoint_steps=32, keep_checkpoint_max=10)\n",
+ "```\n",
+ "\n",
+ "在迭代策略脚本正常结束的情况下,会默认保存最后一个step的CheckPoint文件。\n",
+ "\n",
+ "2. 时间策略\n",
+ "\n",
+ "`CheckpointConfig`中可根据训练的时长进行配置,配置时间策略的参数如下:\n",
+ "\n",
+ "- `save_checkpoint_seconds`:表示每隔多少秒保存一个CheckPoint文件,默认值为0。\n",
+ "- `keep_checkpoint_per_n_minutes`:表示每隔多少分钟保留一个CheckPoint文件,默认值为0。\n",
+ "\n",
+ "```Python\n",
+ "from mindspore.train.callback import CheckpointConfig\n",
+ "\n",
+ "# 每隔30秒保存一个CheckPoint文件,每隔3分钟保留一个CheckPoint文件\n",
+ "config_ck = CheckpointConfig(save_checkpoint_seconds=30, keep_checkpoint_per_n_minutes=3)\n",
+ "```\n",
+ "\n",
+ "`save_checkpoint_seconds`参数不可与`save_checkpoint_steps`参数一起使用。如果同时设置了两个参数,则`save_checkpoint_seconds`参数无效。\n",
+ "\n",
+ "3. 断点续训\n",
+ "\n",
+ "MindSpore提供了断点续训的功能,当用户开启该功能时,如果在训练过程中发生了异常,那么MindSpore会自动保存异常发生时的CheckPoint文件(临终CheckPoint)。断点续训的功能通过CheckpointConfig中的`exception_save`参数(bool类型)控制,设置为True时开启该功能,False关闭该功能,默认为False。断点续训功能保存的临终CheckPoint文件与正常流程保存的CheckPoint互不影响,命名机制和保存路径与正常流程设置保持一致,唯一不同之处在于会在临终CheckPoint文件名最后加上’_breakpoint’进行区分。其用法如下:\n",
+ "\n",
+ "```Python\n",
+ "from mindspore.train.callback import ModelCheckpoint, CheckpointConfig\n",
+ "\n",
+ "# 配置断点续训功能开启\n",
+ "config_ck = CheckpointConfig(save_checkpoint_steps=32, keep_checkpoint_max=10, exception_save=True)\n",
+ "```\n",
+ "\n",
+ "如果在训练过程中发生了异常,那么会自动保存临终CheckPoint,假如在训练中的第10个epoch的第10个step中发生异常,保存的临终CheckPoint文件如下。\n",
+ "\n",
+ "```Python\n",
+ "resnet50-10_10_breakpoint.ckpt # 临终CheckPoint文件名最后会加上'_breakpoint'与正常流程CheckPoint区分开。\n",
+ "```\n",
+ "\n",
+ "### save_checkpoint保存模型\n",
+ "\n",
+ "可以使用`save_checkpoint`函数把网络权重保存到CheckPoint文件,常用参数如下所示:\n",
+ "\n",
+ "- `save_obj`:Cell对象或者数据列表。\n",
+ "- `ckpt_file_name`: checkpoint文件名称。如果文件已存在,将会覆盖原有文件。\n",
+ "- `integrated_save`:在并行场景下是否合并保存拆分的Tensor。默认值为True。\n",
+ "- `async_save`:是否异步执行保存checkpoint文件。默认值为False。\n",
+ "- `append_dict`:需要保存的其他信息。dict的键必须为str类型,dict的值类型必须是float或者bool类型。默认值为None。\n",
+ "\n",
+ "1. `save_obj`参数\n",
+ "\n",
+ "初级教程的[保存与加载章节](https://mindspore.cn/tutorials/zh-CN/master/beginner/save_load.html#%E4%BF%9D%E5%AD%98%E6%A8%A1%E5%9E%8B)已经介绍了当`save_obj`为Cell对象时,如何使用`save_checkpoint`直接保存模型参数。下面介绍当传入数据列表时,如何保存模型参数。传入数据列表时,列表的每个元素为字典类型,比如[{“name”: param_name, “data”: param_data},…], `param_name`的类型必须是str,`param_data`的类型必须是Parameter或者Tensor。示例如下所示:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from mindspore import save_checkpoint, Tensor\n",
+ "from mindspore import dtype as mstype\n",
+ "\n",
+ "save_list = [{\"name\": \"lr\", \"data\": Tensor(0.01, mstype.float32)}, {\"name\": \"train_epoch\", \"data\": Tensor(20, mstype.int32)}]\n",
+ "save_checkpoint(save_list, \"hyper_param.ckpt\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "2. `integrated_save`参数\n",
+ "\n",
+ "表示参数是否合并保存,默认为True。在模型并行场景下,Tensor会被切分到不同卡所运行的程序中。如果integrated_save设置为True,则这些被切分的Tensor会被合并保存到每个checkpoint文件中,这样checkpoint文件保存的就是完整的训练参数。\n",
+ "\n",
+ "```Python\n",
+ "save_checkpoint(net, \"resnet50-2_32.ckpt\", integrated_save=True)\n",
+ "```\n",
+ "\n",
+ "3. `async_save`参数\n",
+ "\n",
+ "表示是否开启异步保存功能,默认为False。如果设置为True,则会开启多线程执行写checkpoint文件操作,从而可以并行执行训练和保存任务,在训练大规模网络时会节省脚本运行的总时长。\n",
+ "\n",
+ "```Python\n",
+ "save_checkpoint(net, \"resnet50-2_32.ckpt\", async_save=True)\n",
+ "```\n",
+ "\n",
+ "4. `append_dict`参数\n",
+ "\n",
+ "需要额外保存的信息,类型为dict类型,目前只支持基础类型的保存,包括int、float、bool等。\n",
+ "\n",
+ "```Python\n",
+ "save_dict = {\"epoch_num\": 2, \"lr\": 0.01}\n",
+ "# 除了net中的参数,save_dict的信息也会保存在ckpt文件中\n",
+ "save_checkpoint(net, \"resnet50-2_32.ckpt\",append_dict=save_dict)\n",
+ "```\n",
+ "\n",
+ "## 迁移学习\n",
+ "\n",
+ "迁移学习场景中,使用预训练模型进行训练时,CheckPoint文件中的模型参数无法直接使用,需要根据实际情况进行修改才能适用于当前网络模型。本节介绍如何删除Resnet50的预训练模型中的全连接层参数。\n",
+ "\n",
+ "首先下载[Resnet50的预训练模型](https://download.mindspore.cn/vision/classification/resnet50_224.ckpt),该模型文件是由MindSpore Vision中的`resnet50`模型在ImageNet数据集上训练得到的。\n",
+ "\n",
+ "使用`load_checkpoint`接口加载训练模型,该接口返回一个Dict类型,该字典的健值key为网络各层的名称,类型为字符型Str;字典的值value为网络层的参数值,类型为Parameter。\n",
+ "\n",
+ "如下示例中由于Resnet50预训练模型的分类类别数为1000,而示例中定义的resnet50网络分类类别数为2,所以需要删除预训练模型中的全连接层参数。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Delete parameter from checkpoint: head.dense.weight\n",
+ "Delete parameter from checkpoint: head.dense.bias\n"
+ ]
+ }
+ ],
+ "source": [
+ "from mindvision.classification.models import resnet50\n",
+ "from mindspore import load_checkpoint, load_param_into_net\n",
+ "from mindvision.dataset import DownLoad\n",
+ "\n",
+ "# 下载Resnet50的预训练模型\n",
+ "dl = DownLoad()\n",
+ "dl.download_url('https://download.mindspore.cn/vision/classification/resnet50_224.ckpt')\n",
+ "# 定义分类类被为2的resnet50网络\n",
+ "resnet = resnet50(2)\n",
+ "# 模型参数保存到param_dict中\n",
+ "param_dict = load_checkpoint(\"resnet50_224.ckpt\")\n",
+ "\n",
+ "# 获取全连接层的参数名列表\n",
+ "param_filter = [x.name for x in resnet.head.get_parameters()]\n",
+ "\n",
+ "def filter_ckpt_parameter(origin_dict, param_filter):\n",
+ " \"\"\"删除origin_dict中包含param_filter参数名的元素\"\"\"\n",
+ " for key in list(origin_dict.keys()): # 获取模型的所有参数名\n",
+ " for name in param_filter: # 遍历模型中待删除的参数名\n",
+ " if name in key:\n",
+ " print(\"Delete parameter from checkpoint:\", key)\n",
+ " del origin_dict[key]\n",
+ " break\n",
+ "\n",
+ "# 删除全连接层\n",
+ "filter_ckpt_parameter(param_dict, param_filter)\n",
+ "\n",
+ "# 打印更新后的模型参数\n",
+ "load_param_into_net(resnet, param_dict)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 模型导出\n",
+ "\n",
+ "MindSpore的`export`可以将网络模型导出为指定格式的文件,用于其他硬件平台的推理。`export`主要参数如下所示:\n",
+ "\n",
+ "- `net`:MindSpore网络结构。\n",
+ "- `inputs`:网络的输入,支持输入类型为Tensor。当输入有多个时,需要一起传入,如`export(network, Tensor(input1), Tensor(input2), file_name='network', file_format='MINDIR')`。\n",
+ "- `file_name`:导出模型的文件名称,如果`file_name`没有包含对应的后缀名(如.mindir),设置`file_format`后系统会为文件名自动添加后缀。\n",
+ "- `file_format`:MindSpore目前支持导出”AIR”,”ONNX”和”MINDIR”格式的模型。\n",
+ "\n",
+ "如下介绍使用`export`将resnet50网络和相应的CheckPoint格式文件生成对应的MindIR、AIR和ONNX格式文件。\n",
+ "\n",
+ "### 导出MindIR格式文件\n",
+ "\n",
+ "如果想跨平台或硬件执行推理(如昇腾AI处理器、MindSpore端侧、GPU等),可以通过网络定义和CheckPoint生成MindIR格式模型文件。当前支持基于静态图。如下使用MindSpore Vision中的`resnet50`模型和该模型在ImageNet数据集上训练得到的模型文件resnet50_224.ckpt,导出MindIR格式文件。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, export, load_checkpoint\n",
+ "from mindvision.classification.models import resnet50\n",
+ "\n",
+ "resnet = resnet50(1000)\n",
+ "load_checkpoint(\"resnet50_224.ckpt\", net=resnet)\n",
+ "\n",
+ "input_np = np.random.uniform(0.0, 1.0, size=[1, 3, 224, 224]).astype(np.float32)\n",
+ "\n",
+ "# 导出文件resnet50_224.mindir到当前文件夹\n",
+ "export(resnet, Tensor(input_np), file_name='resnet50_224', file_format='MINDIR')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "若想在MindIR格式文件中保存模型推理时需要的预处理操作信息,可以将数据集对象传入export接口:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import mindspore.dataset.vision.c_transforms as C\n",
+ "from mindspore import export, load_checkpoint\n",
+ "from mindvision.classification.models import resnet50\n",
+ "from mindvision.dataset import DownLoad\n",
+ "\n",
+ "def create_dataset_for_renset(path):\n",
+ " \"\"\"创建数据集\"\"\"\n",
+ " data_set = ds.ImageFolderDataset(path)\n",
+ " mean = [0.485 * 255, 0.456 * 255, 0.406 * 255]\n",
+ " std = [0.229 * 255, 0.224 * 255, 0.225 * 255]\n",
+ " data_set = data_set.map(operations=[C.Decode(), C.Resize(256), C.CenterCrop(224),\n",
+ " C.Normalize(mean=mean, std=std), C.HWC2CHW()], input_columns=\"image\")\n",
+ " data_set = data_set.batch(1)\n",
+ " return data_set\n",
+ "\n",
+ "dataset_url = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/beginner/DogCroissants.zip\"\n",
+ "path = \"./datasets\"\n",
+ "# 下载并解压数据集\n",
+ "dl = DownLoad()\n",
+ "dl.download_and_extract_archive(url=dataset_url, download_path=path)\n",
+ "# 加载数据集\n",
+ "path = \"./datasets/DogCroissants/val/\"\n",
+ "de_dataset = create_dataset_for_renset(path)\n",
+ "# 定义网络\n",
+ "resnet = resnet50()\n",
+ "\n",
+ "# 加载预处理模型参数到网络中\n",
+ "load_checkpoint(\"resnet50_224.ckpt\", net=resnet)\n",
+ "# 导出带预处理信息的MindIR文件\n",
+ "export(resnet, de_dataset, file_name='resnet50_224', file_format='MINDIR')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> 如果file_name没有包含”.mindir”后缀,系统会为其自动添加”.mindir”后缀。\n",
+ "\n",
+ "为了避免Protocol Buffers的硬件限制,当导出的模型参数大小超过1G时,框架默认会把网络结构和参数分开保存。\n",
+ "\n",
+ "- 网络结构文件的名称以用户指定前缀加_graph.mindir结尾。\n",
+ "- 同级目录下,会生成一个用户指定前缀加_variables的文件夹,里面存放网络的参数。其中参数大小每超过1T会被分开保存成命名为data_1、data_2、data_3等的多个文件。\n",
+ "\n",
+ "以上述代码为例,如果带参数的模型大小超过1G,生成的目录结构如下:\n",
+ "\n",
+ "```Text\n",
+ "├── resnet50_224_graph.mindir\n",
+ "└── resnet50_224_variables\n",
+ " ├── data_1\n",
+ " ├── data_2\n",
+ " └── data_3\n",
+ "\n",
+ "```\n",
+ "\n",
+ "### 导出AIR格式文件\n",
+ "\n",
+ "AIR格式文件用于在昇腾AI处理器上执行推理,导出AIR格式文件需要在昇腾AI处理器上进行操作,可通过网络定义和CheckPoint生成AIR格式模型文件。如下使用MindSpore Vision中的`resnet50`模型和该模型在ImageNet数据集上训练得到的模型文件resnet50_224.ckpt,在昇腾AI处理器上导出AIR格式文件。\n",
+ "\n",
+ "```Python\n",
+ "import numpy as np\n",
+ "from mindspore import Tensor, export, load_checkpoint\n",
+ "from mindvision.classification.models import resnet50\n",
+ "\n",
+ "resnet = resnet50()\n",
+ "# 加载参数到网络中\n",
+ "load_checkpoint(\"resnet50_224.ckpt\", net=resnet)\n",
+ "# 网络输入\n",
+ "input_np = np.random.uniform(0.0, 1.0, size=[1, 3, 224, 224]).astype(np.float32)\n",
+ "# 保存resnet50_224.air文件到当前目录下\n",
+ "export(resnet, Tensor(input_np), file_name='resnet50_224', file_format='AIR')\n",
+ "```\n",
+ "\n",
+ "如果file_name没有包含“.air”后缀,系统会为其自动添加“.air”后缀。\n",
+ "\n",
+ "### 导出ONNX格式文件\n",
+ "\n",
+ "当有了CheckPoint文件后,如果想继续在昇腾AI处理器、GPU或CPU等多种硬件上做推理,需要通过网络和CheckPoint生成对应的ONNX格式模型文件。如下使用MindSpore Vision中的`resnet50`模型和该模型在ImageNet数据集上训练得到的模型文件resnet50_224.ckpt,导出ONNX格式文件。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "from mindspore import Tensor, export, load_checkpoint\n",
+ "from mindvision.classification.models import resnet50\n",
+ "\n",
+ "resnet = resnet50()\n",
+ "load_checkpoint(\"resnet50_224.ckpt\", net=resnet)\n",
+ "\n",
+ "input_np = np.random.uniform(0.0, 1.0, size=[1, 3, 224, 224]).astype(np.float32)\n",
+ "\n",
+ "# 保存resnet50_224.onnx文件到当前目录下\n",
+ "export(resnet, Tensor(input_np), file_name='resnet50_224', file_format='ONNX')"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "> - 如果file_name没有包含”.onnx”后缀,系统会为其自动添加”.onnx”后缀。\n",
+ "> - 目前ONNX格式导出仅支持ResNet系列、YOLOV3、YOLOV4、BERT网络。"
+ ]
+ }
+ ],
+ "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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/tutorials/source_zh_cn/advanced/train/train_eval.ipynb b/tutorials/source_zh_cn/advanced/train/train_eval.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..310abad49b94d490ea7dcf983a8e96c5b1b83185
--- /dev/null
+++ b/tutorials/source_zh_cn/advanced/train/train_eval.ipynb
@@ -0,0 +1,475 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# 训练和评估\n",
+ "\n",
+ "[](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_train_eval.ipynb) [](https://obs.dualstack.cn-north-4.myhuaweicloud.com/mindspore-website/notebook/master/tutorials/zh_cn/advanced/train/mindspore_train_eval.py) [](https://gitee.com/mindspore/docs/blob/master/tutorials/source_zh_cn/advanced/train/train_eval.ipynb)\n",
+ "\n",
+ "前面章节讲解了MindSpore构建网络所使用的基本元素,如MindSpore的网络基本单元、损失函数、优化器和评价函数等。\n",
+ "\n",
+ "本章重点介绍如何使用这些元素自定义训练和评估网络。"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 构建训练和评估\n",
+ "\n",
+ "构建训练网络首先需要构建前向网络,然后在前向网络的基础上叠加损失函数、反向传播和优化器。\n",
+ "\n",
+ "### 定义数据集\n",
+ "\n",
+ "如下示例定义`get_data`函数生成样本数据及对应的标签,定义`create_dataset`函数加载自定义数据集。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import mindspore.dataset as ds\n",
+ "import numpy as np\n",
+ "\n",
+ "def get_data(num, w=2.0, b=3.0):\n",
+ " \"\"\"生成样本数据及对应的标签\"\"\"\n",
+ " for _ in range(num):\n",
+ " x = np.random.uniform(-10.0, 10.0)\n",
+ " noise = np.random.normal(0, 1)\n",
+ " y = x * w + b + noise\n",
+ " yield np.array([x]).astype(np.float32), np.array([y]).astype(np.float32)\n",
+ "\n",
+ "def create_dataset(num_data, batch_size=16):\n",
+ " \"\"\"生成数据集\"\"\"\n",
+ " dataset = ds.GeneratorDataset(list(get_data(num_data)), column_names=['data', 'label'])\n",
+ " dataset = dataset.batch(batch_size)\n",
+ " return dataset"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 构建前向网络\n",
+ "\n",
+ "使用`nn.Cell`构建前向网络,如下示例定义一个简单的线性回归网络`LinearNet`:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import numpy as np\n",
+ "import mindspore.nn as nn\n",
+ "from mindspore.common.initializer import Normal\n",
+ "\n",
+ "class LinearNet(nn.Cell):\n",
+ " def __init__(self):\n",
+ " super().__init__()\n",
+ " self.fc = nn.Dense(1, 1, Normal(0.02), Normal(0.02))\n",
+ "\n",
+ " def construct(self, x):\n",
+ " return self.fc(x)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 构建训练流程\n",
+ "\n",
+ "MindSpore的`nn`模块提供了训练网络封装函数`TrainOneStepCell`,用来封装网络和优化器。其参数如下:\n",
+ "\n",
+ "- `network`:训练网络,只支持单输出网络。\n",
+ "- `optimizer`: 用于更新网络参数的优化器。\n",
+ "- `sens`:反向传播的输入,缩放系数,默认值为1.0。\n",
+ "\n",
+ "如下示例使用`nn.TrainOneStepCell`将上述定义的线性回归网络封装成一个训练网络,并执行训练,打印损失值。\n",
+ "\n",
+ "示例代码中使用`set_train`通过`mode`参数指定模型是否为训练模式,其中`mode`参数默认为True,即默认情况下为训练模式,若`mode`为False,则为评估或推理模式。\n",
+ "\n",
+ "若模型使用了Dropout层或Batch Normalization层,训练模式下会采用Dropout层或Batch Normalization层来防止模型过拟合,非训练模式下不执行Dropout层或Batch Normalization层。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-31T07:16:16.275751Z",
+ "start_time": "2021-12-31T07:16:16.256615Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch: [0 / 2], step: [0 / 10], loss: 124.43444\n",
+ "Epoch: [0 / 2], step: [1 / 10], loss: 76.6322\n",
+ "Epoch: [0 / 2], step: [2 / 10], loss: 15.115915\n",
+ "Epoch: [0 / 2], step: [3 / 10], loss: 14.843063\n",
+ "Epoch: [0 / 2], step: [4 / 10], loss: 38.161774\n",
+ "Epoch: [0 / 2], step: [5 / 10], loss: 106.53122\n",
+ "Epoch: [0 / 2], step: [6 / 10], loss: 92.82173\n",
+ "Epoch: [0 / 2], step: [7 / 10], loss: 15.204596\n",
+ "Epoch: [0 / 2], step: [8 / 10], loss: 9.148388\n",
+ "Epoch: [0 / 2], step: [9 / 10], loss: 27.740812\n",
+ "Epoch: [1 / 2], step: [10 / 10], loss: 64.58249\n",
+ "Epoch: [1 / 2], step: [11 / 10], loss: 72.02158\n",
+ "Epoch: [1 / 2], step: [12 / 10], loss: 3.2674344\n",
+ "Epoch: [1 / 2], step: [13 / 10], loss: 8.735121\n",
+ "Epoch: [1 / 2], step: [14 / 10], loss: 38.89611\n",
+ "Epoch: [1 / 2], step: [15 / 10], loss: 48.88697\n",
+ "Epoch: [1 / 2], step: [16 / 10], loss: 11.520826\n",
+ "Epoch: [1 / 2], step: [17 / 10], loss: 3.750766\n",
+ "Epoch: [1 / 2], step: [18 / 10], loss: 2.4294345\n",
+ "Epoch: [1 / 2], step: [19 / 10], loss: 11.426197\n"
+ ]
+ }
+ ],
+ "source": [
+ "# 生成训练数据集\n",
+ "train_dataset = create_dataset(num_data=160)\n",
+ "\n",
+ "net = LinearNet()\n",
+ "crit = nn.MSELoss()\n",
+ "\n",
+ "# 连接前向网络与损失函数\n",
+ "net_with_criterion = nn.WithLossCell(net, crit)\n",
+ "\n",
+ "opt = nn.Momentum(net.trainable_params(), learning_rate=0.005, momentum=0.9)\n",
+ "\n",
+ "# 定义训练网络,封装网络和优化器\n",
+ "train_net = nn.TrainOneStepCell(net_with_criterion, opt)\n",
+ "# 设置网络为训练模式\n",
+ "train_net.set_train()\n",
+ "\n",
+ "# 获取训练过程数据\n",
+ "epochs = 2\n",
+ "step = 0\n",
+ "steps = train_dataset.get_dataset_size()\n",
+ "for epoch in range(epochs):\n",
+ " for d in train_dataset.create_dict_iterator():\n",
+ " result = train_net(d[\"data\"], d[\"label\"]) # 输出损失值\n",
+ " print(f\"Epoch: [{epoch} / {epochs}], step: [{step} / {steps}], loss: {result}\")\n",
+ " step = step + 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 构建评估流程\n",
+ "\n",
+ "MindSpore的`nn`模块提供了评估网络封装函数`WithEvalCell`,用来在验证集上评估模型训练的效果。其参数如下:\n",
+ "\n",
+ "- `network`:前向网络。\n",
+ "- `loss_fn`:损失函数。\n",
+ "- `add_cast_fp32`:是否将数据类型调整为float32。\n",
+ "\n",
+ "`nn.WithEvalCell`只接受两个输入,分别为数据`data`及其对应的标签`label`,用前面定义的前向网络和损失函数构建一个评估网络,示例如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-31T07:16:16.821621Z",
+ "start_time": "2021-12-31T07:16:16.792286Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "mae: 3.9329519510269164\n",
+ "loss: 21.00230531692505\n"
+ ]
+ }
+ ],
+ "source": [
+ "eval_dataset = create_dataset(num_data=160)\n",
+ "\n",
+ "# 构建评估网络\n",
+ "eval_net = nn.WithEvalCell(net, crit)\n",
+ "eval_net.set_train(False)\n",
+ "loss = nn.Loss()\n",
+ "mae = nn.MAE()\n",
+ "\n",
+ "mae.clear()\n",
+ "loss.clear()\n",
+ "\n",
+ "# 遍历评估数据集\n",
+ "for data in eval_dataset.create_dict_iterator():\n",
+ " outputs = eval_net(data[\"data\"], data[\"label\"])\n",
+ " mae.update(outputs[1], outputs[2])\n",
+ " loss.update(outputs[0])\n",
+ "\n",
+ "# 评估结果\n",
+ "mae_result = mae.eval()\n",
+ "loss_result = loss.eval()\n",
+ "\n",
+ "print(\"mae: \", mae_result)\n",
+ "print(\"loss: \", loss_result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 自定义训练和评估\n",
+ "\n",
+ "### 自定义训练网络\n",
+ "\n",
+ "[自定义损失函数章节](https://www.mindspore.cn/tutorials/zh-CN/master/advanced/network/loss.html#损失函数与模型训练)已经介绍了使用`nn.WithLossCell`将前向网络与损失函数连接起来,本节将介绍如何自定义训练网络。\n",
+ "\n",
+ "如下示例定义`CustomTrainOneStepCell`函数来封装网络和优化器。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Epoch: [0 / 2], step: [0 / 10], loss: 101.77025\n",
+ "Epoch: [0 / 2], step: [1 / 10], loss: 83.62404\n",
+ "Epoch: [0 / 2], step: [2 / 10], loss: 17.073797\n",
+ "Epoch: [0 / 2], step: [3 / 10], loss: 14.658651\n",
+ "Epoch: [0 / 2], step: [4 / 10], loss: 56.425804\n",
+ "Epoch: [0 / 2], step: [5 / 10], loss: 59.841225\n",
+ "Epoch: [0 / 2], step: [6 / 10], loss: 30.201393\n",
+ "Epoch: [0 / 2], step: [7 / 10], loss: 18.574959\n",
+ "Epoch: [0 / 2], step: [8 / 10], loss: 3.676639\n",
+ "Epoch: [0 / 2], step: [9 / 10], loss: 20.492714\n",
+ "Epoch: [1 / 2], step: [10 / 10], loss: 26.22078\n",
+ "Epoch: [1 / 2], step: [11 / 10], loss: 6.8518386\n",
+ "Epoch: [1 / 2], step: [12 / 10], loss: 6.589101\n",
+ "Epoch: [1 / 2], step: [13 / 10], loss: 2.666248\n",
+ "Epoch: [1 / 2], step: [14 / 10], loss: 2.9949698\n",
+ "Epoch: [1 / 2], step: [15 / 10], loss: 3.7182124\n",
+ "Epoch: [1 / 2], step: [16 / 10], loss: 5.941271\n",
+ "Epoch: [1 / 2], step: [17 / 10], loss: 5.331856\n",
+ "Epoch: [1 / 2], step: [18 / 10], loss: 3.4678411\n",
+ "Epoch: [1 / 2], step: [19 / 10], loss: 1.2043725\n"
+ ]
+ }
+ ],
+ "source": [
+ "import mindspore.ops as ops\n",
+ "\n",
+ "class CustomTrainOneStepCell(nn.Cell):\n",
+ " \"\"\"自定义训练网络\"\"\"\n",
+ "\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 # 定义优化器\n",
+ " self.weights = self.optimizer.parameters # 待更新参数\n",
+ " self.grad = ops.GradOperation(get_by_list=True, sens_param=True) # 反向传播获取梯度\n",
+ " self.sens = sens # 网络输出的缩放比\n",
+ "\n",
+ " def construct(self, *inputs):\n",
+ " loss = self.network(*inputs) # 执行前向网络,计算当前输入的损失函数值\n",
+ " sens = ops.fill(loss.dtype, loss.shape, self.sens) # 对损失值执行缩放\n",
+ " grads = self.grad(self.network, self.weights)(*inputs, sens) # 进行反向传播,计算梯度\n",
+ " self.optimizer(grads) # 使用优化器更新权重参数\n",
+ " return loss\n",
+ "\n",
+ "# 定义前向网络\n",
+ "net1 = LinearNet()\n",
+ "# 损失函数\n",
+ "crit = nn.MSELoss()\n",
+ "\n",
+ "# 连接前向网络与损失函数\n",
+ "net_with_criterion = nn.WithLossCell(net1, crit)\n",
+ "\n",
+ "opt = nn.Momentum(net1.trainable_params(), learning_rate=0.005, momentum=0.9)\n",
+ "\n",
+ "# 定义训练网络,封装网络和优化器\n",
+ "train_net = CustomTrainOneStepCell(net_with_criterion, opt)\n",
+ "# 设置网络为训练模式\n",
+ "train_net.set_train()\n",
+ "\n",
+ "# 获取训练过程数据\n",
+ "epochs = 2\n",
+ "step = 0\n",
+ "steps = train_dataset.get_dataset_size()\n",
+ "for epoch in range(epochs):\n",
+ " for d in train_dataset.create_dict_iterator():\n",
+ " result = train_net(d[\"data\"], d[\"label\"]) # 输出损失值\n",
+ " print(f\"Epoch: [{epoch} / {epochs}], step: [{step} / {steps}], loss: {result}\")\n",
+ " step = step + 1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 自定义评估网络\n",
+ "\n",
+ "由于`nn.WithEvalCell`只有两个输入`data`和`label`,对于多个数据或多个标签的场景显然不适用,此时如果想要构建评估网络就需要自定义评估网络。\n",
+ "\n",
+ "在自定义时,如不需要损失函数作为评价指标,则无需定义`loss_fn`。当输入为多数据或多标签时,可参考如下示例来自定义评估网络。"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "CustomWithEvalCell<\n",
+ " (network): LinearNet<\n",
+ " (fc): Dense\n",
+ " >\n",
+ " >"
+ ]
+ },
+ "execution_count": 6,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "class CustomWithEvalCell(nn.Cell):\n",
+ "\n",
+ " def __init__(self, network):\n",
+ " super(CustomWithEvalCell, self).__init__(auto_prefix=False)\n",
+ " self.network = network\n",
+ "\n",
+ " def construct(self, data, label1, label2):\n",
+ " \"\"\"输入数据为三个:一个数据及其对应的两个标签\"\"\"\n",
+ " outputs = self.network(data)\n",
+ " return outputs, label1, label2\n",
+ "\n",
+ "custom_eval_net = CustomWithEvalCell(net)\n",
+ "custom_eval_net.set_train(False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 网络的权重共享\n",
+ "\n",
+ "通过前面的介绍可以看出,前向网络、训练网络和评估网络具有不同的逻辑,因此在需要时我们会构建三张网络。若使用训练好的模型进行评估和推理,需要推理和评估网络中的权重值与训练网络中相同。\n",
+ "\n",
+ "使用模型保存和加载接口,将训练好的模型保存下来,再加载到评估和推理网络中,可以确保权重值相同。在训练平台上完成模型训练,但在推理平台进行推理时,模型保存与加载是必不可少的。\n",
+ "\n",
+ "在网络调测过程中,或使用边训练边推理方式进行模型调优时,往往在同一Python脚本中完成模型训练,评估或推理,此时MindSpore的权重共享机制可确保不同网络间的权重一致性。\n",
+ "\n",
+ "使用MindSpore构建不同网络结构时,只要这些网络结构是在一个实例的基础上封装的,那这个实例中的所有权重便是共享的,一个网络中的权重发生变化,意味着其他网络中的权重同步发生了变化。\n",
+ "\n",
+ "如下示例中,定义训练和评估网络时便使用了权重共享机制:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-31T07:16:17.017589Z",
+ "start_time": "2021-12-31T07:16:16.991264Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# 实例化前向网络\n",
+ "net = LinearNet()\n",
+ "# 设定损失函数并连接前向网络与损失函数\n",
+ "crit = nn.MSELoss()\n",
+ "net_with_criterion = nn.WithLossCell(net, crit)\n",
+ "# 设定优化器\n",
+ "opt = nn.Adam(params=net.trainable_params())\n",
+ "\n",
+ "# 定义训练网络\n",
+ "train_net = nn.TrainOneStepCell(net_with_criterion, opt)\n",
+ "train_net.set_train()\n",
+ "\n",
+ "# 构建评估网络\n",
+ "eval_net = nn.WithEvalCell(net, crit)\n",
+ "eval_net.set_train(False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "`train_net`和`eval_net`均在`net`实例的基础上封装,因此在进行模型评估时,不需要加载`train_net`的权重。\n",
+ "\n",
+ "若在构建`eval_net`时重新的定义前向网络,那`train_net`和`eval_net`之间便没有共享权重,如下:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2021-12-31T07:16:17.046932Z",
+ "start_time": "2021-12-31T07:16:17.018611Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# 定义训练网络\n",
+ "train_net = nn.TrainOneStepCell(net_with_criterion, opt)\n",
+ "train_net.set_train()\n",
+ "\n",
+ "# 再次实例化前向网络\n",
+ "net2 = LinearNet()\n",
+ "# 构建评估网络\n",
+ "eval_net = nn.WithEvalCell(net2, crit)\n",
+ "eval_net.set_train(False)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "此时,若要在模型训练后进行评估,就需要将`train_net`中的权重加载到`eval_net`中。在同一脚本中进行模型训练、评估和推理时,利用好权重共享机制不失为一种更简便的方式。\n",
+ "\n",
+ "在使用Model进行训练时,对于简单的场景,`Model`内部使用`nn.WithLossCell`、`nn.TrainOneStepCell`和`nn.WithEvalCell`在前向`network`实例的基础上构建训练和评估网络,`Model`本身确保了推理、训练、评估网络之间权重共享。\n",
+ "\n",
+ "但对于自定义使用Model的场景,用户需要注意前向网络仅实例化一次。如果构建训练网络和评估网络时分别实例化前向网络,那在使用`eval`进行模型评估时,便需要手动加载训练网络中的权重,否则模型评估使用的将是初始的权重值。"
+ ]
+ }
+ ],
+ "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.9.0"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/tutorials/source_zh_cn/index.rst b/tutorials/source_zh_cn/index.rst
index bcba1642a357024792f865d5903006578f27f6a6..e0a2616cfb9564c055f7c4f2ca651e2ba33ed248 100644
--- a/tutorials/source_zh_cn/index.rst
+++ b/tutorials/source_zh_cn/index.rst
@@ -20,3 +20,15 @@ MindSpore教程
beginner/train
beginner/save_load
beginner/infer
+
+.. toctree::
+ :glob:
+ :maxdepth: 1
+ :caption: 进阶
+ :hidden:
+
+ advance/linear_fitting
+ advance/dataset
+ advance/network
+ advance/train
+ advance/pynative_graph
\ No newline at end of file