本文基于AllenNLP英文tutorial翻译,其中不少错误,仅作为个人学习记录
有一篇帖子总结了一下学习处理NLP问题中间的坑。NLP数据预处理要比CV的麻烦很多。
- 去除停用词,建立词典,加载各种预训练词向量,Sentence -> Word ID -> Word Embedding的过程(Tobias Lee:文本预处理方法小记),其中不仅需要学习pytorch,可能还要学习spacy,NLTK,numpy,pandas,tensorboardX等常用python包。
- 用到RNN时,还要经过pad,pack,pad的过程,像这样的很多函数在使用时需要有数学基础加上简单的实践,感觉对一个新人来说,高维数据的流动有点抽象,不容易理解。
- 数据集的读取,tensorboardX的使用。。。。各种东西要学习。在运行别人的代码后打印出信息,不仅看着上档次,而且可以看到很多实用的信息。。。
AllenNLP是在pytorch基础上的封装,它的目标是处理NLP任务,可以减少很多额外的学习。
- 分词,帮你用spacy,NLTK,或者简单的按空格分词处理。
- 数据集的读取,它内置了很多数据集的读取,你可以在通过学习它的读取方式,在它的基础上对自己需要的数据集进行读取。 、
- 在Sentence -> Word ID -> Word Embedding的过程中,Glove,ELMo,BERT等常用的都可以直接使用,需要word,char粒度的都可以。
- log打印输出,在内置的输出项之外,你可以很方便地加入想要输出的信息。模型的各个组件中的参数都可以存在一个json/jsonnet文件中,修改参数进行实验很方便。
2. A Walk Through AllenNLP
第二部分 实验参数设定
上一节中对训练和评估一个模型有了了解(就是调用JSON、存储模型、评估),这节做的是深入了解实验设定文件,其实就是那个JSON文档。
教程中会解读配置文件的每一节内容,解释所有参数的含义。
初步:Registrable 和 from_params
AllenNLP中大多类继承自Registrable基类,为子类提供了一个命名注册表,这意味着如果有Model,可以这样处理
数据集、实例和域
我们在数据集上训练、评估模型,一个数据集应包含有实例。
在之前标记实验中,每一个数据集是一个包括标记的句子的集合,每一个实例也是标记句子之一。
在标记设置中,每一个实例将包含表示句子的单词/tokens的文本域(textfield)和表示相应词性标记的sequencelabelfield。
如何将句子文本转为数据集?可以使用配置文件指定的DatasetReader。
DatasetReader
第一个需要配置的部分是定义 dataset_reader
"dataset_reader": {
"type": "sequence_tagging",
"word_tag_delimiter": "/",
"token_indexers": {
"tokens": {
"type": "single_id",
"lowercase_tokens": true
},
"token_characters": {
"type": "characters"
}
}
},
这里我们已经指定我们想要使用DatasetReader的子类“sequence_tagging”。这是SequenceTaggingDatasetReader子类。
这个reader假设一个新行分隔句子的文本文件,其中每个句子看起来像下面的一些“单词标记分隔符”{wtd}和一些“标记分隔符”{td}。
word1{wtd}tag1{td} word2{wtd}tag2{td} ...{td} wordn{wtd}tagn
而我们的数据文件是这样的
The/at detectives/nns placed/vbd Barco/np under/in arrest/nn
就是说,需要指定“/”,回看json中的第二个参数“word_tag_delimiter”
"word_tag_delimiter": "/",
因为源文件中已经进行了分词,这里就不需要指定“token_delimiter”。
如果查看SequenceTaggingDatasetReader.read()的代码,它会将每个句子转换为标记的TextField和标记的SequenceLabelField。
后者不是真正可配置的,但前者需要一个TokenIndexers字典,指示如何将标记转换为数组。
在配置中指定两个token indexers。第一个tokens是一个SingledIdTokenIndexer,只是将每个标记的单词表示为单个整数,下面的lowercase为True,表示忽略大小写。
注意,这里为每个token提供了两种不同的编码。每个编码都有一个名称,在本例中为“tokens”和“token_characters”,这些名称稍后将由模型引用。
"token_indexers": {
"tokens": {
"type": "single_id",
"lowercase_tokens": true
},
"token_characters": {
"type": "characters"
}
}
训练和验证数据
下一个部分是指定用于训练和验证的数据。可以是在本地的,也可以是在网上的地址。
"train_data_path": "https://allennlp.s3.amazonaws.com/datasets/getting-started/sentences.small.train",
"validation_data_path": "https://allennlp.s3.amazonaws.com/datasets/getting-started/sentences.small.dev",
模型部分
下一个部分是模型的设置。
"model": {
"type": "simple_tagger",
这一段表明要开始设置模型了,使用模型的simple_tagger,对应SimpleTagger模型。
这个模型包括了一个TextFieldEmbedder和一个Seq2SeqEncoder和一个线性层(最后的这个线性层不可配置,所以json里面没有)。
TextFieldEmbedder
"text_field_embedder": {
"tokens": {
"type": "embedding",
"embedding_dim": 50
},
"token_characters": {
"type": "character_encoding",
"embedding": {
"embedding_dim": 8
},
"encoder": {
"type": "cnn",
"embedding_dim": 8,
"num_filters": 50,
"ngram_filter_sizes": [
5
]
},
"dropout": 0.2
}
},
- TextField中为每个命名编码都有一个条目。每个条目指定一个TokenEmbedder,指示如何嵌入使用该名称编码的标记。 TextFieldEmbedder的输出是这些嵌入的串联。
- “token”输入(由输入中的小写单词的整数编码组成)被输入嵌入模块,该嵌入模块将词汇表单词嵌入到50维空间中,如embedding_dim参数所指定的。
- “token_characters”输入(由每个单词中字符的整数序列编码组成)被输入TokenCharactersEncoder,它将字符嵌入到8维空间中,然后应用使用50个过滤器的CnnEncoder,因此也生成50维输出。可以看到此编码器在训练期间也使用了20%的dropout。
TextFieldEmbedder输出“tokens”是一个50维向量,与“token_characters”的50维向量连接在一起,也就是说,一个100维向量。
因为TextFields和TextFieldEmbedder的编码都是以这种方式配置的,所以尝试使用不同的单词表示作为模型的输入,在简单的单词嵌入之间切换,与字符级CNN连接的单词嵌入,甚至使用预先训练的模型,用于获取上下文嵌入,而无需更改单行代码。
Seq2SeqEncoder
上一个TextFieldEmbedder输出的数据由这里进行处理。
"encoder": {
"type": "lstm",
"input_size": 100,
"hidden_size": 100,
"num_layers": 2,
"dropout": 0.5,
"bidirectional": true
}
注意,这里的input_size是和上面输出的数据维度要一致。
训练模型
剩下的配置文件表明训练过程
"iterator": {"type": "basic", "batch_size": 32},
使用basiciterator进行迭代,填充我们的数据并使用32个batch完成处理。
"trainer": {
"optimizer": "adam",
"num_epochs": 40,
"patience": 10,
"cuda_device": -1
}
最后,我们将使用其默认参数优化:torch.optim.Adam;
我们将进行40次迭代培训;
如果10个时代没有改善,我们会过早地停止;
我们将在CPU上进行训练。如果你想在GPU上训练,你可以将cuda_device更改为其设备ID。如果你只有一个GPU应该是0。
这是我们的整个实验配置。如果我们想要更改我们的优化器,批量大小,嵌入维度或任何其他超参数,我们需要做的就是修改此配置文件并训练另一个模型。
训练配置始终保存为模型存档的一部分,这意味着您始终可以查看已保存模型的训练方式。
终于翻译完了,这是对上一节的补充。看看就好