non_blocking=False的建議應該是non_blocking=True .tar格式和IterableDataset的一種加速數據讀取的思路。 Note
原始文檔:https://www.yuque.com/lart/ugkv9f/ugysgn
聲明: 大部分內容來自知乎和其他博客的分享, 這裡只作為一個收集羅列. 歡迎給出更多建議.
知乎回答(歡迎點贊哦):
resize , 事先處理好保存下來, 訓練的時候直接拿來用。NVIDIA/DALI 。opencv一般要比PIL要快。PIL的惰性加載的策略使得其看上去open要比opencv的imread要快,但是實際上那並沒有完全加載數據。可以對open返回的對象調用其load()方法,從而手動加載數據,這時的速度才是合理的。jpeg讀取, 可以嘗試jpeg4py 。bmp圖(降低解碼時間)。對於大規模的小文件讀取,可以保存為一個可以連續讀取的連續文件格式。可以選擇考慮TFRecord (Tensorflow) , recordIO , hdf5 , pth , n5 , lmdb等。
TFRecord :https://github.com/vahidk/tfrecordlmdb數據庫:Tar文件和IterableDataset的實現預讀取下一次迭代需要的數據。使用案例:
--cache 。機械硬盤換成NVME 固態。參考自如何給你PyTorch 裡的Dataloader 打雞血- MKFMIKU 的文章- 知乎
在訓練中使用低精度( FP16甚至INT8 、二值網絡、三值網絡) 表示取代原有精度( FP32 ) 表示。
可以節約一定的顯存並提速, 但是要小心一些不安全的操作如mean 和sum。
NVIDIA/Apex提供的混合精度支持。torch.cuda.amp以支持混合精度。 更大的batch 在固定的epoch 的情況下往往會帶來更短的訓練時間。但是大的batch 面臨著超參數的設置、顯存佔用問題等諸多考量,這又是另一個備受關注的領域了。
torch.backends.cudnn.benchmark = True可以加速計算。由於計算不同內核大小卷積的cuDNN 算法的性能不同,自動調優器可以運行一個基準來找到最佳算法。當你的輸入大小不經常改變時,建議開啟這個設置。如果輸入大小經常改變,那麼自動調優器就需要太頻繁地進行基準測試,這可能會損害性能。它可以將向前和向後傳播速度提高1.27x 到1.70x。pin_memory=True 。num_worker ,細節討論可見Pytorch 提速指南- 雲夢的文章- 知乎。set_to_none=True來降低的內存佔用,並且可以適度提高性能。但是這也會改變某些行為,具體可見文檔。通過model.zero_grad()或optimizer.zero_grad()將對所有參數執行memset ,並通過讀寫操作更新梯度。但是,將梯度設置為None將不會執行memset ,並且將使用“只寫”操作更新梯度。因此,設置梯度為None更快。eval模式並使用torch.no_grad關閉梯度計算。DistributedDataParallel代替DataParallel 。對於多GPU 來說,即使只有單個節點,也總是優先使用DistributedDataParallel而不是DataParallel ,因為DistributedDataParallel應用於多進程,並為每個GPU 創建一個進程,從而繞過Python 全局解釋器鎖(GIL) 並提高速度。 forward是分開的,他不會因為你不去使用,而不去初始化。@torch.jit.script ,使用PyTroch JIT 將逐點運算融合到單個CUDA kernel 上。 PyTorch 優化了維度很大的張量的運算操作。在PyTorch 中對小張量進行太多的運算操作是非常低效的。所以有可能的話,將計算操作都重寫為批次(batch)的形式,可以減少消耗和提高性能。而如果沒辦法自己手動實現批次的運算操作,那麼可以採用TorchScript 來提升代碼的性能。 TorchScript 是一個Python 函數的子集,但經過了PyTorch 的驗證,PyTorch 可以通過其just in time(jtt) 編譯器來自動優化TorchScript 代碼,提高性能。但更好的做法還是手動實現批次的運算操作。del釋放內存佔用。torch.from_numpy(ndarray)或者torch.as_tensor(data, dtype=None, device=None) ,這可以通過共享內存而避免重新申請空間,具體使用細節和注意事項可參考對應文檔。如果源設備和目標設備都是CPU, torch.from_numpy和torch.as_tensor不會拷貝數據。如果源數據是NumPy 數組,使用torch.from_numpy更快。如果源數據是一個具有相同數據類型和設備類型的張量,那麼torch.as_tensor可以避免拷貝數據,這裡的數據可以是Python 的list, tuple,或者張量。non_blocking=True 。這會在可能的情況下嘗試異步轉換,例如,將頁面鎖定內存中的CPU 張量轉換為CUDA 張量。optimizer.step()的時間。contiguous_pytorch_paramsmemory access cost , memory access cost縮寫為MAC ) 最小, 此時模型速度最快element-wise操作: element-wise操作所帶來的時間消耗遠比在FLOPs 上的體現的數值要多, 因此要盡可能減少element-wise操作。 depthwise convolution也具有低FLOPs 、高MAC 的特點。 在推理中使用低精度( FP16甚至INT8 、二值網絡、三值網絡) 表示取代原有精度( FP32 ) 表示。
TensorRT是NVIDIA 提出的神經網絡推理(Inference) 引擎, 支持訓練後8BIT 量化, 它使用基於交叉熵的模型量化算法, 通過最小化兩個分佈的差異程度來實現Distiller是Intel 基於Pytorch 開源的模型優化工具, 自然也支持Pytorch 中的量化技術NNI集成了多種量化感知的訓練算法, 並支持PyTorch/TensorFlow/MXNet/Caffe2等多個開源框架更多細節可參考有三AI:【雜談】當前模型量化有哪些可用的開源工具?。
profile , cProfile和hotshot , 使用方法基本都差不多, 無非模塊是純Python 還是用C 寫的。原始文檔:https://www.yuque.com/lart/ugkv9f/nvffyf
整理自: Pytorch 有什麼節省內存(顯存) 的小技巧? - 知乎https://www.zhihu.com/question/274635237
inplace的操作盡量啟用。比如relu可以使用inplace=True 。batchnorm和一些特定的激活函數打包成inplace_abn 。每次循環結束時刪除loss, 可以節約很少顯存, 但聊勝於無。可見Tensor to Variable and memory freeing best practices
可以節約一定的顯存並提速, 但是要小心一些不安全的操作如mean 和sum。
NVIDIA/Apex提供的混合精度支持。torch.cuda.amp以支持混合精度。torch.no_grad來包裹代碼。model.eval() ' vs 'with torch.no_grad() 'requires_grad設為False , 讓變量不參與梯度的後向傳播,以減少不必要的梯度的顯存佔用。torch.cuda.empty_cache()這是del的進階版, 使用nvidia-smi會發現顯存有明顯的變化. 但是訓練時最大的顯存佔用似乎沒變. 大家可以試試: How can we release GPU memory cache?del刪除不必要的中間變量, 或者使用replacing variables的形式來減少佔用.把一個batchsize=64分為兩個32 的batch,兩次forward 以後,backward 一次。但會影響batchnorm等和batchsize相關的層。
在PyTorch 的文檔中提到了梯度累加與混合精度並用的例子。
使用梯度累加技術可以對分佈式訓練加速,這可以參考:[原創][深度][PyTorch] DDP 系列第三篇:實戰與技巧- 996 黃金一代的文章- 知乎
PyTorch 中提供了torch.utils.checkpoint 。這是通過在反向傳播期間,在每個檢查點位置重新執行一次前向傳播來實現的。
論文Training Deep Nets with Sublinear Memory Cost 基於梯度檢查點技術,將顯存從O(N) 降到了O(sqrt(N))。對於越深的模型, 這個方法省的顯存就越多, 且速度不會明顯變慢。
可關注文檔中相關章節。
避免使用非確定性算法。
PyTorch 中, torch.use_deterministic_algorithms()可以強制使用確定性算法而不是非確定性算法,並且如果已知操作是非確定性的(並且沒有確定性的替代方案),則會拋出錯誤。
def seed_torch ( seed = 1029 ):
random . seed ( seed )
os . environ [ 'PYTHONHASHSEED' ] = str ( seed )
np . random . seed ( seed )
torch . manual_seed ( seed )
torch . cuda . manual_seed ( seed )
torch . cuda . manual_seed_all ( seed ) # if you are using multi-GPU.
torch . backends . cudnn . benchmark = False
torch . backends . cudnn . deterministic = True
seed_torch ()參考自https://www.zdaiot.com/MLFrameworks/Pytorch/Pytorch%E9%9A%8F%E6%9C%BA%E7%A7%8D%E5%AD%90/
具體細節可見可能95%的人還在犯的PyTorch 錯誤- serendipity 的文章- 知乎
解決方法可參考文檔:
def seed_worker ( worker_id ):
worker_seed = torch . initial_seed () % 2 ** 32
numpy . random . seed ( worker_seed )
random . seed ( worker_seed )
DataLoader (..., worker_init_fn = seed_worker )