1. seq2seq
■ seq2seq는 한 시계열 데이터를 다른 시계열 데이터로 변환하는 신경망 모델로 '인코더-디코더(Encoder-Decoder)' 모델이라는 신경망 모델의 일종이다.
■ Encoder-Decoder 모델은 입력 데이터를 인코딩(부호화)하는 Encoder와 인코딩된 데이터를 디코딩(복호화)하는 모델로 구성된다.
- 인코딩은 데이터를 어떤 형식, 규칙 등에 따라 변환하는 것을 말한다. 예컨대 문자 'A'는 ASCII에서 숫자 65로 인코딩된다.
- 디코딩은 인코딩된 데이터를 원래의 데이터로 되돌리는 것을 말한다. ASCII로 인코딩된 숫자 65를 디코딩하면 문자'A'로 변환된다.
■ 다음 그림은 '나는 학생이다.'라는 한글 문장을 입력 받아, 'I am a student'라는 영어 문장을 출력하는 seq2seq의 예이다.

- 한 시계열 데이터('나는 학생이다')가 다른 시계열 데이터('I am a student')로 변환되는 것을 볼 수 있다.
- 다른 관점에서 보자면, Encoder는 자연어를 이해하는 역할을 수행하고, 이해한 내용을 Decoder에 전달하면 Decoder는 전달받은 내용을 토대로 자연어를 생성하는 역할을 수행한다.
■ 이 예시의 내부 과정은 먼저, Encoder가 입력 문장의 모든 단어를 순차적으로 받아, 모든 단어 정보들을 압축해 하나의 벡터를 만든다.
■ 이 벡터를 컨텍스트 벡터(context vector)라고 한다. 이 예시에서 컨텍스트 벡터에는 번역에 필요한 정보가 압축되어 있다.
■ Encoder는 컨텍스트 벡터를 Decoder로 전달하고, Decoder는 컨텍스트 벡터를 받아서 번역된 단어를 한 개씩 순차적으로 출력한다.
■ 이 예시의 내용이 seq2seq의 전체 그림이다. 이때, Encoder와 Decoder로 RNN을 사용할 수 있다.
■ 예를 들어 문장을 단어 단위로 입력하고 Encoder와 Decoder로 RNN을 사용했을 때, Encoder의 구조는 다음과 같이 시계열 데이터를 은닉 상태 벡터로 변환한다.

- 한국어 문장을 토근 단위로 분할하여 Embedding 레이어에 입력한다. 이를 통해 각 토큰을 밀집 벡터로 변환한다.
- '나', '는'. '학생', '이다' 각각이 밀집 벡터로 변환되어 LSTM 레이어에 입력된다.
- 임베딩된 단어 벡터들이 순서대로 LSTM에 들어가서 각 시점마다 은닉 상태와 기억 셀을 업데이트한다.
- 마지막 토큰('이다')까지 처리한 뒤, 최종 은닉 상태를 Decoder에 전달한다. 전달된 은닉 상태 벡터에는 번역에 필요한 정보가 압축되어 있다.
■ Encoder가 마지막 시점에서 출력하는 은닉 상태 벡터 h가 컨텍스트 벡터이다. 즉, 이 h에 입력 문장을 번역하는데 필요한 정보가 인코딩되는 것이다.
■ 이때, 은닉 상태 벡터 h는 입력 데이터의 길이와 관계없이 항상 동일한 길이(크기)를 갖는 벡터, 고정 길이 벡터이다.
■ 입력 문장은 임의 길이의 문장이므로 즉, 인코딩은 임의 길이의 문장을 고정 길이 벡터로 변환하는 작업으로 볼 수 있다.
- 예를 들어 '나는 학생이다'라는 문장과 '나는 고양이와 강아지이다'라는 서로 다른 길이를 가진 문장이 Encoder의 입력으로 들어가도 동일한 길이의 은닉 상태 벡터 h로 산출된다.
- 단, 두 문장을 미니배치로 처리할 경우 두 문장의 길이를 동일하게 맞춰야 한다.
■ 이 h (Encoder가 출력한 은닉 상태 벡터이자 고정 길이 벡터이며 컨텍스트 벡터)는 디코더의 LSTM 계층으로 들어간다.(디코더 LSTM 계층의 입력으로 사용된다.)
■ Decoder로 RNN을 사용했을 때, Decoder의 구조는 다음과 같다.

