理解 Tensordot

2025-02-21 08:48:00
admin
原创
25
摘要:问题描述:在我学会了如何使用之后einsum,我现在正尝试了解np.tensordot它的工作原理。然而,我有点迷失,特别是关于参数的各种可能性axes。为了理解它,由于我从未练习过张量微积分,我使用以下示例:A = np.random.randint(2, size=(2, 3, 5)) B = np.ra...

问题描述:

在我学会了如何使用之后einsum,我现在正尝试了解np.tensordot它的工作原理。

然而,我有点迷失,特别是关于参数的各种可能性axes

为了理解它,由于我从未练习过张量微积分,我使用以下示例:

A = np.random.randint(2, size=(2, 3, 5))
B = np.random.randint(2, size=(3, 2, 4))

在这种情况下,可能存在哪些不同np.tensordot以及如何手动计算?


解决方案 1:

这个想法tensordot很简单 - 我们输入数组和相应的轴,这些轴是要进行求和缩减的。参与求和缩减的轴在输出中被移除,输入数组中所有剩余的轴在输出中分散为不同的轴,保持输入数组的输入顺序。

让我们看几个具有一轴和两轴求和的样例,并交换输入的位置,看看输出中顺序是如何保持的。

一、和约法的一个轴

输入:

 In [7]: A = np.random.randint(2, size=(2, 6, 5))
   ...:  B = np.random.randint(2, size=(3, 2, 4))
   ...: 

案例 #1:

In [9]: np.tensordot(A, B, axes=((0),(1))).shape
Out[9]: (6, 5, 3, 4)

A : (2, 6, 5) -> reduction of axis=0
B : (3, 2, 4) -> reduction of axis=1

Output : `(2, 6, 5)`, `(3, 2, 4)` ===(2 gone)==> `(6,5)` + `(3,4)` => `(6,5,3,4)`

案例 2(与案例 1 相同,但输入被交换):

In [8]: np.tensordot(B, A, axes=((1),(0))).shape
Out[8]: (3, 4, 6, 5)

B : (3, 2, 4) -> reduction of axis=1
A : (2, 6, 5) -> reduction of axis=0

Output : `(3, 2, 4)`, `(2, 6, 5)` ===(2 gone)==> `(3,4)` + `(6,5)` => `(3,4,6,5)`.

II. 和约的两个轴

输入:

In [11]: A = np.random.randint(2, size=(2, 3, 5))
    ...: B = np.random.randint(2, size=(3, 2, 4))
    ...: 

案例 #1:

In [12]: np.tensordot(A, B, axes=((0,1),(1,0))).shape
Out[12]: (5, 4)

A : (2, 3, 5) -> reduction of axis=(0,1)
B : (3, 2, 4) -> reduction of axis=(1,0)

Output : `(2, 3, 5)`, `(3, 2, 4)` ===(2,3 gone)==> `(5)` + `(4)` => `(5,4)`

案例 #2:

In [14]: np.tensordot(B, A, axes=((1,0),(0,1))).shape
Out[14]: (4, 5)

B : (3, 2, 4) -> reduction of axis=(1,0)
A : (2, 3, 5) -> reduction of axis=(0,1)

Output : `(3, 2, 4)`, `(2, 3, 5)` ===(2,3 gone)==> `(4)` + `(5)` => `(4,5)`

我们可以将其扩展到尽可能多的轴。

解决方案 2:

tensordot交换轴并重塑输入,以便可以应用于np.dot2 个 2d 数组。然后交换并重塑回目标。实验可能比解释更容易。没有特殊的张量数学,只是扩展dot到更高维度。tensor只是意味着超过 2d 的数组。如果您已经熟悉,einsum那么最简单的方法是将结果与之进行比较。

样本测试,对 1 对轴进行求和

In [823]: np.tensordot(A,B,[0,1]).shape
Out[823]: (3, 5, 3, 4)
In [824]: np.einsum('ijk,lim',A,B).shape
Out[824]: (3, 5, 3, 4)
In [825]: np.allclose(np.einsum('ijk,lim',A,B),np.tensordot(A,B,[0,1]))
Out[825]: True

