HOW AI WORKS

GRU는 어떻게 동작하는가
— 게이트 하나를 덜어 낸 값

GRU(Gated Recurrent Unit)는 LSTM을 단순화한 순환 셀입니다. 게이트는 셋에서 둘로, 상태는 둘에서 하나로 줄었고 파라미터는 정확히 4분의 3입니다. 그런데 성능은 대부분의 과제에서 LSTM과 비슷합니다. 무엇을 빼도 괜찮았고, 무엇을 합쳤고, 그 대가로 무엇을 잃었는지를 한 스텝씩 계산하고, 마지막에는 브라우저에서 RNN·GRU·LSTM을 나란히 학습시켜 봅니다.

how-ai-works 시리즈 · 원전: Cho et al., Learning Phrase Representations using RNN Encoder–Decoder for Statistical Machine Translation (EMNLP 2014) · Chung et al., Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling (2014)

앞서 읽으면 좋은 자료: How RNN Works · How LSTM Works. 이 글은 LSTM의 셀 상태와 세 게이트를 안다고 가정하고, 거기서 출발해 GRU로 내려갑니다.
차례
  1. LSTM에서 GRU로 — 세 번의 수술
  2. 한 스텝 따라가기 — 섞기는 선분 위의 한 점이다
  3. 업데이트 게이트 — 기울기가 지나가는 길
  4. 리셋 게이트 — 과거를 끊으려면 스위치가 둘 필요하다
  5. 볼록 결합의 대가 — GRU는 세지 못한다
  6. 직접 학습시켜 보기 — 80칸 전의 한 비트
  7. 파라미터와 행렬 곱 — DS2가 곱셈 순서를 바꾼 이유
  8. 실무 메모 — 프레임워크마다 다른 GRU
  9. 그래서 LSTM이냐 GRU냐
  10. 정리
  11. 이해도 퀴즈

§1 LSTM에서 GRU로 — 세 번의 수술

먼저 식 네 줄입니다. 이 글 전체에서 이 표기를 씁니다.

zt = σ(Wzxt + Uzht−1 + bz)   업데이트 게이트
rt = σ(Wrxt + Urht−1 + br)   리셋 게이트
h̃t = tanh(Whxt + Uh(rt ⊙ ht−1) + bh)   후보
ht = (1 − zt) ⊙ ht−1 + zt ⊙ h̃t   섞기 Chung 외(2014)의 표기 — z가 "새 것을 받는 몫". 원 논문과 PyTorch는 z를 반대로 씁니다(§8)

GRU는 LSTM을 깎아서 만든 것처럼 보이지만, Cho 외는 LSTM에서 "영감을 받아" 새로 설계했다고 씁니다. 그래도 LSTM에서 출발해 세 번 수술하면 GRU에 도착한다고 보면 각 부품이 왜 있는지가 가장 잘 보입니다. 아래에서 한 단계씩 넘겨 보세요. 파라미터 수는 입력·은닉 차원을 바꾸면 다시 계산됩니다.

데모 1 · 수술 단계 — LSTM에서 GRU까지
단계마다 무엇이 사라지고 무엇이 합쳐지는지, 가중치 묶음이 몇 벌이 되는지 봅니다.

파라미터는 게이트마다 W(dh×dx) + U(dh×dh) + b(dh) 한 벌로 셉니다. PyTorch는 편향을 두 개(bih, bhh) 두므로 조금 더 많습니다(§7).

세 수술을 말로 정리하면 이렇습니다.

  1. 망각과 입력을 한 손잡이로. LSTM은 "얼마나 지울지(f)"와 "얼마나 쓸지(i)"를 따로 정합니다. 이것을 f = 1 − i로 묶으면 지운 만큼만 쓸 수 있습니다. Greff 외(2017)가 CIFG(coupled input-forget gate)라 부른 변형이고, 성능을 크게 해치지 않았습니다. 손잡이 하나가 곧 GRU의 업데이트 게이트 z입니다.
  2. 셀 상태와 은닉 상태를 하나로. LSTM은 긴 기억 c를 안쪽에 두고 출력 게이트로 일부만 꺼내 h로 보여 줍니다. GRU는 둘을 합쳐 기억하는 것 = 내보내는 것으로 만들고, 출력 게이트를 없앱니다. 이 단계에서 가중치는 두 벌까지 줄어듭니다.
  3. 리셋 게이트를 더하기. 두 벌짜리 셀은 후보 h̃를 만들 때 항상 과거 ht−1를 통째로 봅니다. 문장이 바뀌는 지점처럼 과거를 무시하고 새로 시작해야 할 때가 있어서, 후보를 만들 때만 과거를 가리는 게이트 r을 하나 더 둡니다(§4). 그래서 GRU는 두 벌이 아니라 세 벌입니다.
