Skip to content

Commit

Permalink
Merge pull request PaddlePaddle#325 from smallv0221/yxp0331
Browse files Browse the repository at this point in the history
Update some doc and add contribution doc
  • Loading branch information
moebius21 authored May 7, 2021
2 parents 33e33c3 + 31cc4cc commit efa617a
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
==============
如何贡献数据集
创建 :class:`DatasetBuilder`
==============

除了使用PaddleNLP内置的数据集以外,我们也鼓励用户向PaddleNLP贡献自己的数据集
数据集的贡献通过定义一个 :class:`DatasetBuilder` 的子类来实现。一个合格的 :class:`DatasetBuilder` 需要遵循一些协议和规范

数据集的贡献通过定义一个 :class:`DatasetBuilder` 的子类来实现。下面我们以 :obj:`LCQMC` 为例了解一下贡献一个合格的 :class:`DatasetBuilder` 通常需要包含哪些方法和参数。
下面我们以 :obj:`LCQMC` 为例了解一下 :class:`DatasetBuilder` 通常需要包含哪些方法和参数。

成员变量
---------------
Expand Down Expand Up @@ -37,15 +37,20 @@
'8f4b71e15e67696cc9e112a459ec42bd'),
}
首先贡献的数据集需要继承 :class:`paddlenlp.datasets.DatasetBuilder` 类,之后最好添加一段注释,简要说明数据集的来源等信息。之后需定义以下成员变量:
首先贡献的数据集需要继承 :class:`paddlenlp.datasets.DatasetBuilder` 类,类名格式为camel case。之后应该添加一段注释,简要说明数据集的来源等信息。之后需定义以下成员变量:

- :attr:`lazy` :数据集的默认类型。:obj:`False` 对应 :class:`MapDataset` ,:obj:`True` 对应 :class:`IterDataset` 。
- :attr:`URL` :数据集压缩包下载地址,需提供有效并稳定的下载链接。如果数据集不是压缩包,可以不再这里提供。
- :attr:`MD5` :数据集压缩包的md5值,用于文件校验,如果数据集文件不是压缩包,可以不再这里提供。
- :attr:`META_INFO` :数据集split信息格式。
- :attr:`SPLITS` :数据集的split信息,包含数据集解压后的不同文件的具体位置,文件名,md5值等,如果数据集不是压缩包则通常在这里提供下载地址,还可以包含诸如不同文件对应的文件读取参数等信息。

除此之外,不同的数据集可能还需要诸如 :attr:`VOCAB_INFO` 等其他成员变量(参见 `iwslt15.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/experimental/iwslt15.py>`__ )。或者成员变量会有其他格式(参见 `glue.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/experimental/glue.py>`__ )。贡献者可以根据实际情况自行调整。
除此之外,不同的数据集可能还需要诸如 :attr:`VOCAB_INFO` 等其他成员变量(参见 `iwslt15.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/iwslt15.py>`__ )。或者成员变量会有其他格式。贡献者可以根据实际情况自行调整。

.. note::

- 如果贡献的数据集没有子数据集,那么 :class:`DatasetBuilder` **必须包含** :attr:`SPLITS` 成员变量,且该变量必须是一个字典,字典的key是该数据集包含的splits。
- 如果贡献的数据集有子数据集,那么 :class:`DatasetBuilder` **必须包含** :attr:`BUILDER_CONFIGS` 成员变量,且该变量必须是一个字典,字典的key是该数据集包含的子数据集的 :attr:`name` 。字典的value是包含该数据集的子数据集split信息的字典,key值必须是 `splits` 。具体格式(参见 `glue.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/glue.py>`__ )

:func:`_get_data` 方法
-----------------------
Expand Down Expand Up @@ -109,7 +114,7 @@
如果数据集提供词典文件,则需要加入 :func:`get_vocab` 方法和 :attr:`VOCAB_INFO` 变量。

该方法会根据 :attr:`VOCAB_INFO` 变量返回一个包含数据集词典信息的 :class:`Dictionary` 对象并作为实例变量传给生成的数据集。用于在训练过程中初始化 :class:`paddlenlp.data.Vocab` 对象。
该方法的写法请参考 `iwslt15.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/experimental/iwslt15.py>`__ 。
该方法的写法请参考 `iwslt15.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/iwslt15.py>`__ 。

.. note::

Expand Down
9 changes: 9 additions & 0 deletions docs/community/contribute_datasets/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
============
如何贡献数据集
============

.. toctree::
:maxdepth: 1

