NLP-transformer学习:(6)dataset 加载与调用
平常其实也经常进行trainning等等,但是觉得还是觉得要补补基础,所以静下心,搞搞基础联系
本章节基于 NLP-transformer学习:(5)讲解了如何做一个简单的训练和模型迁移,这里实践一个长用的dataset
相关课程其实是哔站上的视频课程,但是我这里将其实践,并融入自己的心得,代码地址如下:
https://github.com/MexWayne/mexwayne_transformers-code
提示:以下是本篇文章正文内容,下面案例可供参考
1 什么是datasets
地址:https://huggingface.co/datasets
datasets言而简之就是加载数据集用的
使用之前需要:
pip install datasets
有些特殊的库需要
pip install datasets[vision]
pip install datasets[audio]
2 datasets 实战
2.1 基础操作
加载代码如下:
# if the py name is datasets, the import action will first use the current file
# not the datasets installed by pip
# for example you may meet the error: will be "NameError: name 'load_dataset' is not defined"
from datasets import *
if __name__ == "__main__":
# add a dataset
data_set = load_dataset("madao33/new-title-chinese")
print(data_set)
print("------------------------------")
print("train[0]:")
print(data_set["train"][0])
print("------------------------------")
print("train[:2]:")
print(data_set["train"][:2])
print("------------------------------")
print("train[\"tile\"][:5]:")
print(data_set["train"]["title"][:5])
print("------------------------------")
这里注意的是,使用的python 文件名不能是“datasets”即重名,不然会首先找当前文件,然后报错:
NameError: name ‘load_dataset’ is not defined
当改为非datasets 名字后就可以看到数据加载
可以看到这个数据集中只有训练和验证数据集。
然后我们使用一些切片用法可以看到期望结果:
2.2 加载某一任务或某一部分
(1)加载某个任务
datasets 部分数据中不是只有数据还包含了很多任务
对于super_gule,这个datasets 是一个 任务的集合,如果我们要添加某一任务
我们可以这样做,代码如下:
# if the py name is datasets, the import action will first use the current file
# not the datasets installed by pip
# for example you may meet the error: will be "NameError: name 'load_dataset' is not defined"
from datasets import *
if __name__ == "__main__":
# add specific task
boolq_dataset = load_dataset("super_glue", "boolq",trust_remote_code=True)
print(boolq_dataset)
注意这里有个小细节,如果写成自动化代码时,可以加加上信任主机,这样就不用再敲入一个y
(2)加载某个部分(也叫某个划分)
load_dataset 支持加载某个部分,并且对某个部分进行切片,且切片还可以用%描述,但不能用小数描述
# if the py name is datasets, the import action will first use the current file
# not the datasets installed by pip
# for example you may meet the error: will be "NameError: name 'load_dataset' is not defined"
from datasets import *
if __name__ == "__main__":
## add a dataset
#data_set = load_dataset("madao33/new-title-chinese")
#print(data_set)
## add specific task
#boolq_dataset = load_dataset("super_glue", "boolq",trust_remote_code=True)
#print(boolq_dataset)
dataset = load_dataset("madao33/new-title-chinese", split="train")
print("train:")
print(dataset)
dataset = load_dataset("madao33/new-title-chinese", split="train[10:100]")
print("train 10:100:")
print(dataset)
dataset = load_dataset("madao33/new-title-chinese", split="train[10%:50%]")
print("train 10%:100%:")
print(dataset)
dataset = load_dataset("madao33/new-title-chinese", split=["train[:40%]", "train[40%:]"])
print("train 40% and 60%:")
print(dataset)
运行结果:
2.3 数据划分
这个dataset 自带了个调整比例的 函数:train_test_split
# if the py name is datasets, the import action will first use the current file
# not the datasets installed by pip
# for example you may meet the error: will be "NameError: name 'load_dataset' is not defined"
from datasets import *
if __name__ == "__main__":
datasets = load_dataset("madao33/new-title-chinese")
print("origin train datasets:")
print(datasets["train"])
print("-----------------")
print("make train set as test 0.1:")
dataset = datasets["train"]
print(dataset.train_test_split(test_size=0.1))
print("-----------------")
print("stratify:")
boolq_dataset = load_dataset("super_glue", "boolq",trust_remote_code=True)
dataset = boolq_dataset["train"]
print(dataset.train_test_split(test_size=0.1, stratify_by_column="label"))# 分类数据集可以按照比例划分
print("-----------------")
运行结果:
这里 test_size = 0.1 指,将训练数据的 0.1 用作test,即585 = 5850 × 0.1
stratify: 这样可以均衡数据
2.4 数据选取和过滤
from datasets import *
if __name__ == "__main__":
datasets = load_dataset("madao33/new-title-chinese")
# 选取
filter_res = datasets["train"].select([0, 1])
print("select:")
print(filter_res["title"][:5])
# 过滤
filter_dataset = datasets["train"].filter(lambda example: "中国" in example["title"])
print("filter:")
print(filter_dataset["title"][:5])
结果:
2.4 数据映射
数据映射,就是我们写一个函数,然后对数据集中的每个数据都做这样的处理
(1)将个每个数据处理下,这里举例家了前缀
代码:
from datasets import load_dataset
def add_prefix(example):
example["title"] = 'Prefix: ' + example["title"]
return example
if __name__ == "__main__":
datasets = load_dataset("madao33/new-title-chinese")
prefix_dataset = datasets.map(add_prefix)
print(prefix_dataset["train"][:10]["title"])
运行结果:
可以看到和期望一样,将每个title 加了个”prefix“
(2)将每个数据做tokenizer
from datasets import *
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
def preprocess_function(example, tokenizer = tokenizer):
model_inputs = tokenizer(example["content"], max_length = 512, truncation = True)
labels = tokenizer(example["title"], max_length=32, truncation=True)
# label就是title编码的结果
model_inputs["labels"] = labels["input_ids"]
return model_inputs
if __name__ == "__main__":
processed_datasets = datasets.map(preprocess_function)
print("train:")
print(processed_datasets["train"][:5])
print("validation:")
print(processed_datasets["validation"][:5])
结果可以看到,数据已经和前几章讲的类似,变成了token。
运行结果:
2.5 数据保存与加载
from datasets import *
from transformers import AutoTokenizer
if __name__ == "__main__":
datasets = load_dataset("madao33/new-title-chinese")
processed_datasets = datasets.map(preprocess_function)
print("from web:")
print(processed_datasets["validation"][:2])
processed_datasets = datasets.map(preprocess_function)
processed_datasets.save_to_disk("./processed_data")
processed_datasets = load_from_disk("./processed_data")
print("from local:")
print(processed_datasets["validation"][:2])
结果:
2.6 collater
transformer 的 collater 主要有以下作用:
(1)填充(Padding):
在自然语言处理中,输入序列的长度因为语法表达不同,因此有长短变化。为了将这些不同长度的序列组合成一个批次,collator 会将较短的序列用特殊的填充标记(如 [PAD])补齐,使得每个批次中的所有序列长度相同。
为什么要 padding?因为:
1)需要并行处理
2)简化模型设计,尤其需要使用 attention mask的时候
(2)截断(Truncating):
如果某些序列长度超过模型所能接受的最大长度,collator 会对这些序列进行截断,确保输入不超过模型的长度限制。
(3)格式化批次数据(Batch Formatting):
collator 将数据组织成模型所需的格式,通常包括:
输入的 token ID 序列。注意力掩码(Attention Mask),用于区分实际的 token 和填充的 token。有时还包括 Token Type ID,用于区分输入中的句子对(例如 BERT 模型在句子对任务中使用)。
(4)动态填充(Dynamic Padding):
某些 collator 支持动态填充,这意味着它只根据当前批次中最长的序列长度来进行填充,而不是使用固定的最大长度。这样可以提高计算效率,避免不必要的填充操作。
这里需要说明,动态填充可以根据批次中最长的序列进行填充,这样就可减少不必要的填充,进而减少计算量以节省时间。
代码:
from datasets import *
from transformers import AutoTokenizer
from transformers import DataCollatorWithPadding
def process_function(examples):
tokenized_examples = tokenizer(examples["review"], max_length=128, truncation=True)
tokenized_examples["labels"] = examples["label"]
return tokenized_examples
if __name__ == "__main__":
local_dataset = load_dataset("csv", data_files="/home/mex/Desktop/learn_transformer/mexwayne_transformers_NLP/01-Getting_Started/05-datasets/ChnSentiCorp_htl_all.csv", split="train")
local_dataset = local_dataset.filter(lambda x: x["review"] is not None)
print(local_dataset)
tokenized_dataset = local_dataset.map(process_function, batched=True, remove_columns=local_dataset.column_names)
print(tokenized_dataset[:3])
collator = DataCollatorWithPadding(tokenizer=tokenizer)
from torch.utils.data import DataLoader
dl = DataLoader(tokenized_dataset, batch_size = 4, collate_fn = collator, shuffle = True)
num = 0
for batch in dl:
print(batch["input_ids"].size())
num += 1
if num > 10:
break
print("finish!")
结果: