{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 开发入门\n", "\n", "[![下载Notebook](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/br_base/resource/_static/logo_notebook.svg)](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/br_base/tutorials/zh_cn/orange_pi/mindspore_dev_start.ipynb) [![下载样例代码](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/br_base/resource/_static/logo_download_code.svg)](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/br_base/tutorials/zh_cn/orange_pi/mindspore_dev_start.py) [![查看源文件](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/br_base/resource/_static/logo_source.svg)](https://gitee.com/mindspore/docs/blob/br_base/tutorials/source_zh_cn/orange_pi/dev_start.ipynb)\n", "\n", "因开发者可能会在OrangePi AIpro(下称:香橙派开发板)进行自定义模型和案例开发,本章节通过基于MindSpore的手写数字识别案例,说明香橙派开发板中的开发注意事项。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 环境准备\n", "\n", "开发者拿到香橙派开发板后,首先需要进行硬件资源确认、镜像烧录以及CANN和MindSpore版本的升级,才可运行该案例,具体如下:\n", "\n", "| 香橙派AIpro | 镜像 | CANN Toolkit/Kernels | MindSpore |\n", "| :----:| :----: | :----:| :----: |\n", "| 8T 16G | Ubuntu | 8.1.RC1| 2.6.0 |\n", "\n", "### 镜像烧录\n", "\n", "运行该案例需要烧录香橙派官网Ubuntu镜像,参考[镜像烧录](https://www.mindspore.cn/tutorials/zh-CN/br_base/orange_pi/environment_setup.html#1-%E9%95%9C%E5%83%8F%E7%83%A7%E5%BD%95%E4%BB%A5windows%E7%B3%BB%E7%BB%9F%E4%B8%BA%E4%BE%8B)章节。\n", "\n", "### CANN升级\n", "\n", "参考[CANN升级](https://www.mindspore.cn/tutorials/zh-CN/br_base/orange_pi/environment_setup.html#3-cann%E5%8D%87%E7%BA%A7)章节。\n", "\n", "### MindSpore升级\n", "\n", "参考[MindSpore升级](https://www.mindspore.cn/tutorials/zh-CN/br_base/orange_pi/environment_setup.html#4-mindspore%E5%8D%87%E7%BA%A7)章节。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "/usr/local/miniconda3/lib/python3.9/site-packages/numpy/core/getlimits.py:499: UserWarning: The value of the smallest subnormal for type is zero.\n", " setattr(self, word, getattr(machar, word).flat[0])\n", "/usr/local/miniconda3/lib/python3.9/site-packages/numpy/core/getlimits.py:89: UserWarning: The value of the smallest subnormal for type is zero.\n", " return self._float_to_str(self.smallest_subnormal)\n", "/usr/local/miniconda3/lib/python3.9/site-packages/numpy/core/getlimits.py:499: UserWarning: The value of the smallest subnormal for type is zero.\n", " setattr(self, word, getattr(machar, word).flat[0])\n", "/usr/local/miniconda3/lib/python3.9/site-packages/numpy/core/getlimits.py:89: UserWarning: The value of the smallest subnormal for type is zero.\n", " return self._float_to_str(self.smallest_subnormal)\n" ] } ], "source": [ "import mindspore\n", "from mindspore import mint\n", "from mindspore.nn import Cell, SGD\n", "from mindspore.mint import nn\n", "from mindspore.dataset import vision, transforms\n", "from mindspore.dataset import MnistDataset" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 数据集准备与加载\n", "\n", "MindSpore提供基于Pipeline的[数据引擎](https://www.mindspore.cn/docs/zh-CN/br_base/features/data_engine.html),通过[数据集(Dataset)](https://www.mindspore.cn/tutorials/zh-CN/br_base/beginner/dataset.html)实现高效的数据预处理。在本案例中,我们使用Mnist数据集,自动下载完成后,使用`mindspore.dataset`提供的数据变换进行预处理。\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Defaulting to user installation because normal site-packages is not writeable\n", "Requirement already satisfied: download in /home/HwHiAiUser/.local/lib/python3.9/site-packages (0.3.5)\n", "Requirement already satisfied: tqdm in /home/HwHiAiUser/.local/lib/python3.9/site-packages (from download) (4.66.5)\n", "Requirement already satisfied: six in /usr/local/miniconda3/lib/python3.9/site-packages (from download) (1.16.0)\n", "Requirement already satisfied: requests in /home/HwHiAiUser/.local/lib/python3.9/site-packages (from download) (2.32.2)\n", "Requirement already satisfied: urllib3<3,>=1.21.1 in /home/HwHiAiUser/.local/lib/python3.9/site-packages (from requests->download) (2.2.3)\n", "Requirement already satisfied: idna<4,>=2.5 in /usr/local/miniconda3/lib/python3.9/site-packages (from requests->download) (3.4)\n", "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/miniconda3/lib/python3.9/site-packages (from requests->download) (2023.11.17)\n", "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/miniconda3/lib/python3.9/site-packages (from requests->download) (2.0.4)\n" ] } ], "source": [ "# install download\n", "\n", "!pip install download" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Downloading data from https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/MNIST_Data.zip (10.3 MB)\n", "\n", "file_sizes: 100%|██████████████████████████| 10.8M/10.8M [00:02<00:00, 4.50MB/s]\n", "Extracting zip file...\n", "Successfully downloaded / unzipped to ./\n" ] } ], "source": [ "# Download data from open datasets\n", "from download import download\n", "\n", "url = \"https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/\" \\\n", " \"notebook/datasets/MNIST_Data.zip\"\n", "path = download(url, \"./\", kind=\"zip\", replace=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "MNIST数据集目录结构如下:\n", "\n", "```text\n", "MNIST_Data\n", "└── train\n", " ├── train-images-idx3-ubyte (60000个训练图片)\n", " ├── train-labels-idx1-ubyte (60000个训练标签)\n", "└── test\n", " ├── t10k-images-idx3-ubyte (10000个测试图片)\n", " ├── t10k-labels-idx1-ubyte (10000个测试标签)\n", "\n", "```\n", "\n", "数据下载完成后,获得数据集对象。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "train_dataset = MnistDataset('MNIST_Data/train')\n", "test_dataset = MnistDataset('MNIST_Data/test')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "打印数据集中包含的数据列名,用于dataset的预处理。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['image', 'label']\n" ] } ], "source": [ "print(train_dataset.get_col_names())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "MindSpore的dataset使用数据处理流水线(Data Processing Pipeline),需指定map、batch、shuffle等操作。这里我们使用map对图像数据及标签进行变换处理,将输入的图像缩放为1/255,根据均值0.1307和标准差值0.3081进行归一化处理,然后将处理好的数据集打包为大小为64的batch。" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "def datapipe(dataset, batch_size):\n", " image_transforms = [\n", " vision.Rescale(1.0 / 255.0, 0),\n", " vision.Normalize(mean=(0.1307,), std=(0.3081,)),\n", " vision.HWC2CHW(),\n", " transforms.TypeCast(mindspore.float16)\n", " ]\n", " label_transform = transforms.TypeCast(mindspore.int32)\n", "\n", " dataset = dataset.map(image_transforms, 'image')\n", " dataset = dataset.map(label_transform, 'label')\n", " dataset = dataset.batch(batch_size)\n", " return dataset" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "# Map vision transforms and batch dataset\n", "train_dataset = datapipe(train_dataset, 64)\n", "test_dataset = datapipe(test_dataset, 64)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "可使用[create_tuple_iterator](https://www.mindspore.cn/docs/zh-CN/br_base/api_python/dataset/dataset_method/iterator/mindspore.dataset.Dataset.create_tuple_iterator.html) 或[create_dict_iterator](https://www.mindspore.cn/docs/zh-CN/br_base/api_python/dataset/dataset_method/iterator/mindspore.dataset.Dataset.create_dict_iterator.html)对数据集进行迭代访问,查看数据和标签的shape和datatype。" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Shape of image [N, C, H, W]: (64, 1, 28, 28) Float16\n", "Shape of label: (64,) Int32\n" ] } ], "source": [ "for image, label in test_dataset.create_tuple_iterator():\n", " print(f\"Shape of image [N, C, H, W]: {image.shape} {image.dtype}\")\n", " print(f\"Shape of label: {label.shape} {label.dtype}\")\n", " break" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Shape of image [N, C, H, W]: (64, 1, 28, 28) Float16\n", "Shape of label: (64,) Int32\n" ] } ], "source": [ "for data in test_dataset.create_dict_iterator():\n", " print(f\"Shape of image [N, C, H, W]: {data['image'].shape} {data['image'].dtype}\")\n", " print(f\"Shape of label: {data['label'].shape} {data['label'].dtype}\")\n", " break" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 模型构建" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Network<\n", " (dense1): Linear\n", " (dense2): Linear\n", " (dense3): Linear\n", " (relu): ReLU<>\n", " >\n" ] } ], "source": [ "# Define model\n", "class Network(Cell):\n", " def __init__(self):\n", " super().__init__()\n", " self.flatten = mint.flatten\n", " self.dense1 = nn.Linear(28*28, 512, dtype=mindspore.float16)\n", " self.dense2 = nn.Linear(512, 512, dtype=mindspore.float16)\n", " self.dense3 = nn.Linear(512, 10, dtype=mindspore.float16)\n", " self.relu = nn.ReLU()\n", "\n", " def construct(self, x):\n", " x = self.flatten(x, start_dim=1)\n", " x = self.dense1(x)\n", " x = self.relu(x)\n", " x = self.dense2(x)\n", " x = self.relu(x)\n", " logits = self.dense3(x)\n", " return logits\n", "\n", "model = Network()\n", "print(model)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 模型训练" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在模型训练中,一个完整的训练过程(step)需要实现以下三步:\n", "\n", "1. **正向计算**:模型预测结果(logits),并与正确标签(label)求预测损失(loss)。\n", "2. **反向传播**:利用自动微分机制,自动求模型参数(parameters)对于loss的梯度(gradients)。\n", "3. **参数优化**:将梯度更新到参数上。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "MindSpore使用函数式自动微分机制,因此针对上述步骤需要实现:\n", "\n", "1. 定义正向计算函数。\n", "2. 使用[value_and_grad](https://www.mindspore.cn/docs/zh-CN/br_base/api_python/mindspore/mindspore.value_and_grad.html)通过函数变换获得梯度计算函数。\n", "3. 定义训练函数,使用[set_train](https://www.mindspore.cn/docs/zh-CN/br_base/api_python/nn/mindspore.nn.Cell.html#mindspore.nn.Cell.set_train)设置为训练模式,执行正向计算、反向传播和参数优化。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# Instantiate loss function and optimizer\n", "loss_fn = nn.CrossEntropyLoss()\n", "optimizer = SGD(model.trainable_params(), 1e-2)\n", "\n", "# 1. Define forward function\n", "def forward_fn(data, label):\n", " logits = model(data)\n", " loss = loss_fn(logits, label)\n", " return loss, logits\n", "\n", "# 2. Get gradient function\n", "grad_fn = mindspore.value_and_grad(forward_fn, None, optimizer.parameters, has_aux=True)\n", "\n", "# 3. Define function of one-step training\n", "def train_step(data, label):\n", " (loss, _), grads = grad_fn(data, label)\n", " optimizer(grads)\n", " return loss\n", "\n", "def train(model, dataset):\n", " size = dataset.get_dataset_size()\n", " model.set_train()\n", " for batch, (data, label) in enumerate(dataset.create_tuple_iterator()):\n", " loss = train_step(data, label)\n", "\n", " if batch % 100 == 0:\n", " loss, current = loss.asnumpy(), batch\n", " print(f\"loss: {loss:>7f} [{current:>3d}/{size:>3d}]\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "除训练外,我们定义测试函数,用来评估模型的性能。" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "def test(model, dataset, loss_fn):\n", " num_batches = dataset.get_dataset_size()\n", " model.set_train(False)\n", " total, test_loss, correct = 0, 0, 0\n", " for data, label in dataset.create_tuple_iterator():\n", " pred = model(data)\n", " total += len(data)\n", " test_loss += loss_fn(pred, label).asnumpy()\n", " correct += (pred.argmax(1) == label).asnumpy().sum()\n", " test_loss /= num_batches\n", " correct /= total\n", " print(f\"Test: \\n Accuracy: {(100*correct):>0.1f}%, Avg loss: {test_loss:>8f} \\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "训练过程需多次迭代数据集,一次完整的迭代称为一轮(epoch)。在每一轮,遍历训练集进行训练,结束后使用测试集进行预测。打印每一轮的loss值和预测准确率(Accuracy),可以看到loss在不断下降,Accuracy在不断提高。" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1\n", "-------------------------------\n", "loss: 2.298828 [ 0/938]\n", "loss: 1.756836 [100/938]\n", "loss: 0.783691 [200/938]\n", "loss: 0.732910 [300/938]\n", "loss: 0.426514 [400/938]\n", "loss: 0.547363 [500/938]\n", "loss: 0.283203 [600/938]\n", "loss: 0.833496 [700/938]\n", "loss: 0.241455 [800/938]\n", "loss: 0.342773 [900/938]\n", ".Test: \n", " Accuracy: 90.7%, Avg loss: 0.321171 \n", "\n", "Epoch 2\n", "-------------------------------\n", "loss: 0.275879 [ 0/938]\n", "loss: 0.311035 [100/938]\n", "loss: 0.294189 [200/938]\n", "loss: 0.458740 [300/938]\n", "loss: 0.292725 [400/938]\n", "loss: 0.177612 [500/938]\n", "loss: 0.367920 [600/938]\n", "loss: 0.219482 [700/938]\n", "loss: 0.226685 [800/938]\n", "loss: 0.230103 [900/938]\n", "Test: \n", " Accuracy: 92.8%, Avg loss: 0.253441 \n", "\n", "Epoch 3\n", "-------------------------------\n", "loss: 0.310791 [ 0/938]\n", "loss: 0.213379 [100/938]\n", "loss: 0.247925 [200/938]\n", "loss: 0.227783 [300/938]\n", "loss: 0.518066 [400/938]\n", "loss: 0.197266 [500/938]\n", "loss: 0.199219 [600/938]\n", "loss: 0.143188 [700/938]\n", "loss: 0.383545 [800/938]\n", "loss: 0.290283 [900/938]\n", "Test: \n", " Accuracy: 93.8%, Avg loss: 0.215057 \n", "\n", "Done!\n" ] } ], "source": [ "epochs = 3\n", "for t in range(epochs):\n", " print(f\"Epoch {t+1}\\n-------------------------------\")\n", " train(model, train_dataset)\n", " test(model, test_dataset, loss_fn)\n", "print(\"Done!\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 保存模型\n", "\n", "模型训练完成后,需要将其参数进行保存。" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Saved Model to model.ckpt\n" ] } ], "source": [ "# Save checkpoint\n", "mindspore.save_checkpoint(model, \"model.ckpt\")\n", "print(\"Saved Model to model.ckpt\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 权重加载" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "加载保存的权重分为两步:\n", "\n", "1. 重新实例化模型对象,构造模型。\n", "2. 加载模型参数,并将其加载至模型上。" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[]\n" ] } ], "source": [ "# Instantiate a random initialized model\n", "model = Network()\n", "# Load checkpoint and load parameter to model\n", "param_dict = mindspore.load_checkpoint(\"model.ckpt\")\n", "param_not_load, _ = mindspore.load_param_into_net(model, param_dict)\n", "print(param_not_load)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> `param_not_load`是未被加载的参数列表,为空时代表所有参数均加载成功。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 模型推理\n", "\n", "加载后的模型可以直接用于预测推理。" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Predicted: \"[6 9 4 8 9 3]\", Actual: \"[6 9 4 8 9 3]\"\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "\n", "model.set_train(False)\n", "for data, label in test_dataset:\n", " pred = model(data)\n", " predicted = pred.argmax(1)\n", " print(f'Predicted: \"{predicted[:6]}\", Actual: \"{label[:6]}\"')\n", "\n", " # 显示数字及数字的预测值\n", " plt.figure()\n", " for i in range(6):\n", " plt.subplot(2, 3, i + 1)\n", " # 若预测正确,显示为蓝色;若预测错误,显示为红色\n", " color = 'blue' if predicted[i] == label[i] else 'red'\n", " plt.title('Predicted:{}'.format(predicted[i]), color=color)\n", " plt.imshow(data.asnumpy()[i][0], interpolation=\"None\", cmap=\"gray\")\n", " plt.axis('off')\n", " plt.show()\n", " break" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "本案例已同步上线[GitHub仓](https://github.com/mindspore-courses/orange-pi-mindspore/tree/master/Online/inference/01-quick%20start),更多案例可参考该仓库。\n", "\n", "本案例运行所需环境:\n", "\n", "| 香橙派AIpro | 镜像 | CANN Toolkit/Kernels | MindSpore |\n", "| :----:| :----: | :----:| :----: |\n", "| 8T 16G | Ubuntu | 8.1.RC1| 2.6.0 |" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.2" }, "vscode": { "interpreter": { "hash": "8c9da313289c39257cb28b126d2dadd33153d4da4d524f730c81a4aaccbd2ca7" } } }, "nbformat": 4, "nbformat_minor": 4 }