Pytorch(2):模型搭建與 Module Container

如何搭建一個神經網絡模型

在這裏插入圖片描述
再看看 Pytorch API 的 torch.nn 部分

在這裏插入圖片描述
模型構建有兩要素,一是構建子模塊,二是拼接子模塊。

構建子模塊就是從torch.nn中找到想要的layer,最終通過前向傳播(即forward()函數) 把這些layer拼接在一起。

有了 Pytorch,搭建神經網絡模型就跟搭積木一樣。

nn.Module

每個網絡層(無論是用戶自定義的,還是 Pytorch 內置的)都要繼承nn.Module類,這樣是爲了規範化每個網絡層,更方便管理。

上官方文檔:

class Module(object):
    r"""Base class for all neural network modules.

    Your models should also subclass this class.

    Modules can also contain other Modules, allowing to nest them in
    a tree structure. 
    """

    dump_patches = False
    _version = 1

    def __init__(self):
        """
        Initializes internal Module state, shared by both nn.Module and ScriptModule.
        """
        torch._C._log_api_usage_once("python.nn_module")

        self.training = True
        self._parameters = OrderedDict()
        self._buffers = OrderedDict()
        self._backward_hooks = OrderedDict()
        self._forward_hooks = OrderedDict()
        self._forward_pre_hooks = OrderedDict()
        self._state_dict_hooks = OrderedDict()
        self._load_state_dict_pre_hooks = OrderedDict()
        self._modules = OrderedDict()

		def forward(self, *input):
        r"""Defines the computation performed at every call.

        Should be overridden by all subclasses.

        note:
            Although the recipe for forward pass needs to be defined within
            this function, one should call the :class:`Module` instance afterwards
            instead of this since the former takes care of running the
            registered hooks while the latter silently ignores them.
        """
        raise NotImplementedError

可以看到,nn.Module 共有 8 個參數(8 個有序字典)

  • parameters:存儲管理 nn.Parameter 類(包括權值、偏置等)
  • modules:管理各種子模塊
  • buffers:存儲管理緩衝屬性,如 BN 層中的running_mean
  • hooks(5個):存儲管理鉤子函數

一個構建實例:

class LeNet(nn.Module):
	# 在這裏構建子模塊
    def __init__(self, classes):
    	# 初始化 nn.Module的 8個參數
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16*5*5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, classes)

    def forward(self, x):
        out = F.relu(self.conv1(x))
        out = F.max_pool2d(out, 2)
        out = F.relu(self.conv2(out))
        out = F.max_pool2d(out, 2)
        out = out.view(out.size(0), -1)
        out = F.relu(self.fc1(out))
        out = F.relu(self.fc2(out))
        out = self.fc3(out)
        return out

在實例化 LeNet 類時,在初始化方法中,在初始化父類 nn.Module 的 8 個參數後,每一個賦值操作,如self.conv1 = nn.Conv2d(3, 6, 5),都會被攔截下來,進行類型判斷,如果是 parameter 則添加進self._parameters,如果是 module 則添加進self._modules

上述幾點可通過 Debug 調式驗證。

nn.Module 總結

  • 一個 module 可以包含多個子 module
  • 一個 module 相當於一個運算,必須實現forward()
  • 函數每個module都有8個字典管理它的屬性

Module Container

參考:https://blog.csdn.net/oldmao_2001/article/details/102787546

  • nn.Sequetial:按順序包裝多個網絡層
  • nn.ModuleList:像 Python 中的 list 一樣包裝多個網絡層
  • nn.ModuleDict:像 Python 中的 dict 一樣包裝多個網絡層

注:這裏講的網絡層,全稱是神經網絡層。

發文截止,Containers 最新所有 API
在這裏插入圖片描述

Sequential

nn.Sequential 是 nn.module 的容器,用於按順序包裝一組網絡層,如下圖的LeNet,可以用 2 個 Sequential 把 LeNet 包裝起來。(見白色虛線框)

