我一直在嘗試使用 NumPy 來優化矩陣 C(見下文)的構造。如何進一步優化我的代碼,以便更快地構建矩陣 C?
給定以下矩陣:
Q: array([[78.66 , 47.196 , 31.464 ],
[40.3875, 24.2325, 16.155 ],
[40.4775, 24.2865, 16.191 ],
...,
[55.62 , 33.372 , 22.248 ],
[76.7475, 46.0485, 30.699 ],
[77.3325, 46.3995, 30.933 ]])
S: [[[1,2,3],[],[],[1,...,1125]],
[[],[1,...,200],[300,301][]],
...,
[[1,1125],[],[12],[345,453]]]
gamma: array([[0. , 1.4, 2.5, 3. , 3. ],
[0. , 1.6, 3. , 3.7, 4. ],
[0. , 1.8, 3.5, 4.4, 5. ]])
我有以下代碼來構建三維矩陣 C
# # Matrix C_ijk
C = np.zeros((n,o,p))
for i in range(n):
for j in range(o):
for k in range(p):
for u in range(m-1):
if np.isin(i,S[j][u]):
C[i,j,k] = Q[j,k] * gamma[k,u 1]
編輯:m, n,o和p是定義矩陣維度長度的整數。它們分別是完整模型中的 5、1126、797 和 3。
Q 是尺寸(o,p);S 是尺寸(o,m-1);伽馬是大小(p,m-1);C 是尺寸(n,o,p);
這是一個小示例輸出:
>>> Q
array([[10., 10.],
[20., 20.],
[30., 30.],
[30., 30.]])
>>> S
[[[0, 1], [], [], [2]], [[2], [0], [1], []], [[], [1], [0, 2], []], [[], [2], [], [0, 1]]]
>>> gamma
array([[0. , 0.575, 1.2 , 1.75 , 2. ],
[0. , 0.625, 1.4 , 2.25 , 3. ]])
>>> C
array([[[ 5.75, 6.25],
[24. , 28. ],
[52.5 , 67.5 ],
[60. , 90. ]],
[[ 5.75, 6.25],
[35. , 45. ],
[36. , 42. ],
[60. , 90. ]],
[[20. , 30. ],
[11.5 , 12.5 ],
[52.5 , 67.5 ],
[36. , 42. ]]])
正如@AhmedMohamedAEK 所建議的那樣,以下列方式實施 numba 是否正確?
from numba import njit, prange
@njit(parallel=True)
def matrix_C(Q,S,gamma,n,o,p,m):
C = np.zeros((n,o,p))
for i in prange(n):
for j in prange(o):
for u in prange(m-1):
if np.isin(i,S[j][u]):
C[i,j,:] = Q[j,:] * gamma[:,u 1]
return C
C = matrix_C(Q,S,gamma,n,o,p,m)
uj5u.com熱心網友回復:
除了 Python 回圈之外,特別傷害您的是np.isin最內層回圈中的線性時間查找。以多一點空間為代價,這個問題很容易解決。我們可以創建只指數i, j, u,其中i是S[j][u]的,所以我們并不需要以后進行搜索為他們做。
這是通過以下代碼實作的,創建索引生成器R。運算式有點長,但不難理解。
R = ((i, j, u)
for j in range(o)
for u in range(m - 1)
for i in S[j][u]
if i < n)
這個索引生成器簡化C了很多的計算:
for i, j, u in R:
C[i, j] = Q[j] * gamma[:, u 1]
由于現在完全避免了許多作業,因此這應該比您的初始實施快得多。
完整代碼:
def matrix_C(Q, S, gamma, n, o, p, m):
C = np.zeros((n, o, p))
R = ((i, j, u)
for j in range(o)
for u in range(m - 1)
for i in S[j][u]
if i < n)
for i, j, u in R:
C[i, j] = Q[j] * gamma[:, u 1]
return C
其他想法
這種實施可以進一步加快。僅使用 的最后一個/最大值
u來創建C[i, j]- 由于 的較低值而導致的較早條目u被覆寫。你能想出一種方法,你可以u在構建時立即確定這個最大R嗎?Q[j] * gamma[:, u 1]提前計算每個jand可能是值得的u,并且只在設定 的值時執行查找C[i, j]。
uj5u.com熱心網友回復:
您可以洗掉 k 中的回圈,因為它被所有陣列使用,如下所示:
# # Matrix C_ijk
C = np.zeros((n,o,p))
for i in range(n):
for j in range(o):
for u in range(m-1):
if np.isin(i,S[j][u]):
C[i,j,:] = Q[j,:] * gamma[:,u 1]
然而,非常不鼓勵在 python 中使用嵌套回圈,回圈應該使用外部模塊移動到 C 端,可以使用cython 或numba創建。
編輯: 對于上面的 numba 實作,如果陣列非常大(幾 MB 大小),那么您可以使用并行實作,如下所示:
from numba import njit, prange
@njit(parallel=True)
def matrix_C(Q,S,gamma,n,o,p,m):
C = np.zeros((n,o,p))
for i in prange(n):
for j in range(o):
for k in range(p):
for u in range(m-1):
if np.isin(i,S[j][u]):
C[i,j,k] = Q[j,k] * gamma[k,u 1]
return C
C = matrix_C(Q,S,gamma,n,o,p,m)
但是,如果陣列相對較小,那么我只需洗掉 parallel 和 prange 并使用以下內容:
@njit()
def matrix_C(Q,S,gamma,n,o,p,m):
C = np.zeros((n,o,p))
for i in range(n):
for j in range(o):
for k in range(p):
for u in range(m-1):
if np.isin(i,S[j][u]):
C[i,j,k] = Q[j,k] * gamma[k,u 1]
return C
C = matrix_C(Q,S,gamma,n,o,p,m)
并記住第一次呼叫該函式是在編譯時,所以它會有點慢,但進一步呼叫會很快。
uj5u.com熱心網友回復:
你可以使用np.fromfunction
C = np.fromfunction(function=f, shape=(n, o, p))
其中 f 是
def f(i, j, k):
...
# your logic here
...
return value
python for 回圈很慢(請參閱此處),因此效率更高(至少對于外部 3 個回圈)
祝你好運
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/369821.html
