{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 初学入门\n", "\n", "`CPU` `GPU` `Linux` `入门`\n", "\n", "本节以Minst手写体数字识别为例,介绍LuoJiaNET在通用图像处理上的使用方法\n", "\n", "## 配置运行信息\n", "\n", "LuoJiaNET通过导入context某块,调用context.set_context`来配置运行需要的信息,如运行设备(CPU/GPU/Ascend),并行计算模式等。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "import argparse\n", "from luojianet_ms import context\n", "\n", "context.set_context(mode=context.GRAPH_MODE, device_target='GPU')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在上述代码样例中,运行使用图模式,计算设备选用GPU。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 下载数据集\n", "\n", "我们示例中用到的MNIST数据集是由10类28∗28的灰度图片组成,训练数据集包含60000张图片,测试数据集包含10000张图片。\n", "\n", "以下示例代码将数据集下载并解压到指定位置。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "import requests\n", "\n", "requests.packages.urllib3.disable_warnings()\n", "\n", "def download_dataset(dataset_url, path):\n", " filename = dataset_url.split(\"/\")[-1]\n", " save_path = os.path.join(path, filename)\n", " if os.path.exists(save_path):\n", " return\n", " if not os.path.exists(path):\n", " os.makedirs(path)\n", " res = requests.get(dataset_url, stream=True, verify=False)\n", " with open(save_path, \"wb\") as f:\n", " for chunk in res.iter_content(chunk_size=512):\n", " if chunk:\n", " f.write(chunk)\n", " print(\"The {} file is downloaded and saved in the path {} after processing\".format(os.path.basename(dataset_url), path))\n", "\n", "train_path = \"datasets/MNIST_Data/train\"\n", "test_path = \"datasets/MNIST_Data/test\"\n", "\n", "download_dataset(\"https://luojianet-website.obs.myhuaweicloud.com/notebook/datasets/mnist/train-labels-idx1-ubyte\", train_path)\n", "download_dataset(\"https://luojianet-website.obs.myhuaweicloud.com/notebook/datasets/mnist/train-images-idx3-ubyte\", train_path)\n", "download_dataset(\"https://luojianet-website.obs.myhuaweicloud.com/notebook/datasets/mnist/t10k-labels-idx1-ubyte\", test_path)\n", "download_dataset(\"https://luojianet-website.obs.myhuaweicloud.com/notebook/datasets/mnist/t10k-images-idx3-ubyte\", test_path)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "下载的数据集文件的目录结构如下:\n", "\n", "```text\n", "./datasets/MNIST_Data\n", "├── test\n", "│ ├── t10k-images-idx3-ubyte\n", "│ └── t10k-labels-idx1-ubyte\n", "└── train\n", " ├── train-images-idx3-ubyte\n", " └── train-labels-idx1-ubyte\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 数据处理\n", "\n", "数据集对于模型训练非常重要,好的数据集可以有效提高训练精度和效率。\n", "LuoJiaNET融合LuoJiaNet特性,提供了用于数据处理的API模块 `luojianet_ms.dataset` , 用于存储样本和标签。\n", "在加载数据集前,我们通常会对数据集进行一些处理,`luojianet_ms.dataset`也集成了常见的数据处理方法。 首先导入LuoJiaNET中`luojianet_ms.dataset`和其他相应的模块。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import luojianet_ms.dataset as ds\n", "import luojianet_ms.dataset.transforms.c_transforms as C\n", "import luojianet_ms.dataset.vision.c_transforms as CV\n", "from luojianet_ms.dataset.vision import Inter\n", "from luojianet_ms import dtype as mstype" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "通用数据集处理主要分为四个步骤:\n", "\n", "1. 定义函数`create_dataset`来创建数据集。\n", "2. 定义需要进行的数据增强和处理操作,为之后进行map映射做准备。\n", "3. 使用map映射函数,将数据操作应用到数据集。\n", "4. 进行数据shuffle、batch操作。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def create_dataset(data_path, batch_size=32, repeat_size=1,\n", " num_parallel_workers=1):\n", " # 定义数据集\n", " mnist_ds = ds.MnistDataset(data_path)\n", " resize_height, resize_width = 32, 32\n", " rescale = 1.0 / 255.0\n", " shift = 0.0\n", " rescale_nml = 1 / 0.3081\n", " shift_nml = -1 * 0.1307 / 0.3081\n", "\n", " # 定义所需要操作的map映射\n", " resize_op = CV.Resize((resize_height, resize_width), interpolation=Inter.LINEAR)\n", " rescale_nml_op = CV.Rescale(rescale_nml, shift_nml)\n", " rescale_op = CV.Rescale(rescale, shift)\n", " hwc2chw_op = CV.HWC2CHW()\n", " type_cast_op = C.TypeCast(mstype.int32)\n", "\n", " # 使用map映射函数,将数据操作应用到数据集\n", " mnist_ds = mnist_ds.map(operations=type_cast_op, input_columns=\"label\", num_parallel_workers=num_parallel_workers)\n", " mnist_ds = mnist_ds.map(operations=resize_op, input_columns=\"image\", num_parallel_workers=num_parallel_workers)\n", " mnist_ds = mnist_ds.map(operations=rescale_op, input_columns=\"image\", num_parallel_workers=num_parallel_workers)\n", " mnist_ds = mnist_ds.map(operations=rescale_nml_op, input_columns=\"image\", num_parallel_workers=num_parallel_workers)\n", " mnist_ds = mnist_ds.map(operations=hwc2chw_op, input_columns=\"image\", num_parallel_workers=num_parallel_workers)\n", "\n", " # 进行shuffle、batch、repeat操作\n", " buffer_size = 10000\n", " mnist_ds = mnist_ds.shuffle(buffer_size=buffer_size)\n", " mnist_ds = mnist_ds.batch(batch_size, drop_remainder=True)\n", " mnist_ds = mnist_ds.repeat(count=repeat_size)\n", "\n", " return mnist_ds" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "其中,`batch_size`为每组包含的数据个数,现设置每组包含32个数据。\n", "\n", "## 创建模型\n", "\n", "使用LuoJiaNET定义神经网络需要继承`luojianet_ms.nn.Module`,所有算子都继承自Module类\n", "\n", "神经网络的各层需要预先在`__init__`方法中定义,然后通过定义`call`方法来完成神经网络的构造。经典的LeNet的网络结构,定义网络各层如下:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import luojianet_ms.nn as nn\n", "from luojianet_ms.common.initializer import Normal\n", "\n", "class LeNet5(nn.Module):\n", " \"\"\"\n", " Lenet网络结构\n", " \"\"\"\n", " def __init__(self, num_class=10, num_channel=1):\n", " super(LeNet5, self).__init__()\n", " # 定义所需要的运算\n", " self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode='valid')\n", " self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid')\n", " self.fc1 = nn.Dense(16 * 5 * 5, 120, weight_init=Normal(0.02))\n", " self.fc2 = nn.Dense(120, 84, weight_init=Normal(0.02))\n", " self.fc3 = nn.Dense(84, num_class, weight_init=Normal(0.02))\n", " self.relu = nn.ReLU()\n", " self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2)\n", " self.flatten = nn.Flatten()\n", "\n", " def call(self, x):\n", " # 使用定义好的运算构建前向网络\n", " x = self.conv1(x)\n", " x = self.relu(x)\n", " x = self.max_pool2d(x)\n", " x = self.conv2(x)\n", " x = self.relu(x)\n", " x = self.max_pool2d(x)\n", " x = self.flatten(x)\n", " x = self.fc1(x)\n", " x = self.relu(x)\n", " x = self.fc2(x)\n", " x = self.relu(x)\n", " x = self.fc3(x)\n", " return x\n", "\n", "# 实例化网络\n", "net = LeNet5()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 定义模型损失函数\n", "\n", "损失函数是模型迭代训练的目标函数。\n", "\n", "LuoJiaNET支持的损失函数有`SoftmaxCrossEntropyWithLogits`、`L1Loss`、`MSELoss`等。这里使用交叉熵损失函数`SoftmaxCrossEntropyWithLogits`。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 定义损失函数\n", "net_loss = nn.SoftmaxCrossEntropyWithLogits(sparse=True, reduction='mean')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> 阅读更多有关[在LuoJiaNET中使用损失函数](https://www.luojianet.cn/tutorials/zh-CN/master/optimization.html#损失函数)的信息。\n", "\n", "LuoJiaNET支持的优化器有`Adam`、`AdamWeightDecay`、`Momentum`等。这里使用`Momentum`优化器为例。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 定义优化器\n", "net_opt = nn.Momentum(net.trainable_params(), learning_rate=0.01, momentum=0.9)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> 阅读更多有关[在LuoJiaNET中使用优化器](https://www.luojianet.cn/tutorials/zh-CN/master/optimization.html#优化器)的信息。\n", "\n", "## 训练及保存模型\n", "\n", "LuoJiaNET框架提供了模型保存的函数`ModelCheckpoint`。\n", "`ModelCheckpoint`可以保存网络模型和参数,以便进行后续的微调操作。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from luojianet_ms.train.callback import ModelCheckpoint, CheckpointConfig\n", "# 设置模型保存参数\n", "config_ck = CheckpointConfig(save_checkpoint_steps=1875, keep_checkpoint_max=10)\n", "# 应用模型保存参数\n", "ckpoint = ModelCheckpoint(prefix=\"checkpoint_lenet\", config=config_ck)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "LuoJiaNET提供`model.train`接口进行网络训练,`LossMonitor`可监控训练过程中损失值的变化。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 导入模型训练需要的库\n", "from luojianet_ms.nn import Accuracy\n", "from luojianet_ms.train.callback import LossMonitor\n", "from luojianet_ms import Model\n", "\n", "def train_net(model, epoch_size, data_path, repeat_size, ckpoint_cb, sink_mode):\n", " \"\"\"定义训练的方法\"\"\"\n", " # 加载训练数据集\n", " ds_train = create_dataset(os.path.join(data_path, \"train\"), 32, repeat_size)\n", " model.train(epoch_size, ds_train, callbacks=[ckpoint_cb, LossMonitor(125)], dataset_sink_mode=sink_mode)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "通过运行model.eval接口,在训练过程中交叉验证模型训练的精度。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def test_net(model, data_path):\n", " \"\"\"定义验证的方法\"\"\"\n", " ds_eval = create_dataset(os.path.join(data_path, \"test\"))\n", " acc = model.eval(ds_eval, dataset_sink_mode=False)\n", " print(\"{}\".format(acc))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这里把`train_epoch`设置为1,对数据集进行1个迭代的训练。在`train_net`和 `test_net`方法中,我们加载了之前下载的训练数据集,`mnist_path`是MNIST数据集路径。" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "train_epoch = 1\n", "mnist_path = \"./datasets/MNIST_Data\"\n", "dataset_size = 1\n", "model = Model(net, net_loss, net_opt, metrics={\"Accuracy\": Accuracy()})\n", "train_net(model, train_epoch, mnist_path, dataset_size, ckpoint, False)\n", "test_net(model, mnist_path)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "使用以下命令运行脚本:\n", "\n", "```bash\n", "python lenet.py\n", "```\n", "\n", "## 加载模型" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from luojianet_ms import load_checkpoint, load_param_into_net\n", "# 加载已经保存的用于测试的模型\n", "param_dict = load_checkpoint(\"checkpoint_lenet-1_1875.ckpt\")\n", "# 加载参数到网络中\n", "load_param_into_net(net, param_dict)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "## 验证模型\n", "\n", "我们使用加载的模型和权重进行单个图片数据的分类预测,具体步骤如下:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "from luojianet_ms import Tensor\n", "\n", "# 定义测试数据集,batch_size设置为1,则取出一张图片\n", "ds_test = create_dataset(os.path.join(mnist_path, \"test\"), batch_size=1).create_dict_iterator()\n", "data = next(ds_test)\n", "\n", "# images为测试图片,labels为测试图片的实际分类\n", "images = data[\"image\"].asnumpy()\n", "labels = data[\"label\"].asnumpy()\n", "\n", "# 使用函数model.predict预测image对应分类\n", "output = model.predict(Tensor(data['image']))\n", "predicted = np.argmax(output.asnumpy(), axis=1)\n", "\n", "# 输出预测分类与实际分类\n", "print(f'Predicted: \"{predicted[0]}\", Actual: \"{labels[0]}\"')" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.6" } }, "nbformat": 4, "nbformat_minor": 4 }