Tôi nhận được một số kết quả kiểm tra hiệu quả mà tôi không thể giải thích.Tại sao B = numpy.dot (A, x) lặp đi lặp lại chậm hơn nhiều khi thực hiện B [i,:,:] = numpy.dot (A [i,:,:], x))?
Tôi muốn lắp ráp ma trận B có mục nhập thứ i B [i,:,:] = A [i,:,:]. Chấm (x), trong đó mỗi A [i,:,:] là một ma trận 2D, và như vậy là x.
Tôi có thể thực hiện ba cách này, để kiểm tra hiệu suất tôi tạo ngẫu nhiên (numpy.random.randn
) ma trận A = (10,1000,1000), x = (1000,1200). Tôi có được kết quả thời gian sau:
(1) đơn đa chiều dot sản phẩm
B = A.dot(x)
total time: 102.361 s
(2) vòng lặp thông qua i và thực hiện các sản phẩm chấm 2D
# initialize B = np.zeros([dim1, dim2, dim3])
for i in range(A.shape[0]):
B[i,:,:] = A[i,:,:].dot(x)
total time: 0.826 s
(3) numpy.einsum
B3 = np.einsum("ijk, kl -> ijl", A, x)
total time: 8.289 s
Vì vậy, tùy chọn (2) là nhanh nhất cho đến nay. Nhưng, chỉ xem xét (1) và (2), tôi không thấy sự khác biệt lớn giữa chúng. Làm thế nào có thể lặp đi lặp lại và làm các sản phẩm chấm 2D nhanh hơn ~ 124 lần? Cả hai đều sử dụng numpy.dot. Bất kỳ thông tin chi tiết nào?
tôi bao gồm các mã được sử dụng cho các kết quả trên chỉ dưới đây:
import numpy as np
import numpy.random as npr
import time
dim1, dim2, dim3 = 10, 1000, 1200
A = npr.randn(dim1, dim2, dim2)
x = npr.randn(dim2, dim3)
# consider three ways of assembling the same matrix B: B1, B2, B3
t = time.time()
B1 = np.dot(A,x)
td1 = time.time() - t
print "a single dot product of A [shape = (%d, %d, %d)] with x [shape = (%d, %d)] completes in %.3f s" \
% (A.shape[0], A.shape[1], A.shape[2], x.shape[0], x.shape[1], td1)
B2 = np.zeros([A.shape[0], x.shape[0], x.shape[1]])
t = time.time()
for i in range(A.shape[0]):
B2[i,:,:] = np.dot(A[i,:,:], x)
td2 = time.time() - t
print "taking %d dot products of 2D dot products A[i,:,:] [shape = (%d, %d)] with x [shape = (%d, %d)] completes in %.3f s" \
% (A.shape[0], A.shape[1], A.shape[2], x.shape[0], x.shape[1], td2)
t = time.time()
B3 = np.einsum("ijk, kl -> ijl", A, x)
td3 = time.time() - t
print "using np.einsum, it completes in %.3f s" % td3
'dấu chấm' thực hiện nhiều điều dưới sự che chở, rõ ràng là' np.dot (A, x) 'không gọi BLAS và bằng cách nào đó là mặc định đối với thường trình GEMM bên trong của numpy. Ví dụ reshape của bạn là bỏ qua cơ chế looping và đi thẳng đến một cuộc gọi GEMM 2D thông thường mà không cần sao chép bất kỳ dữ liệu nào, nó sẽ luôn là giải pháp nhanh nhất cho các vấn đề có kích thước hợp lý cho một BLAS tốt. Trên máy tính xách tay của tôi với MKL của nó ~ 50 lần nhanh hơn einsum cho các vấn đề kích thước ban đầu. – Daniel
'tensordot' đang thực hiện cùng một định dạng lại. – hpaulj
Vâng tensordot buộc một bản sao của dữ liệu nội bộ, tôi sẽ không khuyên bạn nên tùy chọn này. – Daniel