Python科学计算(第2版)
上QQ阅读APP看书,第一时间看更新

2.4.9 各种乘积运算

与本节内容对应的Notebook为:02-numpy/numpy-460-functions-dot.ipynb。

本节介绍的函数如表2-9所示。

表2-9 本节要介绍的函数

矩阵的乘积可以使用dot( )计算。对于二维数组,它计算的是矩阵乘积;对于一维数组,它计算的是内积。当需要将一维数组当作列矢量或行矢量进行矩阵运算时,先将一维数组转换为二维数组:

    a = np.array([1, 2, 3])
    a[:, None] a[None, :]
    ---------- -----------
    [[1], [[1, 2, 3]]
     [2], 
     [3]] 

对于多维数组,dot( )的通用计算公式如下,即结果数组中的每个元素都是:数组a的最后轴上的所有元素与数组b的倒数第二轴上的所有元素的乘积和:

    dot(a, b)[i,j,k,m] = sum(a[i,j,:] *
 b[k,:,m])

下面以两个三维数组的乘积演示dot( )的计算结果。首先创建两个三维数组,这两个数组的最后两轴满足矩阵乘积的条件:

    a = np.arange(12).reshape(2, 3, 2)
    b = np.arange(12, 24).reshape(2, 2, 3)
    c = np.dot(a, b)
    c.shape
    (2, 3, 2, 3)

c是数组a和b的多个子矩阵的乘积。我们可以把数组a看作两个形状为(3,2)的矩阵,而把数组b看作两个形状为(2,3)的矩阵。a中的两个矩阵分别与b中的两个矩阵进行矩阵乘积,就得到数组c,c[i, :, j, :]是a中第i个矩阵与b中第j个矩阵的乘积。

    for i, j in np.ndindex(2, 2):
        assert np.alltrue( c[i, :, j, :] == np.dot(a[i], b[j]) )

对于两个一维数组,inner( )和dot( )一样,计算两个数组对应下标元素的乘积和。而对于多维数组,它计算的结果数组中的每个元素都是:数组a和b的最后轴的内积。因此数组a和b的最后轴的长度必须相同:

    inner(a, b)[i,j,k,m] = sum(a[i,j,:]*
b[k,m,:])

下面是对inner( )的演示:

    a = np.arange(12).reshape(2, 3, 2)
    b = np.arange(12, 24).reshape(2, 3, 2)
    c = np.inner(a, b)
    c.shape
    (2, 3, 2, 3)
    for i, j, k, l in np.ndindex(2, 3, 2, 3):
        assert c[i, j, k, l] == np.inner(a[i, j], b[k, l])

outer( )只对一维数组进行计算,如果传入的是多维数组,则先将此数组展平为一维数组之后再进行运算。它计算列向量和行向量的矩阵乘积:

    a = np.array([1, 2, 3])
    b = np.array([4, 5, 6, 7])
     np.outer(a, b)    np.dot(a[:, None], b[None, :])
    ------------------ ------------------------------
    [[ 4, 5, 6, 7],    [[ 4, 5, 6, 7], 
     [ 8, 10, 12, 14],[ 8, 10, 12, 14], 
     [12, 15, 18, 21]][12, 15, 18, 21]] 

tensordot( )将两个多维数组a和b指定轴上的对应元素相乘并求和,它是最一般化的乘积运算函数。下面通过一些例子逐步介绍其用法。下面计算两个矩阵的乘积:❶axes参数有两个元素,第一个元素表示a中的轴,第二个元素表示b中的轴,这两个轴上对应的元素相乘之后求和。❷axes也可以是一个整数,它表示把a中的后axes个轴和b中的前axes个轴进行乘积和运算,而对于乘积和之外的轴则保持不变。

    a = np.random.rand(3, 4)
    b = np.random.rand(4, 5)
    
    c1 = np.tensordot(a, b, axes=[[1], [0]]) ❶
    c2 = np.tensordot(a, b, axes=1)          ❷
    c3 = np.dot(a, b)
    assert np.allclose(c1, c3)
    assert np.allclose(c2, c3)

对于多维数组的dot( )乘积,可以用tensordot(a, b, axes=[[-1], [-2]])表示,即将a的最后轴和b中的倒数第二轴求乘积和:

    a = np.arange(12).reshape(2, 3, 2)
    b = np.arange(12, 24).reshape(2, 2, 3)
    c1 = np.tensordot(a, b, axes=[[-1], [-2]])
    c2 = np.dot(a, b)
    assert np.alltrue(c1 == c2)

在下面的例子中,将a的第1、第2轴与b的第1、第0轴求乘积和,因此c中的每个元素都是按照如下表达式计算的:

    c[i, j, k, l] = np.sum(a[i, :, :, j] *
 b[:, :, k, l].T)

注意由于b对应的axes中的轴是倒序的,因此需要做转置操作。

    a = np.random.rand(4, 5, 6, 7)
    b = np.random.rand(6, 5, 2, 3)
    c = np.tensordot(a, b, axes=[[1, 2], [1, 0]])
    
    for i, j, k, l in np.ndindex(4, 7, 2, 3):
        assert np.allclose(c[i, j, k, l], np.sum(a[i, :, :, j] *
 b[:, :, k, l].T))
    
    c.shape
    (4, 7, 2, 3)