一个通用的用于NLP数据处理的开源库

目录

    • 1、引言
    • 2、安装
    • 3、快速使用
      • 3.1、查看支持的数据集
      • 3.2、加载数据集
      • 3.3、微调BERT模型
        • 3.3.1、tokenizing数据集
        • 3.3.2、格式化数据集
    • 4、总结与展望

1、引言

  作为NLP领域的研究者来说,特别是对需要发论文的学生或搞研究的专家来说,每次处理数据集是很麻烦的事情。大部分研究使用的数据集都是公开的,有些数据处理过程是通用的,而我们一直都在重复造轮子。如果有一个开源库专门用来管理NLP领域的公开研究数据集,那么我们在发论文的时候就会快人一步,至少缩短发论文的周期。
  HuggingFace团队在 2020年5月15日放出了一个名为Datasets的开源项目,该项目用于自然语言处理的数据集和评估指标。兼容Numpy,Pandas, Pytorch和Tensorflow,Datasets库可轻松共享和访问用于自然语言处理(NLP)的数据集和评估指标。项目文档中可以看到Datasets库的一些特性,这里我就不罗嗦重复了,感兴趣的读者可以去看看。
  后面的内容简要的介绍如何使用该库。

2、安装

  使用如下命令即可安装使用:

pip install datasets

  检查安装是否成功:

python -c "from datasets import load_dataset; print(load_dataset('squad', split='train')[0])"

3、快速使用

3.1、查看支持的数据集

一个通用的用于NLP数据处理的开源库_第1张图片到目前为止(我使用的版本是1.4.1),可以看到已经有727个NLP数据集了。

3.2、加载数据集

  下面这2行代码是加载内置的数据集,当首次输入以下代码时,处理脚本会调用builder,它负责加载MRPC/GLUE数据集,接着将被下载,缓存和导入。然后,数据集文件本身被下载并缓存(通常从原始数据集URL)并进行处理以返回datasets.Dataset

from datasets import load_dataset

dataset = load_dataset('glue', 'mrpc', split='train')

  注意,由于国内网络的原因,可能下载这些数据会比较慢。如果你没有指定split参数,此方法将返回一个字典,其中包含数据集中每个数据集的划分数据集,例如train,test数据集。
  对于返回的dataset,可以查询其长度,获取单行,但也可以获取多行,甚至沿列进行索引:

>>> len(dataset)
3668
>>> dataset[0]
{'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .',
 'label': 1,
 'idx': 0}

可以通过Dataset.features()打印数据集来查看数据集中的列:

>>> dataset.features
{'idx': Value(dtype='int32', id=None),
 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
 'sentence1': Value(dtype='string', id=None),
 'sentence2': Value(dtype='string', id=None)}

  如果你想从本地加载你自己的CSV, JSON, text或pandas文件来创建datasets.Dataset, 你可以使用csv, json, text或者pandas builder。以下是一些要从CSV文件加载的示例:

from datasets import load_dataset

# 接受一个文件路径
dataset = load_dataset('csv', data_files='my_file.csv')

# 接受一个列表的文件路径
dataset = load_dataset('csv', data_files=['my_file_1.csv', 
                                          'my_file_2.csv', 
                                          'my_file_3.csv'])
# 接受字典作为文件路径
dataset = load_dataset('csv', data_files={'train': ['my_train_file_1.csv', 
                                                    'my_train_file_2.csv'],
                                          'test': 'my_test_file.csv'})

3.3、微调BERT模型

  我们将使用上面加载的glue数据集中的mrpc(Microsoft Research Paraphrase Corpus)去微调一个BERT模型,mrpc由微软发布,判断两个给定句子,是否具有相同的语义,属于句子对的文本二分类任务;这是一个复述分类(Paraphrase Classification)的句子对分类任务。
  从以上dataset.features可以看到,标签是一个数据集ClassLabel实例,具有两个类别:not_equivalent和equivalent。我们可以使用datasets.Dataset.filter()ClassLabelfeatures方法来调用datasets.ClassLabel.str2int()名称到整数的转换方法来打印每个类的一个示例:

>>> dataset.filter(lambda example: example['label'] == dataset.features['label'].str2int('equivalent'))[0]
{'idx': 0,
 'label': 1,
 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'
}
>>> dataset.filter(lambda example: example['label'] == dataset.features['label'].str2int('not_equivalent'))[0]
{'idx': 1,
 'label': 0,
 'sentence1': "Yucaipa owned Dominick 's before selling the chain to Safeway in 1998 for $ 2.5 billion .",
 'sentence2': "Yucaipa bought Dominick 's in 1995 for $ 693 million and sold it to Safeway for $ 1.8 billion in 1998 ."
}

  现在,我们的目标是训练一个模型,该模型可以从一对句子中预测正确的标签(not_equivalent和equivalent)。导入BERT预训练模型和它的分词器(基于tensorflow2):

>>> from transformers import TFAutoModelForSequenceClassification, AutoTokenizer
>>> model = TFAutoModelForSequenceClassification.from_pretrained("bert-base-cased")
>>> tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')

3.3.1、tokenizing数据集

