簡體   English   中英

Numpy中的矩陣反轉速度

[英]Matrix inversion speed in Numpy

我一直在閱讀“使用 Scikit-Learn 和 TensorFlow 進行機器學習實踐”一書。 在第 4 章(第 110 頁)中,它說:反轉這樣的矩陣的計算復雜度通常約為 O(n^2.4) 到 O(n^3)(取決於實現)。 換句話說,如果將特征數量翻倍,則計算時間大約乘以 2^2.4 = 5.3 到 2^3 = 8。

我在 Python 中測試了上述內容:

import numpy as np
import timeit
nsize = 100
A = np.random.rand(nsize, nsize)
x = np.random.rand(nsize, 1)
b = A.dot(x)
# np.linalg.inv(A) or np.linalg.inv(A).dot(b) - not a big difference in time
timeit.timeit('np.linalg.inv(A)','import numpy as np\nfrom __main__ import A, b', number=10000)

nsize=100大約需要 2.5 秒, nsize=200大約需要 8.5 秒。 計算成本似乎增加了 3.4 倍(8.5/2.5),遠低於作者提到的范圍。 有人可以解釋導致這種“加速”的原因嗎?

Numpy 使用 LAPACK 的 LU 分解(正如@Murali 在評論中指出的那樣)。 LU 分解通常由 OpenBLAS 在大多數平台(或行為類似的英特爾 MKL)上完成。 OpenBLAS 使用dgemm調用(矩陣乘法)來加快 LU 分解。 dgemm是用於大型矩陣的最優化的 BLAS 原語之一。 它利用多線程和 SIMD 指令,因此速度非常快。 話雖如此,在小型矩陣上,使用多個線程會帶來很大的開銷(創建線程、分配工作和等待結果需要時間)。 對於更大的矩陣,這種開銷往往可以忽略不計。 對於具有更小矩陣的 SIMD 指令也是如此:SIMD 指令在計算相對較大的數組時速度很快,但與標量指令相比,它們具有較高的延遲(對於小矩陣可以更好地流水線化)。 循環展開也會導致相同的效果(對於非常非常小的矩陣)以及 CPU 緩存。 除此之外,Numpy 還進行了一些類型檢查和內部工作(以便在其他一些情況下優化內部循環),這需要幾微秒。 所有這些因素的結合導致代碼對於小矩陣非常低效,而對於大矩陣則更快。 這種開銷在測量的加速中引入了偏差。

在我的機器上,對於時間為 100 的矩陣, dgemm調用需要 19% 的時間, dtrsm需要 11%, dlaswp需要 7%(其他調用需要的時間可以忽略不計,rest 在等待或內核中丟失)。 對於大小為 200 的矩陣, dgemm調用占 29%, dtrsm 12%, dlaswp 7%。 對於大小為 1000 的矩陣, dgemm調用占 57%, dtrsm 5%, dlaswp 6%。 正如我們所看到的,大部分時間都浪費在大小為 100 的矩陣上,而大部分時間被 BLAS 操作用於大小為 1000 的矩陣。

請注意,LU 分解很難並行化。 這是一個活躍的研究領域(持續了幾十年)。 此外,遺憾的是,最好的並行化方法(通常基於任務)還沒有在 OpenBLAS 等庫中使用。 AFAIK,MKL 使用了一點。 State 的藝術實現速度更快,特別是對於小矩陣,因為dgemm針對大矩陣進行了適當優化。

可以使用OMP_NUM_THREADS=1在 OpenBLAS 中禁用多線程。 這樣,對於大小為 100 的矩陣,我得到了 151 µs,對於大小為 200 的矩陣,我得到了 800 µs。應該考慮 Numpy 開銷的約 4 µs,以及其他不恆定的開銷,如頁面錯誤、分配、SIMD 指令的延遲、緩存效果,分支預測等。話雖如此,因子達到 5.44(沒有 Numpy 開銷)。 對於 200 VS 400,它是 5.67。 而對於 400 VS 800,它甚至是 6.8。 該因子仍未接近 8(除了其他開銷)的主要原因是 OpenBLAS 未針對使用 1 個線程運行 LU 分解進行優化:內部參數設置為次優,在這種情況下算法不是最優的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM