1. ResNet
2. nn.Module
-
在PyTorch中
nn.Module
類是用於 定義網絡中 前向結構 的父類 -
當要定義自己的網絡結構時就要繼承這個類
-
現有的那些類式接口(如
nn.Linear
、nn.BatchNorm2d
、nn.Conv2d
等)也是繼承這個類的 -
nn.Module
類可以嵌套若干nn.Module
的對象,來形成網絡結構的嵌套組合 -
下面記錄
nn.Module
的功能
3. 繼承nn.Module類的模塊
-
使用其初始化函數創建對象,然后調用forward函數就能使用里面的前向計算過程。
-
包括:Linear、ReLU、Sigmoid、Conv2d、ConvTransposed2d、Dropout...
4. 容器nn.Sequential()
-
nn.Sequential
是一個Sequential容器 -
模塊將按照 構造函數中傳遞的順序 添加到模塊中。
-
通俗的說,就是根據自己的需求,把不同的函數組合成一個(小的)模塊使用 或者 把組合的模塊添加到自己的網絡中。
from torch import nn
conv_module = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
# 具體的使用方法
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv_module = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)
def forward(self, input):
out = self.conv_module(input)
return out
Tip:
-
使用
nn.Module
,我們可以根據自己的需求改變傳播過程,如RNN等; -
如果需要快速構建或者不需要過多的過程,直接使用
nn.Sequential
。
5. 模塊內部參數管理
可以用 .parameters()
或者 .named_parameters()
返回 其內的所有參數的迭代器:
from torch import nn
net = nn.Sequential(
nn.Linear(4, 2), # 輸入維度4,輸出維度2 的線性層
nn.Linear(2, 2)
)
print(list(net.parameters()))
# [Parameter containing:
# tensor([[-0.0829, 0.3424, 0.4514, -0.3981],
# [-0.3401, 0.1429, -0.4525, 0.4991]], requires_grad=True),
# Parameter containing:
# tensor([-0.0321, 0.0872], requires_grad=True),
# Parameter containing:
# tensor([[ 0.0628, 0.3092],
# [ 0.5135, -0.4738]], requires_grad=True),
# Parameter containing:
# tensor([-0.4249, 0.3921], requires_grad=True)]
print(dict(net.named_parameters()))
# {'0.weight': Parameter containing:
# tensor([[-0.0829, 0.3424, 0.4514, -0.3981],
# [-0.3401, 0.1429, -0.4525, 0.4991]], requires_grad=True),
# '0.bias': Parameter containing:
# tensor([-0.0321, 0.0872], requires_grad=True),
# '1.weight': Parameter containing:
# tensor([[ 0.0628, 0.3092],
# [ 0.5135, -0.4738]], requires_grad=True),
# '1.bias': Parameter containing:
# tensor([-0.4249, 0.3921], requires_grad=True)}
Tip:
-
以第0層為例,weight.shape=[2,4],輸出維度在前,輸入維度在后,和Linear定義的時候相反;
-
相比
.parameters()
,.named_parameters()
能看到參數名,默認情況下會使用 所在的層數 + 參數類型 的方式,從0層開始編號; -
使用優化器時,可以直接調用
nn.Module
類定義的參數。
optimizer = optim.SGD(net.parameters(), lr=learning_rate)
6. 模塊樹形結構
-
模塊之間通過 嵌套組合 會形成樹形結構,使用
.children()
可以 獲取其直接孩子結點 -
使用
.modules()
可以 獲取其所有子結點。
from torch import nn
class BaseNet(nn.Module):
def __init__(self):
# super(BaseNet, self).__init__() # python2寫法
super().__init__()
self.net = nn.Linear(4, 3) # 輸入4維,輸出3維的線性層
def forward(self, x):
return self.net(x)
class MyNet(nn.Module):
def __init__(self):
super(MyNet, self).__init__() # 使用Seq容器組合了三個模塊
self.net = nn.Sequential(
BaseNet(),
nn.ReLU(),
nn.Linear(3, 2)
)
def forward(self, x):
return self.net(x)
my_net = MyNet()
print(list(my_net.children())) # 直接孩子
# [Sequential(
# (0): BaseNet(
# (net): Linear(in_features=4, out_features=3, bias=True)
# )
# (1): ReLU()
# (2): Linear(in_features=3, out_features=2, bias=True)
# )]
print(list(my_net.modules())) # 所有孩子
# [MyNet(
# (net): Sequential(
# (0): BaseNet(
# (net): Linear(in_features=4, out_features=3, bias=True)
# )
# (1): ReLU()
# (2): Linear(in_features=3, out_features=2, bias=True)
# )
# ),
# Sequential(
# (0): BaseNet(
# (net): Linear(in_features=4, out_features=3, bias=True)
# )
# (1): ReLU()
# (2): Linear(in_features=3, out_features=2, bias=True)
# ),
# BaseNet(
# (net): Linear(in_features=4, out_features=3, bias=True)
# ),
# Linear(in_features=4, out_features=3, bias=True), ReLU(), Linear(in_features=3, out_features=2, bias=True)]
-
.children()
只返回自己的直系孩子列表,在這里也就是一個nn.Sequential
容器。 -
而使用
.modules()
獲取的所有孩子是包括自己的。
7. 設備
- 使用
.to(device)
可以在具體的CPU/GPU上切換,這會將其所有子模塊也一起轉移過去運行。
device = torch.device('cuda')
net = Net()
net.to(device)
Tip:
-
模塊的
.to(device)
是原地操作並返回自己的引用 -
而Tensor的
.to(device)
不會在當前Tensor上操作,返回的 才是在目標設備上對應創建的Tensor- 所以
net = MLP().to(device)
。
- 所以
8. 加載和保存
-
使用
torch.load()
載入檢查點文件,然后傳入net.load_state_dict()
網絡模型設置參數 -
把當前類所有狀態
net.state_dict()
傳入torch.save()
保存到文件中去。 -
在訓練過程中,每隔一定的迭代次數可以保存一下檢查點,將當前網絡模型的狀態傳進去。
-
eg.
ckpt.mdl
是網絡的一個中間狀態
net.load_state_dict(torch.load('ckpt.mdl'))
# train...
torch.save(net.state_dict(), 'ckpt.mdl')
9. 訓練和測試模式
-
前面的學習中提到
Dropout
和Batch Normalization
在訓練和測試中的行為不同 -
需要對每一個
nn.Module()
模塊 單獨設置訓練狀態和測試狀態 -
可以直接為網絡使用方法
.train()
切換到訓練模式,使用.eval()
方法 切換到測試模式。
# train
net.train()
...
# test
net.eval()
...
10. 實現自定義的類
10.1 nn.Sequential用法
class Flatten(nn.Module):
def __init__(self):
super(Flatten, self).__init__()
def forward(self, input):
return input.view(input.size(0), -1)
class TestNet(nn.Module):
def __init__(self):
super(TestNet, self).__init__()
self.net = nn.Sequential(nn.Conv2d(1, 16, stride=1, padding=1),
nn.MaxPool2d(2, 2),
Flatten(),
nn.Linear(1*14*14, 10))
def forward(self, x):
return self.net(x)
Tip:
-
只有類才能寫到
Sequential
里面,比如F.relu
不可以,要重新定義nn.ReLU
-
如,Flatten類,Reshape類都需要自己實現!
10.2 nn.Parameters 和 .parameters()
-
如果在繼承
nn.Module
類來實現模塊時,出現需要操作Tensor的部分,那么應當使用nn.Parameters
(注意這里P大寫) 將其包裝起來。 -
如果直接使用Tensor,那么就不能用
.parameters()
(注意這里p小寫)獲取到所有參數,也就不能直接傳給優化器去記錄要優化的這些參數了。
class MyLinear(nn.Module):
def __init__(self, inp, outp):
super(MyLinear, self).__init__()
# 線性層的參數w和b,對w而言輸出維度放在前面
# requires_grad = True
self.w = nn.Parameter(torch.randn(outp, inp)) # w: [n^l, n^{l-1}]
self.b = nn.Parameter(torch.randn(outp))
def forward(self, x):
x = x @ self.w.t() + self.b
return x
Tip:
-
上述MyLinear 和 pytorch自帶的Linear類效果一樣
-
使用
nn.Parameter()
包裝Tensor時,自動設置了requires_grad=True
-
即默認情況下認為它是反向傳播優化的參數。