tldr: 行ベクトルですが、適切な次元で行列の乗算を行うと自動的に展開 / 変換されます。
私は常に「numpy 配列は行ベクトルまたは列ベクトルですか」という疑問に悩まされていました。1 次元のベクトルの場合、行でも列でもありません。他の人々がすでに指摘しています。しかし、この問題を正しく理解していないと、独自の代数を実装しようとするときに何が起こるかを想像する際にいつも自信が持てません。この自信のなさは本当に迷惑で、何か間違っているかもしれないと常に不安になります。
私はこの問題を次のように考えています。2 次元の numpy 配列(行列)と 1 次元の numpy 配列が乗算を行う場合、その 2 次元の numpy 配列のどの次元で行列の乗算が行われるのかということです。これは些細な問題ではありません。両方の方法が理論的には機能するかもしれませんが、完全に異なる結果が得られます。そこで、今日はシンプルな Python インタラクティブシェルを使用して、この問題を一気に解決します。
シンプルな実験#
行列 m
を定義します:
>>> m = np.array([[1,2,3],[2,3,4],[3,4,5]])
>>> m
array([[1, 2, 3],
[2, 3, 4],
[3, 4, 5]])
ベクトル v
を定義します:
>>> v = np.array([1,2,3])
>>> v
array([1, 2, 3])
要素ごとの乗算を行います:
>>> m * v
array([[ 1, 4, 9],
[ 2, 6, 12],
[ 3, 8, 15]])
これは非常に直感的に理解できます:両成分の最小要素(3 次元配列)が要素ごとに乗算されます。
行列の乗算を直接行います:
>>> m @ v
array([14, 20, 26])
>>> v @ m
array([14, 20, 26])
興味深いことに、両方の方向で同じ結果が得られます。行列の乗算に可換性のルールは適用されないため、m @ v
と v @ m
が同じ出力を持つだけでなく、少なくとも一方の方向が有効な操作ではないはずです。したがって、この結果から、numpy の演算 @
で暗黙の変換が行われたことが示唆されます。
このような暗黙の変換は、賢くも愚かなものです。賢いのは、インタプリタが形状を推測して何を書きたいのかを推測できることです。しかし、これはまた、コードの論理的なミスをした場合に、インタプリタがエラーをスローせずに進行し、誤った計算を行っている場合でも適切な警告を表示しないということを意味します。これはまた、私が本当に何が起こっているのかに自信を持つことを妨げる原因でもあります。
暗黙の変換を排除する#
したがって、このような曖昧さを排除するために、ベクトルを手動で同じ 2 次元配列にブロードキャストして、何が起こるかを見てみましょう。予想通り、今度はそれらが異なる方法で処理されるのがわかります:
>>> v[None,:]
array([[1, 2, 3]])
# このベクトルを左から乗算
>>> v[None,:] @ m
array([[14, 20, 26]])
# このベクトルを右から乗算
>>> m @ v[None,:]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 1 is different from 3)
もう一方も同じように機能します:
>>> v[:,None]
array([[1],
[2],
[3]])
>>> m @ v[:,None]
array([[14],
[20],
[26]])
>>> v[:, None] @ m
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 3 is different from 1)
結論#
ついに答えが明確になりました。単一のベクトルは、本質的には行ベクトルとして行列で扱われます。心配する必要はありません。numpy の世界は今や私の心の中で完全に整理されています;-)。最初からこのような暗黙の変換が実装されなかったらいいのにと思います。そうすれば、ランタイムエラーがわずかに発生するだけで、この事実をもっと早く学ぶことができたかもしれません。