导语:情感情绪检测是自然语言理解的关键要素。最近,我们将原来的项目迁移到了新的集成系统上,该系统基于麻省理工学院媒体实验室推出的NLP模型搭建而成。
情感情绪检测是自然语言理解的关键要素。最近,我们将原来的项目迁移到了新的集成系统上,该系统基于麻省理工学院媒体实验室推出的NLP模型搭建而成。
代码已经开源了!(详见GitHub:https://github.com/huggingface/torchMoji )
该模型最初的设计使用了TensorFlow、Theano和Keras,接着我们将其移植到了pyTorch上。与Keras相比,pyTorch能让我们更自由地开发和测试各种定制化的神经网络模块,并使用易于阅读的numpy风格来编写代码。在这篇文章中,我将详细说明在移植过程中出现的几个有趣的问题:
如何使用自定义激活功能定制pyTorch LSTM
PackedSequence对象的工作原理及其构建
如何将关注层从Keras转换成pyTorch
如何在pyTorch中加载数据:DataSet和Smart Batching
如何在pyTorch中实现Keras的权重初始化
首先,我们来看看torchMoji/DeepMoji的模型。它是一个相当标准而强大的人工语言处理神经网络,具有两个双LSTM层,其后是关注层和分类器:

torchMoji/DeepMoji模型
DeepMoji有一个很不错的特点:Bjarke Felbo及其协作者能够在一个拥有16亿条记录的海量数据集上训练该模型。因此,预先训练的模型在此训练集中具有非常丰富的情感和情绪表征,我们可以很方便地使用这个训练过的模型。
该模型是使用针对LSTM的回归内核的Theano/Keras默认激活函数hard sigmoid训练的,而pyTorch是基于NVIDIA的cuDNN库建模的,这样,可获得原生支持LSTM的GPU加速与标准的sigmoid回归激活函数:
Keras默认的LSTM和pyTorch默认的LSTM
因此,我写了一个具有hard sigmoid回归激活函数的自定义LSTM层:
这个LSTM单元必须集成在一个完整的模块中,这样才可以使用pyTorch所有的功能。这个集成相关的代码很长,建议直接引用到Github中的相关源代码。
模型的关注层是一个有趣的模块,我们可以分别在Keras和pyTorch的代码中进行比较:
如你所见,主要的算法大致相同,但PyTorch代码中的大部分都是注释,而Keras则需要编写几个附加函数并进行调用。
在编写和调试自定义模块和层时,pyTorch是一个更快的选择;而对于快速训练和测试由标准层构建的模型时,Keras显然更加合适。
Keras有一个不错的掩码功能可以用来处理可变长度序列。那么在pyTorch中又该如何处理这个呢?可以使用PackedSequences! pyTorch文档中有关PackedSequence的介绍并不是很详细,所以这里会详细描述它的细节。
一个拥有5个序列18个令牌的典型NLP批次
假设我们有一批可变长度的序列(在NLP应用中通常就是这样的)。为了在GPU上并行计算这样一个批次,我们希望:
尽可能多地并行处理这个序列,因为LSTM隐藏状态依赖于每个序列的前一个时间步长,以及
以正确的时间步长(每个序列的结尾)停止每个序列的计算。
这可以通过使用pyTorch中的PackedSequence类来实现。我们首先通过减少长度来对序列进行排序,并将它们放到在张量中。然后对张量和序列长度列表调用pack_padded_sequence函数
PackedSequence对象包括:
一个data对象:一个torch.Variable(令牌的总数,每个令牌的维度),在这个简单的例子中有五个令牌序列(用整数表示):(18,1)
一个batch_sizes对象:每个时间步长的令牌数列表,在这个例子中为:6,5,2,4,1
用pack_padded_sequence函数来构造这个对象非常的简单:
如何构造一个PackedSequence对象(batch_first = True)
PackedSequence对象有一个很不错的特性,就是我们无需对序列解包(这一步操作非常慢)即可直接在PackedSequence数据变量上执行许多操作。特别是我们可以对令牌执行任何操作(即对令牌的顺序/上下文不敏感)。当然,我们也可以使用接受PackedSequence作为输入的任何一个pyTorch模块(pyTorch 0.2)。
例如,在我们的NLP模型中,我们可以在对PackedSequence对象不解包的情况下连接两个LSTM模块的输出,并在此对象上应用LSTM。我们还可以在不解包的情况下执行关注层的一些操作。
在Keras中,数据加载和批处理通常隐藏在fit_generator函数中。重申一遍,如果你想要快速地测试模型,Keras很好用,但这也意味着我们不能完全控制模型中的重要部分。
在pyTorch中,我们将使用三个类来完成这个任务:
一个DataSet类,用于保存、预处理和索引数据集
一个BatchSampler类,用于控制样本如何批量收集
一个DataLoader类,负责将这些批次提供给模型
我们的DataSet类非常简单:
我们BatchSampler则更有趣。
我们有几个小的NLP数据集,用于微调情感情绪检测模型。这些数据集有着不同的长度和某些不平衡的种类,所以我们想设计这么一个批量采样器:
在预先定义的样本数中收集批次,这样我们的训练过程就可以不依赖于批次的长度
能够从不平衡的数据集中以平衡的方式进行采样。
在PyTorch中,BatchSampler是一个可以迭代生成批次的类,BatchSampler的每个批处理都包含一个列表,其中包含要在DataSet中选择的样本的索引。
因此,我们可以定义一个用数据集类标签向量来初始化的BatchSampler对象,以构建满足我们需求的批次列表:
将Keras/Tensorflow/Theano代码移植到pyTorch的过程中,最后需要注意的事情是对权重的初始化。
Keras在开发速度方面的另一个强大特点是层的默认初始化。
相反,pyTorch并没有初始化权重,而是由开发者自己来决定。为了在微调权重时获得一致的结果,我们将像如下代码那样复制默认的Keras权重初始化:
当我们针对一个模型比较Keras和pyTorch这两个框架时,我们可以感觉到它们有着不同的哲学和目标。
根据我的经验来看:
Keras非常适合于快速测试在给定任务上组合标准神经网络块的各种方法;
pyTorch非常适合于快速开发和测试自定义的神经网络模块,因为它有着很大的自由度和易于阅读的numpy风格的代码。