nn.Module()
https://zhuanlan.zhihu.com/p/340453841
nn.Module()
nn.Module是nn中十分重要的類,包含網絡各層的定義及forward方法。
pytorch
里面一切自定義操作基本上都是繼承nn.Module
類來實現的。 簡單的說 torch的核心是Module類,所有神經網絡模塊的基類。 模塊也可以包含其他模塊,從而可以將它們嵌套在樹形結構中
如何定義自己的網絡:
- 我們在定義自已的網絡的時候,需要繼承
nn.Module
類, - 重新實現構造函數
__init__
構造函數 - 重新實現
forward
這兩個方法。
注意事項:
-
一般把網絡中具有可學習參數的層(如全連接層、卷積層等)(模塊類的初始化) 放在構造函數
__init__()
中 -
forward方法是必須要重寫的,它是實現模型的功能,實現各個層之間的連接關系的核心
import torch.nn as nn
import torch.nn.functional as F
class Model(nn.Module):
# nn.Module的子類函數必須在構造函數中執行父類的構造函數
def __init__(self):
super(Model, self).__init__() # 等價與nn.Module.__init__()
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)
def forward(self, x):
x = F.relu(self.conv1(x))
return F.relu(self.conv2(x))
model=Model()
print(model)
#Model(
# (conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
# (conv2): Conv2d(20, 20, kernel_size=(5, 5), stride=(1, 1))
# )
1、核心
forward(*input) # 基函數中沒有是實現需要在子函數中實現
apply(fn) # 將Module及其所有的SubModule傳進給定的fn函數操作
add_module(name,module) # 將子模塊加入當前的模塊中,被添加的模塊可以name來獲取
apply(fn)
# 將net中的子模型Linear的 weight設置成 1,bias設置為0.
# Example:
def init_weights(m):
# print(m)
if type(m) == nn.Linear:
m.weight.data.fill_(1.0)
m.bias.data.fill_(0)
net = nn.Sequential(nn.Linear(2, 2))
net.apply(init_weights)
print(list(net.named_parameters()))
# >>> tensor([[1., 1.], [1., 1.]], requires_grad=True)), ('0.bias', Parameter containing:tensor([0., 0.], requires_grad=True))]
state_dict()
返回一個包含模塊完整狀態的字典
>>> net = torch.nn.Linear(2, 2)
>>> net.state_dict()
OrderedDict([('weight', tensor([[-0.3558, 0.2153],
[-0.2785, 0.6982]])), ('bias', tensor([ 0.5771, -0.6232]))])
>>> net.state_dict().keys()
odict_keys(['weight', 'bias'])
add_module()
add_module(name,module)
# 添加子模塊到當前模塊中
# 該添加子模塊能夠使用給定的名字name來訪問
"""輸入參數
name(string) 子模塊的名字
module (Module) 添加到該模塊中的子模塊
"""
2、查看
使用 nn.Module 可以對網絡中的參數進行有效的管理
parameters() # 返回一個 包含模型所有參數 的迭代器
buffers()
children() # 返回當前模型 子模塊的迭代器, 不遞歸 包含子模塊
modules() # 返回一個包含 當前模型 所有模塊的迭代器,遞歸所有葉子module
與之對應的四個
named_parameters()
named_buffers()
named_children()
named_modules()
net = nn.Sequential(
nn.Linear(in_features=4, out_features=2),
nn.Linear(in_features=2, out_features=2)
)
# 隱藏層的編號是從0開始的
list(net.parameters())[0] # [0]是layer0的w
list(net.parameters())[3].shape # [3]是layer1的b
dict(net.named_parameters()).items() # 返回所有層的參數
optimizer = optim.SGD(net.parameters(), lr=1e-3)
# 輸出
torch.Size([2, 4])
torch.Size([2])
dict_items([('0.weight', Parameter containing:
tensor([[ 0.0195, 0.4698, -0.4913, -0.3336],
[ 0.1422, 0.2908, -0.2469, 0.0583]], requires_grad=True)), ('0.bias', Parameter containing:
tensor([-0.4704, -0.1133], requires_grad=True)), ('1.weight', Parameter containing:
tensor([[-0.6511, 0.2442],
[ 0.5658, 0.4419]], requires_grad=True)), ('1.bias', Parameter containing:
tensor([ 0.0114, -0.5664], requires_grad=True))])
3、設置
# 設置為訓練或者測試模式
train() # 將module設置為 training mode,只影響dropout和batchNorm
eval() # 將模型設置成evaluation模式,只影響dropout和batchNorm
requires_grad_() # 用於設置self.parameters()是否需要record梯度,默認情況下是True
zero_grad() # 用於設置self.parameters()的gradients為零
to() # 它可以當成三種函數來使用
# 選擇設備
cpu(device,id=None) #
cuda(device,id=None)
to() # 三種函數來使用
"""
1、張量 to(tensor, non_blocking=False)
2、類型 to(dtype, non_blocking=False) 【cpu,cuda,type.float,double,half】
3、設備 to(device=None, dtype=None, non_blocking=False)
4、存儲 to(memory_format=torch.channels_last)
"""
# 案例1
>>> linear = nn.Linear(2, 2)
>>> print(linear.weight)
Parameter containing:
tensor([[ 0.0822, -0.5305],
[ 0.3486, -0.3749]], requires_grad=True)
>>> linear.to(torch.double)
>>> print(linear.weight)
Parameter containing:
tensor([[ 0.0822, -0.5305],
[ 0.3486, -0.3749]], dtype=torch.float64, requires_grad=True)
# 案例2
>>> gpu1 = torch.device("cuda:0")
>>> linear.to(gpu1, dtype=torch.half, non_blocking=True)
>>> print(linear.weight)
Parameter containing:
tensor([[ 0.0822, -0.5303],
[ 0.3486, -0.3750]], device='cuda:0', dtype=torch.float16,requires_grad=True)
# 案例3
>>> cpu = torch.device("cpu")
>>> linear.to(cpu,dtype=torch.double)
>>> print(linear.weight)
Parameter containing:
tensor([[ 0.0822, -0.5303],
[ 0.3486, -0.3750]], dtype=torch.float64, requires_grad=True)
4、注冊
register_parameter # 向self._parameters注冊新元素
register_buffer # 向self._buffers注冊新元素
register_backward_hook # 向self._backward_hooks注冊新元素
register_forward_pre_hook # 向self._forward_pre_hooks注冊新元素
register_forward_hook # 向self._forward_hooks注冊新元素
5、轉換
可以很方便的將所有運算都轉入到 GPU 上去,使用.device() 函數
to() #
type() # type函數是將所有parameters和buffers都轉成指定的目標類型dst_type
double() # 將parameters和buffers的數據類型轉換成double
float() # 將parameters和buffers的數據類型轉換成float
half() # 將parameters和buffers的數據類型轉換成half
6、加載
可以很方便的進行 save 和 load,以防止突然發生的斷點和系統崩潰現象
load_state_dict(state_dict, strict=True)
# 將state_dict中的參數和緩沖區復制到此模塊及其后代中。如果strict為真,則state_dict的鍵必須與該模塊的state_dict()函數返回的鍵完全匹配。
"""
state_dict (dict) – 保存parameters和persistent buffers的字典。
將state_dict中的parameters和buffers復制到此module和它的后代中。
state_dict中的key必須和 model.state_dict()返回的key一致。
"""
如何將模型連接起來
nn.Sequential()
nn.Sequential(*args)
Sequential 本質上是一個容器,只是繼承了 nn.Module()
,時序容器,Modules 會以他們傳入的順序被添加到容器中
- 繼承了
nn.Module()
class Sequential(Module): # 繼承Module
def __init__(self, *args): # 重寫了構造函數
def _get_item_by_idx(self, iterator, idx):
def __getitem__(self, idx):
def __setitem__(self, idx, module):
def __delitem__(self, idx):
def __len__(self):
def __dir__(self):
def forward(self, input): # 重寫關鍵方法forward
當你使用Sequential
時,Modules
會以傳入的順序來添加layer
到容器中,也可以傳入一個OrderedDict
添加模塊1
# Example of using Sequential
# 三種寫法
model = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
# 采用第一種方式,默認命名方式為 [0,1,2,3,4,...]
print(model)
print(model[2]) # 通過索引獲取第幾個層
'''運行結果為:
Sequential(
(0): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(1): ReLU()
(2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(3): ReLU()
)
Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
'''
添加模塊2
model = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(1,20,5)),
('relu1', nn.ReLU()),
('conv2', nn.Conv2d(20,64,5)),
('relu2', nn.ReLU())
]))
print(model)
print(model[2]) # 通過索引獲取第幾個層
'''運行結果為:
Sequential(
(conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(relu1): ReLU()
(conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(relu2): ReLU()
)
Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
'''
# model[2] 是正確的
# model["conv2"] 是錯誤的
# 這其實是由它的定義實現的,看上面的Sequenrial定義可知,只支持index訪問。
添加模塊3
# 繼承 nn.Module 的方法,可以自定義添加模塊
# add_module(name, module)
import torch.nn as nn
from collections import OrderedDict
model = nn.Sequential()
## 實際上使用的是 nn.module() 的屬性
model.add_module("conv1",nn.Conv2d(1,20,5))
model.add_module('relu1', nn.ReLU())
model.add_module('conv2', nn.Conv2d(20,64,5))
model.add_module('relu2', nn.ReLU())
print(model)
print(model[2]) # 通過索引獲取第幾個層
'''運行結果為:
Sequential(
(conv1): Conv2d(1, 20, kernel_size=(5, 5), stride=(1, 1))
(relu1): ReLU()
(conv2): Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
(relu2): ReLU()
)
Conv2d(20, 64, kernel_size=(5, 5), stride=(1, 1))
'''
nn.ModuleList()
class torch.nn.ModuleList(modules=None)
將你的模型保存在一個list中,可以像python list一樣被索引, moduleList 中包含的modules已經被正確的注冊,對所有module method可見
ModuleList 具有和List 相似的用法,實際上可以把它視作是 Module 和 list 的結合。
# 輸入參數 modules (list, optional) – 將要被添加到MuduleList中的 modules 列表
class Model(nn.Module):
def __init__(self):
super().__init__()
self.layers=nn.ModuleList([
nn.Linear(1,10), nn.ReLU(),
nn.Linear(10,1)])
def forward(self,x):
out = x
for layer in self.layers:
out = layer(out)
return out
model = Model()
print(model)
Model(
(layers): ModuleList(
(0): Linear(in_features=1, out_features=10, bias=True)
(1): ReLU()
(2): Linear(in_features=10, out_features=1, bias=True)
)
)
append(module)
class Model(nn.Module):
def __init__(self):
super().__init__()
self.layers=nn.ModuleList([
nn.Linear(1,10), nn.ReLU(),
nn.Linear(10,1)])
self.layers.append(nn.Linear(1, 5))
def forward(self,x):
out = x
for layer in self.layers:
out = layer(out)
return out
extend(modules)
extend()
,必須也為一個list
self.layers.extend([nn.Linear(size1, size2) for i in range(1, num_layers)])