【问题标题】:numpy broadcasting - explanation of trailing axesnumpy 广播 - 尾轴的解释
【发布时间】:2020-12-24 08:19:29
【问题描述】:

问题

请详细说明 2012 年 Numpy array broadcasting rules 中的答案,并澄清 尾轴 是什么,因为我不确定答案指的是哪个“链接文档页面” .也许在过去的 8 年里发生了变化。

由于trailing axes 中的axes 是复数,因此至少最后两个轴大小必须匹配(单数除外)?如果是,为什么至少有两个?

给出的答案是:

嗯,尾轴的含义在链接中解释过 文档页面。如果你有两个不同维度的数组 数,比如说一个 1x2x3 和另一个 2x3,那么你只比较 尾随常见尺寸,在本例中为 2x3。但是如果你的两个数组 是二维的,那么它们对应的尺寸必须是 等于或其中之一必须为 1

在您的情况下,您有 2x2 and 4x2 and 4 != 2 而不是 4 或 2 等于 1,所以这不起作用。

错误和提出的问题是:

A = np.array([[1,2],[3,4]])
B = np.array([[2,3],[4,6],[6,9],[8,12]])
print("A.shape {}".format(A.shape))
print("B.shape {}".format(B.shape))
A*B
---
A.shape (2, 2)              # <---- The last axis size is 2 in both shapes.
B.shape (4, 2)              # <---- Apparently this "2" is not the size of trailing axis/axes

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-91-7a3f7e97944d> in <module>
      3 print("A.shape {}".format(A.shape))
      4 print("B.shape {}".format(B.shape))
----> 5 A*B

ValueError: operands could not be broadcast together with shapes (2,2) (4,2) 


Since both A and B have two columns, I would have thought this would work. 
So, I'm probably misunderstanding something here about the term "trailing axis", 
and how it applies to N-dimensional arrays.

参考文献

广播规则
为了广播,操作中两个数组的尾轴的大小必须相同或其中之一必须是一个。


更新

根据@Akshay Sehgal 的回复了解。考虑 2 个数组 A.shape = (4,5,1) 和 B.shape = (1,2)。

A = np.arange(20).reshape((4, 5, 1))
B = np.arange(2).reshape((1,2))
print("A.shape {}".format(A.shape))
print("B.shape {}".format(B.shape))
---
A.shape (4, 5, 1)
B.shape (1, 2)

首先看axis=-1,A中的形状01是从01到02广播的,因为它是奇异的,来匹配B的。那么B中的形状01对于axis=-2是从01广播的(单数) 到 05 以匹配 A。结果是形状 (4, 5, 2)。

print("A * B shape is {}".format((A*B).shape))
---
A * B shape is (4, 5, 2)

基于@hpaulj 的回答,一种模拟广播的方法。

print("A.shape {}".format(A.shape))
print("B.shape {}".format(B.shape))
---
A.shape (4, 5, 1)
B.shape (1, 2)

# Check ranks.
print("rank(A) {} rank(B) {}".format(A.ndim, B.ndim))
---
rank(A) 3 rank(B) 2

# Expand B because rank(B) < rank(A).
B = B[
    None,
    ::
]
B.shape
---
(1, 1, 2)

A:(4,5,1)
   ↑ ↑ ↓
B:(1,1,2)
----------
C:(4,5,2)

【问题讨论】:

  • 添加了一个前导暗淡来制作 (1,1,2)。然后所有的 1 都被缩放了

标签: python numpy array-broadcasting


【解决方案1】:

尾随轴是 axis=-1, axis=-2, axis=-3 ... 。广播规则比较尾随轴而不是 leading 轴(axis=0 向前)。

这专门用于将广播应用于不同维度的张量(例如 2D 和 3D 张量)。 Trailing axes 基本上表示将轴视为广播规则的方向。想象一下按形状排列轴。如果您 lead 使用轴,您将拥有类似以下内容 -

考虑 2 个数组 A.shape = (4,5,1)B.shape = (1,2)

