Pytorch 訓練停止,輸出顯示 died with 問題定位過程記錄


  最近使用 Pytorch 進行模型訓練時,模型在訓練到一小部分后程序均被停止。第一次以為是由於機器上其他人的誤操作,故而直接重新拉起訓練。但第二次程序終止時,發現基本與第一次訓練停止的訓練 iteration 一致,故而嘗試對問題進行定位。

  

  問題描述

  具體而言,在使用 Pytorch 訓練時的錯誤信息類似如下所示:

  File "/usr/lib/python3.7/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "/usr/lib/python3.7/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/usr/local/lib/python3.7/dist-packages/torch/distributed/launch.py", line 340, in <module>
    main()
  File "/usr/local/lib/python3.7/dist-packages/torch/distributed/launch.py", line 326, in main
    sigkill_handler(signal.SIGTERM, None)  # not coming back
  File "/usr/local/lib/python3.7/dist-packages/torch/distributed/launch.py", line 301, in sigkill_handler
    raise subprocess.CalledProcessError(returncode=last_return_code, cmd=cmd)
subprocess.CalledProcessError: Command '['/usr/bin/python3', '-u', xxx]' died with <Signals.SIGKILL: 9>.```

  從中可以獲得了信息是程序由於收到了對應的信號 SIGTERM,從而對應的信號處理 handler 被調用,最終使得程序運行的進程被終止。 SIGKILL 與 SIGTERM 均為傳遞終止信息的信號,也就是說有存在行為向運行中的程序發送了終止信號,所以問題在於定位發送信號的原因。

 

  問題定位

  首先由於輸出顯示程序收到了 SIGTERM 信號,所以嘗試尋找發出對應信號的位置。由於發送信號的行為需要經過操作系統 kernel,這里可以通過 dmesg 命令查看最近的內核操作(實際是輸出了內核中記錄行為的 kernel ring buffer 的內容)來查看是否存在相關的信息。具體命令如下所示,其中 dmesg 的 -T 選項表示顯示命令的操作時間。grep 的 -E 參數表示使用拓展的正則表達式進行匹配。 -i 參數表示忽略匹配的大小寫問題,-B num 參數表示在匹配行之前另外輸出 num 行的數據,主要可以用來看是否有上下文的相關信息。命令來自What killed my process and why?

    dmesg -T | grep -E -i -B100 'killed process'

  如在筆者機器上,出現的問題信息如下所示:

  

   這里顯示用戶 uid = 1002 的進程由於 OOM 即 out of memory 的問題被 kill 了,且給出了在終止時整體的虛擬地址的使用量。這里可以通過 id 命令獲取個人用戶的 uid / gid 等內容。

    id        // 獲取當前用戶的 uid 等信息

  通過 id 命令確認了 uid = 1002 即為當前筆者的用戶 id。故而可以確定程序終止的原因是程序運行過程中發生了 OOM 導致系統向程序發送了 SIGTERM 信號。結合之前筆者發現程序總是在訓練到一定的 iteration 時即結束,即可猜測是由於程序存在內存泄露問題,導致隨着訓練的進行,內存占用越來越大,從而最終出現 OOM。

  

  問題復現

  在獲得問題的猜測之后,可以通過重新運行程序來復現,看猜測是否正確。想要確定程序在運行時的內存使用情況,可以使用 ps 或者 top,兩個命令均可以顯示進程的資源使用情況,其中前者輸出命令執行時的系統的結果,而 top 則會顯示系統動態的情況。在運行程序后,使用 top 命令獲取當前運行程序的進程 id。ps 和 top 命令的結果展示如下所示,會顯示進程運行時的許多信息。

    PID     //進程 PID
    USER    //進程所屬的用戶
    PR      //進程優先級 
    NI      //Nice值,與進程調度相關
    VIRT     //進程使用的虛擬地址空間大小
    RES      //進程使用的不可交換物理地址的大小,resident memory size,如使用的 hugepage 等
    SHR      //使用的 shared memory 的大小
    S      //進程所處的狀態,包括 R:running(可運行或這在運行),S:sleeping, T:traced or stopped, Z:zombie, D:uninterruptiable sleep
    %CPU    //CPU使用率,以時間比例計算
    %MEM    //使用的物理內存比例
    TIME        //程序開始至目前的時間
    COMMAND    //運行程序名

  一個 top 運行時的實例如下所示,這里我們可以通過 COMMAND 找到正在運行的程序確定 PID,同時通過 VIRT 查看程序的內存使用情況。

  

   由於 top 時動態更新的結果展示,其中會隨着進程的調度等進行更新,這里主要通過 top 命令獲得執行的程序的 PID,之后通過該 PID 使用 ps 命令查看程序內存的使用情況。主要借助 watch + ps + grep 的命令組合。

    watch -n 1 "ps aux | grep PID"    // watch 命令指定間隔 1s 執行命令,ps 命令顯示進程信息,grep 用於篩選出目標進程所在的行

  在筆者實驗中,通過上述方式確定了程序確實存在內存占用逐漸變大的問題,需要進行調整。

 

  具體問題

  由於筆者對於代碼的修改部分很小,所以比較容易的發現問題。筆者這里的原因是想要統計訓練過程中的平均 loss,所以在代碼中加入了類似如下的代碼:

    loss = L2( GT, output )
    loss.backward()
    total_loss += loss    //統計 loss 的情況

  而問題的主要原因即在於加入的求 total_loss 的代碼部分。Pytorch 中計算產生的 tensor 也就是這里的 loss 默認會納入導數計算的計算圖中,后續所有使用了 loss 的計算式也會被納入計算圖中(也就時 total loss 的計算也被加入了計算圖),所以關於 total_loss 部分的計算圖會逐漸積累變大。在 Pytorch 中,計算圖的存儲位於內存中,故而也就會產生內存占用越來越大的問題。解決的辦法即將 total_loss 部分從計算圖中摘除,或者明確說明 total_loss 不需要進行導數運算即可,可通過 item 函數實現。Pytorch 中 item 函數會直接返回 tensor 具體的數據值,此時的 total_loss 計算過程即與 tensor 計算不相關,不需要進行梯度計算,運算過程就不會被納入計算圖的創建過程中了。

    total_loss += loss.item()

   參考

  Pytorch model training CPU Memory leak issue

  How Computational Graphs are Constructed in PyTorch

  Difference between "detach()" and "with torch.nograd()" in PyTorch?

  How can i process multi loss in pytorch?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM