双向语言模型的上下文化词表示的Tensorflow实现
项目描述
bilm-tf
Tensorflow实现预训练的biLM,用于从"深度上下文化词表示"计算ELMo表示。
此存储库支持训练biLM和用于预测的预训练模型。
我们还在AllenNLP中提供PyTorch实现。
如果您只想进行预测,您可能更喜欢在Tensorflow Hub中提供的版本。
引用
@inproceedings{Peters:2018,
author={Peters, Matthew E. and Neumann, Mark and Iyyer, Mohit and Gardner, Matt and Clark, Christopher and Lee, Kenton and Zettlemoyer, Luke},
title={Deep contextualized word representations},
booktitle={Proc. of NAACL},
year={2018}
}
安装
安装 Python 3.5 或更高版本,TensorFlow 版本 1.2 和 h5py。
pip install tensorflow-gpu==1.2 h5py
python setup.py install
确保在您的环境中运行以下命令,以验证测试是否通过:
python -m unittest discover tests/
使用 Docker 安装
要运行此镜像,您必须使用 nvidia-docker,因为此存储库需要 GPU。
sudo nvidia-docker run -t allennlp/bilm-tf:training-gpu
使用预训练模型
我们提供了几个可用于预训练的英语语言 biLM 模型。每个模型由两个独立的文件指定,一个是包含超参数的 JSON 格式的 "options" 文件,另一个是包含模型权重的 hdf5 格式的文件。预训练模型的链接可在这里找到。
根据您的使用情况,有三种方法可以将 ELMo 表示整合到下游任务中。
- 使用字符输入实时从原始文本中计算表示。这是最通用的方法,可以处理任何输入文本。但这是计算成本最高的方法。
- 预先计算并缓存上下文无关的标记表示,然后使用 biLSTMs 计算输入数据的上下文相关表示。这种方法比 #1 的计算成本更低,但仅适用于固定、规定的词汇表。
- 预先计算整个数据集的表示,并将其保存到文件中。
我们过去已为各种用例使用过所有这些方法。#1 对于在测试时间评估未见数据(例如,公共 SQuAD 排行榜)是必要的。#2 对于大型数据集来说是一个好的折衷方案,其中 #3 的文件大小不可行(SNLI、SQuAD)。#3 是小型数据集或您希望在其他框架中使用 ELMo 的情况下的一个好选择。
在所有情况下,过程大致遵循相同的步骤。首先,创建一个 Batcher
(对于 #2,使用 TokenBatcher
)以将分词字符串转换为字符(或标记)id 的 numpy 数组。然后,加载预训练的 ELMo 模型(类 BidirectionalLanguageModel
)。最后,对于 #1 和 #2,使用 weight_layers
计算最终的 ELMo 表示。对于 #3,使用 BidirectionalLanguageModel
将所有中间层写入文件。
形状约定
每个分词句子是一个 str
列表,一个句子批次的分词句子是一个分词句子的列表(List[List[str]]
)。
Batcher
将这些打包成一个形状为 (n_sentences, max_sentence_length + 2, 50)
的 numpy 数组,其中字符 id 用 0 id 填充,以适应较短的句子。每个句子的第一个和最后一个标记是 Batcher
添加的特殊开始和结束句子 id。
输入字符 id 占位符可以是维度 (None, None, 50)
,每个批次的批处理维度(轴=0)和时间维度(轴=1)由 BidirectionalLanguageModel
构造函数指定的最大批处理大小确定。
在运行批处理推理后,返回的 biLM 嵌入是一个形状为 (n_sentences, 3, max_sentence_length, 1024)
的 numpy 数组,移除特殊开始/结束标记后。
词汇表文件
Batcher
以输入词汇表文件的方式进行优化。这是一个文本文件,每行一个标记,由换行符(\n
)分隔。词汇表中的每个标记都缓存为适当的 50 字符 id 序列一次。由于模型完全基于字符,不在词汇表文件中的标记将在运行时适当处理,这会略微降低运行时间。建议始终在词汇表文件中包括特殊的 <S>
和 </S>
标记(区分大小写)。
基于字符输入的 ELMo
请参阅 usage_character.py
获取详细的用法示例。
预计算并缓存上下文无关的标记表示的 ELMo
为了加快具有固定、规定词汇表的模型推理,可以预先计算上下文无关的标记表示,将其写入文件,并在推理中重用它们。请注意,我们不支持将词汇表外的单词回退到字符输入,因此这仅应在 biLM 用于计算具有固定、定义词汇表的输入嵌入时使用。
要使用此选项
- 首先创建一个包含数据集中所有唯一标记的词汇文件,并添加特殊的
<S>
和</S>
标记。 - 使用完整模型运行
dump_token_embeddings
将标记嵌入写入 hdf5 文件。 - 使用
TokenBatcher
(而不是Batcher
)与您的词汇文件一起使用,并将use_token_inputs=False
和第 2 步中的输出文件名传递给BidirectonalLanguageModel
构造函数。
参见 usage_token.py
以获取详细使用示例。
将整个数据集的 biLM 嵌入导出到单个文件。
要选择此选项,请创建一个包含您标记化数据集的文本文件。每行是一个标记化句子(空格分隔)。然后使用 dump_bilm_embeddings
。
输出文件是 hdf5
格式。输入数据中的每个句子都存储为一个数据集,其键为 str(sentence_id)
,其中 sentence_id
是数据集文件中的行号(从 0 开始索引)。每个句子的嵌入是一个形状为 (3, n_tokens, 1024) 的数组。
参见 usage_cached.py
以获取详细示例。
在新的语料库上训练 biLM
一般来说,训练和使用新的 biLM 的过程是
- 准备输入数据和词汇文件。
- 训练 biLM。
- 测试(计算)biLM 在保留数据上的困惑度。
- 将训练的 biLM 的权重写入 hdf5 文件。
- 参见上述说明,以使用步骤 #4 的输出在下层模型中。
1. 准备输入数据和词汇文件。
要训练和评估 biLM,您需要提供
- 一个词汇文件
- 一组训练文件
- 一组保留文件
词汇文件是一个文本文件,每行一个标记。它还必须在文件中包含特殊标记 <S>
、</S>
和 <UNK>
(区分大小写)。
重要:词汇文件应该按照训练数据中的标记计数降序排序。前三条线应该是特殊标记(<S>
、</S>
和 <UNK>
),然后是最常见的标记,以最不常见的标记结束。
注意:训练中使用的词汇文件可能与预测中使用的不同。
训练数据应随机分成许多训练文件,每个文件包含数据的一个片段。每个文件包含预先标记化和空格分隔的文本,每行一个句子。不要在训练数据中包含 <S>
或 </S>
标记。
所有标记化/规范化都是在训练模型之前完成的,因此词汇文件和训练文件应包括规范化标记。由于默认设置使用基于字符的完整标记表示,因此我们通常不推荐除标记化之外的其他规范化。
最后,为评估训练的 biLM 保留少量训练数据。
2. 训练 biLM。
用于训练 ELMo 模型的超参数可以在 bin/train_elmo.py
中找到。
ELMo 模型是在 3 个 GPU 上训练的。要使用相同的超参数训练新模型,首先从 1 亿词基准 下载训练数据。然后下载 词汇文件。最后,运行
export CUDA_VISIBLE_DEVICES=0,1,2
python bin/train_elmo.py \
--train_prefix='/path/to/1-billion-word-language-modeling-benchmark-r13output/training-monolingual.tokenized.shuffled/*' \
--vocab_file /path/to/vocab-2016-09-10.txt \
--save_dir /output_path/to/checkpoint
3. 评估训练的模型。
使用 bin/run_test.py
评估一个训练模型,例如
export CUDA_VISIBLE_DEVICES=0
python bin/run_test.py \
--test_prefix='/path/to/1-billion-word-language-modeling-benchmark-r13output/heldout-monolingual.tokenized.shuffled/news.en.heldout-000*' \
--vocab_file /path/to/vocab-2016-09-10.txt \
--save_dir /output_path/to/checkpoint
4. 将 tensorflow 检查点转换为 hdf5,以便使用 bilm
或 allennlp
进行预测。
首先,为新训练的模型创建一个 options.json
文件。为此,请参考现有文件中的模板(例如,原始的 options.json
),并根据您的超参数进行修改。
重要:训练后,始终将 n_characters
设置为 262(见下文)。
然后运行
python bin/dump_weights.py \
--save_dir /output_path/to/checkpoint
--outfile /output_path/to/weights.hdf5
常见问题和其他警告
您能提供训练的 tensorflow 检查点吗?
tensorflow 检查点可以通过下载以下文件获得
如何在额外的未标记数据上微调模型?
首先下载上面的检查点文件。然后根据“在新的语料库上训练 biLM”部分中的说明准备数据集,但我们将使用现有的词汇表文件而不是创建一个新的文件。最后,使用脚本 bin/restart.py
在新数据集上使用现有的检查点重新启动训练。对于小数据集(例如,< 10 百万个标记),我们只建议进行少量轮次的微调并监控保留集上的困惑度,否则模型将过度拟合小数据集。
softmax 权重可用吗?
它们在上面的训练检查点中可用。
您能提供更多关于模型训练细节的信息吗?
脚本 bin/train_elmo.py
包含用于训练模型的超参数。原始模型在 3 个 GTX 1080 上训练了 10 个轮次,大约需要两周时间。
对于输入处理,我们使用了这里的原始 1 亿词语言模型基准数据集 这里,以及 793471 个标记的现有词汇表,包括 <S>
、</S>
和 <UNK>
。您可以在 这里 找到我们的词汇表文件。在模型输入时,所有文本都使用基于字符的完整表示,包括词汇表之外的标记。对于 softmax 输出,我们将 OOV 标记替换为 <UNK>
。
模型使用固定大小的 20 个标记窗口进行训练。批次是通过用 <S>
和 </S>
填充句子来构建的,然后将一个或多个句子中的标记打包到每一行中,以完全填充每个批次。因此,我们不分配特殊填充标记的空间。将标记字符串转换为字符 id 列表的 UnicodeCharsVocabulary
总是使用固定数量的字符嵌入 n_characters=261
,因此在训练期间始终设置 n_characters=261
。
为什么我在通过预训练模型运行相同的文本两次时得到略微不同的嵌入?
由于训练方法(见上文),LSTMs 是有状态的,并将它们的状态从批次传递到下一个批次。因此,这引入了少量非确定性,尤其是对于前两个批次。
为什么即使我的数据集很小,训练似乎也需要很长时间?
训练期间梯度更新的数量由以下因素确定
- 训练数据中的标记数 (
n_train_tokens
) - 批次大小 (
batch_size
) - 轮次数 (
n_epochs
)
请确保在 bin/train_elmo.py
中为您的特定数据集设置这些值。
n_characters
和填充是什么问题?
在训练期间,我们通过在每个句子中添加 <S>
和 </S>
来填充每个批次,以恰好 20 个标记填充每个批次,然后将一个或多个句子中的标记打包到每一行中,以完全填充每个批次。因此,我们不分配特殊填充标记的空间。将标记字符串转换为字符 id 列表的 UnicodeCharsVocabulary
总是使用固定数量的字符嵌入 n_characters=261
,因此在训练期间始终设置 n_characters=261
。
然而,为了预测,我们确保每个句子都完全包含在一个批次中,因此使用特殊的填充ID来填充不同长度的句子。这发生在 Batcher
中,请参见这里。因此,在预测时在 options.json
中设置 n_characters=262
。
我该如何使用ELMo来计算句子表示?
简单的方法,如对句子中单词级别的ELMo表示进行平均和最大池化,效果良好,通常在基准数据集上优于监督方法。参见“句子嵌入在下游和语言探查任务中的评估”,Perone等人,2018年 arxiv链接。
我在序列化模型时看到了一个警告,这是否是一个问题?
下面的警告可以安全忽略
2018-08-24 13:04:08,779 : WARNING : Error encountered when serializing lstm_output_embeddings.
Type is unsupported, or the types of the items don't match field type in CollectionDef.
'list' object has no attribute 'name'
项目详情
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。