#Leading axes

A  04  05  01
B  01  02
--------------
No broadcasting
--------------

要考虑尾随轴,您可以将它们视为 -

#Trailing axes

A  04  05  01
B      01  02
--------------
C  04  05  02
--------------

这就是在这种情况下trailing axes 一词的全部含义,即从后向开始而不是引导轴。

换句话说,当考虑使用更高维数组广播(1,2) 形状的数组时,我们会查看形状为2 for axis=-1 的尾随轴,然后以相反的顺序查看1 for axis=-2

【讨论】:

  • 又因为 A (3) 的等级 > B(2) 的等级,B 的两个尾随轴必须匹配才能应用广播?
  • 完全正确,匹配或为 1 用于广播应用。 In order to broadcast, the size of the trailing axes for both arrays in an operation must either be the same size or one of them must be one.希望这能澄清你的问题。
  • 非常感谢。希望您的解释在 Numpy 文档中。
  • 很高兴随时提供帮助。
【解决方案2】:

我解释广播的方式不太关注拖尾轴,而是更多地关注两个规则:

  • 通过添加前导尺寸 1 尺寸来匹配尺寸数量
  • 缩放所有尺寸 1 的尺寸以匹配

在该示例中,配对:

In [233]: A = np.arange(20).reshape((4, 5))
     ...: B = np.arange(2)
In [234]: A
Out[234]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14],
       [15, 16, 17, 18, 19]])
In [235]: B
Out[235]: array([0, 1])
In [236]: A*B
Traceback (most recent call last):
  File "<ipython-input-236-47896efed660>", line 1, in <module>
    A*B
ValueError: operands could not be broadcast together with shapes (4,5) (2,) 

根据第一条规则,(2,) 扩展为 (1,2),也可能扩展为 (4,2),但这是一个死胡同。

但是如果我们给A添加一个维度,使它成为(4,5,1):

In [237]: A[:,:,None]*B
Out[237]: 
array([[[ 0,  0],
        [ 0,  1],
        [ 0,  2],
        ...
        [ 0, 19]]])
In [238]: _.shape
Out[238]: (4, 5, 2)

现在 (2,) 扩展为 (1,1,2),与 (4,5,1) 一起使用

B 以 (1,2) 开头也可以:

In [240]: (A[:,:,None]*B[None,:]).shape
Out[240]: (4, 5, 2)

它可以根据需要向B 添加任意数量的前导维度,但它不能自动向A 添加尾随维度。我们必须自己做。 reshape 可以很好地添加尺寸,但我认为 None/newaxis 成语更好地突出了这一添加。

这种行为可以用拖尾轴来解释(不必是复数),但我认为两步解释更清楚。

我认为区分前轴和后轴有两个原因。引导轴位于最外层(至少对于C 顺序而言),它避免了歧义。

考虑一起使用 (3,) 和 (2,)。我们可以从它们形成 (3,2) 或 (2,3) 数组,但是哪个?

In [241]: np.array([1,2,3])*np.array([4,5])
Traceback (most recent call last):
  File "<ipython-input-241-eaf3e99b50a9>", line 1, in <module>
    np.array([1,2,3])*np.array([4,5])
ValueError: operands could not be broadcast together with shapes (3,) (2,) 

In [242]: np.array([1,2,3])[:,None]*np.array([4,5])
Out[242]: 
array([[ 4,  5],
       [ 8, 10],
       [12, 15]])

In [243]: np.array([1,2,3])*np.array([4,5])[:,None]
Out[243]: 
array([[ 4,  8, 12],
       [ 5, 10, 15]])

显式尾随None 清楚地标识了我们想要的。我们可以添加[None,:],但这不是必需的。

【讨论】:

  • 非常感谢您的思路。通过添加前导维度来匹配排名非常有意义!
  • 由于问题是关于广播中的拖尾轴并且我已经接受了另一个答案,所以我无法改变,但非常感谢广播的观点。
猜你喜欢
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多