序列标注
序列标注是指给定一个输入序列,对序列中的每个Token进行标注的过程。序列标注问题通常用于从文本中提取信息,包括分词 (Word Segmentation)、词性标注 (POS Tagging)、命名实体识别 (NER)、组块分析 (Chunking) 等。
以组块分析 (Chunking) 任务为例:
文本分块包括将文本划分为句法相关的词的一部分。例如,句子:He reckons the current account deficit will narrow to only # 1.8 billion in September . 可以被分块为:
[NP He ] [VP reckons ] [NP the current account deficit ] [VP will narrow ] [PP to ] [NP only # 1.8 billion ] [PP in ] [NP September ]. (NP:名词短语;VP:动词短语;PP:介词短语)
该任务的目标是提出机器学习方法,在训练阶段后可以尽可能好地识别测试数据的块分割。
CoNLL2000Chunking:
CoNLL2000Chunking 数据集中的分块标签基于分块任务常用的 IOB(Inside, Outside, Beginning) 标签方案。句子中的每个单词都标有一个块标签,该标签指示该单词是否是块的一部分,以及它是开头部分、中间部分还是块外部分。
提示
CoNLL2000Chunking 数据集包括一组预定义的块类型,例如名词短语 (NP)、动词短语 (VP) 和介词短语 (PP)。数据集中的块标签由块类型和IOB标签组合而成,内部词使用“I-TYPE”格式,起始词使用“B-TYPE”格式,外部词使用“O”格式。例如,块标签“B-NP”表示名词短语的开头,而块标签“I-VP”表示动词短语的内部词。
示例:
Sentence
They
refuse
to
permit
us
to
enter
.
Chunk Tag
B-NP
B-VP
B-PP
B-VP
B-NP
B-PP
B-VP
O
下面是使用CoNLL2000Chunking数据集的块标签 (chunk tag) 和Bi-LSTM+CRF模型进行组块分析 (Chunking) 任务训练的例子:
定义模型
首先继承 mindnlp.abc 中的 Seq2vecModel 定义模型的 Head,然后使用 mindnlp.modules 中的 CRF 完成 BiLSTM_CRF 模型的定义。
import math
from mindspore import nn
from mindspore.common.initializer import Uniform, HeUniform
from mindnlp.abc import Seq2vecModel
from mindnlp.modules import CRF
class Head(nn.Cell):
""" Head for BiLSTM-CRF model """
def __init__(self, hidden_dim, num_tags):
super().__init__()
weight_init = HeUniform(math.sqrt(5))
bias_init = Uniform(1 / math.sqrt(hidden_dim * 2))
self.hidden2tag = nn.Dense(hidden_dim, num_tags,
weight_init=weight_init, bias_init=bias_init)
def construct(self, context):
return self.hidden2tag(context)
class BiLSTM_CRF(Seq2vecModel):
""" BiLSTM-CRF model """
def __init__(self, encoder, head, num_tags):
super().__init__(encoder, head)
self.encoder = encoder
self.head = head
self.crf = CRF(num_tags, batch_first=True)
def construct(self, text, seq_length, label=None):
output,_,_ = self.encoder(text)
feats = self.head(output)
res = self.crf(feats, label, seq_length)
return res
定义超参数
以下是模型训练过程中需要的一些超参数。
embedding_dim = 16
hidden_dim = 32
数据预处理
通过调用 mindnlp.dataset 中 dataset 的接口下载并预处理数据集。
加载数据集:
from mindnlp.dataset import CoNLL2000Chunking
dataset_train,dataset_test = CoNLL2000Chunking()
初始化词表以进行预处理:
from mindspore.dataset import text
vocab = text.Vocab.from_dataset(dataset_train,columns=["words"],freq_range=None,top_k=None,
special_tokens=["<pad>","<unk>"],special_first=True)
处理数据集:
from mindnlp.dataset import CoNLL2000Chunking_Process
dataset_train = CoNLL2000Chunking_Process(dataset=dataset_train, vocab=vocab,
batch_size=32, max_len=80)
实例化模型
from mindnlp.modules import RNNEncoder
embedding = nn.Embedding(vocab_size=len(vocab.vocab()), embedding_size=embedding_dim,
padding_idx=vocab.tokens_to_ids("<pad>"))
lstm_layer = nn.LSTM(embedding_dim, hidden_dim // 2, bidirectional=True, batch_first=True)
encoder = RNNEncoder(embedding, lstm_layer)
head = Head(hidden_dim, 23)
net = BiLSTM_CRF(encoder, head, 23)
定义优化器
from mindspore import ops
optimizer = nn.SGD(net.trainable_params(), learning_rate=0.01, weight_decay=1e-4)
grad_fn = ops.value_and_grad(net, None, optimizer.parameters)
定义训练步骤
def train_step(data, seq_length, label):
""" train step """
loss, grads = grad_fn(data, seq_length, label)
loss = ops.depend(loss, optimizer(grads))
return loss
训练过程
现在我们已经完成了所有的准备工作,可以开始训练模型了。
from tqdm import tqdm
size = dataset_train.get_dataset_size()
steps = size
with tqdm(total=steps) as t:
for batch, (data, seq_length, label) in enumerate(dataset_train.create_tuple_iterator()):
loss = train_step(data, seq_length ,label)
t.set_postfix(loss=loss)
t.update(1)