當前位置:
首頁 > 最新 > 手把手教你由TensorFlow上手PyTorch

手把手教你由TensorFlow上手PyTorch

來源:機器之心

作者:Illarion Khlestov

本文長度為4100,建議閱讀5分鐘。

本文為你解讀PyTorch 的易用性。

當我第一次嘗試學習 PyTorch 時,沒幾天就放棄了。和 TensorFlow 相比,我很難弄清 PyTorch 的核心要領。但是隨後不久,PyTorch 發布了一個新版本,我決定重新來過。在第二次的學習中,我開始了解這個框架的易用性。在本文中,我會簡要解釋 PyTorch 的核心概念,為你轉入這個框架提供一些必要的動力。其中包含了一些基礎概念,以及先進的功能如學習速率調整、自定義層等等。

PyTorch 的易用性如何?Andrej Karpathy 是這樣評價的

資源

首先要知道的是:PyTorch 的主目錄和教程是分開的。而且因為開發和版本更新的速度過快,有時候兩者之間並不匹配。所以你需要不時查看源代碼:http://pytorch.org/tutorials/。

當然,目前網路上已有了一些 PyTorch 論壇,你可以在其中詢問相關的問題,並很快得到回復:https://discuss.pytorch.org/。

把 PyTorch 當做 NumPy 用

讓我們先看看 PyTorch 本身,其主要構件是張量——這和 NumPy 看起來差不多。這種性質使得 PyTorch 可支持大量相同的 API,所以有時候你可以把它用作是 NumPy 的替代品。PyTorch 的開發者們這麼做的原因是希望這種框架可以完全獲得 GPU 加速帶來的便利,以便你可以快速進行數據預處理,或其他任何機器學習任務。將張量從 NumPy 轉換至 PyTorch 非常容易,反之亦然。讓我們看看如下代碼:

從張量到變數

張量是 PyTorch 的一個完美組件,但是要想構建神經網路這還遠遠不夠。反向傳播怎麼辦?當然,我們可以手動實現它,但是真的需要這樣做嗎?幸好還有自動微分。為了支持這個功能,PyTorch 提供了變數,它是張量之上的封裝。如此,我們可以構建自己的計算圖,並自動計算梯度。每個變數實例都有兩個屬性:包含初始張量本身的.data,以及包含相應張量梯度的.grad

你也許注意到我們手動計算了自己的梯度,這樣看起來很麻煩,我們能使用優化器嗎?當然。

並不是所有的變數都可以自動更新。但是你應該可以從最後一段代碼中看到重點:我們仍然需要在計算新梯度之前將它手動歸零。這是 PyTorch 的核心理念之一。有時我們會不太明白為什麼要這麼做,但另一方面,這樣可以讓我們充分控制自己的梯度。

靜態圖 vs 動態圖

PyTorch 和 TensorFlow 的另一個主要區別在於其不同的計算圖表現形式。TensorFlow 使用靜態圖,這意味著我們是先定義,然後不斷使用它。在 PyTorch 中,每次正向傳播都會定義一個新計算圖。在開始階段,兩者之間或許差別不是很大,但動態圖會在你希望調試代碼,或定義一些條件語句時顯現出自己的優勢。就像你可以使用自己最喜歡的 debugger 一樣!

你可以比較一下 while 循環語句的下兩種定義——第一個是 TensorFlow 中,第二個是 PyTorch 中:

看起來第二種方法比第一個簡單多了,你覺得呢?

模型定義

現在我們看到,想在 PyTorch 中創建 if/else/while 複雜語句非常容易。不過讓我們先回到常見模型中,PyTorch 提供了非常類似於 Keras 的、即開即用的層構造函數:

神經網路包(nn)定義了一系列的模塊,它可以粗略地等價於神經網路的層。模塊接收輸入變數並計算輸出變數,但也可以保存內部狀態,例如包含可學習參數的變數。nn 包還定義了一組在訓練神經網路時常用的損失函數。

如果你想要構建複雜的模型,我們可以將 nn.Module 類子類化。當然,這兩種方式也可以互相結合。

在__init__方法中,我們需要定義之後需要使用的所有層。在正向方法中,我們需要提出如何使用已經定義的層的步驟。而在反向傳播上,和往常一樣,計算是自動進行的。

