深度学习-数据加载优化-训练速度提升一倍
Posted yealxxy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度学习-数据加载优化-训练速度提升一倍相关的知识,希望对你有一定的参考价值。
1,介绍
-
数据加载
深度学习的训练,简单的说就是将数据切分成batch,丢入模型中,并计算loss训练。其中比较重要的一环是数据打batch部分(数据加载部分)。 -
训练时间优化:
深度学习训练往往需要大量的数据,训练过程也比较慢,常见的提升训练速度的方法包括:数据加载优化、模型计算优化、fp16半精度训练、加大batch、多卡训练等方法。这篇文章主要介绍从数据加载的思路提升训练速度。 -
结论:
数据加载优化后,可以提升1倍以上的训练速度。
2,数据加载流程
- 数据加载一般分为四步:
- 从文本中读取数据,并处理成想要的格式。(比如分类任务,就需要输入+label的格式)
- 将读取的输入,转换成模型的输入feature。(nlp中一般是把文本切分成token)
- 创建:dataset、dataloader、sampler。(数据的采样方式,打batch的方式)
- 遍历训练数据,并训练
- 数据优化在第二步、已经第三步中。具体如下
examples, label_list = get_data(data_dir=file_dir)
# 构建 feature
features = convet_text_feature(examples)
# 构建 dataset、dataloader、sampler
train_dataset = ClassifyDataset(features, examples)
train_sampler = SequentialSampler(train_dataset)
train_dataloader = DataLoader(
train_dataset,
sampler=train_sampler,
batch_size=4, collate_fn=torch.Tensor)
# 获取训练数据
for step, batch in enumerate(train_dataloader):
print(batch.shape)
3, 简单版本
(完整代码:https://github.com/xxyliuyang/yl_nlp/blob/main/data_load_opt/simple.py)
- 数据转换成feature步骤:将文本切词并转换成token id;之后,一次性将所有数据padding成最大长度。
def convet_text_feature(examples):
tokenizer = AutoTokenizer.from_pretrained(tokenize_model)
features = []
for example in examples['train']:
ids = tokenizer.convert_tokens_to_ids(tokenizer.tokenize(example.text_a, add_special_tokens=True))
features.append(ids)
# pad
length = [len(ids) for ids in features]
max_length = max(length)
for i, feature in enumerate(features):
features[i] = feature + [tokenizer.pad_token_id for _ in range(max_length-len(feature))]
print(max_length)
return features
- 数据打batch: 随机的方法每次定时去取固定大小的数据即可
4, 高级版本
(完整代码:https://github.com/xxyliuyang/yl_nlp/blob/main/data_load_opt/advance.py)
可能已经发现问题了:
- 问题一:转换成feature过程中一次性padding成最长,那么每次送入模型过程中,padding部分的计算就是多余的计算,大大增加的计算量。
- 优化过程:在转换成feature部分,不进行padding,送入模型之前动态的padding
def convet_text_feature(examples):
features = []
for example in examples['train']:
ids = tokenizer.convert_tokens_to_ids(tokenizer.tokenize(example.text_a, add_special_tokens=True))
features.append(ids)
# no pad
length = [len(ids) for ids in features]
max_length = max(length)
print(max_length)
return features
def batch_list_to_batch_tensors(batch): # 动态padding 函数
batch_tensors = []
max_len = max([len(x) for x in batch])
for feature in batch:
feature = feature + [tokenizer.pad_token_id for _ in range(max_len-len(feature))]
batch_tensors.append(feature)
return torch.tensor(batch_tensors, dtype=torch.long)
- 问题二:如果是随机构建batch,那么输入长度是不确定的,假设一个batch,最小的文本长度是10,最大的文本长度是100,那么10长度的输入也要padding成100,同样无形中增加了过多的padding无效计算。
- 优化过程:通过sampler优化,将相同长度的数据放到一个batch里面,减少padding的长度。
class BucketSampler(Sampler):
def __init__(self, data_source, padding_noise=0.1):
super().__init__(data_source)
self.lengths = [len(x) for x in data_source.features]
self.padding_noise = padding_noise
def _add_noise_to_value(self, value: int):
noise_value = value * self.padding_noise
noise = random.uniform(-noise_value, noise_value)
return value + noise
def __iter__(self):
noisy_lengths = [self._add_noise_to_value(l) for l in self.lengths]
indice_lengths = [(idx, length) for idx, length in enumerate(noisy_lengths)]
indice_lengths.sort(key=lambda x: x[1])
return iter([indice_length[0] for indice_length in indice_lengths])
def __len__(self):
return len(self.lengths)
总结:
数据加载优化:
- 先打batch,然后动态的padding(减少整体数据的padding长度)。
- 将相同长度的数据放到一个batch里面(减少单个batch里面padding的长度)。
以上是关于深度学习-数据加载优化-训练速度提升一倍的主要内容,如果未能解决你的问题,请参考以下文章