
-- ๋ณธ ํฌ์คํ
์ ํ์ดํ ์น๋ก ๋ฐฐ์ฐ๋ ์์ฐ์ด ์ฒ๋ฆฌ (ํ๋น๋ฏธ๋์ด) ์ฑ
์ ์ฐธ๊ณ ํด์ ์์ฑ๋ ๊ธ์
๋๋ค.
-- ์์ค์ฝ๋๋ ์ฌ๊ธฐ
1. NLP์์์ ์ํ์ค
1.1 ์ํ์ค๋
- ์์๊ฐ ์๋ ํญ๋ชฉ์ ๋ชจ์
- ์์ฐจ ๋ฐ์ดํฐ
โถ ์์
The book is on the table.
The boos are on the table.
์ ๋๊ฐ์ ๋ฌธ์ฅ์ ์์ด์์ ๋ค์์ธ์ง ๋ณต์์ธ์ง์ ๋ฐ๋ผ ๋์ฌ๊ฐ ๋ฌ๋ผ์ง๋ค. ์ด๋ฐ ๋ฌธ์ฅ์ ์๋์ ๊ฐ์ด ๋ฌธ์ฅ์ด ๊ธธ์ด์ง ์๋ก ์์กด์ฑ์ด ๋ ๋์์ง ์ ์๋ค.
The book that I got yesterday is on the table.
The books read by the second=grade children are shelved in the lower rack.
๋ฅ๋ฌ๋์์์ ์ํ์ค ๋ชจ๋ธ๋ง์ ์จ๊ฒจ์ง '์ํ ์ ๋ณด(์๋ ์ํ)'๋ฅผ ์ ์งํ๋ ๊ฒ๊ณผ ๊ด๋ จ์ด ์๋ค. ์ํ์ค์ ์๋ ๊ฐ ํญ๋ชฉ์ด ์๋ ์ํ๋ฅผ ์ ๋ฐ์ดํธ ํ๊ณ , ์ํ์ค ํํ์ด๋ผ๊ณ ๋ถ๋ฆฌ๋ ์ด ์๋์ํ์ ๋ฒกํฐ๋ฅผ ์ํ์ค ๋ชจ๋ธ๋ง ์์ ์ ํ์ฉํ๋ ๊ณผ์ ์ ๊ฑฐ์น๋ค. ๊ฐ์ฅ ๋ํ์ ์ธ ์ํ์ค ์ ๊ฒฝ๋ง ๋ชจ๋ธ์ 'RNN(Recurrent nerual network)'์ด๋ค. ๊ทธ๋ผ NLP์์์ ์ํ์ค ๋ชจ๋ฐ๋ฃจ RNN์ ๋ํด์ ์์๋ณด๋๋ก ํ์.
2. ์ํ ์ ๊ฒฝ๋ง, RNN (recurrent neural network)
- RNN์ ๋ชฉ์ ์ ์ํ์ค ํ ์๋ฅผ ๋ชจ๋ธ๋ง ํ๋ ๊ฒ
- ์ ๋ ฅ๊ณผ ์ถ๋ ฅ์ ์ํ์ค ๋จ์๋ก ์ฒ๋ฆฌํจ
- RNN์ ์ข
๋ฅ๋ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ง๋ง, ํด๋น ํฌ์คํ
์์๋ ์๋งRNN์ ๋ํด ๋ค๋ฃฐ ๊ฒ์
- ๋๊ฐ์ RNN ์ ํ์ฉํ sequence2sequence ๋ค์ํ RNN๋ชจ๋ธ์ด NLP์์ญ์์ ํ์ฉ๋๊ณ ์๋ค.
- ๊ฐ์ ํ๋ผ๋ฏธํฐ๋ฅผ ํ์ฉํด์ ํ์ ์คํ ๋ง๋ค ์ถ๋ ฅ์ ๊ณ์ฐํ๊ณ , ์ด๋ ์๋ ์ํ์ ๋ฒกํฐ์ ์์กดํด์ ์ํ์ค์ ์ํ๋ฅผ ๊ฐ์งํ๋ค.
- RNN์ ์ฃผ ๋ชฉ์ ์ ์ฃผ์ด์ง ์๋ ์ํ ๋ฒกํฐ์ ์ ๋ ฅ ๋ฒกํฐ์ ๋ํ ์ถ๋ ฅ์ ๊ณ์ฐํจ์ผ๋ก์จ ์ํ์ค์ ๋ถ๋ณ์ฑ์ ํ์ตํ๋ ๊ฒ์ด๋ค.
2.1 ๋์ ๋ฐฉ์
- ํผ๋ ํฌ์๋ ์ ๊ฒฝ๋ง๊ณผ๋ ๋ค๋ฅด๊ฒ, ์๋์ธต ๋ ธ๋์์ ํ์ฑํ ํจ์๋ฅผ ํตํด ๋์จ ๊ฒฐ๊ณผ๊ฐ์ ์ถ๋ ฅ์ธต ๋ฐฉํฅ์ผ๋ก๋ ๋ดฌ๋ฉด์ ๋์์ ๋ค์ ์๋์ธต ๋ ธ๋์ ๋ค์ ๊ณ์ฐ ์ ๋ ฅ์ผ๋ก ๋ณด๋ด๋ ํน์ง์ ๊ฐ์ง
- ์ฆ, ํ์ฌ ์ ๋ ฅ ๋ฒกํฐ์ ์ด์ ์๋ ์ํ ๋ฒกํฐ๋ก ํ์ฌ์ ์๋ ์ํ ๋ฒกํฐ๋ฅผ ๊ณ์ฐํจ
- ์๋งRNN ์์๋ ์๋๋ฒกํฐ๊ฐ ์์ธก ๋์์ด๋ค.

