Traceback (most recent call last):
File "main.py", line 72, in <module>
trainer.train()
File "/data/yutian/anaconda3/envs/py37/lib/python3.7/site-packages/transformers/trainer.py", line 1411, in train
ignore_keys_for_eval=ignore_keys_for_eval,
File "/data/yutian/anaconda3/envs/py37/lib/python3.7/site-packages/transformers/trainer.py", line 1623, in _inner_training_loop
for step, inputs in enumerate(epoch_iterator):
File "/data/yutian/anaconda3/envs/py37/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 681, in __next__
data = self._next_data()
File "/data/yutian/anaconda3/envs/py37/lib/python3.7/site-packages/torch/utils/data/dataloader.py", line 721, in _next_data
data = self._dataset_fetcher.fetch(index) # may raise StopIteration
File "/data/yutian/anaconda3/envs/py37/lib/python3.7/site-packages/torch/utils/data/_utils/fetch.py", line 52, in fetch
return self.collate_fn(data)
File "/data/yutian/anaconda3/envs/py37/lib/python3.7/site-packages/transformers/data/data_collator.py", line 67, in default_data_collator
return torch_default_data_collator(features)
File "/data/yutian/anaconda3/envs/py37/lib/python3.7/site-packages/transformers/data/data_collator.py", line 131, in torch_default_data_collator
batch[k] = torch.tensor([f[k] for f in features])
ValueError: expected sequence of length 44 at dim 1 (got 40)
问题描述:使用如下代码进行训练时报错,实际上是有输入的一个batch内的维度不同,导致tensor不能拼接。
model = FineTuneT5Model()
# tokenizer = T5Tokenizer.from_pretrained("/data/yutian/DIUR/model_hub/my_t5")
# data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
training_args = TrainingArguments(
output_dir = './checkpoints',
num_train_epochs = 5,
per_device_train_batch_size=2, # batch size per device during training 训练批大小
per_device_eval_batch_size=2, # batch size for evaluation 评估批大小
logging_dir='./logs/trainer_log', # directory for storing logs 日志存储位置
learning_rate=1e-3, # 学习率
save_steps=500,
)
trainer = Trainer(
model = model,
args = training_args,
train_dataset = dataset,
eval_dataset = valid_dataset,
compute_metrics = get_metric_func
)
trainer.train()
来龙去脉:
通过datasets中的Dataset构建数据。希望给模型输入两个文本特征,一个标签。也就是说,现在是一个字典,前两个键的值都是字符串的列表,相当于s2s中的成对语料;第三个键对应的是一个int的列表,希望用于分类的标签。通过如下代码构建Dataset。
data_dict = {'src_text_field':self.src_text_field,
'tgt_text_field':self.tgt_text_field,
'label_field':self.label_field}
dataset = Dataset.from_dict(data_dict)
通过如下代码对src和tgt进行tokenize。需要注意的是,tokenizer返回的都包括input_ids,直接将返回值map给dataset,会导致第二次赋值的时候覆盖。所以,在给tgt做tokenize的时候,新保存了一个键,加入了dataset中。
tokenizer = T5Tokenizer.from_pretrained("/data/yutian/DIUR/model_hub/my_t5")
def src_preprocess_function(examples):
text_token = tokenizer(examples['src_text_field'], padding = True, truncation=True, max_length=256, return_token_type_ids=False)
logging.info(text_token)
return text_token
dataset = dataset.map(src_preprocess_function, batched=True, batch_size=8)
def tgt_preprocess_function(examples):
text_token = tokenizer(examples['tgt_text_field'], padding = True, truncation=True, max_length=256, return_token_type_ids=False, return_attention_mask=False)
new_dict = {'tgt_ids': text_token['input_ids']}
return new_dict
dataset = dataset.map(tgt_preprocess_function, batched=True, batch_size=8)
with open(os.path.join('./cache', self.dataset_name, self.mode+'.pkl'), 'wb') as f:
pickle.dump(dataset, f)
在主函数中,通过打印dataset内的数据,发现每8个数据的对应键size相同。然而在通过Trainer的时候,数据会传给collator。这个的作用是将batch的数据转化成tensor,或者做其它预处理。我自定义了一个collator,传递给trainer。发现tgt_id的size异常。input_id和attention_mask能够向量化(因为形状相同),但是tgt_id长短不一,怀疑在加载的时候被打乱了顺序,或者发生了其它事情。能够保证直接print dataset中的数据时,tgt_id是每8个形状相同;但是不知道什么原因,加载之后不是了。
def DataCollator(features):
for i in features:
print(len(i['tgt_ids']))
return 0
请各位熟悉这几个工具的佬给出建议。目前就是希望输入两个文本域,一个数字域;希望能够给出目前问题的原因或其它使用transformers Trainer、 datasets的建议!
参考GPT和自己的思路,根据报错信息,“expected sequence of length 44 at dim 1 (got 40)”可以得知,你的训练数据的某个batch内的维度不同,具体来说,期望维度为44,而实际维度为40。这可能是由于你的数据中存在长度不同的序列,例如某些文本序列的长度超过了256个token,而在进行tokenize后被截断,导致长度不足44。建议检查一下你的训练数据,确保所有的序列长度都不超过256个token,或者调整你的模型和训练参数,以适应长度不同的序列。此外,也可以在自定义的collator中对长度不足的序列进行padding,使得所有序列长度一致。
下面是一些修改代码的建议,以使所有的样本长度相同:
找到您的数据集中最长的样本,并将所有样本的长度调整为该最长样本的长度。您可以使用torch.nn.utils.rnn.pad_sequence函数对序列进行填充,使它们具有相同的长度。
如果您使用的是datasets库,可以在Dataset.map方法中使用batched=True选项,这会自动将数据集中的样本批次到一个具有相同长度的张量。如果您使用的是自定义的数据集,请确保将所有样本的长度调整为相同的长度。
在使用自定义collator之前,可以尝试使用transformers库中提供的默认collator(default_data_collator),它可以自动调整批次中的样本长度。
如果上述方法仍然无法解决问题,可以尝试将batch size逐渐减小,或者使用更小的模型或更长的训练时间,以便使模型适应更多的长度差异。
以下是修改后的代码示例:
from torch.nn.utils.rnn import pad_sequence
# 1. 找到最长的样本,并将所有样本的长度调整为该最长样本的长度
max_len = max(len(x['input_ids']) for x in dataset)
for example in dataset:
example['input_ids'] = example['input_ids'] + [0] * (max_len - len(example['input_ids']))
example['attention_mask'] = example['attention_mask'] + [0] * (max_len - len(example['attention_mask']))
example['token_type_ids'] = example['token_type_ids'] + [0] * (max_len - len(example['token_type_ids']))
# 2. 使用Dataset.map方法,自动批次到具有相同长度的张量
dataset = dataset.map(lambda x: {'input_ids': x['input_ids'], 'attention_mask': x['attention_mask'], 'token_type_ids': x['token_type_ids'], 'labels': x['label']}, batched=True)
# 3. 尝试使用transformers库中提供的默认collator
from transformers import default_data_collator
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset,
data_collator=default_data_collator, # 使用默认collator
compute_metrics=get_metric_func
)
# 4. 如果仍然无法解决问题,可以逐渐减小batch size或者使用更小的模型或更长的训练时间
5. 训练模型
trainer = Trainer(
model=model,
args=args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
compute_metrics=compute_metrics
)
trainer.train()
6. 测试模型
test_results = trainer.predict(test_dataset=test_dataset)
test_metrics = compute_metrics(test_results)
7. 输出测试结果
print(test_metrics)
以下答案由GPT-3.5大模型与博主波罗歌共同编写:
问题分析:
根据你提供的错误信息,可以看出是 batch 内的数据维度不一致导致的。一般情况下,如果使用 datasets 中自带的 collator 或者默认的 collator,则数据的维度应该是一致的,否则会引起上述错误。
通过你提供的描述,我发现你使用了自定义的 collator,因此我怀疑是在构建数据集时的 preprocess_function 函数出了问题。
根本原因:
从你的代码中可以看出,在 tgt 的 preprocess_function 函数中,将 input_ids 包装成了一个新的字典,并用一个新的键 “tgt_ids” 存储其值。这样做的结果是,在构建 Dataset 后, dataset 内部已经有 “input_ids” 这个键值了,但是在 Trainer 中传递 dataset 时,你的自定义的 collator 中并没有将 “input_ids” 这个键值和 “tgt_ids” 这个键值都包含进来,导致数据维度不一致。
解决方案:
一、在 preprocess_function 函数中将这三个键值都包含进新的字典中,改写如下:
def tgt_preprocess_function(examples):
text_token = tokenizer(
examples['tgt_text_field'],
padding=True, truncation=True,
max_length=256,
return_token_type_ids=False,
return_attention_mask=False
)
new_dict = {
'input_ids': text_token['input_ids'],
'attention_mask': text_token['attention_mask'],
'tgt_ids': text_token['input_ids']
}
return new_dict
dataset = dataset.map(
tgt_preprocess_function,
batched=True,
batch_size=8,
remove_columns=['tgt_text_field']
)
这个函数中,我们将 input_ids 和 attention_mask 分别存在 “input_ids” 和 “attention_mask” 键值下,而将 “tgt_ids” 的值也设置成 input_ids。接着在使用 dataset.map() 函数映射时,我们还用 remove_columns() 移除了原本的 “tgt_text_field” 列。这样,我们可以将所有的键值都包含进来,保证数据维度一致。
二、如果你觉得上面的方法过于复杂,你也可以不使用自定义的 collator,而是使用默认的 collator:
from transformers import DataCollatorWithPadding
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
然后在创建 Trainer 时直接传入 data_collator 即可。
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=valid_dataset,
data_collator=data_collator,
compute_metrics=get_metric_func
)
最后重申一遍,数据维度不一致是造成训练出现错误的主要原因之一,因此在使用 Trainer 时一定要注意数据的维度是否一致,使用预定义的 collator 可以避免一些不必要的问题。
如果我的回答解决了您的问题,请采纳!
该回答引用自ChatGPT:
这个问题的原因在于训练数据中有一个batch的数据的文本长度不同,导致数据不能被正确地拼接成一个tensor。这是一个很常见的问题,在处理自然语言数据时很容易发生。为了解决这个问题,有几种方法:
1.检查训练数据并删除不符合要求的数据。例如,在本例中,可以检查数据中的文本长度是否都小于等于256,如果不是,则可以将这些数据从训练数据中删除。
2.在DataLoader中使用collate_fn参数指定一个自定义的collator,来处理不同长度的数据。这个collator可以将数据按照文本长度进行排序,然后在将它们转换为tensor时,对长度不足的数据进行padding。在transformers库中已经提供了一个默认的DataCollatorWithPadding类,可以用来处理这种情况。
3.在训练时使用动态padding策略,使得每个batch内的数据长度都相同。这可以通过在TrainingArguments中设置pad_to_multiple参数来实现。例如,如果你将pad_to_multiple设置为8,则每个batch中的数据将被填充到长度为8的倍数。
在本例中,可以尝试使用第二种方法,在DataLoader中使用默认的DataCollatorWithPadding类。如果你想自定义collator,可以使用该类作为参考,并在其中添加适当的逻辑。以下是使用默认DataCollatorWithPadding类的示例代码:
from transformers import TrainingArguments, Trainer, DataCollatorWithPadding
training_args = TrainingArguments(
output_dir='./checkpoints',
num_train_epochs=5,
per_device_train_batch_size=2,
per_device_eval_batch_size=2,
logging_dir='./logs/trainer_log',
learning_rate=1e-3,
save_steps=500,
# 设置pad_to_multiple参数,使得每个batch内的数据长度都相同
pad_to_multiple=8,
)
data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset,
data_collator=data_collator,
compute_metrics=get_metric_func,
)
trainer.train()
如果你想自定义collator,可以参考以下示例代码:
from transformers import TrainingArguments, Trainer
class MyCollator:
def __init__(self, tokenizer):
self.tokenizer = tokenizer
def __call__(self, examples):
input_ids = [example['input_ids'] for example in examples]
attention_mask = [example['attention_mask'] for example in examples]
labels = [example['label'] for example in examples]
# 根据input_ids的长度排序,方便后面做padding
sorted_inputs = sorted(zip(input_ids, attention_mask, labels), key=lambda x: len(x[0]))
input_ids = [x[0] for x in sorted_inputs]
attention_mask = [x[1] for x in sorted_inputs]
labels = [x[2] for x in sorted_inputs]
# 对input_ids和attention_mask做padding
input_ids = self