🧠

DeepSeek-R1 (V3) のアーキテクチャの解説

に公開

この記事では DeepSeek-R1 および DeepSeek-V3 のアーキテクチャについて解説します。DeepSeek-R1 は DeepSeek-V3 の事前学習済みモデルに追加学習を施したものなので、アーキテクチャはどちらも同じです。

主に DeepSeek-V3 Technical Report の §2 の内容に沿ってまとめていきます。理解に必要な基礎知識 (特に Attention について) は補足するので、ニューラルネットワークの基本的な知識があれば読めると思います。また、なるべく集合論的な曖昧さがないように書いているので、数学に慣れている人は読みやすいと思います。

元々は DeepSeek-V3 Technical Report および R1 の論文をまとめるつもりでしたが、あまりにも長くなってしまうので分割して、この記事ではアーキテクチャのみを解説することにしました。以下のタイトルの記事も執筆予定です。

語句説明

この記事を読むにあたって重要となる語句をまとめます。

  1. DeepSeek-V3
    • DeepSeek から 2024年12月に発表された LLM モデル
  2. DeepSeek-R1
    • DeepSeek から 2025年1月に発表されて話題になった LLM モデル
    • DeepSeek-V3 の事前学習済モデルをベースに学習方法を工夫したもの
  3. MLA (Multi-head Latent Attention)
    • Multi-Head Attention の一部を Latent space (潜在空間) に置き換えたもの
    • 推論時に必要なキャッシュの量を削減させる
  4. MoE (Mixture-of-Experts)
    • 入力に応じて適切なネットワークに処理を振り分けるもの
    • 学習を効率化させる役割を持つ

アーキテクチャ全体像

DeepSeek-V3 のアーキテクチャは基本的に以下の図の通りで、Transformer Block というものがいくつか連なったものになっています。正確には入力側に文章をベクトル化するための Tokenizer と Embedding があり、出力側にはその逆操作をする層が加わります。

DeepSeek-V3 Technical Reportから引用
DeepSeek-V3 Technical Reportから引用

ポイントは上図の右にある MLA と DeepSeekMoE です。MLA では、MultiHead Attention の性能を落とさずに

  • 推論時の計算量を抑えるための KV-Cache という仕組みに必要なメモリ量の大幅な削減

に成功したようです。DeepSeekMoE では、入力に応じて処理をさせるネットワーク (専門家と呼ばれる) を選択する MoE (専門家の混在) と呼ばれる仕組みの

  1. 各専門家が共通の知識を獲得しようとする懸念の解消
  2. 学習時に特定の専門家にのみ振り分けられる問題の解消方法 (Load Balancing) の改善

に成功したようです。

この記事では

  1. Tokenizer
  2. Embedding
  3. RMS Normalization
  4. Multi-Head Latent Attention (MLA)
  5. DeepSeekMoE (Mixture of Experts)
  6. 最後の出力の処理

についてまとめます。1, 2, 3, 6 は割と一般的なもので、4, 5 は DeepSeek オリジナルの要素が含まれています (4, 5 は別の記事で詳しく解説します)。token 間の関係についての処理は Attention のみで行い、それ以外は token ごとに処理をすることを念頭におくと理解しやすいと思います。

また、上の図のようにブロックの入力の前で分岐して出力後に合流する矢印がありますが、これは skip connection といって学習の際の勾配消失問題を解消する役割を持ち、これによって深い層のネットワークの学習が可能になります。skip connection についてはこの記事では解説しません。

Tokenizer について

まずは入力の最初の処理である Tokenizer について説明します。

Tokenizer とは

Tokenizer とは文章を token と呼ばれる単位に分割する役割を持つものの事です。例えば

「吾輩は猫である。名前はまだ無い。」

という文章を単語単位で分割すると

"吾輩は猫である。名前はまだ無い。"
 ↓
['吾輩', 'は', '猫', 'で', 'ある', '。', '名前', 'は', 'まだ', '無い', '。']

のように文章が分割されます。他にも文字単位で分割する方法も考えられます。どの単位を token とするかはケースバイケースです。

文章を token の列に分割することを token 化 (tokenize) と言います。ニューラルネットワーク上ではこのように文章を token 化した後、各 token に識別番号をふり、文章を番号列として扱います。

一般に、ネットワークが扱うことのできる token 数が多くなると計算コストや必要なメモリが増加します。一方、token 数が少なくなると

  • 文章が細かく分割されすぎて意味のあるテキスト表現を学習しにくい
  • 分割後の token 列が長くなる

などの問題が起きます。

その間をとって、粗すぎず細かすぎず、いい感じの分割を行う方法がいくつか知られています (例えば npaka さんの記事を見てください)。

