Pytorch基础系列(4)
简介
- 循环神经网络的输出取决于当下输入和前一时间的隐变量
- 应用到语言模型中时,循环神经网络根据当前词预测下一次时刻词
- 通常使用困惑度来衡量语言模型的好坏
torch 实现
torch 给出了 rnn 的 api 接口定义
\(x\) 的维度为[seq_len, batch, input_size]
\(x_t\) 的维度为[batch, input_size]
\(h_t\) 的维度 为[batch, hidden_len]
这里假设为 x 的 shape 为[10, 3, 100]: - 10 表示 10 个单词 - 3 表示每次训练 3 句话 - 100 表示每个单词用 100 维的 tensor 表达
所以:\(x_t\) 的 shape为[3, 100]。
假设 hidden_len(memory 的维度)为 20,所以每个 \(h_t\) 的维度为[3, 20]。
根据公式下面给出代码:
\[h_t=tanh(x_tW_{ih}^T+b_{ih}+h_{t−1}W_{hh}^T+b_{hh})\]
复现
这里 rnn = nn. RNN (100, 20) ,指的是用一个长度为 100 的向量表示一个单词,hidden_size 为 20。
1 | import torch |
输出:
odict_keys (['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0'])
torch. Size ([20, 100])
torch. Size ([20, 20])
torch. Size ([20])
torch. Size ([20])
对于 input 的定义:
- 必选参数
input_size,指定输入序列中单个样本的尺寸大小,例如可能用一个 100 长度的向量表示一个单词,则input_size=100 - 必选参数
hidden_size,指的是隐藏层中输出特征的大小,假设为 20 - 必选参数
num_layers,指的是纵向的隐藏层个数,一般设置为 1~10,default=1
如果 num_layers 为 1:
如果 num_layers 为 2:
\[h_t=tanh(x_tW_{ih}^T+b_{ih}+h_{t−1}W_{hh}^T+b_{hh})\]
1 | import torch |
输出:
torch. Size ([10, 3, 20])
torch. Size ([1, 3, 20])
对于多层 RNN 堆叠:
1 | import torch |
输出:
torch. Size ([10, 3, 20])
torch. Size ([4, 3, 20])
实战
问题背景:有 n 句话,每句话都由且仅由 3 个单词组成。我做的是,将每句话的前两个单词作为输入,最后一词作为输出,利用 pytorch 训练一个 TextRNN。
导包
1 | import torch |
数据处理(构建字典和索引)
1 | sentences = ["i like dog", "i love coffee", "i hate milk"] |
vocab
> ['i', 'dog', 'like', 'milk', 'love', 'hate', 'coffee']
word2idx
> {'i': 0, 'dog': 1, 'like': 2, 'milk': 3, 'love': 4, 'hate': 5, 'coffee': 6}
idx2word
> {0: 'i', 1: 'dog', 2: 'like', 3: 'milk', 4: 'love', 5: 'hate', 6: 'coffee'}
n_class
> 7
DataSet 和 DataLoader
1 | batch_size = 2 |
list (dataset)
> [(tensor ([[1., 0., 0., 0., 0., 0., 0.], [0., 0., 1., 0., 0., 0., 0.]]), tensor (1)),
> (tensor ([[1., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 1., 0., 0.]]), tensor (6)),
> (tensor ([[1., 0., 0., 0., 0., 0., 0.], [0., 0., 0., 0., 0., 1., 0.]]), tensor (3))]
list (dataloader)
> [[tensor ([[[1., 0., 0., 0., 0., 0., 0.],[0., 0., 1., 0., 0., 0., 0.]],
> [[1., 0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 1., 0., 0.]]]),
> tensor ([1, 6])],
> [tensor ([[[1., 0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 0., 1., 0.]]]),
> tensor ([3])]]
把函数核心部分进行分解:
1 | input_batch = [] |
input
> [0, 2]
> [0, 4]
> [0, 5]
np. eye(n_class)[input]
> [[1. 0. 0. 0. 0. 0. 0.]
> [0. 0. 1. 0. 0. 0. 0.]]
> [[1. 0. 0. 0. 0. 0. 0.]
> [0. 0. 0. 0. 1. 0. 0.]]
> [[1. 0. 0. 0. 0. 0. 0.]
> [0. 0. 0. 0. 0. 1. 0.]]
input_batch
> [array ([[1., 0., 0., 0., 0., 0., 0.],[0., 0., 1., 0., 0., 0., 0.]]),
> array ([[1., 0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 1., 0., 0.]]),
> array ([[1., 0., 0., 0., 0., 0., 0.],[0., 0., 0., 0., 0., 1., 0.]])]
target_batch
> [1, 6, 3]
定义网络架构
1 | class TextRNN(nn.Module): |
首先是 nn.RNN(input_size, hidden_size) 的两个参数: - input_size 表示每个词的编码维度,由于这里用的 one-hot 编码(不是 WordEmbedding),所以 input_size 就等于词库的大小 len(vocab),即 n_class。 - hidden_size,这个参数没有固定的要求,想将输入数据的维度转为多少维,就设定多少
因为对于一半神经网路而言,输入数据 X 的第一个维度都是 batch_size,但是 pytorch 的 nn.RNN() 要求将 batch_size 放在第二个维度上,所以代码中用 X.transpose(0,1) 将数据的第一个维度和第二个维度互换。
rnn 的输出:rnn 会返回两个结果,即代码中的 out 和 hidden。这里简单说就是,out 指的是下图的红框框起来的所有值;hidden 指的是下图蓝框框起来的所有值。我们需要的是最后时刻的最后一层输出,即 \(Y_3\) 的值,所以使用 out=out[-1] 将其获取。
定义好模型后,就是调用模型、定义优化器 optimizer。
1 | model = TextRNN() |
训练
1 | epoch = 5000 |
Epoch: 1000 cost = 0.000000
Epoch: 1000 cost = 0.000000
Epoch: 2000 cost = 0.000000
Epoch: 2000 cost = 0.000000
Epoch: 3000 cost = 0.000000
Epoch: 3000 cost = 0.000000
Epoch: 4000 cost = 0.000000
Epoch: 4000 cost = 0.000000
Epoch: 5000 cost = 0.000000
Epoch: 5000 cost = 0.000000
预测
1 | input = [sen.split()[:2] for sen in sentences] |
input
> [['i', 'like'], ['i', 'love'], ['i', 'hate']]
hidden
> tensor ([[[0., 0., 0., 0., 0.],
> [0., 0., 0., 0., 0.],
> [0., 0., 0., 0., 0.]]])
predict
> tensor ([[2], [4], [1]])
输出:
> [['i', 'like'], ['i', 'love'], ['i', 'hate']] -> ['dog', 'coffee', 'milk']
其他
nn. RNNCell
相关链接
RNN — PyTorch 1.13 documentation
TextRNN的PyTorch实现 - mathor
RNN Layer - mathor
pytorch中RNN参数的详细解释_pytorch rnn_lwgkzl的博客-CSDN博客
【PyTorch学习笔记】21:nn.RNN和nn.RNNCell的使用_LauZyHou的博客-CSDN博客
【Python学习】transpose函数_python transpose函数_isLauraL的博客-CSDN博客






