Overview
- Text classification 모델을 생성한다.
- 간단하게 Character embedding layer를 두고 문서의 character embedding 평균 백터를 구하여 Fully Connected Layer를 두어 (2개층) 최종적으로 binary classification을 수행하도록 한다.
Network Module 구현하기
- nn.Module을 상속받아 클래스를 생성한다.
- init 함수에 네트워크에서 필요한 Layer들(주로 pytorch에서 기본 제공되는 Layer들을 정의함. 물론 사용자 정의 모듈도 정의 가능함)
- forward 함수 안에 모듈의 input 부터 output까지 네트워크의 흐름을 구현해준다.
이번 구현에서 필요한 pytorch 기본 제공 모듈 살펴보기
- torch.nn.EmbeddingBag
- Text 관련 네트워크에서 사용하는 Embedding Layer에 평균/합계/최대값 등 특정 계산값을 구하는 부분이 추가된 모듈이다.
- 이번 예시에서는 EmbeddingBag을 사용하나. 실제로는 주로 Embedding 레이어를 더 많이 사용한다.
- torch.nn.Linear
- 기본적인 Fully Connedted Layer 이다.
- (주의사항) activation func 는 포함되지 않으며 input * weight 까지만 수행한다.
- torch.nn.BatchNorm1d
- 1차원 batch normalization layer 이다.
- batch normalization 은 다른 포스팅 혹은 유투브 강의를 살펴보면 된다. "Internal Covariate Shift(ICS)" 라는 term만 잘 이해하면 되긴 한다. (다만, batch normalization 의 성능향상 효과는 ICS 와는 상관이 없었다능... 결국 loss function의 landscape가 smoothing 되는 효과 때문이라능... 그게 요 논문)
- Andrew Ng 강의를 보면 batch normalization은 regularization 기능도 한다고 한다. 하지만 주 목적은 학습을 빠르게 하는 것이다.
- (추가) 비교적 최신
(최신이라기엔 시간이 많이 흘렀지만..)인 transformers 같은 network 상에서 사용하는 normalization을 보면 Batch normalization 보다 Layer Normalization을 사용하는 것을 볼 수 있다. 요 차이는 normalization을 수행하는 차원은 B(batch)차원으로 하느냐 L(layer) 차원으로 하느냐 차이이다. Batch Normalization이 아래와 같은 한계점 때문에 Layer Normalization 을 더 많이 사용한다고 하니 참고하면 좋을 듯 하다.
- Batch Normalization 은 Batch 사이즈를 적절히 조절했을 때 제대로 동작한다. 따라서 메모리를 상당히 많이 사용하는 복잡한 모델을 돌릴때 상대적으로 batch 사이즈를 크게 가져갈 수 없는 경우가 발생하고 이에 따라 Batch Normalization 이 제대로 동작하지 않을 수 있다.(Batch 가 너무 커도 잘 동작하지 않는다고 한다.) 반면 Layer 단위로 Normalization을 진행할 경우 이러한 Batch size에 영향을 받지 않는다. (자세한 사항은 batch / layer normalization을 설명한 다른 포스트들을 찾아보면 좋다)
- (참고사항)배치 정규화를 사용할 경우 학습되는 파라미터 중 bias 부분에 해당하는 베타(학습되는 값)가 있는데 이러한 부분이 기존 fc 에서 별도로 추가해서 사용할 수 있는 bias 와 역할이 동일하다. 따라서 배치 정규화를 사용하는 경우 굳이 바이어스를 사용할 필요가 없다고 한다.
- torch.nn.Relu
- activation layer의 한 종류. 다른 activation 함수들도 살펴 보면 좋다.
- Relu 의 출현은 'vanishing gradient' 와 관계가 있으니 관련 내용도 찾아보면 좋다.
- torch.nn.Dropout
- Overfitting 방지를 위한 layer 이다.
- 기본 동작은 일정 확률로 뉴런을 죽이거나 혹은 살린다. DL 프레임워크마다 파라미터로 넘겨주는 값이 때로는 죽이는 확률일때도 있고 살리는 확률일때도 있다. 따라서 사용중인 프레임워크의 기본 파라미터의 의미를 잘 알고 사용하는 것이 좋다. pytorch는 죽이는 확률이 기본 값으로 들어간다.
- (참고사항) 사실 대부분 high level API를 사용하기 때문에 Dropout을 직접 구현할일이 없지만. 직접 구현시에 단순히 일정 확률로 죽이는것으로 끝나는 것이 아니라 backpropagation에서 죽었던 뉴런에 대한 처리를 따로 해주어야 하기 때문에 추가적인 구현이 필요하다. 이 부분은 Andrew Ng 교수님의 강의에 대략적으로 설명이 되어있다.
(유의사항1) BatchNorm, Activation, Dropout 의 적절한 위치는??
- Fully Connected Layer를 구축할 때 사용되는 모듈들의 순서를 어떻게 해야할 지 모르겠는 경우가 있다. 특히 위 제목에 있는 것들...
- 다행히 이러한 궁금증에 대한 대답은 여러 군데에서 찾을 수 있다. 여기에서도.
- 결론은 FC - BATCH - ACTIVATION - DROPOUT 순이다.
- 자세한 이유는 구글링...
(유의사항2) Softmax Layer의 부재... 왜?
- 이전 포스팅에서 대답이 되었지만....
- 구현상의 이슈로 인해 (exp 함수로 인한 overflow 문제) Softmax 와 log 함수를 합한 log_softmax 함수를 주로 사용하며
- NLL_loss 를 사용하면 log_softmax를 모듈의 마지막 레이어에 넣어주어야 하며,
- CrossEntropyLoss 를 사용하려면 log_softmax layer를 생략해야한다.
구현 예시
class MeanCharEmbeddingClsf(nn.Module):
def __init__(self, vocab_size, char_emb_dim, hidden_dim1, hidden_dim2, class_cnt):
'''
define network structure
:param vocab_size:
:param char_emb_dim:
'''
super(MeanCharEmbeddingClsf, self).__init__()
self.char_emb_bag = nn.EmbeddingBag(num_embeddings=vocab_size, embedding_dim=char_emb_dim, mode="mean")
self.fc1 = nn.Linear(char_emb_dim, hidden_dim1)
self.fc2 = nn.Linear(hidden_dim1, hidden_dim2)
self.layer_out = nn.Linear(hidden_dim2, class_cnt)
self.batchnorm1 = nn.BatchNorm1d(hidden_dim1)
self.batchnorm2 = nn.BatchNorm1d(hidden_dim2)
self.do = nn.Dropout(0.2)
self.relu = nn.ReLU()
def forward(self, char_seqs):
# char_seqs (B, CSL, CED)
mean_vec = self.char_emb_bag(char_seqs)
h1 = self.fc1(mean_vec)
h1 = self.batchnorm1(h1)
h1 = self.relu(h1)
h1 = self.do(h1)
h2 = self.fc2(h1)
h2 = self.batchnorm2(h2)
h2 = self.relu(h2)
h2 = self.do(h2)
out = self.layer_out(h2)
return out