# 前言

HuggingFace 是一个人工智能领域的公司,其名下 HuggingFace Hub 则被誉为深度学习界的 GithubHuggingFace Hub 中包括了大量的开源数据集和开源模型,并且拥有类似于 Github PageSpace 网页 Demo,允许用户使用其服务部署在线的 Demo(免费配置包含有 2 vcpu 和 16G ram。

# datasets 库

datasets 库是 HuggingFace 的官方数据集库,其中包括了一些常用的数据加载方法,可以快速从远程 HuggingFace Hub 加载数据集到本地。

简单的使用例如:

from datasets import load_dataset
dataset = load_dataset("minskiter/weibo",save_infos=True)
train,validation,test = dataset['train'],dataset['validation'],dataset['test']

只需要三行代码就可以轻松从 huggingface.co/minskiter 中加载 weibo 这个 NER 数据集。

下载后通过定义 transform 函数,使用 map 函数调用 transform 函数可以轻松实现数据格式的转换。

def transform(example):
  # edit example and return dict 
  # e.g. transform to tensor
  return example
for key in dataset:
  # apply your custom transform
  dataset[key] = dataset[key].map(transform)

# 数据集的构建

数据集以构建 https://huggingface.co/datasets/minskiter/weibo 为例子。

一个 datasets 的数据集主要包括两个部分:数据 和 加载数据的程序:
在本例子中数据文件存放于📁 data 中,而 加载数据的程序为 📃 weibo.py (必须和数据集名字同名的 py 文件)

  1. 在远程 https://huggingface.co/new-dataset 创建好初始的 git 仓库,并 clone 到本地目录
    新建数据集

  2. 将下载好某开源的训练集、验证集、测试集移动到这个目录下的 📁 data (不存在则创建)中,并按照 train、validation、test 的规范命名训练集、验证集和测试集。

  3. 编写加载数据程序
    _info 里定义好数据的特征格式,并写好声明数据来源;
    _split_generators 里主要是分割数据集;
    _generate_examples 里对每个分割好的数据集进行加载。

    import datasets
    from datasets.download.download_manager import DownloadManager
    import pyarrow.parquet as pq
    import json
    _DESCRIPTION = """\
    The Weibo NER dataset is a Chinese Named Entity Recognition dataset 
    drawn from the social media website Sina Weibo.
    """
    _CITATION = """\
    @inproceedings{peng-dredze-2015-named,
        title = "Named Entity Recognition for {C}hinese 
            Social Media with Jointly Trained Embeddings",
        author = "Peng, Nanyun  and Dredze, Mark",
        booktitle = "Proceedings of the 2015 Conference on 
            Empirical Methods in Natural Language Processing",
        month = sep,
        year = "2015",
        address = "Lisbon, Portugal",
        publisher = "Association for Computational Linguistics",
        url = "https://aclanthology.org/D15-1064",
        doi = "10.18653/v1/D15-1064",
        pages = "548--554",
    }
    """
    _URL = "https://huggingface.co/datasets/minskiter/weibo/resolve/main/"
    _URLS = {
        "train": _URL + "data/train.parquet",
        "validation": _URL + "data/validation.parquet",
        "test": _URL + "data/test.parquet",
    }
    class WeiboNamedEntities(datasets.GeneratorBasedBuilder):
        VERSION = datasets.Version("1.0.0")
        def _info(self):
            return datasets.DatasetInfo(
                description=_DESCRIPTION,
                features=datasets.Features(
                    {
                        "text": datasets.Sequence(datasets.Value("string")),
                        "labels": datasets.Sequence(
                            datasets.features.ClassLabel(
                                names=[
                                    'O',
                                    'B-PER.NAM',
                                    'I-PER.NAM',
                                    'E-PER.NAM',
                                    'S-PER.NAM',
                                    'B-ORG.NAM',
                                    'I-ORG.NAM',
                                    'E-ORG.NAM',
                                    'S-ORG.NAM',
                                    'B-LOC.NAM',
                                    'I-LOC.NAM',
                                    'E-LOC.NAM',
                                    'S-LOC.NAM',
                                    'B-GPE.NAM',
                                    'I-GPE.NAM',
                                    'E-GPE.NAM',
                                    'S-GPE.NAM',
                                    'B-PER.NOM',
                                    'I-PER.NOM',
                                    'E-PER.NOM',
                                    'S-PER.NOM',
                                    'B-ORG.NOM',
                                    'I-ORG.NOM',
                                    'E-ORG.NOM',
                                    'S-ORG.NOM',
                                    'B-LOC.NOM',
                                    'I-LOC.NOM',
                                    'E-LOC.NOM',
                                    'S-LOC.NOM',
                                    'B-GPE.NOM',
                                    'I-GPE.NOM',
                                    'E-GPE.NOM',
                                    'S-GPE.NOM',
                                ]
                            )
                        ),
                    }
                ),
                supervised_keys=None,
                homepage="https://aclanthology.org/D15-1064/",
                citation=_CITATION,
            )
        def _split_generators(self, dl_manager: DownloadManager):
            urls_to_download = _URLS
            download_files = dl_manager.download_and_extract(urls_to_download)
            return [
                datasets.SplitGenerator(
                    name=datasets.Split.TRAIN,
                    gen_kwargs={"filepath": download_files["train"]},
                ),
                datasets.SplitGenerator(
                    name=datasets.Split.VALIDATION,
                    gen_kwargs={"filepath": download_files["validation"]},
                ),
                datasets.SplitGenerator(
                    name=datasets.Split.TEST,
                    gen_kwargs={"filepath": download_files["test"]},
                ),
            ]
        def _generate_examples(self, filepath):
            # fix: https://discuss.huggingface.co/t/dataset-preview-error-with-a-dataset-script-and-parquet-files/43160
            with open(filepath, "rb") as f:
                with pq.ParquetFile(f) as file:
                    _id = -1
                    for i in file.iter_batches(batch_size=64):
                        rows = i.to_pylist()
                        for row in rows:
                            _id+=1
                            yield _id, row

    对于一些数据集默认非 arrow 格式的,可以先写一般原始加载程序,然后再利用 datasets 加载后保存为 parquet 格式(这个格式在 dataset 网页上可以直接被 dataviewer 展示)。

  4. 当一切就绪,使用 git push 将代码和数据集更新到 Huggingface Hub 中。

# 可能遇到的问题:

  1. parquet 直接用路径打开不展示且爆了错误:
    这个问题在 https://discuss.huggingface.co/t/dataset-preview-error-with-a-dataset-script-and-parquet-files/43160 中提到过。我们需要额外的使用 open 函数来打开文件指针,由于 open 函数在 dataset 后台被魔改过(支持打开远程文件),所以使用 open 然后再用 pq.ParquetFile 加载,dataviewer 就不会再报加载文件的错误。