- <sos>와 <eos>는 구분 기호로 사용하는 토큰이다. Decoder 문장 생성의 시작, 종료를 알리는 구분자로 사용된다.
- Decoder는 먼저 <sos>라는 시작 토큰을 입력 받는다. 그리고 Encoder로부터 전달된 h와 함께 다음 단어를 예측하기 시작한다.
- Decoder의 LSTM 계층의 출력(은닉 상태)을 Affine 레이어에 통과시킨 다음, Softmax 레이어에 전달한다.
- Softmax 계층은 입력된 점수(score)를 기반으로 다음에 나올 단어를 확률적으로 예측한다.
- 예측된 단어를 다음 시점의 입력으로 사용한다. 이 과정을 종료 토큰 <eos>가 예측될 때까지 반복한다.
■ Decoder는 기본적으로 언어 모델이다. 이 예시에서의 Decoder는 RNNLM으로, 첫 번째 시점에서 <sos>를 입력으로 받고 다음으로 등장할 단어로 'I'를 예측했다. 그리고 단어 'I'를 다음 시점의 입력으로 사용해서 다음에 등장할 단어 'am'을 예측한다.
■ 이 과정을 종료를 의미하는 구분자 <eos>가 다음에 등장할 단어로 예측될 때까지 반복한다.
■ 예시에서 사용한 Encoder와 Decoder가 연결된 구조, 즉 seq2seq의 전체 구조는 다음과 같다.