>>> print(tokenizer(dataset[0]['sentence1'], dataset[0]['sentence2']))
{'input_ids': [101, 7277, 2180, 5303, 4806, 1117, 1711, 117, 2292, 1119, 1270, 107, 1103, 7737, 107, 117, 1104, 9938, 4267, 12223, 21811, 1117, 2554, 119, 102, 11336, 6732, 3384, 1106, 1140, 1112, 1178, 107, 1103, 7737, 107, 117, 7277, 2180, 5303, 4806, 1117, 1711, 1104, 9938, 4267, 12223, 21811, 1117, 2554, 119, 102],
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
}
>>> tokenizer.decode(tokenizer(dataset[0]['sentence1'], dataset[0]['sentence2'])['input_ids'])
'[CLS] Amrozi accused his brother, whom he called " the witness ", of deliberately distorting his evidence. [SEP] Referring to him as only " the witness ", Amrozi accused his brother of deliberately distorting his evidence. [SEP]'

  在我们的例子中,我们希望对整个数据集进行标记化,因此我们将使用一种名为datasets.Dataset.map()的方法将编码过程应用于其整个数据集。为确保我们可以轻松地为模型构建张量批处理,我们将截断输入并将其填充到模型的最大长度。

>>> def encode(examples):
>>>     return tokenizer(examples['sentence1'], examples['sentence2'], truncation=True, padding='max_length')
>>>
>>> dataset = dataset.map(encode, batched=True)
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████| 4/4 [00:02<00:00,  1.75it/s]
>>> dataset[0]
{'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .',
 'label': 1,
 'idx': 0,
 'input_ids': array([  101,  7277,  2180,  5303,  4806,  1117,  1711,   117,  2292, 1119,  1270,   107,  1103,  7737,   107,   117,  1104,  9938, 4267, 12223, 21811,  1117,  2554,   119,   102, 11336,  6732, 3384,  1106,  1140,  1112,  1178,   107,  1103,  7737,   107, 117,  7277,  2180,  5303,  4806,  1117,  1711,  1104,  9938, 4267, 12223, 21811,  1117,  2554,   119,   102]),
 'token_type_ids': array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
 'attention_mask': array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])}

3.3.2、格式化数据集

  现在,我们已经编码了数据集,我们想在tf.data.Dataset中使用它,并使用它来训练我们的模型。为了能够使用此数据集和Tensorflow训练我们的模型,我们需要做三处修改:

  • 在labels中重命名我们的label列,这是TFBertForSequenceClassification中标签的预期输入名称,
  • 从我们的数据集中获取tensorflow张量。
  • 过滤列以仅返回模型输入所需的列子集(input_id,token_type_id和attention_mask)。

  第一个修改只是按如下方式重命名该列的问题(我们也可以在标记化过程中完成它):

>>> dataset = dataset.map(lambda examples: {'labels': examples['label']}, batched=True)

另外两个修改可以由datasets.Dataset.set_format()方法处理,该方法将即时转换datasets.Dataset .__ getitem __()返回的输出,以过滤不需要的列并转换Tensorflow张量中的python对象。
  下面是使用datasets.Dataset.set_format()将正确的格式应用于数据集并将其包装在tf.data.Dataset中的方法:

>>> import tensorflow as tf
>>> dataset.set_format(type='tensorflow', columns=['input_ids', 'token_type_ids', 'attention_mask', 'labels'])
>>> features = {x: dataset[x].to_tensor(default_value=0, shape=[None, tokenizer.model_max_length]) for x in ['input_ids', 'token_type_ids', 'attention_mask']}
>>> tfdataset = tf.data.Dataset.from_tensor_slices((features, dataset["labels"])).batch(32)
>>> next(iter(tfdataset))
({'input_ids': <tf.Tensor: shape=(32, 512), dtype=int32, numpy=
array([[  101,  7277,  2180, ...,     0,     0,     0],
       [  101, 10684,  2599, ...,     0,     0,     0],
       [  101,  1220,  1125, ...,     0,     0,     0],
       ...,
       [  101,  1109,  2026, ...,     0,     0,     0],
       [  101, 22263,  1107, ...,     0,     0,     0],
       [  101,   142,  1813, ...,     0,     0,     0]], dtype=int32)>, 'token_type_ids': <tf.Tensor: shape=(32, 512), dtype=int32, numpy=
array([[0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       ...,
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0],
       [0, 0, 0, ..., 0, 0, 0]], dtype=int32)>, 'attention_mask': <tf.Tensor: shape=(32, 512), dtype=int32, numpy=
array([[1, 1, 1, ..., 0, 0, 0],
       [1, 1, 1, ..., 0, 0, 0],
       [1, 1, 1, ..., 0, 0, 0],
       ...,
       [1, 1, 1, ..., 0, 0, 0],
       [1, 1, 1, ..., 0, 0, 0],
       [1, 1, 1, ..., 0, 0, 0]], dtype=int32)>}, <tf.Tensor: shape=(32,), dtype=int64, numpy=
array([1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
       0, 1, 1, 1, 0, 0, 1, 1, 1, 0])>)

  让我们开始训练BERT模型:

>>> loss_fn = tf.keras.losses.SparseCategoricalCrossentropy(reduction=tf.keras.losses.Reduction.NONE, from_logits=True)
>>> opt = tf.keras.optimizers.Adam(learning_rate=3e-5)
>>> model.compile(optimizer=opt, loss=loss_fn, metrics=["accuracy"])
>>> model.fit(tfdataset, epochs=3)

4、总结与展望

  本文简要的介绍了Datasets库的使用,该库不仅简化了很多数据处理的工作,还省去了找数据的麻烦。不过,在实际研究工作中,我们需要处理自己的数据,可以去官方文档上查看这方面的内容。
  未来,如果有时间的话,可以写一些使用该库来做实体识别和关系抽取等任务的教程。

你可能感兴趣的:(自然语言处理,自然语言处理,tensorflow)