
class RNNModel(torch.nn.Module):
def __init__(self, vocab_size, embedding_dim, hidden_dim, output_dim):
super().__init__()
self.embedding = torch.nn.Embedding(
num_embeddings=vocab_size, #토큰나이저의 어휘 개수
embedding_dim=embedding_dim # 임베딩 벡터 차원
)
self.rnn = torch.nn.RNN(
inout_size=embedding_dim,
hidden_size=hidden_dim,
num_layers=1,
dropout=0.5 # 오버피팅을 방지하기 위해 일부러 일부 파라미터들을 학습시키지 않는 역할. 0.5면 랜덤하게 절반의 파라미터만 학습
)
self.fc = torch.nn.Linear(hidden_dim, output_dim)
self.dropout = torch.nn.Dropout(0.5)
def forward(self, x):
text_lengths = (x != 0).long().sum(dim=0)
embedded = self.dropout(self.embedding(x))
packed = pack_padded_sequence(embedded, text_lengths, enforce_sorted=False)
packed_output, hidden = self.rnn(packed) #RNN 통과
output, output_lengths = pad_packed_sequence(packed_output)
return self.fc(hidden)
packed = pack_padded_sequence(
embedded, text_lengths, enforce_sorted=False
)
text_lengths로 시퀀스의 길이를 지정하고, 정렬되지 않은 배치도 처리할 수 있도록 합니다.이 코드에서는 두 가지 다른 위치에서 드롭아웃을 수행하는데, 그 이유는 각각의 드롭아웃이 다른 층과 다른 목적을 위해 사용되기 때문이야. 이를 각각 자세히 설명해볼게.
self.rnn = torch.nn.RNN(..., dropout=0.5)예를 들어, RNN이 여러 층으로 쌓여 있을 경우 (num_layers > 1), 각 층의 출력에서 다음 층으로 전달될 때 드롭아웃을 수행해서 과적합(overfitting)을 방지하는 역할을 해. 하지만 이 드롭아웃은 층 간에만 적용되기 때문에, 만약 num_layers=1로 지정하면 이 드롭아웃은 적용되지 않게 돼.
따라서 이 드롭아웃은 RNN의 은닉 상태를 다음 층으로 전달할 때만 적용된다고 보면 돼.
self.dropout = torch.nn.Dropout(0.5)이 부분은 입력된 데이터나 임베딩 벡터, 혹은 RNN의 마지막 은닉 상태가 완전 연결 층(fc)으로 넘어가기 전에 드롭아웃을 추가하는 거야. 이는 RNN 층 내부의 드롭아웃과 별도로, 모델의 다른 부분에서 과적합을 방지하기 위한 역할을 수행해.
왜 두 번 드롭아웃을 수행하는가?
이렇게 다양한 위치에서 드롭아웃을 적용하면 모델이 특정 값에 지나치게 의존하는 것을 방지하고, 다양한 패턴을 학습하도록 돕는 역할을 해.