また、文字列を単語の列ではなく byte の列とみなす手法もあり、そのようにすることで原理的にどんな言語でも受け付けられるようになります。

DeepSeek の tokenizer ではありませんが、Llama の tokenizer のデモが公開されています。難しめの漢字を入力すると byte 単位で分割されることが確認できます。

https://belladoreai.github.io/llama3-tokenizer-js/example-demo/build

DeepSeek-V3 の Tokenizer

Tokenizer についての論文中の記述はそれほど詳しくないので、わかる部分だけまとめます。この節は読み飛ばしてもアーキテクチャの理解に影響ありません。

まず、DeepSeek-V3 の Tokenizer は BBPE というアルゴリズムを採用しています。分割の最小単位は文字ではなく byte なので原理的にはどんな言語でも受け付けます。

V3 の Technical Report には V2 の Tokenizer からの変更点のみ書かれているので、まずは V2 の Tokenizer[1] に書かれているもの についてまとめます。

  • 改行、句読点、CJK記号の併合を防ぐために、GPT-2 と同様に事前トークン化を行なった。
  • 数字は1桁ずつに分割した。
  • 約 24 GB の多言語コーパスでトレーニングした。
  • これまでの経験から、トークンの数を 10万 (102,400) とした。
  • さらに15個の特殊なトークンを追加した。

V3 の Tokenizer は上記から以下のような変更を加えたようです。

  • トレーニングコーパスについて
    • 数学サンプルとプログラミングサンプルの比率向上
    • 英語、中国語以外の言語の比率向上
  • プレトークナイザーとトレーニングデータを、多言語圧縮効率を最適化するように変更
  • トークンの数を 128K (コードをみる限り 129280) とした。
  • V2 とは異なり、改行、句読点が含まれる token を禁止しなかった
    • これによりトークン境界バイアス[2]が発生する可能性があるため、改行、句読点などが含まれる token を学習時にランダムで分割した
      • トークン境界バイアスとは学習時に想定していなかった token の分割が推論時に行われると推論が不安定になる現象のこと

Tokenizer の数学的な記号の準備

この後の説明のために、Tokenizer を数学的に、というよりも集合論的に、集合と写像を用いて表します。まず文章 doc とは byte の有限列

doc = (b_1, \cdots, b_n) \quad (b_i \textrm{ は byte})

のことであるとし、文章全体の集合を Doc とします。ただし byte とは 0 以上 255 以下の整数であるとします。

有限部分集合 T \subset Doc で、長さ 1 の byte 列を全て含むものを token の集合といい、t \in T を token といいます。t は byte の有限列です。token の集合 T の要素の数 |T| を語彙数と言います。

token の集合 T を一つ固定します。|T| < \infty なので、token に対して番号 1 \leq j \leq |T| を 1 対 1 に与えることができ、それによって token と番号を同一視できます。この同一視により番号を token と見なす場合は [j] のように [] をつけることとします。

token の集合 T に対して T^{\oplus} を token の有限列全体の集合とおきます (この節だけの記号です)。Tokenizer とは、token の集合 T と写像 \mathrm{Tk}: Doc \to T^{\oplus} の組 (T, \mathrm{Tk}) のことであると定義します。

token の集合 T には、文章の開始を表す token [SOS] (start of sentence) や文章の終了を表す token [EOS] (end of sentence) など、byte 列とは対応しない特殊なトークンを追加する場合がありますが、T を番号の集合とみなせば、それ用の番号を振るのみで追加できます。

ちなみに T の条件として長さ 1 の byte 列を全て含むことを課したので、文章を細かくすれば必ず token の列に変換できますが、場合によってはこの条件を課さないことがあります。その場合は token 化できない byte 列が存在しますが、unknown token [unk] を追加し、token 化できないものは [unk] に対応させるという方法を取ります。

Embedding (単語埋め込み)

Embedding (単語埋め込み) とは

Embedding では token を低次元のベクトル空間 \mathbb{R}^{d_{emb}} に埋め込みます。d_{emb} はベクトル空間の次元です。埋め込みの方法は次のとおりです。

まず token [j] を、j 番目のみ 1、それ以外は 0 である |T| 次元のベクトルに変換します。

このようなベクトルを one-hot ベクトルといい、

\widehat{j} = {}^t (0, \cdots, 0, \overset{j番目}{1},0, \cdots 0)

で表すこととします。{}^t は転置を意味し、\widehat{j} は縦ベクトルであるとします。Attention の初期の論文ではベクトルを横ベクトルで表していましたが、DeepSeek の論文は縦ベクトルなのでこの記事では縦ベクトルで表します。 (縦か横かは行列を右から掛けるか左から掛けるかにのみ影響し、それ以外に本質的な差異はありません。)

