2024. 3. 17. 18:18ㆍ딥러닝 모델: 파이토치
RNN은 시퀀스(sequence) 모델로, 입력과 출력을 시퀀스 단위로 처리하는 모델이다.
번역기를 생각해보면 입력은 번역하고자 하는 문장, 즉 단어 시퀀스이다. 출력에 해당하는 번역된 문장 또한 단어 시퀀스이다. 이러한 시퀀스들을 처리하기 위해 고안된 모델들을 시퀀스 모델이라고 한다. 그 중에서도 RNN은 딥러닝에서 가장 기본적인 시퀀스 모델이다.
♣ 순환신경망(RNN)
앞서 배운 신경망들은 모두 은닉층에서 활성화함수를 지난 값이 출력층 방향으로만 향했다. 이와 같은 신경망들을 Feed Forward Neural Network라고 한다. RNN은 이와 달리 은닉층의 노드에서 활성화함수를 통해 나온 결과값을 출력층 방향으로도 보내면서 다시 은닉층 노드의 다음 계산의 입력으로 보내는 특징을 갖는다.
그림으로 나타내면 위와 같다. x는 입력층의 입력 벡터, y는 출력층의 출력 벡터이다. 편향 b는 그림에서 생략한다. RNN에서 은닉층에서 활성화함수를 통해 결과를 내보내는 역할을 하는 노드를 셀(cell)이라고 한다. 이 셀은 이전의 값을 기억하는 일종의 메모리 역할을 수행하므로 이를 메모리 셀 또는 RNN 셀이라고 표현한다.
은닉층의 메모리 셀은 각각의 시점(time step)에서 바로 이전 시점에서의 은닉층의 메모리 셀에서 나온 값을 자신이 입력으로 사용하는 재귀적 활동을 한다. 앞으로는 현재의 시점을 변수 t로 표현한다. 이는 현재 시점 t에서의 메모리 셀이 갖고 있는 값은 과거의 메모리 셀들의 값에 영향을 받은 것임을 의미한다.
메모리 셀이 출력층 방향으로, 또는 다음 시점 t+1의 자신에게 보내는 값을 은닉 상태(hidden state)라고 한다. 다시 말해 t 시점의 메모리 셀은 t-1 시점의 메모리 셀이 보낸 은닉 상태값을 t 시점의 은닉 상태 계산을 위한 입력값으로 사용한다.
Feed Forward 신경망에서는 뉴런이라는 단위를 사용했지만, RNN에서는 뉴런이라는 단위보다는 입력층과 출력층에서 각각 입력 벡터와 출력 벡터, 은닉층에서는 은닉 상태라는 표현을 주로 사용한다. 그래서 위의 그림에서 회색과 초록색으로 표현한 각 네모들은 기본적으로 벡터 단위를 가정하고 있다.
피드 포워드 신경망과의 차이를 비교하기 위해 RNN을 뉴런 단위로 시각화해보자.
위의 그림은 입력 벡터의 차원이 4, 은닉 상태의 크기가 2, 출력층의 출력 벡터의 차원이 2인 RNN이 시점이 2일 때의 모습을 보여준다. 다시 말해 뉴런 단위로 해석하면 입력층의 뉴런 수는 4, 은닉층의 뉴런 수는 2, 출력층의 뉴런 수는 2이다.
RNN은 입력과 출력의 길이를 다르게 설계할 수 있으므로, 다양한 용도로 사용된다. 아래 그림은 입력과 출력의 길이에 따라서 달라지는 RNN의 다양한 형태를 보여준다.
RNN 셀의 각 시점별 입, 출력의 가장 보편적인 단위는 '단어 벡터'이다. 예를 들어 하나의 입력에 대해서 여러 개를 출력하는 모델(one-to-many)은 하나의 이미지 입력에 대해서 사진의 제목을 출력하는 이미지 캡셔닝(Image Captioning) 작업에 사용할 수 있다. 사진의 제목은 단어들의 나열이므로 시퀀스 출력이다.
또한 단어 시퀀스에 대해서 하나의 출력(may-to-one)을 하는 모델은 입력 문서가 긍정적인지 부정적인지를 판별하는 감성 분류(sentiment classification), 또는 메일이 정상 메일인지 스팸 메일인지 판별하는 스팸 메일 분류에 사용할 수 있다.
다 대 다 (many-to-many) 모델의 경우, 입력 문장으로부터 대답 문장을 출력하는 챗봇과 입력 문장으로부터 번역된 문장을 출력하는 번역기, 개체명 인식이나 품사 태깅과 같은 작업이 속한다.
이제 RNN에 대한 수식을 정의해보자.
현재 시점 t에서의 은닉 상태값을 ht라고 정의한다. 은닉층의 메모리 셀은 ht를 계산하기 위해 총 두 개의 가중치를 갖는다. 하나는 입력층에서 입력값을 위한 가중치 Wx이고, 하나는 이전 시점 t-1의 은닉 상태값인 h t-1을 위한 가중치 Wh이다.
이를 식으로 표현하면 다음과 같다.
출력층은 결과값인 yt를 계산하기 위한 활성화 함수로 상황에 따라 다르다. 이진 분류를 하는 경우에는 시그모이드 함수를, 다중 분류를 하는 경우에는 소프트맥스 함수를 사용할 수 있다.
♣ 파이썬으로 RNN 구현하기
numpy로 직접 RNN 층을 구현해보자. 앞서 메모리 셀에서 은닉 상태를 계산하는 식을 다음과 같이 정의했다.
실제 구현에 앞서 간단히 의사 코드(pseudocode)를 작성한다.
t 시점의 은닉 상태를 hidden_state_t 라는 변수로 선언하였고, 입력 데이터의 길이를 input_length로 선언하였다. 이 경우, 입력 데이터의 길이는 총 시점의 수(timesteps)가 된다. 그리고 t 시점의 입력값을 input_t로 선언하였다.
각 메모리 셀은 각 시점마다 input_t와 hidden_state_t(이전 상태의 은닉 상태)를 입력으로 활성화 함수인 하이퍼볼릭탄젠트 함수를 통해 현 시점의 hidden_state_t를 계산한다.
아래 코드는 이해를 돕기 위해 (timesteps, input_size) 크기의 2D 텐서를 입력으로 받았다고 가정한다. 하지만 실제로 파이토치에서는 (batch_size, timesteps, input_size)의 크기의 3D 텐서를 입력으로 받는다.
시점, 입력의 차원, 초기 은닉 상태를 정의하였다. 현재 초기 은닉 상태는 0의 값을 가지는 벡터로 초기화된 상태이다.
은닉 상태의 크기를 8로 정의하였으므로 8의 차원을 가지는 0의 값으로 구성된 벡터가 출력된다. 이제 가중치와 편향을 정의한다.
이제 모든 시점의 은닉 상태를 출력한다고 가정하고 RNN 층을 동작시켜보자.
np.dot(Wx, input_t)는 아래 예시와 같이 계산된다.
np.dot(Wh, hidden_state_t)는 아래 예시와 같이 계산된다.
----------------------------------------------------------------------------추가 정리
♣ RNN의 작동 방식
RNN은 시계열 또는 순차 데이터를 예측하는 딥러닝을 위한 신경망 아키텍처이다. RNN은 다양한 길이의 순차 데이터로 작업하고 자연 신호 분류, 언어 처리, 비디오 분석 등의 문제를 해결하는 데 특히 효과적이다.
RNN은 과거의 정보를 사용하여 현재 및 미래의 입력에 대한 신경망의 성능을 개선하는 딥러닝 구조이다. 전통적인 신경망들은 입력된 데이터에 대해서만 동작하기 때문에 연속적인 데이터를 처리하기 어렵다. '재귀'는 자신을 참조한다는 의미로, 현재 결과가 이전 결과와 연관성을 가진다는 의미이다. RNN의 독특한 점은 신경망에 은닉 상태 및 루프가 있다는 것이다. 루프 구조를 통해 신경망은 은닉 상태에 과거의 정보를 저장하고 시퀀스에 대해 연산할 수 있다.
즉 RNN은 입력층, 은닉층, 출력층의 3단계 구조로 이뤄져있다. RNN의 가장 큰 특징은 은닉층이 이전 데이터를 참조하도록 서로 연결되어 있다는 점이다. 즉 입력값 xt는 ht라는 결과를 출력함과 동시에 다음 출력값인 ht+1에 영향을 미친다.
RNN에는 두 개의 가중치 세트가 있다. 하나는 은닉 상태 벡터에 대한 가중치 세트이며, 다른 하나는 입력에 대한 가중치 세트이다. 신경망은 훈련 중에 입력과 은닉 상태, 두 가지 모두에 대한 가중치를 학습한다. 출력은 현재 입력, 그리고 이전 입력을 기반으로 하는 은닉 상태를 기반으로 한다.
♣ 참고 자료
https://brunch.co.kr/@linecard/324