วิธีการแสดงออกที่ซับซ้อนนี้โดยใช้ชิ้นส่วนที่เป็นก้อน


14

ฉันต้องการใช้นิพจน์ต่อไปนี้ใน Python: ที่xและyเป็นอาร์เรย์ numpy ขนาดnและkเป็นอาร์เรย์ numpy ขนาดn × n ขนาดnอาจสูงถึงประมาณ 10,000 และฟังก์ชั่นเป็นส่วนหนึ่งของลูปภายในที่จะถูกประเมินหลายครั้งดังนั้นความเร็วจึงมีความสำคัญ

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

เป็นการดีที่ฉันต้องการหลีกเลี่ยงการวนซ้ำไปมาโดยสิ้นเชิง แต่ฉันคิดว่ามันไม่ใช่จุดจบของโลกถ้ามี ปัญหาคือฉันมีปัญหาในการดูวิธีการทำโดยไม่ต้องมีลูปซ้อนกันสองสามครั้งและมีแนวโน้มที่จะทำให้มันค่อนข้างช้า

ใครสามารถดูวิธีการแสดงสมการข้างต้นโดยใช้ numpy ในวิธีที่มีประสิทธิภาพและควรอ่านด้วยหรือไม่ โดยทั่วไปแล้ววิธีที่ดีที่สุดในการเข้าถึงของประเภทนี้คืออะไร


ฉันมีคำถามที่คล้ายกันสองสามวันที่ผ่านมา ฉันถามไปที่ stackoverflow ลองดูโพสต์นี้ ฉันใช้ scipy.weave แทน cython มีใครทราบบ้างไหมว่าสิ่งนี้ทำให้เกิดความแตกต่างด้านประสิทธิภาพ (อย่างมาก) หรือไม่?
seb

คำตอบ:


17

นี่คือทางออกของ Numba บนเครื่องของฉันรุ่น Numba นั้นเร็วกว่ารุ่น Python มากกว่า 1000 เท่าโดยไม่มีมัณฑนากร (สำหรับเมทริกซ์ 200x200, 'k' และเวกเตอร์ความยาว 200 'a') คุณยังสามารถใช้ @autojit decorator ซึ่งเพิ่มประมาณ 10 microseconds ต่อการโทรหนึ่งครั้งเพื่อให้รหัสเดียวกันสามารถใช้งานได้หลายประเภท

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

การเปิดเผยข้อมูล: ฉันเป็นหนึ่งในนักพัฒนา Numba


ขอบคุณที่ดูตรงไปตรงมา ฉันไม่รู้ด้วยซ้ำเกี่ยวกับ numba! Cython, PyPy, Numba ... มันเป็นโลกที่สับสน
นาธาเนียล

3
เทรวิสเจ๋งมากคุณคิดว่าจะเพิ่มการเปิดเผยที่ด้านล่างของคำตอบของคุณว่าคุณเป็นหนึ่งในนักพัฒนา Numba หรือไม่?
Aron Ahmadia

1
n=200

@NatWilson - หากคุณถามคำถามนี้เป็นคำถามเกี่ยวกับ scicomp ฉันยินดีที่จะลองและแก้ไขปัญหาให้กับคุณ :)
Aron Ahmadia

4

นี่เป็นการเริ่มต้น ก่อนอื่นฉันขอโทษสำหรับข้อผิดพลาดใด ๆ

ผมผม-1

แก้ไข: ไม่ขีด จำกัด บนถูกต้องตามที่ให้ไว้ในคำถาม ฉันได้ทิ้งไว้เหมือนที่นี่เพราะคำตอบอื่นตอนนี้ใช้รหัสเดียวกัน แต่การแก้ไขนั้นง่าย

รุ่นแรกที่วนซ้ำ:

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

ฉันทำให้มันเป็นวงเดียวกับชิ้น 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

จากนั้นฉันเขียนโค้ดวนลูปรุ่น Cython (อ่านง่ายขึ้น)

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

บนแล็ปท็อปของฉันอันนี้เร็วกว่ารุ่นลูปประมาณ 200x (และเร็วกว่าเวอร์ชันเวกเตอร์ 1 ลูป 8 เท่า) ฉันแน่ใจว่าคนอื่นสามารถทำได้ดีกว่า

ฉันเล่นกับรุ่น Julia และดูเหมือนว่า (ถ้าฉันจับเวลาถูกต้อง) เทียบได้กับรหัส Cython


x0i1

อ่าฉันเข้าใจแล้ว ฉันรวบรวมสิ่งนั้นจากการรวมต้นฉบับ แต่ไม่แน่ใจว่านั่นคือเจตนา
แน็ตวิลสัน

1

สิ่งที่คุณต้องการดูเหมือนจะเป็นการโน้มน้าวใจ; ฉันคิดว่าวิธีที่เร็วที่สุดเพื่อให้บรรลุผลนั้นจะเป็นnumpy.convolveหน้าที่

คุณอาจต้องแก้ไขดัชนีตามที่คุณต้องการ แต่ฉันคิดว่าคุณอยากลองสิ่งต่อไปนี้:

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

result = np.convolve(a, k*a[::-1])
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.