[딥러닝 스터디] 순환신경망(RNN)

[딥러닝 스터디] 순환신경망(RNN)

다음의 책을 공부하며 정리한 내용입니다.

순환신경망(RNN : Recurrent Neural Network)

: RNN은 입력과 출력을 시퀀스 단위로 처리하는 시퀀스 모델이다. 이때의 입력은 처리하고자 하는 문장, 즉 단어 시퀀스이며, 출력은 처리된 문장 단어 시퀀스이다.

RNN의 가장 큰 특징은 은닉층의 노드에서 나온 결과값이 출력층은닉층 노드의 다음 계산을 위한 입력으로, 둘 모두로 보내진다는 점이다.

대체 텍스트

  • xt : 입력층의 입력 벡터

  • yt : 출력층의 출력 벡터

  • cell : 은닉층에서 결과를 두 방향으로 내보내는(출력층 & 다음연산) 노드. 메모리 셀 혹은 RNN셀이라고 표현한다.

    이때 메모리 셀이 두 방향으로 내보내는 결과를 은닉상태(hidden state) 라고 한다.

피드포워드 신경망에서는 기본적으로 뉴런이라는 단위를 사용했지만, RNN에서는

  • 입력층/출력층 -> 입력벡터/출력벡터
  • 은닉층 -> 은닉상태

의 표현을 일반적으로 사용한다.

피드포워드 신경망과 같이 뉴런 단위로 RNN을 시각화할 경우 아래와 같이 표현할 수 있다.

대체 텍스트

  • 입력벡터 차원(입력층의 뉴런 수) : 4
  • 은닉상태 크기(은닉층의 뉴런 수) : 2
  • 출력벡터 차원(출력층의 뉴런 수) : 2
  • 시점(timestep) : 2

RNN의 활용

: RNN은 입력과 출력의 길이가 고정되어 있지 않다. 즉, 설계에 따라 다양한 용도로 신경망을 사용할 수 있다.

img

일대다 모델

  • 하나의 이미지 입력에 대해서 사진의 제목을 출력하는 이미지 캡셔닝(Image Captioning) 작업에 사용할 수 있다.
  • 사진의 제목은 단어들의 나열이므로 시퀀스 출력이다.

img

다대일 모델

  • 단어 시퀀스에 대해서 하나의 출력(many-to-one)을 하는 모델.
  • 입력 문서가 긍정적인지 부정적인지를 판별하는 감성 분류(sentiment classification), 또는 메일이 정상 메일인지 스팸 메일인지 판별하는 스팸 메일 분류(spam detection)에 사용할 수 있다.
  • 위 그림은 RNN으로 스팸 메일을 분류할 때의 아키텍처를 보여줍니다.

img

다대다 모델

  • 다 대 다(many-to-many)의 모델의 경우에는 입력 문장으로 부터 대답 문장을 출력하는 챗봇과 입력 문장으로부터 번역된 문장을 출력하는 번역기, 개체명 인식이나 품사 태깅과 같은 작업이 속한다.
  • 위 그림은 개체명 인식을 수행할 때의 RNN 아키텍처를 보여줍니다.

RNN의 수식

img

  • ht : 현재시점 t에서의 은닉 상태값
  • wx : 입력층의 입력값에 대한 가중치
  • wt : 이전시점 t-1의 은닉상태값 ht-1 에 대한 가중치 wh

따라서 ht를 계산하는 수식은 다음과 같다.

ht = activation_func((wh * ht-1) + (wx * xt) + b)

이때 활성화 함수는 일반적으로 tanh함수를 사용한다. ReLU를 사용하기도 한다. 출력층 값 yt는 아래와 같이 계산한다.

yt = activation_func((wy * ht) + b)

이때 비선형 활성화 함수 중 하나를 activation func으로 사용한다.


(실습) 파이썬으로 RNN 구현하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import torch
import torch.nn as nn

# 입력과 은닉상태의 크기를 정의한다.
input_size = 5
hidden_size = 8

# 입력텐서(=입력벡터)를 정의한다.
# (배치크기 * 시점의 수 * 입력크기)를 인자로 받는다.
input_vec = torch.Tensor(1, 10, input_size)

# nn.RNN()으로 RNN셀을 정의한다.
# (입력크기 * 은닉상태 크기)를 인자로 받는다. batch_first=True는 입력텐서의 첫번째 차원이 배치크기임을 알려준다.
cell = nn.RNN(input_size, hidden_size, batch_first=True)

# 입력텐서를 RNN셀에 넣어 출력값의 크기를 확인해본다.
# (모든 시점의 은닉상태들, 마지막 시점의 은닉상태)를 반환한다.
outputs, final_output = cell(input_vec)
print(outputs.shape)
print(final_output.shape)
1
2
3
[결과]
torch.Size([1, 10, 8])
torch.Size([1, 1, 8])

다양한 순환신경망

  • 깊은 순환신경망(Deep Recurrent Neural Network)

    : RNN역시 다수의 은닉층을 가질 수 있다. 2개 이상의 은닉층을 가진 RNN을 Deep RNN이라고 한다.

img

깊은 순환 신경망은 nn.RNN()의 인자로 num_layers 파라미터를 추가해줌으로서 구현할 수 있다.

1
2
# (입력텐서 크기, 은닉층 크기, 은닉층 개수)
cell = nn.RNN(input_size = 5, hidden_size = 8, num_layers = 2, batch_first=True)

이때 마지막 시점의 은닉상태는 다음과 같이 바뀐다.

1
2
3
4
print(final_output.shape)

# (층의 개수, 배치 크기, 은닉 상태의 크기)
>> torch.Size([2, 1, 8])
  • 양방향 순환신경망(Bidirectional Recurrent Neural Network)

    : 양방향 순환신경망은 특정 시점 t에서 출력값 ht를 예측할 때 이전시점의 데이터 ht-1뿐만 아니라 이후시점의 데이터로도 예측할 수 있다는 아이디어에서 출발한다.

img

(예제)

1
2
3
4
5
Exercise is very effective at [          ] belly fat.

1) reducing
2) increasing
3) multiplying

정답 reducing을 찾기 위해서는 이전에 나온 단어와 이후에 나온 단어 모두를 참고\해야 결정할 수 있다.

즉, 양방향 RNN은 이전 시점의 데이터뿐만 아니라, 이후 시점의 데이터도 힌트로 활용하기 위해서 고안된 모델이다.

img

  • 양방향 순환 신경망은 하나의 출력값 ht를 예측하기 위해 두개의 메모리 셀을 사용 한다.
    • 첫번째 메모리 셀은 앞 시점의 은닉상태를 전달받아 계산한다.
    • 두번째 메모리 셀은 뒤 시점의 은닉상태를 전달받아 계산한다.
  • 양방향 RNN도 다수의 은닉층을 가질 수 있다.
    • nn.RNN()의 인자로 bidirectional값을 True로 전달하여 구현할 수 있다.
1
2
# (입력텐서 크기, 은닉층 크기, 은닉층 개수, 양방향 여부)
cell = nn.RNN(input_size = 5, hidden_size = 8, num_layers = 2, batch_first=True, bidirectional=True)

이때 마지막 시점의 은닉상태는 다음과 같이 바뀐다.

1
2
3
4
print(final_output.shape)

# (층의 개수 * 2, 배치 크기, 은닉 상태의 크기)
>> torch.Size([4, 1, 8])

댓글