■ 위의 구조를 보면, h가 Encoder와 Decoder를 연결하는 것을 볼 수 있다.
■ 순전파 과정에서는 Encoder에서 출력된 h가 Decoder에 전해지고, 역전파 과정에서는 기울기가 Decoder로부터 h를 통해 Encoder로 전해진다.
■ 위의 예시는 문장을 단어 단위로 분할해서 입력 데이터로 사용한 예시이다.
■ 문장을 반드시 '단어' 단위로 분할해야 하는 것은 아니다. '문자' 단위로 분할해서 입력으로 사용해도 된다.
2. 패딩(padding)을 이용한 가변 길이 시계열 데이터 처리
■ seq2seq 모델에서는 입력·출력 시퀀스 길이가 제각각인 여러 문장을 미니배치로 묶어 한 번에 학습시키고자 할 때, 패딩을 사용하여 길이를 맞추는 방식이 일반적이다.
- 미니배치에 속한 샘플들은 데이터 형상이 모두 동일해야 하기 때문에 길이를 맞춰 형상을 맞춰야 한다.
■ 패딩이란 원래의 데이터에 의미 없는 데이터를 채우는 기법이다.
■ 길이를 맞추는 방법은 패딩을 통해 최대 시퀀스 길이로 모든 샘플 데이터의 길이를 통일하는 것이다.
■ 단, 주의할 점이 있다. 패딩은 가변 길이 시계열 데이터를 처리하기 위해 적용하는 것. 즉, 패딩을 넣은 구간은 실제로는 아무 의미가 없는 구간이다.
■ 그러므로 정확성이 중요하다면, Decoder에 입력된 데이터에 패딩이 적용된 구간은 손실(loss) 계산 과정에서 제외되어야 한다.
■ Encoder도 마찬가지이다. 원래 존재하지 않았던 패딩까지 seq2seq 모델이 학습하지 않도록 처리해야 한다.
■ 입력된 데이터에 패딩이 적용되었다면, '이 시점은 패딩 위치'라고 명시해서 마치 처음부터 패딩이 존재하지 않았던 것처럼 처리할 수 있다.
- 예컨대 <pad> 토큰을 사용해서, <pad> 토큰이 입력으로 들어오는 시점을 무시할 수 있다.
- 혹은 숫자 0을 넣는 방법도 있는데, 이를 제로 패딩(zero padding)이라고 한다. 0이 아닌 다른 숫자로도 패딩에 사용할 수 있다.
-- <pad>는 시퀀스 길이를 맞추기 위한 패딩 토큰이다. <pad> 토큰을 넣어서 시퀀스들의 길이를 동일하게 맞춘다.
■ 패딩 처리 연습 https://hyeon-jae.tistory.com/152
■ seq2seq 구현 https://hyeon-jae.tistory.com/153
3. seq2seq 개선
3.1 입력 데이터 반전(reverse)
■ 이 방법은 다음과 같이 입력 데이터를 반전시킴으로써, 학습 진행 속도를 빠르게 하여 결과적으로 최종 정확도를 개선한다.
- 입력 데이터 반전은 예를 들어, '그는 돈을 센다'가 입력되면 이를 반전시켜 '센다 돈을 그는'을 입력 데이터로 사용하는 것이다.
■ 단순히 입력 시퀀스(데이터)를 거꾸로 뒤집었는데, 일반 순서로 배치했을 때보다 학습 진행 속도가 빨라지는 이유는 입력 초반부의 정보가 디코더 초반부에 도달하기까지 거쳐야 하는 경로 길이가 짧아지기 때문이다.
■ 예를 들어, 입력으로 '그는 돈을 센다'라는 문장을 Encoder에 입력했을 때, Decoder의 출력으로 'He counts money'가 되는 seq2seq 모델을 생각해 보자. (RNN 계층을 이용해 만든 Encoder와 Decoder)
■ 일반 순서로 배치했을 때. 입력 '그'부터 출력 'He'까지 도달하려면 '-는', '돈', '-을', '센다'를 담당하는 각 시점의 RNN 계층을 거쳐야 한다.
■ 이 예에서는 문장이 단순하여 RNN 계층을 몇 단계 거치지 않지만 문장이 길어질 경우, 즉 문장에 많은 단어가 사용되는 경우 매우 많은 스텝을 거치게 되며, 이는 역전파 과정에서 더 많은 기울기 전파를 의미한다.
■ 만약, 입력을 뒤집는다면? 즉, '센다'가 첫 시점에 등장하고 '그'가 마지막 시점에 등장한다면, Encoder의 마지막 입력 '그'와 Decoder가 생성하는 첫 단어 'He'는 바로 옆에 위치하게 된다.
■ 즉, 입력 데이터를 반전시키면 입력의 첫 단어가 RNN 순방향상(오른쪽 방향) 마지막에 등장하므로, Decoder가 첫 단어를 생성할 때까지 필요한 RNN 스텝 수가 줄어들어 기울기 전파가 더 쉽게 일어나고('그'와 'He'간의 기울기 전파 경로가 더 짧아지고), 학습이 원활해진다.
■ 따라서 입력 데이터 반전은 긴 문장에서도 학습 안정성을 높여줄 수 있는 핵심적인 트릭이다.
■ 예를 들어 다음과 같이 word_id의 0이 '그는', 1이 '돈을', 2가 '센다'를 의미한다고 하자. 파이썬에서 입력 데이터 반전은 다음과 같이 간단하게 할 수 있다.
text = '그는 돈을 센다'
word_id
```#결과#```
array([0, 1, 2])
````````````
word_id = word_id[::-1]
word_id
```#결과#```
array([2, 1, 0])
````````````
## 7개 단어로 구성된 문장이 1000개인 경우
word_id2.shape # shape은 행렬
```#결과#```
(1000, 7)
````````````
word_id2[999] # 999번째 문장
```#결과#```
array([ 8, 2, 7, 9, 10, 5, 5])
````````````
word_id2 = word_id2[:, ::-1]
word_id2[999]
```#결과#```
array([ 5, 5, 10, 9, 7, 2, 8])
````````````
- "::-1"는 전체 구간에 대해 -1 스텝으로(거꾸로) 이동하라는 뜻이므로, 원본 배열의 열 순서를 반대로 뒤집어 새로운 배열을 만든다.
3.2 엿보기(peeky)
■ 위의 그림에서 Encoder-Decoder 전체 그림을 보면, 일반적인 Encoder-Decoder 모델애서 Encoder는 Decoder에 필요한 정보(인코딩된 정보) h를 산출해서 Decoder의 첫 번째 시점의 RNN(또는 LSTM) 계층에 전달한다.
■ 반면, 이 방법은 Encoder에서 산출된 h를 Decoder의 모든 시점의 RNN 계층에 전달하는 방법이다. 구조는 다음 그림과 같다.


■ h는 Decoder가 출력을 생성할 때 참조할 수 있는 유일한 정보이다.
■ Peeky Decoder는 이 정보를 매 디코딩 시점마다 Encoder가 만든 컨텍스트 벡터(은닉 상태 벡터)를 계속 추가로 참조하게 만들어 입력 문장의 전체 맥락을 유지하고, 필요한 맥락을 즉시 활용할 수 있으므로 Decoder가 보다 유연하게 정보를 활용할 수 있게 만든다.
■ Peeky Decoder 구현 https://hyeon-jae.tistory.com/154
'딥러닝' 카테고리의 다른 글
트랜스포머 (Transformer) (1) (0) | 2025.01.09 |
---|---|
어텐션(Attention) (1) (0) | 2025.01.08 |
게이트(gate)가 추가된 RNN - LSTM, GRU (0) | 2025.01.06 |
순환 신경망(RNN) (2) (0) | 2025.01.06 |
순환 신경망(RNN) (1) (0) | 2025.01.06 |