自定義層

如果我們想要定義一些非標準反向傳播模型要怎麼辦?這裡有一個例子——XNOR 網路:

在這裡我們不會深入細節,如果你對它感興趣,可以參考一下原始論文:https://arxiv.org/abs/1603.05279

與我們問題相關的是反向傳播需要權重必須介於-1 到 1 之間。在 PyTorch 中,這可以很容易實現:

正如你所見,我們應該只定義兩種方法:一個為正向傳播,一個為反向傳播。如果我們需要從正向通道訪問一些變數,我們可以將它們存儲在 ctx 變數中。注意:在此前的 API 正向/反向傳播不是靜態的,我們存儲變數需要以 self.save_for_backward(input) 的形式,並以 input, _ = self.saved_tensors 的方式接入。

在 CUDA 上訓練模型

我們曾經討論過傳遞一個張量到 CUDA 上。但如果希望傳遞整個模型,我們可以通過調用.cuda() 來完成,並將每個輸入變數傳遞到.cuda() 中。在所有計算後,我們需要用返回.cpu() 的方法來獲得結果。

同時,PyTorch 也支持在源代碼中直接分配設備

因為有些時候我們想在 CPU 和 GPU 中運行相同的模型,而無需改動代碼,我們會需要一種封裝:

權重初始化

反向排除子圖

有時,當你希望保留模型中的某些層或者為生產環境做準備的時候,禁用某些層的自動梯度機制非常有用。在這種思路下,PyTorch 設計了兩個 flag:requires_grad 和 volatile。第一個可以禁用當前層的梯度,但子節點仍然可以計算。第二個可以禁用自動梯度,同時效果沿用至所有子節點。

訓練過程

當然,PyTorch 還有一些其他賣點。例如你可以設定學習速率,讓它以特定規則進行變化。或者你可以通過簡單的訓練標記允許/禁止批規範層和 dropout。如果你想要做的話,讓 CPU 和 GPU 的隨機運算元不同也是可以的。

同時,你也可以添加模型信息,或存儲/載入一小段代碼。如果你的模型是由 OrderedDict 或基於類的模型字元串,它的表示會包含層名。

根據 PyTorch 文檔,用 state_dict() 的方式存儲文檔更好。

記錄

訓練過程的記錄是一個非常重要的部分。不幸的是,PyTorch 目前還沒有像 Tensorboard 這樣的東西。所以你只能使用普通文本記錄 Python 了,你也可以試試一些第三方庫:

logger:https://github.com/oval-group/logger

Crayon:https://github.com/torrvision/crayon

tensorboard_logger:https://github.com/TeamHG-Memex/tensorboard_logger

tensorboard-pytorch:https://github.com/lanpa/tensorboard-pytorch

Visdom:https://github.com/facebookresearch/visdom

掌控數據

你可能會記得 TensorFlow 中的數據載入器,甚至想要實現它的一些功能。對於我來說,我花了四個小時來掌握其中所有管道的執行原理。

GIF/241K

首先,我想在這裡添加一些代碼,但我認為上圖足以解釋它的基礎理念了。

有兩件事你需要事先知道:

PyTorch 的圖維度和 TensorFlow 的不同。前者的是 [Batch_size × channels × height × width] 的形式。但如果你沒有通過預處理步驟 torchvision.transforms.ToTensor() 進行交互,則可以進行轉換。在 transforms 包中還有很多有用小工具。

你很可能會使用固定內存的 GPU。對此,你只需要對 cuda() 調用額外的標誌 async = True,並從標記為 pin_memory = True 的 DataLoader 中獲取固定批次。

最終架構

現在我們了解了模型、優化器和很多其他細節。是時候來個總結了:

這裡有一段用於解讀的偽代碼:

結論

希望本文可以讓你了解 PyTorch 的如下特點:

它可以用來代替 Numpy

它的原型設計非常快

調試和使用條件流非常簡單

有很多方便且開箱即用的工具

PyTorch 是一個正在快速發展的框架,背靠一個富有活力的社區。現在是嘗試 PyTorch 的好時機。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 數據派THU 的精彩文章:

Michael I.Jordan最新清華授課筆記!
數據蔣堂 有序遍歷語法

TAG:數據派THU |