본문 바로가기

인공지능/DeepLearning

[PyTorch] Parameter Management

네트워크를 설계하고 손실 함수를 최소화하는 파라미터 값을 찾는 것을 목표로 학습을 반복한다.

훈련 후 향후 예측을 위해 이러한 매개 변수가 필요하다. 또한 과학적 이해를 얻기 위해 매개 변수가 필요하기도 하다.

대부분 딥러닝 프레임 워크에 의해 무거운 작업을 수행하기 때문에 핵심적인 세부사항은 무시할 수도 있지만,

표준 layer의 구조를 벗어나게 된다면 매개 변수를 선언하고 조작하는 것이 필요할 때가 존재하게 된다.


Parameter Access

구현된 모델에서 매개 변수에 액세스 하는 방법이 존재한다.

모델이 Sequential클래스를 통해 정의되면 모델의 각 layer를 인덱싱하여 액세스 할 수 있다.

출력은 해당 계층의 weight 값과, bias 값에 해당하는 두 개의 매개 변수를 포함하여, float32 형태로 저장된다.

다음은 두번째 layer의 매개 변수를 액세스 하는 방법이다.

import torch
from torch import nn

net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))

print(net[2].state_dict())
더보기

OrderedDict([('weight', tensor([[-0.1687, -0.2764,  0.2321,  0.0575,  0.0550, -0.1191,  0.3472, -0.2139]])), ('bias', tensor([-0.2068]))])


Targeted Parameters

layer의 각 매개 변수는 매개 변수 클래스의 인스턴스로 표시된다.

매개 변수로 유용한 작업을 수행하려면 먼저 기본 숫자 값에 액세스를 해야 한다.

매개 변수는 값과 기울기 등 추가 정보를 포함한 개체이며, 값 외에도 매개 변수를 통해 gradient에 액세스 할 수 있다.

아직 네트워크에 대한 역전파를 호출하지 않았으므로 기울기에는 값이 들어있지 않다.

import torch
from torch import nn

net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))

print(type(net[2].bias))
print(net[2].bias)
print(net[2].bias.data)
print(net[2].weight.grad == None)
더보기

<class 'torch.nn.parameter.Parameter'>

 

Parameter contatining:

tensor([-0.1644], requires_grad = True)

 

tensor([-0.1644])

 

True


All Parameters at Once

모든 매개 변수를 하나씩 액세스 하여 확인하는 것은 번거로울 수 있다.

더 복잡한 블록에서는 특히 더 어려워 질 수 있으며, 각 매개 변수를 액세스 하기 위해 반복해야 하기 때문이다.

다음은 첫 번째 레이어에 엑세스하는 방법과 모든 레이어에 한 번에 액세스 하는 방법을 보여준다.

from torch import nn

net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))

print(*[(name, param.shape) for name, param in net[0].named_parameters()])
print(*[(name, param.shape) for name, param in net.named_parameters()])
더보기

('weight', torch.Size([8, 4])), ('bias', torch.Size([8]))

 

('0.weight', torch.Size([8, 4])) ('0.bias', torch.Size([8])) ('2.weight', torch.Size([1, 8])) ('2.bias', torch.Size([1]))


Collecting Parameters from Nested Blocks

여러 블록을 서로 중첩하는 경우 매개 변수 명명 규칙을 정의 할 수 있다.

from torch import nn

def block1():
    return nn.Sequential(nn.Linear(4, 8), nn.ReLU(),
                         nn.Linear(8, 4), nn.ReLU())

def block2():
    net = nn.Sequential()
    for i in range(4):
        # Nested here
        net.add_module(f'block {i}', block1())
    return net

net = nn.Sequential(block2(), nn.Linear(4, 1))
print(net)
더보기