②에서 멈춘 두 벌짜리 셀도 실제로 있습니다 — Zhou 외(2016)의 MGU(Minimal Gated Unit)는 게이트 하나가 업데이트와 리셋을 겸합니다. "게이트로 조절되는 덧셈형 상태"라는 뼈대만 남기면 얼마나 줄일 수 있는지 보여 주는 쪽입니다.

§2 한 스텝 따라가기 — 섞기는 선분 위의 한 점이다

GRU 한 스텝의 마지막 줄을 다시 봅시다.

ht = (1 − zt) ⊙ ht−1 + zt ⊙ h̃t 두 계수의 합이 항상 1 — 볼록 결합(convex combination)

계수의 합이 1이라는 것은 새 상태가 이전 상태와 후보를 잇는 선분 위 어딘가에 놓인다는 뜻입니다. z는 그 선분 위의 위치입니다. 0이면 이전 상태 그대로, 1이면 후보로 완전히 갈아타고, 0.3이면 30%만큼 후보 쪽으로 이동합니다. 한 차원짜리 GRU로 직접 움직여 보세요.

데모 2 · 한 스텝 계산기 (은닉 1차원)
게이트 값은 원래 σ(…)로 계산되지만, 여기서는 효과를 보기 위해 직접 정합니다.

두 게이트가 하는 일이 확실히 다르다는 게 보입니다.

LSTM으로 옮겨 적으면 f = 1 − z, i = z, o = 1이고 c = h인 셀입니다. LSTM의 갱신 ct = f ⊙ ct−1 + i ⊙ C̃t에서 f + i는 0부터 2까지 아무 값이나 될 수 있는데, GRU에서는 항상 정확히 1입니다.

§3 업데이트 게이트 — 기울기가 지나가는 길

바닐라 RNN의 문제는 기울기가 한 스텝 거슬러 갈 때마다 U와 tanh의 미분을 곱해야 한다는 것이었습니다. 그 곱이 1보다 작으면 k스텝 뒤에는 λk로 사라집니다. GRU에서 한 스텝의 야코비안을 쓰면 이렇습니다.

∂ht / ∂ht−1 = diag(1 − zt) + (후보·게이트를 거치는 항들) 첫 항은 행렬 곱도, tanh도 거치지 않는다 — z가 0에 가까우면 거의 항등 행렬

첫 항이 핵심입니다. z ≈ 0인 구간에서는 상태가 거의 그대로 복사되고, 기울기도 거의 그대로 거꾸로 흐릅니다. LSTM에서 f ≈ 1일 때 셀 상태 레일이 하는 일과 같습니다. 여기에 결정적인 차이가 하나 더 있는데, z는 상수가 아니라 입력을 보고 스텝마다 정해진다는 것입니다.

데모 3 · 이동 평균 vs 샘플 앤 홀드
입력은 대부분 잡음이고, 가끔 사건(큰 점)이 옵니다. 목표는 "마지막 사건의 값"을 들고 있는 것(점선)입니다. 단순하게 보려고 후보는 입력 그대로 h̃t = xt로 둡니다.
잡음 입력 사건 상태 h 목표 (마지막 사건의 값)

z를 고정하면 GRU는 지수 이동 평균(EMA)입니다. 반감기는 ln 0.5 / ln(1 − z)스텝이라 z를 작게 하면 오래 기억하지만 사건에 느리게 반응하고, 크게 하면 빨리 반응하지만 잡음에 휩쓸립니다. 어떤 상수를 골라도 둘 다 잡을 수는 없습니다.

z가 입력을 보게 하면 이 맞교환이 사라집니다. 사건이 오면 z ≈ 1로 갈아타고, 잡음이 오면 z ≈ 0으로 버팁니다. 아래 차트가 그 결과입니다 — 사건과 사건 사이에서 ∏(1 − z)가 거의 1로 유지되어, 마지막 스텝의 기울기가 수십 칸 전 사건까지 줄지 않고 도착합니다. 바닐라 RNN(λ = 0.9)의 회색 선과 비교해 보세요.

아래 차트는 야코비안의 첫 항(복사 경로)만 곱한 것입니다. 실제 기울기에는 후보와 게이트를 거치는 항도 더해지므로 정확한 값은 아닙니다. 다만 "게이트가 닫혀 있는 동안 기울기가 곱셈 없이 지나가는 길이 있다"는 구조는 그대로입니다.

