您的位置 首页 PyTorch 项目

Pytorch实战:使用 RNN 对姓名进行分类

PyTorch入门实战教程

本文我们构建基于字母层级(粒度是字母而不是单词或者单个的字) 循环神经网络RNN 来姓名进行分类预测。

在每一次循环过程中,字母层级的RNN 会以字母列表方式输入 姓名(单词),神经网络会输出一个预测结果outpu 和 隐藏状态hidden_state,且 隐藏状态hidden_state会作为参数传入到下一个层网络中。我们将RNN最终的输出的结果作为预测结果(类别标签)。

具体的,我们从 18 种语言的成千上万个姓名数据中开始训练,并根据姓氏拼写来预测该姓名所属语言类别 。

准备数据

Data 数据地址:https://github.com/spro/practical-pytorch/tree/master/data/names

在 data文件夹 中有18 个txt文件,且都是以 某种语言名.txt 命名。 每个txt文件中含有很多姓氏名,每个姓氏名独占一行,有些语言使用的是 Unicode码(含有除了26英文字母以外的其他字符),我们需要将其统一成 ASCII码

all_filenames结果

将将Unicode码转换成标准的ASCII码,直接谷歌找到的stackoverflow上的解决办法。

打印结果

构建 语言类别-姓名映射字典,形如  {language1: [name1, name2, ...], language2: [name_x1, name_x2, ...]}

现在我们有 category_names 语言-姓名映射词典。

显示前5个姓名

将姓名转化为Tensors

跟机器学习类似,在这里我们也需要将文本转化为具体的计算机能理解的数据形式。

为了表征单个的字符, 我们使用 独热编码向量one-hot vector, 该向量的尺寸为 1 x n_letters(每个字符是2维向量)

例如

每个由多个字符(每个字符是2维)组成的姓名 转化为3维,尺寸为 name_length x 1 x n_letters

在pytorch中,所有输入的数据都假设是在batch中。所以才能看到尺寸 name_length x 1 x n_letters 中的 1

打印上面三行代码运行结果

定义letter_to_tensor函数

现在我们运行letter_to_tensor(‘J’)

显示上面代码运行结果

显示上面代码运行结果

构建神经网络

注意看图中各个参数解读:

  • input: 输入的数据
  • hidden: 神经网络现有的参数矩阵
  • combined: input矩阵与hidden矩阵合并,两个矩阵的行数一致,input和hidden分布位于新矩阵的 左侧和右侧
  • i2o:对输入的数据转化为output的计算过程
  • 12h:将输入的数据转化为hidden参数的计算过程
  • output:当前网络的输出
  • hidden:当前网络传递给下层网络的参数

大家仔细看看琢磨琢磨这个图构造。现在我们先看看 combined 这个操作

打印结果

开始DIY我们第一个循环神经网络RNN,各个参数解读:

  • input_size: 表征字母的向量的特征数量(向量长度)
  • hidden_size: 隐藏层特征数量(列数)
  • output_size: 语言数目,18
  • i2h: 隐藏网络参数的计算过程。输入的数据尺寸为input_size + hidden_size, 输出的尺寸为 hidden_size
  • i2o: 输出网络参数的计算过程。输入的数据尺寸为input_size + hidden_size, 输出的尺寸为 output_size

检验我们构建的RNN网络

定义好 RNN 类之后,我们可以创建RNN的实例

要运行此网络,我们需要给网络传入:

  • input(在我们的例子中,是当前字母的Tensor)
  • hidden(我们首先将隐藏层参数初始化为零)

经过网络内部的运算,我们将得到:

  • output(每种语言的可能性的大小)
  • next_hidden(传递给下一个网络的隐藏状态hidden)

显示上面代码运行结果

现在我们使用 line_to_tensor  替换 letter_to_tensor  来构件输入的数据。注意在本例子中,给RNN网络一次输入一个姓名数据,但对该网络而言,是将姓名数据拆分成字母数组数据,逐次输入训练网络,直到这个姓名最后一个字母数组输入完成,才输出真正的预测结果(姓名所属的语言类别)。

输入 RNN神经网络 的数据的粒度变细,不再是 姓名数组数据(三维),而是组成姓名的字母的数组或矩阵(二维)

显示上述结果

现在我们看看output这个tensor中的含有数据,想办法从中提取出预测的 语言类别信息

具体思路:

  1. 因为output是tensor,我们可以先获取这个tensor中的data
  2. 再使用基于data的topk方法,提取tensor中似然值最大的索引值。

该索引值就是 所属语言类别的索引值 ,具体我们可以看下面的例子更好的理解tensor的操作方法。

