Pytroch의 torch.nn을 사용하지 않고, 다층 퍼셉트론을 구현하고 두 모델을 비교해보는 과제에서 학습 초기에 두 모델의 loss 값이 다르게 나타났다.

그 이유는 torch.nn.linear의 소스코드에서 확인할 수 있었는데, reset_parameter()를 사용하는 과정이 없었기 때문이다.

__constants__ = ['in_features', 'out_features']
in_features: int
out_features: int
weight: Tensor
def __init__(self, in_features: int, out_features: int, bias: bool = True,
                 device=None, dtype=None) -> None:
        factory_kwargs = {'device': device, 'dtype': dtype}
        super().__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = Parameter(torch.empty((out_features, in_features), **factory_kwargs))
        if bias:
            self.bias = Parameter(torch.empty(out_features, **factory_kwargs))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

reset_parameters는 PyTorch에서 Linear 레이어의 가중치와 바이어스를 kaiming 초기화를 사용해 초기화하는 과정이다. 이러한 초기화는 신경망의 학습 성능에 중요한 영향을 미친다.

def reset_parameters(self) -> None:
        init.kaiming_uniform_(self.weight, a=math.sqrt(5))
        if self.bias is not None:
            fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
            bound = 1 / math.sqrt(fan_in) if fan_in > 0 else 0
            init.uniform_(self.bias, -bound, bound)

kaiming_uniform_

kaiming_uniform_ 함수는 가중치를 Kaiming 균등 분포(Kaiming uniform distribution)를 사용하여 초기화한다. 여기서 a=math.sqrt(5)는 분포의 스케일을 조정한다.

a=math.sqrt(5)로 설정하면, 이는 uniform(-1/sqrt(in_features), 1/sqrt(in_features))으로 초기화하는 것과 동일하다.

Kaiming 초기화는 주로 ReLU와 같은 비선형 활성화 함수를 사용하는 신경망에서 좋은 성능을 보이며, 입력 노드의 수를 고려해 가중치를 초기화한다.

bound

bound는 바이어스의 초기화 범위를 설정하는데 사용된다.

init.uniform_(self.bias, -bound, bound) 바이어스를 -bound에서 bound 사이의 균등 분포를 따르도록 초기화합니다. 이는 self.bias가 적절한 범위 내에서 초기화되도록 합니다.

<aside> <img src="data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NCIgaGVpZ2h0PSI0NCIgZmlsbD0iI2U3YmNiNSIgY2xhc3M9ImJpIGJpLWNoYXQtZG90cy1maWxsIiB2aWV3Qm94PSIwIDAgMTYgMTYiIGlkPSJpY29uLWNoYXQtZG90cy1maWxsLTM0NCI+PHBhdGggZD0iTTE2IDhjMCAzLjg2Ni0zLjU4MiA3LTggN2E5LjA2IDkuMDYgMCAwIDEtMi4zNDctLjMwNmMtLjU4NC4yOTYtMS45MjUuODY0LTQuMTgxIDEuMjM0LS4yLjAzMi0uMzUyLS4xNzYtLjI3My0uMzYyLjM1NC0uODM2LjY3NC0xLjk1Ljc3LTIuOTY2Qy43NDQgMTEuMzcgMCA5Ljc2IDAgOGMwLTMuODY2IDMuNTgyLTcgOC03czggMy4xMzQgOCA3ek01IDhhMSAxIDAgMSAwLTIgMCAxIDEgMCAwIDAgMiAwem00IDBhMSAxIDAgMSAwLTIgMCAxIDEgMCAwIDAgMiAwem0zIDFhMSAxIDAgMSAwIDAtMiAxIDEgMCAwIDAgMCAyeiI+PC9wYXRoPjwvc3ZnPg==" alt="data:image/svg+xml;charset=utf-8;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0NCIgaGVpZ2h0PSI0NCIgZmlsbD0iI2U3YmNiNSIgY2xhc3M9ImJpIGJpLWNoYXQtZG90cy1maWxsIiB2aWV3Qm94PSIwIDAgMTYgMTYiIGlkPSJpY29uLWNoYXQtZG90cy1maWxsLTM0NCI+PHBhdGggZD0iTTE2IDhjMCAzLjg2Ni0zLjU4MiA3LTggN2E5LjA2IDkuMDYgMCAwIDEtMi4zNDctLjMwNmMtLjU4NC4yOTYtMS45MjUuODY0LTQuMTgxIDEuMjM0LS4yLjAzMi0uMzUyLS4xNzYtLjI3My0uMzYyLjM1NC0uODM2LjY3NC0xLjk1Ljc3LTIuOTY2Qy43NDQgMTEuMzcgMCA5Ljc2IDAgOGMwLTMuODY2IDMuNTgyLTcgOC03czggMy4xMzQgOCA3ek01IDhhMSAxIDAgMSAwLTIgMCAxIDEgMCAwIDAgMiAwem00IDBhMSAxIDAgMSAwLTIgMCAxIDEgMCAwIDAgMiAwem0zIDFhMSAxIDAgMSAwIDAtMiAxIDEgMCAwIDAgMCAyeiI+PC9wYXRoPjwvc3ZnPg==" width="40px" /> 가중치 초기화는 사용되는 활성화 함수에 따라 다르게 설계되어야 한다. 예를 들어, ReLU와 같은 활성화 함수는 입력값이 양수일 때만 활성화되므로, Kaiming 초기화(Kaiming initialization)가 자주 사용된다. 이는 ReLU가 활성화될 확률을 고려해 가중치를 초기화한다.

반면, 시그모이드(sigmoid)나 하이퍼볼릭 탄젠트(tanh)와 같은 활성화 함수를 사용하는 경우, 기울기 소실 문제가 더 자주 발생할 수 있기 때문에 Xavier 초기화(Xavier initialization)를 사용해 가중치를 적절한 범위로 설정한다.

</aside>