Làm thế nào để khởi tạo các trọng số và độ lệch (ví dụ: với khởi tạo He hoặc Xavier) trong một mạng trong PyTorch?
Làm thế nào để khởi tạo các trọng số và độ lệch (ví dụ: với khởi tạo He hoặc Xavier) trong một mạng trong PyTorch?
Câu trả lời:
Để khởi tạo trọng số của một lớp, hãy sử dụng một hàm từ torch.nn.init
. Ví dụ:
conv1 = torch.nn.Conv2d(...)
torch.nn.init.xavier_uniform(conv1.weight)
Ngoài ra, bạn có thể sửa đổi các tham số bằng cách ghi vào conv1.weight.data
(là a torch.Tensor
). Thí dụ:
conv1.weight.data.fill_(0.01)
Điều tương tự cũng áp dụng cho các thành kiến:
conv1.bias.data.fill_(0.01)
nn.Sequential
hoặc tùy chỉnh nn.Module
Chuyển một hàm khởi tạo đến torch.nn.Module.apply
. Nó sẽ khởi tạo các trọng số trong toàn bộ một nn.Module
cách đệ quy.
apply ( fn ): Áp dụng
fn
đệ quy cho mọi mô-đun con (được trả về bởi.children()
) cũng như chính nó. Việc sử dụng điển hình bao gồm khởi tạo các tham số của mô hình (xem thêm torch-nn-init).
Thí dụ:
def init_weights(m):
if type(m) == nn.Linear:
torch.nn.init.xavier_uniform(m.weight)
m.bias.data.fill_(0.01)
net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)
Nếu bạn tuân theo nguyên tắc dao cạo của Occam , bạn có thể nghĩ rằng đặt tất cả các trọng lượng thành 0 hoặc 1 sẽ là giải pháp tốt nhất. Đây không phải là trường hợp.
Với mọi trọng lượng như nhau, tất cả các nơ-ron ở mỗi lớp đều tạo ra cùng một đầu ra. Điều này khiến bạn khó quyết định nên điều chỉnh mức tạ nào.
# initialize two NN's with 0 and 1 constant weights
model_0 = Net(constant_weight=0)
model_1 = Net(constant_weight=1)
Validation Accuracy
9.625% -- All Zeros
10.050% -- All Ones
Training Loss
2.304 -- All Zeros
1552.281 -- All Ones
Một phân bố đồng đều có xác suất bằng nhau chọn bất kỳ số từ một tập hợp các số.
Hãy xem mạng nơ-ron đào tạo tốt như thế nào bằng cách sử dụng khởi tạo trọng số đồng nhất, ở đâu low=0.0
và high=1.0
.
Dưới đây, chúng ta sẽ thấy một cách khác (bên cạnh mã lớp Net) để khởi tạo các trọng số của mạng. Để xác định trọng số bên ngoài định nghĩa mô hình, chúng ta có thể:
- Xác định một hàm gán trọng số theo loại lớp mạng, sau đó
- Áp dụng các trọng số đó cho một mô hình đã khởi tạo bằng cách sử dụng
model.apply(fn)
, áp dụng một chức năng cho mỗi lớp mô hình.
# takes in a module and applies the specified weight initialization
def weights_init_uniform(m):
classname = m.__class__.__name__
# for every Linear layer in a model..
if classname.find('Linear') != -1:
# apply a uniform distribution to the weights and a bias=0
m.weight.data.uniform_(0.0, 1.0)
m.bias.data.fill_(0)
model_uniform = Net()
model_uniform.apply(weights_init_uniform)
Validation Accuracy
36.667% -- Uniform Weights
Training Loss
3.208 -- Uniform Weights
Quy tắc chung để thiết lập trọng số trong mạng nơ-ron là đặt chúng gần bằng 0 mà không quá nhỏ.
Thực hành tốt là bắt đầu trọng số của bạn trong phạm vi [-y, y] trong đó
y=1/sqrt(n)
(n là số đầu vào cho một nơ-ron nhất định).
# takes in a module and applies the specified weight initialization
def weights_init_uniform_rule(m):
classname = m.__class__.__name__
# for every Linear layer in a model..
if classname.find('Linear') != -1:
# get the number of the inputs
n = m.in_features
y = 1.0/np.sqrt(n)
m.weight.data.uniform_(-y, y)
m.bias.data.fill_(0)
# create a new model with these weights
model_rule = Net()
model_rule.apply(weights_init_uniform_rule)
dưới đây chúng tôi so sánh hiệu suất của NN, trọng số được khởi tạo với phân phối đồng đều [-0,5,0,5) so với trọng số có trọng số được khởi tạo bằng quy tắc chung
Validation Accuracy
75.817% -- Centered Weights [-0.5, 0.5)
85.208% -- General Rule [-y, y)
Training Loss
0.705 -- Centered Weights [-0.5, 0.5)
0.469 -- General Rule [-y, y)
Phân phối chuẩn phải có giá trị trung bình là 0 và độ lệch chuẩn
y=1/sqrt(n)
, trong đó n là số lượng đầu vào cho NN
## takes in a module and applies the specified weight initialization
def weights_init_normal(m):
'''Takes in a module and initializes all linear layers with weight
values taken from a normal distribution.'''
classname = m.__class__.__name__
# for every Linear layer in a model
if classname.find('Linear') != -1:
y = m.in_features
# m.weight.data shoud be taken from a normal distribution
m.weight.data.normal_(0.0,1/np.sqrt(y))
# m.bias.data should be 0
m.bias.data.fill_(0)
dưới đây, chúng tôi hiển thị hiệu suất của hai NN, một NN được khởi tạo bằng cách sử dụng phân phối đồng đều và NN còn lại sử dụng phân phối chuẩn
Validation Accuracy
85.775% -- Uniform Rule [-y, y)
84.717% -- Normal Distribution
Training Loss
0.329 -- Uniform Rule [-y, y)
0.443 -- Normal Distribution
PyTorch sẽ thay bạn làm điều đó. Nếu bạn nghĩ về điều này, điều này có rất nhiều ý nghĩa. Tại sao chúng ta nên khởi tạo các lớp, khi PyTorch có thể làm điều đó theo xu hướng mới nhất.
Kiểm tra ví dụ lớp Linear .
Trong __init__
phương thức nó sẽ gọi hàm Kaiming He init.
def reset_parameters(self):
init.kaiming_uniform_(self.weight, a=math.sqrt(3))
if self.bias is not None:
fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
bound = 1 / math.sqrt(fan_in)
init.uniform_(self.bias, -bound, bound)
Tương tự đối với các loại lớp khác. Ví conv2d
dụ kiểm tra ở đây .
Cần lưu ý: Lợi ích của việc khởi tạo thích hợp là tốc độ đào tạo nhanh hơn. Nếu vấn đề của bạn đáng được khởi tạo đặc biệt, bạn có thể thực hiện sau đó.
xavier_uniform
khởi tạo cho các trọng số (với độ lệch được khởi tạo bằng 0), thay vì sử dụng khởi tạo mặc định, độ chính xác xác thực của tôi sau 30 kỷ nguyên của RMSprop tăng từ 82% lên 86%. Tôi cũng có độ chính xác xác thực là 86% khi sử dụng mô hình VGG16 tích hợp sẵn của Pytorch (không được đào tạo trước), vì vậy tôi nghĩ rằng tôi đã triển khai nó một cách chính xác. (Tôi đã sử dụng tỷ lệ học tập là 0,00001.)
import torch.nn as nn
# a simple network
rand_net = nn.Sequential(nn.Linear(in_features, h_size),
nn.BatchNorm1d(h_size),
nn.ReLU(),
nn.Linear(h_size, h_size),
nn.BatchNorm1d(h_size),
nn.ReLU(),
nn.Linear(h_size, 1),
nn.ReLU())
# initialization function, first checks the module type,
# then applies the desired changes to the weights
def init_normal(m):
if type(m) == nn.Linear:
nn.init.uniform_(m.weight)
# use the modules apply function to recursively apply the initialization
rand_net.apply(init_normal)
Xin lỗi vì đã đến muộn, tôi hy vọng câu trả lời của tôi sẽ giúp đỡ
Để khởi tạo trọng lượng bằng cách normal distribution
sử dụng:
torch.nn.init.normal_(tensor, mean=0, std=1)
Hoặc để constant distribution
viết:
torch.nn.init.constant_(tensor, value)
Hoặc sử dụng uniform distribution
:
torch.nn.init.uniform_(tensor, a=0, b=1) # a: lower_bound, b: upper_bound
Bạn có thể kiểm tra các phương pháp khác để khởi tạo tensors tại đây
Nếu bạn muốn linh hoạt hơn, bạn cũng có thể đặt trọng lượng theo cách thủ công .
Giả sử bạn có đầu vào của tất cả những thứ:
import torch
import torch.nn as nn
input = torch.ones((8, 8))
print(input)
tensor([[1., 1., 1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1.],
[1., 1., 1., 1., 1., 1., 1., 1.]])
Và bạn muốn tạo một lớp dày đặc mà không có độ lệch (để chúng ta có thể hình dung):
d = nn.Linear(8, 8, bias=False)
Đặt tất cả các trọng số thành 0,5 (hoặc bất kỳ thứ gì khác):
d.weight.data = torch.full((8, 8), 0.5)
print(d.weight.data)
Trọng lượng:
Out[14]:
tensor([[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000]])
Tất cả trọng lượng của bạn bây giờ là 0,5. Chuyển dữ liệu qua:
d(input)
Out[13]:
tensor([[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.],
[4., 4., 4., 4., 4., 4., 4., 4.]], grad_fn=<MmBackward>)
Hãy nhớ rằng mỗi nơ-ron nhận được 8 đầu vào, tất cả đều có trọng số 0,5 và giá trị là 1 (và không có độ lệch), vì vậy nó tổng cộng tối đa 4 cho mỗi đầu vào.
apply
Ví dụ: nếu bạn không thể sử dụng nếu mô hình không triển khai Sequential
trực tiếp:
# see UNet at https://github.com/milesial/Pytorch-UNet/tree/master/unet
def init_all(model, init_func, *params, **kwargs):
for p in model.parameters():
init_func(p, *params, **kwargs)
model = UNet(3, 10)
init_all(model, torch.nn.init.normal_, mean=0., std=1)
# or
init_all(model, torch.nn.init.constant_, 1.)
def init_all(model, init_funcs):
for p in model.parameters():
init_func = init_funcs.get(len(p.shape), init_funcs["default"])
init_func(p)
model = UNet(3, 10)
init_funcs = {
1: lambda x: torch.nn.init.normal_(x, mean=0., std=1.), # can be bias
2: lambda x: torch.nn.init.xavier_normal_(x, gain=1.), # can be weight
3: lambda x: torch.nn.init.xavier_uniform_(x, gain=1.), # can be conv1D filter
4: lambda x: torch.nn.init.xavier_uniform_(x, gain=1.), # can be conv2D filter
"default": lambda x: torch.nn.init.constant(x, 1.), # everything else
}
init_all(model, init_funcs)
Bạn có thể thử với torch.nn.init.constant_(x, len(x.shape))
để kiểm tra xem chúng đã được khởi tạo thích hợp chưa:
init_funcs = {
"default": lambda x: torch.nn.init.constant_(x, len(x.shape))
}
Nếu bạn thấy cảnh báo không dùng nữa (@ Fábio Perez) ...
def init_weights(m):
if type(m) == nn.Linear:
torch.nn.init.xavier_uniform_(m.weight)
m.bias.data.fill_(0.01)
net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)
Vì tôi chưa có đủ danh tiếng cho đến nay, tôi không thể thêm nhận xét bên dưới
câu trả lời được đăng bởi prosti vào ngày 26 tháng 6 '19 lúc 13:16 .
def reset_parameters(self):
init.kaiming_uniform_(self.weight, a=math.sqrt(3))
if self.bias is not None:
fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
bound = 1 / math.sqrt(fan_in)
init.uniform_(self.bias, -bound, bound)
Nhưng tôi muốn chỉ ra rằng thực sự chúng ta biết một số giả định trong bài báo của Kaiming He , Nghiên cứu sâu hơn về bộ chỉnh lưu: Vượt qua hiệu suất ở cấp độ con người trên phân loại ImageNet , là không phù hợp, mặc dù có vẻ như phương pháp khởi tạo được thiết kế có chủ ý sẽ tạo ra một cú hích trong thực tế .
Ví dụ: trong phần phụ của Trường hợp lan truyền ngược , họ giả định rằng $ w_l $ và $ \ delta y_l $ độc lập với nhau. Nhưng như chúng ta đã biết, hãy lấy bản đồ điểm $ \ delta y ^ L_i $ làm ví dụ, nó thường là $ y_i-softmax (y ^ L_i) = y_i-softmax (w ^ L_ix ^ L_i) $ nếu chúng ta sử dụng mục tiêu hàm mất entropy chéo.
Vì vậy, tôi nghĩ rằng lý do cơ bản thực sự tại sao He's Initialization hoạt động tốt vẫn chưa được làm sáng tỏ. Vì mọi người đã chứng kiến sức mạnh của nó trong việc thúc đẩy đào tạo học sâu.
reset_parameters
phương pháp trong mã nguồn của nhiều mô-đun. Tôi có nên ghi đè phương thức để khởi tạo trọng số không?