Làm thế nào để diễn tả biểu thức phức tạp này bằng cách sử dụng các lát numpy


14

Tôi muốn triển khai biểu thức sau trong Python: trong đó và là các mảng numpy có kích thước nk là một mảng numpy có kích thước n \ lần n . Kích thước n có thể lên tới khoảng 10000 và chức năng là một phần của vòng lặp bên trong sẽ được đánh giá nhiều lần, vì vậy tốc độ rất quan trọng.x

xi=j=1i1kij,jaijaj,
xynkn×nn

Lý tưởng nhất là tôi muốn tránh một vòng lặp hoàn toàn, mặc dù tôi đoán đó không phải là ngày tận thế nếu có. Vấn đề là tôi gặp khó khăn khi xem cách thực hiện mà không có một vài vòng lặp lồng nhau, và điều đó có khả năng làm cho nó khá chậm.

Bất cứ ai cũng có thể thấy cách diễn đạt phương trình trên bằng cách sử dụng numpy theo cách hiệu quả và tốt nhất là cũng có thể đọc được? Tổng quát hơn, cách tốt nhất để tiếp cận loại điều này là gì?


Tôi đã có một câu hỏi tương tự một vài ngày trước. Tôi hỏi nó tại stackoverflow. Kiểm tra bài này . Tôi sử dụng scipy.weave thay vì cython. Có ai biết nếu điều này làm cho bất kỳ (đáng kể) hiệu suất khác biệt?
seb

Câu trả lời:


17

Đây là giải pháp Numba. Trên máy của tôi, phiên bản Numba nhanh hơn 1000 lần so với phiên bản python mà không có bộ trang trí (đối với ma trận 200x200, 'k' và vectơ dài 200 'a'). Bạn cũng có thể sử dụng trình trang trí @autojit, thêm khoảng 10 micro giây cho mỗi cuộc gọi để cùng một mã sẽ hoạt động với nhiều loại.

from numba import jit, autojit

@jit('f8[:](f8[:,:],f8[:])')
#@autojit
def looped_ver(k, a):
    x = np.empty_like(a)
    for i in range(x.size):
        sm = 0.0
        for j in range(0, i+1):
            sm += k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

Tiết lộ: Tôi là một trong những nhà phát triển Numba.


Cảm ơn, điều đó có vẻ khá đơn giản. Tôi thậm chí còn không biết về Numba! Cython, PyPy, Numba ... đó là một thế giới khó hiểu.
Nathaniel

3
Travis, rất tuyệt, bạn có phiền khi thêm một tiết lộ vào cuối câu trả lời của bạn rằng bạn là một trong những nhà phát triển tê không?
Aron Ahmadia

1
n=200

@NatWilson - nếu bạn hỏi đây là một câu hỏi trên scicomp, tôi rất vui lòng thử và giải quyết nó cho bạn :)
Aron Ahmadia

4

Đây là một khởi đầu. Đầu tiên, tôi xin lỗi vì bất kỳ sai lầm.

ii1

Chỉnh sửa: Không, giới hạn trên là chính xác như được cung cấp trong câu hỏi. Tôi đã để nó như ở đây vì một câu trả lời khác hiện sử dụng cùng một mã, nhưng cách khắc phục rất đơn giản.

Đầu tiên là một phiên bản lặp:

def looped_ver(k, a):
    x = np.empty_like(a)
    for i in range(x.size):
        sm = 0
        for j in range(0, i+1):
            sm += k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

Tôi đã làm cho nó một vòng lặp duy nhất với các lát numpy:

def vectorized_ver(k, a):
    ktr = zeros_like(k)
    ar = zeros_like(k)
    sz = len(a)
    for i in range(sz):
        ktr[i,:i+1] = k[::-1].diagonal(-sz+i+1)
        a_ = a[:i+1]
        ar[i,:i+1] = a_[::-1] * a_
    return np.sum(ktr * ar, 1)

n=5000

Sau đó, tôi đã viết một phiên bản Cython của mã vòng lặp (dễ đọc hơn).

import numpy as np
import cython
cimport numpy as np

@cython.boundscheck(False)
@cython.wraparound(False)
def cyth_ver(double [:, ::1] k not None,
              double [:] a not None):
    cdef double[:] x = np.empty_like(a)
    cdef double sm
    cdef int i, j

    for i in range(len(a)):
        sm = 0.0
        for j in range(i+1):
            sm = sm + k[i-j,j] * a[i-j] * a[j]
        x[i] = sm
    return x

Trên máy tính xách tay của tôi, phiên bản này nhanh hơn khoảng 200 lần so với phiên bản vòng lặp (và nhanh hơn 8 lần so với phiên bản véc tơ 1 vòng). Tôi chắc rằng những người khác có thể làm tốt hơn.

Tôi đã chơi với một phiên bản Julia và dường như (nếu tôi hẹn giờ đúng) có thể so sánh với mã Cython.


x0Tôi-1

Ah tôi thấy. Tôi đã thu thập nó từ tổng kết ban đầu, nhưng không chắc đó là ý định.
Nat Wilson

1

Những gì bạn muốn dường như là một tổ hợp; Tôi nghĩ rằng cách nhanh nhất để đạt được nó sẽ là numpy.convolvechức năng.

Bạn có thể phải sửa các chỉ số theo nhu cầu chính xác của mình nhưng tôi nghĩ bạn muốn thử một cái gì đó như:

import numpy as np
a = [1, 2, 3, 4, 5]
k = [2, 4, 6, 8, 10]

result = np.convolve(a, k*a[::-1])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.