Embedding は線形写像 W^{emb}: \mathbb{R}^{|T|} \to \mathbb{R}^{d_{emb}}、つまり |T| \times d_{emb} 行列 W^{emb} を用いて

で与えられます。ここで、W^{emb} はバイアスなしの線形層で与えられます。\mathrm{Emb} により token t \in Td_{emb} 次元のベクトルで表されます。

以後、線型写像を W を用いて表すときは、バイアスなしの線形層で与えられ、学習時に値が更新されるものとします。

tonek の列に対しては、token 毎に埋め込みます。

単語埋め込みが線形であることの補足

ニューラルネットワークでは非線形性を持たせるために活性化関数と呼ばれるものを用いますが、言語の意味に関する処理を行う場合は活性化関数を用いずに、線形な変換のみを行う場合が多いです。それはおそらく、単語間の意味がある種の線形性を持つと考えられているからです。

それは2013年に発表された Word2Vec と呼ばれるモデルにおいて、おそらく最初に確認されました。有名なのは

「王」-「男」+「女」=「女王」

という式で、Word2Vec により埋め込まれた単語ベクトル空間の中で左辺を計算すると、「女王」の単語ベクトルに近い値になります。(ちなみに近さはベクトルの成す角度のみで測るのでベクトルの大きさは関係なく、正確には線形性とはいいません。)

活性化関数を用いると線形性が崩れてしまい、単語間の意味が失われる可能性があるため、 Attention においても線形な変換が主に用いられるのだと思います。

RMS Normalization について

ここからは Transformer Block の中身の解説になります。Transformer Block の入力と出力の形式は、長さ L の token の列を埋め込んだ

(W^{emb}\widehat{j_1} \ , W^{emb}\widehat{j_2} \ , \cdots, W^{emb} \widehat{j_{L}} ) \in (\mathbb{R}^{d_{emb}})^{L}

と同じ形式ですが、Attention 以外は token の位置毎に処理されます。それを念頭に置くと理解しやすくなるかもしれません。

以後、Transformer Block の入力データについては位置の添字になるべく 1 \leq \ell \leq L を用いるようにしますが、それ以外の場合はその限りではありません (この L は論文中の記号での Transformer Block の数と被っていますが、別物です)。

Normalization はデータのスケールを調整して学習を安定させる役割を持ちます。まずは、RMS Normalization のベースとなった Layer Normalization について説明します。

Layer Normalization とは

一般にデータ

(x_1, x_2, \cdots, x_n) \in \mathbb{R}^n

に対して、平均 \mu および分散 \sigma

\begin{align*}\mu &= \frac{1}{n}\sum_{i=1}^{n}x_i \\ \sigma &= \frac{1}{n}\sum_{i=1}^n (x_i -\mu)^2 \end{align*}

を計算し、x_i

\overline{x}_i = \frac{x_i -\mu}{\sqrt{\sigma}}

に置き換えると (\overline{x}_1, \cdots, \overline{x}_n) の平均は 0、分散は 1 になります。Layer Nomalization ではさらに、n 個の学習可能なパラメータ \gamma_i1 個の学習可能なパラメータ \beta と、小さい定数 \varepsilon を用いて

\widetilde{x}_i = \frac{x_i -\mu}{\sqrt{\sigma + \varepsilon}} \gamma_i + \beta

と変換します。\varepsilon は分母が 0 にならないように便宜的につけています。

RMS Normalization とは

RMS Normalization は Layer Normalization の性能を落とさずに処理を簡略化したものです。RMS は Root Mean Square の略で、Layer Normalization における平均を取ることによるデータの中心化を省略し、分散の代わりに 2 乗の平均の平方根を取ったものになります。

つまり、入力データ

(x_1, x_2, \cdots, x_n) \in \mathbb{R}^{n}

n 個の学習可能なパラメータ \gamma_i と小さい定数 \varepsilon を用いて

\overline{x}_i = \frac{x_i}{\sqrt{\varepsilon + {\displaystyle \frac{1}{n} \sum_{i=1}^n x_i^2}}} \gamma_i

と変換します。

DeepSeek のアーキテクチャにおける RMS Normalization への入力データの形は、入力された token の数を L として

(x_1, \cdots, x_{L}) \in (\mathbb{R}^{d_{emb}})^{L}

となります。例えば Embedding 直後では、各 token の埋め込みベクトル

(W^{emb} \widehat{j_1} \ , W^{emb} \widehat{j_2} \ , \cdots, W^{emb} \widehat{j_{L}}) \in (\mathbb{R}^{d_{emb}})^{L}

が入力データとなります。これに対して RMS Normalization を、各 token の位置毎に適用します。つまり

