Làm thế nào để khởi tạo trọng số trong PyTorch?


Câu trả lời:


150

Lớp đơn

Để 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.Modulecá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)

6
Tôi đã tìm thấy một reset_parametersphươ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?
Yang Bo

1
điều gì sẽ xảy ra nếu tôi muốn sử dụng một phân phối Chuẩn với một số trung bình và std?
Charlie Parker

12
Khởi tạo mặc định là gì nếu tôi không chỉ định?
xjcl

khởi tạo mặc định ít nhất cho các lớp tuyến tính là her: pytorch.org/docs/stable/nn.html#linear-layers
arash javan

40

Chúng tôi so sánh các phương thức khởi tạo trọng số khác nhau bằng cách sử dụng cùng một kiến ​​trúc mạng nơ-ron (NN).

Tất cả các Zeros hoặc Ones

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)
  • Sau 2 kỷ nguyên:

âm mưu giảm tập luyện với trọng lượng khởi tạo không đổi

Validation Accuracy
9.625% -- All Zeros
10.050% -- All Ones
Training Loss
2.304  -- All Zeros
1552.281  -- All Ones

Khởi tạo đồng nhất

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.0high=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ể:

  1. Xác định một hàm gán trọng số theo loại lớp mạng, sau đó
  2. Á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)
  • Sau 2 kỷ nguyên:

nhập mô tả hình ảnh ở đây

Validation Accuracy
36.667% -- Uniform Weights
Training Loss
3.208  -- Uniform Weights

Quy tắc chung để thiết lập trọng số

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

  • Sau 2 kỷ nguyên:

biểu đồ hiển thị hiệu suất của việc khởi tạo đồng nhất trọng số so với quy tắc chung của khởi tạo

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 để khởi tạo trọng số

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

  • Sau 2 kỷ nguyên:

hiệu suất khởi tạo trọng số bằng cách sử dụng phân phối đồng đều so với 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

7
Nhiệm vụ bạn tối ưu hóa là gì? Và làm thế nào một giải pháp tất cả các số không có thể không mất mát?
khấu trừ vào

19

Để khởi tạo các lớp, bạn thường không cần làm gì cả.

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í conv2ddụ 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 đó.


Tuy nhiên, việc khởi tạo mặc định không phải lúc nào cũng cho kết quả tốt nhất. Gần đây tôi đã triển khai kiến ​​trúc VGG16 trong Pytorch và đào tạo nó trên tập dữ liệu CIFAR-10 và tôi nhận thấy rằng chỉ cần chuyển sang xavier_uniformkhở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.)
littleO

Điều này là do họ chưa sử dụng Định mức hàng loạt trong VGG16. Đúng là việc khởi tạo thích hợp rất quan trọng và đối với một số kiến ​​trúc bạn cần chú ý. Ví dụ: nếu bạn sử dụng (nn.conv2d (), chuỗi ReLU ()), bạn sẽ bắt đầu khởi tạo Kaiming He được thiết kế cho lớp chuyển đổi của bạn. PyTorch không thể dự đoán chức năng kích hoạt của bạn sau lượt chuyển đổi. Điều này có ý nghĩa nếu bạn đánh giá các giá trị riêng, nhưng thông thường bạn không phải làm gì nhiều nếu sử dụng Định mức hàng loạt, chúng sẽ chuẩn hóa kết quả đầu ra cho bạn. Nếu bạn dự định giành chiến thắng trong cuộc thi SotaBench, điều đó rất quan trọng.
prosti

7
    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)

5

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 distributionsử dụng:

torch.nn.init.normal_(tensor, mean=0, std=1)

Hoặc để constant distributionviế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


2

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.


1

Lặp lại các tham số

applyVí dụ: nếu bạn không thể sử dụng nếu mô hình không triển khai Sequentialtrực tiếp:

Giống nhau cho tất cả

# 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.) 

Tùy thuộc vào hình dạng

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))
}

0

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)

1
Bạn có thể bình luận ở đó tại câu trả lời của Fábio Perez để giữ cho câu trả lời rõ ràng.
Phani Rithvij

0

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.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.