MLP三大工作超详细解读:why do we need?
极市导读
本文作者详细介绍了最近火爆CV圈三项关于MLP的工作。 >>加入极市CV技术交流群,走在计算机视觉的最前沿
专栏目录:https://zhuanlan.zhihu.com/p/348593638
本文目录
1 MLP-Mixer: An all-MLP Architecture for Vision
(来自Google Research, Brain Team,ViT作者团队)
1.1 MLP-Mixer原理分析
1.1.1 仅仅靠着MLP就真的无法解决复杂数据集的分类任务吗?
1.1.2 MLP-Mixer是如何处理输入图片的?
1.1.3 MLP-Mixer与之前Conv1×1的不同之处在哪里?
1.1.4 MLP-Mixer架构
1.1.5 MLP-Mixer实验
1.2 MLP-Mixer代码解读2 RepMLP:卷积重参数化为全连接层进行图像识别
(来自清华大学,旷视,RepVGG作者团队)
2.1 RepMLP原理分析
2.1.1 深度学习模型的几个性质
2.1.2 RepMLP模块
2.1.3 如何将卷积等效成FC层?3 ResMLP:ImageNet数据集训练残差MLP网络
(来自Facebook AI,索邦大学)
3.1 ResMLP原理分析
3.2 ResMLP代码解读作者主页:https://www.zhihu.com/people/wang-jia-hao-53-3
1 MLP-Mixer: An all-MLP Architecture for Vision
论文名称:MLP-Mixer: An all-MLP Architecture for Vision
论文地址:
https://arxiv.org/abs/2105.01601
1.1 MLP-Mixer原理分析
1.1.1 仅仅靠着MLP就真的无法解决复杂数据集的分类任务吗?
神经网络主要有三个基本要素:权重、偏置和激活函数。
权重: 神经元之间的连接强度由权重表示,权重的大小表示可能性的大小
偏置: 偏置的设置是为了正确分类样本,是模型中一个重要的参数,即保证通过输入算出的输出值不能随便激活。
激活函数: 起非线性映射的作用,其可将神经元的输出幅度限制在一定范围内,一般限制在(-1~1) 或 (0~1) 之间。最常用的激活函数是Sigmoid函数,其可将 (-∞,+∞) 的数映射到(0~1) 的范围内。
典型的MLP包括包括三层:输入层、隐层和输出层,MLP神经网络不同层之间是全连接的( 全连接的意思就是:上一层的任何一个神经元与下一层的所有神经元都有连接)。
问:MLP模型能不能进行图像识别的任务呢?
答:当然可以。 MLP的最经典例子就是数字识别,即我们随便给出一张上面写有数字的图片并作为输入,由它最终给出图片上的数字到底是几。
对于一张写有数字的图片,我们可将其分解为由28*28=784个像素点构成,每个像素点的值在 (0~1) 之间,其表示灰度值,值越大该像素点则越亮,越低则越暗,以此表达图片上的数字并将这786个像素点作为神经网络的输入。而输出则由十个神经元构成,分别表示 (0~9) 这十个数字,这十个神经元的值也是在 (0~1) 之间,也表示灰度值,但神经元值越大表示从输入经判断后是该数字的可能性越大。如下面这个视频所示 :
视频来自:3Blue1Brown (from YouTube)
MLP模型识别数字的过程
隐层的层数和神经元的选择需根据具体情况选择,此例选择两层隐层,每层16个神经元。那么根据上面的叙述,根据权重、偏置的个数此神经网络将会有(784×16+16)+(16×16+16)+(16×10+10) = 13002个参数需要去调节。
输入的784个像素点输入模型以后,首先通过第一层的权重,其结果与偏置参数相加以后,通过激活函数,使模型因此有了非线性的性质。第一层的输出再经过地层的权重和偏置,激活函数得到最终的结果,如下面这个视频所示:
MLP模型识别数字的过程
所以,一个MLP模型是可以识别图像,完成图像分类任务的。
但是,上面的2个例子的图像都来自很简单的数据集MNIST,MNIST数据集一共只有10类,所以靠着2层的MLP就能完成分类任务。但是对于ImageNet这种1000类的数据集来讲,MLP模型就显得力不从心了。所以人们后来开发出了借助CNN,Transformer模型 (比如AlexNet,VGG,ResNet,EfficientNet,GhostNet,ViT,DeiT等) 来解决复杂分类数据集的问题。 比如2012年AlexNet赢得了图像分类挑战赛的冠军,代表CNN模型为代表的深度学习方法开始超越传统方法而逐渐流行。VGG-Net使用以3×3卷积为主要结构的深度神经网络达到了当时的SOTA结果。ResNet使用skip-connection与BN操作使得训练特别深的深度神经网络成为可能。后来的Group Convolution以及Depth-wise Convolution使得卷积操作变得更高效。Non-local Network和SE-Net通过即插即用的操作进一步提升了CNN的性能。
但是,不使用CNN和Transformer,仅仅靠着MLP就真的无法解决复杂数据集的分类任务吗?
本文给出的MLP-Mixer就是为了解决这个问题,它仅仅依赖于基础矩阵乘法运算和数据的布局变换 (reshapes and transpositions) ,以及非线性层。结果表明,尽管Mixer仅仅使用了MLP层,但是其结果却是极具竞争力的。作者在大型数据集 (约1亿=100M张图片,对比 ImageNet 是1.28M训练集,5万验证集和10万测试集) 上训练以后可以在ImageNet 的验证集上达到87.94% 的准确率 (对比 EfficientNet-B7是84.7%, ViT-B是77.9% );若在 稍微小一点的数据集 (约1-10M张图片) 上训练,也能达到接近CNN架构的性能。
由此可见,仅仅靠着MLP真的可以解决复杂数据集的分类任务,而且和CNN,Transformer模型的性能相当。
1.1.2 MLP-Mixer是如何处理输入图片的?
一张图片的维度是 的。
之前基于Transformer的ViT模型是怎么处理这个输入image的呢?
答: ViT 模型处理图片的方式是将其分块 (patch) ,每个patch的大小是 ,那样的话一共可以分成 个patch,其中:
接下来我们得到了 个patch,把每一个大小是 的patch给展平 (Flatten),就是展成一维向量,得到一个长度是 的向量。个这样的向量拼在一起就结合成一个维度是 的张量,再把张量通过线性映射将第2维的大小为 ,这样最后我们就得到了一个维度是 的张量。它由 个 的向量组成,每个向量我们把它称为1个 token。这个张量才是后续Transformer模型的真正输入。
现在基于MLP的 MLP-Mixer 是怎么处理这个输入image的呢?
答:和ViT是一样的。
把输入图片分块 (patch),每个patch的大小是 ,一共可以分成 个patch,把每一个大小是 的patch给展平 (Flatten),就是展成一维向量,得到一个长度是 的向量。个这样的向量拼在一起就结合成一个维度是 的张量,再把张量通过线性映射将第2维的大小为 称为hidden dimension。这样最后我们就得到了一个维度是 的张量。它由 个 的向量组成,每个向量我们把它称为1个 token。这个张量 才是后续MLP-MiXer模型的真正输入,如下图1所示。
1.1.3 MLP-Mixer与之前Conv1×1的不同之处在哪里?
对于1×1卷积其实相当于是作了矩阵乘法,因为假设对一个 的张量作1×1卷积,卷积核的维度是 ,则输出的张量维度是 。就相当于给它成了一个维度是 的矩阵。所以其实就相当于是在每一个空间位置 (each spatial location) 上的矩阵乘法。但是问题是1×1卷积不能结合不同空间位置的信息。
1×1卷积可以结合不同channels的信息,但不能结合不同空间位置的信息。
上节说道MLP-Mixer的输入是维度是 的张量,那么这个Mixer不仅混合各个channels之间的信息,也混合不同空间位置 (tokens/patches) 之间的信息。 所以主要分成了2种层:channel-mixing MLPs 层和 token-mixing MLPs 层。
channel-mixing MLPs层结合不同channels的信息,是一种按位置per-location的操作。
token-mixing MLPs层结合不同tokens的信息,是一种跨位置cross-location的操作。
那么对于一个维度是 的输入矩阵来讲,channel-mixing MLPs层应该是作用于它的每一行,而token-mixing MLPs层作用于它的每一列。
MLP-Mixer即可以靠channel-mixing MLPs层结合不同channels的信息,也可以靠token-mixing MLPs层结合不同空间位置的信息。
1.1.4 MLP-Mixer架构
有了上面的铺垫再来理解MLP-Mixer的结构就很简单了。如上图2所示,Mixer 由大小相同的多个层和一个分类头组成。每个层由 2 个 MLP 块组成,其中,第一个块是 token-mixing MLP 块,第二个是 channel-mixing MLP 块。其中分类头就包括Global Average Pooling和一个全连接层。
每个MLP 块如右下角小图所示,包含2个全连接层和中间一个GELU激活函数 ,给模型融入非线性成分,每个MLP 块代码如下图3所示 (这1节的代码是JAX/Flax的,想参考PyTorch代码的读者请看第2节)。
首先看这个 token-mixing MLP 块,取输入的每个channel ,对它依次通过Layer Normalization,和MLP,最后再进行残差连接得到 token-mixing MLP 块输出,如上式1.1 Top所示。token-mixing MLP 块的hidden dimension命名为 .
再看这个 channel-mixing MLP 块,取上一步输出的每个空间位置 ,对它依次通过Layer Normalization,和MLP,最后再进行残差连接得到 channel-mixing MLP 块输出,如上式1.1 Bottom所示。channel-mixing MLP 块的hidden dimension命名为 。
经过了 token-mixing MLP 块和channel-mixing MLP 块以后就算是过了一个Block,每个Block的代码如下图4所示。
依次通过 个这样的Block和分类头就得到的最终的输出,整体结构的代码如下图5所示:
几个值得注意的地方:
因为只有MLP层,所以MLP-Mixer的计算复杂度与序列长度 是线性相关的;而基于Transformer的ViT等模型的计算复杂度与 是平方相关的。
MLP-Mixer没有使用位置编码信息,因为token-mixing MLP把不同空间位置的信息融合起来了,所以对输入的顺序敏感,可以自动地学习到位置信息。token-mixing MLP 对输入 token 的顺序很敏感,因此能够学会表征位置。
MLP-Mixer每一层的输入和输出的维度是一致的,与Transformer保持一致,而不是像CNN那样是pyramidal structure,即越深的层的输入分辨率越低。
1.1.5 MLP-Mixer实验
数据集:
验证集: ImageNet (1.28M training,1k classes),ReaL,CIFAR10/100 (50k examples, 10/100 classes),Oxford-IIIT Pets (3.7k examples, 36 classes), and Oxford Flowers-102 (2k examples, 102 classes),Visual Task Adaptation Benchmark (VTAB-1k)。
训练集: ILSVRC2021 ImageNet,ImageNet-21k (14M training,21k classes),JFT-300M (300M training,18k classes)。
超参数设置:
参数 | 值 |
---|---|
训练优化器 | Adam |
参数 | beta1=0.9,beta2=0.999 |
学习率warm up策略 | linear |
学习率decay策略 | linear |
预训练分辨率 | 224 |
数据增强策略 | RandAugment, mixup, dropout, stochastic depth |
Fine-tune优化器 | SGD |
batch size | 512 |
学习率warm up策略 | linear |
其实写这些超参数也没啥意义,因为我们也没有作者的庞大私有数据集,也没法复现结果orz。
模型设置:
不同大小的模型配置如下图6所示。
S, B, L, H分别代表:small, base, large, huge。B, L, H都是12层的模型,S是8层模型。
16, 32代表patch的大小。
大型数据集性能比较
在大型数据集JFT-300和ImageNet-21k上的不同类型模型的性能对比如下图7所示。黄色圆点的代表基于ResNet的模型,蓝色圆点的代表基于Transformer的模型,粉色圆点的代表基于MLP的模型。结果显示在ImageNet-21k上MLP-Mixer性能略微逊色于其他模型,但是如果在最大的JFT-300上预训练的话,Mixer在ImageNet上的性能就能够达到87.94%,超过了大多数的其他模型。
训练时长或者数据集大小的影响
下图8左图所示是模型性能随训练时长或者数据集大小的帕累托曲线,证明pre-training cost 和下游任务的性能有着明显的相关关系。图8右图所示是当分别把JFT-300的数据集取3%训练233 epochs,10%训练70 epochs,30%训练23 epochs和100%训练7 epochs时的不同模型的性能变化。结果表明小数据集时模型都展示出了过拟合现象,BiT模型过拟合程度较低。当数据量持续增长时,Mixer-L/32和Mixer-L/16比BiT模型增长更快。这个现象在ViT模型中也存在,但是Mixer模型的这一特点比ViT更显著。这可能是因为Mixer 架构学习到的一些underlying distribution与self-attention layers机制不匹配。
模型大小的影响
改变模型大小的方式包含:
改变层数,hidden dimension和MLP的宽度: 影响预训练时间和模型的吞吐率。
Fine-tune数据的分辨率: 只影响模型的吞吐率。
结果如下图9所示,只使用ImageNet作为训练集时,Mixer-B/16达到了76.44%的性能,比ViT-B/16少了3个点。但是当训练数据量增加时,Mixer模型的性能逐步提升,Mixer-H/14在JFT-300的性能仅仅比ViT-H/14低0.3%。
权重可视化
首先要明确权重的维度是多大的。
输入的维度是 的张量。所以每个神经元有196个权值。比如Mixer-B/16模型,其 代表它的token-Mixing MLP层的hidden dimension = 384。那么token-Mixing MLP层的第一个全连接层就有384×196 = 75264个参数,看成是384个196维的参数,把每个196维的参数变成14×14的正方形,就得到了384个正方形,如下图10所示。
下图11是前3层在JFT-300的权重可视化。作者发现第1层包含了好多local interactions,后2层包含了更多的mixing across larger regions,这个性质与CNN相似。
1.2 MLP-Mixer代码解读
原论文开源的是JAX/Flax版本的代码,下文解读的这份PyTorch代码来自Phil Wang大佬的实现,链接如下:
https://github.com/lucidrains/mlp-mixer-pytorch
使用这份代码的步骤很简单,只需要2步:
第1步安装依赖库:
$ pip install mlp-mixer-pytorch
第2步测试:
import torch
from mlp_mixer_pytorch import MLPMixer
model = MLPMixer(
image_size = 256,
patch_size = 16,
dim = 512,
depth = 12,
num_classes = 1000
)
img = torch.randn(1, 3, 256, 256)
pred = model(img) # (1, 1000)
MLP-Mixer:
from torch import nn
from einops.layers.torch import Rearrange
# LayerNorm + Add.
class PreNormResidual(nn.Module):
def __init__(self, dim, fn):
super().__init__()
self.fn = fn
self.norm = nn.LayerNorm(dim)
def forward(self, x):
return self.fn(self.norm(x)) + x
# 一个MLP块:由2个全连接层和中间的激活函数构成.
def FeedForward(dim, expansion_factor = 4, dropout = 0.):
return nn.Sequential(
nn.Linear(dim, dim * expansion_factor),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(dim * expansion_factor, dim),
nn.Dropout(dropout)
)
def MLPMixer(*, image_size, patch_size, dim, depth, num_classes, expansion_factor = 4, dropout = 0.):
assert (image_size % patch_size) == 0, 'image must be divisible by patch size'
num_patches = (image_size // patch_size) ** 2
return nn.Sequential(
Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_size, p2 = patch_size),
nn.Linear((patch_size ** 2) * 3, dim, 1),
*[nn.Sequential(
PreNormResidual(dim, nn.Sequential(
Rearrange('b n c -> b c n'),
FeedForward(num_patches, expansion_factor, dropout),
Rearrange('b c n -> b n c'),
)),
PreNormResidual(dim, FeedForward(dim, expansion_factor, dropout))
) for _ in range(depth)],
nn.LayerNorm(dim),
Rearrange('b n c -> b c n'),
nn.AdaptiveAvgPool1d(1),
Rearrange('b c () -> b c'),
nn.Linear(dim, num_classes)
)
其中:
Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_size, p2 = patch_size)将图片分patch。
Rearrange('b n c -> b c n')把patch这个维度置换到最后一维,进行MLP,这是token-mixing MLP 块的做法。
Rearrange('b c n -> b n c')把channel这个维度置换到最后一维,进行MLP,这是channel-mixing MLP 块的做法。
在每个Block的开始和最后使用PreNormResidual进行Layer Norm和求和的操作。
Rearrange('b n c -> b c n')和nn.AdaptiveAvgPool1d(1)先把patch这个维度放在最后,再紧接着Global Average Pooling把最后一维归一化。
最后Rearrange('b c () -> b c')和nn.Linear(dim, num_classes)完成分类任务。
小结:
MLP-Mixer即可以靠channel-mixing MLPs层结合不同channels的信息,也可以靠token-mixing MLPs层结合不同空间位置的信息。
CNN的特点是inductive bias,ViT靠大量数据 (JFT-300数据集)使性能战胜了CNN,说明大量的数据是可以战胜inductive bias的,这个MLP-Mixer也是一样。卷积相当于是一种认为设计的学习模式:即局部假设。能够以天然具备学习相邻信息的优势,但长远看来,在数据和算力提升的前提下,相比于attention甚至MLP,可能成为了限制。因为不用滑窗,也不用attention的方法其实是CNN的母集。
早起人们放弃MLP而使用CNN的原因是算力不足,CNN更节省算力,训练好模型更容易。现在算力资源提高了,就有了重新回到MLP的可能。MLP-Mixer说明在分类这种简单的任务上是可以通过算力的堆砌来训练出比CNN更广义的MLP模型 (CNN可以看做是狭义的MLP)。
最后,channel-mixing MLPs层相当于1×1 convolution,而token-mixing MLPs层相当于广义的depth-wise convolution,只是MLP-Mixer让这两种类型的层交替执行了。
2 RepMLP:卷积重参数化为全连接层进行图像识别 (Arxiv)
论文名称:RepMLP: Re-parameterizing Convolutions into Fully-connected Layers for Image Recognition
论文地址:
https://arxiv.org/abs/2105.01883
开源预训练模型和代码 (PyTorch版):
https://github.com/DingXiaoH/RepMLP
2.1 RepMLP原理分析:
本文是MLP-Mixer后一天挂在Arxiv上的工作,也是使用纯MLP模型完成图像分类任务的工作。它借助重参数技术,将卷积与FC巧妙地融合,加强分类网络的性能。在ImageNet分类、人脸识别以及语义分割等任务(无论是否具有平移不变性)上均能涨点。结合FC层的全局表征能力和位置先验性质以及卷积层的局部先验性质,可以在大幅增加参数的同时不会造成推理速度的显著降低。
2.1.1 深度学习模型的几个性质
局部先验性质
卷积的局部先验性质的意思是: 卷积操作默认图片中的某个像素与周围的像素联系更加密切,而与距它较远的像素联系更少。所以卷积操作只处理某个局部邻域的信息。这一性质也称为归纳偏置 (inductive bias) 或者局部先验性质 (local prior)。FC层不具备这个能力。
全局建模能力
与其同时,捕获长距离依赖 (long-range dependency) 的能力,这一性质也称为全局建模能力 (global capacity) 。传统卷积拥有长距离依赖的能力的方法是使用更大的感受野,堆叠更多的卷积层。但是这也势必会导致计算十分低效,且造成优化困难。Transformer模型被用来解决这个问题,其中的self-attention层专门用来捕获长距离依赖,但是缺少了局部先验性质。FC层也具备这个能力,这很容易理解,因为FC层的输出与每个输入都有连接。
位置先验性质
位置先验性质的意思是:比如人通过人脸识别来解锁手机,脸部的照片一般情况下都在屏幕的中心,眼睛一般在图像上方,鼻子一般在中部。这一性质就是位置先验,也叫做positional perception,而一层卷积是不具备这个性质的。FC层具备这个能力,因为FC层的参数是位置相关的,而不像卷积操作,不同位置的参数是共享的。
综上,作者看上了全连接层具备的全局建模能力和位置先验性质,使用FC层取代卷积操作。此外,FC层的计算更高效,对于应用场景更友好。
但是,FC层却不具备局部先验性质,作者解决这个问题的办法是使用结构重参数技术 (structural reparameterization technique)。
2.1.2 RepMLP模块
所谓结构重参数的意思已经在RepVGG里面有讲到,就是:训练时的结构对应一组参数,推理时我们想要的结构对应另一组参数;只要能把前者的参数等价转换为后者,就可以将前者的结构等价转换为后者, 官方描述为:
The meaning of structural reparameterization is that the training-time model has a set of parameters while the inference-time model has another set, and we parameterize the latter with the parameters transformed from the former.
注意结构重参数技术 整个过程只转换模型一次,就是训完以后把训练模型等效为部署模型的那个时候,转换完成后,训练的模型变成了全新的部署模型,训练的模型可以丢弃。
问:RepVGG是怎么使用结构重参数的?
答: 在训练模型时,为每一个3x3卷积层添加平行的1x1卷积分支和恒等映射分支,构成一个RepVGG Block。在部署 (deploy) 时,把1×1卷积分支和恒等映射分等效为3×3分支,权重weights和偏置bias 叠加在之前训练的3×3分支上面。
问:RepMLP是怎么使用结构重参数的?
答:在训练模型时,为每一个MLP层添加平行的卷积+BN分支,构成一个RepMLP Block。在部署(deploy) 时,把平行的卷积分支等效为MLP分支,使deploy模型只有MLP操作。
与RepVGG的一卷到底相比,RepMLP运行速度更快,并且具备全局建模能力和位置先验性质。
在CIFAR数据集上,简简单单的MLP模型即可取得与CNN非常接近的性能。通过将RepMLP插入到现有CNN中,我们在ImageNet数据集上提升ResNets模型精度达1.8%,在人脸识别任务上提升2.9%,在Cityscapes提升2.3%mIoU精度且具有更低的FLOPs。
本文的贡献是使得MLP层同时具备局部先验性质,全局建模能力和位置先验性质,使其适用于图像识别任务且不造成任何推理耗时增加。
RepMLP的架构如上图12所示。训练阶段的RepMLP由3部分构成:Global Perceptron、Partition Perceptron、Local Perceptron。
对于卷积层,假设特征图表示为,用字母 和 分别代表卷积和全连接层的权值。本文采用了PyTorch风格的数据排布与伪代码风格,比如卷积处理的数据流表示如下:
其中表示输出特征, 表示输出通道数, 表示pad的像素数量,表示卷积核 (暂时假设groups=1) 。为简单起见,我们还假设。
对于全连接层,假设P和Q为输入与输出维度,分别表示输入与输出,表示全连接层核,计算公式如下:
假设FC不会改变输入输出分辨率,即。我们采用RS (reshape)来表示仅仅改变张量形变而不会改变内存数据顺序的操作,它是一种cost-free的操作。输入首先会被展平 (Flatten) 为 个长度为的向量,即,然后乘以核得到输出,最后再RS为。为更好的阅读,在歧义的情况下我们忽略RS而简写为:
这种形式的全连接层无法充分利用图像的局部信息,并忽视了位置信息。
而且这种形式的全连接层所需要的参数也很大,假设,此时FC层的参数量高达10G,这样巨大的参数量是难以接受的,而且如上文所述,无法充分利用图像的局部信息。
所以作者提出了3种Global Perceptron 和 Partition Perceptron来节约参数量。
Global Perceptron
Global Perceptron是把特征图分成一样大小的部分来节约参数量,比如,的特征图可以被拆分为,我们将每个视作一个部分。假设表示期望部分的高和宽,则特征图的变化过程如下图13所示:
我们看到经过了Global Perceptron,维度为 的特征图最终通过PyTorch里面的reshape和permute操作变成了 。通过这种方式,参数量可以从下降到。
但是,强行把特征图分成不同的部分会打破不同部分之间的相关性。各个部分会被分开对待而忽视了它们原本的位置上的相关性。为对每个分区添加相关性,Global Perceptron采用了以下几个操作:
对每个分区采用均值池化得到1个像素。 将上述所得送入到BN+1个两层的MLP。 将上述所得进行reshape并与每个部分的特征相加。 得到的结果传入Partition Perceptron 和 Local Perceptron层
则特征图维度的完整的变化过程如下图14所示,具体过程如图12所示。
Partition Perceptron
Partition Perceptron 包含FC与BN层,并以每个部分的特征作为输入。Global Perceptron的输出将通过reshape、re-arrange、reshape等操作变为。作者进一步采用组卷及降低FC3的参数量,定义如下:
类似的,组形式的FC核 ,此时参数量可以减少g倍。尽管这种形式的FC不被某些框架(如Pytorch)支持,但我们可以采用卷积代替实现。它的实现包含三个步骤:
将输入特征Reshape 为spatial size = 的特征; 采用groups = g 的卷积; 将上述所得特征 Reshape 为。
整个过程定义如下:
则特征图维度的完整的变化过程如下图15所示,具体过程如图12所示。
Local Perceptron
Local Perceptron将每个部分的特征经由几个卷积进行处理。所有卷积分支的输出与Partition Perceptron的输出相加作为最终的输出。特征图维度的完整的变化过程如上图16所示,具体过程如图12所示。
2.1.3 如何将卷积等效成FC层?
假设FC核为:,卷积核 ,我们期望构建满足:
注意到:对任意与同形状的核,MMUL的加法特征满足:
因此,只要可以构建与同形状的,我们就可以将F合并到并满足:
很明显,一定存在 (因为卷积可以视为稀疏版FC层)。
所以接下来要做的事情是如何把这个卷积操作 给等效成全连接操作 ,换句话讲,如何找到 。这个 的神奇之处就在于:给某一个张量乘以 ,就相当于是给这个张量的Reshape成的四维形式做卷积。
作者给出了一种方法,这也是本文我认为最妙的地方:
现在全连接操作是咋算的呢?计算形式是:
给上式插入一个Identity Matrix ,这个式子就变成了:
因为这里面都是矩阵乘法,多乘一个单位矩阵没啥关系。
前面说过:这个 的神奇之处就在于:给某一个张量乘以 ,就相当于是给这个张量的Reshape成的四维形式做卷积。
那么2.10式是给 这个张量乘以 ,就相当于是给 这个张量的四维形式 做卷积,这里:
所以我们把矩阵乘法变成卷积,就有:
但是有一点不同, 的结果是2维的,而卷积的结果是4维度的 ,所以需要再来个Reshape,所以
结合上式2.12,2.10就变为了:
这个绿到发光的部分正是我们要找的 。
所以:
2.14式就是我们要找的等效全连接操作 ,算法就是卷积核的等效FC核可以通过对恒等矩阵进行卷积并添加合适reshape得到。这个转换既便捷又可导。
PyTorch格式的伪代码如下:
Algorithm 1 PyTorch code for converting groupwsie conv into FC.
Input: C, h, w, g, O, conv kernel, conv bias
I = torch.eye(C * h * w // g).repeat(1, g).reshape(C * h * w // g, C, h, w)
fc kernel = F.conv2d(I, conv kernel, padding=conv kernel.size(2)//2, groups=g)
fc kernel = fc kernel.reshape(O * h * w // g, C * h * w).t() # Note the transpose
fc bias = conv bias.repeat interleave(h * w)
return: fc kernel, fc bias
下一步,按照上式1.3的做法,把BN的参数合并到卷积中:
这是因为:
式1.3是2D BN的融合方法,那么1D BN也是同理,融合进 里面。所以说按照2.14式的做法我们就可以把所有的卷积操作等效成FC了。
Experiment:
数据集:CIFAR10,ImageNet
超参数:
参数 | 值 |
---|---|
数据增广策略 | padding, RandomCrop, RandomFlip |
bs | 128 |
学习率变化 | cosine learning rate annealing |
CIFAR10实验:
这里作者构建了2个模型,PureMLP和ConvNet,如下图17所示:
PureMLP由FC层 (Conv1×1)和RepMLP构成,图中的16×16,32×32代表分辨率。左右的改变其实就是左边的RepMLP层对标右面的3×3卷积。所有的RepMLP层都使用 。为了使左右2侧的计算量尽量接近,左侧3个stage的channel分别是16,32,64,而左侧3个stage的channel分别是32,64,128。结果如下图18所示:
可以看到:
纯MLP模型能够以52.8M FLPs达到91.11%的精度,不过该结果弱于Wide ConvNet;
作者随后进行了一些对照实验,转换前的计算量达到118.9M,高于包含卷积与BN,其计算量非常大,这说明了结构重参数的重要性。
移除Local Perceptron,模型精度下降8.5%,说明了局部先验的重要性。 移除Global Perceptron,模型精度下降1.5%,说明了全局建模的重要性。 替换FC3为卷积 (K=9, p=4),尽管其感受野更大,但仍造成精度下降3.5.%,说明了FC要比卷积更有力。
ImageNet实验:
超参数:
参数 | 值 |
---|---|
数据增广策略 | mixup, AutoAugment, Random Crop, RandomFlip |
bs | 256 |
学习率变化 | cosine learning rate annealing |
weight decay | e-4 |
采用ResNet作为基线模型并用于评估RepMLP作为传统ConvNet构建模型的性能。模块定义如下图19所示。假设ResNet的几个stage的特征依次是 。
只对stride=1的层替换了RepMLP,仅仅对 进行替换时,结果如下图20所示。 时RepMLP-Res50具有比ResNet50更少的参数量,更快的推理速度(快10%)。
前两行说明:当前深度学习框架对于组形式卷积支持程度并不够好,参数量提升59%,但推理速度仅下降了0.7%;更进一步的组形式优化有可能使得RepMLP更高效。
下图21是对不同层的特征进行替换时的性能变化,超参数设置为 。
采用RepMLP模块替换ResNet中的模块会导致轻微的速度下降,但精度出现了显著提升。比如,仅仅采用RepMLP替换 即可带来0.94%精度提升,参数量仅增加5M; 的替换可以取得最佳的均衡。
下图22为更高输入分辨率下不同模型的性能对比。
在320×320的高输入分辨率下,超参数设置为 。 意思是对于 使用group=8,而对 使用group=16。
得到的结论是:
相比同参数量的传统ConvNet,RepMLP-Res50的计算量更低、推理速度更快。比如,相比224×224输入的ResNet101,RepMLP-Res50仅需50%FLOPs,更少的参数量,推理速度快50%,也可以取得同等精度;当输入分辨率为320×320时,RepMLP-Res50在精度、速度以及FLOPs方面的优势更大。
提升RepMLP的参数量不会导致推理速度的大幅下降。比如,从RepMLP-Res50-g8/16到RepMLP-Res50-g4/8,参数量提升47%,但FLOPs仅提升3.6%,推理速度仅从311下降到305。这对于高吞吐量的大型服务器推理是非常有用的。
相比于EfficientNet (在端测设备效率较高而在GPU端是低效的),RepMLP-Res50在速度于精度方面表现更优。
除了前述模块改进外,作者还提供了一种更轻量化的高速版本如下图23所示,性能见下图24。
轻量版RepMLP取得了与原始ResNet50相当的性能(77.14 vs 77.19),但FLOPs降低30%,推理速度快55%,达到1074 examples per second。
小结:
RepMLP借助结构重参数技术,将MLP与卷积巧妙结合,不仅保留了MLP的全局建模能力和位置先验性质,还能融入卷积的局部先验性质。在ImageNet分类、人脸识别以及语义分割等任务(无论是否具有平移不变性)上均能涨点。作为模型压缩的方法,它可以在GPU端实现模型推理速度的提升。
3 ResMLP:ImageNet数据集训练残差MLP网络 (Arxiv)
论文名称:ResMLP: Feedforward networks for image classification with data-efficient training
论文地址:
https://arxiv.org/pdf/2105.03404.pdf
3.1 ResMLP原理分析:
本文第一作者就是 DeiT 一作 Hugo Touvron 博士。他曾经针对视觉 Transformer 模型 ViT 需要大量数据集训练的难题提出了DeiT模型,通过一组优秀的超参数和蒸馏操作实现了仅仅使用 ImageNet 数据集就能达到很强的性能,详见下面链接。现在他又按照这个思路写了一篇,针对视觉 MLP 模型 MLP-Mixer 需要大量数据集训练的难题提出了 ResMLP 模型,通过残差结构和蒸馏操作实现了仅仅使用 ImageNet 数据集就能达到很强的性能。
下面列举几个Hugo Touvron 博士最近的研究和对应的论文以及论文解读,他在视觉Transformer模型上面灌了很多水。
DeiT:
https://arxiv.org/pdf/2012.12877.pdf
搞懂Vision Transformer 原理和代码,看这篇技术综述就够了(三)
CaiT:
https://arxiv.org/pdf/2103.17239.pdf
搞懂 Vision Transformer 原理和代码,看这篇技术综述就够了(八)
本文不使用任何 Self-attention 模块,只有 Linear Layers 和 GELU 激活函数为模型提供非线性。另外一个有意思的是,ResMLP不会使用任何正则化层 (Batch Norm, Group Norm, Layer Norm) 来稳定训练,其训练方式和 DeiT 和 CaiT 保持一致。
ResMLP架构如上图25所示,怎么处理输入image的呢?
答:和ViT是一样的。
把输入图片分块 (patch),每个patch的大小是 ,那样的话一共可以分成 个patch,其中:
接下来我们得到了 个patch,把每一个大小是 的patch给展平 (Flatten),就是展成一维向量,得到一个长度是 的向量。个这样的向量拼在一起就结合成一个维度是 的张量,再把张量通过线性映射将第2维的大小为 ,这样最后我们就得到了一个维度是 的张量。它由 个 的向量组成,每个向量我们把它称为1个 token。这个张量才是后续Transformer模型的真正输入。
每一个Block的结构也很简单:sublayer + feedforward sublayer。
sublayer 就是图25左边那部分,由一个线性层和残差连接构成,不使用Layer Normalization,但是会使用2个Affine Transformation:
式中的 都是可学习的参数。此操作简单地重新缩放和给输入分量一个偏置,而且不会占用很多推理时间。注意 是对输入的每一个patch单独做的,就是在 这个维度去做,也就是说不会涉及到batch之间和patch之间的信息交流。与CaiT里面的Layer Scale方法很像,相同的是scale值 一开始都会初始化成一个很小的值 (初始化 ),不同的是Affine Transformation还有一个偏置项。
每个sublayer会有2个Affine Transformation,我们注意到一个是在残差前面 (取代pre-normalization操作),一个是在残差内部 (取代post-normalization操作)。
feedforward sublayer 就是图25右边那部分,由两个线性层和残差连接构成,中间使用GELU激活函数,不使用Layer Normalization,但是会使用2个Affine Transformation。
所以每个Block的表达式可以写成:
这里 是每个层的可学习权重参数, 。注意按照这个式子图25貌似有点问题,箭头应该放在A的前面分叉。
问:与Transfomer的Block相比,ResMLP的主要的区别在于,将 Self-attention 层替换为线性层。那么线性层与Self-attention的区别在哪里呢?
答: 个人理解是Self-attention是数据依赖的,即不同的input可以得到不同的attention map,然后这个attention map还会与input计算出的value作用,可以看成是data-dependent的超参数。
而线性层不是数据依赖的,即不同的input都会使用相同的权重参数得到 ,换句话讲,MLP模型没有依赖于输入数据的超参数,这就使模型更加general。
问:为什么sublayer 要做2次转置,不做行不行?
答:不行。 这个做法与MLP-Mixer保持一致。第1次转置把patches这个维度甩到最后一维,目的是融合不同patches之间的信息;第1次转置把channels这个维度甩到最后一维,目的是融合不同channels之间的信息。如果不做就没法融合这2个维度的信息,所以不做不行。
问:需要像Transformer模型一样用class token吗?
答:不需要。 作者也尝试在最后两层使用Transformer,前面的层的attention统一替换成Linear层,做法和CaiT模型一致。发现这样做会进一步涨点。但是参数量和计算量也会上升,这个模型命名为class-MLP。
另外值得注意的是:ResMLP模型不使用位置编码,因为ResMLP把不同空间位置的信息融合起来了,所以对输入的顺序敏感,可以自动地学习到位置信息。ResMLP 对输入 token 的顺序很敏感,因此能够学会表征位置。
Experiments:
数据集: 训练:ImageNet (1.28M 训练集,1000类),测试:ImageNet-real,ImegeNet-v2
超参数:
参数 | 值 |
---|---|
优化器 | Lamb |
学习率 | 5e-3 |
weight decay | 0.2 |
其他与DeiT一致。
ImageNet实验结果
下图26所示为各种模型在ImageNet的实验结果。就参数量,计算量和准确率的trade-off而言,基于MLP的模型不如基于Transformer的或者CNN的模型,但是却展示出了非常鼓舞人心的结果。作者可能觉得比较尴尬,所以这样解释道:
那些基于卷积和Transformer的模型经过了years of research and careful optimization towards accuracy, parameters and FLOPs,所以结果比MLP好是有情可原的。 ResMLP证实了结构并不适限制模型性能的因素,尤其是在训练数据量有限的情况下。
知识蒸馏结果
仿照DeiT的做法作者也对ResMLP模型进行蒸馏,就是把label替换为教师模型RegNet-16GF的输出结果,结果如下图27所示。
图27:知识蒸馏结果
迁移学习性能
先在ImageNet上预训练,然后在各种小数据集上面Fine-tune得到的结果如下图所示。Fine-tune的小数据集包括CIFAR-10, CIFAR-100, Flowers-1022, Stanford Cars, iNaturalist。迁移学习性能与其他模型相当,说明MLP模型在合适的数据增强策略的加持下减少了过拟合。
可视化结果
下图是线性层3.2式的 矩阵权重的可视化结果,可视化的方法是把 的每一行即 个值reshape成正方形排列,所以一共就有 个正方形。浅层的可视化结果展现出卷积的特征,展示出一种attention map的感觉;有趣的是,在许多层中,大的权重沿着两个轴延伸,最突出的是在第7层。网络的最后7层是不同的,比如第20层和第22层:它们包括patch本身的尖峰和其他幅度较大或较小的patch之间的扩散响应。
权重的稀疏度
下图30红色线是线性层3.2式的 矩阵权重的稀疏度的可视化结果。蓝色线是线性层3.2式的 矩阵权重。稀疏权重的标准是这个权值小于最大权值的5%。结果显示不论是 还是 都是极其稀疏的。 矩阵的稀疏度更大,它主要负责patches之间的信息融合,说明不是任意的patches之间的信息都需要融合,在能够预见未来会有MLP稀疏化方面的研究。
图30:权重的稀疏度
过拟合表现
因为MLP模型极易产生过拟合,所以作者在下图31中展示了ResMLP模型的泛化能力。从吐0中可以看出:ResMLP模型的泛化能力与其他基于Transformer相当。
3.2 ResMLP代码解读
这份PyTorch代码并非来自原作者,而是来自Phil Wang大佬的实现,链接如下:
https://github.com/lucidrains/res-mlp-pytorch
不得不说大哥 code 是真滴快啊!!!ResMLP是21年5月7号上传的arxiv,大哥10号上午就把工作复现出来了。这样的大神我们不夸,我们舔orz。
使用这份代码的步骤很简单,只需要2步:
第1步安装依赖库:
$ pip install res-mlp-pytorch
第2步测试:
import torch
from res_mlp_pytorch import ResMLP
model = ResMLP(
image_size = 256,
patch_size = 16,
dim = 512,
depth = 12,
num_classes = 1000
)
img = torch.randn(1, 3, 256, 256)
pred = model(img) # (1, 1000)
ResMLP:
定义Affine操作:
class Affine(nn.Module):
def __init__(self, dim, fn):
super().__init__()
self.g = nn.Parameter(torch.ones(1, 1, dim))
self.b = nn.Parameter(torch.zeros(1, 1, dim))
self.fn = fn
def forward(self, x):
x = x * self.g + self.b
return self.fn(x)
注意是在输入的最后一个维度作用的对角矩阵和偏置,因为self.g, self.b初始化在了最后一维 (即channels的维度)。
总架构:
class LayerScaleResidual(nn.Module): # https://arxiv.org/abs/2103.17239
def __init__(self, dim, depth, fn):
super().__init__()
if depth <= 18:
init_eps = 0.1
elif depth > 18 and depth <= 24:
init_eps = 1e-5
else:
init_eps = 1e-6
scale = torch.zeros(1, 1, dim).fill_(init_eps)
self.scale = nn.Parameter(scale)
self.fn = fn
def forward(self, x):
return self.fn(x) * self.scale + x
def ResMLP(*, image_size, patch_size, dim, depth, num_classes, expansion_factor = 4):
assert (image_size % patch_size) == 0, 'image must be divisible by patch size'
num_patches = (image_size // patch_size) ** 2
wrapper = lambda i, fn: LayerScaleResidual(dim, i + 1, Affine(dim, fn))
return nn.Sequential(
Rearrange('b c (h p1) (w p2) -> b (h w) (p1 p2 c)', p1 = patch_size, p2 = patch_size),
nn.Linear((patch_size ** 2) * 3, dim),
*[nn.Sequential(
wrapper(i, nn.Conv1d(num_patches, num_patches, 1)),
wrapper(i, nn.Sequential(
nn.Linear(dim, dim * expansion_factor),
nn.GELU(),
nn.Linear(dim * expansion_factor, dim)
))
) for i in range(depth)],
Reduce('b n c -> b c', 'mean'),
nn.Linear(dim, num_classes)
)
wrapper(i, nn.Conv1d(num_patches, num_patches, 1))代表 linear sublayer,输出通道数为 (num_patches),输入通道数为 (num_patches),使用1×1的1维卷积代表MLP。
这里实现的方式特别巧妙,这样可以使得变量的维度一直是 (bs, N, d),直接使用1维卷积去搞N这个维度,而不用进行转置操作了。如果仍然使用nn.Linear,那就还需要把N这一维度通过转置操作甩在最后才能使用nn.Linear(N,N)。wrapper(i, nn.Sequential(
nn.Linear(dim, dim * expansion_factor),
nn.GELU(),
nn.Linear(dim * expansion_factor, dim)
))代表feedforward sublayer。注意wrapper是个lambda函数,真正执行的函数是LayerScaleResidual,为一个残差操作,一路是残差,另一路是Affine操作再乘以scale,这里的scale相当于又进行了一次Affine操作。
小结:
ResMLP是一种完全基于多层感知机(MLP)进行图像分类的体系结构。它是一个简单的残差网络,它交替线性层,其中图像 patches在通道之间独立且相同地交互;以及2层前馈网络,其中通道中的每个 patch独立地相互作用,达到了鼓舞人心的性能。
总结:
本文介绍了目前的3个在分类任务上使用MLP模型的工作。
MLP-Mixer即可以靠channel-mixing MLPs层结合不同channels的信息,也可以靠token-mixing MLPs层结合不同空间位置的信息。卷积相当于是一种认为设计的学习模式:即局部假设。能够以天然具备学习相邻信息的优势,但长远看来,在数据和算力提升的前提下,相比于attention甚至MLP,可能成为了限制。因为不用滑窗,也不用attention的方法其实是CNN的母集。但是channel-mixing MLPs层相当于1×1 convolution,而token-mixing MLPs层相当于广义的depth-wise convolution,只是MLP-Mixer让这两种类型的层交替执行了。
RepMLP将训练时的卷积操作通过结构重参数化技术转化成部署时的MLP,但不是纯MLP模型,也包含卷积的成分,只是靠MLP提升性能。
ResMLP针对视觉 MLP 模型 MLP-Mixer 需要大量数据集训练的难题提出了 ResMLP 模型,通过残差结构和蒸馏操作实现了仅仅使用 ImageNet 数据集就能达到较强的性能。