显示上面两行代码运行结果

上面的两行代码,

其中第一行代码得到tensor中的data

第二行代码得到某姓姓名(这里我们实际上只输入了一个字母,姑且当成只有一个字母的姓名)的 所属语言的似然值 及 所属语言类别的索引值

显示上面tpo_n和  top_i

接下来我们继续看

显示top_i[0][0]

准备训练RNN

在训练前,我们把上面刚刚测试的求 所属语言类别的索引值 方法封装成函数 category_from_output

该函数输入:

  • output: RNN网络输出的output

该函数输出:

  • 语言类别
  • 语言类别索引值

显示category_from_output(output)运行结果

类比机器学习中需要将数据打乱,这里我们也要增入随机性(打乱)。

但不是将训练数据打乱,而是每次训练时随机的从数据集中抽取一种语言中的一个姓名。

这里我们定义了 random_training_pair 函数, 函数返回的是一个元组(category, name, category_tensor, name_tensor):

  • category: 语言名
  • name: 姓名
  • category_tensor
  • name_tensor

在定义函数前先看下面几个例子,更好的理解函数内部的运算过程。

显示category

上面的随机抽取了 一种语言, 接下来我们在 该语言 中抽取一个 姓名

显示name

训练过程中我们要有标签数据,在本文中  所属语言的索引值 作为 标签

由于pytorch中训练过程中使用的都是tensor结构数据,其中的元素都是浮点型数值,所以这里我们使用LongTensor, 可以保证标签是整数。

另外要注意的是,pytorch中运算的数据都是batch。所以我们要将 所属语言的索引值 放入一个list中,再将该list传入torch.LongTensor()中.

显示category_tensor

同理,name也要转化为tensor,这里我们调用name_to_tensor函数即可。

显示name_tensor

刚刚几个例子,相信大家已经明白了函数内部的实现方法,现在将其封装成 random_training_pair函数

上述代码块运行结果

训练RNN网络

我们使用 nn.CrossEntropyLoss 作为评判标准,来检验 姓名真实所属的语言truth  与 预测该姓名得到预测所属语言类别predict 比对,计算RNN网络训练的误差。

我们也创建了 优化器optimizer, 常用的优化器是SGD算法 。当 每次训练网络,我们比对结果, 好则改之, 无则加勉, 让该网络改善的学习率learning rate(改进的速度)设置为0.005 。

注意学习率learning rate不能设置的太大或者太小:

  • 所谓欲速则不达,太大导致训练效果不佳。容易大条
  • 太小了会导致训练速度太慢,遥遥无期。

每轮训练将:

  • 创建input(name_tensor)和 input对应的语言类别标签(category_tensor)
  • 当输入姓名第一个字母时,需要初始化隐藏层参数。
  • 读取姓名中的每个字母的数组信息,传入rnn,并将网络输出的hidden_state和下一个字母数组信息传入之后的RNN网络中
  • 使用criterion比对 最终输出结果  与 姓名真实所属的语言标签 作比较
  • 更新网络参数,改进网络。
  • 循环往复以上几步

现在我们可以使用一大堆姓名和语言数据来训练RNN网络,因为 train函数 会同时返回 预测结果 和 训练误差, 我们可以打印并可视化这些信息。

为了方便,我们每训练5000次(5000个姓名),就打印 一个姓名的预测结果,并 查看该姓名是否预测正确

我们对每1000次的训练累计误差,最终将误差 可视化出来。

上面代码块运行结果

绘制训练误差

从误差图中可以看出,随着训练轮数的增加,模型的每1000次训练的平均误差越来越小。

手动检验训练的模型

为了方便,我们定义了 predict(rnn, input_name, n_predictions=3)函数

  • rnn: 训练得到的rnn网络
  • input_name: 姓名字符串
  • n_predictions:该姓名预测结果的前n_predictions个预测结果

上述代码块运行结果

思考Exercises

比照本文,我们还可做很多类似的训练,比如

  • 根据任意词汇 -> 所属语言
  • 根据名字 -> 名字的性别
  • 文章标题 -> 文章所属话题

为了得到更准确的神经网络(更准确):

  • 添加更多层Add more linear layers
  • 尝试使用 nn.LSTM 或者 nn.GRU
  • 聚合多种(如rnn、lstm、gru)为更高级的网络

项目源码:https://github.com/spro/practical-pytorch

文章来源:大邓和他的Python(DaDengAndHisPython)

本站微信群、QQ群(三群号 726282629):

PyTorch入门实战教程

发表回复

您的电子邮箱地址不会被公开。

返回顶部