Migliori pratiche di Torch.multiprocessing
Tuttavia, la memoria virtuale è solo un lato della storia. Cosa succede se il problema persiste dopo aver regolato il disco di scambio?
L'altro lato della storia riguarda i problemi di fondo del modulo torch.multiprocessing. Sulla pagina web ufficiale sono presenti numerosi consigli sulle migliori pratiche:
Ma oltre a questi, dovrebbero essere considerati altri tre approcci, soprattutto per quanto riguarda l’utilizzo della memoria.
La prima cosa è la perdita di memoria condivisa. Perdita significa che la memoria non viene rilasciata correttamente dopo ogni esecuzione del lavoratore figlio e osserverai questo fenomeno quando monitorerai l'utilizzo della memoria virtuale in fase di runtime. Il consumo di memoria continuerà ad aumentare e raggiungerà il punto di essere “senza memoria”. Questa è una tipica perdita di memoria.
Quindi cosa causerà la perdita?
Diamo un'occhiata alla classe DataLoader stessa:
https://github.com/pytorch/pytorch/blob/main/torch/utils/data/dataloader.py
Guardando sotto il cofano di DataLoader, vedremo che quando nums_worker > 0, viene chiamato _MultiProcessingDataLoaderIter. All'interno di _MultiProcessingDataLoaderIter, Torch.multiprocessing crea la coda di lavoro. Torch.multiprocessing utilizza due diverse strategie per la condivisione della memoria e il caching: descrittore_file E file_system. Mentre file_system non richiede la memorizzazione nella cache dei descrittori di file, è soggetto a perdite di memoria condivisa.
Per verificare quale strategia di condivisione sta utilizzando la tua macchina, aggiungi semplicemente lo script:
torch.multiprocessing.get_sharing_strategy()
Per ottenere il limite del descrittore di file di sistema (Linux), esegui il seguente comando nel terminale:
ulimit -n
Per cambiare la tua strategia di condivisione in descrittore_file:
torch.multiprocessing.set_sharing_strategy(âfile_descriptorâ)
Per contare il numero di descrittori di file aperti, eseguire il comando seguente:
ls /proc/self/fd | wc -l
Finché il sistema lo consente, il descrittore_file si consiglia una strategia
Il secondo è il metodo di avvio del lavoratore multiprocessore. In parole povere, si tratta del dibattito se utilizzare un fork o lo spawn come metodo di partenza del lavoratore. Il fork è il modo predefinito per avviare il multiprocessing in Linux e può evitare la copia di alcuni file, quindi è molto più veloce, ma potrebbe avere problemi nella gestione dei tensori CUDA e delle librerie di terze parti come OpenCV nel tuo DataLoader.
Per utilizzare il metodo spawn, puoi semplicemente passare l'argomento multiprocessing_context= “spawn”.. al DataLoader.
Tre: rendere selezionabili/serializzabili gli oggetti del set di dati
C'è un post molto carino che discute ulteriormente dell'effetto “copia su lettura” per la piegatura del processo: https://ppwwyyxx.com/blog/2022/Demystify-RAM-Usage-in-Multiprocess-DataLoader/
In poche parole, lo è non è più un buon approccio per creare un elenco di nomi di file e caricarli nel metodo __getitem__. Crea un array Numpy o un dataframe panda per archiviare l'elenco dei nomi di file a scopo di serializzazione. E se hai familiarità con HuggingFace, utilizzare un CSV/dataframe è il modo consigliato per caricare un set di dati locale: https://huggingface.co/docs/datasets/v2.19.0/en/package_reference/loading_methods#datasets.load_dataset.example-2
Fonte: towardsdatascience.com