教程】Tensorflow vs PyTorch —— 循环神经

作者: Hongtao洪滔 | 来源:发表于2020-03-27 06:37 被阅读0次

image form unsplash.com by @chrishenryphoto

循环神经网络 (RNN) 主要处理时间序列的数据,比如语音,文字等等。循环神经网络的基本概念在之前的文章中已经介绍过了,本文主要是比较一下 Tensorflow 和 PyTorch 中如何搭建和训练循环神经网络。

获取文章代码请关注微信公众号"tensor_torch" 二维码见文末

1. RNN 回顾

与处理空间相关性的 CNN 相对应,RNN 主要用于处理时间相关性的问题。以分类影评的问题为例(即判断影评是正面还是负面 IMDB 数据集),我们当然也可以使用全连接的神经网络,将里面每一个单词都连接起来,不过这样的话,模型既占用大量计算资源,又无法感知前后语句的相关性。

在 RNN 中,一句话的每个单词,共享相同结构的神经网络和相应的权值,引入状态张量 (h),将每个单词的输入与输出连接起来,就很好地解决了全连接神经网络的问题,同时也让神经网络记录了前后语意。

局部相关性权重共享这两个特性使得 CNN 在处理图片的时候非常高效。
前后相关性权重共享这两个特性使得 RNN 在处理时间序列的问题时非常高效。

image-2020032594619409

对初学者来说,理解 RNN 最困难的地方是,神经网络需要数据的在时间维度展开。

例如:

  • 图片 shape 为 [b, w, h, c], 我们可以理解为在 b (batch) 维度展开,将[w, h, c] 这样的图片输入神经网络。
  • 句子 shape 为[b, seq_len, embeded_dim] (seq_len 即句子中单词的数量,embeded_dim 指单词编码的维度) 需要在 seq_len 这个维度展开,将每一个单词送入神经网络。

2. 数据导入

在 Tensorflow 中,我们可以直接在 keras.datasets.imdb中导入IMDB的数据库,并转化成Dataset 对象进行预处理,关于Tensorflow 2.0 数据导入和处理的部分参见前面这篇文章。这里注意两点

  1. 数据已经进行了编码,所以数据类型是 int 而不是 string.
  2. 使用了 keras.preprocessing.sequence.pad_sequences将每个句子的长度 (seq_len) 进行了固定。

在 PyTorch 中, 我们从 torchtext.datasets.IMDB 中获取,注意需要将数据的 train_data 和 test_data 进行分离,并且初始化清楚文本(TEXT) 和 标签(LABEL)对象。

TEXT = data.Field(tokenize='spacy')
LABEL = data.LabelField(dtype=torch.float)
train_data, test_data = datasets.IMDB.splits(TEXT, LABEL)

由于这里文本的数据类型是 string 需要编码成 int 以便进行计算,这里通过 glove 进行编码,同时确定 batch size。

TEXT.build_vocab(train_data, max_size=10000, vectors='glove.6B.100d')
LABEL.build_vocab(train_data)

batchsz = 64
device = torch.device('cuda')
train_iterator, test_iterator = data.BucketIterator.splits(
    (train_data, test_data),
    batch_size = batchsz,
    device=device
)

这里需要特别注意:

在 Tensorflow 输入数据的 shape 为 [b, seq_len] 经过 embedding 层之后为 [b, seq_len, embeding_dim]
在 PyTorch 中输入数据的 shape 为 [seq_len, b] 经过 embedding 层之后为[seq_len, b, embeding_dim]

3. RNN 神经网络搭建

首先,在 Tensorflow 中调用 keras.layers.Embedding 对象,在 PyTorch 中调用 torch.nn.Embedding对象对输入进行编码。

其中相对应的参数:
input_dimnum_embeddings相对应,即表示字库容量。
output_dimembedding_dim相对应,即表示编码维度。

此时将数据的 shape:

Tensorflow: 由 [b, seq_len] 变为 [b, seq_len, embedding_len]
PyTorch: Tensorflow: 由 [seq_len, b] 变为 [seq_len, b, embedding_len]

其次, 在 Tensorflow 中调用 keras.layers.SimpleRNN 对象,在 PyTorch 中调用 torch.nn.RNN对象搭建 RNN 层。
这里需要注意的是