另一个,对两个进行总结。

In [826]: np.tensordot(A,B,[(0,1),(1,0)]).shape
Out[826]: (5, 4)
In [827]: np.einsum('ijk,jim',A,B).shape
Out[827]: (5, 4)
In [828]: np.allclose(np.einsum('ijk,jim',A,B),np.tensordot(A,B,[(0,1),(1,0)]))
Out[828]: True

我们可以对这对做同样的(1,0)事情。考虑到尺寸的混合,我认为没有其他组合。

解决方案 3:

上面的答案很棒,对我理解有很大帮助tensordot。但它们没有展示操作背后的实际数学。这就是为什么我自己在 TF 2 中做了等效操作并决定在这里分享它们的原因:

a = tf.constant([1,2.])
b = tf.constant([2,3.])
print(f"{tf.tensordot(a, b, 0)}     tf.einsum('i,j', a, b)        - ((the last 0 axes of a), (the first 0 axes of b))")
print(f"{tf.tensordot(a, b, ((),()))}     tf.einsum('i,j', a, b)        - ((() axis of a), (() axis of b))")
print(f"{tf.tensordot(b, a, 0)}     tf.einsum('i,j->ji', a, b)    - ((the last 0 axes of b), (the first 0 axes of a))")
print(f"{tf.tensordot(a, b, 1)}         tf.einsum('i,i', a, b)        - ((the last 1 axes of a), (the first 1 axes of b))")
print(f"{tf.tensordot(a, b, ((0,), (0,)))}         tf.einsum('i,i', a, b)        - ((0th axis of a), (0th axis of b))")
print(f"{tf.tensordot(a, b, (0,0))}         tf.einsum('i,i', a, b)        - ((0th axis of a), (0th axis of b))")

[[2. 3.]
 [4. 6.]]    tf.einsum('i,j', a, b)     - ((the last 0 axes of a), (the first 0 axes of b))
[[2. 3.]
 [4. 6.]]    tf.einsum('i,j', a, b)     - ((() axis of a), (() axis of b))
[[2. 4.]
 [3. 6.]]    tf.einsum('i,j->ji', a, b) - ((the last 0 axes of b), (the first 0 axes of a))
8.0          tf.einsum('i,i', a, b)     - ((the last 1 axes of a), (the first 1 axes of b))
8.0          tf.einsum('i,i', a, b)     - ((0th axis of a), (0th axis of b))
8.0          tf.einsum('i,i', a, b)     - ((0th axis of a), (0th axis of b))

对于(2,2)形状:

a = tf.constant([[1,2],
                 [-2,3.]])

b = tf.constant([[-2,3],
                 [0,4.]])
print(f"{tf.tensordot(a, b, 0)}     tf.einsum('ij,kl', a, b)    - ((the last 0 axes of a), (the first 0 axes of b))")
print(f"{tf.tensordot(a, b, (0,0))}     tf.einsum('ij,ik', a, b)    - ((0th axis of a), (0th axis of b))")
print(f"{tf.tensordot(a, b, (0,1))}     tf.einsum('ij,ki', a, b)    - ((0th axis of a), (1st axis of b))")
print(f"{tf.tensordot(a, b, 1)}     tf.matmul(a, b)        - ((the last 1 axes of a), (the first 1 axes of b))")
print(f"{tf.tensordot(a, b, ((1,), (0,)))}     tf.einsum('ij,jk', a, b)    - ((1st axis of a), (0th axis of b))")
print(f"{tf.tensordot(a, b, (1, 0))}     tf.matmul(a, b)        - ((1st axis of a), (0th axis of b))")
print(f"{tf.tensordot(a, b, 2)}     tf.reduce_sum(tf.multiply(a, b))    - ((the last 2 axes of a), (the first 2 axes of b))")
print(f"{tf.tensordot(a, b, ((0,1), (0,1)))}     tf.einsum('ij,ij->', a, b)        - ((0th axis of a, 1st axis of a), (0th axis of b, 1st axis of b))")
[[[[-2.  3.]
   [ 0.  4.]]
  [[-4.  6.]
   [ 0.  8.]]]

 [[[ 4. -6.]
   [-0. -8.]]
  [[-6.  9.]
   [ 0. 12.]]]]  tf.einsum('ij,kl', a, b)   - ((the last 0 axes of a), (the first 0 axes of b))
