RNN


\[W_{xh}, W_{hh}, W_{hy} 는 각각 W_{xt}, W_{ht}, W_{ht}를 W_{ht}, W_{ht}, y_t 로 변환 시켜 준다.\]


RNN의 은닉층, 출력층

\[은닉층 : h_t = f_w(h_{t-1}, x_t)\] \[은닉층 : h_t = tanh(W_{hh}h_{t-1} + W_{xh}X_t)\] \[출력층 : y_t = W_{hy}h_t\]



RNN 실습 (PackedSequence)

  • 데이터 전처리

    • index화가 된 데이터를 이용.
vocab_size = 100  #전체 단어 수
pad_id = 0  # pad token의 id


#index화된 데이터   
data = [
  [85,14,80,34,99,20,31,65,53,86,3,58,30,4,11,6,50,71,74,13],
  [62,76,79,66,32],
  [93,77,16,67,46,74,24,70],
  [19,83,88,22,57,40,75,82,4,46],
  [70,28,30,24,76,84,92,76,77,51,7,20,82,94,57],
  [58,13,40,61,88,18,92,89,8,14,61,67,49,59,45,12,47,5],
  [22,5,21,84,39,6,9,84,36,59,32,30,69,70,82,56,1],
  [94,21,79,24,3,86],
  [80,80,33,63,34,63],
  [87,32,79,65,2,96,43,80,85,20,41,52,95,50,35,96,24,80]
] 

padding 처리 (불균형한 길이를 보완하는 역할인 것 같다.)

max_len = len(max(data, key=len))

valid_lens = []
for i, seq in enumerate(tqdm(data)):

  valid_lens.append(len(seq))  # padding 처리 전의 길이 저장.

  if len(seq) < max_len:
    data[i] = seq + [pad_id] * (max_len - len(seq))  # max 길이에 모자른 만큼 0으로 채운다. (padding)   

만들어진 data를 이용해서 batch로 사용.

# B: batch size, L: maximum sequence length
batch = torch.LongTensor(data)  # (B, L)
batch_lens = torch.LongTensor(valid_lens)  # (B)



  • RNN 사용

    • embedding layer 설정을 해야 함.
embedding_size = 256
embedding = nn.Embedding(vocab_size, embedding_size)

# d_w: embedding size
batch_emb = embedding(batch)  # (B, L, d_w)


RNN 모델 선언

hidden_size = 512  # RNN의 hidden size
num_layers = 1  # 쌓을 RNN layer의 개수
num_dirs = 1  # 1: 단방향 RNN, 2: 양방향 RNN

rnn = nn.RNN(
    input_size=embedding_size,
    hidden_size=hidden_size,
    num_layers=num_layers,
    bidirectional=True if num_dirs > 1 else False
)

# 처음 hidden state 정의
h_0 = torch.zeros((num_layers * num_dirs, batch.shape[0], hidden_size))  # (num_layers * num_dirs, B, d_h)


hidden_state 와 h_n의 shape

hidden_states : 각 time step에 해당하는 hidden state들의 묶음.
h_n : 모든 sequence를 거치고 나온 마지막 hidden state.

hidden_states, h_n = rnn(batch_emb.transpose(0, 1), h_0)
torch.Size([20, 10, 512])
torch.Size([1, 10, 512])


full connected layer 적용

num_classes = 2
classification_layer = nn.Linear(hidden_size, num_classes)

# C: number of classes
output = classification_layer(h_n.squeeze(0))  # (1, B, d_h) => (B, C)



  • PackedSequence

    • padding으로 인해서 불필요한 pad에 대한 계산이 이루어진다.
    • packing함으로써 불필요한 pad를 무시하는 효과를 얻을 수 있다.


데이터를 padding 전 원래 길이 기준으로 정렬한다. (길이가 긴 것부터 내림차순으로)

sorted_lens, sorted_idx = batch_lens.sort(descending=True)
sorted_batch = batch[sorted_idx]


pack_padded_sequence 메서드를 이용해서 PackedSequence object를 사용한다.

sorted_batch_emb = embedding(sorted_batch)

#packed_batch[0]의 shape는 123으로 불필요한 pad값을 뺀 수이다. 
packed_batch = pack_padded_sequence(sorted_batch_emb.transpose(0, 1), sorted_lens)

packed_output을 다시 원래 형태로 돌려놓기 위해 pad_packed_sequence를 이용한다.

outputs, outputs_lens = pad_packed_sequence(packed_outputs)