什么是層?什么是塊?
在深度學習中,層(Layer)?和塊(Block)?是構建神經網絡的核心概念,尤其在 PyTorch、TensorFlow 等框架中,二者既緊密關聯又有明確分工。理解它們的定義、關系和用法,是掌握神經網絡設計的基礎。
一、核心定義
1. 層(Layer)
層是神經網絡中最基本的計算單元,實現特定的數學操作(如線性變換、卷積、激活函數等)。
- 功能單一:通常只完成一種特定計算(如
nn.Linear
實現線性變換y = Wx + b
,nn.ReLU
實現激活函數y = max(0, x)
)。- 可復用性低:單個層一般不單獨使用,需與其他層組合才能完成復雜任務。
2. 塊(Block)
塊是由多個層(或其他塊)組合而成的復雜單元,封裝了一組相關的計算邏輯。
- 功能復合:可以包含多個層(如 “卷積層 + 激活函數 + 池化層” 組成的卷積塊),甚至嵌套其他塊(如 ResNet 中的殘差塊包含多個卷積塊)。
- 可復用性高:塊可以被看作 “超級層”,在網絡中重復使用(如 Transformer 中的 Encoder 塊被重復堆疊)。
二、本質關系:層是塊的 “原子”,塊是層的 “組合”
在 PyTorch 中,所有層和塊都繼承自
nn.Module
類,因此它們在接口上保持一致(都有__init__
初始化方法和forward
前向傳播方法)。
- 層是 “最小化的塊”:單個層(如
nn.Linear
)可以視為只包含一個計算步驟的特殊塊。- 塊是 “結構化的層集合”:塊通過組合多個層(或塊),實現更復雜的功能(如特征提取、殘差連接等)。
三、具體區別與聯系
維度 層(Layer) 塊(Block) 組成 單一計算單元(如矩陣乘法、卷積) 多個層 / 塊的組合(如 “線性層 + 激活函數 + dropout”) 功能 實現基礎操作(如線性變換、非線性激活) 實現復雜功能(如特征提取、殘差連接、注意力機制) 復用性 低(通常作為塊的組成部分) 高(可作為模塊重復嵌入到不同網絡中) 示例 nn.Linear
、nn.Conv2d
、nn.ReLU
nn.Sequential
、ResNet 的殘差塊、Transformer 的 Encoder 塊
為什么需要自定義 Sequential?
雖然 PyTorch 已有
nn.Sequential
,但自定義版本有以下用途:
- 學習原理:理解 PyTorch 如何管理模塊和參數。
- 擴展功能:例如,添加日志記錄、中間輸出緩存等功能。
- 簡化接口:在特定場景下提供更簡潔的 API。
MySequential
和nn.Sequential
功能基本相同,有那些細微差異?
特性 MySequential
nn.Sequential
模塊命名 自動生成索引(如 "0", "1") 可自定義名稱(如 nn.Sequential(relu=nn.ReLU())
)初始化方式 接收任意數量的模塊 接收多個模塊或有序字典 實現復雜度 約 20 行代碼 更復雜(支持更多特性)
完整代碼
"""
文件名: 5.1
作者: 墨塵
日期: 2025/7/13
項目名: dl_env
備注: 輸出結果不一樣,是因為Linear權值是隨機初始化的
"""
import torch
from torch import nn
from torch.nn import functional as F"""多層感知機,使用自定義塊實現"""
class MLP(nn.Module):# 用模型參數聲明層。這里,我們聲明兩個全連接的層def __init__(self):# 調用MLP的父類Module的構造函數來執行必要的初始化。# 這樣,在類實例化時也可以指定其他函數參數,例如模型參數params(稍后將介紹)super().__init__()self.hidden = nn.Linear(20, 256) # 隱藏層self.out = nn.Linear(256, 10) # 輸出層# 定義模型的前向傳播,即如何根據輸入X返回所需的模型輸出def forward(self, X):# 注意,這里我們使用ReLU的函數版本,其在nn.functional模塊中定義。return self.out(F.relu(self.hidden(X)))"""自定義順序塊,按傳入順序連接多個模塊"""# MySequential的核心目標是:將多個層按傳入的順序連接起來,前一層的輸出作為后一層的輸入
class MySequential(nn.Module):def __init__(self, *args):"""初始化順序塊,接收任意數量的PyTorch模塊參數:*args: 任意數量的nn.Module子類實例(如nn.Linear, nn.ReLU等)"""# 調用父類nn.Module的構造函數,完成必要的初始化super().__init__()# 遍歷所有傳入的模塊for idx, module in enumerate(args):# 將模塊添加到PyTorch內置的有序字典_modules中# 鍵: 模塊的索引(字符串形式)# 值: 具體的模塊實例# _modules是nn.Module的特殊屬性,PyTorch會自動管理其中的所有模塊# 包括參數初始化、設備同步、序列化等self._modules[str(idx)] = moduledef forward(self, X):"""定義前向傳播邏輯,按順序依次調用所有模塊參數:X: 輸入張量返回:經過所有模塊處理后的輸出張量"""# 按_modules中保存的順序遍歷所有模塊# OrderedDict保證了遍歷時模塊的順序與添加時一致for block in self._modules.values():# 將輸入數據依次通過每個模塊# 前一個模塊的輸出直接作為下一個模塊的輸入X = block(X)# 返回最終輸出return X"""在前向傳播函數中執行代碼"""
class FixedHiddenMLP(nn.Module):def __init__(self):"""自定義神經網絡模塊,展示PyTorch中的特殊用法:1. 使用固定權重(訓練期間不更新)2. 層參數共享3. 前向傳播中的控制流"""super().__init__()# 創建固定權重矩陣(隨機初始化,但不參與訓練)# requires_grad=False:禁用梯度計算,訓練時權重不會更新self.rand_weight = torch.rand((20, 20), requires_grad=False)# 定義可訓練的線性層self.linear = nn.Linear(20, 20)def forward(self, X):"""定義前向傳播邏輯,包含非常規操作:1. 使用固定權重矩陣進行矩陣乘法2. 復用同一個線性層(參數共享)3. 使用while循環控制輸出規模"""# 第一層:可訓練的線性變換X = self.linear(X)# 第二層:使用固定隨機權重進行矩陣乘法,添加偏置1,再通過ReLU激活# torch.mm:矩陣乘法# self.rand_weight在訓練過程中保持不變X = F.relu(torch.mm(X, self.rand_weight) + 1)# 第三層:復用第一個線性層(參數共享)# 相當于兩個不同層共享同一組參數X = self.linear(X)# 控制流:如果張量X的絕對值之和大于1,則不斷將X除以2# 這是一個自定義的輸出規范化策略while X.abs().sum() > 1:X /= 2# 返回標量值:所有元素的和return X.sum()if __name__ == '__main__':"""多層感知機構建一個包含輸入層→隱藏層→輸出層的全連接神經網絡,對隨機生成的輸入數據進行計算并輸出結果。"""# 線性變換負責特征的線性映射,激活函數負責注入非線性,兩者交替使用才能讓網絡有能力學習復雜數據。# 傳播過程拆解:# 輸入X(形狀(2,20))→ 第 1 層線性變換 → 輸出X1(形狀(2,256))# X1→ 第 2 層 ReLU 激活 → 輸出X2(形狀(2,256),所有元素非負)# X2→ 第 3 層線性變換 → 輸出Y(形狀(2,10))# 1. 定義神經網絡# 這個前向傳播函數非常簡單: 它將列表中的每個塊連接在一起,將每個塊的輸出作為下一個塊的輸入。net = nn.Sequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))# 2. 生成輸入數據X = torch.rand(2, 20)# 3. 前向傳播并打印輸出print(net(X))"""自定義塊"""net = MLP()print(net(X))"""順序塊"""net = MySequential(nn.Linear(20, 256), nn.ReLU(), nn.Linear(256, 10))print(net(X))"""在前向傳播函數中執行代碼"""net = FixedHiddenMLP()print(net(X))