[Review] DeepSeek
DeepSeek
DeepSeek는 DeepSeek에서 만든 언어 모델로, 적은 비용으로 ChatGPT와 비슷한 성능을 낸다고 하는 모델이다. 여러 버전이 있는데 V1 -> V2 -> V3 -> R1 순으로 발전되었다. 각 버전에 구조적인 변화나 학습 방법을 변경하여 성능을 끌어올리는데, 그 특징은 다음 그림과 같다.
V1과 V2에서 Multi-Head Latent Attention(MLA)와 Mixture of Experts(MoE) 구조를 채택하고, V3에서 Multi-Token Predict(MTP)와 Auxiliary-Loss free load balancing 기법을 적용한다. 마지막으로 위와 같은 구조적이고 메커니즘적인 변화로 학습한 V3 모델에 강화학습을 적용하여 추론 능력을 극대화함으로써 Chat-GPT의 o1과 맞먹는 성능을 가진 R1 모델을 만든다.
중국이 미국의 반도체 규제로 인해 적은 메모리와 bandwidth를 가진 GPU로 성능을 극대화하기 위해 여러 적용한 여러 기술을 크게 다음의 다섯 가지 기술로 나누었고 bottom-up 방식으로 하나씩 정리했다.
- Multi-Head Latent Attention(MLA)
- Mixture of Experts(MoE)
- Multi-Token Predict(MTP)
- Auxiliary-loss free load balancing
- Reinforcement learning
Mixture of Experts(MoE)
Q?
Inference 할 때 마다 사용하지 않을 파라미터를 끄고 사용할 파라미터를 키는건가? 여기에 발생하는 오버헤드는?
Introduction to MoE
모델의 파라미터를 키우면 모델의 capability가 향상된다는 것은 자명하다. 하지만 자원은 한정되어 있기 때문에 scaling에도 한계가 있다. Scaling의 이점은 취하면서 주어진 자원에 타협하기 위해 MoE가 제안되었다. MoE는 모델의 파라미터 중 전문가들(experts)을 양성해, 입력이 들어왔을 때 필요한 expert만 활성화하는 구조를 말한다. 이 구조를 따름으로써 모델의 전체 파라미터가 활성화되는 것이 아니기 떄문에 모든 파라미터를 사용했을 때 보다 적은 메모리를 요구해 메모리를 절약할 수 있으며 절약한 메모리로 모델의 전체 크기를 키워 capability를 높인다. 보다 적은 메모리로 experts의 조합을 통해 문제를 해결하는 것이 MoE의 특징이다.
FFN의 입력을 $u_t$라고 할 때 MoE의 출력 $h_t$수식으로 나타내면 다음과 같다.
\[\begin{aligned} h_t &=\Sigma_{i=1}^N(g_{i,t}FFN_i(u_t)) + u_t \\ g_{i,t} &=\left\{ \begin{array}{ll} s_{i,t}, & s_{i,t} \in Topk(\{s_{j,t}|1 \leq j<N\},K) \\ 0, & \text{otherwise}, \end{array} \right. \\ s_{i,t}&=Softmax_i(u_t^T e_i), \end{aligned}\]N은 FFN의 개수, t는 t번째 토큰을 의미한다. 입력 $u_t$에 i번째 FFN이 활성화될지 결정하는 벡터 $e_i$를 곱해 $i$를 파라미터로 하는 softmax를 거친다. 그러면 t번째 토큰이 i번째 FFN에 대해 활성화되는 정도를 $s_{i,t}$로 나타낼 수 있다. 이후, 상위 K개의 $s_{i,t}$만 취해 모두 더한다. 마지막으로 residual connection을 위해 원본 $u_t$를 더하면 토큰 하나에 대한 MoE Layer의 출력을 구할 수 있다.
Router
MoE의 첫번째 관건은 입력이 들어왔을 때 어떤 experts를 활성화할지 결정하는 것이다. 활성화할 experts를 선택하는 방법은 router이다. Router는 아래 그림과 같다.
먼저 왼쪽의 (a)가 이전 수식에서 설명한 가장 기본적인 MoE이다. Router의 K값이 2라는 것은, 활성화할 N개의 FFN 중 상위 두 개의 값을 선택하는 것이다. 다음으로 가운데의 (b)는 fine-grained expert segmentation으로, 각 FFN을 m개의 experts로 세분화하는 것이다. 그림을 보면 K는 4이고, FFN의 개수가 2N이 되었기 때문에 m은 2라고 볼 수 있다. FFN을 세분화하면 동일한 비용으로 훨씬 더 많은 경우의 수를 표현할 수 있다. 예를 들어 N이 16이고 k가 2라고 했을 때, (a)의 경우에 ${16}C_2$, 즉 120가지 경우의 수가 있다. 하지만 (b)의 경우 m이 4가 되어 하나의 FFN을 네 개로 쪼갠다면, 동일한 메모리로 ${64}C_8$, 즉 4,426,165,368개의 경우의 수를 표현할 수 있다.
마지막으로 오른쪽의 (c)는 DeepSeek에 적용된 MoE 구조로 (b)의 구조에 shared expert를 추가한 것이다. 다른 입력이 공통의 지식을 필요로 할 경우가 생기는데, 만약 대부분의 도메인에서 필요한 기본적인 지식이 있다면 학습을 진행함에 따라 여러 experts가 동일한 지식을 공유하게 될 수 있다. 이것은 지식의 중복이기 때문에 expert가 제기능을 한다고 보기 어렵다. 따라서 DeepSeek에서는 shared expert를 두어 이 문제를 해결했다. 여러 도메인 간에 공유되는 지식은 shared expert가 학습하도록 해서 지식의 중복을 제거한다.
Load Balancer
MoE의 두 번째 관건은 expert를 얼만큼, 어느 규칙에 따라 활성화할지 결정하는 것이다. 만약 K의 값을 키워 FFN의 개수인 N에 도달한다면, 모델의 모든 파라미터를 활성화하는 것이기 때문에 MoE를 적용하는 의미가 없어진다. 반대로 K의 값을 너무 줄인다면, 좋은 성능을 낼 수 없다. 또한 expert를 활성화 할 때, 특정 expert만 과도하게 자주 활성화된다면 다른 experts는 제 기능을 하지 못하게 되고, 반대로 특정 expert가 거의 사용되지 않는다면 그것은 없는 expert와 마찬가지가 된다.
게다가 거대 언어모델의 환경을 고려하면, 특정 GPU에 있는 네트워크만 과도하게 활성화되는 것도 문제가 된다. 특정 GPU의 네트워크만 활성화된다면, 부하가 그 GPU에만 걸리게 되어 병목현상이 발생한다.
따라서, experts 활성화를 균형있게 조절하는 것이 중요하다. 하지만 여기서 사용하는 load balancer는 DeepSeek가 발전함에 따라 사용하지 않기 때문에 일단 다루지 않는다.
Multi-Head Latent Attention(MLA)
트랜스포머는 입력 토큰의 개수(N)가 늘어남에 따라 지수적으로 메모리를 더 차지한다. 이것은 긴 길이의 출력을 내뱉어야 하는 LLM에게 치명적인 문제이다. 이를 해결하기 위해 GQA, MQA 등 다양한 방법이 제안되었고, DeepSeek는 MLA라는 독자적인 방법을 제안하였다. MLA를 설명하기에 앞서, 기존 Multi-Head Attention(MHA)이 가지는 병목현상과 이를 해결하기위한 기존 방법을 먼저 짚고 넘어간다.
MHA
MHA는 Transformer를 제안한 논문에서 제시한 토큰간의 attention score를 계산하기 위한 방법이다. 간단히 수식을 살펴보면 다음과 같다.
\[(score)=softmax(\frac{QK^T}{\sqrt{d}})V\]$Q$와 $K$는 쿼리와 키를 의미하고, $Q,K \in R^{n\times d}$이다. 따라서 $Q\cdot K^T\in R^{n\times n}$이기 때문에 $n^2$에 비례해 메모리를 차지한다. 또한 Transformer의 inference 단계에서 토큰 하나씩 생성할 때, 각 단계에서 이전 토큰을 예측할 때 사용한 모든 K,V 값들을 사용한다. 따라서 이것을 cache해 사용하는데, 이것은은 $n$에 비례해 늘어난다. LLM은 굉장히 긴 길이의 문장을 처리해야하기 때문에 Transformer의 이러한 특성이 치명적인 문제가 된다.
Efficient Attentions
위에서 언급한 MHA의 문제점을 해결하기 위해 다양한 방법이 제안되었다. 그 중 하나는 Grouped-Query Attention(GQA)이다. GQA는 쿼리 M개를 그룹지어서 하나의 키에 매핑하는 방식이다. 예를 들어 N개의 토큰이 있고, 그것을 M개씩 그룹짓는다면 $\frac{N}{M}$개의 키와 밸류만 필요하다. 따라서 $\frac{N}{M}$에 비례해 메모리가 사용된다. 하지만 M이 상수이기 때문에 결국 복잡도는 같다는 문제가 있다. 이를 해결하기 위해 모든 쿼리를 하나의 키와 밸류로 연관짓는 방법이 Multi-Query Attention(MQA)이다. 모든 쿼리를 하나의 키와 밸류로 매핑했기 때문에 복잡도가 더이상 n에 비례하지 않게 되었다. 하지만 메모리를 절약함에 따라 적은 정보를 표현하게 되어 성능이 하락했다. DeepSeek는 모델의 성능을 유지하며 메모리를 절약하기 위해, 키와 밸류를 latent 공간으로 압축해 꺼내쓰는 방법을 제안하였다.
MLA
이 논문에서 집중한 문제는 K,V cache가 n에 비례한다는 점이다. 따라서 K,V를 latent 공간으로 매핑한 다음 필요할 때 꺼내 씀으로써 캐시 메모리를 절약하는 방법을 선택했다. 우선 inference 단계에서 토큰 하나씩 생성하기 때문에 attention layer의 입력값을 토큰별로 구분해 $h_t$로 표현하고 다음 수식을 통해 압축된 KV인 $c_t^{KV}$를 생성한다. \(c_t^{KV} = W^{DKV}h_t\) $W^{DKV}$의 DKV는 Down-project KV의 약자로 보이고, $W^{DKV}\in R^{d_c\times d}$를 만족하며 KV는 $c_t^{KV}\in R^{d_c}$가 되어 latent 공간으로 압축된다. 압축된 KV를 사용하기 위해서는 다음 수식을 통한다.
\[k_t^C=W^{UK}c_t^{KV}\\ v_t^C=W^{UV}c_t^{KV}\]압축된 $c_t$를 Up-project K,V 가중치를 이용해 $k,v$를 가져온다. Up-project 가중치는 $W^{UK},W^{UV}\in R^{d_hn_h\times d_c}$를 만족해 $k_t^C, v_t^C \in R^{d_hn_h}$가 되어 원래 차원으로 복구된다. 이 방법으로 KV cache를 사용하면 압축된 KV인 $c_t^{KV}$, Down-project 가중치 $W^{DKV}$, up-project 가중치 $W^{UK},W^{UV}$가 필요하다. 이 네 가지 값들은 모두 입력의 길이($N$)에 비례하지 않고, 모델의 차원($d$)에 비례하기 때문에 효율적인 KV cache 방법이다.
- 근데 각 토큰에 대해 down-project를 해서 차원을 줄인 건 알겠는데, 결국은 모든 이전 토큰의 KV를 구해야 하는 것은 동일한게 아닌가 모르겠다.
Decoupled Rotary Position Embedding
위와 같이 구조를 변경하며 position embedding 적용에 문제가 발생했다.
References
- DeepSeek LLM: Scaling Open-Source Language Models with Longtermism. arXiv, 2024년 1월 5일.
- DeepSeekMoE: Towards Ultimate Expert Specialization in Mixture-of-Experts Language Models. arXiv, 2024년 1월 11일.
- DeepSeek-V2: A Strong, Economical, and Efficient Mixture-of-Experts Language Model. arXiv, 2024년 6월 19일.
- Auxiliary-Loss-Free Load Balancing Strategy for Mixture-of-Experts
- DeepSeek-V3 Technical Report. arXiv, 2024년 12월 27일
- DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning. arXiv, 2025년 1월 22일.
댓글남기기