{ "cells": [ { "cell_type": "markdown", "source": [ "# 使用字符级RNN分类名称\n", "\n", "`Ascend` `GPU` `进阶` `自然语言处理` `全流程`\n", "\n", "[![](https://gitee.com/mindspore/docs/raw/r1.5/resource/_static/logo_modelarts.png)](https://authoring-modelarts-cnnorth4.huaweicloud.com/console/lab?share-url-b64=aHR0cHM6Ly9taW5kc3BvcmUtd2Vic2l0ZS5vYnMuY24tbm9ydGgtNC5teWh1YXdlaWNsb3VkLmNvbS9ub3RlYm9vay9yMS41L3R1dG9yaWFscy96aF9jbi9taW5kc3BvcmVfcm5uX2NsYXNzaWZpY2F0aW9uLmlweW5i&imageid=59a6e9f5-93c0-44dd-85b0-82f390c5d53b) [![](https://gitee.com/mindspore/docs/raw/r1.5/resource/_static/logo_notebook.png)](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/r1.5/tutorials/zh_cn/mindspore_rnn_classification.ipynb) [![](https://gitee.com/mindspore/docs/raw/r1.5/resource/_static/logo_download_code.png)](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/r1.5/tutorials/zh_cn/mindspore_rnn_classification.py) [![](https://gitee.com/mindspore/docs/raw/r1.5/resource/_static/logo_source.png)](https://gitee.com/mindspore/docs/blob/r1.5/tutorials/source_zh_cn/intermediate/text/rnn_classification.ipynb)" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 概述" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "循环神经网络(Recurrent Neural Network, RNN)是一类以序列(sequence)数据为输入,在序列的演进方向进行递归(recursion)且所有节点(循环单元)按链式连接的递归神经网络(recursive neural network),常用于NLP领域当中来解决序列化数据的建模问题。\n", "\n", "本教程我们将建立和训练基本的字符级RNN模型对单词进行分类,以帮助理解循环神经网络原理。实验中,我们将训练来自18种语言的数千种姓氏,并根据拼写内容预测名称的来源。\n", "\n", "> 本篇基于GPU/Ascend环境运行。" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 准备环节" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### 环境配置" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "我们使用`PyNative`模式运行实验,使用Ascend环境。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 1, "source": [ "from mindspore import context\n", "\n", "context.set_context(mode=context.PYNATIVE_MODE, device_target=\"Ascend\")" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "### 准备数据\n", "\n", "数据集是来自18种语言的数千种姓氏,点击[这里](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/intermediate/data.zip)下载数据,并将其提取到当前目录。\n", "\n", "数据集目录结构为`data/names`,目录中包含 18 个文本文件,名称为`[Language].txt`。 每个文件包含一系列名称,每行一个名称。数据大多数是罗马化的,需要将其从Unicode转换为ASCII。\n", "\n", "可在Jupyter Notebook中执行以下代码完成数据集的下载,并将数据集解压完成。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 2, "source": [ "!wget -N https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/datasets/intermediate/data.zip\n", "!unzip -n data.zip" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "下载解压后的数据集目录如下:\n", "\n", "```text\n", ".\n", "└─ data\n", " ├─ eng-fra.txt\n", " └─ names\n", " ├── Arabic.txt\n", " ├── Chinese.txt\n", " ...\n", "```" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 数据处理" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 导入模块。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 3, "source": [ "from io import open\n", "import glob\n", "import os\n", "import unicodedata\n", "import string" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 定义`find_files`函数,查找符合通配符要求的文件。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 4, "source": [ "def find_files(path): \n", " return glob.glob(path)\n", "\n", "print(find_files('data/names/*.txt'))" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "['data/names/German.txt', 'data/names/Dutch.txt', 'data/names/English.txt', 'data/names/Italian.txt', 'data/names/Vietnamese.txt', 'data/names/Portuguese.txt', 'data/names/Korean.txt', 'data/names/Spanish.txt', 'data/names/French.txt', 'data/names/Russian.txt', 'data/names/Greek.txt', 'data/names/Arabic.txt', 'data/names/Irish.txt', 'data/names/Chinese.txt', 'data/names/Czech.txt', 'data/names/Polish.txt', 'data/names/Japanese.txt', 'data/names/Scottish.txt']\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 定义`unicode_to_ascii`函数,将Unicode转换为ASCII。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 5, "source": [ "all_letters = string.ascii_letters + \" .,;'\"\n", "n_letters = len(all_letters)\n", "\n", "def unicode_to_ascii(s):\n", " return ''.join(\n", " c for c in unicodedata.normalize('NFD', s)\n", " if unicodedata.category(c) != 'Mn'\n", " and c in all_letters\n", " )\n", "\n", "print(unicode_to_ascii('Bélanger'))" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "Belanger\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 定义`read_lines`函数,读取文件,并将文件每一行内容的编码转换为ASCII。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 6, "source": [ "def read_lines(filename):\n", " lines = open(filename, encoding='utf-8').read().strip().split('\\n')\n", " return [unicode_to_ascii(line) for line in lines]" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "定义`category_lines`字典和`all_categories`列表。\n", "\n", "- `category_lines`:key为语言的类别,value为名称的列表。\n", "- `all_categories`:所有语言的种类。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 7, "source": [ "category_lines = {}\n", "all_categories = []\n", "\n", "for filename in find_files('data/names/*.txt'):\n", " category = os.path.splitext(os.path.basename(filename))[0]\n", " all_categories.append(category)\n", " lines = read_lines(filename)\n", " category_lines[category] = lines\n", "\n", "n_categories = len(all_categories)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 将语言选定为French,内容为前5行的数据进行打印显示。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 8, "source": [ "print(category_lines['French'][:5])" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "['Abel', 'Abraham', 'Adam', 'Albert', 'Allard']\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 将名称转换为向量" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "因为字符无法进行数学运算,所以需要将名称转变为向量。\n", "\n", "为了表示单个字母,我们使用大小为`<1 x n_letters>`的one-hot向量,因为将离散型特征使用one-hot编码,会让特征之间的距离计算更加合理。\n", "\n", "> one-hot向量用0填充,但当前字母的索引处的数字为1,例如 `\"b\" = <0 1 0 0 0 ...>`。\n", "\n", "为了组成单词,我们将其中的一些向量连接成2D矩阵``。" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 导入模块" ], "metadata": {} }, { "cell_type": "code", "execution_count": 9, "source": [ "import numpy as np\n", "\n", "from mindspore import Tensor\n", "from mindspore import dtype as mstype" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 定义`letter_to_index`函数,从`all_letters`列表中查找字母索引。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 10, "source": [ "def letter_to_index(letter):\n", " return all_letters.find(letter)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 定义`letter_to_tensor`函数,将字母转换成维度是`<1 x n_letters>`的one-hot向量。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 11, "source": [ "def letter_to_tensor(letter):\n", " tensor = Tensor(np.zeros((1, n_letters)),mstype.float32)\n", " tensor[0,letter_to_index(letter)] = 1.0\n", " return tensor" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 定义`line_to_tensor`函数,将一行转化为``的one-hot向量。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 12, "source": [ "def line_to_tensor(line):\n", " tensor = Tensor(np.zeros((len(line), 1, n_letters)),mstype.float32)\n", " for li, letter in enumerate(line):\n", " tensor[li,0,letter_to_index(letter)] = 1.0\n", " return tensor" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 分别将字母A和单词Alex转换为one-hot向量,并打印显示。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 13, "source": [ "print(letter_to_tensor('A'))\n", "print(line_to_tensor('Alex').shape)" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "[[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.\n", " 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.\n", " 0. 0. 0. 0. 0. 0. 0. 0. 0.]]\n", "(4, 1, 57)\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 创建网络\n", "\n", "创建的RNN网络只有`i2o`和`i2h`两个线性层,它们在输入`input`和隐藏状态`hidden`下运行,在线性层`i2o`的输出之后是`LogSoftmax`层。其中,网络结构如图所示。\n", "\n", "![rnn](https://gitee.com/mindspore/docs/raw/r1.5/tutorials/source_zh_cn/intermediate/text/images/run1.png)" ], "metadata": {} }, { "cell_type": "code", "execution_count": 14, "source": [ "from mindspore import nn, ops\n", "\n", "class RNN(nn.Cell):\n", " def __init__(self, input_size, hidden_size, output_size):\n", " super(RNN, self).__init__()\n", " self.hidden_size = hidden_size\n", " self.i2h = nn.Dense(input_size + hidden_size, hidden_size)\n", " self.i2o = nn.Dense(input_size + hidden_size, output_size)\n", " self.softmax = nn.LogSoftmax(axis=1)\n", "\n", " def construct(self, input, hidden):\n", " op = ops.Concat(axis=1)\n", " combined = op((input, hidden))\n", " hidden = self.i2h(combined)\n", " output = self.i2o(combined)\n", " output = self.softmax(output)\n", " return output, hidden\n", "\n", " def initHidden(self):\n", " return Tensor(np.zeros((1, self.hidden_size)),mstype.float32)\n", " \n", "n_hidden = 128\n", "rnn = RNN(n_letters, n_hidden, n_categories)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "要运行此网络,我们需要输入代表当前字母的one-hot向量,以及上一个字母输出的隐藏状态(将隐藏状态初始化为0)。此网络将输出属于每种语言的概率和下一个字母需要输入的隐藏状态。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 15, "source": [ "input = letter_to_tensor('A')\n", "hidden = Tensor(np.zeros((1, n_hidden)), mstype.float32)\n", "output, next_hidden = rnn(input, hidden)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "为了提高效率,避免在每一步中都创建一个新向量,因此将使用`line_to_tensor`而不是`letter_to_tensor`,同时采取切片操作。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 16, "source": [ "input = line_to_tensor('Albert')\n", "hidden = Tensor(np.zeros((1, n_hidden)), mstype.float32)\n", "output, next_hidden = rnn(input[0], hidden)\n", "print(output)" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "[[-2.893743 -2.8924723 -2.8802812 -2.884685 -2.8995385 -2.8837736\n", " -2.9037814 -2.8999913 -2.8988907 -2.894345 -2.901554 -2.8825603\n", " -2.8956528 -2.8768175 -2.8908525 -2.8856812 -2.8936315 -2.8692 ]]\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "可以看到,输出为`<1 x n_categories>`形式的向量,其中每个数字都代表了分类的可能性。" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 训练\n", "\n", "### 准备训练\n", "\n", "- 定义`category_from_output`函数,获得网络模型输出的最大值,也就是分类类别概率为最大的类别。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 17, "source": [ "def category_from_output(output):\n", " topk = ops.TopK(sorted=True)\n", " top_n, top_i = topk(output, 1)\n", " category_i = top_i.asnumpy().item(0)\n", " return all_categories[category_i], category_i\n", "\n", "print(category_from_output(output))" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "('Scottish', 17)\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 通过`random_training`函数随机选择一种语言和其中一个名称作为训练数据。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 18, "source": [ "import random\n", "\n", "# 随机选择\n", "def random_choice(l):\n", " return l[random.randint(0, len(l) - 1)]\n", "\n", "# 随机选择一种语言和一个名称\n", "def random_training():\n", " category = random_choice(all_categories)\n", " line = random_choice(category_lines[category])\n", " category_tensor = Tensor([all_categories.index(category)], mstype.int32)\n", " line_tensor = line_to_tensor(line)\n", " return category, line, category_tensor, line_tensor\n", "\n", "# 随机选10组\n", "for i in range(10):\n", " category, line, category_tensor, line_tensor = random_training()\n", " print('category =', category, '/ line =', line)" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "category = Polish / line = Dziedzic\n", "category = Japanese / line = Nagano\n", "category = Russian / line = Harlampovich\n", "category = Korean / line = Youj\n", "category = Greek / line = Horiatis\n", "category = Polish / line = Warszawski\n", "category = Italian / line = Barsetti\n", "category = Spanish / line = Cuellar\n", "category = English / line = Feetham\n", "category = Japanese / line = Okita\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### 训练网络" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 定义`NLLLoss`损失函数。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 19, "source": [ "import mindspore.ops as ops\n", "\n", "class NLLLoss(nn.LossBase):\n", " def __init__(self, reduction='mean'):\n", " super(NLLLoss, self).__init__(reduction)\n", " self.one_hot = ops.OneHot()\n", " self.reduce_sum = ops.ReduceSum()\n", "\n", " def construct(self, logits, label):\n", " label_one_hot = self.one_hot(label, ops.shape(logits)[-1], ops.scalar_to_array(1.0), ops.scalar_to_array(0.0))\n", " loss = self.reduce_sum(-1.0 * logits * label_one_hot, (1,))\n", " return self.get_loss(loss)" ], "outputs": [], "metadata": {} }, { "cell_type": "code", "execution_count": 20, "source": [ "criterion = NLLLoss()" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "每个循环训练将会执行下面几个步骤:\n", "\n", "- 创建输入和目标向量\n", "- 初始化隐藏状态\n", "- 学习每个字母并保存下一个字母的隐藏状态\n", "- 比较最终输出与目标值\n", "- 反向传播梯度变化\n", "- 返回输出和损失值" ], "metadata": {} }, { "cell_type": "markdown", "source": [ "- MindSpore将损失函数,优化器等操作都封装到了Cell中,但是本教程的rnn网络需要循环一个序列长度之后再求损失,所以我们需要自定义`WithLossCellRnn`类,将网络和Loss连接起来。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 21, "source": [ "class WithLossCellRnn(nn.Cell):\n", " def __init__(self, backbone, loss_fn):\n", " super(WithLossCellRnn, self).__init__(auto_prefix=True)\n", " self._backbone = backbone\n", " self._loss_fn = loss_fn\n", "\n", " def construct(self, line_tensor, hidden, category_tensor):\n", " for i in range(line_tensor.shape[0]):\n", " output, hidden = self._backbone(line_tensor[i], hidden)\n", " return self._loss_fn(output, category_tensor)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 创建优化器、`WithLossCellRnn`实例和`TrainOneStepCell`训练网络。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 22, "source": [ "rnn_cf = RNN(n_letters, n_hidden, n_categories)\n", "optimizer = nn.Momentum(filter(lambda x: x.requires_grad, rnn_cf.get_parameters()), 0.001, 0.9)\n", "net_with_criterion = WithLossCellRnn(rnn_cf, criterion)\n", "net = nn.TrainOneStepCell(net_with_criterion, optimizer)\n", "net.set_train()\n", "\n", "# 训练网路\n", "def train(category_tensor, line_tensor):\n", " hidden = rnn_cf.initHidden()\n", " loss = net(line_tensor, hidden, category_tensor)\n", " for i in range(line_tensor.shape[0]):\n", " output, hidden = rnn_cf(line_tensor[i], hidden)\n", " return output, loss" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 为了跟踪网络模型训练过程中的耗时,定义`time_since`函数,用来计算训练运行的时间,方便我们持续看到训练的整个过程。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 23, "source": [ "import time\n", "import math\n", "\n", "n_iters = 10000\n", "print_every = 500\n", "plot_every = 100\n", "current_loss = 0\n", "all_losses = []\n", "\n", "def time_since(since):\n", " now = time.time()\n", " s = now - since\n", " m = math.floor(s / 60)\n", " s -= m * 60\n", " return '%dm %ds' % (m, s)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 通过`print_every`(500)次迭代就打印一次,分别打印迭代次数、迭代进度、迭代所用时间、损失值、语言名称、预测语言类型、是否正确,其中通过✓、✗来表示模型判断的正误。同时,根据`plot_every`的值计算平均损失,将其添加进`all_losses`列表,以便于后面绘制训练过程中损失函数的图像。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 24, "source": [ "start = time.time()\n", "\n", "for iter in range(1, n_iters + 1):\n", " category, line, category_tensor, line_tensor = random_training()\n", " output, loss = train(category_tensor, line_tensor)\n", " current_loss += loss\n", "\n", " # 分别打印迭代次数、迭代进度、迭代所用时间、损失值、语言名称、预测语言类型、是否正确\n", " if iter % print_every == 0:\n", " guess, guess_i = category_from_output(output)\n", " correct = '✓' if guess == category else '✗ (%s)' % category\n", " print('%d %d%% (%s) %s %s / %s %s' % (iter, iter / n_iters * 100, time_since(start), loss.asnumpy(), line, guess, correct))\n", "\n", " # 将loss的平均值添加至all_losses\n", " if iter % plot_every == 0:\n", " all_losses.append((current_loss / plot_every).asnumpy())\n", " current_loss = 0" ], "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "500 5% (0m 56s) 2.8811955 Cheng / Korean ✗ (Chinese)\n", "1000 10% (1m 47s) 2.8138192 Glennon / Russian ✗ (English)\n", "1500 15% (2m 37s) 2.7881339 Marugo / Italian ✗ (Japanese)\n", "2000 20% (3m 29s) 2.9860206 O'Meara / Japanese ✗ (Irish)\n", "2500 25% (4m 18s) 2.857955 Wen / Irish ✗ (Chinese)\n", "3000 30% (5m 12s) 2.411959 O'Hannigain / Irish ✓\n", "3500 35% (6m 28s) 2.1828568 Hishikawa / Japanese ✓\n", "4000 40% (7m 17s) 2.5861049 Kennedy / Irish ✓\n", "4500 45% (9m 45s) 3.1115925 La / Japanese ✗ (Vietnamese)\n", "5000 50% (12m 36s) 2.811106 Cavey / Russian ✗ (French)\n", "5500 55% (13m 35s) 2.8926034 Christodoulou / Vietnamese ✗ (Greek)\n", "6000 60% (14m 22s) 2.5833995 Nanami / Italian ✗ (Japanese)\n", "6500 65% (15m 6s) 2.9273236 Sissons / Greek ✗ (English)\n", "7000 70% (15m 54s) 2.8183262 Houttum / Vietnamese ✗ (Dutch)\n", "7500 75% (16m 41s) 3.0385728 Winograd / Arabic ✗ (Polish)\n", "8000 80% (17m 31s) 3.0026562 Morales / Greek ✗ (Spanish)\n", "8500 85% (18m 21s) 2.670665 Roach / Vietnamese ✗ (Irish)\n", "9000 90% (19m 8s) 3.0125608 Kendrick / Polish ✗ (English)\n", "9500 95% (19m 58s) 2.8149955 Kazmier / German ✗ (Czech)\n", "10000 100% (20m 46s) 2.3972077 Chin / Irish ✗ (Korean)\n" ] } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "### 绘制结果\n", "\n", "从`all_losses`绘制网络模型学习过程中每个step得到的损失值,可显示网络学习情况。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 25, "source": [ "import matplotlib.pyplot as plt\n", "\n", "plt.figure()\n", "plt.plot(all_losses)" ], "outputs": [ { "output_type": "execute_result", "data": { "text/plain": [ "[]" ] }, "metadata": {}, "execution_count": 26 }, { "output_type": "display_data", "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" } } ], "metadata": {} }, { "cell_type": "markdown", "source": [ "## 评估结果\n", "\n", "- 为了查看网络在不同分类上的表现,我们将创建一个混淆矩阵,行坐标为实际语言,列坐标为预测的语言。为了计算混淆矩阵,使用`evaluate()`函数进行模型推理。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 27, "source": [ "# 在混淆矩阵中记录正确预测\n", "confusion = Tensor(np.zeros((n_categories, n_categories)), mstype.float32)\n", "n_confusion = 1000\n", "\n", "# 模型推理\n", "def evaluate(line_tensor):\n", " hidden = rnn_cf.initHidden()\n", " for i in range(line_tensor.shape[0]):\n", " output, hidden = rnn_cf(line_tensor[i], hidden)\n", " return output\n", "\n", "# 运行样本,并记录正确的预测\n", "for i in range(n_confusion):\n", " category, line, category_tensor, line_tensor = random_training()\n", " output = evaluate(line_tensor)\n", " guess, guess_i = category_from_output(output)\n", " category_i = all_categories.index(category)\n", " confusion[category_i, guess_i] += 1\n", "\n", "for i in range(n_categories):\n", " confusion[i] / Tensor(np.sum(confusion[i].asnumpy()), mstype.float32)" ], "outputs": [], "metadata": {} }, { "cell_type": "markdown", "source": [ "- 使用`matplotlib`绘制混淆矩阵的图像。" ], "metadata": {} }, { "cell_type": "code", "execution_count": 28, "source": [ "from matplotlib import ticker\n", "\n", "# 绘制图表\n", "fig = plt.figure()\n", "ax = fig.add_subplot(111)\n", "cax = ax.matshow(confusion.asnumpy())\n", "fig.colorbar(cax)\n", "\n", "# 设定轴\n", "ax.set_xticklabels([''] + all_categories, rotation=90)\n", "ax.set_yticklabels([''] + all_categories)\n", "\n", "# 在坐标处添加标签\n", "ax.xaxis.set_major_locator(ticker.MultipleLocator(1))\n", "ax.yaxis.set_major_locator(ticker.MultipleLocator(1))\n", "\n", "plt.show()" ], "outputs": [ { "output_type": "display_data", "data": { "image/png": "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", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" } } ], "metadata": {} } ], "metadata": { "kernelspec": { "display_name": "MindSpore-python3.7-aarch64", "language": "python", "name": "mindspore-python3.7-aarch64" }, "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.5" }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false } }, "nbformat": 4, "nbformat_minor": 4 }