- ๊ตฌ์ฒด์ ์ธ ๊ณ์ฐ ๋ฐฉ์์ ์๋์ ๊ฐ๋ค

- ์๋-์๋ ๊ฐ์ค์น ํ๋ ฌ์ ์ฌ์ฉํด ์ด์ ์๋ ์ํ ๋ฒกํฐ๋ฅผ ๋งคํ
- ์ ๋ ฅ-์๋ ๊ฐ์ค์น ํ๋ ฌ์ ์ฌ์ฉํด ํ์ฌ ์ ๋ ฅ ๋ฒกํฐ๋ฅผ ๋งคํ
- ๋ ๊ฐ์ ๊ฐ์ ๋ํ์ฌ ์๋ก์ด ์๋ ๋ฒกํฐ๋ฅผ ์์ฑํ๋ ๊ณผ์ ์ ๊ฑฐ์น๋ค.
3. RNN ๊ตฌํํ๊ธฐ
- RNN์ ํ์ฉํด์ '์ฑ์จ ๋ถ๋ฅ'ํ๋ ์์ ๋ฅผ ์งํํ ์์
- ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด์ฃผ๋Dataset, Vocabulary, Vectorizer class๋ ์ด์ ํฌ์คํ ๊ณผ ๋์ผ (ํด๋ฆญ)
- ์ํ์ค ํํ์ ์ธํ ๋ฐ์ดํฐ๋ฅผ ํ์ฉํ๋ ๊ฒ์ด ์ฐจ์ด์
โถ column_gather ํจ์
- ๋ฐฐ์น ํ ์ธ๋ฑ์ค๋ฅผ ์ํํ๋ฉด์ x_lengths์ ์๋ ๊ฐ์ ํด๋นํ๋ ์ธ๋ฑ์ค ์์น (์ฆ, ์ํ์ค์ ๋ง์ง๋ง ์ธ๋ฑ์ค์ ์๋)์ ๋ฒกํฐ๋ฅผ ๋ฐํํ๋ ํจ์
def column_gather(y_out, x_lengths):
''' y_out์ ์๋ ๊ฐ ๋ฐ์ดํฐ ํฌ์ธํธ์์ ๋ง์ง๋ง ๋ฒกํฐ ์ถ์ถํฉ๋๋ค
์กฐ๊ธ ๋ ๊ตฌ์ฒด์ ์ผ๋ก ๋งํ๋ฉด ๋ฐฐ์น ํ ์ธ๋ฑ์ค๋ฅผ ์ํํ๋ฉด์
x_lengths์ ์๋ ๊ฐ์ ํด๋นํ๋ ์ธ๋ฑ์ค ์์น์ ๋ฒกํฐ๋ฅผ ๋ฐํํฉ๋๋ค.
๋งค๊ฐ๋ณ์:
y_out (torch.FloatTensor, torch.cuda.FloatTensor)
shape: (batch, sequence, feature)
x_lengths (torch.LongTensor, torch.cuda.LongTensor)
shape: (batch,)
๋ฐํ๊ฐ:
y_out (torch.FloatTensor, torch.cuda.FloatTensor)
shape: (batch, feature)
'''
x_lengths = x_lengths.long().detach().cpu().numpy() - 1
out = []
for batch_index, column_index in enumerate(x_lengths):
out.append(y_out[batch_index, column_index])
return torch.stack(out)
โถ์๋งRNN ํจ์
- RNNCell์ ์ฌ์ฉํ์ฌ ๋ง๋ RNN ๋ชจ๋ธ
- ์ต์ข ๊ฐ์ผ๋ก ๊ฐ ํ์step์์์ ์๋๋ฒกํฐ๋ฅผ ๋ฐํํ๋ค
class ElmanRNN(nn.Module):
""" RNNCell์ ์ฌ์ฉํ์ฌ ๋ง๋ ์๋ง RNN """
def __init__(self, input_size, hidden_size, batch_first=False):
"""
๋งค๊ฐ๋ณ์:
input_size (int): ์
๋ ฅ ๋ฒกํฐ ํฌ๊ธฐ
hidden_size (int): ์๋ ์ํ ๋ฒกํฐ ํฌ๊ธฐ
batch_first (bool): 0๋ฒ์งธ ์ฐจ์์ด ๋ฐฐ์น์ธ์ง ์ฌ๋ถ
"""
super(ElmanRNN, self).__init__()
# RNNCell์ ์ฌ์ฉํ์ฌ ์
๋ ฅ-์๋ ๊ฐ์ค์น ํ๋ ฌ๊ณผ ์๋-์๋๊ฐ์ค์นํ๋ ฌ์ ๋ง๋๋ ๊ฒ์
# RNNCell์ ํธ์ถํ ๋๋ง๋ค ์
๋ ฅ๋ฒกํฐ์ ์๋ํ๋ ฌ์ ๋ฐ๋๋ค
self.rnn_cell = nn.RNNCell(input_size, hidden_size)
self.batch_first = batch_first
self.hidden_size = hidden_size
def _initial_hidden(self, batch_size):
return torch.zeros((batch_size, self.hidden_size))
def forward(self, x_in, initial_hidden=None):
# ์
๋ ฅํ
์๋ฅผ ์ํํ๋ฉด์ ํ์
์คํ
๋ง๋ค ์๋์ํ ๋ฒกํฐ๋ฅผ ๊ณ์ฐํด์ฃผ๋ forward๋ฉ์๋
""" ElmanRNN์ ์ ๋ฐฉํฅ ๊ณ์ฐ
๋งค๊ฐ๋ณ์:
x_in (torch.Tensor): ์
๋ ฅ ๋ฐ์ดํฐ ํ
์
If self.batch_first: x_in.shape = (batch_size, seq_size, feat_size)
Else: x_in.shape = (seq_size, batch_size, feat_size)
initial_hidden (torch.Tensor): RNN์ ์ด๊ธฐ ์๋ ์ํ
๋ฐํ๊ฐ:
hiddens (torch.Tensor): ๊ฐ ํ์ ์คํ
์์ RNN ์ถ๋ ฅ
If self.batch_first:
hiddens.shape = (batch_size, seq_size, hidden_size)
Else: hiddens.shape = (seq_size, batch_size, hidden_size)
"""
# batch_first๊ฐ True์ด๋ฉด์ถ๋ ฅ์๋์ํ์ ๋ฐฐ์น ์ฐจ์์ 0๋ฒ์งธ๋ก ๋ฐ๊ฟ(?)
if self.batch_first:
batch_size, seq_size, feat_size = x_in.size()
x_in = x_in.permute(1, 0, 2)
else:
seq_size, batch_size, feat_size = x_in.size()
hiddens = []
if initial_hidden is None:
initial_hidden = self._initial_hidden(batch_size)
initial_hidden = initial_hidden.to(x_in.device)
hidden_t = initial_hidden
for t in range(seq_size):
hidden_t = self.rnn_cell(x_in[t], hidden_t)
hiddens.append(hidden_t)
hiddens = torch.stack(hiddens)
if self.batch_first:
hiddens = hiddens.permute(1, 0, 2)
return hiddens
โถํ๋ จ์์ ์ฌ์ฉํ SurnameClassifier ํจ์
- RNN๊ณผ Linear์ธต์ผ๋ก ๋๋จ
- RNN์ธต์์ ์ํ์ค์ ๋ฒกํฐ ํํ (ํ๋ ๋ฒกํฐ)๋ฅผ ๊ณ์ฐํด์ฃผ๊ณ ์ฑ์จ์ ๋ง์ง๋ง ๋ฌธ์์ ํด๋นํ๋ ๋ฒกํฐ๋ฅผ ์ถ์ถํด์ฃผ๋ ์ญํ ์ ์ํ (์ฑ์จ์ ๋ง์ง๋ง ๋ฌธ์์ ํด๋นํ๋ ๋ฒกํฐ๋, ์ต์ข ๋ฒกํฐ๋ฅผ ๋งํ๋ค.)
- ์ต์ข ๋ฒกํฐ๊ฐ ์ ์ฒด์ํ์ค ์ ๋ ฅ์ ๊ฑฐ์ณ ์ ๋ฌ๋ ๊ฒฐ๊ณผ๋ฌผ์ธ ์์ฝ๋ฒกํฐ๋ผ๊ณ ํ ์์๋ค.
- Linear์ธต์์ ์์ฝ๋ฒกํฐ๋ฅผ ํ์ฉํ์ฌ ์์ธก๋ฒกํฐ๋ฅผ ๊ณ์ฐํ๋ค.
# RNN์ธต, Linear ์ธต์ผ๋ก ๋๋จ
class SurnameClassifier(nn.Module):
""" RNN์ผ๋ก ํน์ฑ์ ์ถ์ถํ๊ณ MLP๋ก ๋ถ๋ฅํ๋ ๋ถ๋ฅ ๋ชจ๋ธ """
def __init__(self, embedding_size, num_embeddings, num_classes,
rnn_hidden_size, batch_first=True, padding_idx=0):
"""
๋งค๊ฐ๋ณ์:
embedding_size (int): ๋ฌธ์ ์๋ฒ ๋ฉ์ ํฌ๊ธฐ
num_embeddings (int): ์๋ฒ ๋ฉํ ๋ฌธ์ ๊ฐ์
num_classes (int): ์์ธก ๋ฒกํฐ์ ํฌ๊ธฐ
๋
ธํธ: ๊ตญ์ ๊ฐ์
rnn_hidden_size (int): RNN์ ์๋ ์ํ ํฌ๊ธฐ
batch_first (bool): ์
๋ ฅ ํ
์์ 0๋ฒ์งธ ์ฐจ์์ด ๋ฐฐ์น์ธ์ง ์ํ์ค์ธ์ง ๋ํ๋ด๋ ํ๋๊ทธ
padding_idx (int): ํ
์ ํจ๋ฉ์ ์ํ ์ธ๋ฑ์ค;
torch.nn.Embedding์ ์ฐธ๊ณ ํ์ธ์
"""
super(SurnameClassifier, self).__init__()
#๋จผ์ ์๋ฒ ๋ฉ์ธต์ ์ฌ์ฉํ์ฌ ์ ์๋ฅผ ์๋ฒ ๋ฉํด์ค
self.emb = nn.Embedding(num_embeddings=num_embeddings,
embedding_dim=embedding_size,
padding_idx=padding_idx)
# ๊ทธ ๋ค์ RNN์ธต์ผ๋ก ์ํ์ค์ ๋ฒกํฐํํ์ ๊ณ์ฐํด์ค
# ์ด ๋ฒกํฐ๋ ์ฑ์จ์ ์๋ ๊ฐ ๋ฌธ์์ ๋ํ ์๋์ํ๋ฅผ ๋ํ๋
# ์ฑ์จ์ ๋ง์ง๋ง ๋ฌธ์์ ํด๋นํ๋ ๋ฒกํฐ๋ฅผ ์ถ์ถ (์ต์ข
๋ฒกํฐ)
# ์ด ์ต์ข
๋ฒกํฐ๊ฐ ์ ์ฒด ์ํ์ค ์
๋ ฅ์ ๊ฑฐ์ณ ์ ๋ฌ๋ ๊ฒฐ๊ณผ๋ฌผ์ด๋ผ๊ณ ํ ์์์ (์ฑ์จ๋ฅผ ์์ฝํ ๋ฒกํฐ)
self.rnn = ElmanRNN(input_size=embedding_size,
hidden_size=rnn_hidden_size,
batch_first=batch_first)
# ์์ฝ๋ฒกํฐ๋ฅผ linear ์ธต์ผ๋ก ์ ๋ฌํ์ฌ ์์ธก๋ฒกํฐ ๊ณ์ฐ
# ์์ธก๋ฒกํฐ๋ฅผ ์ฌ์ฉํ์ฌ softmaxํจ์์ ์ ์ฉํ๊ฑฐ๋, ํ๋ จ ์์ค์ ๊ณ์ฐํ์ฌ ์ฑ์จ์ ๋ํ ํ๋ฅ ๋ถํฌ๋ฅผ ๋ง๋ ๋ค.
self.fc1 = nn.Linear(in_features=rnn_hidden_size,
out_features=rnn_hidden_size)
self.fc2 = nn.Linear(in_features=rnn_hidden_size,
out_features=num_classes)
# ์ํ์ค์ ๊ธธ์ด x_length๊ฐ ํ์
# ์ํ์ค์ ๊ธธ์ด๋ gather_column()ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํ
์์์ ์ํ์ค๋ง๋ค ๋ง์ง๋ง ๋ฒกํฐ๋ฅผ ์ถ์ถํ์ฌ ๋ฐํ
def forward(self, x_in, x_lengths=None, apply_softmax=False):
""" ๋ถ๋ฅ๊ธฐ์ ์ ๋ฐฉํฅ ๊ณ์ฐ
๋งค๊ฐ๋ณ์:
x_in (torch.Tensor): ์
๋ ฅ ๋ฐ์ดํฐ ํ
์
x_in.shape๋ (batch, input_dim)์
๋๋ค
x_lengths (torch.Tensor): ๋ฐฐ์น์ ์๋ ๊ฐ ์ํ์ค์ ๊ธธ์ด
์ํ์ค์ ๋ง์ง๋ง ๋ฒกํฐ๋ฅผ ์ฐพ๋๋ฐ ์ฌ์ฉํฉ๋๋ค
apply_softmax (bool): ์ํํธ๋งฅ์ค ํ์ฑํ ํจ์๋ฅผ ์ํ ํ๋๊ทธ
ํฌ๋ก์ค-์ํธ๋กํผ ์์ค์ ์ฌ์ฉํ๋ ค๋ฉด False๋ก ์ง์ ํฉ๋๋ค
๋ฐํ๊ฐ:
๊ฒฐ๊ณผ ํ
์. tensor.shape๋ (batch, output_dim)์
๋๋ค.
"""
x_embedded = self.emb(x_in)
y_out = self.rnn(x_embedded)
if x_lengths is not None:
# column_gather : ์กฐ๊ธ ๋ ๊ตฌ์ฒด์ ์ผ๋ก ๋งํ๋ฉด ๋ฐฐ์น ํ ์ธ๋ฑ์ค๋ฅผ ์ํํ๋ฉด์
# x_lengths์ ์๋ ๊ฐ์ ํด๋นํ๋(์ํ์ค์ ๋ง์ง๋ง ์ธ๋ฑ์ค์ ์๋) ์ธ๋ฑ์ค ์์น์ ๋ฒกํฐ๋ฅผ ๋ฐํํ๋ ํจ์
y_out = column_gather(y_out, x_lengths)
else:
y_out = y_out[:, -1, :]
y_out = F.relu(self.fc1(F.dropout(y_out, 0.5)))
y_out = self.fc2(F.dropout(y_out, 0.5))
if apply_softmax:
y_out = F.softmax(y_out, dim=1)
return y_out