2017-11-26 27 views
5

Tôi có hai mã mảnh. Người đầu tiên là:Đối số `out` trong` numpy.einsum` không thể hoạt động như mong đợi

A = np.arange(3*4*3).reshape(3, 4, 3) 
P = np.arange(1, 4) 
A[:, 1:, :] = np.einsum('j, ijk->ijk', P, A[:, 1:, :]) 

và kết quả A là:

array([[[ 0, 1, 2], 
     [ 6, 8, 10], 
     [ 18, 21, 24], 
     [ 36, 40, 44]], 

     [[ 12, 13, 14], 
     [ 30, 32, 34], 
     [ 54, 57, 60], 
     [ 84, 88, 92]], 

     [[ 24, 25, 26], 
     [ 54, 56, 58], 
     [ 90, 93, 96], 
     [132, 136, 140]]]) 

Điều thứ hai là:

A = np.arange(3*4*3).reshape(3, 4, 3) 
P = np.arange(1, 4) 
np.einsum('j, ijk->ijk', P, A[:, 1:, :], out=A[:,1:,:]) 

và kết quả A là:

array([[[ 0, 1, 2], 
     [ 0, 0, 0], 
     [ 0, 0, 0], 
     [ 0, 0, 0]], 

     [[12, 13, 14], 
     [ 0, 0, 0], 
     [ 0, 0, 0], 
     [ 0, 0, 0]], 

     [[24, 25, 26], 
     [ 0, 0, 0], 
     [ 0, 0, 0], 
     [ 0, 0, 0]]]) 

Vì vậy, kết quả tôi khác nhau. Ở đây tôi muốn sử dụng out để tiết kiệm bộ nhớ. Đó có phải là lỗi trong số numpy.einsum không? Hoặc tôi đã bỏ lỡ một cái gì đó?

Nhân tiện, phiên bản numpy của tôi là 1.13.3.

Trả lời

4

Tôi chưa sử dụng thông số out mới này trước đây, nhưng đã làm việc với einsum trước đây và có ý tưởng chung về cách hoạt động (hoặc ít nhất được sử dụng).

Dường như với tôi như nó khởi tạo mảng out về 0 trước khi bắt đầu lặp lại. Điều đó sẽ giải thích cho tất cả các số 0 trong khối A[:,1:,:]. Nếu thay vào đó tôi ban đầu out mảng riêng biệt, các giá trị mong muốn được chèn

In [471]: B = np.ones((3,4,3),int) 
In [472]: np.einsum('j, ijk->ijk', P, A[:, 1:, :], out=B[:,1:,:]) 
Out[472]: 
array([[[ 3, 4, 5], 
     [ 12, 14, 16], 
     [ 27, 30, 33]], 

     [[ 15, 16, 17], 
     [ 36, 38, 40], 
     [ 63, 66, 69]], 

     [[ 27, 28, 29], 
     [ 60, 62, 64], 
     [ 99, 102, 105]]]) 
In [473]: B 
Out[473]: 
array([[[ 1, 1, 1], 
     [ 3, 4, 5], 
     [ 12, 14, 16], 
     [ 27, 30, 33]], 

     [[ 1, 1, 1], 
     [ 15, 16, 17], 
     [ 36, 38, 40], 
     [ 63, 66, 69]], 

     [[ 1, 1, 1], 
     [ 27, 28, 29], 
     [ 60, 62, 64], 
     [ 99, 102, 105]]]) 

Các Python phần einsum không cho tôi biết nhiều, ngoại trừ nó như thế nào quyết định để vượt qua các mảng out với phần c, (là một trong những danh sách các tmp_operands):

c_einsum (einsum_str, * tmp_operands, ** einsum_kwargs)

tôi biết rằng nó thiết lập một tương đương c-api của np.nditer, sử dụng str để xác định các trục và các lần lặp.

Nó lặp cái gì đó như phần này trong hướng dẫn lặp:

https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.nditer.html#reduction-iteration

Thông báo đặc biệt là bước it.reset(). Điều đó đặt bộ đệm out thành 0 trước khi lặp lại. Sau đó nó lặp qua các phần tử của mảng đầu vào và mảng đầu ra, ghi các giá trị tính toán vào phần tử đầu ra. Vì nó đang thực hiện tổng sản phẩm (ví dụ: out[:] += ...), nó phải bắt đầu bằng phương tiện chặn sạch.

