Einops: Clear and Reliable Tensor Manipulations with Einstein-like Notation.
einops用于实现对张量(Tensor)的操作,它可以更简单灵活地实现张量的变换,并且支持numpy, pytorch, tensorflow, jax等深度学习库。
einops全称是Einstein-Inspired Notation for operations,即受爱因斯坦求和约定(Einstein summation)启发的符号法。
安装einops库:
pip install einops
einops库提供了简洁的API:
# 三大核心操作
from einops import rearrange, reduce, repeat
# 三个深度学习库相关层
from einops.layers.torch import Rearrange, Reduce, EinMix
# 若干辅助函数
from einops import asnumpy, parse_shape, einsum, pack, unpack
1. 核心操作 rearrange, reduce, repeat
(1) rearrange
rearrange
能够根据给定的模式对元素进行重新排列。
from einops import rearrange
output_tensor = rearrange(input_tensor, 'b c h w -> b h w c')
⚪ 增加维度
x = rearrange(x, 'b h w c -> b 1 h w 1 c')
⚪ Flatten操作
y = rearrange(x, 'b c h w -> b (c h w)')
⚪ PixelShuffle中的空间重排操作
y = rearrange(x, 'b (h1 w1 c) h w -> b c (h h1) (w w1)', h1=2, w1=2)
⚪ ShuffleNet中的通道打乱操作
y = rearrange(x, 'b (g1 g2 c) h w-> b (g2 g1 c) h w', g1=4, g2=4)
⚪ 拆分张量
y1, y2 = rearrange(x, 'b (split c) h w -> split b c h w', split=2)
(2) reduce
reduce
能够同时实现重排和压缩。
from einops import reduce
output_tensor = reduce(input_tensor, 'b c (h h2) (w w2) -> b h w c', 'mean', h2=2, w2=2)
⚪ 全局平均池化操作
y = reduce(x, 'b c h w -> b c', reduction='mean')
⚪ $2\times 2$最大池化操作
y = reduce(x, 'b c (h h1) (w w1) -> b c h w', reduction='max', h1=2, w1=2)
# you can skip names for reduced axes
y = reduce(x, 'b c (h 2) (w 2) -> b c h w', reduction='max')
⚪ 通道级的均值归一化
y = x - reduce(y, 'b c h w -> 1 c 1 1', 'mean')
(3) repeat
repeat
能够能够沿着新的轴复制元素。
from einops import repeat
output_tensor = repeat(input_tensor, 'h w -> h w c', c=3)
2. 相关层 Rearrange, Reduce, EinMix
einops库为不同的深度学习库提供了同名的网络层:
from einops.layers.torch import Rearrange, Reduce, EinMix
from einops.layers.tensorflow import Rearrange, Reduce, EinMix
from einops.layers.flax import Rearrange, Reduce, EinMix
from einops.layers.gluon import Rearrange, Reduce, EinMix
from einops.layers.keras import Rearrange, Reduce, EinMix
from einops.layers.chainer import Rearrange, Reduce, EinMix
einops库提供的网络层与上节介绍的核心操作具有相同的参数,可以用于构建深度网络的模块。比如在卷积网络中设置flatten层:
from torch.nn import Sequential, Conv2d, MaxPool2d, Linear, ReLU
from einops.layers.torch import Rearrange
model = Sequential(
...,
Conv2d(6, 16, kernel_size=5),
MaxPool2d(kernel_size=2),
# flattening without need to write forward
Rearrange('b c h w -> b (c h w)'),
Linear(16*5*5, 120),
ReLU(),
Linear(120, 10),
)
总而言之,pytorch中的tensor.view
等操作均可以用einops库替换。更多使用einops库构建深度网络模块的例子可参考Writing a better code with pytorch and einops。
EinMix
提供基于MLP架构的网络层,特定的MLP网络能够与卷积网络或Transformer网络表现相当,更多细节可参考EinMix: universal toolkit for advanced MLP architectures。
3. 辅助函数 asnumpy, parse_shape, einsum, pack, unpack
(1) asnumpy
asnumpy
能够把张量转换为numpy格式。
from einops import asnumpy
y_numpy = asnumpy(y)
(2) parse_shape
parse_shape
能够打印张量的维度。
from einops import parse_shape
parse_shape(x_5d, 'b c x y z')
# {'b': 10, 'c': 32, 'x': 100, 'y': 10, 'z': 20}
parse_shape(x_5d, 'batch c _ _ _')
# {'batch': 10, 'c': 32}
(3) einsum
einsum
实现了灵活的点积操作:
C = einsum(A, B, 'b t1 head c, b t2 head c -> b head t1 t2')
(4) pack
和 unpack
pack
和 unpack
实现了多个张量的组合和拆分,并且不需要预先调整张量到相同的尺寸。
from einops import pack, unpack
image_rgb = np.random.random([h, w, 3])
image_depth = np.random.random([h, w])
image_rgbd, ps = pack([image_rgb, image_depth], 'h w *')
assert image_rgbd.shape == (h, w, 4)
# ps: [(3,), ()]
unpacked_rgb, unpacked_depth = unpack(image_rgbd, ps, 'h w *')
# 也可指定每个拆分维度
rgb, depth = unpack(image_rgbd, [[3], [1]], 'h w *')
其中ps
表示Packed Shapes,记录了合并/拆分张量时每个张量在待处理维度上的占比。
⚪ 视觉Transformer中的CLS token
patch_tokens = np.random.random([batch, height, width, c])
class_tokens = np.zeros([batch, c])
input_packed, ps = pack([class_tokens, patch_tokens], 'b * c')
output_packed = transformer(input_packed)
class_token_emb, patch_tokens_emb = unpack(output_packed, ps, 'b * c_out')