Multiprocessing: ใช้ tqdm เพื่อแสดงแถบความคืบหน้า


109

เพื่อให้โค้ดของฉันเป็น "ไพโธนิก" มากขึ้นและเร็วขึ้นฉันใช้ "มัลติโพรเซสเซอร์" และฟังก์ชันแผนที่เพื่อส่ง a) ฟังก์ชันและ b) ช่วงของการทำซ้ำ

โซลูชันที่ฝังไว้ (เช่นเรียก tqdm โดยตรงในช่วง tqdm.tqdm (ช่วง (0, 30)) ไม่ทำงานกับการประมวลผลหลายขั้นตอน (ตามสูตรในโค้ดด้านล่าง)

แถบความคืบหน้าจะแสดงตั้งแต่ 0 ถึง 100% (เมื่อ python อ่านรหัส?) แต่ไม่ได้ระบุความคืบหน้าจริงของฟังก์ชันแผนที่

จะแสดงแถบความคืบหน้าเพื่อระบุขั้นตอนที่ฟังก์ชัน 'แผนที่' เป็นอย่างไร?

from multiprocessing import Pool
import tqdm
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   p = Pool(2)
   r = p.map(_foo, tqdm.tqdm(range(0, 30)))
   p.close()
   p.join()

ยินดีให้ความช่วยเหลือหรือข้อเสนอแนะ ...


คุณสามารถโพสต์ข้อมูลโค้ดของแถบความคืบหน้าได้หรือไม่
Alex

2
สำหรับคนในการค้นหาวิธีการแก้ปัญหาที่มี.starmap(): นี่คือแพทช์สำหรับPoolการเพิ่มซึ่งจะทำงานร่วมกับ.istarmap() tqdm
Darkonaut

คำตอบ:


139

ใช้ imap แทน map ซึ่งส่งคืนตัววนซ้ำของค่าที่ประมวลผล

from multiprocessing import Pool
import tqdm
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   with Pool(2) as p:
      r = list(tqdm.tqdm(p.imap(_foo, range(30)), total=30))

14
คำสั่งปิดท้าย list () รอให้ตัววนซ้ำสิ้นสุด total = จำเป็นต้องใช้เนื่องจาก tqdm ไม่ทราบว่าการวนซ้ำจะนานแค่ไหน
hkyi

19
มีวิธีแก้ปัญหาที่คล้ายกันสำหรับstarmap()?
tarashypka

3
for i in tqdm.tqdm(...): pass อาจจะตรงไปตรงมามากกว่านั้นlist(tqdm.tqdm)
savfod

1
วิธีนี้ใช้งานได้ แต่มีใครพิมพ์แถบความคืบหน้าอย่างต่อเนื่องบนบรรทัดใหม่สำหรับการทำซ้ำแต่ละครั้งหรือไม่?
Dennis Subachev

3
ลักษณะการทำงานเป็นสายเมื่อที่เฉพาะเจาะจงของchunk_size p.imapสามารถtqdmอัปเดตการทำซ้ำทุกครั้งแทนทุกชิ้นได้หรือไม่
huangbiubiu

57

พบวิธีแก้ไข: ระวัง! เนื่องจากการประมวลผลหลายขั้นตอนเวลาในการประมาณค่า (การวนซ้ำต่อลูปเวลารวม ฯลฯ ) อาจไม่เสถียร แต่แถบความคืบหน้าทำงานได้อย่างสมบูรณ์

หมายเหตุ: Context manager for Pool พร้อมใช้งานจาก Python เวอร์ชัน 3.3 เท่านั้น

from multiprocessing import Pool
import time
from tqdm import *

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
    with Pool(processes=2) as p:
        max_ = 30
        with tqdm(total=max_) as pbar:
            for i, _ in enumerate(p.imap_unordered(_foo, range(0, max_))):
                pbar.update()

2
pbar.close()ไม่จำเป็นจะปิดโดยอัตโนมัติเมื่อสิ้นสุดwith
Sagar Kar

5
การtqdmโทรครั้งที่สอง / ภายในจำเป็นหรือไม่?
shadowtalker

7
แล้วผลลัพธ์ของ _foo (my_number) ที่ส่งคืนเป็น "r" จะเป็นอย่างไร
ลิเก

4
มีวิธีแก้ปัญหาที่คล้ายกันสำหรับstarmap()?
tarashypka

3
@shadowtalker - ดูเหมือนว่าจะใช้งานไม่ได้;) อย่างไรก็ตาม - imap_unorderedเป็นกุญแจสำคัญที่นี่จะให้ประสิทธิภาพที่ดีที่สุดและการประมาณแถบความคืบหน้าที่ดีที่สุด
Tomasz Gandor