x_\ell = \begin{pmatrix} x_{\ell,1} \\ x_{\ell, 2} \\ \vdots \\ x_{\ell, d_{emb}} \end{pmatrix} \in \mathbb{R}^{d_{emb}}

に対して

\mathrm{RMS}(x_j)=\begin{pmatrix} {\displaystyle \frac{x_{\ell, 1}} {\sqrt{\varepsilon + {\displaystyle \frac{1}{d_{emb}} \sum_{i=1}^{d_{emb}} x_{\ell, i}^2} }} } \gamma_1 \\ \vdots \\ {\displaystyle \frac{x_{\ell, d_{emb}}} {\sqrt{\varepsilon + {\displaystyle \frac{1}{d_{emb}} \sum_{i=1}^{d_{emb}} x_{\ell, i}^2} }} } \gamma_{d_{emb}} \end{pmatrix}\in \mathbb{R}^{d_{emb}}

と定めます。ただし \gamma_1, \cdots, \gamma_{d_{emb}} は学習可能なパラメータで、(x_1, \cdots, x_{L}) \in (\mathbb{R}^{d_{emb}})^{L} に対して共通です。

MultiHead Latent Attention (MLA)

MultiHead Latent Attention (MLA) については記事を分けて、以下の記事で解説します。

「Attention の基礎から始める DeepSeek MLA (Multi-Head Latent Attention) の解説」

要点だけまとめると以下のようになります。

  • 推論時にキャッシュされる情報の次元を低くする (Latent Space に埋め込む) ことでキャッシュ量を減らした
  • キャッシュ量を減らしたが、精度は落ちていない
  • Latent Space に埋め込む影響で Position Encoding のやり方が少し特殊

DeepSeekMoE (Mixture of Experts)

DeepSeekMoE については記事を分けて、以下の記事で解説します。

「DeepSeek MoE (Mixture of Expert) の解説」

要点だけまとめると以下のようになります。

  • shared expert と呼ばれる、常に処理を行う expert を入れることで、各 expert が共通の知識を獲得しようとする冗長性を解消し、各 expert をより専門化させた。
  • 従来使われていた損失関数を使わないことで、学習を効率化。

出力部分

以上で Transfomer Block 内の説明が終わったので、最後に出力の部分の処理について説明します。最後に出力の部分では、Transfomer Block の出力

(x_1, \cdots, x_L) \in (\mathbb{R}^{d_{emb}})^L

の各 x_\ell (1 \leq \ell \leq L) を、token の集合 T 上の確率分布に変換します。

そのために、線型写像 W: \mathbb{R}^{d_{emb}} \to \mathbb{R}^{|T|} (|T| は全ての token の数) を用意して

W (\mathrm{RMS}(x_\ell))

と、x_\ell\mathbb{R}^{|T|} 次元のベクトルに変換します (\mathrm{RMS} は RMS Normalization です)。それに \mathrm{softmax}: \mathbb{R}^{|T|} \to (0, 1)^{|T|} を適用して、T 上の確率分布 p_{\ell} が得られます。

p_{\ell}([j]) = \mathrm{softmax}(W (\mathrm{RMS}(x_\ell)))_j

ただし [j]j 番目の token を表し、\mathrm{softmax}\alpha = (\alpha_1, \cdots, \alpha_{|T|}) に対して

\mathrm{softmax}(\alpha) = \left(\frac{e^{\alpha_1}}{\sum_{i=1}^{|T|} e^{\alpha_j}}, \cdots, \frac{e^{\alpha_{|T|}}}{\sum_{i=1}^{|T|} e^{\alpha_j}}\right)

で定義される関数です。

学習時はこの確率分布を使って損失関数を計算し、推論時は確率の最も高い token を選ぶか、確率分布に従ってサンプリングを行います (損失関数については事前学習の記事で書きます。)。

\mathrm{softmax} は温度と呼ばれるパラメータ \tau を用いて

\mathrm{softmax}_{\tau} (\alpha)_j = \frac{\exp(\alpha_j /\tau)}{\sum_{i=1}^{|T|} \exp(\alpha_j /\tau)}

変形される場合があります。\tau = 1 の場合は \mathrm{softmax} と同じです。\tau < 1 の場合は確率の高いものがより強調されランダム性が下がり、\tau > 1 の場合は確率が全体的になだらかになりランダム性が上がります。

以上で、DeepSeek-R1 (V3) のアーキテクチャを一通り説明できました。

脚注
  1. DeepSeek-AI. DeepSeek LLM Scaling Open-Source Language Models with Longtermism. §2.1 ↩︎

  2. The Art of Prompt Design: Prompt Boundaries and Token Healing ↩︎

GitHubで編集を提案

Discussion