numpyの内積(4)多次元配列


2023年 10月 05日

3次元配列に対する積

3次元以上の多次元配列に対する積がnp.dot()でできるかどうか、試してみよう。

>>> import numpy as np
>>> M234 = np.array(range(1,25)).reshape(2,3,4); M234
array([[[ 1,  2,  3,  4],
        [ 5,  6,  7,  8],
        [ 9, 10, 11, 12]],

       [[13, 14, 15, 16],
        [17, 18, 19, 20],
        [21, 22, 23, 24]]])
>>> M43 = np.array(range(1,13)).reshape(4,3); M43
array([[ 1,  2,  3],
       [ 4,  5,  6],
       [ 7,  8,  9],
       [10, 11, 12]])
>>> np.dot(M234,M43)
array([[[ 70,  80,  90],
        [158, 184, 210],
        [246, 288, 330]],

       [[334, 392, 450],
        [422, 496, 570],
        [510, 600, 690]]])

(2,3,4)型の配列と(4,3)型の配列の積は、(2,3,3)型の配列になった。

それなら、(2,3,4)型と(4,3,2)型の配列の積は、(2,3,3,2)型になるだろうか。

>>> M432 = np.array(range(1,25)).reshape(4,3,2); M432
array([[[ 1,  2],
        [ 3,  4],
        [ 5,  6]],

       [[ 7,  8],
        [ 9, 10],
        [11, 12]],

       [[13, 14],
        [15, 16],
        [17, 18]],

       [[19, 20],
        [21, 22],
        [23, 24]]])
>>> np.dot(M234,M432)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<__array_function__ internals>", line 180, in dot
ValueError: shapes (2,3,4) and (4,3,2) not aligned: 4 (dim 2) != 3 (dim 1)

積和を計算するのに、第1配列の第2サイズが4で、第2配列の第1サイズが3で、サイズ違いで計算不可と怒っているようだ。

ということは、第1配列の第2サイズと第2配列の第1サイズが一致すれば計算してくれそうなので試してみよう。

>>> M342 = np.array(range(1,25)).reshape(3,4,2); M342
array([[[ 1,  2],
        [ 3,  4],
        [ 5,  6],
        [ 7,  8]],

       [[ 9, 10],
        [11, 12],
        [13, 14],
        [15, 16]],

       [[17, 18],
        [19, 20],
        [21, 22],
        [23, 24]]])
>>> np.dot(M234,M342)
array([[[[  50,   60],
         [ 130,  140],
         [ 210,  220]],

        [[ 114,  140],
         [ 322,  348],
         [ 530,  556]],

        [[ 178,  220],
         [ 514,  556],
         [ 850,  892]]],


       [[[ 242,  300],
         [ 706,  764],
         [1170, 1228]],

        [[ 306,  380],
         [ 898,  972],
         [1490, 1564]],

        [[ 370,  460],
         [1090, 1180],
         [1810, 1900]]]])
>>> np.shape(np.dot(M234,M342))
(2, 3, 3, 2)
>>> 

今度は、第1引数を2次元配列、第2引数を3次元配列にして、np.dot()を行ってみた。

>>> M64 = np.array(range(1,25)).reshape(6,4); M64
array([[ 1,  2,  3,  4],
       [ 5,  6,  7,  8],
       [ 9, 10, 11, 12],
       [13, 14, 15, 16],
       [17, 18, 19, 20],
       [21, 22, 23, 24]])
>>> np.dot(M64,M342)
array([[[  50,   60],
        [ 130,  140],
        [ 210,  220]],

       [[ 114,  140],
        [ 322,  348],
        [ 530,  556]],

       [[ 178,  220],
        [ 514,  556],
        [ 850,  892]],

       [[ 242,  300],
        [ 706,  764],
        [1170, 1228]],

       [[ 306,  380],
        [ 898,  972],
        [1490, 1564]],

       [[ 370,  460],
        [1090, 1180],
        [1810, 1900]]])

以上から、第1引数配列の最終次元のサイズと、第2引数配列の(最終−1)次元のサイズが一致していれば、多次元配列同士の配列の積が求まるようである。

ベクトルと多次元配列の積

まず、ベクトルの代わりに、単なるリストを与えてみよう。

>>> np.dot([1,2,3,4],M2345)
array([[[ 110,  120,  130,  140,  150],
        [ 310,  320,  330,  340,  350],
        [ 510,  520,  530,  540,  550]],

       [[ 710,  720,  730,  740,  750],
        [ 910,  920,  930,  940,  950],
        [1110, 1120, 1130, 1140, 1150]]])
>>> np.dot(M2345,[1,2,3,4,5])
array([[[  55,  130,  205,  280],
        [ 355,  430,  505,  580],
        [ 655,  730,  805,  880]],

       [[ 955, 1030, 1105, 1180],
        [1255, 1330, 1405, 1480],
        [1555, 1630, 1705, 1780]]])

np.dot()の第2引数に、リスト、1次元配列、2次元配列を与えてみた。

>>> L5 = [1,2,3,4,5]
>>> M5 = np.array([1,2,3,4,5])
>>> M51 = np.array([1,2,3,4,5]).reshape(5,1)
>>> np.dot(M2345,L5)
array([[[  55,  130,  205,  280],
        [ 355,  430,  505,  580],
        [ 655,  730,  805,  880]],

       [[ 955, 1030, 1105, 1180],
        [1255, 1330, 1405, 1480],
        [1555, 1630, 1705, 1780]]])
>>> np.dot(M2345,M5)
array([[[  55,  130,  205,  280],
        [ 355,  430,  505,  580],
        [ 655,  730,  805,  880]],

       [[ 955, 1030, 1105, 1180],
        [1255, 1330, 1405, 1480],
        [1555, 1630, 1705, 1780]]])
>>> np.dot(M2345,M51)
array([[[[  55],
         [ 130],
         [ 205],
         [ 280]],

        [[ 355],
         [ 430],
         [ 505],
         [ 580]],

        [[ 655],
         [ 730],
         [ 805],
         [ 880]]],


       [[[ 955],
         [1030],
         [1105],
         [1180]],

        [[1255],
         [1330],
         [1405],
         [1480]],

        [[1555],
         [1630],
         [1705],
         [1780]]]])
>>> np.shape(np.dot(M2345,M51))
(2, 3, 4, 1)

nreshape()により、5行1列の配列にした場合、見かけは縦ベクトルに見えるが、しっかり多次元配列同士の積になっていて、結果もちゃんと異なった。

意味

さて、多次元配列の積が行えることが分かった。

しかし、多次元配列ってどういう風に扱えばよいのだろうか。
次回までに考えておこう。