keras.layers.SimpleRNN 一次只能搭建一层 RNN 层,如果要叠加多层,参数 return_sequences需要打开以传递中间的状态张量。输出为最后一层的最后一个单词的output,shape 为 [b, units] (unitis 为 神经元个数与 PyTorch 中的 hidden_dim 相对应)

torch.nn.RNN 一次可以搭建多层 RNN 层,同时输出 output 和 状态张量 。output 包含了每个单词的输出,其shape 为[seq_len, b, hidden_dim]; 状态张量 的是每层最后单词的output 其shape 为[num_layers, b, hidden_dim]。(hidden_dim 为神经元个数)

最后通过全连接层输出分类结果。但是需要注意的是在处理 IMDB 的问题中,我们需要最后一层的最后一个单词的输出 (Tensorflow 是直接给出这个输出的),所以在 PyTorch 中需要对数据进行切片。

代码如下

# ---------------Tensorflow-------------
class RNN_model(keras.Model):
    def __init__(self, num_units):
        super().__init__()
    
        # embedding   [b, 80] ->[b, 80 embedding_len=100]  
        self.embedding = layers.Embedding(input_dim=total_words,output_dim=embedding_len,input_length=max_review_words)
        # [b, 80, 100] ->[b, num_units]
        self.RNN1 = layers.SimpleRNN(units=num_units,dropout=0.5, return_sequences=True)
        self.RNN2 = layers.SimpleRNN(units=num_units,dropout=0.5, return_sequences=False)
        
        # [b, num_units] ->[b,1]
        self.fc = layers.Dense(1)
    
    def call(self,x, training = None):
        outputs = self.embedding(x)
        outputs = self.RNN1(outputs, training = training)
        outputs = self.RNN2(outputs, training = training)
        outputs = self.fc(outputs)
        return outputs
# ---------------PyTorch----------------
class RNN_nn(nn.Module):

  def __init__(self, vocab_size, hidden_dim):
    super().__init__()

    self.embedding = nn.Embedding(num_embeddings=vocab_size, embedding_dim=embedding_len)
    self.rnn = nn.RNN(input_size=embedding_len, hidden_size=hidden_dim, num_layers=2, dropout=0.5)
    self.fc = nn.Linear(in_features=hidden_dim, out_features=1)

  def forward(self, x):  

    
    #[seq_len, b] -> [seq_len, b, embedding_len = 100]
    output = self.embedding(x)
    # print('embedding size: ', output.shape)
    
    #[seq_len, b, embedding_len] ->
    # out: [seq_len, b, hidden_dim]
    # h:   [n_layer=2, b, hidden_dim]
    # last seq of out == last layer of h
    output, h = self.rnn(output)


    # Last seq of the output
    out = output[-1:,:,:].squeeze()
    
    out = self.fc(out)

    return out

神经网络的训练和验证部分与全连接神经网络以及CNN非常类似,这里就不赘述了,感兴趣的读者请参考源代码。


相关文章
【教程】Tensorflow vs PyTorch —— 正则化和Dropout等措施
【教程】Tensorflow vs PyTorch —— 可视化 Tensorboard vs Visdom
【教程】Tensorflow vs PyTorch —— 神经网络的搭建和训练
【教程】Tensorflow vs PyTorch —— 自动求导
【教程】Tensorflow vs PyTorch —— 数学运算
【教程】Tensorflow vs PyTorch —— 张量的基本操作
Tensorflow 2 vs PyTorch 对比学习教程开启
Tensorflow 2.0 --- ResNet 实战 CIFAR100 数据集
Tensorflow2.0——可视化工具tensorboard
Tensorflow2.0-数据加载和预处理
Tensorflow 2.0 快速入门 —— 引入Keras 自定义模型
Tensorflow 2.0 快速入门 —— 自动求导与线性回归
Tensorflow 2.0 轻松实现迁移学习
Tensorflow入门——Eager模式像原生Python一样简洁优雅
Tensorflow 2.0 —— 与 Keras 的深度融合


如果喜欢本教程,欢迎关注微信公众号和代码仓(如果喜欢不要忘了给星哟!),

相关文章

网友评论

    本文标题:教程】Tensorflow vs PyTorch —— 循环神经

    本文链接:https://www.haomeiwen.com/subject/awekuhtx.html