Tôi đoán một chút về những gì đang thực sự xảy ra, nhưng có vẻ hợp lý với tôi rằng nó không nên ra bộ đệm đầu ra để bắt đầu. Nếu mảng đó giống với một trong các đầu vào, điều đó sẽ kết thúc với việc tính toán.

Vì vậy, tôi không nghĩ rằng phương pháp này sẽ hoạt động và giúp bạn tiết kiệm bộ nhớ. Nó cần một bộ đệm sạch để tích lũy kết quả. Khi đã xong, bạn có thể viết các giá trị trở lại vào A. Nhưng với bản chất của một sản phẩm như dot, bạn không thể sử dụng cùng một mảng cho đầu vào và đầu ra.

In [476]: A[:,1:,:] = np.einsum('j, ijk->ijk', P, A[:, 1:, :]) 
In [477]: A 
Out[477]: 
array([[[ 0, 1, 2], 
     [ 3, 4, 5], 
     [ 12, 14, 16], 
     [ 27, 30, 33]], 
     ....) 
+0

hpaulj, @ely một trong hai bạn biết liệu đây có phải là (hoặc phải là) theo bất kỳ cách nào liên quan đến _ mới này "Kiểm tra hoạt động tại chỗ nếu đầu vào chồng chéo đầu ra và tạo thời gian để tránh vấn đề." _ Tính năng trong [numpy1.13 ] (https://docs.scipy.org/doc/numpy/release.html)? –

+1

@PaulPanzer, tôi nghi ngờ từ các lưu ý phát hành rằng có một sự thay đổi trong xử lý '+ =' như hoạt động, và liên quan 'ufunc'. 'np.add (fib [: - 2], fib [1: -1], out = fib [2:])', https://stackoverflow.com/questions/47427603/recurrence-with-numpy, mang lại sự khác biệt kết quả dtype và phiên bản (12 v 13). Trong '13' có vẻ như toàn bộ mảng được đệm và không có đệ quy. Nhưng ngay cả với tham số 'out',' einsum' không phải là 'ufunc'. – hpaulj

3

Trong mã nguồn C cho einsum, there is a section sẽ đưa mảng theo quy định của out và làm một số zero-thiết lập.

Nhưng trong ví dụ Python source code, có các đường dẫn thực thi gọi hàm tensordot trước khi giảm dần các đối số để gọi c_einsum.

Điều này có nghĩa rằng một số hoạt động có thể được tính toán trước (do đó thay đổi mảng của bạn A trên một số co pass) với tensordot, trước bất kỳ phụ mảng là bao giờ thiết lập để không bởi zero-setter bên trong mã C cho einsum.

Một cách khác để đặt nó là: trên mỗi lần thực hiện các thao tác co lại tiếp theo, NumPy có nhiều lựa chọn khả dụng cho nó. Để sử dụng trực tiếp tensordot mà không cần nhập mã einsum cấp C? Hoặc để chuẩn bị các đối số và chuyển đến cấp độ C (mà sẽ liên quan đến việc ghi đè một số phụ của mảng đầu ra với tất cả các số không). Hoặc để sắp xếp lại các hoạt động và lặp lại kiểm tra?

Tùy thuộc vào thứ tự mà nó chọn cho các tối ưu hóa này, bạn có thể kết thúc với các mảng phụ tất cả 0 không mong đợi.

Đặt cược tốt nhất là không cố gắng thông minh và sử dụng cùng một mảng cho đầu ra. Bạn nói đó là vì bạn muốn tiết kiệm bộ nhớ. Có, trong một số trường hợp đặc biệt, thao tác einsum có thể có thể thực hiện được tại chỗ. Nhưng nó hiện không phát hiện nếu đây là trường hợp và cố gắng để tránh zero-thiết lập. Và trong một số lượng lớn các trường hợp, ghi đè lên một trong các mảng đầu vào ở giữa hoạt động tổng thể sẽ gây ra nhiều vấn đề, giống như cố gắng thêm vào danh sách bạn đang nối vòng trực tiếp, v.v.

Các vấn đề liên quan