728x90

 

지금까지 시퀀스 투 시퀀스 모델에 대해서 살펴보았고, 이번에 어떻게 동작하는지 구체적인 예시로 언어 모델 language model에 대해서 살펴보자. 기본적으로 무한한 입력 데이터 스트림, 그러니까 데이터 길이 제한없이 입력을 받아서, 매 시점마다 다음에 올 문자를 예측하는 순환 신경망 모델을 만든다고 할때 이 모델을 언어 모델이라고 부른다.

 

 언어 모델을 다룰때는 기본적으로 고정 크기의 단어 집합을 가지고 있으며, 지금 우리가 다루는 경우에 단어 사전은 h, e, l, o로 이루어져 있으며 훈련 과정에서 사용하여야 한다. 오른쪽 그림을 보면 훈련 시퀀스로 "hello"라는 말을 처리하고자 하는경우인데, 이 입력 시퀀스의 각 문자들을 단어 사전 만큼의 길이를 가지는 원핫 인코딩 벡터로 변환시켜서 사용할 수 있다.

 

 단어 사전 h, e, l, o와 사전의 길이를 알고 있고, 문자 h를 벡터로 바꾸고자 하는 경우 [1 0 0 0]이 되며, 이는 단어 사전의 첫번째 원소가 h를 나타내기 때문이다. 이 입력 시퀀스/원핫 인코딩 벡터를 순환 신경망에 입력으로 넣을수가 있겠다.

 

 그래서 입력 벡터들의 시퀀스를 순환 신경망에 넣어서 은닉 상태들의 시퀀스를 구할수 있고, 

-> 원 핫 인코딩 벡터로 해당 타임 스탭의 은닉 상태를 구한다.

 그리고 이 순환 신경망이 다음 시퀀스 원소로 뭐가 올지 예측하는 작업을 하기 때문에, 매 타임 스탭마다 얻는 출력 행렬들로 단어 사전에 존재하는 각 원소들에 대한 분포를 예측하므로 그러니까 어떤 단어일 확률이 가장 높은지 찾아내는데 사용할수 있겠다. 

-> 매 타임 스탭마다 다음에 올 단어가 뭔지 예측한다.

 

 입력으로 시퀀스 첫번째 요소인 "h"를 받았을때, 다음에 올 원소/단어가 e여야 하는데 교차 엔트로피 비용을 사용한다.

 다음으로 "he"를 입력으로 받은경우, 다음 단어가 세번째 단어로 "l"이 와야 하는것으로 예측해야 되겠다.

 

 

네번째 단어도 그렇게 진행해서

 전체 타겟 출력들 "ello"을 보면 타겟 입력 "hell"에서 한칸 이동한것과 동일한게 되겠다. 훈련된 언어 모델을 얻으면, 언어 모델은 학습하는 과정에서 입력 시퀀스를 주고, 출력은 입력 시퀀스를 시프트 시킨것과 같은 형태가 된다.

* 입력 : hell -> 출력 : ello

  그렇게 해서 매 타임스탭마다 다음 단어가 무엇이 올지 예측하도록 학습하게 된다. 일단 언어 모델을 학습해서 구하면, 꽤 다양한 일들을 할수 있겠는데, 학습 시킨 언어 모델로 새로운 텍스트를 만들어 낼수가 있다. 

 

 

 예를 들면 초기화 시드 토큰으로 문자 "h"를 주면, 순환 신경망이 주어진 초기화 시드 토큰에 따라 문자를 생성해내도록 한다고 할때 이게 처리 되는 과정은 일단 입력 토큰으로 "h"를 주자. 그러면 "h"에 대한 원 핫 인코딩이 한 층을 지나가면서 처리될것이고, 이 타임 스탭에서 다음 문자가 어떤게 올지 예측 확률 분포(softmax)를 얻을것이다. 

 

  이 모델은 다음 타임 스탭에 와야하는 단어에 대한 예측 분포를 가지므로, 이 분포로부터 다음 타임 스탭에 이 모델이 오는게 적합하다고 판단하는 단어를 구할수가 있겠다.

-> 출력 층에서 교차 엔트로피 비용이 나오고, 이 비용에 소프트 맥스를 취하여 예측 확률 분포를 구한다. 이 예측 확률 분포가 다음 타임 스탭에서 올 가능성이 높은 단어를 고를수가 있겠다.

* 다음 단어가 잘 오도록 학습 되야겠다.

 

 

