代码之家  ›  专栏  ›  技术社区  ›  mon

numpy广播-尾轴解释

  •  0
  • mon  · 技术社区  · 4 年前

    提问

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

    As 在里面 trailing axes 如果是复数,则至少最后两个轴的大小必须匹配(单数除外)?如果是这样,为什么至少有两个?

    给出的答案是:

    那么,尾轴的含义解释如下 链接 文档 页面。如果你有两个不同维度的数组 数字,比如一个1x2x3和另一个2x3,然后你只比较 尾随通用尺寸,在本例中为2x3。但是 如果你的两个数组 如果是二维的,那么它们的相应大小必须是 相等或其中之一必须为1 .

    在你的例子中,你有一个2x2、4x2和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.
    

    工具书类

    广播规则
    为了广播,大小 后轴 因为操作中的两个数组必须大小相同,或者其中一个数组必须为1。


    更新

    根据@Akshay Sehgal的回复理解。考虑2个数组A.形状=(4,5,1)和B.形状=(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)
    

    首先,观察轴=-1,A中的形状01从01广播到02,因为它是奇异的,以匹配B的形状。然后,轴=-2的B中的形状01:01从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 回复  |  直到 4 年前
        1
  •  1
  •   Akshay Sehgal    4 年前

    牵引轴是 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 按照相反的顺序。

        2
  •  1
  •   hpaulj    4 年前

    我解释广播的方式较少关注尾随轴,而更多地关注两条规则:

    • 通过添加前导尺寸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)一起工作

    从(1,2)开始 B 也起作用:

    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,:] 但这不是必要的。