หน่วยความจำและเวลาในการดำเนินการเทนเซอร์ของหลาม

เป้าหมาย เป้าหมายของฉันคือการคำนวณเทนเซอร์ที่กำหนดโดยสูตรที่คุณเห็นด้านล่าง ดัชนี i, j, k, l เริ่มจาก 0 ถึง 40 และ p, m, x จาก 0 ถึง 80

สูตรสำหรับเทนเซอร์

แนวทางเทนเซอร์ดอต ผลรวมนี้เป็นเพียงการหดตัวของดัชนีเทนเซอร์จำนวนมหาศาล 6 ดัชนี ฉันพยายามใช้เทนเซอร์ดอทซึ่งช่วยให้สามารถคำนวณได้ แต่ปัญหาของฉันก็คือหน่วยความจำ แม้ว่าฉันจะใช้เทนเซอร์จุดหนึ่งแล้วตามด้วยอีกจุดก็ตาม (ฉันทำงานใน colab ดังนั้นฉันจึงมี RAM 12GB)

การวนซ้ำแบบซ้อนเข้าใกล้ แต่มีสมมาตรเพิ่มเติมบางอย่างที่ควบคุมเมทริกซ์ B กล่าวคือ องค์ประกอบเดียวที่ไม่ใช่ศูนย์ของ B{ijpx} เท่านั้นที่ i+j= p+x ดังนั้นฉันจึงสามารถเขียน p และ m เป็นฟังก์ชันของ x (p=i+j-x, m=k+l-x) จากนั้นฉันก็ทำ 5 ลูปสำหรับ i,j,k,l,x แต่ในทางกลับกัน เวลาเป็นปัญหาเนื่องจากการคำนวณใช้เวลา 136 วินาที และฉันต้องการทำซ้ำหลายครั้ง

เป้าหมายการกำหนดเวลาในแนวทางลูปแบบซ้อน การลดเวลาลงสิบเท่าก็น่าจะน่าพอใจ แต่หากเป็นไปได้ที่จะลดเวลาลงสัก 100 เท่าก็เกินพอ

คุณมีความคิดใด ๆ ที่จะแก้ไขปัญหาหน่วยความจำหรือลดเวลาหรือไม่? คุณจะจัดการกับผลรวมดังกล่าวโดยมีข้อจำกัดเพิ่มเติมได้อย่างไร

(หมายเหตุ: เมทริกซ์ A มีความสมมาตร และฉันยังไม่ได้ใช้ข้อเท็จจริงนี้เลย ไม่มีความสมมาตรอีกต่อไป)

นี่คือโค้ดสำหรับการวนซ้ำแบบซ้อน:

for i in range (0,40):
  for j in range (0,40):
    for k in range (0,40):
      for l in range (0,40):
            Sum=0
            for x in range (0,80):
              p=i+j-x
              m=k+l-x
              if p>=0 and p<80 and m>=0 and m<80:
                Sum += A[p,m]*B[i,j,p,x]*B[k,l,m,x]
            T[i,j,k,l]= Sum

และโค้ดสำหรับแนวทางเทนเซอร์ดอท:

P=np.tensordot(A,B,axes=((0),(2)))
T=np.tensordot(P,B,axes=((0,3),(2,3)))

