Memori dan waktu dalam operasi tensor python

Tujuan Tujuan saya adalah menghitung tensor yang diberikan oleh rumus yang dapat Anda lihat di bawah. Indeks i, j, k, l berkisar dari 0 hingga 40 dan p, m, x dari 0 hingga 80.

Rumus tensor

Pendekatan Tensordot Penjumlahan ini hanya mengontrak 6 indeks tensor yang sangat besar. Saya mencoba melakukannya dengan titik tensor yang memungkinkan penghitungan seperti itu tetapi masalah saya adalah memori meskipun saya melakukan satu titik tensor diikuti oleh titik lainnya. (Saya bekerja di colab jadi saya memiliki RAM 12GB)

Pendekatan loop bersarang Namun ada beberapa kesimetrian tambahan yang mengatur matriks B, yaitu satu-satunya elemen bukan nol dari B{ijpx} yang sedemikian rupa sehingga i+j= p+x. Oleh karena itu saya dapat menulis p dan m sebagai fungsi dari x (p=i+j-x, m=k+l-x)dan kemudian saya melakukan 5 loop yaitu untuk i,j,k,l,x tetapi kemudian di sisi lain masalahnya adalah waktunya karena perhitungannya memakan waktu 136 detik dan saya ingin mengulanginya berkali-kali.

Tujuan pengaturan waktu dalam pendekatan loop bersarang Pengurangan waktu sebanyak sepuluh kali lipat sudah cukup memuaskan, namun jika memungkinkan untuk menguranginya sebanyak 100 kali lipat, hal ini sudah lebih dari cukup.

Apakah Anda punya ide untuk mengatasi masalah memori atau mengurangi waktunya? Bagaimana Anda menangani penjumlahan tersebut dengan batasan tambahan?

(Keterangan: Matriks A simetris dan saya belum pernah menggunakan fakta ini sejauh ini. Tidak ada lagi kesimetrian.)

Berikut ini kode untuk loop bersarang:

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

Dan kode untuk pendekatan tensor dot:

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 sumber
comment
Bisakah Anda menunjukkan upaya Anda dengan tensordot dan dengan loop bersarang, sehingga kami mendapatkan gambaran yang lebih baik tentang masalahnya?   -  person jdehesa    schedule 29.01.2019
comment
Apakah Anda juga sudah mencoba dengan tf.einsum? Seperti: t = tf.einsum('pm,ijpx,klmx->ijkl', a, b, b). Tidak yakin itu akan membuat perbedaan dengan tensordot tetapi boleh mencobanya.   -  person jdehesa    schedule 29.01.2019
comment
Apakah Anda juga memerlukan ini di TensorFlow atau NumPy? Sepertinya kode Anda adalah NumPy tetapi tagnya menyertakan TensorFlow.   -  person jdehesa    schedule 29.01.2019
comment
Saya bekerja di NumPy, maaf tentang tag yang menyesatkan. Saya memperbarui pertanyaannya sedikit. Dan tidak, saya belum mencoba pendekatan tf.einsum.   -  person Michal    schedule 29.01.2019
comment
Oke terima kasih atas pembaruannya. Nah di NumPy juga sama kok, t = np.einsum('pm,ijpx,klmx->ijkl', a, b, b).   -  person jdehesa    schedule 29.01.2019
comment
Untuk memperjelas, einsum jauh lebih lama daripada loop bersarang karena tidak menggunakan batasan tambahan. Setelah lebih dari 5 menit menunggu, saya menghentikan prosesnya.   -  person Michal    schedule 29.01.2019


Jawaban (1)


Numba mungkin pilihan terbaik Anda di sini. Saya menyusun fungsi ini berdasarkan kode Anda. Saya mengubahnya sedikit untuk menghindari beberapa iterasi yang tidak perlu dan blok 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

Mari kita lihat aksinya:

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)

Seperti yang Anda lihat, solusi Numba tiga kali lipat lebih cepat, dan tidak memerlukan memori lebih dari yang diperlukan.

person jdehesa    schedule 29.01.2019
comment
Terima kasih banyak!! :) Kode Anda berfungsi dengan baik. Waktunya dikurangi dengan faktor 100 yang merupakan surga waktu saya. Saya baru saja mengedit baris di, dj, dx, _ = B.shape menjadi di, dj, dx, _ = (40,40,80,80) karena tensor keluaran seharusnya memiliki semuanya untuk dimensi ukuran 40. - person Michal; 29.01.2019