sharing_dataset.rst
how_to_write_a_DatasetBuilder.rst
98 changes: 98 additions & 0 deletions docs/community/contribute_datasets/sharing_dataset.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
========================
分享你的数据集
========================

除了使用PaddleNLP内置的数据集以外,我们也鼓励用户向PaddleNLP贡献自己的数据集。

下面我们来介绍一下贡献数据集的详细流程:

配置环境
---------------

#. 编写和测试PaddleNLP代码需要依赖python3.6以上版本以及最新版本的PaddlePaddle。请确保正确安装以上依赖。
#. 在PaddleNLP的github页面上点击Fork按钮,在自己的github中创建一份PaddleNLP repo的副本。
#. 将您frok的内容下载到本地,并将官方repo作为remote。

.. code-block::
git clone https://github.com/USERNAME/PaddleNLP
cd PaddleNLP
git remote add upstream https://github.com/PaddlePaddle/PaddleNLP.git
#. 安装pre-commit钩子,它可以帮助我们格式化源代码,再提交前自动检查代码问题。不满足钩子的PR **不能** 被提交到PaddleNLP。

.. code-block::
pip install pre-commit
pre-commit install
添加一个 :class:`DatasetBuilder`
----------------------------------

#. 创建一个新的本地分支,一般从develop 分支上创建新分支。

.. code-block::
git checkout -b my-new-dataset
#. 找到您本地repo下的 `PaddleNLP/paddlenlp/datasets/` 路径,PaddleNLP的所有数据集代码都储存在这个文件夹下。

.. code-block::
cd paddlenlp/datasets
#. 为您的数据集确定一个 `name`,例如 `squad` , `chnsenticorp` 等,这个 `name` 就是您的数据集被读取时的名称。

.. note::

- 为了方便别人使用您的数据集,确保这个 `name` **不会太长而且能够正确的表义**。
- 数据集的 `name` 格式应为snake case。

#. 在该路径下创建python文件,文件名是数据集的 `name`,例如 `squad.py` 。并在这个文件中编写数据集的 :class:`DatasetBuilder` 代码。

:class:`DatasetBuilder` 的编写可以参考教程 :doc:`如何创建一个DatasetBuilder <./how_to_write_a_DatasetBuilder>` 。里面给出了详细的步骤和规范。

我们也推荐您参考已有数据集的 :class:`DatasetBuilder` 进行创建,从已有代码copy一些共用部分可能对您编写自己的数据集代码有所帮助,下面是一些已有数据集的示例:

- `iwslt15.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/iwslt15.py>`__ 翻译数据集,包含词表文件。
- `glue.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/glue.py>`__ glue数据集,包含多个子数据集,文件格式为tsv。
- `squad.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/squad.py>`__ 阅读理解数据集,文件格式为json。
- `imdb.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/imdb.py>`__ imdb数据集,每个split包含多个文件。
- `ptb.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/ptb.py>`__ 语料库数据集。
- `msra_ner.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/paddlenlp/datasets/msra_ner.py>`__ 序列标注数据集。

#. 开发完成后,可以使用 :attr:`load_dataset` 测试您创建的数据集中的split能否正确被识别。也可以使用 :attr:`print` 看看数据集读入的格式是否符合您的预期:

.. code-block::
from paddlenlp.datasets import load_dataset
ds = load_dataset('your_dataset_name', splits='your_split')
print(ds[0])
提交您的成果
---------------

#. 当您认为数据集的代码已经ready后,就可以在本地commit您的修改了:

.. code-block::
git add PaddleNLP/paddlenlp/datasets/your_dataset_name.py
git commit
#. 在提交修改之前,最好获取获取先upstream的最新代码并更新当前分支。

.. code-block::
git fetch upstream
git pull upstream develop
#. 将本地的修改推送到GitHub上,并在GitHub上向PaddleNLP提交Pull Request。

.. code-block::
git push origin my-new-dataset
以上就是像PaddleNLP贡献数据集的完整流程了。我们看到您的PR后会尽快review,如果有任何问题都会尽快反馈给您。如果没有问题的话我们就会合入到PaddleNLP repo,您贡献的数据集就可以供其他人使用啦。

