===================== 快速实现序列标注模型 ===================== 这一部分的内容主要展示如何使用fastNLP实现序列标注任务。您可以使用fastNLP的各个组件快捷,方便地完成序列标注任务,达到出色的效果。 在阅读这篇Tutorial前,希望您已经熟悉了fastNLP的基础使用,尤其是数据的载入以及模型的构建,通过这个小任务的能让您进一步熟悉fastNLP的使用。 命名实体识别(name entity recognition, NER) ------------------------------------------ 命名实体识别任务是从文本中抽取出具有特殊意义或者指代性非常强的实体,通常包括人名、地名、机构名和时间等。 如下面的例子中 我来自复旦大学。 其中“复旦大学”就是一个机构名,命名实体识别就是要从中识别出“复旦大学”这四个字是一个整体,且属于机构名这个类别。这个问题在实际做的时候会被 转换为序列标注问题 针对"我来自复旦大学"这句话,我们的预测目标将是[O, O, O, B-ORG, I-ORG, I-ORG, I-ORG],其中O表示out,即不是一个实体,B-ORG是ORG( organization的缩写)这个类别的开头(Begin),I-ORG是ORG类别的中间(Inside)。 在本tutorial中我们将通过fastNLP尝试写出一个能够执行以上任务的模型。 载入数据 ------------------------------------------ fastNLP的数据载入主要是由Loader与Pipe两个基类衔接完成的,您可以通过 :doc:`使用Loader和Pipe处理数据 ` 了解如何使用fastNLP提供的数据加载函数。下面我们以微博命名实体任务来演示一下在fastNLP进行序列标注任务。 .. code-block:: python from fastNLP.io import WeiboNERPipe data_bundle = WeiboNERPipe().process_from_file() print(data_bundle.get_dataset('train')[:2]) 打印的数据如下 :: +-------------------------------------------------+------------------------------------------+------------------------------------------+---------+ | raw_chars | target | chars | seq_len | +-------------------------------------------------+------------------------------------------+------------------------------------------+---------+ | ['一', '节', '课', '的', '时', '间', '真', '... | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, ... | [8, 211, 775, 3, 49, 245, 89, 26, 101... | 16 | | ['回', '复', '支', '持', ',', '赞', '成', '... | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... | [116, 480, 127, 109, 2, 446, 134, 2, ... | 59 | +-------------------------------------------------+------------------------------------------+------------------------------------------+---------+ 模型构建 -------------------------------- 首先选择需要使用的Embedding类型。关于Embedding的相关说明可以参见 :doc:`使用Embedding模块将文本转成向量 ` 。 在这里我们使用通过word2vec预训练的中文汉字embedding。 .. code-block:: python from fastNLP.embeddings import StaticEmbedding embed = StaticEmbedding(vocab=data_bundle.get_vocab('chars'), model_dir_or_name='cn-char-fastnlp-100d') 选择好Embedding之后,我们可以使用fastNLP中自带的 :class:`fastNLP.models.BiLSTMCRF` 作为模型。 .. code-block:: python from fastNLP.models import BiLSTMCRF data_bundle.rename_field('chars', 'words') # 这是由于BiLSTMCRF模型的forward函数接受的words,而不是chars,所以需要把这一列重新命名 model = BiLSTMCRF(embed=embed, num_classes=len(data_bundle.get_vocab('target')), num_layers=1, hidden_size=200, dropout=0.5, target_vocab=data_bundle.get_vocab('target')) 下面我们选择用来评估模型的metric,以及优化用到的优化函数。 .. code-block:: python from fastNLP import SpanFPreRecMetric from torch.optim import Adam from fastNLP import LossInForward metric = SpanFPreRecMetric(tag_vocab=data_bundle.get_vocab('target')) optimizer = Adam(model.parameters(), lr=1e-2) loss = LossInForward() 使用Trainer进行训练 .. code-block:: python from fastNLP import Trainer import torch device= 0 if torch.cuda.is_available() else 'cpu' trainer = Trainer(data_bundle.get_dataset('train'), model, loss=loss, optimizer=optimizer, dev_data=data_bundle.get_dataset('dev'), metrics=metric, device=device) trainer.train() 训练过程输出为:: input fields after batch(if batch size is 2): target: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2, 26]) seq_len: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2]) words: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2, 26]) target fields after batch(if batch size is 2): target: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2, 26]) seq_len: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2]) training epochs started 2019-09-25-10-43-09 Evaluate data in 0.62 seconds! Evaluation on dev at Epoch 1/10. Step:43/430: SpanFPreRecMetric: f=0.070352, pre=0.100962, rec=0.053985 ... Evaluate data in 0.61 seconds! Evaluation on dev at Epoch 10/10. Step:430/430: SpanFPreRecMetric: f=0.51223, pre=0.581699, rec=0.457584 In Epoch:7/Step:301, got best dev performance: SpanFPreRecMetric: f=0.515528, pre=0.65098, rec=0.426735 Reloaded the best model. 训练结束之后过,可以通过 :class:`~fastNLP.Tester` 测试其在测试集上的性能 .. code-block::python from fastNLP import Tester tester = Tester(data_bundle.get_dataset('test'), model, metrics=metric) tester.test() 输出为:: [tester] SpanFPreRecMetric: f=0.482399, pre=0.530086, rec=0.442584 使用更强的Bert做序列标注 -------------------------------- 在fastNLP使用Bert进行任务,您只需要切换为 :class:`fastNLP.embeddings.BertEmbedding` 即可。 .. code-block:: python from fastNLP.io import WeiboNERPipe data_bundle = WeiboNERPipe().process_from_file() data_bundle.rename_field('chars', 'words') from fastNLP.embeddings import BertEmbedding embed = BertEmbedding(vocab=data_bundle.get_vocab('words'), model_dir_or_name='cn') model = BiLSTMCRF(embed=embed, num_classes=len(data_bundle.get_vocab('target')), num_layers=1, hidden_size=200, dropout=0.5, target_vocab=data_bundle.get_vocab('target')) from fastNLP import SpanFPreRecMetric from torch import Adam from fastNLP import LossInForward metric = SpanFPreRecMetric(tag_vocab=data_bundle.get_vocab('target')) optimizer = Adam(model.parameters(), lr=2e-5) loss = LossInForward() from fastNLP import Trainer import torch device= 0 if torch.cuda.is_available() else 'cpu' trainer = Trainer(data_bundle.get_dataset('train'), model, loss=loss, optimizer=optimizer, batch_size=12, dev_data=data_bundle.get_dataset('dev'), metrics=metric, device=device) trainer.train() from fastNLP import Tester tester = Tester(data_bundle.get_dataset('test'), model, metrics=metric) tester.test() 输出为:: training epochs started 2019-09-25-07-15-43 Evaluate data in 2.02 seconds! Evaluation on dev at Epoch 1/10. Step:113/1130: SpanFPreRecMetric: f=0.0, pre=0.0, rec=0.0 ... Evaluate data in 2.17 seconds! Evaluation on dev at Epoch 10/10. Step:1130/1130: SpanFPreRecMetric: f=0.647332, pre=0.589852, rec=0.717224 In Epoch:6/Step:678, got best dev performance: SpanFPreRecMetric: f=0.669963, pre=0.645238, rec=0.696658 Reloaded the best model. Evaluate data in 1.82 seconds! [tester] SpanFPreRecMetric: f=0.641774, pre=0.626424, rec=0.657895 可以看出通过使用Bert,效果有明显的提升,从48.2提升到了64.1。