Pytorch源碼與運行原理淺析--網路篇(一)

前言

申請的專欄開通了,剛好最近閑下來了,就打算開這個坑了hhhhh

第一篇就先講一講pytorch的運行機制好了。。。

記得當時剛剛接觸的時候一直搞不明白,為什麼自己只是定義了幾個網路,就可以完整的訓練整個模型,它背後的機制又是如何,搞明白了這個,才有可能去做更多的定製的更改,比如更改loss,反傳方式,梯度下降機制,甚至自定義參數更新速率(比如學習率隨著迭代輪數下降),文章比較淺顯,希望各位大神不吝賜教。

知識儲備

看此文章的前提,大概需要你寫過一個利用pytorch的訓練程序,哪怕官網上的MNIST。

因為本文目的是告訴你為什麼這麼寫

為什麼不用TensorFlow

其實我之前是有用TF的,但是,emmmmmmmm.......

之後接觸了Pytorch,那一整天都在感嘆"還有這種操作?"

個人感覺TF不是一個易於理解和易於擴展的框架。

比如說,我想實現學習率隨迭代輪數降低,需要修改哪些?

那麼,讓我們開始吧

從MNIST說起

網路定義篇

import torch.nn as nnnclass Net(nn.Module):n def __init__(self):n super(Net, self).__init__()n self.conv1 = nn.Conv2d(1, 10, kernel_size=5)n self.conv2 = nn.Conv2d(10, 20, kernel_size=5)n self.conv2_drop = nn.Dropout2d()n self.fc1 = nn.Linear(320, 50)n self.fc2 = nn.Linear(50, 10)nn def forward(self, x):n x = F.relu(F.max_pool2d(self.conv1(x), 2))n x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))n x = x.view(-1, 320)n x = F.relu(self.fc1(x))n x = F.dropout(x, training=self.training)n x = self.fc2(x)n return F.log_softmax(x)n

這一段是MNIST給的定義Net的代碼,那麼,讓我們看一看,這一段代碼說明了什麼,首先,__init__方法直接定義了你的網路,這就是你的模型中含有的全部的東西,你的模型本身也只有__init__ 中的屬性會被每一次訓練的時候更改,可以說這個思路是十分的清晰了。

之後,是forward方法,這裡定義了如何處理傳入的數據(就是那個x),返回這個神經網路的output

這裡,我把它比作名詞和動詞的關係,__init__()方法定義了網路本身,或者說定義了一個個的名詞,而我們也需要一系列的"猜測"過程,猜出這些名詞是什麼。而forward()方法,則是一個個的動詞,它提供了如何處理這些名詞的方式。

而之後,我們來看看,運行的時候,發生了什麼

首先,我們看看torch.nn.Module,看看它是如何定義的。

torch.nn.Module

源代碼在此處

class Module(object):n dump_patches = Falsenn def __init__(self):n self._backend = thnn_backendn self._parameters = OrderedDict()n self._buffers = OrderedDict()n self._backward_hooks = OrderedDict()n self._forward_hooks = OrderedDict()n self._forward_pre_hooks = OrderedDict()n self._modules = OrderedDict()n self.training = Truenn def forward(self, *input):n raise NotImplementedErrorn

(代碼不完整,只截取了一段)

可以看到,Module類定義了一系列訓練時使用的變數比如參數(感覺這是是緩存的參數,用來之後做參數更新用的),buffers,幾個hooks(個人感覺這些hooks是之後與loss,反傳之類的步驟通訊數據用的)

反傳裡面是有一個判斷的邏輯,判斷你的子類有沒有定義網路,沒有就報錯(講真,這個想法很棒啊QwQ,子類重寫父類方法,沒有重寫就是個報錯hhhhhh)

def register_buffer(self, name, tensor):n self._buffers[name] = tensornndef register_parameter(self, name, param):n if _parameters not in self.__dict__:n raise AttributeError(n "cannot assign parameter before Module.__init__() call")n if param is None:n self._parameters[name] = Nonen elif not isinstance(param, Parameter):n raise TypeError("cannot assign {} object to parameter {} "n "(torch.nn.Parameter or None required)"n .format(torch.typename(param), name))n elif param.grad_fn:n raise ValueError(n "Cannot assign non-leaf Variable to parameter {0}. Model "n "parameters must be created explicitly. To express {0} "n "as a function of another variable, compute the value in "n "the forward() method.".format(name))n else:n self._parameters[name] = paramn