35

ขออภัยที่มาช้า แต่หากคุณต้องการเพียงแผนที่พร้อมกันฉันได้เพิ่มฟังก์ชันนี้ในtqdm>=4.42.0:

from tqdm.contrib.concurrent import process_map  # or thread_map
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   r = process_map(_foo, range(0, 30), max_workers=2)

ข้อมูลอ้างอิง: https://tqdm.github.io/docs/contrib.concurrent/และhttps://github.com/tqdm/tqdm/blob/master/examples/parallel_bars.py

มันสนับสนุนmax_workersและchunksizeและคุณยังสามารถเปลี่ยนได้อย่างง่ายดายจากการprocess_mapthread_map


1
ขอบคุณสำหรับสิ่งนี้. ใช้งานได้ง่ายดีกว่าโซลูชันอื่น ๆ ที่ฉันเคยลอง
user3340499

เจ๋ง (+1) แต่พ่นHBox(children=(FloatProgress(value=0.0, max=30.0), HTML(value='')))ใน Jupyter
Ébe Isaac


ฉันพบปัญหาเกี่ยวกับการสนทนาเพื่อแฮ็ค tqdm_notebook อย่างไรก็ตามไม่สามารถใช้วิธีแก้ปัญหาสำหรับ tqdm.contrib.concurrent ได้
Ébe Isaac

นี่มันอัศจรรย์มาก. ใช้งานได้ทันทีจากกล่อง
Lars Larsson

23

คุณสามารถใช้p_tqdmแทน

https://github.com/swansonk14/p_tqdm

from p_tqdm import p_map
import time

def _foo(my_number):
   square = my_number * my_number
   time.sleep(1)
   return square 

if __name__ == '__main__':
   r = p_map(_foo, list(range(0, 30)))

1
pip installนี้ทำงานได้ดีมากและมันก็ง่ายมากที่จะ นี่กำลังแทนที่ tqdm สำหรับความต้องการส่วนใหญ่ของฉัน
crypdick

Merci Victor;)
Gabriel Romon

p_tqdmถูก จำกัด ไว้ที่multiprocessing.Poolไม่สามารถใช้ได้กับเธรด
pateheo

9

ขึ้นอยู่กับคำตอบของ Xavi imap_unordered_barMartínezผมเขียนฟังก์ชั่น สามารถใช้ในลักษณะเดียวimap_unorderedกับความแตกต่างเดียวที่แสดงแถบการประมวลผล

from multiprocessing import Pool
import time
from tqdm import *

def imap_unordered_bar(func, args, n_processes = 2):
    p = Pool(n_processes)
    res_list = []
    with tqdm(total = len(args)) as pbar:
        for i, res in tqdm(enumerate(p.imap_unordered(func, args))):
            pbar.update()
            res_list.append(res)
    pbar.close()
    p.close()
    p.join()
    return res_list

def _foo(my_number):
    square = my_number * my_number
    time.sleep(1)
    return square 

if __name__ == '__main__':
    result = imap_unordered_bar(_foo, range(5))

3
สิ่งนี้จะวาดแถบใหม่ในแต่ละขั้นตอนของบรรทัดใหม่ อัพไลน์เดิมทีไร
misantroop

