NLP/RNN (language Model)
RNN for NLP
RNN
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)