실제 GRU에는 은닉 유닛마다 자기 z가 있습니다. 어떤 유닛은 z를 늘 작게 유지해 느린 시간 척도를 맡고, 어떤 유닛은 z를 크게 두어 방금 들어온 입력을 따라갑니다. 시간 척도를 유닛마다, 스텝마다 학습하는 셈입니다.

§4 리셋 게이트 — 과거를 끊으려면 스위치가 둘 필요하다

업데이트 게이트만으로 "완전히 잊기"가 될까요? z = 1이면 ht = h̃t로 이전 상태를 버리니 될 것 같지만, 후보 h̃t = tanh(Whxt + Uhht−1) 안에 과거가 또 들어 있습니다. 문을 닫았는데 새로 들이는 손님이 과거를 업고 들어오는 격입니다.

확인하는 방법이 있습니다. 서로 다른 과거를 가진 두 시퀀스 A와 B를 준비하고, 경계 이후로는 완전히 같은 입력을 넣습니다. 과거가 완전히 잊혔다면 경계 직후부터 두 상태가 정확히 같아야 합니다.

데모 4 · 두 과거가 합류하는가
A는 +0.8을 8번, B는 −0.8을 8번 본 뒤 경계(t = 9)를 지나 같은 입력 12개를 봅니다. 경계 한 칸에서만 게이트 값을 바꿉니다. 그 밖의 스텝은 z = 0.3, r = 1로 고정입니다.

z = 1만으로는 두 선이 경계에서 가까워질 뿐 붙지 않습니다. r = 0만으로도 붙지 않습니다(이전 상태가 1 − z만큼 남으니까요). 둘을 함께 끊어야 차이가 정확히 0이 되고, 그 뒤로는 영원히 같은 궤적을 갑니다. LSTM에서는 망각 게이트 f = 0 하나로 되는 일이, GRU에서는 두 게이트의 합작입니다.

그래서 리셋 게이트는 "짧은 의존성을 담당하는 유닛"에서 자주 활발해집니다. Cho 외(2014)는 학습된 GRU에서 리셋 게이트가 자주 켜지고 꺼지는 유닛은 짧은 문맥을, 업데이트 게이트가 대부분 닫혀 있는 유닛은 긴 문맥을 붙잡는 경향이 있다고 설명합니다.

§5 볼록 결합의 대가 — GRU는 세지 못한다

§2에서 본 성질로 돌아갑시다. GRU의 상태는 이전 상태와 후보 사이의 점이라 항상 (−1, 1) 안에 있습니다. LSTM은 다릅니다. f = 1, i = 1이면 ct = ct−1 + C̃t — 지우지 않고 계속 더할 수 있어서 셀 상태가 경계 없이 자랍니다.

이 차이가 드러나는 대표 과제가 anbn입니다. a를 n개 본 뒤 b를 정확히 n개 봤는지 판단하려면 개수를 세어야 합니다. LSTM은 a에 +1, b에 −1을 더하는 카운터가 되고, 끝에서 c = 0인지만 보면 됩니다. GRU에게도 최선을 다할 기회를 줍니다 — a면 후보 +1 쪽으로, b면 −1 쪽으로 z만큼 이동하는 셀입니다.

데모 5 · 손으로 심은 카운터 — LSTM vs GRU
LSTM: i = f = o = 1, C̃ = +1(a) / −1(b). GRU: h̃ = +1(a) / −1(b), z는 상수. 위 차트는 anbm을 읽는 동안의 상태, 아래 차트는 m = n과 m = n + 1을 가르는 끝 상태의 차이를 n에 따라 그린 것입니다.

LSTM은 n이 얼마든 m = n이면 끝 상태가 정확히 c = 0이고, 한 개라도 어긋나면 c = ±1입니다. 차이가 n과 무관하게 늘 1입니다. GRU는 m = n일 때의 끝 상태가 n마다 다르고(고정된 "정답 위치"가 없음), m = n과 m = n + 1의 차이가 z(2 − qn)qn(q = 1 − z)로 n에 대해 지수적으로 작아집니다. 그 차이가 float32로 구분할 수 있는 한계보다 작아지면, 가중치를 어떻게 정해도 두 경우를 가를 수 없습니다. z를 작게 하면 한계 n이 늘어나지만 대신 차이 자체가 z배로 작아집니다 — 어떤 상수 z도 한계를 없애지는 못합니다.

