Zhangzhe's Blog

The projection of my life.

0%

URL

TL;DR

  • 本文提出一种应用于视觉任务的 Transformer

Algorithm

Architecture

vit.gif

code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import torch
from torch import nn
from einops import rearrange, repeat
from einops.layers.torch import Rearrange
# helpers
def pair(t):
return t if isinstance(t, tuple) else (t, t)
# classes
class PreNorm(nn.Module):
def __init__(self, dim, fn):
super().__init__()
self.norm = nn.LayerNorm(dim)
self.fn = fn
def forward(self, x, **kwargs):
return self.fn(self.norm(x), **kwargs)
class FeedForward(nn.Module):
def __init__(self, dim, hidden_dim, dropout=0.0):
super().__init__()
self.net = nn.Sequential(
nn.Linear(dim, hidden_dim),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(hidden_dim, dim),
nn.Dropout(dropout),
)
def forward(self, x):
return self.net(x)
class Attention(nn.Module):
def __init__(self, dim, heads=8, dim_head=64, dropout=0.0):
super().__init__()
inner_dim = dim_head * heads
project_out = not (heads == 1 and dim_head == dim)
self.heads = heads
self.scale = dim_head**-0.5
self.attend = nn.Softmax(dim=-1)
self.to_qkv = nn.Linear(dim, inner_dim * 3, bias=False)
self.to_out = (
nn.Sequential(nn.Linear(inner_dim, dim), nn.Dropout(dropout))
if project_out
else nn.Identity()
)
def forward(self, x):
qkv = self.to_qkv(x).chunk(3, dim=-1)
q, k, v = map(lambda t: rearrange(t, "b n (h d) -> b h n d", h=self.heads), qkv)
dots = torch.matmul(q, k.transpose(-1, -2)) * self.scale
attn = self.attend(dots)
out = torch.matmul(attn, v)
out = rearrange(out, "b h n d -> b n (h d)")
return self.to_out(out)
class Transformer(nn.Module):
def __init__(self, dim, depth, heads, dim_head, mlp_dim, dropout=0.0):
super().__init__()
self.layers = nn.ModuleList([])
for _ in range(depth):
self.layers.append(
nn.ModuleList(
[
PreNorm(
dim,
Attention(
dim, heads=heads, dim_head=dim_head, dropout=dropout
),
),
PreNorm(dim, FeedForward(dim, mlp_dim, dropout=dropout)),
]
)
)
def forward(self, x):
for attn, ff in self.layers:
x = attn(x) + x
x = ff(x) + x
return x
class ViT(nn.Module):
def __init__(
self,
*,
image_size,
patch_size,
num_classes,
dim,
depth,
heads,
mlp_dim,
pool="cls",
channels=3,
dim_head=64,
dropout=0.0,
emb_dropout=0.0
):
super().__init__()
image_height, image_width = pair(image_size)
patch_height, patch_width = pair(patch_size)
assert (
image_height % patch_height == 0 and image_width % patch_width == 0
), "Image dimensions must be divisible by the patch size."
num_patches = (image_height // patch_height) * (image_width // patch_width)
patch_dim = channels * patch_height * patch_width
assert pool in {
"cls",
"mean",
}, "pool type must be either cls (cls token) or mean (mean pooling)"
self.to_patch_embedding = nn.Sequential(
Rearrange(
"b c (h p1) (w p2) -> b (h w) (p1 p2 c)",
p1=patch_height,
p2=patch_width,
),
nn.Linear(patch_dim, dim),
)
self.pos_embedding = nn.Parameter(torch.randn(1, num_patches + 1, dim))
self.cls_token = nn.Parameter(torch.randn(1, 1, dim))
self.dropout = nn.Dropout(emb_dropout)
self.transformer = Transformer(dim, depth, heads, dim_head, mlp_dim, dropout)
self.pool = pool
self.to_latent = nn.Identity()
self.mlp_head = nn.Sequential(nn.LayerNorm(dim), nn.Linear(dim, num_classes))
def forward(self, img):
x = self.to_patch_embedding(img)
b, n, _ = x.shape
cls_tokens = repeat(self.cls_token, "() n d -> b n d", b=b)
x = torch.cat((cls_tokens, x), dim=1)
x += self.pos_embedding[:, : (n + 1)]
x = self.dropout(x)
x = self.transformer(x)
x = x.mean(dim=1) if self.pool == "mean" else x[:, 0]
x = self.to_latent(x)
return self.mlp_head(x)
vit = ViT(
image_size=100,
patch_size=10,
num_classes=10,
dim=128,
depth=12,
heads=8,
mlp_dim=256,
)
img = torch.randn(2, 3, 100, 100)
out = vit(img)
print(out.shape) # [2, 10]

Thought

  • 一个优雅的网络结构,就该像 ViT 这样,用一张动图 + 一段不长的代码完美表示。显然 Swin Transformer 不够优雅…

URL

TL;DR

  • 本文提出一种图像自监督预训练方法: Masked Autoencoders (MAE),通过 mask 很高比例的图像 patch,并使用非对称 encoder-decoder 结构重建整个图进行预训练
    • 随机 mask 很大的比例,例如:75% ,一方面提高自监督难度以达到自监督效果,另一方面减小 encoder 大小
    • 只将未 mask 的 patch 送入神经网络,重建整张图
  • BERT 非常相似,BERT 在自然语言中进行自监督预训练从而使得网络参数量可以达到惊人的一千亿;MAE 也希望通过图像自监督预训练提高网络参数和网络效果

Algorithm

视觉模型 masked autoencoder 与语言模型 masked autoencoder 有何差异

  • 视觉模型通常使用 CNN,CNN 无法直接对 mask tokenspositional embeddings 等自监督标记进行有效整合;Vision Transformers (ViT) 提出了一种有效的解决方法
  • 语言是人生成的经过高度信息聚合的,所以信息冗余较少,mask 很小比例后重建难度就较高;图像有大量信息冗余,所以需要 mask 很大比例才有重建难度,才能起到自监督效果
  • 由于语言自监督模型重建的信息维度较高,所以 encoder-decoder 结构中的 decoder 可以非常简单(通常是一个 MLP);但视觉自监督模型重建的信息维度是像素,所以 decoder 结构在网络中扮演一个关键角色

什么是 Autoencoder

  • Autoencoder 是一种经典的表征学习方法,可以将输入通过 encoder 映射到表征空间,再通过 decoder 解码
  • Denoising autoencoders (DAE) 是将输入破坏,再通过 decoder 重建出破坏前的原始输入,Masked Autoencoder 就是一种 DAE

Architecture

  • MAEencoder 部分与 ViTencoder 相同,输入为 未 mask 的图像 patch
  • MAEdecoder 部分输入包含两部分:
    • encoder 部分对 未 mask 的图像 patch encoder 后的表征(输入 decoder 的浅蓝色方块)
    • mask tokens(输入 decoder 的灰色方块)
  • decoder 的结构更浅更窄,每个 token 的计算量只有 encoder10%
  • 只在 mask patch 上计算 loss,损失函数就是简单的 mean squared error(MSE)
    mae1.png

自监督重建效果

mae2.png

不同 mask 比例下的自监督效果对比

mae3.png

寻找最合适的 mask 比例

mae4.png

Though

  • 将 Masked Language Model 优雅地应用在图像自监督中,简单、有效、mask sense
  • 使在图像上应用超大规模预训练模型成为可能

URL

TL;DR

  • 一种 用于语义理解的预训练深层双向 transformer,任务相关的 fine-tuning 即可 SOTA
  • BERT 全称:Bidirection Encoder Representation from Transformer
  • 使用 无标签文本 进行预训练,自监督预训练的方法包括:
    • masked language model:预测被遮挡的词的 token(不需要重建整个句子)
    • next sentence prediction:预测两个句子是否是前后相连的关系(只需预测 是 或者 否)

Algorithm

自监督语义表征方法

  • 自监督语义表征方法常用的监督方法有两种:
    • feature based:任务相关的预训练
    • fine-tuning:在预训练过程中增加任务相关参数,预训练结束后只需要替换这些任务相关参数,在任务数据上有监督 end-to-end fine-tuning 很少 step 即可
  • 相较于 GPT 的 left -> right 单向语义结构,双向语义结构对下游任务更友好

BERT 模型结构

  • BERT 参数含义:
    • L:transformer layers
    • H:hidden size
    • A:self-attention heads
  • BERTbase(L=12,H=768,A=12,Total Parameters=110M)BERT_{base}(L=12,H=768,A=12,Total\ Parameters=110M)
  • BERTlarge(L=24,H=1024,A=16,Total Parameters=340M)BERT_{large}(L=24,H=1024,A=16,Total\ Parameters=340M)

BERT 输入输出结构

bert1.png

输入

  • BERT 输入可以是一个句子,也可以是多个句子的 concat(用特殊分隔符分隔),例如问答数据集中,输入的句子是 <question, answer> concat
    • [CLS] 为 Sequence 开始标志位
    • [SEP] 为 Sentence 结束标志位
  • Token Embeddings 是将句子转化成词向量,英文版本 BERT 用的分词器是 WordPiece embeddings,词表容量 30,000
  • Segment Embeddings 用于指明句子如何分割
  • Position Embedding 用于记录原始位置信息(与 CNN 不同,Transformer 中没有位置信息)

输出

bert3.png

其中:CRH, TiRH,  H means hidden sizeC\in \mathbb{R}^H,\ T_i \in \mathbb{R}^H,\ \ H\ means\ hidden\ size

BERT 预训练细节

Masked Language Model

  • mask 策略:
    • 训练数据随机 mask 15% 的位置
    • mask 数据的 80% 使用 [MASK] token 去 mask
    • mask 数据的 10% 使用随机 token 去 mask
    • mask 数据的 10% 什么都不改变
  • 计算 loss
    • 假如第 ii 个 token 被 mask,那只计算输出 TiT_i 映射到词表空间的 token 分布和真实 token 之间的交叉熵 Loss(本质就是分类)

Next Sentence Prediction

  • 数据生成策略:
    • 每个样本由两个句子组成,50% 的可能两个句子是前后句的关系,50% 的可能两个句子不是前后句的关系,预测 IsNext OR NotNext
  • 计算 loss
    • 只用输出 C 映射到二分类空间的结果和二分类 label,计算交叉熵损失
  • NSP 数据集中,无监督预训练即可达到 97% - 98% 的准确率

BERT fine-tuning

  • 对于不同的任务,只需要替换 BERT 之后的网络结构,使用有监督数据 end-to-end fine-tuning 即可

消融实验

bert4.png

分析了 NSP、双向网络结构等对效果的影响

效果对比

bert2.png

URL

TL;DR

  • 多任务模型共享网络结构,在一些弱关联或者负相关的任务上,可能出现 跷跷板效应(Seesaw Phenomenon),即一个任务的效果和另外一个任务的效果无法同时提升
  • 针对跷跷板效应问题,本文提出一种 CGC(Customized Gate Control) 结构,任务之间部分共享底层网络
  • 提出一种 CGC 的升级版本 - PLE(Progressive Layered Extraction)
  • 单层网络结构 CGC 与多层网络结构 PLE 都优于 MMOE

Algorithm

不同多任务网络结构对比

ple1.png

Customized Gate Control(CGC)

ple3.png

  • CGC 结构部分共享表征,部分独用表征,共享表征和独占表征的权重分配是通过门控制
    yk(x)=tk(gk(x))y^k(x) = t^k(g^k(x)),其中 tkt^k 表示第 k 个任务的 tower
    gk(x)=wk(x)Sk(x)g^k(x) = w^k(x)S^k(x),其中 wk(x)w^k(x) 表示表征选择器 wk(x)Rmk+msw^k(x) \in\mathbb{R}^{m_k+m_s}mkm_kmsm_s 分别表示独占和共享表征的数量
    wk(x)=Softmax(wgkx),wgkR(mk+ms)×dw^k(x) = Softmax(w^k_gx),w^k_g\in\mathbb{R}^{(m_k+m_s)\times d}d 表示表征向量的长度
    Sk(x)=[E(k,1)T,E(k,2)T,...,E(k,mk)T,E(s,1)T,E(s,2)T,...,E(s,ms)T]S^k(x) = [E^T_{(k,1)}, E^T_{(k,2)}, ... ,E^T_{(k,m_k)},E^T_{(s,1)}, E^T_{(s,2)}, ... ,E^T_{(s,m_s)}]E 表示表征,Sk(x)Rd×(mk+ms)S^k(x)\in\mathbb{R}^{d\times(m_k+m_s)} 表示表征集合

Progressive Layered Extractio(PLE)

ple4.png

  • PLE 相当于 CGC 的多层网络结构
    yk(x)=tk(gk,N(x))y^k(x) = t^k(g^{k,N}(x))N 表示网络总层数
    gk,j(x)=wk,j(gk,j1(x))Sk,j(x)g^{k,j}(x) = w^{k,j}(g^{k,j-1}(x))S^{k,j}(x)

效果对比

ple2.png
ple5.png
ple6.png
ple7.png

Thought

  • MMOE 的改进版,从效果来看,对于不正相关甚至互逆的任务,都可以有效解决跷跷板问题
  • 但本质还是更 general 的 MMOE,没有跳出 MMOE 对多任务的定义

URL

https://arxiv.org/pdf/2105.02358.pdf

TL;DR

  • 本文提出一种 Multi-Head External Attention 使用两层全连接层和两个 normalization 层替代 transformer 的 attention 层,以降低计算复杂度
  • 提出一种 DoubleNorm 标准化层替代 attention 中的 softmax
  • 在一些任务中没有超越 transformer 达到 SOTA,只是提出一种关于 attention 的思考

Algorithm

网络结构

  • external attention 对比 self attention
    eanet1.png
  • multi-head external attention 对比 multi-head self attention
    eanet2.png

数学表示与伪代码表示

  • self attention
    A=(α)i,j=softmax(QKT),   () means matrixA = (\alpha)_{i,j} = softmax(QK^T),\ \ \ (\cdot)\ means\ matrix
    Fout=AVF_{out} = AV
    其中:
    FRN×dF\in\mathbb{R}^{N\times d} 表示输入,Q=FW1,K=FW1,V=F,W1Rd×dQ=FW_1,K=FW_1,V=F,W_1\in\mathbb{R}^{d\times d'}
    QRN×d,KRN×d,ARN×NQ\in\mathbb{R}^{N\times d'},K\in\mathbb{R}^{N\times d'},A\in\mathbb{R}^{N\times N}
    FoutRN×dF_{out}\in\mathbb{R}^{N\times d} 表示输出
  • 简化 self attention
    A=(α)i,j=softmax(FFT)A = (\alpha)_{i,j} = softmax(FF^T)
    Fout=AVF_{out} = AV
  • external attention
    A=(α)i,j=Norm(FMT)A = (\alpha)_{i,j} = Norm(FM^T)
    Fout=AMF_{out} = AM
    以上两步可以共享 MLP 权重,MRS×dM\in\mathbb{R}^{S\times d},也可以不共享,分成 MkM_kMvM_v
    计算复杂度 O(dSN)
    伪代码表示:
    eanet5.png
  • multi-head external attention
    hi=ExternalAttention(Fi,Mk,Mv)h_i = ExternalAttention(F_i,M_k,M_v)
    Fout=MultiHead(F,Mk,Mv)=Concat(h1,...,hH)WoF_{out} = MultiHead(F,M_k,M_v) = Concat(h_1,...,h_H)W_o
    伪代码表示:
    eanet6.png
  • DoubleNorm
    本文提出一种 double-normalization 结构,先在第一维做 softmax,再在第二维做 average
    (α~)i,j=FMkT(\tilde{\alpha})_{i,j} = FM_k^T
    α^i,j=exp(α~i,j)/kexp(α~k,j)\hat{\alpha}_{i,j}=exp(\tilde{\alpha}_{i,j})/\sum_k exp(\tilde{\alpha}_{k,j})
    αi,j=α^i,j/kα^i,k\alpha_{i,j}=\hat{\alpha}_{i,j}/\sum_k \hat{\alpha}_{i,k}

EA 结构用法示例

eanet4.png

External Attention 和 DoubleNorm 的消融实验

eanet3.png

算法表现

  • 本文中对 EANet 与其他网络结构在不同任务上的对比实验做的非常详细,建议去看原文

Thought

  • 在一些任务上超越了 self-attention 并不能说明 external attention 结构优于 self-attention 结构
  • 在参数量较小的情况下,external attention 结构可带来较多增益
  • 本文更像是对 《Attention is all you need》 的嘲讽之作

URL

TL;DR

  • 本文提出一种高效的 Channel Attention 算法,与 SENet 相比,效果更好,参数量与计算量更低

Algorithm

网络结构

eca1.png

  • SENet 结构的区别:
    • 两层 FC 变成一层 FC
    • FC 权重稀疏(kernel_size = k 的 1D Conv

对比实验

  • 本文将 ECA-Net 与以下 Channel Attention Block 进行了对比,目的是:如何对 FC 稀疏可以使得模型最终效果最好
    • SENet:Squeeze and Excitation Network
    • SE-Var1:SE 变种 1
    • SE-Var2:SE 变种 2
    • SE-Var3:SE 变种 3
    • SE-GC1:SE 通道分组 1
    • SE-GC2:SE 通道分组 2
    • SE-GC3:SE 通道分组 3
    • ECA-NS:ECA-Net 的动态版
  • 数学表示
    xRW×H×C : inputx \in \mathbb{R}^{W\times H\times C}\ :\ input
    g : Global Average Poolg\ :\ Global\ Average\ Pool
    σ : Sigmoid\sigma\ :\ Sigmoid
  • SENet
    • Channel-wise Attention Weight:w=σ(f{W1,W2}(g(x)))w = \sigma(f_{\{W_1,W_2\}}(g(x))),记 y=g(x)y = g(x)
    • f{W1,W2}(y)=W2ReLU(W1y)f_{\{W_1,W_2\}}(y) = W_2 ReLU(W_1 y)
    • W1RC×CrW_1\in \mathbb{R}^{C\times \frac{C}{r}}
    • W2RCr×CW_2\in \mathbb{R}^{\frac{C}{r} \times C}
    • 参数个数:2×C2r2\times \frac{C^2}{r}
  • SE-Var
    • Channel-wise Attention Weight:w=σ(Wy)w = \sigma (Wy)
    • WRC×CW \in \mathbb{R}^{C\times C}
    • SE-Var1WW 是一个 单位矩阵,参数个数:1
    • SE-Var2WW 是一个 对角矩阵,参数个数:CC
    • SE-Var3WW 是一个 普通矩阵,参数个数:C2C^2
  • SE-GC
    • WW 是一个 分块对角矩阵,每个块边长 CG\frac{C}{G},对角线包含 GG 个块,一共包含非零元素(参数个数):CG×CG×G=C2G\frac{C}{G}\times \frac{C}{G}\times G = \frac{C^2}{G}
    • SE-GC1SE-GC2SE-GC3 的区别只是 GG 的取值不同
  • ECA-NS
    • WW 是一个 阶梯状矩阵,每行连续 KK 个非零元素,阶梯状叠加 CC 行,参数个数:K×CK\times C
  • ECA-Net
    • ECA-NS 中每行元素都一样的特例,参数量:KK
      eca2.png

代码表示

eca3.png

对下游任务的提升

  • ImageNet 分类
    eca4.png
  • COCO 目标检测
    eca5.png
  • COCO 实例分割
    eca6.png

Thought

  • SE-Net 的简化版,但参数量与计算量都更小
  • 但是性能比 SE-Net 更好这个结论感觉不太 make sense

四种 Norm 对比

norms.png

  • BN:垂直于 C 维度归一化
  • LN:垂直于 N 维度归一化
  • IN:垂直于 N, C 维度归一化
  • GNGN={LN,   g=1IN,   g=cGN = \begin{cases} LN,\ \ \ g=1\\ IN,\ \ \ g=c \end{cases}g 表示每个 group 覆盖的 channel

四种 Norm 的 Affine 参数 Shape

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import torch
inp = torch.randn(3, 4, 5, 6)
# batch norm
batchnorm = torch.nn.BatchNorm2d(num_features=4)
batchnorm.weight.shape # torch.Size([4])
out = batchnorm(inp)
out.shape # torch.Size([3, 4, 5, 6])
out.permute(1,0,2,3).reshape(4, -1).mean(1).data # tensor([-5.9605e-09, 1.5895e-08, -5.2982e-09, -7.9473e-09]) 几乎是 0
out.permute(1,0,2,3).reshape(4, -1).std(1).data # tensor([1.0056, 1.0056, 1.0056, 1.0056]) 几乎是 1
# layer norm
layernorm = torch.nn.LayerNorm(normalized_shape=(4, 5, 6))
layernorm.weight.shape # torch.Size([4, 5, 6])
out = layernorm(inp)
out.shape # torch.Size([3, 4, 5, 6])
out.reshape(3, -1).mean(1).data # tensor([ 3.9736e-09, -5.9605e-09, 2.7816e-08]) 几乎是 0
out.reshape(3, -1).std(1).data # tensor([1.0042, 1.0042, 1.0042]) 几乎是 1
# instance norm
instancenorm = torch.nn.InstanceNorm2d(num_features=4)
out = instancenorm(inp)
out.shape # torch.Size([3, 4, 5, 6])
out.reshape(12, -1).mean(1).data # tensor([ 1.7881e-08, 1.7881e-08, -2.3842e-08, -1.9868e-08, 0.0000e+00, -3.9736e-09, 0.0000e+00, 0.0000e+00, 0.0000e+00, 7.9473e-09, 1.9868e-08, -2.1855e-08]) 几乎是 0
out.reshape(12, -1).std(1).data # tensor([1.0171, 1.0171, 1.0171, 1.0171, 1.0171, 1.0171, 1.0171, 1.0171, 1.0171, 1.0171, 1.0171, 1.0171]) 几乎是 1
# group norm
groupnorm = torch.nn.GroupNorm(num_groups=2, num_channels=4)
groupnorm.weight.shape # torch.Size([4, 5, 6])
out = groupnorm(inp)
out.shape # torch.Size([3, 4, 5, 6])
out.reshape(6, -1).mean(1).data # tensor([ 1.9868e-09, 3.1789e-08, -1.9868e-08, 0.0000e+00, 1.5895e-08, 5.9605e-08]) 几乎是 0
out.reshape(6, -1).std(1).data # tensor([1.0084, 1.0084, 1.0084, 1.0084, 1.0084, 1.0084]) 几乎是 1

URL

https://arxiv.org/pdf/2109.09310.pdf

TL;DR

  • 本文提出一种多功能的卷积核,可以有效提高算法效果,降低计算量与存储空间
  • 具体做法是:将 一个 主卷积核与 多个 二值 mask 点积生成 多个次级卷积核,用次级卷积核进行计算
  • 二值 mask 包括 H、W、C 方向,即每个次级卷积核的 H、W、C 都可能不同

Algorithm

思路

  • 神经网络轻量化(参数量与计算量)常用方法:
    • 模型压缩,主要包括:
      • 权重分解
      • 网络剪枝
      • 权重量化
      • 知识蒸馏
    • 网络结构轻量化,主要包括:
      • Xception
      • MobileNet series
      • ShuffleNet series
      • OctConv
      • MixConv
  • 本论文思路:
    • 一个 主卷积核与 多个 二值 mask 点积生成 多个次级卷积核
    • 二值 mask 作用在 H、W、C 上,所以每 个次级卷积核的感受野可能不同,关注的 channel 可能不同

网络设计

在空间方向设计多功能卷积

lv1.png

  • 空间方向就是指 H、W 方向

  • 图中用一个极端例子演示,实际上二值 mask 不是由人手工设计的,而是神经网络自动学习得到

  • 普通卷积计算过程:

    • input: xRH×W×cinput:\ x\in\mathbb{R}^{H\times W\times c}
    • filter: fRd×d×cfilter:\ f \in\mathbb{R}^{d\times d\times c}
    • output: y=f@x  ,   yRH×Woutput:\ y = f @ x\ \ ,\ \ \ y\in\mathbb{R}^{H'\times W'} (“@” means convolution operation)
  • 多功能卷积计算过程(在空间方向上):

    • filter: fRd×dfilter:\ f \in\mathbb{R}^{d\times d}
    • binery mask: Mi(p,q,c)={1, if p,qip,qd+1i0, otherwisebinery\ mask:\ M_i(p,q,c) = \begin{cases} 1,\ if\ p,q \ge i \mid p,q \le d+1-i\\ 0,\ otherwise \end{cases}
    • secondary filter: {f^1, f^2, ... ,f^s}  ,    s=d2 ,   f^i=Mifsecondary\ filter:\ \{\hat{f}_1,\ \hat{f}_2,\ ...\ ,\hat{f}_s\}\ \ ,\ \ \ \ s=\lceil \frac{d}{2} \rceil\ ,\ \ \ \hat{f}_i = M_i \circ f
    • output: y=[(Mif) @ x+b1,... ,(Msf) @ x+bs],output: \ y = [(M_i\circ f)\ @\ x + b_1, ...\ , (M_s\circ f)\ @\ x + b_s],
      $s.t.\ s = \lceil \frac{d}{2} \lceil,\ { M_i }s_{i=1}\in{0, 1}{d\times d\times c}\ $

    “[]” means concat,bib_i means bias

    • naive version:output: y=i=1s(Mif) @ x+b=[i=1s(Mi)f] @ x+b,output:\ y = \sum_{i=1}^s(M_i\circ f)\ @\ x + b = [\sum_{i=1}^s(M_i)\circ f]\ @\ x + b,

    $ s.t.\ s = \lceil \frac{d}{2} \lceil,\ { M_i }s_{i=1}\in{0, 1}{d\times d\times c}\ $

在 Channel 方向设计多功能卷积

lv2.png

  • 在卷积神经网络中 C >> H、W,所以在 Channel 方向设计多功能卷积非常必要

  • 数学表示:

    • y=[f^1 @ x1, ... ,f^n @ xn]y = [\hat{f}_1\ @\ x_1,\ ...\ ,\hat{f}_n\ @\ x_n]

    $ s.t.\ \ \forall i, \hat{f}_i \in \mathbb{R}^{d\times d\times c},\ \ n = (c - \hat{c})/g+1$

    • 其中:
      • 省略 bias
      • g means channel-wise stride
      • c^\hat{c} means non-zeros channels
      • n means 一个 filter 用几次
      • [] means concat

学习策略

mask 具体如何设计

lv3.png

  • mask 两种设计策略:
    • 方案 a:每个主卷积核共享一套二值 mask
    • 方案 b:每个二值 mask 只用一次
  • 实验证明,方案 b 效果更好,原因是:方案 b 中二值 mask 的假设空间更大

如何让主卷积核对应的多个二值 mask 相似性

  • 极端情况:如果一个主卷积核对应的所有二值 mask 都相同,那理论上模型效果与只是用主卷积核运算表现基本相同

  • 所以需要加入一种使得 同一个主卷积核对应的多个每个二值 mask 更倾向不同 的损失函数

  • 损失函数数学表示:

    • minF,ML=L0(F,M)+λLortho(M)\min_{F,M}\mathcal L = \mathcal L_0(F,M) + \lambda\mathcal L_{ortho}(M)
    • M=[vec(M1), ... ,vec(Ms)]M = [vec(M_1),\ ...\ ,vec(M_s)]

    $ \mathcal{L}_{ortho} = \frac{1}{2} \lVert \frac{1}{d2c}MTM-I\rVert_F^2$

    • 其中:
      • Lortho\mathcal{L}_{ortho} means loss of Orthogonal(正交损失)
      • L0\mathcal{L}_0 means 任务相关 loss
      • MiM_i 表示一个主卷积核的一个二值 mask
      • 正交矩阵的性质:
        • 假设 M 是一个正交矩阵,则 MTM=IM^TM=I
        • 正交矩阵的列向量线性无关

对主卷积核的优化方法

  • 总 loss 包括 任务相关 lossmask 正交 loss
  • 只有 任务相关 loss 与主卷积核相关
  • Lfi=L0fi=j=1sL0f^ijMj\frac{\partial\mathcal L}{\partial f_i} = \frac{\partial\mathcal L_0}{\partial f_i} = \sum_{j=1}^{s}\frac{\partial\mathcal L_0}{\partial \hat f_{ij}}\circ M_j
  • LF=[L0f1, .... ,L0fk]\frac{\partial\mathcal L}{\partial F} = [\frac{\partial\mathcal L_0}{\partial f_1},\ ....\ ,\frac{\partial\mathcal L_0}{\partial f_k}]
    • 其中 k 表示主卷积核的个数
  • FFηLFF \leftarrow F - \eta \frac{\mathcal L}{F}

对二值 mask 的优化方法

  • 先将 Lortho\mathcal L_{ortho} 展开:
    • Lortho=12Tr[(1d2cMTMI)(1d2cMTMI)T]=12Tr[1d4c2MTMMTM21d2cMTM+I]\mathcal L_{ortho} = \frac{1}{2}Tr[(\frac{1}{d^2c}M^TM-I)(\frac{1}{d^2c}M^TM-I)^T] = \frac{1}{2}Tr[\frac{1}{d^4c^2}M^TMM^TM - 2\frac{1}{d^2c}M^TM+I]
  • 所以:
    • LorthoM=12(4d4c2MMTM4d2cM)=2d4c2MMTM2d2cM\frac{\partial\mathcal L_{ortho}}{\partial M} = \frac{1}{2}(\frac{4}{d^4c^2}MM^TM - \frac{4}{d^2c}M) = \frac{2}{d^4c^2}MM^TM - \frac{2}{d^2c}M
  • \because 交换门,L0Mj=i=1kL0f^ijfi\therefore \frac{\partial\mathcal L_{0}}{\partial M_j} = \sum_{i=1}^k\frac{\partial\mathcal L_{0}}{\partial\hat f_{ij}}\circ f_i
  • LM=[L0M1, ... ,L0Ms]+λLorthoM\frac{\partial\mathcal L}{\partial M} = [\frac{\partial\mathcal L_0}{\partial M_1},\ ...\ , \frac{\partial\mathcal L_0}{\partial M_s}] + \lambda \frac{\partial\mathcal L_{ortho}}{\partial M}
  • 因为 M 是 binary 的离散值,所以需要一个代理的连续变量 H,实现 直通估计器 STE 的作用
  • M=sign(H),   H=clip(H,0,1)M = sign(H),\ \ \ H = clip(H,0,1)
  • LM=LH\frac{\partial\mathcal L}{\partial M}=\frac{\partial\mathcal L}{\partial H}
  • {HMHclip(HηLH,0,1)\begin{cases} H\leftarrow M\\ H\leftarrow clip(H-\eta \frac{\partial\mathcal L}{\partial H},0,1) \end{cases}
    lv4.png

Thought

  • 这篇文章在 2018 年就已经发表在 nips 上了,最近做了一些详细实验后重新挂在了 arxiv 上了
  • 本文实验非常详细,虽然没有开源,前向计算和反向计算的数学推导很精彩~~(敲 latex 敲的想哭)~~,像一篇 survey
  • 这篇文章 inference 阶段有点重参数化的感觉,RepMLP 思想和本文有点相似
  • STE 部分让我想起了 DoReFa-Net 😂

URL

https://arxiv.org/pdf/2110.07641.pdf

TL;DR

  • 本文提出一种浅层神经网络 ParNet,12层深度可以在 ImageNet 上达到 80.72% 准确率的效果
  • 将网络变宽,因为多个横向 Branch 可以并行计算,而纵向深度只能顺序计算
  • RepVGGSENet 的结构合并成了 RepVGG-SSE 结构
  • ParNet 表示 parallel substructure network

Algorithm

整体结构

parnet2.png

  • 使用 SENet 结构的原因:网络深度过浅,下采样次数太少,只能通过 SE 结构获得全局信息
  • 使用 RepVGG 结构的原因:结构重参数化白给的涨点,不要白不要
  • 使用三个分支的原因:
    • 三个分支分别处理不同分辨率级别的 feature,最终 fusion
    • 选 “三” 是效果和速度的 tradeoff
  • 如何做 model scale:
    • 传统网络(例如 ResNet)做 model scale 的方法:
      • 缩放 H、W:减少下采样次数
      • 增加 C:增加卷积核数量
      • 增加深度:堆叠 block
    • ParNetmodel scale 的方法:
      • 增大 H、W:减少下采样次数
      • 增加 C:增加卷积核数量
      • 增加 Stream:增加横向 Branch 数量
  • SSE 是指 Skip-Squeeze-and-Excitation为降低网络层数,将 SE 结构中 GAP 后的 feature 做一层 Conv 而不是两层 MLP
  • SiLU(x)=xsigmoid(x)SiLU(x) = x * sigmoid(x)
  • 本网络除了 SSE 结构之外,没有跳边连接

效果

parnet1.png

这个图有点不公平,因为横坐标表示层数而不是参数量

  • ResNet 对比
    parnet3.png
  • 对下游任务也有涨点加速的作用
    parnet4.png

URL

https://arxiv.org/pdf/2103.16788.pdf

TL;DR

  • 本文提出一种 动态可扩展表征增量学习方法,目前是增量学习的 SOTA
  • 提出一种两阶段(表征学习阶段分类学习阶段)的增量学习方法,更好的平衡 stability-plasticity (稳定性与可塑性)

Algorithm

问题定义

  • Dt\mathcal{D}_t 表是第 t 次增量学习的数据集,Mt\mathcal{M}_t 表示第 t 次增量学习前的模型(已隐含前 t-1 次增量学习的所有数据),现可获得的所有数据 D~t=DtMt\tilde{\mathcal{D}}_t=\mathcal{D}_t\cup\mathcal{M}_t,用 D~t\tilde{\mathcal{D}}_t 去训练一个新的模型(表征器 + 分类器)

整体结构

der1.png

表征学习阶段

  • 目的:为 D~t\tilde{\mathcal{D}}_t 训练一个表征器(特征提取器)
  • 具体做法:冻结(eval 模式)已有的表征器;为新的增量数据训练一个表征器;两个表征器结果 concat 作为新的表征结果
  • u=Φt(x)=[Φt1(x),Ft(x)]u=\Phi_t(x)=[\Phi_{t-1}(x),\mathcal{F}_t(x)],其中,xDtx \in \mathcal{D}_tΦt1\Phi_{t-1} 表示已有的(前 t-1 次增量数据训练的)表征器,Ft\mathcal{F}_t 表示为第 t 次增量数据训练的表征器,二者结果 concat 即为新的表征结果 uu

分类器学习阶段

  • 由于新加入数据 Dt\mathcal{D}_t 后,特征 uu 的维度和数据分布都发生了变化,所以需要重新训练一个分类器
  • y^=argmaxPHtPHt(yx)=softmax(Ht(u))\hat{y} = argmax_{P_{\mathcal{H}_t}} P_{\mathcal{H}_t}(y|x)=softmax(\mathcal{H}_t(u)),其中 Ht\mathcal{H}_t 表示第 t 次增量学习训练的分类器

算法细节

Training Loss

  • LHt=1D~ti=1D~tlog(PHt(y=yixi))\mathcal{L}_{\mathcal{H}_t} = -\frac{1}{|\tilde{D}_t|}\sum_{i=1}^{|\tilde{D}_t|}log(P_{\mathcal{H}_t}(y=y_i|x_i)),根据新的表征 uu 训练
  • 辅助 loss:LHta=1D~ti=1D~tlog(PHta(y=yixi))\mathcal{L}_{\mathcal{H}_t^a} = -\frac{1}{|\tilde{D}_t|}\sum_{i=1}^{|\tilde{D}_t|}log(P_{\mathcal{H}_t^a}(y=y_i|x_i)),只训练新表征器 Ft\mathcal{F}_t
  • 二者加权相加即为可扩展表征损失函数: LER=LHt+λαLHta\mathcal{L}_{ER}=\mathcal{L}_{\mathcal{H}_t}+\lambda_{\alpha}\mathcal{L}_{\mathcal{H}_t^a}

动态扩展

  • 加入了 Channel-level Masks
  • 加入了稀疏正则化:Sparsity Loss
  • 可扩展表征损失函数加上稀疏正则化即为最终损失函数: LER=LHt+λαLHta+λsLS\mathcal{L}_{ER}=\mathcal{L}_{\mathcal{H}_t}+\lambda_{\alpha}\mathcal{L}_{\mathcal{H}_t^a}+\lambda_s\mathcal{L}_S

表现

der2.png

Thought

  • 正如题目所说,这种增量学习只适用于类别增量,不适用于数据增量,对于类别固定,数据为流式数据(例如:每日回流的数据)并不适用
  • 不适用于流式数据的原因是:这种方法随着增量次数变大,模型会变得越来越大,不适合流式数据这种频繁更新的数据