mirror of
https://gitee.com/fastnlp/fastNLP.git
synced 2024-12-02 12:17:35 +08:00
增加新的tutorial; 删除各embedding中requires_grad的设置,统一到基类实现
This commit is contained in:
parent
2fc14790c7
commit
917cedf808
@ -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
|
||||
|
@ -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>`_
|
||||
|
131
docs/source/tutorials/tutorial_2_vocabulary.rst
Normal file
131
docs/source/tutorials/tutorial_2_vocabulary.rst
Normal 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('我') # 会输出1,Vocabulary中默认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的
|
||||
表示相同。
|
@ -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不断0,1循环。比如"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的表示。
|
||||
|
219
docs/source/tutorials/tutorial_4_load_dataset.rst
Normal file
219
docs/source/tutorials/tutorial_4_load_dataset.rst
Normal 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 |
|
||||
+--------------------------------------------------------------------------------------+
|
||||
| 迈向 充满 希望 的 新 世纪 —— 一九九八年 新年 讲话 ( 附 图片 1 张 ) |
|
||||
| 中共中央 总书记 、 国家 主席 江 泽民 |
|
||||
+--------------------------------------------------------------------------------------+
|
||||
|
||||
------------------------------------------
|
||||
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"
|
@ -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>
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
"""
|
||||
|
@ -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的表示进行Dropout。0.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):
|
||||
"""
|
||||
|
@ -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::
|
||||
|
@ -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,比如train,dev,test不在同一个目录下,或者名称中不包含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,比如train,dev,test不在同一个目录下,或者名称中不包含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, 数据集的目录地址。直接到该目录下读取相应的数据即可。
|
||||
|
Loading…
Reference in New Issue
Block a user