Sequential(

   (0): Sequential(

     (block 0): Sequential(

        (0): Linear(in_features=4, out_features=8, bias=True)

        (1): ReLU()

        (2): Linear(in_features=8, out_features=4, bias=True)

        (3): ReLU()

     )

     (block 1): Sequential(

        (0): Linear(in_features=4, out_features=8, bias=True)

        (1): ReLU()

        (2): Linear(in_features=8, out_features=4, bias=True)

        (3): ReLU()

     )

     (block 2): Sequential(

         (0): Linear(in_features=4, out_features=8, bias=True)

        (1): ReLU()

        (2): Linear(in_features=8, out_features=4, bias=True)

        (3): ReLU()

     )

     (block 3): Sequential(

        (0): Linear(in_features=4, out_features=8, bias=True)

        (1): ReLU()

        (2): Linear(in_features=8, out_features=4, bias=True)

        (3): ReLU()

     )

   )

   (1): Linear(in_features=4, out_features=1, bias=True)

)


매개 변수를 액세스하는 방법도 중요하지만 매개 변수를 올바르게 초기화하는 방법 또한 중요하다.

딥러닝 프레임 워크는 layer에 기본 임의 초기화를 제공한다. 프레임 워크를 통해 사용자 지정 초기 값을 만들 수 있다.

Pytorch의 경우 nn.init 모듈을 이용해 다양한 사전 설정 초기화 방법을 제공한다.

 

Built-in Initialization

Pytorch의 내장된 초기화 함수를 사용하여 설계한 네트워크의 매개변수를 한 번에 초기화할 수 있다.

또한 원하는 레이어를 지정해 지정한 레이어만 따로 초기화를 할 수도 있다.

from torch import nn

net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))

def init_normal(m):
    if type(m) == nn.Linear:
        nn.init.normal_(m.weight, mean=0, std=0.01)
        nn.init.zeros_(m.bias)

net.apply(init_normal)
print('layer 1 : ', net[0].weight.data, net[0].bias.data)
print('layer 2 : ', net[2].weight.data, net[2].bias.data)

print('\n----------------------------------------------------------------------------------\n')

def init_constant(m):
    if type(m) == nn.Linear:
        nn.init.constant_(m.weight, 1)
        nn.init.zeros_(m.bias)

net[2].apply(init_constant)
print('layer 1 : ', net[0].weight.data, net[0].bias.data)
print('layer 2 : ', net[2].weight.data, net[2].bias.data)

Custom Initialization

때로는 특별한 초기화 방법을 필요로 할 때 딥 러닝 프레임워크에서는 제공하지 않는 초기화 방법이 있다.

w가 그림과 같은 특수한 분포를 사용할 경우, 우리는 Initialization을 custom하여 사용한다.

from torch import nn

net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(), nn.Linear(8, 1))

def my_init(m):
    if type(m) == nn.Linear:
        print("Init", *[(name, param.shape)
                        for name, param in m.named_parameters()][0])
        nn.init.uniform_(m.weight, -10, 10)
        m.weight.data *= m.weight.data.abs() >= 5

net.apply(my_init)
print(net[0].weight.data)
print(net[2].weight.data)

Tied Parameters

종종 하나의 레이어가 아닌 2개 이상의 레이어에서 파라미터를 공유하는 경우가 존재한다.

shared에 nn.Linear를 지정해줌으로써 파라미터를 공유하고 있다.

net[2]의 값만 바꿨는데도 net [4]도 함께 바뀌어 동일한 값인걸 확인할 수 있다.

from torch import nn

shared = nn.Linear(8, 8)
net = nn.Sequential(nn.Linear(4, 8), nn.ReLU(),
                    shared, nn.ReLU(),
                    shared, nn.ReLU(),
                    nn.Linear(8, 1))

print(net[2].weight.data[0] == net[4].weight.data[0])

net[2].weight.data[0] = 100
print(net[2].weight.data[0] == net[4].weight.data[0])

 

ref. Dive into Deep Learning, Aston Zhang and Zachary C. Lipton and Mu Li and Alexander J. Smola, 2020