From 999a14381747068e9e6a7cc370037b320197db00 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Wed, 5 Jun 2019 20:13:23 +0800 Subject: [PATCH 001/139] fix a bug in metrics.py --- fastNLP/core/metrics.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fastNLP/core/metrics.py b/fastNLP/core/metrics.py index f994bd31..868d67b1 100644 --- a/fastNLP/core/metrics.py +++ b/fastNLP/core/metrics.py @@ -438,7 +438,7 @@ def _bio_tag_to_spans(tags, ignore_labels=None): class SpanFPreRecMetric(MetricBase): - """ + r""" 别名::class:`fastNLP.SpanFPreRecMetric` :class:`fastNLP.core.metrics.SpanFPreRecMetric` 在序列标注问题中,以span的方式计算F, pre, rec. @@ -476,7 +476,7 @@ class SpanFPreRecMetric(MetricBase): label的f1, pre, rec :param str f_type: 'micro'或'macro'. 'micro':通过先计算总体的TP,FN和FP的数量,再计算f, precision, recall; 'macro': 分布计算每个类别的f, precision, recall,然后做平均(各类别f的权重相同) - :param float beta: f_beta分数,:math:`f_beta = \frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)}`. + :param float beta: f_beta分数, :math:`f_{beta} = \frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)}` . 常用为beta=0.5, 1, 2. 若为0.5则精确率的权重高于召回率;若为1,则两者平等;若为2,则召回率权重高于精确率。 """ @@ -699,16 +699,16 @@ def _pred_topk(y_prob, k=1): class SQuADMetric(MetricBase): - """ + r""" 别名::class:`fastNLP.SQuADMetric` :class:`fastNLP.core.metrics.SQuADMetric` SQuAD数据集metric - :param pred1: 参数映射表中`pred1`的映射关系,None表示映射关系为`pred1`->`pred1` - :param pred2: 参数映射表中`pred2`的映射关系,None表示映射关系为`pred2`->`pred2` - :param target1: 参数映射表中`target1`的映射关系,None表示映射关系为`target1`->`target1` - :param target2: 参数映射表中`target2`的映射关系,None表示映射关系为`target2`->`target2` - :param float beta: f_beta分数,:math:`f_beta = \frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)}`. + :param pred1: 参数映射表中 `pred1` 的映射关系,None表示映射关系为 `pred1` -> `pred1` + :param pred2: 参数映射表中 `pred2` 的映射关系,None表示映射关系为 `pred2` -> `pred2` + :param target1: 参数映射表中 `target1` 的映射关系,None表示映射关系为 `target1` -> `target1` + :param target2: 参数映射表中 `target2` 的映射关系,None表示映射关系为 `target2` -> `target2` + :param float beta: f_beta分数, :math:`f_{beta} = \frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)}` . 常用为beta=0.5, 1, 2. 若为0.5则精确率的权重高于召回率;若为1,则两者平等;若为2,则召回率权重高于精确率。 :param bool right_open: right_open为true表示start跟end指针指向一个左闭右开区间,为false表示指向一个左闭右闭区间。 :param bool print_predict_stat: True则输出预测答案是否为空与正确答案是否为空的统计信息, False则不输出 From efea6ceaf5e33bf0400ff566b26a2b7f758982ef Mon Sep 17 00:00:00 2001 From: wyg <1505116161@qq.com> Date: Thu, 11 Jul 2019 14:04:32 +0800 Subject: [PATCH 002/139] [verify] train_char_cnn optimization --- .../text_classification/data/yelpLoader.py | 7 +++-- .../text_classification/train_char_cnn.py | 31 ++++++++++++++----- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/reproduction/text_classification/data/yelpLoader.py b/reproduction/text_classification/data/yelpLoader.py index 04ae94f9..d2272a88 100644 --- a/reproduction/text_classification/data/yelpLoader.py +++ b/reproduction/text_classification/data/yelpLoader.py @@ -131,7 +131,9 @@ class yelpLoader(DataSetLoader): src_vocab_op: VocabularyOption = None, tgt_vocab_op: VocabularyOption = None, embed_opt: EmbeddingOption = None, - char_level_op=False): + char_level_op=False, + split_dev_op=True + ): paths = check_dataloader_paths(paths) datasets = {} info = DataBundle(datasets=self.load(paths)) @@ -172,7 +174,8 @@ class yelpLoader(DataSetLoader): info.vocabs[target_name]=tgt_vocab - info.datasets['train'],info.datasets['dev']=info.datasets['train'].split(0.1, shuffle=False) + if split_dev_op: + info.datasets['train'], info.datasets['dev'] = info.datasets['train'].split(0.1, shuffle=False) for name, dataset in info.datasets.items(): dataset.set_input("words") diff --git a/reproduction/text_classification/train_char_cnn.py b/reproduction/text_classification/train_char_cnn.py index 050527fe..e4bb9220 100644 --- a/reproduction/text_classification/train_char_cnn.py +++ b/reproduction/text_classification/train_char_cnn.py @@ -8,7 +8,8 @@ sys.path.append('../..') from fastNLP.core.const import Const as C import torch.nn as nn from data.yelpLoader import yelpLoader -from data.sstLoader import sst2Loader +#from data.sstLoader import sst2Loader +from fastNLP.io.data_loader.sst import SST2Loader from data.IMDBLoader import IMDBLoader from model.char_cnn import CharacterLevelCNN from fastNLP.core.vocabulary import Vocabulary @@ -20,16 +21,20 @@ from torch.optim import SGD from torch.autograd import Variable import torch from fastNLP import BucketSampler +from torch.optim.lr_scheduler import CosineAnnealingLR, LambdaLR +from fastNLP.core import LRScheduler +from utils.util_init import set_rng_seeds ##hyper #todo 这里加入fastnlp的记录 class Config(): + #seed=7777 model_dir_or_name="en-base-uncased" embedding_grad= False, bert_embedding_larers= '4,-2,-1' train_epoch= 50 num_classes=2 - task= "IMDB" + task= "yelp_p" #yelp_p datapath = {"train": "/remote-home/ygwang/yelp_polarity/train.csv", "test": "/remote-home/ygwang/yelp_polarity/test.csv"} @@ -46,6 +51,7 @@ class Config(): number_of_characters=69 extra_characters='' max_length=1014 + weight_decay = 1e-5 char_cnn_config={ "alphabet": { @@ -104,12 +110,15 @@ class Config(): } ops=Config +# set_rng_seeds(ops.seed) +# print('RNG SEED: {}'.format(ops.seed)) + ##1.task相关信息:利用dataloader载入dataInfo -#dataloader=sst2Loader() +#dataloader=SST2Loader() #dataloader=IMDBLoader() dataloader=yelpLoader(fine_grained=True) -datainfo=dataloader.process(ops.datapath,char_level_op=True) +datainfo=dataloader.process(ops.datapath,char_level_op=True,split_dev_op=False) char_vocab=ops.char_cnn_config["alphabet"]["en"]["lower"]["alphabet"] ops.number_of_characters=len(char_vocab) ops.embedding_dim=ops.number_of_characters @@ -186,12 +195,20 @@ model=CharacterLevelCNN(ops,embedding) ## 3. 声明loss,metric,optimizer loss=CrossEntropyLoss metric=AccuracyMetric -optimizer= SGD([param for param in model.parameters() if param.requires_grad==True], lr=ops.lr) +#optimizer= SGD([param for param in model.parameters() if param.requires_grad==True], lr=ops.lr) +optimizer = SGD([param for param in model.parameters() if param.requires_grad == True], + lr=ops.lr, momentum=0.9, weight_decay=ops.weight_decay) +callbacks = [] +# callbacks.append(LRScheduler(CosineAnnealingLR(optimizer, 5))) +callbacks.append( + LRScheduler(LambdaLR(optimizer, lambda epoch: ops.lr if epoch < + ops.train_epoch * 0.8 else ops.lr * 0.1)) +) ## 4.定义train方法 def train(model,datainfo,loss,metrics,optimizer,num_epochs=100): - trainer = Trainer(datainfo.datasets['train'], model, optimizer=optimizer, loss=loss(target='target'), - metrics=[metrics(target='target')], dev_data=datainfo.datasets['test'], device=0, check_code_level=-1, + trainer = Trainer(datainfo.datasets['train'], model, optimizer=optimizer, loss=loss(target='target'),batch_size=ops.batch_size, + metrics=[metrics(target='target')], dev_data=datainfo.datasets['test'], device=[0,1,2], check_code_level=-1, n_epochs=num_epochs) print(trainer.train()) From 6c7009dded49b763d69fd947fbf882a6f69b5656 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Thu, 11 Jul 2019 15:11:25 +0800 Subject: [PATCH 003/139] =?UTF-8?q?tutorials=20=E6=A0=87=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ docs/source/tutorials/tutorial_1_data_preprocess.rst | 2 +- docs/source/tutorials/tutorial_2_load_dataset.rst | 6 +++--- docs/source/tutorials/tutorial_4_loss_optimizer.rst | 5 +++-- docs/source/tutorials/tutorial_5_datasetiter.rst | 6 ++++-- docs/source/tutorials/tutorial_6_seq_labeling.rst | 2 +- docs/source/tutorials/tutorial_7_modules_models.rst | 2 +- docs/source/tutorials/tutorial_8_metrics.rst | 6 +++--- docs/source/tutorials/tutorial_9_callback.rst | 6 +++--- 9 files changed, 21 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index b294e54b..6eff6497 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ python -m spacy download en ## fastNLP教程 +- [0. 快速入门](https://fastnlp.readthedocs.io/zh/latest/user/quickstart.html) - [1. 使用DataSet预处理文本](https://fastnlp.readthedocs.io/zh/latest/tutorials/tutorial_1_data_preprocess.html) - [2. 使用DataSetLoader加载数据集](https://fastnlp.readthedocs.io/zh/latest/tutorials/tutorial_2_load_dataset.html) - [3. 使用Embedding模块将文本转成向量](https://fastnlp.readthedocs.io/zh/latest/tutorials/tutorial_3_embedding.html) @@ -48,6 +49,7 @@ python -m spacy download en - [7. 使用Modules和Models快速搭建自定义模型](https://fastnlp.readthedocs.io/zh/latest/tutorials/tutorial_7_modules_models.html) - [8. 使用Metric快速评测你的模型](https://fastnlp.readthedocs.io/zh/latest/tutorials/tutorial_8_metrics.html) - [9. 使用Callback自定义你的训练过程](https://fastnlp.readthedocs.io/zh/latest/tutorials/tutorial_9_callback.html) +- [10. 使用fitlog 辅助 fastNLP 进行科研](https://fastnlp.readthedocs.io/zh/latest/tutorials/tutorial_10_fitlog.html) diff --git a/docs/source/tutorials/tutorial_1_data_preprocess.rst b/docs/source/tutorials/tutorial_1_data_preprocess.rst index cd97ca75..76d02655 100644 --- a/docs/source/tutorials/tutorial_1_data_preprocess.rst +++ b/docs/source/tutorials/tutorial_1_data_preprocess.rst @@ -1,5 +1,5 @@ ============================== -数据格式及预处理教程 +使用DataSet预处理文本 ============================== :class:`~fastNLP.DataSet` 是fastNLP中用于承载数据的容器。可以将DataSet看做是一个表格, diff --git a/docs/source/tutorials/tutorial_2_load_dataset.rst b/docs/source/tutorials/tutorial_2_load_dataset.rst index 2576992d..efccc14b 100644 --- a/docs/source/tutorials/tutorial_2_load_dataset.rst +++ b/docs/source/tutorials/tutorial_2_load_dataset.rst @@ -1,6 +1,6 @@ -========================= -数据集加载教程 -========================= +================================= +使用DataSetLoader加载数据集 +================================= 这一部分是一个关于如何加载数据集的教程 diff --git a/docs/source/tutorials/tutorial_4_loss_optimizer.rst b/docs/source/tutorials/tutorial_4_loss_optimizer.rst index 2a4d159a..a6e1730a 100644 --- a/docs/source/tutorials/tutorial_4_loss_optimizer.rst +++ b/docs/source/tutorials/tutorial_4_loss_optimizer.rst @@ -1,8 +1,9 @@ ============================================================================== -Loss 和 optimizer 教程 ———— 以文本分类为例 +动手实现一个文本分类器I-使用Trainer和Tester快速训练和测试 ============================================================================== -我们使用和 :doc:`/user/quickstart` 中一样的任务来进行详细的介绍。给出一段评价性文字,预测其情感倾向是积极(label=1)、消极(label=0)还是中性(label=2),使用 :class:`~fastNLP.Trainer` 和 :class:`~fastNLP.Tester` 来进行快速训练和测试,损失函数之前的内容与 :doc:`/tutorials/tutorial_5_datasetiter` 中的完全一样,如已经阅读过可以跳过。 +我们使用和 :doc:`/user/quickstart` 中一样的任务来进行详细的介绍。给出一段评价性文字,预测其情感倾向是积极(label=1)、 +消极(label=0)还是中性(label=2),使用 :class:`~fastNLP.Trainer` 和 :class:`~fastNLP.Tester` 来进行快速训练和测试。 -------------- 数据处理 diff --git a/docs/source/tutorials/tutorial_5_datasetiter.rst b/docs/source/tutorials/tutorial_5_datasetiter.rst index b57bd5c8..23d26deb 100644 --- a/docs/source/tutorials/tutorial_5_datasetiter.rst +++ b/docs/source/tutorials/tutorial_5_datasetiter.rst @@ -1,8 +1,10 @@ ============================================================================== -DataSetIter 教程 ———— 以文本分类为例 +动手实现一个文本分类器II-使用DataSetIter实现自定义训练过程 ============================================================================== -我们使用和 :doc:`/user/quickstart` 中一样的任务来进行详细的介绍。给出一段评价性文字,预测其情感倾向是积极(label=1)、消极(label=0)还是中性(label=2),使用:class:`~fastNLP.DataSetIter` 类来编写自己的训练过程。自己编写训练过程之前的内容与 :doc:`/tutorials/tutorial_4_loss_optimizer` 中的完全一样,如已经阅读过可以跳过。 +我们使用和 :doc:`/user/quickstart` 中一样的任务来进行详细的介绍。给出一段评价性文字,预测其情感倾向是积极(label=1)、 +消极(label=0)还是中性(label=2),使用 :class:`~fastNLP.DataSetIter` 类来编写自己的训练过程。 +自己编写训练过程之前的内容与 :doc:`/tutorials/tutorial_4_loss_optimizer` 中的完全一样,如已经阅读过可以跳过。 -------------- 数据处理 diff --git a/docs/source/tutorials/tutorial_6_seq_labeling.rst b/docs/source/tutorials/tutorial_6_seq_labeling.rst index 490db6f5..31c20dd1 100644 --- a/docs/source/tutorials/tutorial_6_seq_labeling.rst +++ b/docs/source/tutorials/tutorial_6_seq_labeling.rst @@ -1,5 +1,5 @@ ===================== -序列标注教程 +快速实现序列标注模型 ===================== 这一部分的内容主要展示如何使用fastNLP 实现序列标注任务。你可以使用fastNLP的各个组件快捷,方便地完成序列标注任务,达到出色的效果。 diff --git a/docs/source/tutorials/tutorial_7_modules_models.rst b/docs/source/tutorials/tutorial_7_modules_models.rst index d69d9d2e..bc557709 100644 --- a/docs/source/tutorials/tutorial_7_modules_models.rst +++ b/docs/source/tutorials/tutorial_7_modules_models.rst @@ -1,5 +1,5 @@ ====================================== -Modules 和 models 的教程 +使用Modules和Models快速搭建自定义模型 ====================================== :mod:`~fastNLP.modules` 和 :mod:`~fastNLP.models` 用于构建 fastNLP 所需的神经网络模型,它可以和 torch.nn 中的模型一起使用。 diff --git a/docs/source/tutorials/tutorial_8_metrics.rst b/docs/source/tutorials/tutorial_8_metrics.rst index a3c6770e..0b4f86c8 100644 --- a/docs/source/tutorials/tutorial_8_metrics.rst +++ b/docs/source/tutorials/tutorial_8_metrics.rst @@ -1,6 +1,6 @@ -===================== -Metric 教程 -===================== +=============================== +使用Metric快速评测你的模型 +=============================== 在进行训练时,fastNLP提供了各种各样的 :mod:`~fastNLP.core.metrics` 。 如 :doc:`/user/quickstart` 中所介绍的,:class:`~fastNLP.AccuracyMetric` 类的对象被直接传到 :class:`~fastNLP.Trainer` 中用于训练 diff --git a/docs/source/tutorials/tutorial_9_callback.rst b/docs/source/tutorials/tutorial_9_callback.rst index 01fbb6c3..e1222670 100644 --- a/docs/source/tutorials/tutorial_9_callback.rst +++ b/docs/source/tutorials/tutorial_9_callback.rst @@ -1,6 +1,6 @@ -============================================================================== -Callback 教程 -============================================================================== +=================================================== +使用Callback自定义你的训练过程 +=================================================== 在训练时,我们常常要使用trick来提高模型的性能(如调节学习率),或者要打印训练中的信息。 这里我们提供Callback类,在Trainer中插入代码,完成一些自定义的操作。 From df0bc2aec30b27759ed0a11f231e64ff94995cc5 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Fri, 12 Jul 2019 00:03:41 +0800 Subject: [PATCH 004/139] =?UTF-8?q?=E6=A0=B9=E6=8D=AE=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastNLP/__init__.py | 2 +- fastNLP/core/__init__.py | 8 +- fastNLP/core/batch.py | 5 +- fastNLP/core/callback.py | 14 +- fastNLP/core/dataset.py | 184 +++++++------ fastNLP/core/field.py | 5 +- fastNLP/core/losses.py | 14 +- fastNLP/core/metrics.py | 16 +- fastNLP/core/optimizer.py | 7 +- fastNLP/core/tester.py | 9 +- fastNLP/core/trainer.py | 542 +++++++++++++++++++------------------ fastNLP/core/utils.py | 27 +- fastNLP/core/vocabulary.py | 36 +-- 13 files changed, 460 insertions(+), 409 deletions(-) diff --git a/fastNLP/__init__.py b/fastNLP/__init__.py index 6b43da13..0eaa5d81 100644 --- a/fastNLP/__init__.py +++ b/fastNLP/__init__.py @@ -5,7 +5,7 @@ fastNLP 由 :mod:`~fastNLP.core` 、 :mod:`~fastNLP.io` 、:mod:`~fastNLP.module - :mod:`~fastNLP.core` 是fastNLP 的核心模块,包括 DataSet、 Trainer、 Tester 等组件。详见文档 :doc:`/fastNLP.core` - :mod:`~fastNLP.io` 是实现输入输出的模块,包括了数据集的读取,模型的存取等功能。详见文档 :doc:`/fastNLP.io` - :mod:`~fastNLP.modules` 包含了用于搭建神经网络模型的诸多组件,可以帮助用户快速搭建自己所需的网络。详见文档 :doc:`/fastNLP.modules` -- :mod:`~fastNLP.models` 包含了一些使用 fastNLP 实现的完整网络模型,包括CNNText、SeqLabeling等常见模型。详见文档 :doc:`/fastNLP.models` +- :mod:`~fastNLP.models` 包含了一些使用 fastNLP 实现的完整网络模型,包括 :class:`~fastNLP.models.CNNText` 、 :class:`~fastNLP.models.SeqLabeling` 等常见模型。详见文档 :doc:`/fastNLP.models` fastNLP 中最常用的组件可以直接从 fastNLP 包中 import ,他们的文档如下: """ diff --git a/fastNLP/core/__init__.py b/fastNLP/core/__init__.py index efc83017..c9f51123 100644 --- a/fastNLP/core/__init__.py +++ b/fastNLP/core/__init__.py @@ -1,12 +1,12 @@ """ core 模块里实现了 fastNLP 的核心框架,常用的功能都可以从 fastNLP 包中直接 import。当然你也同样可以从 core 模块的子模块中 import, -例如 Batch 组件有两种 import 的方式:: +例如 :class:`~fastNLP.DataSetIter` 组件有两种 import 的方式:: # 直接从 fastNLP 中 import - from fastNLP import Batch + from fastNLP import DataSetIter - # 从 core 模块的子模块 batch 中 import - from fastNLP.core.batch import Batch + # 从 core 模块的子模块 batch 中 import DataSetIter + from fastNLP.core.batch import DataSetIter 对于常用的功能,你只需要在 :doc:`fastNLP` 中查看即可。如果想了解各个子模块的具体作用,您可以在下面找到每个子模块的具体文档。 diff --git a/fastNLP/core/batch.py b/fastNLP/core/batch.py index 2d8c1a80..64c5f48e 100644 --- a/fastNLP/core/batch.py +++ b/fastNLP/core/batch.py @@ -1,18 +1,17 @@ """ -batch 模块实现了 fastNLP 所需的 Batch 类。 +batch 模块实现了 fastNLP 所需的 :class:`~fastNLP.core.batch.DataSetIter` 类。 """ __all__ = [ + "BatchIter", "DataSetIter", "TorchLoaderIter", ] import atexit -from queue import Empty, Full import numpy as np import torch -import torch.multiprocessing as mp import torch.utils.data from numbers import Number diff --git a/fastNLP/core/callback.py b/fastNLP/core/callback.py index 8a202795..6f855397 100644 --- a/fastNLP/core/callback.py +++ b/fastNLP/core/callback.py @@ -2,11 +2,11 @@ r""" callback模块实现了 fastNLP 中的许多 callback 类,用于增强 :class:`~fastNLP.Trainer` 类。 虽然Trainer本身已经集成了一些功能,但仍然不足以囊括训练过程中可能需要到的功能, -比如负采样,learning rate decay, Early Stop等。 -为了解决这个问题fastNLP引入了callback的机制,Callback 是一种在Trainer训练过程中特定阶段会运行的函数集合。 -关于Trainer的详细文档,请参见 :doc:`trainer 模块` +比如负采样,learning rate decay 和 early stop等。 +为了解决这个问题,fastNLP引入了callback的机制,:class:`~fastNLP.Callback` 是一种在Trainer训练过程中特定阶段会运行的函数集合。 +关于 :class:`~fastNLP.Trainer` 的详细文档,请参见 :doc:`trainer 模块` -我们将 :meth:`~fastNLP.Train.train` 这个函数内部分为以下的阶段,在对应阶段会触发相应的调用:: +我们将 :meth:`~fastNLP.Trainer.train` 这个函数内部分为以下的阶段,在对应阶段会触发相应的调用:: callback.on_train_begin() # 开始进行训练 for i in range(1, n_epochs+1): @@ -31,8 +31,8 @@ callback模块实现了 fastNLP 中的许多 callback 类,用于增强 :class: callback.on_train_end() # 训练结束 callback.on_exception() # 这是一个特殊的步骤,在训练过程中遭遇exception会跳转到这里。 -如下面的例子所示,我们可以使用内置的 callback 类,或者继承 :class:`~fastNLP.core.callback.Callback` -定义自己的 callback 类:: +如下面的例子所示,我们可以使用内置的 callback 组件,或者继承 :class:`~fastNLP.core.callback.Callback` +定义自己的 callback 组件:: from fastNLP import Callback, EarlyStopCallback, Trainer, CrossEntropyLoss, AccuracyMetric from fastNLP.models import CNNText @@ -448,7 +448,7 @@ class FitlogCallback(Callback): 并将验证结果写入到fitlog中。这些数据集的结果是根据dev上最好的结果报道的,即如果dev在第3个epoch取得了最佳,则 fitlog中记录的关于这些数据集的结果就是来自第三个epoch的结果。 - :param ~fastNLP.DataSet,dict(~fastNLP.DataSet) data: 传入DataSet对象,会使用多个Trainer中的metric对数据进行验证。如果需要传入多个 + :param ~fastNLP.DataSet,Dict[~fastNLP.DataSet] data: 传入DataSet对象,会使用多个Trainer中的metric对数据进行验证。如果需要传入多个 DataSet请通过dict的方式传入,dict的key将作为对应dataset的name传递给fitlog。若tester不为None时,data需要通过 dict的方式传入。如果仅传入DataSet, 则被命名为test :param ~fastNLP.Tester tester: Tester对象,将在on_valid_end时调用。tester中的DataSet会被称为为`test` diff --git a/fastNLP/core/dataset.py b/fastNLP/core/dataset.py index 8d2c13e7..7b7fa87a 100644 --- a/fastNLP/core/dataset.py +++ b/fastNLP/core/dataset.py @@ -1,7 +1,7 @@ """ :class:`~fastNLP.core.dataset.DataSet` 是fastNLP中用于承载数据的容器。可以将DataSet看做是一个表格, -每一行是一个sample (在fastNLP中被称为 :mod:`~.instance` ), -每一列是一个feature (在fastNLP中称为 :mod:`.field` )。 +每一行是一个sample (在fastNLP中被称为 :mod:`~fastNLP.core.instance` ), +每一列是一个feature (在fastNLP中称为 :mod:`~fastNLP.core.field` )。 .. csv-table:: Following is a demo layout of DataSet :header: "sentence", "words", "seq_len" @@ -13,57 +13,64 @@ 在fastNLP内部每一行是一个 :class:`~fastNLP.Instance` 对象; 每一列是一个 :class:`~fastNLP.FieldArray` 对象。 -1 DataSet的创建 - 创建DataSet主要有以下的3种方式 +---------------------------- +1.DataSet的创建 +---------------------------- -1.1 传入dict +创建DataSet主要有以下的3种方式 - Example:: +1.1 传入dict +---------------------------- - from fastNLP import DataSet - data = {'sentence':["This is the first instance .", "Second instance .", "Third instance ."], - 'words': [['this', 'is', 'the', 'first', 'instance', '.'], ['Second', 'instance', '.'], ['Third', 'instance', '.'], - 'seq_len': [6, 3, 3]} - dataset = DataSet(data) - # 传入的dict的每个key的value应该为具有相同长度的list + .. code-block:: -1.2 通过构建Instance + from fastNLP import DataSet + data = {'sentence':["This is the first instance .", "Second instance .", "Third instance ."], + 'words': [['this', 'is', 'the', 'first', 'instance', '.'], ['Second', 'instance', '.'], ['Third', 'instance', '.'], + 'seq_len': [6, 3, 3]} + dataset = DataSet(data) + # 传入的dict的每个key的value应该为具有相同长度的list - Example:: +1.2 通过 Instance 构建 +---------------------------- - from fastNLP import DataSet - from fastNLP import Instance - dataset = DataSet() - instance = Instance(sentence="This is the first instance", - words=['this', 'is', 'the', 'first', 'instance', '.'], - seq_len=6) - dataset.append(instance) - # 可以继续append更多内容,但是append的instance应该和第一个instance拥有完全相同的field + .. code-block:: -1.3 通过list(Instance) + from fastNLP import DataSet + from fastNLP import Instance + dataset = DataSet() + instance = Instance(sentence="This is the first instance", + words=['this', 'is', 'the', 'first', 'instance', '.'], + seq_len=6) + dataset.append(instance) + # 可以继续append更多内容,但是append的instance应该和第一个instance拥有完全相同的field - Example:: +1.3 通过 List[Instance] 构建 +-------------------------------------- - from fastNLP import DataSet - from fastNLP import Instance - instances = [] - instances.append(Instance(sentence="This is the first instance", - words=['this', 'is', 'the', 'first', 'instance', '.'], - seq_len=6)) - instances.append(Instance(sentence="Second instance .", - words=['Second', 'instance', '.'], - seq_len=3)) - dataset = DataSet(instances) + .. code-block:: -2 DataSet与预处理 - 常见的预处理有如下几种 + from fastNLP import DataSet + from fastNLP import Instance + instances = [] + winstances.append(Instance(sentence="This is the first instance", + ords=['this', 'is', 'the', 'first', 'instance', '.'], + seq_len=6)) + instances.append(Instance(sentence="Second instance .", + words=['Second', 'instance', '.'], + seq_len=3)) + dataset = DataSet(instances) + +-------------------------------------- +2.DataSet与预处理 +-------------------------------------- -2.1 从某个文本文件读取内容 # +常见的预处理有如下几种 - .. todo:: - 引用DataLoader +2.1 从某个文本文件读取内容 +-------------------------------------- - Example:: + .. code-block:: from fastNLP import DataSet from fastNLP import Instance @@ -78,9 +85,13 @@ sent, label = line.strip().split('\t') dataset.append(Instance(sentence=sent, label=label)) + .. note:: + 直接读取特定数据集的数据请参考 :doc:`/tutorials/tutorial_2_load_dataset` + 2.2 对DataSet中的内容处理 +-------------------------------------- - Example:: + .. code-block:: from fastNLP import DataSet data = {'sentence':["This is the first instance .", "Second instance .", "Third instance ."]} @@ -97,8 +108,9 @@ dataset.apply(get_words, new_field_name='words') 2.3 删除DataSet的内容 +-------------------------------------- - Example:: + .. code-block:: from fastNLP import DataSet dataset = DataSet({'a': list(range(-5, 5))}) @@ -113,15 +125,17 @@ 2.4 遍历DataSet的内容 +-------------------------------------- - Example:: + .. code-block:: for instance in dataset: # do something 2.5 一些其它操作 +-------------------------------------- - Example:: + .. code-block:: # 检查是否存在名为'a'的field dataset.has_field('a') # 或 ('a' in dataset) @@ -129,21 +143,25 @@ dataset.rename_field('a', 'b') # DataSet的长度 len(dataset) + +-------------------------------------- +3.DataSet与自然语言处理(NLP) +-------------------------------------- -3 DataSet与自然语言处理(NLP) - 在目前深度学习的模型中,大都依赖于随机梯度下降法(SGD)进行模型的优化。随机梯度下降需要将数据切分成一个一个的Batch, - 一个Batch进行一次前向计算(forward)与梯度后向传播(backward)。在自然语言处理的场景下,往往还需要对数据进行pad。这是 - 由于句子的长度一般是不同的,但是一次Batch中的每个field都必须是一个tensor,所以需要将所有句子都补齐到相同的长度。 +在目前深度学习的模型中,大都依赖于随机梯度下降法(SGD)进行模型的优化。随机梯度下降需要将数据切分成一个个的 batch, +一个batch进行一次前向计算(forward)与梯度后向传播(backward)。在自然语言处理的场景下,往往还需要对数据进行pad。这是 +由于句子的长度一般是不同的,但是一次batch中的每个field都必须是一个tensor,所以需要将所有句子都补齐到相同的长度。 -3.1 DataSet与Batch +3.1 DataSet与DataSetIter +-------------------------------------- - 我们先看fastNLP中如何将数据分成一个一个的Batch的例子, 这里我们使用随机生成的数据来模拟一个二分类文本分类任务, + 我们先看fastNLP中如何将数据分成一个一个的batch的例子, 这里我们使用随机生成的数据来模拟一个二分类文本分类任务, words和characters是输入,labels是文本类别 - Example:: + .. code-block:: from fastNLP import DataSet - from fastNLP import Batch + from fastNLP import DataSetIter from fastNLP import SequentialSampler from fastNLP import EngChar2DPadder @@ -163,7 +181,7 @@ d.set_target('label') d.set_input('words', 'chars') - for batch_x, batch_y in Batch(d, sampler=SequentialSampler(), batch_size=2): + for batch_x, batch_y in DataSetIter(d, sampler=SequentialSampler(), batch_size=2): print("batch_x:", batch_x) print("batch_y:", batch_y) break @@ -182,23 +200,26 @@ # [ 0, 0, 0, 0, 0]]])} # {'label': tensor([0, 0])} - 其中 :class:`~fastNLP.Batch` 是用于从DataSet中按照batch_size为大小取出batch的迭代器, - :class:`~fastNLP.SequentialSampler` 用于指示 Batch 以怎样的 + 其中 :class:`~fastNLP.DataSetIter` 是用于从DataSet中按照batch_size为大小取出batch的迭代器, + :class:`~fastNLP.SequentialSampler` 用于指示 :class:`~fastNLP.DataSetIter` 以怎样的 顺序从DataSet中取出instance以组成一个batch, - 更详细的说明请参照 :class:`~fastNLP.Batch` 和 :class:`~fastNLP.SequentialSampler` 文档。 + 更详细的说明请参照 :class:`~fastNLP.DataSetIter` 和 :class:`~fastNLP.SequentialSampler` 文档。 - 通过DataSet.set_input('words', 'chars'), fastNLP将认为'words'和'chars'这两个field都是input,并将它们都放入迭代器 - 生成的第一个dict中; DataSet.set_target('labels'), fastNLP将认为'labels'这个field是target,并将其放入到迭代器的第 + 通过 ``DataSet.set_input('words', 'chars')`` , fastNLP将认为 `words` 和 `chars` 这两个field都是input,并将它们都放入迭代器 + 生成的第一个dict中; ``DataSet.set_target('labels')`` , fastNLP将认为 `labels` 这个field是target,并将其放入到迭代器的第 二个dict中。如上例中所打印结果。分为input和target的原因是由于它们在被 :class:`~fastNLP.Trainer` 所使用时会有所差异, 详见 :class:`~fastNLP.Trainer` - 当把某个field设置为'target'或者'input'的时候(两者不是互斥的,可以同时设为input和target),fastNLP不仅仅只是将其放 - 置到不同的dict中,而还会对被设置为input或target的field进行类型检查。类型检查的目的是为了看能否把该field转为 - pytorch的torch.LongTensor或torch.FloatTensor类型(也可以在Batch中设置输出numpy类型,参考 :class:`~fastNLP.Batch` ),如上例所示, - fastNLP已将words,chars和label转为了Tensor类型。如果field在每个instance都拥有相同的维度(不能超过两维),且最内层 - 的元素都为相同的type(int, float, np.int*, np.float*),则fastNLP默认将对该field进行pad。也支持全为str的field作为 - target和input,这种情况下,fastNLP默认不进行pad。另外,当某个field已经被设置为了target或者input后,之后append的 - instance对应的field必须要和前面已有的内容一致,否则会报错。 + 当把某个field设置为 `target` 或者 `input` 的时候(两者不是互斥的,可以同时设为两种),fastNLP不仅仅只是将其放 + 置到不同的dict中,而还会对被设置为 `input` 或 `target` 的 field 进行类型检查。类型检查的目的是为了看能否把该 field 转为 + pytorch的 :class:`torch.LongTensor` 或 :class:`torch.FloatTensor` 类型 + (也可以在 :class:`~fastNLP.DataSetIter` 中设置输出numpy类型,参考 :class:`~fastNLP.DataSetIter` )。 + + 如上例所示,fastNLP已将 `words` ,`chars` 和 `label` 转为了 :class:`Tensor` 类型。 + 如果 field 在每个 `instance` 都拥有相同的维度(不能超过两维),且最内层的元素都为相同的 type(int, float, np.int*, np.float*), + 则fastNLP默认将对该 field 进行pad。也支持全为str的field作为target和input,这种情况下,fastNLP默认不进行pad。 + 另外,当某个 field 已经被设置为了 target 或者 input 后,之后 `append` 的 + `instance` 对应的 field 必须要和前面已有的内容一致,否则会报错。 可以查看field的dtype:: @@ -217,6 +238,7 @@ 错误:: from fastNLP import DataSet + d = DataSet({'data': [1, 'a']}) d.set_input('data') >> RuntimeError: Mixed data types in Field data: [, ] @@ -231,6 +253,7 @@ 当某个field被设置为忽略type之后,fastNLP将不对其进行pad。 3.2 DataSet与pad +-------------------------------------- 在fastNLP里,pad是与一个field绑定的。即不同的field可以使用不同的pad方式,比如在英文任务中word需要的pad和 character的pad方式往往是不同的。fastNLP是通过一个叫做 :class:`~fastNLP.Padder` 的子类来完成的。 @@ -240,7 +263,7 @@ 如果 :class:`~fastNLP.AutoPadder` 或 :class:`~fastNLP.EngChar2DPadder` 无法满足需求, 也可以自己写一个 :class:`~fastNLP.Padder` 。 - Example:: + .. code-block:: from fastNLP import DataSet from fastNLP import EngChar2DPadder @@ -405,7 +428,7 @@ class DataSet(object): """ 将一个instance对象append到DataSet后面。 - :param instance: :class:`~fastNLP.Instance` 类型。若DataSet不为空,则instance应该拥有和DataSet完全一样的field。 + :param ~fastNLP.Instance instance: 若DataSet不为空,则instance应该拥有和DataSet完全一样的field。 """ if len(self.field_arrays) == 0: @@ -431,7 +454,7 @@ class DataSet(object): 将fieldarray添加到DataSet中. :param str field_name: 新加入的field的名称 - :param fieldarray: :class:`~fastNLP.FieldArray` 类型。需要加入DataSet的field的内容 + :param ~fastNLP.core.FieldArray fieldarray: 需要加入DataSet的field的内容 :return: """ if not isinstance(fieldarray, FieldArray): @@ -447,8 +470,7 @@ class DataSet(object): :param str field_name: 新增的field的名称 :param list fields: 需要新增的field的内容 - :param None, padder: :class:`~fastNLP.Padder` 类型, - 如果为None,则不进行pad,默认使用 :class:`~fastNLP.AutoPadder` 自动判断是否需要做pad。 + :param None,~fastNLP.Padder padder: 如果为None,则不进行pad,默认使用 :class:`~fastNLP.AutoPadder` 自动判断是否需要做pad。 :param bool is_input: 新加入的field是否是input :param bool is_target: 新加入的field是否是target :param bool ignore_type: 是否忽略对新加入的field的类型检查 @@ -510,7 +532,7 @@ class DataSet(object): """ 返回一个dict,key为field_name, value为对应的 :class:`~fastNLP.FieldArray` - :return: dict: 返回如上所述的字典 + :return dict: 返回如上所述的字典 """ return self.field_arrays @@ -518,7 +540,7 @@ class DataSet(object): """ 返回一个list,包含所有 field 的名字 - :return: list: 返回如上所述的列表 + :return list: 返回如上所述的列表 """ return sorted(self.field_arrays.keys()) @@ -612,7 +634,7 @@ class DataSet(object): dataset.set_padder('chars', padder) # 则chars这个field会使用EngChar2DPadder进行pad操作 :param str field_name: 设置field的padding方式为padder - :param None, Padder padder: 设置为None即删除padder, 即对该field不进行pad操作。 + :param None,~fastNLP.Padder padder: 设置为None即删除padder, 即对该field不进行pad操作。 """ if field_name not in self.field_arrays: raise KeyError("There is no field named {}.".format(field_name)) @@ -660,7 +682,7 @@ class DataSet(object): 2. is_target: bool, 如果为True则将名为 `new_field_name` 的field设置为target 3. ignore_type: bool, 如果为True则将名为 `new_field_name` 的field的ignore_type设置为true, 忽略其类型 - :return: list(Any), 里面的元素为func的返回值,所以list长度为DataSet的长度 + :return List[Any]: 里面的元素为func的返回值,所以list长度为DataSet的长度 """ assert len(self) != 0, "Null DataSet cannot use apply_field()." @@ -687,7 +709,7 @@ class DataSet(object): """ 将results作为加入到新的field中,field名称为new_field_name - :param list(str) results: 一般是apply*()之后的结果 + :param List[str] results: 一般是apply*()之后的结果 :param str new_field_name: 新加入的field的名称 :param dict kwargs: 用户apply*()时传入的自定义参数 :return: @@ -730,7 +752,7 @@ class DataSet(object): 3. ignore_type: bool, 如果为True则将 `new_field_name` 的field的ignore_type设置为true, 忽略其类型 - :return: list(Any), 里面的元素为func的返回值,所以list长度为DataSet的长度 + :return List[Any]: 里面的元素为func的返回值,所以list长度为DataSet的长度 """ assert len(self) != 0, "Null DataSet cannot use apply()." idx = -1 @@ -795,7 +817,7 @@ class DataSet(object): :param float ratio: 0`pred` - :param target: 参数映射表中`target`的映射关系,None表示映射关系为`target`->`target` - :param str reduction: 支持'mean','sum'和'none'. + :param pred: 参数映射表中 `pred` 的映射关系,None表示映射关系为 `pred` -> `pred` + :param target: 参数映射表中 `target` 的映射关系,None表示映射关系为 `target` -> `target` + :param str reduction: 支持 `mean` ,`sum` 和 `none` . """ def __init__(self, pred=None, target=None, reduction='mean'): @@ -286,11 +286,11 @@ class NLLLoss(LossBase): 负对数似然损失函数 - :param pred: 参数映射表中`pred`的映射关系,None表示映射关系为`pred`->`pred` - :param target: 参数映射表中`target`的映射关系,None表示映射关系为`target`->`target` + :param pred: 参数映射表中 `pred` 的映射关系,None表示映射关系为 `pred` -> `pred` + :param target: 参数映射表中 `target` 的映射关系,None表示映射关系为 `target` -> `target` :param ignore_idx: ignore的index,在计算loss时将忽略target中标号为ignore_idx的内容, 可以通过该值代替 传入seq_len. - :param str reduction: 支持'mean','sum'和'none'. + :param str reduction: 支持 `mean` ,`sum` 和 `none` . """ def __init__(self, pred=None, target=None, ignore_idx=-100, reduction='mean'): diff --git a/fastNLP/core/metrics.py b/fastNLP/core/metrics.py index f75b6c90..f23eab91 100644 --- a/fastNLP/core/metrics.py +++ b/fastNLP/core/metrics.py @@ -27,14 +27,14 @@ from abc import abstractmethod class MetricBase(object): """ - 所有metrics的基类,,所有的传入到Trainer, Tester的Metric需要继承自该对象,需要覆盖写入evaluate(), get_metric()方法。 + 所有metrics的基类,所有的传入到Trainer, Tester的Metric需要继承自该对象,需要覆盖写入evaluate(), get_metric()方法。 evaluate(xxx)中传入的是一个batch的数据。 get_metric(xxx)当所有数据处理完毕,调用该方法得到最终的metric值 以分类问题中,Accuracy计算为例 - 假设model的forward返回dict中包含'pred'这个key, 并且该key需要用于Accuracy:: + 假设model的forward返回dict中包含 `pred` 这个key, 并且该key需要用于Accuracy:: class Model(nn.Module): def __init__(xxx): @@ -43,7 +43,7 @@ class MetricBase(object): # do something return {'pred': pred, 'other_keys':xxx} # pred's shape: batch_size x num_classes - 假设dataset中'label'这个field是需要预测的值,并且该field被设置为了target + 假设dataset中 `label` 这个field是需要预测的值,并且该field被设置为了target 对应的AccMetric可以按如下的定义, version1, 只使用这一次:: class AccMetric(MetricBase): @@ -478,7 +478,7 @@ class SpanFPreRecMetric(MetricBase): 别名::class:`fastNLP.SpanFPreRecMetric` :class:`fastNLP.core.metrics.SpanFPreRecMetric` 在序列标注问题中,以span的方式计算F, pre, rec. - 比如中文Part of speech中,会以character的方式进行标注,句子'中国在亚洲'对应的POS可能为(以BMES为例) + 比如中文Part of speech中,会以character的方式进行标注,句子 `中国在亚洲` 对应的POS可能为(以BMES为例) ['B-NN', 'E-NN', 'S-DET', 'B-NN', 'E-NN']。该metric就是为类似情况下的F1计算。 最后得到的metric结果为:: @@ -502,15 +502,15 @@ class SpanFPreRecMetric(MetricBase): :param tag_vocab: 标签的 :class:`~fastNLP.Vocabulary` 。支持的标签为"B"(没有label);或"B-xxx"(xxx为某种label,比如POS中的NN), 在解码时,会将相同xxx的认为是同一个label,比如['B-NN', 'E-NN']会被合并为一个'NN'. - :param str pred: 用该key在evaluate()时从传入dict中取出prediction数据。 为None,则使用'pred'取数据 - :param str target: 用该key在evaluate()时从传入dict中取出target数据。 为None,则使用'target'取数据 - :param str seq_len: 用该key在evaluate()时从传入dict中取出sequence length数据。为None,则使用'seq_len'取数据。 + :param str pred: 用该key在evaluate()时从传入dict中取出prediction数据。 为None,则使用 `pred` 取数据 + :param str target: 用该key在evaluate()时从传入dict中取出target数据。 为None,则使用 `target` 取数据 + :param str seq_len: 用该key在evaluate()时从传入dict中取出sequence length数据。为None,则使用 `seq_len` 取数据。 :param str encoding_type: 目前支持bio, bmes, bmeso, bioes :param list ignore_labels: str 组成的list. 这个list中的class不会被用于计算。例如在POS tagging时传入['NN'],则不会计算'NN'这 个label :param bool only_gross: 是否只计算总的f1, precision, recall的值;如果为False,不仅返回总的f1, pre, rec, 还会返回每个 label的f1, pre, rec - :param str f_type: 'micro'或'macro'. 'micro':通过先计算总体的TP,FN和FP的数量,再计算f, precision, recall; 'macro': + :param str f_type: `micro` 或 `macro` . `micro` :通过先计算总体的TP,FN和FP的数量,再计算f, precision, recall; `macro` : 分布计算每个类别的f, precision, recall,然后做平均(各类别f的权重相同) :param float beta: f_beta分数, :math:`f_{beta} = \frac{(1 + {beta}^{2})*(pre*rec)}{({beta}^{2}*pre + rec)}` . 常用为beta=0.5, 1, 2. 若为0.5则精确率的权重高于召回率;若为1,则两者平等;若为2,则召回率权重高于精确率。 diff --git a/fastNLP/core/optimizer.py b/fastNLP/core/optimizer.py index 1fe035bf..f0dfdef0 100644 --- a/fastNLP/core/optimizer.py +++ b/fastNLP/core/optimizer.py @@ -5,7 +5,8 @@ optimizer 模块定义了 fastNLP 中所需的各种优化器,一般做为 :cl __all__ = [ "Optimizer", "SGD", - "Adam" + "Adam", + "AdamW" ] import torch @@ -104,6 +105,10 @@ class Adam(Optimizer): class AdamW(TorchOptimizer): r"""对AdamW的实现,该实现应该会在pytorch更高版本中出现,https://github.com/pytorch/pytorch/pull/21250。这里提前加入 + + .. todo:: + 翻译成中文 + The original Adam algorithm was proposed in `Adam: A Method for Stochastic Optimization`_. The AdamW variant was proposed in `Decoupled Weight Decay Regularization`_. Arguments: diff --git a/fastNLP/core/tester.py b/fastNLP/core/tester.py index 7048d0ae..c1d270d1 100644 --- a/fastNLP/core/tester.py +++ b/fastNLP/core/tester.py @@ -1,7 +1,7 @@ """ tester模块实现了 fastNLP 所需的Tester类,能在提供数据、模型以及metric的情况下进行性能测试。 -Example:: +.. code-block:: import numpy as np import torch @@ -60,15 +60,14 @@ class Tester(object): Tester是在提供数据,模型以及metric的情况下进行性能测试的类。需要传入模型,数据以及metric进行验证。 - :param data: 需要测试的数据集, :class:`~fastNLP.DataSet` 类型 + :param ~fastNLP.DataSet data: 需要测试的数据集 :param torch.nn.module model: 使用的模型 - :param metrics: :class:`~fastNLP.core.metrics.MetricBase` 或者一个列表的 :class:`~fastNLP.core.metrics.MetricBase` + :param ~fastNLP.core.metrics.MetricBase,List[~fastNLP.core.metrics.MetricBase] metrics: 测试时使用的metrics :param int batch_size: evaluation时使用的batch_size有多大。 :param str,int,torch.device,list(int) device: 将模型load到哪个设备。默认为None,即Trainer不对模型 的计算位置进行管理。支持以下的输入: - 1. str: ['cpu', 'cuda', 'cuda:0', 'cuda:1', ...] 依次为'cpu'中, 可见的第一个GPU中, 可见的第一个GPU中, - 可见的第二个GPU中; + 1. str: ['cpu', 'cuda', 'cuda:0', 'cuda:1', ...] 依次为'cpu'中, 可见的第一个GPU中,可见的第一个GPU中,可见的第二个GPU中; 2. torch.device:将模型装载到torch.device上。 diff --git a/fastNLP/core/trainer.py b/fastNLP/core/trainer.py index eabda99c..322136d1 100644 --- a/fastNLP/core/trainer.py +++ b/fastNLP/core/trainer.py @@ -11,288 +11,310 @@ Trainer在fastNLP中用于组织单任务的训练过程,可以避免用户在 (5) 保存获得更好验证性能的模型。 -1 Trainer的基本使用 - 下面的例子是使用神经网络来进行预测一个序列中是否有偶数个1。 - - Example:: - - import numpy as np - from torch import nn - import torch - import torch.nn.functional as F - from torch.optim import SGD - - from fastNLP import DataSet - from fastNLP import Trainer - from fastNLP import CrossEntropyLoss - from fastNLP import AccuracyMetric - from fastNLP.modules.decoder import MLP - - # 模型 - class Model(nn.Module): - def __init__(self, input_num): - super().__init__() - self.fcs = MLP([input_num, 40, 40, 2], 'relu') - - def forward(self, x): - x = self.fcs(x) - return {'pred': x} - model = Model(10) - - # 生成数据 - def generate_psedo_dataset(num_samples): - dataset = DataSet() - data = np.random.randint(2, size=(num_samples, 10)) - label = np.sum(data, axis=1)%2 - dataset = DataSet({'x':data.astype(float), 'label': label}) - dataset.set_input('x') - dataset.set_target('label') - return dataset - tr_dataset = generate_psedo_dataset(1000) - dev_data = generate_psedo_dataset(100) - - # 训练 - trainer = Trainer(tr_dataset, model, loss=CrossEntropyLoss(target='label'), - optimizer=SGD(model.parameters(), lr=0.1),n_epochs=1000, - dev_data = dev_data, metrics=AccuracyMetric(target='label')) - trainer.train() - - 由上面的例子可以看出通过使用Trainer,可以使得训练部分的代码大幅减少。 - 使用Trainer需要满足以下几个条件: + +---------------------------- +1. Trainer的基本使用 +---------------------------- + +下面的例子是使用神经网络来进行预测一个序列中是否有偶数个1。 + +.. code-block:: python + + import numpy as np + from torch import nn + import torch + import torch.nn.functional as F + from torch.optim import SGD + + from fastNLP import DataSet + from fastNLP import Trainer + from fastNLP import CrossEntropyLoss + from fastNLP import AccuracyMetric + from fastNLP.modules.decoder import MLP + + # 模型 + class Model(nn.Module): + def __init__(self, input_num): + super().__init__() + self.fcs = MLP([input_num, 40, 40, 2], 'relu') + + def forward(self, x): + x = self.fcs(x) + return {'pred': x} + model = Model(10) + + # 生成数据 + def generate_psedo_dataset(num_samples): + dataset = DataSet() + data = np.random.randint(2, size=(num_samples, 10)) + label = np.sum(data, axis=1)%2 + dataset = DataSet({'x':data.astype(float), 'label': label}) + dataset.set_input('x') + dataset.set_target('label') + return dataset + tr_dataset = generate_psedo_dataset(1000) + dev_data = generate_psedo_dataset(100) + + # 训练 + trainer = Trainer(tr_dataset, model, loss=CrossEntropyLoss(target='label'), + optimizer=SGD(model.parameters(), lr=0.1),n_epochs=1000, + dev_data = dev_data, metrics=AccuracyMetric(target='label')) + trainer.train() + +由上面的例子可以看出通过使用Trainer,可以使得训练部分的代码大幅减少。 +使用Trainer需要满足以下几个条件: 1.1 模型 - 1 模型的forward()的参数名需要与DataSet中的名字对应。实际上fastNLP在将DataSet中的数据传递给模型forward()时,是 - 通过匹配名称实现的。所以上例中,如果Model的forward函数修改为forward(self, data), 则DataSet中的'x'这个field就应该 - 改名为'data'。 +---------------------------- + +1 模型的forward()的参数名需要与DataSet中的名字对应。实际上fastNLP在将DataSet中的数据传递给模型forward()时,是 +通过匹配名称实现的。所以上例中,如果Model的forward函数修改为forward(self, data), 则DataSet中的'x'这个field就应该 +改名为'data'。 - 2 传递给forward()的参数是DataSet中被设置为input的那些field。但如果forward()中没有对应的参数,则不会将数据传递 - 给forward()。例如,DataSet中'x1', 'x2'都是input,但是模型的函数为forward(self, x1), 那么'x2'不会传递给forward()。 +2 传递给forward()的参数是DataSet中被设置为input的那些field。但如果forward()中没有对应的参数,则不会将数据传递 +给forward()。例如,DataSet中'x1', 'x2'都是input,但是模型的函数为forward(self, x1), 那么'x2'不会传递给forward()。 - 3 模型的forward()返回值需要为一个dict。 +3 模型的forward()返回值需要为一个dict。 1.2 Loss - fastNLP中的为了不限制forward函数的返回内容数量(比如一些复杂任务需要返回多个内容,如Dependency Parsing, - :mod:`Loss` 与 :mod:`Metric` 都使用了通过名称来匹配相应内容的策略。如上面的例子中 +---------------------------- - Example:: +fastNLP中的为了不限制forward函数的返回内容数量(比如一些复杂任务需要返回多个内容,如Dependency Parsing, +:mod:`Loss` 与 :mod:`Metric` 都使用了通过名称来匹配相应内容的策略。如上面的例子中 - trainer = Trainer(tr_dataset, model, loss=CrossEntropyLoss(target='label'), - optimizer=SGD(model.parameters(), lr=0.1),n_epochs=1000, - dev_data = dev_data, metrics=AccuracyMetric(target='label')) +.. code-block:: python - loss被设置为了 :class:`~fastNLP.CrossEntropyLoss` , 但在初始化的时候传入了target='label'这个参数, - :class:`~fastNLP.CrossEntropyLoss` 的初始化参数为(pred=None, target=None, padding_idx=-100)。 - - 这里的两个参数分别为计算CrossEntropy时需要使用到的模型的预测值与真实值。 - 其中 `pred` 一般来自于模型forward()的返回结果,`target` 一般是来自于DataSet中被设置为target的field。 - 由于每个人对真实值或者model的返回值取名并不一样,所以fastNLP的 :mod:`Loss` 提供一种类似于映射的机制来匹配对应的值, - 比如这里 :class:`~fastNLP.CrossEntropyLoss` 将尝试找到名为'label'的内容来作为真实值得到loss; - 而pred=None, 则 :class:`~fastNLP.CrossEntropyLoss` 使用'pred'作为名称匹配预测值, - 正好forward的返回值也叫pred,所以这里不需要申明pred。 - - 尽管fastNLP使用了映射机制来使得loss的计算变得比较灵活,但有些情况下loss必须在模型中进行计算,比如使用了CRF的模型。 - fastNLP中提供了 :class:`~fastNLP.LossInForward` 这个loss。 - 这个loss的原理是直接在forward()的返回结果中找到loss_key(默认寻找'loss')指定的那个tensor,并使用它作为loss。 - 如果Trainer初始化没有提供loss则默认使用 :class:`~fastNLP.LossInForward` 。 - - .. todo:: - 补充一个例子 详细例子可以参照 + trainer = Trainer(tr_dataset, model, loss=CrossEntropyLoss(target='label'), + optimizer=SGD(model.parameters(), lr=0.1),n_epochs=1000, + dev_data = dev_data, metrics=AccuracyMetric(target='label')) + +loss被设置为了 :class:`~fastNLP.CrossEntropyLoss` , 但在初始化的时候传入了target='label'这个参数, +:class:`~fastNLP.CrossEntropyLoss` 的初始化参数为(pred=None, target=None, padding_idx=-100)。 + +这里的两个参数分别为计算CrossEntropy时需要使用到的模型的预测值与真实值。 +其中 `pred` 一般来自于模型forward()的返回结果,`target` 一般是来自于DataSet中被设置为target的field。 +由于每个人对真实值或者model的返回值取名并不一样,所以fastNLP的 :mod:`Loss` 提供一种类似于映射的机制来匹配对应的值, +比如这里 :class:`~fastNLP.CrossEntropyLoss` 将尝试找到名为'label'的内容来作为真实值得到loss; +而pred=None, 则 :class:`~fastNLP.CrossEntropyLoss` 使用'pred'作为名称匹配预测值, +正好forward的返回值也叫pred,所以这里不需要申明pred。 + +尽管fastNLP使用了映射机制来使得loss的计算变得比较灵活,但有些情况下loss必须在模型中进行计算,比如使用了CRF的模型。 +fastNLP中提供了 :class:`~fastNLP.LossInForward` 这个loss。 +这个loss的原理是直接在forward()的返回结果中找到loss_key(默认寻找'loss')指定的那个tensor,并使用它作为loss。 +如果Trainer初始化没有提供loss则默认使用 :class:`~fastNLP.LossInForward` 。 + +.. todo:: + 补充一个例子 详细例子可以参照 1.3 Metric - :mod:`Metric` 使用了与上述Loss一样的策略,即使用名称进行匹配。 - AccuracyMetric(target='label')的情况与CrossEntropyLoss 是同理的。 - - 在进行验证时,可能用到的计算与forward()中不太一致,没有办法直接从forward()的结果中得到预测值,这时模型可以提供一个predict()方法, - 如果提供的模型具有predict方法,则在模型验证时将调用predict()方法获取预测结果, - 传入到predict()的参数也是从DataSet中被设置为input的field中选择出来的; - 与forward()一样,返回值需要为一个dict。 +---------------------------- + +:mod:`Metric` 使用了与上述Loss一样的策略,即使用名称进行匹配。 +AccuracyMetric(target='label')的情况与CrossEntropyLoss 是同理的。 + +在进行验证时,可能用到的计算与forward()中不太一致,没有办法直接从forward()的结果中得到预测值,这时模型可以提供一个predict()方法, +如果提供的模型具有predict方法,则在模型验证时将调用predict()方法获取预测结果, +传入到predict()的参数也是从DataSet中被设置为input的field中选择出来的; +与forward()一样,返回值需要为一个dict。 + +.. todo:: + 补充一个例子 具体例子可以参考 - .. todo:: - 补充一个例子 具体例子可以参考 +---------------------------- +2. Trainer的代码检查 +---------------------------- -2 Trainer的代码检查 - 由于在fastNLP中采取了映射的机制,所以难免可能存在对应出错的情况。Trainer提供一种映射检查机制,可以通过check_code_level来进行控制 - 比如下面的例子中,由于各种原因产生的报错 +由于在fastNLP中采取了映射的机制,所以难免可能存在对应出错的情况。Trainer提供一种映射检查机制,可以通过check_code_level来进行控制 +比如下面的例子中,由于各种原因产生的报错 Example2.1 - :: - - import numpy as np - from torch import nn - import torch - from torch.optim import SGD - from fastNLP import Trainer - from fastNLP import DataSet - - class Model(nn.Module): - def __init__(self): - super().__init__() - self.fc = nn.Linear(1, 1) - def forward(self, x, b): - loss = torch.mean((self.fc(x)-b)**2) - return {'loss': loss} - model = Model() - - dataset = DataSet({'a': np.arange(10), 'b':np.arange(10)*2}) - dataset.set_input('a', 'b') - - trainer = Trainer(dataset, model, loss=None, optimizer=SGD(model.parameters(), lr=0.001)) - - trainer = Trainer(dataset, model, SGD(model.parameters())) - # 会报以下的错误 - # input fields after batch(if batch size is 2): - # a: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2]) - # b: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2]) - # There is no target field. - # .... - # NameError: - # Problems occurred when calling Model.forward(self, x, b) - # missing param: ['x'] - # unused field: ['a'] - # Suggestion: You need to provide ['x'] in DataSet and set it as input. - - 这里就是由于在Trainer初始化的时候,fastNLP会尝试使用一个batch_size=2的batch去运行一遍forward()以及backward()。这里有两类 - 信息可以为你提供参考 - - 1 'input fields after batch...'这部分显示的是train dataset经过Batch操作后,每个field对应的类型以及进行shape。这里 - 因为train dataset没有target所以没有显示。根据这里可以看出是否正确将需要的内容设置为了input或target。 - - 2 NameError,NameError发生在映射出错的情况。这里报错的原因是由于尝试进行forward计算时(可以通过Model.forward(self, x, b)判断 - 出当前是在调取forward),却没有获取到forward()函数中需要的'x';在报错信息中同时指出了缺'x',而'a'没有被使用,那么可能 - 就是由于field的名称不对。这里将dataset中'a'这个field的名称改为'x',或者model的参数从'x'修改为'a'都可以解决问题。 - - 下面的例子是由于loss计算的时候找不到需要的值 +---------------------------- + +.. code-block:: python + + import numpy as np + from torch import nn + import torch + from torch.optim import SGD + from fastNLP import Trainer + from fastNLP import DataSet + + class Model(nn.Module): + def __init__(self): + super().__init__() + self.fc = nn.Linear(1, 1) + def forward(self, x, b): + loss = torch.mean((self.fc(x)-b)**2) + return {'loss': loss} + model = Model() + + dataset = DataSet({'a': np.arange(10), 'b':np.arange(10)*2}) + dataset.set_input('a', 'b') + + trainer = Trainer(dataset, model, loss=None, optimizer=SGD(model.parameters(), lr=0.001)) + + trainer = Trainer(dataset, model, SGD(model.parameters())) + # 会报以下的错误 + # input fields after batch(if batch size is 2): + # a: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2]) + # b: (1)type:torch.Tensor (2)dtype:torch.int64, (3)shape:torch.Size([2]) + # There is no target field. + # .... + # NameError: + # Problems occurred when calling Model.forward(self, x, b) + # missing param: ['x'] + # unused field: ['a'] + # Suggestion: You need to provide ['x'] in DataSet and set it as input. + +这里就是由于在Trainer初始化的时候,fastNLP会尝试使用一个batch_size=2的batch去运行一遍forward()以及backward()。这里有两类 +信息可以为你提供参考 + +1 'input fields after batch...'这部分显示的是train dataset经过Batch操作后,每个field对应的类型以及进行shape。这里 +因为train dataset没有target所以没有显示。根据这里可以看出是否正确将需要的内容设置为了input或target。 + +2 NameError,NameError发生在映射出错的情况。这里报错的原因是由于尝试进行forward计算时(可以通过Model.forward(self, x, b)判断 +出当前是在调取forward),却没有获取到forward()函数中需要的'x';在报错信息中同时指出了缺'x',而'a'没有被使用,那么可能 +就是由于field的名称不对。这里将dataset中'a'这个field的名称改为'x',或者model的参数从'x'修改为'a'都可以解决问题。 + +下面的例子是由于loss计算的时候找不到需要的值 Example2.2 - :: - - import numpy as np - from torch import nn - from torch.optim import SGD - from fastNLP import Trainer - from fastNLP import DataSet - from fastNLP import L1Loss - import torch - - class Model(nn.Module): - def __init__(self): - super().__init__() - self.fc = nn.Linear(1, 1) - def forward(self, a): - return {'pred_b': self.fc(a.unsqueeze(1)).squeeze(1), 'No use':1} - - model = Model() - - dataset = DataSet({'a': np.arange(10, dtype=float), 'b':np.arange(10, dtype=float)*2}) - - dataset.set_input('a') - dataset.set_target('b') - - trainer = Trainer(dataset, model, loss=L1Loss(target='label'), optimizer=SGD(model.parameters(), lr=0.001)) - # 报错信息如下 - # input fields after batch(if batch size is 2): - # a: (1)type:torch.Tensor (2)dtype:torch.float32, (3)shape:torch.Size([2]) - # target fields after batch(if batch size is 2): - # b: (1)type:torch.Tensor (2)dtype:torch.float32, (3)shape:torch.Size([2]) - # .... - # NameError: - # Problems occurred when calling L1Loss.get_loss(self, pred, target) - # missing param: ['pred(assign to `pred` in `L1Loss`)', 'label(assign to `target` in `L1Loss`)'] - # unused field: ['b'] - # unused param: ['pred_b', 'No use'] - # target field: ['b'] - # param from Model.forward(self, a): ['pred_b', 'No use'] - # Suggestion: (1). Check key assignment for `target` when initialize L1Loss. Or provide `label` in DataSet or output of Model.forward(self, a). - # (2). Check key assignment for `pred` when initialize L1Loss. Or provide `pred` in DataSet or output of Model.forward(self, a). - - 报错信息也包含两部分: - - 1 第一部分与上面是一样的 - - 2 这里报错的原因是由于计算loss的时候找不到相应的值(通过L1Loss.get_loss(self, pred, target)判断出来的); - 报错的原因是因为 `pred` 和 `label` (我们在初始化L1Loss时将target指定为了label)都没有找到。 - 这里'unused field'是DataSet中出现了,但却没有被设置为input或者target的field; - 'unused param'是forward()中返回且没有被使用到的内容;'target field'是被设置为了target的field; - 'param from Model.forward(self, a)'是forward()返回的所有key。"Suggestion"是关于当前错误处理的建议。 - - 但是在一些情况下,比如forward()返回值只有一个,target也只有一个,fastNLP不会进行匹配,而直接将forward()的结果作为pred, - 将DataSet中的target设置为target。上面的例子在返回值中加入了一个'No use'则只是为了使得Loss去匹配结果。 - - - 下面是带有dev dataset时如果出现错误会发生的报错, +---------------------------- + +.. code-block:: python + + import numpy as np + from torch import nn + from torch.optim import SGD + from fastNLP import Trainer + from fastNLP import DataSet + from fastNLP import L1Loss + import torch + + class Model(nn.Module): + def __init__(self): + super().__init__() + self.fc = nn.Linear(1, 1) + def forward(self, a): + return {'pred_b': self.fc(a.unsqueeze(1)).squeeze(1), 'No use':1} + + model = Model() + + dataset = DataSet({'a': np.arange(10, dtype=float), 'b':np.arange(10, dtype=float)*2}) + + dataset.set_input('a') + dataset.set_target('b') + + trainer = Trainer(dataset, model, loss=L1Loss(target='label'), optimizer=SGD(model.parameters(), lr=0.001)) + # 报错信息如下 + # input fields after batch(if batch size is 2): + # a: (1)type:torch.Tensor (2)dtype:torch.float32, (3)shape:torch.Size([2]) + # target fields after batch(if batch size is 2): + # b: (1)type:torch.Tensor (2)dtype:torch.float32, (3)shape:torch.Size([2]) + # .... + # NameError: + # Problems occurred when calling L1Loss.get_loss(self, pred, target) + # missing param: ['pred(assign to `pred` in `L1Loss`)', 'label(assign to `target` in `L1Loss`)'] + # unused field: ['b'] + # unused param: ['pred_b', 'No use'] + # target field: ['b'] + # param from Model.forward(self, a): ['pred_b', 'No use'] + # Suggestion: (1). Check key assignment for `target` when initialize L1Loss. Or provide `label` in DataSet or output of Model.forward(self, a). + # (2). Check key assignment for `pred` when initialize L1Loss. Or provide `pred` in DataSet or output of Model.forward(self, a). + +报错信息也包含两部分: + +1 第一部分与上面是一样的 + +2 这里报错的原因是由于计算loss的时候找不到相应的值(通过L1Loss.get_loss(self, pred, target)判断出来的); +报错的原因是因为 `pred` 和 `label` (我们在初始化L1Loss时将target指定为了label)都没有找到。 +这里'unused field'是DataSet中出现了,但却没有被设置为input或者target的field; +'unused param'是forward()中返回且没有被使用到的内容;'target field'是被设置为了target的field; +'param from Model.forward(self, a)'是forward()返回的所有key。"Suggestion"是关于当前错误处理的建议。 + +但是在一些情况下,比如forward()返回值只有一个,target也只有一个,fastNLP不会进行匹配,而直接将forward()的结果作为pred, +将DataSet中的target设置为target。上面的例子在返回值中加入了一个'No use'则只是为了使得Loss去匹配结果。 + + +下面是带有dev dataset时如果出现错误会发生的报错, Example2.3 - :: +---------------------------- + +.. code-block:: python + + import numpy as np + from torch import nn + from torch.optim import SGD + from fastNLP import Trainer + from fastNLP import DataSet + from fastNLP import AccuracyMetric + import torch + + class Model(nn.Module): + def __init__(self): + super().__init__() + self.fc = nn.Linear(1, 1) + def forward(self, a, b): + loss = torch.mean((self.fc(a.float().unsqueeze(1))-b.float())**2) + return {'loss': loss} + def predict(self, a): # 使用predict()进行验证 + return {'output':self.fc(a.float().unsqueeze(1))} #这里return的值不包含'pred'这个key + model = Model() + + dataset = DataSet({'a': np.arange(10), 'b':np.arange(10)*2}) + dev_data = DataSet({'a': np.arange(10, 20), 'b':np.arange(10, 20)*2}) + + dataset.set_input('a', 'b') + dev_data.set_input('a') # 这里没有设置target + + trainer = Trainer(dataset, model, loss=None, optimizer=SGD(model.parameters(), lr=0.001), + dev_data=dev_data, metrics=AccuracyMetric()) + + # 报错信息 + # ... + # NameError: + # Problems occurred when calling AccuracyMetric.evaluate(self, pred, target, seq_len=None) + # missing param: ['pred(assign to `pred` in `AccuracyMetric`)', 'target(assign to `target` in `AccuracyMetric`)'] + # unused param: ['output'] + # target field: [] + # param from Model.predict(self, a): ['output'] + # Suggestion: (1). Check key assignment for `pred` when initialize AccuracyMetric. Or provide `pred` in DataSet or output of Model.predict(self, a). + # (2). Check key assignment for `target` when initialize AccuracyMetric. Or provide `target` in DataSet or output of Model.predict(self, a). + +报错信息和前面都是类似的,但是可以通过'AccuracyMetric.evaluate(self, pred, target, seq_len=None)'看出这里是evaluation +的时候发生了错误。这样避免了需要在完成一整个epoch的训练才能发现evaluation弄错的情况。这里的修改是通过在初始化metric的时候 +指明通过'output'获取`pred`, 即AccuracyMetric(pred='output')。 + +可以通过check_code_level调节检查的强度。默认为0,即进行检查。 + +---------------------------- +3. Trainer与callback +---------------------------- + +虽然Trainer本身已经集成了一些功能,但仍然不足以囊括训练过程中可能需要到的功能,比如负采样,learning rate decay, Early Stop等。 +为了解决这个问题fastNLP引入了callback的机制,:class:`~fastNLP.Callback` 是一种在Trainer训练过程中特定阶段会运行的函数集合, +所有的 :class:`~fastNLP.Callback` 都具有on_*(比如on_train_start, on_backward_begin)等函数。 +如果 Callback 实现了该函数,则Trainer运行至对应阶段,会进行调用,例如:: + + from fastNLP import Callback, EarlyStopCallback, Trainer, CrossEntropyLoss, AccuracyMetric + from fastNLP.models import CNNText + + start_time = time.time() - import numpy as np - from torch import nn - from torch.optim import SGD - from fastNLP import Trainer - from fastNLP import DataSet - from fastNLP import AccuracyMetric - import torch - - class Model(nn.Module): - def __init__(self): - super().__init__() - self.fc = nn.Linear(1, 1) - def forward(self, a, b): - loss = torch.mean((self.fc(a.float().unsqueeze(1))-b.float())**2) - return {'loss': loss} - def predict(self, a): # 使用predict()进行验证 - return {'output':self.fc(a.float().unsqueeze(1))} #这里return的值不包含'pred'这个key - model = Model() - - dataset = DataSet({'a': np.arange(10), 'b':np.arange(10)*2}) - dev_data = DataSet({'a': np.arange(10, 20), 'b':np.arange(10, 20)*2}) - - dataset.set_input('a', 'b') - dev_data.set_input('a') # 这里没有设置target - - trainer = Trainer(dataset, model, loss=None, optimizer=SGD(model.parameters(), lr=0.001), - dev_data=dev_data, metrics=AccuracyMetric()) - - # 报错信息 - # ... - # NameError: - # Problems occurred when calling AccuracyMetric.evaluate(self, pred, target, seq_len=None) - # missing param: ['pred(assign to `pred` in `AccuracyMetric`)', 'target(assign to `target` in `AccuracyMetric`)'] - # unused param: ['output'] - # target field: [] - # param from Model.predict(self, a): ['output'] - # Suggestion: (1). Check key assignment for `pred` when initialize AccuracyMetric. Or provide `pred` in DataSet or output of Model.predict(self, a). - # (2). Check key assignment for `target` when initialize AccuracyMetric. Or provide `target` in DataSet or output of Model.predict(self, a). - - 报错信息和前面都是类似的,但是可以通过'AccuracyMetric.evaluate(self, pred, target, seq_len=None)'看出这里是evaluation - 的时候发生了错误。这样避免了需要在完成一整个epoch的训练才能发现evaluation弄错的情况。这里的修改是通过在初始化metric的时候 - 指明通过'output'获取`pred`, 即AccuracyMetric(pred='output')。 - - 可以通过check_code_level调节检查的强度。默认为0,即进行检查。 - -3 Trainer与callback - 虽然Trainer本身已经集成了一些功能,但仍然不足以囊括训练过程中可能需要到的功能,比如负采样,learning rate decay, Early Stop等。 - 为了解决这个问题fastNLP引入了callback的机制,:class:`~fastNLP.Callback` 是一种在Trainer训练过程中特定阶段会运行的函数集合, - 所有的 :class:`~fastNLP.Callback` 都具有on_*(比如on_train_start, on_backward_begin)等函数。 - 如果 Callback 实现了该函数,则Trainer运行至对应阶段,会进行调用,例如:: + class MyCallback(Callback): + def on_epoch_end(self): + print('{:d}ms\n\n'.format(round((time.time()-start_time)*1000))) - from fastNLP import Callback, EarlyStopCallback, Trainer, CrossEntropyLoss, AccuracyMetric - from fastNLP.models import CNNText - - start_time = time.time() - - class MyCallback(Callback): - def on_epoch_end(self): - print('{:d}ms\n\n'.format(round((time.time()-start_time)*1000))) - - model = CNNText((len(vocab),50), num_classes=5, padding=2, dropout=0.1) - trainer = Trainer(model=model, train_data=train_data, dev_data=dev_data, loss=CrossEntropyLoss(), - metrics=AccuracyMetric(), callbacks=[MyCallback(),EarlyStopCallback(10)]) - trainer.train() - - 这里,我们通过继承 :class:`~fastNLP.Callback` 类定义了自己的 callback 的,并和内置的 :class:`~fastNLP.EarlyStopCallback` - 一起传给了 :class:`~fastNLP.Trainer` ,增强了 :class:`~fastNLP.Trainer` 的功能 + model = CNNText((len(vocab),50), num_classes=5, padding=2, dropout=0.1) + trainer = Trainer(model=model, train_data=train_data, dev_data=dev_data, loss=CrossEntropyLoss(), + metrics=AccuracyMetric(), callbacks=[MyCallback(),EarlyStopCallback(10)]) + trainer.train() - fastNLP已经自带了很多callback函数供使用,可以参考 :doc:`fastNLP.core.callback` 。 +这里,我们通过继承 :class:`~fastNLP.Callback` 类定义了自己的 callback 的,并和内置的 :class:`~fastNLP.EarlyStopCallback` +一起传给了 :class:`~fastNLP.Trainer` ,增强了 :class:`~fastNLP.Trainer` 的功能 + +fastNLP已经自带了很多callback函数供使用,可以参考 :doc:`fastNLP.core.callback` 。 """ __all__ = [ diff --git a/fastNLP/core/utils.py b/fastNLP/core/utils.py index 9b23240c..2847e724 100644 --- a/fastNLP/core/utils.py +++ b/fastNLP/core/utils.py @@ -4,7 +4,6 @@ utils模块实现了 fastNLP 内部和外部所需的很多工具。其中用户 __all__ = [ "cache_results", "seq_len_to_mask", - "Option", ] import _pickle @@ -24,26 +23,27 @@ _CheckRes = namedtuple('_CheckRes', ['missing', 'unused', 'duplicated', 'require class Option(dict): """a dict can treat keys as attributes""" + def __getattr__(self, item): try: return self.__getitem__(item) except KeyError: raise AttributeError(item) - + def __setattr__(self, key, value): if key.startswith('__') and key.endswith('__'): raise AttributeError(key) self.__setitem__(key, value) - + def __delattr__(self, item): try: self.pop(item) except KeyError: raise AttributeError(item) - + def __getstate__(self): return self - + def __setstate__(self, state): self.update(state) @@ -163,6 +163,7 @@ def cache_results(_cache_fp, _refresh=False, _verbose=1): return wrapper_ + def _save_model(model, model_name, save_dir, only_param=False): """ 存储不含有显卡信息的state_dict或model :param model: @@ -673,7 +674,7 @@ def seq_len_to_mask(seq_len, max_len=None): 将一个表示sequence length的一维数组转换为二维的mask,不包含的位置为0。 转变 1-d seq_len到2-d mask. - Example:: + .. code-block:: >>> seq_len = torch.arange(2, 16) >>> mask = seq_len_to_mask(seq_len) @@ -691,7 +692,7 @@ def seq_len_to_mask(seq_len, max_len=None): :param np.ndarray,torch.LongTensor seq_len: shape将是(B,) :param int max_len: 将长度pad到这个长度。默认(None)使用的是seq_len中最长的长度。但在nn.DataParallel的场景下可能不同卡的seq_len会有 区别,所以需要传入一个max_len使得mask的长度是pad到该长度。 - :return: np.ndarray or torch.Tensor, shape将是(B, max_length)。 元素类似为bool或torch.uint8 + :return: np.ndarray, torch.Tensor 。shape将是(B, max_length), 元素类似为bool或torch.uint8 """ if isinstance(seq_len, np.ndarray): assert len(np.shape(seq_len)) == 1, f"seq_len can only have one dimension, got {len(np.shape(seq_len))}." @@ -737,7 +738,8 @@ class _pseudo_tqdm: def __exit__(self, exc_type, exc_val, exc_tb): del self -def iob2(tags:List[str])->List[str]: + +def iob2(tags: List[str]) -> List[str]: """ 检查数据是否是合法的IOB数据,如果是IOB1会被自动转换为IOB2。两者的差异见 https://datascience.stackexchange.com/questions/37824/difference-between-iob-and-iob2-format @@ -760,7 +762,8 @@ def iob2(tags:List[str])->List[str]: tags[i] = "B" + tag[1:] return tags -def iob2bioes(tags:List[str])->List[str]: + +def iob2bioes(tags: List[str]) -> List[str]: """ 将iob的tag转换为bioes编码 :param tags: List[str]. 编码需要是大写的。 @@ -773,15 +776,15 @@ def iob2bioes(tags:List[str])->List[str]: else: split = tag.split('-')[0] if split == 'B': - if i+1!=len(tags) and tags[i+1].split('-')[0] == 'I': + if i + 1 != len(tags) and tags[i + 1].split('-')[0] == 'I': new_tags.append(tag) else: new_tags.append(tag.replace('B-', 'S-')) elif split == 'I': - if i + 1 Date: Fri, 12 Jul 2019 00:14:11 +0800 Subject: [PATCH 005/139] =?UTF-8?q?=E6=9B=B4=E6=96=B0fastNLP=E6=A1=86?= =?UTF-8?q?=E6=9E=B6=E5=9B=BE=E5=8F=8A=E6=B5=81=E7=A8=8B=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/figures/text_classification.png | Bin 73948 -> 322157 bytes docs/source/figures/workflow.png | Bin 336236 -> 250045 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/source/figures/text_classification.png b/docs/source/figures/text_classification.png index 0d36a2a1a1819b56d32d378ae9e11a3e56aba2b9..215027082eab87b13ec8894f42bb22d021467fd1 100644 GIT binary patch literal 322157 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%0DqOS*+~u>V`cp+?B*u^g1c*_zQ$jSj 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�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

`dy1(Y-4&; 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%0AjdP!=ozmJb;^JJDCN{47Qej|vi22fQws#Z z;4?Vl$%U7`u%Zo|@;Tea0Lz64Lw^bULQHu5ESuR#$Hx6+s#|Yiye`r2HokdRh=3Um z1E7pYz{4{gIRJ#1<;E2j1bGhYzm}Sy7J2|XU94CoY^?#c*si6wg1c3QuOLy~o-;Lj z!^Tg%ck&k%47ck#s&|dS;Gkrd4xFcs8p+g=7Ix@vLxer!5cR=>8aWfs$ilFgsohE{ z59nDMZEtHcdLfh`#aQgRFAg- z-Be-ko63{#)hpBp zhtgna^w@EBwd7`+?Vbih16r}a;Y4s5nC&LvOxowHAQ6ad@6pHDjdYL*i-2CXJ3w<5 z@_Xr>E-Sbus``Qgbb-RZVdfK4Oe! zF|99Nl$Hqqg{;vUYDBZ(X)H4X1PYZ+a2>8iun^jB^72N z8%Mq|BB>0i%0wdSBoX^{@k%)Hr~s|bWUN_9^4*dCG^Z;0^$uF5QE&fiz7&Hbbvb#4 zEG5=h6Tj`~@BCsgqk46w#wY2$<$={_M6Q2xr@YH<3VbC*?GCd(fc79DSrW)a~cyp!eWsafT8{a(By<`4%HR!6}qAtlRv z=gy;bzykb^oL$MS?BHz=H|lG0z%|PhMofS7xXoK+WJ8c~6*VNW=j{^dzO68J3t72u z9?Uv{3_Iwio7Of2e{Z$b=+Kko+m$E1(6v=&zev3Ig&wn;)*tu4cQ%fqXioI;OXd{dwjlce`r zs@*}ZK$&}?Z>ibG^Hb=zkKR}<*96)kg&hmVr9=b{nJ+v=scyYnbSsYIkhaneFqCxP zh;(l;msho#OJ%n%7JWGAVr98h;H|N(IGbvF7klB-Ljnj@I)j}~dVjoE*$q(s5^YBOzZl-R#LVO(vm z6j~wfT(igejKwC7?Yy>4f1%jD1TeE2XQ<+fr+| z4bWsZ?rR`oW=~{bo@C|vOb|o1GUpdAq56hwVS$XISk~E+M@{b+`t%6B`!Ygx_E^v} zKGcXBMcvW1qbotkZ5FJ`UBDl6H}M=#SovPyh1VG1J3hrTwLwx+8Our<7BK3SOVB6{ z9sKFBq(RZ%sL2hsv+dV@6ZcujG3`I>I^Bi1Wi1Autz#Fi#E-j86XGZE%>2 z%RL28^k$E4IW1S0!2mzG$3-A!H?c zwOOiz$_Ro6;+m;3#eBA ztnguGwY@ZTEPIpUb=B0TC0VqmJ5E6~RDF4>rq^v;hXpq~*kG{N;_z{KWtHU(O0L9Q z*`#6ZV3n)s!P?A(38z_B3309D3dhOWh$|%zj2H}ZKl;iz!FU6TZpWodvt*meC~V)? z)!3-M0Sk$+SN~k@JGIQ1H+9Z!x6aa%GBOll9@QPICdG9u_Hp>|783Pp*z|ec-P^VO zHicmHXlGqigVuGnE7Ld3wvYHGdOI0Ha)jh^^CODInI}!X9Z3qq>(5dA9$BV_kh$tq z-E&>Rni_3M5yrA5F;tSKp_x&V<|Rv!Z;p+5&F+A2=Ac(*=)_ZGdt;+@#O)q=g9R;M z8s=|C*L8^!ky)Jb_KAfV0=g0~*t?67?&k6c-?G-rB_7m=bQi3bda#iT`OC}s&7A6z z{Z-s&HayAS_)zZE&S)Qv=%?YiLx|z8QR_)ox_9h%S%QEiW;sNU#ka!Gd{WLoGh6%# zpHD0`3+`vdg#!T$PC|h!6$%1pDuXx13xh1Ov^SQ5m7iE>boPD9RDYz+qFPa!ulE3^ zwB8vcngqlc2oN!Tnn)R)F!TNmni2?UwYQaf-nQ`;cKhD(-EQYyVpiONeL8GxCM?{c z$-FPEIxDp^W)Zt3IyN5z70kZLcHN&!LSZHhaMZK$B>4XB2HTz-S)n7agK zd%h^$P@k9K6od^mM3`I!sO}jFBAzAuGe!f1@34pt zs`oGwuUYKqMP2A$Y;5+CC_5Q{ZN|+)0#W=P;>ph_2HkwRC_1R5fjen62p%j;;*z5{ zU$rH18vi@Q{N;rG4{MuqBA zEQ2$p8dq79;O#bocvp{F-jRyA{ny1hue1vIO23UxiJQupD?TQ$u;E!KfGW6g5Z{z4 z=eyHjOY>C!?O5r~<*y|fxRA1pOAF%(&D*r;E9^oq0s5xZ!AkE2VZohM1iOh-@9;2s zR^jXvw}?g7R%QRmkw6HhfU582dr@R|WI&hIxCN=S9;-J3we@X+brwF&(NZ-Mr?gk0 z8iiaq2?nnO@d-Wru2xrDbHfZv5`_ z?ltE8b{{-|8PU5#;9rF#B35!tdyOSas#w7yW7`>KmBitk*>1k+ajyHc+Keov#-1ym9TAly4@&9y zuQym@9oRDEcjz3n*oizVYq8g#?+LjV6=>E?@m*h;%a`AL|0>o~ z65S7wBD`uolS`go5MfCrZa3!CW&bOD(ZxvaeF%gvo*v?h0;$aP-<0a->&w64o$?w6 z_o98t&~Sj~Y1l`#AZ4qz)B)C(@nEXUc2`%;2^PCq{T|olcZOHB21Le!4he#8vR&nF zmFg0LxR=~JSl)jD9MZO&Yco|reJ^EvD@knck;BL17!yF@M&)OhbIsnbN3aM{vy@2z ziHF*5zIx|hSm?1-ijqoKuO>}i8*+E8C1fNFrFt~Zqm+$rGh##7VaN zRhu-y;dsW;W~O?Vj(vZ*o>HtY@JhfN`Arf zNGbgpv0EtAEa5TU?m&QX;W_4k!=g#~N0f^lHWHLKwaVwv=*=jm#sOYgCjhV%^VoocXsrN?XMVfQRut}CcZy|w*3^_zuBy3Q>Gf8wxS zMVT_u#ToAS$wiph%XPE(&zhDUan-K2##2s-o6py#Y^$F8`EFu|9mWAbn&0aull}>Q z!qNZRh;#eW;28M{q!u_)B1B1M!W19Nzc-_7iS=s23pW5Uxn8a+D-xBS0_vS;;hyvQ z_Ou(RQ327af@a_NDmS8tUv7Ip2Dr)f;~I|7`yroJP>z& z#?$~(q3EaVm(#<#&C46p z?p0xWtV#~E6MQMfl$B@o0GEgN6^8h`p3MmFq0YkKvQ`!HYW4@Fr}c6s4+S>UUHOYQ zTLf7$ISJaUFwR9TXsCL;oX33&vjx-d0j%n$T;D3*h>dOm6>6&Q+vZMh0R-Pb2P#S> zhL&@l0gWqIWEzI~$&3lnxTQU^I)@d`kqW~jsSXk(h8Shhu0I?G2keOs3The3q!Z{X zFk^grUIJ52CQ}|Yns5GPUO{_@qTl{W? zZ=lruT9dSMV}~;WHJ$Ai`Hb9V{4qM0oq}S)7u=h#^MhSX?C0mZM66q)H9&0+({mmU zi4O5f!&@R?C9lOvz4(l5aCFS#H-mMkz4v?%Dx;*gykz@N6eY3W&2e;vPH17<+38;! zZGr_U59mv4^Hk`K_hzxF;z~1#tEy(p1P)Z><1LfxaM_Gx^R=k8R$l*O)^cLoXI^Ox z&@(9pRmSoU6QOj1rMs^U__bnS$*Uyu`KxuHD=c9sNTsAZHTUL4qW?Xg&7q~r%BZCn zd%vWq%6oj}_GHfl72eg*Ko1q~<*cZho;$d^>u=?!n`MKtzi$rS&#UWkjsGBZf!kA&4*qwfi&<>@By~NyP@cQ+-ulL(gYb{bjDmxPcfnGrk z6RXowQXvWY_*|iu^*yV}dTc|gTW3RMqrCfi;-baFN+rvDSIpQ|cXV?!pg@`+z;6|Y zlESHuMQ6G8zG$8;8m9*CWJzl2vEy;p9}MdXoUSAFV49a)^yWlzGk`-kBr>c2O3?si zJ&N`Yx1~;btzk0_q(k>_?$`+JY{Y*=S@KKBu*8uNw_4ni`6UK-{)1xoyMrq}$jXO1MB!!z7w@8H98s-p(Mo=btl&@pa^k7HRB}F(7 zPWkFX(DEdi!z~I1k1JCayln%{Yek+_)db(6Meyun;Y$KZQeLZb^6*MOxc&y?G>kvD zypgDoa}M`~SaP-X23uUPh*Qg+J9Uw(PEw@X@A3bOy|0dndW#xW6i`qx=;j(oi*8ua*==EMujPI@W{`l6r|FC{*II+*} zz0W@9EG&Re6XQyb<~K= zI;-`N{e>sK1q&^@BABT^i4OPpNFc5eiv@!rm9qWLS`BEn|7-)|%?K^KzK{5$fn?+H_<& z|L`Z}8#9(SjNAN;ivo(wLLX&Pi_NB?GNorfJjS10JQQH!hz>}fO<#2tZBj+ReMxRa zZEUo&eqEZ8p=h5D;r}jX})(uaA(NDPUixHf4 zZQ{eDxTS6@6Uwth=xcZq#kO^}3xO1fXSmdo<-+aH7muiF3r%ll*L#(Xcs&|8Sz?x} z4N9w1I?h@j=m=7^uMM`>I@u)!Keu>91(rlPZ(Ab_r%a|oz39%<-t508D3@6as^Z8n zJ0O(qpQcCN3rar`c%$TObT=Ym{uzH$AijkV{}`JTqWddnH~SvucA`s^DjR6yAG)fL ztaSS>NMJBOKV-(Y5$k2!)X!7xdV)H{>5PS1cMw9=g++qEe)_1w)H5fjHJv_FE9E%* zC9bfX{?_Uh|CfqN8jZdnW_rtTz)Z(UA3?;>&4BCPDD5B7gm+*n6!BtCSx8xm!YoS5 zWMJvNDX7LF)C}gPdOmeI@8rcWgsN63y&Q5zH%n7=>Xk76A>2?cN5+>f1lE@Z-)#7! zQOz%kdqYJy@V72w`-&ZoxcX^{o~KH&5C@ZTp?`kJnwv(OkK10y!O4z68`-*=I`Zy< zNpE10pFp&xIm$+fJp1N5gH-uek9OAg(cU3R`4ylx(?Qpuew-|*S9J8M`cf?O%%GAh zx8!U!<&2_4QJEcTv|6Jcscst0=QpE%UwiBXjdaqcfQxcHsx!x~THU#X^vi{ z)N`0(k_ld{ShO=D)r25z(gbcJLXeh`mVfTj{WdyGs^5f%MXi`NMl}p>&2{BC^(9Wb zk5+-C!d8**ZQnyFz4B}}hTq$U3E7JY2#oKjl$s7XbtFh{vT|EC1h5;gcaxS*W|0QL zl`|v;2|_bfD6L~0IjLQThg1qVU1>A}gQnpqP$>soc3mCx+e!K`frY!9+D=I^G*1x{ zZK&Q`&{c02DGBn2!v42JK z%XK*9R*$iDxV#i!1N__vudpjYIJrT>9Ur?@hUQyXiwTZ*DSk{+aqlnf;KpqTHCOGA z|JnB!T5t*0BKr@c?Az8uBpRG5pYpOM{S6G?dLP!=IQH%Po3ro};NzqKu52rL|LoOy z%;|QQKYgiGCK>El@C{LZxgGl_)9*$ipxK|kRGK0Ws1*I+#Gl=k<^f(Rjr~K^9cjnB zR4R4lF@}-$Qg~mCf|p7Wf6(m9P*dRg2Sxg*s^5_0+qAR?>J7hr{|**ga4mM)nI@o2 z_l5HNVZZ~t9KRd#@1Z>Tz@>d1N5?Pyn*INo7O*whpP}wQw*Hq_un_$MM=z;T#TvoQQKPb%&y=4ROcfZvZ6 z|6%?+(filI{GYQ;#Vxjkc385_FS#BaTgHE|0K$mG*)foMf5g-4@afanXb*YOovge_ zds#IhpLjMoUnZ`@_@>!Pn`j(k;Gv8+w4DSHmEe#&vXINSZ(bF_sUl1|_x!n+-*c~X z_hR6=0sW7iE#_PhWrnfV?j;CI0z)KhU|W6R-S~%clU*T!34fb;CdtonZs33lW3Z?xA&xcJh1EK9jCST7EkD~|Z^)~&9}i%z(| zL~k7(t%u@?n`S|ppHf3RYY5z7S6T=P9D03o_u&q2056hx1?}fVcGWfyk5V zZ~J|H2k)USm|K5&{hX2Z`8Ky93NI+`Hw6BCOXL6(&-K^OC1@Md7njjZF2Sn?Z|m;O z01lLKJXqcmOmm3nmqdRKE__RO@qcWLhjrXg!N}pnml7IA`sR6_q9Mmo#sN0;5{+_vZGn1za-VvQqYmH(vMajm5 z*7g;1{LyL*=m$NM+d7#x zx9np2B?&e_?>aQ8Z6uz0#MQ(^WXbU$5>PV*#7k}SxcMZYK>BYeVDr@DfKuqC>4fv1 zV+KkeVz}^?F5Cs>+n$+3^A!EC0jFOY^tfUm$7v>|#`(RSO;N4AC}ZL}UknFgVP$O3 zn$5ZrIbdskyXNvoxyJkw*9L~*^H-|MAiCe!z^JPJ4Fljp8n^h88dbLMakd8|3Qz$5 zN`Pjg!aygPe6N#xqln`vz_kh@aT(=BW@Y7A(1hQhAPA)DAn8%$CJ+&IfP!A`ZzyQO zpr8n@_RCk#zv)=WDoZcFyqI$=MD3zC=A;-F*6enM8Pb}_;pXp9P=$r1LUo~C#2LGn zBUh|5R5k1JQOq-tHUWSMn=CuPq3~ZI0Y(p4*jZ1F%c8x7Opb5Ur9*@v2(T^D$%zRI zQ_)yx1=H_T;q2*?6~-iykHM*-7vYLI@WzM1ce*i)CPHn;PpgLXmmRvq=@ONg zvu&fN<|#C(MKgY>1Dag{3l$?&#>XV<1<#5V_0=B_x(0X)h@llaDWs$f@A<@UY(XR5 za<^i4L|IknVMf~dMx(5qW@)$d{JCRtj?$b#T(>u1pPl z(S`eS{Vqe~nRDcbPO!J^+5J(}e9E({)AWgN=DKWyyS}Yx!~*kPZkVtF8jzK~>)UOS zyN1RzY$v)s1o6cmf`I*>(ZN%Oiv3WkFQ*^=8Oc)vA80Zs2f}?{cJS;aeqX7~ z#-=In(!w>(O;n|FO$8=6H?Zd(Q`kAchT`#)n)|L;<|fwhDrMElYbxPhUoIE=hoTCP z0PVT_UXt(Yb;#&pNqR$bx(F-3>cxN6FD(*CjUx@+{ts!y)f^_r*It?F;PLT~pe#e3 zxLB5PQt4^s!GD&F`S`2?>`(2Zc+P$KyFEhW)eyv${${M(Bi-)zcfg{#CkDPg2>Cbo zT&!TxBZp>I_FeRI63?+p<+9CXrjVX^Wed9W(qmzKds@aRf<=T z1W9+L`&%6TWs$V2K<3DA-SPkTr(FZ5=z8XR{oi2i8Fud(c%7E=5vEsJd(@t%0ER&v zZQA$E!h13jPuyN-bT<1D&_zaCkKnEsvR@v3{bym{KGIa;D2E_3E8c%=yl=lBISGnS z4k%HJ-jw{=0w;FK)Ez8D4baa1akJwEx>%I#|72vlHQ+x?VX#TUE& ztH|6OJ_PZ*`6FJyLz_CI`sdpBb$l^+c{Z_kvS60B`s$I+a8@B+= zlzU*hGe1rzhnenW7u%1Wz%OYkv;+Vv(m!HF3}8j^nr7_Jh$r9;y?pCO;uV8RZwRD% zWq&QiNc>)JopIzxRS?I74Ge&sy7o_}dknp-w9j;6nCTK$uKx@vK7e*iKhh4o>ZD`q zog=hM;{GM0Of^;qA-pc^N0y#21)92vxbw>kssD2N-(3M~l2<8{75vDW(G0X69pS~j z`(EV>SVh4a(2nFs+T~(AD{-ZgpJ7Gqh_?RLj|$nw2e69ax0ji}hf|SRMtlfjO8O&< zT`U0oif+PwM!b~jc|~r@ALk1Kn{be*h~9s9Q~y=+9$GyZs^V5Fvpx=cb`|$xy)**Wp@iWOQ2Fyvpx=+8qK)+Lhh<*KF z44i;`6x=`d>tzD{w0IG~pZ<|QK|e3(0Hpw3Iu8rwo)#i~Kp1+>$Byo6(-dGH^Br?l z^j@%%r%ZboqnQtIXD4rw?>-9RAQl%LtxL!9CA_;_W+N1PM!t`cStE+U{n43p*bAyg zEk&0e;{S24id+F;PB~xMH@*;37Q}czXfphw1mu7JG*J8?VLQ9`lN*$Ersl+jcR#6DuP2z$Q#d#m{Q)=bB6? zVO1(q(U{Yv2T&M$?nW96;}lGvo%yH0Za1DMLO{1s3Ol;=MOEX!&wG3Y1a=X0^NpX1 z-VORU(ob|^y3x3SZjr_>_RI#@OOfg9q~LztXk|{GginEpq*AEUs zeE!n3XZ7l^?R46$Mnqx&;THw?jQ8g<4uI3fPc|*e*HknQXY66?Uwej}=K-e$NpxwG zR8Bcr3+|R|T4?~QBl&UI{_;QsrpyqT8dmqpDkjyi-@TOg;F1ZY0G#XlC-m>%0R%b! zy3Uo&OOe-EaJLeQdxG$#kFro zH73weEwyft-b-O=W`&KX^9|<2V2<`Q(-RcH``C5eD^09zck3 z=dBh~PAW;^TqE1vXyOLYY*Oz!IRAlS_Xkk3E*)A$TY~gn!R73-r*{9Z4p?4ADM_>a zfjtI*)fZ_{J#$Tkm*V8;9$@_dX7%5-gZqtlL-IGcz>>Ih$iCeYV)F_f%$6nF-H7j( z*1``n<+}zWc`zjvss3)b;tqi+sVbH(?jG$QodArbiz4fXtpebRqS@v41dF|xDT!Iu zcC*GmB#S!(G&tWuu5n4cyQ3~1LA$NHCpU0}Um>hv*a5^8@T{p+PA!P$&##HlN7!oBHs+O%PcXDV?8^=2Qd*#Pv(LJS zg;igK_Q9;3%iOxs`v^(27vk5O#s`Vc)EtAZMW5L-^7~jIT01p2Zte#tIL)3^soVqM zIW~H)i{HH*vQ+Zkh#;6_km|+}6Z;e z8smzwcZM5`iX1%|aQ*!d8rI6@Qy;|XXg2FdMqb)Cz8B}6`bV%ZAD)!>Di1IHG*13u z6^kq&2172$Qtiu%yYczOKJXHQ&}-q-MgMPV;2-AO@59pt6Nx&zDD39MpHCzb0Mv$+ zz!kB5O~4N`(hdT=ZBWy6>=v#4!1y}jb3LG1CaB zJ@G^P|Iq#uHuw+iKjF9kxcvtq`YNseDVg1#`);xNPs!}Y#{bv4y}u|=whC4I;>_b) zy?q!=&}xJIktHtbG`gR052jUI4ptdcU)jJ>PS*MSc+nf1-Cz2;WBT%3VB8(Q{a~Ks ztT6QwQDrf^eVw0AmV((EVWbfm-5oCd+?vbqg64HS(|@>e!w7a?(-sdZ?4Qru#Ts9o zso*o9`G27Qfd2om{u2iNpE8W+fTC9U;{K8m=ZB>}ph6{xT4vJoRcvj~)Krv9xxA+> ztp#v>9Vt$&>}wN2u7E;ITkh?E?RQ%UY0?#^y*hXd825`bejv68Ej^fpm+XlCzO($$ zUd9zm(2gj7ib=hCe;-EXgy-?d{F*5kMSY8cB#ix@44PDJ{fkQZcQ6rgFe%H?jjz|G z{(ff3BPtKGj*^D%!Oy?LeXo|9UjnyY{2F}UgxL3uLBi?`fJU;SaBpC^|MCE9c!q#H zzS`- zodf!b0sRtbUI1&2@UZ+d4GiweKME2M=RRYKC4R9)%)dl<2*WrGrDl3z5DK{Cw05}c zbcXRp(*SJCYTyq7PZ=|;`Vbc1RQcrm)EOby&36ONNUF29{GR9q3UW8`jz?Wtcy}h= z5Rs2)%(a{Cy)sugGQ2S!8+iQ$?>Te$#@LnNCue&Vq?P-(=Bo&5C3*S@m&Or+-uNUn zP0{>kRb3{z^OF6IFB=m*jO4=*hE9m!4=`QF^!(Zllg@M_>v$&ROuz9M zXI3{<-(ZcRvL?}E8aD@*Srctvd1*nIq}evA9PP~R5=>-d%cW&z3cj-OY_qyO*uB)&%+5#2cjw1{9QTf(PI)Urg^X z%5Dna-p6A6w-tC}}+Q!ol@mZe;Z@dy_h~^RKE_JlF+$hUCtR0a(1xL-9 zIEcF}MGpFvqfzC9vrc*>O6e-6kZx-wzO#Nb!a{SPN!S%jUA+ROncg4plW6&@b?^!8D_cDY+)XV(kvw6`fep=LSWLW}_p)rz&?Uo;@Y;kAn+c!6TpLr%x3p(Z5n1s5O zoAu?BUeXbn>CTStMsjs&oT1rxFWRecowxTk)mTT0B&qpamdE)c{1Pd@5UOwg5*a_5 z$8~_qb#u9JsfmAN8|m(znqoWMSxuy@Ab6N1gJw8fBwMl&km+s^YqD;;u5f!Wjap9$H`js`DN>@(Fb@>u3=~s zWQA7U=@O*e9y!o+7qT{bKfWWnD0>W$$=K~l8SOVL{ETa9!7dSb(}|UtT!fNfHIPdF ztrK93s+;%Y41iKHHn>>!KPedtl8*;OsW#md@$+{AB(J~qm@VQ@K(x$k;rG; zKk|a5MvtT^{sGfeW9OUqt3iG4R{3=BLu+1Hc_r7i+~Vp@EwaY!>((kl_7{xZ&IG6} zvbl$oZW!a3q;(vZ%0{Ma$yk6v)FF-1Od&oxf?qVwew-8X2hbew6vG$TUT#|v3lnm6 zDqE_N3SrZxj@@ie7Sq;z@D|&$jv+ayN!@vV*q5y)7a>Gp)KkT4J@&3Hm`QJWqQ$b} zAnrTBT_c|!5d`Q3@FpoXYPxNVW;-s84VU2)GpDRI=!a?&yP{TEG^fEco*Is$!HG*V zag=8F%wQe&tDg}q1VdfUI}5Ou&R2$A))$);D3i}|noJCR7P~UtMG_h)0Q9O}Z=Mmm z)#vxEWwU}|g3fjV%gxubO?z@~uOx8OOUG!WpY^|4?zY|AXH(#wS({neIZJflm(=`< zN~^!JY$jW~Ey}mWIyCJ2Z5Lh>xTe?)^gasJcH@=vFzhe3trnFM-d-*QdRNUwz&r7R zWQ0O7a&sCQSiap?&JtZ<^L!K4T`NS)^YOuT@q(H=^wEUcTf-zH@6Je@t`5*$_ql<0 z9%dk3&zz~6YfzwG-*T(-zPf*m^IVZ`9Ha9NMs^dI!BDuZ0}S56z#6=O5H40vnnV`I=Pb^_Ol8-7Hv>;QQ4MI&L89v_G&}Q;>y^|H zF;sz7LGk`$S{JbkTuB7QHpz$fb@f;cV;Z8(7U>|AZ^t zZMx1Vn5S18u)bPQt{HWFx>k6{vdk_jk6GtvC`z+xgs^mT z&c0kdO40bk%kzoQwuW2uu`X7VrA3c?d{bfhXnzWx8s~wf7?n~7%hZj$hW>EF7_!Hd zR<|X>F1^0Op%@W9Z77UV1x$vYh=Ol4TGa3lECgH`@#7Hdnxb~uS&9wJP%j{6OB^p> z$*>59OAU5t3RzTm985$`QG?Z|m#vcPBix72#J)e_iy~a-o3A+Jn|tp#jgSB_iy9$9 z812$dRmBPEavYB);pw-LFGckuGuK?IULB|4eh0jWGtxpE_a-5E5+ko4Q>;$c?MYShyjqu zL?L+(25*CiUb$|Ap-S)SW+}Y(bj^}{o9PPAL%w$t*UHh`i6$&m2|DecKfDqzNDP1R zMcX>c<7%&0l3?r?lj}B#`vOTdTs|&kDBpU#sc@xJeFTc$go?jSBW!kBs3xHpDDHEN z(4Y1?BXvyd0{R#R6(+ z++7-?cykG*#Oq0RCPal9S+!LP%4XI9qcQX(dvuKptrh8B$}9?RXc9ur=%$#}1u;~s z7I2&ODrgkX1E;bv0%mb5Zp2rMyR8tqpIzOv2taX0dcb4^?|`E+#j@5#)*-Okt@i>; zp~r4xG}Q2M<6IlFs%=4$^?0XX#^eaXZ8cZY2Ra3Jz)R@2tfg8pcrPZ-%?;CZo`oes zSqncsysg)wvCdfH`2IPaWxs8gezN7D>ss|x2Sj}QLm!cjv0!Bt4l@wxg}XYZ&H?>j{ggh2bZyYxJ%KDht+$t> zq^c0~RQ4JRR}knH>iA+L zhf!<7@Jn}FD3`r}E=wcB8>C!o;osxw9;d z=3E6DRVCLzaskl|b>E)8Px`WNmX74j%)xXtY9UY$Q%9e9IJ04i5wV06zyQeNI5 znPJqaK;az+KT`HL3%L9bxUk=~CJU_=*btKtKaD?mkJtJb$ zo$kz#?P7IZ!|?u#5CVz5<;ga^O0T2UBl%mkbdM=d69{iTSa`vSA96sc-YJCEwC6q5 zABaNQDN^8^myQb}pN_}6!wa``l-6IK7avZ8WY$SVezdo-OTIvn`I?Q-nA36bGq=eM ztV=7=P!4S*Kw&N|xS}vAhvx`0gis`?7uy&)M@-luf?RRZ&FLq+{~)U5CA@t#9b?&A zPx~0*r02O#s}DYtb{RgCjfA7@?o>0J3wo#nE#Glkc=hmlmCtderpjOgwW&0|;?Meh z&3Bv)lE^vl%al>K&KQi;214!4THf4DT*^xy!RWA7M-u`q;7zn<7%^N^ug9bTD2g8K zHFf*>0L}VXf=kNI*{6K?xv*JB) z^vivkmmcV08SE4PG2*k4_DtRxotfJhR$T8KkFu6pp6+7HH|>#2-+3WQJsDJR`)Zmm zYjMhzX(bbWl@dF%GIa`^$SK#!1Xa&_U2>l?dT_`$toriJ{55P-UjrZh`l<0h;0yB8 zw{uxA7JuQf@Yd^frKPb(lhs*rpExy+v&w3^^XnslY^*#g1q*~be5je+5uk|t@@BN~ z9wzF~hOlaqTaPt7CDC$tT_~XiYs_m0vdS8585I(qKD|zWo+n^8|5$j!mJ7ISCjr+J3-spbS) z*jaJOnR4`wYKe0wr<4@e#8*!q6 z)cVsr-tjvdvFIW#XQ1fZ%9ecSduBv?Rzjx8F5kOtXHvR8?3*~ECWN7yY7`)4VnsU$ z1xNY@ckCjIGfNak*B3^_&0wi7%pGzMDDhC*K!2abYll~WNN^;>!qud7a|)6eS_Wnr zVX<%&pDd-sJwq?W%(WCxvwcy^eS4+V(HhstE>YTjvn>N&UX<^$ChD1Y?-L1p-urZI zD114waQGS1U*6ejxvog(L8g8?j35M1ak7D2QE8yXZ)N8omLlDA1AR4^vEoOYHpzu1 zdpO4l;jMWUxQk=zYF^&Q@}24Okfun!`_nU0$uUmGsi}l%PGBkuqrvukx9!cs#X6?5 zjakd1Z0@{vGu^Vqu)BkaY*u#7Sxq4ZN2lu+h37p9-T|}ZtC{pkF~_a1Z-AJsY+3G= zegb>Za;x~rv^LslG6s|D0&MCC%7ENw)&E4?#PBf5B+C(ho?CbuoLdPVk`b21z&cjD z_Ft0kk9bT#LtXIEN<5jFA@m{t;AUrmF0cdDtjNNV08H|27`Zct)SYTiWd~3Xw|Yqn zZ`&jw4iN~*q!*aG2Gr9x=Ncl`3*1o!nB1Khhjza+vdpsiDsDB%{phtO++o+R3nJw( zkYLs<<{U%B(tzfyK5IFQat=s~oiU3Xc@!}wPkUf-|7yBDt{kY?gO)CeB}h9L5c(7Y z#pKH&1xxbJZ<^!Osqk3;AnkVAeMQ-w2qAQL_vpF3P zLc-&4%ibVNyzrZ2Okdp@nMABZxF`JDq=g0DHrrxrK`}E)c24ZoO=&y*;I@t|^Mgl~~acclFV;tC5cggbGOu z#l%}+hT)7QXB*yt7~@Jbe`)!N#XT(R<~&nLFWnO``Bs zP&VzUadfMS3)!G3_>8Vu*0W03(NHvUejoGUnRd!yO1mjDLXAiabZa8Kuic)_!PLsV z$2N>@6#KM2?9AHNLEDg?2x$C|8>-VI?xLp5@{;y%*yPNdC5i_W$WA5MX z0{F&J{~Oow|0V2k_Bvd)KBskj(%PLLA{?yL<_3+^~5d^D`}MuHtIA`q$vNJes3 zk5$so0xdca!zu{QNr8i+%TxUvK7r(?t3(O>?>ZCs!4GZTDBp2?V?U^st>2d4U3%9f zUA>Gw1iq}6t_6p$qr#?16}X1V7y~Ks1#rE8YtsadOpEo+Ay1p14?(Q zehm%PDNwimM9W{Guf!fnT7-mGBmBZSd@W#>RXz^OFGPpmqL6`LGo|Bwq|3vjk0o4LY=3HrmW~M+cPp1U0&9Fm{e41bFCs+NFa`55 zBLl7x%l)#y(wCesqg_pb-20ZV%F8M4VIZy~NR@(#~ryvIjk#YoQiS0>CTkQFxX4 zW9a!!JidbE`33}!{ySF-mq^N=C_0)kW>WN=IV(sEf0 zt~4}aj>)~8Z`!MbM9m}&dI(w0FSA>AKONCqPoe7>tvv0c2QneE3w`Rb%$n9e)2aX9 zksK7>7)vDLWR_jA&??Pwu#6nI*PV6OviaRp%jUPZwXfTfh#sk_9ujn3^=m^2aMaK^ z=(wPLD0D#ryhr)VZ4hA<31ql4#BjCj2a=YnX^Qn~T0&gdxdy_QgkxOqUyWLBs?zQK za7GMi)qQki1a{tJBpjC{D3kZsv;{WtIM|*de`YhX)TQk%xHI3aSB-cci!LQC_kqVNR8}csP%Brvi6))547--(pa!cn%{Tnk>%^pnMCZ*QaeuK8253cDW0vsTgFL zajeXKEMl5NEwn8d(>++}EEh;?AN$}g#6m1o{#Pe-K&5zWukHBA^_Ub36}s2b%lCa%3H7iHV2+9*7u zjx`Io)tFu*Lm0SH#f97XeZ%%3GfDR#|clwI6K_+BjrfXIydB5T@TpFZ)89yv3ZVWYG5qv z=UJRMyu+;te}O=8@}-#k`&uPOPmsCg%mf>*x)W)4xj+in*x>`OXU@@5UYKl8M7s>j z(b8foeV6gg>5-?Zxl?7G3m_{gPr#o(z(o&Sp$Re1L&^S`UH$R)f;8$p_AUl+T(?b$ zbhXW~MeoOyf=Q%xXRAfjfy`Oo{dGoVZ^onx!0%OjRi!o%+MBH> z`cW+`#v;)3(8%*Uuf+Vzr?-~$2BLVgfl6tGl~P8bL*2=M>lpyHh6#mJcLX}aN!V;p zrrh$B9a&`zF0-y}3R?rap^l;_S{J?TKhEFwi{^aOE)natQ_Ot#%J;zhOLK*%6{kcg zPhm2!khEy9YvwX>orbX=^@wIGPEs5%fLhai(L?U~iDHRQ*0Cs!R4CZ3dKav$MaE&+ zDw4b!w7)MLFJ9=0>Nu=|XC>2p4 zai?@+pWXTkQQ|`?s+z*^mCl*wm;;{VCnr=@$g>8iUBxVN?#_;N!)-+;OXG2&<(sz> z0)J7T5$A;MbApPTKH5eOq7?_?o0YjputO;kemN14ZiA>h-%PzBn!i9n2T$Ae^H~~( zXnxw}9;^`B8Pg$hV|S2Fv_uN7wL8b>5#Ei^uLGysJu?X>w2x?aY|r+{h8?U2zx>>H z2Fh6_OXq4rM_CRF_Jrx-zmG+k#6cfZyIhj9;_SOclq4&l^RaNI9aDRx`B#?yNPahL zx-k<62@_qOU^up!^yHU;PrvA=4rT3(wHyOBNbOJpK8P`k4-|zIcjw!hG`J3VJ3(#U zfBvx26pazyTvo>6SkYwQ~R|;Wp0lQ}{ND(Cu-4 zZGK~)-u85)?&xrr-u9G#Mw-I->T0G@q>Z*N1qEthH4HYmK#D81P37_1? zAr>mzB-|0|&f?-GHg-1yx6km-@|!~|3(F?+Y~D_F7n)m_bxG;v3|>VM*6Jf(Jks51 zs~`5RCGIGxJ-U~cyJS3$A8+PkQ7%6`-@fVVsls?QF~`J{m(oi7$@saQzy@IP$*XjE zHg)QQJ!PM? zqa*2n6396Yr^E*a6ht{0&v5A8D9b13S6yGMU+6v?_%6Mc8ee$X|7a1|$HGDgtXugB z+bE>IX2(UI;b@8=4E(s^GO2d{#4SHEr4&6Y%1e$n5fB{Tey07XR?DH}(&n0!D^qz-*`2P9a6C&y8wM)Ych*GJ64zP>GBe`@kmt*Z9xC zpAS0Md?wSwN6H*ImF%H*wy&v@*^J?0Xw90pC@~RMbt5`(=d4ut$uof;%iCKKJA>D= zQ|8C@MCfdSHzRBsx=DqMZ%?9ZvQ}Zozsl`TR7^3%!#xz4c{Vn7jkux?p`svp9!k_H zC5euU-G1a4QDzJss?zH)@ki9WEbD#;Dl5v1=PM`ruFW%+luHl7*~?Bk@fm|+A0$ut z(-LH1yy@K}Dzn?=bFyKtryu&V1stmJuNO11jGdE=1=_sEeS7|Kw!z92ajYfqCe6;M ztZyx<iGJH^pG?>8H%5N>_R>emLumJNKNh>%flJKyYzg0)=awkqbf9yj}&G|Ct)|HO>+h{ zBbO)@4~rjM6|ilTlD`ivhv(o&hOwPNUFy!iIA`->gZ==p%_dcAjW^SiC^1{2Ae zTgSsVE>uI&CQ8gP>h7}(W8^*YCuf}MW2JqsOAB0bT4j#o$w};~H2qoXLu4FN6>6=) z4^afyIK1pIH^GjGjfjV36hhUHnzAty6&7EOyC3ViYSgKNu4Gmv(XzS&?vH6VsNz!1 z#gTXT6x1f=o7==oU(!Cb-9%-<>fWKDXT(tD3o@n`s+1?$`ZK)s&qVgtP(KjS*s^z5 z*3ivPb=CkcOS@tjT}a|DU%PKQsD>^_Fx*>q7h378oKZpD3cT=;X?8k$BRMJBMJIO% zl4q)&R+P=cA{}<^&Imq-3d~MC_pj{@Q7Ln7myWCq&dAnSjmsP-1ZEn;t6By1PLQ1% zci&=R@qv=(hfWENY>Re94y6qV9!CtiwKM>zjEpKdA@XXK&}18|}GltIxiJ4VSA_UhoO4Y*BlX5pFrSudbNMoFIHL4ydHz-dhG;qjU6`GUb zewR2sDW5Qo4;hX0%k$ID2agescqrlWorZc0Yanx!Ko4O{uWt0fPEvw|Fh@gj(9Efl^WMaPg zfR>8de4@kTq<*&Xhu-1SHbxiXuXQ~3r3xE#qilHbI#R4y#rXg2Nmi-ANCcCp*UdRkV{ zogImbUuD`#D&IhX_j{9w4bQ62&&-u0R9*dNw^tTL35F$=4{hBYx~ae^c6xCgR0 z7&w-$x4%>=npnAAe`oGv5hUyIy;xFi-3NC~*H~Gz!wh)E)gyRkJ_|=G6S9`+v?adp)Ip{IY zNxW!az#+U>kNj99{9bPIf?|b;oA*@)y-Bo4<&#P`EHoJ&1Z_G@CRUc)5(!P?Z+L(T z;+mnyG}LWLlD;p_igNe4nZPx`ZcIb0aQXTjSFmwHe?dBi^3?^&XA2{>UG*fK9Rs7@ zloex*?i#%3K>=FHOiuk9i@O~ersC)%_t9WwgXG3J8mEcZg$p$lBgw+sM$GE@=>ug9 zYnSwD{H|cOWx-aFc6O%|52saYb4i9f^%zmkQDqIwo2jFi!iSX_+U;*l!B4my}>sTh5eUXz)acDEeKqz~t| zdByZ%03!$&Y^$kAX2b{hcPKQOV5y9@&)hOgtWfA@*;e6#NwRL8Ob(CBVc9<5hAtWO z-|(hsDOTUaH{Av$bclzQ1hY{0705a&(B)if;Pw3I2d#H3QDz1D$%(Gw3~kG<;%h%u1lX2A{bCbN(1B@$^ouAB&dQT?nUj z2NjQiT+}Iu`8p%ZqxHVN7{5k}e1pKy)p~6$Dr;(W%D|5+E>W&SeytKpstz-wh<+-D zi|r9+l5`@=!aPmSM)4`M-*7ehtV}UMYb`8XFJrtF%K=>JCuMl`BuOldw`_0SovcQ~ zcV1T_Elr%Jy9Mb^R(XGI7WDb73Pi&lFYqcAy^@Id(j^VTZPft@Xvd#CJKs)etqTMzpF&nmpVB=|I z3IZKqX@B*QgT-tb4;`|T9q7tzUO40vV($M651qMF&F01~C9v@B7E*F3x%goAD`}x3 z(uYME^A-zTTFzzypv1{vC{!~_HSGQ8u}BM6A9#*QMnC#9z3@}aR%!LTiaa9Ws5E@D z16p&FM6sqkc z$QLFEAvpCi{x&%}cS=fgL1D>9cd#|=)5QhDR1~HCf;Ye17xevT! zJIdOw9mh^AaoK{qvugLdXnLn&r1_=n9L-GYB7?3O7=7xh=@T|;J~GHvx1wu_iK}l(YDc?_2lEL?Y88~p^(}rmWZ-?<9b80 zm$4UE7sy@o{aXwh^H=)&@HGxzCI3uhNQ!E+qacfdyI6)GJ~1J9ZMM;_W&xDlj_-sL zpoXXcdaSycXzym(Bd;3fn6Fdjmsr~R8}<()aNcv z6PZv}%`H55tK(QxvLs_H&dxFjwa>GXkeCp{dj z9Z*BHGJSgJ!SeJqw-a@eT|?tXk6jnR=JbxvRa|{>P@F^02!|PBL{@RLh6mR@(x9@{ znlYByQU&Vf+QR1NX^^|P=G0`_?{7icv^w1o?s!?7H;^ASm3w#Fsbxhi1AfXgZIsyP z2(8Q&tcYwQpBJ)|Yep0aYelmOuCWHX!5P+CvWJq?NY8IAza~o^_HR&$=rHnqK_-64 z0Lx5mK$_#Ssw4-^Y@o2I8S#2LSt;lB&<bq^^t%#vX(&kXMYExv`)N8jc*}+sCk`3WYYiq^ zHHKNwSiS0Ry3axpI=756v8L_HtywL}diCNpa{=Wizravz%41yU60X*KQTbfI_@=(s z7S^ikRebjOmuk6eBtnMbubM|l`uhBomkwavWP3hHQ%;gcj<6Oy6uit~alDrolpHtI zip0Np2@Q#eU{uz$4F&;y8}*x>z$Q>cY-DkcsAcg1sCfvP?JJ0U%T2-kdFLt|Cq={R z!+EO#$MK$Vr|J7TVY!o=dCO5DRO#}`0q<&S>zziHEDV!a%aRO~H$jEA{<|lqn%b6@ z38)1wl8|;JM|m1J5k$z|sYgu0Iy2M_aRQ81Q{TNiqj=kGYeAG_zP1ffTyaKPfR6lj zf+P~1Q818m;wiOtNM5e)iJ5}|0THRM23~b+t|8e#GEYlCS=!H zAgP}AN^LKnlOId+)FYQk86YoA3NKye>*c&}vOYhwxLD6t4?+WzXk($reJxy$q})d; z@DAy!M!k6xuaq|6u7HiSNHp$%)VrK5RzlNF=oKtTzTK>y42+D)wMS4}8o*oG{5At& zS$%XPkrfs-BodmB+^(u^)Y2T(w2I9(H-rp7@v@yu4m^N0M08v1ib2TdNYt#ck)zmI zqpsXkGE+ski}^`^+nfC)U8%1(nsdCS-2+1t%S{Q7vo03gBs-`X@%sH1pW$H}Gp)&; z0_|pjr>K|b==nuCFG;8;rnO*4>r!`_P~&Hviw?onSgnt}{5ixe)8w9^eti@J#LxM` zEb9Zk=_$cX@NSmB386z1;#EkP{t6!DguX|Jod%aZ7N{LMyVv8oW5KJ>-Gj7t%4 zV#{djT&G!;fx@ArU5y>@kpE0E0xl1t zK6pH!dQf7s7vpD)jeFtx;i;0#BwbZ)5}qq)T3V%S zKwTZT1ln}k78J6CdrA^fT2e%C-l`W&tBQG*jo;fPp#^F??6I|t9(<&*D7}lbqT7TW`80~DFZOl?n?#H{> zBlv2vdD?C}yb1LQv3_~53#k+9x`wkDn~f(;%h7+tNj1OC<$e#(@fzc{vZtI#8N?BC z|2pftRBhW0owtgLh;u%V`@b1o=hR7!%XjS;Y7xzn%zZF=53PS#Cpzr4uFU|^{EU7Yg)C{SVM`8qdGA*ynX1sVjYEk$S+fxAh>bdHYQqB|)FvH? zvahjqU~`?_poqvMBPLc|Os3$F5i0+D??Dx(7@WXS5)YWY`q1HE>>PudUs>Gs!+dr# z7s(|OdSbAQ$n$i$j&HRbVA7U+6bCBw;jXBLI5MhaRln1xPd7B&hBvisM;y7y!GD~b z^ZoRtXmzNtPQ?KZrXZsB#8G^37RWS&ZQ1EfcAIKv7AS>V1V<2V2Qw;`p0Mc6OI7Wa z;W$UdXY-zN2?T)=i8G}!Cx@HB!HgUEX7$_0CbZF;p=2IPGADzn(c{8s2GIb@7vPvl zt41|AB6LAbNcxdKp7C2aO}gXCiUT+V(a5EOib9ZIOgev_t5EDvFXts@78c1SzG>z@ z#aeJ|sLdej)slvx_8?TPWrR0*4xZJRM(gXvEu6o?s?59I|Co%kBmrd z`@^;zT2if6{!PSUG75F4>>1v6lLtr3k53v>sJ7hyi8F<mIIl#Aezc~9+<6aV&R z(`oNB7bNB>UmEgvuRXGUqpamvZ8#rntzjE)9BzQq=D}>HBony`<4g>>u$(+esT9KD zDw!-1KD?9%!jeWIZsYcgEo)|151KhW%v9vCR5b(9vK_CON$e6YWkA!$wONw6Cm z0ZV6ouVEtQ`ocEVJNn|U1mV08s|X#IE*R9W!sD}}q*t{rAC z)#Vo6za_lGA3|YDJRzu`pkFNYVdV1*uJouI1E?n3L#F$CvtL{j59x!5Pb|l;rOKXN zv&N03ccN^zhHov3Z-8TLjm$SdP22+MkZbOpR~O!dC#B~bwOxQruM`2}Yp}rs#VwmL z?aR+lO>5>0()I|&nZ0r-D5@tZeW^@v4LPU{c9gGjh)mF+*B=+q(IMMJa=@Xzz9n#E zrDhJ**HZCUBcA~*oWt;0$XT2G2W2$ryeqDl(q{EYY@q^^t|n%R3Ca}f`{6g;vEr8n zqT|7_kyXN`oUm1?_*Y?J7b6rUB13MtfJ)c~csZJk+cc~l?9fwYdTLD>!5;9dE%{7` zpY|Gh$*i~Z&f#7~Yo5~%_coS#`PD;418=-JP1|q(AIiQuD#~sDn-&B_R1^>dMY<$L zq*0_LC5P@9x;sTsLO~d0ND)vvhHe9-hK8X*r5Qk4`rUKyJ?HBA-E-G{|2gZpaLMyL zd+%@UPjJxWkwMZ-AGuf9&_l@=6jc=Yo7rqhq&Y7Q&K!`s85C|R=ZDA1mgK3w&{=I= zsq;8Fq%a=b&AB1U5^>?i6~k&->w$|>jOBzCbm>~TrRrTG{7X)Tr~&7Z{FJI9*2Z2K zZ`3=dQqP0kLfrf02y{FVx1B;vomR{vX#XX{+R)^i9D~F-N|Na}X?NVcgUacIU)~C7 z8_%YuDScoIP46FzeU~jNB)y{Zo|}5Z7B?`Mi{-o(~07tiI^Is}6YV=hAtd~aeE-zz%v$O=D-UVt@==`b& z$uQrH41vo_oI=LF&O>&@Nhb|=!40_+;9#ka%q0_B6I_Np%(t7>P_R9W$pv=`uH!V; z^5#q$uR$@M^H{2@OF7W8D%DR})fQcl9Pnz2s1iOeAf%veRn^$5^R9`~efWF#-GN-W zR}WCB^E6WPF!826zuWqkdL4`x50H|n&Cx&W(yduIdS_XT7YWOoHSD7~zcnjXQrm*G z$M|?o^sXuqS8xAA6QAd$F@LuLU}?f{_m!D{3AXlxGY<7ecy3H{(|(>Sn(~?n;P=_G zD8u`3BVb2e0NLMsy330sOV=9jnWzn~<&}__PCpf7g5T_86kN4HG3F*`7xs3XKi^W` zL%LTBLl@&_tk4h-e~JttuCTR&6dasyf!4a_KpqOVbH2_EG1A5vNf-AEP0rHoxrtMt zJF2uN10n86eyY+OY177Pto$YYuFXpWLHGBDmxs`!@)I-fUCe2GzQ<@xyf;^p#t9kV zeVLZX)a+kpUNu{P5%$Ts1&J}H z&gD{n>}`b^K(kG9*J*q`jnw2D7jND^T6yMoz&Tm{+5f-o)<<|yb@o){u(S&IbnX0s zh(IgjLHS6HX3%V6?uS_2-6HGRv@U}3b&gfsPAkDFT%W3svXZR>koC34fhW5<{c>P` zT7ni}K^ot4E6~a;^!UkPppA(p9ce|S z-EgPJ^Cp&%yNj^5dc6&h#YR?E))dPNjWJzLGsTAWm`|V=t2#*Qp1K0ZFHd@&SU ztM}(Z<_sgW(v}I~QSe^>OHbPTcpTGqg47W2rGl9ED0*4DMi6ur5bFHu&6(PH8Zs3R zO#v<@8<31n@QU`Oz{12czf4PGBF!yMRNA1n;cd`6I+VaK75r&Enufm?%)NS>Jbt{% zu^BV&u6AKff&)>hfZrgf|2SvVCQtoc{!l&xjK~k-pu?NV!15`%tP|=qnjXdI$i_K% zqV$}oCA{8;NS=Pw=b@+L4!u3y((dJb=0!h6my;a1rl@a>f8QBc%C~e{UFqibOJA*} z?@mSNEdgLdB8`Amq<~cGgaO2E=;DdN#EkZv2eWUO(&veaFxpMUd6sjzsu?^K;yW)3 zb;~~9$lV8vS@N2Kyp5L6>`Ans#5)R>u2W|@Z!U@Z-FIMNgUF(cV8@{(HWw*q;$0m3BdWiI5G}CeQ zF63v{r!%v@xS`00za5(X@-n3q4lcpP|M;KG?(>2=Q(^ z+G8rkg75G16D@9Mc9eu~1@`%*%`_O|jc?d$5sP|v+(kwRx{W!n3wftquO}k+JMEoM=I5pzG1Yt`##!)Fs@{0rI z{!quhF`l}mTeW}&F4NU%dmvRLn3FTUN1Xw4nygX3Yd>lN4|KDla7{O!JON?{`a5qz z6lbVA^jTC=Z9oZAtc0{obM0i`iro}G0eG)fu`BQ6;!Q_k5y;qU6K^ca;kiEiyW7r zGgivi&Jjl?{Q0!f!m!LGe=P#7Q-ggf+v~%t+8*5OkdEd@=EbkhkIJih zB#rD+J<(|q3}lEMVn{e?!X^6q?o6$d;3Lf>CB}e4>_;%?Per7hIB07I3Uo@u#%Y%Z z-pWw`c2%U0J>WWL;qr~&+I5QrgyJr8SuOSeo7vG6rzUpLI#ucFz1Tteis#O6^E4m3 zZX12vWq{=AA#)unfH1_%Tu-a50iSIcwzCqJ7N0?|CRjUdu%o(jLL8QtStaU4x4Q*C zoz@AX=}0d0>?U|Q_Q^3y;-Uf}ONB@SX*jpuEgIId&6*3;*Vjbco!1WY2rQZg2&?yX z_doh<-yX8jxkhlEnDJt~l%}Q+&Efv3>)6tu1qhk*-}H|ZT5UFPHQpGoRarTXebD61 zA>jPdCV#R}iBY=En%jv8pIBS_h)HHrQD`{luO5#nDkTs(&uzkwG{XthvPZH+O*js5gA2?DCZXKjlPy*UYGEkHN4kz8l zIoP^<;0S>@A`WG@xw)w=#MK+O-p=~W)F-*C?RJuZXEF`0-BeLi?IJ4K+@UTVGG1nM za=0g<9AhJc9v5Y*_-;_m;{pb6lyw^LaH+8??L9qlOy^=12CH_nL6RBRN{}6xuh2Ga zeXq$3wHxU|mAlVLDW)I5Mxs2{CXmecE8K4uITLnXzpqCAT_xe^5GON67|OPODDK!h zayf`93(ePD0_i#{p>j_x8Oc^QTpq2iJ+J`xDDvw z{Xy4l)gk6*&Aw(c_5m~&ieM~{5gqalBe~2nI`MVI-TAgP2{XR7ib^bYtZJkUhKHCy zQ}^dbjmb0Ol@1dl5_EQIDdOrR%fM>=fzkVKM!8_feQZ@S!@Nwty41c3m9ccx5=X6Dx8<6pmtEhXMjU zfJ?;`cWZU|=fw5|*j05P>YFuI>ReN5m2pp%-Z2rY@6zqA; zHgBE$Q!@G$Cn@q86$^WBq(II*~wFKrH|!$On8=xjPva- zV>R|k0liewm!4p|V9q;VB&bc~@M{kZhx#QrnYfjz3Mb8-^wXl<_l1te$@BFhym@Zp zp4=}!O0Kn4+U3{Ro)2*N=y8DJ^i=ZHt`bkPf({P63b||OwL4z>p;3-vo;z7L0oOmI zIAim_6`DJEpyxpH0$lF8wa#A8CD6xkaVzM|yieBxoYmTu{DNbnU2wf;r_ENT!WM0> z_%0)`4$^4jJPKH>)=uXJ`Y{&TgVa%=ZGxtRZ(*Wa*%C;f+{qKb0ia~POExqghlVt5 zhu+5tI!=!IXH%E4Gv`nSYiSte-7$QWh*e) zK4y?N8(VAMMJ-CSU2B&@6u)`A*fRjes8TB1BHD{v)yyn&@YDgfLtlT>ed`-VR0WIu z7o{?P5G(}LL*p-LtdS1cY8vu1AGzmE-T5i?9x8irILC|Z<#RqNj89VU_-)%Su1q6E z_zEQJB_X>h=y+dcd`|UimI;HaKA!r|@^yWi=&$VzSLxOJ7Bj%PRfE#?J#E=s(pe}s)&28;=eNoqZny7@XK zjvkhP%kJ5H+k->%J!6l(nk2P?QECo27T35*mfV37if7=bFp=E*Mrkpx2?`p4-blH% zs!7ms(D6@~gTBa&(s2OsU_*T98cW^(I%yn#)MR#t2hd#e^tL}+=h6yg<%U!M@vgB? z`c?L}y*Z*xQ+%bW7uP1TAJY=ZgEy=u$JgjWg3+XH=(96R>3{=2QL8SS25Ql{Oa zD;q5bS@PD19}Gwpd&oYo{rovp(%aE&s8oC-lr&rVoyv&&O>uOQj_Nlr%nw*Bcbibj zhuAo~0jF6hy?ajS<|X@%iTPTV!F+q&Zas0&sg(B5+_!jIj0m2qNQGqudOO9DHE)pR z*??I{F0Q7IxHp_DrB*?^XZjn&xpU$?r`9w0W1^}Aqs1yWLNsO~ovq9Ibb0NHf^a+w zqT{{nRQ}$k!0Cuq6TPHyIlmi{W$YY%n(KU*LRSyDWlLYr0)jq5F+iv5N9D%UBdas7 zM7rsXpdXrVwLRvJSC2*|4)I4e21h0)!iV)lzJul)nS9b@V?r=a-n>Yb3?sl z+>^L_e8pBv@^0^rWYH+6++%@l3@^9nxL_m%f41;wtW;ECWKG1*LH4s->wDL&zB~J8 zs%cfOCE3@0oUz|B+vLLgGqy6mkOFw1*7LH-Vm{?qVW47@1Togs$v4k5+%W*a<4?{$ zaAKY6@)XFv3^wG#U)8TwO;`Y)an>fuK_x&Cb$it4S34H3IosP@>}Pr*b_+;G#0aNZ zeGmclii`ks7c(|kqJ$-5@2zIDVs9V<)2?-9%3pYxZ~fT{E4t;1y>zJl2}U%}tq8A+ zdKMlS^aY59jrs042eoPU)wTwRoq4&tl*F!{l+_)C$5+!NW1hosv#PjT8%yf@XS&Z~W8rYwzHxTLy4NAFo>l1Lb~KoLFge@w zsvr0aToOKeDxvXp&e&a6D>A-m(d(trV#Q_D%L?-}bsb+crLaGv9{(T`)oycAcg6hJ z(y@^RlC6~_J5XkvO8?oiNHeS;(EH5mErDTdW!G+@;L`gjA~ zp3WT+4I7JyE(yQoL5qHQmHv7=JN~z9-?Yh=qDl4BUH9VnNknA{FIvn!*507*@d)=M zk6%o*T2r9f&{lk7OiN9A+bRu6-;YPL7)H8I>DM1cg`38wzUGh&YIw-gRM{*Zkk&SA zi7gphdYCRXe+|Y=AsF-6mt|Xw>x5 z{PEZFxG1xNDf?}Vh#d3qKHSHJx=mEWb9uTx&V&{KWAAGku zSS_0!QL}r2^`0HPZ9-2zEM_(aHmsBwFHF}73_EF@Sd<-i(OgmWXbs;LTiviQLx;+ zf$64uJ7=6gbJ22b-+v^tC?mS1c<1}NlzOjA!Ghzfnx2@4T`PJ;_oJXz7vWY*B6bPv z-JR`M$=jb5Y3Z49ETW>{;TrX{+=8XYA7aQvx;AcfUe{|8*>iA|xWFLK7n*m8`QV33 ze=sW_UU&ZIwn(_gkO?>Er)0>?LZY9=81J;eD?Rg%2MrD=iDUuGL`r*f>#nrti4{bK z;j{`U(`)26#cFU@>(yEgI5E=+)id?SNu zXp*1$tQJ+@1_MXIQvgNE0Su8Xa926Sm4e2q5D0$dU^;jvu|PwWcAO`xHN>lH_}%RY zjC~b>xTfO~whz3EpD3vQDCMrh!;SgOP2ppJEVZw#h9@nD~gMPDx+$zDC$8B{Go zJqXy_VIPVjSOzn2<%tb^ZuL~`E%EFc<-dAcCU;XZ3 z7V~)|TCoX1gkx*ciy)ksE1{D+#it)3*#G<=C6)00$&+F7AGo#?*&K2-`LwEH646)sNORjuTC;HN16 zeMRdw717G$sixt4w`UrCnp)h z`Z7bJX(5V6koY(og+_Usw=r|9w3+V6&4VK#x3b1;oBTOPDG5R0_wMam7eZCB%_mra z$JoZN8+O`!U`VqSLhEkX7#MYp_iDf#dS_dX{K|la9~dG25E(-Bu70>wCjek?zDl&E zReh0=elnY`m{E!XAc+AqV_#FiG8U2AtSNU)AEZCL7q>2t17GCSBdXq2&bQpXz{-N4 zEYlZqf-7GaT2N4;CX4A2@0A~}9*NWz`y?MXTIOagV4hH2>rO7}g?R~S8{KUay11GW zLPfc}MrV8!c(BZhEHryz)CejA=VqeJljg;AfiAW^enpYHkStz^#vsy?w!fr4CO(|D zFgL{b90lE-2zP<37g!XOJ6`g#Uir>q%+{Ae<$)fyqmfCrT{P%~^Pr6yO;PvY3082@ z$tuqbduUace3+i|Y@*u(lnLbfE6Z}LQ_tl+KZQx2WBu^$@_D_e~vUnPG%17BSO|eO`E^$2~EE@mS{> zXo6Q9J7e6JO)S<$ETxq4&=poq$wTfgho{hb=Xx3&FR_D%&pzHV6uc&##^IazhZeve z1vl6|A(yykX@(L^k1yoUpDViSs-|?{xXknY6D~~ktGMxWb@D^uKriLqZa6j5r;1^` zpU7LQS3t1+^IwRx2?>?hW_#Yc4~ir*qkb|C-lm^fCp2}7m6c|LK$vBIMBgd#jdX;f z43l0YQK@c5tzRr}tb<{O)r&*y-Mq2?W;mUwXZIq%;LBwILcJU2Rp+_3;%0D+W_E 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̺|c)_4e} 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

6M9JW6{$|G23c16-kT`NqgTP9 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%0A!9bJTQr(*jpbfdzbha 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

*sjS)olf50lR5GL}b`)_E4&u>v7-N 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�xwr>SxwuJeh=7GpVtU?bm+9 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 e*#DcO7VFg*iD}!PLB_ej$K=E*!=mGu(0>Dt Date: Fri, 12 Jul 2019 00:15:15 +0800 Subject: [PATCH 006/139] =?UTF-8?q?=E6=9B=B4=E6=96=B0DataSetLoader?= =?UTF-8?q?=E7=9A=84=E6=96=87=E6=A1=A3=E4=BB=A5=E5=8F=8A=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E6=95=99=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/fastNLP.io.data_loader.rst | 7 ++ docs/source/fastNLP.io.rst | 1 + .../tutorials/tutorial_2_load_dataset.rst | 100 ++++++++---------- fastNLP/io/__init__.py | 1 + fastNLP/io/data_loader/__init__.py | 2 +- fastNLP/io/data_loader/conll.py | 2 +- fastNLP/io/data_loader/imdb.py | 3 + fastNLP/io/data_loader/mnli.py | 2 + fastNLP/io/data_loader/mtl.py | 3 + fastNLP/io/data_loader/qnli.py | 2 + fastNLP/io/data_loader/quora.py | 2 + fastNLP/io/data_loader/rte.py | 2 + fastNLP/io/data_loader/snli.py | 2 + fastNLP/io/data_loader/sst.py | 4 +- fastNLP/io/data_loader/yelp.py | 5 + 15 files changed, 82 insertions(+), 56 deletions(-) create mode 100644 docs/source/fastNLP.io.data_loader.rst diff --git a/docs/source/fastNLP.io.data_loader.rst b/docs/source/fastNLP.io.data_loader.rst new file mode 100644 index 00000000..9261fa5c --- /dev/null +++ b/docs/source/fastNLP.io.data_loader.rst @@ -0,0 +1,7 @@ +fastNLP.io.data\_loader +========================== + +.. automodule:: fastNLP.io.data_loader + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/fastNLP.io.rst b/docs/source/fastNLP.io.rst index fad05a21..33afbbee 100644 --- a/docs/source/fastNLP.io.rst +++ b/docs/source/fastNLP.io.rst @@ -12,6 +12,7 @@ fastNLP.io .. toctree:: :titlesonly: + fastNLP.io.data_loader fastNLP.io.base_loader fastNLP.io.dataset_loader fastNLP.io.embed_loader diff --git a/docs/source/tutorials/tutorial_2_load_dataset.rst b/docs/source/tutorials/tutorial_2_load_dataset.rst index efccc14b..d964d8c7 100644 --- a/docs/source/tutorials/tutorial_2_load_dataset.rst +++ b/docs/source/tutorials/tutorial_2_load_dataset.rst @@ -6,7 +6,7 @@ 教程目录: - - `Part I: 数据集信息`_ + - `Part I: 数据集容器`_ - `Part II: 数据集的使用方式`_ - `Part III: 不同数据类型的DataSetLoader`_ - `Part IV: DataSetLoader举例`_ @@ -14,11 +14,11 @@ ---------------------------- -Part I: 数据集信息 +Part I: 数据集容器 ---------------------------- -在fastNLP中,我们使用 :class:`~fastNLP.io.base_loader.DataInfo` 来存储数据集信息。 :class:`~fastNLP.io.base_loader.DataInfo` -类包含了两个重要内容: `datasets` 和 `vocabs` 。 +在fastNLP中,我们使用 :class:`~fastNLP.io.base_loader.DataBundle` 来存储数据集信息。 +:class:`~fastNLP.io.base_loader.DataBundle` 类包含了两个重要内容: `datasets` 和 `vocabs` 。 `datasets` 是一个 `key` 为数据集名称(如 `train` , `dev` ,和 `test` 等), `value` 为 :class:`~fastNLP.DataSet` 的字典。 @@ -113,21 +113,13 @@ Part IV: DataSetLoader举例 .. code-block:: python - def _load(self, path): - ds = JsonLoader._load(self, path) # SNLI数据集原始文件为Json格式,可以采用JsonLoader来读取数据集文件 + data = SNLILoader().process( + paths='path/to/snli/data', to_lower=False, seq_len_type=arg.seq_len_type, + get_index=True, concat=False, + ) - parentheses_table = str.maketrans({'(': None, ')': None}) - # 字符串匹配格式:SNLI数据集的文本中由括号分割开的,组成树结构,因此 - # 我们将这些括号去除。 + 这里的data即可直接传入 :class:`~fastNLP.Trainer` 进行 - ds.apply(lambda ins: ins[Const.INPUTS(0)].translate(parentheses_table).strip().split(), - new_field_name=Const.INPUTS(0)) - # 把第一句话的内容用上面的字符串匹配格式进行替换,并将句子分割为一个由单词组成的list - ds.apply(lambda ins: ins[Const.INPUTS(1)].translate(parentheses_table).strip().split(), - new_field_name=Const.INPUTS(1)) - # 对第二句话的内容进行同样的预处理 - ds.drop(lambda x: x[Const.TARGET] == '-') # 将标签为'-'的样本丢掉 - return ds ------------------------------------------ Part V: fastNLP封装好的数据集加载器 @@ -138,56 +130,58 @@ fastNLP封装好的数据集加载器可以适用于多种类型的任务: - `文本分类任务`_ - `序列标注任务`_ - `Matching任务`_ - - `指代消解任务`_ - - `摘要任务`_ 文本分类任务 ------------------- -文本分类任务 +========================== ================================================================== +数据集名称 数据集加载器 +-------------------------- ------------------------------------------------------------------ +IMDb :class:`~fastNLP.io.data_loader.IMDBLoader` +-------------------------- ------------------------------------------------------------------ +SST :class:`~fastNLP.io.data_loader.SSTLoader` +-------------------------- ------------------------------------------------------------------ +SST-2 :class:`~fastNLP.io.data_loader.SST2Loader` +-------------------------- ------------------------------------------------------------------ +Yelp Polarity :class:`~fastNLP.io.data_loader.YelpLoader` +-------------------------- ------------------------------------------------------------------ +Yelp Full :class:`~fastNLP.io.data_loader.YelpLoader` +-------------------------- ------------------------------------------------------------------ +MTL16 :class:`~fastNLP.io.data_loader.MTL16Loader` +========================== ================================================================== 序列标注任务 ------------------- -序列标注任务 - - -Matching任务 -------------------- - -:class:`~fastNLP.io.data_loader.matching.SNLILoader` - 一个关于SNLI数据集的DataSetLoader。SNLI数据集来自 - `SNLI Data Set `_ . - -:class:`~fastNLP.io.data_loader.matching.MNLILoader` - 一个关于MultiNLI数据集的DataSetLoader。MultiNLI数据集来自 `GLUE benchmark `_ - -:class:`~fastNLP.io.data_loader.matching.QNLILoader` - 一个关于QNLI数据集的DataSetLoader。QNLI数据集来自 `GLUE benchmark `_ - -:class:`~fastNLP.io.data_loader.matching.RTELoader` - 一个关于Recognizing Textual Entailment数据集(RTE)的DataSetLoader。RTE数据集来自 - `GLUE benchmark `_ +========================== ================================================================== +数据集名称 数据集加载器 +-------------------------- ------------------------------------------------------------------ +Conll :class:`~fastNLP.io.data_loader.ConllLoader` +-------------------------- ------------------------------------------------------------------ +Conll2003 :class:`~fastNLP.io.data_loader.Conll2003Loader` +-------------------------- ------------------------------------------------------------------ +人民日报数据集 :class:`~fastNLP.io.data_loader.PeopleDailyCorpusLoader` +========================== ================================================================== -:class:`~fastNLP.io.data_loader.matching.QuoraLoader` - 一个关于Quora数据集的DataSetLoader。 - - -指代消解任务 -------------------- - -指代消解任务 - - - -摘要任务 +Matching任务 ------------------- -摘要任务 - +========================== ================================================================== +数据集名称 数据集加载器 +-------------------------- ------------------------------------------------------------------ +SNLI :class:`~fastNLP.io.data_loader.SNLILoader` +-------------------------- ------------------------------------------------------------------ +MultiNLI :class:`~fastNLP.io.data_loader.MNLILoader` +-------------------------- ------------------------------------------------------------------ +QNLI :class:`~fastNLP.io.data_loader.QNLILoader` +-------------------------- ------------------------------------------------------------------ +RTE :class:`~fastNLP.io.data_loader.RTELoader` +-------------------------- ------------------------------------------------------------------ +Quora Pair Dataset :class:`~fastNLP.io.data_loader.QuoraLoader` +========================== ================================================================== diff --git a/fastNLP/io/__init__.py b/fastNLP/io/__init__.py index d1d1dc5d..19d20923 100644 --- a/fastNLP/io/__init__.py +++ b/fastNLP/io/__init__.py @@ -23,6 +23,7 @@ __all__ = [ 'ConllLoader', 'Conll2003Loader', + 'IMDBLoader', 'MatchingLoader', 'PeopleDailyCorpusLoader', 'SNLILoader', diff --git a/fastNLP/io/data_loader/__init__.py b/fastNLP/io/data_loader/__init__.py index d4777ff8..07ed428b 100644 --- a/fastNLP/io/data_loader/__init__.py +++ b/fastNLP/io/data_loader/__init__.py @@ -1,5 +1,5 @@ """ -用于读数据集的模块, 具体包括: +用于读数据集的模块, 可以读取文本分类、序列标注、Matching任务的数据集 这些模块的使用方法如下: """ diff --git a/fastNLP/io/data_loader/conll.py b/fastNLP/io/data_loader/conll.py index 61f4f61b..9e21eda6 100644 --- a/fastNLP/io/data_loader/conll.py +++ b/fastNLP/io/data_loader/conll.py @@ -10,7 +10,7 @@ class ConllLoader(DataSetLoader): 别名::class:`fastNLP.io.ConllLoader` :class:`fastNLP.io.data_loader.ConllLoader` 读取Conll格式的数据. 数据格式详见 http://conll.cemantix.org/2012/data.html. 数据中以"-DOCSTART-"开头的行将被忽略,因为 - 该符号在conll 2003中被用为文档分割符。 + 该符号在conll 2003中被用为文档分割符。 列号从0开始, 每列对应内容为:: diff --git a/fastNLP/io/data_loader/imdb.py b/fastNLP/io/data_loader/imdb.py index bf53c5be..d3636cde 100644 --- a/fastNLP/io/data_loader/imdb.py +++ b/fastNLP/io/data_loader/imdb.py @@ -13,9 +13,12 @@ from ..utils import get_tokenizer class IMDBLoader(DataSetLoader): """ + 别名::class:`fastNLP.io.IMDBLoader` :class:`fastNLP.io.data_loader.IMDBLoader` + 读取IMDB数据集,DataSet包含以下fields: words: list(str), 需要分类的文本 + target: str, 文本的标签 """ diff --git a/fastNLP/io/data_loader/mnli.py b/fastNLP/io/data_loader/mnli.py index 5d857533..65863f3d 100644 --- a/fastNLP/io/data_loader/mnli.py +++ b/fastNLP/io/data_loader/mnli.py @@ -12,7 +12,9 @@ class MNLILoader(MatchingLoader, CSVLoader): 读取MNLI数据集,读取的DataSet包含fields:: words1: list(str),第一句文本, premise + words2: list(str), 第二句文本, hypothesis + target: str, 真实标签 数据来源: diff --git a/fastNLP/io/data_loader/mtl.py b/fastNLP/io/data_loader/mtl.py index 940ece51..cbca413d 100644 --- a/fastNLP/io/data_loader/mtl.py +++ b/fastNLP/io/data_loader/mtl.py @@ -10,9 +10,12 @@ from ..utils import check_dataloader_paths class MTL16Loader(CSVLoader): """ + 别名::class:`fastNLP.io.MTL16Loader` :class:`fastNLP.io.data_loader.MTL16Loader` + 读取MTL16数据集,DataSet包含以下fields: words: list(str), 需要分类的文本 + target: str, 文本的标签 数据来源:https://pan.baidu.com/s/1c2L6vdA diff --git a/fastNLP/io/data_loader/qnli.py b/fastNLP/io/data_loader/qnli.py index ff6302b2..84b0f3d6 100644 --- a/fastNLP/io/data_loader/qnli.py +++ b/fastNLP/io/data_loader/qnli.py @@ -12,7 +12,9 @@ class QNLILoader(MatchingLoader, CSVLoader): 读取QNLI数据集,读取的DataSet包含fields:: words1: list(str),第一句文本, premise + words2: list(str), 第二句文本, hypothesis + target: str, 真实标签 数据来源: diff --git a/fastNLP/io/data_loader/quora.py b/fastNLP/io/data_loader/quora.py index 12cc42ce..d0ee41ec 100644 --- a/fastNLP/io/data_loader/quora.py +++ b/fastNLP/io/data_loader/quora.py @@ -12,7 +12,9 @@ class QuoraLoader(MatchingLoader, CSVLoader): 读取MNLI数据集,读取的DataSet包含fields:: words1: list(str),第一句文本, premise + words2: list(str), 第二句文本, hypothesis + target: str, 真实标签 数据来源: diff --git a/fastNLP/io/data_loader/rte.py b/fastNLP/io/data_loader/rte.py index c6c64ef8..f8c5e2fc 100644 --- a/fastNLP/io/data_loader/rte.py +++ b/fastNLP/io/data_loader/rte.py @@ -12,7 +12,9 @@ class RTELoader(MatchingLoader, CSVLoader): 读取RTE数据集,读取的DataSet包含fields:: words1: list(str),第一句文本, premise + words2: list(str), 第二句文本, hypothesis + target: str, 真实标签 数据来源: diff --git a/fastNLP/io/data_loader/snli.py b/fastNLP/io/data_loader/snli.py index 8334fcfd..1db0ac5b 100644 --- a/fastNLP/io/data_loader/snli.py +++ b/fastNLP/io/data_loader/snli.py @@ -12,7 +12,9 @@ class SNLILoader(MatchingLoader, JsonLoader): 读取SNLI数据集,读取的DataSet包含fields:: words1: list(str),第一句文本, premise + words2: list(str), 第二句文本, hypothesis + target: str, 真实标签 数据来源: https://nlp.stanford.edu/projects/snli/snli_1.0.zip diff --git a/fastNLP/io/data_loader/sst.py b/fastNLP/io/data_loader/sst.py index df46b47f..0d881e65 100644 --- a/fastNLP/io/data_loader/sst.py +++ b/fastNLP/io/data_loader/sst.py @@ -104,7 +104,9 @@ class SSTLoader(DataSetLoader): class SST2Loader(CSVLoader): """ - 数据来源"SST":'https://firebasestorage.googleapis.com/v0/b/mtl-sentence-representations.appspot.com/o/data%2FSST-2.zip?alt=media&token=aabc5f6b-e466-44a2-b9b4-cf6337f84ac8', + 别名::class:`fastNLP.io.SST2Loader` :class:`fastNLP.io.data_loader.SST2Loader` + + 数据来源 SST: https://firebasestorage.googleapis.com/v0/b/mtl-sentence-representations.appspot.com/o/data%2FSST-2.zip?alt=media&token=aabc5f6b-e466-44a2-b9b4-cf6337f84ac8 """ def __init__(self): diff --git a/fastNLP/io/data_loader/yelp.py b/fastNLP/io/data_loader/yelp.py index c287a90c..333fcab0 100644 --- a/fastNLP/io/data_loader/yelp.py +++ b/fastNLP/io/data_loader/yelp.py @@ -13,12 +13,17 @@ from ..utils import check_dataloader_paths, get_tokenizer class YelpLoader(DataSetLoader): """ + 别名::class:`fastNLP.io.YelpLoader` :class:`fastNLP.io.data_loader.YelpLoader` 读取Yelp_full/Yelp_polarity数据集, DataSet包含fields: + words: list(str), 需要分类的文本 + target: str, 文本的标签 + chars:list(str),未index的字符列表 数据集:yelp_full/yelp_polarity + :param fine_grained: 是否使用SST-5标准,若 ``False`` , 使用SST-2。Default: ``False`` :param lower: 是否需要自动转小写,默认为False。 """ From 273657048ffe7ba140e71c2dd5c9248c149b0119 Mon Sep 17 00:00:00 2001 From: xuyige Date: Fri, 12 Jul 2019 01:47:51 +0800 Subject: [PATCH 007/139] =?UTF-8?q?=E6=9B=B4=E6=94=B9=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tutorials/tutorial_2_load_dataset.rst | 53 ++++++++++++++++--- fastNLP/core/optimizer.py | 35 ++++++------ 2 files changed, 64 insertions(+), 24 deletions(-) diff --git a/docs/source/tutorials/tutorial_2_load_dataset.rst b/docs/source/tutorials/tutorial_2_load_dataset.rst index d964d8c7..7f4509f2 100644 --- a/docs/source/tutorials/tutorial_2_load_dataset.rst +++ b/docs/source/tutorials/tutorial_2_load_dataset.rst @@ -91,11 +91,11 @@ Part IV: DataSetLoader举例 以Matching任务为例子: - :class:`~fastNLP.io.data_loader.matching.MatchingLoader` - 我们在fastNLP当中封装了一个Matching任务数据集的数据加载类: :class:`~fastNLP.io.data_loader.matching.MatchingLoader` . + :class:`~fastNLP.io.data_loader.MatchingLoader` + 我们在fastNLP当中封装了一个Matching任务数据集的数据加载类: :class:`~fastNLP.io.data_loader.MatchingLoader` . 在MatchingLoader类当中我们封装了一个对数据集中的文本内容进行进一步的预处理的函数: - :meth:`~fastNLP.io.data_loader.matching.MatchingLoader.process` + :meth:`~fastNLP.io.data_loader.MatchingLoader.process` 这个函数具有各种预处理option,如: - 是否将文本转成全小写 - 是否需要序列长度信息,需要什么类型的序列长度信息 @@ -104,21 +104,58 @@ Part IV: DataSetLoader举例 具体内容参见 :meth:`fastNLP.io.MatchingLoader.process` 。 - :class:`~fastNLP.io.data_loader.matching.SNLILoader` + :class:`~fastNLP.io.data_loader.SNLILoader` 一个关于SNLI数据集的DataSetLoader。SNLI数据集来自 `SNLI Data Set `_ . - 在 :class:`~fastNLP.io.data_loader.matching.SNLILoader` 的 :meth:`~fastNLP.io.data_loader.matching.SNLILoader._load` - 函数中,我们用以下代码将数据集内容从文本文件读入内存 + 在 :class:`~fastNLP.io.data_loader.SNLILoader` 的 :meth:`~fastNLP.io.data_loader.SNLILoader._load` + 函数中,我们用以下代码将数据集内容从文本文件读入内存: .. code-block:: python data = SNLILoader().process( - paths='path/to/snli/data', to_lower=False, seq_len_type=arg.seq_len_type, + paths='path/to/snli/data', to_lower=False, seq_len_type='seq_len', get_index=True, concat=False, ) + print(data) - 这里的data即可直接传入 :class:`~fastNLP.Trainer` 进行 + 输出的内容是:: + + In total 3 datasets: + train has 549367 instances. + dev has 9842 instances. + test has 9824 instances. + In total 2 vocabs: + words has 43154 entries. + target has 3 entries. + + + 这里的data是一个 :class:`~fastNLP.io.base_loader.DataBundle` ,取 ``datasets`` 字典里的内容即可直接传入 + :class:`~fastNLP.Trainer` 或者 :class:`~fastNLP.Tester` 进行训练或者测试。 + + :class:`~fastNLP.io.data_loader.IMDBLoader` + 以IMDB数据集为例,在 :class:`~fastNLP.io.data_loader.IMDBLoader` 的 :meth:`~fastNLP.io.data_loader.IMDBLoader._load` + 函数中,我们用以下代码将数据集内容从文本文件读入内存: + + .. code-block:: python + + data = IMDBLoader().process( + paths={'train': 'path/to/train/file', 'test': 'path/to/test/file'} + ) + print(data) + + 输出的内容是:: + + In total 3 datasets: + train has 22500 instances. + test has 25000 instances. + dev has 2500 instances. + In total 2 vocabs: + words has 82846 entries. + target has 2 entries. + + + 这里的将原来的train集按9:1的比例分成了训练集和验证集。 ------------------------------------------ diff --git a/fastNLP/core/optimizer.py b/fastNLP/core/optimizer.py index f0dfdef0..3036257c 100644 --- a/fastNLP/core/optimizer.py +++ b/fastNLP/core/optimizer.py @@ -104,25 +104,28 @@ class Adam(Optimizer): class AdamW(TorchOptimizer): - r"""对AdamW的实现,该实现应该会在pytorch更高版本中出现,https://github.com/pytorch/pytorch/pull/21250。这里提前加入 + r""" + 别名::class:`fastNLP.AdamW` :class:`fastNLP.core.optimizer.AdamW` + + 对AdamW的实现,该实现应该会在pytorch更高版本中出现,https://github.com/pytorch/pytorch/pull/21250。这里提前加入 .. todo:: 翻译成中文 The original Adam algorithm was proposed in `Adam: A Method for Stochastic Optimization`_. The AdamW variant was proposed in `Decoupled Weight Decay Regularization`_. - Arguments: - params (iterable): iterable of parameters to optimize or dicts defining - parameter groups - lr (float, optional): learning rate (default: 1e-3) - betas (Tuple[float, float], optional): coefficients used for computing - running averages of gradient and its square (default: (0.9, 0.99)) - eps (float, optional): term added to the denominator to improve - numerical stability (default: 1e-8) - weight_decay (float, optional): weight decay coefficient (default: 1e-2) - amsgrad (boolean, optional): whether to use the AMSGrad variant of this - algorithm from the paper `On the Convergence of Adam and Beyond`_ - (default: False) + + :param params (iterable): iterable of parameters to optimize or dicts defining + parameter groups + :param lr (float, optional): learning rate (default: 1e-3) + :param betas (Tuple[float, float], optional): coefficients used for computing + running averages of gradient and its square (default: (0.9, 0.99)) + :param eps (float, optional): term added to the denominator to improve + numerical stability (default: 1e-8) + :param weight_decay (float, optional): weight decay coefficient (default: 1e-2) + algorithm from the paper `On the Convergence of Adam and Beyond`_ + (default: False) + .. _Adam\: A Method for Stochastic Optimization: https://arxiv.org/abs/1412.6980 .. _Decoupled Weight Decay Regularization: @@ -152,9 +155,9 @@ class AdamW(TorchOptimizer): def step(self, closure=None): """Performs a single optimization step. - Arguments: - closure (callable, optional): A closure that reevaluates the model - and returns the loss. + + :param closure: (callable, optional) A closure that reevaluates the model + and returns the loss. """ loss = None if closure is not None: From 6cf1a8562b320a6775faddef1c3f92fe24718e17 Mon Sep 17 00:00:00 2001 From: xuyige Date: Fri, 12 Jul 2019 03:19:43 +0800 Subject: [PATCH 008/139] rename Batch to DataSetIter in enas_trainer --- fastNLP/models/enas_trainer.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fastNLP/models/enas_trainer.py b/fastNLP/models/enas_trainer.py index ef596b03..7abcc45f 100644 --- a/fastNLP/models/enas_trainer.py +++ b/fastNLP/models/enas_trainer.py @@ -14,7 +14,7 @@ except: from ..core.utils import _pseudo_tqdm as tqdm from ..core.trainer import Trainer -from ..core.batch import Batch +from ..core.batch import DataSetIter from ..core.callback import CallbackManager, CallbackException from ..core.dataset import DataSet from ..core.utils import _move_dict_value_to_device @@ -124,8 +124,8 @@ class ENASTrainer(Trainer): len(self.train_data) % self.batch_size != 0)) * self.n_epochs with inner_tqdm(total=total_steps, postfix='loss:{0:<6.5f}', leave=False, dynamic_ncols=True) as pbar: avg_loss = 0 - data_iterator = Batch(self.train_data, batch_size=self.batch_size, sampler=self.sampler, as_numpy=False, - prefetch=self.prefetch) + data_iterator = DataSetIter(self.train_data, batch_size=self.batch_size, sampler=self.sampler, as_numpy=False, + prefetch=self.prefetch) for epoch in range(1, self.n_epochs + 1): pbar.set_description_str(desc="Epoch {}/{}".format(epoch, self.n_epochs)) last_stage = (epoch > self.n_epochs + 1 - self.final_epochs) @@ -209,8 +209,8 @@ class ENASTrainer(Trainer): total_loss = 0 train_idx = 0 avg_loss = 0 - data_iterator = Batch(self.train_data, batch_size=self.batch_size, sampler=self.sampler, as_numpy=False, - prefetch=self.prefetch) + data_iterator = DataSetIter(self.train_data, batch_size=self.batch_size, sampler=self.sampler, as_numpy=False, + prefetch=self.prefetch) for batch_x, batch_y in data_iterator: _move_dict_value_to_device(batch_x, batch_y, device=self._model_device) @@ -262,8 +262,8 @@ class ENASTrainer(Trainer): if not isinstance(entropies, np.ndarray): entropies = entropies.data.cpu().numpy() - data_iterator = Batch(self.dev_data, batch_size=self.batch_size, sampler=self.sampler, as_numpy=False, - prefetch=self.prefetch) + data_iterator = DataSetIter(self.dev_data, batch_size=self.batch_size, sampler=self.sampler, as_numpy=False, + prefetch=self.prefetch) for inputs, targets in data_iterator: valid_loss, hidden, _ = self.get_loss(inputs, targets, hidden, dag) From 570b214dfb7bc45a58159f3dd53e5be254e2e5d2 Mon Sep 17 00:00:00 2001 From: xuyige Date: Fri, 12 Jul 2019 04:07:47 +0800 Subject: [PATCH 009/139] =?UTF-8?q?=E5=A2=9E=E5=8A=A0fastNLP.embeddings?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E5=B9=B6=E4=BF=AE=E6=94=B9=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84=E7=8E=B0=E6=9C=89=E4=BB=A3=E7=A0=81=E4=BB=A5=E9=80=82?= =?UTF-8?q?=E9=85=8DfastNLP.embeddings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastNLP/embeddings/__init__.py | 21 + fastNLP/embeddings/bert_embedding.py | 321 +++++ fastNLP/embeddings/char_embedding.py | 280 +++++ fastNLP/embeddings/contextual_embedding.py | 100 ++ fastNLP/embeddings/elmo_embedding.py | 326 +++++ fastNLP/embeddings/embedding.py | 180 +++ fastNLP/embeddings/stack_embedding.py | 92 ++ fastNLP/embeddings/static_embedding.py | 217 ++++ fastNLP/embeddings/utils.py | 47 + fastNLP/io/data_loader/matching.py | 2 +- fastNLP/models/bert.py | 2 +- fastNLP/models/biaffine_parser.py | 2 +- fastNLP/models/cnn_text_classification.py | 5 +- fastNLP/models/sequence_labeling.py | 9 +- fastNLP/models/snli.py | 9 +- fastNLP/models/star_transformer.py | 2 +- fastNLP/modules/__init__.py | 2 - fastNLP/modules/encoder/__init__.py | 16 +- fastNLP/modules/encoder/_bert.py | 1069 ---------------- fastNLP/modules/encoder/_elmo.py | 192 +-- fastNLP/modules/encoder/bert.py | 934 +++++++++++++- fastNLP/modules/encoder/embedding.py | 1083 ----------------- fastNLP/modules/utils.py | 28 - .../README.md | 2 + .../main.py | 14 +- reproduction/Star_transformer/train.py | 4 +- reproduction/Summarization/BertSum/model.py | 2 +- .../joint_cws_parse/{readme.md => README.md} | 0 .../joint_cws_parse/models/CharParser.py | 2 +- reproduction/joint_cws_parse/train.py | 8 +- .../matching/data/MatchingDataLoader.py | 4 + reproduction/matching/matching_bert.py | 3 +- reproduction/matching/matching_cntn.py | 5 +- reproduction/matching/matching_esim.py | 11 +- reproduction/matching/matching_mwan.py | 16 +- reproduction/matching/model/bert.py | 2 +- reproduction/matching/model/cntn.py | 2 +- reproduction/matching/model/esim.py | 3 +- .../seqence_labelling/cws/model/model.py | 2 +- .../ner/train_cnn_lstm_crf_conll2003.py | 6 +- .../seqence_labelling/ner/train_idcnn.py | 13 +- .../seqence_labelling/ner/train_ontonote.py | 5 +- reproduction/text_classification/model/HAN.py | 2 +- .../text_classification/model/dpcnn.py | 2 +- reproduction/text_classification/train_HAN.py | 8 +- .../text_classification/train_awdlstm.py | 11 +- .../text_classification/train_char_cnn.py | 14 +- .../text_classification/train_dpcnn.py | 11 +- .../text_classification/train_lstm.py | 13 +- .../text_classification/train_lstm_att.py | 13 +- test/embeddings/test_char_embedding.py | 26 + test/modules/encoder/test_bert.py | 2 +- 52 files changed, 2589 insertions(+), 2556 deletions(-) create mode 100644 fastNLP/embeddings/__init__.py create mode 100644 fastNLP/embeddings/bert_embedding.py create mode 100644 fastNLP/embeddings/char_embedding.py create mode 100644 fastNLP/embeddings/contextual_embedding.py create mode 100644 fastNLP/embeddings/elmo_embedding.py create mode 100644 fastNLP/embeddings/embedding.py create mode 100644 fastNLP/embeddings/stack_embedding.py create mode 100644 fastNLP/embeddings/static_embedding.py create mode 100644 fastNLP/embeddings/utils.py delete mode 100644 fastNLP/modules/encoder/_bert.py delete mode 100644 fastNLP/modules/encoder/embedding.py rename reproduction/joint_cws_parse/{readme.md => README.md} (100%) create mode 100644 test/embeddings/test_char_embedding.py diff --git a/fastNLP/embeddings/__init__.py b/fastNLP/embeddings/__init__.py new file mode 100644 index 00000000..92320fde --- /dev/null +++ b/fastNLP/embeddings/__init__.py @@ -0,0 +1,21 @@ +""" +embeddings 模块里实现了 +""" + +__all__ = [ + "Embedding", + "StaticEmbedding", + "ElmoEmbedding", + "BertEmbedding", + "StackEmbedding", + "LSTMCharEmbedding", + "CNNCharEmbedding", +] + + +from .embedding import Embedding +from .static_embedding import StaticEmbedding +from .elmo_embedding import ElmoEmbedding +from .bert_embedding import BertEmbedding +from .char_embedding import CNNCharEmbedding, LSTMCharEmbedding +from .stack_embedding import StackEmbedding diff --git a/fastNLP/embeddings/bert_embedding.py b/fastNLP/embeddings/bert_embedding.py new file mode 100644 index 00000000..f9077e10 --- /dev/null +++ b/fastNLP/embeddings/bert_embedding.py @@ -0,0 +1,321 @@ + +import os +import collections + +from torch import nn +import torch +import numpy as np +from itertools import chain + +from ..core.vocabulary import Vocabulary +from ..io.file_utils import _get_base_url, cached_path, PRETRAINED_BERT_MODEL_DIR +from ..modules.encoder.bert import _WordPieceBertModel, BertModel, BertTokenizer +from .contextual_embedding import ContextualEmbedding + + +class BertEmbedding(ContextualEmbedding): + """ + 别名::class:`fastNLP.embeddings.BertEmbedding` :class:`fastNLP.embeddings.bert_embedding.BertEmbedding` + + 使用BERT对words进行encode的Embedding。建议将输入的words长度限制在450以内,而不要使用512。这是由于预训练的bert模型长 + 度限制为512个token,而因为输入的word是未进行word piece分割的,在分割之后长度可能会超过最大长度限制。 + + Example:: + + >>> embedding = BertEmbedding(vocab, model_dir_or_name='en-base-uncased', requires_grad=False, layers='4,-2,-1') + + + :param fastNLP.Vocabulary vocab: 词表 + :param str model_dir_or_name: 模型所在目录或者模型的名称。默认值为 ``en-base-uncased``. + :param str layers:最终结果中的表示。以','隔开层数,可以以负数去索引倒数几层 + :param str pool_method: 因为在bert中,每个word会被表示为多个word pieces, 当获取一个word的表示的时候,怎样从它的word pieces + 中计算得到它对应的表示。支持``last``, ``first``, ``avg``, ``max``。 + :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 + :param float dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。 + :param bool include_cls_sep: bool,在bert计算句子的表示的时候,需要在前面加上[CLS]和[SEP], 是否在结果中保留这两个内容。 这样 + 会使得word embedding的结果比输入的结果长两个token。在使用 :class::StackEmbedding 可能会遇到问题。 + :param bool requires_grad: 是否需要gradient。 + """ + def __init__(self, vocab: Vocabulary, model_dir_or_name: str='en-base-uncased', layers: str='-1', + pool_method: str='first', word_dropout=0, dropout=0, requires_grad: bool=False, + include_cls_sep: bool=False): + super(BertEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) + + # 根据model_dir_or_name检查是否存在并下载 + if model_dir_or_name.lower() in PRETRAINED_BERT_MODEL_DIR: + PRETRAIN_URL = _get_base_url('bert') + model_name = PRETRAINED_BERT_MODEL_DIR[model_dir_or_name] + model_url = PRETRAIN_URL + model_name + model_dir = cached_path(model_url) + # 检查是否存在 + elif os.path.isdir(os.path.expanduser(os.path.abspath(model_dir_or_name))): + model_dir = model_dir_or_name + else: + raise ValueError(f"Cannot recognize {model_dir_or_name}.") + + self.model = _WordBertModel(model_dir=model_dir, vocab=vocab, layers=layers, + pool_method=pool_method, include_cls_sep=include_cls_sep) + + self.requires_grad = requires_grad + self._embed_size = len(self.model.layers)*self.model.encoder.hidden_size + + def _delete_model_weights(self): + del self.model + + def forward(self, words): + """ + 计算words的bert embedding表示。计算之前会在每句话的开始增加[CLS]在结束增加[SEP], 并根据include_cls_sep判断要不要 + 删除这两个token的表示。 + + :param torch.LongTensor words: [batch_size, max_len] + :return: torch.FloatTensor. batch_size x max_len x (768*len(self.layers)) + """ + words = self.drop_word(words) + outputs = self._get_sent_reprs(words) + if outputs is not None: + return self.dropout(words) + outputs = self.model(words) + outputs = torch.cat([*outputs], dim=-1) + + return self.dropout(words) + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + :return: + """ + requires_grads = set([param.requires_grad for name, param in self.named_parameters() + if 'word_pieces_lengths' not in name]) + if len(requires_grads) == 1: + return requires_grads.pop() + else: + return None + + @requires_grad.setter + def requires_grad(self, value): + for name, param in self.named_parameters(): + if 'word_pieces_lengths' in name: # 这个不能加入到requires_grad中 + continue + param.requires_grad = value + + +class BertWordPieceEncoder(nn.Module): + """ + 读取bert模型,读取之后调用index_dataset方法在dataset中生成word_pieces这一列。 + + :param str model_dir_or_name: 模型所在目录或者模型的名称。默认值为``en-base-uncased`` + :param str layers:最终结果中的表示。以','隔开层数,可以以负数去索引倒数几层 + :param bool requires_grad: 是否需要gradient。 + """ + def __init__(self, model_dir_or_name: str='en-base-uncased', layers: str='-1', + requires_grad: bool=False): + super().__init__() + PRETRAIN_URL = _get_base_url('bert') + + if model_dir_or_name in PRETRAINED_BERT_MODEL_DIR: + model_name = PRETRAINED_BERT_MODEL_DIR[model_dir_or_name] + model_url = PRETRAIN_URL + model_name + model_dir = cached_path(model_url) + # 检查是否存在 + elif os.path.isdir(model_dir_or_name): + model_dir = model_dir_or_name + else: + raise ValueError(f"Cannot recognize {model_dir_or_name}.") + + self.model = _WordPieceBertModel(model_dir=model_dir, layers=layers) + self._embed_size = len(self.model.layers) * self.model.encoder.hidden_size + self.requires_grad = requires_grad + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + :return: + """ + requires_grads = set([param.requires_grad for name, param in self.named_parameters()]) + if len(requires_grads) == 1: + return requires_grads.pop() + else: + return None + + @requires_grad.setter + def requires_grad(self, value): + for name, param in self.named_parameters(): + param.requires_grad = value + + @property + def embed_size(self): + return self._embed_size + + def index_datasets(self, *datasets, field_name): + """ + 使用bert的tokenizer新生成word_pieces列加入到datasets中,并将他们设置为input。如果首尾不是 + [CLS]与[SEP]会在首尾额外加入[CLS]与[SEP], 且将word_pieces这一列的pad value设置为了bert的pad value。 + + :param datasets: DataSet对象 + :param field_name: 基于哪一列的内容生成word_pieces列。这一列中每个数据应该是List[str]的形式。 + :return: + """ + self.model.index_dataset(*datasets, field_name=field_name) + + def forward(self, word_pieces, token_type_ids=None): + """ + 计算words的bert embedding表示。传入的words中应该自行包含[CLS]与[SEP]的tag。 + + :param words: batch_size x max_len + :param token_type_ids: batch_size x max_len, 用于区分前一句和后一句话 + :return: torch.FloatTensor. batch_size x max_len x (768*len(self.layers)) + """ + outputs = self.model(word_pieces, token_type_ids) + outputs = torch.cat([*outputs], dim=-1) + + return outputs + + +class _WordBertModel(nn.Module): + def __init__(self, model_dir:str, vocab:Vocabulary, layers:str='-1', pool_method:str='first', include_cls_sep:bool=False): + super().__init__() + + self.tokenzier = BertTokenizer.from_pretrained(model_dir) + self.encoder = BertModel.from_pretrained(model_dir) + # 检查encoder_layer_number是否合理 + encoder_layer_number = len(self.encoder.encoder.layer) + self.layers = list(map(int, layers.split(','))) + for layer in self.layers: + if layer<0: + assert -layer<=encoder_layer_number, f"The layer index:{layer} is out of scope for " \ + f"a bert model with {encoder_layer_number} layers." + else: + assert layer Dropout(x) -> CNN(x) -> activation(x) -> pool -> fc -> Dropout. + 不同的kernel大小的fitler结果是concat起来的。 + + Example:: + + >>> cnn_char_embed = CNNCharEmbedding(vocab) + + + :param vocab: 词表 + :param embed_size: 该word embedding的大小,默认值为50. + :param char_emb_size: character的embed的大小。character是从vocab中生成的。默认值为50. + :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 + :param float dropout: 以多大的概率drop + :param filter_nums: filter的数量. 长度需要和kernels一致。默认值为[40, 30, 20]. + :param kernel_sizes: kernel的大小. 默认值为[5, 3, 1]. + :param pool_method: character的表示在合成一个表示时所使用的pool方法,支持'avg', 'max'. + :param activation: CNN之后使用的激活方法,支持'relu', 'sigmoid', 'tanh' 或者自定义函数. + :param min_char_freq: character的最少出现次数。默认值为2. + """ + def __init__(self, vocab: Vocabulary, embed_size: int=50, char_emb_size: int=50, word_dropout:float=0, + dropout:float=0.5, filter_nums: List[int]=(40, 30, 20), kernel_sizes: List[int]=(5, 3, 1), + pool_method: str='max', activation='relu', min_char_freq: int=2): + super(CNNCharEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) + + for kernel in kernel_sizes: + assert kernel % 2 == 1, "Only odd kernel is allowed." + + assert pool_method in ('max', 'avg') + self.dropout = nn.Dropout(dropout) + self.pool_method = pool_method + # activation function + if isinstance(activation, str): + if activation.lower() == 'relu': + self.activation = F.relu + elif activation.lower() == 'sigmoid': + self.activation = F.sigmoid + elif activation.lower() == 'tanh': + self.activation = F.tanh + elif activation is None: + self.activation = lambda x: x + elif callable(activation): + self.activation = activation + else: + raise Exception( + "Undefined activation function: choose from: [relu, tanh, sigmoid, or a callable function]") + + print("Start constructing character vocabulary.") + # 建立char的词表 + self.char_vocab = _construct_char_vocab_from_vocab(vocab, min_freq=min_char_freq) + self.char_pad_index = self.char_vocab.padding_idx + print(f"In total, there are {len(self.char_vocab)} distinct characters.") + # 对vocab进行index + max_word_len = max(map(lambda x: len(x[0]), vocab)) + self.words_to_chars_embedding = nn.Parameter(torch.full((len(vocab), max_word_len), + fill_value=self.char_pad_index, dtype=torch.long), + requires_grad=False) + self.word_lengths = nn.Parameter(torch.zeros(len(vocab)).long(), requires_grad=False) + for word, index in vocab: + # if index!=vocab.padding_idx: # 如果是pad的话,直接就为pad_value了。修改为不区分pad, 这样所有的也是同一个embed + self.words_to_chars_embedding[index, :len(word)] = \ + torch.LongTensor([self.char_vocab.to_index(c) for c in word]) + self.word_lengths[index] = len(word) + self.char_embedding = nn.Embedding(len(self.char_vocab), char_emb_size) + + self.convs = nn.ModuleList([nn.Conv1d( + char_emb_size, filter_nums[i], kernel_size=kernel_sizes[i], bias=True, padding=kernel_sizes[i] // 2) + for i in range(len(kernel_sizes))]) + self._embed_size = embed_size + self.fc = nn.Linear(sum(filter_nums), embed_size) + self.init_param() + + def forward(self, words): + """ + 输入words的index后,生成对应的words的表示。 + + :param words: [batch_size, max_len] + :return: [batch_size, max_len, embed_size] + """ + words = self.drop_word(words) + batch_size, max_len = words.size() + chars = self.words_to_chars_embedding[words] # batch_size x max_len x max_word_len + word_lengths = self.word_lengths[words] # batch_size x max_len + max_word_len = word_lengths.max() + chars = chars[:, :, :max_word_len] + # 为1的地方为mask + chars_masks = chars.eq(self.char_pad_index) # batch_size x max_len x max_word_len 如果为0, 说明是padding的位置了 + chars = self.char_embedding(chars) # batch_size x max_len x max_word_len x embed_size + chars = self.dropout(chars) + reshaped_chars = chars.reshape(batch_size*max_len, max_word_len, -1) + reshaped_chars = reshaped_chars.transpose(1, 2) # B' x E x M + conv_chars = [conv(reshaped_chars).transpose(1, 2).reshape(batch_size, max_len, max_word_len, -1) + for conv in self.convs] + conv_chars = torch.cat(conv_chars, dim=-1).contiguous() # B x max_len x max_word_len x sum(filters) + conv_chars = self.activation(conv_chars) + if self.pool_method == 'max': + conv_chars = conv_chars.masked_fill(chars_masks.unsqueeze(-1), float('-inf')) + chars, _ = torch.max(conv_chars, dim=-2) # batch_size x max_len x sum(filters) + else: + conv_chars = conv_chars.masked_fill(chars_masks.unsqueeze(-1), 0) + chars = torch.sum(conv_chars, dim=-2)/chars_masks.eq(0).sum(dim=-1, keepdim=True).float() + chars = self.fc(chars) + return self.dropout(chars) + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + :return: + """ + params = [] + for name, param in self.named_parameters(): + if 'words_to_chars_embedding' not in name and 'word_lengths' not in name: + params.append(param.requires_grad) + requires_grads = set(params) + if len(requires_grads) == 1: + return requires_grads.pop() + else: + return None + + @requires_grad.setter + def requires_grad(self, value): + for name, param in self.named_parameters(): + if 'words_to_chars_embedding' in name or 'word_lengths' in name: # 这个不能加入到requires_grad中 + continue + param.requires_grad = value + + def init_param(self): + for name, param in self.named_parameters(): + if 'words_to_chars_embedding' in name or 'word_lengths' in name: # 这个不能reset + continue + if param.data.dim()>1: + nn.init.xavier_uniform_(param, 1) + else: + nn.init.uniform_(param, -1, 1) + + +class LSTMCharEmbedding(TokenEmbedding): + """ + 别名::class:`fastNLP.embeddings.LSTMCharEmbedding` :class:`fastNLP.embeddings.char_embedding.LSTMCharEmbedding` + + 使用LSTM的方式对character进行encode. embed(x) -> Dropout(x) -> LSTM(x) -> activation(x) -> pool + + Example:: + + >>> lstm_char_embed = LSTMCharEmbedding(vocab) + + :param vocab: 词表 + :param embed_size: embedding的大小。默认值为50. + :param char_emb_size: character的embedding的大小。默认值为50. + :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 + :param dropout: 以多大概率drop + :param hidden_size: LSTM的中间hidden的大小,如果为bidirectional的,hidden会除二,默认为50. + :param pool_method: 支持'max', 'avg' + :param activation: 激活函数,支持'relu', 'sigmoid', 'tanh', 或者自定义函数. + :param min_char_freq: character的最小出现次数。默认值为2. + :param bidirectional: 是否使用双向的LSTM进行encode。默认值为True。 + """ + def __init__(self, vocab: Vocabulary, embed_size: int=50, char_emb_size: int=50, word_dropout:float=0, + dropout:float=0.5, hidden_size=50,pool_method: str='max', activation='relu', min_char_freq: int=2, + bidirectional=True): + super(LSTMCharEmbedding, self).__init__(vocab) + + assert hidden_size % 2 == 0, "Only even kernel is allowed." + + assert pool_method in ('max', 'avg') + self.pool_method = pool_method + self.dropout = nn.Dropout(dropout) + # activation function + if isinstance(activation, str): + if activation.lower() == 'relu': + self.activation = F.relu + elif activation.lower() == 'sigmoid': + self.activation = F.sigmoid + elif activation.lower() == 'tanh': + self.activation = F.tanh + elif activation is None: + self.activation = lambda x: x + elif callable(activation): + self.activation = activation + else: + raise Exception( + "Undefined activation function: choose from: [relu, tanh, sigmoid, or a callable function]") + + print("Start constructing character vocabulary.") + # 建立char的词表 + self.char_vocab = _construct_char_vocab_from_vocab(vocab, min_freq=min_char_freq) + self.char_pad_index = self.char_vocab.padding_idx + print(f"In total, there are {len(self.char_vocab)} distinct characters.") + # 对vocab进行index + self.max_word_len = max(map(lambda x: len(x[0]), vocab)) + self.words_to_chars_embedding = nn.Parameter(torch.full((len(vocab), self.max_word_len), + fill_value=self.char_pad_index, dtype=torch.long), + requires_grad=False) + self.word_lengths = nn.Parameter(torch.zeros(len(vocab)).long(), requires_grad=False) + for word, index in vocab: + # if index!=vocab.padding_idx: # 如果是pad的话,直接就为pad_value了. 修改为不区分pad与否 + self.words_to_chars_embedding[index, :len(word)] = \ + torch.LongTensor([self.char_vocab.to_index(c) for c in word]) + self.word_lengths[index] = len(word) + self.char_embedding = nn.Embedding(len(self.char_vocab), char_emb_size) + + self.fc = nn.Linear(hidden_size, embed_size) + hidden_size = hidden_size // 2 if bidirectional else hidden_size + + self.lstm = LSTM(char_emb_size, hidden_size, bidirectional=bidirectional, batch_first=True) + self._embed_size = embed_size + self.bidirectional = bidirectional + + def forward(self, words): + """ + 输入words的index后,生成对应的words的表示。 + + :param words: [batch_size, max_len] + :return: [batch_size, max_len, embed_size] + """ + words = self.drop_word(words) + batch_size, max_len = words.size() + chars = self.words_to_chars_embedding[words] # batch_size x max_len x max_word_len + word_lengths = self.word_lengths[words] # batch_size x max_len + max_word_len = word_lengths.max() + chars = chars[:, :, :max_word_len] + # 为mask的地方为1 + chars_masks = chars.eq(self.char_pad_index) # batch_size x max_len x max_word_len 如果为0, 说明是padding的位置了 + chars = self.char_embedding(chars) # batch_size x max_len x max_word_len x embed_size + chars = self.dropout(chars) + reshaped_chars = chars.reshape(batch_size * max_len, max_word_len, -1) + char_seq_len = chars_masks.eq(0).sum(dim=-1).reshape(batch_size * max_len) + lstm_chars = self.lstm(reshaped_chars, char_seq_len)[0].reshape(batch_size, max_len, max_word_len, -1) + # B x M x M x H + + lstm_chars = self.activation(lstm_chars) + if self.pool_method == 'max': + lstm_chars = lstm_chars.masked_fill(chars_masks.unsqueeze(-1), float('-inf')) + chars, _ = torch.max(lstm_chars, dim=-2) # batch_size x max_len x H + else: + lstm_chars = lstm_chars.masked_fill(chars_masks.unsqueeze(-1), 0) + chars = torch.sum(lstm_chars, dim=-2) / chars_masks.eq(0).sum(dim=-1, keepdim=True).float() + + chars = self.fc(chars) + + return self.dropout(chars) + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + :return: + """ + params = [] + for name, param in self.named_parameters(): + if 'words_to_chars_embedding' not in name and 'word_lengths' not in name: + params.append(param) + requires_grads = set(params) + if len(requires_grads) == 1: + return requires_grads.pop() + else: + return None + + @requires_grad.setter + def requires_grad(self, value): + for name, param in self.named_parameters(): + if 'words_to_chars_embedding' in name or 'word_lengths' in name: # 这个不能加入到requires_grad中 + continue + param.requires_grad = value diff --git a/fastNLP/embeddings/contextual_embedding.py b/fastNLP/embeddings/contextual_embedding.py new file mode 100644 index 00000000..1831af4e --- /dev/null +++ b/fastNLP/embeddings/contextual_embedding.py @@ -0,0 +1,100 @@ + +from abc import abstractmethod +import torch + +from ..core.vocabulary import Vocabulary +from ..core.dataset import DataSet +from ..core.batch import DataSetIter +from ..core.sampler import SequentialSampler +from ..core.utils import _move_model_to_device, _get_model_device +from .embedding import TokenEmbedding + + +class ContextualEmbedding(TokenEmbedding): + def __init__(self, vocab: Vocabulary, word_dropout:float=0.0, dropout:float=0.0): + super(ContextualEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) + + def add_sentence_cache(self, *datasets, batch_size=32, device='cpu', delete_weights: bool=True): + """ + 由于动态embedding生成比较耗时,所以可以把每句话embedding缓存下来,这样就不需要每次都运行生成过程。 + + :param datasets: DataSet对象 + :param batch_size: int, 生成cache的sentence表示时使用的batch的大小 + :param device: 参考 :class::fastNLP.Trainer 的device + :param delete_weights: 似乎在生成了cache之后删除权重,在不需要finetune动态模型的情况下,删除权重会大量减少内存占用。 + :return: + """ + for index, dataset in enumerate(datasets): + try: + assert isinstance(dataset, DataSet), "Only fastNLP.DataSet object is allowed." + assert 'words' in dataset.get_input_name(), "`words` field has to be set as input." + except Exception as e: + print(f"Exception happens at {index} dataset.") + raise e + + sent_embeds = {} + _move_model_to_device(self, device=device) + device = _get_model_device(self) + pad_index = self._word_vocab.padding_idx + print("Start to calculate sentence representations.") + with torch.no_grad(): + for index, dataset in enumerate(datasets): + try: + batch = DataSetIter(dataset, batch_size=batch_size, sampler=SequentialSampler()) + for batch_x, batch_y in batch: + words = batch_x['words'].to(device) + words_list = words.tolist() + seq_len = words.ne(pad_index).sum(dim=-1) + max_len = words.size(1) + # 因为有些情况可能包含CLS, SEP, 从后面往前计算比较安全。 + seq_len_from_behind = (max_len - seq_len).tolist() + word_embeds = self(words).detach().cpu().numpy() + for b in range(words.size(0)): + length = seq_len_from_behind[b] + if length==0: + sent_embeds[tuple(words_list[b][:seq_len[b]])] = word_embeds[b] + else: + sent_embeds[tuple(words_list[b][:seq_len[b]])] = word_embeds[b, :-length] + except Exception as e: + print(f"Exception happens at {index} dataset.") + raise e + print("Finish calculating sentence representations.") + self.sent_embeds = sent_embeds + if delete_weights: + self._delete_model_weights() + + def _get_sent_reprs(self, words): + """ + 获取sentence的表示,如果有缓存,则返回缓存的值; 没有缓存则返回None + + :param words: torch.LongTensor + :return: + """ + if hasattr(self, 'sent_embeds'): + words_list = words.tolist() + seq_len = words.ne(self._word_pad_index).sum(dim=-1) + _embeds = [] + for b in range(len(words)): + words_i = tuple(words_list[b][:seq_len[b]]) + embed = self.sent_embeds[words_i] + _embeds.append(embed) + max_sent_len = max(map(len, _embeds)) + embeds = words.new_zeros(len(_embeds), max_sent_len, self.embed_size, dtype=torch.float, + device=words.device) + for i, embed in enumerate(_embeds): + embeds[i, :len(embed)] = torch.FloatTensor(embed).to(words.device) + return embeds + return None + + @abstractmethod + def _delete_model_weights(self): + """删除计算表示的模型以节省资源""" + raise NotImplementedError + + def remove_sentence_cache(self): + """ + 删除缓存的句子表示. 删除之后如果模型权重没有被删除,将开始使用动态计算权重。 + + :return: + """ + del self.sent_embeds diff --git a/fastNLP/embeddings/elmo_embedding.py b/fastNLP/embeddings/elmo_embedding.py new file mode 100644 index 00000000..f669d121 --- /dev/null +++ b/fastNLP/embeddings/elmo_embedding.py @@ -0,0 +1,326 @@ + +import os + +import torch +import torch.nn as nn +import torch.nn.functional as F +import json +import codecs + +from ..core.vocabulary import Vocabulary +from ..io.file_utils import cached_path, _get_base_url, PRETRAINED_ELMO_MODEL_DIR +from ..modules.encoder._elmo import ElmobiLm, ConvTokenEmbedder +from .contextual_embedding import ContextualEmbedding + + +class ElmoEmbedding(ContextualEmbedding): + """ + 别名::class:`fastNLP.modules.ElmoEmbedding` :class:`fastNLP.modules.encoder.embedding.ElmoEmbedding` + + 使用ELMo的embedding。初始化之后,只需要传入words就可以得到对应的embedding。 + 我们提供的ELMo预训练模型来自 https://github.com/HIT-SCIR/ELMoForManyLangs + + Example:: + + >>> embedding = ElmoEmbedding(vocab, model_dir_or_name='en', layers='2', requires_grad=True) + + :param vocab: 词表 + :param model_dir_or_name: 可以有两种方式调用预训练好的ELMo embedding:第一种是传入ELMo权重的文件名,第二种是传入ELMo版本的名称, + 目前支持的ELMo包括{`en` : 英文版本的ELMo, `cn` : 中文版本的ELMo,}。第二种情况将自动查看缓存中是否存在该模型,没有的话将自动下载 + :param layers: str, 指定返回的层数, 以,隔开不同的层。如果要返回第二层的结果'2', 返回后两层的结果'1,2'。不同的层的结果 + 按照这个顺序concat起来。默认为'2'。'mix'会使用可学习的权重结合不同层的表示(权重是否可训练与requires_grad保持一致, + 初始化权重对三层结果进行mean-pooling, 可以通过ElmoEmbedding.set_mix_weights_requires_grad()方法只将mix weights设置为可学习。) + :param requires_grad: bool, 该层是否需要gradient, 默认为False. + :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 + :param float dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。 + :param cache_word_reprs: 可以选择对word的表示进行cache; 设置为True的话,将在初始化的时候为每个word生成对应的embedding, + 并删除character encoder,之后将直接使用cache的embedding。默认为False。 + """ + + def __init__(self, vocab: Vocabulary, model_dir_or_name: str = 'en', layers: str = '2', requires_grad: bool = False, + word_dropout=0.0, dropout=0.0, cache_word_reprs: bool = False): + super(ElmoEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) + + # 根据model_dir_or_name检查是否存在并下载 + if model_dir_or_name.lower() in PRETRAINED_ELMO_MODEL_DIR: + PRETRAIN_URL = _get_base_url('elmo') + model_name = PRETRAINED_ELMO_MODEL_DIR[model_dir_or_name] + model_url = PRETRAIN_URL + model_name + model_dir = cached_path(model_url) + # 检查是否存在 + elif os.path.isdir(os.path.expanduser(os.path.abspath(model_dir_or_name))): + model_dir = model_dir_or_name + else: + raise ValueError(f"Cannot recognize {model_dir_or_name}.") + self.model = _ElmoModel(model_dir, vocab, cache_word_reprs=cache_word_reprs) + + if layers == 'mix': + self.layer_weights = nn.Parameter(torch.zeros(self.model.config['lstm']['n_layers'] + 1), + requires_grad=requires_grad) + self.gamma = nn.Parameter(torch.ones(1), requires_grad=requires_grad) + self._get_outputs = self._get_mixed_outputs + self._embed_size = self.model.config['lstm']['projection_dim'] * 2 + else: + layers = list(map(int, layers.split(','))) + assert len(layers) > 0, "Must choose one output" + for layer in layers: + assert 0 <= layer <= 2, "Layer index should be in range [0, 2]." + self.layers = layers + self._get_outputs = self._get_layer_outputs + self._embed_size = len(self.layers) * self.model.config['lstm']['projection_dim'] * 2 + + self.requires_grad = requires_grad + + def _get_mixed_outputs(self, outputs): + # outputs: num_layers x batch_size x max_len x hidden_size + # return: batch_size x max_len x hidden_size + weights = F.softmax(self.layer_weights + 1 / len(outputs), dim=0).to(outputs) + outputs = torch.einsum('l,lbij->bij', weights, outputs) + return self.gamma.to(outputs) * outputs + + def set_mix_weights_requires_grad(self, flag=True): + """ + 当初始化ElmoEmbedding时layers被设置为mix时,可以通过调用该方法设置mix weights是否可训练。如果layers不是mix,调用 + 该方法没有用。 + :param bool flag: 混合不同层表示的结果是否可以训练。 + :return: + """ + if hasattr(self, 'layer_weights'): + self.layer_weights.requires_grad = flag + self.gamma.requires_grad = flag + + def _get_layer_outputs(self, outputs): + if len(self.layers) == 1: + outputs = outputs[self.layers[0]] + else: + outputs = torch.cat(tuple([*outputs[self.layers]]), dim=-1) + + return outputs + + def forward(self, words: torch.LongTensor): + """ + 计算words的elmo embedding表示。根据elmo文章中介绍的ELMO实际上是有2L+1层结果,但是为了让结果比较容易拆分,token的 + 被重复了一次,使得实际上layer=0的结果是[token_embedding;token_embedding], 而layer=1的结果是[forward_hiddens; + backward_hiddens]. + + :param words: batch_size x max_len + :return: torch.FloatTensor. batch_size x max_len x (512*len(self.layers)) + """ + words = self.drop_word(words) + outputs = self._get_sent_reprs(words) + if outputs is not None: + return self.dropout(outputs) + outputs = self.model(words) + outputs = self._get_outputs(outputs) + return self.dropout(outputs) + + def _delete_model_weights(self): + for name in ['layers', 'model', 'layer_weights', 'gamma']: + if hasattr(self, name): + delattr(self, name) + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + + :return: + """ + requires_grads = set([param.requires_grad for name, param in self.named_parameters() + if 'words_to_chars_embedding' not in name and 'words_to_words' not in name]) + if len(requires_grads) == 1: + return requires_grads.pop() + else: + return None + + @requires_grad.setter + def requires_grad(self, value): + for name, param in self.named_parameters(): + if 'words_to_chars_embedding' in name or 'words_to_words' in name: # 这个不能加入到requires_grad中 + continue + param.requires_grad = value + + +class _ElmoModel(nn.Module): + """ + 该Module是ElmoEmbedding中进行所有的heavy lifting的地方。做的工作,包括 + (1) 根据配置,加载模型; + (2) 根据vocab,对模型中的embedding进行调整. 并将其正确初始化 + (3) 保存一个words与chars的对应转换,获取时自动进行相应的转换 + (4) 设计一个保存token的embedding,允许缓存word的表示。 + + """ + + def __init__(self, model_dir: str, vocab: Vocabulary = None, cache_word_reprs: bool = False): + super(_ElmoModel, self).__init__() + self.model_dir = model_dir + dir = os.walk(self.model_dir) + config_file = None + weight_file = None + config_count = 0 + weight_count = 0 + for path, dir_list, file_list in dir: + for file_name in file_list: + if file_name.__contains__(".json"): + config_file = file_name + config_count += 1 + elif file_name.__contains__(".pkl"): + weight_file = file_name + weight_count += 1 + if config_count > 1 or weight_count > 1: + raise Exception(f"Multiple config files(*.json) or weight files(*.hdf5) detected in {model_dir}.") + elif config_count == 0 or weight_count == 0: + raise Exception(f"No config file or weight file found in {model_dir}") + + config = json.load(open(os.path.join(model_dir, config_file), 'r')) + self.weight_file = os.path.join(model_dir, weight_file) + self.config = config + + OOV_TAG = '' + PAD_TAG = '' + BOS_TAG = '' + EOS_TAG = '' + BOW_TAG = '' + EOW_TAG = '' + + # For the model trained with character-based word encoder. + char_lexicon = {} + with codecs.open(os.path.join(model_dir, 'char.dic'), 'r', encoding='utf-8') as fpi: + for line in fpi: + tokens = line.strip().split('\t') + if len(tokens) == 1: + tokens.insert(0, '\u3000') + token, i = tokens + char_lexicon[token] = int(i) + + # 做一些sanity check + for special_word in [PAD_TAG, OOV_TAG, BOW_TAG, EOW_TAG]: + assert special_word in char_lexicon, f"{special_word} not found in char.dic." + + # 从vocab中构建char_vocab + char_vocab = Vocabulary(unknown=OOV_TAG, padding=PAD_TAG) + # 需要保证在里面 + char_vocab.add_word_lst([BOW_TAG, EOW_TAG, BOS_TAG, EOS_TAG]) + + for word, index in vocab: + char_vocab.add_word_lst(list(word)) + + self.bos_index, self.eos_index, self._pad_index = len(vocab), len(vocab) + 1, vocab.padding_idx + # 根据char_lexicon调整, 多设置一位,是预留给word padding的(该位置的char表示为全0表示) + char_emb_layer = nn.Embedding(len(char_vocab) + 1, int(config['char_cnn']['embedding']['dim']), + padding_idx=len(char_vocab)) + + # 读入预训练权重 这里的elmo_model 包含char_cnn和 lstm 的 state_dict + elmo_model = torch.load(os.path.join(self.model_dir, weight_file), map_location='cpu') + + char_embed_weights = elmo_model["char_cnn"]['char_emb_layer.weight'] + + found_char_count = 0 + for char, index in char_vocab: # 调整character embedding + if char in char_lexicon: + index_in_pre = char_lexicon.get(char) + found_char_count += 1 + else: + index_in_pre = char_lexicon[OOV_TAG] + char_emb_layer.weight.data[index] = char_embed_weights[index_in_pre] + + print(f"{found_char_count} out of {len(char_vocab)} characters were found in pretrained elmo embedding.") + # 生成words到chars的映射 + max_chars = config['char_cnn']['max_characters_per_token'] + + self.words_to_chars_embedding = nn.Parameter(torch.full((len(vocab) + 2, max_chars), + fill_value=len(char_vocab), + dtype=torch.long), + requires_grad=False) + for word, index in list(iter(vocab)) + [(BOS_TAG, len(vocab)), (EOS_TAG, len(vocab) + 1)]: + if len(word) + 2 > max_chars: + word = word[:max_chars - 2] + if index == self._pad_index: + continue + elif word == BOS_TAG or word == EOS_TAG: + char_ids = [char_vocab.to_index(BOW_TAG)] + [char_vocab.to_index(word)] + [ + char_vocab.to_index(EOW_TAG)] + char_ids += [char_vocab.to_index(PAD_TAG)] * (max_chars - len(char_ids)) + else: + char_ids = [char_vocab.to_index(BOW_TAG)] + [char_vocab.to_index(c) for c in word] + [ + char_vocab.to_index(EOW_TAG)] + char_ids += [char_vocab.to_index(PAD_TAG)] * (max_chars - len(char_ids)) + self.words_to_chars_embedding[index] = torch.LongTensor(char_ids) + + self.char_vocab = char_vocab + + self.token_embedder = ConvTokenEmbedder( + config, self.weight_file, None, char_emb_layer) + elmo_model["char_cnn"]['char_emb_layer.weight'] = char_emb_layer.weight + self.token_embedder.load_state_dict(elmo_model["char_cnn"]) + + self.output_dim = config['lstm']['projection_dim'] + + # lstm encoder + self.encoder = ElmobiLm(config) + self.encoder.load_state_dict(elmo_model["lstm"]) + + if cache_word_reprs: + if config['char_cnn']['embedding']['dim'] > 0: # 只有在使用了chars的情况下有用 + print("Start to generate cache word representations.") + batch_size = 320 + # bos eos + word_size = self.words_to_chars_embedding.size(0) + num_batches = word_size // batch_size + \ + int(word_size % batch_size != 0) + + self.cached_word_embedding = nn.Embedding(word_size, + config['lstm']['projection_dim']) + with torch.no_grad(): + for i in range(num_batches): + words = torch.arange(i * batch_size, + min((i + 1) * batch_size, word_size)).long() + chars = self.words_to_chars_embedding[words].unsqueeze(1) # batch_size x 1 x max_chars + word_reprs = self.token_embedder(words.unsqueeze(1), + chars).detach() # batch_size x 1 x config['encoder']['projection_dim'] + self.cached_word_embedding.weight.data[words] = word_reprs.squeeze(1) + + print("Finish generating cached word representations. Going to delete the character encoder.") + del self.token_embedder, self.words_to_chars_embedding + else: + print("There is no need to cache word representations, since no character information is used.") + + def forward(self, words): + """ + + :param words: batch_size x max_len + :return: num_layers x batch_size x max_len x hidden_size + """ + # 扩展, + batch_size, max_len = words.size() + expanded_words = words.new_zeros(batch_size, max_len + 2) # 因为pad一定为0, + seq_len = words.ne(self._pad_index).sum(dim=-1) + expanded_words[:, 1:-1] = words + expanded_words[:, 0].fill_(self.bos_index) + expanded_words[torch.arange(batch_size).to(words), seq_len + 1] = self.eos_index + seq_len = seq_len + 2 + zero_tensor = expanded_words.new_zeros(expanded_words.shape) + mask = (expanded_words == zero_tensor).unsqueeze(-1) + if hasattr(self, 'cached_word_embedding'): + token_embedding = self.cached_word_embedding(expanded_words) + else: + if hasattr(self, 'words_to_chars_embedding'): + chars = self.words_to_chars_embedding[expanded_words] + else: + chars = None + token_embedding = self.token_embedder(expanded_words, chars) # batch_size x max_len x embed_dim + + encoder_output = self.encoder(token_embedding, seq_len) + if encoder_output.size(2) < max_len + 2: + num_layers, _, output_len, hidden_size = encoder_output.size() + dummy_tensor = encoder_output.new_zeros(num_layers, batch_size, + max_len + 2 - output_len, hidden_size) + encoder_output = torch.cat((encoder_output, dummy_tensor), 2) + sz = encoder_output.size() # 2, batch_size, max_len, hidden_size + token_embedding = token_embedding.masked_fill(mask, 0) + token_embedding = torch.cat((token_embedding, token_embedding), dim=2).view(1, sz[1], sz[2], sz[3]) + encoder_output = torch.cat((token_embedding, encoder_output), dim=0) + + # 删除, . 这里没有精确地删除,但应该也不会影响最后的结果了。 + encoder_output = encoder_output[:, :, 1:-1] + return encoder_output diff --git a/fastNLP/embeddings/embedding.py b/fastNLP/embeddings/embedding.py new file mode 100644 index 00000000..1ac1df3b --- /dev/null +++ b/fastNLP/embeddings/embedding.py @@ -0,0 +1,180 @@ + +import torch.nn as nn +from abc import abstractmethod +import torch + +from .utils import get_embeddings + + +class Embedding(nn.Module): + """ + 别名::class:`fastNLP.embeddings.Embedding` :class:`fastNLP.embeddings.embedding.Embedding` + + Embedding组件. 可以通过self.num_embeddings获取词表大小; self.embedding_dim获取embedding的维度""" + + def __init__(self, init_embed, word_dropout=0, dropout=0.0, unk_index=None): + """ + + :param tuple(int,int),torch.FloatTensor,nn.Embedding,numpy.ndarray init_embed: Embedding的大小(传入tuple(int, int), + 第一个int为vocab_zie, 第二个int为embed_dim); 如果为Tensor, Embedding, ndarray等则直接使用该值初始化Embedding; + :param float word_dropout: 按照一定概率随机将word设置为unk_index,这样可以使得unk这个token得到足够的训练, 且会对网络有 + 一定的regularize的作用。 + :param float dropout: 对Embedding的输出的dropout。 + :param int unk_index: drop word时替换为的index。fastNLP的Vocabulary的unk_index默认为1。 + """ + super(Embedding, self).__init__() + + self.embed = get_embeddings(init_embed) + + self.dropout = nn.Dropout(dropout) + if not isinstance(self.embed, TokenEmbedding): + self._embed_size = self.embed.weight.size(1) + if word_dropout>0 and not isinstance(unk_index, int): + raise ValueError("When drop word is set, you need to pass in the unk_index.") + else: + self._embed_size = self.embed.embed_size + unk_index = self.embed.get_word_vocab().unknown_idx + self.unk_index = unk_index + self.word_dropout = word_dropout + + def forward(self, x): + """ + :param torch.LongTensor x: [batch, seq_len] + :return: torch.Tensor : [batch, seq_len, embed_dim] + """ + if self.word_dropout>0 and self.training: + mask = torch.ones_like(x).float() * self.word_dropout + mask = torch.bernoulli(mask).byte() # dropout_word越大,越多位置为1 + x = x.masked_fill(mask, self.unk_index) + x = self.embed(x) + return self.dropout(x) + + @property + def num_embedding(self)->int: + if isinstance(self.embed, nn.Embedding): + return self.embed.weight.size(0) + else: + return self.embed.num_embedding + + def __len__(self): + return len(self.embed) + + @property + def embed_size(self) -> int: + return self._embed_size + + @property + def embedding_dim(self) -> int: + return self._embed_size + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + :return: + """ + if not isinstance(self.embed, TokenEmbedding): + return self.embed.weight.requires_grad + else: + return self.embed.requires_grad + + @requires_grad.setter + def requires_grad(self, value): + if not isinstance(self.embed, TokenEmbedding): + self.embed.weight.requires_grad = value + else: + self.embed.requires_grad = value + + @property + def size(self): + if isinstance(self.embed, TokenEmbedding): + return self.embed.size + else: + return self.embed.weight.size() + + +class TokenEmbedding(nn.Module): + def __init__(self, vocab, word_dropout=0.0, dropout=0.0): + super(TokenEmbedding, self).__init__() + assert vocab.padding is not None, "Vocabulary must have a padding entry." + self._word_vocab = vocab + self._word_pad_index = vocab.padding_idx + if word_dropout>0: + assert vocab.unknown is not None, "Vocabulary must have unknown entry when you want to drop a word." + self.word_dropout = word_dropout + self._word_unk_index = vocab.unknown_idx + self.dropout_layer = nn.Dropout(dropout) + + def drop_word(self, words): + """ + 按照设定随机将words设置为unknown_index。 + + :param torch.LongTensor words: batch_size x max_len + :return: + """ + if self.word_dropout > 0 and self.training: + mask = torch.ones_like(words).float() * self.word_dropout + mask = torch.bernoulli(mask).byte() # dropout_word越大,越多位置为1 + words = words.masked_fill(mask, self._word_unk_index) + return words + + def dropout(self, words): + """ + 对embedding后的word表示进行drop。 + + :param torch.FloatTensor words: batch_size x max_len x embed_size + :return: + """ + return self.dropout_layer(words) + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + :return: + """ + requires_grads = set([param.requires_grad for param in self.parameters()]) + if len(requires_grads) == 1: + return requires_grads.pop() + else: + return None + + @requires_grad.setter + def requires_grad(self, value): + for param in self.parameters(): + param.requires_grad = value + + def __len__(self): + return len(self._word_vocab) + + @property + def embed_size(self) -> int: + return self._embed_size + + @property + def embedding_dim(self) -> int: + return self._embed_size + + @property + def num_embedding(self) -> int: + """ + 这个值可能会大于实际的embedding矩阵的大小。 + :return: + """ + return len(self._word_vocab) + + def get_word_vocab(self): + """ + 返回embedding的词典。 + + :return: Vocabulary + """ + return self._word_vocab + + @property + def size(self): + return torch.Size(self.num_embedding, self._embed_size) + + @abstractmethod + def forward(self, *input): + raise NotImplementedError diff --git a/fastNLP/embeddings/stack_embedding.py b/fastNLP/embeddings/stack_embedding.py new file mode 100644 index 00000000..e5c7c7a4 --- /dev/null +++ b/fastNLP/embeddings/stack_embedding.py @@ -0,0 +1,92 @@ +from typing import List + +import torch +from torch import nn as nn + +from .embedding import TokenEmbedding + + +class StackEmbedding(TokenEmbedding): + """ + 别名::class:`fastNLP.embeddings.StackEmbedding` :class:`fastNLP.embeddings.stack_embedding.StackEmbedding` + + 支持将多个embedding集合成一个embedding。 + + Example:: + + >>> embed_1 = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50', requires_grad=True) + >>> embed_2 = StaticEmbedding(vocab, model_dir_or_name='en-word2vec-300', requires_grad=True) + + + :param embeds: 一个由若干个TokenEmbedding组成的list,要求每一个TokenEmbedding的词表都保持一致 + :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。不同embedidng会在相同的位置 + 被设置为unknown。如果这里设置了dropout,则组成的embedding就不要再设置dropout了。 + :param float dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。 + + """ + def __init__(self, embeds: List[TokenEmbedding], word_dropout=0, dropout=0): + vocabs = [] + for embed in embeds: + if hasattr(embed, 'get_word_vocab'): + vocabs.append(embed.get_word_vocab()) + _vocab = vocabs[0] + for vocab in vocabs[1:]: + assert vocab == _vocab, "All embeddings in StackEmbedding should use the same word vocabulary." + + super(StackEmbedding, self).__init__(_vocab, word_dropout=word_dropout, dropout=dropout) + assert isinstance(embeds, list) + for embed in embeds: + assert isinstance(embed, TokenEmbedding), "Only TokenEmbedding type is supported." + self.embeds = nn.ModuleList(embeds) + self._embed_size = sum([embed.embed_size for embed in self.embeds]) + + def append(self, embed: TokenEmbedding): + """ + 添加一个embedding到结尾。 + :param embed: + :return: + """ + assert isinstance(embed, TokenEmbedding) + self.embeds.append(embed) + + def pop(self): + """ + 弹出最后一个embed + :return: + """ + return self.embeds.pop() + + @property + def embed_size(self): + return self._embed_size + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + :return: + """ + requires_grads = set([embed.requires_grad for embed in self.embeds()]) + if len(requires_grads) == 1: + return requires_grads.pop() + else: + return None + + @requires_grad.setter + def requires_grad(self, value): + for embed in self.embeds(): + embed.requires_grad = value + + def forward(self, words): + """ + 得到多个embedding的结果,并把结果按照顺序concat起来。 + + :param words: batch_size x max_len + :return: 返回的shape和当前这个stack embedding中embedding的组成有关 + """ + outputs = [] + words = self.drop_word(words) + for embed in self.embeds: + outputs.append(embed(words)) + outputs = self.dropout(torch.cat(outputs, dim=-1)) + return outputs \ No newline at end of file diff --git a/fastNLP/embeddings/static_embedding.py b/fastNLP/embeddings/static_embedding.py new file mode 100644 index 00000000..c8778e35 --- /dev/null +++ b/fastNLP/embeddings/static_embedding.py @@ -0,0 +1,217 @@ + +import os + +import torch +import torch.nn as nn +import numpy as np +import warnings + +from ..core.vocabulary import Vocabulary +from ..io.file_utils import PRETRAIN_STATIC_FILES, _get_base_url, cached_path +from .embedding import TokenEmbedding + + +class StaticEmbedding(TokenEmbedding): + """ + 别名::class:`fastNLP.embeddings.StaticEmbedding` :class:`fastNLP.embeddings.static_embedding.StaticEmbedding` + + StaticEmbedding组件. 给定embedding的名称,根据vocab从embedding中抽取相应的数据。该Embedding可以就按照正常的embedding使用了 + + Example:: + + >>> embed = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50') + + + :param vocab: Vocabulary. 若该项为None则会读取所有的embedding。 + :param model_dir_or_name: 可以有两种方式调用预训练好的static embedding:第一种是传入embedding的文件名,第二种是传入embedding + 的名称。目前支持的embedding包括{`en` 或者 `en-glove-840b-300` : glove.840B.300d, `en-glove-6b-50` : glove.6B.50d, + `en-word2vec-300` : GoogleNews-vectors-negative300}。第二种情况将自动查看缓存中是否存在该模型,没有的话将自动下载。 + :param bool requires_grad: 是否需要gradient. 默认为True + :param callable init_method: 如何初始化没有找到的值。可以使用torch.nn.init.*中各种方法。调用该方法时传入一个tensor对象。 + :param bool lower: 是否将vocab中的词语小写后再和预训练的词表进行匹配。如果你的词表中包含大写的词语,或者就是需要单独 + 为大写的词语开辟一个vector表示,则将lower设置为False。 + :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 + :param float dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。 + :param bool normailize: 是否对vector进行normalize,使得每个vector的norm为1。 + """ + def __init__(self, vocab: Vocabulary, model_dir_or_name: str='en', requires_grad: bool=True, init_method=None, + lower=False, dropout=0, word_dropout=0, normalize=False): + super(StaticEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) + + # 得到cache_path + if model_dir_or_name.lower() in PRETRAIN_STATIC_FILES: + PRETRAIN_URL = _get_base_url('static') + model_name = PRETRAIN_STATIC_FILES[model_dir_or_name] + model_url = PRETRAIN_URL + model_name + model_path = cached_path(model_url) + # 检查是否存在 + elif os.path.isfile(os.path.expanduser(os.path.abspath(model_dir_or_name))): + model_path = model_dir_or_name + else: + raise ValueError(f"Cannot recognize {model_dir_or_name}.") + + # 读取embedding + if lower: + lowered_vocab = Vocabulary(padding=vocab.padding, unknown=vocab.unknown) + for word, index in vocab: + if not vocab._is_word_no_create_entry(word): + lowered_vocab.add_word(word.lower()) # 先加入需要创建entry的 + for word in vocab._no_create_word.keys(): # 不需要创建entry的 + if word in vocab: + lowered_word = word.lower() + if lowered_word not in lowered_vocab.word_count: + lowered_vocab.add_word(lowered_word) + lowered_vocab._no_create_word[lowered_word] += 1 + print(f"All word in vocab have been lowered. There are {len(vocab)} words, {len(lowered_vocab)} unique lowered " + f"words.") + embedding = self._load_with_vocab(model_path, vocab=lowered_vocab, init_method=init_method, + normalize=normalize) + # 需要适配一下 + if not hasattr(self, 'words_to_words'): + self.words_to_words = torch.arange(len(lowered_vocab, )).long() + if lowered_vocab.unknown: + unknown_idx = lowered_vocab.unknown_idx + else: + unknown_idx = embedding.size(0) - 1 # 否则是最后一个为unknow + words_to_words = nn.Parameter(torch.full((len(vocab),), fill_value=unknown_idx).long(), + requires_grad=False) + for word, index in vocab: + if word not in lowered_vocab: + word = word.lower() + if lowered_vocab._is_word_no_create_entry(word): # 如果不需要创建entry,已经默认unknown了 + continue + words_to_words[index] = self.words_to_words[lowered_vocab.to_index(word)] + self.words_to_words = words_to_words + else: + embedding = self._load_with_vocab(model_path, vocab=vocab, init_method=init_method, + normalize=normalize) + self.embedding = nn.Embedding(num_embeddings=embedding.shape[0], embedding_dim=embedding.shape[1], + padding_idx=vocab.padding_idx, + max_norm=None, norm_type=2, scale_grad_by_freq=False, + sparse=False, _weight=embedding) + self._embed_size = self.embedding.weight.size(1) + self.requires_grad = requires_grad + + @property + def requires_grad(self): + """ + Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + :return: + """ + requires_grads = set([param.requires_grad for name, param in self.named_parameters() + if 'words_to_words' not in name]) + if len(requires_grads) == 1: + return requires_grads.pop() + else: + return None + + @requires_grad.setter + def requires_grad(self, value): + for name, param in self.named_parameters(): + if 'words_to_words' in name: + continue + param.requires_grad = value + + def _load_with_vocab(self, embed_filepath, vocab, dtype=np.float32, padding='', unknown='', + normalize=True, error='ignore', init_method=None): + """ + 从embed_filepath这个预训练的词向量中抽取出vocab这个词表的词的embedding。EmbedLoader将自动判断embed_filepath是 + word2vec(第一行只有两个元素)还是glove格式的数据。 + + :param str embed_filepath: 预训练的embedding的路径。 + :param vocab: 词表 :class:`~fastNLP.Vocabulary` 类型,读取出现在vocab中的词的embedding。 + 没有出现在vocab中的词的embedding将通过找到的词的embedding的正态分布采样出来,以使得整个Embedding是同分布的。 + :param dtype: 读出的embedding的类型 + :param str padding: 词表中padding的token + :param str unknown: 词表中unknown的token + :param bool normalize: 是否将每个vector归一化到norm为1 + :param str error: `ignore` , `strict` ; 如果 `ignore` ,错误将自动跳过; 如果 `strict` , 错误将抛出。 + 这里主要可能出错的地方在于词表有空行或者词表出现了维度不一致。 + :param init_method: 如何初始化没有找到的值。可以使用torch.nn.init.*中各种方法。默认使用torch.nn.init.zeros_ + :return torch.tensor: shape为 [len(vocab), dimension], dimension由pretrain的embedding决定。 + """ + assert isinstance(vocab, Vocabulary), "Only fastNLP.Vocabulary is supported." + if not os.path.exists(embed_filepath): + raise FileNotFoundError("`{}` does not exist.".format(embed_filepath)) + with open(embed_filepath, 'r', encoding='utf-8') as f: + line = f.readline().strip() + parts = line.split() + start_idx = 0 + if len(parts) == 2: + dim = int(parts[1]) + start_idx += 1 + else: + dim = len(parts) - 1 + f.seek(0) + matrix = {} + found_count = 0 + for idx, line in enumerate(f, start_idx): + try: + parts = line.strip().split() + word = ''.join(parts[:-dim]) + nums = parts[-dim:] + # 对齐unk与pad + if word == padding and vocab.padding is not None: + word = vocab.padding + elif word == unknown and vocab.unknown is not None: + word = vocab.unknown + if word in vocab: + index = vocab.to_index(word) + matrix[index] = torch.from_numpy(np.fromstring(' '.join(nums), sep=' ', dtype=dtype, count=dim)) + found_count += 1 + except Exception as e: + if error == 'ignore': + warnings.warn("Error occurred at the {} line.".format(idx)) + else: + print("Error occurred at the {} line.".format(idx)) + raise e + print("Found {} out of {} words in the pre-training embedding.".format(found_count, len(vocab))) + for word, index in vocab: + if index not in matrix and not vocab._is_word_no_create_entry(word): + if vocab.unknown_idx in matrix: # 如果有unkonwn,用unknown初始化 + matrix[index] = matrix[vocab.unknown_idx] + else: + matrix[index] = None + + vectors = torch.zeros(len(matrix), dim) + if init_method: + init_method(vectors) + else: + nn.init.uniform_(vectors, -np.sqrt(3/dim), np.sqrt(3/dim)) + + if vocab._no_create_word_length>0: + if vocab.unknown is None: # 创建一个专门的unknown + unknown_idx = len(matrix) + vectors = torch.cat((vectors, torch.zeros(1, dim)), dim=0).contiguous() + else: + unknown_idx = vocab.unknown_idx + words_to_words = nn.Parameter(torch.full((len(vocab),), fill_value=unknown_idx).long(), + requires_grad=False) + for order, (index, vec) in enumerate(matrix.items()): + if vec is not None: + vectors[order] = vec + words_to_words[index] = order + self.words_to_words = words_to_words + else: + for index, vec in matrix.items(): + if vec is not None: + vectors[index] = vec + + if normalize: + vectors /= (torch.norm(vectors, dim=1, keepdim=True) + 1e-12) + + return vectors + + def forward(self, words): + """ + 传入words的index + + :param words: torch.LongTensor, [batch_size, max_len] + :return: torch.FloatTensor, [batch_size, max_len, embed_size] + """ + if hasattr(self, 'words_to_words'): + words = self.words_to_words[words] + words = self.drop_word(words) + words = self.embedding(words) + words = self.dropout(words) + return words diff --git a/fastNLP/embeddings/utils.py b/fastNLP/embeddings/utils.py new file mode 100644 index 00000000..ff5d8733 --- /dev/null +++ b/fastNLP/embeddings/utils.py @@ -0,0 +1,47 @@ +import numpy as np +import torch +from torch import nn as nn + +from ..core.vocabulary import Vocabulary + + +def _construct_char_vocab_from_vocab(vocab:Vocabulary, min_freq:int=1): + """ + 给定一个word的vocabulary生成character的vocabulary. + + :param vocab: 从vocab + :param min_freq: + :return: + """ + char_vocab = Vocabulary(min_freq=min_freq) + for word, index in vocab: + if not vocab._is_word_no_create_entry(word): + char_vocab.add_word_lst(list(word)) + return char_vocab + + +def get_embeddings(init_embed): + """ + 根据输入的init_embed生成nn.Embedding对象。 + + :param init_embed: 可以是 tuple:(num_embedings, embedding_dim), 即embedding的大小和每个词的维度;也可以传入 + nn.Embedding 对象, 此时就以传入的对象作为embedding; 传入np.ndarray也行,将使用传入的ndarray作为作为Embedding初始 + 化; 传入orch.Tensor, 将使用传入的值作为Embedding初始化。 + :return nn.Embedding embeddings: + """ + if isinstance(init_embed, tuple): + res = nn.Embedding( + num_embeddings=init_embed[0], embedding_dim=init_embed[1]) + nn.init.uniform_(res.weight.data, a=-np.sqrt(3/res.weight.data.size(1)), + b=np.sqrt(3/res.weight.data.size(1))) + elif isinstance(init_embed, nn.Module): + res = init_embed + elif isinstance(init_embed, torch.Tensor): + res = nn.Embedding.from_pretrained(init_embed, freeze=False) + elif isinstance(init_embed, np.ndarray): + init_embed = torch.tensor(init_embed, dtype=torch.float32) + res = nn.Embedding.from_pretrained(init_embed, freeze=False) + else: + raise TypeError( + 'invalid init_embed type: {}'.format((type(init_embed)))) + return res \ No newline at end of file diff --git a/fastNLP/io/data_loader/matching.py b/fastNLP/io/data_loader/matching.py index cecaee96..481b5056 100644 --- a/fastNLP/io/data_loader/matching.py +++ b/fastNLP/io/data_loader/matching.py @@ -6,7 +6,7 @@ from ...core.const import Const from ...core.vocabulary import Vocabulary from ..base_loader import DataBundle, DataSetLoader from ..file_utils import _get_base_url, cached_path, PRETRAINED_BERT_MODEL_DIR -from ...modules.encoder._bert import BertTokenizer +from ...modules.encoder.bert import BertTokenizer class MatchingLoader(DataSetLoader): diff --git a/fastNLP/models/bert.py b/fastNLP/models/bert.py index fb186ce4..adecab60 100644 --- a/fastNLP/models/bert.py +++ b/fastNLP/models/bert.py @@ -8,7 +8,7 @@ from torch import nn from .base_model import BaseModel from ..core.const import Const from ..modules.encoder import BertModel -from ..modules.encoder._bert import BertConfig +from ..modules.encoder.bert import BertConfig class BertForSequenceClassification(BaseModel): diff --git a/fastNLP/models/biaffine_parser.py b/fastNLP/models/biaffine_parser.py index 8533e7af..dc13ba42 100644 --- a/fastNLP/models/biaffine_parser.py +++ b/fastNLP/models/biaffine_parser.py @@ -20,7 +20,7 @@ from ..modules.dropout import TimestepDropout from ..modules.encoder.transformer import TransformerEncoder from ..modules.encoder.variational_rnn import VarLSTM from ..modules.utils import initial_parameter -from ..modules.utils import get_embeddings +from ..embeddings.utils import get_embeddings from .base_model import BaseModel from ..core.utils import seq_len_to_mask diff --git a/fastNLP/models/cnn_text_classification.py b/fastNLP/models/cnn_text_classification.py index 081dd510..e7d5f0ca 100644 --- a/fastNLP/models/cnn_text_classification.py +++ b/fastNLP/models/cnn_text_classification.py @@ -6,8 +6,9 @@ import torch import torch.nn as nn from ..core.const import Const as C +from ..core.utils import seq_len_to_mask from ..modules import encoder -from fastNLP import seq_len_to_mask +from ..embeddings import embedding class CNNText(torch.nn.Module): @@ -33,7 +34,7 @@ class CNNText(torch.nn.Module): super(CNNText, self).__init__() # no support for pre-trained embedding currently - self.embed = encoder.Embedding(init_embed) + self.embed = embedding.Embedding(init_embed) self.conv_pool = encoder.ConvMaxpool( in_channels=self.embed.embedding_dim, out_channels=kernel_nums, diff --git a/fastNLP/models/sequence_labeling.py b/fastNLP/models/sequence_labeling.py index 8e6a5db1..ff8cbd30 100644 --- a/fastNLP/models/sequence_labeling.py +++ b/fastNLP/models/sequence_labeling.py @@ -10,6 +10,7 @@ import torch import torch.nn as nn from .base_model import BaseModel +from ..embeddings import embedding from ..modules import decoder, encoder from ..modules.decoder.crf import allowed_transitions from ..core.utils import seq_len_to_mask @@ -32,10 +33,10 @@ class SeqLabeling(BaseModel): def __init__(self, init_embed, hidden_size, num_classes): super(SeqLabeling, self).__init__() - self.Embedding = encoder.embedding.Embedding(init_embed) - self.Rnn = encoder.lstm.LSTM(self.Embedding.embedding_dim, hidden_size) + self.Embedding = embedding.Embedding(init_embed) + self.Rnn = encoder.LSTM(self.Embedding.embedding_dim, hidden_size) self.Linear = nn.Linear(hidden_size, num_classes) - self.Crf = decoder.crf.ConditionalRandomField(num_classes) + self.Crf = decoder.ConditionalRandomField(num_classes) self.mask = None def forward(self, words, seq_len, target): @@ -129,7 +130,7 @@ class AdvSeqLabel(nn.Module): super().__init__() - self.Embedding = encoder.embedding.Embedding(init_embed) + self.Embedding = embedding.Embedding(init_embed) self.norm1 = torch.nn.LayerNorm(self.Embedding.embedding_dim) self.Rnn = encoder.LSTM(input_size=self.Embedding.embedding_dim, hidden_size=hidden_size, num_layers=2, dropout=dropout, diff --git a/fastNLP/models/snli.py b/fastNLP/models/snli.py index d12524cc..0a76d48a 100644 --- a/fastNLP/models/snli.py +++ b/fastNLP/models/snli.py @@ -8,11 +8,10 @@ import torch.nn.functional as F from torch.nn import CrossEntropyLoss -from fastNLP.models import BaseModel -from fastNLP.modules.encoder.embedding import TokenEmbedding -from fastNLP.modules.encoder.lstm import LSTM -from fastNLP.core.const import Const -from fastNLP.core.utils import seq_len_to_mask +from .base_model import BaseModel +from ..embeddings.embedding import TokenEmbedding +from ..core.const import Const +from ..core.utils import seq_len_to_mask class ESIM(BaseModel): diff --git a/fastNLP/models/star_transformer.py b/fastNLP/models/star_transformer.py index bb91a5b6..97593f72 100644 --- a/fastNLP/models/star_transformer.py +++ b/fastNLP/models/star_transformer.py @@ -13,7 +13,7 @@ from torch import nn from ..modules.encoder.star_transformer import StarTransformer from ..core.utils import seq_len_to_mask -from ..modules.utils import get_embeddings +from ..embeddings.utils import get_embeddings from ..core.const import Const diff --git a/fastNLP/modules/__init__.py b/fastNLP/modules/__init__.py index 2cd2216c..43ec3f5f 100644 --- a/fastNLP/modules/__init__.py +++ b/fastNLP/modules/__init__.py @@ -24,7 +24,6 @@ __all__ = [ "ConvolutionCharEncoder", "LSTMCharEncoder", "ConvMaxpool", - "Embedding", "LSTM", "StarTransformer", "TransformerEncoder", @@ -48,4 +47,3 @@ from . import encoder from .decoder import * from .dropout import TimestepDropout from .encoder import * -from .utils import get_embeddings diff --git a/fastNLP/modules/encoder/__init__.py b/fastNLP/modules/encoder/__init__.py index 7b5bc070..051a0c01 100644 --- a/fastNLP/modules/encoder/__init__.py +++ b/fastNLP/modules/encoder/__init__.py @@ -1,19 +1,11 @@ __all__ = [ - # "BertModel", + "BertModel", "ConvolutionCharEncoder", "LSTMCharEncoder", "ConvMaxpool", - "Embedding", - "StaticEmbedding", - "ElmoEmbedding", - "BertEmbedding", - "StackEmbedding", - "LSTMCharEmbedding", - "CNNCharEmbedding", - "LSTM", "StarTransformer", @@ -31,12 +23,10 @@ __all__ = [ "MultiHeadAttention", ] -from ._bert import BertModel -from .bert import BertWordPieceEncoder + +from .bert import BertModel from .char_encoder import ConvolutionCharEncoder, LSTMCharEncoder from .conv_maxpool import ConvMaxpool -from .embedding import Embedding, StaticEmbedding, ElmoEmbedding, BertEmbedding, \ - StackEmbedding, LSTMCharEmbedding, CNNCharEmbedding from .lstm import LSTM from .star_transformer import StarTransformer from .transformer import TransformerEncoder diff --git a/fastNLP/modules/encoder/_bert.py b/fastNLP/modules/encoder/_bert.py deleted file mode 100644 index 61a5d7d1..00000000 --- a/fastNLP/modules/encoder/_bert.py +++ /dev/null @@ -1,1069 +0,0 @@ - - - -""" -这个页面的代码很大程度上参考(复制粘贴)了https://github.com/huggingface/pytorch-pretrained-BERT的代码, 如果你发现该代码对你 - 有用,也请引用一下他们。 -""" - - - -from ...core.vocabulary import Vocabulary -import collections - -import unicodedata -import numpy as np -from itertools import chain -import copy -import json -import math -import os - -import torch -from torch import nn -import glob -import sys - -CONFIG_FILE = 'bert_config.json' - - -class BertConfig(object): - """Configuration class to store the configuration of a `BertModel`. - """ - def __init__(self, - vocab_size_or_config_json_file, - hidden_size=768, - num_hidden_layers=12, - num_attention_heads=12, - intermediate_size=3072, - hidden_act="gelu", - hidden_dropout_prob=0.1, - attention_probs_dropout_prob=0.1, - max_position_embeddings=512, - type_vocab_size=2, - initializer_range=0.02, - layer_norm_eps=1e-12): - """Constructs BertConfig. - - Args: - vocab_size_or_config_json_file: Vocabulary size of `inputs_ids` in `BertModel`. - hidden_size: Size of the encoder layers and the pooler layer. - num_hidden_layers: Number of hidden layers in the Transformer encoder. - num_attention_heads: Number of attention heads for each attention layer in - the Transformer encoder. - intermediate_size: The size of the "intermediate" (i.e., feed-forward) - layer in the Transformer encoder. - hidden_act: The non-linear activation function (function or string) in the - encoder and pooler. If string, "gelu", "relu" and "swish" are supported. - hidden_dropout_prob: The dropout probabilitiy for all fully connected - layers in the embeddings, encoder, and pooler. - attention_probs_dropout_prob: The dropout ratio for the attention - probabilities. - max_position_embeddings: The maximum sequence length that this model might - ever be used with. Typically set this to something large just in case - (e.g., 512 or 1024 or 2048). - type_vocab_size: The vocabulary size of the `token_type_ids` passed into - `BertModel`. - initializer_range: The sttdev of the truncated_normal_initializer for - initializing all weight matrices. - layer_norm_eps: The epsilon used by LayerNorm. - """ - if isinstance(vocab_size_or_config_json_file, str) or (sys.version_info[0] == 2 - and isinstance(vocab_size_or_config_json_file, unicode)): - with open(vocab_size_or_config_json_file, "r", encoding='utf-8') as reader: - json_config = json.loads(reader.read()) - for key, value in json_config.items(): - self.__dict__[key] = value - elif isinstance(vocab_size_or_config_json_file, int): - self.vocab_size = vocab_size_or_config_json_file - self.hidden_size = hidden_size - self.num_hidden_layers = num_hidden_layers - self.num_attention_heads = num_attention_heads - self.hidden_act = hidden_act - self.intermediate_size = intermediate_size - self.hidden_dropout_prob = hidden_dropout_prob - self.attention_probs_dropout_prob = attention_probs_dropout_prob - self.max_position_embeddings = max_position_embeddings - self.type_vocab_size = type_vocab_size - self.initializer_range = initializer_range - self.layer_norm_eps = layer_norm_eps - else: - raise ValueError("First argument must be either a vocabulary size (int)" - "or the path to a pretrained model config file (str)") - - @classmethod - def from_dict(cls, json_object): - """Constructs a `BertConfig` from a Python dictionary of parameters.""" - config = BertConfig(vocab_size_or_config_json_file=-1) - for key, value in json_object.items(): - config.__dict__[key] = value - return config - - @classmethod - def from_json_file(cls, json_file): - """Constructs a `BertConfig` from a json file of parameters.""" - with open(json_file, "r", encoding='utf-8') as reader: - text = reader.read() - return cls.from_dict(json.loads(text)) - - def __repr__(self): - return str(self.to_json_string()) - - def to_dict(self): - """Serializes this instance to a Python dictionary.""" - output = copy.deepcopy(self.__dict__) - return output - - def to_json_string(self): - """Serializes this instance to a JSON string.""" - return json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n" - - def to_json_file(self, json_file_path): - """ Save this instance to a json file.""" - with open(json_file_path, "w", encoding='utf-8') as writer: - writer.write(self.to_json_string()) - - -def gelu(x): - return x * 0.5 * (1.0 + torch.erf(x / math.sqrt(2.0))) - - -def swish(x): - return x * torch.sigmoid(x) - - -ACT2FN = {"gelu": gelu, "relu": torch.nn.functional.relu, "swish": swish} - - -class BertLayerNorm(nn.Module): - def __init__(self, hidden_size, eps=1e-12): - """Construct a layernorm module in the TF style (epsilon inside the square root). - """ - super(BertLayerNorm, self).__init__() - self.weight = nn.Parameter(torch.ones(hidden_size)) - self.bias = nn.Parameter(torch.zeros(hidden_size)) - self.variance_epsilon = eps - - def forward(self, x): - u = x.mean(-1, keepdim=True) - s = (x - u).pow(2).mean(-1, keepdim=True) - x = (x - u) / torch.sqrt(s + self.variance_epsilon) - return self.weight * x + self.bias - - -class BertEmbeddings(nn.Module): - """Construct the embeddings from word, position and token_type embeddings. - """ - def __init__(self, config): - super(BertEmbeddings, self).__init__() - self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=0) - self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) - self.token_type_embeddings = nn.Embedding(config.type_vocab_size, config.hidden_size) - - # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load - # any TensorFlow checkpoint file - self.LayerNorm = BertLayerNorm(config.hidden_size, eps=config.layer_norm_eps) - self.dropout = nn.Dropout(config.hidden_dropout_prob) - - def forward(self, input_ids, token_type_ids=None): - seq_length = input_ids.size(1) - position_ids = torch.arange(seq_length, dtype=torch.long, device=input_ids.device) - position_ids = position_ids.unsqueeze(0).expand_as(input_ids) - if token_type_ids is None: - token_type_ids = torch.zeros_like(input_ids) - - words_embeddings = self.word_embeddings(input_ids) - position_embeddings = self.position_embeddings(position_ids) - token_type_embeddings = self.token_type_embeddings(token_type_ids) - - embeddings = words_embeddings + position_embeddings + token_type_embeddings - embeddings = self.LayerNorm(embeddings) - embeddings = self.dropout(embeddings) - return embeddings - - -class BertSelfAttention(nn.Module): - def __init__(self, config): - super(BertSelfAttention, self).__init__() - if config.hidden_size % config.num_attention_heads != 0: - raise ValueError( - "The hidden size (%d) is not a multiple of the number of attention " - "heads (%d)" % (config.hidden_size, config.num_attention_heads)) - self.num_attention_heads = config.num_attention_heads - self.attention_head_size = int(config.hidden_size / config.num_attention_heads) - self.all_head_size = self.num_attention_heads * self.attention_head_size - - self.query = nn.Linear(config.hidden_size, self.all_head_size) - self.key = nn.Linear(config.hidden_size, self.all_head_size) - self.value = nn.Linear(config.hidden_size, self.all_head_size) - - self.dropout = nn.Dropout(config.attention_probs_dropout_prob) - - def transpose_for_scores(self, x): - new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) - x = x.view(*new_x_shape) - return x.permute(0, 2, 1, 3) - - def forward(self, hidden_states, attention_mask): - mixed_query_layer = self.query(hidden_states) - mixed_key_layer = self.key(hidden_states) - mixed_value_layer = self.value(hidden_states) - - query_layer = self.transpose_for_scores(mixed_query_layer) - key_layer = self.transpose_for_scores(mixed_key_layer) - value_layer = self.transpose_for_scores(mixed_value_layer) - - # Take the dot product between "query" and "key" to get the raw attention scores. - attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) - attention_scores = attention_scores / math.sqrt(self.attention_head_size) - # Apply the attention mask is (precomputed for all layers in BertModel forward() function) - attention_scores = attention_scores + attention_mask - - # Normalize the attention scores to probabilities. - attention_probs = nn.Softmax(dim=-1)(attention_scores) - - # This is actually dropping out entire tokens to attend to, which might - # seem a bit unusual, but is taken from the original Transformer paper. - attention_probs = self.dropout(attention_probs) - - context_layer = torch.matmul(attention_probs, value_layer) - context_layer = context_layer.permute(0, 2, 1, 3).contiguous() - new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) - context_layer = context_layer.view(*new_context_layer_shape) - return context_layer - - -class BertSelfOutput(nn.Module): - def __init__(self, config): - super(BertSelfOutput, self).__init__() - self.dense = nn.Linear(config.hidden_size, config.hidden_size) - self.LayerNorm = BertLayerNorm(config.hidden_size, eps=config.layer_norm_eps) - self.dropout = nn.Dropout(config.hidden_dropout_prob) - - def forward(self, hidden_states, input_tensor): - hidden_states = self.dense(hidden_states) - hidden_states = self.dropout(hidden_states) - hidden_states = self.LayerNorm(hidden_states + input_tensor) - return hidden_states - - -class BertAttention(nn.Module): - def __init__(self, config): - super(BertAttention, self).__init__() - self.self = BertSelfAttention(config) - self.output = BertSelfOutput(config) - - def forward(self, input_tensor, attention_mask): - self_output = self.self(input_tensor, attention_mask) - attention_output = self.output(self_output, input_tensor) - return attention_output - - -class BertIntermediate(nn.Module): - def __init__(self, config): - super(BertIntermediate, self).__init__() - self.dense = nn.Linear(config.hidden_size, config.intermediate_size) - if isinstance(config.hidden_act, str) or (sys.version_info[0] == 2 and isinstance(config.hidden_act, unicode)): - self.intermediate_act_fn = ACT2FN[config.hidden_act] - else: - self.intermediate_act_fn = config.hidden_act - - def forward(self, hidden_states): - hidden_states = self.dense(hidden_states) - hidden_states = self.intermediate_act_fn(hidden_states) - return hidden_states - - -class BertOutput(nn.Module): - def __init__(self, config): - super(BertOutput, self).__init__() - self.dense = nn.Linear(config.intermediate_size, config.hidden_size) - self.LayerNorm = BertLayerNorm(config.hidden_size, eps=config.layer_norm_eps) - self.dropout = nn.Dropout(config.hidden_dropout_prob) - - def forward(self, hidden_states, input_tensor): - hidden_states = self.dense(hidden_states) - hidden_states = self.dropout(hidden_states) - hidden_states = self.LayerNorm(hidden_states + input_tensor) - return hidden_states - - -class BertLayer(nn.Module): - def __init__(self, config): - super(BertLayer, self).__init__() - self.attention = BertAttention(config) - self.intermediate = BertIntermediate(config) - self.output = BertOutput(config) - - def forward(self, hidden_states, attention_mask): - attention_output = self.attention(hidden_states, attention_mask) - intermediate_output = self.intermediate(attention_output) - layer_output = self.output(intermediate_output, attention_output) - return layer_output - - -class BertEncoder(nn.Module): - def __init__(self, config): - super(BertEncoder, self).__init__() - layer = BertLayer(config) - self.layer = nn.ModuleList([copy.deepcopy(layer) for _ in range(config.num_hidden_layers)]) - - def forward(self, hidden_states, attention_mask, output_all_encoded_layers=True): - all_encoder_layers = [] - for layer_module in self.layer: - hidden_states = layer_module(hidden_states, attention_mask) - if output_all_encoded_layers: - all_encoder_layers.append(hidden_states) - if not output_all_encoded_layers: - all_encoder_layers.append(hidden_states) - return all_encoder_layers - - -class BertPooler(nn.Module): - def __init__(self, config): - super(BertPooler, self).__init__() - self.dense = nn.Linear(config.hidden_size, config.hidden_size) - self.activation = nn.Tanh() - - def forward(self, hidden_states): - # We "pool" the model by simply taking the hidden state corresponding - # to the first token. - first_token_tensor = hidden_states[:, 0] - pooled_output = self.dense(first_token_tensor) - pooled_output = self.activation(pooled_output) - return pooled_output - - -class BertModel(nn.Module): - """BERT(Bidirectional Embedding Representations from Transformers). - - 如果你想使用预训练好的权重矩阵,请在以下网址下载. - sources:: - - 'bert-base-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-pytorch_model.bin", - 'bert-large-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-pytorch_model.bin", - 'bert-base-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-pytorch_model.bin", - 'bert-large-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-pytorch_model.bin", - 'bert-base-multilingual-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-uncased-pytorch_model.bin", - 'bert-base-multilingual-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-cased-pytorch_model.bin", - 'bert-base-chinese': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese-pytorch_model.bin", - 'bert-base-german-cased': "https://int-deepset-models-bert.s3.eu-central-1.amazonaws.com/pytorch/bert-base-german-cased-pytorch_model.bin", - 'bert-large-uncased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-pytorch_model.bin", - 'bert-large-cased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-pytorch_model.bin", - 'bert-large-uncased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-finetuned-squad-pytorch_model.bin", - 'bert-large-cased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-finetuned-squad-pytorch_model.bin", - 'bert-base-cased-finetuned-mrpc': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-finetuned-mrpc-pytorch_model.bin" - - - 用预训练权重矩阵来建立BERT模型:: - - model = BertModel.from_pretrained("path/to/weights/directory") - - 用随机初始化权重矩阵来建立BERT模型:: - - model = BertModel() - - :param int vocab_size: 词表大小,默认值为30522,为BERT English uncase版本的词表大小 - :param int hidden_size: 隐层大小,默认值为768,为BERT base的版本 - :param int num_hidden_layers: 隐藏层数,默认值为12,为BERT base的版本 - :param int num_attention_heads: 多头注意力头数,默认值为12,为BERT base的版本 - :param int intermediate_size: FFN隐藏层大小,默认值是3072,为BERT base的版本 - :param str hidden_act: FFN隐藏层激活函数,默认值为``gelu`` - :param float hidden_dropout_prob: FFN隐藏层dropout,默认值为0.1 - :param float attention_probs_dropout_prob: Attention层的dropout,默认值为0.1 - :param int max_position_embeddings: 最大的序列长度,默认值为512, - :param int type_vocab_size: 最大segment数量,默认值为2 - :param int initializer_range: 初始化权重范围,默认值为0.02 - """ - - def __init__(self, config, *inputs, **kwargs): - super(BertModel, self).__init__() - if not isinstance(config, BertConfig): - raise ValueError( - "Parameter config in `{}(config)` should be an instance of class `BertConfig`. " - "To create a model from a Google pretrained model use " - "`model = {}.from_pretrained(PRETRAINED_MODEL_NAME)`".format( - self.__class__.__name__, self.__class__.__name__ - )) - super(BertModel, self).__init__() - self.config = config - self.hidden_size = self.config.hidden_size - self.embeddings = BertEmbeddings(config) - self.encoder = BertEncoder(config) - self.pooler = BertPooler(config) - self.apply(self.init_bert_weights) - - def init_bert_weights(self, module): - """ Initialize the weights. - """ - if isinstance(module, (nn.Linear, nn.Embedding)): - # Slightly different from the TF version which uses truncated_normal for initialization - # cf https://github.com/pytorch/pytorch/pull/5617 - module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) - elif isinstance(module, BertLayerNorm): - module.bias.data.zero_() - module.weight.data.fill_(1.0) - if isinstance(module, nn.Linear) and module.bias is not None: - module.bias.data.zero_() - - def forward(self, input_ids, token_type_ids=None, attention_mask=None, output_all_encoded_layers=True): - if attention_mask is None: - attention_mask = torch.ones_like(input_ids) - if token_type_ids is None: - token_type_ids = torch.zeros_like(input_ids) - - # We create a 3D attention mask from a 2D tensor mask. - # Sizes are [batch_size, 1, 1, to_seq_length] - # So we can broadcast to [batch_size, num_heads, from_seq_length, to_seq_length] - # this attention mask is more simple than the triangular masking of causal attention - # used in OpenAI GPT, we just need to prepare the broadcast dimension here. - extended_attention_mask = attention_mask.unsqueeze(1).unsqueeze(2) - - # Since attention_mask is 1.0 for positions we want to attend and 0.0 for - # masked positions, this operation will create a tensor which is 0.0 for - # positions we want to attend and -10000.0 for masked positions. - # Since we are adding it to the raw scores before the softmax, this is - # effectively the same as removing these entirely. - extended_attention_mask = extended_attention_mask.to(dtype=next(self.parameters()).dtype) # fp16 compatibility - extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 - - embedding_output = self.embeddings(input_ids, token_type_ids) - encoded_layers = self.encoder(embedding_output, - extended_attention_mask, - output_all_encoded_layers=output_all_encoded_layers) - sequence_output = encoded_layers[-1] - pooled_output = self.pooler(sequence_output) - if not output_all_encoded_layers: - encoded_layers = encoded_layers[-1] - return encoded_layers, pooled_output - - @classmethod - def from_pretrained(cls, pretrained_model_dir, *inputs, **kwargs): - state_dict = kwargs.get('state_dict', None) - kwargs.pop('state_dict', None) - cache_dir = kwargs.get('cache_dir', None) - kwargs.pop('cache_dir', None) - from_tf = kwargs.get('from_tf', False) - kwargs.pop('from_tf', None) - # Load config - config_file = os.path.join(pretrained_model_dir, CONFIG_FILE) - config = BertConfig.from_json_file(config_file) - # logger.info("Model config {}".format(config)) - # Instantiate model. - model = cls(config, *inputs, **kwargs) - if state_dict is None: - files = glob.glob(os.path.join(pretrained_model_dir, '*.bin')) - if len(files)==0: - raise FileNotFoundError(f"There is no *.bin file in {pretrained_model_dir}") - elif len(files)>1: - raise FileExistsError(f"There are multiple *.bin files in {pretrained_model_dir}") - weights_path = files[0] - state_dict = torch.load(weights_path, map_location='cpu') - - old_keys = [] - new_keys = [] - for key in state_dict.keys(): - new_key = None - if 'gamma' in key: - new_key = key.replace('gamma', 'weight') - if 'beta' in key: - new_key = key.replace('beta', 'bias') - if new_key: - old_keys.append(key) - new_keys.append(new_key) - for old_key, new_key in zip(old_keys, new_keys): - state_dict[new_key] = state_dict.pop(old_key) - - missing_keys = [] - unexpected_keys = [] - error_msgs = [] - # copy state_dict so _load_from_state_dict can modify it - metadata = getattr(state_dict, '_metadata', None) - state_dict = state_dict.copy() - if metadata is not None: - state_dict._metadata = metadata - - def load(module, prefix=''): - local_metadata = {} if metadata is None else metadata.get(prefix[:-1], {}) - module._load_from_state_dict( - state_dict, prefix, local_metadata, True, missing_keys, unexpected_keys, error_msgs) - for name, child in module._modules.items(): - if child is not None: - load(child, prefix + name + '.') - - load(model, prefix='' if hasattr(model, 'bert') else 'bert.') - if len(missing_keys) > 0: - print("Weights of {} not initialized from pretrained model: {}".format( - model.__class__.__name__, missing_keys)) - if len(unexpected_keys) > 0: - print("Weights from pretrained model not used in {}: {}".format( - model.__class__.__name__, unexpected_keys)) - return model - - -def whitespace_tokenize(text): - """Runs basic whitespace cleaning and splitting on a piece of text.""" - text = text.strip() - if not text: - return [] - tokens = text.split() - return tokens - - -class WordpieceTokenizer(object): - """Runs WordPiece tokenization.""" - - def __init__(self, vocab, unk_token="[UNK]", max_input_chars_per_word=100): - self.vocab = vocab - self.unk_token = unk_token - self.max_input_chars_per_word = max_input_chars_per_word - - def tokenize(self, text): - """Tokenizes a piece of text into its word pieces. - - This uses a greedy longest-match-first algorithm to perform tokenization - using the given vocabulary. - - For example: - input = "unaffable" - output = ["un", "##aff", "##able"] - - Args: - text: A single token or whitespace separated tokens. This should have - already been passed through `BasicTokenizer`. - - Returns: - A list of wordpiece tokens. - """ - - output_tokens = [] - for token in whitespace_tokenize(text): - chars = list(token) - if len(chars) > self.max_input_chars_per_word: - output_tokens.append(self.unk_token) - continue - - is_bad = False - start = 0 - sub_tokens = [] - while start < len(chars): - end = len(chars) - cur_substr = None - while start < end: - substr = "".join(chars[start:end]) - if start > 0: - substr = "##" + substr - if substr in self.vocab: - cur_substr = substr - break - end -= 1 - if cur_substr is None: - is_bad = True - break - sub_tokens.append(cur_substr) - start = end - - if is_bad: - output_tokens.append(self.unk_token) - else: - output_tokens.extend(sub_tokens) - return output_tokens - - -def load_vocab(vocab_file): - """Loads a vocabulary file into a dictionary.""" - vocab = collections.OrderedDict() - index = 0 - with open(vocab_file, "r", encoding="utf-8") as reader: - while True: - token = reader.readline() - if not token: - break - token = token.strip() - vocab[token] = index - index += 1 - return vocab - -class BasicTokenizer(object): - """Runs basic tokenization (punctuation splitting, lower casing, etc.).""" - - def __init__(self, - do_lower_case=True, - never_split=("[UNK]", "[SEP]", "[PAD]", "[CLS]", "[MASK]")): - """Constructs a BasicTokenizer. - - Args: - do_lower_case: Whether to lower case the input. - """ - self.do_lower_case = do_lower_case - self.never_split = never_split - - def tokenize(self, text): - """Tokenizes a piece of text.""" - text = self._clean_text(text) - # This was added on November 1st, 2018 for the multilingual and Chinese - # models. This is also applied to the English models now, but it doesn't - # matter since the English models were not trained on any Chinese data - # and generally don't have any Chinese data in them (there are Chinese - # characters in the vocabulary because Wikipedia does have some Chinese - # words in the English Wikipedia.). - text = self._tokenize_chinese_chars(text) - orig_tokens = whitespace_tokenize(text) - split_tokens = [] - for token in orig_tokens: - if self.do_lower_case and token not in self.never_split: - token = token.lower() - token = self._run_strip_accents(token) - split_tokens.extend(self._run_split_on_punc(token)) - - output_tokens = whitespace_tokenize(" ".join(split_tokens)) - return output_tokens - - def _run_strip_accents(self, text): - """Strips accents from a piece of text.""" - text = unicodedata.normalize("NFD", text) - output = [] - for char in text: - cat = unicodedata.category(char) - if cat == "Mn": - continue - output.append(char) - return "".join(output) - - def _run_split_on_punc(self, text): - """Splits punctuation on a piece of text.""" - if text in self.never_split: - return [text] - chars = list(text) - i = 0 - start_new_word = True - output = [] - while i < len(chars): - char = chars[i] - if _is_punctuation(char): - output.append([char]) - start_new_word = True - else: - if start_new_word: - output.append([]) - start_new_word = False - output[-1].append(char) - i += 1 - - return ["".join(x) for x in output] - - def _tokenize_chinese_chars(self, text): - """Adds whitespace around any CJK character.""" - output = [] - for char in text: - cp = ord(char) - if self._is_chinese_char(cp): - output.append(" ") - output.append(char) - output.append(" ") - else: - output.append(char) - return "".join(output) - - def _is_chinese_char(self, cp): - """Checks whether CP is the codepoint of a CJK character.""" - # This defines a "chinese character" as anything in the CJK Unicode block: - # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) - # - # Note that the CJK Unicode block is NOT all Japanese and Korean characters, - # despite its name. The modern Korean Hangul alphabet is a different block, - # as is Japanese Hiragana and Katakana. Those alphabets are used to write - # space-separated words, so they are not treated specially and handled - # like the all of the other languages. - if ((cp >= 0x4E00 and cp <= 0x9FFF) or # - (cp >= 0x3400 and cp <= 0x4DBF) or # - (cp >= 0x20000 and cp <= 0x2A6DF) or # - (cp >= 0x2A700 and cp <= 0x2B73F) or # - (cp >= 0x2B740 and cp <= 0x2B81F) or # - (cp >= 0x2B820 and cp <= 0x2CEAF) or - (cp >= 0xF900 and cp <= 0xFAFF) or # - (cp >= 0x2F800 and cp <= 0x2FA1F)): # - return True - - return False - - def _clean_text(self, text): - """Performs invalid character removal and whitespace cleanup on text.""" - output = [] - for char in text: - cp = ord(char) - if cp == 0 or cp == 0xfffd or _is_control(char): - continue - if _is_whitespace(char): - output.append(" ") - else: - output.append(char) - return "".join(output) - - -def _is_whitespace(char): - """Checks whether `chars` is a whitespace character.""" - # \t, \n, and \r are technically contorl characters but we treat them - # as whitespace since they are generally considered as such. - if char == " " or char == "\t" or char == "\n" or char == "\r": - return True - cat = unicodedata.category(char) - if cat == "Zs": - return True - return False - - -def _is_control(char): - """Checks whether `chars` is a control character.""" - # These are technically control characters but we count them as whitespace - # characters. - if char == "\t" or char == "\n" or char == "\r": - return False - cat = unicodedata.category(char) - if cat.startswith("C"): - return True - return False - - -def _is_punctuation(char): - """Checks whether `chars` is a punctuation character.""" - cp = ord(char) - # We treat all non-letter/number ASCII as punctuation. - # Characters such as "^", "$", and "`" are not in the Unicode - # Punctuation class but we treat them as punctuation anyways, for - # consistency. - if ((cp >= 33 and cp <= 47) or (cp >= 58 and cp <= 64) or - (cp >= 91 and cp <= 96) or (cp >= 123 and cp <= 126)): - return True - cat = unicodedata.category(char) - if cat.startswith("P"): - return True - return False - - -class BertTokenizer(object): - """Runs end-to-end tokenization: punctuation splitting + wordpiece""" - - def __init__(self, vocab_file, do_lower_case=True, max_len=None, do_basic_tokenize=True, - never_split=("[UNK]", "[SEP]", "[PAD]", "[CLS]", "[MASK]")): - """Constructs a BertTokenizer. - - Args: - vocab_file: Path to a one-wordpiece-per-line vocabulary file - do_lower_case: Whether to lower case the input - Only has an effect when do_wordpiece_only=False - do_basic_tokenize: Whether to do basic tokenization before wordpiece. - max_len: An artificial maximum length to truncate tokenized sequences to; - Effective maximum length is always the minimum of this - value (if specified) and the underlying BERT model's - sequence length. - never_split: List of tokens which will never be split during tokenization. - Only has an effect when do_wordpiece_only=False - """ - if not os.path.isfile(vocab_file): - raise ValueError( - "Can't find a vocabulary file at path '{}'. To load the vocabulary from a Google pretrained " - "model use `tokenizer = BertTokenizer.from_pretrained(PRETRAINED_MODEL_NAME)`".format(vocab_file)) - self.vocab = load_vocab(vocab_file) - self.ids_to_tokens = collections.OrderedDict( - [(ids, tok) for tok, ids in self.vocab.items()]) - self.do_basic_tokenize = do_basic_tokenize - if do_basic_tokenize: - self.basic_tokenizer = BasicTokenizer(do_lower_case=do_lower_case, - never_split=never_split) - self.wordpiece_tokenizer = WordpieceTokenizer(vocab=self.vocab) - self.max_len = max_len if max_len is not None else int(1e12) - - def _reinit_on_new_vocab(self, vocab): - """ - 在load bert之后,可能会对vocab进行重新排列。重新排列之后调用这个函数重新初始化与vocab相关的性质 - - :param vocab: - :return: - """ - self.vocab = vocab - self.wordpiece_tokenizer = WordpieceTokenizer(vocab=self.vocab) - - def tokenize(self, text): - split_tokens = [] - if self.do_basic_tokenize: - for token in self.basic_tokenizer.tokenize(text): - for sub_token in self.wordpiece_tokenizer.tokenize(token): - split_tokens.append(sub_token) - else: - split_tokens = self.wordpiece_tokenizer.tokenize(text) - return split_tokens - - def convert_tokens_to_ids(self, tokens): - """Converts a sequence of tokens into ids using the vocab.""" - ids = [] - for token in tokens: - ids.append(self.vocab[token]) - if len(ids) > self.max_len: - print( - "Token indices sequence length is longer than the specified maximum " - " sequence length for this BERT model ({} > {}). Running this" - " sequence through BERT will result in indexing errors".format(len(ids), self.max_len) - ) - return ids - - def convert_ids_to_tokens(self, ids): - """Converts a sequence of ids in wordpiece tokens using the vocab.""" - tokens = [] - for i in ids: - tokens.append(self.ids_to_tokens[i]) - return tokens - - def save_vocabulary(self, vocab_path): - """Save the tokenizer vocabulary to a directory or file.""" - index = 0 - if os.path.isdir(vocab_path): - vocab_file = os.path.join(vocab_path, VOCAB_NAME) - else: - vocab_file = vocab_path - with open(vocab_file, "w", encoding="utf-8") as writer: - for token, token_index in sorted(self.vocab.items(), key=lambda kv: kv[1]): - if index != token_index: - print("Saving vocabulary to {}: vocabulary indices are not consecutive." - " Please check that the vocabulary is not corrupted!".format(vocab_file)) - index = token_index - writer.write(token + u'\n') - index += 1 - return vocab_file - - @classmethod - def from_pretrained(cls, model_dir, *inputs, **kwargs): - """ - 给定path,直接读取vocab. - - """ - pretrained_model_name_or_path = os.path.join(model_dir, VOCAB_NAME) - print("loading vocabulary file {}".format(pretrained_model_name_or_path)) - max_len = 512 - kwargs['max_len'] = min(kwargs.get('max_len', int(1e12)), max_len) - # Instantiate tokenizer. - tokenizer = cls(pretrained_model_name_or_path, *inputs, **kwargs) - return tokenizer - -VOCAB_NAME = 'vocab.txt' - -class _WordBertModel(nn.Module): - def __init__(self, model_dir:str, vocab:Vocabulary, layers:str='-1', pool_method:str='first', include_cls_sep:bool=False): - super().__init__() - - self.tokenzier = BertTokenizer.from_pretrained(model_dir) - self.encoder = BertModel.from_pretrained(model_dir) - # 检查encoder_layer_number是否合理 - encoder_layer_number = len(self.encoder.encoder.layer) - self.layers = list(map(int, layers.split(','))) - for layer in self.layers: - if layer<0: - assert -layer<=encoder_layer_number, f"The layer index:{layer} is out of scope for " \ - f"a bert model with {encoder_layer_number} layers." - else: - assert layer 1 or weight_count > 1: - raise Exception(f"Multiple config files(*.json) or weight files(*.hdf5) detected in {model_dir}.") - elif config_count == 0 or weight_count == 0: - raise Exception(f"No config file or weight file found in {model_dir}") - - config = json.load(open(os.path.join(model_dir, config_file), 'r')) - self.weight_file = os.path.join(model_dir, weight_file) - self.config = config - - OOV_TAG = '' - PAD_TAG = '' - BOS_TAG = '' - EOS_TAG = '' - BOW_TAG = '' - EOW_TAG = '' - - # For the model trained with character-based word encoder. - char_lexicon = {} - with codecs.open(os.path.join(model_dir, 'char.dic'), 'r', encoding='utf-8') as fpi: - for line in fpi: - tokens = line.strip().split('\t') - if len(tokens) == 1: - tokens.insert(0, '\u3000') - token, i = tokens - char_lexicon[token] = int(i) - - # 做一些sanity check - for special_word in [PAD_TAG, OOV_TAG, BOW_TAG, EOW_TAG]: - assert special_word in char_lexicon, f"{special_word} not found in char.dic." - - # 从vocab中构建char_vocab - char_vocab = Vocabulary(unknown=OOV_TAG, padding=PAD_TAG) - # 需要保证在里面 - char_vocab.add_word_lst([BOW_TAG, EOW_TAG, BOS_TAG, EOS_TAG]) - - for word, index in vocab: - char_vocab.add_word_lst(list(word)) - - self.bos_index, self.eos_index, self._pad_index = len(vocab), len(vocab) + 1, vocab.padding_idx - # 根据char_lexicon调整, 多设置一位,是预留给word padding的(该位置的char表示为全0表示) - char_emb_layer = nn.Embedding(len(char_vocab) + 1, int(config['char_cnn']['embedding']['dim']), - padding_idx=len(char_vocab)) - - # 读入预训练权重 这里的elmo_model 包含char_cnn和 lstm 的 state_dict - elmo_model = torch.load(os.path.join(self.model_dir, weight_file), map_location='cpu') - - char_embed_weights = elmo_model["char_cnn"]['char_emb_layer.weight'] - - found_char_count = 0 - for char, index in char_vocab: # 调整character embedding - if char in char_lexicon: - index_in_pre = char_lexicon.get(char) - found_char_count += 1 - else: - index_in_pre = char_lexicon[OOV_TAG] - char_emb_layer.weight.data[index] = char_embed_weights[index_in_pre] - - print(f"{found_char_count} out of {len(char_vocab)} characters were found in pretrained elmo embedding.") - # 生成words到chars的映射 - max_chars = config['char_cnn']['max_characters_per_token'] - - self.words_to_chars_embedding = nn.Parameter(torch.full((len(vocab) + 2, max_chars), - fill_value=len(char_vocab), - dtype=torch.long), - requires_grad=False) - for word, index in list(iter(vocab)) + [(BOS_TAG, len(vocab)), (EOS_TAG, len(vocab) + 1)]: - if len(word) + 2 > max_chars: - word = word[:max_chars - 2] - if index == self._pad_index: - continue - elif word == BOS_TAG or word == EOS_TAG: - char_ids = [char_vocab.to_index(BOW_TAG)] + [char_vocab.to_index(word)] + [ - char_vocab.to_index(EOW_TAG)] - char_ids += [char_vocab.to_index(PAD_TAG)] * (max_chars - len(char_ids)) - else: - char_ids = [char_vocab.to_index(BOW_TAG)] + [char_vocab.to_index(c) for c in word] + [ - char_vocab.to_index(EOW_TAG)] - char_ids += [char_vocab.to_index(PAD_TAG)] * (max_chars - len(char_ids)) - self.words_to_chars_embedding[index] = torch.LongTensor(char_ids) - - self.char_vocab = char_vocab - - self.token_embedder = ConvTokenEmbedder( - config, self.weight_file, None, char_emb_layer) - elmo_model["char_cnn"]['char_emb_layer.weight'] = char_emb_layer.weight - self.token_embedder.load_state_dict(elmo_model["char_cnn"]) - - self.output_dim = config['lstm']['projection_dim'] - - # lstm encoder - self.encoder = ElmobiLm(config) - self.encoder.load_state_dict(elmo_model["lstm"]) - - if cache_word_reprs: - if config['char_cnn']['embedding']['dim'] > 0: # 只有在使用了chars的情况下有用 - print("Start to generate cache word representations.") - batch_size = 320 - # bos eos - word_size = self.words_to_chars_embedding.size(0) - num_batches = word_size // batch_size + \ - int(word_size % batch_size != 0) - - self.cached_word_embedding = nn.Embedding(word_size, - config['lstm']['projection_dim']) - with torch.no_grad(): - for i in range(num_batches): - words = torch.arange(i * batch_size, - min((i + 1) * batch_size, word_size)).long() - chars = self.words_to_chars_embedding[words].unsqueeze(1) # batch_size x 1 x max_chars - word_reprs = self.token_embedder(words.unsqueeze(1), - chars).detach() # batch_size x 1 x config['encoder']['projection_dim'] - self.cached_word_embedding.weight.data[words] = word_reprs.squeeze(1) - - print("Finish generating cached word representations. Going to delete the character encoder.") - del self.token_embedder, self.words_to_chars_embedding - else: - print("There is no need to cache word representations, since no character information is used.") - - def forward(self, words): - """ - - :param words: batch_size x max_len - :return: num_layers x batch_size x max_len x hidden_size - """ - # 扩展, - batch_size, max_len = words.size() - expanded_words = words.new_zeros(batch_size, max_len + 2) # 因为pad一定为0, - seq_len = words.ne(self._pad_index).sum(dim=-1) - expanded_words[:, 1:-1] = words - expanded_words[:, 0].fill_(self.bos_index) - expanded_words[torch.arange(batch_size).to(words), seq_len + 1] = self.eos_index - seq_len = seq_len + 2 - zero_tensor = expanded_words.new_zeros(expanded_words.shape) - mask = (expanded_words == zero_tensor).unsqueeze(-1) - if hasattr(self, 'cached_word_embedding'): - token_embedding = self.cached_word_embedding(expanded_words) - else: - if hasattr(self, 'words_to_chars_embedding'): - chars = self.words_to_chars_embedding[expanded_words] - else: - chars = None - token_embedding = self.token_embedder(expanded_words, chars) # batch_size x max_len x embed_dim - - encoder_output = self.encoder(token_embedding, seq_len) - if encoder_output.size(2) < max_len + 2: - num_layers, _, output_len, hidden_size = encoder_output.size() - dummy_tensor = encoder_output.new_zeros(num_layers, batch_size, - max_len + 2 - output_len, hidden_size) - encoder_output = torch.cat((encoder_output, dummy_tensor), 2) - sz = encoder_output.size() # 2, batch_size, max_len, hidden_size - token_embedding = token_embedding.masked_fill(mask, 0) - token_embedding = torch.cat((token_embedding, token_embedding), dim=2).view(1, sz[1], sz[2], sz[3]) - encoder_output = torch.cat((token_embedding, encoder_output), dim=0) - - # 删除, . 这里没有精确地删除,但应该也不会影响最后的结果了。 - encoder_output = encoder_output[:, :, 1:-1] - return encoder_output diff --git a/fastNLP/modules/encoder/bert.py b/fastNLP/modules/encoder/bert.py index 1819cc69..6d32ae74 100644 --- a/fastNLP/modules/encoder/bert.py +++ b/fastNLP/modules/encoder/bert.py @@ -1,79 +1,919 @@ + + +""" +这个页面的代码很大程度上参考(复制粘贴)了https://github.com/huggingface/pytorch-pretrained-BERT的代码, 如果你发现该代码对你 + 有用,也请引用一下他们。 +""" + + +import collections + +import unicodedata +import copy +import json +import math import os -from torch import nn + import torch -from ...io.file_utils import _get_base_url, cached_path, PRETRAINED_BERT_MODEL_DIR -from ._bert import _WordPieceBertModel, BertModel +from torch import nn +import glob +import sys +CONFIG_FILE = 'bert_config.json' -class BertWordPieceEncoder(nn.Module): + +class BertConfig(object): + """Configuration class to store the configuration of a `BertModel`. """ - 读取bert模型,读取之后调用index_dataset方法在dataset中生成word_pieces这一列。 + def __init__(self, + vocab_size_or_config_json_file, + hidden_size=768, + num_hidden_layers=12, + num_attention_heads=12, + intermediate_size=3072, + hidden_act="gelu", + hidden_dropout_prob=0.1, + attention_probs_dropout_prob=0.1, + max_position_embeddings=512, + type_vocab_size=2, + initializer_range=0.02, + layer_norm_eps=1e-12): + """Constructs BertConfig. + + Args: + vocab_size_or_config_json_file: Vocabulary size of `inputs_ids` in `BertModel`. + hidden_size: Size of the encoder layers and the pooler layer. + num_hidden_layers: Number of hidden layers in the Transformer encoder. + num_attention_heads: Number of attention heads for each attention layer in + the Transformer encoder. + intermediate_size: The size of the "intermediate" (i.e., feed-forward) + layer in the Transformer encoder. + hidden_act: The non-linear activation function (function or string) in the + encoder and pooler. If string, "gelu", "relu" and "swish" are supported. + hidden_dropout_prob: The dropout probabilitiy for all fully connected + layers in the embeddings, encoder, and pooler. + attention_probs_dropout_prob: The dropout ratio for the attention + probabilities. + max_position_embeddings: The maximum sequence length that this model might + ever be used with. Typically set this to something large just in case + (e.g., 512 or 1024 or 2048). + type_vocab_size: The vocabulary size of the `token_type_ids` passed into + `BertModel`. + initializer_range: The sttdev of the truncated_normal_initializer for + initializing all weight matrices. + layer_norm_eps: The epsilon used by LayerNorm. + """ + if isinstance(vocab_size_or_config_json_file, str) or (sys.version_info[0] == 2 + and isinstance(vocab_size_or_config_json_file, unicode)): + with open(vocab_size_or_config_json_file, "r", encoding='utf-8') as reader: + json_config = json.loads(reader.read()) + for key, value in json_config.items(): + self.__dict__[key] = value + elif isinstance(vocab_size_or_config_json_file, int): + self.vocab_size = vocab_size_or_config_json_file + self.hidden_size = hidden_size + self.num_hidden_layers = num_hidden_layers + self.num_attention_heads = num_attention_heads + self.hidden_act = hidden_act + self.intermediate_size = intermediate_size + self.hidden_dropout_prob = hidden_dropout_prob + self.attention_probs_dropout_prob = attention_probs_dropout_prob + self.max_position_embeddings = max_position_embeddings + self.type_vocab_size = type_vocab_size + self.initializer_range = initializer_range + self.layer_norm_eps = layer_norm_eps + else: + raise ValueError("First argument must be either a vocabulary size (int)" + "or the path to a pretrained model config file (str)") + + @classmethod + def from_dict(cls, json_object): + """Constructs a `BertConfig` from a Python dictionary of parameters.""" + config = BertConfig(vocab_size_or_config_json_file=-1) + for key, value in json_object.items(): + config.__dict__[key] = value + return config + + @classmethod + def from_json_file(cls, json_file): + """Constructs a `BertConfig` from a json file of parameters.""" + with open(json_file, "r", encoding='utf-8') as reader: + text = reader.read() + return cls.from_dict(json.loads(text)) + + def __repr__(self): + return str(self.to_json_string()) + + def to_dict(self): + """Serializes this instance to a Python dictionary.""" + output = copy.deepcopy(self.__dict__) + return output + + def to_json_string(self): + """Serializes this instance to a JSON string.""" + return json.dumps(self.to_dict(), indent=2, sort_keys=True) + "\n" + + def to_json_file(self, json_file_path): + """ Save this instance to a json file.""" + with open(json_file_path, "w", encoding='utf-8') as writer: + writer.write(self.to_json_string()) + - :param str model_dir_or_name: 模型所在目录或者模型的名称。默认值为``en-base-uncased`` - :param str layers:最终结果中的表示。以','隔开层数,可以以负数去索引倒数几层 - :param bool requires_grad: 是否需要gradient。 +def gelu(x): + return x * 0.5 * (1.0 + torch.erf(x / math.sqrt(2.0))) + + +def swish(x): + return x * torch.sigmoid(x) + + +ACT2FN = {"gelu": gelu, "relu": torch.nn.functional.relu, "swish": swish} + + +class BertLayerNorm(nn.Module): + def __init__(self, hidden_size, eps=1e-12): + """Construct a layernorm module in the TF style (epsilon inside the square root). + """ + super(BertLayerNorm, self).__init__() + self.weight = nn.Parameter(torch.ones(hidden_size)) + self.bias = nn.Parameter(torch.zeros(hidden_size)) + self.variance_epsilon = eps + + def forward(self, x): + u = x.mean(-1, keepdim=True) + s = (x - u).pow(2).mean(-1, keepdim=True) + x = (x - u) / torch.sqrt(s + self.variance_epsilon) + return self.weight * x + self.bias + + +class BertEmbeddings(nn.Module): + """Construct the embeddings from word, position and token_type embeddings. """ - def __init__(self, model_dir_or_name: str='en-base-uncased', layers: str='-1', - requires_grad: bool=False): - super().__init__() - PRETRAIN_URL = _get_base_url('bert') - - if model_dir_or_name in PRETRAINED_BERT_MODEL_DIR: - model_name = PRETRAINED_BERT_MODEL_DIR[model_dir_or_name] - model_url = PRETRAIN_URL + model_name - model_dir = cached_path(model_url) - # 检查是否存在 - elif os.path.isdir(model_dir_or_name): - model_dir = model_dir_or_name + def __init__(self, config): + super(BertEmbeddings, self).__init__() + self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=0) + self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) + self.token_type_embeddings = nn.Embedding(config.type_vocab_size, config.hidden_size) + + # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load + # any TensorFlow checkpoint file + self.LayerNorm = BertLayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, input_ids, token_type_ids=None): + seq_length = input_ids.size(1) + position_ids = torch.arange(seq_length, dtype=torch.long, device=input_ids.device) + position_ids = position_ids.unsqueeze(0).expand_as(input_ids) + if token_type_ids is None: + token_type_ids = torch.zeros_like(input_ids) + + words_embeddings = self.word_embeddings(input_ids) + position_embeddings = self.position_embeddings(position_ids) + token_type_embeddings = self.token_type_embeddings(token_type_ids) + + embeddings = words_embeddings + position_embeddings + token_type_embeddings + embeddings = self.LayerNorm(embeddings) + embeddings = self.dropout(embeddings) + return embeddings + + +class BertSelfAttention(nn.Module): + def __init__(self, config): + super(BertSelfAttention, self).__init__() + if config.hidden_size % config.num_attention_heads != 0: + raise ValueError( + "The hidden size (%d) is not a multiple of the number of attention " + "heads (%d)" % (config.hidden_size, config.num_attention_heads)) + self.num_attention_heads = config.num_attention_heads + self.attention_head_size = int(config.hidden_size / config.num_attention_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + + self.query = nn.Linear(config.hidden_size, self.all_head_size) + self.key = nn.Linear(config.hidden_size, self.all_head_size) + self.value = nn.Linear(config.hidden_size, self.all_head_size) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + + def transpose_for_scores(self, x): + new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) + x = x.view(*new_x_shape) + return x.permute(0, 2, 1, 3) + + def forward(self, hidden_states, attention_mask): + mixed_query_layer = self.query(hidden_states) + mixed_key_layer = self.key(hidden_states) + mixed_value_layer = self.value(hidden_states) + + query_layer = self.transpose_for_scores(mixed_query_layer) + key_layer = self.transpose_for_scores(mixed_key_layer) + value_layer = self.transpose_for_scores(mixed_value_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + # Apply the attention mask is (precomputed for all layers in BertModel forward() function) + attention_scores = attention_scores + attention_mask + + # Normalize the attention scores to probabilities. + attention_probs = nn.Softmax(dim=-1)(attention_scores) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs = self.dropout(attention_probs) + + context_layer = torch.matmul(attention_probs, value_layer) + context_layer = context_layer.permute(0, 2, 1, 3).contiguous() + new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) + context_layer = context_layer.view(*new_context_layer_shape) + return context_layer + + +class BertSelfOutput(nn.Module): + def __init__(self, config): + super(BertSelfOutput, self).__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.LayerNorm = BertLayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertAttention(nn.Module): + def __init__(self, config): + super(BertAttention, self).__init__() + self.self = BertSelfAttention(config) + self.output = BertSelfOutput(config) + + def forward(self, input_tensor, attention_mask): + self_output = self.self(input_tensor, attention_mask) + attention_output = self.output(self_output, input_tensor) + return attention_output + + +class BertIntermediate(nn.Module): + def __init__(self, config): + super(BertIntermediate, self).__init__() + self.dense = nn.Linear(config.hidden_size, config.intermediate_size) + if isinstance(config.hidden_act, str) or (sys.version_info[0] == 2 and isinstance(config.hidden_act, unicode)): + self.intermediate_act_fn = ACT2FN[config.hidden_act] else: - raise ValueError(f"Cannot recognize {model_dir_or_name}.") + self.intermediate_act_fn = config.hidden_act + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +class BertOutput(nn.Module): + def __init__(self, config): + super(BertOutput, self).__init__() + self.dense = nn.Linear(config.intermediate_size, config.hidden_size) + self.LayerNorm = BertLayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertLayer(nn.Module): + def __init__(self, config): + super(BertLayer, self).__init__() + self.attention = BertAttention(config) + self.intermediate = BertIntermediate(config) + self.output = BertOutput(config) + + def forward(self, hidden_states, attention_mask): + attention_output = self.attention(hidden_states, attention_mask) + intermediate_output = self.intermediate(attention_output) + layer_output = self.output(intermediate_output, attention_output) + return layer_output + + +class BertEncoder(nn.Module): + def __init__(self, config): + super(BertEncoder, self).__init__() + layer = BertLayer(config) + self.layer = nn.ModuleList([copy.deepcopy(layer) for _ in range(config.num_hidden_layers)]) + + def forward(self, hidden_states, attention_mask, output_all_encoded_layers=True): + all_encoder_layers = [] + for layer_module in self.layer: + hidden_states = layer_module(hidden_states, attention_mask) + if output_all_encoded_layers: + all_encoder_layers.append(hidden_states) + if not output_all_encoded_layers: + all_encoder_layers.append(hidden_states) + return all_encoder_layers + + +class BertPooler(nn.Module): + def __init__(self, config): + super(BertPooler, self).__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.activation = nn.Tanh() + + def forward(self, hidden_states): + # We "pool" the model by simply taking the hidden state corresponding + # to the first token. + first_token_tensor = hidden_states[:, 0] + pooled_output = self.dense(first_token_tensor) + pooled_output = self.activation(pooled_output) + return pooled_output + + +class BertModel(nn.Module): + """BERT(Bidirectional Embedding Representations from Transformers). + + 如果你想使用预训练好的权重矩阵,请在以下网址下载. + sources:: + + 'bert-base-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased-pytorch_model.bin", + 'bert-large-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-pytorch_model.bin", + 'bert-base-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-pytorch_model.bin", + 'bert-large-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-pytorch_model.bin", + 'bert-base-multilingual-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-uncased-pytorch_model.bin", + 'bert-base-multilingual-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual-cased-pytorch_model.bin", + 'bert-base-chinese': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese-pytorch_model.bin", + 'bert-base-german-cased': "https://int-deepset-models-bert.s3.eu-central-1.amazonaws.com/pytorch/bert-base-german-cased-pytorch_model.bin", + 'bert-large-uncased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-pytorch_model.bin", + 'bert-large-cased-whole-word-masking': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-pytorch_model.bin", + 'bert-large-uncased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased-whole-word-masking-finetuned-squad-pytorch_model.bin", + 'bert-large-cased-whole-word-masking-finetuned-squad': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-cased-whole-word-masking-finetuned-squad-pytorch_model.bin", + 'bert-base-cased-finetuned-mrpc': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased-finetuned-mrpc-pytorch_model.bin" + + + 用预训练权重矩阵来建立BERT模型:: + + model = BertModel.from_pretrained("path/to/weights/directory") + + 用随机初始化权重矩阵来建立BERT模型:: - self.model = _WordPieceBertModel(model_dir=model_dir, layers=layers) - self._embed_size = len(self.model.layers) * self.model.encoder.hidden_size - self.requires_grad = requires_grad + model = BertModel() - @property - def requires_grad(self): + :param int vocab_size: 词表大小,默认值为30522,为BERT English uncase版本的词表大小 + :param int hidden_size: 隐层大小,默认值为768,为BERT base的版本 + :param int num_hidden_layers: 隐藏层数,默认值为12,为BERT base的版本 + :param int num_attention_heads: 多头注意力头数,默认值为12,为BERT base的版本 + :param int intermediate_size: FFN隐藏层大小,默认值是3072,为BERT base的版本 + :param str hidden_act: FFN隐藏层激活函数,默认值为``gelu`` + :param float hidden_dropout_prob: FFN隐藏层dropout,默认值为0.1 + :param float attention_probs_dropout_prob: Attention层的dropout,默认值为0.1 + :param int max_position_embeddings: 最大的序列长度,默认值为512, + :param int type_vocab_size: 最大segment数量,默认值为2 + :param int initializer_range: 初始化权重范围,默认值为0.02 + """ + + def __init__(self, config, *inputs, **kwargs): + super(BertModel, self).__init__() + if not isinstance(config, BertConfig): + raise ValueError( + "Parameter config in `{}(config)` should be an instance of class `BertConfig`. " + "To create a model from a Google pretrained model use " + "`model = {}.from_pretrained(PRETRAINED_MODEL_NAME)`".format( + self.__class__.__name__, self.__class__.__name__ + )) + super(BertModel, self).__init__() + self.config = config + self.hidden_size = self.config.hidden_size + self.embeddings = BertEmbeddings(config) + self.encoder = BertEncoder(config) + self.pooler = BertPooler(config) + self.apply(self.init_bert_weights) + + def init_bert_weights(self, module): + """ Initialize the weights. """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 + if isinstance(module, (nn.Linear, nn.Embedding)): + # Slightly different from the TF version which uses truncated_normal for initialization + # cf https://github.com/pytorch/pytorch/pull/5617 + module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) + elif isinstance(module, BertLayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + + def forward(self, input_ids, token_type_ids=None, attention_mask=None, output_all_encoded_layers=True): + if attention_mask is None: + attention_mask = torch.ones_like(input_ids) + if token_type_ids is None: + token_type_ids = torch.zeros_like(input_ids) + + # We create a 3D attention mask from a 2D tensor mask. + # Sizes are [batch_size, 1, 1, to_seq_length] + # So we can broadcast to [batch_size, num_heads, from_seq_length, to_seq_length] + # this attention mask is more simple than the triangular masking of causal attention + # used in OpenAI GPT, we just need to prepare the broadcast dimension here. + extended_attention_mask = attention_mask.unsqueeze(1).unsqueeze(2) + + # Since attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + extended_attention_mask = extended_attention_mask.to(dtype=next(self.parameters()).dtype) # fp16 compatibility + extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 + + embedding_output = self.embeddings(input_ids, token_type_ids) + encoded_layers = self.encoder(embedding_output, + extended_attention_mask, + output_all_encoded_layers=output_all_encoded_layers) + sequence_output = encoded_layers[-1] + pooled_output = self.pooler(sequence_output) + if not output_all_encoded_layers: + encoded_layers = encoded_layers[-1] + return encoded_layers, pooled_output + + @classmethod + def from_pretrained(cls, pretrained_model_dir, *inputs, **kwargs): + state_dict = kwargs.get('state_dict', None) + kwargs.pop('state_dict', None) + cache_dir = kwargs.get('cache_dir', None) + kwargs.pop('cache_dir', None) + from_tf = kwargs.get('from_tf', False) + kwargs.pop('from_tf', None) + # Load config + config_file = os.path.join(pretrained_model_dir, CONFIG_FILE) + config = BertConfig.from_json_file(config_file) + # logger.info("Model config {}".format(config)) + # Instantiate model. + model = cls(config, *inputs, **kwargs) + if state_dict is None: + files = glob.glob(os.path.join(pretrained_model_dir, '*.bin')) + if len(files)==0: + raise FileNotFoundError(f"There is no *.bin file in {pretrained_model_dir}") + elif len(files)>1: + raise FileExistsError(f"There are multiple *.bin files in {pretrained_model_dir}") + weights_path = files[0] + state_dict = torch.load(weights_path, map_location='cpu') + + old_keys = [] + new_keys = [] + for key in state_dict.keys(): + new_key = None + if 'gamma' in key: + new_key = key.replace('gamma', 'weight') + if 'beta' in key: + new_key = key.replace('beta', 'bias') + if new_key: + old_keys.append(key) + new_keys.append(new_key) + for old_key, new_key in zip(old_keys, new_keys): + state_dict[new_key] = state_dict.pop(old_key) + + missing_keys = [] + unexpected_keys = [] + error_msgs = [] + # copy state_dict so _load_from_state_dict can modify it + metadata = getattr(state_dict, '_metadata', None) + state_dict = state_dict.copy() + if metadata is not None: + state_dict._metadata = metadata + + def load(module, prefix=''): + local_metadata = {} if metadata is None else metadata.get(prefix[:-1], {}) + module._load_from_state_dict( + state_dict, prefix, local_metadata, True, missing_keys, unexpected_keys, error_msgs) + for name, child in module._modules.items(): + if child is not None: + load(child, prefix + name + '.') + + load(model, prefix='' if hasattr(model, 'bert') else 'bert.') + if len(missing_keys) > 0: + print("Weights of {} not initialized from pretrained model: {}".format( + model.__class__.__name__, missing_keys)) + if len(unexpected_keys) > 0: + print("Weights from pretrained model not used in {}: {}".format( + model.__class__.__name__, unexpected_keys)) + return model + + +def whitespace_tokenize(text): + """Runs basic whitespace cleaning and splitting on a piece of text.""" + text = text.strip() + if not text: + return [] + tokens = text.split() + return tokens + + +class WordpieceTokenizer(object): + """Runs WordPiece tokenization.""" + + def __init__(self, vocab, unk_token="[UNK]", max_input_chars_per_word=100): + self.vocab = vocab + self.unk_token = unk_token + self.max_input_chars_per_word = max_input_chars_per_word + + def tokenize(self, text): + """Tokenizes a piece of text into its word pieces. + + This uses a greedy longest-match-first algorithm to perform tokenization + using the given vocabulary. + + For example: + input = "unaffable" + output = ["un", "##aff", "##able"] + + Args: + text: A single token or whitespace separated tokens. This should have + already been passed through `BasicTokenizer`. + + Returns: + A list of wordpiece tokens. + """ + + output_tokens = [] + for token in whitespace_tokenize(text): + chars = list(token) + if len(chars) > self.max_input_chars_per_word: + output_tokens.append(self.unk_token) + continue + + is_bad = False + start = 0 + sub_tokens = [] + while start < len(chars): + end = len(chars) + cur_substr = None + while start < end: + substr = "".join(chars[start:end]) + if start > 0: + substr = "##" + substr + if substr in self.vocab: + cur_substr = substr + break + end -= 1 + if cur_substr is None: + is_bad = True + break + sub_tokens.append(cur_substr) + start = end + + if is_bad: + output_tokens.append(self.unk_token) + else: + output_tokens.extend(sub_tokens) + return output_tokens + + +def load_vocab(vocab_file): + """Loads a vocabulary file into a dictionary.""" + vocab = collections.OrderedDict() + index = 0 + with open(vocab_file, "r", encoding="utf-8") as reader: + while True: + token = reader.readline() + if not token: + break + token = token.strip() + vocab[token] = index + index += 1 + return vocab + +class BasicTokenizer(object): + """Runs basic tokenization (punctuation splitting, lower casing, etc.).""" + + def __init__(self, + do_lower_case=True, + never_split=("[UNK]", "[SEP]", "[PAD]", "[CLS]", "[MASK]")): + """Constructs a BasicTokenizer. + + Args: + do_lower_case: Whether to lower case the input. + """ + self.do_lower_case = do_lower_case + self.never_split = never_split + + def tokenize(self, text): + """Tokenizes a piece of text.""" + text = self._clean_text(text) + # This was added on November 1st, 2018 for the multilingual and Chinese + # models. This is also applied to the English models now, but it doesn't + # matter since the English models were not trained on any Chinese data + # and generally don't have any Chinese data in them (there are Chinese + # characters in the vocabulary because Wikipedia does have some Chinese + # words in the English Wikipedia.). + text = self._tokenize_chinese_chars(text) + orig_tokens = whitespace_tokenize(text) + split_tokens = [] + for token in orig_tokens: + if self.do_lower_case and token not in self.never_split: + token = token.lower() + token = self._run_strip_accents(token) + split_tokens.extend(self._run_split_on_punc(token)) + + output_tokens = whitespace_tokenize(" ".join(split_tokens)) + return output_tokens + + def _run_strip_accents(self, text): + """Strips accents from a piece of text.""" + text = unicodedata.normalize("NFD", text) + output = [] + for char in text: + cat = unicodedata.category(char) + if cat == "Mn": + continue + output.append(char) + return "".join(output) + + def _run_split_on_punc(self, text): + """Splits punctuation on a piece of text.""" + if text in self.never_split: + return [text] + chars = list(text) + i = 0 + start_new_word = True + output = [] + while i < len(chars): + char = chars[i] + if _is_punctuation(char): + output.append([char]) + start_new_word = True + else: + if start_new_word: + output.append([]) + start_new_word = False + output[-1].append(char) + i += 1 + + return ["".join(x) for x in output] + + def _tokenize_chinese_chars(self, text): + """Adds whitespace around any CJK character.""" + output = [] + for char in text: + cp = ord(char) + if self._is_chinese_char(cp): + output.append(" ") + output.append(char) + output.append(" ") + else: + output.append(char) + return "".join(output) + + def _is_chinese_char(self, cp): + """Checks whether CP is the codepoint of a CJK character.""" + # This defines a "chinese character" as anything in the CJK Unicode block: + # https://en.wikipedia.org/wiki/CJK_Unified_Ideographs_(Unicode_block) + # + # Note that the CJK Unicode block is NOT all Japanese and Korean characters, + # despite its name. The modern Korean Hangul alphabet is a different block, + # as is Japanese Hiragana and Katakana. Those alphabets are used to write + # space-separated words, so they are not treated specially and handled + # like the all of the other languages. + if ((cp >= 0x4E00 and cp <= 0x9FFF) or # + (cp >= 0x3400 and cp <= 0x4DBF) or # + (cp >= 0x20000 and cp <= 0x2A6DF) or # + (cp >= 0x2A700 and cp <= 0x2B73F) or # + (cp >= 0x2B740 and cp <= 0x2B81F) or # + (cp >= 0x2B820 and cp <= 0x2CEAF) or + (cp >= 0xF900 and cp <= 0xFAFF) or # + (cp >= 0x2F800 and cp <= 0x2FA1F)): # + return True + + return False + + def _clean_text(self, text): + """Performs invalid character removal and whitespace cleanup on text.""" + output = [] + for char in text: + cp = ord(char) + if cp == 0 or cp == 0xfffd or _is_control(char): + continue + if _is_whitespace(char): + output.append(" ") + else: + output.append(char) + return "".join(output) + + +def _is_whitespace(char): + """Checks whether `chars` is a whitespace character.""" + # \t, \n, and \r are technically contorl characters but we treat them + # as whitespace since they are generally considered as such. + if char == " " or char == "\t" or char == "\n" or char == "\r": + return True + cat = unicodedata.category(char) + if cat == "Zs": + return True + return False + + +def _is_control(char): + """Checks whether `chars` is a control character.""" + # These are technically control characters but we count them as whitespace + # characters. + if char == "\t" or char == "\n" or char == "\r": + return False + cat = unicodedata.category(char) + if cat.startswith("C"): + return True + return False + + +def _is_punctuation(char): + """Checks whether `chars` is a punctuation character.""" + cp = ord(char) + # We treat all non-letter/number ASCII as punctuation. + # Characters such as "^", "$", and "`" are not in the Unicode + # Punctuation class but we treat them as punctuation anyways, for + # consistency. + if ((cp >= 33 and cp <= 47) or (cp >= 58 and cp <= 64) or + (cp >= 91 and cp <= 96) or (cp >= 123 and cp <= 126)): + return True + cat = unicodedata.category(char) + if cat.startswith("P"): + return True + return False + + +class BertTokenizer(object): + """Runs end-to-end tokenization: punctuation splitting + wordpiece""" + + def __init__(self, vocab_file, do_lower_case=True, max_len=None, do_basic_tokenize=True, + never_split=("[UNK]", "[SEP]", "[PAD]", "[CLS]", "[MASK]")): + """Constructs a BertTokenizer. + + Args: + vocab_file: Path to a one-wordpiece-per-line vocabulary file + do_lower_case: Whether to lower case the input + Only has an effect when do_wordpiece_only=False + do_basic_tokenize: Whether to do basic tokenization before wordpiece. + max_len: An artificial maximum length to truncate tokenized sequences to; + Effective maximum length is always the minimum of this + value (if specified) and the underlying BERT model's + sequence length. + never_split: List of tokens which will never be split during tokenization. + Only has an effect when do_wordpiece_only=False + """ + if not os.path.isfile(vocab_file): + raise ValueError( + "Can't find a vocabulary file at path '{}'. To load the vocabulary from a Google pretrained " + "model use `tokenizer = BertTokenizer.from_pretrained(PRETRAINED_MODEL_NAME)`".format(vocab_file)) + self.vocab = load_vocab(vocab_file) + self.ids_to_tokens = collections.OrderedDict( + [(ids, tok) for tok, ids in self.vocab.items()]) + self.do_basic_tokenize = do_basic_tokenize + if do_basic_tokenize: + self.basic_tokenizer = BasicTokenizer(do_lower_case=do_lower_case, + never_split=never_split) + self.wordpiece_tokenizer = WordpieceTokenizer(vocab=self.vocab) + self.max_len = max_len if max_len is not None else int(1e12) + + def _reinit_on_new_vocab(self, vocab): + """ + 在load bert之后,可能会对vocab进行重新排列。重新排列之后调用这个函数重新初始化与vocab相关的性质 + + :param vocab: :return: """ - requires_grads = set([param.requires_grad for name, param in self.named_parameters()]) - if len(requires_grads)==1: - return requires_grads.pop() + self.vocab = vocab + self.wordpiece_tokenizer = WordpieceTokenizer(vocab=self.vocab) + + def tokenize(self, text): + split_tokens = [] + if self.do_basic_tokenize: + for token in self.basic_tokenizer.tokenize(text): + for sub_token in self.wordpiece_tokenizer.tokenize(token): + split_tokens.append(sub_token) + else: + split_tokens = self.wordpiece_tokenizer.tokenize(text) + return split_tokens + + def convert_tokens_to_ids(self, tokens): + """Converts a sequence of tokens into ids using the vocab.""" + ids = [] + for token in tokens: + ids.append(self.vocab[token]) + if len(ids) > self.max_len: + print( + "Token indices sequence length is longer than the specified maximum " + " sequence length for this BERT model ({} > {}). Running this" + " sequence through BERT will result in indexing errors".format(len(ids), self.max_len) + ) + return ids + + def convert_ids_to_tokens(self, ids): + """Converts a sequence of ids in wordpiece tokens using the vocab.""" + tokens = [] + for i in ids: + tokens.append(self.ids_to_tokens[i]) + return tokens + + def save_vocabulary(self, vocab_path): + """Save the tokenizer vocabulary to a directory or file.""" + index = 0 + if os.path.isdir(vocab_path): + vocab_file = os.path.join(vocab_path, VOCAB_NAME) else: - return None + vocab_file = vocab_path + with open(vocab_file, "w", encoding="utf-8") as writer: + for token, token_index in sorted(self.vocab.items(), key=lambda kv: kv[1]): + if index != token_index: + print("Saving vocabulary to {}: vocabulary indices are not consecutive." + " Please check that the vocabulary is not corrupted!".format(vocab_file)) + index = token_index + writer.write(token + u'\n') + index += 1 + return vocab_file + + @classmethod + def from_pretrained(cls, model_dir, *inputs, **kwargs): + """ + 给定path,直接读取vocab. + + """ + pretrained_model_name_or_path = os.path.join(model_dir, VOCAB_NAME) + print("loading vocabulary file {}".format(pretrained_model_name_or_path)) + max_len = 512 + kwargs['max_len'] = min(kwargs.get('max_len', int(1e12)), max_len) + # Instantiate tokenizer. + tokenizer = cls(pretrained_model_name_or_path, *inputs, **kwargs) + return tokenizer - @requires_grad.setter - def requires_grad(self, value): - for name, param in self.named_parameters(): - param.requires_grad = value +VOCAB_NAME = 'vocab.txt' - @property - def embed_size(self): - return self._embed_size - def index_datasets(self, *datasets, field_name): +class _WordPieceBertModel(nn.Module): + """ + 这个模块用于直接计算word_piece的结果. + + """ + def __init__(self, model_dir:str, layers:str='-1'): + super().__init__() + + self.tokenzier = BertTokenizer.from_pretrained(model_dir) + self.encoder = BertModel.from_pretrained(model_dir) + # 检查encoder_layer_number是否合理 + encoder_layer_number = len(self.encoder.encoder.layer) + self.layers = list(map(int, layers.split(','))) + for layer in self.layers: + if layer<0: + assert -layer<=encoder_layer_number, f"The layer index:{layer} is out of scope for " \ + f"a bert model with {encoder_layer_number} layers." + else: + assert layer0 and not isinstance(unk_index, int): - raise ValueError("When drop word is set, you need to pass in the unk_index.") - else: - self._embed_size = self.embed.embed_size - unk_index = self.embed.get_word_vocab().unknown_idx - self.unk_index = unk_index - self.word_dropout = word_dropout - - def forward(self, x): - """ - :param torch.LongTensor x: [batch, seq_len] - :return: torch.Tensor : [batch, seq_len, embed_dim] - """ - if self.word_dropout>0 and self.training: - mask = torch.ones_like(x).float() * self.word_dropout - mask = torch.bernoulli(mask).byte() # dropout_word越大,越多位置为1 - x = x.masked_fill(mask, self.unk_index) - x = self.embed(x) - return self.dropout(x) - - @property - def num_embedding(self)->int: - if isinstance(self.embed, nn.Embedding): - return self.embed.weight.size(0) - else: - return self.embed.num_embedding - - def __len__(self): - return len(self.embed) - - @property - def embed_size(self) -> int: - return self._embed_size - - @property - def embedding_dim(self) -> int: - return self._embed_size - - @property - def requires_grad(self): - """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 - :return: - """ - if not isinstance(self.embed, TokenEmbedding): - return self.embed.weight.requires_grad - else: - return self.embed.requires_grad - - @requires_grad.setter - def requires_grad(self, value): - if not isinstance(self.embed, TokenEmbedding): - self.embed.weight.requires_grad = value - else: - self.embed.requires_grad = value - - @property - def size(self): - if isinstance(self.embed, TokenEmbedding): - return self.embed.size - else: - return self.embed.weight.size() - - -class TokenEmbedding(nn.Module): - def __init__(self, vocab, word_dropout=0.0, dropout=0.0): - super(TokenEmbedding, self).__init__() - assert vocab.padding is not None, "Vocabulary must have a padding entry." - self._word_vocab = vocab - self._word_pad_index = vocab.padding_idx - if word_dropout>0: - assert vocab.unknown is not None, "Vocabulary must have unknown entry when you want to drop a word." - self.word_dropout = word_dropout - self._word_unk_index = vocab.unknown_idx - self.dropout_layer = nn.Dropout(dropout) - - def drop_word(self, words): - """ - 按照设定随机将words设置为unknown_index。 - - :param torch.LongTensor words: batch_size x max_len - :return: - """ - if self.word_dropout > 0 and self.training: - mask = torch.ones_like(words).float() * self.word_dropout - mask = torch.bernoulli(mask).byte() # dropout_word越大,越多位置为1 - words = words.masked_fill(mask, self._word_unk_index) - return words - - def dropout(self, words): - """ - 对embedding后的word表示进行drop。 - - :param torch.FloatTensor words: batch_size x max_len x embed_size - :return: - """ - return self.dropout_layer(words) - - @property - def requires_grad(self): - """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 - :return: - """ - requires_grads = set([param.requires_grad for param in self.parameters()]) - if len(requires_grads) == 1: - return requires_grads.pop() - else: - return None - - @requires_grad.setter - def requires_grad(self, value): - for param in self.parameters(): - param.requires_grad = value - - def __len__(self): - return len(self._word_vocab) - - @property - def embed_size(self) -> int: - return self._embed_size - - @property - def embedding_dim(self) -> int: - return self._embed_size - - @property - def num_embedding(self) -> int: - """ - 这个值可能会大于实际的embedding矩阵的大小。 - :return: - """ - return len(self._word_vocab) - - def get_word_vocab(self): - """ - 返回embedding的词典。 - - :return: Vocabulary - """ - return self._word_vocab - - @property - def size(self): - return torch.Size(self.num_embedding, self._embed_size) - - @abstractmethod - def forward(self, *input): - raise NotImplementedError - -class StaticEmbedding(TokenEmbedding): - """ - 别名::class:`fastNLP.modules.StaticEmbedding` :class:`fastNLP.modules.encoder.embedding.StaticEmbedding` - - StaticEmbedding组件. 给定embedding的名称,根据vocab从embedding中抽取相应的数据。该Embedding可以就按照正常的embedding使用了 - - Example:: - - >>> embed = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50') - - - :param vocab: Vocabulary. 若该项为None则会读取所有的embedding。 - :param model_dir_or_name: 可以有两种方式调用预训练好的static embedding:第一种是传入embedding的文件名,第二种是传入embedding - 的名称。目前支持的embedding包括{`en` 或者 `en-glove-840b-300` : glove.840B.300d, `en-glove-6b-50` : glove.6B.50d, - `en-word2vec-300` : GoogleNews-vectors-negative300}。第二种情况将自动查看缓存中是否存在该模型,没有的话将自动下载。 - :param bool requires_grad: 是否需要gradient. 默认为True - :param callable init_method: 如何初始化没有找到的值。可以使用torch.nn.init.*中各种方法。调用该方法时传入一个tensor对象。 - :param bool lower: 是否将vocab中的词语小写后再和预训练的词表进行匹配。如果你的词表中包含大写的词语,或者就是需要单独 - 为大写的词语开辟一个vector表示,则将lower设置为False。 - :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 - :param float dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。 - :param bool normailize: 是否对vector进行normalize,使得每个vector的norm为1。 - """ - def __init__(self, vocab: Vocabulary, model_dir_or_name: str='en', requires_grad: bool=True, init_method=None, - lower=False, dropout=0, word_dropout=0, normalize=False): - super(StaticEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) - - # 得到cache_path - if model_dir_or_name.lower() in PRETRAIN_STATIC_FILES: - PRETRAIN_URL = _get_base_url('static') - model_name = PRETRAIN_STATIC_FILES[model_dir_or_name] - model_url = PRETRAIN_URL + model_name - model_path = cached_path(model_url) - # 检查是否存在 - elif os.path.isfile(os.path.expanduser(os.path.abspath(model_dir_or_name))): - model_path = model_dir_or_name - else: - raise ValueError(f"Cannot recognize {model_dir_or_name}.") - - # 读取embedding - if lower: - lowered_vocab = Vocabulary(padding=vocab.padding, unknown=vocab.unknown) - for word, index in vocab: - if not vocab._is_word_no_create_entry(word): - lowered_vocab.add_word(word.lower()) # 先加入需要创建entry的 - for word in vocab._no_create_word.keys(): # 不需要创建entry的 - if word in vocab: - lowered_word = word.lower() - if lowered_word not in lowered_vocab.word_count: - lowered_vocab.add_word(lowered_word) - lowered_vocab._no_create_word[lowered_word] += 1 - print(f"All word in vocab have been lowered. There are {len(vocab)} words, {len(lowered_vocab)} unique lowered " - f"words.") - embedding = self._load_with_vocab(model_path, vocab=lowered_vocab, init_method=init_method, - normalize=normalize) - # 需要适配一下 - if not hasattr(self, 'words_to_words'): - self.words_to_words = torch.arange(len(lowered_vocab, )).long() - if lowered_vocab.unknown: - unknown_idx = lowered_vocab.unknown_idx - else: - unknown_idx = embedding.size(0) - 1 # 否则是最后一个为unknow - words_to_words = nn.Parameter(torch.full((len(vocab),), fill_value=unknown_idx).long(), - requires_grad=False) - for word, index in vocab: - if word not in lowered_vocab: - word = word.lower() - if lowered_vocab._is_word_no_create_entry(word): # 如果不需要创建entry,已经默认unknown了 - continue - words_to_words[index] = self.words_to_words[lowered_vocab.to_index(word)] - self.words_to_words = words_to_words - else: - embedding = self._load_with_vocab(model_path, vocab=vocab, init_method=init_method, - normalize=normalize) - self.embedding = nn.Embedding(num_embeddings=embedding.shape[0], embedding_dim=embedding.shape[1], - padding_idx=vocab.padding_idx, - max_norm=None, norm_type=2, scale_grad_by_freq=False, - sparse=False, _weight=embedding) - self._embed_size = self.embedding.weight.size(1) - self.requires_grad = requires_grad - - @property - def requires_grad(self): - """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 - :return: - """ - requires_grads = set([param.requires_grad for name, param in self.named_parameters() - if 'words_to_words' not in name]) - if len(requires_grads) == 1: - return requires_grads.pop() - else: - return None - - @requires_grad.setter - def requires_grad(self, value): - for name, param in self.named_parameters(): - if 'words_to_words' in name: - continue - param.requires_grad = value - - def _load_with_vocab(self, embed_filepath, vocab, dtype=np.float32, padding='', unknown='', - normalize=True, error='ignore', init_method=None): - """ - 从embed_filepath这个预训练的词向量中抽取出vocab这个词表的词的embedding。EmbedLoader将自动判断embed_filepath是 - word2vec(第一行只有两个元素)还是glove格式的数据。 - - :param str embed_filepath: 预训练的embedding的路径。 - :param vocab: 词表 :class:`~fastNLP.Vocabulary` 类型,读取出现在vocab中的词的embedding。 - 没有出现在vocab中的词的embedding将通过找到的词的embedding的正态分布采样出来,以使得整个Embedding是同分布的。 - :param dtype: 读出的embedding的类型 - :param str padding: 词表中padding的token - :param str unknown: 词表中unknown的token - :param bool normalize: 是否将每个vector归一化到norm为1 - :param str error: `ignore` , `strict` ; 如果 `ignore` ,错误将自动跳过; 如果 `strict` , 错误将抛出。 - 这里主要可能出错的地方在于词表有空行或者词表出现了维度不一致。 - :param init_method: 如何初始化没有找到的值。可以使用torch.nn.init.*中各种方法。默认使用torch.nn.init.zeros_ - :return torch.tensor: shape为 [len(vocab), dimension], dimension由pretrain的embedding决定。 - """ - assert isinstance(vocab, Vocabulary), "Only fastNLP.Vocabulary is supported." - if not os.path.exists(embed_filepath): - raise FileNotFoundError("`{}` does not exist.".format(embed_filepath)) - with open(embed_filepath, 'r', encoding='utf-8') as f: - line = f.readline().strip() - parts = line.split() - start_idx = 0 - if len(parts) == 2: - dim = int(parts[1]) - start_idx += 1 - else: - dim = len(parts) - 1 - f.seek(0) - matrix = {} - found_count = 0 - for idx, line in enumerate(f, start_idx): - try: - parts = line.strip().split() - word = ''.join(parts[:-dim]) - nums = parts[-dim:] - # 对齐unk与pad - if word == padding and vocab.padding is not None: - word = vocab.padding - elif word == unknown and vocab.unknown is not None: - word = vocab.unknown - if word in vocab: - index = vocab.to_index(word) - matrix[index] = torch.from_numpy(np.fromstring(' '.join(nums), sep=' ', dtype=dtype, count=dim)) - found_count += 1 - except Exception as e: - if error == 'ignore': - warnings.warn("Error occurred at the {} line.".format(idx)) - else: - print("Error occurred at the {} line.".format(idx)) - raise e - print("Found {} out of {} words in the pre-training embedding.".format(found_count, len(vocab))) - for word, index in vocab: - if index not in matrix and not vocab._is_word_no_create_entry(word): - if vocab.unknown_idx in matrix: # 如果有unkonwn,用unknown初始化 - matrix[index] = matrix[vocab.unknown_idx] - else: - matrix[index] = None - - vectors = torch.zeros(len(matrix), dim) - if init_method: - init_method(vectors) - else: - nn.init.uniform_(vectors, -np.sqrt(3/dim), np.sqrt(3/dim)) - - if vocab._no_create_word_length>0: - if vocab.unknown is None: # 创建一个专门的unknown - unknown_idx = len(matrix) - vectors = torch.cat((vectors, torch.zeros(1, dim)), dim=0).contiguous() - else: - unknown_idx = vocab.unknown_idx - words_to_words = nn.Parameter(torch.full((len(vocab),), fill_value=unknown_idx).long(), - requires_grad=False) - for order, (index, vec) in enumerate(matrix.items()): - if vec is not None: - vectors[order] = vec - words_to_words[index] = order - self.words_to_words = words_to_words - else: - for index, vec in matrix.items(): - if vec is not None: - vectors[index] = vec - - if normalize: - vectors /= (torch.norm(vectors, dim=1, keepdim=True) + 1e-12) - - return vectors - - def forward(self, words): - """ - 传入words的index - - :param words: torch.LongTensor, [batch_size, max_len] - :return: torch.FloatTensor, [batch_size, max_len, embed_size] - """ - if hasattr(self, 'words_to_words'): - words = self.words_to_words[words] - words = self.drop_word(words) - words = self.embedding(words) - words = self.dropout(words) - return words - - -class ContextualEmbedding(TokenEmbedding): - def __init__(self, vocab: Vocabulary, word_dropout:float=0.0, dropout:float=0.0): - super(ContextualEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) - - def add_sentence_cache(self, *datasets, batch_size=32, device='cpu', delete_weights: bool=True): - """ - 由于动态embedding生成比较耗时,所以可以把每句话embedding缓存下来,这样就不需要每次都运行生成过程。 - - :param datasets: DataSet对象 - :param batch_size: int, 生成cache的sentence表示时使用的batch的大小 - :param device: 参考 :class::fastNLP.Trainer 的device - :param delete_weights: 似乎在生成了cache之后删除权重,在不需要finetune动态模型的情况下,删除权重会大量减少内存占用。 - :return: - """ - for index, dataset in enumerate(datasets): - try: - assert isinstance(dataset, DataSet), "Only fastNLP.DataSet object is allowed." - assert 'words' in dataset.get_input_name(), "`words` field has to be set as input." - except Exception as e: - print(f"Exception happens at {index} dataset.") - raise e - - sent_embeds = {} - _move_model_to_device(self, device=device) - device = _get_model_device(self) - pad_index = self._word_vocab.padding_idx - print("Start to calculate sentence representations.") - with torch.no_grad(): - for index, dataset in enumerate(datasets): - try: - batch = DataSetIter(dataset, batch_size=batch_size, sampler=SequentialSampler()) - for batch_x, batch_y in batch: - words = batch_x['words'].to(device) - words_list = words.tolist() - seq_len = words.ne(pad_index).sum(dim=-1) - max_len = words.size(1) - # 因为有些情况可能包含CLS, SEP, 从后面往前计算比较安全。 - seq_len_from_behind = (max_len - seq_len).tolist() - word_embeds = self(words).detach().cpu().numpy() - for b in range(words.size(0)): - length = seq_len_from_behind[b] - if length==0: - sent_embeds[tuple(words_list[b][:seq_len[b]])] = word_embeds[b] - else: - sent_embeds[tuple(words_list[b][:seq_len[b]])] = word_embeds[b, :-length] - except Exception as e: - print(f"Exception happens at {index} dataset.") - raise e - print("Finish calculating sentence representations.") - self.sent_embeds = sent_embeds - if delete_weights: - self._delete_model_weights() - - def _get_sent_reprs(self, words): - """ - 获取sentence的表示,如果有缓存,则返回缓存的值; 没有缓存则返回None - - :param words: torch.LongTensor - :return: - """ - if hasattr(self, 'sent_embeds'): - words_list = words.tolist() - seq_len = words.ne(self._word_pad_index).sum(dim=-1) - _embeds = [] - for b in range(len(words)): - words_i = tuple(words_list[b][:seq_len[b]]) - embed = self.sent_embeds[words_i] - _embeds.append(embed) - max_sent_len = max(map(len, _embeds)) - embeds = words.new_zeros(len(_embeds), max_sent_len, self.embed_size, dtype=torch.float, - device=words.device) - for i, embed in enumerate(_embeds): - embeds[i, :len(embed)] = torch.FloatTensor(embed).to(words.device) - return embeds - return None - - @abstractmethod - def _delete_model_weights(self): - """删除计算表示的模型以节省资源""" - raise NotImplementedError - - def remove_sentence_cache(self): - """ - 删除缓存的句子表示. 删除之后如果模型权重没有被删除,将开始使用动态计算权重。 - - :return: - """ - del self.sent_embeds - - -class ElmoEmbedding(ContextualEmbedding): - """ - 别名::class:`fastNLP.modules.ElmoEmbedding` :class:`fastNLP.modules.encoder.embedding.ElmoEmbedding` - - 使用ELMo的embedding。初始化之后,只需要传入words就可以得到对应的embedding。 - 我们提供的ELMo预训练模型来自 https://github.com/HIT-SCIR/ELMoForManyLangs - - Example:: - - >>> embedding = ElmoEmbedding(vocab, model_dir_or_name='en', layers='2', requires_grad=True) - - :param vocab: 词表 - :param model_dir_or_name: 可以有两种方式调用预训练好的ELMo embedding:第一种是传入ELMo权重的文件名,第二种是传入ELMo版本的名称, - 目前支持的ELMo包括{`en` : 英文版本的ELMo, `cn` : 中文版本的ELMo,}。第二种情况将自动查看缓存中是否存在该模型,没有的话将自动下载 - :param layers: str, 指定返回的层数, 以,隔开不同的层。如果要返回第二层的结果'2', 返回后两层的结果'1,2'。不同的层的结果 - 按照这个顺序concat起来。默认为'2'。'mix'会使用可学习的权重结合不同层的表示(权重是否可训练与requires_grad保持一致, - 初始化权重对三层结果进行mean-pooling, 可以通过ElmoEmbedding.set_mix_weights_requires_grad()方法只将mix weights设置为可学习。) - :param requires_grad: bool, 该层是否需要gradient, 默认为False. - :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 - :param float dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。 - :param cache_word_reprs: 可以选择对word的表示进行cache; 设置为True的话,将在初始化的时候为每个word生成对应的embedding, - 并删除character encoder,之后将直接使用cache的embedding。默认为False。 - """ - def __init__(self, vocab: Vocabulary, model_dir_or_name: str='en', layers: str='2', requires_grad: bool=False, - word_dropout=0.0, dropout=0.0, cache_word_reprs: bool=False): - super(ElmoEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) - - # 根据model_dir_or_name检查是否存在并下载 - if model_dir_or_name.lower() in PRETRAINED_ELMO_MODEL_DIR: - PRETRAIN_URL = _get_base_url('elmo') - model_name = PRETRAINED_ELMO_MODEL_DIR[model_dir_or_name] - model_url = PRETRAIN_URL + model_name - model_dir = cached_path(model_url) - # 检查是否存在 - elif os.path.isdir(os.path.expanduser(os.path.abspath(model_dir_or_name))): - model_dir = model_dir_or_name - else: - raise ValueError(f"Cannot recognize {model_dir_or_name}.") - self.model = _ElmoModel(model_dir, vocab, cache_word_reprs=cache_word_reprs) - - if layers=='mix': - self.layer_weights = nn.Parameter(torch.zeros(self.model.config['lstm']['n_layers']+1), - requires_grad=requires_grad) - self.gamma = nn.Parameter(torch.ones(1), requires_grad=requires_grad) - self._get_outputs = self._get_mixed_outputs - self._embed_size = self.model.config['lstm']['projection_dim'] * 2 - else: - layers = list(map(int, layers.split(','))) - assert len(layers) > 0, "Must choose one output" - for layer in layers: - assert 0 <= layer <= 2, "Layer index should be in range [0, 2]." - self.layers = layers - self._get_outputs = self._get_layer_outputs - self._embed_size = len(self.layers) * self.model.config['lstm']['projection_dim'] * 2 - - self.requires_grad = requires_grad - - def _get_mixed_outputs(self, outputs): - # outputs: num_layers x batch_size x max_len x hidden_size - # return: batch_size x max_len x hidden_size - weights = F.softmax(self.layer_weights+1/len(outputs), dim=0).to(outputs) - outputs = torch.einsum('l,lbij->bij', weights, outputs) - return self.gamma.to(outputs)*outputs - - def set_mix_weights_requires_grad(self, flag=True): - """ - 当初始化ElmoEmbedding时layers被设置为mix时,可以通过调用该方法设置mix weights是否可训练。如果layers不是mix,调用 - 该方法没有用。 - :param bool flag: 混合不同层表示的结果是否可以训练。 - :return: - """ - if hasattr(self, 'layer_weights'): - self.layer_weights.requires_grad = flag - self.gamma.requires_grad = flag - - def _get_layer_outputs(self, outputs): - if len(self.layers) == 1: - outputs = outputs[self.layers[0]] - else: - outputs = torch.cat(tuple([*outputs[self.layers]]), dim=-1) - - return outputs - - def forward(self, words: torch.LongTensor): - """ - 计算words的elmo embedding表示。根据elmo文章中介绍的ELMO实际上是有2L+1层结果,但是为了让结果比较容易拆分,token的 - 被重复了一次,使得实际上layer=0的结果是[token_embedding;token_embedding], 而layer=1的结果是[forward_hiddens; - backward_hiddens]. - - :param words: batch_size x max_len - :return: torch.FloatTensor. batch_size x max_len x (512*len(self.layers)) - """ - words = self.drop_word(words) - outputs = self._get_sent_reprs(words) - if outputs is not None: - return self.dropout(outputs) - outputs = self.model(words) - outputs = self._get_outputs(outputs) - return self.dropout(outputs) - - def _delete_model_weights(self): - for name in ['layers', 'model', 'layer_weights', 'gamma']: - if hasattr(self, name): - delattr(self, name) - - @property - def requires_grad(self): - """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 - - :return: - """ - requires_grads = set([param.requires_grad for name, param in self.named_parameters() - if 'words_to_chars_embedding' not in name and 'words_to_words' not in name]) - if len(requires_grads) == 1: - return requires_grads.pop() - else: - return None - - @requires_grad.setter - def requires_grad(self, value): - for name, param in self.named_parameters(): - if 'words_to_chars_embedding' in name or 'words_to_words' in name: # 这个不能加入到requires_grad中 - continue - param.requires_grad = value - - -class BertEmbedding(ContextualEmbedding): - """ - 别名::class:`fastNLP.modules.BertEmbedding` :class:`fastNLP.modules.encoder.embedding.BertEmbedding` - - 使用BERT对words进行encode的Embedding。建议将输入的words长度限制在450以内,而不要使用512。这是由于预训练的bert模型长 - 度限制为512个token,而因为输入的word是未进行word piece分割的,在分割之后长度可能会超过最大长度限制。 - - Example:: - - >>> embedding = BertEmbedding(vocab, model_dir_or_name='en-base-uncased', requires_grad=False, layers='4,-2,-1') - - - :param fastNLP.Vocabulary vocab: 词表 - :param str model_dir_or_name: 模型所在目录或者模型的名称。默认值为 ``en-base-uncased``. - :param str layers:最终结果中的表示。以','隔开层数,可以以负数去索引倒数几层 - :param str pool_method: 因为在bert中,每个word会被表示为多个word pieces, 当获取一个word的表示的时候,怎样从它的word pieces - 中计算得到它对应的表示。支持``last``, ``first``, ``avg``, ``max``。 - :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 - :param float dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。 - :param bool include_cls_sep: bool,在bert计算句子的表示的时候,需要在前面加上[CLS]和[SEP], 是否在结果中保留这两个内容。 这样 - 会使得word embedding的结果比输入的结果长两个token。在使用 :class::StackEmbedding 可能会遇到问题。 - :param bool requires_grad: 是否需要gradient。 - """ - def __init__(self, vocab: Vocabulary, model_dir_or_name: str='en-base-uncased', layers: str='-1', - pool_method: str='first', word_dropout=0, dropout=0, requires_grad: bool=False, - include_cls_sep: bool=False): - super(BertEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) - - # 根据model_dir_or_name检查是否存在并下载 - if model_dir_or_name.lower() in PRETRAINED_BERT_MODEL_DIR: - PRETRAIN_URL = _get_base_url('bert') - model_name = PRETRAINED_BERT_MODEL_DIR[model_dir_or_name] - model_url = PRETRAIN_URL + model_name - model_dir = cached_path(model_url) - # 检查是否存在 - elif os.path.isdir(os.path.expanduser(os.path.abspath(model_dir_or_name))): - model_dir = model_dir_or_name - else: - raise ValueError(f"Cannot recognize {model_dir_or_name}.") - - self.model = _WordBertModel(model_dir=model_dir, vocab=vocab, layers=layers, - pool_method=pool_method, include_cls_sep=include_cls_sep) - - self.requires_grad = requires_grad - self._embed_size = len(self.model.layers)*self.model.encoder.hidden_size - - def _delete_model_weights(self): - del self.model - - def forward(self, words): - """ - 计算words的bert embedding表示。计算之前会在每句话的开始增加[CLS]在结束增加[SEP], 并根据include_cls_sep判断要不要 - 删除这两个token的表示。 - - :param torch.LongTensor words: [batch_size, max_len] - :return: torch.FloatTensor. batch_size x max_len x (768*len(self.layers)) - """ - words = self.drop_word(words) - outputs = self._get_sent_reprs(words) - if outputs is not None: - return self.dropout(words) - outputs = self.model(words) - outputs = torch.cat([*outputs], dim=-1) - - return self.dropout(words) - - @property - def requires_grad(self): - """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 - :return: - """ - requires_grads = set([param.requires_grad for name, param in self.named_parameters() - if 'word_pieces_lengths' not in name]) - if len(requires_grads) == 1: - return requires_grads.pop() - else: - return None - - @requires_grad.setter - def requires_grad(self, value): - for name, param in self.named_parameters(): - if 'word_pieces_lengths' in name: # 这个不能加入到requires_grad中 - continue - param.requires_grad = value - - -def _construct_char_vocab_from_vocab(vocab:Vocabulary, min_freq:int=1): - """ - 给定一个word的vocabulary生成character的vocabulary. - - :param vocab: 从vocab - :param min_freq: - :return: - """ - char_vocab = Vocabulary(min_freq=min_freq) - for word, index in vocab: - if not vocab._is_word_no_create_entry(word): - char_vocab.add_word_lst(list(word)) - return char_vocab - - -class CNNCharEmbedding(TokenEmbedding): - """ - 别名::class:`fastNLP.modules.CNNCharEmbedding` :class:`fastNLP.modules.encoder.embedding.CNNCharEmbedding` - - 使用CNN生成character embedding。CNN的结果为, embed(x) -> Dropout(x) -> CNN(x) -> activation(x) -> pool -> fc -> Dropout. - 不同的kernel大小的fitler结果是concat起来的。 - - Example:: - - >>> cnn_char_embed = CNNCharEmbedding(vocab) - - - :param vocab: 词表 - :param embed_size: 该word embedding的大小,默认值为50. - :param char_emb_size: character的embed的大小。character是从vocab中生成的。默认值为50. - :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 - :param float dropout: 以多大的概率drop - :param filter_nums: filter的数量. 长度需要和kernels一致。默认值为[40, 30, 20]. - :param kernel_sizes: kernel的大小. 默认值为[5, 3, 1]. - :param pool_method: character的表示在合成一个表示时所使用的pool方法,支持'avg', 'max'. - :param activation: CNN之后使用的激活方法,支持'relu', 'sigmoid', 'tanh' 或者自定义函数. - :param min_char_freq: character的最少出现次数。默认值为2. - """ - def __init__(self, vocab: Vocabulary, embed_size: int=50, char_emb_size: int=50, word_dropout:float=0, - dropout:float=0.5, filter_nums: List[int]=(40, 30, 20), kernel_sizes: List[int]=(5, 3, 1), - pool_method: str='max', activation='relu', min_char_freq: int=2): - super(CNNCharEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout) - - for kernel in kernel_sizes: - assert kernel % 2 == 1, "Only odd kernel is allowed." - - assert pool_method in ('max', 'avg') - self.dropout = nn.Dropout(dropout) - self.pool_method = pool_method - # activation function - if isinstance(activation, str): - if activation.lower() == 'relu': - self.activation = F.relu - elif activation.lower() == 'sigmoid': - self.activation = F.sigmoid - elif activation.lower() == 'tanh': - self.activation = F.tanh - elif activation is None: - self.activation = lambda x: x - elif callable(activation): - self.activation = activation - else: - raise Exception( - "Undefined activation function: choose from: [relu, tanh, sigmoid, or a callable function]") - - print("Start constructing character vocabulary.") - # 建立char的词表 - self.char_vocab = _construct_char_vocab_from_vocab(vocab, min_freq=min_char_freq) - self.char_pad_index = self.char_vocab.padding_idx - print(f"In total, there are {len(self.char_vocab)} distinct characters.") - # 对vocab进行index - max_word_len = max(map(lambda x: len(x[0]), vocab)) - self.words_to_chars_embedding = nn.Parameter(torch.full((len(vocab), max_word_len), - fill_value=self.char_pad_index, dtype=torch.long), - requires_grad=False) - self.word_lengths = nn.Parameter(torch.zeros(len(vocab)).long(), requires_grad=False) - for word, index in vocab: - # if index!=vocab.padding_idx: # 如果是pad的话,直接就为pad_value了。修改为不区分pad, 这样所有的也是同一个embed - self.words_to_chars_embedding[index, :len(word)] = \ - torch.LongTensor([self.char_vocab.to_index(c) for c in word]) - self.word_lengths[index] = len(word) - self.char_embedding = nn.Embedding(len(self.char_vocab), char_emb_size) - - self.convs = nn.ModuleList([nn.Conv1d( - char_emb_size, filter_nums[i], kernel_size=kernel_sizes[i], bias=True, padding=kernel_sizes[i] // 2) - for i in range(len(kernel_sizes))]) - self._embed_size = embed_size - self.fc = nn.Linear(sum(filter_nums), embed_size) - self.init_param() - - def forward(self, words): - """ - 输入words的index后,生成对应的words的表示。 - - :param words: [batch_size, max_len] - :return: [batch_size, max_len, embed_size] - """ - words = self.drop_word(words) - batch_size, max_len = words.size() - chars = self.words_to_chars_embedding[words] # batch_size x max_len x max_word_len - word_lengths = self.word_lengths[words] # batch_size x max_len - max_word_len = word_lengths.max() - chars = chars[:, :, :max_word_len] - # 为1的地方为mask - chars_masks = chars.eq(self.char_pad_index) # batch_size x max_len x max_word_len 如果为0, 说明是padding的位置了 - chars = self.char_embedding(chars) # batch_size x max_len x max_word_len x embed_size - chars = self.dropout(chars) - reshaped_chars = chars.reshape(batch_size*max_len, max_word_len, -1) - reshaped_chars = reshaped_chars.transpose(1, 2) # B' x E x M - conv_chars = [conv(reshaped_chars).transpose(1, 2).reshape(batch_size, max_len, max_word_len, -1) - for conv in self.convs] - conv_chars = torch.cat(conv_chars, dim=-1).contiguous() # B x max_len x max_word_len x sum(filters) - conv_chars = self.activation(conv_chars) - if self.pool_method == 'max': - conv_chars = conv_chars.masked_fill(chars_masks.unsqueeze(-1), float('-inf')) - chars, _ = torch.max(conv_chars, dim=-2) # batch_size x max_len x sum(filters) - else: - conv_chars = conv_chars.masked_fill(chars_masks.unsqueeze(-1), 0) - chars = torch.sum(conv_chars, dim=-2)/chars_masks.eq(0).sum(dim=-1, keepdim=True).float() - chars = self.fc(chars) - return self.dropout(chars) - - @property - def requires_grad(self): - """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 - :return: - """ - params = [] - for name, param in self.named_parameters(): - if 'words_to_chars_embedding' not in name and 'word_lengths' not in name: - params.append(param.requires_grad) - requires_grads = set(params) - if len(requires_grads) == 1: - return requires_grads.pop() - else: - return None - - @requires_grad.setter - def requires_grad(self, value): - for name, param in self.named_parameters(): - if 'words_to_chars_embedding' in name or 'word_lengths' in name: # 这个不能加入到requires_grad中 - continue - param.requires_grad = value - - def init_param(self): - for name, param in self.named_parameters(): - if 'words_to_chars_embedding' in name or 'word_lengths' in name: # 这个不能reset - continue - if param.data.dim()>1: - nn.init.xavier_uniform_(param, 1) - else: - nn.init.uniform_(param, -1, 1) - -class LSTMCharEmbedding(TokenEmbedding): - """ - 别名::class:`fastNLP.modules.LSTMCharEmbedding` :class:`fastNLP.modules.encoder.embedding.LSTMCharEmbedding` - - 使用LSTM的方式对character进行encode. embed(x) -> Dropout(x) -> LSTM(x) -> activation(x) -> pool - - Example:: - - >>> lstm_char_embed = LSTMCharEmbedding(vocab) - - :param vocab: 词表 - :param embed_size: embedding的大小。默认值为50. - :param char_emb_size: character的embedding的大小。默认值为50. - :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。 - :param dropout: 以多大概率drop - :param hidden_size: LSTM的中间hidden的大小,如果为bidirectional的,hidden会除二,默认为50. - :param pool_method: 支持'max', 'avg' - :param activation: 激活函数,支持'relu', 'sigmoid', 'tanh', 或者自定义函数. - :param min_char_freq: character的最小出现次数。默认值为2. - :param bidirectional: 是否使用双向的LSTM进行encode。默认值为True。 - """ - def __init__(self, vocab: Vocabulary, embed_size: int=50, char_emb_size: int=50, word_dropout:float=0, - dropout:float=0.5, hidden_size=50,pool_method: str='max', activation='relu', min_char_freq: int=2, - bidirectional=True): - super(LSTMCharEmbedding, self).__init__(vocab) - - assert hidden_size % 2 == 0, "Only even kernel is allowed." - - assert pool_method in ('max', 'avg') - self.pool_method = pool_method - self.dropout = nn.Dropout(dropout) - # activation function - if isinstance(activation, str): - if activation.lower() == 'relu': - self.activation = F.relu - elif activation.lower() == 'sigmoid': - self.activation = F.sigmoid - elif activation.lower() == 'tanh': - self.activation = F.tanh - elif activation is None: - self.activation = lambda x: x - elif callable(activation): - self.activation = activation - else: - raise Exception( - "Undefined activation function: choose from: [relu, tanh, sigmoid, or a callable function]") - - print("Start constructing character vocabulary.") - # 建立char的词表 - self.char_vocab = _construct_char_vocab_from_vocab(vocab, min_freq=min_char_freq) - self.char_pad_index = self.char_vocab.padding_idx - print(f"In total, there are {len(self.char_vocab)} distinct characters.") - # 对vocab进行index - self.max_word_len = max(map(lambda x: len(x[0]), vocab)) - self.words_to_chars_embedding = nn.Parameter(torch.full((len(vocab), self.max_word_len), - fill_value=self.char_pad_index, dtype=torch.long), - requires_grad=False) - self.word_lengths = nn.Parameter(torch.zeros(len(vocab)).long(), requires_grad=False) - for word, index in vocab: - # if index!=vocab.padding_idx: # 如果是pad的话,直接就为pad_value了. 修改为不区分pad与否 - self.words_to_chars_embedding[index, :len(word)] = \ - torch.LongTensor([self.char_vocab.to_index(c) for c in word]) - self.word_lengths[index] = len(word) - self.char_embedding = nn.Embedding(len(self.char_vocab), char_emb_size) - - self.fc = nn.Linear(hidden_size, embed_size) - hidden_size = hidden_size // 2 if bidirectional else hidden_size - - self.lstm = LSTM(char_emb_size, hidden_size, bidirectional=bidirectional, batch_first=True) - self._embed_size = embed_size - self.bidirectional = bidirectional - - def forward(self, words): - """ - 输入words的index后,生成对应的words的表示。 - - :param words: [batch_size, max_len] - :return: [batch_size, max_len, embed_size] - """ - words = self.drop_word(words) - batch_size, max_len = words.size() - chars = self.words_to_chars_embedding[words] # batch_size x max_len x max_word_len - word_lengths = self.word_lengths[words] # batch_size x max_len - max_word_len = word_lengths.max() - chars = chars[:, :, :max_word_len] - # 为mask的地方为1 - chars_masks = chars.eq(self.char_pad_index) # batch_size x max_len x max_word_len 如果为0, 说明是padding的位置了 - chars = self.char_embedding(chars) # batch_size x max_len x max_word_len x embed_size - chars = self.dropout(chars) - reshaped_chars = chars.reshape(batch_size * max_len, max_word_len, -1) - char_seq_len = chars_masks.eq(0).sum(dim=-1).reshape(batch_size * max_len) - lstm_chars = self.lstm(reshaped_chars, char_seq_len)[0].reshape(batch_size, max_len, max_word_len, -1) - # B x M x M x H - - lstm_chars = self.activation(lstm_chars) - if self.pool_method == 'max': - lstm_chars = lstm_chars.masked_fill(chars_masks.unsqueeze(-1), float('-inf')) - chars, _ = torch.max(lstm_chars, dim=-2) # batch_size x max_len x H - else: - lstm_chars = lstm_chars.masked_fill(chars_masks.unsqueeze(-1), 0) - chars = torch.sum(lstm_chars, dim=-2) / chars_masks.eq(0).sum(dim=-1, keepdim=True).float() - - chars = self.fc(chars) - - return self.dropout(chars) - - @property - def requires_grad(self): - """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 - :return: - """ - params = [] - for name, param in self.named_parameters(): - if 'words_to_chars_embedding' not in name and 'word_lengths' not in name: - params.append(param) - requires_grads = set(params) - if len(requires_grads) == 1: - return requires_grads.pop() - else: - return None - - @requires_grad.setter - def requires_grad(self, value): - for name, param in self.named_parameters(): - if 'words_to_chars_embedding' in name or 'word_lengths' in name: # 这个不能加入到requires_grad中 - continue - param.requires_grad = value - - -class StackEmbedding(TokenEmbedding): - """ - 别名::class:`fastNLP.modules.StackEmbedding` :class:`fastNLP.modules.encoder.embedding.StackEmbedding` - - 支持将多个embedding集合成一个embedding。 - - Example:: - - >>> embed_1 = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50', requires_grad=True) - >>> embed_2 = StaticEmbedding(vocab, model_dir_or_name='en-word2vec-300', requires_grad=True) - - - :param embeds: 一个由若干个TokenEmbedding组成的list,要求每一个TokenEmbedding的词表都保持一致 - :param float word_dropout: 以多大的概率将一个词替换为unk。这样既可以训练unk也是一定的regularize。不同embedidng会在相同的位置 - 被设置为unknown。如果这里设置了dropout,则组成的embedding就不要再设置dropout了。 - :param float dropout: 以多大的概率对embedding的表示进行Dropout。0.1即随机将10%的值置为0。 - - """ - def __init__(self, embeds: List[TokenEmbedding], word_dropout=0, dropout=0): - vocabs = [] - for embed in embeds: - if hasattr(embed, 'get_word_vocab'): - vocabs.append(embed.get_word_vocab()) - _vocab = vocabs[0] - for vocab in vocabs[1:]: - assert vocab == _vocab, "All embeddings in StackEmbedding should use the same word vocabulary." - - super(StackEmbedding, self).__init__(_vocab, word_dropout=word_dropout, dropout=dropout) - assert isinstance(embeds, list) - for embed in embeds: - assert isinstance(embed, TokenEmbedding), "Only TokenEmbedding type is supported." - self.embeds = nn.ModuleList(embeds) - self._embed_size = sum([embed.embed_size for embed in self.embeds]) - - def append(self, embed: TokenEmbedding): - """ - 添加一个embedding到结尾。 - :param embed: - :return: - """ - assert isinstance(embed, TokenEmbedding) - self.embeds.append(embed) - - def pop(self): - """ - 弹出最后一个embed - :return: - """ - return self.embeds.pop() - - @property - def embed_size(self): - return self._embed_size - - @property - def requires_grad(self): - """ - Embedding的参数是否允许优化。True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化、部分不允许 - :return: - """ - requires_grads = set([embed.requires_grad for embed in self.embeds()]) - if len(requires_grads)==1: - return requires_grads.pop() - else: - return None - - @requires_grad.setter - def requires_grad(self, value): - for embed in self.embeds(): - embed.requires_grad = value - - def forward(self, words): - """ - 得到多个embedding的结果,并把结果按照顺序concat起来。 - - :param words: batch_size x max_len - :return: 返回的shape和当前这个stack embedding中embedding的组成有关 - """ - outputs = [] - words = self.drop_word(words) - for embed in self.embeds: - outputs.append(embed(words)) - outputs = self.dropout(torch.cat(outputs, dim=-1)) - return outputs - diff --git a/fastNLP/modules/utils.py b/fastNLP/modules/utils.py index 3c6a3d27..4a9e034d 100644 --- a/fastNLP/modules/utils.py +++ b/fastNLP/modules/utils.py @@ -1,6 +1,5 @@ from functools import reduce -import numpy as np import torch import torch.nn as nn import torch.nn.init as init @@ -70,33 +69,6 @@ def initial_parameter(net, initial_method=None): net.apply(weights_init) -def get_embeddings(init_embed): - """ - 根据输入的init_embed生成nn.Embedding对象。 - - :param init_embed: 可以是 tuple:(num_embedings, embedding_dim), 即embedding的大小和每个词的维度;也可以传入 - nn.Embedding 对象, 此时就以传入的对象作为embedding; 传入np.ndarray也行,将使用传入的ndarray作为作为Embedding初始 - 化; 传入orch.Tensor, 将使用传入的值作为Embedding初始化。 - :return nn.Embedding embeddings: - """ - if isinstance(init_embed, tuple): - res = nn.Embedding( - num_embeddings=init_embed[0], embedding_dim=init_embed[1]) - nn.init.uniform_(res.weight.data, a=-np.sqrt(3/res.weight.data.size(1)), - b=np.sqrt(3/res.weight.data.size(1))) - elif isinstance(init_embed, nn.Module): - res = init_embed - elif isinstance(init_embed, torch.Tensor): - res = nn.Embedding.from_pretrained(init_embed, freeze=False) - elif isinstance(init_embed, np.ndarray): - init_embed = torch.tensor(init_embed, dtype=torch.float32) - res = nn.Embedding.from_pretrained(init_embed, freeze=False) - else: - raise TypeError( - 'invalid init_embed type: {}'.format((type(init_embed)))) - return res - - def summary(model: nn.Module): """ 得到模型的总参数量 diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/README.md b/reproduction/LSTM+self_attention_sentiment_analysis/README.md index 2dff7caa..dfb337ec 100644 --- a/reproduction/LSTM+self_attention_sentiment_analysis/README.md +++ b/reproduction/LSTM+self_attention_sentiment_analysis/README.md @@ -1,5 +1,7 @@ # Prototype +这是一个很旧版本的reproduction,待修改 + ## Word2Idx.py A mapping model between words and indexes diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/main.py b/reproduction/LSTM+self_attention_sentiment_analysis/main.py index 871dc476..05077530 100644 --- a/reproduction/LSTM+self_attention_sentiment_analysis/main.py +++ b/reproduction/LSTM+self_attention_sentiment_analysis/main.py @@ -1,6 +1,9 @@ +# 这是一个很旧版本的代码 + +""" import torch.nn.functional as F -from fastNLP.core.trainer import ClassificationTrainer +from fastNLP.core.trainer import Trainer from fastNLP.core.utils import ClassPreprocess as Preprocess from fastNLP.io.config_io import ConfigLoader from fastNLP.io.config_io import ConfigSection @@ -8,7 +11,7 @@ from fastNLP.io.dataset_loader import DummyClassificationReader as Dataset_loade from fastNLP.models.base_model import BaseModel from fastNLP.modules.aggregator.self_attention import SelfAttention from fastNLP.modules.decoder.mlp import MLP -from fastNLP.modules.encoder.embedding import Embedding as Embedding +from fastNLP.embeddings.embedding import Embedding as Embedding from fastNLP.modules.encoder.lstm import LSTM train_data_path = 'small_train_data.txt' @@ -61,12 +64,13 @@ class SELF_ATTENTION_YELP_CLASSIFICATION(BaseModel): train_args = ConfigSection() ConfigLoader("good path").load_config('config.cfg',{"train": train_args}) -train_args['vocab'] = len(word2index) +# train_args['vocab'] = len(word2index) -trainer = ClassificationTrainer(**train_args.data) +trainer = Trainer(**train_args.data) # for k in train_args.__dict__.keys(): # print(k, train_args[k]) model = SELF_ATTENTION_YELP_CLASSIFICATION(train_args) -trainer.train(model,train_data , dev_data) +trainer.train() +""" diff --git a/reproduction/Star_transformer/train.py b/reproduction/Star_transformer/train.py index f1e5c2f9..d8e2576b 100644 --- a/reproduction/Star_transformer/train.py +++ b/reproduction/Star_transformer/train.py @@ -1,7 +1,7 @@ -from util import get_argparser, set_gpu, set_rng_seeds, add_model_args +from reproduction.Star_transformer.util import get_argparser, set_gpu, set_rng_seeds, add_model_args seed = set_rng_seeds(15360) print('RNG SEED {}'.format(seed)) -from datasets import load_seqtag, load_sst, load_snli, EmbedLoader, MAX_LEN +from reproduction.Star_transformer.datasets import load_seqtag, load_sst, load_snli, EmbedLoader, MAX_LEN import torch.nn as nn import torch import numpy as np diff --git a/reproduction/Summarization/BertSum/model.py b/reproduction/Summarization/BertSum/model.py index 655ad16e..1ee821fc 100644 --- a/reproduction/Summarization/BertSum/model.py +++ b/reproduction/Summarization/BertSum/model.py @@ -2,7 +2,7 @@ import torch from torch import nn from torch.nn import init -from fastNLP.modules.encoder._bert import BertModel +from fastNLP.modules.encoder.bert import BertModel class Classifier(nn.Module): diff --git a/reproduction/joint_cws_parse/readme.md b/reproduction/joint_cws_parse/README.md similarity index 100% rename from reproduction/joint_cws_parse/readme.md rename to reproduction/joint_cws_parse/README.md diff --git a/reproduction/joint_cws_parse/models/CharParser.py b/reproduction/joint_cws_parse/models/CharParser.py index 1ed5ea2d..c07c070e 100644 --- a/reproduction/joint_cws_parse/models/CharParser.py +++ b/reproduction/joint_cws_parse/models/CharParser.py @@ -12,7 +12,7 @@ from torch.nn import functional as F from fastNLP.modules.dropout import TimestepDropout from fastNLP.modules.encoder.variational_rnn import VarLSTM from fastNLP import seq_len_to_mask -from fastNLP.modules import Embedding +from fastNLP.embeddings import Embedding def drop_input_independent(word_embeddings, dropout_emb): diff --git a/reproduction/joint_cws_parse/train.py b/reproduction/joint_cws_parse/train.py index 2f8b0d04..0c34614b 100644 --- a/reproduction/joint_cws_parse/train.py +++ b/reproduction/joint_cws_parse/train.py @@ -2,15 +2,15 @@ import sys sys.path.append('../..') from reproduction.joint_cws_parse.data.data_loader import CTBxJointLoader -from fastNLP.modules.encoder.embedding import StaticEmbedding +from fastNLP.embeddings.static_embedding import StaticEmbedding from torch import nn from functools import partial from reproduction.joint_cws_parse.models.CharParser import CharParser from reproduction.joint_cws_parse.models.metrics import SegAppCharParseF1Metric, CWSMetric -from fastNLP import cache_results, BucketSampler, Trainer +from fastNLP import BucketSampler, Trainer from torch import optim -from reproduction.joint_cws_parse.models.callbacks import DevCallback, OptimizerCallback -from torch.optim.lr_scheduler import LambdaLR, StepLR +from reproduction.joint_cws_parse.models.callbacks import DevCallback +from torch.optim.lr_scheduler import StepLR from fastNLP import Tester from fastNLP import GradientClipCallback, LRScheduler import os diff --git a/reproduction/matching/data/MatchingDataLoader.py b/reproduction/matching/data/MatchingDataLoader.py index 67fa4c8d..bba26a8a 100644 --- a/reproduction/matching/data/MatchingDataLoader.py +++ b/reproduction/matching/data/MatchingDataLoader.py @@ -1,3 +1,7 @@ +""" +这个文件的内容已合并到fastNLP.io.data_loader里,这个文件的内容不再更新 +""" + import os diff --git a/reproduction/matching/matching_bert.py b/reproduction/matching/matching_bert.py index 75112d5a..3ed75fd1 100644 --- a/reproduction/matching/matching_bert.py +++ b/reproduction/matching/matching_bert.py @@ -3,9 +3,8 @@ import numpy as np import torch from fastNLP.core import Trainer, Tester, AccuracyMetric, Const, Adam +from fastNLP.io.data_loader import SNLILoader, RTELoader, MNLILoader, QNLILoader, QuoraLoader -from reproduction.matching.data.MatchingDataLoader import SNLILoader, RTELoader, \ - MNLILoader, QNLILoader, QuoraLoader from reproduction.matching.model.bert import BertForNLI diff --git a/reproduction/matching/matching_cntn.py b/reproduction/matching/matching_cntn.py index d813164d..098f3bc4 100644 --- a/reproduction/matching/matching_cntn.py +++ b/reproduction/matching/matching_cntn.py @@ -1,11 +1,10 @@ import argparse import torch -import os from fastNLP.core import Trainer, Tester, Adam, AccuracyMetric, Const -from fastNLP.modules.encoder.embedding import StaticEmbedding +from fastNLP.embeddings import StaticEmbedding +from fastNLP.io.data_loader import QNLILoader, RTELoader, SNLILoader, MNLILoader -from reproduction.matching.data.MatchingDataLoader import QNLILoader, RTELoader, SNLILoader, MNLILoader from reproduction.matching.model.cntn import CNTNModel # define hyper-parameters diff --git a/reproduction/matching/matching_esim.py b/reproduction/matching/matching_esim.py index d878608f..2ff6916a 100644 --- a/reproduction/matching/matching_esim.py +++ b/reproduction/matching/matching_esim.py @@ -7,11 +7,10 @@ from torch.optim.lr_scheduler import StepLR from fastNLP.core import Trainer, Tester, AccuracyMetric, Const from fastNLP.core.callback import GradientClipCallback, LRScheduler -from fastNLP.modules.encoder.embedding import ElmoEmbedding, StaticEmbedding - -from reproduction.matching.data.MatchingDataLoader import SNLILoader, RTELoader, \ - MNLILoader, QNLILoader, QuoraLoader -from reproduction.matching.model.esim import ESIMModel +from fastNLP.embeddings.static_embedding import StaticEmbedding +from fastNLP.embeddings.elmo_embedding import ElmoEmbedding +from fastNLP.io.data_loader import SNLILoader, RTELoader, MNLILoader, QNLILoader, QuoraLoader +from fastNLP.models.snli import ESIM # define hyper-parameters @@ -81,7 +80,7 @@ else: raise RuntimeError(f'NOT support {arg.embedding} embedding yet!') # define model -model = ESIMModel(embedding, num_labels=len(data_info.vocabs[Const.TARGET])) +model = ESIM(embedding, num_labels=len(data_info.vocabs[Const.TARGET])) # define optimizer and callback optimizer = Adamax(lr=arg.lr, params=model.parameters()) diff --git a/reproduction/matching/matching_mwan.py b/reproduction/matching/matching_mwan.py index e96ee0c9..31af54c5 100644 --- a/reproduction/matching/matching_mwan.py +++ b/reproduction/matching/matching_mwan.py @@ -1,23 +1,17 @@ -import sys - -import os import random import numpy as np import torch -from torch.optim import Adadelta, SGD +from torch.optim import Adadelta from torch.optim.lr_scheduler import StepLR -from tqdm import tqdm - from fastNLP import CrossEntropyLoss from fastNLP import cache_results -from fastNLP.core import Trainer, Tester, Adam, AccuracyMetric, Const -from fastNLP.core.predictor import Predictor -from fastNLP.core.callback import GradientClipCallback, LRScheduler, FitlogCallback -from fastNLP.modules.encoder.embedding import ElmoEmbedding, StaticEmbedding +from fastNLP.core import Trainer, Tester, AccuracyMetric, Const +from fastNLP.core.callback import LRScheduler, FitlogCallback +from fastNLP.embeddings import StaticEmbedding -from fastNLP.io.data_loader import MNLILoader, QNLILoader, QuoraLoader, SNLILoader, RTELoader +from fastNLP.io.data_loader import MNLILoader, QNLILoader, SNLILoader, RTELoader from reproduction.matching.model.mwan import MwanModel import fitlog diff --git a/reproduction/matching/model/bert.py b/reproduction/matching/model/bert.py index 9b3a78b2..a21f8c36 100644 --- a/reproduction/matching/model/bert.py +++ b/reproduction/matching/model/bert.py @@ -4,7 +4,7 @@ import torch.nn as nn from fastNLP.core.const import Const from fastNLP.models import BaseModel -from fastNLP.modules.encoder.bert import BertModel +from fastNLP.embeddings.bert import BertModel class BertForNLI(BaseModel): diff --git a/reproduction/matching/model/cntn.py b/reproduction/matching/model/cntn.py index 0b4803fa..a0a104a3 100644 --- a/reproduction/matching/model/cntn.py +++ b/reproduction/matching/model/cntn.py @@ -6,7 +6,7 @@ import numpy as np from torch.nn import CrossEntropyLoss from fastNLP.models import BaseModel -from fastNLP.modules.encoder.embedding import TokenEmbedding +from fastNLP.embeddings.embedding import TokenEmbedding from fastNLP.core.const import Const diff --git a/reproduction/matching/model/esim.py b/reproduction/matching/model/esim.py index 187e565d..87e5ba65 100644 --- a/reproduction/matching/model/esim.py +++ b/reproduction/matching/model/esim.py @@ -5,8 +5,7 @@ import torch.nn.functional as F from torch.nn import CrossEntropyLoss from fastNLP.models import BaseModel -from fastNLP.modules.encoder.embedding import TokenEmbedding -from fastNLP.modules.encoder.lstm import LSTM +from fastNLP.embeddings.embedding import TokenEmbedding from fastNLP.core.const import Const from fastNLP.core.utils import seq_len_to_mask diff --git a/reproduction/seqence_labelling/cws/model/model.py b/reproduction/seqence_labelling/cws/model/model.py index bdd9002d..de945ac3 100644 --- a/reproduction/seqence_labelling/cws/model/model.py +++ b/reproduction/seqence_labelling/cws/model/model.py @@ -1,6 +1,6 @@ from torch import nn import torch -from fastNLP.modules import Embedding +from fastNLP.embeddings import Embedding import numpy as np from reproduction.seqence_labelling.cws.model.module import FeatureFunMax, SemiCRFShiftRelay from fastNLP.modules import LSTM diff --git a/reproduction/seqence_labelling/ner/train_cnn_lstm_crf_conll2003.py b/reproduction/seqence_labelling/ner/train_cnn_lstm_crf_conll2003.py index e9d18048..caa0247a 100644 --- a/reproduction/seqence_labelling/ner/train_cnn_lstm_crf_conll2003.py +++ b/reproduction/seqence_labelling/ner/train_cnn_lstm_crf_conll2003.py @@ -1,7 +1,7 @@ import sys sys.path.append('../../..') -from fastNLP.modules.encoder.embedding import CNNCharEmbedding, StaticEmbedding, BertEmbedding, ElmoEmbedding, StackEmbedding +from fastNLP.embeddings.embedding import CNNCharEmbedding, StaticEmbedding from fastNLP.core.vocabulary import VocabularyOption from reproduction.seqence_labelling.ner.model.lstm_cnn_crf import CNNBiLSTMCRF @@ -9,13 +9,11 @@ from fastNLP import Trainer from fastNLP import SpanFPreRecMetric from fastNLP import BucketSampler from fastNLP import Const -from torch.optim import SGD, Adam +from torch.optim import SGD from fastNLP import GradientClipCallback from fastNLP.core.callback import FitlogCallback, LRScheduler from torch.optim.lr_scheduler import LambdaLR -from fastNLP.core.optimizer import AdamW # from reproduction.seqence_labelling.ner.model.swats import SWATS -from reproduction.seqence_labelling.chinese_ner.callbacks import SaveModelCallback from fastNLP import cache_results import fitlog diff --git a/reproduction/seqence_labelling/ner/train_idcnn.py b/reproduction/seqence_labelling/ner/train_idcnn.py index a21499ab..53f2798f 100644 --- a/reproduction/seqence_labelling/ner/train_idcnn.py +++ b/reproduction/seqence_labelling/ner/train_idcnn.py @@ -1,21 +1,18 @@ from reproduction.seqence_labelling.ner.data.OntoNoteLoader import OntoNoteNERDataLoader -from reproduction.seqence_labelling.ner.data.Conll2003Loader import Conll2003DataLoader -from fastNLP.core.callback import FitlogCallback, LRScheduler +from fastNLP.core.callback import LRScheduler from fastNLP import GradientClipCallback -from torch.optim.lr_scheduler import LambdaLR, CosineAnnealingLR -from torch.optim import SGD, Adam +from torch.optim.lr_scheduler import LambdaLR +from torch.optim import Adam from fastNLP import Const -from fastNLP import RandomSampler, BucketSampler +from fastNLP import BucketSampler from fastNLP import SpanFPreRecMetric from fastNLP import Trainer, Tester from fastNLP.core.metrics import MetricBase from reproduction.seqence_labelling.ner.model.dilated_cnn import IDCNN from fastNLP.core.utils import Option -from fastNLP.modules.encoder.embedding import CNNCharEmbedding, StaticEmbedding +from fastNLP.embeddings.embedding import StaticEmbedding from fastNLP.core.utils import cache_results from fastNLP.core.vocabulary import VocabularyOption -import fitlog -import sys import torch.cuda import os os.environ['FASTNLP_BASE_URL'] = 'http://10.141.222.118:8888/file/download/' diff --git a/reproduction/seqence_labelling/ner/train_ontonote.py b/reproduction/seqence_labelling/ner/train_ontonote.py index 6548cb9f..01fcd032 100644 --- a/reproduction/seqence_labelling/ner/train_ontonote.py +++ b/reproduction/seqence_labelling/ner/train_ontonote.py @@ -2,18 +2,17 @@ import sys sys.path.append('../../..') -from fastNLP.modules.encoder.embedding import CNNCharEmbedding, StaticEmbedding +from fastNLP.embeddings.embedding import CNNCharEmbedding from reproduction.seqence_labelling.ner.model.lstm_cnn_crf import CNNBiLSTMCRF from fastNLP import Trainer from fastNLP import SpanFPreRecMetric from fastNLP import BucketSampler from fastNLP import Const -from torch.optim import SGD, Adam +from torch.optim import SGD from torch.optim.lr_scheduler import LambdaLR from fastNLP import GradientClipCallback from fastNLP.core.callback import FitlogCallback, LRScheduler -from reproduction.seqence_labelling.ner.model.swats import SWATS import fitlog fitlog.debug() diff --git a/reproduction/text_classification/model/HAN.py b/reproduction/text_classification/model/HAN.py index 0902d1e4..7ebbe30f 100644 --- a/reproduction/text_classification/model/HAN.py +++ b/reproduction/text_classification/model/HAN.py @@ -1,7 +1,7 @@ import torch import torch.nn as nn from torch.autograd import Variable -from fastNLP.modules.utils import get_embeddings +from fastNLP.embeddings.utils import get_embeddings from fastNLP.core import Const as C diff --git a/reproduction/text_classification/model/dpcnn.py b/reproduction/text_classification/model/dpcnn.py index dafe62bc..ae2d46bd 100644 --- a/reproduction/text_classification/model/dpcnn.py +++ b/reproduction/text_classification/model/dpcnn.py @@ -1,6 +1,6 @@ import torch import torch.nn as nn -from fastNLP.modules.utils import get_embeddings +from fastNLP.embeddings.utils import get_embeddings from fastNLP.core import Const as C diff --git a/reproduction/text_classification/train_HAN.py b/reproduction/text_classification/train_HAN.py index b1135342..a8b06146 100644 --- a/reproduction/text_classification/train_HAN.py +++ b/reproduction/text_classification/train_HAN.py @@ -9,11 +9,9 @@ os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID" from fastNLP.core.const import Const as C from fastNLP.core import LRScheduler -import torch.nn as nn -from fastNLP.io.dataset_loader import SSTLoader -from reproduction.text_classification.data.yelpLoader import yelpLoader +from fastNLP.io.data_loader import YelpLoader from reproduction.text_classification.model.HAN import HANCLS -from fastNLP.modules.encoder.embedding import StaticEmbedding, CNNCharEmbedding, StackEmbedding +from fastNLP.embeddings import StaticEmbedding from fastNLP import CrossEntropyLoss, AccuracyMetric from fastNLP.core.trainer import Trainer from torch.optim import SGD @@ -44,7 +42,7 @@ ops = Config() ##1.task相关信息:利用dataloader载入dataInfo -datainfo = yelpLoader(fine_grained=True).process(paths=ops.datapath, train_ds=['train']) +datainfo = YelpLoader(fine_grained=True).process(paths=ops.datapath, train_ds=['train']) print(len(datainfo.datasets['train'])) print(len(datainfo.datasets['test'])) diff --git a/reproduction/text_classification/train_awdlstm.py b/reproduction/text_classification/train_awdlstm.py index 007b2910..b0f2af49 100644 --- a/reproduction/text_classification/train_awdlstm.py +++ b/reproduction/text_classification/train_awdlstm.py @@ -5,20 +5,13 @@ import os os.environ['FASTNLP_BASE_URL'] = 'http://10.141.222.118:8888/file/download/' os.environ['FASTNLP_CACHE_DIR'] = '/remote-home/hyan01/fastnlp_caches' - -import torch.nn as nn - from data.IMDBLoader import IMDBLoader -from fastNLP.modules.encoder.embedding import StaticEmbedding +from fastNLP.embeddings import StaticEmbedding from model.awd_lstm import AWDLSTMSentiment -from fastNLP.core.const import Const as C from fastNLP import CrossEntropyLoss, AccuracyMetric -from fastNLP import Trainer, Tester +from fastNLP import Trainer from torch.optim import Adam -from fastNLP.io.model_io import ModelLoader, ModelSaver - -import argparse class Config(): diff --git a/reproduction/text_classification/train_char_cnn.py b/reproduction/text_classification/train_char_cnn.py index e4bb9220..0b8fc535 100644 --- a/reproduction/text_classification/train_char_cnn.py +++ b/reproduction/text_classification/train_char_cnn.py @@ -7,23 +7,17 @@ import sys sys.path.append('../..') from fastNLP.core.const import Const as C import torch.nn as nn -from data.yelpLoader import yelpLoader +from fastNLP.io.data_loader import YelpLoader #from data.sstLoader import sst2Loader -from fastNLP.io.data_loader.sst import SST2Loader -from data.IMDBLoader import IMDBLoader from model.char_cnn import CharacterLevelCNN -from fastNLP.core.vocabulary import Vocabulary -from fastNLP.models.cnn_text_classification import CNNText -from fastNLP.modules.encoder.embedding import CNNCharEmbedding,StaticEmbedding,StackEmbedding,LSTMCharEmbedding from fastNLP import CrossEntropyLoss, AccuracyMetric from fastNLP.core.trainer import Trainer from torch.optim import SGD from torch.autograd import Variable import torch -from fastNLP import BucketSampler -from torch.optim.lr_scheduler import CosineAnnealingLR, LambdaLR +from torch.optim.lr_scheduler import LambdaLR from fastNLP.core import LRScheduler -from utils.util_init import set_rng_seeds + ##hyper #todo 这里加入fastnlp的记录 @@ -117,7 +111,7 @@ ops=Config ##1.task相关信息:利用dataloader载入dataInfo #dataloader=SST2Loader() #dataloader=IMDBLoader() -dataloader=yelpLoader(fine_grained=True) +dataloader=YelpLoader(fine_grained=True) datainfo=dataloader.process(ops.datapath,char_level_op=True,split_dev_op=False) char_vocab=ops.char_cnn_config["alphabet"]["en"]["lower"]["alphabet"] ops.number_of_characters=len(char_vocab) diff --git a/reproduction/text_classification/train_dpcnn.py b/reproduction/text_classification/train_dpcnn.py index 70570970..6cce453b 100644 --- a/reproduction/text_classification/train_dpcnn.py +++ b/reproduction/text_classification/train_dpcnn.py @@ -3,15 +3,14 @@ import torch.cuda from fastNLP.core.utils import cache_results from torch.optim import SGD -from torch.optim.lr_scheduler import CosineAnnealingLR, LambdaLR +from torch.optim.lr_scheduler import CosineAnnealingLR from fastNLP.core.trainer import Trainer from fastNLP import CrossEntropyLoss, AccuracyMetric -from fastNLP.modules.encoder.embedding import StaticEmbedding, CNNCharEmbedding, StackEmbedding +from fastNLP.embeddings import StaticEmbedding from reproduction.text_classification.model.dpcnn import DPCNN -from data.yelpLoader import yelpLoader +from fastNLP.io.data_loader import YelpLoader from fastNLP.core.sampler import BucketSampler -import torch.nn as nn -from fastNLP.core import LRScheduler, Callback +from fastNLP.core import LRScheduler from fastNLP.core.const import Const as C from fastNLP.core.vocabulary import VocabularyOption from utils.util_init import set_rng_seeds @@ -59,7 +58,7 @@ print('RNG SEED: {}'.format(ops.seed)) @cache_results(ops.model_dir_or_name+'-data-cache') def load_data(): - datainfo = yelpLoader(fine_grained=True, lower=True).process( + datainfo = YelpLoader(fine_grained=True, lower=True).process( paths=ops.datapath, train_ds=['train'], src_vocab_op=ops.src_vocab_op) for ds in datainfo.datasets.values(): ds.apply_field(len, C.INPUT, C.INPUT_LEN) diff --git a/reproduction/text_classification/train_lstm.py b/reproduction/text_classification/train_lstm.py index 4ecc61a1..40f77061 100644 --- a/reproduction/text_classification/train_lstm.py +++ b/reproduction/text_classification/train_lstm.py @@ -3,20 +3,13 @@ import os os.environ['FASTNLP_BASE_URL'] = 'http://10.141.222.118:8888/file/download/' os.environ['FASTNLP_CACHE_DIR'] = '/remote-home/hyan01/fastnlp_caches' - -import torch.nn as nn - -from data.IMDBLoader import IMDBLoader -from fastNLP.modules.encoder.embedding import StaticEmbedding +from fastNLP.io.data_loader import IMDBLoader +from fastNLP.embeddings import StaticEmbedding from model.lstm import BiLSTMSentiment -from fastNLP.core.const import Const as C from fastNLP import CrossEntropyLoss, AccuracyMetric -from fastNLP import Trainer, Tester +from fastNLP import Trainer from torch.optim import Adam -from fastNLP.io.model_io import ModelLoader, ModelSaver - -import argparse class Config(): diff --git a/reproduction/text_classification/train_lstm_att.py b/reproduction/text_classification/train_lstm_att.py index a6f0dd03..1052f606 100644 --- a/reproduction/text_classification/train_lstm_att.py +++ b/reproduction/text_classification/train_lstm_att.py @@ -3,20 +3,13 @@ import os os.environ['FASTNLP_BASE_URL'] = 'http://10.141.222.118:8888/file/download/' os.environ['FASTNLP_CACHE_DIR'] = '/remote-home/hyan01/fastnlp_caches' - -import torch.nn as nn - -from data.IMDBLoader import IMDBLoader -from fastNLP.modules.encoder.embedding import StaticEmbedding +from fastNLP.io.data_loader import IMDBLoader +from fastNLP.embeddings import StaticEmbedding from model.lstm_self_attention import BiLSTM_SELF_ATTENTION -from fastNLP.core.const import Const as C from fastNLP import CrossEntropyLoss, AccuracyMetric -from fastNLP import Trainer, Tester +from fastNLP import Trainer from torch.optim import Adam -from fastNLP.io.model_io import ModelLoader, ModelSaver - -import argparse class Config(): diff --git a/test/embeddings/test_char_embedding.py b/test/embeddings/test_char_embedding.py new file mode 100644 index 00000000..ceafe4f5 --- /dev/null +++ b/test/embeddings/test_char_embedding.py @@ -0,0 +1,26 @@ +import unittest + +import torch + +from fastNLP import Vocabulary, DataSet, Instance +from fastNLP.embeddings.char_embedding import LSTMCharEmbedding, CNNCharEmbedding + + +class TestCharEmbed(unittest.TestCase): + def test_case_1(self): + ds = DataSet([Instance(words=['hello', 'world']), Instance(words=['Jack'])]) + vocab = Vocabulary().from_dataset(ds, field_name='words') + self.assertEqual(len(vocab), 5) + embed = LSTMCharEmbedding(vocab, embed_size=60) + x = torch.LongTensor([[2, 1, 0], [4, 3, 4]]) + y = embed(x) + self.assertEqual(tuple(y.size()), (2, 3, 60)) + + def test_case_2(self): + ds = DataSet([Instance(words=['hello', 'world']), Instance(words=['Jack'])]) + vocab = Vocabulary().from_dataset(ds, field_name='words') + self.assertEqual(len(vocab), 5) + embed = CNNCharEmbedding(vocab, embed_size=60) + x = torch.LongTensor([[2, 1, 0], [4, 3, 4]]) + y = embed(x) + self.assertEqual(tuple(y.size()), (2, 3, 60)) diff --git a/test/modules/encoder/test_bert.py b/test/modules/encoder/test_bert.py index 2a799478..0fcf01e4 100644 --- a/test/modules/encoder/test_bert.py +++ b/test/modules/encoder/test_bert.py @@ -8,7 +8,7 @@ from fastNLP.models.bert import BertModel class TestBert(unittest.TestCase): def test_bert_1(self): - from fastNLP.modules.encoder._bert import BertConfig + from fastNLP.modules.encoder.bert import BertConfig config = BertConfig(32000) model = BertModel(config) From 76198acc5c9f066df78d80d69253716f886753f1 Mon Sep 17 00:00:00 2001 From: xuyige Date: Fri, 12 Jul 2019 04:09:27 +0800 Subject: [PATCH 010/139] update README.md: add description in fastNLP.embeddings --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6eff6497..1e81db24 100644 --- a/README.md +++ b/README.md @@ -55,12 +55,17 @@ python -m spacy download en ## 内置组件 -大部分用于的 NLP 任务神经网络都可以看做由编码器(encoder)、解码器(decoder)两种模块组成。 +大部分用于的 NLP 任务神经网络都可以看做由词嵌入(embeddings)和两种模块:编码器(encoder)、解码器(decoder)组成。 + +以文本分类任务为例,下图展示了一个BiLSTM+Attention实现文本分类器的模型流程图: ![](./docs/source/figures/text_classification.png) -fastNLP 在 modules 模块中内置了两种模块的诸多组件,可以帮助用户快速搭建自己所需的网络。 两种模块的功能和常见组件如下: +fastNLP 在 embeddings 模块中内置了几种不同的embedding:静态embedding(GloVe、word2vec)、上下文相关embedding +(ELMo、BERT)、字符embedding(基于CNN或者LSTM的CharEmbedding) + +与此同时,fastNLP 在 modules 模块中内置了两种模块的诸多组件,可以帮助用户快速搭建自己所需的网络。 两种模块的功能和常见组件如下: @@ -104,6 +109,10 @@ fastNLP的大致工作流程如上图所示,而项目结构如下: + + + + From 327833a77f1dd99ad20b7853177a2338ff2c35cd Mon Sep 17 00:00:00 2001 From: xuyige Date: Fri, 12 Jul 2019 04:11:03 +0800 Subject: [PATCH 011/139] fix bug in test code --- test/models/test_bert.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/models/test_bert.py b/test/models/test_bert.py index 38a16f9b..05ee6d5a 100644 --- a/test/models/test_bert.py +++ b/test/models/test_bert.py @@ -8,7 +8,7 @@ from fastNLP.models.bert import * class TestBert(unittest.TestCase): def test_bert_1(self): from fastNLP.core.const import Const - from fastNLP.modules.encoder._bert import BertConfig + from fastNLP.modules.encoder.bert import BertConfig model = BertForSequenceClassification(2, BertConfig(32000)) @@ -23,7 +23,7 @@ class TestBert(unittest.TestCase): def test_bert_2(self): from fastNLP.core.const import Const - from fastNLP.modules.encoder._bert import BertConfig + from fastNLP.modules.encoder.bert import BertConfig model = BertForMultipleChoice(2, BertConfig(32000)) @@ -38,7 +38,7 @@ class TestBert(unittest.TestCase): def test_bert_3(self): from fastNLP.core.const import Const - from fastNLP.modules.encoder._bert import BertConfig + from fastNLP.modules.encoder.bert import BertConfig model = BertForTokenClassification(7, BertConfig(32000)) @@ -53,7 +53,7 @@ class TestBert(unittest.TestCase): def test_bert_4(self): from fastNLP.core.const import Const - from fastNLP.modules.encoder._bert import BertConfig + from fastNLP.modules.encoder.bert import BertConfig model = BertForQuestionAnswering(BertConfig(32000)) From 65f92855ba3b5a6a072da807bee4ab14b963dbc4 Mon Sep 17 00:00:00 2001 From: xuyige Date: Fri, 12 Jul 2019 04:24:08 +0800 Subject: [PATCH 012/139] add testing code in stack embedding --- test/embeddings/test_stack_embeddings.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 test/embeddings/test_stack_embeddings.py diff --git a/test/embeddings/test_stack_embeddings.py b/test/embeddings/test_stack_embeddings.py new file mode 100644 index 00000000..2eb0b414 --- /dev/null +++ b/test/embeddings/test_stack_embeddings.py @@ -0,0 +1,20 @@ +import unittest + +import torch + +from fastNLP import Vocabulary, DataSet, Instance +from fastNLP.embeddings import LSTMCharEmbedding, CNNCharEmbedding, StackEmbedding + + +class TestCharEmbed(unittest.TestCase): + def test_case_1(self): + ds = DataSet([Instance(words=['hello', 'world']), Instance(words=['hello', 'Jack'])]) + vocab = Vocabulary().from_dataset(ds, field_name='words') + self.assertEqual(len(vocab), 5) + cnn_embed = CNNCharEmbedding(vocab, embed_size=60) + lstm_embed = LSTMCharEmbedding(vocab, embed_size=70) + embed = StackEmbedding([cnn_embed, lstm_embed]) + x = torch.LongTensor([[2, 1, 0], [4, 3, 4]]) + y = embed(x) + self.assertEqual(tuple(y.size()), (2, 3, 130)) + From bedb792b3c869c3b86b7714586d0bc6d2e74af63 Mon Sep 17 00:00:00 2001 From: xuyige Date: Fri, 12 Jul 2019 04:33:32 +0800 Subject: [PATCH 013/139] =?UTF-8?q?=E5=B0=86test=5Fembed=5Floader=E7=9A=84?= =?UTF-8?q?almost=20equal=E8=8C=83=E5=9B=B4=E7=A8=8D=E7=A8=8D=E8=B0=83?= =?UTF-8?q?=E5=A4=A7=E4=B8=80=E4=BA=9B=EF=BC=8C=E4=BB=A5=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E5=81=B6=E7=84=B6=E7=9A=84=E6=B5=8B=E8=AF=95=E4=B8=8D=E9=80=9A?= =?UTF-8?q?=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/io/test_embed_loader.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/io/test_embed_loader.py b/test/io/test_embed_loader.py index ff8ecfcf..dfbc5205 100644 --- a/test/io/test_embed_loader.py +++ b/test/io/test_embed_loader.py @@ -16,7 +16,7 @@ class TestEmbedLoader(unittest.TestCase): self.assertEqual(g_m.shape, (4, 50)) w_m = EmbedLoader.load_with_vocab(word2vec, vocab, normalize=True) self.assertEqual(w_m.shape, (4, 50)) - self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), 4) + self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), delta=1e-3) def test_load_without_vocab(self): words = ['the', 'of', 'in', 'a', 'to', 'and'] @@ -28,13 +28,13 @@ class TestEmbedLoader(unittest.TestCase): self.assertIn(word, vocab) w_m, vocab = EmbedLoader.load_without_vocab(word2vec, normalize=True) self.assertEqual(w_m.shape, (8, 50)) - self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), 8) + self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), delta=1e-4) for word in words: self.assertIn(word, vocab) # no unk w_m, vocab = EmbedLoader.load_without_vocab(word2vec, normalize=True, unknown=None) self.assertEqual(w_m.shape, (7, 50)) - self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), 7) + self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), delta=1e-4) for word in words: self.assertIn(word, vocab) From defcaae4bd404aeba16d22966281c54d98ff8995 Mon Sep 17 00:00:00 2001 From: xuyige Date: Fri, 12 Jul 2019 04:39:17 +0800 Subject: [PATCH 014/139] =?UTF-8?q?=E5=B0=86test=5Fembed=5Floader=E7=9A=84?= =?UTF-8?q?almost=20equal=E8=8C=83=E5=9B=B4=E7=A8=8D=E7=A8=8D=E8=B0=83?= =?UTF-8?q?=E5=A4=A7=E4=B8=80=E4=BA=9B=EF=BC=8C=E4=BB=A5=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E5=81=B6=E7=84=B6=E7=9A=84=E6=B5=8B=E8=AF=95=E4=B8=8D=E9=80=9A?= =?UTF-8?q?=E8=BF=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/io/test_embed_loader.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/io/test_embed_loader.py b/test/io/test_embed_loader.py index dfbc5205..bbfe8858 100644 --- a/test/io/test_embed_loader.py +++ b/test/io/test_embed_loader.py @@ -16,7 +16,7 @@ class TestEmbedLoader(unittest.TestCase): self.assertEqual(g_m.shape, (4, 50)) w_m = EmbedLoader.load_with_vocab(word2vec, vocab, normalize=True) self.assertEqual(w_m.shape, (4, 50)) - self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), delta=1e-3) + self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), 4, delta=1e-4) def test_load_without_vocab(self): words = ['the', 'of', 'in', 'a', 'to', 'and'] @@ -28,13 +28,13 @@ class TestEmbedLoader(unittest.TestCase): self.assertIn(word, vocab) w_m, vocab = EmbedLoader.load_without_vocab(word2vec, normalize=True) self.assertEqual(w_m.shape, (8, 50)) - self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), delta=1e-4) + self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), 8, delta=1e-4) for word in words: self.assertIn(word, vocab) # no unk w_m, vocab = EmbedLoader.load_without_vocab(word2vec, normalize=True, unknown=None) self.assertEqual(w_m.shape, (7, 50)) - self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), delta=1e-4) + self.assertAlmostEqual(np.linalg.norm(w_m, axis=1).sum(), 7, delta=1e-4) for word in words: self.assertIn(word, vocab) From ffbba0f895c413bf8c55dad196d2cd4d586f10ee Mon Sep 17 00:00:00 2001 From: lyhuang18 <42239874+lyhuang18@users.noreply.github.com> Date: Fri, 12 Jul 2019 06:47:04 +0800 Subject: [PATCH 015/139] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=BB=A5=E9=80=82=E9=85=8D=E6=96=B0embeddings=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reproduction/text_classification/model/awd_lstm.py | 4 ++-- reproduction/text_classification/model/lstm.py | 4 ++-- .../text_classification/model/lstm_self_attention.py | 6 +++--- reproduction/text_classification/train_awdlstm.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/reproduction/text_classification/model/awd_lstm.py b/reproduction/text_classification/model/awd_lstm.py index 0d8f711a..c9c8a153 100644 --- a/reproduction/text_classification/model/awd_lstm.py +++ b/reproduction/text_classification/model/awd_lstm.py @@ -2,7 +2,7 @@ import torch import torch.nn as nn from fastNLP.core.const import Const as C from .awdlstm_module import LSTM -from fastNLP.modules import encoder +from fastNLP.embeddings.utils import get_embeddings from fastNLP.modules.decoder.mlp import MLP @@ -14,7 +14,7 @@ class AWDLSTMSentiment(nn.Module): nfc=128, wdrop=0.5): super(AWDLSTMSentiment,self).__init__() - self.embed = encoder.Embedding(init_embed) + self.embed = get_embeddings(init_embed) self.lstm = LSTM(input_size=self.embed.embedding_dim, hidden_size=hidden_dim, num_layers=num_layers, bidirectional=True, wdrop=wdrop) self.mlp = MLP(size_layer=[hidden_dim* 2, nfc, num_classes]) diff --git a/reproduction/text_classification/model/lstm.py b/reproduction/text_classification/model/lstm.py index 388f3f1c..fd1089dd 100644 --- a/reproduction/text_classification/model/lstm.py +++ b/reproduction/text_classification/model/lstm.py @@ -2,7 +2,7 @@ import torch import torch.nn as nn from fastNLP.core.const import Const as C from fastNLP.modules.encoder.lstm import LSTM -from fastNLP.modules import encoder +from fastNLP.embeddings.utils import get_embeddings from fastNLP.modules.decoder.mlp import MLP @@ -13,7 +13,7 @@ class BiLSTMSentiment(nn.Module): num_layers=1, nfc=128): super(BiLSTMSentiment,self).__init__() - self.embed = encoder.Embedding(init_embed) + self.embed = get_embeddings(init_embed) self.lstm = LSTM(input_size=self.embed.embedding_dim, hidden_size=hidden_dim, num_layers=num_layers, bidirectional=True) self.mlp = MLP(size_layer=[hidden_dim* 2, nfc, num_classes]) diff --git a/reproduction/text_classification/model/lstm_self_attention.py b/reproduction/text_classification/model/lstm_self_attention.py index 239635fe..9a39049d 100644 --- a/reproduction/text_classification/model/lstm_self_attention.py +++ b/reproduction/text_classification/model/lstm_self_attention.py @@ -2,8 +2,8 @@ import torch import torch.nn as nn from fastNLP.core.const import Const as C from fastNLP.modules.encoder.lstm import LSTM -from fastNLP.modules import encoder -from fastNLP.modules.aggregator.attention import SelfAttention +from fastNLP.embeddings.utils import get_embeddings +from fastNLP.modules.encoder.attention import SelfAttention from fastNLP.modules.decoder.mlp import MLP @@ -16,7 +16,7 @@ class BiLSTM_SELF_ATTENTION(nn.Module): attention_hops=1, nfc=128): super(BiLSTM_SELF_ATTENTION,self).__init__() - self.embed = encoder.Embedding(init_embed) + self.embed = get_embeddings(init_embed) self.lstm = LSTM(input_size=self.embed.embedding_dim, hidden_size=hidden_dim, num_layers=num_layers, bidirectional=True) self.attention = SelfAttention(input_size=hidden_dim * 2 , attention_unit=attention_unit, attention_hops=attention_hops) self.mlp = MLP(size_layer=[hidden_dim* 2*attention_hops, nfc, num_classes]) diff --git a/reproduction/text_classification/train_awdlstm.py b/reproduction/text_classification/train_awdlstm.py index b0f2af49..b2a67fdb 100644 --- a/reproduction/text_classification/train_awdlstm.py +++ b/reproduction/text_classification/train_awdlstm.py @@ -5,7 +5,7 @@ import os os.environ['FASTNLP_BASE_URL'] = 'http://10.141.222.118:8888/file/download/' os.environ['FASTNLP_CACHE_DIR'] = '/remote-home/hyan01/fastnlp_caches' -from data.IMDBLoader import IMDBLoader +from fastNLP.io.data_loader import IMDBLoader from fastNLP.embeddings import StaticEmbedding from model.awd_lstm import AWDLSTMSentiment From 807a8f1fb34dffc91309840d3155837a8dc2aa10 Mon Sep 17 00:00:00 2001 From: yh Date: Fri, 12 Jul 2019 09:27:11 +0800 Subject: [PATCH 016/139] =?UTF-8?q?1.=20BucketSampler=E4=B8=8D=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E8=87=AA=E5=B7=B1=E4=BC=A0=E5=85=A5batch=5Fsize?= =?UTF-8?q?=E4=BA=86=EF=BC=8C=E7=94=B1Trainer=E8=87=AA=E5=8A=A8=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=202.=20sequence=20labeling=E4=B8=AD=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?BiLSTMCRF=203.=20embedding=E4=B8=AD=E4=BF=AE=E5=A4=8Dbug=204.?= =?UTF-8?q?=20=E5=B0=86=E4=B9=8B=E5=89=8D=E7=89=88=E6=9C=AC=E7=9A=84reprod?= =?UTF-8?q?uction=E7=A7=BB=E5=8A=A8=E5=88=B0legacy=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E5=A4=B9=205.=20sequence=20labeling=E4=B8=AD=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E4=B8=AD=E6=96=87NER=206.=20text=20classification=E4=B8=ADload?= =?UTF-8?q?er=E5=A2=9E=E5=BC=BA=EF=BC=8C=E4=BF=AE=E6=94=B9lstm=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=EF=BC=8C=E5=A2=9E=E5=8A=A0train=5Fbert.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastNLP/core/sampler.py | 17 ++- fastNLP/core/trainer.py | 2 + fastNLP/io/base_loader.py | 2 +- fastNLP/models/sequence_labeling.py | 61 +++++++++- fastNLP/modules/encoder/embedding.py | 2 +- fastNLP/modules/encoder/lstm.py | 4 - .../{ => legacy}/Biaffine_parser/cfg.cfg | 0 .../{ => legacy}/Biaffine_parser/infer.py | 0 .../{ => legacy}/Biaffine_parser/main.py | 0 .../{ => legacy}/Biaffine_parser/run.py | 0 .../{ => legacy}/Biaffine_parser/util.py | 0 .../Chinese_word_segmentation/__init__.py | 0 .../Chinese_word_segmentation/cws.cfg | 0 .../cws_io/__init__.py | 0 .../cws_io/cws_reader.py | 0 .../models/__init__.py | 0 .../models/cws_model.py | 2 +- .../models/cws_transformer.py | 4 +- .../process/__init__.py | 0 .../process/cws_processor.py | 2 +- .../process/span_converter.py | 0 .../Chinese_word_segmentation/utils.py | 0 .../README.md | 0 .../Word2Idx.py | 0 .../config.cfg | 0 .../dataloader.py | 0 .../example.py | 0 .../main.py | 0 .../predict.py | 0 .../prepare.py | 0 .../{ => legacy}/POS_tagging/pos_processor.py | 0 .../{ => legacy}/POS_tagging/pos_reader.py | 0 .../{ => legacy}/POS_tagging/pos_tag.cfg | 0 .../{ => legacy}/POS_tagging/train_pos_tag.py | 0 .../{ => legacy}/POS_tagging/utils.py | 0 .../chinese_ner/data/ChineseNER.py | 115 ++++++++++++++++++ .../chinese_ner/data/__init__.py | 0 .../chinese_ner/train_cn_ner.py | 94 ++++++++++++++ .../seqence_labelling/ner/train_ontonote.py | 76 +++++++----- .../text_classification/data/IMDBLoader.py | 2 +- .../text_classification/model/lstm.py | 8 +- .../text_classification/train_bert.py | 33 +++++ 42 files changed, 376 insertions(+), 48 deletions(-) rename reproduction/{ => legacy}/Biaffine_parser/cfg.cfg (100%) rename reproduction/{ => legacy}/Biaffine_parser/infer.py (100%) rename reproduction/{ => legacy}/Biaffine_parser/main.py (100%) rename reproduction/{ => legacy}/Biaffine_parser/run.py (100%) rename reproduction/{ => legacy}/Biaffine_parser/util.py (100%) rename reproduction/{ => legacy}/Chinese_word_segmentation/__init__.py (100%) rename reproduction/{ => legacy}/Chinese_word_segmentation/cws.cfg (100%) rename reproduction/{ => legacy}/Chinese_word_segmentation/cws_io/__init__.py (100%) rename reproduction/{ => legacy}/Chinese_word_segmentation/cws_io/cws_reader.py (100%) rename reproduction/{ => legacy}/Chinese_word_segmentation/models/__init__.py (100%) rename reproduction/{ => legacy}/Chinese_word_segmentation/models/cws_model.py (98%) rename reproduction/{ => legacy}/Chinese_word_segmentation/models/cws_transformer.py (97%) rename reproduction/{ => legacy}/Chinese_word_segmentation/process/__init__.py (100%) rename reproduction/{ => legacy}/Chinese_word_segmentation/process/cws_processor.py (99%) rename reproduction/{ => legacy}/Chinese_word_segmentation/process/span_converter.py (100%) rename reproduction/{ => legacy}/Chinese_word_segmentation/utils.py (100%) rename reproduction/{ => legacy}/LSTM+self_attention_sentiment_analysis/README.md (100%) rename reproduction/{ => legacy}/LSTM+self_attention_sentiment_analysis/Word2Idx.py (100%) rename reproduction/{ => legacy}/LSTM+self_attention_sentiment_analysis/config.cfg (100%) rename reproduction/{ => legacy}/LSTM+self_attention_sentiment_analysis/dataloader.py (100%) rename reproduction/{ => legacy}/LSTM+self_attention_sentiment_analysis/example.py (100%) rename reproduction/{ => legacy}/LSTM+self_attention_sentiment_analysis/main.py (100%) rename reproduction/{ => legacy}/LSTM+self_attention_sentiment_analysis/predict.py (100%) rename reproduction/{ => legacy}/LSTM+self_attention_sentiment_analysis/prepare.py (100%) rename reproduction/{ => legacy}/POS_tagging/pos_processor.py (100%) rename reproduction/{ => legacy}/POS_tagging/pos_reader.py (100%) rename reproduction/{ => legacy}/POS_tagging/pos_tag.cfg (100%) rename reproduction/{ => legacy}/POS_tagging/train_pos_tag.py (100%) rename reproduction/{ => legacy}/POS_tagging/utils.py (100%) create mode 100644 reproduction/seqence_labelling/chinese_ner/data/ChineseNER.py create mode 100644 reproduction/seqence_labelling/chinese_ner/data/__init__.py create mode 100644 reproduction/seqence_labelling/chinese_ner/train_cn_ner.py diff --git a/fastNLP/core/sampler.py b/fastNLP/core/sampler.py index c5784f59..d8ba1ad1 100644 --- a/fastNLP/core/sampler.py +++ b/fastNLP/core/sampler.py @@ -62,16 +62,27 @@ class BucketSampler(Sampler): 带Bucket的 `Random Sampler`. 可以随机地取出长度相似的元素 :param int num_buckets: bucket的数量 - :param int batch_size: batch的大小 + :param int batch_size: batch的大小. 默认为None,Trainer在调用BucketSampler时,会将该值正确设置,如果是非Trainer场景使用,需 + 要显示传递该值 :param str seq_len_field_name: 对应序列长度的 `field` 的名字 """ - def __init__(self, num_buckets=10, batch_size=32, seq_len_field_name='seq_len'): + def __init__(self, num_buckets=10, batch_size=None, seq_len_field_name='seq_len'): self.num_buckets = num_buckets self.batch_size = batch_size self.seq_len_field_name = seq_len_field_name - + + def set_batch_size(self, batch_size): + """ + + :param int batch_size: 每个batch的大小 + :return: + """ + self.batch_size = batch_size + def __call__(self, data_set): + if self.batch_size is None: + raise RuntimeError("batch_size is None.") seq_lens = data_set.get_all_fields()[self.seq_len_field_name].content total_sample_num = len(seq_lens) diff --git a/fastNLP/core/trainer.py b/fastNLP/core/trainer.py index eabda99c..8fa44438 100644 --- a/fastNLP/core/trainer.py +++ b/fastNLP/core/trainer.py @@ -443,6 +443,8 @@ class Trainer(object): if sampler is None: sampler = RandomSampler() + elif hasattr(sampler, 'set_batch_size'): + sampler.set_batch_size(batch_size) if isinstance(train_data, DataSet): self.data_iterator = DataSetIter( diff --git a/fastNLP/io/base_loader.py b/fastNLP/io/base_loader.py index 62793836..5d61c16a 100644 --- a/fastNLP/io/base_loader.py +++ b/fastNLP/io/base_loader.py @@ -111,7 +111,7 @@ def _uncompress(src, dst): class DataBundle: """ - 经过处理的数据信息,包括一系列数据集(比如:分开的训练集、验证集和测试集)及它们所用的词表和词嵌入。 + 经过处理的数据信息,包括一系列数据集(比如:分开的训练集、验证集和测试集)以及各个field对应的vocabulary。 :param vocabs: 从名称(字符串)到 :class:`~fastNLP.Vocabulary` 类型的dict :param datasets: 从名称(字符串)到 :class:`~fastNLP.DataSet` 类型的dict diff --git a/fastNLP/models/sequence_labeling.py b/fastNLP/models/sequence_labeling.py index 8e6a5db1..506ebdc6 100644 --- a/fastNLP/models/sequence_labeling.py +++ b/fastNLP/models/sequence_labeling.py @@ -3,17 +3,76 @@ """ __all__ = [ "SeqLabeling", - "AdvSeqLabel" + "AdvSeqLabel", + "BiLSTMCRF" ] import torch import torch.nn as nn +import torch.nn.functional as F from .base_model import BaseModel from ..modules import decoder, encoder from ..modules.decoder.crf import allowed_transitions from ..core.utils import seq_len_to_mask from ..core.const import Const as C +from ..modules import LSTM +from ..modules import get_embeddings +from ..modules import ConditionalRandomField + + +class BiLSTMCRF(BaseModel): + """ + 结构为BiLSTM + FC + Dropout + CRF. + TODO 补充文档 + :param embed: tuple: + :param num_classes: + :param num_layers: + :param hidden_size: + :param dropout: + :param target_vocab: + :param encoding_type: + """ + def __init__(self, embed, num_classes, num_layers=1, hidden_size=100, dropout=0.5, + target_vocab=None, encoding_type=None): + super().__init__() + self.embed = get_embeddings(embed) + + if num_layers>1: + self.lstm = LSTM(embed.embedding_dim, num_layers=num_layers, hidden_size=hidden_size, bidirectional=True, + batch_first=True, dropout=dropout) + else: + self.lstm = LSTM(embed.embedding_dim, num_layers=num_layers, hidden_size=hidden_size, bidirectional=True, + batch_first=True) + + self.dropout = nn.Dropout(dropout) + self.fc = nn.Linear(hidden_size, num_classes) + + trans = None + if target_vocab is not None and encoding_type is not None: + trans = allowed_transitions(target_vocab.idx2word, encoding_type=encoding_type, include_start_end=True) + + self.crf = ConditionalRandomField(num_classes, include_start_end_trans=True, allowed_transitions=trans) + + def _forward(self, words, seq_len=None, target=None): + words = self.embed(words) + feats = self.lstm(words, seq_len=seq_len) + feats = self.fc(feats) + feats = self.dropout(feats) + logits = F.log_softmax(feats, dim=-1) + mask = seq_len_to_mask(seq_len) + if target is None: + pred, _ = self.crf.viterbi_decode(logits, mask) + return {C.OUTPUT:pred} + else: + loss = self.crf(logits, target, mask).mean() + return {C.LOSS:loss} + + def forward(self, words, seq_len, target): + return self._forward(words, seq_len, target) + + def predict(self, words, seq_len): + return self._forward(words, seq_len) class SeqLabeling(BaseModel): diff --git a/fastNLP/modules/encoder/embedding.py b/fastNLP/modules/encoder/embedding.py index 050a423a..0639959b 100644 --- a/fastNLP/modules/encoder/embedding.py +++ b/fastNLP/modules/encoder/embedding.py @@ -689,7 +689,7 @@ class BertEmbedding(ContextualEmbedding): outputs = self.model(words) outputs = torch.cat([*outputs], dim=-1) - return self.dropout(words) + return self.dropout(outputs) @property def requires_grad(self): diff --git a/fastNLP/modules/encoder/lstm.py b/fastNLP/modules/encoder/lstm.py index 5e599a65..695f56b8 100644 --- a/fastNLP/modules/encoder/lstm.py +++ b/fastNLP/modules/encoder/lstm.py @@ -10,10 +10,6 @@ import torch import torch.nn as nn import torch.nn.utils.rnn as rnn -from ..utils import initial_parameter -from torch import autograd - - class LSTM(nn.Module): """ 别名::class:`fastNLP.modules.LSTM` :class:`fastNLP.modules.encoder.lstm.LSTM` diff --git a/reproduction/Biaffine_parser/cfg.cfg b/reproduction/legacy/Biaffine_parser/cfg.cfg similarity index 100% rename from reproduction/Biaffine_parser/cfg.cfg rename to reproduction/legacy/Biaffine_parser/cfg.cfg diff --git a/reproduction/Biaffine_parser/infer.py b/reproduction/legacy/Biaffine_parser/infer.py similarity index 100% rename from reproduction/Biaffine_parser/infer.py rename to reproduction/legacy/Biaffine_parser/infer.py diff --git a/reproduction/Biaffine_parser/main.py b/reproduction/legacy/Biaffine_parser/main.py similarity index 100% rename from reproduction/Biaffine_parser/main.py rename to reproduction/legacy/Biaffine_parser/main.py diff --git a/reproduction/Biaffine_parser/run.py b/reproduction/legacy/Biaffine_parser/run.py similarity index 100% rename from reproduction/Biaffine_parser/run.py rename to reproduction/legacy/Biaffine_parser/run.py diff --git a/reproduction/Biaffine_parser/util.py b/reproduction/legacy/Biaffine_parser/util.py similarity index 100% rename from reproduction/Biaffine_parser/util.py rename to reproduction/legacy/Biaffine_parser/util.py diff --git a/reproduction/Chinese_word_segmentation/__init__.py b/reproduction/legacy/Chinese_word_segmentation/__init__.py similarity index 100% rename from reproduction/Chinese_word_segmentation/__init__.py rename to reproduction/legacy/Chinese_word_segmentation/__init__.py diff --git a/reproduction/Chinese_word_segmentation/cws.cfg b/reproduction/legacy/Chinese_word_segmentation/cws.cfg similarity index 100% rename from reproduction/Chinese_word_segmentation/cws.cfg rename to reproduction/legacy/Chinese_word_segmentation/cws.cfg diff --git a/reproduction/Chinese_word_segmentation/cws_io/__init__.py b/reproduction/legacy/Chinese_word_segmentation/cws_io/__init__.py similarity index 100% rename from reproduction/Chinese_word_segmentation/cws_io/__init__.py rename to reproduction/legacy/Chinese_word_segmentation/cws_io/__init__.py diff --git a/reproduction/Chinese_word_segmentation/cws_io/cws_reader.py b/reproduction/legacy/Chinese_word_segmentation/cws_io/cws_reader.py similarity index 100% rename from reproduction/Chinese_word_segmentation/cws_io/cws_reader.py rename to reproduction/legacy/Chinese_word_segmentation/cws_io/cws_reader.py diff --git a/reproduction/Chinese_word_segmentation/models/__init__.py b/reproduction/legacy/Chinese_word_segmentation/models/__init__.py similarity index 100% rename from reproduction/Chinese_word_segmentation/models/__init__.py rename to reproduction/legacy/Chinese_word_segmentation/models/__init__.py diff --git a/reproduction/Chinese_word_segmentation/models/cws_model.py b/reproduction/legacy/Chinese_word_segmentation/models/cws_model.py similarity index 98% rename from reproduction/Chinese_word_segmentation/models/cws_model.py rename to reproduction/legacy/Chinese_word_segmentation/models/cws_model.py index b41ad87d..0d10d2e5 100644 --- a/reproduction/Chinese_word_segmentation/models/cws_model.py +++ b/reproduction/legacy/Chinese_word_segmentation/models/cws_model.py @@ -4,7 +4,7 @@ from torch import nn from fastNLP.models.base_model import BaseModel from fastNLP.modules.decoder.mlp import MLP -from reproduction.Chinese_word_segmentation.utils import seq_lens_to_mask +from reproduction.legacy.Chinese_word_segmentation.utils import seq_lens_to_mask class CWSBiLSTMEncoder(BaseModel): diff --git a/reproduction/Chinese_word_segmentation/models/cws_transformer.py b/reproduction/legacy/Chinese_word_segmentation/models/cws_transformer.py similarity index 97% rename from reproduction/Chinese_word_segmentation/models/cws_transformer.py rename to reproduction/legacy/Chinese_word_segmentation/models/cws_transformer.py index e8ae5ecc..ae8a5a7f 100644 --- a/reproduction/Chinese_word_segmentation/models/cws_transformer.py +++ b/reproduction/legacy/Chinese_word_segmentation/models/cws_transformer.py @@ -9,7 +9,7 @@ from torch import nn import torch # from fastNLP.modules.encoder.transformer import TransformerEncoder -from reproduction.Chinese_word_segmentation.models.transformer import TransformerEncoder +from reproduction.legacy.Chinese_word_segmentation.models import TransformerEncoder from fastNLP.modules.decoder.crf import ConditionalRandomField,seq_len_to_byte_mask from fastNLP.modules.decoder.crf import allowed_transitions @@ -79,7 +79,7 @@ class TransformerCWS(nn.Module): return {'pred': probs, 'seq_lens':seq_lens} -from reproduction.Chinese_word_segmentation.models.dilated_transformer import TransformerDilateEncoder +from reproduction.legacy.Chinese_word_segmentation.models import TransformerDilateEncoder class TransformerDilatedCWS(nn.Module): def __init__(self, vocab_num, embed_dim=100, bigram_vocab_num=None, bigram_embed_dim=100, num_bigram_per_char=None, diff --git a/reproduction/Chinese_word_segmentation/process/__init__.py b/reproduction/legacy/Chinese_word_segmentation/process/__init__.py similarity index 100% rename from reproduction/Chinese_word_segmentation/process/__init__.py rename to reproduction/legacy/Chinese_word_segmentation/process/__init__.py diff --git a/reproduction/Chinese_word_segmentation/process/cws_processor.py b/reproduction/legacy/Chinese_word_segmentation/process/cws_processor.py similarity index 99% rename from reproduction/Chinese_word_segmentation/process/cws_processor.py rename to reproduction/legacy/Chinese_word_segmentation/process/cws_processor.py index 614d9ef5..1f64bed2 100644 --- a/reproduction/Chinese_word_segmentation/process/cws_processor.py +++ b/reproduction/legacy/Chinese_word_segmentation/process/cws_processor.py @@ -4,7 +4,7 @@ import re from fastNLP.api.processor import Processor from fastNLP.core.dataset import DataSet from fastNLP.core.vocabulary import Vocabulary -from reproduction.Chinese_word_segmentation.process.span_converter import SpanConverter +from reproduction.legacy.Chinese_word_segmentation.process.span_converter import SpanConverter _SPECIAL_TAG_PATTERN = '<[a-zA-Z]+>' diff --git a/reproduction/Chinese_word_segmentation/process/span_converter.py b/reproduction/legacy/Chinese_word_segmentation/process/span_converter.py similarity index 100% rename from reproduction/Chinese_word_segmentation/process/span_converter.py rename to reproduction/legacy/Chinese_word_segmentation/process/span_converter.py diff --git a/reproduction/Chinese_word_segmentation/utils.py b/reproduction/legacy/Chinese_word_segmentation/utils.py similarity index 100% rename from reproduction/Chinese_word_segmentation/utils.py rename to reproduction/legacy/Chinese_word_segmentation/utils.py diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/README.md b/reproduction/legacy/LSTM+self_attention_sentiment_analysis/README.md similarity index 100% rename from reproduction/LSTM+self_attention_sentiment_analysis/README.md rename to reproduction/legacy/LSTM+self_attention_sentiment_analysis/README.md diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/Word2Idx.py b/reproduction/legacy/LSTM+self_attention_sentiment_analysis/Word2Idx.py similarity index 100% rename from reproduction/LSTM+self_attention_sentiment_analysis/Word2Idx.py rename to reproduction/legacy/LSTM+self_attention_sentiment_analysis/Word2Idx.py diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/config.cfg b/reproduction/legacy/LSTM+self_attention_sentiment_analysis/config.cfg similarity index 100% rename from reproduction/LSTM+self_attention_sentiment_analysis/config.cfg rename to reproduction/legacy/LSTM+self_attention_sentiment_analysis/config.cfg diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/dataloader.py b/reproduction/legacy/LSTM+self_attention_sentiment_analysis/dataloader.py similarity index 100% rename from reproduction/LSTM+self_attention_sentiment_analysis/dataloader.py rename to reproduction/legacy/LSTM+self_attention_sentiment_analysis/dataloader.py diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/example.py b/reproduction/legacy/LSTM+self_attention_sentiment_analysis/example.py similarity index 100% rename from reproduction/LSTM+self_attention_sentiment_analysis/example.py rename to reproduction/legacy/LSTM+self_attention_sentiment_analysis/example.py diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/main.py b/reproduction/legacy/LSTM+self_attention_sentiment_analysis/main.py similarity index 100% rename from reproduction/LSTM+self_attention_sentiment_analysis/main.py rename to reproduction/legacy/LSTM+self_attention_sentiment_analysis/main.py diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/predict.py b/reproduction/legacy/LSTM+self_attention_sentiment_analysis/predict.py similarity index 100% rename from reproduction/LSTM+self_attention_sentiment_analysis/predict.py rename to reproduction/legacy/LSTM+self_attention_sentiment_analysis/predict.py diff --git a/reproduction/LSTM+self_attention_sentiment_analysis/prepare.py b/reproduction/legacy/LSTM+self_attention_sentiment_analysis/prepare.py similarity index 100% rename from reproduction/LSTM+self_attention_sentiment_analysis/prepare.py rename to reproduction/legacy/LSTM+self_attention_sentiment_analysis/prepare.py diff --git a/reproduction/POS_tagging/pos_processor.py b/reproduction/legacy/POS_tagging/pos_processor.py similarity index 100% rename from reproduction/POS_tagging/pos_processor.py rename to reproduction/legacy/POS_tagging/pos_processor.py diff --git a/reproduction/POS_tagging/pos_reader.py b/reproduction/legacy/POS_tagging/pos_reader.py similarity index 100% rename from reproduction/POS_tagging/pos_reader.py rename to reproduction/legacy/POS_tagging/pos_reader.py diff --git a/reproduction/POS_tagging/pos_tag.cfg b/reproduction/legacy/POS_tagging/pos_tag.cfg similarity index 100% rename from reproduction/POS_tagging/pos_tag.cfg rename to reproduction/legacy/POS_tagging/pos_tag.cfg diff --git a/reproduction/POS_tagging/train_pos_tag.py b/reproduction/legacy/POS_tagging/train_pos_tag.py similarity index 100% rename from reproduction/POS_tagging/train_pos_tag.py rename to reproduction/legacy/POS_tagging/train_pos_tag.py diff --git a/reproduction/POS_tagging/utils.py b/reproduction/legacy/POS_tagging/utils.py similarity index 100% rename from reproduction/POS_tagging/utils.py rename to reproduction/legacy/POS_tagging/utils.py diff --git a/reproduction/seqence_labelling/chinese_ner/data/ChineseNER.py b/reproduction/seqence_labelling/chinese_ner/data/ChineseNER.py new file mode 100644 index 00000000..cec5ab76 --- /dev/null +++ b/reproduction/seqence_labelling/chinese_ner/data/ChineseNER.py @@ -0,0 +1,115 @@ + + +from fastNLP.io.base_loader import DataSetLoader, DataBundle +from fastNLP.io import ConllLoader +from reproduction.seqence_labelling.ner.data.utils import iob2bioes, iob2 +from fastNLP import Const +from reproduction.utils import check_dataloader_paths +from fastNLP import Vocabulary + +class ChineseNERLoader(DataSetLoader): + """ + 读取中文命名实体数据集,包括PeopleDaily, MSRA-NER, Weibo。数据在这里可以找到https://github.com/OYE93/Chinese-NLP-Corpus/tree/master/NER + 请确保输入数据的格式如下, 共两列,第一列为字,第二列为标签,不同句子以空行隔开 + 我 O + 们 O + 变 O + 而 O + 以 O + 书 O + 会 O + ... + + """ + def __init__(self, encoding_type:str='bioes'): + """ + + :param str encoding_type: 支持bio和bioes格式 + """ + super().__init__() + self._loader = ConllLoader(headers=['raw_chars', 'target'], indexes=[0, 1]) + + assert encoding_type in ('bio', 'bioes') + + self._tag_converters = [iob2] + if encoding_type == 'bioes': + self._tag_converters.append(iob2bioes) + + def load(self, path:str): + dataset = self._loader.load(path) + def convert_tag_schema(tags): + for converter in self._tag_converters: + tags = converter(tags) + return tags + if self._tag_converters: + dataset.apply_field(convert_tag_schema, field_name=Const.TARGET, new_field_name=Const.TARGET) + return dataset + + def process(self, paths, bigrams=False, trigrams=False): + """ + + :param paths: + :param bool, bigrams: 是否包含生成bigram feature, [a, b, c, d] -> [ab, bc, cd, d] + :param bool, trigrams: 是否包含trigram feature,[a, b, c, d] -> [abc, bcd, cd, d] + :return: DataBundle + 包含以下的fields + raw_chars: List[str] + chars: List[int] + seq_len: int, 字的长度 + bigrams: List[int], optional + trigrams: List[int], optional + target: List[int] + """ + paths = check_dataloader_paths(paths) + data = DataBundle() + input_fields = [Const.CHAR_INPUT, Const.INPUT_LEN, Const.TARGET] + target_fields = [Const.TARGET, Const.INPUT_LEN] + + for name, path in paths.items(): + dataset = self.load(path) + if bigrams: + dataset.apply_field(lambda raw_chars: [c1+c2 for c1, c2 in zip(raw_chars, raw_chars[1:]+[''])], + field_name='raw_chars', new_field_name='bigrams') + + if trigrams: + dataset.apply_field(lambda raw_chars: [c1+c2+c3 for c1, c2, c3 in zip(raw_chars, + raw_chars[1:]+[''], + raw_chars[2:]+['']*2)], + field_name='raw_chars', new_field_name='trigrams') + data.datasets[name] = dataset + + char_vocab = Vocabulary().from_dataset(data.datasets['train'], field_name='raw_chars', + no_create_entry_dataset=[dataset for name, dataset in data.datasets.items() if name!='train']) + char_vocab.index_dataset(*data.datasets.values(), field_name='raw_chars', new_field_name=Const.CHAR_INPUT) + data.vocabs[Const.CHAR_INPUT] = char_vocab + + target_vocab = Vocabulary(unknown=None, padding=None).from_dataset(data.datasets['train'], field_name=Const.TARGET) + target_vocab.index_dataset(*data.datasets.values(), field_name=Const.TARGET) + data.vocabs[Const.TARGET] = target_vocab + + if bigrams: + bigram_vocab = Vocabulary().from_dataset(data.datasets['train'], field_name='bigrams', + no_create_entry_dataset=[dataset for name, dataset in + data.datasets.items() if name != 'train']) + bigram_vocab.index_dataset(*data.datasets.values(), field_name='bigrams', new_field_name='bigrams') + data.vocabs['bigrams'] = bigram_vocab + input_fields.append('bigrams') + + if trigrams: + trigram_vocab = Vocabulary().from_dataset(data.datasets['train'], field_name='trigrams', + no_create_entry_dataset=[dataset for name, dataset in + data.datasets.items() if name != 'train']) + trigram_vocab.index_dataset(*data.datasets.values(), field_name='trigrams', new_field_name='trigrams') + data.vocabs['trigrams'] = trigram_vocab + input_fields.append('trigrams') + + for name, dataset in data.datasets.items(): + dataset.add_seq_len(Const.CHAR_INPUT) + dataset.set_input(*input_fields) + dataset.set_target(*target_fields) + + return data + + + + diff --git a/reproduction/seqence_labelling/chinese_ner/data/__init__.py b/reproduction/seqence_labelling/chinese_ner/data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/reproduction/seqence_labelling/chinese_ner/train_cn_ner.py b/reproduction/seqence_labelling/chinese_ner/train_cn_ner.py new file mode 100644 index 00000000..1993898b --- /dev/null +++ b/reproduction/seqence_labelling/chinese_ner/train_cn_ner.py @@ -0,0 +1,94 @@ + + + +from reproduction.seqence_labelling.chinese_ner.data.ChineseNER import ChineseNERLoader +from fastNLP.modules.encoder.embedding import StaticEmbedding + +from torch import nn +import torch +from fastNLP.modules import get_embeddings +from fastNLP.modules import LSTM +from fastNLP.modules import ConditionalRandomField +from fastNLP.modules import allowed_transitions +import torch.nn.functional as F +from fastNLP import seq_len_to_mask +from fastNLP.core.const import Const as C +from fastNLP import SpanFPreRecMetric, Trainer +from fastNLP import cache_results + +class CNBiLSTMCRFNER(nn.Module): + def __init__(self, char_embed, num_classes, bigram_embed=None, trigram_embed=None, num_layers=1, hidden_size=100, + dropout=0.5, target_vocab=None, encoding_type=None): + super().__init__() + + self.char_embed = get_embeddings(char_embed) + embed_size = self.char_embed.embedding_dim + if bigram_embed: + self.bigram_embed = get_embeddings(bigram_embed) + embed_size += self.bigram_embed.embedding_dim + if trigram_embed: + self.trigram_ebmbed = get_embeddings(trigram_embed) + embed_size += self.bigram_embed.embedding_dim + + if num_layers>1: + self.lstm = LSTM(embed_size, num_layers=num_layers, hidden_size=hidden_size//2, bidirectional=True, + batch_first=True, dropout=dropout) + else: + self.lstm = LSTM(embed_size, num_layers=num_layers, hidden_size=hidden_size//2, bidirectional=True, + batch_first=True) + + self.dropout = nn.Dropout(dropout) + self.fc = nn.Linear(hidden_size, num_classes) + + trans = None + if target_vocab is not None and encoding_type is not None: + trans = allowed_transitions(target_vocab.idx2word, encoding_type=encoding_type, include_start_end=True) + + self.crf = ConditionalRandomField(num_classes, include_start_end_trans=True, allowed_transitions=trans) + + def _forward(self, chars, bigrams=None, trigrams=None, seq_len=None, target=None): + chars = self.char_embed(chars) + if hasattr(self, 'bigram_embed'): + bigrams = self.bigram_embed(bigrams) + chars = torch.cat((chars, bigrams), dim=-1) + if hasattr(self, 'trigram_embed'): + trigrams = self.trigram_embed(trigrams) + chars = torch.cat((chars, trigrams), dim=-1) + feats, _ = self.lstm(chars, seq_len=seq_len) + feats = self.fc(feats) + feats = self.dropout(feats) + logits = F.log_softmax(feats, dim=-1) + mask = seq_len_to_mask(seq_len) + if target is None: + pred, _ = self.crf.viterbi_decode(logits, mask) + return {C.OUTPUT: pred} + else: + loss = self.crf(logits, target, mask).mean() + return {C.LOSS:loss} + + def forward(self, chars, target, bigrams=None, trigrams=None, seq_len=None): + return self._forward(chars, bigrams, trigrams, seq_len, target) + + def predict(self, chars, seq_len=None, bigrams=None, trigrams=None): + return self._forward(chars, bigrams, trigrams, seq_len) + +# data_bundle = pickle.load(open('caches/msra.pkl', 'rb')) +@cache_results('caches/msra.pkl', _refresh=False) +def get_data(): + data_bundle = ChineseNERLoader().process('/remote-home/hyan01/exps/fastNLP/others/data/MSRA-NER', bigrams=True) + char_embed = StaticEmbedding(data_bundle.vocabs['chars'], + model_dir_or_name='/remote-home/hyan01/exps/CWS/pretrain/vectors/1grams_t3_m50_corpus.txt') + bigram_embed = StaticEmbedding(data_bundle.vocabs['bigrams'], + model_dir_or_name='/remote-home/hyan01/exps/CWS/pretrain/vectors/2gram_t3_m50_merge.txt') + return data_bundle, char_embed, bigram_embed +data_bundle, char_embed, bigram_embed = get_data() +print(data_bundle) +# exit(0) +data_bundle.datasets['train'].set_input('target') +data_bundle.datasets['dev'].set_input('target') +model = CNBiLSTMCRFNER(char_embed, num_classes=len(data_bundle.vocabs['target']), bigram_embed=bigram_embed) + +Trainer(data_bundle.datasets['train'], model, batch_size=640, + metrics=SpanFPreRecMetric(data_bundle.vocabs['target'], encoding_type='bioes'), + num_workers=2, dev_data=data_bundle. datasets['dev'], device=3).train() + diff --git a/reproduction/seqence_labelling/ner/train_ontonote.py b/reproduction/seqence_labelling/ner/train_ontonote.py index 6548cb9f..33f015d8 100644 --- a/reproduction/seqence_labelling/ner/train_ontonote.py +++ b/reproduction/seqence_labelling/ner/train_ontonote.py @@ -12,54 +12,72 @@ from fastNLP import Const from torch.optim import SGD, Adam from torch.optim.lr_scheduler import LambdaLR from fastNLP import GradientClipCallback +from fastNLP.core.vocabulary import VocabularyOption from fastNLP.core.callback import FitlogCallback, LRScheduler -from reproduction.seqence_labelling.ner.model.swats import SWATS +from functools import partial +from torch import nn +from fastNLP import cache_results import fitlog fitlog.debug() +fitlog.set_log_dir('logs/') + +fitlog.add_hyper_in_file(__file__) +#######hyper +normalize = False +divide_std = True +lower = False +lr = 0.015 +dropout = 0.5 +batch_size = 20 +init_method = 'default' +job_embed = False +data_name = 'ontonote' +#######hyper + + +init_method = {'default': None, + 'xavier': partial(nn.init.xavier_normal_, gain=0.02), + 'normal': partial(nn.init.normal_, std=0.02) + }[init_method] + from reproduction.seqence_labelling.ner.data.OntoNoteLoader import OntoNoteNERDataLoader encoding_type = 'bioes' -data = OntoNoteNERDataLoader(encoding_type=encoding_type).process('/hdd/fudanNLP/fastNLP/others/data/v4/english', - lower=True) - -import joblib -raw_data = joblib.load('/hdd/fudanNLP/fastNLP/others/NER-with-LS/data/ontonotes_with_data.joblib') -def convert_to_ids(raw_words): - ids = [] - for word in raw_words: - id = raw_data['word_to_id'][word] - id = raw_data['id_to_emb_map'][id] - ids.append(id) - return ids -word_embed = raw_data['emb_matrix'] -for name, dataset in data.datasets.items(): - dataset.apply_field(convert_to_ids, field_name='raw_words', new_field_name=Const.INPUT) +@cache_results('caches/ontonotes.pkl') +def cache(): + data = OntoNoteNERDataLoader(encoding_type=encoding_type).process('../../../../others/data/v4/english', + lower=lower, + word_vocab_opt=VocabularyOption(min_freq=1)) + char_embed = CNNCharEmbedding(vocab=data.vocabs['cap_words'], embed_size=30, char_emb_size=30, filter_nums=[30], + kernel_sizes=[3]) + word_embed = StaticEmbedding(vocab=data.vocabs[Const.INPUT], + model_dir_or_name='/remote-home/hyan01/fastnlp_caches/glove.6B.100d/glove.6B.100d.txt', + requires_grad=True, + normalize=normalize, + init_method=init_method) + return data, char_embed, word_embed +data, char_embed, word_embed = cache() print(data) -char_embed = CNNCharEmbedding(vocab=data.vocabs['cap_words'], embed_size=30, char_emb_size=30, filter_nums=[30], - kernel_sizes=[3]) -# word_embed = StaticEmbedding(vocab=data.vocabs[Const.INPUT], -# model_dir_or_name='/hdd/fudanNLP/pretrain_vectors/glove.6B.100d.txt', -# requires_grad=True) model = CNNBiLSTMCRF(word_embed, char_embed, hidden_size=1200, num_layers=1, tag_vocab=data.vocabs[Const.TARGET], - encoding_type=encoding_type) + encoding_type=encoding_type, dropout=dropout) -callbacks = [GradientClipCallback(clip_value=5, clip_type='value'), - FitlogCallback(data.datasets['test'], verbose=1)] +callbacks = [ + GradientClipCallback(clip_value=5, clip_type='value'), + FitlogCallback(data.datasets['test'], verbose=1) + ] -optimizer = SGD(model.parameters(), lr=0.01, momentum=0.9) +optimizer = SGD(model.parameters(), lr=lr, momentum=0.9) scheduler = LRScheduler(LambdaLR(optimizer, lr_lambda=lambda epoch: 1 / (1 + 0.05 * epoch))) callbacks.append(scheduler) -# optimizer = SWATS(model.parameters(), verbose=True) -# optimizer = Adam(model.parameters(), lr=0.005) -trainer = Trainer(train_data=data.datasets['train'], model=model, optimizer=optimizer, sampler=BucketSampler(num_buckets=100), - device=0, dev_data=data.datasets['dev'], batch_size=10, +trainer = Trainer(train_data=data.datasets['dev'][:100], model=model, optimizer=optimizer, sampler=None, + device=0, dev_data=data.datasets['dev'][:100], batch_size=batch_size, metrics=SpanFPreRecMetric(tag_vocab=data.vocabs[Const.TARGET], encoding_type=encoding_type), callbacks=callbacks, num_workers=1, n_epochs=100) trainer.train() \ No newline at end of file diff --git a/reproduction/text_classification/data/IMDBLoader.py b/reproduction/text_classification/data/IMDBLoader.py index d57ee41b..94244431 100644 --- a/reproduction/text_classification/data/IMDBLoader.py +++ b/reproduction/text_classification/data/IMDBLoader.py @@ -10,7 +10,6 @@ from fastNLP import Const from functools import partial from reproduction.utils import check_dataloader_paths, get_tokenizer - class IMDBLoader(DataSetLoader): """ 读取IMDB数据集,DataSet包含以下fields: @@ -51,6 +50,7 @@ class IMDBLoader(DataSetLoader): datasets = {} info = DataBundle() + paths = check_dataloader_paths(paths) for name, path in paths.items(): dataset = self.load(path) datasets[name] = dataset diff --git a/reproduction/text_classification/model/lstm.py b/reproduction/text_classification/model/lstm.py index 388f3f1c..93b4d8a9 100644 --- a/reproduction/text_classification/model/lstm.py +++ b/reproduction/text_classification/model/lstm.py @@ -2,7 +2,7 @@ import torch import torch.nn as nn from fastNLP.core.const import Const as C from fastNLP.modules.encoder.lstm import LSTM -from fastNLP.modules import encoder +from fastNLP.modules import get_embeddings from fastNLP.modules.decoder.mlp import MLP @@ -13,14 +13,14 @@ class BiLSTMSentiment(nn.Module): num_layers=1, nfc=128): super(BiLSTMSentiment,self).__init__() - self.embed = encoder.Embedding(init_embed) + self.embed = get_embeddings(init_embed) self.lstm = LSTM(input_size=self.embed.embedding_dim, hidden_size=hidden_dim, num_layers=num_layers, bidirectional=True) - self.mlp = MLP(size_layer=[hidden_dim* 2, nfc, num_classes]) + self.mlp = MLP(size_layer=[hidden_dim*2, nfc, num_classes]) def forward(self, words): x_emb = self.embed(words) output, _ = self.lstm(x_emb) - output = self.mlp(output[:,-1,:]) + output = self.mlp(torch.max(output, dim=1)[0]) return {C.OUTPUT: output} def predict(self, words): diff --git a/reproduction/text_classification/train_bert.py b/reproduction/text_classification/train_bert.py index e69de29b..4db54958 100644 --- a/reproduction/text_classification/train_bert.py +++ b/reproduction/text_classification/train_bert.py @@ -0,0 +1,33 @@ +import sys +sys.path.append('../../') + +from reproduction.text_classification.data.IMDBLoader import IMDBLoader +from fastNLP.modules.encoder.embedding import BertEmbedding +from reproduction.text_classification.model.lstm import BiLSTMSentiment +from fastNLP import Trainer +from fastNLP import CrossEntropyLoss, AccuracyMetric +from fastNLP import cache_results +from fastNLP import Tester + +# 对返回结果进行缓存,下一次运行就会自动跳过预处理 +@cache_results('imdb.pkl') +def get_data(): + data_bundle = IMDBLoader().process('imdb/') + return data_bundle +data_bundle = get_data() + +print(data_bundle) + +# 删除超过512, 但由于英语中会把word进行word piece处理,所以截取的时候做一点的裕量 +data_bundle.datasets['train'].drop(lambda x:len(x['words'])>400) +data_bundle.datasets['dev'].drop(lambda x:len(x['words'])>400) +data_bundle.datasets['test'].drop(lambda x:len(x['words'])>400) +bert_embed = BertEmbedding(data_bundle.vocabs['words'], requires_grad=False, + model_dir_or_name="en-base") +model = BiLSTMSentiment(bert_embed, len(data_bundle.vocabs['target'])) + +Trainer(data_bundle.datasets['train'], model, optimizer=None, loss=CrossEntropyLoss(), device=0, + batch_size=10, dev_data=data_bundle.datasets['dev'], metrics=AccuracyMetric()).train() + +# 在测试集上测试一下效果 +Tester(data_bundle.datasets['test'], model, batch_size=32, metrics=AccuracyMetric()).test() \ No newline at end of file From 391793a9614b373fc8ee1659e256adc70b824a86 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Fri, 12 Jul 2019 10:36:46 +0800 Subject: [PATCH 017/139] =?UTF-8?q?=E6=9C=80=E6=96=B0=E7=9A=84docs?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/fastNLP.core.batch.rst | 6 ++--- docs/source/fastNLP.core.callback.rst | 6 ++--- docs/source/fastNLP.core.const.rst | 6 ++--- docs/source/fastNLP.core.dataset.rst | 6 ++--- docs/source/fastNLP.core.field.rst | 6 ++--- docs/source/fastNLP.core.instance.rst | 6 ++--- docs/source/fastNLP.core.losses.rst | 6 ++--- docs/source/fastNLP.core.metrics.rst | 6 ++--- docs/source/fastNLP.core.optimizer.rst | 6 ++--- docs/source/fastNLP.core.rst | 9 ++++---- docs/source/fastNLP.core.sampler.rst | 6 ++--- docs/source/fastNLP.core.tester.rst | 6 ++--- docs/source/fastNLP.core.trainer.rst | 6 ++--- docs/source/fastNLP.core.utils.rst | 6 ++--- docs/source/fastNLP.core.vocabulary.rst | 6 ++--- .../fastNLP.embeddings.bert_embedding.rst | 7 ++++++ .../fastNLP.embeddings.char_embedding.rst | 7 ++++++ ...astNLP.embeddings.contextual_embedding.rst | 7 ++++++ .../fastNLP.embeddings.elmo_embedding.rst | 7 ++++++ docs/source/fastNLP.embeddings.embedding.rst | 7 ++++++ docs/source/fastNLP.embeddings.rst | 21 ++++++++++++++++++ .../fastNLP.embeddings.stack_embedding.rst | 7 ++++++ .../fastNLP.embeddings.static_embedding.rst | 7 ++++++ docs/source/fastNLP.embeddings.utils.rst | 7 ++++++ docs/source/fastNLP.io.base_loader.rst | 6 ++--- docs/source/fastNLP.io.config_io.rst | 7 ++++++ docs/source/fastNLP.io.data_loader.rst | 6 ++--- docs/source/fastNLP.io.dataset_loader.rst | 6 ++--- docs/source/fastNLP.io.embed_loader.rst | 6 ++--- docs/source/fastNLP.io.file_utils.rst | 7 ++++++ docs/source/fastNLP.io.model_io.rst | 6 ++--- docs/source/fastNLP.io.rst | 17 +++++++++----- docs/source/fastNLP.io.utils.rst | 7 ++++++ docs/source/fastNLP.models.base_model.rst | 7 ++++++ docs/source/fastNLP.models.bert.rst | 7 ++++++ .../source/fastNLP.models.biaffine_parser.rst | 6 ++--- ...fastNLP.models.cnn_text_classification.rst | 6 ++--- .../source/fastNLP.models.enas_controller.rst | 7 ++++++ docs/source/fastNLP.models.enas_model.rst | 7 ++++++ docs/source/fastNLP.models.enas_trainer.rst | 7 ++++++ docs/source/fastNLP.models.enas_utils.rst | 7 ++++++ docs/source/fastNLP.models.rst | 14 +++++++----- .../fastNLP.models.sequence_labeling.rst | 6 ++--- docs/source/fastNLP.models.snli.rst | 6 ++--- .../fastNLP.models.star_transformer.rst | 6 ++--- docs/source/fastNLP.modules.decoder.crf.rst | 7 ------ docs/source/fastNLP.modules.decoder.mlp.rst | 7 ------ docs/source/fastNLP.modules.decoder.rst | 16 +++----------- docs/source/fastNLP.modules.decoder.utils.rst | 7 ------ docs/source/fastNLP.modules.encoder.bert.rst | 7 ------ .../fastNLP.modules.encoder.char_encoder.rst | 7 ------ .../fastNLP.modules.encoder.conv_maxpool.rst | 7 ------ .../fastNLP.modules.encoder.embedding.rst | 7 ------ docs/source/fastNLP.modules.encoder.lstm.rst | 7 ------ docs/source/fastNLP.modules.encoder.rst | 22 +++---------------- ...stNLP.modules.encoder.star_transformer.rst | 7 ------ .../fastNLP.modules.encoder.transformer.rst | 7 ------ ...astNLP.modules.encoder.variational_rnn.rst | 7 ------ docs/source/fastNLP.modules.rst | 6 ++--- docs/source/fastNLP.rst | 18 +++++++-------- 60 files changed, 254 insertions(+), 209 deletions(-) create mode 100644 docs/source/fastNLP.embeddings.bert_embedding.rst create mode 100644 docs/source/fastNLP.embeddings.char_embedding.rst create mode 100644 docs/source/fastNLP.embeddings.contextual_embedding.rst create mode 100644 docs/source/fastNLP.embeddings.elmo_embedding.rst create mode 100644 docs/source/fastNLP.embeddings.embedding.rst create mode 100644 docs/source/fastNLP.embeddings.rst create mode 100644 docs/source/fastNLP.embeddings.stack_embedding.rst create mode 100644 docs/source/fastNLP.embeddings.static_embedding.rst create mode 100644 docs/source/fastNLP.embeddings.utils.rst create mode 100644 docs/source/fastNLP.io.config_io.rst create mode 100644 docs/source/fastNLP.io.file_utils.rst create mode 100644 docs/source/fastNLP.io.utils.rst create mode 100644 docs/source/fastNLP.models.base_model.rst create mode 100644 docs/source/fastNLP.models.bert.rst create mode 100644 docs/source/fastNLP.models.enas_controller.rst create mode 100644 docs/source/fastNLP.models.enas_model.rst create mode 100644 docs/source/fastNLP.models.enas_trainer.rst create mode 100644 docs/source/fastNLP.models.enas_utils.rst delete mode 100644 docs/source/fastNLP.modules.decoder.crf.rst delete mode 100644 docs/source/fastNLP.modules.decoder.mlp.rst delete mode 100644 docs/source/fastNLP.modules.decoder.utils.rst delete mode 100644 docs/source/fastNLP.modules.encoder.bert.rst delete mode 100644 docs/source/fastNLP.modules.encoder.char_encoder.rst delete mode 100644 docs/source/fastNLP.modules.encoder.conv_maxpool.rst delete mode 100644 docs/source/fastNLP.modules.encoder.embedding.rst delete mode 100644 docs/source/fastNLP.modules.encoder.lstm.rst delete mode 100644 docs/source/fastNLP.modules.encoder.star_transformer.rst delete mode 100644 docs/source/fastNLP.modules.encoder.transformer.rst delete mode 100644 docs/source/fastNLP.modules.encoder.variational_rnn.rst diff --git a/docs/source/fastNLP.core.batch.rst b/docs/source/fastNLP.core.batch.rst index 33a5b730..03008b52 100644 --- a/docs/source/fastNLP.core.batch.rst +++ b/docs/source/fastNLP.core.batch.rst @@ -2,6 +2,6 @@ fastNLP.core.batch ================== .. automodule:: fastNLP.core.batch - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.callback.rst b/docs/source/fastNLP.core.callback.rst index 31ec627b..74a7825d 100644 --- a/docs/source/fastNLP.core.callback.rst +++ b/docs/source/fastNLP.core.callback.rst @@ -2,6 +2,6 @@ fastNLP.core.callback ===================== .. automodule:: fastNLP.core.callback - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.const.rst b/docs/source/fastNLP.core.const.rst index c9e3bd97..330a8883 100644 --- a/docs/source/fastNLP.core.const.rst +++ b/docs/source/fastNLP.core.const.rst @@ -2,6 +2,6 @@ fastNLP.core.const ================== .. automodule:: fastNLP.core.const - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.dataset.rst b/docs/source/fastNLP.core.dataset.rst index b377cb0f..1ad94bb6 100644 --- a/docs/source/fastNLP.core.dataset.rst +++ b/docs/source/fastNLP.core.dataset.rst @@ -2,6 +2,6 @@ fastNLP.core.dataset ==================== .. automodule:: fastNLP.core.dataset - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.field.rst b/docs/source/fastNLP.core.field.rst index 7686e79a..7fc099c9 100644 --- a/docs/source/fastNLP.core.field.rst +++ b/docs/source/fastNLP.core.field.rst @@ -2,6 +2,6 @@ fastNLP.core.field ================== .. automodule:: fastNLP.core.field - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.instance.rst b/docs/source/fastNLP.core.instance.rst index 14393a91..6e496ac1 100644 --- a/docs/source/fastNLP.core.instance.rst +++ b/docs/source/fastNLP.core.instance.rst @@ -2,6 +2,6 @@ fastNLP.core.instance ===================== .. automodule:: fastNLP.core.instance - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.losses.rst b/docs/source/fastNLP.core.losses.rst index d2dd492b..8e63dfa1 100644 --- a/docs/source/fastNLP.core.losses.rst +++ b/docs/source/fastNLP.core.losses.rst @@ -2,6 +2,6 @@ fastNLP.core.losses =================== .. automodule:: fastNLP.core.losses - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.metrics.rst b/docs/source/fastNLP.core.metrics.rst index 69afff36..d3b87bb8 100644 --- a/docs/source/fastNLP.core.metrics.rst +++ b/docs/source/fastNLP.core.metrics.rst @@ -2,6 +2,6 @@ fastNLP.core.metrics ==================== .. automodule:: fastNLP.core.metrics - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.optimizer.rst b/docs/source/fastNLP.core.optimizer.rst index e2100d2e..c80be53f 100644 --- a/docs/source/fastNLP.core.optimizer.rst +++ b/docs/source/fastNLP.core.optimizer.rst @@ -2,6 +2,6 @@ fastNLP.core.optimizer ====================== .. automodule:: fastNLP.core.optimizer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.rst b/docs/source/fastNLP.core.rst index 82c13e46..cacc6622 100644 --- a/docs/source/fastNLP.core.rst +++ b/docs/source/fastNLP.core.rst @@ -2,15 +2,15 @@ fastNLP.core ============ .. automodule:: fastNLP.core - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: 子模块 ---------- .. toctree:: - :titlesonly: + :maxdepth: 1 fastNLP.core.batch fastNLP.core.callback @@ -26,4 +26,3 @@ fastNLP.core fastNLP.core.trainer fastNLP.core.utils fastNLP.core.vocabulary - diff --git a/docs/source/fastNLP.core.sampler.rst b/docs/source/fastNLP.core.sampler.rst index 1810d59c..0110f0c0 100644 --- a/docs/source/fastNLP.core.sampler.rst +++ b/docs/source/fastNLP.core.sampler.rst @@ -2,6 +2,6 @@ fastNLP.core.sampler ==================== .. automodule:: fastNLP.core.sampler - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.tester.rst b/docs/source/fastNLP.core.tester.rst index a9e7e09f..4d71a27b 100644 --- a/docs/source/fastNLP.core.tester.rst +++ b/docs/source/fastNLP.core.tester.rst @@ -2,6 +2,6 @@ fastNLP.core.tester =================== .. automodule:: fastNLP.core.tester - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.trainer.rst b/docs/source/fastNLP.core.trainer.rst index 9e518d4b..60bf2d5b 100644 --- a/docs/source/fastNLP.core.trainer.rst +++ b/docs/source/fastNLP.core.trainer.rst @@ -2,6 +2,6 @@ fastNLP.core.trainer ==================== .. automodule:: fastNLP.core.trainer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.utils.rst b/docs/source/fastNLP.core.utils.rst index fcd3f50c..3f80b4e8 100644 --- a/docs/source/fastNLP.core.utils.rst +++ b/docs/source/fastNLP.core.utils.rst @@ -2,6 +2,6 @@ fastNLP.core.utils ================== .. automodule:: fastNLP.core.utils - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.core.vocabulary.rst b/docs/source/fastNLP.core.vocabulary.rst index b3bf4bac..ba9598b9 100644 --- a/docs/source/fastNLP.core.vocabulary.rst +++ b/docs/source/fastNLP.core.vocabulary.rst @@ -2,6 +2,6 @@ fastNLP.core.vocabulary ======================= .. automodule:: fastNLP.core.vocabulary - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.embeddings.bert_embedding.rst b/docs/source/fastNLP.embeddings.bert_embedding.rst new file mode 100644 index 00000000..24ceff1c --- /dev/null +++ b/docs/source/fastNLP.embeddings.bert_embedding.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.bert\_embedding +================================== + +.. automodule:: fastNLP.embeddings.bert_embedding + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.embeddings.char_embedding.rst b/docs/source/fastNLP.embeddings.char_embedding.rst new file mode 100644 index 00000000..501089d8 --- /dev/null +++ b/docs/source/fastNLP.embeddings.char_embedding.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.char\_embedding +================================== + +.. automodule:: fastNLP.embeddings.char_embedding + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.embeddings.contextual_embedding.rst b/docs/source/fastNLP.embeddings.contextual_embedding.rst new file mode 100644 index 00000000..92e25c4d --- /dev/null +++ b/docs/source/fastNLP.embeddings.contextual_embedding.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.contextual\_embedding +======================================== + +.. automodule:: fastNLP.embeddings.contextual_embedding + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.embeddings.elmo_embedding.rst b/docs/source/fastNLP.embeddings.elmo_embedding.rst new file mode 100644 index 00000000..76669ee3 --- /dev/null +++ b/docs/source/fastNLP.embeddings.elmo_embedding.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.elmo\_embedding +================================== + +.. automodule:: fastNLP.embeddings.elmo_embedding + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.embeddings.embedding.rst b/docs/source/fastNLP.embeddings.embedding.rst new file mode 100644 index 00000000..5960d2cd --- /dev/null +++ b/docs/source/fastNLP.embeddings.embedding.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.embedding +============================ + +.. automodule:: fastNLP.embeddings.embedding + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.embeddings.rst b/docs/source/fastNLP.embeddings.rst new file mode 100644 index 00000000..25d05246 --- /dev/null +++ b/docs/source/fastNLP.embeddings.rst @@ -0,0 +1,21 @@ +fastNLP.embeddings +================== + +.. automodule:: fastNLP.embeddings + :members: + :undoc-members: + :show-inheritance: + +子模块 +---------- + +.. toctree:: + + fastNLP.embeddings.bert_embedding + fastNLP.embeddings.char_embedding + fastNLP.embeddings.contextual_embedding + fastNLP.embeddings.elmo_embedding + fastNLP.embeddings.embedding + fastNLP.embeddings.stack_embedding + fastNLP.embeddings.static_embedding + fastNLP.embeddings.utils diff --git a/docs/source/fastNLP.embeddings.stack_embedding.rst b/docs/source/fastNLP.embeddings.stack_embedding.rst new file mode 100644 index 00000000..4d2115f7 --- /dev/null +++ b/docs/source/fastNLP.embeddings.stack_embedding.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.stack\_embedding +=================================== + +.. automodule:: fastNLP.embeddings.stack_embedding + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.embeddings.static_embedding.rst b/docs/source/fastNLP.embeddings.static_embedding.rst new file mode 100644 index 00000000..e46de81a --- /dev/null +++ b/docs/source/fastNLP.embeddings.static_embedding.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.static\_embedding +==================================== + +.. automodule:: fastNLP.embeddings.static_embedding + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.embeddings.utils.rst b/docs/source/fastNLP.embeddings.utils.rst new file mode 100644 index 00000000..263bfbd6 --- /dev/null +++ b/docs/source/fastNLP.embeddings.utils.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.utils +======================== + +.. automodule:: fastNLP.embeddings.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.io.base_loader.rst b/docs/source/fastNLP.io.base_loader.rst index c1f9ac14..057867f4 100644 --- a/docs/source/fastNLP.io.base_loader.rst +++ b/docs/source/fastNLP.io.base_loader.rst @@ -2,6 +2,6 @@ fastNLP.io.base\_loader ======================= .. automodule:: fastNLP.io.base_loader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.io.config_io.rst b/docs/source/fastNLP.io.config_io.rst new file mode 100644 index 00000000..106d083d --- /dev/null +++ b/docs/source/fastNLP.io.config_io.rst @@ -0,0 +1,7 @@ +fastNLP.io.config\_io +===================== + +.. automodule:: fastNLP.io.config_io + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.io.data_loader.rst b/docs/source/fastNLP.io.data_loader.rst index 9261fa5c..8f990102 100644 --- a/docs/source/fastNLP.io.data_loader.rst +++ b/docs/source/fastNLP.io.data_loader.rst @@ -2,6 +2,6 @@ fastNLP.io.data\_loader ========================== .. automodule:: fastNLP.io.data_loader - :members: - :undoc-members: - :show-inheritance: \ No newline at end of file + :members: + :undoc-members: + :show-inheritance: \ No newline at end of file diff --git a/docs/source/fastNLP.io.dataset_loader.rst b/docs/source/fastNLP.io.dataset_loader.rst index d6663e59..e7990714 100644 --- a/docs/source/fastNLP.io.dataset_loader.rst +++ b/docs/source/fastNLP.io.dataset_loader.rst @@ -2,6 +2,6 @@ fastNLP.io.dataset\_loader ========================== .. automodule:: fastNLP.io.dataset_loader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.io.embed_loader.rst b/docs/source/fastNLP.io.embed_loader.rst index 7a8e730c..69e1f7ff 100644 --- a/docs/source/fastNLP.io.embed_loader.rst +++ b/docs/source/fastNLP.io.embed_loader.rst @@ -2,6 +2,6 @@ fastNLP.io.embed\_loader ======================== .. automodule:: fastNLP.io.embed_loader - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.io.file_utils.rst b/docs/source/fastNLP.io.file_utils.rst new file mode 100644 index 00000000..4a46e889 --- /dev/null +++ b/docs/source/fastNLP.io.file_utils.rst @@ -0,0 +1,7 @@ +fastNLP.io.file_utils +===================== + +.. automodule:: fastNLP.io.file_utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.io.model_io.rst b/docs/source/fastNLP.io.model_io.rst index 50d4c25a..537ce752 100644 --- a/docs/source/fastNLP.io.model_io.rst +++ b/docs/source/fastNLP.io.model_io.rst @@ -2,6 +2,6 @@ fastNLP.io.model\_io ==================== .. automodule:: fastNLP.io.model_io - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.io.rst b/docs/source/fastNLP.io.rst index 33afbbee..da9dbcfe 100644 --- a/docs/source/fastNLP.io.rst +++ b/docs/source/fastNLP.io.rst @@ -2,19 +2,26 @@ fastNLP.io ========== .. automodule:: fastNLP.io - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: 子模块 ---------- .. toctree:: - :titlesonly: fastNLP.io.data_loader + +Submodules +---------- + +.. toctree:: + fastNLP.io.base_loader + fastNLP.io.config_io fastNLP.io.dataset_loader fastNLP.io.embed_loader + fastNLP.io.file_utils fastNLP.io.model_io - + fastNLP.io.utils diff --git a/docs/source/fastNLP.io.utils.rst b/docs/source/fastNLP.io.utils.rst new file mode 100644 index 00000000..0b3f3938 --- /dev/null +++ b/docs/source/fastNLP.io.utils.rst @@ -0,0 +1,7 @@ +fastNLP.io.utils +================ + +.. automodule:: fastNLP.io.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.base_model.rst b/docs/source/fastNLP.models.base_model.rst new file mode 100644 index 00000000..fab1e6bf --- /dev/null +++ b/docs/source/fastNLP.models.base_model.rst @@ -0,0 +1,7 @@ +fastNLP.models.base\_model +========================== + +.. automodule:: fastNLP.models.base_model + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.bert.rst b/docs/source/fastNLP.models.bert.rst new file mode 100644 index 00000000..3b9482eb --- /dev/null +++ b/docs/source/fastNLP.models.bert.rst @@ -0,0 +1,7 @@ +fastNLP.models.bert +=================== + +.. automodule:: fastNLP.models.bert + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.biaffine_parser.rst b/docs/source/fastNLP.models.biaffine_parser.rst index a3dd1836..f19504e8 100644 --- a/docs/source/fastNLP.models.biaffine_parser.rst +++ b/docs/source/fastNLP.models.biaffine_parser.rst @@ -2,6 +2,6 @@ fastNLP.models.biaffine\_parser =============================== .. automodule:: fastNLP.models.biaffine_parser - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.cnn_text_classification.rst b/docs/source/fastNLP.models.cnn_text_classification.rst index a935d0bf..eacf6916 100644 --- a/docs/source/fastNLP.models.cnn_text_classification.rst +++ b/docs/source/fastNLP.models.cnn_text_classification.rst @@ -2,6 +2,6 @@ fastNLP.models.cnn\_text\_classification ======================================== .. automodule:: fastNLP.models.cnn_text_classification - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.enas_controller.rst b/docs/source/fastNLP.models.enas_controller.rst new file mode 100644 index 00000000..1ac97fac --- /dev/null +++ b/docs/source/fastNLP.models.enas_controller.rst @@ -0,0 +1,7 @@ +fastNLP.models.enas\_controller module +====================================== + +.. automodule:: fastNLP.models.enas_controller + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.enas_model.rst b/docs/source/fastNLP.models.enas_model.rst new file mode 100644 index 00000000..92dbb810 --- /dev/null +++ b/docs/source/fastNLP.models.enas_model.rst @@ -0,0 +1,7 @@ +fastNLP.models.enas\_model +========================== + +.. automodule:: fastNLP.models.enas_model + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.enas_trainer.rst b/docs/source/fastNLP.models.enas_trainer.rst new file mode 100644 index 00000000..eea79599 --- /dev/null +++ b/docs/source/fastNLP.models.enas_trainer.rst @@ -0,0 +1,7 @@ +fastNLP.models.enas\_trainer +============================ + +.. automodule:: fastNLP.models.enas_trainer + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.enas_utils.rst b/docs/source/fastNLP.models.enas_utils.rst new file mode 100644 index 00000000..c5e45b5f --- /dev/null +++ b/docs/source/fastNLP.models.enas_utils.rst @@ -0,0 +1,7 @@ +fastNLP.models.enas\_utils +========================== + +.. automodule:: fastNLP.models.enas_utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.rst b/docs/source/fastNLP.models.rst index 5858ebcd..5a237569 100644 --- a/docs/source/fastNLP.models.rst +++ b/docs/source/fastNLP.models.rst @@ -2,19 +2,23 @@ fastNLP.models ============== .. automodule:: fastNLP.models - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: 子模块 ---------- .. toctree:: - :titlesonly: + fastNLP.models.base_model + fastNLP.models.bert fastNLP.models.biaffine_parser fastNLP.models.cnn_text_classification + fastNLP.models.enas_controller + fastNLP.models.enas_model + fastNLP.models.enas_trainer + fastNLP.models.enas_utils fastNLP.models.sequence_labeling fastNLP.models.snli fastNLP.models.star_transformer - diff --git a/docs/source/fastNLP.models.sequence_labeling.rst b/docs/source/fastNLP.models.sequence_labeling.rst index 6d569fe1..85e28f06 100644 --- a/docs/source/fastNLP.models.sequence_labeling.rst +++ b/docs/source/fastNLP.models.sequence_labeling.rst @@ -2,6 +2,6 @@ fastNLP.models.sequence\_labeling ================================= .. automodule:: fastNLP.models.sequence_labeling - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.snli.rst b/docs/source/fastNLP.models.snli.rst index 24c2cc53..3b9b555c 100644 --- a/docs/source/fastNLP.models.snli.rst +++ b/docs/source/fastNLP.models.snli.rst @@ -2,6 +2,6 @@ fastNLP.models.snli =================== .. automodule:: fastNLP.models.snli - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.models.star_transformer.rst b/docs/source/fastNLP.models.star_transformer.rst index c93fb8cd..69d5c5b2 100644 --- a/docs/source/fastNLP.models.star_transformer.rst +++ b/docs/source/fastNLP.models.star_transformer.rst @@ -2,6 +2,6 @@ fastNLP.models.star\_transformer ================================ .. automodule:: fastNLP.models.star_transformer - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.modules.decoder.crf.rst b/docs/source/fastNLP.modules.decoder.crf.rst deleted file mode 100644 index 6d5b0d5b..00000000 --- a/docs/source/fastNLP.modules.decoder.crf.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.decoder.CRF -=========================== - -.. automodule:: fastNLP.modules.decoder.crf - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.decoder.mlp.rst b/docs/source/fastNLP.modules.decoder.mlp.rst deleted file mode 100644 index 7d661ebf..00000000 --- a/docs/source/fastNLP.modules.decoder.mlp.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.decoder.MLP -=========================== - -.. automodule:: fastNLP.modules.decoder.mlp - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.decoder.rst b/docs/source/fastNLP.modules.decoder.rst index e42a9f39..ecc2adbd 100644 --- a/docs/source/fastNLP.modules.decoder.rst +++ b/docs/source/fastNLP.modules.decoder.rst @@ -2,17 +2,7 @@ fastNLP.modules.decoder ======================= .. automodule:: fastNLP.modules.decoder - :members: - :undoc-members: - :show-inheritance: - -子模块 ----------- - -.. toctree:: - :titlesonly: - - fastNLP.modules.decoder.crf - fastNLP.modules.decoder.mlp - fastNLP.modules.decoder.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.modules.decoder.utils.rst b/docs/source/fastNLP.modules.decoder.utils.rst deleted file mode 100644 index da979d99..00000000 --- a/docs/source/fastNLP.modules.decoder.utils.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.decoder.utils -============================= - -.. automodule:: fastNLP.modules.decoder.utils - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.bert.rst b/docs/source/fastNLP.modules.encoder.bert.rst deleted file mode 100644 index 66bd0bbd..00000000 --- a/docs/source/fastNLP.modules.encoder.bert.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.encoder.bert -============================ - -.. automodule:: fastNLP.modules.encoder.bert - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.char_encoder.rst b/docs/source/fastNLP.modules.encoder.char_encoder.rst deleted file mode 100644 index 61ea3340..00000000 --- a/docs/source/fastNLP.modules.encoder.char_encoder.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.encoder.char\_encoder -===================================== - -.. automodule:: fastNLP.modules.encoder.char_encoder - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.conv_maxpool.rst b/docs/source/fastNLP.modules.encoder.conv_maxpool.rst deleted file mode 100644 index 7058a723..00000000 --- a/docs/source/fastNLP.modules.encoder.conv_maxpool.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.encoder.conv\_maxpool -===================================== - -.. automodule:: fastNLP.modules.encoder.conv_maxpool - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.embedding.rst b/docs/source/fastNLP.modules.encoder.embedding.rst deleted file mode 100644 index 4427b3bf..00000000 --- a/docs/source/fastNLP.modules.encoder.embedding.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.encoder.embedding -================================= - -.. automodule:: fastNLP.modules.encoder.embedding - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.lstm.rst b/docs/source/fastNLP.modules.encoder.lstm.rst deleted file mode 100644 index f9cbea88..00000000 --- a/docs/source/fastNLP.modules.encoder.lstm.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.encoder.lstm -============================ - -.. automodule:: fastNLP.modules.encoder.lstm - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.rst b/docs/source/fastNLP.modules.encoder.rst index b15232fa..0562f12d 100644 --- a/docs/source/fastNLP.modules.encoder.rst +++ b/docs/source/fastNLP.modules.encoder.rst @@ -2,22 +2,6 @@ fastNLP.modules.encoder ======================= .. automodule:: fastNLP.modules.encoder - :members: - :undoc-members: - :show-inheritance: - -子模块 ----------- - -.. toctree:: - :titlesonly: - - fastNLP.modules.encoder.bert - fastNLP.modules.encoder.char_encoder - fastNLP.modules.encoder.conv_maxpool - fastNLP.modules.encoder.embedding - fastNLP.modules.encoder.lstm - fastNLP.modules.encoder.star_transformer - fastNLP.modules.encoder.transformer - fastNLP.modules.encoder.variational_rnn - + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.star_transformer.rst b/docs/source/fastNLP.modules.encoder.star_transformer.rst deleted file mode 100644 index 0c406782..00000000 --- a/docs/source/fastNLP.modules.encoder.star_transformer.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.encoder.star\_transformer -========================================= - -.. automodule:: fastNLP.modules.encoder.star_transformer - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.transformer.rst b/docs/source/fastNLP.modules.encoder.transformer.rst deleted file mode 100644 index 6a40c597..00000000 --- a/docs/source/fastNLP.modules.encoder.transformer.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.encoder.transformer -=================================== - -.. automodule:: fastNLP.modules.encoder.transformer - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.encoder.variational_rnn.rst b/docs/source/fastNLP.modules.encoder.variational_rnn.rst deleted file mode 100644 index 348fb3d8..00000000 --- a/docs/source/fastNLP.modules.encoder.variational_rnn.rst +++ /dev/null @@ -1,7 +0,0 @@ -fastNLP.modules.encoder.variational\_rnn -======================================== - -.. automodule:: fastNLP.modules.encoder.variational_rnn - :members: - :undoc-members: - :show-inheritance: diff --git a/docs/source/fastNLP.modules.rst b/docs/source/fastNLP.modules.rst index 7f75cfdc..3160fe99 100644 --- a/docs/source/fastNLP.modules.rst +++ b/docs/source/fastNLP.modules.rst @@ -2,9 +2,9 @@ fastNLP.modules =============== .. automodule:: fastNLP.modules - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: 子模块 ----------- diff --git a/docs/source/fastNLP.rst b/docs/source/fastNLP.rst index f0c3d41c..076d9df0 100644 --- a/docs/source/fastNLP.rst +++ b/docs/source/fastNLP.rst @@ -2,19 +2,17 @@ API 文档 =============== .. automodule:: fastNLP - :members: - :undoc-members: - :show-inheritance: + :members: + :undoc-members: + :show-inheritance: 内部模块 ----------- .. toctree:: - :titlesonly: - :maxdepth: 3 - - fastNLP.core - fastNLP.io - fastNLP.modules - fastNLP.models + fastNLP.core + fastNLP.embeddings + fastNLP.io + fastNLP.models + fastNLP.modules From c99f02aa3b9abf1d2ccd1db3bccbea0407cfb1ec Mon Sep 17 00:00:00 2001 From: ChenXin Date: Fri, 12 Jul 2019 10:46:29 +0800 Subject: [PATCH 018/139] =?UTF-8?q?API=E6=96=87=E6=A1=A3=E5=85=A5=E5=8F=A3?= =?UTF-8?q?=E7=9A=84=E4=BB=8B=E7=BB=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastNLP/__init__.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fastNLP/__init__.py b/fastNLP/__init__.py index 0eaa5d81..ea2cd13a 100644 --- a/fastNLP/__init__.py +++ b/fastNLP/__init__.py @@ -1,11 +1,12 @@ """ -fastNLP 由 :mod:`~fastNLP.core` 、 :mod:`~fastNLP.io` 、:mod:`~fastNLP.modules`、:mod:`~fastNLP.models` -等子模块组成,你可以点进去查看每个模块的文档。 +fastNLP 由 :mod:`~fastNLP.core` 、 :mod:`~fastNLP.io` 、:mod:`~fastNLP.embeddings` 、 :mod:`~fastNLP.modules`、 +:mod:`~fastNLP.models` 等子模块组成,你可以查看每个模块的文档。 -- :mod:`~fastNLP.core` 是fastNLP 的核心模块,包括 DataSet、 Trainer、 Tester 等组件。详见文档 :doc:`/fastNLP.core` -- :mod:`~fastNLP.io` 是实现输入输出的模块,包括了数据集的读取,模型的存取等功能。详见文档 :doc:`/fastNLP.io` -- :mod:`~fastNLP.modules` 包含了用于搭建神经网络模型的诸多组件,可以帮助用户快速搭建自己所需的网络。详见文档 :doc:`/fastNLP.modules` -- :mod:`~fastNLP.models` 包含了一些使用 fastNLP 实现的完整网络模型,包括 :class:`~fastNLP.models.CNNText` 、 :class:`~fastNLP.models.SeqLabeling` 等常见模型。详见文档 :doc:`/fastNLP.models` +- :mod:`~fastNLP.core` 是fastNLP 的核心模块,包括 DataSet、 Trainer、 Tester 等组件。详见文档 :doc:`fastNLP.core` +- :mod:`~fastNLP.io` 是实现输入输出的模块,包括了数据集的读取,模型的存取等功能。详见文档 :doc:`fastNLP.io` +- :mod:`~fastNLP.embeddings` 提供用于构建复杂网络模型所需的各种embedding。详见文档 :doc:`fastNLP.embeddings` +- :mod:`~fastNLP.modules` 包含了用于搭建神经网络模型的诸多组件,可以帮助用户快速搭建自己所需的网络。详见文档 :doc:`fastNLP.modules` +- :mod:`~fastNLP.models` 包含了一些使用 fastNLP 实现的完整网络模型,包括 :class:`~fastNLP.models.CNNText` 、 :class:`~fastNLP.models.SeqLabeling` 等常见模型。详见文档 :doc:`fastNLP.models` fastNLP 中最常用的组件可以直接从 fastNLP 包中 import ,他们的文档如下: """ @@ -61,4 +62,5 @@ __version__ = '0.4.5' from .core import * from . import models from . import modules +from . import embeddings from .io import data_loader From d6ae241bbb51df3c8636331f4fc4607741cd3dd7 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Fri, 12 Jul 2019 10:49:18 +0800 Subject: [PATCH 019/139] =?UTF-8?q?decoder=E9=83=A8=E5=88=86=E7=9A=84?= =?UTF-8?q?=E5=88=AB=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastNLP/modules/decoder/crf.py | 52 ++++++++++++++++---------------- fastNLP/modules/decoder/mlp.py | 10 +++--- fastNLP/modules/decoder/utils.py | 10 +++--- 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/fastNLP/modules/decoder/crf.py b/fastNLP/modules/decoder/crf.py index c0717d6f..7c496868 100644 --- a/fastNLP/modules/decoder/crf.py +++ b/fastNLP/modules/decoder/crf.py @@ -11,7 +11,7 @@ from ..utils import initial_parameter def allowed_transitions(id2target, encoding_type='bio', include_start_end=False): """ - 别名::class:`fastNLP.modules.allowed_transitions` :class:`fastNLP.modules.decoder.crf.allowed_transitions` + 别名::class:`fastNLP.modules.allowed_transitions` :class:`fastNLP.modules.decoder.allowed_transitions` 给定一个id到label的映射表,返回所有可以跳转的(from_tag_id, to_tag_id)列表。 @@ -31,7 +31,7 @@ def allowed_transitions(id2target, encoding_type='bio', include_start_end=False) id_label_lst = list(id2target.items()) if include_start_end: id_label_lst += [(start_idx, 'start'), (end_idx, 'end')] - + def split_tag_label(from_label): from_label = from_label.lower() if from_label in ['start', 'end']: @@ -41,7 +41,7 @@ def allowed_transitions(id2target, encoding_type='bio', include_start_end=False) from_tag = from_label[:1] from_label = from_label[2:] return from_tag, from_label - + for from_id, from_label in id_label_lst: if from_label in ['', '']: continue @@ -93,7 +93,7 @@ def _is_transition_allowed(encoding_type, from_tag, from_label, to_tag, to_label return to_tag in ['end', 'b', 'o'] else: raise ValueError("Unexpect tag {}. Expect only 'B', 'I', 'O'.".format(from_tag)) - + elif encoding_type == 'bmes': """ 第一行是to_tag, 第一列是from_tag,y任意条件下可转,-只有在label相同时可转,n不可转 @@ -151,7 +151,7 @@ def _is_transition_allowed(encoding_type, from_tag, from_label, to_tag, to_label class ConditionalRandomField(nn.Module): """ - 别名::class:`fastNLP.modules.ConditionalRandomField` :class:`fastNLP.modules.decoder.crf.ConditionalRandomField` + 别名::class:`fastNLP.modules.ConditionalRandomField` :class:`fastNLP.modules.decoder.ConditionalRandomField` 条件随机场。 提供forward()以及viterbi_decode()两个方法,分别用于训练与inference。 @@ -163,21 +163,21 @@ class ConditionalRandomField(nn.Module): allowed_transitions()函数得到;如果为None,则所有跃迁均为合法 :param str initial_method: 初始化方法。见initial_parameter """ - + def __init__(self, num_tags, include_start_end_trans=False, allowed_transitions=None, initial_method=None): - + super(ConditionalRandomField, self).__init__() - + self.include_start_end_trans = include_start_end_trans self.num_tags = num_tags - + # the meaning of entry in this matrix is (from_tag_id, to_tag_id) score self.trans_m = nn.Parameter(torch.randn(num_tags, num_tags)) if self.include_start_end_trans: self.start_scores = nn.Parameter(torch.randn(num_tags)) self.end_scores = nn.Parameter(torch.randn(num_tags)) - + if allowed_transitions is None: constrain = torch.zeros(num_tags + 2, num_tags + 2) else: @@ -185,9 +185,9 @@ class ConditionalRandomField(nn.Module): for from_tag_id, to_tag_id in allowed_transitions: constrain[from_tag_id, to_tag_id] = 0 self._constrain = nn.Parameter(constrain, requires_grad=False) - + initial_parameter(self, initial_method) - + def _normalizer_likelihood(self, logits, mask): """Computes the (batch_size,) denominator term for the log-likelihood, which is the sum of the likelihoods across all possible state sequences. @@ -200,21 +200,21 @@ class ConditionalRandomField(nn.Module): alpha = logits[0] if self.include_start_end_trans: alpha = alpha + self.start_scores.view(1, -1) - + flip_mask = mask.eq(0) - + for i in range(1, seq_len): emit_score = logits[i].view(batch_size, 1, n_tags) trans_score = self.trans_m.view(1, n_tags, n_tags) tmp = alpha.view(batch_size, n_tags, 1) + emit_score + trans_score alpha = torch.logsumexp(tmp, 1).masked_fill(flip_mask[i].view(batch_size, 1), 0) + \ alpha.masked_fill(mask[i].byte().view(batch_size, 1), 0) - + if self.include_start_end_trans: alpha = alpha + self.end_scores.view(1, -1) - + return torch.logsumexp(alpha, 1) - + def _gold_score(self, logits, tags, mask): """ Compute the score for the gold path. @@ -226,7 +226,7 @@ class ConditionalRandomField(nn.Module): seq_len, batch_size, _ = logits.size() batch_idx = torch.arange(batch_size, dtype=torch.long, device=logits.device) seq_idx = torch.arange(seq_len, dtype=torch.long, device=logits.device) - + # trans_socre [L-1, B] mask = mask.byte() flip_mask = mask.eq(0) @@ -243,7 +243,7 @@ class ConditionalRandomField(nn.Module): score = score + st_scores + ed_scores # return [B,] return score - + def forward(self, feats, tags, mask): """ 用于计算CRF的前向loss,返回值为一个batch_size的FloatTensor,可能需要mean()求得loss。 @@ -258,9 +258,9 @@ class ConditionalRandomField(nn.Module): mask = mask.transpose(0, 1).float() all_path_score = self._normalizer_likelihood(feats, mask) gold_path_score = self._gold_score(feats, tags, mask) - + return all_path_score - gold_path_score - + def viterbi_decode(self, logits, mask, unpad=False): """给定一个特征矩阵以及转移分数矩阵,计算出最佳的路径以及对应的分数 @@ -277,7 +277,7 @@ class ConditionalRandomField(nn.Module): batch_size, seq_len, n_tags = logits.size() logits = logits.transpose(0, 1).data # L, B, H mask = mask.transpose(0, 1).data.byte() # L, B - + # dp vpath = logits.new_zeros((seq_len, batch_size, n_tags), dtype=torch.long) vscore = logits[0] @@ -286,7 +286,7 @@ class ConditionalRandomField(nn.Module): if self.include_start_end_trans: transitions[n_tags, :n_tags] += self.start_scores.data transitions[:n_tags, n_tags + 1] += self.end_scores.data - + vscore += transitions[n_tags, :n_tags] trans_score = transitions[:n_tags, :n_tags].view(1, n_tags, n_tags).data for i in range(1, seq_len): @@ -297,17 +297,17 @@ class ConditionalRandomField(nn.Module): vpath[i] = best_dst vscore = best_score.masked_fill(mask[i].eq(0).view(batch_size, 1), 0) + \ vscore.masked_fill(mask[i].view(batch_size, 1), 0) - + if self.include_start_end_trans: vscore += transitions[:n_tags, n_tags + 1].view(1, -1) - + # backtrace batch_idx = torch.arange(batch_size, dtype=torch.long, device=logits.device) seq_idx = torch.arange(seq_len, dtype=torch.long, device=logits.device) lens = (mask.long().sum(0) - 1) # idxes [L, B], batched idx from seq_len-1 to 0 idxes = (lens.view(1, -1) - seq_idx.view(-1, 1)) % seq_len - + ans = logits.new_empty((seq_len, batch_size), dtype=torch.long) ans_score, last_tags = vscore.max(1) ans[idxes[0], batch_idx] = last_tags diff --git a/fastNLP/modules/decoder/mlp.py b/fastNLP/modules/decoder/mlp.py index 418b3a77..9d9d80f2 100644 --- a/fastNLP/modules/decoder/mlp.py +++ b/fastNLP/modules/decoder/mlp.py @@ -10,7 +10,7 @@ from ..utils import initial_parameter class MLP(nn.Module): """ - 别名::class:`fastNLP.modules.MLP` :class:`fastNLP.modules.decoder.mlp.MLP` + 别名::class:`fastNLP.modules.MLP` :class:`fastNLP.modules.decoder.MLP` 多层感知器 @@ -40,7 +40,7 @@ class MLP(nn.Module): >>> print(x) >>> print(y) """ - + def __init__(self, size_layer, activation='relu', output_activation=None, initial_method=None, dropout=0.0): super(MLP, self).__init__() self.hiddens = nn.ModuleList() @@ -51,9 +51,9 @@ class MLP(nn.Module): self.output = nn.Linear(size_layer[i - 1], size_layer[i]) else: self.hiddens.append(nn.Linear(size_layer[i - 1], size_layer[i])) - + self.dropout = nn.Dropout(p=dropout) - + actives = { 'relu': nn.ReLU(), 'tanh': nn.Tanh(), @@ -82,7 +82,7 @@ class MLP(nn.Module): else: raise ValueError("should set activation correctly: {}".format(activation)) initial_parameter(self, initial_method) - + def forward(self, x): """ :param torch.Tensor x: MLP接受的输入 diff --git a/fastNLP/modules/decoder/utils.py b/fastNLP/modules/decoder/utils.py index 249f3ff6..9e773336 100644 --- a/fastNLP/modules/decoder/utils.py +++ b/fastNLP/modules/decoder/utils.py @@ -6,7 +6,7 @@ import torch def viterbi_decode(logits, transitions, mask=None, unpad=False): r""" - 别名::class:`fastNLP.modules.viterbi_decode` :class:`fastNLP.modules.decoder.utils.viterbi_decode` + 别名::class:`fastNLP.modules.viterbi_decode` :class:`fastNLP.modules.decoder.viterbi_decode` 给定一个特征矩阵以及转移分数矩阵,计算出最佳的路径以及对应的分数 @@ -30,11 +30,11 @@ def viterbi_decode(logits, transitions, mask=None, unpad=False): mask = mask.transpose(0, 1).data.byte() # L, B else: mask = logits.new_ones((seq_len, batch_size), dtype=torch.uint8) - + # dp vpath = logits.new_zeros((seq_len, batch_size, n_tags), dtype=torch.long) vscore = logits[0] - + trans_score = transitions.view(1, n_tags, n_tags).data for i in range(1, seq_len): prev_score = vscore.view(batch_size, n_tags, 1) @@ -44,14 +44,14 @@ def viterbi_decode(logits, transitions, mask=None, unpad=False): vpath[i] = best_dst vscore = best_score.masked_fill(mask[i].eq(0).view(batch_size, 1), 0) + \ vscore.masked_fill(mask[i].view(batch_size, 1), 0) - + # backtrace batch_idx = torch.arange(batch_size, dtype=torch.long, device=logits.device) seq_idx = torch.arange(seq_len, dtype=torch.long, device=logits.device) lens = (mask.long().sum(0) - 1) # idxes [L, B], batched idx from seq_len-1 to 0 idxes = (lens.view(1, -1) - seq_idx.view(-1, 1)) % seq_len - + ans = logits.new_empty((seq_len, batch_size), dtype=torch.long) ans_score, last_tags = vscore.max(1) ans[idxes[0], batch_idx] = last_tags From a09cf518d7d923c5adae48d3f8f54009682a795b Mon Sep 17 00:00:00 2001 From: ChenXin Date: Fri, 12 Jul 2019 11:05:57 +0800 Subject: [PATCH 020/139] =?UTF-8?q?modules=E5=85=A5=E5=8F=A3=E7=9A=84?= =?UTF-8?q?=E4=BB=8B=E7=BB=8D=E5=92=8Cdropout=E7=9A=84=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastNLP/modules/__init__.py | 27 +++++++++++++-------------- fastNLP/modules/dropout.py | 6 ++---- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/fastNLP/modules/__init__.py b/fastNLP/modules/__init__.py index 43ec3f5f..7b0237dc 100644 --- a/fastNLP/modules/__init__.py +++ b/fastNLP/modules/__init__.py @@ -1,22 +1,19 @@ """ -大部分用于的 NLP 任务神经网络都可以看做由编码 :mod:`~fastNLP.modules.encoder` 、 -解码 :mod:`~fastNLP.modules.decoder` 两种模块组成。 .. image:: figures/text_classification.png -:mod:`~fastNLP.modules` 中实现了 fastNLP 提供的诸多模块组件,可以帮助用户快速搭建自己所需的网络。 -两种模块的功能和常见组件如下: +大部分用于的 NLP 任务神经网络都可以看做由 :mod:`embedding` 、 :mod:`~fastNLP.modules.encoder` 、 +:mod:`~fastNLP.modules.decoder` 三种模块组成。 本模块中实现了 fastNLP 提供的诸多模块组件, +可以帮助用户快速搭建自己所需的网络。几种模块的功能和常见组件如下: + +.. csv-table:: + :header: "类型", "功能", "常见组件" + + "embedding", 参见 :doc:`fastNLP.embeddings` , "Elmo, Bert" + "encoder", "将输入编码为具有表示能力的向量", "CNN, LSTM, Transformer" + "decoder", "将具有某种表示意义的向量解码为需要的输出形式 ", "MLP, CRF" + "其它", "配合其它组件使用的组件", "Dropout" -+-----------------------+-----------------------+-----------------------+ -| module type | functionality | example | -+=======================+=======================+=======================+ -| encoder | 将输入编码为具有具 | embedding, RNN, CNN, | -| | 有表示能力的向量 | transformer | -+-----------------------+-----------------------+-----------------------+ -| decoder | 将具有某种表示意义的 | MLP, CRF | -| | 向量解码为需要的输出 | | -| | 形式 | | -+-----------------------+-----------------------+-----------------------+ """ __all__ = [ @@ -40,6 +37,8 @@ __all__ = [ "ConditionalRandomField", "viterbi_decode", "allowed_transitions", + + "TimestepDropout", ] from . import decoder diff --git a/fastNLP/modules/dropout.py b/fastNLP/modules/dropout.py index 1363165c..0ea2a2d9 100644 --- a/fastNLP/modules/dropout.py +++ b/fastNLP/modules/dropout.py @@ -5,10 +5,8 @@ import torch class TimestepDropout(torch.nn.Dropout): """ - 别名::class:`fastNLP.modules.TimestepDropout` - - 接受的参数shape为``[batch_size, num_timesteps, embedding_dim)]`` 使用同一个mask(shape为``(batch_size, embedding_dim)``) - 在每个timestamp上做dropout。 + 传入参数的shape为 ``(batch_size, num_timesteps, embedding_dim)`` + 使用同一个shape为 ``(batch_size, embedding_dim)`` 的mask在每个timestamp上做dropout。 """ def forward(self, x): From f3a9fc5b7912038bc41cfd6f53515f88405f8b9d Mon Sep 17 00:00:00 2001 From: ChenXin Date: Fri, 12 Jul 2019 11:16:21 +0800 Subject: [PATCH 021/139] =?UTF-8?q?encoder=E9=87=8C=E9=9D=A2=E7=9A=84?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E5=92=8C=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fastNLP/modules/__init__.py | 14 ++++-- fastNLP/modules/encoder/__init__.py | 12 ++--- fastNLP/modules/encoder/attention.py | 54 ++++++++++----------- fastNLP/modules/encoder/bert.py | 44 ++++++++++------- fastNLP/modules/encoder/char_encoder.py | 23 ++++----- fastNLP/modules/encoder/conv_maxpool.py | 15 +++--- fastNLP/modules/encoder/lstm.py | 5 +- fastNLP/modules/encoder/pooling.py | 30 ++++++------ fastNLP/modules/encoder/star_transformer.py | 50 +++++++++---------- fastNLP/modules/encoder/transformer.py | 10 ++-- fastNLP/modules/encoder/variational_rnn.py | 48 +++++++++--------- 11 files changed, 161 insertions(+), 144 deletions(-) diff --git a/fastNLP/modules/__init__.py b/fastNLP/modules/__init__.py index 7b0237dc..0e16d48c 100644 --- a/fastNLP/modules/__init__.py +++ b/fastNLP/modules/__init__.py @@ -17,22 +17,30 @@ """ __all__ = [ - # "BertModel", + "BertModel", + "ConvolutionCharEncoder", "LSTMCharEncoder", + "ConvMaxpool", + "LSTM", + "StarTransformer", + "TransformerEncoder", + "VarRNN", "VarLSTM", "VarGRU", - + "MaxPool", "MaxPoolWithMask", "AvgPool", + "AvgPoolWithMask", + "MultiHeadAttention", - + "MLP", "ConditionalRandomField", "viterbi_decode", diff --git a/fastNLP/modules/encoder/__init__.py b/fastNLP/modules/encoder/__init__.py index 051a0c01..4946a70f 100644 --- a/fastNLP/modules/encoder/__init__.py +++ b/fastNLP/modules/encoder/__init__.py @@ -1,17 +1,17 @@ __all__ = [ "BertModel", - + "ConvolutionCharEncoder", "LSTMCharEncoder", - + "ConvMaxpool", - + "LSTM", - + "StarTransformer", - + "TransformerEncoder", - + "VarRNN", "VarLSTM", "VarGRU", diff --git a/fastNLP/modules/encoder/attention.py b/fastNLP/modules/encoder/attention.py index 0a42d889..fe3f7fd8 100644 --- a/fastNLP/modules/encoder/attention.py +++ b/fastNLP/modules/encoder/attention.py @@ -8,8 +8,6 @@ import torch import torch.nn.functional as F from torch import nn -from fastNLP.modules.dropout import TimestepDropout - from fastNLP.modules.utils import initial_parameter @@ -18,7 +16,7 @@ class DotAttention(nn.Module): .. todo:: 补上文档 """ - + def __init__(self, key_size, value_size, dropout=0.0): super(DotAttention, self).__init__() self.key_size = key_size @@ -26,7 +24,7 @@ class DotAttention(nn.Module): self.scale = math.sqrt(key_size) self.drop = nn.Dropout(dropout) self.softmax = nn.Softmax(dim=2) - + def forward(self, Q, K, V, mask_out=None): """ @@ -45,7 +43,7 @@ class DotAttention(nn.Module): class MultiHeadAttention(nn.Module): """ - 别名::class:`fastNLP.modules.MultiHeadAttention` :class:`fastNLP.modules.encoder.attention.MultiHeadAttention` + 别名::class:`fastNLP.modules.MultiHeadAttention` :class:`fastNLP.modules.encoder.MultiHeadAttention` :param input_size: int, 输入维度的大小。同时也是输出维度的大小。 :param key_size: int, 每个head的维度大小。 @@ -53,14 +51,14 @@ class MultiHeadAttention(nn.Module): :param num_head: int,head的数量。 :param dropout: float。 """ - + def __init__(self, input_size, key_size, value_size, num_head, dropout=0.1): super(MultiHeadAttention, self).__init__() self.input_size = input_size self.key_size = key_size self.value_size = value_size self.num_head = num_head - + in_size = key_size * num_head self.q_in = nn.Linear(input_size, in_size) self.k_in = nn.Linear(input_size, in_size) @@ -69,14 +67,14 @@ class MultiHeadAttention(nn.Module): self.attention = DotAttention(key_size=key_size, value_size=value_size, dropout=dropout) self.out = nn.Linear(value_size * num_head, input_size) self.reset_parameters() - + def reset_parameters(self): sqrt = math.sqrt nn.init.normal_(self.q_in.weight, mean=0, std=sqrt(2.0 / (self.input_size + self.key_size))) nn.init.normal_(self.k_in.weight, mean=0, std=sqrt(2.0 / (self.input_size + self.key_size))) nn.init.normal_(self.v_in.weight, mean=0, std=sqrt(2.0 / (self.input_size + self.value_size))) nn.init.xavier_normal_(self.out.weight) - + def forward(self, Q, K, V, atte_mask_out=None): """ @@ -92,7 +90,7 @@ class MultiHeadAttention(nn.Module): q = self.q_in(Q).view(batch, sq, n_head, d_k) k = self.k_in(K).view(batch, sk, n_head, d_k) v = self.v_in(V).view(batch, sk, n_head, d_v) - + # transpose q, k and v to do batch attention q = q.permute(2, 0, 1, 3).contiguous().view(-1, sq, d_k) k = k.permute(2, 0, 1, 3).contiguous().view(-1, sk, d_k) @@ -100,7 +98,7 @@ class MultiHeadAttention(nn.Module): if atte_mask_out is not None: atte_mask_out = atte_mask_out.repeat(n_head, 1, 1) atte = self.attention(q, k, v, atte_mask_out).view(n_head, batch, sq, d_v) - + # concat all heads, do output linear atte = atte.permute(1, 2, 0, 3).contiguous().view(batch, sq, -1) output = self.out(atte) @@ -124,11 +122,11 @@ class BiAttention(nn.Module): \end{array} """ - + def __init__(self): super(BiAttention, self).__init__() self.inf = 10e12 - + def forward(self, in_x1, in_x2, x1_len, x2_len): """ :param torch.Tensor in_x1: [batch_size, x1_seq_len, hidden_size] 第一句的特征表示 @@ -139,36 +137,36 @@ class BiAttention(nn.Module): torch.Tensor out_x2: [batch_size, x2_seq_len, hidden_size] 第一句attend到的特征表示 """ - + assert in_x1.size()[0] == in_x2.size()[0] assert in_x1.size()[2] == in_x2.size()[2] # The batch size and hidden size must be equal. assert in_x1.size()[1] == x1_len.size()[1] and in_x2.size()[1] == x2_len.size()[1] # The seq len in in_x and x_len must be equal. assert in_x1.size()[0] == x1_len.size()[0] and x1_len.size()[0] == x2_len.size()[0] - + batch_size = in_x1.size()[0] x1_max_len = in_x1.size()[1] x2_max_len = in_x2.size()[1] - + in_x2_t = torch.transpose(in_x2, 1, 2) # [batch_size, hidden_size, x2_seq_len] - + attention_matrix = torch.bmm(in_x1, in_x2_t) # [batch_size, x1_seq_len, x2_seq_len] - + a_mask = x1_len.le(0.5).float() * -self.inf # [batch_size, x1_seq_len] a_mask = a_mask.view(batch_size, x1_max_len, -1) a_mask = a_mask.expand(-1, -1, x2_max_len) # [batch_size, x1_seq_len, x2_seq_len] b_mask = x2_len.le(0.5).float() * -self.inf b_mask = b_mask.view(batch_size, -1, x2_max_len) b_mask = b_mask.expand(-1, x1_max_len, -1) # [batch_size, x1_seq_len, x2_seq_len] - + attention_a = F.softmax(attention_matrix + a_mask, dim=2) # [batch_size, x1_seq_len, x2_seq_len] attention_b = F.softmax(attention_matrix + b_mask, dim=1) # [batch_size, x1_seq_len, x2_seq_len] - + out_x1 = torch.bmm(attention_a, in_x2) # [batch_size, x1_seq_len, hidden_size] attention_b_t = torch.transpose(attention_b, 1, 2) out_x2 = torch.bmm(attention_b_t, in_x1) # [batch_size, x2_seq_len, hidden_size] - + return out_x1, out_x2 @@ -182,10 +180,10 @@ class SelfAttention(nn.Module): :param float drop: dropout概率,默认值为0.5 :param str initial_method: 初始化参数方法 """ - + def __init__(self, input_size, attention_unit=300, attention_hops=10, drop=0.5, initial_method=None, ): super(SelfAttention, self).__init__() - + self.attention_hops = attention_hops self.ws1 = nn.Linear(input_size, attention_unit, bias=False) self.ws2 = nn.Linear(attention_unit, attention_hops, bias=False) @@ -194,7 +192,7 @@ class SelfAttention(nn.Module): self.drop = nn.Dropout(drop) self.tanh = nn.Tanh() initial_parameter(self, initial_method) - + def _penalization(self, attention): """ compute the penalization term for attention module @@ -208,7 +206,7 @@ class SelfAttention(nn.Module): mat = torch.bmm(attention, attention_t) - self.I[:attention.size(0)] ret = (torch.sum(torch.sum((mat ** 2), 2), 1).squeeze() + 1e-10) ** 0.5 return torch.sum(ret) / size[0] - + def forward(self, input, input_origin): """ :param torch.Tensor input: [baz, senLen, h_dim] 要做attention的矩阵 @@ -218,14 +216,14 @@ class SelfAttention(nn.Module): """ input = input.contiguous() size = input.size() # [bsz, len, nhid] - + input_origin = input_origin.expand(self.attention_hops, -1, -1) # [hops,baz, len] input_origin = input_origin.transpose(0, 1).contiguous() # [baz, hops,len] - + y1 = self.tanh(self.ws1(self.drop(input))) # [baz,len,dim] -->[bsz,len, attention-unit] attention = self.ws2(y1).transpose(1, 2).contiguous() # [bsz,len, attention-unit]--> [bsz, len, hop]--> [baz,hop,len] - + attention = attention + (-999999 * (input_origin == 0).float()) # remove the weight on padding token. attention = F.softmax(attention, 2) # [baz ,hop, len] return torch.bmm(attention, input), self._penalization(attention) # output1 --> [baz ,hop ,nhid] diff --git a/fastNLP/modules/encoder/bert.py b/fastNLP/modules/encoder/bert.py index 6d32ae74..ddd22ed0 100644 --- a/fastNLP/modules/encoder/bert.py +++ b/fastNLP/modules/encoder/bert.py @@ -1,11 +1,11 @@ - - - """ 这个页面的代码很大程度上参考(复制粘贴)了https://github.com/huggingface/pytorch-pretrained-BERT的代码, 如果你发现该代码对你 有用,也请引用一下他们。 """ +__all__ = [ + "BertModel" +] import collections @@ -26,6 +26,7 @@ CONFIG_FILE = 'bert_config.json' class BertConfig(object): """Configuration class to store the configuration of a `BertModel`. """ + def __init__(self, vocab_size_or_config_json_file, hidden_size=768, @@ -65,7 +66,7 @@ class BertConfig(object): layer_norm_eps: The epsilon used by LayerNorm. """ if isinstance(vocab_size_or_config_json_file, str) or (sys.version_info[0] == 2 - and isinstance(vocab_size_or_config_json_file, unicode)): + and isinstance(vocab_size_or_config_json_file, unicode)): with open(vocab_size_or_config_json_file, "r", encoding='utf-8') as reader: json_config = json.loads(reader.read()) for key, value in json_config.items(): @@ -150,6 +151,7 @@ class BertLayerNorm(nn.Module): class BertEmbeddings(nn.Module): """Construct the embeddings from word, position and token_type embeddings. """ + def __init__(self, config): super(BertEmbeddings, self).__init__() self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=0) @@ -331,7 +333,10 @@ class BertPooler(nn.Module): class BertModel(nn.Module): - """BERT(Bidirectional Embedding Representations from Transformers). + """ + 别名::class:`fastNLP.modules.BertModel` :class:`fastNLP.modules.encoder.BertModel` + + BERT(Bidirectional Embedding Representations from Transformers). 如果你想使用预训练好的权重矩阵,请在以下网址下载. sources:: @@ -449,9 +454,9 @@ class BertModel(nn.Module): model = cls(config, *inputs, **kwargs) if state_dict is None: files = glob.glob(os.path.join(pretrained_model_dir, '*.bin')) - if len(files)==0: + if len(files) == 0: raise FileNotFoundError(f"There is no *.bin file in {pretrained_model_dir}") - elif len(files)>1: + elif len(files) > 1: raise FileExistsError(f"There are multiple *.bin files in {pretrained_model_dir}") weights_path = files[0] state_dict = torch.load(weights_path, map_location='cpu') @@ -580,6 +585,7 @@ def load_vocab(vocab_file): index += 1 return vocab + class BasicTokenizer(object): """Runs basic tokenization (punctuation splitting, lower casing, etc.).""" @@ -765,8 +771,8 @@ class BertTokenizer(object): [(ids, tok) for tok, ids in self.vocab.items()]) self.do_basic_tokenize = do_basic_tokenize if do_basic_tokenize: - self.basic_tokenizer = BasicTokenizer(do_lower_case=do_lower_case, - never_split=never_split) + self.basic_tokenizer = BasicTokenizer(do_lower_case=do_lower_case, + never_split=never_split) self.wordpiece_tokenizer = WordpieceTokenizer(vocab=self.vocab) self.max_len = max_len if max_len is not None else int(1e12) @@ -821,7 +827,7 @@ class BertTokenizer(object): for token, token_index in sorted(self.vocab.items(), key=lambda kv: kv[1]): if index != token_index: print("Saving vocabulary to {}: vocabulary indices are not consecutive." - " Please check that the vocabulary is not corrupted!".format(vocab_file)) + " Please check that the vocabulary is not corrupted!".format(vocab_file)) index = token_index writer.write(token + u'\n') index += 1 @@ -841,6 +847,7 @@ class BertTokenizer(object): tokenizer = cls(pretrained_model_name_or_path, *inputs, **kwargs) return tokenizer + VOCAB_NAME = 'vocab.txt' @@ -849,7 +856,8 @@ class _WordPieceBertModel(nn.Module): 这个模块用于直接计算word_piece的结果. """ - def __init__(self, model_dir:str, layers:str='-1'): + + def __init__(self, model_dir: str, layers: str = '-1'): super().__init__() self.tokenzier = BertTokenizer.from_pretrained(model_dir) @@ -858,11 +866,11 @@ class _WordPieceBertModel(nn.Module): encoder_layer_number = len(self.encoder.encoder.layer) self.layers = list(map(int, layers.split(','))) for layer in self.layers: - if layer<0: - assert -layer<=encoder_layer_number, f"The layer index:{layer} is out of scope for " \ + if layer < 0: + assert -layer <= encoder_layer_number, f"The layer index:{layer} is out of scope for " \ f"a bert model with {encoder_layer_number} layers." else: - assert layer - +
fastNLP.modules 实现了用于搭建神经网络模型的诸多组件
fastNLP.embeddings 实现了将序列index转为向量序列的功能,包括读取预训练embedding等
fastNLP.io 实现了读写功能,包括数据读入,模型读写等
fastNLP.io 实现了读写功能,包括数据读入,模型读写等 实现了读写功能,包括数据读入与预处理,模型读写,自动下载等
diff --git a/docs/source/tutorials/tutorial_2_load_dataset.rst b/docs/source/tutorials/tutorial_2_load_dataset.rst index 4fa4a84d..17ad6baf 100644 --- a/docs/source/tutorials/tutorial_2_load_dataset.rst +++ b/docs/source/tutorials/tutorial_2_load_dataset.rst @@ -1,57 +1,53 @@ -================================= -使用DataSetLoader加载数据集 -================================= +======================================= +使用Loader和Pipe加载并处理数据集 +======================================= 这一部分是一个关于如何加载数据集的教程 教程目录: - - `Part I: 数据集容器`_ - - `Part II: 数据集的使用方式`_ - - `Part III: 不同数据类型的DataSetLoader`_ - - `Part IV: DataSetLoader举例`_ - - `Part V: fastNLP封装好的数据集加载器`_ + - `Part I: 数据集容器DataBundle`_ + - `Part II: 加载数据集的基类Loader`_ + - `Part III: 不同格式类型的基础Loader`_ + - `Part IV: 使用Pipe对数据集进行预处理`_ + - `Part V: fastNLP封装好的Loader和Pipe`_ ----------------------------- -Part I: 数据集容器 ----------------------------- +------------------------------------ +Part I: 数据集容器DataBundle +------------------------------------ -在fastNLP中,我们使用 :class:`~fastNLP.io.base_loader.DataBundle` 来存储数据集信息。 -:class:`~fastNLP.io.base_loader.DataBundle` 类包含了两个重要内容: `datasets` 和 `vocabs` 。 +在fastNLP中,我们使用 :class:`~fastNLP.io.data_bundle.DataBundle` 来存储数据集信息。 +:class:`~fastNLP.io.data_bundle.DataBundle` 类包含了两个重要内容: `datasets` 和 `vocabs` 。 `datasets` 是一个 `key` 为数据集名称(如 `train` , `dev` ,和 `test` 等), `value` 为 :class:`~fastNLP.DataSet` 的字典。 `vocabs` 是一个 `key` 为词表名称(如 :attr:`fastNLP.Const.INPUT` 表示输入文本的词表名称, :attr:`fastNLP.Const.TARGET` 表示目标 的真实标签词表的名称,等等), `value` 为词表内容( :class:`~fastNLP.Vocabulary` )的字典。 ----------------------------- -Part II: 数据集的使用方式 ----------------------------- +------------------------------------- +Part II: 加载数据集的基类Loader +------------------------------------- -在fastNLP中,我们采用 :class:`~fastNLP.io.base_loader.DataSetLoader` 来作为加载数据集的基类。 -:class:`~fastNLP.io.base_loader.DataSetLoader` 定义了各种DataSetLoader所需的API接口,开发者应该继承它实现各种的DataSetLoader。 -在各种数据集的DataSetLoader当中,至少应该编写如下内容: +在fastNLP中,我们采用 :class:`~fastNLP.io.loader.Loader` 来作为加载数据集的基类。 +:class:`~fastNLP.io.loader.Loader` 定义了各种Loader所需的API接口,开发者应该继承它实现各种的Loader。 +在各种数据集的Loader当中,至少应该编写如下内容: - - _load 函数:从一个数据文件中读取数据到一个 :class:`~fastNLP.DataSet` - - load 函数(可以使用基类的方法):从一个或多个数据文件中读取数据到一个或多个 :class:`~fastNLP.DataSet` - - process 函数:一个或多个从数据文件中读取数据,并处理成可以训练的 :class:`~fastNLP.io.DataBundle` + - _load 函数:从一个数据文件中读取数据,返回一个 :class:`~fastNLP.DataSet` + - load 函数:从文件或者文件夹中读取数据并组装成 :class:`~fastNLP.io.data_bundle.DataBundle` - **\*process函数中可以调用load函数或_load函数** - -DataSetLoader的_load或者load函数返回的 :class:`~fastNLP.DataSet` 当中,内容为数据集的文本信息,process函数返回的 -:class:`~fastNLP.io.DataBundle` 当中, `datasets` 的内容为已经index好的、可以直接被 :class:`~fastNLP.Trainer` -接受的内容。 +Loader的load函数返回的 :class:`~fastNLP.io.data_bundle.DataBundle` 里面包含了数据集的原始数据。 -------------------------------------------------------- -Part III: 不同数据类型的DataSetLoader +Part III: 不同格式类型的基础Loader -------------------------------------------------------- -:class:`~fastNLP.io.dataset_loader.CSVLoader` +:class:`~fastNLP.io.loader.CSVLoader` 读取CSV类型的数据集文件。例子如下: .. code-block:: python + from fastNLP.io.loader import CSVLoader data_set_loader = CSVLoader( headers=('words', 'target'), sep='\t' ) @@ -67,17 +63,18 @@ Part III: 不同数据类型的DataSetLoader The performances are an absolute joy . 4 -:class:`~fastNLP.io.dataset_loader.JsonLoader` +:class:`~fastNLP.io.loader.JsonLoader` 读取Json类型的数据集文件,数据必须按行存储,每行是一个包含各类属性的Json对象。例子如下: .. code-block:: python - data_set_loader = JsonLoader( + from fastNLP.io.loader import JsonLoader + oader = JsonLoader( fields={'sentence1': 'words1', 'sentence2': 'words2', 'gold_label': 'target'} ) # 表示将Json对象中'sentence1'、'sentence2'和'gold_label'对应的值赋给'words1'、'words2'、'target'这三个fields - data_set = data_set_loader._load('path/to/your/file') + data_set = loader._load('path/to/your/file') 数据集内容样例如下 :: @@ -86,139 +83,68 @@ Part III: 不同数据类型的DataSetLoader {"annotator_labels": ["entailment"], "captionID": "3416050480.jpg#4", "gold_label": "entailment", "pairID": "3416050480.jpg#4r1e", "sentence1": "A person on a horse jumps over a broken down airplane.", "sentence1_binary_parse": "( ( ( A person ) ( on ( a horse ) ) ) ( ( jumps ( over ( a ( broken ( down airplane ) ) ) ) ) . ) )", "sentence1_parse": "(ROOT (S (NP (NP (DT A) (NN person)) (PP (IN on) (NP (DT a) (NN horse)))) (VP (VBZ jumps) (PP (IN over) (NP (DT a) (JJ broken) (JJ down) (NN airplane)))) (. .)))", "sentence2": "A person is outdoors, on a horse.", "sentence2_binary_parse": "( ( A person ) ( ( ( ( is outdoors ) , ) ( on ( a horse ) ) ) . ) )", "sentence2_parse": "(ROOT (S (NP (DT A) (NN person)) (VP (VBZ is) (ADVP (RB outdoors)) (, ,) (PP (IN on) (NP (DT a) (NN horse)))) (. .)))"} ------------------------------------------ -Part IV: DataSetLoader举例 +Part IV: 使用Pipe对数据集进行预处理 ------------------------------------------ -以Matching任务为例子: - - :class:`~fastNLP.io.data_loader.MatchingLoader` - 我们在fastNLP当中封装了一个Matching任务数据集的数据加载类: :class:`~fastNLP.io.data_loader.MatchingLoader` . - - 在MatchingLoader类当中我们封装了一个对数据集中的文本内容进行进一步的预处理的函数: - :meth:`~fastNLP.io.data_loader.MatchingLoader.process` - 这个函数具有各种预处理option,如: - - 是否将文本转成全小写 - - 是否需要序列长度信息,需要什么类型的序列长度信息 - - 是否需要用BertTokenizer来获取序列的WordPiece信息 - - 等等 +在fastNLP中,我们采用 :class:`~fastNLP.io.pipe.Pipe` 来作为加载数据集的基类。 +:class:`~fastNLP.io.pipe.Pipe` 定义了各种Pipe所需的API接口,开发者应该继承它实现各种的Pipe。 +在各种数据集的Pipe当中,至少应该编写如下内容: - 具体内容参见 :meth:`fastNLP.io.MatchingLoader.process` 。 + - process 函数:对输入的 :class:`~fastNLP.io.data_bundle.DataBundle` 进行处理(如构建词表、 + 将dataset的文本内容转成index等等),然后返回该 :class:`~fastNLP.io.data_bundle.DataBundle` + - process_from_file 函数:输入数据集所在文件夹,读取内容并组装成 :class:`~fastNLP.io.data_bundle.DataBundle` , + 然后调用相对应的process函数对数据进行预处理 - :class:`~fastNLP.io.data_loader.SNLILoader` - 一个关于SNLI数据集的DataSetLoader。SNLI数据集来自 - `SNLI Data Set `_ . +以SNLI数据集为例,写一个自定义Pipe的例子如下: - 在 :class:`~fastNLP.io.data_loader.SNLILoader` 的 :meth:`~fastNLP.io.data_loader.SNLILoader._load` - 函数中,我们用以下代码将数据集内容从文本文件读入内存: +.. code-block:: python - .. code-block:: python + from fastNLP.io.loader import SNLILoader + from fastNLP.io.pipe import MatchingPipe - data = SNLILoader().process( - paths='path/to/snli/data', to_lower=False, seq_len_type='seq_len', - get_index=True, concat=False, - ) - print(data) + class MySNLIPipe(MatchingPipe): - 输出的内容是:: + def process(self, data_bundle): + data_bundle = super(MySNLIPipe, self).process(data_bundle) + # MatchingPipe类里封装了一个关于matching任务的process函数,可以直接继承使用 + # 如果有需要进行额外的预处理操作可以在这里加入您的代码 + return data_bundle - In total 3 datasets: - train has 549367 instances. - dev has 9842 instances. - test has 9824 instances. - In total 2 vocabs: - words has 43154 entries. - target has 3 entries. + def process_from_file(self, paths=None): + data_bundle = SNLILoader().load(paths) # 使用SNLILoader读取原始数据集 + # SNLILoader的load函数中,paths如果为None则会自动下载 + return self.process(data_bundle) # 调用相对应的process函数对data_bundle进行处理 +调用Pipe示例: - 这里的data是一个 :class:`~fastNLP.io.base_loader.DataBundle` ,取 ``datasets`` 字典里的内容即可直接传入 - :class:`~fastNLP.Trainer` 或者 :class:`~fastNLP.Tester` 进行训练或者测试。 +.. code-block:: python - :class:`~fastNLP.io.data_loader.IMDBLoader` - 以IMDB数据集为例,在 :class:`~fastNLP.io.data_loader.IMDBLoader` 的 :meth:`~fastNLP.io.data_loader.IMDBLoader._load` - 函数中,我们用以下代码将数据集内容从文本文件读入内存: + from fastNLP.io.pipe import SNLIBertPipe + data_bundle = SNLIBertPipe(lower=True, tokenizer=arg.tokenizer).process_from_file() + print(data_bundle) - .. code-block:: python +输出的内容是:: - data = IMDBLoader().process( - paths={'train': 'path/to/train/file', 'test': 'path/to/test/file'} - ) - print(data) + In total 3 datasets: + train has 549367 instances. + dev has 9842 instances. + test has 9824 instances. + In total 2 vocabs: + words has 34184 entries. + target has 3 entries. - 输出的内容是:: - - In total 3 datasets: - train has 22500 instances. - test has 25000 instances. - dev has 2500 instances. - In total 2 vocabs: - words has 82846 entries. - target has 2 entries. - - - 这里的将原来的train集按9:1的比例分成了训练集和验证集。 +这里表示一共有3个数据集和2个词表。其中: + - 3个数据集分别为train、dev、test数据集,分别有549367、9842、9824个instance + - 2个词表分别为words词表与target词表。其中words词表为句子文本所构建的词表,一共有34184个单词; + target词表为目标标签所构建的词表,一共有3种标签。(注:如果有多个输入,则句子文本所构建的词表将 + 会被命名为words1以对应相对应的列名) ------------------------------------------ -Part V: fastNLP封装好的数据集加载器 +Part V: fastNLP封装好的Loader和Pipe ------------------------------------------ -fastNLP封装好的数据集加载器可以适用于多种类型的任务: - - - `文本分类任务`_ - - `序列标注任务`_ - - `Matching任务`_ - - -文本分类任务 -------------------- - -========================== ================================================================== -数据集名称 数据集加载器 --------------------------- ------------------------------------------------------------------ -IMDb :class:`~fastNLP.io.data_loader.IMDBLoader` --------------------------- ------------------------------------------------------------------ -SST :class:`~fastNLP.io.data_loader.SSTLoader` --------------------------- ------------------------------------------------------------------ -SST-2 :class:`~fastNLP.io.data_loader.SST2Loader` --------------------------- ------------------------------------------------------------------ -Yelp Polarity :class:`~fastNLP.io.data_loader.YelpLoader` --------------------------- ------------------------------------------------------------------ -Yelp Full :class:`~fastNLP.io.data_loader.YelpLoader` --------------------------- ------------------------------------------------------------------ -MTL16 :class:`~fastNLP.io.data_loader.MTL16Loader` -========================== ================================================================== - - - -序列标注任务 -------------------- - -========================== ================================================================== -数据集名称 数据集加载器 --------------------------- ------------------------------------------------------------------ -Conll :class:`~fastNLP.io.data_loader.ConllLoader` --------------------------- ------------------------------------------------------------------ -Conll2003 :class:`~fastNLP.io.data_loader.Conll2003Loader` --------------------------- ------------------------------------------------------------------ -人民日报数据集 :class:`~fastNLP.io.data_loader.PeopleDailyCorpusLoader` -========================== ================================================================== - - - -Matching任务 -------------------- - -========================== ================================================================== -数据集名称 数据集加载器 --------------------------- ------------------------------------------------------------------ -SNLI :class:`~fastNLP.io.data_loader.SNLILoader` --------------------------- ------------------------------------------------------------------ -MultiNLI :class:`~fastNLP.io.data_loader.MNLILoader` --------------------------- ------------------------------------------------------------------ -QNLI :class:`~fastNLP.io.data_loader.QNLILoader` --------------------------- ------------------------------------------------------------------ -RTE :class:`~fastNLP.io.data_loader.RTELoader` --------------------------- ------------------------------------------------------------------ -Quora Pair Dataset :class:`~fastNLP.io.data_loader.QuoraLoader` -========================== ================================================================== +fastNLP封装了多种任务/数据集的Loader和Pipe并提供自动下载功能,具体参见文档 + +`fastNLP可加载的embedding与数据集 `_ diff --git a/docs/source/tutorials/tutorial_3_embedding.rst b/docs/source/tutorials/tutorial_3_embedding.rst index 489b43b4..07dc30bc 100644 --- a/docs/source/tutorials/tutorial_3_embedding.rst +++ b/docs/source/tutorials/tutorial_3_embedding.rst @@ -12,6 +12,7 @@ - `Part IV: 使用预训练的Contextual Embedding(ELMo & BERT)`_ - `Part V: 使用character-level的embedding`_ - `Part VI: 叠加使用多个embedding`_ + - `Part VII: fastNLP支持的预训练Embedding`_ @@ -35,12 +36,14 @@ Part II: 使用随机初始化的embedding .. code-block:: python + from fastNLP import Embedding embed = Embedding(10000, 50) 也可以传入一个初始化的参数矩阵: .. code-block:: python + from fastNLP import Embedding embed = Embedding(init_embed) 其中的init_embed可以是torch.FloatTensor、torch.nn.Embedding或者numpy.ndarray。 @@ -59,6 +62,7 @@ Embedding,例子如下: .. code-block:: python + from fastNLP import StaticEmbedding embed = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50', requires_grad=True) vocab为根据数据集构建的词表,model_dir_or_name可以是一个路径,也可以是embedding模型的名称: @@ -67,34 +71,13 @@ vocab为根据数据集构建的词表,model_dir_or_name可以是一个路径 和word2vec类型的权重文件都支持) 2 如果传入的是模型名称,那么fastNLP将会根据名称查找embedding模型,如果在cache目录下找到模型则会 - 自动加载;如果找不到则会自动下载。可以通过环境变量 ``FASTNLP_CACHE_DIR`` 来自定义cache目录,如:: + 自动加载;如果找不到则会自动下载到cache目录。默认的cache目录为 `~/.fastNLP` 文件夹。可以通过环境 + 变量 ``FASTNLP_CACHE_DIR`` 来自定义cache目录,如:: $ FASTNLP_CACHE_DIR=~/fastnlp_cache_dir python your_python_file.py 这个命令表示fastNLP将会在 `~/fastnlp_cache_dir` 这个目录下寻找模型,找不到则会自动将模型下载到这个目录 -目前支持的静态embedding模型有: - - ========================== ================================ - 模型名称 模型 - -------------------------- -------------------------------- - en glove.840B.300d - -------------------------- -------------------------------- - en-glove-840d-300 glove.840B.300d - -------------------------- -------------------------------- - en-glove-6b-50 glove.6B.50d - -------------------------- -------------------------------- - en-word2vec-300 谷歌word2vec 300维 - -------------------------- -------------------------------- - en-fasttext 英文fasttext 300维 - -------------------------- -------------------------------- - cn 腾讯中文词向量 200维 - -------------------------- -------------------------------- - cn-fasttext 中文fasttext 300维 - ========================== ================================ - - - ----------------------------------------------------------- Part IV: 使用预训练的Contextual Embedding(ELMo & BERT) ----------------------------------------------------------- @@ -106,62 +89,20 @@ Part IV: 使用预训练的Contextual Embedding(ELMo & BERT) .. code-block:: python + from fastNLP import ElmoEmbedding embed = ElmoEmbedding(vocab, model_dir_or_name='small', requires_grad=False) -目前支持的ElmoEmbedding模型有: - - ========================== ================================ - 模型名称 模型 - -------------------------- -------------------------------- - small allennlp ELMo的small - -------------------------- -------------------------------- - medium allennlp ELMo的medium - -------------------------- -------------------------------- - original allennlp ELMo的original - -------------------------- -------------------------------- - 5.5b-original allennlp ELMo的5.5B original - ========================== ================================ - BERT-embedding的使用方法如下: .. code-block:: python + from fastNLP import BertEmbedding embed = BertEmbedding( vocab, model_dir_or_name='en-base-cased', requires_grad=False, layers='4,-2,-1' ) 其中layers变量表示需要取哪几层的encode结果。 -目前支持的BertEmbedding模型有: - - ========================== ==================================== - 模型名称 模型 - -------------------------- ------------------------------------ - en bert-base-cased - -------------------------- ------------------------------------ - en-base-uncased bert-base-uncased - -------------------------- ------------------------------------ - en-base-cased bert-base-cased - -------------------------- ------------------------------------ - en-large-uncased bert-large-uncased - -------------------------- ------------------------------------ - en-large-cased bert-large-cased - -------------------------- ------------------------------------ - -------------------------- ------------------------------------ - en-large-cased-wwm bert-large-cased-whole-word-mask - -------------------------- ------------------------------------ - en-large-uncased-wwm bert-large-uncased-whole-word-mask - -------------------------- ------------------------------------ - en-base-cased-mrpc bert-base-cased-finetuned-mrpc - -------------------------- ------------------------------------ - -------------------------- ------------------------------------ - multilingual bert-base-multilingual-cased - -------------------------- ------------------------------------ - multilingual-base-uncased bert-base-multilingual-uncased - -------------------------- ------------------------------------ - multilingual-base-cased bert-base-multilingual-cased - ========================== ==================================== - ----------------------------------------------------- Part V: 使用character-level的embedding ----------------------------------------------------- @@ -173,6 +114,7 @@ CNNCharEmbedding的使用例子如下: .. code-block:: python + from fastNLP import CNNCharEmbedding embed = CNNCharEmbedding(vocab, embed_size=100, char_emb_size=50) 这表示这个CNNCharEmbedding当中character的embedding维度大小为50,返回的embedding结果维度大小为100。 @@ -181,12 +123,12 @@ CNNCharEmbedding的使用例子如下: .. code-block:: python + from fastNLP import LSTMCharEmbedding embed = LSTMCharEmbedding(vocab, embed_size=100, char_emb_size=50) 这表示这个LSTMCharEmbedding当中character的embedding维度大小为50,返回的embedding结果维度大小为100。 - ----------------------------------------------------- Part VI: 叠加使用多个embedding ----------------------------------------------------- @@ -197,6 +139,7 @@ Part VI: 叠加使用多个embedding .. code-block:: python + from fastNLP import StaticEmbedding, StackEmbedding embed_1 = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50', requires_grad=True) embed_2 = StaticEmbedding(vocab, model_dir_or_name='en-word2vec-300', requires_grad=True) @@ -208,7 +151,17 @@ StackEmbedding会把多个embedding的结果拼接起来,如上面例子的sta .. code-block:: python + from fastNLP import StaticEmbedding, StackEmbedding, ElmoEmbedding elmo_embedding = ElmoEmbedding(vocab, model_dir_or_name='medium', layers='0,1,2', requires_grad=False) glove_embedding = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50', requires_grad=True) stack_embed = StackEmbedding([elmo_embedding, glove_embedding]) + +------------------------------------------ +Part VII: fastNLP支持的预训练Embedding +------------------------------------------ + +fastNLP支持多种预训练Embedding并提供自动下载功能,具体参见文档 + +`fastNLP可加载的embedding与数据集 `_ + diff --git a/docs/source/user/tutorials.rst b/docs/source/user/tutorials.rst index 196f9c29..3e9e1b54 100644 --- a/docs/source/user/tutorials.rst +++ b/docs/source/user/tutorials.rst @@ -8,7 +8,7 @@ fastNLP 详细使用教程 :maxdepth: 1 使用DataSet预处理文本 - 使用DataSetLoader加载数据集 + 使用Loader和Pipe加载并处理数据集 使用Embedding模块将文本转成向量 动手实现一个文本分类器I-使用Trainer和Tester快速训练和测试 动手实现一个文本分类器II-使用DataSetIter实现自定义训练过程 diff --git a/fastNLP/io/__init__.py b/fastNLP/io/__init__.py index a3ea0148..8ed1956a 100644 --- a/fastNLP/io/__init__.py +++ b/fastNLP/io/__init__.py @@ -38,7 +38,6 @@ __all__ = [ 'JsonLoader', 'CWSLoader', - "CWSPipe", 'MNLILoader', "QuoraLoader", @@ -46,6 +45,8 @@ __all__ = [ "QNLILoader", "RTELoader", + "Pipe", + "YelpFullPipe", "YelpPolarityPipe", "SSTPipe", @@ -59,6 +60,8 @@ __all__ = [ "PeopleDailyPipe", "WeiboNERPipe", + "CWSPipe", + "MatchingBertPipe", "RTEBertPipe", "SNLIBertPipe", diff --git a/fastNLP/io/file_utils.py b/fastNLP/io/file_utils.py index 9dbb515d..bd02158e 100644 --- a/fastNLP/io/file_utils.py +++ b/fastNLP/io/file_utils.py @@ -59,7 +59,7 @@ PRETRAIN_STATIC_FILES = { 'en-fasttext-crawl': "crawl-300d-2M.vec.zip", 'cn': "tencent_cn.zip", - 'cn-tencent': "tencent_cn.txt.zip", + 'cn-tencent': "tencent_cn.zip", 'cn-fasttext': "cc.zh.300.vec.gz", 'cn-sgns-literature-word': 'sgns.literature.word.txt.zip', } diff --git a/fastNLP/io/loader/__init__.py b/fastNLP/io/loader/__init__.py index 820c33be..6c23f213 100644 --- a/fastNLP/io/loader/__init__.py +++ b/fastNLP/io/loader/__init__.py @@ -62,8 +62,8 @@ __all__ = [ "PeopleDailyNERLoader", "WeiboNERLoader", - # 'CSVLoader', - # 'JsonLoader', + 'CSVLoader', + 'JsonLoader', 'CWSLoader', diff --git a/fastNLP/io/loader/classification.py b/fastNLP/io/loader/classification.py index 67e19773..f64a26e7 100644 --- a/fastNLP/io/loader/classification.py +++ b/fastNLP/io/loader/classification.py @@ -5,7 +5,6 @@ import warnings import os import random import shutil -import numpy as np import glob import time diff --git a/fastNLP/io/loader/conll.py b/fastNLP/io/loader/conll.py index 5dc4c6d7..b5241cff 100644 --- a/fastNLP/io/loader/conll.py +++ b/fastNLP/io/loader/conll.py @@ -11,9 +11,10 @@ import shutil import time import random + class ConllLoader(Loader): """ - 别名::class:`fastNLP.io.ConllLoader` :class:`fastNLP.io.data_loader.ConllLoader` + 别名::class:`fastNLP.io.ConllLoader` :class:`fastNLP.io.loader.ConllLoader` ConllLoader支持读取的数据格式: 以空行隔开两个sample,除了分割行,每一行用空格或者制表符隔开不同的元素。如下例所示: diff --git a/fastNLP/io/loader/csv.py b/fastNLP/io/loader/csv.py index 166f912b..5195cc8e 100644 --- a/fastNLP/io/loader/csv.py +++ b/fastNLP/io/loader/csv.py @@ -6,7 +6,7 @@ from .loader import Loader class CSVLoader(Loader): """ - 别名::class:`fastNLP.io.CSVLoader` :class:`fastNLP.io.dataset_loader.CSVLoader` + 别名::class:`fastNLP.io.CSVLoader` :class:`fastNLP.io.loader.CSVLoader` 读取CSV格式的数据集, 返回 ``DataSet`` 。 diff --git a/fastNLP/io/pipe/matching.py b/fastNLP/io/pipe/matching.py index 0d1b4e82..ffa6375b 100644 --- a/fastNLP/io/pipe/matching.py +++ b/fastNLP/io/pipe/matching.py @@ -181,8 +181,8 @@ class MatchingPipe(Pipe): "This site includes a...", "The Government Executive...", "not_entailment" "...", "..." - :param data_bundle: - :return: + :param data_bundle: 通过loader读取得到的data_bundle,里面包含了数据集的原始数据内容 + :return: data_bundle """ data_bundle = self._tokenize(data_bundle, [Const.RAW_WORDS(0), Const.RAW_WORDS(1)], [Const.INPUTS(0), Const.INPUTS(1)]) diff --git a/fastNLP/io/pipe/pipe.py b/fastNLP/io/pipe/pipe.py index a2b74301..cc45dee4 100644 --- a/fastNLP/io/pipe/pipe.py +++ b/fastNLP/io/pipe/pipe.py @@ -2,6 +2,9 @@ from .. import DataBundle class Pipe: + """ + 别名::class:`fastNLP.io.Pipe` :class:`fastNLP.io.pipe.Pipe` + """ def process(self, data_bundle: DataBundle) -> DataBundle: """ 对输入的DataBundle进行处理,然后返回该DataBundle。 From 9e16791c538b856184efd4095ab0faed5ff4d2ce Mon Sep 17 00:00:00 2001 From: ChenXin Date: Sun, 25 Aug 2019 17:08:19 +0800 Subject: [PATCH 131/139] fix some importing bugs --- fastNLP/io/pipe/cws.py | 84 ++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 35 deletions(-) diff --git a/fastNLP/io/pipe/cws.py b/fastNLP/io/pipe/cws.py index 6ea1ae0c..4ca0219c 100644 --- a/fastNLP/io/pipe/cws.py +++ b/fastNLP/io/pipe/cws.py @@ -1,10 +1,13 @@ +import re +from itertools import chain + from .pipe import Pipe +from .utils import _indexize from .. import DataBundle from ..loader import CWSLoader -from ... import Const -from itertools import chain -from .utils import _indexize -import re +from ...core.const import Const + + def _word_lens_to_bmes(word_lens): """ @@ -13,11 +16,11 @@ def _word_lens_to_bmes(word_lens): """ tags = [] for word_len in word_lens: - if word_len==1: + if word_len == 1: tags.append('S') else: tags.append('B') - tags.extend(['M']*(word_len-2)) + tags.extend(['M'] * (word_len - 2)) tags.append('E') return tags @@ -30,10 +33,10 @@ def _word_lens_to_segapp(word_lens): """ tags = [] for word_len in word_lens: - if word_len==1: + if word_len == 1: tags.append('SEG') else: - tags.extend(['APP']*(word_len-1)) + tags.extend(['APP'] * (word_len - 1)) tags.append('SEG') return tags @@ -97,13 +100,21 @@ def _digit_span_to_special_tag(span): else: return '' + def _find_and_replace_digit_spans(line): - # only consider words start with number, contains '.', characters. - # If ends with space, will be processed - # If ends with Chinese character, will be processed - # If ends with or contains english char, not handled. - # floats are replaced by - # otherwise unkdgt + """ + only consider words start with number, contains '.', characters. + + If ends with space, will be processed + + If ends with Chinese character, will be processed + + If ends with or contains english char, not handled. + + floats are replaced by + + otherwise unkdgt + """ new_line = '' pattern = '\d[\d\\.﹒·]*(?=[\u4e00-\u9fff ,%,。!<-“])' prev_end = 0 @@ -136,17 +147,18 @@ class CWSPipe(Pipe): :param bool bigrams: 是否增加一列bigram. bigram的构成是['复', '旦', '大', '学', ...]->["复旦", "旦大", ...] :param bool trigrams: 是否增加一列trigram. trigram的构成是 ['复', '旦', '大', '学', ...]->["复旦大", "旦大学", ...] """ + def __init__(self, dataset_name=None, encoding_type='bmes', replace_num_alpha=True, bigrams=False, trigrams=False): - if encoding_type=='bmes': + if encoding_type == 'bmes': self.word_lens_to_tags = _word_lens_to_bmes else: self.word_lens_to_tags = _word_lens_to_segapp - + self.dataset_name = dataset_name self.bigrams = bigrams self.trigrams = trigrams self.replace_num_alpha = replace_num_alpha - + def _tokenize(self, data_bundle): """ 将data_bundle中的'chars'列切分成一个一个的word. @@ -162,10 +174,10 @@ class CWSPipe(Pipe): char = [] subchar = [] for c in word: - if c=='<': + if c == '<': subchar.append(c) continue - if c=='>' and subchar[0]=='<': + if c == '>' and subchar[0] == '<': char.append(''.join(subchar)) subchar = [] if subchar: @@ -175,12 +187,12 @@ class CWSPipe(Pipe): char.extend(subchar) chars.append(char) return chars - + for name, dataset in data_bundle.datasets.items(): dataset.apply_field(split_word_into_chars, field_name=Const.CHAR_INPUT, new_field_name=Const.CHAR_INPUT) return data_bundle - + def process(self, data_bundle: DataBundle) -> DataBundle: """ 可以处理的DataSet需要包含raw_words列 @@ -196,42 +208,43 @@ class CWSPipe(Pipe): :return: """ data_bundle.copy_field(Const.RAW_WORD, Const.CHAR_INPUT) - + if self.replace_num_alpha: data_bundle.apply_field(_find_and_replace_alpha_spans, Const.CHAR_INPUT, Const.CHAR_INPUT) data_bundle.apply_field(_find_and_replace_digit_spans, Const.CHAR_INPUT, Const.CHAR_INPUT) - + self._tokenize(data_bundle) - + for name, dataset in data_bundle.datasets.items(): - dataset.apply_field(lambda chars:self.word_lens_to_tags(map(len, chars)), field_name=Const.CHAR_INPUT, + dataset.apply_field(lambda chars: self.word_lens_to_tags(map(len, chars)), field_name=Const.CHAR_INPUT, new_field_name=Const.TARGET) - dataset.apply_field(lambda chars:list(chain(*chars)), field_name=Const.CHAR_INPUT, + dataset.apply_field(lambda chars: list(chain(*chars)), field_name=Const.CHAR_INPUT, new_field_name=Const.CHAR_INPUT) input_field_names = [Const.CHAR_INPUT] if self.bigrams: for name, dataset in data_bundle.datasets.items(): - dataset.apply_field(lambda chars: [c1+c2 for c1, c2 in zip(chars, chars[1:]+[''])], + dataset.apply_field(lambda chars: [c1 + c2 for c1, c2 in zip(chars, chars[1:] + [''])], field_name=Const.CHAR_INPUT, new_field_name='bigrams') input_field_names.append('bigrams') if self.trigrams: for name, dataset in data_bundle.datasets.items(): - dataset.apply_field(lambda chars: [c1+c2+c3 for c1, c2, c3 in zip(chars, chars[1:]+[''], chars[2:]+['']*2)], + dataset.apply_field(lambda chars: [c1 + c2 + c3 for c1, c2, c3 in + zip(chars, chars[1:] + [''], chars[2:] + [''] * 2)], field_name=Const.CHAR_INPUT, new_field_name='trigrams') input_field_names.append('trigrams') - + _indexize(data_bundle, input_field_names, Const.TARGET) - + input_fields = [Const.TARGET, Const.INPUT_LEN] + input_field_names target_fields = [Const.TARGET, Const.INPUT_LEN] for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.CHAR_INPUT) - + data_bundle.set_input(*input_fields) data_bundle.set_target(*target_fields) - + return data_bundle - + def process_from_file(self, paths=None) -> DataBundle: """ @@ -239,8 +252,9 @@ class CWSPipe(Pipe): :return: """ if self.dataset_name is None and paths is None: - raise RuntimeError("You have to set `paths` when calling process_from_file() or `dataset_name `when initialization.") + raise RuntimeError( + "You have to set `paths` when calling process_from_file() or `dataset_name `when initialization.") if self.dataset_name is not None and paths is not None: raise RuntimeError("You cannot specify `paths` and `dataset_name` simultaneously") data_bundle = CWSLoader(self.dataset_name).load(paths) - return self.process(data_bundle) \ No newline at end of file + return self.process(data_bundle) From 34e17e97935f69aef54a9d75694713f0823c41fe Mon Sep 17 00:00:00 2001 From: ChenXin Date: Mon, 26 Aug 2019 10:07:52 +0800 Subject: [PATCH 132/139] update the fastNLP.__init__ : use loader&pipe to replace data_loader --- fastNLP/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fastNLP/__init__.py b/fastNLP/__init__.py index 2720f292..19efac31 100644 --- a/fastNLP/__init__.py +++ b/fastNLP/__init__.py @@ -65,8 +65,8 @@ __all__ = [ ] __version__ = '0.4.5' -from .core import * +from . import embeddings from . import models from . import modules -from . import embeddings -from .io import data_loader +from .core import * +from .io import loader, pipe From 9535ec60b65e7a2bc70394f444b5067bcb161ad9 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Mon, 26 Aug 2019 10:17:43 +0800 Subject: [PATCH 133/139] update the doc system: use customized tool to generate the rst files --- docs/count.py | 133 +++++++----------- docs/source/conf.py | 8 +- docs/source/fastNLP.core.batch.rst | 6 +- docs/source/fastNLP.core.callback.rst | 6 +- docs/source/fastNLP.core.const.rst | 6 +- docs/source/fastNLP.core.dataset.rst | 6 +- docs/source/fastNLP.core.field.rst | 6 +- docs/source/fastNLP.core.instance.rst | 6 +- docs/source/fastNLP.core.losses.rst | 6 +- docs/source/fastNLP.core.metrics.rst | 6 +- docs/source/fastNLP.core.optimizer.rst | 6 +- docs/source/fastNLP.core.rst | 9 +- docs/source/fastNLP.core.sampler.rst | 6 +- docs/source/fastNLP.core.tester.rst | 6 +- docs/source/fastNLP.core.trainer.rst | 6 +- docs/source/fastNLP.core.utils.rst | 6 +- docs/source/fastNLP.core.vocabulary.rst | 6 +- .../fastNLP.embeddings.bert_embedding.rst | 10 +- .../fastNLP.embeddings.char_embedding.rst | 10 +- ...astNLP.embeddings.contextual_embedding.rst | 7 + .../fastNLP.embeddings.elmo_embedding.rst | 10 +- docs/source/fastNLP.embeddings.embedding.rst | 6 +- docs/source/fastNLP.embeddings.rst | 10 +- .../fastNLP.embeddings.stack_embedding.rst | 10 +- .../fastNLP.embeddings.static_embedding.rst | 10 +- docs/source/fastNLP.embeddings.utils.rst | 6 +- docs/source/fastNLP.io.data_bundle.rst | 10 +- docs/source/fastNLP.io.data_loader.rst | 8 -- docs/source/fastNLP.io.dataset_loader.rst | 9 +- docs/source/fastNLP.io.embed_loader.rst | 10 +- docs/source/fastNLP.io.file_utils.rst | 10 +- docs/source/fastNLP.io.loader.rst | 5 +- docs/source/fastNLP.io.model_io.rst | 10 +- docs/source/fastNLP.io.pipe.rst | 5 +- docs/source/fastNLP.io.rst | 21 +-- docs/source/fastNLP.io.utils.rst | 6 +- .../source/fastNLP.models.biaffine_parser.rst | 10 +- ...fastNLP.models.cnn_text_classification.rst | 10 +- docs/source/fastNLP.models.rst | 9 +- .../fastNLP.models.sequence_labeling.rst | 10 +- docs/source/fastNLP.models.snli.rst | 6 +- .../fastNLP.models.star_transformer.rst | 10 +- docs/source/fastNLP.modules.decoder.rst | 5 +- docs/source/fastNLP.modules.encoder.rst | 5 +- docs/source/fastNLP.modules.rst | 15 +- docs/source/fastNLP.modules.utils.rst | 6 +- docs/source/fastNLP.rst | 9 +- 47 files changed, 223 insertions(+), 279 deletions(-) create mode 100644 docs/source/fastNLP.embeddings.contextual_embedding.rst delete mode 100644 docs/source/fastNLP.io.data_loader.rst diff --git a/docs/count.py b/docs/count.py index d906f4c0..e1aad115 100644 --- a/docs/count.py +++ b/docs/count.py @@ -1,98 +1,65 @@ import os +import sys -def find_all(path='../fastNLP'): - head_list = [] - alias_list = [] - for path, dirs, files in os.walk(path): +def find_all_modules(): + modules = {} + children = {} + to_doc = set() + root = '../fastNLP' + for path, dirs, files in os.walk(root): for file in files: if file.endswith('.py'): name = ".".join(path.split('/')[1:]) if file.split('.')[0] != "__init__": name = name + '.' + file.split('.')[0] - if len(name.split('.')) < 3 or name.startswith('fastNLP.core'): - heads, alias = find_one(path + '/' + file) - for h in heads: - head_list.append(name + "." + h) - for a in alias: - alias_list.append(a) - heads = {} - for h in head_list: - end = h.split('.')[-1] - file = h[:-len(end) - 1] - if end not in heads: - heads[end] = set() - heads[end].add(file) - alias = {} - for a in alias_list: - for each in a: - end = each.split('.')[-1] - file = each[:-len(end) - 1] - if end not in alias: - alias[end] = set() - alias[end].add(file) - print("IN alias NOT IN heads") - for item in alias: - if item not in heads: - print(item, alias[item]) - elif len(heads[item]) != 2: - print(item, alias[item], heads[item]) - - print("\n\nIN heads NOT IN alias") - for item in heads: - if item not in alias: - print(item, heads[item]) + __import__(name) + m = sys.modules[name] + modules[name] = m + try: + m.__all__ + except: + print(name, "__all__ missing") + continue + if m.__doc__ is None: + print(name, "__doc__ missing") + continue + if "undocumented" not in m.__doc__: + to_doc.add(name) + for module in to_doc: + t = ".".join(module.split('.')[:-1]) + if t in to_doc: + if t not in children: + children[t] = set() + children[t].add(module) + for m in children: + children[m] = sorted(children[m]) + return modules, to_doc, children -def find_class(path): - with open(path, 'r') as fin: - lines = fin.readlines() - pars = {} - for i, line in enumerate(lines): - if line.strip().startswith('class'): - line = line.strip()[len('class'):-1].strip() - if line[-1] == ')': - line = line[:-1].split('(') - name = line[0].strip() - parents = line[1].split(',') - for i in range(len(parents)): - parents[i] = parents[i].strip() - if len(parents) == 1: - pars[name] = parents[0] - else: - pars[name] = tuple(parents) - return pars +def create_rst_file(modules, name, children): + m = modules[name] + with open("./source/" + name + ".rst", "w") as fout: + t = "=" * len(name) + fout.write(name + "\n") + fout.write(t + "\n") + fout.write("\n") + fout.write(".. automodule:: " + name + "\n") + if len(m.__all__) > 0: + fout.write(" :members: " + ", ".join(m.__all__) + "\n") + fout.write(" :inherited-members:\n") + fout.write("\n") + if name in children: + fout.write("子模块\n------\n\n.. toctree::\n\n") + for module in children[name]: + fout.write(" " + module + "\n") -def find_one(path): - head_list = [] - alias = [] - with open(path, 'r') as fin: - lines = fin.readlines() - flag = False - for i, line in enumerate(lines): - if line.strip().startswith('__all__'): - line = line.strip()[len('__all__'):].strip() - if line[-1] == ']': - line = line[1:-1].strip()[1:].strip() - head_list.append(line.strip("\"").strip("\'").strip()) - else: - flag = True - elif line.strip() == ']': - flag = False - elif flag: - line = line.strip()[:-1].strip("\"").strip("\'").strip() - if len(line) == 0 or line[0] == '#': - continue - head_list.append(line) - if line.startswith('def') or line.startswith('class'): - if lines[i + 2].strip().startswith("别名:"): - names = lines[i + 2].strip()[len("别名:"):].split() - names[0] = names[0][len(":class:`"):-1] - names[1] = names[1][len(":class:`"):-1] - alias.append((names[0], names[1])) - return head_list, alias +def main(): + modules, to_doc, children = find_all_modules() + for name in to_doc: + create_rst_file(modules, name, children) if __name__ == "__main__": - find_all() # use to check __all__ + main() diff --git a/docs/source/conf.py b/docs/source/conf.py index 2e10bc89..83cb7185 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,12 +48,14 @@ extensions = [ autodoc_default_options = { 'member-order': 'bysource', 'special-members': '__init__', - 'undoc-members': True, + 'undoc-members': False, } +autoclass_content = "class" + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] - +# template_bridge # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # @@ -113,7 +115,7 @@ html_static_path = ['_static'] # -- Options for HTMLHelp output --------------------------------------------- # Output file base name for HTML help builder. -htmlhelp_basename = 'fastNLPdoc' +htmlhelp_basename = 'fastNLP doc' # -- Options for LaTeX output ------------------------------------------------ diff --git a/docs/source/fastNLP.core.batch.rst b/docs/source/fastNLP.core.batch.rst index 03008b52..50ad6fed 100644 --- a/docs/source/fastNLP.core.batch.rst +++ b/docs/source/fastNLP.core.batch.rst @@ -2,6 +2,6 @@ fastNLP.core.batch ================== .. automodule:: fastNLP.core.batch - :members: - :undoc-members: - :show-inheritance: + :members: BatchIter, DataSetIter, TorchLoaderIter + :inherited-members: + diff --git a/docs/source/fastNLP.core.callback.rst b/docs/source/fastNLP.core.callback.rst index 74a7825d..d37ddb11 100644 --- a/docs/source/fastNLP.core.callback.rst +++ b/docs/source/fastNLP.core.callback.rst @@ -2,6 +2,6 @@ fastNLP.core.callback ===================== .. automodule:: fastNLP.core.callback - :members: - :undoc-members: - :show-inheritance: + :members: Callback, GradientClipCallback, EarlyStopCallback, FitlogCallback, EvaluateCallback, LRScheduler, ControlC, LRFinder, TensorboardCallback, WarmupCallback, SaveModelCallback, EchoCallback, TesterCallback, CallbackException, EarlyStopError + :inherited-members: + diff --git a/docs/source/fastNLP.core.const.rst b/docs/source/fastNLP.core.const.rst index 330a8883..82a1992e 100644 --- a/docs/source/fastNLP.core.const.rst +++ b/docs/source/fastNLP.core.const.rst @@ -2,6 +2,6 @@ fastNLP.core.const ================== .. automodule:: fastNLP.core.const - :members: - :undoc-members: - :show-inheritance: + :members: Const + :inherited-members: + diff --git a/docs/source/fastNLP.core.dataset.rst b/docs/source/fastNLP.core.dataset.rst index 1ad94bb6..e13d7f1c 100644 --- a/docs/source/fastNLP.core.dataset.rst +++ b/docs/source/fastNLP.core.dataset.rst @@ -2,6 +2,6 @@ fastNLP.core.dataset ==================== .. automodule:: fastNLP.core.dataset - :members: - :undoc-members: - :show-inheritance: + :members: DataSet + :inherited-members: + diff --git a/docs/source/fastNLP.core.field.rst b/docs/source/fastNLP.core.field.rst index 7fc099c9..73dad8af 100644 --- a/docs/source/fastNLP.core.field.rst +++ b/docs/source/fastNLP.core.field.rst @@ -2,6 +2,6 @@ fastNLP.core.field ================== .. automodule:: fastNLP.core.field - :members: - :undoc-members: - :show-inheritance: + :members: Padder, AutoPadder, EngChar2DPadder + :inherited-members: + diff --git a/docs/source/fastNLP.core.instance.rst b/docs/source/fastNLP.core.instance.rst index 6e496ac1..010567b9 100644 --- a/docs/source/fastNLP.core.instance.rst +++ b/docs/source/fastNLP.core.instance.rst @@ -2,6 +2,6 @@ fastNLP.core.instance ===================== .. automodule:: fastNLP.core.instance - :members: - :undoc-members: - :show-inheritance: + :members: Instance + :inherited-members: + diff --git a/docs/source/fastNLP.core.losses.rst b/docs/source/fastNLP.core.losses.rst index 8e63dfa1..daf246f8 100644 --- a/docs/source/fastNLP.core.losses.rst +++ b/docs/source/fastNLP.core.losses.rst @@ -2,6 +2,6 @@ fastNLP.core.losses =================== .. automodule:: fastNLP.core.losses - :members: - :undoc-members: - :show-inheritance: + :members: LossBase, LossFunc, LossInForward, CrossEntropyLoss, BCELoss, L1Loss, NLLLoss + :inherited-members: + diff --git a/docs/source/fastNLP.core.metrics.rst b/docs/source/fastNLP.core.metrics.rst index d3b87bb8..96748a78 100644 --- a/docs/source/fastNLP.core.metrics.rst +++ b/docs/source/fastNLP.core.metrics.rst @@ -2,6 +2,6 @@ fastNLP.core.metrics ==================== .. automodule:: fastNLP.core.metrics - :members: - :undoc-members: - :show-inheritance: + :members: MetricBase, AccuracyMetric, SpanFPreRecMetric, ExtractiveQAMetric + :inherited-members: + diff --git a/docs/source/fastNLP.core.optimizer.rst b/docs/source/fastNLP.core.optimizer.rst index c80be53f..44e45c4f 100644 --- a/docs/source/fastNLP.core.optimizer.rst +++ b/docs/source/fastNLP.core.optimizer.rst @@ -2,6 +2,6 @@ fastNLP.core.optimizer ====================== .. automodule:: fastNLP.core.optimizer - :members: - :undoc-members: - :show-inheritance: + :members: Optimizer, SGD, Adam, AdamW + :inherited-members: + diff --git a/docs/source/fastNLP.core.rst b/docs/source/fastNLP.core.rst index 08d161b7..56de46e9 100644 --- a/docs/source/fastNLP.core.rst +++ b/docs/source/fastNLP.core.rst @@ -2,12 +2,11 @@ fastNLP.core ============ .. automodule:: fastNLP.core - :members: - :undoc-members: - :show-inheritance: + :members: DataSet, Instance, FieldArray, Padder, AutoPadder, EngChar2DPadder, Vocabulary, DataSetIter, BatchIter, TorchLoaderIter, Const, Tester, Trainer, cache_results, seq_len_to_mask, get_seq_len, logger, Callback, GradientClipCallback, EarlyStopCallback, FitlogCallback, EvaluateCallback, LRScheduler, ControlC, LRFinder, TensorboardCallback, WarmupCallback, SaveModelCallback, EchoCallback, TesterCallback, CallbackException, EarlyStopError, LossFunc, CrossEntropyLoss, L1Loss, BCELoss, NLLLoss, LossInForward, AccuracyMetric, SpanFPreRecMetric, ExtractiveQAMetric, Optimizer, SGD, Adam, AdamW, SequentialSampler, BucketSampler, RandomSampler, Sampler + :inherited-members: -Submodules ----------- +子模块 +------ .. toctree:: diff --git a/docs/source/fastNLP.core.sampler.rst b/docs/source/fastNLP.core.sampler.rst index 0110f0c0..56291894 100644 --- a/docs/source/fastNLP.core.sampler.rst +++ b/docs/source/fastNLP.core.sampler.rst @@ -2,6 +2,6 @@ fastNLP.core.sampler ==================== .. automodule:: fastNLP.core.sampler - :members: - :undoc-members: - :show-inheritance: + :members: Sampler, BucketSampler, SequentialSampler, RandomSampler + :inherited-members: + diff --git a/docs/source/fastNLP.core.tester.rst b/docs/source/fastNLP.core.tester.rst index 4d71a27b..90ec2a88 100644 --- a/docs/source/fastNLP.core.tester.rst +++ b/docs/source/fastNLP.core.tester.rst @@ -2,6 +2,6 @@ fastNLP.core.tester =================== .. automodule:: fastNLP.core.tester - :members: - :undoc-members: - :show-inheritance: + :members: Tester + :inherited-members: + diff --git a/docs/source/fastNLP.core.trainer.rst b/docs/source/fastNLP.core.trainer.rst index 60bf2d5b..92c08718 100644 --- a/docs/source/fastNLP.core.trainer.rst +++ b/docs/source/fastNLP.core.trainer.rst @@ -2,6 +2,6 @@ fastNLP.core.trainer ==================== .. automodule:: fastNLP.core.trainer - :members: - :undoc-members: - :show-inheritance: + :members: Trainer + :inherited-members: + diff --git a/docs/source/fastNLP.core.utils.rst b/docs/source/fastNLP.core.utils.rst index 3f80b4e8..027a43e9 100644 --- a/docs/source/fastNLP.core.utils.rst +++ b/docs/source/fastNLP.core.utils.rst @@ -2,6 +2,6 @@ fastNLP.core.utils ================== .. automodule:: fastNLP.core.utils - :members: - :undoc-members: - :show-inheritance: + :members: cache_results, seq_len_to_mask, get_seq_len + :inherited-members: + diff --git a/docs/source/fastNLP.core.vocabulary.rst b/docs/source/fastNLP.core.vocabulary.rst index ba9598b9..ac07a8c6 100644 --- a/docs/source/fastNLP.core.vocabulary.rst +++ b/docs/source/fastNLP.core.vocabulary.rst @@ -2,6 +2,6 @@ fastNLP.core.vocabulary ======================= .. automodule:: fastNLP.core.vocabulary - :members: - :undoc-members: - :show-inheritance: + :members: Vocabulary, VocabularyOption + :inherited-members: + diff --git a/docs/source/fastNLP.embeddings.bert_embedding.rst b/docs/source/fastNLP.embeddings.bert_embedding.rst index 24ceff1c..51828cb0 100644 --- a/docs/source/fastNLP.embeddings.bert_embedding.rst +++ b/docs/source/fastNLP.embeddings.bert_embedding.rst @@ -1,7 +1,7 @@ -fastNLP.embeddings.bert\_embedding -================================== +fastNLP.embeddings.bert_embedding +================================= .. automodule:: fastNLP.embeddings.bert_embedding - :members: - :undoc-members: - :show-inheritance: + :members: BertEmbedding, BertWordPieceEncoder + :inherited-members: + diff --git a/docs/source/fastNLP.embeddings.char_embedding.rst b/docs/source/fastNLP.embeddings.char_embedding.rst index 501089d8..a9b129d8 100644 --- a/docs/source/fastNLP.embeddings.char_embedding.rst +++ b/docs/source/fastNLP.embeddings.char_embedding.rst @@ -1,7 +1,7 @@ -fastNLP.embeddings.char\_embedding -================================== +fastNLP.embeddings.char_embedding +================================= .. automodule:: fastNLP.embeddings.char_embedding - :members: - :undoc-members: - :show-inheritance: + :members: CNNCharEmbedding, LSTMCharEmbedding + :inherited-members: + diff --git a/docs/source/fastNLP.embeddings.contextual_embedding.rst b/docs/source/fastNLP.embeddings.contextual_embedding.rst new file mode 100644 index 00000000..ee64c7a0 --- /dev/null +++ b/docs/source/fastNLP.embeddings.contextual_embedding.rst @@ -0,0 +1,7 @@ +fastNLP.embeddings.contextual_embedding +======================================= + +.. automodule:: fastNLP.embeddings.contextual_embedding + :members: ContextualEmbedding + :inherited-members: + diff --git a/docs/source/fastNLP.embeddings.elmo_embedding.rst b/docs/source/fastNLP.embeddings.elmo_embedding.rst index 76669ee3..06cc13af 100644 --- a/docs/source/fastNLP.embeddings.elmo_embedding.rst +++ b/docs/source/fastNLP.embeddings.elmo_embedding.rst @@ -1,7 +1,7 @@ -fastNLP.embeddings.elmo\_embedding -================================== +fastNLP.embeddings.elmo_embedding +================================= .. automodule:: fastNLP.embeddings.elmo_embedding - :members: - :undoc-members: - :show-inheritance: + :members: ElmoEmbedding + :inherited-members: + diff --git a/docs/source/fastNLP.embeddings.embedding.rst b/docs/source/fastNLP.embeddings.embedding.rst index 5960d2cd..4d5fcf46 100644 --- a/docs/source/fastNLP.embeddings.embedding.rst +++ b/docs/source/fastNLP.embeddings.embedding.rst @@ -2,6 +2,6 @@ fastNLP.embeddings.embedding ============================ .. automodule:: fastNLP.embeddings.embedding - :members: - :undoc-members: - :show-inheritance: + :members: Embedding, TokenEmbedding + :inherited-members: + diff --git a/docs/source/fastNLP.embeddings.rst b/docs/source/fastNLP.embeddings.rst index 6872e91d..8376408c 100644 --- a/docs/source/fastNLP.embeddings.rst +++ b/docs/source/fastNLP.embeddings.rst @@ -2,17 +2,17 @@ fastNLP.embeddings ================== .. automodule:: fastNLP.embeddings - :members: - :undoc-members: - :show-inheritance: + :members: Embedding, TokenEmbedding, StaticEmbedding, ElmoEmbedding, BertEmbedding, BertWordPieceEncoder, StackEmbedding, LSTMCharEmbedding, CNNCharEmbedding, get_embeddings + :inherited-members: -Submodules ----------- +子模块 +------ .. toctree:: fastNLP.embeddings.bert_embedding fastNLP.embeddings.char_embedding + fastNLP.embeddings.contextual_embedding fastNLP.embeddings.elmo_embedding fastNLP.embeddings.embedding fastNLP.embeddings.stack_embedding diff --git a/docs/source/fastNLP.embeddings.stack_embedding.rst b/docs/source/fastNLP.embeddings.stack_embedding.rst index 4d2115f7..6af91623 100644 --- a/docs/source/fastNLP.embeddings.stack_embedding.rst +++ b/docs/source/fastNLP.embeddings.stack_embedding.rst @@ -1,7 +1,7 @@ -fastNLP.embeddings.stack\_embedding -=================================== +fastNLP.embeddings.stack_embedding +================================== .. automodule:: fastNLP.embeddings.stack_embedding - :members: - :undoc-members: - :show-inheritance: + :members: StackEmbedding + :inherited-members: + diff --git a/docs/source/fastNLP.embeddings.static_embedding.rst b/docs/source/fastNLP.embeddings.static_embedding.rst index e46de81a..2df1c329 100644 --- a/docs/source/fastNLP.embeddings.static_embedding.rst +++ b/docs/source/fastNLP.embeddings.static_embedding.rst @@ -1,7 +1,7 @@ -fastNLP.embeddings.static\_embedding -==================================== +fastNLP.embeddings.static_embedding +=================================== .. automodule:: fastNLP.embeddings.static_embedding - :members: - :undoc-members: - :show-inheritance: + :members: StaticEmbedding + :inherited-members: + diff --git a/docs/source/fastNLP.embeddings.utils.rst b/docs/source/fastNLP.embeddings.utils.rst index 263bfbd6..13e5936b 100644 --- a/docs/source/fastNLP.embeddings.utils.rst +++ b/docs/source/fastNLP.embeddings.utils.rst @@ -2,6 +2,6 @@ fastNLP.embeddings.utils ======================== .. automodule:: fastNLP.embeddings.utils - :members: - :undoc-members: - :show-inheritance: + :members: get_embeddings + :inherited-members: + diff --git a/docs/source/fastNLP.io.data_bundle.rst b/docs/source/fastNLP.io.data_bundle.rst index a6273956..71a921f1 100644 --- a/docs/source/fastNLP.io.data_bundle.rst +++ b/docs/source/fastNLP.io.data_bundle.rst @@ -1,7 +1,7 @@ -fastNLP.io.data\_bundle -======================= +fastNLP.io.data_bundle +====================== .. automodule:: fastNLP.io.data_bundle - :members: - :undoc-members: - :show-inheritance: + :members: DataBundle + :inherited-members: + diff --git a/docs/source/fastNLP.io.data_loader.rst b/docs/source/fastNLP.io.data_loader.rst deleted file mode 100644 index 0b4f5d0b..00000000 --- a/docs/source/fastNLP.io.data_loader.rst +++ /dev/null @@ -1,8 +0,0 @@ -fastNLP.io.data\_loader -======================= - -.. automodule:: fastNLP.io.data_loader - :members: - :undoc-members: - :show-inheritance: - diff --git a/docs/source/fastNLP.io.dataset_loader.rst b/docs/source/fastNLP.io.dataset_loader.rst index e7990714..c211ecf9 100644 --- a/docs/source/fastNLP.io.dataset_loader.rst +++ b/docs/source/fastNLP.io.dataset_loader.rst @@ -1,7 +1,6 @@ -fastNLP.io.dataset\_loader -========================== +fastNLP.io.dataset_loader +========================= .. automodule:: fastNLP.io.dataset_loader - :members: - :undoc-members: - :show-inheritance: + :members: CSVLoader, JsonLoader + diff --git a/docs/source/fastNLP.io.embed_loader.rst b/docs/source/fastNLP.io.embed_loader.rst index 69e1f7ff..581f5c1b 100644 --- a/docs/source/fastNLP.io.embed_loader.rst +++ b/docs/source/fastNLP.io.embed_loader.rst @@ -1,7 +1,7 @@ -fastNLP.io.embed\_loader -======================== +fastNLP.io.embed_loader +======================= .. automodule:: fastNLP.io.embed_loader - :members: - :undoc-members: - :show-inheritance: + :members: EmbedLoader, EmbeddingOption + :inherited-members: + diff --git a/docs/source/fastNLP.io.file_utils.rst b/docs/source/fastNLP.io.file_utils.rst index 944550d7..0815e068 100644 --- a/docs/source/fastNLP.io.file_utils.rst +++ b/docs/source/fastNLP.io.file_utils.rst @@ -1,7 +1,7 @@ -fastNLP.io.file\_utils -====================== +fastNLP.io.file_utils +===================== .. automodule:: fastNLP.io.file_utils - :members: - :undoc-members: - :show-inheritance: + :members: cached_path, get_filepath, get_cache_path, split_filename_suffix, get_from_cache + :inherited-members: + diff --git a/docs/source/fastNLP.io.loader.rst b/docs/source/fastNLP.io.loader.rst index bbdc1d7a..060b5450 100644 --- a/docs/source/fastNLP.io.loader.rst +++ b/docs/source/fastNLP.io.loader.rst @@ -2,7 +2,6 @@ fastNLP.io.loader ================= .. automodule:: fastNLP.io.loader - :members: - :undoc-members: - :show-inheritance: + :members: Loader, YelpLoader, YelpFullLoader, YelpPolarityLoader, IMDBLoader, SSTLoader, SST2Loader, ConllLoader, Conll2003Loader, Conll2003NERLoader, OntoNotesNERLoader, CTBLoader, MsraNERLoader, PeopleDailyNERLoader, WeiboNERLoader, CSVLoader, JsonLoader, CWSLoader, MNLILoader, QuoraLoader, SNLILoader, QNLILoader, RTELoader + :inherited-members: diff --git a/docs/source/fastNLP.io.model_io.rst b/docs/source/fastNLP.io.model_io.rst index 537ce752..183122b1 100644 --- a/docs/source/fastNLP.io.model_io.rst +++ b/docs/source/fastNLP.io.model_io.rst @@ -1,7 +1,7 @@ -fastNLP.io.model\_io -==================== +fastNLP.io.model_io +=================== .. automodule:: fastNLP.io.model_io - :members: - :undoc-members: - :show-inheritance: + :members: ModelLoader, ModelSaver + :inherited-members: + diff --git a/docs/source/fastNLP.io.pipe.rst b/docs/source/fastNLP.io.pipe.rst index bf126585..d35d2ddc 100644 --- a/docs/source/fastNLP.io.pipe.rst +++ b/docs/source/fastNLP.io.pipe.rst @@ -2,7 +2,6 @@ fastNLP.io.pipe =============== .. automodule:: fastNLP.io.pipe - :members: - :undoc-members: - :show-inheritance: + :members: Pipe, CWSPipe, YelpFullPipe, YelpPolarityPipe, SSTPipe, SST2Pipe, IMDBPipe, Conll2003NERPipe, OntoNotesNERPipe, MsraNERPipe, WeiboNERPipe, PeopleDailyPipe, Conll2003Pipe, MatchingBertPipe, RTEBertPipe, SNLIBertPipe, QuoraBertPipe, QNLIBertPipe, MNLIBertPipe, MatchingPipe, RTEPipe, SNLIPipe, QuoraPipe, QNLIPipe, MNLIPipe + :inherited-members: diff --git a/docs/source/fastNLP.io.rst b/docs/source/fastNLP.io.rst index 0cd5d3f2..2aacb883 100644 --- a/docs/source/fastNLP.io.rst +++ b/docs/source/fastNLP.io.rst @@ -2,27 +2,18 @@ fastNLP.io ========== .. automodule:: fastNLP.io - :members: - :undoc-members: - :show-inheritance: + :members: DataBundle, EmbedLoader, Loader, YelpLoader, YelpFullLoader, YelpPolarityLoader, IMDBLoader, SSTLoader, SST2Loader, ConllLoader, Conll2003Loader, Conll2003NERLoader, OntoNotesNERLoader, CTBLoader, MsraNERLoader, WeiboNERLoader, PeopleDailyNERLoader, CSVLoader, JsonLoader, CWSLoader, MNLILoader, QuoraLoader, SNLILoader, QNLILoader, RTELoader, Pipe, YelpFullPipe, YelpPolarityPipe, SSTPipe, SST2Pipe, IMDBPipe, Conll2003Pipe, Conll2003NERPipe, OntoNotesNERPipe, MsraNERPipe, PeopleDailyPipe, WeiboNERPipe, CWSPipe, MatchingBertPipe, RTEBertPipe, SNLIBertPipe, QuoraBertPipe, QNLIBertPipe, MNLIBertPipe, MatchingPipe, RTEPipe, SNLIPipe, QuoraPipe, QNLIPipe, MNLIPipe, ModelLoader, ModelSaver + :inherited-members: -Subpackages ------------ - -.. toctree:: - - fastNLP.io.data_loader - fastNLP.io.loader - fastNLP.io.pipe - -Submodules ----------- +子模块 +------ .. toctree:: fastNLP.io.data_bundle - fastNLP.io.dataset_loader fastNLP.io.embed_loader fastNLP.io.file_utils + fastNLP.io.loader fastNLP.io.model_io + fastNLP.io.pipe fastNLP.io.utils diff --git a/docs/source/fastNLP.io.utils.rst b/docs/source/fastNLP.io.utils.rst index 0b3f3938..3bff3c45 100644 --- a/docs/source/fastNLP.io.utils.rst +++ b/docs/source/fastNLP.io.utils.rst @@ -2,6 +2,6 @@ fastNLP.io.utils ================ .. automodule:: fastNLP.io.utils - :members: - :undoc-members: - :show-inheritance: + :members: check_loader_paths + :inherited-members: + diff --git a/docs/source/fastNLP.models.biaffine_parser.rst b/docs/source/fastNLP.models.biaffine_parser.rst index f19504e8..c3dbb0a5 100644 --- a/docs/source/fastNLP.models.biaffine_parser.rst +++ b/docs/source/fastNLP.models.biaffine_parser.rst @@ -1,7 +1,7 @@ -fastNLP.models.biaffine\_parser -=============================== +fastNLP.models.biaffine_parser +============================== .. automodule:: fastNLP.models.biaffine_parser - :members: - :undoc-members: - :show-inheritance: + :members: BiaffineParser, GraphParser + :inherited-members: + diff --git a/docs/source/fastNLP.models.cnn_text_classification.rst b/docs/source/fastNLP.models.cnn_text_classification.rst index eacf6916..fe4bb157 100644 --- a/docs/source/fastNLP.models.cnn_text_classification.rst +++ b/docs/source/fastNLP.models.cnn_text_classification.rst @@ -1,7 +1,7 @@ -fastNLP.models.cnn\_text\_classification -======================================== +fastNLP.models.cnn_text_classification +====================================== .. automodule:: fastNLP.models.cnn_text_classification - :members: - :undoc-members: - :show-inheritance: + :members: CNNText + :inherited-members: + diff --git a/docs/source/fastNLP.models.rst b/docs/source/fastNLP.models.rst index 36875b85..88854a79 100644 --- a/docs/source/fastNLP.models.rst +++ b/docs/source/fastNLP.models.rst @@ -2,12 +2,11 @@ fastNLP.models ============== .. automodule:: fastNLP.models - :members: - :undoc-members: - :show-inheritance: + :members: CNNText, SeqLabeling, AdvSeqLabel, ESIM, StarTransEnc, STSeqLabel, STNLICls, STSeqCls, BiaffineParser, GraphParser + :inherited-members: -Submodules ----------- +子模块 +------ .. toctree:: diff --git a/docs/source/fastNLP.models.sequence_labeling.rst b/docs/source/fastNLP.models.sequence_labeling.rst index 85e28f06..b66e637e 100644 --- a/docs/source/fastNLP.models.sequence_labeling.rst +++ b/docs/source/fastNLP.models.sequence_labeling.rst @@ -1,7 +1,7 @@ -fastNLP.models.sequence\_labeling -================================= +fastNLP.models.sequence_labeling +================================ .. automodule:: fastNLP.models.sequence_labeling - :members: - :undoc-members: - :show-inheritance: + :members: SeqLabeling, AdvSeqLabel + :inherited-members: + diff --git a/docs/source/fastNLP.models.snli.rst b/docs/source/fastNLP.models.snli.rst index 3b9b555c..8551051a 100644 --- a/docs/source/fastNLP.models.snli.rst +++ b/docs/source/fastNLP.models.snli.rst @@ -2,6 +2,6 @@ fastNLP.models.snli =================== .. automodule:: fastNLP.models.snli - :members: - :undoc-members: - :show-inheritance: + :members: ESIM + :inherited-members: + diff --git a/docs/source/fastNLP.models.star_transformer.rst b/docs/source/fastNLP.models.star_transformer.rst index 69d5c5b2..f4b5989e 100644 --- a/docs/source/fastNLP.models.star_transformer.rst +++ b/docs/source/fastNLP.models.star_transformer.rst @@ -1,7 +1,7 @@ -fastNLP.models.star\_transformer -================================ +fastNLP.models.star_transformer +=============================== .. automodule:: fastNLP.models.star_transformer - :members: - :undoc-members: - :show-inheritance: + :members: StarTransEnc, STNLICls, STSeqCls, STSeqLabel + :inherited-members: + diff --git a/docs/source/fastNLP.modules.decoder.rst b/docs/source/fastNLP.modules.decoder.rst index ecc2adbd..b121f9e9 100644 --- a/docs/source/fastNLP.modules.decoder.rst +++ b/docs/source/fastNLP.modules.decoder.rst @@ -2,7 +2,6 @@ fastNLP.modules.decoder ======================= .. automodule:: fastNLP.modules.decoder - :members: - :undoc-members: - :show-inheritance: + :members: MLP, ConditionalRandomField, viterbi_decode, allowed_transitions + :inherited-members: diff --git a/docs/source/fastNLP.modules.encoder.rst b/docs/source/fastNLP.modules.encoder.rst index e60f9fa4..6b44a192 100644 --- a/docs/source/fastNLP.modules.encoder.rst +++ b/docs/source/fastNLP.modules.encoder.rst @@ -2,7 +2,6 @@ fastNLP.modules.encoder ======================= .. automodule:: fastNLP.modules.encoder - :members: - :undoc-members: - :show-inheritance: + :members: ConvolutionCharEncoder, LSTMCharEncoder, ConvMaxpool, LSTM, StarTransformer, TransformerEncoder, VarRNN, VarLSTM, VarGRU, MaxPool, MaxPoolWithMask, AvgPool, AvgPoolWithMask, MultiHeadAttention + :inherited-members: diff --git a/docs/source/fastNLP.modules.rst b/docs/source/fastNLP.modules.rst index 06494b53..6134d0dd 100644 --- a/docs/source/fastNLP.modules.rst +++ b/docs/source/fastNLP.modules.rst @@ -2,21 +2,14 @@ fastNLP.modules =============== .. automodule:: fastNLP.modules - :members: - :undoc-members: - :show-inheritance: + :members: ConvolutionCharEncoder, LSTMCharEncoder, ConvMaxpool, LSTM, StarTransformer, TransformerEncoder, VarRNN, VarLSTM, VarGRU, MaxPool, MaxPoolWithMask, AvgPool, AvgPoolWithMask, MultiHeadAttention, MLP, ConditionalRandomField, viterbi_decode, allowed_transitions, TimestepDropout + :inherited-members: -Subpackages ------------ +子模块 +------ .. toctree:: fastNLP.modules.decoder fastNLP.modules.encoder - -Submodules ----------- - -.. toctree:: - fastNLP.modules.utils diff --git a/docs/source/fastNLP.modules.utils.rst b/docs/source/fastNLP.modules.utils.rst index c0219435..e28ca35a 100644 --- a/docs/source/fastNLP.modules.utils.rst +++ b/docs/source/fastNLP.modules.utils.rst @@ -2,6 +2,6 @@ fastNLP.modules.utils ===================== .. automodule:: fastNLP.modules.utils - :members: - :undoc-members: - :show-inheritance: + :members: initial_parameter, summary + :inherited-members: + diff --git a/docs/source/fastNLP.rst b/docs/source/fastNLP.rst index e3ba429d..f22ea936 100644 --- a/docs/source/fastNLP.rst +++ b/docs/source/fastNLP.rst @@ -2,12 +2,11 @@ fastNLP ======= .. automodule:: fastNLP - :members: - :undoc-members: - :show-inheritance: + :members: Instance, FieldArray, DataSetIter, BatchIter, TorchLoaderIter, Vocabulary, DataSet, Const, Trainer, Tester, Callback, GradientClipCallback, EarlyStopCallback, TensorboardCallback, LRScheduler, ControlC, LRFinder, Padder, AutoPadder, EngChar2DPadder, AccuracyMetric, SpanFPreRecMetric, ExtractiveQAMetric, Optimizer, SGD, Adam, AdamW, Sampler, SequentialSampler, BucketSampler, RandomSampler, LossFunc, CrossEntropyLoss, L1Loss, BCELoss, NLLLoss, LossInForward, cache_results, logger + :inherited-members: -Subpackages ------------ +子模块 +------ .. toctree:: From efe88263bb2fb7bebacb8022eb86c390e266ec36 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Mon, 26 Aug 2019 10:21:10 +0800 Subject: [PATCH 134/139] add __all__ and __doc__ for all files in module 'core', using 'undocumented' tags --- fastNLP/core/__init__.py | 67 +++++++++++++++++++++- fastNLP/core/_logger.py | 38 ++++++------ fastNLP/core/_parallel_utils.py | 21 ++++--- fastNLP/core/const.py | 26 ++++++--- fastNLP/core/dist_trainer.py | 22 +++---- fastNLP/core/field.py | 19 ++++-- fastNLP/core/predictor.py | 28 ++++----- fastNLP/core/vocabulary.py | 28 +++++---- fastNLP/embeddings/contextual_embedding.py | 10 ++-- 9 files changed, 178 insertions(+), 81 deletions(-) diff --git a/fastNLP/core/__init__.py b/fastNLP/core/__init__.py index 1feaf3fb..efee08b5 100644 --- a/fastNLP/core/__init__.py +++ b/fastNLP/core/__init__.py @@ -10,8 +10,72 @@ core 模块里实现了 fastNLP 的核心框架,常用的功能都可以从 fa 对于常用的功能,你只需要在 :doc:`fastNLP` 中查看即可。如果想了解各个子模块的具体作用,您可以在下面找到每个子模块的具体文档。 - """ +__all__ = [ + "DataSet", + + "Instance", + + "FieldArray", + "Padder", + "AutoPadder", + "EngChar2DPadder", + + "Vocabulary", + + "DataSetIter", + "BatchIter", + "TorchLoaderIter", + + "Const", + + "Tester", + "Trainer", + + "cache_results", + "seq_len_to_mask", + "get_seq_len", + "logger", + + "Callback", + "GradientClipCallback", + "EarlyStopCallback", + "FitlogCallback", + "EvaluateCallback", + "LRScheduler", + "ControlC", + "LRFinder", + "TensorboardCallback", + "WarmupCallback", + 'SaveModelCallback', + "EchoCallback", + "TesterCallback", + "CallbackException", + "EarlyStopError", + + "LossFunc", + "CrossEntropyLoss", + "L1Loss", + "BCELoss", + "NLLLoss", + "LossInForward", + + "AccuracyMetric", + "SpanFPreRecMetric", + "ExtractiveQAMetric", + + "Optimizer", + "SGD", + "Adam", + "AdamW", + + "SequentialSampler", + "BucketSampler", + "RandomSampler", + "Sampler", +] + +from ._logger import logger from .batch import DataSetIter, BatchIter, TorchLoaderIter from .callback import Callback, GradientClipCallback, EarlyStopCallback, FitlogCallback, EvaluateCallback, \ LRScheduler, ControlC, LRFinder, TensorboardCallback, WarmupCallback, SaveModelCallback, EchoCallback, \ @@ -28,4 +92,3 @@ from .tester import Tester from .trainer import Trainer from .utils import cache_results, seq_len_to_mask, get_seq_len from .vocabulary import Vocabulary -from ._logger import logger diff --git a/fastNLP/core/_logger.py b/fastNLP/core/_logger.py index 50266d7a..7198cfbd 100644 --- a/fastNLP/core/_logger.py +++ b/fastNLP/core/_logger.py @@ -1,15 +1,15 @@ +"""undocumented""" + +__all__ = [ + 'logger', +] + import logging import logging.config -import torch -import _pickle as pickle import os import sys import warnings -__all__ = [ - 'logger', -] - ROOT_NAME = 'fastNLP' try: @@ -25,7 +25,7 @@ if tqdm is not None: class TqdmLoggingHandler(logging.Handler): def __init__(self, level=logging.INFO): super().__init__(level) - + def emit(self, record): try: msg = self.format(record) @@ -59,14 +59,14 @@ def _add_file_handler(logger, path, level='INFO'): if os.path.abspath(path) == h.baseFilename: # file path already added return - + # File Handler if os.path.exists(path): assert os.path.isfile(path) warnings.warn('log already exists in {}'.format(path)) dirname = os.path.abspath(os.path.dirname(path)) os.makedirs(dirname, exist_ok=True) - + file_handler = logging.FileHandler(path, mode='a') file_handler.setLevel(_get_level(level)) file_formatter = logging.Formatter(fmt='%(asctime)s - %(module)s - [%(levelname)s] - %(message)s', @@ -87,7 +87,7 @@ def _set_stdout_handler(logger, stdout='tqdm', level='INFO'): break if stream_handler is not None: logger.removeHandler(stream_handler) - + # Stream Handler if stdout == 'plain': stream_handler = logging.StreamHandler(sys.stdout) @@ -95,7 +95,7 @@ def _set_stdout_handler(logger, stdout='tqdm', level='INFO'): stream_handler = TqdmLoggingHandler(level) else: stream_handler = None - + if stream_handler is not None: stream_formatter = logging.Formatter('%(message)s') stream_handler.setLevel(level) @@ -103,38 +103,40 @@ def _set_stdout_handler(logger, stdout='tqdm', level='INFO'): logger.addHandler(stream_handler) - class FastNLPLogger(logging.getLoggerClass()): def __init__(self, name): super().__init__(name) - + def add_file(self, path='./log.txt', level='INFO'): """add log output file and level""" _add_file_handler(self, path, level) - + def set_stdout(self, stdout='tqdm', level='INFO'): """set stdout format and level""" _set_stdout_handler(self, stdout, level) + logging.setLoggerClass(FastNLPLogger) + + # print(logging.getLoggerClass()) # print(logging.getLogger()) def _init_logger(path=None, stdout='tqdm', level='INFO'): """initialize logger""" level = _get_level(level) - + # logger = logging.getLogger() logger = logging.getLogger(ROOT_NAME) logger.propagate = False logger.setLevel(level) - + _set_stdout_handler(logger, stdout, level) - + # File Handler if path is not None: _add_file_handler(logger, path, level) - + return logger diff --git a/fastNLP/core/_parallel_utils.py b/fastNLP/core/_parallel_utils.py index 6b24d9f9..ce745820 100644 --- a/fastNLP/core/_parallel_utils.py +++ b/fastNLP/core/_parallel_utils.py @@ -1,11 +1,14 @@ +"""undocumented""" + +__all__ = [] import threading + import torch from torch import nn from torch.nn.parallel.parallel_apply import get_a_var - -from torch.nn.parallel.scatter_gather import scatter_kwargs, gather from torch.nn.parallel.replicate import replicate +from torch.nn.parallel.scatter_gather import scatter_kwargs, gather def parallel_apply(modules, func_name, inputs, kwargs_tup=None, devices=None): @@ -27,11 +30,11 @@ def parallel_apply(modules, func_name, inputs, kwargs_tup=None, devices=None): assert len(modules) == len(devices) else: devices = [None] * len(modules) - + lock = threading.Lock() results = {} grad_enabled = torch.is_grad_enabled() - + def _worker(i, module, input, kwargs, device=None): torch.set_grad_enabled(grad_enabled) if device is None: @@ -47,20 +50,20 @@ def parallel_apply(modules, func_name, inputs, kwargs_tup=None, devices=None): except Exception as e: with lock: results[i] = e - + if len(modules) > 1: threads = [threading.Thread(target=_worker, args=(i, module, input, kwargs, device)) for i, (module, input, kwargs, device) in enumerate(zip(modules, inputs, kwargs_tup, devices))] - + for thread in threads: thread.start() for thread in threads: thread.join() else: _worker(0, modules[0], inputs[0], kwargs_tup[0], devices[0]) - + outputs = [] for i in range(len(inputs)): output = results[i] @@ -79,6 +82,7 @@ def _data_parallel_wrapper(func_name, device_ids, output_device): :param output_device: nn.DataParallel中的output_device :return: """ + def wrapper(network, *inputs, **kwargs): inputs, kwargs = scatter_kwargs(inputs, kwargs, device_ids, dim=0) if len(device_ids) == 1: @@ -86,6 +90,7 @@ def _data_parallel_wrapper(func_name, device_ids, output_device): replicas = replicate(network, device_ids[:len(inputs)]) outputs = parallel_apply(replicas, func_name, inputs, kwargs, device_ids[:len(replicas)]) return gather(outputs, output_device) + return wrapper @@ -99,4 +104,4 @@ def _model_contains_inner_module(model): if isinstance(model, nn.Module): if isinstance(model, (nn.DataParallel, nn.parallel.DistributedDataParallel)): return True - return False \ No newline at end of file + return False diff --git a/fastNLP/core/const.py b/fastNLP/core/const.py index 27e8d1cb..ad5d1f1e 100644 --- a/fastNLP/core/const.py +++ b/fastNLP/core/const.py @@ -1,3 +1,13 @@ +""" +.. todo:: + doc +""" + +__all__ = [ + "Const" +] + + class Const: """ fastNLP中field命名常量。 @@ -25,47 +35,47 @@ class Const: LOSS = 'loss' RAW_WORD = 'raw_words' RAW_CHAR = 'raw_chars' - + @staticmethod def INPUTS(i): """得到第 i 个 ``INPUT`` 的命名""" i = int(i) + 1 return Const.INPUT + str(i) - + @staticmethod def CHAR_INPUTS(i): """得到第 i 个 ``CHAR_INPUT`` 的命名""" i = int(i) + 1 return Const.CHAR_INPUT + str(i) - + @staticmethod def RAW_WORDS(i): i = int(i) + 1 return Const.RAW_WORD + str(i) - + @staticmethod def RAW_CHARS(i): i = int(i) + 1 return Const.RAW_CHAR + str(i) - + @staticmethod def INPUT_LENS(i): """得到第 i 个 ``INPUT_LEN`` 的命名""" i = int(i) + 1 return Const.INPUT_LEN + str(i) - + @staticmethod def OUTPUTS(i): """得到第 i 个 ``OUTPUT`` 的命名""" i = int(i) + 1 return Const.OUTPUT + str(i) - + @staticmethod def TARGETS(i): """得到第 i 个 ``TARGET`` 的命名""" i = int(i) + 1 return Const.TARGET + str(i) - + @staticmethod def LOSSES(i): """得到第 i 个 ``LOSS`` 的命名""" diff --git a/fastNLP/core/dist_trainer.py b/fastNLP/core/dist_trainer.py index 7c64fee4..3a293447 100644 --- a/fastNLP/core/dist_trainer.py +++ b/fastNLP/core/dist_trainer.py @@ -1,29 +1,29 @@ -""" +"""undocumented 正在开发中的分布式训练代码 """ +import logging +import os +import time +from datetime import datetime + import torch import torch.cuda -import torch.optim import torch.distributed as dist -from torch.utils.data.distributed import DistributedSampler +import torch.optim +from pkg_resources import parse_version from torch.nn.parallel import DistributedDataParallel as DDP -import os +from torch.utils.data.distributed import DistributedSampler from tqdm import tqdm -import time -from datetime import datetime, timedelta -from functools import partial +from ._logger import logger from .batch import DataSetIter, BatchIter from .callback import DistCallbackManager, CallbackException, TesterCallback from .dataset import DataSet from .losses import _prepare_losser from .optimizer import Optimizer from .utils import _build_args -from .utils import _move_dict_value_to_device from .utils import _get_func_signature -from ._logger import logger -import logging -from pkg_resources import parse_version +from .utils import _move_dict_value_to_device __all__ = [ 'get_local_rank', diff --git a/fastNLP/core/field.py b/fastNLP/core/field.py index b3f024f8..05f987c2 100644 --- a/fastNLP/core/field.py +++ b/fastNLP/core/field.py @@ -1,18 +1,25 @@ +""" +.. todo:: + doc +""" + __all__ = [ "Padder", "AutoPadder", "EngChar2DPadder", ] -from numbers import Number -import torch -import numpy as np -from typing import Any from abc import abstractmethod -from copy import deepcopy from collections import Counter -from .utils import _is_iterable +from copy import deepcopy +from numbers import Number +from typing import Any + +import numpy as np +import torch + from ._logger import logger +from .utils import _is_iterable class SetInputOrTargetException(Exception): diff --git a/fastNLP/core/predictor.py b/fastNLP/core/predictor.py index 2d6a7380..c6b8fc90 100644 --- a/fastNLP/core/predictor.py +++ b/fastNLP/core/predictor.py @@ -1,13 +1,15 @@ -""" - ..todo:: - 检查这个类是否需要 -""" +"""undocumented""" + +__all__ = [ + "Predictor" +] + from collections import defaultdict import torch -from . import DataSetIter from . import DataSet +from . import DataSetIter from . import SequentialSampler from .utils import _build_args, _move_dict_value_to_device, _get_model_device @@ -21,7 +23,7 @@ class Predictor(object): :param torch.nn.Module network: 用来完成预测任务的模型 """ - + def __init__(self, network): if not isinstance(network, torch.nn.Module): raise ValueError( @@ -29,7 +31,7 @@ class Predictor(object): self.network = network self.batch_size = 1 self.batch_output = [] - + def predict(self, data: DataSet, seq_len_field_name=None): """用已经训练好的模型进行inference. @@ -41,27 +43,27 @@ class Predictor(object): raise ValueError("Only Dataset class is allowed, not {}.".format(type(data))) if seq_len_field_name is not None and seq_len_field_name not in data.field_arrays: raise ValueError("Field name {} not found in DataSet {}.".format(seq_len_field_name, data)) - + prev_training = self.network.training self.network.eval() network_device = _get_model_device(self.network) batch_output = defaultdict(list) data_iterator = DataSetIter(data, batch_size=self.batch_size, sampler=SequentialSampler(), as_numpy=False) - + if hasattr(self.network, "predict"): predict_func = self.network.predict else: predict_func = self.network.forward - + with torch.no_grad(): for batch_x, _ in data_iterator: _move_dict_value_to_device(batch_x, _, device=network_device) refined_batch_x = _build_args(predict_func, **batch_x) prediction = predict_func(**refined_batch_x) - + if seq_len_field_name is not None: seq_lens = batch_x[seq_len_field_name].tolist() - + for key, value in prediction.items(): value = value.cpu().numpy() if len(value.shape) == 1 or (len(value.shape) == 2 and value.shape[1] == 1): @@ -74,6 +76,6 @@ class Predictor(object): batch_output[key].extend(tmp_batch) else: batch_output[key].append(value) - + self.network.train(prev_training) return batch_output diff --git a/fastNLP/core/vocabulary.py b/fastNLP/core/vocabulary.py index 92f54f9a..52d33a5a 100644 --- a/fastNLP/core/vocabulary.py +++ b/fastNLP/core/vocabulary.py @@ -1,16 +1,22 @@ +""" +.. todo:: + doc +""" + __all__ = [ "Vocabulary", "VocabularyOption", ] -from functools import wraps from collections import Counter +from functools import partial +from functools import wraps + +from ._logger import logger from .dataset import DataSet from .utils import Option -from functools import partial -import numpy as np from .utils import _is_iterable -from ._logger import logger + class VocabularyOption(Option): def __init__(self, @@ -51,7 +57,7 @@ def _check_build_status(func): self.rebuild = True if self.max_size is not None and len(self.word_count) >= self.max_size: logger.info("[Warning] Vocabulary has reached the max size {} when calling {} method. " - "Adding more words may cause unexpected behaviour of Vocabulary. ".format( + "Adding more words may cause unexpected behaviour of Vocabulary. ".format( self.max_size, func.__name__)) return func(self, *args, **kwargs) @@ -199,7 +205,7 @@ class Vocabulary(object): self.build_reverse_vocab() self.rebuild = False return self - + def build_reverse_vocab(self): """ 基于 `word to index` dict, 构建 `index to word` dict. @@ -279,19 +285,19 @@ class Vocabulary(object): if not isinstance(field[0][0], str) and _is_iterable(field[0][0]): raise RuntimeError("Only support field with 2 dimensions.") return [[self.to_index(c) for c in w] for w in field] - + new_field_name = new_field_name or field_name - + if type(new_field_name) == type(field_name): if isinstance(new_field_name, list): assert len(new_field_name) == len(field_name), "new_field_name should have same number elements with " \ - "field_name." + "field_name." elif isinstance(new_field_name, str): field_name = [field_name] new_field_name = [new_field_name] else: raise TypeError("field_name and new_field_name can only be str or List[str].") - + for idx, dataset in enumerate(datasets): if isinstance(dataset, DataSet): try: @@ -377,7 +383,7 @@ class Vocabulary(object): :return: bool """ return word in self._no_create_word - + def to_index(self, w): """ 将词转为数字. 若词不再词典中被记录, 将视为 unknown, 若 ``unknown=None`` , 将抛出``ValueError``:: diff --git a/fastNLP/embeddings/contextual_embedding.py b/fastNLP/embeddings/contextual_embedding.py index 2c304da7..9910a44b 100644 --- a/fastNLP/embeddings/contextual_embedding.py +++ b/fastNLP/embeddings/contextual_embedding.py @@ -8,15 +8,17 @@ __all__ = [ ] from abc import abstractmethod + import torch -from ..core.vocabulary import Vocabulary -from ..core.dataset import DataSet +from .embedding import TokenEmbedding +from ..core import logger from ..core.batch import DataSetIter +from ..core.dataset import DataSet from ..core.sampler import SequentialSampler from ..core.utils import _move_model_to_device, _get_model_device -from .embedding import TokenEmbedding -from ..core import logger +from ..core.vocabulary import Vocabulary + class ContextualEmbedding(TokenEmbedding): def __init__(self, vocab: Vocabulary, word_dropout: float = 0.0, dropout: float = 0.0): From 0d5f43b451473fe25703cb1f9798fcf03eb64c76 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Mon, 26 Aug 2019 10:25:01 +0800 Subject: [PATCH 135/139] add __all__ and __doc__ for all files in module 'io', using 'undocumented' tags --- fastNLP/io/data_bundle.py | 7 +- fastNLP/io/dataset_loader.py | 6 +- fastNLP/io/embed_loader.py | 9 +- fastNLP/io/file_reader.py | 16 ++- fastNLP/io/file_utils.py | 23 +++- fastNLP/io/loader/classification.py | 26 +++-- fastNLP/io/loader/conll.py | 84 +++++++++------ fastNLP/io/loader/csv.py | 10 +- fastNLP/io/loader/cws.py | 17 ++- fastNLP/io/loader/json.py | 10 +- fastNLP/io/loader/loader.py | 13 ++- fastNLP/io/loader/matching.py | 82 ++++++++------ fastNLP/io/pipe/classification.py | 161 +++++++++++++++------------- fastNLP/io/pipe/conll.py | 79 ++++++++------ fastNLP/io/pipe/cws.py | 6 ++ fastNLP/io/pipe/matching.py | 75 ++++++++----- fastNLP/io/pipe/pipe.py | 6 ++ fastNLP/io/pipe/utils.py | 38 ++++--- fastNLP/io/utils.py | 25 +++-- 19 files changed, 439 insertions(+), 254 deletions(-) diff --git a/fastNLP/io/data_bundle.py b/fastNLP/io/data_bundle.py index 1e663f1e..db60a86f 100644 --- a/fastNLP/io/data_bundle.py +++ b/fastNLP/io/data_bundle.py @@ -1,10 +1,15 @@ +""" +.. todo:: + doc +""" __all__ = [ 'DataBundle', ] import _pickle as pickle -from typing import Union, Dict import os +from typing import Union, Dict + from ..core.dataset import DataSet from ..core.vocabulary import Vocabulary diff --git a/fastNLP/io/dataset_loader.py b/fastNLP/io/dataset_loader.py index 82e96597..fca0de69 100644 --- a/fastNLP/io/dataset_loader.py +++ b/fastNLP/io/dataset_loader.py @@ -1,4 +1,4 @@ -""" +"""undocumented .. warning:: 本模块将在 `0.5.0版本` 中被废弃,由 :mod:`~fastNLP.io.loader` 和 :mod:`~fastNLP.io.pipe` 模块替代。 @@ -23,10 +23,10 @@ __all__ = [ ] +from .data_bundle import DataSetLoader +from .file_reader import _read_csv, _read_json from ..core.dataset import DataSet from ..core.instance import Instance -from .file_reader import _read_csv, _read_json -from .data_bundle import DataSetLoader class JsonLoader(DataSetLoader): diff --git a/fastNLP/io/embed_loader.py b/fastNLP/io/embed_loader.py index c58385e1..780d91e4 100644 --- a/fastNLP/io/embed_loader.py +++ b/fastNLP/io/embed_loader.py @@ -1,17 +1,22 @@ +""" +.. todo:: + doc +""" __all__ = [ "EmbedLoader", "EmbeddingOption", ] +import logging import os import warnings import numpy as np -from ..core.vocabulary import Vocabulary from .data_bundle import BaseLoader from ..core.utils import Option -import logging +from ..core.vocabulary import Vocabulary + class EmbeddingOption(Option): def __init__(self, diff --git a/fastNLP/io/file_reader.py b/fastNLP/io/file_reader.py index 0320572c..7a953098 100644 --- a/fastNLP/io/file_reader.py +++ b/fastNLP/io/file_reader.py @@ -1,7 +1,11 @@ -""" +"""undocumented 此模块用于给其它模块提供读取文件的函数,没有为用户提供 API """ + +__all__ = [] + import json + from ..core import logger @@ -24,8 +28,8 @@ def _read_csv(path, encoding='utf-8', headers=None, sep=',', dropna=True): headers = headers.split(sep) start_idx += 1 elif not isinstance(headers, (list, tuple)): - raise TypeError("headers should be list or tuple, not {}." \ - .format(type(headers))) + raise TypeError("headers should be list or tuple, not {}." \ + .format(type(headers))) for line_idx, line in enumerate(f, start_idx): contents = line.rstrip('\r\n').split(sep) if len(contents) != len(headers): @@ -82,6 +86,7 @@ def _read_conll(path, encoding='utf-8', indexes=None, dropna=True): :if False, raise ValueError when reading invalid data. default: True :return: generator, every time yield (line number, conll item) """ + def parse_conll(sample): sample = list(map(list, zip(*sample))) sample = [sample[i] for i in indexes] @@ -89,14 +94,15 @@ def _read_conll(path, encoding='utf-8', indexes=None, dropna=True): if len(f) <= 0: raise ValueError('empty field') return sample + with open(path, 'r', encoding=encoding) as f: sample = [] start = next(f).strip() - if start!='': + if start != '': sample.append(start.split()) for line_idx, line in enumerate(f, 1): line = line.strip() - if line=='': + if line == '': if len(sample): try: res = parse_conll(sample) diff --git a/fastNLP/io/file_utils.py b/fastNLP/io/file_utils.py index bd02158e..8ecdff25 100644 --- a/fastNLP/io/file_utils.py +++ b/fastNLP/io/file_utils.py @@ -1,12 +1,27 @@ +""" +.. todo:: + doc +""" + +__all__ = [ + "cached_path", + "get_filepath", + "get_cache_path", + "split_filename_suffix", + "get_from_cache", +] + import os +import re +import shutil +import tempfile from pathlib import Path from urllib.parse import urlparse -import re + import requests -import tempfile -from tqdm import tqdm -import shutil from requests import HTTPError +from tqdm import tqdm + from ..core import logger PRETRAINED_BERT_MODEL_DIR = { diff --git a/fastNLP/io/loader/classification.py b/fastNLP/io/loader/classification.py index f64a26e7..ec00d2b4 100644 --- a/fastNLP/io/loader/classification.py +++ b/fastNLP/io/loader/classification.py @@ -1,12 +1,24 @@ -from ...core.dataset import DataSet -from ...core.instance import Instance -from .loader import Loader -import warnings +"""undocumented""" + +__all__ = [ + "YelpLoader", + "YelpFullLoader", + "YelpPolarityLoader", + "IMDBLoader", + "SSTLoader", + "SST2Loader", +] + +import glob import os import random import shutil -import glob import time +import warnings + +from .loader import Loader +from ...core.dataset import DataSet +from ...core.instance import Instance class YelpLoader(Loader): @@ -58,7 +70,7 @@ class YelpLoader(Loader): class YelpFullLoader(YelpLoader): - def download(self, dev_ratio: float = 0.1, re_download:bool=False): + def download(self, dev_ratio: float = 0.1, re_download: bool = False): """ 自动下载数据集,如果你使用了这个数据集,请引用以下的文章 @@ -127,7 +139,7 @@ class YelpPolarityLoader(YelpLoader): if time.time() - modify_time > 1 and re_download: # 通过这种比较丑陋的方式判断一下文件是否是才下载的 shutil.rmtree(data_dir) data_dir = self._get_dataset_path(dataset_name=dataset_name) - + if not os.path.exists(os.path.join(data_dir, 'dev.csv')): if dev_ratio > 0: assert 0 < dev_ratio < 1, "dev_ratio should be in range (0,1)." diff --git a/fastNLP/io/loader/conll.py b/fastNLP/io/loader/conll.py index b5241cff..1bd1b448 100644 --- a/fastNLP/io/loader/conll.py +++ b/fastNLP/io/loader/conll.py @@ -1,15 +1,28 @@ -from typing import Dict, Union +"""undocumented""" + +__all__ = [ + "ConllLoader", + "Conll2003Loader", + "Conll2003NERLoader", + "OntoNotesNERLoader", + "CTBLoader", + "CNNERLoader", + "MsraNERLoader", + "WeiboNERLoader", + "PeopleDailyNERLoader" +] -from .loader import Loader -from ...core.dataset import DataSet -from ..file_reader import _read_conll -from ...core.instance import Instance -from ...core.const import Const import glob import os +import random import shutil import time -import random + +from .loader import Loader +from ..file_reader import _read_conll +from ...core.const import Const +from ...core.dataset import DataSet +from ...core.instance import Instance class ConllLoader(Loader): @@ -47,6 +60,7 @@ class ConllLoader(Loader): :param bool dropna: 是否忽略非法数据,若 ``False`` ,遇到非法数据时抛出 ``ValueError`` 。Default: ``True`` """ + def __init__(self, headers, indexes=None, dropna=True): super(ConllLoader, self).__init__() if not isinstance(headers, (list, tuple)): @@ -60,7 +74,7 @@ class ConllLoader(Loader): if len(indexes) != len(headers): raise ValueError self.indexes = indexes - + def _load(self, path): """ 传入的一个文件路径,将该文件读入DataSet中,field由ConllLoader初始化时指定的headers决定。 @@ -101,12 +115,13 @@ class Conll2003Loader(ConllLoader): "[...]", "[...]", "[...]", "[...]" """ + def __init__(self): headers = [ 'raw_words', 'pos', 'chunk', 'ner', ] super(Conll2003Loader, self).__init__(headers=headers) - + def _load(self, path): """ 传入的一个文件路径,将该文件读入DataSet中,field由ConllLoader初始化时指定的headers决定。 @@ -127,7 +142,7 @@ class Conll2003Loader(ConllLoader): ins = {h: data[i] for i, h in enumerate(self.headers)} ds.append(Instance(**ins)) return ds - + def download(self, output_dir=None): raise RuntimeError("conll2003 cannot be downloaded automatically.") @@ -158,12 +173,13 @@ class Conll2003NERLoader(ConllLoader): "[...]", "[...]" """ + def __init__(self): headers = [ 'raw_words', 'target', ] super().__init__(headers=headers, indexes=[0, 3]) - + def _load(self, path): """ 传入的一个文件路径,将该文件读入DataSet中,field由ConllLoader初始化时指定的headers决定。 @@ -184,7 +200,7 @@ class Conll2003NERLoader(ConllLoader): ins = {h: data[i] for i, h in enumerate(self.headers)} ds.append(Instance(**ins)) return ds - + def download(self): raise RuntimeError("conll2003 cannot be downloaded automatically.") @@ -204,13 +220,13 @@ class OntoNotesNERLoader(ConllLoader): "[...]", "[...]" """ - + def __init__(self): super().__init__(headers=[Const.RAW_WORD, Const.TARGET], indexes=[3, 10]) - - def _load(self, path:str): + + def _load(self, path: str): dataset = super()._load(path) - + def convert_to_bio(tags): bio_tags = [] flag = None @@ -227,7 +243,7 @@ class OntoNotesNERLoader(ConllLoader): flag = None bio_tags.append(bio_label) return bio_tags - + def convert_word(words): converted_words = [] for word in words: @@ -236,7 +252,7 @@ class OntoNotesNERLoader(ConllLoader): converted_words.append(word) continue # 以下是由于这些符号被转义了,再转回来 - tfrs = {'-LRB-':'(', + tfrs = {'-LRB-': '(', '-RRB-': ')', '-LSB-': '[', '-RSB-': ']', @@ -248,12 +264,12 @@ class OntoNotesNERLoader(ConllLoader): else: converted_words.append(word) return converted_words - + dataset.apply_field(convert_word, field_name=Const.RAW_WORD, new_field_name=Const.RAW_WORD) dataset.apply_field(convert_to_bio, field_name=Const.TARGET, new_field_name=Const.TARGET) - + return dataset - + def download(self): raise RuntimeError("Ontonotes cannot be downloaded automatically, you can refer " "https://github.com/yhcc/OntoNotes-5.0-NER to download and preprocess.") @@ -262,13 +278,13 @@ class OntoNotesNERLoader(ConllLoader): class CTBLoader(Loader): def __init__(self): super().__init__() - - def _load(self, path:str): + + def _load(self, path: str): pass class CNNERLoader(Loader): - def _load(self, path:str): + def _load(self, path: str): """ 支持加载形如以下格式的内容,一行两列,以空格隔开两个sample @@ -331,10 +347,11 @@ class MsraNERLoader(CNNERLoader): "[...]", "[...]" """ + def __init__(self): super().__init__() - - def download(self, dev_ratio:float=0.1, re_download:bool=False)->str: + + def download(self, dev_ratio: float = 0.1, re_download: bool = False) -> str: """ 自动下载MSAR-NER的数据,如果你使用该数据,请引用 Gina-Anne Levow, 2006, The Third International Chinese Language Processing Bakeoff: Word Segmentation and Named Entity Recognition. @@ -356,7 +373,7 @@ class MsraNERLoader(CNNERLoader): if time.time() - modify_time > 1 and re_download: # 通过这种比较丑陋的方式判断一下文件是否是才下载的 shutil.rmtree(data_dir) data_dir = self._get_dataset_path(dataset_name=dataset_name) - + if not os.path.exists(os.path.join(data_dir, 'dev.conll')): if dev_ratio > 0: assert 0 < dev_ratio < 1, "dev_ratio should be in range (0,1)." @@ -380,15 +397,15 @@ class MsraNERLoader(CNNERLoader): finally: if os.path.exists(os.path.join(data_dir, 'middle_file.conll')): os.remove(os.path.join(data_dir, 'middle_file.conll')) - + return data_dir class WeiboNERLoader(CNNERLoader): def __init__(self): super().__init__() - - def download(self)->str: + + def download(self) -> str: """ 自动下载Weibo-NER的数据,如果你使用了该数据,请引用 Nanyun Peng and Mark Dredze, 2015, Named Entity Recognition for Chinese Social Media with Jointly Trained Embeddings. @@ -397,7 +414,7 @@ class WeiboNERLoader(CNNERLoader): """ dataset_name = 'weibo-ner' data_dir = self._get_dataset_path(dataset_name=dataset_name) - + return data_dir @@ -427,11 +444,12 @@ class PeopleDailyNERLoader(CNNERLoader): "[...]", "[...]" """ + def __init__(self): super().__init__() - + def download(self) -> str: dataset_name = 'peopledaily' data_dir = self._get_dataset_path(dataset_name=dataset_name) - + return data_dir diff --git a/fastNLP/io/loader/csv.py b/fastNLP/io/loader/csv.py index 5195cc8e..0d6e35fa 100644 --- a/fastNLP/io/loader/csv.py +++ b/fastNLP/io/loader/csv.py @@ -1,7 +1,13 @@ +"""undocumented""" + +__all__ = [ + "CSVLoader", +] + +from .loader import Loader +from ..file_reader import _read_csv from ...core.dataset import DataSet from ...core.instance import Instance -from ..file_reader import _read_csv -from .loader import Loader class CSVLoader(Loader): diff --git a/fastNLP/io/loader/cws.py b/fastNLP/io/loader/cws.py index fab7639c..2fbb1091 100644 --- a/fastNLP/io/loader/cws.py +++ b/fastNLP/io/loader/cws.py @@ -1,11 +1,18 @@ -from .loader import Loader -from ...core.dataset import DataSet -from ...core.instance import Instance +"""undocumented""" + +__all__ = [ + "CWSLoader" +] + import glob import os -import time -import shutil import random +import shutil +import time + +from .loader import Loader +from ...core.dataset import DataSet +from ...core.instance import Instance class CWSLoader(Loader): diff --git a/fastNLP/io/loader/json.py b/fastNLP/io/loader/json.py index 8856b73a..012dee5a 100644 --- a/fastNLP/io/loader/json.py +++ b/fastNLP/io/loader/json.py @@ -1,7 +1,13 @@ +"""undocumented""" + +__all__ = [ + "JsonLoader" +] + +from .loader import Loader +from ..file_reader import _read_json from ...core.dataset import DataSet from ...core.instance import Instance -from ..file_reader import _read_json -from .loader import Loader class JsonLoader(Loader): diff --git a/fastNLP/io/loader/loader.py b/fastNLP/io/loader/loader.py index e7b419ac..22636a27 100644 --- a/fastNLP/io/loader/loader.py +++ b/fastNLP/io/loader/loader.py @@ -1,8 +1,15 @@ -from ...core.dataset import DataSet -from .. import DataBundle -from ..utils import check_loader_paths +"""undocumented""" + +__all__ = [ + "Loader" +] + from typing import Union, Dict + +from .. import DataBundle from ..file_utils import _get_dataset_url, get_cache_path, cached_path +from ..utils import check_loader_paths +from ...core.dataset import DataSet class Loader: diff --git a/fastNLP/io/loader/matching.py b/fastNLP/io/loader/matching.py index 26455914..7f03ca3e 100644 --- a/fastNLP/io/loader/matching.py +++ b/fastNLP/io/loader/matching.py @@ -1,10 +1,21 @@ +"""undocumented""" + +__all__ = [ + "MNLILoader", + "SNLILoader", + "QNLILoader", + "RTELoader", + "QuoraLoader", +] + +import os import warnings -from .loader import Loader +from typing import Union, Dict + from .json import JsonLoader -from ...core.const import Const +from .loader import Loader from .. import DataBundle -import os -from typing import Union, Dict +from ...core.const import Const from ...core.dataset import DataSet from ...core.instance import Instance @@ -22,10 +33,11 @@ class MNLILoader(Loader): "...", "...","." """ + def __init__(self): super().__init__() - - def _load(self, path:str): + + def _load(self, path: str): ds = DataSet() with open(path, 'r', encoding='utf-8') as f: f.readline() # 跳过header @@ -50,8 +62,8 @@ class MNLILoader(Loader): if raw_words1 and raw_words2 and target: ds.append(Instance(raw_words1=raw_words1, raw_words2=raw_words2, target=target)) return ds - - def load(self, paths:str=None): + + def load(self, paths: str = None): """ :param str paths: 传入数据所在目录,会在该目录下寻找dev_matched.tsv, dev_mismatched.tsv, test_matched.tsv, @@ -64,13 +76,13 @@ class MNLILoader(Loader): paths = self.download() if not os.path.isdir(paths): raise NotADirectoryError(f"{paths} is not a valid directory.") - - files = {'dev_matched':"dev_matched.tsv", - "dev_mismatched":"dev_mismatched.tsv", - "test_matched":"test_matched.tsv", - "test_mismatched":"test_mismatched.tsv", - "train":'train.tsv'} - + + files = {'dev_matched': "dev_matched.tsv", + "dev_mismatched": "dev_mismatched.tsv", + "test_matched": "test_matched.tsv", + "test_mismatched": "test_mismatched.tsv", + "train": 'train.tsv'} + datasets = {} for name, filename in files.items(): filepath = os.path.join(paths, filename) @@ -78,11 +90,11 @@ class MNLILoader(Loader): if 'test' not in name: raise FileNotFoundError(f"{name} not found in directory {filepath}.") datasets[name] = self._load(filepath) - + data_bundle = DataBundle(datasets=datasets) - + return data_bundle - + def download(self): """ 如果你使用了这个数据,请引用 @@ -106,14 +118,15 @@ class SNLILoader(JsonLoader): "...", "...", "." """ + def __init__(self): super().__init__(fields={ 'sentence1': Const.RAW_WORDS(0), 'sentence2': Const.RAW_WORDS(1), 'gold_label': Const.TARGET, }) - - def load(self, paths: Union[str, Dict[str, str]]=None) -> DataBundle: + + def load(self, paths: Union[str, Dict[str, str]] = None) -> DataBundle: """ 从指定一个或多个路径中的文件中读取数据,返回:class:`~fastNLP.io.DataBundle` 。 @@ -138,11 +151,11 @@ class SNLILoader(JsonLoader): paths = _paths else: raise NotADirectoryError(f"{paths} is not a valid directory.") - + datasets = {name: self._load(path) for name, path in paths.items()} data_bundle = DataBundle(datasets=datasets) return data_bundle - + def download(self): """ 如果您的文章使用了这份数据,请引用 @@ -169,12 +182,13 @@ class QNLILoader(JsonLoader): test数据集没有target列 """ + def __init__(self): super().__init__() - + def _load(self, path): ds = DataSet() - + with open(path, 'r', encoding='utf-8') as f: f.readline() # 跳过header if path.endswith("test.tsv"): @@ -198,7 +212,7 @@ class QNLILoader(JsonLoader): if raw_words1 and raw_words2 and target: ds.append(Instance(raw_words1=raw_words1, raw_words2=raw_words2, target=target)) return ds - + def download(self): """ 如果您的实验使用到了该数据,请引用 @@ -225,12 +239,13 @@ class RTELoader(Loader): test数据集没有target列 """ + def __init__(self): super().__init__() - - def _load(self, path:str): + + def _load(self, path: str): ds = DataSet() - + with open(path, 'r', encoding='utf-8') as f: f.readline() # 跳过header if path.endswith("test.tsv"): @@ -254,7 +269,7 @@ class RTELoader(Loader): if raw_words1 and raw_words2 and target: ds.append(Instance(raw_words1=raw_words1, raw_words2=raw_words2, target=target)) return ds - + def download(self): return self._get_dataset_path('rte') @@ -281,12 +296,13 @@ class QuoraLoader(Loader): "...","." """ + def __init__(self): super().__init__() - - def _load(self, path:str): + + def _load(self, path: str): ds = DataSet() - + with open(path, 'r', encoding='utf-8') as f: for line in f: line = line.strip() @@ -298,6 +314,6 @@ class QuoraLoader(Loader): if raw_words1 and raw_words2 and target: ds.append(Instance(raw_words1=raw_words1, raw_words2=raw_words2, target=target)) return ds - + def download(self): raise RuntimeError("Quora cannot be downloaded automatically.") diff --git a/fastNLP/io/pipe/classification.py b/fastNLP/io/pipe/classification.py index f42d5400..30c591a4 100644 --- a/fastNLP/io/pipe/classification.py +++ b/fastNLP/io/pipe/classification.py @@ -1,26 +1,39 @@ +"""undocumented""" + +__all__ = [ + "YelpFullPipe", + "YelpPolarityPipe", + "SSTPipe", + "SST2Pipe", + 'IMDBPipe' +] + +import re + from nltk import Tree +from .pipe import Pipe +from .utils import get_tokenizer, _indexize, _add_words_field, _drop_empty_instance from ..data_bundle import DataBundle -from ...core.vocabulary import Vocabulary -from ...core.const import Const from ..loader.classification import IMDBLoader, YelpFullLoader, SSTLoader, SST2Loader, YelpPolarityLoader +from ...core.const import Const from ...core.dataset import DataSet from ...core.instance import Instance +from ...core.vocabulary import Vocabulary -from .utils import get_tokenizer, _indexize, _add_words_field, _drop_empty_instance -from .pipe import Pipe -import re nonalpnum = re.compile('[^0-9a-zA-Z?!\']+') + class _CLSPipe(Pipe): """ 分类问题的基类,负责对classification的数据进行tokenize操作。默认是对raw_words列操作,然后生成words列 """ - def __init__(self, tokenizer:str='spacy', lang='en'): + + def __init__(self, tokenizer: str = 'spacy', lang='en'): self.tokenizer = get_tokenizer(tokenizer, lang=lang) - + def _tokenize(self, data_bundle, field_name=Const.INPUT, new_field_name=None): """ 将DataBundle中的数据进行tokenize @@ -33,9 +46,9 @@ class _CLSPipe(Pipe): new_field_name = new_field_name or field_name for name, dataset in data_bundle.datasets.items(): dataset.apply_field(self.tokenizer, field_name=field_name, new_field_name=new_field_name) - + return data_bundle - + def _granularize(self, data_bundle, tag_map): """ 该函数对data_bundle中'target'列中的内容进行转换。 @@ -47,9 +60,9 @@ class _CLSPipe(Pipe): """ for name in list(data_bundle.datasets.keys()): dataset = data_bundle.get_dataset(name) - dataset.apply_field(lambda target:tag_map.get(target, -100), field_name=Const.TARGET, + dataset.apply_field(lambda target: tag_map.get(target, -100), field_name=Const.TARGET, new_field_name=Const.TARGET) - dataset.drop(lambda ins:ins[Const.TARGET] == -100) + dataset.drop(lambda ins: ins[Const.TARGET] == -100) data_bundle.set_dataset(dataset, name) return data_bundle @@ -69,7 +82,7 @@ def _clean_str(words): t = ''.join(tt) if t != '': words_collection.append(t) - + return words_collection @@ -89,19 +102,20 @@ class YelpFullPipe(_CLSPipe): 1、2归为1类,3归为1类,4、5归为1类;若为5, 则有5分类问题。 :param str tokenizer: 使用哪种tokenize方式将数据切成单词。支持'spacy'和'raw'。raw使用空格作为切分。 """ - def __init__(self, lower:bool=False, granularity=5, tokenizer:str='spacy'): + + def __init__(self, lower: bool = False, granularity=5, tokenizer: str = 'spacy'): super().__init__(tokenizer=tokenizer, lang='en') self.lower = lower assert granularity in (2, 3, 5), "granularity can only be 2,3,5." self.granularity = granularity - - if granularity==2: + + if granularity == 2: self.tag_map = {"1": 0, "2": 0, "4": 1, "5": 1} - elif granularity==3: - self.tag_map = {"1": 0, "2": 0, "3":1, "4": 2, "5": 2} + elif granularity == 3: + self.tag_map = {"1": 0, "2": 0, "3": 1, "4": 2, "5": 2} else: self.tag_map = {"1": 0, "2": 1, "3": 2, "4": 3, "5": 4} - + def _tokenize(self, data_bundle, field_name=Const.INPUT, new_field_name=None): """ 将DataBundle中的数据进行tokenize @@ -116,7 +130,7 @@ class YelpFullPipe(_CLSPipe): dataset.apply_field(self.tokenizer, field_name=field_name, new_field_name=new_field_name) dataset.apply_field(_clean_str, field_name=field_name, new_field_name=new_field_name) return data_bundle - + def process(self, data_bundle): """ 传入的DataSet应该具备如下的结构 @@ -131,30 +145,30 @@ class YelpFullPipe(_CLSPipe): :param data_bundle: :return: """ - + # 复制一列words data_bundle = _add_words_field(data_bundle, lower=self.lower) - + # 进行tokenize data_bundle = self._tokenize(data_bundle=data_bundle, field_name=Const.INPUT) - + # 根据granularity设置tag data_bundle = self._granularize(data_bundle, tag_map=self.tag_map) - + # 删除空行 data_bundle = _drop_empty_instance(data_bundle, field_name=Const.INPUT) - + # index data_bundle = _indexize(data_bundle=data_bundle) - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUT) - + data_bundle.set_input(Const.INPUT, Const.INPUT_LEN) data_bundle.set_target(Const.TARGET) - + return data_bundle - + def process_from_file(self, paths=None): """ @@ -179,27 +193,28 @@ class YelpPolarityPipe(_CLSPipe): :param bool lower: 是否对输入进行小写化。 :param str tokenizer: 使用哪种tokenize方式将数据切成单词。支持'spacy'和'raw'。raw使用空格作为切分。 """ - def __init__(self, lower:bool=False, tokenizer:str='spacy'): + + def __init__(self, lower: bool = False, tokenizer: str = 'spacy'): super().__init__(tokenizer=tokenizer, lang='en') self.lower = lower - + def process(self, data_bundle): # 复制一列words data_bundle = _add_words_field(data_bundle, lower=self.lower) - + # 进行tokenize data_bundle = self._tokenize(data_bundle=data_bundle, field_name=Const.INPUT) # index data_bundle = _indexize(data_bundle=data_bundle) - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUT) - + data_bundle.set_input(Const.INPUT, Const.INPUT_LEN) data_bundle.set_target(Const.TARGET) - + return data_bundle - + def process_from_file(self, paths=None): """ @@ -230,7 +245,7 @@ class SSTPipe(_CLSPipe): 0、1归为1类,2归为1类,3、4归为1类;若为5, 则有5分类问题。 :param str tokenizer: 使用哪种tokenize方式将数据切成单词。支持'spacy'和'raw'。raw使用空格作为切分。 """ - + def __init__(self, subtree=False, train_subtree=True, lower=False, granularity=5, tokenizer='spacy'): super().__init__(tokenizer=tokenizer, lang='en') self.subtree = subtree @@ -238,15 +253,15 @@ class SSTPipe(_CLSPipe): self.lower = lower assert granularity in (2, 3, 5), "granularity can only be 2,3,5." self.granularity = granularity - - if granularity==2: + + if granularity == 2: self.tag_map = {"0": 0, "1": 0, "3": 1, "4": 1} - elif granularity==3: - self.tag_map = {"0": 0, "1": 0, "2":1, "3": 2, "4": 2} + elif granularity == 3: + self.tag_map = {"0": 0, "1": 0, "2": 1, "3": 2, "4": 2} else: self.tag_map = {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4} - - def process(self, data_bundle:DataBundle): + + def process(self, data_bundle: DataBundle): """ 对DataBundle中的数据进行预处理。输入的DataSet应该至少拥有raw_words这一列,且内容类似与 @@ -277,26 +292,26 @@ class SSTPipe(_CLSPipe): instance = Instance(raw_words=' '.join(tree.leaves()), target=tree.label()) ds.append(instance) data_bundle.set_dataset(ds, name) - + _add_words_field(data_bundle, lower=self.lower) - + # 进行tokenize data_bundle = self._tokenize(data_bundle=data_bundle, field_name=Const.INPUT) - + # 根据granularity设置tag data_bundle = self._granularize(data_bundle, tag_map=self.tag_map) - + # index data_bundle = _indexize(data_bundle=data_bundle) - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUT) - + data_bundle.set_input(Const.INPUT, Const.INPUT_LEN) data_bundle.set_target(Const.TARGET) - + return data_bundle - + def process_from_file(self, paths=None): data_bundle = SSTLoader().load(paths) return self.process(data_bundle=data_bundle) @@ -316,11 +331,12 @@ class SST2Pipe(_CLSPipe): :param bool lower: 是否对输入进行小写化。 :param str tokenizer: 使用哪种tokenize方式将数据切成单词。支持'spacy'和'raw'。raw使用空格作为切分。 """ + def __init__(self, lower=False, tokenizer='spacy'): super().__init__(tokenizer=tokenizer, lang='en') self.lower = lower - - def process(self, data_bundle:DataBundle): + + def process(self, data_bundle: DataBundle): """ 可以处理的DataSet应该具备如下的结构 @@ -335,15 +351,15 @@ class SST2Pipe(_CLSPipe): :return: """ _add_words_field(data_bundle, self.lower) - + data_bundle = self._tokenize(data_bundle=data_bundle) - + src_vocab = Vocabulary() src_vocab.from_dataset(data_bundle.datasets['train'], field_name=Const.INPUT, - no_create_entry_dataset=[dataset for name,dataset in data_bundle.datasets.items() if + no_create_entry_dataset=[dataset for name, dataset in data_bundle.datasets.items() if name != 'train']) src_vocab.index_dataset(*data_bundle.datasets.values(), field_name=Const.INPUT) - + tgt_vocab = Vocabulary(unknown=None, padding=None) tgt_vocab.from_dataset(data_bundle.datasets['train'], field_name=Const.TARGET) datasets = [] @@ -351,18 +367,18 @@ class SST2Pipe(_CLSPipe): if dataset.has_field(Const.TARGET): datasets.append(dataset) tgt_vocab.index_dataset(*datasets, field_name=Const.TARGET) - + data_bundle.set_vocab(src_vocab, Const.INPUT) data_bundle.set_vocab(tgt_vocab, Const.TARGET) - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUT) - + data_bundle.set_input(Const.INPUT, Const.INPUT_LEN) data_bundle.set_target(Const.TARGET) - + return data_bundle - + def process_from_file(self, paths=None): """ @@ -390,11 +406,12 @@ class IMDBPipe(_CLSPipe): :param bool lower: 是否将words列的数据小写。 :param str tokenizer: 使用什么tokenizer来将句子切分为words. 支持spacy, raw两种。raw即使用空格拆分。 """ - def __init__(self, lower:bool=False, tokenizer:str='spacy'): + + def __init__(self, lower: bool = False, tokenizer: str = 'spacy'): super().__init__(tokenizer=tokenizer, lang='en') self.lower = lower - - def process(self, data_bundle:DataBundle): + + def process(self, data_bundle: DataBundle): """ 期待的DataBunlde中输入的DataSet应该类似于如下,有两个field,raw_words和target,且均为str类型 @@ -409,25 +426,26 @@ class IMDBPipe(_CLSPipe): target列应该为str。 :return: DataBundle """ + # 替换
def replace_br(raw_words): raw_words = raw_words.replace("
", ' ') return raw_words - + for name, dataset in data_bundle.datasets.items(): dataset.apply_field(replace_br, field_name=Const.RAW_WORD, new_field_name=Const.RAW_WORD) - + _add_words_field(data_bundle, lower=self.lower) self._tokenize(data_bundle, field_name=Const.INPUT, new_field_name=Const.INPUT) _indexize(data_bundle) - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUT) dataset.set_input(Const.INPUT, Const.INPUT_LEN) dataset.set_target(Const.TARGET) - + return data_bundle - + def process_from_file(self, paths=None): """ @@ -437,8 +455,5 @@ class IMDBPipe(_CLSPipe): # 读取数据 data_bundle = IMDBLoader().load(paths) data_bundle = self.process(data_bundle) - + return data_bundle - - - diff --git a/fastNLP/io/pipe/conll.py b/fastNLP/io/pipe/conll.py index 617d1236..2efec8e0 100644 --- a/fastNLP/io/pipe/conll.py +++ b/fastNLP/io/pipe/conll.py @@ -1,13 +1,25 @@ +"""undocumented""" + +__all__ = [ + "Conll2003NERPipe", + "Conll2003Pipe", + "OntoNotesNERPipe", + "MsraNERPipe", + "PeopleDailyPipe", + "WeiboNERPipe" +] + from .pipe import Pipe -from .. import DataBundle +from .utils import _add_chars_field +from .utils import _indexize, _add_words_field from .utils import iob2, iob2bioes -from ...core.const import Const +from .. import DataBundle from ..loader.conll import Conll2003NERLoader, OntoNotesNERLoader -from .utils import _indexize, _add_words_field -from .utils import _add_chars_field from ..loader.conll import PeopleDailyNERLoader, WeiboNERLoader, MsraNERLoader, ConllLoader +from ...core.const import Const from ...core.vocabulary import Vocabulary + class _NERPipe(Pipe): """ NER任务的处理Pipe, 该Pipe会(1)复制raw_words列,并命名为words; (2)在words, target列建立词表 @@ -20,14 +32,14 @@ class _NERPipe(Pipe): :param: str encoding_type: target列使用什么类型的encoding方式,支持bioes, bio两种。 :param bool lower: 是否将words小写化后再建立词表,绝大多数情况都不需要设置为True。 """ - + def __init__(self, encoding_type: str = 'bio', lower: bool = False): if encoding_type == 'bio': self.convert_tag = iob2 else: self.convert_tag = lambda words: iob2bioes(iob2(words)) self.lower = lower - + def process(self, data_bundle: DataBundle) -> DataBundle: """ 支持的DataSet的field为 @@ -46,21 +58,21 @@ class _NERPipe(Pipe): # 转换tag for name, dataset in data_bundle.datasets.items(): dataset.apply_field(self.convert_tag, field_name=Const.TARGET, new_field_name=Const.TARGET) - + _add_words_field(data_bundle, lower=self.lower) - + # index _indexize(data_bundle) - + input_fields = [Const.TARGET, Const.INPUT, Const.INPUT_LEN] target_fields = [Const.TARGET, Const.INPUT_LEN] - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUT) - + data_bundle.set_input(*input_fields) data_bundle.set_target(*target_fields) - + return data_bundle @@ -84,7 +96,7 @@ class Conll2003NERPipe(_NERPipe): :param: str encoding_type: target列使用什么类型的encoding方式,支持bioes, bio两种。 :param bool lower: 是否将words小写化后再建立词表,绝大多数情况都不需要设置为True。 """ - + def process_from_file(self, paths) -> DataBundle: """ @@ -94,7 +106,7 @@ class Conll2003NERPipe(_NERPipe): # 读取数据 data_bundle = Conll2003NERLoader().load(paths) data_bundle = self.process(data_bundle) - + return data_bundle @@ -125,8 +137,8 @@ class Conll2003Pipe(Pipe): else: self.ner_convert_tag = lambda tags: iob2bioes(iob2(tags)) self.lower = lower - - def process(self, data_bundle)->DataBundle: + + def process(self, data_bundle) -> DataBundle: """ 输入的DataSet应该类似于如下的形式 @@ -145,9 +157,9 @@ class Conll2003Pipe(Pipe): dataset.drop(lambda x: "-DOCSTART-" in x[Const.RAW_WORD]) dataset.apply_field(self.chunk_convert_tag, field_name='chunk', new_field_name='chunk') dataset.apply_field(self.ner_convert_tag, field_name='ner', new_field_name='ner') - + _add_words_field(data_bundle, lower=self.lower) - + # index _indexize(data_bundle, input_field_names=Const.INPUT, target_field_names=['pos', 'ner']) # chunk中存在一些tag只在dev中出现,没在train中 @@ -155,18 +167,18 @@ class Conll2003Pipe(Pipe): tgt_vocab.from_dataset(*data_bundle.datasets.values(), field_name='chunk') tgt_vocab.index_dataset(*data_bundle.datasets.values(), field_name='chunk') data_bundle.set_vocab(tgt_vocab, 'chunk') - + input_fields = [Const.INPUT, Const.INPUT_LEN] target_fields = ['pos', 'ner', 'chunk', Const.INPUT_LEN] - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUT) - + data_bundle.set_input(*input_fields) data_bundle.set_target(*target_fields) - + return data_bundle - + def process_from_file(self, paths): """ @@ -194,7 +206,7 @@ class OntoNotesNERPipe(_NERPipe): :param: str encoding_type: target列使用什么类型的encoding方式,支持bioes, bio两种。 :param bool lower: 是否将words小写化后再建立词表,绝大多数情况都不需要设置为True。 """ - + def process_from_file(self, paths): data_bundle = OntoNotesNERLoader().load(paths) return self.process(data_bundle) @@ -211,13 +223,13 @@ class _CNNERPipe(Pipe): :param: str encoding_type: target列使用什么类型的encoding方式,支持bioes, bio两种。 """ - + def __init__(self, encoding_type: str = 'bio'): if encoding_type == 'bio': self.convert_tag = iob2 else: self.convert_tag = lambda words: iob2bioes(iob2(words)) - + def process(self, data_bundle: DataBundle) -> DataBundle: """ 支持的DataSet的field为 @@ -239,21 +251,21 @@ class _CNNERPipe(Pipe): # 转换tag for name, dataset in data_bundle.datasets.items(): dataset.apply_field(self.convert_tag, field_name=Const.TARGET, new_field_name=Const.TARGET) - + _add_chars_field(data_bundle, lower=False) - + # index _indexize(data_bundle, input_field_names=Const.CHAR_INPUT, target_field_names=Const.TARGET) - + input_fields = [Const.TARGET, Const.CHAR_INPUT, Const.INPUT_LEN] target_fields = [Const.TARGET, Const.INPUT_LEN] - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.CHAR_INPUT) - + data_bundle.set_input(*input_fields) data_bundle.set_target(*target_fields) - + return data_bundle @@ -272,6 +284,7 @@ class MsraNERPipe(_CNNERPipe): target。返回的DataSet中被设置为input有chars, target, seq_len; 设置为target有target。 """ + def process_from_file(self, paths=None) -> DataBundle: data_bundle = MsraNERLoader().load(paths) return self.process(data_bundle) @@ -291,6 +304,7 @@ class PeopleDailyPipe(_CNNERPipe): raw_chars列为List[str], 是未转换的原始数据; chars列为List[int],是转换为index的输入数据; target列是List[int],是转换为index的 target。返回的DataSet中被设置为input有chars, target, seq_len; 设置为target有target。 """ + def process_from_file(self, paths=None) -> DataBundle: data_bundle = PeopleDailyNERLoader().load(paths) return self.process(data_bundle) @@ -312,6 +326,7 @@ class WeiboNERPipe(_CNNERPipe): :param: str encoding_type: target列使用什么类型的encoding方式,支持bioes, bio两种。 """ + def process_from_file(self, paths=None) -> DataBundle: data_bundle = WeiboNERLoader().load(paths) return self.process(data_bundle) diff --git a/fastNLP/io/pipe/cws.py b/fastNLP/io/pipe/cws.py index 4ca0219c..748cf10a 100644 --- a/fastNLP/io/pipe/cws.py +++ b/fastNLP/io/pipe/cws.py @@ -1,3 +1,9 @@ +"""undocumented""" + +__all__ = [ + "CWSPipe" +] + import re from itertools import chain diff --git a/fastNLP/io/pipe/matching.py b/fastNLP/io/pipe/matching.py index ffa6375b..699438c8 100644 --- a/fastNLP/io/pipe/matching.py +++ b/fastNLP/io/pipe/matching.py @@ -1,9 +1,25 @@ +"""undocumented""" + +__all__ = [ + "MatchingBertPipe", + "RTEBertPipe", + "SNLIBertPipe", + "QuoraBertPipe", + "QNLIBertPipe", + "MNLIBertPipe", + "MatchingPipe", + "RTEPipe", + "SNLIPipe", + "QuoraPipe", + "QNLIPipe", + "MNLIPipe", +] from .pipe import Pipe from .utils import get_tokenizer +from ..loader.matching import SNLILoader, MNLILoader, QNLILoader, RTELoader, QuoraLoader from ...core.const import Const from ...core.vocabulary import Vocabulary -from ..loader.matching import SNLILoader, MNLILoader, QNLILoader, RTELoader, QuoraLoader class MatchingBertPipe(Pipe): @@ -24,12 +40,13 @@ class MatchingBertPipe(Pipe): :param bool lower: 是否将word小写化。 :param str tokenizer: 使用什么tokenizer来将句子切分为words. 支持spacy, raw两种。raw即使用空格拆分。 """ - def __init__(self, lower=False, tokenizer: str='raw'): + + def __init__(self, lower=False, tokenizer: str = 'raw'): super().__init__() - + self.lower = bool(lower) self.tokenizer = get_tokenizer(tokenizer=tokenizer) - + def _tokenize(self, data_bundle, field_names, new_field_names): """ @@ -43,62 +60,62 @@ class MatchingBertPipe(Pipe): dataset.apply_field(lambda words: self.tokenizer(words), field_name=field_name, new_field_name=new_field_name) return data_bundle - + def process(self, data_bundle): for dataset in data_bundle.datasets.values(): if dataset.has_field(Const.TARGET): dataset.drop(lambda x: x[Const.TARGET] == '-') - + for name, dataset in data_bundle.datasets.items(): dataset.copy_field(Const.RAW_WORDS(0), Const.INPUTS(0), ) dataset.copy_field(Const.RAW_WORDS(1), Const.INPUTS(1), ) - + if self.lower: for name, dataset in data_bundle.datasets.items(): dataset[Const.INPUTS(0)].lower() dataset[Const.INPUTS(1)].lower() - + data_bundle = self._tokenize(data_bundle, [Const.INPUTS(0), Const.INPUTS(1)], [Const.INPUTS(0), Const.INPUTS(1)]) - + # concat两个words def concat(ins): words0 = ins[Const.INPUTS(0)] words1 = ins[Const.INPUTS(1)] words = words0 + ['[SEP]'] + words1 return words - + for name, dataset in data_bundle.datasets.items(): dataset.apply(concat, new_field_name=Const.INPUT) dataset.delete_field(Const.INPUTS(0)) dataset.delete_field(Const.INPUTS(1)) - + word_vocab = Vocabulary() word_vocab.from_dataset(*[dataset for name, dataset in data_bundle.datasets.items() if 'train' in name], field_name=Const.INPUT, no_create_entry_dataset=[dataset for name, dataset in data_bundle.datasets.items() if 'train' not in name]) word_vocab.index_dataset(*data_bundle.datasets.values(), field_name=Const.INPUT) - + target_vocab = Vocabulary(padding=None, unknown=None) target_vocab.from_dataset(data_bundle.datasets['train'], field_name=Const.TARGET) has_target_datasets = [dataset for name, dataset in data_bundle.datasets.items() if dataset.has_field(Const.TARGET)] target_vocab.index_dataset(*has_target_datasets, field_name=Const.TARGET) - + data_bundle.set_vocab(word_vocab, Const.INPUT) data_bundle.set_vocab(target_vocab, Const.TARGET) - + input_fields = [Const.INPUT, Const.INPUT_LEN] target_fields = [Const.TARGET] - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUT) dataset.set_input(*input_fields, flag=True) for fields in target_fields: if dataset.has_field(fields): dataset.set_target(fields, flag=True) - + return data_bundle @@ -150,12 +167,13 @@ class MatchingPipe(Pipe): :param bool lower: 是否将所有raw_words转为小写。 :param str tokenizer: 将原始数据tokenize的方式。支持spacy, raw. spacy是使用spacy切分,raw就是用空格切分。 """ - def __init__(self, lower=False, tokenizer: str='raw'): + + def __init__(self, lower=False, tokenizer: str = 'raw'): super().__init__() - + self.lower = bool(lower) self.tokenizer = get_tokenizer(tokenizer=tokenizer) - + def _tokenize(self, data_bundle, field_names, new_field_names): """ @@ -169,7 +187,7 @@ class MatchingPipe(Pipe): dataset.apply_field(lambda words: self.tokenizer(words), field_name=field_name, new_field_name=new_field_name) return data_bundle - + def process(self, data_bundle): """ 接受的DataBundle中的DataSet应该具有以下的field, target列可以没有 @@ -186,35 +204,35 @@ class MatchingPipe(Pipe): """ data_bundle = self._tokenize(data_bundle, [Const.RAW_WORDS(0), Const.RAW_WORDS(1)], [Const.INPUTS(0), Const.INPUTS(1)]) - + for dataset in data_bundle.datasets.values(): if dataset.has_field(Const.TARGET): dataset.drop(lambda x: x[Const.TARGET] == '-') - + if self.lower: for name, dataset in data_bundle.datasets.items(): dataset[Const.INPUTS(0)].lower() dataset[Const.INPUTS(1)].lower() - + word_vocab = Vocabulary() word_vocab.from_dataset(*[dataset for name, dataset in data_bundle.datasets.items() if 'train' in name], field_name=[Const.INPUTS(0), Const.INPUTS(1)], no_create_entry_dataset=[dataset for name, dataset in data_bundle.datasets.items() if 'train' not in name]) word_vocab.index_dataset(*data_bundle.datasets.values(), field_name=[Const.INPUTS(0), Const.INPUTS(1)]) - + target_vocab = Vocabulary(padding=None, unknown=None) target_vocab.from_dataset(data_bundle.datasets['train'], field_name=Const.TARGET) has_target_datasets = [dataset for name, dataset in data_bundle.datasets.items() if dataset.has_field(Const.TARGET)] target_vocab.index_dataset(*has_target_datasets, field_name=Const.TARGET) - + data_bundle.set_vocab(word_vocab, Const.INPUTS(0)) data_bundle.set_vocab(target_vocab, Const.TARGET) - + input_fields = [Const.INPUTS(0), Const.INPUTS(1), Const.INPUT_LENS(0), Const.INPUT_LENS(1)] target_fields = [Const.TARGET] - + for name, dataset in data_bundle.datasets.items(): dataset.add_seq_len(Const.INPUTS(0), Const.INPUT_LENS(0)) dataset.add_seq_len(Const.INPUTS(1), Const.INPUT_LENS(1)) @@ -222,7 +240,7 @@ class MatchingPipe(Pipe): for fields in target_fields: if dataset.has_field(fields): dataset.set_target(fields, flag=True) - + return data_bundle @@ -254,4 +272,3 @@ class MNLIPipe(MatchingPipe): def process_from_file(self, paths=None): data_bundle = MNLILoader().load(paths) return self.process(data_bundle) - diff --git a/fastNLP/io/pipe/pipe.py b/fastNLP/io/pipe/pipe.py index cc45dee4..a1435fd3 100644 --- a/fastNLP/io/pipe/pipe.py +++ b/fastNLP/io/pipe/pipe.py @@ -1,3 +1,9 @@ +"""undocumented""" + +__all__ = [ + "Pipe", +] + from .. import DataBundle diff --git a/fastNLP/io/pipe/utils.py b/fastNLP/io/pipe/utils.py index 8facd8d9..f32f58b7 100644 --- a/fastNLP/io/pipe/utils.py +++ b/fastNLP/io/pipe/utils.py @@ -1,8 +1,18 @@ +"""undocumented""" + +__all__ = [ + "iob2", + "iob2bioes", + "get_tokenizer", +] + from typing import List -from ...core.vocabulary import Vocabulary + from ...core.const import Const +from ...core.vocabulary import Vocabulary + -def iob2(tags:List[str])->List[str]: +def iob2(tags: List[str]) -> List[str]: """ 检查数据是否是合法的IOB数据,如果是IOB1会被自动转换为IOB2。两种格式的区别见 https://datascience.stackexchange.com/questions/37824/difference-between-iob-and-iob2-format @@ -25,7 +35,8 @@ def iob2(tags:List[str])->List[str]: tags[i] = "B" + tag[1:] return tags -def iob2bioes(tags:List[str])->List[str]: + +def iob2bioes(tags: List[str]) -> List[str]: """ 将iob的tag转换为bioes编码 :param tags: @@ -38,12 +49,12 @@ def iob2bioes(tags:List[str])->List[str]: else: split = tag.split('-')[0] if split == 'B': - if i+1!=len(tags) and tags[i+1].split('-')[0] == 'I': + if i + 1 != len(tags) and tags[i + 1].split('-')[0] == 'I': new_tags.append(tag) else: new_tags.append(tag.replace('B-', 'S-')) elif split == 'I': - if i + 1List[str]: return new_tags -def get_tokenizer(tokenizer:str, lang='en'): +def get_tokenizer(tokenizer: str, lang='en'): """ :param str tokenizer: 获取tokenzier方法 @@ -97,13 +108,13 @@ def _indexize(data_bundle, input_field_names=Const.INPUT, target_field_names=Con name != 'train']) src_vocab.index_dataset(*data_bundle.datasets.values(), field_name=input_field_name) data_bundle.set_vocab(src_vocab, input_field_name) - + for target_field_name in target_field_names: tgt_vocab = Vocabulary(unknown=None, padding=None) tgt_vocab.from_dataset(data_bundle.datasets['train'], field_name=target_field_name) tgt_vocab.index_dataset(*data_bundle.datasets.values(), field_name=target_field_name) data_bundle.set_vocab(tgt_vocab, target_field_name) - + return data_bundle @@ -116,7 +127,7 @@ def _add_words_field(data_bundle, lower=False): :return: 传入的DataBundle """ data_bundle.copy_field(field_name=Const.RAW_WORD, new_field_name=Const.INPUT, ignore_miss_dataset=True) - + if lower: for name, dataset in data_bundle.datasets.items(): dataset[Const.INPUT].lower() @@ -132,7 +143,7 @@ def _add_chars_field(data_bundle, lower=False): :return: 传入的DataBundle """ data_bundle.copy_field(field_name=Const.RAW_CHAR, new_field_name=Const.CHAR_INPUT, ignore_miss_dataset=True) - + if lower: for name, dataset in data_bundle.datasets.items(): dataset[Const.CHAR_INPUT].lower() @@ -147,6 +158,7 @@ def _drop_empty_instance(data_bundle, field_name): :param str field_name: 对哪个field进行检查,如果为None,则任意field为空都会删掉 :return: 传入的DataBundle """ + def empty_instance(ins): if field_name: field_value = ins[field_name] @@ -157,10 +169,8 @@ def _drop_empty_instance(data_bundle, field_name): if field_value in ((), {}, [], ''): return True return False - + for name, dataset in data_bundle.datasets.items(): dataset.drop(empty_instance) - + return data_bundle - - diff --git a/fastNLP/io/utils.py b/fastNLP/io/utils.py index faec2a55..e1de2ae7 100644 --- a/fastNLP/io/utils.py +++ b/fastNLP/io/utils.py @@ -1,10 +1,20 @@ -import os +""" +.. todo:: + doc +""" -from typing import Union, Dict +__all__ = [ + "check_loader_paths" +] + +import os from pathlib import Path +from typing import Union, Dict + from ..core import logger -def check_loader_paths(paths:Union[str, Dict[str, str]])->Dict[str, str]: + +def check_loader_paths(paths: Union[str, Dict[str, str]]) -> Dict[str, str]: """ 检查传入dataloader的文件的合法性。如果为合法路径,将返回至少包含'train'这个key的dict。类似于下面的结果:: @@ -33,11 +43,13 @@ def check_loader_paths(paths:Union[str, Dict[str, str]])->Dict[str, str]: path_pair = ('train', filename) if 'dev' in filename: if path_pair: - raise Exception("File:{} in {} contains bot `{}` and `dev`.".format(filename, paths, path_pair[0])) + raise Exception( + "File:{} in {} contains bot `{}` and `dev`.".format(filename, paths, path_pair[0])) path_pair = ('dev', filename) if 'test' in filename: if path_pair: - raise Exception("File:{} in {} contains bot `{}` and `test`.".format(filename, paths, path_pair[0])) + raise Exception( + "File:{} in {} contains bot `{}` and `test`.".format(filename, paths, path_pair[0])) path_pair = ('test', filename) if path_pair: files[path_pair[0]] = os.path.join(paths, path_pair[1]) @@ -46,7 +58,7 @@ def check_loader_paths(paths:Union[str, Dict[str, str]])->Dict[str, str]: return files else: raise FileNotFoundError(f"{paths} is not a valid file path.") - + elif isinstance(paths, dict): if paths: if 'train' not in paths: @@ -65,6 +77,7 @@ def check_loader_paths(paths:Union[str, Dict[str, str]])->Dict[str, str]: else: raise TypeError(f"paths only supports str and dict. not {type(paths)}.") + def get_tokenizer(): try: import spacy From efa9496d09d139658683eec0b4a6ae44b93dd88c Mon Sep 17 00:00:00 2001 From: ChenXin Date: Mon, 26 Aug 2019 10:25:51 +0800 Subject: [PATCH 136/139] add __all__ and __doc__ for all files in module 'models', using 'undocumented' tags --- fastNLP/models/base_model.py | 4 ++++ fastNLP/models/bert.py | 8 ++++++-- fastNLP/models/cnn_text_classification.py | 7 ++++++- fastNLP/models/enas_controller.py | 9 +++++++-- fastNLP/models/enas_model.py | 5 ++++- fastNLP/models/enas_trainer.py | 14 +++++++++----- fastNLP/models/enas_utils.py | 8 ++++++-- fastNLP/models/sequence_labeling.py | 12 ++++++------ fastNLP/models/snli.py | 7 +++++-- 9 files changed, 53 insertions(+), 21 deletions(-) diff --git a/fastNLP/models/base_model.py b/fastNLP/models/base_model.py index 2646d580..61edb91f 100644 --- a/fastNLP/models/base_model.py +++ b/fastNLP/models/base_model.py @@ -1,3 +1,7 @@ +"""undocumented""" + +__all__ = [] + import torch from ..modules.decoder.mlp import MLP diff --git a/fastNLP/models/bert.py b/fastNLP/models/bert.py index 3afccc14..0a89b765 100644 --- a/fastNLP/models/bert.py +++ b/fastNLP/models/bert.py @@ -1,16 +1,20 @@ -""" +"""undocumented bert.py is modified from huggingface/pytorch-pretrained-BERT, which is licensed under the Apache License 2.0. """ + +__all__ = [] + import os + import torch from torch import nn from .base_model import BaseModel from ..core.const import Const +from ..core.utils import seq_len_to_mask from ..modules.encoder import BertModel from ..modules.encoder.bert import BertConfig, CONFIG_FILE -from ..core.utils import seq_len_to_mask class BertForSequenceClassification(BaseModel): diff --git a/fastNLP/models/cnn_text_classification.py b/fastNLP/models/cnn_text_classification.py index e00a0697..37a60c35 100644 --- a/fastNLP/models/cnn_text_classification.py +++ b/fastNLP/models/cnn_text_classification.py @@ -1,3 +1,8 @@ +""" +.. todo:: + doc +""" + __all__ = [ "CNNText" ] @@ -7,8 +12,8 @@ import torch.nn as nn from ..core.const import Const as C from ..core.utils import seq_len_to_mask -from ..modules import encoder from ..embeddings import embedding +from ..modules import encoder class CNNText(torch.nn.Module): diff --git a/fastNLP/models/enas_controller.py b/fastNLP/models/enas_controller.py index e83c6b51..eec820e4 100644 --- a/fastNLP/models/enas_controller.py +++ b/fastNLP/models/enas_controller.py @@ -1,5 +1,10 @@ -# Code Modified from https://github.com/carpedm20/ENAS-pytorch -"""A module with NAS controller-related code.""" +"""undocumented +Code Modified from https://github.com/carpedm20/ENAS-pytorch +A module with NAS controller-related code. +""" + +__all__ = [] + import collections import os diff --git a/fastNLP/models/enas_model.py b/fastNLP/models/enas_model.py index b6b683c0..2e8ca713 100644 --- a/fastNLP/models/enas_model.py +++ b/fastNLP/models/enas_model.py @@ -1,7 +1,10 @@ -""" +"""undocumented Module containing the shared RNN model. Code Modified from https://github.com/carpedm20/ENAS-pytorch """ + +__all__ = [] + import collections import numpy as np diff --git a/fastNLP/models/enas_trainer.py b/fastNLP/models/enas_trainer.py index 7abcc45f..98d778cd 100644 --- a/fastNLP/models/enas_trainer.py +++ b/fastNLP/models/enas_trainer.py @@ -1,11 +1,15 @@ -# Code Modified from https://github.com/carpedm20/ENAS-pytorch +"""undocumented +Code Modified from https://github.com/carpedm20/ENAS-pytorch +""" + +__all__ = [] + import math -import numpy as np import time -import torch - from datetime import datetime, timedelta +import numpy as np +import torch from torch.optim import Adam try: @@ -15,7 +19,7 @@ except: from ..core.trainer import Trainer from ..core.batch import DataSetIter -from ..core.callback import CallbackManager, CallbackException +from ..core.callback import CallbackException from ..core.dataset import DataSet from ..core.utils import _move_dict_value_to_device from . import enas_utils as utils diff --git a/fastNLP/models/enas_utils.py b/fastNLP/models/enas_utils.py index 4e402a9a..cd6c2503 100644 --- a/fastNLP/models/enas_utils.py +++ b/fastNLP/models/enas_utils.py @@ -1,7 +1,11 @@ -# Code Modified from https://github.com/carpedm20/ENAS-pytorch +"""undocumented +Code Modified from https://github.com/carpedm20/ENAS-pytorch +""" + +__all__ = [] -from collections import defaultdict import collections +from collections import defaultdict import numpy as np import torch diff --git a/fastNLP/models/sequence_labeling.py b/fastNLP/models/sequence_labeling.py index 4bf3f95f..0dff21f0 100644 --- a/fastNLP/models/sequence_labeling.py +++ b/fastNLP/models/sequence_labeling.py @@ -1,5 +1,5 @@ """ - 本模块实现了几种序列标注模型 +本模块实现了几种序列标注模型 """ __all__ = [ "SeqLabeling", @@ -12,14 +12,14 @@ import torch.nn as nn import torch.nn.functional as F from .base_model import BaseModel -from ..embeddings import embedding -from ..modules import decoder, encoder -from ..modules.decoder.crf import allowed_transitions -from ..core.utils import seq_len_to_mask from ..core.const import Const as C -from ..modules import LSTM +from ..core.utils import seq_len_to_mask +from ..embeddings import embedding from ..embeddings import get_embeddings from ..modules import ConditionalRandomField +from ..modules import LSTM +from ..modules import decoder, encoder +from ..modules.decoder.crf import allowed_transitions class BiLSTMCRF(BaseModel): diff --git a/fastNLP/models/snli.py b/fastNLP/models/snli.py index 3be942e8..5ca4052d 100644 --- a/fastNLP/models/snli.py +++ b/fastNLP/models/snli.py @@ -1,3 +1,7 @@ +""" +.. todo:: + doc +""" __all__ = [ "ESIM" ] @@ -5,13 +9,12 @@ __all__ = [ import torch import torch.nn as nn import torch.nn.functional as F - from torch.nn import CrossEntropyLoss from .base_model import BaseModel -from ..embeddings.embedding import TokenEmbedding, Embedding from ..core.const import Const from ..core.utils import seq_len_to_mask +from ..embeddings.embedding import TokenEmbedding, Embedding class ESIM(BaseModel): From 2cf9c0ebb1722aae734ceb971b889c43198729a2 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Mon, 26 Aug 2019 10:26:55 +0800 Subject: [PATCH 137/139] add __all__ and __doc__ for all files in module 'modules', using 'undocumented' tags --- fastNLP/modules/decoder/__init__.py | 6 +++- fastNLP/modules/decoder/crf.py | 5 +++- fastNLP/modules/decoder/mlp.py | 2 ++ fastNLP/modules/decoder/utils.py | 2 ++ fastNLP/modules/dropout.py | 6 +++- fastNLP/modules/encoder/__init__.py | 10 +++++-- fastNLP/modules/encoder/_elmo.py | 4 ++- fastNLP/modules/encoder/attention.py | 2 ++ fastNLP/modules/encoder/bert.py | 8 +++--- fastNLP/modules/encoder/char_encoder.py | 2 ++ fastNLP/modules/encoder/conv_maxpool.py | 2 ++ fastNLP/modules/encoder/lstm.py | 3 +- fastNLP/modules/encoder/pooling.py | 2 ++ fastNLP/modules/encoder/star_transformer.py | 3 +- fastNLP/modules/encoder/transformer.py | 2 ++ fastNLP/modules/encoder/variational_rnn.py | 3 +- fastNLP/modules/utils.py | 32 ++++++++++++++------- 17 files changed, 69 insertions(+), 25 deletions(-) diff --git a/fastNLP/modules/decoder/__init__.py b/fastNLP/modules/decoder/__init__.py index 664618b2..57acb172 100644 --- a/fastNLP/modules/decoder/__init__.py +++ b/fastNLP/modules/decoder/__init__.py @@ -1,3 +1,7 @@ +""" +.. todo:: + doc +""" __all__ = [ "MLP", "ConditionalRandomField", @@ -6,6 +10,6 @@ __all__ = [ ] from .crf import ConditionalRandomField +from .crf import allowed_transitions from .mlp import MLP from .utils import viterbi_decode -from .crf import allowed_transitions diff --git a/fastNLP/modules/decoder/crf.py b/fastNLP/modules/decoder/crf.py index 9f19afef..b47d0162 100644 --- a/fastNLP/modules/decoder/crf.py +++ b/fastNLP/modules/decoder/crf.py @@ -1,3 +1,5 @@ +"""undocumented""" + __all__ = [ "ConditionalRandomField", "allowed_transitions" @@ -9,13 +11,14 @@ from torch import nn from ..utils import initial_parameter from ...core import Vocabulary + def allowed_transitions(id2target, encoding_type='bio', include_start_end=False): """ 别名::class:`fastNLP.modules.allowed_transitions` :class:`fastNLP.modules.decoder.allowed_transitions` 给定一个id到label的映射表,返回所有可以跳转的(from_tag_id, to_tag_id)列表。 - :param dict,Vocabulary id2target: key是label的indices,value是str类型的tag或tag-label。value可以是只有tag的, 比如"B", "M"; 也可以是 + :param dict, ~fastNLP.Vocabulary id2target: key是label的indices,value是str类型的tag或tag-label。value可以是只有tag的, 比如"B", "M"; 也可以是 "B-NN", "M-NN", tag和label之间一定要用"-"隔开。一般可以通过Vocabulary.idx2word得到id2label。 :param str encoding_type: 支持"bio", "bmes", "bmeso", "bioes"。 :param bool include_start_end: 是否包含开始与结尾的转换。比如在bio中,b/o可以在开头,但是i不能在开头; diff --git a/fastNLP/modules/decoder/mlp.py b/fastNLP/modules/decoder/mlp.py index 9d9d80f2..f6e687a7 100644 --- a/fastNLP/modules/decoder/mlp.py +++ b/fastNLP/modules/decoder/mlp.py @@ -1,3 +1,5 @@ +"""undocumented""" + __all__ = [ "MLP" ] diff --git a/fastNLP/modules/decoder/utils.py b/fastNLP/modules/decoder/utils.py index 3d5ac3f8..118b1414 100644 --- a/fastNLP/modules/decoder/utils.py +++ b/fastNLP/modules/decoder/utils.py @@ -1,3 +1,5 @@ +"""undocumented""" + __all__ = [ "viterbi_decode" ] diff --git a/fastNLP/modules/dropout.py b/fastNLP/modules/dropout.py index 0ea2a2d9..24c20cc6 100644 --- a/fastNLP/modules/dropout.py +++ b/fastNLP/modules/dropout.py @@ -1,4 +1,8 @@ -__all__ = [] +"""undocumented""" + +__all__ = [ + "TimestepDropout" +] import torch diff --git a/fastNLP/modules/encoder/__init__.py b/fastNLP/modules/encoder/__init__.py index 1e99a0fd..0dfc18de 100644 --- a/fastNLP/modules/encoder/__init__.py +++ b/fastNLP/modules/encoder/__init__.py @@ -1,3 +1,8 @@ +""" +.. todo:: + doc +""" + __all__ = [ # "BertModel", @@ -24,13 +29,12 @@ __all__ = [ "MultiHeadAttention", ] +from .attention import MultiHeadAttention from .bert import BertModel from .char_encoder import ConvolutionCharEncoder, LSTMCharEncoder from .conv_maxpool import ConvMaxpool from .lstm import LSTM +from .pooling import MaxPool, MaxPoolWithMask, AvgPool, AvgPoolWithMask from .star_transformer import StarTransformer from .transformer import TransformerEncoder from .variational_rnn import VarRNN, VarLSTM, VarGRU - -from .pooling import MaxPool, MaxPoolWithMask, AvgPool, AvgPoolWithMask -from .attention import MultiHeadAttention diff --git a/fastNLP/modules/encoder/_elmo.py b/fastNLP/modules/encoder/_elmo.py index befae8bc..554cf8a9 100644 --- a/fastNLP/modules/encoder/_elmo.py +++ b/fastNLP/modules/encoder/_elmo.py @@ -1,7 +1,9 @@ -""" +"""undocumented 这个页面的代码大量参考了 allenNLP """ +__all__ = [] + from typing import Optional, Tuple, List, Callable import torch diff --git a/fastNLP/modules/encoder/attention.py b/fastNLP/modules/encoder/attention.py index fe3f7fd8..02bd078a 100644 --- a/fastNLP/modules/encoder/attention.py +++ b/fastNLP/modules/encoder/attention.py @@ -1,3 +1,5 @@ +"""undocumented""" + __all__ = [ "MultiHeadAttention" ] diff --git a/fastNLP/modules/encoder/bert.py b/fastNLP/modules/encoder/bert.py index b74c4da0..5026f48a 100644 --- a/fastNLP/modules/encoder/bert.py +++ b/fastNLP/modules/encoder/bert.py @@ -1,4 +1,4 @@ -""" +"""undocumented 这个页面的代码很大程度上参考(复制粘贴)了https://github.com/huggingface/pytorch-pretrained-BERT的代码, 如果你发现该代码对你 有用,也请引用一下他们。 """ @@ -8,17 +8,17 @@ __all__ = [ ] import collections - -import unicodedata import copy import json import math import os +import unicodedata import torch from torch import nn -from ...core import logger + from ..utils import _get_file_name_base_on_postfix +from ...core import logger CONFIG_FILE = 'bert_config.json' VOCAB_NAME = 'vocab.txt' diff --git a/fastNLP/modules/encoder/char_encoder.py b/fastNLP/modules/encoder/char_encoder.py index 6a6e1470..e40bd0dd 100644 --- a/fastNLP/modules/encoder/char_encoder.py +++ b/fastNLP/modules/encoder/char_encoder.py @@ -1,3 +1,5 @@ +"""undocumented""" + __all__ = [ "ConvolutionCharEncoder", "LSTMCharEncoder" diff --git a/fastNLP/modules/encoder/conv_maxpool.py b/fastNLP/modules/encoder/conv_maxpool.py index 8ce6b163..68415189 100644 --- a/fastNLP/modules/encoder/conv_maxpool.py +++ b/fastNLP/modules/encoder/conv_maxpool.py @@ -1,3 +1,5 @@ +"""undocumented""" + __all__ = [ "ConvMaxpool" ] diff --git a/fastNLP/modules/encoder/lstm.py b/fastNLP/modules/encoder/lstm.py index e2358132..1f3eae6d 100644 --- a/fastNLP/modules/encoder/lstm.py +++ b/fastNLP/modules/encoder/lstm.py @@ -1,7 +1,8 @@ -""" +"""undocumented 轻量封装的 Pytorch LSTM 模块. 可在 forward 时传入序列的长度, 自动对padding做合适的处理. """ + __all__ = [ "LSTM" ] diff --git a/fastNLP/modules/encoder/pooling.py b/fastNLP/modules/encoder/pooling.py index d8aa54ad..b1272284 100644 --- a/fastNLP/modules/encoder/pooling.py +++ b/fastNLP/modules/encoder/pooling.py @@ -1,3 +1,5 @@ +"""undocumented""" + __all__ = [ "MaxPool", "MaxPoolWithMask", diff --git a/fastNLP/modules/encoder/star_transformer.py b/fastNLP/modules/encoder/star_transformer.py index 3927a494..02d7a6a0 100644 --- a/fastNLP/modules/encoder/star_transformer.py +++ b/fastNLP/modules/encoder/star_transformer.py @@ -1,6 +1,7 @@ -""" +"""undocumented Star-Transformer 的encoder部分的 Pytorch 实现 """ + __all__ = [ "StarTransformer" ] diff --git a/fastNLP/modules/encoder/transformer.py b/fastNLP/modules/encoder/transformer.py index bc488e54..ce9172d5 100644 --- a/fastNLP/modules/encoder/transformer.py +++ b/fastNLP/modules/encoder/transformer.py @@ -1,3 +1,5 @@ +"""undocumented""" + __all__ = [ "TransformerEncoder" ] diff --git a/fastNLP/modules/encoder/variational_rnn.py b/fastNLP/modules/encoder/variational_rnn.py index 8e5e804b..933555c8 100644 --- a/fastNLP/modules/encoder/variational_rnn.py +++ b/fastNLP/modules/encoder/variational_rnn.py @@ -1,6 +1,7 @@ -""" +"""undocumented Variational RNN 的 Pytorch 实现 """ + __all__ = [ "VarRNN", "VarLSTM", diff --git a/fastNLP/modules/utils.py b/fastNLP/modules/utils.py index ead75711..09574782 100644 --- a/fastNLP/modules/utils.py +++ b/fastNLP/modules/utils.py @@ -1,10 +1,20 @@ +""" +.. todo:: + doc +""" + +__all__ = [ + "initial_parameter", + "summary" +] + +import os from functools import reduce import torch import torch.nn as nn import torch.nn.init as init -import glob -import os + def initial_parameter(net, initial_method=None): """A method used to initialize the weights of PyTorch models. @@ -40,7 +50,7 @@ def initial_parameter(net, initial_method=None): init_method = init.uniform_ else: init_method = init.xavier_normal_ - + def weights_init(m): # classname = m.__class__.__name__ if isinstance(m, nn.Conv2d) or isinstance(m, nn.Conv1d) or isinstance(m, nn.Conv3d): # for all the cnn @@ -66,7 +76,7 @@ def initial_parameter(net, initial_method=None): else: init.normal_(w.data) # bias # print("init else") - + net.apply(weights_init) @@ -79,11 +89,11 @@ def summary(model: nn.Module): """ train = [] nontrain = [] - + def layer_summary(module: nn.Module): def count_size(sizes): - return reduce(lambda x, y: x*y, sizes) - + return reduce(lambda x, y: x * y, sizes) + for p in module.parameters(recurse=False): if p.requires_grad: train.append(count_size(p.shape)) @@ -91,7 +101,7 @@ def summary(model: nn.Module): nontrain.append(count_size(p.shape)) for subm in module.children(): layer_summary(subm) - + layer_summary(model) total_train = sum(train) total_nontrain = sum(nontrain) @@ -101,7 +111,7 @@ def summary(model: nn.Module): strings.append('Trainable params: {:,}'.format(total_train)) strings.append('Non-trainable params: {:,}'.format(total_nontrain)) max_len = len(max(strings, key=len)) - bar = '-'*(max_len + 3) + bar = '-' * (max_len + 3) strings = [bar] + strings + [bar] print('\n'.join(strings)) return total, total_train, total_nontrain @@ -128,9 +138,9 @@ def _get_file_name_base_on_postfix(dir_path, postfix): :param postfix: 形如".bin", ".json"等 :return: str,文件的路径 """ - files = list(filter(lambda filename:filename.endswith(postfix), os.listdir(os.path.join(dir_path)))) + files = list(filter(lambda filename: filename.endswith(postfix), os.listdir(os.path.join(dir_path)))) if len(files) == 0: raise FileNotFoundError(f"There is no file endswith *{postfix} file in {dir_path}") elif len(files) > 1: raise FileExistsError(f"There are multiple *{postfix} files in {dir_path}") - return os.path.join(dir_path, files[0]) \ No newline at end of file + return os.path.join(dir_path, files[0]) From e1f234841cf763839c767ebf4d6e750c5391adb4 Mon Sep 17 00:00:00 2001 From: ChenXin Date: Mon, 26 Aug 2019 11:00:45 +0800 Subject: [PATCH 138/139] mark the dataloader.__init__ as undocumented --- fastNLP/io/data_loader/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastNLP/io/data_loader/__init__.py b/fastNLP/io/data_loader/__init__.py index b3ca9021..8a9dd60b 100644 --- a/fastNLP/io/data_loader/__init__.py +++ b/fastNLP/io/data_loader/__init__.py @@ -1,4 +1,4 @@ -""" +"""undocumented .. warning:: 本模块在 `0.5.0版本` 中被废弃,由 :mod:`~fastNLP.io.loader` 和 :mod:`~fastNLP.io.pipe` 模块替代。 From ffd5fd813559cee2930f5d0d0274357fb151cc4c Mon Sep 17 00:00:00 2001 From: ChenXin Date: Mon, 26 Aug 2019 11:58:20 +0800 Subject: [PATCH 139/139] delete the old doc-tool --- docs/format.py | 68 -------------------------------------------------- 1 file changed, 68 deletions(-) delete mode 100644 docs/format.py diff --git a/docs/format.py b/docs/format.py deleted file mode 100644 index 67671ae7..00000000 --- a/docs/format.py +++ /dev/null @@ -1,68 +0,0 @@ -import os - - -def shorten(file, to_delete, cut=False): - if file.endswith("index.rst") or file.endswith("conf.py"): - return - res = [] - with open(file, "r") as fin: - lines = fin.readlines() - for line in lines: - if cut and line.rstrip() == "Submodules": - break - else: - res.append(line.rstrip()) - for i, line in enumerate(res): - if line.endswith(" package"): - res[i] = res[i][:-len(" package")] - res[i + 1] = res[i + 1][:-len(" package")] - elif line.endswith(" module"): - res[i] = res[i][:-len(" module")] - res[i + 1] = res[i + 1][:-len(" module")] - else: - for name in to_delete: - if line.endswith(name): - res[i] = "del" - - with open(file, "w") as fout: - for line in res: - if line != "del": - print(line, file=fout) - - -def clear(path='./source/'): - files = os.listdir(path) - to_delete = [ - "fastNLP.core.dist_trainer", - "fastNLP.core.predictor", - - "fastNLP.io.file_reader", - "fastNLP.io.config_io", - - "fastNLP.embeddings.contextual_embedding", - - "fastNLP.modules.dropout", - "fastNLP.models.base_model", - "fastNLP.models.bert", - "fastNLP.models.enas_utils", - "fastNLP.models.enas_controller", - "fastNLP.models.enas_model", - "fastNLP.models.enas_trainer", - ] - for file in files: - if not os.path.isdir(path + file): - res = file.split('.') - if len(res) > 4: - to_delete.append(file[:-4]) - elif len(res) == 4: - shorten(path + file, to_delete, True) - else: - shorten(path + file, to_delete) - for file in to_delete: - try: - os.remove(path + file + ".rst") - except: - pass - - -clear()

3T4-|gOZwwT~ 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 n35=Evz`@zkbUbP0l+XkKF7nb9 literal 336236 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%0AVFfebQkty{&fodb_g; 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%0DeW>cs!yO|P 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