當然也可以只用一個 Sequential,但不直觀。

代碼如下

class LeNetSequential(nn.Module):
    def __init__(self, classes):
        super(LeNetSequential, self).__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 6, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),
            nn.Conv2d(6, 16, 5),
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2, stride=2),)

        self.classifier = nn.Sequential(
            nn.Linear(16*5*5, 120),
            nn.ReLU(),
            nn.Linear(120, 84),
            nn.ReLU(),
            nn.Linear(84, classes),)

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size()[0], -1)
        x = self.classifier(x)
        return x

上面定義的各個層的命名都是序號,在多個層的時候不是很方便調試或者閱讀,可以在創建 Sequential 的時候用 OrderedDict 指定層的名字,具體做法如下:

# 寫法二(更直觀)
class LeNetSequentialOrderDict(nn.Module):
    def __init__(self, classes):
        super(LeNetSequentialOrderDict, self).__init__()

        self.features = nn.Sequential(OrderedDict({
            'conv1': nn.Conv2d(3, 6, 5),
            'relu1': nn.ReLU(inplace=True),
            'pool1': nn.MaxPool2d(kernel_size=2, stride=2),

            'conv2': nn.Conv2d(6, 16, 5),
            'relu2': nn.ReLU(inplace=True),
            'pool2': nn.MaxPool2d(kernel_size=2, stride=2),
        }))

        self.classifier = nn.Sequential(OrderedDict({
            'fc1': nn.Linear(16*5*5, 120),
            'relu3': nn.ReLU(),

            'fc2': nn.Linear(120, 84),
            'relu4': nn.ReLU(inplace=True),

            'fc3': nn.Linear(84, classes),
        }))

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size()[0], -1)
        x = self.classifier(x)
        return x

小結:

  • 順序性:各網絡層之間嚴格按照順序構建
  • 自帶 forward():自帶的 forward() 裏,通過for循環依次執行前向傳播運算
ModuleList

nn.ModuleList 是 nn.module 的容器,用於包裝一組網絡層,以迭代方式調用網絡層

主要方法:

  • append():在ModuleList後面添加網絡層
  • extend():拼接兩個ModuleList
  • insert():指定在ModuleList中位置插入網絡層

下面例子是創建一個20層的 FC 網絡模型

class ModuleList(nn.Module):
    def __init__(self):
        super(ModuleList, self).__init__()
        self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(20)])

    def forward(self, x):
        for i, linear in enumerate(self.linears):
            x = linear(x)
        return x
ModuleLDict

nn.ModuleDict 是 nn.module 的容器,用於包裝一組網絡層,以索引方式調用網絡層主要方法:

  • clear():清空 ModuleDict
  • items():返回可迭代的鍵值對(key-value pairs)
  • keys():返回字典的鍵(key)
  • values():回字典的值(value)
  • pop():返回一對鍵值,並從字典中刪除
class ModuleDict(nn.Module):
    def __init__(self):
        super(ModuleDict, self).__init__()
        self.choices = nn.ModuleDict({
            'conv': nn.Conv2d(10, 10, 3),
            'pool': nn.MaxPool2d(3)
        })

        self.activations = nn.ModuleDict({
            'relu': nn.ReLU(),
            'prelu': nn.PReLU()
        })

    def forward(self, x, choice, act):
        x = self.choices[choice](x)
        x = self.activations[act](x)
        return x


net = ModuleDict()

fake_img = torch.randn((4, 10, 32, 32))

output = net(fake_img, 'conv', 'relu')

print(output)

模型容器總結
  • nn.Sequential:順序性,各網絡層之間嚴格按順序執行,常用於 block 構建
  • nn.ModuleList:迭代性,常用於大量重複網絡層構建,通過 for 循環實現重複構建
  • nn.ModuleDict:索引性,常用於可選擇的網絡層
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章