오늘은 저번 주에 읽어본 LLaMA 논문에서 등장한 변경 사항들에 대해 좀 더 알아보는 시간을 가질 것입니다. 그렇게까지 자세히는 알아보지는 않겠지만 대략적으로라도 어떤 느낌인지를 알아놓으면 LLaMA라는 모델을 이해하는 데 많은 도움이 될 것 같습니다.
Pre-normalization은 GPT3에서 영감을 받은 변경점입니다. 원래는 결과 출력 단계에서 normalization을 수행하였는데, LLaMA는 각 sub-layer에서 normalization을 수행하는 pre-normalization 방식을 사용하였습니다.
Pre-normaliztaion은 모델이 노이즈의 영향을 덜 받도록(re-centrig)하고 입력값과 가중치의 scale에 상관없이 output의 representation을 일정하게 유지(re-scaling)하게 해준다고 합니다. 이러한 특성들 덕분에 Pre-normalization을 사용하면 좀 더 안정적인 학습을 진행할 수 있다고 합니다.
LLaMA에서는 pre-normalization에 RMSNorm 기법을 사용하였습니다. RMSNorm은 위에서 설명한 두 특성 중 re-scaling의 효과를 좀 더 키워준다고 합니다.
RMSNorm에 사용되는 RMS(RootMeanSquare)는 수식으로 나타내면 입니다. 이 식을 보면 알 수 있듯이 RootMeanSquare라는 이름대로 제곱 평균의 제곱근을 구한 것입니다.
RMSNorm은 수식으로 나타내면 이고 은 계산 결과에 영향을 거의 미치지 않으면서 밑변이 0이 되는 것을 방지하기 위한 아주 작은 수이며 는 학습 가능한 파라미터입니다.
제가 참고한 블로그에 따르면 RMSNorm은 기존의 LayerNormalization에 비해 계산 비용이 낮으며(평균, 분산을 고려하는 연산을 제곱 평균만을 고려하는 연산으로 치환) 배치 크기에도 덜 민감하다고 합니다.
SwiGLU Activation Function은 PaLM에서 영감을 얻은 변경점입니다. SwiGLU는 최근 ReLU와 GELU를 대체하고 있는 activation function으로 여러 가지 장점이 있다고 합니다.
장점을 간략하게 설명해 보면
1. ReLU보다 빠르고 최적화된 수렴
2. non-monotonic한 특성으로 input과 output 사이의 복잡한 비선형적 관계를 포착할 수 있음
3. Gating 방식을 사용해 overfitting을 줄여주고 generalization 효과를 가져옴
4. 다른 activation function에 비해 좋은 성능을 보여줌
위의 내용을 들 수 있습니다.
SwiGLU는 Swish와 GLU라는 activation function을 합친 것입니다. Swish의 Swi와 GLU라는 단어를 합쳐서 SwiGLU가 된 것이죠. 이제 SwiGLU에 대해 알아보기 전에 Swish와 GLU에 대해 각각 설명을 간단히 해보겠습니다.
Swish는 2017년 구글의 연구진들이 제안한 activation function이며 수식으로는 로 나타낼 수 있습니다. 여기서 는 학습 가능한 파라미터입니다. Swish는 다양한 분야, 특히 딥러닝에서 좋은 성능을 냈으며 여기에는 ReLU보다 smooth한 성질 때문에 좀 더 빠르게 최적화된 수렴 결과를 얻을 수 있다고 합니다.
GLU는 2016년 마이크로소프트의 연구진들이 제안한 것으로 수식으로는 로 나타낼 수 있습니다. 여기서 와 는 학습 가능한 파라미터이며 ⊗ 기호는 아다마르 곱 기호로 같은위치의 원소 별로 곱해준다는 뜻입니다. 학습 과정에서는 의 값에 따라 결괏값이 gate됩니다. 의 값에 따라 결괏값이 얼마나 반영될지 결정되는 것이죠.
위에서 언급했듯이 SwiGLU는 Swish와 GLU를 합친 activation function입니다. 그렇다면 어떤 식으로 합쳤다는 뜻일까요? SwiGLU를 수식으로 나타내면 입니다. GLU 수식에서 sigmoid 대신 Swish 함수를 사용한 것이죠.
이렇게 두 함수를 합치면서 각각의 장점들은 챙기면서 단점은 서로 보완할 수 있게 되었습니다. SwiGLU는 다양한 task에서 Swish와 GLU의 성능을 뛰어넘었습니다.
RoPE(Rotary Position Embedding)는 GPTNeo에서 영감을 받은 변경점입니다. RoPE는 이름대로 Position Embedding의 위치에 변화를 줘가며 사용하는 것입니다.
일단 이런 Embedding 방식이 등장한 배경을 살펴보면 기존의 absolute positional embedding 방식은 내적 연산을 수행하면 position informational을 제대로 유지하지 못한다는 문제점이 있었습니다. 하지만 relative positional embedding 방식에서는 내적 연산을 수행해도 positional information을 온전히 유지할 수 있습니다. 그렇지만 기존의 relative 방식은 효율성 부분에서 문제가 존재하였습니다. 예를 들자면 T5 모델에서는 relative positional embedding을 위해서 N⨉N 크기의 추가 어텐션 행렬이 필요하다는 문제가 있었습니다.
RoPE는 그런 문제를 해결하기 위해 등장한 방식으로 매우 효율적이면서도 relative positional embedding 방식의 장점인 유연성을 살릴 수 있는 embedding 방식입니다.
RoPE의 컨셉은 매우 간단합니다. 두 행렬이 있을 때, 두 행렬에 동일한 연산을 수행하면 각 요소들의 절대적인 위치는 변하더라도 상대적인 위치는 변하지 않는다는 것입니다.
이를 positional embedding에 대응해 생각해 보면 Query 행렬과 Key 행렬을 동일한 이동 거리만큼 이동시키면 Query와 Key의 상대적인 위치는 변하지 않는다는 것을 알 수 있습니다. 이를 통해서 Query 행렬과 Key 행렬의 위치를 이동시키며 사용할 수 있는 것입니다. 회전이란 행렬의 각 요소들의 절대적인 위치를 이동시키더라도 다른 행렬의 요소도 함께 동일한 이동 거리만큼 이동시키면 상대적인 위치는 변하지 않는다는 뜻입니다.
이 방법을 이용하면 간단하게 구현이 가능하고 효율성 부분에서도 문제가 없이 relative positional embedding을 이용할 수 있습니다.
저번 주에 읽은 LLaMA 논문에서 짧게 언급만 되고 넘어간 변경점들에 대해 좀 더 자세히 알아보았다. 분명히 논문에서는 몇 줄 안 되는 내용이었는데 좀 더 알아보려고 하니 다들 꽤나 방대한 양의 내용들이었다. 모든 내용을 깊게 다뤄보기에는 내 실력이 아직 너무나 부족하기 때문에 겉핥기 식으로라도 내용을 정리해 보았는데 그럼에도 꽤나 많은 것을 얻어갈 수 있었던 것 같다.
이 글을 쓰면서 최대한 잘못된 정보를 싣지 않기 위해 여러 블로그와 논문을 뒤져보며 나름 교차 검증을 했는데 과연 잘못된 정보가 없을지 모르겠다.
이런 글을 쓸때마다 늘 하는 이야기지만 이 글을 읽으시는 분이 계신다면 너무 맹신하지는 말고 만약 잘못된 부분을 찾았으면 댓글로 남겨주셨으면 한다.
ps. 참고로 사진에 나온 동물은 라마가 아니라 알파카다.