• [代码学习]einsum详解


    einsum详解

    该函数用于对一组输入 Tensor 进行 Einstein 求和,该函数目前仅适用于paddle动态图

    Einstein 求和是一种采用 Einstein 标记法描述的 Tensor 求和,输入单个或多个 Tensor,输出单个 Tensor。

    在这里插入图片描述

    paddle.einsum(equation, *operands)

    参数

    • equation (str):求和标记
    • operands (Tensor, [Tensor, …]):输入 Tensor

    返回

    • Tensor:输出 Tensor

    求和特例

    • 单操作数

      • 迹:trace

      • 对角元:diagonal

      • 转置:transpose

      • 求和:sum

    • 双操作数

      • 内积:dot

      • 外积:outer

      • 广播乘积:mul,*

      • 矩阵乘:matmul

      • 批量矩阵乘:bmm

    • 多操作数

      • 广播乘积:mul,*

      • 多矩阵乘:A.matmul(B).matmul(C)

    关于求和标记的约定

    • 维度分量下标:Tensor 的维度分量下标使用英文字母表示,不区分大小写,如’ijk’表示 Tensor 维度分量为 i,j,k

    • 下标对应输入操作数:维度下标以`,`分段,按顺序 1-1 对应输入操作数

    • 广播维度:省略号`…`表示维度的广播分量,例如,'i…j’表示首末分量除外的维度需进行广播对齐

    • 自由标和哑标:输入标记中仅出现一次的下标为自由标,重复出现的下标为哑标,哑标对应的维度分量将被规约消去

    • 输出:输出 Tensor 的维度分量既可由输入标记自动推导,也可以用输出标记定制化

    • 自动推导输出

      • 广播维度分量位于维度向量高维位置,自由标维度分量按字母顺序排序,位于维度向量低纬位置,哑标维度分量不输出
    • 定制化输出

      • 维度标记中`->`右侧为输出标记

      • 若输出包含广播维度,则输出标记需包含`…`

      • 输出标记为空时,对输出进行全量求和,返回该标量

      • 输出不能包含输入标记中未出现的下标

      • 输出下标不可以重复出现

      • 哑标出现在输出标记中则自动提升为自由标

      • 输出标记中未出现的自由标被降为哑标

    例子

    • ‘…ij, …jk’,该标记中 i,k 为自由标,j 为哑标,输出维度’…ik’

    • ‘ij -> i’,i 为自由标,j 为哑标

    • ‘…ij, …jk -> …ijk’,i,j,k 均为自由标

    • ‘…ij, …jk -> ij’,若输入 Tensor 中的广播维度不为空,则该标记为无效标记

    求和规则

    Einsum 求和过程理论上等价于如下四步,但实现中实际执行的步骤会有差异。

    • 第一步,维度对齐:将所有标记按字母序排序,按照标记顺序将输入 Tensor 逐一转置、补齐维度,使得处理后的所有 Tensor 其维度标记保持一致

    • 第二步,广播乘积:以维度下标为索引进行广播点乘

    • 第三步,维度规约:将哑标对应的维度分量求和消除

    • 第四步,转置输出:若存在输出标记,则按标记进行转置,否则按广播维度+字母序自由标的顺序转置,返回转之后的 Tensor 作为输出

    关于 trace 和 diagonal 的标记约定(待实现功能)

    • 在单个输入 Tensor 的标记中重复出现的下标称为对角标,对角标对应的坐标轴需进行对角化操作,如’i…i’表示需对首尾坐标轴进行对角化

    • 若无输出标记或输出标记中不包含对角标,则对角标对应维度规约为标量,相应维度取消,等价于 trace 操作

    • 若输出标记中包含对角标,则保留对角标维度,等价于 diagonal 操作

    实例实践

    首先,看一下一维度简单实验:

    import paddle
    
    # 定义两个输入矩阵
    # paddle.seed(102)
    # x = paddle.rand([4])
    # y = paddle.rand([5])
    x = paddle.to_tensor([1,2,], dtype='float32')
    y = paddle.to_tensor([3,4,5], dtype='float32')
    
    # sum
    sum_x = paddle.einsum('i->', x).numpy()
    
    # dot
    dox_x = paddle.einsum('i,i->', x, x).numpy()
    
    # outer
    outer_xy = paddle.einsum("i,j->ij", x, y).numpy()
    
    print(f"x: {x.numpy()}, shape: {x.shape}")
    print(f"y: {y.numpy()}, shape: {y.shape}")
    print(f"sum_x: {sum_x}, shape: {sum_x.shape}")
    print(f"dox_x: {dox_x}, shape: {dox_x.shape}")
    print(f"outer_xy: {outer_xy}, shape: {outer_xy.shape}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    结果输出为:

    x: [1. 2.], shape: [2]
    y: [3. 4. 5.], shape: [3]
    sum_x: 3.0, shape: ()
    dox_x: 5.0, shape: ()
    outer_xy: [[ 3.  4.  5.]
     [ 6.  8. 10.]], shape: (2, 3)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    然后,看一下高纬度的实验:

    import paddle
    
    # A = paddle.rand([2, 3, 2])
    # B = paddle.rand([2, 2, 3])
    A = paddle.to_tensor([[[1,2],[1,2],[1,2]], [[1,2],[1,2],[1,2]]], dtype='float32')
    B = paddle.to_tensor([[[3,4,5],[3,4,5]], [[3,4,5],[3,4,5]]], dtype='float32')
    
    # transpose
    transpose_A = paddle.einsum('ijk->kji', A)
    
    # batch matrix multiplication
    BMM_AB = paddle.einsum('ijk, ikl->ijl', A,B)
    
    # Ellipsis transpose
    ET_A = paddle.einsum('...jk->...kj', A)
    
    # Ellipsis batch matrix multiplication
    EBMM_AB = paddle.einsum('...jk, ...kl->...jl', A,B)
    
    print(f"A: {A.numpy()}, shape: {A.shape}")
    print(f"B: {B.numpy()}, shape: {B.shape}")
    print(f"transpose_A: {transpose_A.numpy()}, shape: {transpose_A.shape}")
    print(f"BMM_AB: {BMM_AB.numpy()}, shape: {BMM_AB.shape}")
    print(f"ET_A: {ET_A.numpy()}, shape: {ET_A.shape}")
    print(f"EBMM_AB: {EBMM_AB.numpy()}, shape: {EBMM_AB.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

    结果输出为:

    A: [[[1. 2.]
      [1. 2.]
      [1. 2.]]
    
     [[1. 2.]
      [1. 2.]
      [1. 2.]]], shape: [2, 3, 2]
    B: [[[3. 4. 5.]
      [3. 4. 5.]]
    
     [[3. 4. 5.]
      [3. 4. 5.]]], shape: [2, 2, 3]
    transpose_A: [[[1. 1.]
      [1. 1.]
      [1. 1.]]
    
     [[2. 2.]
      [2. 2.]
      [2. 2.]]], shape: [2, 3, 2]
    BMM_AB: [[[ 9. 12. 15.]
      [ 9. 12. 15.]
      [ 9. 12. 15.]]
    
     [[ 9. 12. 15.]
      [ 9. 12. 15.]
      [ 9. 12. 15.]]], shape: [2, 3, 3]
    ET_A: [[[1. 1. 1.]
      [2. 2. 2.]]
    
     [[1. 1. 1.]
      [2. 2. 2.]]], shape: [2, 2, 3]
    EBMM_AB: [[[ 9. 12. 15.]
      [ 9. 12. 15.]
      [ 9. 12. 15.]]
    
     [[ 9. 12. 15.]
      [ 9. 12. 15.]
      [ 9. 12. 15.]]], shape: [2, 3, 3]
    
    • 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

    reference

    关于matmul可以查看:https://blog.csdn.net/orDream/article/details/133744368
    官方链接:
    @misc{BibEntry2023Oct,
    title = {{einsum-API文档-PaddlePaddle深度学习平台}},
    year = {2023},
    month = oct,
    urldate = {2023-10-10},
    language = {chinese},
    note = {[Online; accessed 10. Oct. 2023]},
    url = {https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/einsum_cn.html}
    }

  • 相关阅读:
    Adobe Audition 入门系列(合集)
    华为网络工程师认证有了解的吗?
    YOLOv5全面解析教程③:更快更好的边界框回归损失
    Java - 反射
    《在线编程-Python篇》Python入门 01 输入输出
    若依框架学习——新建模块(图文)
    ADSP-21489的图形化编程详解(3:音效开发例程-直通三个例程讲清楚)
    循环结构--do-while循环
    MFC 皮肤库配置
    一文搞懂mysql索引底层逻辑,干货满满!
  • 原文地址:https://blog.csdn.net/orDream/article/details/133747553