面试复盘 一
关于面试:开始
这里先讲面试的其中三个问题:
实现Batch Norm
Batch Norm,批量规范化负责将一个batch的数据进行规范化。和很多地方一样,“规范”总是和均值方差相关。简单来说,规范化就是使得这个batch的数据的均值为0,方差为1,即
这里,我们给方差加上了一个小量,是防止除0的情况。和是可学习的参数,形状均为(特征维度,),将数据映射到一个新的分布。如此以来,机器学习的IID假设就被强制满足了。这里需要注意的是,一个batch应该勉强满足IID,所以按理说batch大小需要远大于数据的种类数(对于有类别的数据)。batch太小时,可想见Batch Norm会起到反效果。Batch Norm也可以解决深度网络累积偏差过大导致的梯度爆炸问题。
关于和的必要性
一般的解释是,如果使用作为输出,那么batch norm的输出总是均值为0,方差为1的,网络的表达能力就受限了。我认为这个说法太过笼统。从分布的角度看,假设输入的是一个n维正态分布,那么经过规范化后,输出的分布是。和初始化分别是1和0,并不改变分布。而且,如果Batch Norm后面还有一个Linear层,不妨将重新写为对角矩阵,则显然和可以被后面的Linear层吸收。这也引出了一个重要的结论:Batch Norm必须用于激活函数之前,即
否则,Batch Norm是完全无效的。
稍微跑题了。但总之,作为一个简单的线性变换,表达能力受限这个说法,我表示存疑,尤其是对于学习相对关系的分类网络等而言。也有人说,这是为了避免激活函数时效,考虑到如Sigmoid这样的函数,在0附近近似是线性的。到了优化后期,还是需要启用激活函数的非线性部分,所以才加了偏移参数。
我个人认为,和的存在,其实是延后了分布偏移的过程,使得分布的偏移主动且可学习。如果分布真的是正态分布,那么这两个参数,如果手动设置,可以将输出的分布还原回输入的分布。但初始时将其设为0,1,强制将偏移延后,使得初始化的深层网络开始时是稳定的,然后逐步开始偏移,启用激活函数。这样来看,这两个参数的必要性更多和稳定性有关。
Batch Norm如何加速收敛
我们主要考虑两种情况:
使用了类似于Sigmoid这样的激活函数。如果网络非常深,由于累积偏差,假设在某一层,大多数数据的取值落在。观察Sigmoid可以发现,这个区间的梯度非常小。而Batch Norm初始时可以将这个区间的数据映射到0附近,这样梯度就不会消失。这也是为什么Batch Norm必须用在激活函数之前的原因。
较大的情况。由于是可学习的,当某几层对输出的影响较大时,会倾向于变大,使得这部分区域的梯度变大,从而加速收敛。
由此看来,Batch Norm的作用和Sigmoid或类似函数有关。对于常用的ReLU等激活函数,则效果还是存疑的。不过搭配ReLU的话,可能就需要考虑设置非0的了。
实现(Numpy)
上面的部分只讲了Batch Norm的目的和原理。实现的时候,需要考虑的就多了,比如:需要记录均值方差,以进行推理;以及如何更新均值和方差。其中更新方式,和优化器Adam等类似,使用动量。均值和方差的计算还考虑了全连接层和卷积层的情况。我想如果面试遇见,能考虑两种情况多半是加分项。
Backward更新和的部分,我不觉得是一时半会能用numpy写出来的。我觉得写出这两个参数就可以了,要是要求写计算图和自动微分也太变态了。
其他
我记得当时在上深度学习课的时候,好像是沈为讲的,就是说在新的工作中,Batch Norm被发现在某些情况下并不有用。况且,即使作者进行了解释,人们并为真正确认这种方法到底为何有效。只能说,深度学习是这样的。这样看来,现在的计算机视觉新技术,像NeRF技术明确把神经网络当作拟合器并使用很小的网络,3D Gaussian Splatting彻底不使用神经网络,起码在解释性上是优雅的。不过这也导致了我在面试时默写不出Batch Norm。我不知道该如何评价了。
区分Batch Norm和Layer Norm
上面详细解释了Batch Norm。这里直接讲Layer Norm,相必读者自然能明白两者的区别。简单来说,Layer Norm就是将上面的实现改为了
显然,Layer Norm是对每个样本的所有特征进行规范化,而Batch Norm是对每个特征的所有样本进行规范化。
使用那个Norm取决于网络结构和所使用数据的信息分布。考虑图像和文本,图像往往只能包含某些特定的信息,图片和图片之间内容往往不同,而长文本之间各自就已经独立,比如一个作文数据集,每篇作文的分布是相似的。在计算机视觉领域,对于数据(N,图片)来说,我们往往认为每一组图片的整体分布是一致的,所以Batch Norm更适合。而对于自然语言处理等领域,数据更多是(N,句子),我们认为每段文本的分布是一致的,所以Layer Norm更适合。Transformer网络中就使用了Layer Norm。
解释DDP和DP
DP(Data Parallelism)是数据并行,DDP(Distributed Data Parallelism)是分布式数据并行。这两个概念都是大模型训练的基础知识。
DP
DP,是将模型复制在多个GPU上。以8卡服务器为例,在一个Batch训练时,这个Batch会被分为8份(由于梯度会合并,严格来说并不要求8等份,但没病的话一般是等份)。每个GPU处理自己分到的数据,各自进行求导,反向传播的时候,各自计算梯度,然后将梯度汇总到一个GPU上(默认为GPU0),更新参数,然后将新的模型拷贝回其他GPU。也有说法说是其他GPU将前向计算结果拷贝到GPU0,然后GPU0再计算Loss。这样的话想来会更慢,而且主卡的显存占用会更高。
其中,如果是普通的机器,梯度的拷贝和模型的拷贝需要通过CPU。不考虑GPU能够直接通信的集群,可以进行的优化是:CPU负责分发数据,回收梯度,整合梯度,发回各个GPU,GPU各自使用梯度更新自己的模型。这样的话,虽然CPU整合梯度的速度不如GPU,但是免除了CPU将7卡的梯度一个个发送到GPU0的,和复制模型的时间,理论上效率会高的多。还有一个问题就是,主卡的显存占用可能会过高,考虑到由于Dropout等,不同梯度图并不是直接合并的。但是本人卡最多的机器也就是2080Ti双卡,并没有进行测试的机会。
DDP
上面提到的优化,就是DDP的主要思路。DDP的区别就是以同步梯度,每张卡独立更新参数,代替单卡更新参数和模型拷贝。此外,DDP还会使用NCCL库(Nvidia的库,看了下居然是纯软件,就是说不需要加NVlink之类的)进行GPU间数据同步,主要使用all_reduce
机制。
这样一来,一般来说都不会去使用DP,一般都是使用DDP的。DP几乎已经弃用了。
Reference
PS: 后面的Transformer将单独开一篇独立标题,所以没有面试复盘-2了。