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

zoya·2024년 5월 1일

인공지능 공부

목록 보기
15/19

순환 신경망(RNN)은 시퀀스(Sequence)모델입니다. 입력과 출력을 시퀀스 단위로 처리하는 모델입니다. 번역기를 생각해보면 입력은 번역하고자 하는 문장 즉, 단어 시퀀스입니다. 출력에 해당되는 번역된 문장 또한 단어 시퀀스입니다. 이러한 시퀀스들을 처리하기 위해 고안된 모델들을 시퀀스 모델이라고 합니다. 그 중에서도 RNN은 딥 러닝에 있어 가장 기본적인 시퀀스 모델입니다.

1. 순환 신경망

앞서 배운 신경망들은 전부 은닉층에서 활성화 함수를 지난 값은 오직 출력층 방향으로만 향했습니다. 이와 같은 신경망을 피드 포워드 신경망(Feed Forward Neural Network)이라고 합니다. 물론 한 방향으로만 가는 신경망만 있는 것은 아닙니다.
RNN은 은닉층의 노드에서 활성화 함수를 통해 나온 결과값을 출력층 방향으로도 보내면서 다시 은닉층 노드의 다음 계산의 입력으로 보내는 특징을 가지고 있습니다.

이를 그림으로 표현하면 위와 같습니다. x는 입력층의 입력 벡터, y는 출력층의 출력 벡터입니다. 실제로는 편향 b도 입력으로 존재할 수 있지만 앞으로의 그림에서는 생략하겠습니다. RNN에서는 은닉층에서 활성화 함수를 통해 결과를 내보내는 역할을 하는 노드를 셀(cell) 이라고 합니다. 이 셀은 이전의 값을 기억하려고 하는 일종의 메모리 역할을 수행하므로 이를 메모리 셀 또는 RNN 셀이라고 표현합니다.

은닉층의 메모리 셀은 각각의 시점에서 바로 이전 시점에서의 은닉칭의 메모리 셀에서 나온 값을 자신의 입력으로 사용하는 재귀적 활동을 하고 있습니다. 앞으로는 현재 시점을 변수 t로 표현하겠습니다. 이는 현재 시점 t에서의 메모리 셀이 갖고 있는 값은 과거의 메모리 셀들의 값에 영향을 받은 것임을 의미합니다. 그렇다면 메모리 셀이 가지고 있는 이 값은 뭐라고 부를까요?
메모리 셀이 출력층 방향으로 또는 다음 시점인 t+1의 자신에게 보내는 값을 은닉 상태(hidden state)라고 합니다. 다시 말해 t 시점의 메모리 셀은 t-1 시점의 메모리 셀이 보낸 은닉 상태값을 t 시점의 은닉 상태 계산을 위한 입력값으로 사용합니다.

RNN을 표현할 때는 일반적으로 위의 그림에서 좌측과 같이 화살표로 사이클을 그려서 재귀 형태로 표현하기도 하지만 우측과 같이 사이클을 그리는 화살표 대신 여러 시점으로 펼쳐서 표현하기도 합니다. 두 그림은 동일한 그림으로 단지 사이클을 그리는 화살표를 사용하여 표현하였느냐 아니면 시점의 흐름에 따라서 표현하였느냐의 차이일 뿐 둘 다 동일한 RNN을 표현하고 있습니다.

Feed Forward Network 에서는 뉴런이라는 단위를 사용했지만 RNN에서는 뉴런이라는 단위보다는 다른 표현을 사용합니다. 입력층과 출력층에서는 각각 입력 벡터와 출력 벡터, 은닉층에서는 은닉 상태라는 표현을 주로 사용합니다. 그래서 사실 위의 그림에서 회색과 초록색으로 표현한 각 사각형들은 기본적으로 벡터 단위를 가정하고 있습니다. Feed Forward Network와의 차이를 비교하기 위해서 RNN을 뉴런 단위로 시각화해보겠습니다.

위의 그림은 입력 벡터의 차원이 4, 은닉 상태의 크기가 2, 출력층의 출력 벡터의 차원이 2인 RNN이 시점이 2일 때의 모습을 보여줍니다. 다시 말해 뉴런 단위로 해석하면 입력층의 뉴런 수는 4, 은닉층의 뉴런 수는2, 출력층의 뉴런 수는 2입니다.

RNN은 입력과 출력의 길이를 다르게 설계할 수 있으므로 다양한 용도로 사용할 수 있습니다. 위 그림은 입력과 출력의 길이에 따라서 달라지는 RNN의 다양한 형태를 보여줍니다.
이런 구조가 자연어 처리에서는 어떻게 사용될 수 있는지 예를 들어보겠습니다. RNN 셀의 각 시점 별 입출력의 단위는 사용자가 정의하기 나름이지만 가장 보편적인 단위는 단어 벡터입니다.

단어 시퀀스에 대해서 하나의 출력을 하는 모델은 입력 문서가 긍정적인지 부정적인지를 판별하는 감성 분류(Sentiment Classification), 스팸 메일 분류(Spam Detection)에 사용할 수 있습니다. 위 그림은 RNN으로 스팸 메일을 분류할 때의 구조를 보여줍니다.

many-to-many의 모델의 경우에는 입력 문장을부터 대답 문장을 출력하는 챗봇과 입력 문장으로부터 번역된 문장을 출력하는 번역기, 개체명 인식이나 품사 태깅과 같은 작업이 속합니다. 위 그림은 개체명 인식을 수행할 때의 RNN 구조입니다.


RNN에 대한 수식을 정의해보겠습니다.

