tldr: 它是行向量,只是在進行矩陣乘法時自動擴展 / 轉換為適當的維度。
我一直被「numpy 陣列是行向量還是列向量」這個問題困擾。我知道對於一維向量來說,它既不是行也不是列,正如其他人所指出的。但如果沒有對這個問題的正確理解,每次我試圖想像在實現自己的代數時會發生什麼,我都不夠自信,而這種不自信真的很煩人,因為我總是擔心自己可能寫錯了什麼。
我將這個問題視為,當一個 2-D numpy 陣列(即矩陣)和一個 1-D numpy 陣列相乘時,這個 2-D 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-D 陣列並看看會發生什麼。正如預期的那樣,這次我們可以看到它們是如何被不同對待的:
>>> 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 的世界現在在我心中是完美有序的;)。我希望這種隱式轉換從一開始就沒有實現,那樣我可能會更早學到這個事實,只需幾個運行時錯誤。