yutianCHN 2023-03-02 23:27 采纳率: 0%
浏览 142
已结题

关于Transformers Trainer, datasets Dataset的问题

Transformers Trainer && datasets Dataset 问题

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的建议!

  • 写回答

3条回答 默认 最新

  • 「已注销」 2023-03-03 07:31
    关注

    参考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)
    
    
    评论

报告相同问题?

问题事件

  • 系统已结题 3月10日
  • 修改了问题 3月2日
  • 创建了问题 3月2日