如果您对贡献数据集还有任何疑问,欢迎加入官方QQ技术交流群: 973379845向我们提出。我们会尽快为您解答。
12 changes: 7 additions & 5 deletions docs/data_prepare/data_preprocess.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Dataset中通常为原始数据,需要经过一定的数据处理并进行采
print(tokenizer(text=['天气','不错'])) # [{'input_ids': [101, 1921, 3698, 102], 'token_type_ids': [0, 0, 0, 0]},
# {'input_ids': [101, 679, 7231, 102], 'token_type_ids': [0, 0, 0, 0]}]
关于 :func:`__call__` 方法的其他参数和功能,请移步PreTrainedTokenizer
关于 :func:`__call__` 方法的其他参数和功能,请查阅PreTrainedTokenizer

paddlenlp内置的 :class:`paddlenlp.datasets.MapDataset` 的 :func:`map` 方法支持传入一个函数,对数据集内的数据进行统一转换。下面我们以 :obj:`LCQMC` 的数据处理流程为例:

Expand Down Expand Up @@ -98,7 +98,9 @@ paddlenlp内置的 :class:`paddlenlp.datasets.MapDataset` 的 :func:`map` 方法
可以看到,在本例中两种实现的结果是相同的。但是在诸如阅读理解,对话等任务中,一条原始数据可能会产生多个 *feature* 的情况(参见 `run_squad.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/examples/machine_reading_comprehension/SQuAD/run_squad.py>`__ )通常需要将 :attr:`batched` 参数设置为 :obj:`True` 。

关于 :func:`map` 方法的其他参数和 :class:`paddlenlp.datasets.MapDataset` 的其他数据处理方法,请移步MapDataset。
:func:`map` 方法还有一个 :attr:`num_workers` 参数,当其大于0时进行多进程数据处理,可以提高处理速度。但是需要注意如果在数据处理的函数中用到了 **数据index** 的相关信息,多进程处理可能会导致错误的结果。

关于 :func:`map` 方法的其他参数和 :class:`paddlenlp.datasets.MapDataset` 的其他数据处理方法,请查阅 :doc:`dataset <../source/paddlenlp.datasets.dataset>` 。

Batchify
-----------
Expand Down Expand Up @@ -132,11 +134,11 @@ PaddleNLP内置了多种collate function,配合 :class:`paddle.io.BatchSampler
train_data_loader = DataLoader(dataset=train_ds, batch_sampler=train_batch_sampler, collate_fn=train_batchify_fn)
到此,一个完整的数据准备流程就完成了。关于更多batchify方法,请移步collate
到此,一个完整的数据准备流程就完成了。关于更多batchify方法,请查阅 :doc:`collate <../source/paddlenlp.data.collate>`

.. note::

- 当需要进行 **单机多卡** 训练时,需要将 :class:`BatchSampler` 更换为 :class:`DistributedBatchSampler` 。更多有关 :class:`paddle.io.BatchSampler` 的信息,请移步 `BatchSampler <https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/dataloader/batch_sampler/BatchSampler_cn.html>`_。
- 当需要进行 **单机多卡** 训练时,需要将 :class:`BatchSampler` 更换为 :class:`DistributedBatchSampler` 。更多有关 :class:`paddle.io.BatchSampler` 的信息,请查阅 `BatchSampler <https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/fluid/dataloader/batch_sampler/BatchSampler_cn.html>`_。

- 当需要诸如batch内排序,按token组batch等更复杂的组batch功能时。可以使用PaddleNLP内置的 :class:`SamplerHelper` 。相关用例请参考 `reader.py <https://github.com/PaddlePaddle/PaddleNLP/blob/develop/examples/machine_translation/transformer/reader.py>`__。

