增加新的tutorial; 删除各embedding中requires_grad的设置,统一到基类实现

This commit is contained in:
yh 2019-09-10 10:38:02 +08:00
parent 2fc14790c7
commit 917cedf808
20 changed files with 812 additions and 432 deletions

View File

@ -1,21 +1,20 @@
==============================
使用DataSet预处理文本
DataSet
==============================
:class:`~fastNLP.DataSet` 是fastNLP中用于承载数据的容器。可以将DataSet看做是一个表格
每一行是一个sample (在fastNLP中被称为 :mod:`~fastNLP.core.instance` )
每一列是一个feature (在fastNLP中称为 :mod:`~fastNLP.core.field` )
:class:`~fastNLP.DataSet` 是fastNLP用于承载数据的类,一般训练集、验证集和测试集会被加载为三个单独的:class:`~fastNLP.DataSet`对象。
:class:`~fastNLP.DataSet`中的数据组织形式类似一个表格,比如下面 :class:`~fastNLP.DataSet` 一共有3列列在fastNLP中被称为field
.. csv-table::
:header: "sentence", "words", "seq_len"
:header: "raw_chars", "chars", "seq_len"
"This is the first instance .", "[This, is, the, first, instance, .]", 6
"Second instance .", "[Second, instance, .]", 3
"历任公司副总经理、总工程师,", "[历 任 公 司 副 总 经 理 、 总 工 程 师 ]", 6
"Third instance .", "[Third, instance, .]", 3
"...", "[...]", "..."
上面是一个样例数据中 DataSet 的存储结构。其中它的每一行是一个 :class:`~fastNLP.Instance` 对象; 每一列是一个 :class:`~fastNLP.FieldArray` 对象。
每一行是一个instance (在fastNLP中被称为 :mod:`~fastNLP.core.Instance` )
每一列是一个field (在fastNLP中称为 :mod:`~fastNLP.core.FieldArray` )。
-----------------------------
数据集构建和删除
@ -26,11 +25,23 @@
.. code-block:: python
from fastNLP import DataSet
data = {'sentence':["This is the first instance .", "Second instance .", "Third instance ."],
data = {'raw_words':["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
print(dataset)
输出为::
+------------------------------+------------------------------------------------+---------+
| raw_words | words | seq_len |
+------------------------------+------------------------------------------------+---------+
| This is the first instance . | ['this', 'is', 'the', 'first', 'instance', ... | 6 |
| Second instance . | ['Second', 'instance', '.'] | 3 |
| Third instance . | ['Third', 'instance', '.'] | 3 |
+------------------------------+------------------------------------------------+---------+
我们还可以使用 :func:`~fastNLP.DataSet.append` 方法向数据集内增加数据
@ -39,7 +50,7 @@
from fastNLP import DataSet
from fastNLP import Instance
dataset = DataSet()
instance = Instance(sentence="This is the first instance",
instance = Instance(raw_words="This is the first instance",
words=['this', 'is', 'the', 'first', 'instance', '.'],
seq_len=6)
dataset.append(instance)
@ -52,10 +63,10 @@
from fastNLP import DataSet
from fastNLP import Instance
dataset = DataSet([
Instance(sentence="This is the first instance",
Instance(raw_words="This is the first instance",
words=['this', 'is', 'the', 'first', 'instance', '.'],
seq_len=6),
Instance(sentence="Second instance .",
Instance(raw_words="Second instance .",
words=['Second', 'instance', '.'],
seq_len=3)
])
@ -106,24 +117,49 @@ FastNLP 同样提供了多种删除数据的方法 :func:`~fastNLP.DataSet.drop`
.. code-block:: python
from fastNLP import DataSet
data = {'sentence':["This is the first instance .", "Second instance .", "Third instance ."]}
data = {'raw_words':["This is the first instance .", "Second instance .", "Third instance ."]}
dataset = DataSet(data)
# 将句子分成单词形式, 详见DataSet.apply()方法
dataset.apply(lambda ins: ins['sentence'].split(), new_field_name='words')
dataset.apply(lambda ins: ins['raw_words'].split(), new_field_name='words')
# 或使用DataSet.apply_field()
dataset.apply_field(lambda sent:sent.split(), field_name='sentence', new_field_name='words')
dataset.apply_field(lambda sent:sent.split(), field_name='raw_words', new_field_name='words')
# 除了匿名函数,也可以定义函数传递进去
def get_words(instance):
sentence = instance['sentence']
sentence = instance['raw_words']
words = sentence.split()
return words
dataset.apply(get_words, new_field_name='words')
除了手动处理数据集之外,你还可以使用 fastNLP 提供的各种 :class:`~fastNLP.io.base_loader.DataSetLoader` 来进行数据处理。
详细请参考这篇教程 :doc:`使用DataSetLoader加载数据集 </tutorials/tutorial_2_load_dataset>`
除了手动处理数据集之外,你还可以使用 fastNLP 提供的各种 :class:`~fastNLP.io.Loader`:class:`~fastNLP.io.Pipe` 来进行数据处理。
详细请参考这篇教程 :doc:`使用Loader和Pipe处理数据 </tutorials/tutorial_2_load_dataset>`
-----------------------------
fastNLP中field的命名习惯
-----------------------------
在英文任务中fastNLP常用的field名称有:
- raw_words: 表示的是原始的str。例如"This is a demo sentence ."。存在多个raw_words的情况例如matching任务它们会被定义为
raw_words0, raw_words1。但在conll格式下raw_words列也可能为["This", "is", "a", "demo", "sentence", "."]的形式。
- words: 表示的是已经tokenize后的词语。例如["This", "is", "a", "demo", "sentence"], 但由于str并不能直接被神经网络所使用
所以words中的内容往往被转换为int如[3, 10, 4, 2, 7, ...]等。多列words的情况会被命名为words0, words1
- target: 表示目标值。分类场景下,只有一个值;序列标注场景下是一个序列。
- seq_len: 一般用于表示words列的长度
在中文任务中fastNLP常用的field名称有:
- raw_chars: 表示的是原始的连续汉字序列。例如"这是一个示例。"
- chars: 表示已经切分为单独的汉字的序列。例如["这", "是", "一", "个", "示", "例", "。"]。但由于神经网络不能识别汉字,所以一般
该列会被转为int形式如[3, 4, 5, 6, ...]。
- raw_words: 如果原始汉字序列中已经包含了词语的边界则该列称为raw_words。如"上海 浦东 开发 与 法制 建设 同步"。
- words: 表示单独的汉字词语序列。例如["上海", "", "浦东", "开发", "与", "法制", "建设", ...]或[2, 3, 4, ...]
- target: 表示目标值。分类场景下,只有一个值;序列标注场景下是一个序列。
- seq_len: 表示输入序列的长度
# TODO 这一段移动到datasetiter那里
-----------------------------
DataSet与pad

View File

@ -1,150 +0,0 @@
=======================================
使用Loader和Pipe加载并处理数据集
=======================================
这一部分是一个关于如何加载数据集的教程
教程目录:
- `Part I: 数据集容器DataBundle`_
- `Part II: 加载数据集的基类Loader`_
- `Part III: 不同格式类型的基础Loader`_
- `Part IV: 使用Pipe对数据集进行预处理`_
- `Part V: fastNLP封装好的Loader和Pipe`_
------------------------------------
Part I: 数据集容器DataBundle
------------------------------------
在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: 加载数据集的基类Loader
-------------------------------------
在fastNLP中我们采用 :class:`~fastNLP.io.loader.Loader` 来作为加载数据集的基类。
:class:`~fastNLP.io.loader.Loader` 定义了各种Loader所需的API接口开发者应该继承它实现各种的Loader。
在各种数据集的Loader当中至少应该编写如下内容:
- _load 函数:从一个数据文件中读取数据,返回一个 :class:`~fastNLP.DataSet`
- load 函数:从文件或者文件夹中读取数据并组装成 :class:`~fastNLP.io.data_bundle.DataBundle`
Loader的load函数返回的 :class:`~fastNLP.io.data_bundle.DataBundle` 里面包含了数据集的原始数据。
--------------------------------------------------------
Part III: 不同格式类型的基础Loader
--------------------------------------------------------
:class:`~fastNLP.io.loader.CSVLoader`
读取CSV类型的数据集文件。例子如下
.. code-block:: python
from fastNLP.io.loader import CSVLoader
data_set_loader = CSVLoader(
headers=('words', 'target'), sep='\t'
)
# 表示将CSV文件中每一行的第一项填入'words' field第二项填入'target' field。
# 其中每两项之间由'\t'分割开来
data_set = data_set_loader._load('path/to/your/file')
数据集内容样例如下 ::
But it does not leave you with much . 1
You could hate it for the same reason . 1
The performances are an absolute joy . 4
:class:`~fastNLP.io.loader.JsonLoader`
读取Json类型的数据集文件数据必须按行存储每行是一个包含各类属性的Json对象。例子如下
.. code-block:: python
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 = loader._load('path/to/your/file')
数据集内容样例如下 ::
{"annotator_labels": ["neutral"], "captionID": "3416050480.jpg#4", "gold_label": "neutral", "pairID": "3416050480.jpg#4r1n", "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 training his horse for a competition.", "sentence2_binary_parse": "( ( A person ) ( ( is ( ( training ( his horse ) ) ( for ( a competition ) ) ) ) . ) )", "sentence2_parse": "(ROOT (S (NP (DT A) (NN person)) (VP (VBZ is) (VP (VBG training) (NP (PRP$ his) (NN horse)) (PP (IN for) (NP (DT a) (NN competition))))) (. .)))"}
{"annotator_labels": ["contradiction"], "captionID": "3416050480.jpg#4", "gold_label": "contradiction", "pairID": "3416050480.jpg#4r1c", "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 at a diner, ordering an omelette.", "sentence2_binary_parse": "( ( A person ) ( ( ( ( is ( at ( a diner ) ) ) , ) ( ordering ( an omelette ) ) ) . ) )", "sentence2_parse": "(ROOT (S (NP (DT A) (NN person)) (VP (VBZ is) (PP (IN at) (NP (DT a) (NN diner))) (, ,) (S (VP (VBG ordering) (NP (DT an) (NN omelette))))) (. .)))"}
{"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: 使用Pipe对数据集进行预处理
------------------------------------------
在fastNLP中我们采用 :class:`~fastNLP.io.pipe.Pipe` 来作为加载数据集的基类。
:class:`~fastNLP.io.pipe.Pipe` 定义了各种Pipe所需的API接口开发者应该继承它实现各种的Pipe。
在各种数据集的Pipe当中至少应该编写如下内容:
- 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函数对数据进行预处理
以SNLI数据集为例写一个自定义Pipe的例子如下
.. code-block:: python
from fastNLP.io.loader import SNLILoader
from fastNLP.io.pipe import MatchingPipe
class MySNLIPipe(MatchingPipe):
def process(self, data_bundle):
data_bundle = super(MySNLIPipe, self).process(data_bundle)
# MatchingPipe类里封装了一个关于matching任务的process函数可以直接继承使用
# 如果有需要进行额外的预处理操作可以在这里加入您的代码
return data_bundle
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示例
.. code-block:: python
from fastNLP.io.pipe import SNLIBertPipe
data_bundle = SNLIBertPipe(lower=True, tokenizer=arg.tokenizer).process_from_file()
print(data_bundle)
输出的内容是::
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.
这里表示一共有3个数据集和2个词表。其中
- 3个数据集分别为train、dev、test数据集分别有549367、9842、9824个instance
- 2个词表分别为words词表与target词表。其中words词表为句子文本所构建的词表一共有34184个单词
target词表为目标标签所构建的词表一共有3种标签。如果有多个输入则句子文本所构建的词表将
会被命名为words1以对应相对应的列名
------------------------------------------
Part V: fastNLP封装好的Loader和Pipe
------------------------------------------
fastNLP封装了多种任务/数据集的Loader和Pipe并提供自动下载功能具体参见文档
`fastNLP可加载的embedding与数据集 <https://docs.qq.com/sheet/DVnpkTnF6VW9UeXdh?c=A1A0A0>`_

View File

@ -0,0 +1,131 @@
==============================
Vocabulary
==============================
:class:`~fastNLP.Vocabulary`是包含字或词与index关系的类用于将文本转换为index。
-----------------------------
构建Vocabulary
-----------------------------
.. code-block:: python
from fastNLP import Vocabulary
vocab = Vocabulary()
vocab.add_word_lst(['复', '旦', '大', '学']) # 加入新的字
vocab.add_word('上海') # `上海`会作为一个整体
vocab.to_index('复') # 应该会为3
vocab.to_index('我') # 会输出1Vocabulary中默认pad的index为0, unk(没有找到的词)的index为1
# 在构建target的Vocabulary时词表中应该用不上pad和unk可以通过以下的初始化
vocab = Vocabulary(unknown=None, pad=None)
vocab.add_word_lst(['positive', 'negative'])
vocab.to_index('positive') # 输出0
vocab.to_index('neutral') # 会报错
除了通过以上的方式建立词表Vocabulary还可以通过使用下面的函数直从 :class:`~fastNLP.DataSet` 中的某一列建立词表以及将该列转换为index
.. code-block:: python
from fastNLP import Vocabulary
from fastNLP import DataSet
dataset = DataSet({'chars': [
['今', '天', '天', '气', '很', '好', '。'],
['被', '这', '部', '电', '影', '浪', '费', '了', '两', '个', '小', '时', '。']
],
'target': ['neutral', 'negative']
})
vocab = Vocabulary()
vocab.from_dataset(dataset, field_name='chars')
vocab.index_dataset(dataset, field_name='chars')
target_vocab = Vocabulary(padding=None, unknown=None)
target_vocab.from_dataset(dataset, field_name='target')
target_vocab.index_dataset(dataset, field_name='target')
print(dataset)
输出内容为::
+---------------------------------------------------+--------+
| chars | target |
+---------------------------------------------------+--------+
| [4, 2, 2, 5, 6, 7, 3] | 0 |
| [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 3] | 1 |
+---------------------------------------------------+--------+
-----------------------------
一些使用tips
-----------------------------
在通过使用from_dataset()函数在DataSet上建立词表时将测试集和验证集放入参数no_create_entry_dataset中如下所示
.. code-block:: python
from fastNLP import Vocabulary
from fastNLP import DataSet
tr_data = DataSet({'chars': [
['今', '天', '心', '情', '很', '好', '。'],
['被', '这', '部', '电', '影', '浪', '费', '了', '两', '个', '小', '时', '。']
],
'target': ['positive', 'negative']
})
dev_data = DataSet({'chars': [
['住', '宿', '条', '件', '还', '不', '错'],
['糟', '糕', '的', '天', '气', '', '无', '法', '出', '行', '。']
],
'target': ['positive', 'negative']
})
vocab = Vocabulary()
# 将验证集或者测试集在建立词表是放入no_create_entry_dataset这个参数中。
vocab.from_dataset(tr_data, field_name='chars', no_create_entry_dataset=[dev_data])
:class:`~fastNLP.Vocabulary` 中的`no_create_entry`, 建议在添加来自于测试集和验证集的词的时候将该参数置为True, 或将验证集和测试集
传入`no_create_entry_dataset`参数。它们的意义是在接下来的模型会使用pretrain的embedding(包括glove, word2vec, elmo与bert)且会finetune的
情况下如果仅使用来自于train的数据建立vocabulary会导致只出现在test与dev中的词语无法充分利用到来自于预训练embedding的信息(因为他们
会被认为是unk)所以在建立词表的时候将test与dev考虑进来会使得最终的结果更好。通过与fastNLP中的各种Embedding配合使用会有如下的效果
如果一个词出现在了train中但是没在预训练模型中embedding会为随机初始化且它单独的一个vector如果finetune embedding的话
这个词在更新之后可能会有更好的表示; 而如果这个词仅出现在了dev或test中那么就不能为它们单独建立vector而应该让它指向unk这个vector的
值(当unk的值更新时这个词也使用的是更新之后的vector)。所以被认为是no_create_entry的token将首先从预训练的词表中寻找它的表示
果找到了,就使用该表示; 如果没有找到则认为该词的表示应该为unk的表示。
下面我们结合部分:code:`~fastNLP.embeddings.StaticEmbedding`的例子来说明下该值造成的影响,如果您对
:code:`~fastNLP.embeddings.StaticEmbedding`不太了解,您可以先参考\{Embedding教程的引用}部分再来阅读该部分
.. code-block:: python
import torch
from fastNLP.embeddings import StaticEmbedding
from fastNLP import Vocabulary
vocab = Vocabulary()
vocab.add_word('train')
vocab.add_word('only_in_train') # 仅在train出现但肯定在预训练词表中不存在
vocab.add_word('test', no_create_entry=True) # 该词只在dev或test中出现
vocab.add_word('only_in_test', no_create_entry=True) # 这个词肯定在预训练中找不到
embed = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50d')
print(embed(torch.LongTensor([vocab.to_index('train')])))
print(embed(torch.LongTensor([vocab.to_index('only_in_train')])))
print(embed(torch.LongTensor([vocab.to_index('test')])))
print(embed(torch.LongTensor([vocab.to_index('only_in_test')])))
print(embed(torch.LongTensor([vocab.unknown_idx])))
输出结果(只截取了部分vector)::
tensor([[ 0.9497, 0.3433, 0.8450, -0.8852, ...]], grad_fn=<EmbeddingBackward>) # train
tensor([[ 0.0540, -0.0557, -0.0514, -0.1688, ...]], grad_fn=<EmbeddingBackward>) # only_in_train
tensor([[ 0.1318, -0.2552, -0.0679, 0.2619, ...]], grad_fn=<EmbeddingBackward>) # test
tensor([[0., 0., 0., 0., 0., ...]], grad_fn=<EmbeddingBackward>) # only_in_test
tensor([[0., 0., 0., 0., 0., ...]], grad_fn=<EmbeddingBackward>) # unk
首先train和test都能够从预训练中找到对应的vector所以它们是各自的vector表示; only_in_train在预训练中找不到StaticEmbedding为它
新建了一个entry所以它有一个单独的vector; 而only_in_dev在预训练中找不到被指向了unk的值(fastNLP用零向量初始化unk)与最后一行unk的
表示相同。

View File

@ -7,161 +7,446 @@
教程目录:
- `Part I: embedding介绍`_
- `Part II: 使用随机初始化的embedding`_
- `Part III: 使用预训练的静态embedding`_
- `Part IV: 使用预训练的Contextual Embedding(ELMo & BERT)`_
- `Part V: 使用character-level的embedding`_
- `Part VI: 叠加使用多个embedding`_
- `Part VII: fastNLP支持的预训练Embedding`_
- `Part II: 使用预训练的静态embedding`_
- `Part III: 使用随机初始化的embedding`_
- `Part IV: ELMo Embedding`_
- `Part V: Bert Embedding`_
- `Part VI: 使用character-level的embedding`_
- `Part VII: 叠加使用多个embedding`_
- `Part VIII: Embedding的其它说明`_
- `Part IX: StaticEmbedding的使用建议`_
---------------------------------------
Part I: embedding介绍
---------------------------------------
与torch.nn.Embedding类似fastNLP的embedding接受的输入是一个被index好的序列输出的内容是这个序列的embedding结果。
fastNLP的embedding包括了预训练embedding和随机初始化embedding。
Embedding是一种词嵌入技术可以将字或者词转换为实向量。目前使用较多的预训练词嵌入有word2vec, fasttext, glove, character embedding,
elmo以及bert。
但使用这些词嵌入方式的时候都需要做一些加载上的处理比如预训练的word2vec, fasttext以及glove都有着超过几十万个词语的表示但一般任务大概
只会用到其中几万个词,如果直接加载所有的词汇,会导致内存占用变大以及运行速度变慢,需要从预训练文件中抽取本次实验的用到的词汇;而对于英文的
elmo和character embedding, 需要将word拆分成character才能使用Bert的使用更是涉及到了Byte pair encoding(BPE)相关的内容。为了方便
大家的使用fastNLP通过:class:`~fastNLP.Vocabulary`统一了不同embedding的使用。下面我们将讲述一些例子来说明一下
---------------------------------------
Part II: 使用随机初始化的embedding
Part II: 使用预训练的静态embedding
---------------------------------------
使用随机初始化的embedding参见 :class:`~fastNLP.embeddings.embedding.Embedding`
可以传入词表大小和embedding维度
在fastNLP中加载预训练的word2vec, glove以及fasttext都使用的是 :class:`~fastNLP.embeddings.StaticEmbedding`。另外,为了方便大家的
使用fastNLP提供了多种静态词向量的自动下载并缓存(默认缓存到~/.fastNLP/embeddings文件夹下)的功能,支持自动下载的预训练向量可以在
`<https://docs.qq.com/sheet/DVnpkTnF6VW9UeXdh?c=A1A0A0>`_
查看。
.. code-block:: python
from fastNLP import Embedding
embed = Embedding(10000, 50)
import torch
from fastNLP.embeddings import StaticEmbedding
from fastNLP import Vocabulary
也可以传入一个初始化的参数矩阵:
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
embed = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50d', requires_grad=True)
words = torch.LongTensor([[vocab.to_index(word) for word in "this is a demo .".split()]])
print(embed(words).size())
输出为::
torch.Size([1, 5, 50])
fastNLP的StaticEmbedding在初始化之后就和pytorch中的Embedding是类似的了。:class:`~fastNLP.embeddings.StaticEmbedding`的初始化
主要是从model_dir_or_name提供的词向量中抽取出:class:`~fastNLP.Vocabulary`中词语的vector。
除了可以通过使用预先提供的Embedding:class:`~fastNLP.embeddings.StaticEmbedding`也支持加载本地的预训练词向量glove, word2vec以及
fasttext格式的。通过将model_dir_or_name修改为本地的embedding文件路径即可使用本地的embedding。
---------------------------------------
Part III: 使用随机初始化的embedding
---------------------------------------
有时候需要使用随机初始化的Embedding也可以通过使用 :class:`~fastNLP.embeddings.StaticEmbedding`获得。只需要将model_dir_or_name
置为None且传入embedding_dim如下例所示
.. code-block:: python
from fastNLP import Embedding
embed = Embedding(init_embed)
from fastNLP.embeddings import StaticEmbedding
from fastNLP import Vocabulary
其中的init_embed可以是torch.FloatTensor、torch.nn.Embedding或者numpy.ndarray。
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
embed = StaticEmbedding(vocab, model_dir_or_name=None, embedding_dim=30)
---------------------------------------
Part III: 使用预训练的静态embedding
---------------------------------------
words = torch.LongTensor([[vocab.to_index(word) for word in "this is a demo .".split()]])
print(embed(words).size())
在使用预训练的embedding之前需要根据数据集的内容构建一个词表 :class:`~fastNLP.core.vocabulary.Vocabulary` ,在
预训练embedding类初始化的时候需要将这个词表作为参数传入。
输出为::
在fastNLP中我们提供了 :class:`~fastNLP.embeddings.StaticEmbedding` 这一个类。
通过 :class:`~fastNLP.embeddings.StaticEmbedding` 可以加载预训练好的静态
Embedding例子如下
torch.Size([1, 5, 30])
.. 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模型的名称
1 如果传入的是路径那么fastNLP将会根据该路径来读取预训练的权重文件并将embedding加载进来(glove
和word2vec类型的权重文件都支持)
2 如果传入的是模型名称那么fastNLP将会根据名称查找embedding模型如果在cache目录下找到模型则会
自动加载如果找不到则会自动下载到cache目录。默认的cache目录为 `~/.fastNLP` 文件夹。可以通过环境
变量 ``FASTNLP_CACHE_DIR`` 来自定义cache目录如::
$ FASTNLP_CACHE_DIR=~/fastnlp_cache_dir python your_python_file.py
这个命令表示fastNLP将会在 `~/fastnlp_cache_dir` 这个目录下寻找模型,找不到则会自动将模型下载到这个目录
-----------------------------------------------------------
Part IV: 使用预训练的Contextual Embedding(ELMo & BERT)
Part IV: ELMo Embedding
-----------------------------------------------------------
在fastNLP中我们提供了ELMo和BERT的embedding :class:`~fastNLP.embeddings.ElmoEmbedding`
:class:`~fastNLP.embeddings.BertEmbedding`
:class:`~fastNLP.embeddings.BertEmbedding` 。可自动下载的ElmoEmbedding可以
`<https://docs.qq.com/sheet/DVnpkTnF6VW9UeXdh?c=A1A0A0>`_找到。
与静态embedding类似ELMo的使用方法如下
.. code-block:: python
from fastNLP import ElmoEmbedding
embed = ElmoEmbedding(vocab, model_dir_or_name='small', requires_grad=False)
from fastNLP.embeddings import ElmoEmbedding
from fastNLP import Vocabulary
BERT-embedding的使用方法如下
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
embed = ElmoEmbedding(vocab, model_dir_or_name='en-small', requires_grad=False)
words = torch.LongTensor([[vocab.to_index(word) for word in "this is a demo .".split()]])
print(embed(words).size())
输出为::
torch.Size([1, 5, 256])
也可以输出多层的ELMo结果fastNLP将在不同层的结果在最后一维上拼接下面的代码需要在上面的代码执行结束之后执行
.. code-block:: python
from fastNLP import BertEmbedding
embed = BertEmbedding(
vocab, model_dir_or_name='en-base-cased', requires_grad=False, layers='4,-2,-1'
)
embed = ElmoEmbedding(vocab, model_dir_or_name='en-small', requires_grad=False, layers='1,2')
print(embed(words).size())
其中layers变量表示需要取哪几层的encode结果。
输出为::
torch.Size([1, 5, 512])
另外,根据`<https://arxiv.org/abs/1802.05365>`_不同层之间使用可学习的权重可以使得ELMo的效果更好在fastNLP中可以通过以下的初始化
实现3层输出的结果通过可学习的权重进行加法融合。
.. code-block:: python
embed = ElmoEmbedding(vocab, model_dir_or_name='en-small', requires_grad=True, layers='mix')
print(embed(words).size())
输出为::
torch.Size([1, 5, 256])
-----------------------------------------------------------
Part V: Bert Embedding
-----------------------------------------------------------
虽然Bert并不算严格意义上的Embedding但通过将Bert封装成Embedding的形式将极大减轻使用的复杂程度。可自动下载的Bert Embedding可以
`<https://docs.qq.com/sheet/DVnpkTnF6VW9UeXdh?c=A1A0A0>`_找到。我们将使用下面的例子讲述一下
BertEmbedding的使用
.. code-block:: python
from fastNLP.embeddings import BertEmbedding
from fastNLP import Vocabulary
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
embed = BertEmbedding(vocab, model_dir_or_name='en-base-cased')
words = torch.LongTensor([[vocab.to_index(word) for word in "this is a demo .".split()]])
print(embed(words).size())
输出为::
torch.Size([1, 5, 768])
可以通过申明使用指定层数的output也可以使用多层的output下面的代码需要在上面的代码执行结束之后执行
.. code-block:: python
# 使用后面两层的输出
embed = BertEmbedding(vocab, model_dir_or_name='en-base-cased', layers='10,11')
print(embed(words).size()) # 结果将是在最后一维做拼接
输出为::
torch.Size([1, 5, 1536])
在Bert中还存在两个特殊的字符[CLS]和[SEP],默认情况下这两个字符是自动加入并且在计算结束之后会自动删除,以使得输入的序列长度和输出的序列
长度是一致的,但是有些分类的情况,必须需要使用[CLS]的表示,这种情况可以通过在初始化时申明一下需要保留[CLS]的表示,如下例所示
.. code-block:: python
embed = BertEmbedding(vocab, model_dir_or_name='en-base-cased', layers='-1', include_cls_sep=True)
print(embed(words).size()) # 结果将在序列维度上增加2
# 取出句子的cls表示
cls_reps = embed(words)[:, 0] # shape: [batch_size, 768]
输出为::
torch.Size([1, 7, 768])
在英文Bert模型中一个英文单词可能会被切分为多个subword例如"fairness"会被拆分为["fair", "##ness"]这样一个word对应的将有两个输出
:class:`~fastNLP.embeddings.BertEmbedding`会使用pooling方法将一个word的subword的表示合并成一个vector通过pool_method可以控制
该pooling方法支持的有"first"(即使用fair的表示作为fairness的表示), "last"(使用##ness的表示作为fairness的表示), "max"(对fair和
##ness在每一维上做max),"avg"(对fair和##ness每一维做average)。
.. code-block:: python
embed = BertEmbedding(vocab, model_dir_or_name='en-base-cased', layers='-1', pool_method='max')
print(embed(words).size())
输出为::
torch.Size([1, 5, 768])
另外,根据`<https://arxiv.org/abs/1810.04805>`_ Bert的还存在一种用法句子之间通过[SEP]拼接起来前一句话的token embedding为0
后一句话的token embedding为1。BertEmbedding能够自动识别句子中间的[SEP]来正确设置对应的token_type_id的。
.. code-block:: python
vocab = Vocabulary()
vocab.add_word_lst("this is a demo . [SEP] another sentence .".split())
embed = BertEmbedding(vocab, model_dir_or_name='en-base-cased', layers='-1', pool_method='max')
words = torch.LongTensor([[vocab.to_index(word) for word in "this is a demo . [SEP] another sentence .".split()]])
print(embed(words).size())
输出为::
torch.Size([1, 9, 768])
在多个[SEP]的情况下将会使token_type_id不断01循环。比如"first sentence [SEP] second sentence [SEP] third sentence", 它们的
token_type_id将是[0, 0, 0, 1, 1, 1, 0, 0]。但请注意[SEP]一定要大写的,不能是[sep],否则无法识别。
更多:class:`~fastNLP.embedding.BertEmbedding`的使用,请参考\ref{找人写一篇BertEmbedding的使用教程}
-----------------------------------------------------
Part V: 使用character-level的embedding
Part VI: 使用character-level的embedding
-----------------------------------------------------
除了预训练的embedding以外fastNLP还提供了CharEmbedding :class:`~fastNLP.embeddings.CNNCharEmbedding`
:class:`~fastNLP.embeddings.LSTMCharEmbedding`
除了预训练的embedding以外fastNLP还提供了两种Character Embedding :class:`~fastNLP.embeddings.CNNCharEmbedding`
:class:`~fastNLP.embeddings.LSTMCharEmbedding` 。一般在使用character embedding时需要在预处理的时候将word拆分成character
会使得预处理过程变得非常繁琐。在fastNLP中使用character embedding也只需要传入:class:`~fastNLP.Vocabulary`即可,而且该
Vocabulary与其它Embedding使用的Vocabulary是一致的如下面的例子所示
CNNCharEmbedding的使用例子如下
.. code-block:: python
from fastNLP import CNNCharEmbedding
embed = CNNCharEmbedding(vocab, embed_size=100, char_emb_size=50)
from fastNLP.embeddings import CNNCharEmbedding
from fastNLP import Vocabulary
这表示这个CNNCharEmbedding当中character的embedding维度大小为50返回的embedding结果维度大小为100。
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
# character的embedding维度大小为50返回的embedding结果维度大小为64。
embed = CNNCharEmbedding(vocab, embed_size=64, char_emb_size=50)
words = torch.LongTensor([[vocab.to_index(word) for word in "this is a demo .".split()]])
print(embed(words).size())
输出为::
torch.Size([1, 5, 64])
与CNNCharEmbedding类似LSTMCharEmbedding的使用例子如下
.. code-block:: python
from fastNLP import LSTMCharEmbedding
embed = LSTMCharEmbedding(vocab, embed_size=100, char_emb_size=50)
from fastNLP.embeddings import LSTMCharEmbeddding
from fastNLP import Vocabulary
这表示这个LSTMCharEmbedding当中character的embedding维度大小为50返回的embedding结果维度大小为100。
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
# character的embedding维度大小为50返回的embedding结果维度大小为64。
embed = LSTMCharEmbeddding(vocab, embed_size=64, char_emb_size=50)
words = torch.LongTensor([[vocab.to_index(word) for word in "this is a demo .".split()]])
print(embed(words).size())
输出为::
torch.Size([1, 5, 64])
-----------------------------------------------------
Part VI: 叠加使用多个embedding
Part VII: 叠加使用多个embedding
-----------------------------------------------------
在fastNLP中我们使用 :class:`~fastNLP.embeddings.StackEmbedding` 来叠加多个embedding
例子如下:
单独使用Character Embedding往往效果并不是很好需要同时结合word embedding。在fastNLP中可以通过:class:`~fastNLP.embeddings.StackEmbedding`
来叠加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)
from fastNLP.embeddings import StaticEmbedding, StackEmbedding, CNNCharEmbedding
from fastNLP import Vocabulary
stack_embed = StackEmbedding([embed_1, embed_2])
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
StackEmbedding会把多个embedding的结果拼接起来如上面例子的stack_embed返回的embedding维度为350维。
word_embed = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50d')
char_embed = CNNCharEmbedding(vocab, embed_size=64, char_emb_size=50)
embed = StackEmbedding([word_embed, char_embed])
除此以外还可以把静态embedding跟上下文相关的embedding拼接起来
words = torch.LongTensor([[vocab.to_index(word) for word in "this is a demo .".split()]])
print(embed(words).size()) # 输出embedding的维度为50+64=114
输出为::
torch.Size([1, 5, 114])
:class:`~fastNLP.embeddings.StaticEmbedding`, :class:`~fastNLP.embeddings.ElmoEmbedding`,
:class:`~fastNLP.embeddings.CNNCharEmbedding`, :class:`~fastNLP.embeddings.BertEmbedding`等都可以互相拼接。
:class:`~fastNLP.embeddings.StackEmbedding`的使用也是和其它Embedding是一致的即输出index返回对应的表示。但能够拼接起来的Embedding
必须使用同样的:class:`~fastNLP.Vocabulary`,因为只有使用同样的:class:`~fastNLP.Vocabulary`才能保证同一个index指向的是同一个词或字
-----------------------------------------------------------
Part VIII: Embedding的其它说明
-----------------------------------------------------------
(1) 获取各种Embedding的dimension
.. 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)
from fastNLP.embeddings import *
stack_embed = StackEmbedding([elmo_embedding, glove_embedding])
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
------------------------------------------
Part VII: fastNLP支持的预训练Embedding
------------------------------------------
static_embed = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50d')
print(static_embed.embedding_dim) # 50
char_embed = CNNCharEmbedding(vocab, embed_size=30)
print(char_embed.embedding_dim) # 30
elmo_embed_1 = ElmoEmbedding(vocab, model_dir_or_name='en-small', layers='2')
print(elmo_embed_1.embedding_dim) # 256
elmo_embed_2 = ElmoEmbedding(vocab, model_dir_or_name='en-small', layers='1,2')
print(elmo_embed_2.embedding_dim) # 512
bert_embed_1 = BertEmbedding(vocab, layers='-1', model_dir_or_name='en-base-cased')
print(bert_embed_1.embedding_dim) # 768
bert_embed_2 = BertEmbedding(vocab, layers='2,-1', model_dir_or_name='en-base-cased')
print(bert_embed_2.embedding_dim) # 1536
stack_embed = StackEmbedding([static_embed, char_embed])
print(stack_embed.embedding_dim) # 80
fastNLP支持多种预训练Embedding并提供自动下载功能具体参见文档
(2) 设置Embedding的权重是否更新
`fastNLP可加载的embedding与数据集 <https://docs.qq.com/sheet/DVnpkTnF6VW9UeXdh?c=A1A0A0>`_
.. code-block:: python
from fastNLP.embeddings import *
vocab = Vocabulary()
vocab.add_word_lst("this is a demo .".split())
embed = BertEmbedding(vocab, model_dir_or_name='en-base-cased')
embed.requires_grad = False # BertEmbedding不更新
(3) 各种Embedding中word_dropout与dropout的说明
fastNLP中所有的Embedding都支持传入word_dropout和dropout参数word_dropout指示的是以多大概率将输入的word置为unk的index这样既可以
是的unk得到训练也可以有一定的regularize效果; dropout参数是在获取到word的表示之后以多大概率将一些维度的表示置为0。
如果使用:class:`~fastNLP.embeddings.StackEmbedding`且需要用到word_dropout建议将word_dropout设置在:class:`~fastNLP.embeddings.StackEmbedding`
-----------------------------------------------------------
Part IX: StaticEmbedding的使用建议
-----------------------------------------------------------
在英文的命名实体识别(NER)任务中,由`<http://xxx.itp.ac.cn/pdf/1511.08308.pdf>`_ 指出同时使用cnn character embedding和word embedding
会使得NER的效果有比较大的提升。正如你在\ref{引用第七节}看到的那样fastNLP支持将:class:`~fastNLP.embeddings.CNNCharacterEmbedding`
:class:`~fastNLP.embeddings.StaticEmbedding`拼成一个:class:`~fastNLP.embeddings.StackEmbedding`。如果通过这种方式使用,需要
在预处理文本时,不要将词汇小写化(因为Character Embedding需要利用词语中的大小写信息)且不要将出现频次低于某个阈值的word设置为unk(因为
Character embedding需要利用字形信息);但:class:`~fastNLP.embeddings.StaticEmbedding`使用的某些预训练词嵌入的词汇表中只有小写的词
语, 且某些低频词并未在预训练中出现需要被剔除。即(1) character embedding需要保留大小写而某些static embedding不需要保留大小写。(2)
character embedding需要保留所有的字形, 而static embedding需要设置一个最低阈值以学到更好的表示。
(1) fastNLP如何解决关于大小写的问题
fastNLP通过在:class:`~fastNLP.embeddings.StaticEmbedding`增加了一个lower参数解决该问题。如下面的例子所示
.. code-block:: python
from fastNLP.embeddings import StaticEmbedding
from fastNLP import Vocabulary
vocab = Vocabulary().add_word_lst("The the a A".split())
# 下面用随机的StaticEmbedding演示但与使用预训练时效果是一致的
embed = StaticEmbedding(vocab, model_name_or_dir=None, embedding_dim=5)
print(embed(torch.LongTensor([vocab.to_index('The')])))
print(embed(torch.LongTensor([vocab.to_index('the')])))
输出为::
tensor([[-0.4685, 0.4572, 0.5159, -0.2618, -0.6871]], grad_fn=<EmbeddingBackward>)
tensor([[ 0.2615, 0.1490, -0.2491, 0.4009, -0.3842]], grad_fn=<EmbeddingBackward>)
可以看到"The"与"the"的vector是不一致的。但如果我们在初始化:class:`~fastNLP.embeddings.StaticEmbedding`将lower设置为True效果将
如下所示
.. code-block:: python
from fastNLP.embeddings import StaticEmbedding
from fastNLP import Vocabulary
vocab = Vocabulary().add_word_lst("The the a A".split())
# 下面用随机的StaticEmbedding演示但与使用预训练时效果是一致的
embed = StaticEmbedding(vocab, model_name_or_dir=None, embedding_dim=5, lower=True)
print(embed(torch.LongTensor([vocab.to_index('The')])))
print(embed(torch.LongTensor([vocab.to_index('the')])))
输出为::
tensor([[-0.2237, 0.6825, -0.3459, -0.1795, 0.7516]], grad_fn=<EmbeddingBackward>)
tensor([[-0.2237, 0.6825, -0.3459, -0.1795, 0.7516]], grad_fn=<EmbeddingBackward>)
可以看到"The"与"the"的vector是一致的。他们实际上也是引用的同一个vector。通过将lower设置为True可以在:class:`~fastNLP.embeddings.StaticEmbedding`
实现类似具备相同小写结果的词语引用同一个vector。
(2) fastNLP如何解决min_freq的问题
fastNLP通过在:class:`~fastNLP.embeddings.StaticEmbedding`增加了一个min_freq参数解决该问题。如下面的例子所示
.. code-block:: python
from fastNLP.embeddings import StaticEmbedding
from fastNLP import Vocabulary
vocab = Vocabulary().add_word_lst("the the the a".split())
# 下面用随机的StaticEmbedding演示但与使用预训练时效果是一致的
embed = StaticEmbedding(vocab, model_name_or_dir=None, embedding_dim=5, min_freq=2)
print(embed(torch.LongTensor([vocab.to_index('the')])))
print(embed(torch.LongTensor([vocab.to_index('a')])))
print(embed(torch.LongTensor([vocab.unknown_idx])))
输出为::
tensor([[ 0.0454, 0.3375, 0.6758, -0.2026, -0.4715]], grad_fn=<EmbeddingBackward>)
tensor([[-0.7602, 0.0149, 0.2733, 0.3974, 0.7371]], grad_fn=<EmbeddingBackward>)
tensor([[-0.7602, 0.0149, 0.2733, 0.3974, 0.7371]], grad_fn=<EmbeddingBackward>)
其中最后一行为unknown值的vector可以看到a的vector表示与unknown是一样的这是由于a的频次低于了2所以被指向了unknown的表示而the由于
词频超过了2次所以它是单独的表示。
在计算min_freq时也会考虑到lower的作用比如
.. code-block:: python
from fastNLP.embeddings import StaticEmbedding
from fastNLP import Vocabulary
vocab = Vocabulary().add_word_lst("the the the a A".split())
# 下面用随机的StaticEmbedding演示但与使用预训练时效果是一致的
embed = StaticEmbedding(vocab, model_name_or_dir=None, embedding_dim=5, min_freq=2, lower=True)
print(embed(torch.LongTensor([vocab.to_index('the')])))
print(embed(torch.LongTensor([vocab.to_index('a')])))
print(embed(torch.LongTensor([vocab.to_index('A')])))
print(embed(torch.LongTensor([vocab.unknown_idx])))
输出为::
tensor([[-0.7453, -0.5542, 0.5039, 0.6195, -0.4723]], grad_fn=<EmbeddingBackward>) # the
tensor([[ 0.0170, -0.0995, -0.5743, -0.2469, -0.2095]], grad_fn=<EmbeddingBackward>) # a
tensor([[ 0.0170, -0.0995, -0.5743, -0.2469, -0.2095]], grad_fn=<EmbeddingBackward>) # A
tensor([[ 0.6707, -0.5786, -0.6967, 0.0111, 0.1209]], grad_fn=<EmbeddingBackward>) # unk
可以看到a不再和最后一行的unknown共享一个表示了这是由于a与A都算入了a的词频且A的表示也是a的表示。

View File

@ -0,0 +1,219 @@
=======================================
使用Loader和Pipe加载并处理数据集
=======================================
这一部分是一个关于如何加载数据集的教程
教程目录:
- `Part I: 数据集容器DataBundle`_
- `Part II: 加载的各种数据集的Loader`_
- `Part III: 使用Pipe对数据集进行预处理`_
- `Part IV: fastNLP封装好的Loader和Pipe`_
- `Part V: 不同格式类型的基础Loader`_
------------------------------------
Part I: 数据集容器DataBundle
------------------------------------
而由于对于同一个任务训练集验证集和测试集会共用同一个词表以及具有相同的目标值所以在fastNLP中我们使用了 :class:`~fastNLP.io.DataBundle`
来承载同一个任务的多个数据集 :class:`~fastNLP.DataSet` 以及它们的词表 :class:`~fastNLP.Vocabulary`。下面会有例子介绍:class:`~fastNLP.io.DataBundle`
的相关使用。
:class: `~fastNLP.io.DataBundle` 在fastNLP中主要在各个 :class: `~fastNLP.io.Loader` 和 :class: `~fastNLP.io.Pipe` 中被使用。
下面我们将先介绍一下 :class: `~fastNLP.io.Loader` 和 :class: `~fastNLP.io.Pipe`, 之后我们将给出相应的例子。
-------------------------------------
Part II: 加载的各种数据集的Loader
-------------------------------------
在fastNLP中所有的数据Loader都可以通过其文档判断其支持读取的数据格式以及读取之后返回的 :class:`~fastNLP.DataSet` 的格式。例如
\ref 加个引用。
- download 函数:自动将该数据集下载到缓存地址,默认缓存地址为~/.fastNLP/datasets/。由于版权等原因不是所有的Loader都实现了该方法。
该方法会返回下载后文件所处的缓存地址。可以查看对应Loader的download的方法的文档来判断该Loader加载的数据。
- _load 函数:从一个数据文件中读取数据,返回一个 :class:`~fastNLP.DataSet`。返回的DataSet的格式可从Loader文档判断。
- load 函数:从文件或者文件夹中读取数据并组装成 :class:`~fastNLP.io.DataBundle`。支持接受的参数类型有以下的几种
- None, 将尝试读取自动缓存的数据仅支持提供了自动下载数据的Loader
- 文件夹路径, 默认将尝试在该路径下匹配文件名中含有`train`, `test`, `dev`的文件,如果有多个文件含有这相同的关键字,将无法通过
该方式读取
- dict, 例如{'train':"/path/to/tr.conll", 'dev':"/to/validate.conll", "test":"/to/te.conll"}
.. code-block:: python
from fastNLP.io import CWSLoader
loader = CWSLoader(dataset_name='pku')
data_bundle = loader.load()
print(data_bundle)
输出内容为::
In total 3 datasets:
dev has 1831 instances.
train has 17223 instances.
test has 1944 instances.
这里表示一共有3个数据集。其中
- 3个数据集分别为train、dev、test数据集分别有17223、1831、1944个instance
也可以取出DataSet并DataSet中的具体内容
.. code-block:: python
tr_data = data_bundle.get_dataset('train')
print(tr_data[:2])
输出为::
+--------------------------------------------------------------------------------------+
| raw_words |
+--------------------------------------------------------------------------------------+
| 迈向 充满 希望 的 新 世纪 —— 一九九八年 新年 讲话 附 图片 |
| 中共中央 总书记 、 国家 主席 江 泽民 |
+--------------------------------------------------------------------------------------+
------------------------------------------
Part III: 使用Pipe对数据集进行预处理
------------------------------------------
通过:class:`~fastNLP.io.Loader` 可以将文本数据读入,但并不能直接被神经网络使用,还需要进行一定的预处理。
在fastNLP中我们使用 :class:`~fastNLP.io.Pipe`的子类作为数据预处理的类Pipe和Loader一般具备一一对应的关系该关系可以从其名称判断
例如:class:`~fastNLP.io.CWSLoader`:class:`~fastNLP.io.CWSPipe`是一一对应的。一般情况下Pipe处理包含以下的几个过程(1)将raw_words或
raw_chars进行tokenize以切分成不同的词或字; (2) 再建立词或字的 :class:`~fastNLP.Vocabulary`, 并将词或字转换为index; (3)将target
列建立词表并将target列转为index;
所有的Pipe都可通过其文档查看通过该Pipe之后DataSet中的field的情况; 如 \ref{TODO 添加对例子的引用}
各种数据集的Pipe当中都包含了以下的两个函数:
- process 函数:对输入的 :class:`~fastNLP.io.DataBundle` 进行处理, 然后返回处理之后的 :class:`~fastNLP.io.DataBundle`
process函数的文档中包含了该Pipe支持处理的DataSet的格式。
- process_from_file 函数输入数据集所在文件夹使用对应的Loader读取数据(所以该函数支持的参数类型是由于其对应的Loader的load函数
决定的)然后调用相对应的process函数对数据进行预处理。相当于是把Load和process放在一个函数中执行。
接着上面CWSLoader的例子我们展示一下CWSPipe的功能
.. code-block:: python
from fastNLP.io import CWSPipe
data_bundle = CWSPipe().process(data_bundle)
print(data_bundle)
输出内容为::
In total 3 datasets:
dev has 1831 instances.
train has 17223 instances.
test has 1944 instances.
In total 2 vocabs:
chars has 4777 entries.
target has 4 entries.
表示一共有3个数据集和2个词表。其中
- 3个数据集分别为train、dev、test数据集分别有17223、1831、1944个instance
- 2个词表分别为chars词表与target词表。其中chars词表为句子文本所构建的词表一共有4777个字
target词表为目标标签所构建的词表一共有4种标签。
相较于之前CWSLoader读取的DataBundle新增了两个Vocabulary。 我们可以打印一下处理之后的DataSet
.. code-block:: python
tr_data = data_bundle.get_dataset('train')
print(tr_data[:2])
输出为::
+---------------------------------------------------+------------------------------------+------------------------------------+---------+
| raw_words | chars | target | seq_len |
+---------------------------------------------------+------------------------------------+------------------------------------+---------+
| 迈向 充满 希望 的 新 世纪 —— 一九九八年... | [1224, 178, 674, 544, 573, 435,... | [0, 1, 0, 1, 0, 1, 2, 2, 0, 1, ... | 29 |
| 中共中央 总书记 、 国家 主席 江 泽民 | [11, 212, 11, 335, 124, 256, 10... | [0, 3, 3, 1, 0, 3, 1, 2, 0, 1, ... | 15 |
+---------------------------------------------------+------------------------------------+------------------------------------+---------+
可以看到有两列为int的field: chars和target。这两列的名称同时也是DataBundle中的Vocabulary的名称。可以通过下列的代码获取并查看Vocabulary的
信息
.. code-block:: python
vocab = data_bundle.get_vocab('target')
print(vocab)
输出为::
Vocabulary(['B', 'E', 'S', 'M']...)
------------------------------------------
Part IV: fastNLP封装好的Loader和Pipe
------------------------------------------
fastNLP封装了多种任务/数据集的Loader和Pipe并提供自动下载功能具体参见文档
`fastNLP可加载数据集 <https://docs.qq.com/sheet/DVnpkTnF6VW9UeXdh?c=A1A0A0>`_
--------------------------------------------------------
Part V: 不同格式类型的基础Loader
--------------------------------------------------------
除了上面提到的针对具体任务的Loader我们还提供了CSV格式和JSON格式的Loader
:class:`~fastNLP.io.loader.CSVLoader`
读取CSV类型的数据集文件。例子如下
.. code-block:: python
from fastNLP.io.loader import CSVLoader
data_set_loader = CSVLoader(
headers=('raw_words', 'target'), sep='\t'
)
# 表示将CSV文件中每一行的第一项填入'words' field第二项填入'target' field。
# 其中项之间由'\t'分割开来
data_set = data_set_loader._load('path/to/your/file')
数据集内容样例如下 ::
But it does not leave you with much . 1
You could hate it for the same reason . 1
The performances are an absolute joy . 4
读取之后的DataSet具有以下的field
.. csv-table::
:header: raw_words, target
"But it does not leave you with much .", "1"
"You could hate it for the same reason .", "1"
"The performances are an absolute joy .", "4"
:class:`~fastNLP.io.loader.JsonLoader`
读取Json类型的数据集文件数据必须按行存储每行是一个包含各类属性的Json对象。例子如下
.. code-block:: python
from fastNLP.io.loader import JsonLoader
oader = JsonLoader(
fields={'sentence1': 'raw_words1', 'sentence2': 'raw_words2', 'gold_label': 'target'}
)
# 表示将Json对象中'sentence1'、'sentence2'和'gold_label'对应的值赋给'raw_words1'、'raw_words2'、'target'这三个fields
data_set = loader._load('path/to/your/file')
数据集内容样例如下 ::
{"annotator_labels": ["neutral"], "captionID": "3416050480.jpg#4", "gold_label": "neutral", "pairID": "3416050480.jpg#4r1n", "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 training his horse for a competition.", "sentence2_binary_parse": "( ( A person ) ( ( is ( ( training ( his horse ) ) ( for ( a competition ) ) ) ) . ) )", "sentence2_parse": "(ROOT (S (NP (DT A) (NN person)) (VP (VBZ is) (VP (VBG training) (NP (PRP$ his) (NN horse)) (PP (IN for) (NP (DT a) (NN competition))))) (. .)))"}
{"annotator_labels": ["contradiction"], "captionID": "3416050480.jpg#4", "gold_label": "contradiction", "pairID": "3416050480.jpg#4r1c", "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 at a diner, ordering an omelette.", "sentence2_binary_parse": "( ( A person ) ( ( ( ( is ( at ( a diner ) ) ) , ) ( ordering ( an omelette ) ) ) . ) )", "sentence2_parse": "(ROOT (S (NP (DT A) (NN person)) (VP (VBZ is) (PP (IN at) (NP (DT a) (NN diner))) (, ,) (S (VP (VBG ordering) (NP (DT an) (NN omelette))))) (. .)))"}
{"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)))) (. .)))"}
读取之后的DataSet具有以下的field
.. csv-table::
:header: raw_words0, raw_words1, target
"A person on a horse jumps over a broken down airplane.", "A person is training his horse for a competition.", "neutral"
"A person on a horse jumps over a broken down airplane.", "A person is at a diner, ordering an omelette.", "contradiction"
"A person on a horse jumps over a broken down airplane.", "A person is outdoors, on a horse.", "entailment"

View File

@ -8,13 +8,14 @@ fastNLP 详细使用教程
:maxdepth: 1
使用DataSet预处理文本 </tutorials/tutorial_1_data_preprocess>
使用Loader和Pipe加载并处理数据集 </tutorials/tutorial_2_load_dataset>
使用Vocabulary转换文本与index </tutorials/tutorial_2_vocabulary>
使用Embedding模块将文本转成向量 </tutorials/tutorial_3_embedding>
动手实现一个文本分类器I-使用Trainer和Tester快速训练和测试 </tutorials/tutorial_4_loss_optimizer>
使用Loader和Pipe加载并处理数据集 </tutorials/tutorial_4_load_dataset>
动手实现一个文本分类器II-使用DataSetIter实现自定义训练过程 </tutorials/tutorial_5_datasetiter>
快速实现序列标注模型 </tutorials/tutorial_6_seq_labeling>
使用Modules和Models快速搭建自定义模型 </tutorials/tutorial_7_modules_models>
使用Metric快速评测你的模型 </tutorials/tutorial_8_metrics>
使用Callback自定义你的训练过程 </tutorials/tutorial_9_callback>
使用fitlog 辅助 fastNLP 进行科研 </tutorials/tutorial_10_fitlog>
动手实现一个文本分类器I-使用Trainer和Tester快速训练和测试 </tutorials/tutorial_6_loss_optimizer>
使用Metric快速评测你的模型 </tutorials/tutorial_7_metrics>
使用Modules和Models快速搭建自定义模型 </tutorials/tutorial_8_modules_models>
快速实现序列标注模型 </tutorials/tutorial_9_seq_labeling>
使用Callback自定义你的训练过程 </tutorials/tutorial_10_callback>
使用fitlog 辅助 fastNLP 进行科研 </tutorials/tutorial_11_fitlog>

View File

@ -127,27 +127,6 @@ class BertEmbedding(ContextualEmbedding):
words.masked_fill_(sep_mask, self._word_sep_index)
return 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):
"""
@ -175,23 +154,6 @@ class BertWordPieceEncoder(nn.Module):
self.word_dropout = word_dropout
self.dropout_layer = nn.Dropout(dropout)
@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

View File

@ -140,40 +140,6 @@ class CNNCharEmbedding(TokenEmbedding):
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 reset_parameters(self):
for name, param in self.named_parameters():
if 'words_to_chars_embedding' in name or 'word_lengths' in name: # 这个不能reset
continue
if 'char_embedding' in name:
continue
if param.data.dim() > 1:
nn.init.xavier_uniform_(param, 1)
else:
nn.init.uniform_(param, -1, 1)
class LSTMCharEmbedding(TokenEmbedding):
"""
@ -293,27 +259,3 @@ class LSTMCharEmbedding(TokenEmbedding):
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

View File

@ -55,7 +55,7 @@ class ElmoEmbedding(ContextualEmbedding):
并删除character encoder之后将直接使用cache的embedding默认为False
"""
def __init__(self, vocab: Vocabulary, model_dir_or_name: str = 'en', layers: str = '2', requires_grad: bool = False,
def __init__(self, vocab: Vocabulary, model_dir_or_name: str = 'en', layers: str = '2', requires_grad: bool = True,
word_dropout=0.0, dropout=0.0, cache_word_reprs: bool = False):
super(ElmoEmbedding, self).__init__(vocab, word_dropout=word_dropout, dropout=dropout)
@ -137,27 +137,6 @@ class ElmoEmbedding(ContextualEmbedding):
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):
"""

View File

@ -115,6 +115,10 @@ class Embedding(nn.Module):
class TokenEmbedding(nn.Module):
"""
fastNLP中各种Embedding的基类
"""
def __init__(self, vocab, word_dropout=0.0, dropout=0.0):
super(TokenEmbedding, self).__init__()
if vocab.rebuild:

View File

@ -22,10 +22,11 @@ class StackEmbedding(TokenEmbedding):
Example::
>>> from fastNLP import Vocabulary
>>> from fastNLP.embeddings import StaticEmbedding
>>> from fastNLP.embeddings import StaticEmbedding, StackEmbedding
>>> vocab = Vocabulary().add_word_lst("The whether is good .".split())
>>> embed_1 = StaticEmbedding(vocab, model_dir_or_name='en-glove-6b-50d', requires_grad=True)
>>> embed_2 = StaticEmbedding(vocab, model_dir_or_name='en-word2vec-300', requires_grad=True)
>>> embed = StackEmbedding([embed_1, embed_2])
:param embeds: 一个由若干个TokenEmbedding组成的list要求每一个TokenEmbedding的词表都保持一致
:param float word_dropout: 以多大的概率将一个词替换为unk这样既可以训练unk也是一定的regularize不同embedidng会在相同的位置
@ -57,35 +58,26 @@ class StackEmbedding(TokenEmbedding):
:return:
"""
assert isinstance(embed, TokenEmbedding)
self._embed_size += embed.embed_size
self.embeds.append(embed)
return self
def pop(self):
"""
弹出最后一个embed
:return:
"""
return self.embeds.pop()
embed = self.embeds.pop()
self._embed_size -= embed.embed_size
return embed
@property
def embed_size(self):
return self._embed_size
@property
def requires_grad(self):
"""
Embedding的参数是否允许优化True: 所有参数运行优化; False: 所有参数不允许优化; None: 部分允许优化部分不允许
该Embedding输出的vector的最后一维的维度
: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
return self._embed_size
def forward(self, words):
"""

View File

@ -54,13 +54,16 @@ class StaticEmbedding(TokenEmbedding):
如果输入为None则使用embedding_dim的维度随机初始化一个embedding
:param int embedding_dim: 随机初始化的embedding的维度当该值为大于0的值时将忽略model_dir_or_name
:param bool requires_grad: 是否需要gradient. 默认为True
:param callable init_method: 如何初始化没有找到的值可以使用torch.nn.init.*中各种方法调用该方法时传入一个tensor对
:param callable init_method: 如何初始化没有找到的值可以使用torch.nn.init.*中各种方法, 传入的方法应该接受一个tensor
inplace地修改其值
:param bool lower: 是否将vocab中的词语小写后再和预训练的词表进行匹配如果你的词表中包含大写的词语或者就是需要单独
为大写的词语开辟一个vector表示则将lower设置为False
:param float dropout: 以多大的概率对embedding的表示进行Dropout0.1即随机将10%的值置为0
:param float word_dropout: 以多大的概率将一个词替换为unk这样既可以训练unk也是一定的regularize
:param bool normalize: 是否对vector进行normalize使得每个vector的norm为1
:param int min_freq: Vocabulary词频数小于这个数量的word将被指向unk
:param dict **kwarngs: only_train_min_freq, 仅对train中的词语使用min_freq筛选; only_norm_found_vector是否仅对在预训练中
找到的词语使用normalize
"""
def __init__(self, vocab: Vocabulary, model_dir_or_name: str = 'en', embedding_dim=-1, requires_grad: bool = True,
@ -183,27 +186,6 @@ class StaticEmbedding(TokenEmbedding):
return embed
@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='<pad>', unknown='<unk>',
error='ignore', init_method=None):
"""

View File

@ -31,7 +31,6 @@ class YelpLoader(Loader):
"1","I got 'new' tires from the..."
"1","Don't waste your time..."
读取YelpFull, YelpPolarity的数据可以通过xxx下载并预处理数据
读取的DataSet将具备以下的数据结构
.. csv-table::

View File

@ -34,29 +34,27 @@ class Loader:
"""
从指定一个或多个路径中的文件中读取数据返回 :class:`~fastNLP.io.DataBundle`
读取的field根据ConllLoader初始化时传入的headers决定
:param Union[str, Dict[str, str]] paths: 支持以下的几种输入方式
(0) 如果为None则先查看本地是否有缓存如果没有则自动下载并缓存
(1) 传入一个目录, 该目录下名称包含train的被认为是train包含test的被认为是test包含dev的被认为是dev如果检测到多个文件
名包含'train' 'dev' 'test'则会报错::
data_bundle = ConllLoader().load('/path/to/dir') # 返回的DataBundle中datasets根据目录下是否检测到train、
# dev、 test等有所变化可以通过以下的方式取出DataSet
tr_data = data_bundle.datasets['train']
te_data = data_bundle.datasets['test'] # 如果目录下有文件包含test这个字段
data_bundle = xxxLoader().load('/path/to/dir') # 返回的DataBundle中datasets根据目录下是否检测到train、
# dev、 test等有所变化可以通过以下的方式取出DataSet
tr_data = data_bundle.get_dataset('train')
te_data = data_bundle.get_dataset('test') # 如果目录下有文件包含test这个字段
(2) 传入文件路径::
(2) 传入一个dict比如traindevtest不在同一个目录下或者名称中不包含train, dev, test::
data_bundle = ConllLoader().load("/path/to/a/train.conll") # 返回DataBundle对象, datasets中仅包含'train'
tr_data = data_bundle.datasets['train'] # 可以通过以下的方式取出DataSet
paths = {'train':"/path/to/tr.conll", 'dev':"/to/validate.conll", "test":"/to/te.conll"}
data_bundle = xxxLoader().load(paths) # 返回的DataBundle中的dataset中包含"train", "dev", "test"
dev_data = data_bundle.get_dataset('dev')
(3) 传入一个dict比如traindevtest不在同一个目录下或者名称中不包含train, dev, test::
(3) 传入文件路径::
paths = {'train':"/path/to/tr.conll", 'dev':"/to/validate.conll", "test":"/to/te.conll"}
data_bundle = ConllLoader().load(paths) # 返回的DataBundle中的dataset中包含"train", "dev", "test"
dev_data = data_bundle.datasets['dev']
data_bundle = xxxLoader().load("/path/to/a/train.conll") # 返回DataBundle对象, datasets中仅包含'train'
tr_data = data_bundle.get_dataset('train') # 取出DataSet
:return: 返回的 :class:`~fastNLP.io.DataBundle`
"""
@ -78,7 +76,7 @@ class Loader:
@staticmethod
def _get_dataset_path(dataset_name):
"""
传入dataset的名称获取读取数据的目录如果数据不存在会尝试自动下载并缓存
传入dataset的名称获取读取数据的目录如果数据不存在会尝试自动下载并缓存如果支持的话
:param str dataset_name: 数据集的名称
:return: str, 数据集的目录地址直接到该目录下读取相应的数据即可