buffer和parameter的註冊,這裡有一點需要提醒,在你自定義的網路中,如果你用了類似

self.some_dict[keys] = nn.Conv2d(10, 20, kernel_size=5)n

這種語句的話,pytorch是沒有辦法這個變數的,也不會參與之後的傳參之類的

在定義了上面那句話之後你必須用類似

# method 1nsetattr(self, some_name, self.some_dict[keys])n# method 2nself.register_parameter(some_name, self.some_dict[keys])n

比如筆者自己的代碼

self.LocalConv1 = {i + 1: nn.Conv2d(32, 32, 3, stride=1, padding=0) for i in range(4)}n for i in self.LocalConv1:n setattr(self, LocalConvPart%d % i, self.LocalConv1[i])n self.GlobalFullConnect = nn.Linear(7 * 2 * 32, 400)n self.LocalFullConnect = {i + 1: nn.Linear(32 * 23 * 16, 100) for i in range(4)}n for i in self.LocalFullConnect:n setattr(self, LocalFullConnectPart%d % i, self.LocalFullConnect[i])n

建議使用方法1,因為Module類重載了__setattr__()方法,如下

def __setattr__(self, name, value):n def remove_from(*dicts):n for d in dicts:n if name in d:n del d[name]nn params = self.__dict__.get(_parameters)n if isinstance(value, Parameter):n if params is None:n raise AttributeError(n "cannot assign parameters before Module.__init__() call")n remove_from(self.__dict__, self._buffers, self._modules)n self.register_parameter(name, value)n elif params is not None and name in params:n if value is not None:n raise TypeError("cannot assign {} as parameter {} (torch.nn.Parameter or None expected)".format(torch.typename(value), name))n self.register_parameter(name, value)n else:n modules = self.__dict__.get(_modules)n if isinstance(value, Module):n if modules is None:n raise AttributeError(n "cannot assign module before Module.__init__() call")n remove_from(self.__dict__, self._parameters, self._buffers)n modules[name] = valuen elif modules is not None and name in modules:n if value is not None:n raise TypeError("cannot assign {} as child module {} "n "(torch.nn.Module or None expected)"n .format(torch.typename(value), name))n modules[name] = valuen else:n buffers = self.__dict__.get(_buffers)n if buffers is not None and name in buffers:n if value is not None and not torch.is_tensor(value):n raise TypeError("cannot assign {} as buffer {} "n "(torch.Tensor or None expected)"n .format(torch.typename(value), name))n buffers[name] = valuen else:n object.__setattr__(self, name, value)n

其實差別不大,可以看到加了很多判斷。

然後之後apply()方法

def _apply(self, fn):n for module in self.children():n module._apply(fn)nn for param in self._parameters.values():n if param is not None:n param.data = fn(param.data)n if param._grad is not None:n param._grad.data = fn(param._grad.data)nn for key, buf in self._buffers.items():n if buf is not None:n self._buffers[key] = fn(buf)n return selfnndef apply(self, fn):n for module in self.children():n module.apply(fn)n fn(self)n return selfn

這兩個方法就是更新參數的核心過程了,pytorch的更新參數最底層的方法都是這兩個方法定義的。

之後的cpu(),cuda()之類的方法大家都知道是幹什麼的,我就不贅述了,啊,順帶提一句,這個cuda()方法是對每個變數都covert to cuda的,十分的方便。

def register_backward_hook(self, hook):n handle = hooks.RemovableHandle(self._backward_hooks)n self._backward_hooks[handle.id] = hookn return handlenndef register_forward_pre_hook(self, hook):n handle = hooks.RemovableHandle(self._forward_pre_hooks)n self._forward_pre_hooks[handle.id] = hookn return handlenndef register_forward_hook(self, hook):n handle = hooks.RemovableHandle(self._forward_hooks)n self._forward_hooks[handle.id] = hookn return handlen

訓練過程的參數傳遞,這些方法完成了神經網路,Loss,梯度下降等演算法等等一系列計算的之間的數據通信。

結語

先寫到這裡,nn.Module大概寫了一半左右吧,希望各位大佬們給出建議QwQ


推薦閱讀:

深度學習新手必學:使用 Pytorch 搭建一個 N-Gram 模型
項目推薦:自然場景中文文字檢測和不定長中文OCR識別
PyTorch實戰指南
【筆記】Finding Tiny Faces
PyTorch中如何使用tensorboard可視化

TAG:深度学习DeepLearning | PyTorch | 机器学习 |