前言
Pytorch釋出已經有一段時間了,我們在使用中也發現了其獨特的動态圖設計,讓我們可以高效地進行神經網絡的構造、實作我們的想法。那麼Pytorch是怎麼來的,追根溯源,pytorch可以說是torch的python版,然後增加了很多新的特性,那麼pytorch和torch的具體差別是什麼,這篇文章大緻對兩者進行一下簡要分析,有一個宏觀的了解。
上面的對比圖來源于官網,官方認為,這兩者最大的差別就是Pytorch重新設計了model模型和intermediate中間變量的關系,在Pytorch中所有計算的中間變量都存在于計算圖中,所有的model都可以共享中間變量。而在torch中的中間變量則在每一個子產品中,想要調用其他子產品的參數就必須首先複制這個子產品然後再調用。
當然,Python有很多特性是lua語言不具備的,Python的debug功能比lua強大很多,是以效率也就提升了。
Pytorch與Torch
接下來讓我們稍微稍微具體談一下兩者的差別(ps:torch是火炬的意思)。
我們都知道Pytorch采用python語言接口來實作程式設計,而torch是采用lua語言,Lua是一個什麼樣的語言,可以這樣說,Lua相當于一個小型加強版的C,支援類和面向對象,運作效率極高,與C語言結合“特别默契”,也就是說在Lua中使用C語言非常容易也很舒服。
是以,torch是采用C語言作為底層,然後lua語言為接口的深度學習庫。而Pytorch呢,Pytorch其實也是主要采用C語言為接口(相關文章),另外除了C語言那還有C++了,因為Pytorch吸收結合了caffe2,進行了很多代碼合并,現在Pytorch的底層雖然大部分還是C語言,但是接口什麼的也逐漸向C++過渡。
目前來看,兩者的底層庫的C語言部分差別還是不大,盡管Pytorch使用了C++接口,但是由于代碼的相容性,使用torch拓展的底層代碼在Pytorch中照樣可以編譯使用。
編寫模型方面的差別
我們來簡單說一下pytorch和torch在編寫模型上一些簡單的差別,pytorch在編寫模型的時候最大的特點就是利用autograd技術來實作自動求導,也就是不需要我們再去麻煩地寫一些反向的計算函數,這點上繼承了torch。
舉個例子:
pytorch中,我們通過繼承
nn.Module
設計一個層,然後定義我們平常使用的成員函數:
__init__
和
forward
,這兩個函數相比我們都很熟悉,另外
content_hook
是一個hook函數,通常在需要讀取中間參數的時候使用:
# 這個層是風格遷移中的内容層
class ContentLoss(nn.Module):
def __init__(self, target, weight):
super(ContentLoss, self).__init__()
self.target = target.detach()
self.weight = weight
self.loss = 0
def forward(self, input):
self.loss = F.mse_loss(input, self.target) * self.weight
return input
# 這個hook通過register_backward_hook後進行綁定才能使用
# 通過綁定後,這裡的函數在這個層進行backward的時候會執行
# 在裡面我們可以自定義一些操作實作其他的功能,比如修改grad_input
def content_hook(self, module, grad_input, grad_output):
return grad_input
而在torch中是這樣設計的,我們利用lua語言的特定來設計class,
__init
和
updateOutput
和上面對應的
__init__
和
forward
功能相同。其實torch也是有
forward
函數實作,但是由于torch的局限性,不建議直接修改
forward
,我們需要修改
updateOutput
函數來實作forward操作:
local ContentLoss, parent = torch.class('nn.ContentLoss', 'nn.Module')
function ContentLoss:__init(strength, target)
parent.__init(self)
self.strength = strength
self.target = target
self.loss = 0
self.crit = nn.MSECriterion()
end
-- 就是得到輸入輸出output
function ContentLoss:updateOutput(input)
if input:nElement() == self.target:nElement() then
self.loss = self.crit:forward(input, self.target) * self.strength
else
print('WARNING: Skipping content loss')
end
self.output = input
return self.output
end
-- 這裡的函數在backward的時候會執行
function ContentLoss:updateGradInput(input, gradOutput)
if input:nElement() == self.target:nElement() then
self.gradInput = self.crit:backward(input, self.target)
end
self.gradInput:mul(self.strength)
self.gradInput:add(gradOutput)
return self.gradInput
end
我們通過對比Pytorch和Torch自頂層的設計大概分析了一下兩者的差別,其實兩者的很多功能函數的操作方式和命名都是類似的:
pytorch:
torch:
依賴庫差別
Pytorch借助于Python強大的第三方庫,已經存在的庫可以直接使用,利用我們的圖像讀取直接使用Python自帶的PIL圖像庫或者python-opencv都可以,其他各種想要實作的功能都可以利用python強大的第三方庫實作:
https://oldpan.me/archives/pytorch-transforms-opencv-scikit-image https://oldpan.me/archives/pytorch-tensor-image-transform
而在torch中同樣有很多Lua語言下開發的很多包:
torch可以很友善地拓展cuda和c代碼實作更加豐富的自定義層和算法操作。
而pytorch的可以看這裡:https://oldpan.me/archives/pytorch-combine-c-and-cuda
後記
暫且說這麼多,Pytorch和Torch都很優秀,現在仍然有很多優秀的項目是使用torch來編寫,Pytorch和torch的思想都值得我們去借鑒,閑暇之餘,我們也可以看看Torch的代碼,體驗一下其優秀的構架和設計。