赞
踩
torch.utils.data主要包括以下三个类:
pytorch中加载数据的顺序是:
# 定义学习集 DataLoader
train_data = torch.utils.data.DataLoader(各种设置...)
# 将数据喂入神经网络进行训练
for i, (input, target) in enumerate(train_data):
循环代码行......
Dataset是我们用的数据集的库,是Pytorch中所有数据集加载类中应该继承的父类。其中父类中的两个私有成员函数必须被重载,否则将会触发错误提示。其中__len__应该返回数据集的大小,而__getitem__应该编写支持数据集索引的函数
class Dataset(object):
def __init__(self):
...
def __getitem__(self, index):
return ...
def __len__(self):
return ...
上面三个方法是最基本的,其中__getitem__是最主要的方法,它规定了如何读取数据。其主要作用是能让该类可以像list一样通过索引值对数据进行访问。
class FirstDataset(data.Dataset):#需要继承data.Dataset
def __init__(self):
# 初始化,定义你用于训练的数据集(文件路径或文件名列表),以什么比例进行sample(多个数据集的情况),每个epoch训练样本的数目,预处理方法等等
#也就是在这个模块里,我们所做的工作就是初始化该类的一些基本参数。
pass
def __getitem__(self, index):
#从文件中读取一个数据(例如,使用numpy.fromfile,PIL.Image.open)。
#预处理数据(例如torchvision.Transform)。
#返回数据对(例如图像和标签)。
#这里需要注意的是,第一步:read one data,是一个data
pass
def __len__(self):
# 定义为数据集的总大小。
图片加载的dataset可以参考帖子:《带你详细了解并使用Dataset以及DataLoader》
人民币二分类参考:《pytorch - 数据读取机制中的Dataloader与Dataset》
dataloader类调用torch.utils.Data.DataLoader,实际过程中数据集往往很大,通过DataLoader加载数据集使用mini-batch的时候可以使用多线程并行处理,这样可以加快我们准备数据集的速度。Datasets就是构建这个工具函数的实例参数之一。一般可以这么写:
train_loader = DataLoader(dataset=train_data, batch_size=6, shuffle=True ,num_workers=4)
test_loader = DataLoader(dataset=test_data, batch_size=6, shuffle=False,num_workers=4)
下面看看dataloader代码:
class DataLoader(object):
def __init__(self, dataset, batch_size=1, shuffle=False, sampler=None,
batch_sampler=None, num_workers=0, collate_fn=default_collate,
pin_memory=False, drop_last=False, timeout=0,
worker_init_fn=None)
self.dataset = dataset
self.batch_size = batch_size
self.num_workers = num_workers
self.collate_fn = collate_fn
self.pin_memory = pin_memory
self.drop_last = drop_last
self.timeout = timeout
self.worker_init_fn = worker_init_fn
想用随机抽取的模式加载输入,可以设置 sampler 或 batch_sampler。如何定义抽样规则,可以看sampler.py脚本,或者这篇帖子:《一文弄懂Pytorch的DataLoader, DataSet, Sampler之间的关系》
DataLoader__next__函数用for循环来遍历数据进行读取。
def __next__(self):
if self.num_workers == 0:
indices = next(self.sample_iter)
batch = self.collate_fn([self.dataset[i] for i in indices]) # this line
if self.pin_memory:
batch = _utils.pin_memory.pin_memory_batch(batch)
return batch
仔细看可以发现,前面还有一个self.collate_fn方法,这个是干嘛用的呢?在介绍前我们需要知道每个参数的意义:
看到这不难猜出collate_fn的作用就是将一个batch的数据进行合并操作。默认的collate_fn是将img和label分别合并成imgs和labels,所以如果你的__getitem__方法只是返回 img, label,那么你可以使用默认的collate_fn方法,但是如果你每次读取的数据有img, box, label等等,那么你就需要自定义collate_fn来将对应的数据合并成一个batch数据,这样方便后续的训练步骤。
def __setattr__(self, attr, val):
if self.__initialized and attr in ('batch_size', 'sampler', 'drop_last'):
raise ValueError('{} attribute should not be set after {} is '
'initialized'.format(attr, self.__class__.__name__))
super(DataLoader, self).__setattr__(attr, val)
def __iter__(self):
return _DataLoaderIter(self)
def __len__(self):
return len(self.batch_sampler)
当代码运行到要从torch.utils.data.DataLoader类生成的对象中取数据的时候,比如:
train_data=torch.utils.data.DataLoader(...)
for i, (input, target) in enumerate(train_data):
就会调用DataLoader类的__iter__方法:return DataLoaderIter(self),此时牵扯到DataLoaderIter类:
def __iter__(self):
if self.num_workers == 0:
return _SingleProcessDataLoaderIter(self)
else:
self.check_worker_number_rationality()
return _MultiProcessingDataLoaderIter(self)
MultiProcessingDataLoaderIter继承的是BaseDataLoaderIter,开始初始化,然后Dataloader进行初始化,然后进入 next __()方法 随机生成索引,进而生成batch,最后调用 _get_data() 方法得到data。idx, data = self._get_data(), data = self.data_queue.get(timeout=timeout)
总结一下:
DataLoaderIter的源码及详细解读参考:《PyTorch源码解读之torch.utils.data.DataLoader》
ataloader本质上是一个可迭代对象,但是dataloader不能像列表那样用索引的形式去访问,而是使用迭代遍历的方式。
for i in dataLoader:
print(i.keys())
也可以使用enumerate(dataloader)的形式访问。
在计算i的类型时,发现其为一个字典,打印这个字典的关键字可得到
for i in dataLoader:
print(i.keys())
dict_keys(['text', 'audio', 'vision', 'labels'])
同理,计算 **i[‘text’]**发现其为一个张量,打印该张量信息
print(i['text'].shape) #64*39*768
此时的64恰好就是我们设置的batchsize,并且最后一个i值的text的shape为2439768,即24个数据
GLUE榜单包含了9个句子级别的分类任务,分别是:
加载数据集
from datasets import load_dataset
raw_datasets = load_dataset("glue","sst2")
预处理数据
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
def tokenize_function(examples):
return tokenizer(examples["sentence"], padding="max_length", truncation=True)
tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))
full_train_dataset = tokenized_datasets["train"]
full_eval_dataset = tokenized_datasets["test"]
定义评估函数
import numpy as np
from datasets import load_metric
metric = load_metric("glue","sst2")#改成"accuracy"效果一样吗?
def compute_metrics(eval_pred):
logits, labels = eval_pred
predictions = np.argmax(logits, axis=-1)
return metric.compute(predictions=predictions, references=labels)
加载模型
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", num_labels=2)
配置 Trainer参数:
from transformers import TrainingArguments,Trainer args = TrainingArguments( "ft-sst2", # 输出路径,存放检查点和其他输出文件 evaluation_strategy="epoch", # 定义每轮结束后进行评价 learning_rate=2e-5, # 定义初始学习率 per_device_train_batch_size=16, # 定义训练批次大小 per_device_eval_batch_size=16, # 定义测试批次大小 num_train_epochs=2, # 定义训练轮数 ) trainer = Trainer( model=model, args=training_args, train_dataset=small_train_dataset, eval_dataset=small_eval_dataset, compute_metrics=compute_metrics, )
开始训练:
trainer.train()
训练完毕后,执行以下代码,得到模型在验证集上的效果:
trainer.evaluate()
{'epoch': 2,
'eval_loss': 0.9351930022239685,
'eval_accuracy'': 0.7350917431192661
}
重新启动笔记本以释放一些内存,或执行以下代码:
del model
del pytorch_model
del trainer
torch.cuda.empty_cache()
首先,我们需要定义数据加载器,我们将使用它来迭代批次。 在这样做之前,我们只需要对我们的 tokenized_datasets 应用一些后处理:
tokenized_datasets 对每个步骤处理如下:
tokenized_datasets = tokenized_datasets.remove_columns(["sentence","idx"])#删除多余的“sebtence”列和“idx”列,否则会报错forward() got an unexpected keyword argument 'idx'
tokenized_datasets = tokenized_datasets.rename_column("label", "labels")#列“label”重命名为“labels”,否则报错forward() got an unexpected keyword argument 'label'
tokenized_datasets.set_format("torch")#返回 PyTorch 张量,否则报错'list' object has no attribute 'size'
二三步也可以合并:
columns = ['input_ids', 'token_type_ids', 'attention_mask', 'labels']
tokenized_datasets.set_format(type='torch', columns=columns)
切出一部分数据集
small_train_dataset = tokenized_datasets["train"].shuffle(seed=42).select(range(1000))
small_eval_dataset = tokenized_datasets["test"].shuffle(seed=42).select(range(1000))
定义dataloaders:
from torch.utils.data import DataLoader
train_dataloader = DataLoader(small_train_dataset, shuffle=True, batch_size=8)
eval_dataloader = DataLoader(small_eval_dataset, batch_size=8)
定义模型:
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained("bert-base-cased", num_labels=2)
定义优化器optimizer 和学习率调度器scheduler:
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=5e-5)
#默认使用的学习率调度器只是线性衰减从最大值(此处为 5e-5)到 0:
from transformers import get_scheduler
num_epochs = 3
num_training_steps = num_epochs * len(train_dataloader)
lr_scheduler = get_scheduler(
"linear",
optimizer=optimizer,
num_warmup_steps=0,
num_training_steps=num_training_steps
)
使用GPU进行训练:
import torch
device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
model.to(device)
使用 tqdm 库在训练步骤数上添加了一个进度条,并定义训练循环:
from tqdm.auto import tqdm
progress_bar = tqdm(range(num_training_steps))
model.train()#设置train状态,启用 Batch Normalization 和 Dropout。
for epoch in range(num_epochs):
for batch in train_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
outputs = model(**batch)
loss = outputs.loss
loss.backward()
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
progress_bar.update(1)
编写评估循环,在循环完成时计算最终结果之前累积每个批次的预测:
metric= load_metric("accuracy")
model.eval()
for batch in eval_dataloader:
batch = {k: v.to(device) for k, v in batch.items()}
with torch.no_grad():
outputs = model(**batch)
logits = outputs.logits
predictions = torch.argmax(logits, dim=-1)
metric.add_batch(predictions=predictions, references=batch["labels"])
metric.compute()
dataset = load_dataset('glue', 'rte')
metric = load_metric('glue', 'rte')
tokenizer = BertTokenizerFast.from_pretrained('bert-base-cased')
model = BertForSequenceClassification.from_pretrained('bert-base-cased', return_dict=True)
def tokenize(examples):
return tokenizer(examples['hypothesis'],examples['premiere'] truncation=True, padding='max_length')
dataset = dataset.map(tokenize, batched=True)
其它代码一样.更多文本分类参考datawhale-transformer教程4.1:《文本分类》
要查看更多微调示例,您可以参考:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。