Expand Down Expand Up @@ -164,7 +166,7 @@ PaddleNLP内置了多种collate function,配合 :class:`paddle.io.BatchSampler
.. note::

- :class:`Vocab` 除了可以从本地词典文件初始化之外,还提供多种初始化方法,包括从 :class:`dictionary` 创建、从数据集创建等。详情请移步Vocab
- :class:`Vocab` 除了可以从本地词典文件初始化之外,还提供多种初始化方法,包括从 :class:`dictionary` 创建、从数据集创建等。详情请查阅Vocab
- 除了使用内置的 :class:`JiebaTokenizer` 外,用户还可以使用任何自定义的方式或第三方库进行分词,之后使用 :func:`Vocab.to_indices` 方法将token转为id。

之后与基于预训练模型的数据处理流程相似,编写数据处理函数并传入 :func:`map` 方法:
Expand Down
2 changes: 1 addition & 1 deletion docs/data_prepare/dataset_self_defined.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

.. note::

需要注意的是,只有从 :class:`DatasetBuilder` 初始化的数据集具有将数据中的label自动转为id的功能(详细条件参见 :doc:`如何贡献数据集 <../community/contribute_dataset>`)。
需要注意的是,只有PaddleNLP内置的数据集具有将数据中的label自动转为id的功能(详细条件参见 :doc:`创建DatasetBuilder <../community/contribute_datasets/how_to_write_a_DatasetBuilder>`)。

像上例中的自定义数据集需要在自定义的convert to feature方法中添加label转id的功能。

Expand Down
8 changes: 4 additions & 4 deletions docs/data_prepare/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
核心API
----------

- :func:`load_dataset` :数据集快速加载接口,通过传入数据集读取脚本的名称和其他参数调用 :class:`DatasetBuilder` 子类的相关方法生成数据集。关于加载数据集的详细方法,请移步 :doc:`加载数据集 <./dataset_load>` 。
- :class:`DatasetBuilder` : :class:`DatasetBuilder` 是一个基类,所有的内置数据集都继承自该类,该类的主要功能是下载和读取数据集文件并生成Dataset。其中大部分方法已经封装,不对贡献者暴露。贡献者通过重写 :func:`_get_data` 和 :func:`_read` 等方法像社区贡献数据集。详细信息请移步 :doc:`如何贡献数据集 </community/contribute_dataset>` 。
- :class:`MapDataset/IterDataset` :PaddleNLP内置数据集类型,分别是对 :class:`paddle.io.Dataset` 和 :class:`paddle.io.IterableDataset` 的扩展。内置诸如 :func:`map` , :func:`filter` 等适用于NLP任务的数据处理功能。同时还能帮助用户简单创建自定义数据集。详细信息请移步***和 :doc:`如何自定义数据集 <./dataset_self_defined>` 。
- :func:`load_dataset` :数据集快速加载接口,通过传入数据集读取脚本的名称和其他参数调用 :class:`DatasetBuilder` 子类的相关方法生成数据集。关于加载数据集的详细方法,请查阅 :doc:`加载数据集 <./dataset_load>` 。
- :class:`DatasetBuilder` : :class:`DatasetBuilder` 是一个基类,所有的内置数据集都继承自该类,该类的主要功能是下载和读取数据集文件并生成Dataset。其中大部分方法已经封装,不对贡献者暴露。贡献者通过重写 :func:`_get_data` 和 :func:`_read` 等方法像社区贡献数据集。详细信息请查阅 :doc:`如何贡献数据集 </community/contribute_dataset>` 。
- :class:`MapDataset/IterDataset` :PaddleNLP内置数据集类型,分别是对 :class:`paddle.io.Dataset` 和 :class:`paddle.io.IterableDataset` 的扩展。内置诸如 :func:`map` , :func:`filter` 等适用于NLP任务的数据处理功能。同时还能帮助用户简单创建自定义数据集。详细信息请查阅***和 :doc:`如何自定义数据集 <./dataset_self_defined>` 。

数据处理流程设计
-----------------
Expand All @@ -29,4 +29,4 @@

.. image:: /imgs/data_preprocess_pipline.png

关于数据处理的详细信息,请移步 :doc:`./data_preprocess` 。
关于数据处理的详细信息,请查阅 :doc:`./data_preprocess` 。
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
:maxdepth: 2
:caption: 社区交流共建

如何贡献数据集 <community/contribute_dataset>
如何贡献数据集 <community/contribute_datasets/index>
如何贡献模型 <community/contribute_models>
如何贡献文档案例 <community/contribute_docs>
如何加入兴趣小组 <community/join_in_PaddleNLP-SIG>
Expand Down
11 changes: 10 additions & 1 deletion paddlenlp/datasets/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ def read_datasets(self, splits=None, data_files=None):

def read(self, filename, split='train'):
"""
Returns an dataset containing all the examples that can be read from the file path.
Returns a dataset containing all the examples that can be read from the file path.
If `self.lazy` is False, this eagerly reads all instances from `self._read()`
and returns a `MapDataset`.
Expand All @@ -521,6 +521,15 @@ def read(self, filename, split='train'):
relies on the generator created from `self._read()` to lazily produce examples.
In this case your implementation of `_read()` must also be lazy
(that is, not load all examples into memory at once).
Args:
filename (str): Path of data file to read, usually provided by `_get_data`
function.
split (str, optional): The split name of selected dataset. This only makes
a different when data files of different splits have different structures.
Returns:
A `MapDataset|IterDataset`.
"""

label_list = self.get_labels()
Expand Down

0 comments on commit efa617a

Please sign in to comment.