Weiss, Goldberg & Yahav, 「On the Practical Computational Power of Finite Precision RNNs for Language Recognition」(ACL 2018)가 이것을 정리하고 실험으로 확인했습니다. anbn을 학습시키면 LSTM은 학습 때 본 것보다 훨씬 긴 n으로 일반화했고, GRU는 그러지 못했습니다. 표현력만 보면 LSTM이 GRU보다 넓다는 뜻입니다. 다만 실제 언어 과제에서 이 차이가 드러나는 경우는 드뭅니다(§9).

§6 직접 학습시켜 보기 — 80칸 전의 한 비트

지금까지는 게이트 값을 손으로 정했습니다. 이번에는 브라우저에서 역전파로 실제로 학습시킵니다. 과제는 단순합니다 — 시퀀스 첫 칸에 ±1이 오고, 나머지 T − 1칸은 [−1, 1] 균등 잡음입니다. 마지막 칸에서 첫 칸의 부호를 맞히면 됩니다. 은닉 8차원, 배치 32, Adam(학습률 0.02), 기울기 노름 클리핑 1로 세 셀을 같은 조건에서 돌립니다.

데모 6 · 브라우저 학습 — RNN vs GRU vs LSTM
선은 정확도의 이동 평균입니다. 0.5는 동전 던지기, 1.0이면 완전히 기억한 것입니다. 게이트 편향 b는 초기화 값입니다 — GRU는 업데이트 게이트 편향을 −b(시작할 때 z ≈ σ(−b)), LSTM은 망각 게이트 편향을 +b로 둡니다.
스텝 0 / 600

기본 설정(T = 80, b = 3, 시드 1)에서 돌리면 이렇게 됩니다.

이제 게이트 편향을 0으로 내리고 T = 20으로 돌려 보세요. GRU와 LSTM이 T = 20에서도 실패하고, 오히려 바닐라 RNN이 풉니다. 편향 0이면 시작할 때 z ≈ 0.5라 한 스텝마다 상태의 절반이 잡음으로 덮이고, 복사 경로 ∏(1 − z)가 0.5k로 바닐라 RNN보다 빨리 사라지기 때문입니다. 게이트는 "닫힌 상태"에서 출발해야 기울기 길이 열린 채로 학습이 시작됩니다. LSTM의 망각 편향을 1로 두라는 오래된 조언(Gers 외 2000, Jozefowicz 외 2015)과 같은 이야기이고, GRU에서는 업데이트 편향을 음수로 두는 것이 짝입니다.

PyTorch의 nn.GRU와 nn.LSTM은 모든 편향을 같은 균등 분포로 초기화합니다 — 이 트릭을 자동으로 해 주지 않습니다. (Keras LSTM은 unit_forget_bias=True가 기본이지만, GRU에는 해당 옵션이 없습니다.) 긴 의존성이 중요한 과제라면 직접 편향을 채워 넣을 가치가 있습니다.

학습은 부동소수점 연산의 마지막 자리에도 민감해서, 브라우저에 따라 "풀린 스텝"이 조금 다르게 나올 수 있습니다. 이것은 장난감 과제이고 시드 몇 개의 결과입니다. 이 데모가 보여 주려는 것은 "GRU가 LSTM보다 낫다"가 아니라, 게이트가 기울기의 복사 경로를 만든다는 것과 그 경로는 초기화가 열어 줘야 쓸 수 있다는 것입니다.

§7 파라미터와 행렬 곱 — DS2가 곱셈 순서를 바꾼 이유

GRU는 가중치가 세 벌이니 파라미터는 LSTM의 정확히 3/4입니다. 계산기로 확인해 봅시다.

데모 7 · 파라미터 계산기

FLOP도 같은 비율로 줄어듭니다. 그런데 GPU에서 순환층의 속도를 정하는 것은 FLOP보다 ht−1에 걸리는 행렬 곱을 몇 번, 어떤 순서로 부르는가입니다. 순환층은 스텝 t가 끝나야 t + 1을 시작할 수 있어서, 한 스텝 안의 행렬 곱들이 순서대로 이어지면 그대로 지연이 됩니다.

원래 GRU에는 걸림돌이 하나 있습니다. Uh(rt ⊙ ht−1)는 rt가 나와야 시작할 수 있습니다. 그래서 Uzh와 Urh를 한 번에 묶더라도 Uh 곱은 따로, 뒤에 해야 합니다. Deep Speech 2(Amodei 외 2015, 3.4절)는 곱셈 순서를 바꿨습니다.

원래 GRU:  h̃t = tanh(Whxt + Uh(rt ⊙ ht−1) + bh)
리셋 나중: h̃t = tanh(Whxt + rt ⊙ (Uhht−1 + bhn) + bh) Uh를 먼저 곱하면 Uz·Ur·Uh를 쌓은 (3dh×dh) 행렬 하나로 끝난다
데모 7-B · 한 스텝에 ht−1을 몇 번 곱하나 — 그리고 두 변형은 같은 모델인가