วิธีแก้ไขในกรณีของฉัน (Windows / Powershell): Colorama
misantroop

'pbar.close () ไม่จำเป็นมันจะถูกปิดโดยอัตโนมัติเมื่อสิ้นสุดด้วย' เช่นเดียวกับความคิดเห็นที่ซาก้าตอบในคำตอบของ @ scipy
Tejas Shetty

1
import multiprocessing as mp
import tqdm


some_iterable = ...

def some_func():
    # your logic
    ...


if __name__ == '__main__':
    with mp.Pool(mp.cpu_count()-2) as p:
        list(tqdm.tqdm(p.imap(some_func, iterable), total=len(iterable)))

1

นี่คือสิ่งที่ฉันใช้เมื่อคุณต้องการรับผลลัพธ์กลับจากฟังก์ชันการทำงานแบบขนานของคุณ ฟังก์ชั่นนี้ทำบางสิ่งบางอย่าง (มีอีกโพสต์ของฉันที่อธิบายเพิ่มเติม) แต่ประเด็นสำคัญคือมีคิวงานที่รอดำเนินการและคิวงานที่เสร็จสมบูรณ์ เมื่อผู้ปฏิบัติงานเสร็จสิ้นในแต่ละงานในคิวที่รอดำเนินการจึงเพิ่มผลลัพธ์ในคิวงานที่เสร็จสมบูรณ์ คุณสามารถรวมเช็คเข้ากับคิวงานที่เสร็จสมบูรณ์ด้วยแถบความคืบหน้า tqdm ฉันไม่ได้วางการใช้งานฟังก์ชัน do_work () ที่นี่มันไม่เกี่ยวข้องเนื่องจากข้อความในที่นี้คือการตรวจสอบคิวงานที่เสร็จสมบูรณ์และอัปเดตแถบความคืบหน้าทุกครั้งที่มีผลลัพธ์

def par_proc(job_list, num_cpus=None, verbose=False):

# Get the number of cores
if not num_cpus:
    num_cpus = psutil.cpu_count(logical=False)

print('* Parallel processing')
print('* Running on {} cores'.format(num_cpus))

# Set-up the queues for sending and receiving data to/from the workers
tasks_pending = mp.Queue()
tasks_completed = mp.Queue()

# Gather processes and results here
processes = []
results = []

# Count tasks
num_tasks = 0

# Add the tasks to the queue
for job in job_list:
    for task in job['tasks']:
        expanded_job = {}
        num_tasks = num_tasks + 1
        expanded_job.update({'func': pickle.dumps(job['func'])})
        expanded_job.update({'task': task})
        tasks_pending.put(expanded_job)

# Set the number of workers here
num_workers = min(num_cpus, num_tasks)

# We need as many sentinels as there are worker processes so that ALL processes exit when there is no more
# work left to be done.
for c in range(num_workers):
    tasks_pending.put(SENTINEL)

print('* Number of tasks: {}'.format(num_tasks))

# Set-up and start the workers
for c in range(num_workers):
    p = mp.Process(target=do_work, args=(tasks_pending, tasks_completed, verbose))
    p.name = 'worker' + str(c)
    processes.append(p)
    p.start()

# Gather the results
completed_tasks_counter = 0

with tqdm(total=num_tasks) as bar:
    while completed_tasks_counter < num_tasks:
        results.append(tasks_completed.get())
        completed_tasks_counter = completed_tasks_counter + 1
        bar.update(completed_tasks_counter)

for p in processes:
    p.join()

return results

-3

วิธีนี้ง่ายและได้ผล

from multiprocessing.pool import ThreadPool
import time
from tqdm import tqdm

def job():
    time.sleep(1)
    pbar.update()

pool = ThreadPool(5)
with tqdm(total=100) as pbar:
    for i in range(100):
        pool.apply_async(job)
    pool.close()
    pool.join()
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.