현재 시점 t에서의 은닉 상태값을 ht라고 정의하겠습니다. 은닉층의 메모리 셀은 ht를 계산하기 위해서 총 2개의 가중치를 가지게 됩니다. 하나는 입력층에서 입력값을 위한 가중치 Wx이고 하나는 이전 시점 t-1의 은닉 상태값인 ht-1을 위한 가중치 Wh입니다.
이를 식으로 표현하면 다음과 같습니다.

RNN의 은닉층 연산을 벡터와 행렬 연산으로 이해할 수 있습니다. 자연어 처리에서 RNN의 입력 Xt는 대부분의 경우에서 단어 벡터로 간주할 수 있는데 단어 벡터의 차원을 d라고 하고 은닉 상태의 크기를 Dh라고 하였을 때 각 벡터와 행렬의 크기는 다음과 같습니다.

배치 크기가 1이고 d와 Dh 두 값 모두를 4로 가정하였을 때 RNN의 은닉층 연산을 그림으로 표현하면 다음과 같습니다.

이때 ht를 계산하기 위한 활성화 함수로는 주로 하이퍼볼릭탄젠트 함수(tanh)가 사용되지만 ReLU로 바꿔 사용하는 시도도 있습니다.
출력층은 결과값인 Yt를 계산하기 위한 활성화 함수로는 상황에 따라 다릅니다. 예를 들어서 이진 분류를 해야하는 경우라면 시그모이드 함수를 사용할 수 있고 다양한 선택지 중 하나를 고르는 문제라면 소프트맥스 함수를 사용할 수 있습니다.

1. RNN 구현

import torch
import torch.nn as nn

input_size = 5
hidden_size = 8

inputs = torch.Tensor(1, 10, 5)

cell = nn.RNN(input_size, hidden_size, batch_first=True)
outputs, _status = cell(inputs)

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


앞서 RNN도 다수의 은닉층을 가질 수 있다고 했습니다. 위 그림은 순환 신경망에서 은닉층이 1개 더 추가되어 은닉층이 2개인 깊은 순환 신경망의 모습을 보여줍니다. 첫번째 은닉층은 다음 은닉층에 모든 시점에 대해서 은닉 상태 값을 다음 은닉층으로 보내주고 있습니다.
깊은 순환 신경망을 파이토치로 구현할 때는 nn.RNN()의 인자인 num_layers에 값을 전달하여 층을 쌓습니다. 층이 2개인 깊은 순환 신경망의 경우에는 다음과 같이 구현합니다.

import torch
import torch.nn as nn

input_size = 5
hidden_size = 8

inputs = torch.Tensor(1, 10, 5)

cell = nn.RNN(input_size, hidden_size, num_layers=2, batch_first=True)

3. 양방향 순환 신경망(Bidirectional Recurrent Neural Network)

양방향 순환 신경망은 시점 t에서의 출력값을 예측할 때 이전 시점의 데이터뿐만 아니라 이후 데이터로도 예측할 수 있다는 아이디어에 기반합니다.

Exercise is very effective at [ ] belly fat.

1) reducing
2) increasing
3) multiplying

'운동은 복부 지방을 [] 효과적이다.' 라는 영어 문장에서 정답은 reducing 입니다. 정답을 고르기 전에 belly fat이라는 목적어를 모른다면 정답을 고르기가 어렵습니다.
즉, RNN이 과거 시점의 데이터들을 참고해서 찾고자하는 정답을 예측하지만 실제 문제에서는 과거 시점의 데이터만 고려하는 것이 아니라 향후 시점의 데이터에 힌트가 있는 경우도 많습니다.
그래서 이전 시점의 데이터뿐만 아니라 이후 시점의 데이터도 힌트로 활용하기 위해서 고안된 것이 양방향 RNN 입니다.

양방향 RNN은 하나의 출력값을 예측하기 위해 기본적으로 두 개의 메모리 셀을 사용합니다. 첫번째 메모리 셀은 앞에서 보았던 것처럼 앞 시점의 은닉 상태(Forward States)를 전달받아 현재의 은닉 상태를 계산합니다. 주황색 셀이 이에 해당됩니다. 두번째 메모리 셀은 앞 시점의 은닉 상태가 아니라 뒤 시점의 은닉 상태(Backward Stated)를 전달 받아 현재의 은닉 상태를 계산합니다. 초록색 메모리 셀에 해당됩니다. 그리고 이 두 개의 값 모두가 출력층에서 출력값을 예측하기 위해 사용됩니다.

물론 양방향 RNN도 다수의 은닉층을 가질 수 있습니다. 아래의 그림은 양방향 순환 신경망에서 은닉층이 1개 더 추가되어 은닉층이 2개인 깊은 양방향 순환 신경망의 구조입니다.

다른 인공 신경망 모델들도 마찬가지이지만 은닉층을 무조건 추가한다고 해서 모델의 성능이 좋아지는 것은 아닙니다. 은닉층을 추가하면 학습할 수 있는 양이 많아지지만 또한 반대로 훈련 데이터 또한 그만큼 많이 필요합니다.
양방향 순환 신경망을 파이토치로 구현할 때는 nn.RNN()의 인자인 bidirectional에 값을 True로 전달하면 됩니다.

import torch
import torch.nn as nn

input_size = 5
hidden_size = 8

inputs = torch.Tensor(1, 10, 5)

cell = nn.RNN(input_size, hidden_size, num_layers=2, batch_first=True, bidirectional=True)
outputs, _status = cell(inputs)
profile
동물을 좋아하는 개발자(희망)의 저장소

0개의 댓글