FLOP은 한 개도 다르지 않습니다. 달라지는 것은 순서대로 이어지는 행렬 곱이 2번에서 1번으로 준다는 것, 작은 행렬 곱 여러 번이 큰 행렬 곱 한 번이 된다는 것입니다(따로 부르면 3번 → 1번). GPU는 큰 행렬 곱 하나를 훨씬 효율적으로 처리합니다. 그래서 cuDNN이 이 변형을 구현했고, PyTorch nn.GRU와 Keras(reset_after=True가 TF 2의 기본값)도 이쪽입니다.

대신 7-B의 아래 수치가 보여 주듯 같은 가중치를 넣어도 출력이 다릅니다. 두 변형은 다른 모델이라, 원 논문식으로 학습한 가중치를 PyTorch에 그대로 옮기면 조용히 틀린 값이 나옵니다.

§8 실무 메모 — 프레임워크마다 다른 GRU

수식으로는 한 줄 차이지만, 가중치를 옮기거나 게이트를 시각화할 때 실제로 부딪히는 차이들입니다.

이 글 (Chung 외 표기)Cho 외 원 논문PyTorch nn.GRUKeras GRU (TF 2)
섞기 식(1−z)h + z·h̃z·h + (1−z)·h̃z·h + (1−z)·nz·h + (1−z)·h̃
z = 1의 뜻새 것으로 교체이전 상태 유지
리셋 위치U 곱 앞U 곱 앞U 곱 뒤U 곱 뒤 (기본값)
편향게이트당 1개게이트당 1개2개 (bih, bhh)2개 (reset_after일 때)
가중치 쌓는 순서——r, z, nz, r, h
LSTM의 순서 (참고)——i, f, g, oi, f, c, o

§9 그래서 LSTM이냐 GRU냐

여러 대규모 비교가 비슷한 결론을 냅니다 — 대체로 비슷하고, 과제와 규모에 따라 갈린다.

연구무엇을 비교했나결론
Chung 외 (2014)다성 음악·음성 신호 모델링에서 tanh RNN / LSTM / GRU게이트 있는 두 셀이 tanh RNN보다 확실히 낫고, LSTM과 GRU 사이는 데이터셋마다 승자가 갈려 우열을 가리지 못함
Jozefowicz 외 (ICML 2015)1만 개 넘는 구조를 진화 탐색GRU가 LSTM보다 나은 과제가 있었지만, LSTM의 망각 편향을 1로 초기화하면 격차가 대부분 사라짐
Greff 외 (2017)LSTM 변형 8종 × 음성·필기·음악입력·망각 결합(CIFG, GRU의 핵심)은 성능을 크게 해치지 않음. 망각 게이트와 출력 활성화는 빼면 안 되는 부품
Weiss 외 (ACL 2018)anbn 등 세기가 필요한 형식 언어LSTM은 세기를 배워 일반화, GRU는 실패 — 표현력 차이(§5)
Deep Speech 2 (2015)음성 인식, 3,800만 파라미터(표 3)와 1억 파라미터(표 11)7층 기준 WER은 GRU 7.79 vs RNN 9.78로 GRU 승. 그런데 1억 규모에서는 RNN 7.73 vs GRU 7.78로 뒤집힘 — 이후 실험 대부분은 바닐라 RNN

실무에서의 선택 기준은 대개 이렇게 정리됩니다.

§10 정리

바닐라 RNNGRULSTM
가중치 묶음134
상태hhc, h
갱신tanh(Wx + Uh)(1−z)h + z·h̃f·c + i·C̃
계수의 합—항상 1 (볼록 결합)0 ~ 2 (자유)
상태의 범위(−1, 1)(−1, 1)c는 제한 없음
기울기 복사 경로없음∏(1 − z)∏ f
완전히 잊기—z = 1 이고 r = 0f = 0
세기불가불가 (지수적으로 흐려짐)가능

한 문장으로 줄이면 이렇습니다.

GRU는 LSTM의 "지울 양"과 "쓸 양"을 한 손잡이로 묶고 기억과 출력을 하나로 합친 셀입니다. 그 덕에 가중치가 한 벌 줄었고 기울기의 복사 경로는 그대로 남았습니다. 대신 상태가 늘 두 점 사이에 갇혀서, 끝없이 더해 가는 일은 할 수 없습니다.

헷갈리기 쉬운 것들:

§11 이해도 퀴즈