บางครั้งคุณต้องเขียนโค้ด numpy ไม่ใช่สำนวนถ้าคุณจริงๆต้องการที่จะเพิ่มความเร็วในการคำนวณของคุณที่คุณไม่สามารถทำอะไรกับ numpy พื้นเมือง
numba
รวบรวมไพ ธ อนโค้ดของคุณไปยัง C ระดับต่ำเนื่องจากจำนวนของตัวเองจำนวนมากมักจะเร็วเท่ากับ C ซึ่งส่วนใหญ่แล้วจะมีประโยชน์หากปัญหาของคุณไม่ได้ทำให้ตัวเองกลายเป็น vectorization ดั้งเดิมที่มีจำนวนมาก นี่คือตัวอย่างหนึ่ง (โดยที่ฉันสันนิษฐานว่าดัชนีนั้นต่อเนื่องกันและเรียงลำดับซึ่งสะท้อนออกมาในข้อมูลตัวอย่างด้วย):
import numpy as np
import numba
# use the inflated example of roganjosh https://stackoverflow.com/a/58788534
data = [1.00, 1.05, 1.30, 1.20, 1.06, 1.54, 1.33, 1.87, 1.67]
index = [0, 0, 1, 1, 1, 1, 2, 3, 3]
data = np.array(data * 500) # using arrays is important for numba!
index = np.sort(np.random.randint(0, 30, 4500))
# jit-decorate; original is available as .py_func attribute
@numba.njit('f8[:](f8[:], i8[:])') # explicit signature implies ahead-of-time compile
def diffmedian_jit(data, index):
res = np.empty_like(data)
i_start = 0
for i in range(1, index.size):
if index[i] == index[i_start]:
continue
# here: i is the first _next_ index
inds = slice(i_start, i) # i_start:i slice
res[inds] = data[inds] - np.median(data[inds])
i_start = i
# also fix last label
res[i_start:] = data[i_start:] - np.median(data[i_start:])
return res
และนี่คือเวลาที่ใช้%timeit
เวทมนต์ของ IPython :
>>> %timeit diffmedian_jit.py_func(data, index) # non-jitted function
... %timeit diffmedian_jit(data, index) # jitted function
...
4.27 ms ± 109 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
65.2 µs ± 1.01 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
การใช้ข้อมูลตัวอย่างที่อัปเดตในคำถามตัวเลขเหล่านี้ (เช่นรันไทม์ของฟังก์ชัน python เทียบกับรันไทม์ของฟังก์ชัน JIT ที่เร่งความเร็ว) คือ
>>> %timeit diffmedian_jit.py_func(data, groups)
... %timeit diffmedian_jit(data, groups)
2.45 s ± 34.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
93.6 ms ± 518 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
จำนวนนี้เป็น 65x speedup ในเคสขนาดเล็กและ 26x speedup ในเคสที่ใหญ่กว่า (เมื่อเทียบกับโค้ด loopy แบบช้า) โดยใช้โค้ดเร่งความเร็ว ข้อดีอีกอย่างคือ (ไม่เหมือนกับ vectorization ทั่วไปกับ native numpy) เราไม่ต้องการหน่วยความจำเพิ่มเติมเพื่อให้ได้ความเร็วนี้มันคือทั้งหมดที่เกี่ยวกับการปรับปรุงและคอมไพล์โค้ดระดับต่ำที่จบลงด้วยการทำงาน
ฟังก์ชั่นดังกล่าวข้างต้นถือว่าอาร์เรย์ numpy int เป็นint64
ค่าเริ่มต้นซึ่งไม่ได้เป็นกรณีใน Windows ทางเลือกอื่นคือการลบลายเซ็นออกจากการเรียกไปยังnumba.njit
ทำให้การคอมไพล์แบบทันเวลาพอดี แต่นี่หมายความว่าฟังก์ชั่นจะถูกรวบรวมระหว่างการประมวลผลครั้งแรกซึ่งสามารถเข้าไปยุ่งกับผลลัพธ์เวลา (เราสามารถดำเนินการฟังก์ชั่นครั้งเดียวด้วยตนเองโดยใช้ประเภทข้อมูลตัวแทนหรือเพียงยอมรับว่าการดำเนินการเวลาแรกจะช้ากว่ามาก ถูกละเว้น) นี่คือสิ่งที่ฉันพยายามป้องกันโดยการระบุลายเซ็นซึ่งเป็นต้นเหตุของการรวบรวมล่วงหน้า
อย่างไรก็ตามในกรณี JIT อย่างถูกต้องมัณฑนากรที่เราต้องการนั้นเป็นเพียง
@numba.njit
def diffmedian_jit(...):
โปรดทราบว่าการกำหนดเวลาด้านบนที่ฉันแสดงสำหรับฟังก์ชั่นการคอมไพล์ jit จะใช้งานได้เมื่อฟังก์ชั่นได้รับการรวบรวม สิ่งนี้อาจเกิดขึ้นตามคำจำกัดความ (ด้วยการรวบรวมความกระตือรือร้นเมื่อมีการส่งผ่านลายเซ็นอย่างชัดเจนไปยังnumba.njit
) หรือระหว่างการเรียกใช้ฟังก์ชันแรก (ด้วยการรวบรวมขี้เกียจเมื่อไม่มีการส่งผ่านลายเซ็นไปยังnumba.njit
) หากฟังก์ชั่นจะถูกดำเนินการเพียงครั้งเดียวแล้วเวลาในการรวบรวมควรพิจารณาด้วยความเร็วของวิธีนี้ โดยทั่วไปแล้วมันเป็นเพียงฟังก์ชั่นการรวบรวมที่คุ้มค่าถ้าเวลารวมของการคอมไพล์ + การประมวลผลน้อยกว่ารันไทม์ที่ไม่ได้คอมไพล์ (ซึ่งจริงในกรณีข้างต้นซึ่งฟังก์ชันไพ ธ อนช้ามาก) สิ่งนี้ส่วนใหญ่เกิดขึ้นเมื่อคุณเรียกใช้ฟังก์ชันที่คอมไพล์แล้วหลายครั้ง
ในฐานะที่เป็นmax9111ตั้งข้อสังเกตในความคิดเห็นหนึ่งคุณลักษณะที่สำคัญของnumba
เป็นcache
คำหลักjit
ที่จะ การส่งผ่านcache=True
ไปยังnumba.jit
จะเก็บฟังก์ชั่นที่คอมไพล์ไปยังดิสก์ดังนั้นในระหว่างการดำเนินการครั้งต่อไปของโมดูลหลามที่กำหนดฟังก์ชั่นจะถูกโหลดจากที่นั่นแทนที่จะทำการคอมไพล์ซ้ำอีกครั้ง
scipy.ndimage.median
ข้อเสนอแนะในคำตอบที่เชื่อมโยง? สำหรับฉันมันไม่ได้ดูเหมือนว่าจะต้องมีองค์ประกอบจำนวนเท่ากันต่อฉลาก หรือฉันคิดถึงอะไรบางอย่าง?