自然语言处理:AllenNLP训练管道构建与语言检测案例
立即解锁
发布时间: 2025-09-01 00:44:05 阅读量: 14 订阅数: 20 AIGC 


实战自然语言处理
### 自然语言处理:AllenNLP训练管道构建与语言检测案例
#### 1. 构建AllenNLP训练管道
在自然语言处理中,构建训练管道是一项关键任务。当处理不同语言的词汇时,会遇到一些挑战。例如,英语和法语的单词在大多数情况下看起来差异很大,但也有一些单词在两种语言中完全相同,如“chat”在英语中是“talk”的意思,在法语中却是“cat”的意思。为避免这种冲突,Vocabulary实现了命名空间(namespaces),为不同类型的项目分配独立的集合。
在`form_instances()`函数调用中,有一个`min_count`参数。对于每个命名空间,它指定了一个项目要包含在词汇表中所需的在数据集中出现的最小次数。所有出现频率低于此阈值的项目都被视为“未知”项目。这是因为在典型语言中,少数单词出现频率很高(如英语中的“the”“a”“of”),而大量单词出现频率极低,呈现长尾分布。这些极不常见的单词不太可能为模型增加有用信息,而且会增加词汇表和模型参数的大小。因此,常见做法是截断长尾,将所有不常见的单词合并为一个实体`<UNK>`(表示“未知”单词)。
最后,Token Indexer是AllenNLP的一个抽象概念,它接收一个标记并返回其索引,或表示该标记的索引列表。在大多数情况下,唯一标记与其索引之间存在一对一的映射,但根据模型的不同,可能需要更高级的方法来索引标记(如使用字符n - 元组)。创建词汇表后,可以让数据加载器使用指定的词汇表对标记进行索引:
```python
train_data_loader.index_with(vocab)
dev_data_loader.index_with(vocab)
```
#### 2. 标记嵌入器和循环神经网络(RNN)
使用词汇表和标记索引器对单词进行索引后,需要将它们转换为嵌入向量。AllenNLP的`TokenEmbedder`抽象概念接收单词索引作为输入,并生成单词嵌入向量作为输出。如果只是想将唯一标记一对一地映射到嵌入向量,可以使用`Embedding`类:
```python
token_embedding = Embedding(
num_embeddings=vocab.get_vocab_size('tokens'),
embedding_dim=EMBEDDING_DIM)
```
这将创建一个`Embedding`实例,它以一对一的方式将单词ID转换为固定长度的向量。`num_embeddings`表示该实例可以支持的唯一单词数量,等于`tokens`词汇表命名空间的大小。`embedding_dim`表示嵌入向量的维度。
接下来,定义RNN将可变长度的输入(嵌入单词列表)转换为输入的固定长度向量表示。可以将RNN看作是一种消耗一系列事物(单词)并返回固定长度向量的神经网络结构。AllenNLP将此类模型抽象为`Seq2VecEncoder`类,可以使用`PytorchSeq2VecWrapper`创建一个LSTM RNN:
```python
encoder = PytorchSeq2VecWrapper(
torch.nn.LSTM(EMBEDDING_DIM, HIDDEN_DIM, batch_first=True))
```
这里实际上是包装了PyTorch的`LSTM`实现,使其可以插入到AllenNLP管道的其余部分。`torch.nn.LSTM()`的第一个参数是输入向量的维度,第二个参数是LSTM内部状态的维度,`batch_first`指定了用于批处理的输入/输出张量的结构。
#### 3. 构建自己的模型
定义好所有子组件后,就可以构建执行预测的模型了。借助AllenNLP设计良好的抽象概念,可以通过继承AllenNLP的`Model`类并重写`forward()`方法来轻松构建模型。以下是用于句子分类的LSTM RNN的定义:
```python
@Model.register("lstm_classifier")
class LstmClassifier(Model):
def __init__(self,
embedder: TextFieldEmbedder,
encoder: Seq2VecEncoder,
vocab: Vocabulary,
positive_label: str = '4') -> None:
super().__init__(vocab)
self.embedder = embedder
self.encoder = encoder
self.linear = torch.nn.Linear(
in_features=encoder.get_output_dim(),
out_features=vocab.get_vocab_size('labels'))
positive_index = vocab.get_token_index(
positive_label, namespace='labels')
self.accuracy = CategoricalAccuracy()
self.f1_measure = F1Measure(positive_index)
self.loss_function = torch.nn.CrossEntropyLoss()
def forward(self,
tokens: Dict[str, torch.Tensor],
label: torch.Tensor = None) -> torch.Tensor:
mask = get_text_field_mask(tokens)
embeddings = self.embedder(tokens)
encoder_out = self.encoder(embeddings, mask)
logits = self.linear(encoder_out)
output = {"logits": logits}
if label is not None:
self.accuracy(logits, label)
self.f1_measure(logits, label)
output["loss"] = self.loss_function(logits, label)
return output
def get_metrics(self, reset: bool = False) -> Dict[str, float]:
return {'accuracy': self.accuracy.get_metric(reset),
**self.f1_measure.get_metric(reset)}
```
每个AllenNLP的`Model`都继承自PyTorch的`Module`类,这意味着在必要时可以使用PyTorch的低级操作,在利用AllenNLP高级抽象的同时,为定义模型提供了很大的灵活性。
#### 4. 整合所有组件
最后,实现训练情感分析器的完整管道:
```python
EMBEDDING_DIM = 128
HIDDEN_DIM = 128
reader = StanfordSentimentTreeBankDatasetReader()
train_path = 'path/to/sst/dataset/train.txt'
dev_path = 'path/to/sst/dataset/dev.txt'
sampler = BucketBatchSampler(batch_size=32, sorting_keys=["tokens"])
train_data_loader = MultiProcessDataLoader(
reader, train_path, batch_sampler=sampler)
dev_data_loader = MultiProcessDataLoader(
reader, dev_path, batch_sampler=sampler)
vocab = Vocabulary.from_instances(chain(train_data_loader.iter_instances(),
dev_data_loader.iter_instances()),
min_count={'tokens': 3})
train_data_loader.index_with(vocab)
dev_data_loader.index_with(vocab)
token_embedding = Embedding(
num_embeddings=vocab.get_vocab_size('tokens'),
embedding_dim=EMBEDDING_DIM)
word_embeddings = BasicTextFieldEmbedder({"tokens": token_embedding})
encoder = PytorchSeq2VecWrapper(
torch.nn.LSTM(EMBEDDING_DIM, HIDDEN_DIM, batch_first=True))
model = LstmClassifier(word_embeddings, encoder, vocab)
optimizer = optim.Adam(model.parameters())
```
0
0
复制全文
相关推荐