이렇게 구한 문자를 가지고와서, 첫번째 타임 스탭의 출력이었던거를 다음 타임 스탭의 입력으로 사용하면 되겠다. 그래서 문자 e를 가지고 와서 다음 타임 스탭에 입력으로 넣고

 

 이 순환 신경망의 다른 계층을 통과하여 새로운 은닉 상태를 계산하고, 출력에 대한 예측 분포를 계산하여 다음 단어가 무엇이 오는지 예측 할 수  있겠다.

 

 이런 과정을 반복하게 되겠다. 학습된 언어 모델을 가지고 있으면 초기 토큰/단어을 가지고 그 다음으로 나올 가능성이 가장 큰 새 토큰/단어를 생성시킨다고 볼수 있다. 

 

 

 

 여기서 제대로 다루지 못한 부분이 있는데, 입력 시퀀스의 인코딩을 원 핫 벡터들의 집합이라고만 했다. 순환 신경망의 첫번째 계층에서 어떤 일들이 일어나는지 한번 살펴보자.

 

 바닐라 순환 신경망을 다시 떠올려보면, 입력 벡터가 주어지고 가중치 행렬을 곱한다고 했었다. 

 

 그런데 입력 벡터가 원 핫 인코딩이라면 한 슬롯만 1이고, 나머지 슬롯들은 전체가 0이 되버린다. 그러면 가중치 행렬과의 곱은 너무 단순해지는데, 원 핫 벡터와 가중치 행렬을 곱한 결과 벡터의 한 컬럼(w_11, w_21, w_31)만 추출해버리게 된다.

 

 

 이런 식으로 곱셈 연산을 구현하기 보다는 가중치 행렬의 행들을 더 효율적으로 추출할수 있도록 구현할수가 있는데, 가장 쉽게 사용하는 방법은 입력과 순환 신경망 사이에 임베딩 계층이라고 부르는 계층을 추가하는 방법이다.

 

 여기 보면 임베딩 계층이 추가되어 있는데, 입력 시퀀스는 원핫 벡터의 집합으로 인코딩 되고, 임베딩 계층은 원핫 벡터와 희소 행렬을 묵시적으로 곱연산을 수행한다. 그렇게 해서 임베딩 계층은 주어진 임베딩 행렬의 각 열들을 따로 따로 학습하게 된다. (처음엔 w11, w21, w31, 다음엔 w12, w22, w32, 다음엔 ... 이런 식으로 각 열들을 따로 학습)

