手把手教你學會深度學習框架—PyTorch

摘要: PyTorch是一個基於Python語言的深度學習框架,專門針對 GPU 加速的深度神經網絡(DNN)的程序開發。基本上,它所有的程序都是用python寫的,這就使得它的源碼看上去比較簡潔,在機器學習領域中有廣泛的應用。

PyTorch是一個靈活的深度學習框架,它允許通過動態神經網絡(即if條件語句和while循環語句那樣利用動態控制流的網絡)自動分化。它支持GPU加速、分佈式訓練、多種優化以及更多的、更簡潔的特性。

神經網絡是計算圖形的一個子類。計算圖形接收輸入數據,並且數據被路由到那些可能由對數據進行處理的節點進行轉換。在深度學習中,神經網絡中的神經元通常用參數和可微函數進行數據變換,從而可以通過梯度下降來優化參數以最大程度的減少損失。更廣泛來說,函數可以是隨機的,並且圖形的結構可以是動態的。因此,雖然神經網絡可以很好地適合數據流開發,但是PyTorch的API卻圍繞著命令行式的編程,這是一種更常見的考慮程序的方式。這使得讀取複雜程序的代碼和理由變得更容易,而不必浪費大量的性能;PyTorch實際上運行的速度相當快,並帶有很多優化,你可以放心地忘記你是個最終用戶。

該文件的其餘部分是基於官方的MNIST示例,並且應該僅僅是在學習了官方初級教程之後再看。為了提高可讀性,代碼放在了帶有註釋的區塊中,因此不會被分割成不同的函數或者是文件,因為通常要用於清晰的、模塊化的代碼。

手把手教你學會深度學習框架—PyTorch

這些是非常標準的程序或者是包導入代碼,特別是用於解決計算機視覺問題的視覺模塊:

手把手教你學會深度學習框架—PyTorch

argparse是一種處理在Python中命令行參數的標準方法。

它是一種編寫與設備無關的代碼的好方法(在可用時受益於GPU加速,但當不可用時則返回到CPU)是選擇並保存適當的torch.device,它可以用來決定應該存儲張量的位置。更多資料請參閱官方文檔。PyTorch方法是將設備放置在用戶的控制之下,這對於簡單的例子來說可能看起來是件討厭的事情,但是它使得更容易計算出張量的位置是對調試有用還是使得手動使用設備變得高效。

對於可重複的實驗,有必要為任何使用隨機數生成的進行隨機種子設置。注意,cuDNN使用非確定性算法,並且可以使用torch.backends.cudnn.enabled = False來進行禁用。

手把手教你學會深度學習框架—PyTorch

由於torchvision模型在~/.torch/models/下面進行保存的,我在~/.torch/datasets保存torchvision數據集。通常來說,如果結束重用幾個數據集,那麼將數據集與代碼分離開來存放是非常值得的。torchvision.transforms包含很多給單個圖片的方便轉換的功能,如修剪和正常化。

DataLoader含有許多可選方案,但是在batch_size和shuffle參數之外,num_workers和pin_memory對於效率也是值得了解一下的。num_workers > 0使用了子進程來進行異步加載數據,而不是在這個過程中使用主進程塊。pin_memory使用pinned RAM來加速RAM到GPU的傳輸。

手把手教你學會深度學習框架—PyTorch

網絡初始化通常包括一些成員變量和可訓練參數的層,以及可能分開的可訓練參數和不可訓練的緩衝器。前向傳遞之後,使用那些來自純函數F的函數(不包含參數)的結合。有些人傾向具有完全功能的網絡(例如,保持參數分離和使用F.conv2d,而不是nn.Conv2d)或者是那些完全分層的網絡(例如,nn.ReLU,而不是F.relu)。

to(device)是將設備參數(和緩衝器)發送到GPU的簡便方法,如果設備被設置為GPU,則不做任何操作(當設備被設置為CPU)時。在將網絡參數傳遞給優化器之前,將它們傳遞給合適的設備是非常重要的,否則優化器將不會正確跟蹤參數。