[[-2. -5.]
 [-4. 18.]]      tf.einsum('ij,ik', a, b)   - ((0th axis of a), (0th axis of b))
[[-8. -8.]
 [ 5. 12.]]      tf.einsum('ij,ki', a, b)   - ((0th axis of a), (1st axis of b))
[[-2. 11.]
 [ 4.  6.]]      tf.matmul(a, b)            - ((the last 1 axes of a), (the first 1 axes of b))
[[-2. 11.]
 [ 4.  6.]]      tf.einsum('ij,jk', a, b)   - ((1st axis of a), (0th axis of b))
[[-2. 11.]
 [ 4.  6.]]      tf.matmul(a, b)            - ((1st axis of a), (0th axis of b))
16.0    tf.reduce_sum(tf.multiply(a, b))    - ((the last 2 axes of a), (the first 2 axes of b))
16.0    tf.einsum('ij,ij->', a, b)          - ((0th axis of a, 1st axis of a), (0th axis of b, 1st axis of b))

解决方案 4:

除了上述答案之外,如果将np.tensordot分解为嵌套循环,会更容易理解:

认为:

import numpy as np
a = np.arange(24).reshape(2,3,4)
b = np.arange(30).reshape(3,5,2)

那么a

array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 18, 19],
        [20, 21, 22, 23]]])

并且b

array([[[ 0,  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]]])

c = np.tensordot(a, b, axes=([0,1],[2,0]))

相当于

c = np.zeros((4,5))
for i in range(4):
    for j in range(5):
        for p in range(2):
            for q in range(3):
                c[i,j] += a[p,q,i] * b[q,j,p]

两个张量中具有相同维度(此处为 2 和 3)的轴可以通过对它们求和来减少。参数axes =([0,1],[2,0]) 与axes =([1,0],[0,2])相同。

最后一个c

array([[ 808,  928, 1048, 1168, 1288],
       [ 871, 1003, 1135, 1267, 1399],
       [ 934, 1078, 1222, 1366, 1510],
       [ 997, 1153, 1309, 1465, 1621]])
相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1343  
  信创产业的蓬勃发展推动着各行业数字化转型加速,数据库迁移作为其中关键一环,面临诸多挑战。信创数据库迁移旨在将传统数据库平稳过渡到信创环境,以满足自主可控、安全可靠的需求。这一过程涉及技术、业务等多方面因素,稍有不慎就可能出现各种问题,影响业务的正常运行。深入探讨信创数据库迁移过程中的常见问题及解决方案,对于保障迁移工作...
2027年信创国产化   41  
  随着信息技术的飞速发展,信创国产化成为了国家战略的重要组成部分。国产化信创产品名录涵盖了众多领域,其在各个关键应用场景中发挥着重要作用。而信创国产化操作系统作为其中的核心环节,具备五大核心优势,为我国信息技术产业的自主可控发展提供了坚实支撑。关键应用场景之办公领域在办公领域,国产化信创产品有着广泛且深入的应用。如今,越...
国产信创系统   37  
  随着信息技术的飞速发展,信创国产化操作系统在政府部门的推广应用具有重要的战略意义。它不仅关乎国家信息安全,更是推动国内信息技术产业自主创新、实现科技自立自强的关键举措。在当前复杂的国际形势下,政府部门积极推广信创国产化操作系统,对于保障国家政务信息的安全稳定运行,提升信息技术的自主可控能力,具有不可替代的重要作用。推广...
信创产品有哪些   28  
  在企业数字化转型的进程中,信创数据库解决方案的选择至关重要。它不仅关乎企业数据的安全存储与管理,更影响着企业业务的稳定运行与未来发展。合适的信创数据库能够助力企业在复杂多变的市场环境中提升竞争力,保障数据主权与安全。然而,面对市场上众多的信创数据库产品和解决方案,企业往往感到困惑,不知如何做出正确的选择。接下来,我们将...
信创电脑   24  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用