模組¶
PyTorch 使用模組來表示神經網路。模組是
有狀態計算的建構塊。 PyTorch 提供了一個強大的模組庫,並且可以輕鬆定義新的自定義模組,從而可以輕鬆構建複雜的多層神經網路。
與 PyTorch 的 autograd 系統緊密整合。 模組使 PyTorch 的 Optimizers 可以輕鬆指定可學習的參數以進行更新。
易於使用和轉換。 模組可以輕鬆保存和還原、在 CPU / GPU / TPU 裝置之間傳輸、修剪、量化等等。
本筆記描述了模組,適用於所有 PyTorch 使用者。由於模組是 PyTorch 的基礎,本筆記中的許多主題在其他筆記或教學文件中都有詳細說明,此處也提供了許多這些文件的連結。
一個簡單的自定義模組¶
首先,讓我們看一下 PyTorch 的 Linear
模組的一個更簡單的自定義版本。此模組將仿射轉換應用於其輸入。
import torch
from torch import nn
class MyLinear(nn.Module):
def __init__(self, in_features, out_features):
super().__init__()
self.weight = nn.Parameter(torch.randn(in_features, out_features))
self.bias = nn.Parameter(torch.randn(out_features))
def forward(self, input):
return (input @ self.weight) + self.bias
這個簡單的模組具有以下模組的基本特徵
它繼承自基礎 Module 類別。 所有模組都應該子類化
Module
,以便與其他模組組合。它定義了一些用於計算的「狀態」。 在這裡,狀態包含隨機初始化的
weight
和bias
張量,它們定義了仿射轉換。由於它們中的每一個都被定義為Parameter
,因此它們會被註冊到模組中,並會自動追蹤並從呼叫parameters()
返回。參數可以被認為是模組計算的「可學習」方面(稍後會詳細介紹)。請注意,模組不一定需要有狀態,也可以是無狀態的。它定義了一個執行計算的 forward() 函數。 對於這個仿射轉換模組,輸入與
weight
參數進行矩陣相乘(使用@
簡寫符號),並添加到bias
參數以產生輸出。更一般地說,模組的forward()
實作可以執行涉及任意數量的輸入和輸出的任意計算。
這個簡單的模組演示了模組如何將狀態和計算封裝在一起。可以建構並呼叫此模組的實例
m = MyLinear(4, 3)
sample_input = torch.randn(4)
m(sample_input)
: tensor([-0.3037, -1.0413, -4.2057], grad_fn=<AddBackward0>)
請注意,模組本身是可呼叫的,並且呼叫它會調用其 forward()
函數。這個名稱是指「前向傳遞」和「反向傳遞」的概念,它們適用於每個模組。「前向傳遞」負責將模組表示的計算應用於給定的輸入(如上面的程式碼片段所示)。「反向傳遞」計算模組輸出相對於其輸入的梯度,這些梯度可用於通過梯度下降方法「訓練」參數。PyTorch 的 autograd 系統會自動處理這種反向傳遞計算,因此無需為每個模組手動實作 backward()
函數。透過連續的前向/反向傳遞訓練模組參數的過程在 使用模組進行神經網路訓練 中有詳細介紹。
可以透過呼叫 parameters()
或 named_parameters()
來迭代模組註冊的完整參數集,後者包括每個參數的名稱
for parameter in m.named_parameters():
print(parameter)
: ('weight', Parameter containing:
tensor([[ 1.0597, 1.1796, 0.8247],
[-0.5080, -1.2635, -1.1045],
[ 0.0593, 0.2469, -1.4299],
[-0.4926, -0.5457, 0.4793]], requires_grad=True))
('bias', Parameter containing:
tensor([ 0.3634, 0.2015, -0.8525], requires_grad=True))
通常,模組註冊的參數是模組計算中應該「學習」的方面。本筆記的後續章節將展示如何使用 PyTorch 的其中一個 Optimizers 來更新這些參數。不過,在我們開始之前,讓我們先檢查一下模組如何彼此組合。
模組作為建構區塊¶
模組可以包含其他模組,使其成為開發更精細功能的有用建構區塊。最簡單的方法是使用 Sequential
模組。它允許我們將多個模組串聯在一起
net = nn.Sequential(
MyLinear(4, 3),
nn.ReLU(),
MyLinear(3, 1)
)
sample_input = torch.randn(4)
net(sample_input)
: tensor([-0.6749], grad_fn=<AddBackward0>)
請注意,Sequential
會自動將第一個 MyLinear
模組的輸出作為輸入饋送到 ReLU
中,並將該輸出作為輸入饋送到第二個 MyLinear
模組中。如圖所示,它僅限於具有單個輸入和輸出的模組的依序鏈接。
通常,建議為超出最簡單用例的任何內容定義自定義模組,因為這可以完全靈活地控制子模組如何用於模組的計算。
例如,這是一個實現為自定義模組的簡單神經網路
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super().__init__()
self.l0 = MyLinear(4, 3)
self.l1 = MyLinear(3, 1)
def forward(self, x):
x = self.l0(x)
x = F.relu(x)
x = self.l1(x)
return x
此模組由兩個「子元件」或「子模組」(l0
和 l1
)組成,它們定義了神經網路的層,並用於模組的 forward()
方法中的計算。可以透過呼叫 children()
或 named_children()
來迭代模組的直接子元件
net = Net()
for child in net.named_children():
print(child)
: ('l0', MyLinear())
('l1', MyLinear())
要深入到僅僅是直接子元件之外,modules()
和 named_modules()
會遞迴地迭代模組及其子模組
class BigNet(nn.Module):
def __init__(self):
super().__init__()
self.l1 = MyLinear(5, 4)
self.net = Net()
def forward(self, x):
return self.net(self.l1(x))
big_net = BigNet()
for module in big_net.named_modules():
print(module)
: ('', BigNet(
(l1): MyLinear()
(net): Net(
(l0): MyLinear()
(l1): MyLinear()
)
))
('l1', MyLinear())
('net', Net(
(l0): MyLinear()
(l1): MyLinear()
))
('net.l0', MyLinear())
('net.l1', MyLinear())
有時,模組需要動態定義子模組。ModuleList
和 ModuleDict
模組在這裡很有用;它們從列表或字典註冊子模組
class DynamicNet(nn.Module):
def __init__(self, num_layers):
super().__init__()
self.linears = nn.ModuleList(
[MyLinear(4, 4) for _ in range(num_layers)])
self.activations = nn.ModuleDict({
'relu': nn.ReLU(),
'lrelu': nn.LeakyReLU()
})
self.final = MyLinear(4, 1)
def forward(self, x, act):
for linear in self.linears:
x = linear(x)
x = self.activations[act](x)
x = self.final(x)
return x
dynamic_net = DynamicNet(3)
sample_input = torch.randn(4)
output = dynamic_net(sample_input, 'relu')
對於任何給定的模組,其參數包含其直接參數以及所有子模組的參數。這意味著呼叫 parameters()
和 named_parameters()
將會遞迴地包含子參數,以便方便地優化網路中的所有參數。
for parameter in dynamic_net.named_parameters():
print(parameter)
: ('linears.0.weight', Parameter containing:
tensor([[-1.2051, 0.7601, 1.1065, 0.1963],
[ 3.0592, 0.4354, 1.6598, 0.9828],
[-0.4446, 0.4628, 0.8774, 1.6848],
[-0.1222, 1.5458, 1.1729, 1.4647]], requires_grad=True))
('linears.0.bias', Parameter containing:
tensor([ 1.5310, 1.0609, -2.0940, 1.1266], requires_grad=True))
('linears.1.weight', Parameter containing:
tensor([[ 2.1113, -0.0623, -1.0806, 0.3508],
[-0.0550, 1.5317, 1.1064, -0.5562],
[-0.4028, -0.6942, 1.5793, -1.0140],
[-0.0329, 0.1160, -1.7183, -1.0434]], requires_grad=True))
('linears.1.bias', Parameter containing:
tensor([ 0.0361, -0.9768, -0.3889, 1.1613], requires_grad=True))
('linears.2.weight', Parameter containing:
tensor([[-2.6340, -0.3887, -0.9979, 0.0767],
[-0.3526, 0.8756, -1.5847, -0.6016],
[-0.3269, -0.1608, 0.2897, -2.0829],
[ 2.6338, 0.9239, 0.6943, -1.5034]], requires_grad=True))
('linears.2.bias', Parameter containing:
tensor([ 1.0268, 0.4489, -0.9403, 0.1571], requires_grad=True))
('final.weight', Parameter containing:
tensor([[ 0.2509], [-0.5052], [ 0.3088], [-1.4951]], requires_grad=True))
('final.bias', Parameter containing:
tensor([0.3381], requires_grad=True))
使用 to()
也可以輕鬆地將所有參數移動到不同的設備或更改其精度。
# Move all parameters to a CUDA device
dynamic_net.to(device='cuda')
# Change precision of all parameters
dynamic_net.to(dtype=torch.float64)
dynamic_net(torch.randn(5, device='cuda', dtype=torch.float64))
: tensor([6.5166], device='cuda:0', dtype=torch.float64, grad_fn=<AddBackward0>)
更廣泛地說,可以使用 apply()
函數將任意函數遞迴地應用於模組及其子模組。 例如,將自定義初始化應用於模組及其子模組的參數。
# Define a function to initialize Linear weights.
# Note that no_grad() is used here to avoid tracking this computation in the autograd graph.
@torch.no_grad()
def init_weights(m):
if isinstance(m, nn.Linear):
nn.init.xavier_normal_(m.weight)
m.bias.fill_(0.0)
# Apply the function recursively on the module and its submodules.
dynamic_net.apply(init_weights)
這些範例展示了如何通過模組組合形成精密的類神經網路,並方便地進行操作。 為了能夠以最少的樣板程式碼快速輕鬆地構建類神經網路,PyTorch 在 torch.nn
命名空間中提供了大量效能良好的模組,這些模組執行常見的類神經網路操作,如池化 (pooling)、卷積 (convolutions)、損失函數 (loss functions) 等。
在下一節中,我們將提供一個訓練類神經網路的完整範例。
如需更多資訊,請參閱:
PyTorch 提供的模組庫:torch.nn
定義類神經網路模組:https://pytorch.dev.org.tw/tutorials/beginner/examples_nn/polynomial_module.html
使用模組進行類神經網路訓練¶
一旦構建了網路,就必須對其進行訓練,並且可以使用 torch.optim
中的 PyTorch 優化器輕鬆地優化其參數。
# Create the network (from previous section) and optimizer
net = Net()
optimizer = torch.optim.SGD(net.parameters(), lr=1e-4, weight_decay=1e-2, momentum=0.9)
# Run a sample training loop that "teaches" the network
# to output the constant zero function
for _ in range(10000):
input = torch.randn(4)
output = net(input)
loss = torch.abs(output)
net.zero_grad()
loss.backward()
optimizer.step()
# After training, switch the module to eval mode to do inference, compute performance metrics, etc.
# (see discussion below for a description of training and evaluation modes)
...
net.eval()
...
在這個簡化的範例中,網路學習簡單地輸出零,因為任何非零輸出都會根據其絕對值通過使用 torch.abs()
作為損失函數來「懲罰」。雖然這不是一個非常有趣的任務,但訓練的關鍵部分都存在。
建立一個網路。
建立一個優化器(在本例中,是一個隨機梯度下降優化器),並將網路的參數與之關聯。
- 一個訓練迴圈…
取得一個輸入,
執行網路,
計算一個損失,
將網路參數的梯度歸零,
呼叫 loss.backward() 來更新參數的梯度,
呼叫 optimizer.step() 將梯度應用於參數。
在執行完上面的程式碼片段後,請注意網路的參數已更改。 特別是,檢查 l1
的 weight
參數的值表明,其值現在更接近於 0(正如預期的那樣)。
print(net.l1.weight)
: Parameter containing:
tensor([[-0.0013],
[ 0.0030],
[-0.0008]], requires_grad=True)
請注意,上述過程完全是在網路模組處於「訓練模式」時完成的。 模組預設為訓練模式,可以使用 train()
和 eval()
在訓練和評估模式之間切換。 根據它們所處的模式,它們的行為可能會有所不同。 例如,BatchNorm
模組在訓練期間維護一個運行平均值和變異數,當模組處於評估模式時,這些值不會更新。 一般來說,模組在訓練期間應處於訓練模式,並且僅在推論或評估時切換到評估模式。 以下是一個自定義模組的範例,該模組在兩種模式下的行為不同。
class ModalModule(nn.Module):
def __init__(self):
super().__init__()
def forward(self, x):
if self.training:
# Add a constant only in training mode.
return x + 1.
else:
return x
m = ModalModule()
x = torch.randn(4)
print('training mode output: {}'.format(m(x)))
: tensor([1.6614, 1.2669, 1.0617, 1.6213, 0.5481])
m.eval()
print('evaluation mode output: {}'.format(m(x)))
: tensor([ 0.6614, 0.2669, 0.0617, 0.6213, -0.4519])
訓練類神經網路通常很棘手。 如需更多資訊,請參閱:
模組狀態¶
在前一節中,我們演示了訓練模組的「參數」,即計算的可學習方面。 現在,如果我們要將訓練好的模型儲存到磁碟,我們可以通過儲存它的 state_dict
(即「狀態字典」)來實現。
# Save the module
torch.save(net.state_dict(), 'net.pt')
...
# Load the module later on
new_net = Net()
new_net.load_state_dict(torch.load('net.pt'))
: <All keys matched successfully>
模組的 state_dict
包含影響其計算的狀態。 這包括但不限於模組的參數。 對於某些模組,擁有超出參數範圍的狀態可能是有用的,這些狀態會影響模組計算但不可學習。 對於這種情況,PyTorch 提供了「緩衝區」的概念,包括「持久性」和「非持久性」緩衝區。 以下是模組可以擁有的各種狀態類型的概述。
參數:計算的可學習方面;包含在
state_dict
中。緩衝區:計算的不可學習方面。
持久性緩衝區:包含在
state_dict
中(即,在儲存和載入時序列化)。非持久性緩衝區:不包含在
state_dict
中(即,從序列化中排除)。
作為使用緩衝區的動機範例,考慮一個維護運行平均值的簡單模組。 我們希望運行平均值的當前值被視為模組的 state_dict
的一部分,以便在載入模組的序列化形式時將其還原,但我們不希望它是可學習的。 這個程式碼片段展示了如何使用 register_buffer()
來完成此操作。
class RunningMean(nn.Module):
def __init__(self, num_features, momentum=0.9):
super().__init__()
self.momentum = momentum
self.register_buffer('mean', torch.zeros(num_features))
def forward(self, x):
self.mean = self.momentum * self.mean + (1.0 - self.momentum) * x
return self.mean
現在,運行平均值的當前值被視為模組的 state_dict
的一部分,並且在從磁碟載入模組時將被正確還原。
m = RunningMean(4)
for _ in range(10):
input = torch.randn(4)
m(input)
print(m.state_dict())
: OrderedDict([('mean', tensor([ 0.1041, -0.1113, -0.0647, 0.1515]))]))
# Serialized form will contain the 'mean' tensor
torch.save(m.state_dict(), 'mean.pt')
m_loaded = RunningMean(4)
m_loaded.load_state_dict(torch.load('mean.pt'))
assert(torch.all(m.mean == m_loaded.mean))
如先前所述,緩衝區可以透過將它們標記為非持久性,從模組的 state_dict
中省略。
self.register_buffer('unserialized_thing', torch.randn(5), persistent=False)
持久性和非持久性緩衝區都會受到使用 to()
應用於整個模型的裝置/dtype 變更的影響。
# Moves all module parameters and buffers to the specified device / dtype
m.to(device='cuda', dtype=torch.float64)
可以使用 buffers()
或 named_buffers()
迭代模組的緩衝區。
for buffer in m.named_buffers():
print(buffer)
以下類別示範了在模組中註冊參數和緩衝區的各種方法。
class StatefulModule(nn.Module):
def __init__(self):
super().__init__()
# Setting a nn.Parameter as an attribute of the module automatically registers the tensor
# as a parameter of the module.
self.param1 = nn.Parameter(torch.randn(2))
# Alternative string-based way to register a parameter.
self.register_parameter('param2', nn.Parameter(torch.randn(3)))
# Reserves the "param3" attribute as a parameter, preventing it from being set to anything
# except a parameter. "None" entries like this will not be present in the module's state_dict.
self.register_parameter('param3', None)
# Registers a list of parameters.
self.param_list = nn.ParameterList([nn.Parameter(torch.randn(2)) for i in range(3)])
# Registers a dictionary of parameters.
self.param_dict = nn.ParameterDict({
'foo': nn.Parameter(torch.randn(3)),
'bar': nn.Parameter(torch.randn(4))
})
# Registers a persistent buffer (one that appears in the module's state_dict).
self.register_buffer('buffer1', torch.randn(4), persistent=True)
# Registers a non-persistent buffer (one that does not appear in the module's state_dict).
self.register_buffer('buffer2', torch.randn(5), persistent=False)
# Reserves the "buffer3" attribute as a buffer, preventing it from being set to anything
# except a buffer. "None" entries like this will not be present in the module's state_dict.
self.register_buffer('buffer3', None)
# Adding a submodule registers its parameters as parameters of the module.
self.linear = nn.Linear(2, 3)
m = StatefulModule()
# Save and load state_dict.
torch.save(m.state_dict(), 'state.pt')
m_loaded = StatefulModule()
m_loaded.load_state_dict(torch.load('state.pt'))
# Note that non-persistent buffer "buffer2" and reserved attributes "param3" and "buffer3" do
# not appear in the state_dict.
print(m_loaded.state_dict())
: OrderedDict([('param1', tensor([-0.0322, 0.9066])),
('param2', tensor([-0.4472, 0.1409, 0.4852])),
('buffer1', tensor([ 0.6949, -0.1944, 1.2911, -2.1044])),
('param_list.0', tensor([ 0.4202, -0.1953])),
('param_list.1', tensor([ 1.5299, -0.8747])),
('param_list.2', tensor([-1.6289, 1.4898])),
('param_dict.bar', tensor([-0.6434, 1.5187, 0.0346, -0.4077])),
('param_dict.foo', tensor([-0.0845, -1.4324, 0.7022])),
('linear.weight', tensor([[-0.3915, -0.6176],
[ 0.6062, -0.5992],
[ 0.4452, -0.2843]])),
('linear.bias', tensor([-0.3710, -0.0795, -0.3947]))])
如需更多資訊,請參閱:
模組初始化¶
預設情況下,由 torch.nn
提供的模組的參數和浮點緩衝區,會在模組實例化期間,使用一種歷史上被認為對該模組類型表現良好的初始化方案,初始化為 CPU 上的 32 位元浮點數值。在某些使用案例中,可能希望使用不同的 dtype、裝置 (例如 GPU) 或初始化技術進行初始化。
範例
# Initialize module directly onto GPU.
m = nn.Linear(5, 3, device='cuda')
# Initialize module with 16-bit floating point parameters.
m = nn.Linear(5, 3, dtype=torch.half)
# Skip default parameter initialization and perform custom (e.g. orthogonal) initialization.
m = torch.nn.utils.skip_init(nn.Linear, 5, 3)
nn.init.orthogonal_(m.weight)
請注意,上面示範的裝置和 dtype 選項也適用於為模組註冊的任何浮點緩衝區。
m = nn.BatchNorm2d(3, dtype=torch.half)
print(m.running_mean)
: tensor([0., 0., 0.], dtype=torch.float16)
雖然模組編寫者可以使用任何裝置或 dtype 來初始化其自訂模組中的參數,但良好的做法是預設也使用 dtype=torch.float
和 device='cpu'
。或者,您可以透過遵循上述慣例,為您的自訂模組提供這些領域的完全彈性,即所有 torch.nn
模組都遵循此慣例。
提供一個
device
建構函式 kwarg,它適用於模組註冊的任何參數/緩衝區。提供一個
dtype
建構函式 kwarg,它適用於模組註冊的任何參數/浮點緩衝區。僅在模組的建構函式中使用初始化函數(即來自
torch.nn.init
的函數)來初始化參數和緩衝區。請注意,這僅在使用skip_init()
時才需要;請參閱 此頁面 以獲得說明。
如需更多資訊,請參閱:
模組 Hook¶
在 使用模組進行神經網路訓練 中,我們示範了模組的訓練過程,該過程迭代地執行前向和後向傳遞,並在每次迭代中更新模組參數。為了更好地控制此過程,PyTorch 提供了 “hooks”,可以在前向或後向傳遞期間執行任意計算,甚至可以根據需要修改傳遞的執行方式。此功能的一些有用範例包括除錯、視覺化啟動、深入檢查梯度等。可以將 Hooks 新增到您自己尚未編寫的模組中,這意味著此功能可以應用於第三方或 PyTorch 提供的模組。
PyTorch 為模組提供兩種 hooks
前向 hooks 在前向傳遞期間被呼叫。可以使用
register_forward_pre_hook()
和register_forward_hook()
為給定模組安裝它們。這些 hooks 將分別在呼叫 forward 函數之前和之後立即被呼叫。或者,可以使用類似的register_module_forward_pre_hook()
和register_module_forward_hook()
函數為所有模組全域安裝這些 hooks。後向 hooks 在後向傳遞期間被呼叫。可以使用
register_full_backward_pre_hook()
和register_full_backward_hook()
安裝它們。當此模組的後向計算完成時,將會呼叫這些 hooks。register_full_backward_pre_hook()
將允許使用者存取輸出的梯度,而register_full_backward_hook()
將允許使用者存取輸入和輸出的梯度。或者,可以使用register_module_full_backward_hook()
和register_module_full_backward_pre_hook()
為所有模組全域安裝它們。
所有的 Hook 都允許使用者回傳一個更新後的值,這個值將會用於後續的計算中。因此,這些 Hook 可以用來執行任意程式碼,在模組的前向/反向傳播過程中,或是修改輸入/輸出,而不需要更改模組的 forward()
函式。
下方範例展示了前向和反向 Hook 的用法
torch.manual_seed(1)
def forward_pre_hook(m, inputs):
# Allows for examination and modification of the input before the forward pass.
# Note that inputs are always wrapped in a tuple.
input = inputs[0]
return input + 1.
def forward_hook(m, inputs, output):
# Allows for examination of inputs / outputs and modification of the outputs
# after the forward pass. Note that inputs are always wrapped in a tuple while outputs
# are passed as-is.
# Residual computation a la ResNet.
return output + inputs[0]
def backward_hook(m, grad_inputs, grad_outputs):
# Allows for examination of grad_inputs / grad_outputs and modification of
# grad_inputs used in the rest of the backwards pass. Note that grad_inputs and
# grad_outputs are always wrapped in tuples.
new_grad_inputs = [torch.ones_like(gi) * 42. for gi in grad_inputs]
return new_grad_inputs
# Create sample module & input.
m = nn.Linear(3, 3)
x = torch.randn(2, 3, requires_grad=True)
# ==== Demonstrate forward hooks. ====
# Run input through module before and after adding hooks.
print('output with no forward hooks: {}'.format(m(x)))
: output with no forward hooks: tensor([[-0.5059, -0.8158, 0.2390],
[-0.0043, 0.4724, -0.1714]], grad_fn=<AddmmBackward>)
# Note that the modified input results in a different output.
forward_pre_hook_handle = m.register_forward_pre_hook(forward_pre_hook)
print('output with forward pre hook: {}'.format(m(x)))
: output with forward pre hook: tensor([[-0.5752, -0.7421, 0.4942],
[-0.0736, 0.5461, 0.0838]], grad_fn=<AddmmBackward>)
# Note the modified output.
forward_hook_handle = m.register_forward_hook(forward_hook)
print('output with both forward hooks: {}'.format(m(x)))
: output with both forward hooks: tensor([[-1.0980, 0.6396, 0.4666],
[ 0.3634, 0.6538, 1.0256]], grad_fn=<AddBackward0>)
# Remove hooks; note that the output here matches the output before adding hooks.
forward_pre_hook_handle.remove()
forward_hook_handle.remove()
print('output after removing forward hooks: {}'.format(m(x)))
: output after removing forward hooks: tensor([[-0.5059, -0.8158, 0.2390],
[-0.0043, 0.4724, -0.1714]], grad_fn=<AddmmBackward>)
# ==== Demonstrate backward hooks. ====
m(x).sum().backward()
print('x.grad with no backwards hook: {}'.format(x.grad))
: x.grad with no backwards hook: tensor([[ 0.4497, -0.5046, 0.3146],
[ 0.4497, -0.5046, 0.3146]])
# Clear gradients before running backward pass again.
m.zero_grad()
x.grad.zero_()
m.register_full_backward_hook(backward_hook)
m(x).sum().backward()
print('x.grad with backwards hook: {}'.format(x.grad))
: x.grad with backwards hook: tensor([[42., 42., 42.],
[42., 42., 42.]])
進階功能¶
PyTorch 也提供了一些設計用於模組的更進階功能。所有這些功能都適用於自定義編寫的模組,但需要注意的是,某些功能可能要求模組符合特定的約束才能被支援。關於這些功能及其對應需求的深入討論,請參閱以下連結。
性能分析¶
PyTorch Profiler 可以幫助識別模型中的性能瓶頸。 它可以測量並輸出內存使用量和花費時間的性能特徵。
使用剪枝改善內存使用¶
大型深度學習模型通常過度參數化,導致高內存使用率。為了應對這個問題,PyTorch 提供了模型剪枝機制,有助於減少內存使用率,同時保持任務準確性。 剪枝教學 描述了如何使用 PyTorch 提供的剪枝技術,或在必要時定義自定義剪枝技術。
參數化¶
對於某些應用,在模型訓練期間約束參數空間可能是有益的。 例如,強制學習到的參數的正交性可以改善 RNN 的收斂性。 PyTorch 提供了一種應用 參數化 的機制,例如這種正交性約束,並進一步允許定義自定義約束。
使用 FX 轉換模組¶
PyTorch 的 FX 組件提供了一種靈活的方式,透過直接操作模組計算圖來轉換模組。 這可用於以程式化的方式生成或操作模組,以用於廣泛的用例。 若要探索 FX,請查看使用 FX 進行 卷積 + 批次正規化融合 和 CPU 性能分析 的這些範例。