神經網絡(nn.Module)和優化器(optim.Optimizer)都具有保存和加載其內部狀態的能力,並且.load_state_dict(state_dict)是推薦這麼做的方法,你將需要重新加載這兩個狀態以恢復之前保存的狀態字典的訓練。保存整個對象可能會容易出錯。

這裡沒有指出的一些要點是,正向傳遞可以使用控制流,例如,成員變量,或者甚至數據本身可以決定if語句的執行。在中間打印出張量也是非常有效的,這會使調試變得更加容易。最後,前向傳遞可以使用多個參數。用一個簡短的代碼片段來說明這一點:

手把手教你學會深度學習框架—PyTorch

默認情況下,網絡模塊設置為訓練模式—這影響了一些模塊的運行效果,最明顯的是流失和批量標準化。無論如何,最好通過.train()來進行手動設置參數,它將訓練標誌繼承到所有的子模塊。

在用loss.backward()收集一組新的梯度並用optimiser.step()進行反向傳播之前,有必要手動地集中那些用優化器.zero_grad()優化過了參數的梯度。默認情況下,PyTorch逐漸增加梯度,這是非常方便的,尤其是當你沒有足夠的資源來計算所有你一次性需要的梯度的時候。

PyTorch使用基於磁帶的自動梯度系統—它按一定的順序收集對張量進行的操作,然後對它們進行重放以進行逆向模式求導。這就是為什麼它是超級靈活的原因,並且允許任意的計算圖形。如果張量中沒有一個需要梯度(當構造張量時,你必須設置requires_grad=True),則不存儲任何圖形!然而,網絡往往趨向那些具有需要梯度的參數,所以從一個網絡的輸出所做的任何計算都將存儲在圖形中。因此,如果要想存儲由此產生的數據,那麼你需要手動禁用梯度,或者更常見地,將其存儲為Python數字(通過使用PyTorch標量上的.item())或numpy數組。請在autograd上參閱官方文檔以瞭解更多信息。

切割計算圖形的一種方法是使用.detach(),當通過截斷反向傳播時間來訓練RNNs時,可以使用這個方法來隱藏狀態。當一個成分是另一個網絡的輸出時,它也很方便的區分一個損耗,但是這個網絡不應該在損失方面被優化 — 例如在GAN訓練中從生成器的輸出中訓練一個鑑別器,或者使用值函數作為基線(例如A2C)的算法訓練一個演員評論算法的策略,另一種防止梯度計算的技術在GAN訓練中是有效的(訓練來自鑑別器的生成器),以及通常在微調中是通過網絡參數並設置param.requires_grad = False進行循環。

除了在控制檯或者在日誌文件中的日誌記錄結果外,檢查點模型參數(和優化器狀態)是非常重要的,用於以防萬一。你還可以使用torch.save()來保存普通的Python對象,但其它標準選擇包括在內置的配置中。

手把手教你學會深度學習框架—PyTorch

其他:

CUDA調試錯誤,通常是邏輯問題,會在CPU上產生更明白易懂的錯誤消息。如果你正在計劃使用GPU,最好的方式是能在CPU和GPU之間輕鬆地切換。一個更普遍的開發技巧是能夠設置你的代碼,以便在啟動一個合適的工作任務之前快速運行所有的邏輯來檢查代碼—示例是準備一個小的、合成的數據集,運行一個訓練、測試周期等等。如果是一個CUDA錯誤,或者你真的不能切換到CPU模式,那麼設置CUDA_LAUNCH_BLOCKING=1將使CUDA內核同步啟動,從而會提供更清楚明確的錯誤消息。

對於torch.multiprocessing的記錄,甚至只是一次性運行多個PyTorch腳本。因為PyTorch使用多線程的BLAS庫來加速CPU上的線性代數運算,因此它通常會使用多個內核。如果想同時使用多個處理進程或者多個腳本來運行多個程序,那麼你可以手動地通過將環境變量OMP_NUM_THREADS設置為1或另一個小的數字參數來實現—這減少了CPU大幅震動的機會。官方文檔中有特別用於多處理技術的註釋。

阿里云云棲社區組織翻譯。

文章原標題《PyTorch - The Hitchiker's Guide to PyTorch' by Kai Arulkumaran GitHub:》

作者: Alberto Artasanchez


分享到:


相關文章: