深度学习训练加速方法

一.GPU利用率低怎么办

    • 增加batch size,增加GPU的内存占用率
    • 在数据加载时候,将num_workers线程数设置稍微大一点,推荐是8,16等,且开启pin_memory=True。
torch.utils.data.DataLoader(image_datasets[x],
                            batch_size=batch_size, 
                            shuffle=True,
                            num_workers=8,
                            pin_memory=True)

打开pin_memory打开,就省掉了将数据从CPU传入到缓存RAM里面,再给传输到GPU上;
为True时是直接映射到GPU的相关内存块上,省掉了一点数据传输时间。

二.训练加速

  • 数据并行指的是,多张 GPU 使用相同的模型副本,但是使用不同的数据批进行训练。

数据并行的具体原理流程为:

  1. 将模型加载至主设备上,作为 controller,一般设置为 cuda:0
  2. 在每次迭代时,执行如下操作:
  3. 将 controller 模型复制(broadcast)到每一个指定的 GPU 上
  4. 将总输入的数据 batch,进行均分,分别作为各对应副本的输入 (scatter)
  5. 每个副本独立进行前向传播,并进行反向传播,但只是求取梯度
  6. 将各副本的梯度汇总(gather)到 controller 设备,并进行求和 (reduced add)
    During the backwards pass, gradients from each replica are summed into the original module.
  7. 更具总梯度,更新 controller 设备上的参数(其他设备也会被更新)
net = torch.nn.DataParallel(model, device_ids=[0, 1, 2])
output = net(input_var)  # input_var can be on any device, including CP
# 流程伪代码
def train_batch(data, k):
    split data into k parts
    for i = 1, ..., k:  # run in parallel
        compute grad_i w.r.t. weight_i using data_i on the i-th GPU
    grad = grad_1 + ... + grad_k
    for i = 1, ..., k:  # run in parallel
        copy grad to i-th GPU
        update weight_i by using grad
  • 模型并行指的是,将模型的不同部分,分别放置于不同的 GPU 上,并将中间结果在 GPU 之间进行传递,使用同一部分数据。解决了单个模型太大,不能存放于一个 GPU 的情况。然而,需要注意的是,相较于在单个 GPU 上运行,其速度更慢。
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()

        self.features_1 = nn.Sequential(
            nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3),
            nn.BatchNorm2d(16),
            nn.ReLU(inplace=True),  # 30
            ......
            nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True),  # 12
        ).to('cuda:0')

        self.features_2 = nn.Sequential(
            nn.Conv2d(in_channels=128, out_channels=256, kernel_size=3, stride=2),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True),  # 5
            ......).to('cuda:1')  # 1

        self.classifier = nn.Sequential(
            nn.Dropout(),
            ......
            nn.Linear(1024, class_num)).to('cuda:1')

    def forward(self, x):
        out = self.features_1(x.to('cuda:0'))
        out = self.features_2(out.to('cuda:1'))
        out = out.view(-1, 384)
        out = self.classifier(out)
        out = F.softmax(out, dim=1)
        return out
# 此时,不在此需要使用 model = model.cuda()
model = ToyModel()

loss_fn = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.001)

optimizer.zero_grad()

for data in trainloader:
    images, labels = data

    # 要处理的部分
    images = images.to('cuda:0')
    labels = labels.to('cuda:1')   # 必须与输出所在 GPU 一致

    outputs = net(images)
    loss = criterion(outputs, labels)
    loss.backward()
    optimizer.step()

三.参考

BERT论文及QA

一. 论文

[论文笔记][NAACL-HLT-2019]BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding

二. 常见问题

  • Mask相对于CBOW有什么异同点?
    • 相同点
      • CBOW的核心思想是:给定上下文,根据它的上文 Context-Before 和下文 Context-after 去预测input word。而BERT本质上也是这么做的,但是BERT的做法是给定一个句子,会随机Mask 15%的词,然后让BERT来预测这些Mask的词。
    • 不同点
      • 首先,在CBOW中,每个单词都会成为input word,而BERT不是这么做的,原因是这样做的话,训练数据就太大了,而且训练时间也会非常长。
      • 其次,对于输入数据部分,CBOW中的输入数据只有待预测单词的上下文,而BERT的输入是带有[MASK] token的“完整”句子,也就是说BERT在输入端将待预测的input word用[MASK] token代替了。
      • 另外,通过CBOW模型训练后,每个单词的word embedding是唯一的,因此并不能很好的处理一词多义的问题,而BERT模型得到的word embedding(token embedding)融合了上下文的信息,就算是同一个单词,在不同的上下文环境下,得到的word embedding是不一样的。
  • BERT的两个预训练任务对应的损失函数是什么?
    • BERT的损失函数由两部分组成,第一部分是来自 Mask-LM 的单词级别分类任务(多分类交叉熵),另一部分是句子级别的分类任务(二分类交叉熵)。通过这两个任务的联合学习,可以使得 BERT 学习到的表征既有 token 级别信息,同时也包含了句子级别的语义信息。
    • 具体的预训练工程实现细节方面,BERT 还利用了一系列策略,使得模型更易于训练,比如对于学习率的 warm-up 策略,使用的激活函数不再是普通的 ReLu,而是 GeLu,也使用了 dropout 等常见的训练技巧。
  • BERT为什么如此有效?
    • 引入Masked Language Model(MLM)预训练目标,能够获取上下文相关的双向特征表示;
    • 引入Next Sentence Prediction(NSP)预训练目标,擅长处理句子或段落的匹配任务;
    • 引入强大的特征抽取机制Transformer(多种机制并存):
      • Multi-Head self attention:多头机制类似于“多通道”特征抽取,self attention通过attention mask动态编码变长序列,解决长距离依赖(无位置偏差)、可并行计算;
      • Feed-forward :在位置维度计算非线性层级特征;
      • Layer Norm & Residuals:加速训练,使“深度”网络更加健壮;
    • 引入大规模、高质量的文本数据;
  • BERT为和ELMo的区别?
    • ELMo本质是上一种自回归语言模型,只是2个单向语言模型(前向和后向)的集成,不能同时获取上下文表示。不能采取2层biLSTM同时进行特征抽取构建双向语言模型,否则会出现标签泄漏的问题;因此ELMO前向和后向的LSTM参数独立,共享词向量,独立构建语言模型;所以通过BERT的[MASK]来遮掩标签
  • BERT为啥要加MASK?
    • 为了在语言模型的训练中,使用上下文信息又不泄露标签信息,采用了Masked LM
  • Bert的双向体现在什么地方?
    • 双向主要体现在Bert的预训练任务一:遮蔽语言模型(MLM),[MASK]通过attention均结合了左右上下文的信息,这体现了双向,attention是双向的,但GPT通过attention mask达到单向.
    • BERT(Transformer encoder)
    • GPT(Transformer decoder)
  • Bert的是怎样预训练的?
    • 下面两个任务共享Bert,使用不同的输出层,做Muti-Task。
      • MLM:将一句被mask的句子输入Bert模型,对模型输出的矩阵中mask对应位置的向量做分类,标签就是被mask的字在字典中对应的下标
      • NSP:训练一个下一句预测的二元分类任务。具体 来说,在为每个训练前的例子选择句子 A 和 B 时,50% 的情况下 B 是真的在 A 后面的下一个句子, 50% 的情况下是来自语料库的随机句子,然后将[cls]对应的向量取出做二分类训练
  • MASK的缺点
    • 因为Bert用于下游任务微调时, [MASK] 标记不会出现,它只出现在预训练任务中。这就造成了预训练和微调之间的不匹配,微调不出现[MASK]这个标记,模型好像就没有了着力点、不知从哪入手。所以只将80%的替换为[mask],但这也只是缓解、不能解决
    • 相较于传统语言模型,Bert的每批次训练数据中只有 15% 的标记被预测,这导致模型需要更多的训练步骤来收敛。
    • 被mask掉的token是相互独立的, 忽略了这些token之间的联系
  • BERT和Transformer学到了什么?(What does BERT learn about the structure of language?)
    • BERT的低层网络就学习到了短语级别的信息表征,BERT的中层网络就学习到了丰富的语言学特征(句法信息),而BERT的高层网络则学习到了丰富的语义信息特征
  • 分类任务时为啥用第一个[CLS] token?
    • 因为pretrain的时候做nsp任务时,就是用的第一个token
  • BERT的三个Embedding直接相加会对语义有影响吗?(为何能相加)?
    • Embedding的数学本质,就是以one hot为输入的单层全连接的参数矩阵
    • 假设 token Embedding 矩阵维度是 [4,768];position Embedding 矩阵维度是 [3,768];segment Embedding 矩阵维度是 [2,768]。对于一个字,假设它的 token one-hot 是[1,0,0,0];它的 position one-hot 是[1,0,0];它的 segment one-hot 是[1,0]。那这个字最后的 word Embedding,就是上面三种 Embedding 的加和。如此得到的 word Embedding,和concat后的特征:[1,0,0,0,1,0,0,1,0],再过维度为 [4+3+2,768] = [9, 768] 的全连接层,得到的向量其实就是一样的。
  • BERT和GPT的区别
    • BERT是Transformer的Encoder,是一个双向自编码语言模型,可以建模双向语义,GPT是Transformer的Decoder,是一个单向自回归语言模型适合处理生成任务
    • BERT使用[CLS]/[SEP]在pretraining和finetuning阶段,GPT只在finetuning阶段使用[CLS]/[SEP]
    • BERT是输入embedding额外加了segment embedding.

三. 参考