person Michal    schedule 29.01.2019    source แหล่งที่มา
comment
คุณสามารถแสดงความพยายามของคุณด้วย tensordot และลูปที่ซ้อนกัน เพื่อให้เราเข้าใจปัญหาได้ดีขึ้นหรือไม่   -  person jdehesa    schedule 29.01.2019
comment
คุณได้ลองใช้ tf.einsum แล้วหรือยัง ชอบ: t = tf.einsum('pm,ijpx,klmx->ijkl', a, b, b). ไม่แน่ใจว่าจะสร้างความแตกต่างกับเทนเซอร์ดอต แต่อาจลองดู   -  person jdehesa    schedule 29.01.2019
comment
คุณต้องการสิ่งนี้ใน TensorFlow หรือ NumPy ด้วยหรือไม่ ดูเหมือนว่ารหัสของคุณคือ NumPy แต่แท็กมี TensorFlow   -  person jdehesa    schedule 29.01.2019
comment
ฉันทำงานใน NumPy ขออภัยเกี่ยวกับแท็กที่ทำให้เข้าใจผิด ฉันอัปเดตคำถามเล็กน้อย และไม่ฉันไม่ได้ลองใช้วิธี tf.einsum   -  person Michal    schedule 29.01.2019
comment
โอเค ขอบคุณสำหรับการอัปเดต มันก็เหมือนกันใน NumPy จริงๆ t = np.einsum('pm,ijpx,klmx->ijkl', a, b, b)   -  person jdehesa    schedule 29.01.2019
comment
เพื่อชี้แจงให้กระจ่างว่า einsum นั้นยาวกว่าลูปที่ซ้อนกันมาก เนื่องจากไม่ได้ใช้ข้อจำกัดเพิ่มเติม หลังจากรอนานกว่า 5 นาที ฉันก็ปิดกระบวนการนี้   -  person Michal    schedule 29.01.2019


คำตอบ (1)


Numba อาจเป็นทางออกที่ดีที่สุดของคุณที่นี่ ฉันรวบรวมฟังก์ชันนี้ตามรหัสของคุณ ฉันเปลี่ยนแปลงเล็กน้อยเพื่อหลีกเลี่ยงการทำซ้ำที่ไม่จำเป็นและบล็อก if:

import numpy as np
import numba as nb

@nb.njit(parallel=True)
def my_formula_nb(A, B):
    di, dj, dx, _ = B.shape
    T = np.zeros((di, dj, di, dj), dtype=A.dtype)
    for i in nb.prange (di):
        for j in nb.prange (dj):
            for k in nb.prange (di):
                for l in nb.prange (dj):
                    sum = 0
                    x_start = max(0, i + j - dx + 1, k + l - dx + 1)
                    x_end = min(dx, i + j + 1, k + l + 1)
                    for x in range(x_start, x_end):
                        p = i + j - x
                        m = k + l - x
                        sum += A[p, m] * B[i, j, p, x] * B[k, l, m, x]
                    T[i, j, k, l] = sum
    return T

มาดูกันในทางปฏิบัติ:

import numpy as np

def make_problem(di, dj, dx):
    a = np.random.rand(dx, dx)
    a = a + a.T
    b = np.random.rand(di, dj, dx, dx)
    b_ind = np.indices(b.shape)
    b_mask = b_ind[0] + b_ind[1] != b_ind[2] + b_ind[3]
    b[b_mask] = 0
    return a, b

# Generate a problem
np.random.seed(100)
a, b = make_problem(15, 20, 25)
# Solve with Numba function
t1 = my_formula_nb(a, b)
# Solve with einsum
t2 = np.einsum('pm,ijpx,klmx->ijkl', a, b, b)
# Check result
print(np.allclose(t1, t2))
# True

# Benchmark (IPython)
%timeit np.einsum('pm,ijpx,klmx->ijkl', a, b, b)
# 4.5 s ± 39.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit my_formula_nb(a, b)
# 6.06 ms ± 20.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

อย่างที่คุณเห็น โซลูชัน Numba นั้นเร็วกว่าประมาณสามเท่า และไม่ควรใช้หน่วยความจำเกินความจำเป็น

person jdehesa    schedule 29.01.2019
comment
ขอบคุณมาก!! :) รหัสของคุณทำงานได้อย่างยอดเยี่ยม ระยะเวลาลดลง 100 เท่า ซึ่งเป็นสวรรค์แห่งจังหวะเวลาของฉัน ฉันเพิ่งแก้ไขบรรทัด di, dj, dx, _ = B.shape ให้เป็น di, dj, dx, _ = (40,40,80,80) เนื่องจากเทนเซอร์เอาต์พุตควรจะมีทั้งหมดสำหรับขนาดขนาด 40 - person Michal; 29.01.2019