* 단어 -> 임베딩 벡터(원핫 인코딩) -> 임베딩 벡터(원핫 인코딩)으로 임베딩 행렬(을 열로 분리해서) 학습.

 

 이게 순환 신경망에서 흔히 나타는 구조이며, 분리된 임베딩 계층을 가지고 이 분리된 임베딩 계층(열)은 은닉 상태를 계산하기 전과 원핫 벡터 사이에 위치한다. 

 

 

 

 그래서 지금까지 본 것들을 학습시키기 위해서, 순환 신경망 계산 그래프 예시를 살펴보았다. 순환 신경망을 학습하기 위해서 우선 본 건, 시간에 따라 펼쳐지는 계산 그래프가 필요하였으며, 매 타임 스탭/시점 마다 비용을 계산 후 전체 시퀀스 길이만큼 진행해서 하나의 비용으로 합해야 한다. 

 

 이 과정을 시간 흐름에 따른 역전파 backpropagation through time이라고 부르는데, 순전파를 하는 동안 시간도 흘러가고, 역전파를 하는 과정에서도 순전파에서 진행한대로 역으로 흘러가기 때문이다.

 

 

 이 시간 흐름에 따른 역전파에서의 문제는 아주 긴 시퀀스를 다뤄야 하는 경우로 학습 시켜야할 시퀀스가 몇백만개의 단어라면 계산 그래프로 수백만 타임 스탭에 풀어나가려면 아주 많은 메모리 공간이 필요할것이고 우리가 가진 GPU로는 학습하기 어려울 것이다.

 

 

 이런 이유로 순환 신경망을 학습하는 경우, 특히 아주아주 긴 시퀀스를 처리하는 순환 신경망 언어 모델을 학습하는경우 절단 시간 역전파 truncated back propagation through time이라고 하는 근사 알고리즘을 대안으로 사용한다.

 

 위 슬라이드에 이 방법에 대한 아이디어를 볼수 있는데, 무한개의 시퀀스를 다루는 신경망을 학습하고자하는 경우로 시퀀스의 일부를 첫 토큰으로 수샙가내 수백개를 가지고와서 그 시퀀스 일부만큼 신경망을 풀어나가겠다. 그리고 시퀀스의 첫 덩어리의 비용만 계산하고, 시퀀스 초기 덩어리 진행한 만큼 역전파를 하여 가중치를 갱신하며 초기 시퀀스 덩어리 맨 끝에서부터 은닉 층 값을 고쳐나가겠다.

 

 그 다음으로 시퀀스의 두번째 덩어리를 받아서 은닉 가중치들을 지나가는데, 이 가중치들은 첫번째 덩어리를 처리하고나서 수백개의 문자로 된 두번째된 시퀀스 덩어리를 풀어나가며 구한다.

 

 그리고 이렇게 다음으로 들어온 수백개의 문자 시퀀스를 풀어나가면서 두번째 덩어리에 대한 비용을 계산하고, 전체 시퀀스가 아니라 두 번째 시퀀스 덩어리만큼 역전파를 진행하겠다. 그리고 이 비용으로 각 가중치 행렬들의 그라디언트를 계산하여 가중치 행렬을 갱신시키고 다음 시퀀스 덩어리로 넘어간다.

 

 다음으로 진행을 하는데, 두번째 덩어리가 지나가면서 얻은 은닉상태로 순방향으로 세번째 시퀀스 덩어리를 풀어나가는데 사용하고, 시간 변화에 따른 절단 역전파를 해서 가중치는 갱신 되겠다.

 

 이 시간 변화에 따른 역전파 알고리즘이 하는 일은 기본적으로 은닉 정보를 계속 순방향으로 이동 시키면서 무한개의 시퀀스를 처리할수가 있으나, 시퀀스의 일부 덩어리만 가지고 역전파를하면서 GPU 메모리 공간 사용량을 크게 줄임으로서, 이 절삭 역전파 알고리즘 truncated back propagation으로 유한한 메모리 공간을 가지고 있더라도 무한개의 시퀀스를 순환 신경망에 학습 시키는게 가능하도록 만들었다.

 

 

(질문 1) 시간 변화에 따른 절삭 역전파 방법을 사용할때 어떻게 두번째 덩어리의 h0를 설정할까요.

 

 가지고 있는 최종 은닉 상태를 사용하겠다. 일단 첫번째 작업을 마치고 나서 첫번째 덩어리에 대한 최종 은닉 상태를 가지게 되는데, 이 은닉 상태가 두번째 덩어리에서 사용항 초기 은닉 상태가 되겠다. 은닉 상태를 통해서 정보를 시간에 따라 순방향으로 계속 전달하다보니 잠재적으로는 무한하게 처리할수 있겠다. 하지만 역전파는 시퀀스의 일부 덩어리만 가지고 한다.

 

 

(질문 2) 절삭 역전파 알고리즘 수행하는 동안 가중치 갱신이 어떻게 되는가?

 

 한 덩어리 만큼 순전파를 하고, 그 덩어리만큼 역전파도 하고, 가중치 행렬을 갱신한 다음에 은닉 상태를 순방향으로 복사시키고 역방향을 갱신시키기도 한다. 다시말하면 순방향으로는 복사시키고 역방향으로는 가중치 행렬 갱신에 사용된다. 시퀀스 일부 덩어리를 역전파를 할때마다 그 덩어리 비용의 가중치 행렬에 대해서 미분치를 구하고, 이 그라디언트를 신경망 가중치 갱신에 사용하겠다.

 

 

 시간 변화에 따른 절삭된 역전파 알고리즘을 정리하자면, 데이터들을 한 덩어리씩 처리함으로서 메모리 부족 문제를 해결하였다. 나머지 학습하는데 필요한 시퀀스의 모든 정보들을 그 순환 신경망의 최종 은닉 상태에 저장이 시킨 덕분이다.

 

gist.github.com/karpathy/d4dee566867f8291f086

 

 

 이 전체 과정이 복잡해 보이기는 하지만, 112줄의 파이썬 코드만으로도 절삭 역전파 알고리즘 처리과정과 언어 모델 그리고 새로운 텍스트를 생성하기까지의 전체 처리과정을 구현할수가 있다. 여기서는 파이토치나 그라디언트를 계산하기 위해서 오토 그래프 같은 방법들이 사용하지도 않았지만, 파이토치를 사용한다면 4~50라인으로 만들수가 있겠다.

300x250

+ Recent posts