ホームページ  >  記事  >  テクノロジー周辺機器  >  Llama3 レイヤー 1 を手動でティアリングする: llama3 を最初から実装する

Llama3 レイヤー 1 を手動でティアリングする: llama3 を最初から実装する

WBOY
WBOYオリジナル
2024-06-01 17:45:421011ブラウズ

1. Llama3 のアーキテクチャ

このシリーズの記事では、llama3 を最初から実装します。

Llama3 の全体的なアーキテクチャ:

手撕Llama3第1层: 从零开始实现llama3写真

Llama3 のモデルパラメータ:

LlaMa 3 モデルにおけるこれらのパラメータの実際の値を見てみましょう。

手撕Llama3第1层: 从零开始实现llama3写真

[1]コンテキストウィンドウ(context-window)

LlaMaクラスをインスタンス化する際、変数max_seq_lenはコンテキストウィンドウを定義します。クラスには他にもパラメータがありますが、このパラメータは変圧器モデルに最も直接関係しています。ここでの max_seq_len は 8K です。

手撕Llama3第1层: 从零开始实现llama3Pictures

[2] 語彙サイズと注目レイヤー

Transformer クラスは語彙とレイヤー数を定義するモデルです。ここでの語彙とは、モデルが認識して処理できる単語 (およびトークン) のセットを指します。アテンション レイヤーは、モデルで使用されるトランスフォーマー ブロック (アテンション レイヤーとフィードフォワード レイヤーの組み合わせ) を指します。

手撕Llama3第1层: 从零开始实现llama3写真

これらの数字によると、LlaMa 3 の語彙数は 128K で、これは非常に多くなります。さらに、32個のトランスブロックを備えています。

[3] フィーチャー次元とアテンションヘッド

フィーチャー次元とアテンションヘッドがセルフアテンション モジュールに導入されます。特徴次元は、埋め込み空間内のトークンのベクトル サイズを指します (特徴次元は、入力データまたは埋め込みベクトルの次元サイズを指します)。一方、アテンションヘッドには、トランスフォーマーのセルフ アテンション メカニズムを駆動する QK モジュールが含まれます。

手撕Llama3第1层: 从零开始实现llama3写真

[4] 隠れ次元

隠れ次元とは、フィードフォワードニューラルネットワーク(Feed Forward)の隠れ層の次元サイズを指します。フィードフォワード ニューラル ネットワークには通常 1 つ以上の隠れ層が含まれており、これらの隠れ層の次元によってネットワークの容量と複雑さが決まります。 Transformer モデルでは、モデルの表現能力を高めるために、フィードフォワード ニューラル ネットワークの隠れ層の次元は通常、特徴次元の倍数になります。 LLama3 では、隠れ次元は特徴次元の 1.3 倍です。隠れ層と隠れ次元は 2 つの概念であることに注意してください。

隠れ層の数が多いほど、ネットワークはより豊富な表現を内部で作成および操作してから、より小さな出力次元に投影し直すことができます。

手撕Llama3第1层: 从零开始实现llama3Picture

[5] 上記のパラメーターを Transformer に結合します

最初の行列は入力特徴行列で、これはアテンション レイヤーによって処理されてアテンション加重特徴を生成します。この画像では、入力特徴行列のサイズは 5 x 3 のみですが、実際の Llama 3 モデルでは 8K x 4096 まで大きくなります。

次はフィードフォワード ネットワークの隠れ層で、5325 まで成長し、最後の層で 4096 に戻ります。

手撕Llama3第1层: 从零开始实现llama3写真

[6] 多層のトランスフォーマーブロック

LlaMa 3 は上記の 32 個のトランスフォーマー ブロックを結合し、出力は最後のブロックに到達するまで 1 つのブロックから次のブロックに渡されます。

手撕Llama3第1层: 从零开始实现llama3写真

[7] すべてをまとめる

上記のすべての部分を開始したら、それらを組み合わせて、LlaMa エフェクトがどのように作成されるかを確認します。

手撕Llama3第1层: 从零开始实现llama3写真

ステップ 1: まず、サイズ 8K(コンテキスト ウィンドウ) x 128K(語彙サイズ) の入力行列を用意します。この行列は、この高次元行列を低次元行列に変換するための埋め込みプロセスを受けます。

ステップ 2: この場合、この低次元の結果は 4096 になります。これは、前に見た LlaMa モデルの特徴の指定された次元です。

ニューラル ネットワークでは、次元の強化と次元の削減は一般的な操作であり、それぞれに異なる目的と効果があります。

次元の強化は通常、より複雑な特徴やパターンを捕捉できるようにモデルの容量を増やすことです。入力データが高次元空間にマッピングされると、モデルによってさまざまな特徴の組み合わせをより簡単に区別できるようになります。これは、モデルがより複雑な決定境界を学習するのに役立つため、非線形問題を扱う場合に特に役立ちます。

次元削減は、モデルの複雑さと過学習のリスクを軽減することです。特徴空間の次元を減らすことにより、モデルはより洗練された一般化された特徴表現を学習するように強制できます。さらに、次元削減を正則化方法として使用して、モデルの汎化能力を向上させることができます。場合によっては、次元削減により計算コストが削減され、モデルの操作効率が向上することもあります。

実際のアプリケーションでは、次元を増やしてから次元を減らすという戦略は、特徴抽出と変換のプロセスとみなすことができます。このプロセスでは、モデルはまず次元を増やすことでデータの本質的な構造を調査し、次に次元を減らすことで最も有用な特徴とパターンを抽出します。この方法は、十分な複雑さを維持しながら、モデルがトレーニング データへの過剰適合を回避するのに役立ちます。

ステップ 3: この機能は、Transformer ブロックを通じて、最初にアテンション層によって、次に FFN 層によって処理されます。アテンション層はフィーチャ全体を水平方向に処理し、FFN 層はディメンション全体を垂直方向に処理します。

ステップ 4: Transformer ブロックの 32 レイヤーに対してステップ 3 を繰り返します。最後に、結果として得られる行列の次元は、特徴の次元に使用されるものと同じになります。

ステップ 5: 最後に、モデルが語彙内で使用可能な単語を選択してマッピングできるように、この行列は元の語彙行列サイズ (128K) に変換されて戻されます。

これが、LlaMa 3 がこれらのベンチマークで高いスコアを獲得し、LlaMa 3 効果を生み出す方法です。

いくつかの混同されやすい用語を簡単にまとめます:

1. max_seq_len (最大シーケンス長)

これは、モデルが 1 回の処理で受け入れることができるトークンの最大数です。

LlaMa 3-8B モデルでは、このパラメーターは 8,000 トークン、つまりコンテキスト ウィンドウ サイズ = 8K に設定されています。これは、モデルが 1 回の処理で考慮できるトークンの最大数が 8,000 であることを意味します。これは、長いテキストを理解したり、長期の会話のコンテキストを維持したりするために重要です。

2. Vocabulary-size (語彙)

これは、モデルが認識できるすべての異なるトークンの数です。これには、考えられるすべての単語、句読点、特殊文字が含まれます。モデルの語彙は 128,000 で、Vocabulary-size = 128K として表されます。これは、モデルがさまざまな単語、句読点、特殊文字を含む 128,000 の異なるトークンを認識して処理できることを意味します。

3. アテンションレイヤー

Transformer モデルの主要コンポーネント。これは主に、入力データのどの部分が最も重要であるか (つまり、どのトークンが「出席」しているか) を学習することによって入力データを処理する役割を果たします。モデルにはそのようなレイヤーが複数ある場合があり、それぞれが異なる観点から入力データを理解しようとします。

LlaMa 3-8B モデルには 32 の処理レイヤーが含まれています。つまり、レイヤー数 = 32 です。これらの層には、複数のアテンション層と他のタイプのネットワーク層が含まれており、それぞれが異なる観点から入力データを処理して理解します。

4. Transformer ブロック

複数の異なるレイヤーのモジュールが含まれており、通常は少なくとも 1 つのアテンション レイヤーとフィードフォワード ネットワークが含まれます。モデルには複数のトランスフォーマー ブロックを含めることができ、これらのブロックは順番に接続され、各ブロックの出力は次のブロックの入力になります。トランスフォーマーブロックはデコーダー層とも呼ばれます。

Transformer モデルのコンテキストでは、通常、モデルには「32 レイヤー」があると言いますが、これはモデルに「32 の Transformer ブロック」があると言うのと同じです。各 Transformer ブロックには通常、セルフ アテンション レイヤーとフィードフォワード ニューラル ネットワーク レイヤーが含まれており、これら 2 つのサブレイヤーが合わせて完全な処理ユニットまたは「レイヤー」を形成します。

つまり、モデルに 32 個の Transformer ブロックがあると言うとき、実際にはモデルが 32 個のそのような処理ユニットで構成されており、それぞれがデータのセルフアテンション処理とフィードフォワード ネットワーク処理が可能であることを説明しています。このプレゼンテーションでは、モデルの階層構造と各レベルでのその処理能力を強調します。

要約すると、Transformer モデルの構造を説明する場合、「32 レイヤー」と「32 Transformer ブロック」は基本的に同義であり、どちらもモデルに 32 の独立したデータ処理サイクルが含まれており、各サイクルには自己注意とフィードフォワード ネットワーク操作が含まれていることを意味します。

5. 特徴次元

これは、入力トークンがモデル内でベクトルとして表現されるときの各ベクトルの次元です。

各トークンは、モデル内の 4096 個の特徴を含むベクトル、つまり、Feature-dimension = 4096 に変換されます。この高次元により、モデルはより豊富な意味情報と文脈上の関係をキャプチャできるようになります。

6. アテンション ヘッド

各アテンション レイヤーには複数のアテンション ヘッドがあり、各ヘッドは異なる視点から入力データを個別に分析します。

各アテンション レイヤーには 32 個の独立したアテンション ヘッドが含まれています。つまり、アテンション ヘッドの数 = 32 です。これらのヘッドは入力データをさまざまな側面から分析し、より包括的なデータ分析機能を共同で提供します。

7. 隠れた次元

これは通常、フィードフォワード ネットワークの層の幅、つまり各層のニューロンの数を指します。通常、非表示ディメンションはフィーチャ ディメンションよりも大きくなり、モデルが内部的により豊富なデータ表現を作成できるようになります。

フィードフォワード ネットワークでは、隠れ層の次元は 5325、つまり隠れ次元 = 5325 です。これは特徴の次元よりも大きいため、モデルは内部層間でより深い特徴の変換と学習を実行できます。

関係と値:

アテンション レイヤーとアテンション ヘッドの関係: 各アテンション レイヤーには複数のアテンション ヘッドを含めることができます。

数値関係: モデルには複数のトランスフォーマー ブロックが含まれる場合があり、各ブロックにはアテンション レイヤーと 1 つ以上の他のレイヤーが含まれます。各アテンション レイヤーには複数のアテンション ヘッドがある場合があります。このようにして、モデル全体がさまざまなレイヤーとヘッドで複雑なデータ処理を実行します。

Llama3 モデルの公式リンク スクリプトをダウンロードします: https://llama.meta.com/llama-downloads/

2. モデルを表示します

次のコードは、tiktoken ライブラリを使用して、バイト ペアベース エンコーディング (BPE) トークナイザー。このトークナイザーは、特に自然言語処理および機械学習モデルで使用するために、テキスト データを処理するように設計されています。

hello world に入り、単語セグメンターが単語分割をどのように実行するかを確認します。

from pathlib import Pathimport tiktokenfrom tiktoken.load import load_tiktoken_bpeimport torchimport jsonimport matplotlib.pyplot as plttokenizer_path = "Meta-Llama-3-8B/tokenizer.model"special_tokens = ["<|begin_of_text|>","<|end_of_text|>","<|reserved_special_token_0|>","<|reserved_special_token_1|>","<|reserved_special_token_2|>","<|reserved_special_token_3|>","<|start_header_id|>","<|end_header_id|>","<|reserved_special_token_4|>","<|eot_id|>",# end of turn] + [f"<|reserved_special_token_{i}|>" for i in range(5, 256 - 5)]mergeable_ranks = load_tiktoken_bpe(tokenizer_path)tokenizer = tiktoken.Encoding(name=Path(tokenizer_path).name,pat_str=r"(?i:'s|'t|'re|'ve|'m|'ll|'d)|[^\r\n\p{L}\p{N}]?\p{L}+|\p{N}{1,3}| ?[^\s\p{L}\p{N}]+[\r\n]*|\s*[\r\n]+|\s+(?!\S)|\s+",mergeable_ranks=mergeable_ranks,special_tokens={token: len(mergeable_ranks) + i for i, token in enumerate(special_tokens)},)tokenizer.decode(tokenizer.encode("hello world!"))

手撕Llama3第1层: 从零开始实现llama3画像

モデルファイルを読み取る


手撕Llama3第1层: 从零开始实现llama3

ロードされたモデルファイルに含まれる最初の 20 個のパラメータまたは重みの名前を表示します。

model = torch.load("Meta-Llama-3-8B/consolidated.00.pth")print(json.dumps(list(model.keys())[:20], indent=4))

手撕Llama3第1层: 从零开始实现llama3Pictures

  1. "tok_embeddings.weight": これは、入力単語 (またはより一般的にはトークン) を固定次元ベクトルに変換する単語埋め込み層がモデルにあることを示します。これは、ほとんどの自然言語処理モデルの最初のステップです。
  2. 「layers.0.attention...」および「layers.1.attention...」: これらのパラメーターは複数のレイヤーを表し、各レイヤーにはアテンション メカニズム モジュールが含まれます。このモジュールでは、wq、wk、wv、wo はそれぞれクエリ、キー、値、出力の重み行列を表します。これは、Transformer モデルのコア コンポーネントであり、入力シーケンスのさまざまな部分間の関係をキャプチャするために使用されます。
  3. 「layers.0.feed_forward...」および「layers.1.feed_forward...」: これらのパラメーターは、各レイヤーにフィード フォワード ネットワーク (フィード フォワード ネットワーク) も含まれていることを示します。フィード フォワード ネットワークは通常 2 つの線形変換で構成されます。真ん中に非線形活性化関数があります。 w1、w2、および w3 は、このフィードフォワード ネットワーク内の異なる線形層の重みを表す場合があります。
  4. 「layers.0.attention_norm.weight」および「layers.1.attention_norm.weight」: これらのパラメーターは、安定化トレーニング プロセスのために各層のattentionモジュールの背後に正規化層(おそらく層正規化)があることを示します。
  5. 「layers.0.ffn_norm.weight」および「layers.1.ffn_norm.weight」: これらのパラメーターは、フィードフォワード ネットワークの背後にも正規化層があることを示します。上記コードの出力内容は下図と同じで、Llama3のtransformerブロックです。

手撕Llama3第1层: 从零开始实现llama3写真

全体として、この出力は、Transformer アーキテクチャに基づく深層学習モデルの主要なコンポーネントを明らかにします。このモデルは、テキスト分類、機械翻訳、質問応答システムなどの自然言語処理タスクで広く使用されています。アテンション メカニズム、フィードフォワード ネットワーク、正規化層など、各層の構造はほぼ同じであり、モデルが複雑な入力シーケンスの特徴を捉えるのに役立ちます。

Llama3 モデルのパラメーター構成を表示します:

with open("Meta-Llama-3-8B/params.json", "r") as f:config = json.load(f)config

手撕Llama3第1层: 从零开始实现llama3图片

  1. 'dim': 4096 - 表示模型中的隐藏层维度或特征维度。这是模型处理数据时每个向量的大小。
  2. 'n_layers': 32 - 表示模型中层的数量。在基于Transformer的模型中,这通常指的是编码器和解码器中的层的数量。
  3. 'n_heads': 32 - 表示在自注意力(Self-Attention)机制中,头(head)的数量。多头注意力机制是Transformer模型的关键特性之一,它允许模型在不同的表示子空间中并行捕获信息。
  4. 'n_kv_heads': 8 - 这个参数不是标准Transformer模型的常见配置,可能指的是在某些特定的注意力机制中,用于键(Key)和值(Value)的头的数量。
  5. 'vocab_size': 128256 - 表示模型使用的词汇表大小。这是模型能够识别的不同单词或标记的总数。
  6. 'multiple_of': 1024 - 这可能是指模型的某些维度需要是1024的倍数,以确保模型结构的对齐或优化。
  7. 'ffn_dim_multiplier': 1.3 - 表示前馈网络(Feed-Forward Network, FFN)的维度乘数。在Transformer模型中,FFN是每个注意力层后的一个网络,这个乘数可能用于调整FFN的大小。
  8. 'norm_eps': 1e-05 - 表示在归一化层(如Layer Normalization)中使用的epsilon值,用于防止除以零的错误。这是数值稳定性的一个小技巧。
  9. 'rope_theta': 500000.0 - 这个参数不是标准Transformer模型的常见配置,可能是指某种特定于模型的技术或优化的参数。它可能与位置编码或某种正则化技术有关。

我们使用这个配置来推断模型的细节,比如:

  1. 模型有32个Transformer层
  2. 每个多头注意力块有32个头
  3. 词汇表的大小等等 
dim = config["dim"]n_layers = config["n_layers"]n_heads = config["n_heads"]n_kv_heads = config["n_kv_heads"]vocab_size = config["vocab_size"]multiple_of = config["multiple_of"]ffn_dim_multiplier = config["ffn_dim_multiplier"]norm_eps = config["norm_eps"]rope_theta = torch.tensor(config["rope_theta"])

手撕Llama3第1层: 从零开始实现llama3图片

将Text转化为Token

代码如下:

prompt = "the answer to the ultimate question of life, the universe, and everything is "tokens = [128000] + tokenizer.encode(prompt)print(tokens)tokens = torch.tensor(tokens)prompt_split_as_tokens = [tokenizer.decode([token.item()]) for token in tokens]print(prompt_split_as_tokens)

[128000, 1820, 4320, 311, 279, 17139, 3488, 315, 2324, 11, 279, 15861, 11, 323, 4395, 374, 220]['<|begin_of_text|>', 'the', ' answer', ' to', ' the', ' ultimate', ' question', ' of', ' life', ',', ' the', ' universe', ',', ' and', ' everything', ' is', ' ']

将令牌转换为它们的嵌入表示

截止到目前,我们的[17x1]令牌现在变成了[17x4096],即长度为4096的17个嵌入(每个令牌一个)。

下图是为了验证我们输入的这句话,是17个token。

手撕Llama3第1层: 从零开始实现llama3图片

代码如下:

embedding_layer = torch.nn.Embedding(vocab_size, dim)embedding_layer.weight.data.copy_(model["tok_embeddings.weight"])token_embeddings_unnormalized = embedding_layer(tokens).to(torch.bfloat16)token_embeddings_unnormalized.shape

手撕Llama3第1层: 从零开始实现llama3图片

三、构建Transformer的第一层

我们接着使用 RMS 归一化对嵌入进行归一化,也就是图中这个位置:

手撕Llama3第1层: 从零开始实现llama3图片

使用公式如下:

手撕Llama3第1层: 从零开始实现llama3图片

代码如下:

# def rms_norm(tensor, norm_weights):# rms = (tensor.pow(2).mean(-1, keepdim=True) + norm_eps)**0.5# return tensor * (norm_weights / rms)def rms_norm(tensor, norm_weights):return (tensor * torch.rsqrt(tensor.pow(2).mean(-1, keepdim=True) + norm_eps)) * norm_weights

这段代码定义了一个名为 rms_norm 的函数,它实现了对输入张量(tensor)的RMS(Root Mean Square,均方根)归一化处理。这个函数接受两个参数:tensor 和 norm_weights。tensor 是需要进行归一化处理的输入张量,而 norm_weights 是归一化时使用的权重。

函数的工作原理如下:

  1. 首先,计算输入张量每个元素的平方(tensor.pow(2))。
  2. 然后,对平方后的张量沿着最后一个维度(-1)计算均值(mean),并保持维度不变(keepdim=True),这样得到每个元素的均方值。
  3. 接着,将均方值加上一个很小的正数 norm_eps(为了避免除以零的情况),然后计算其平方根的倒数(torch.rsqrt),得到RMS的倒数。
  4. 最后,将输入张量与RMS的倒数相乘,再乘以归一化权重 norm_weights,得到归一化后的张量。

在进行归一化处理后,我们的数据形状仍然保持为 [17x4096],这与嵌入层的形状相同,只不过数据已经过归一化。

token_embeddings = rms_norm(token_embeddings_unnormalized, model["layers.0.attention_norm.weight"])token_embeddings.shape

手撕Llama3第1层: 从零开始实现llama3图片

手撕Llama3第1层: 从零开始实现llama3图片

接下来,我们介绍注意力机制的实现,也就是下图中的红框标注的位置:

手撕Llama3第1层: 从零开始实现llama3图片

手撕Llama3第1层: 从零开始实现llama3图片

我们一步一步地解释这张图,详细说明每个步骤。

1. 输入句子

  • 描述:这是我们的输入句子。
  • 解释:输入句子被表示为一个矩阵 ( X ),其中每一行代表一个词的嵌入向量。

2. 嵌入每个词

  • 描述:我们对每个词进行嵌入。
  • 解释:输入句子中的每个词被转换为一个高维向量,这些向量组成了矩阵 ( X )。

3. 分成8个头

  • 描述:将矩阵 ( X ) 分成8个头。我们用权重矩阵 ( W^Q )、( W^K ) 和 ( W^V ) 分别乘以 ( X )。
  • 解释:多头注意力机制将输入矩阵 ( X ) 分成多个头(这里是8个),每个头有自己的查询(Query)、键(Key)和值(Value)矩阵。具体来说,输入矩阵 ( X ) 分别与查询权重矩阵 ( W^Q )、键权重矩阵 ( W^K ) 和值权重矩阵 ( W^V ) 相乘,得到查询矩阵 ( Q )、键矩阵 ( K ) 和值矩阵 ( V )。

4. 计算注意力

  • 描述:使用得到的查询、键和值矩阵计算注意力。
  • 解释:对于每个头,使用查询矩阵 ( Q )、键矩阵 ( K ) 和值矩阵 ( V ) 计算注意力分数。具体步骤包括:

计算 ( Q ) 和 ( K ) 的点积。

对点积结果进行缩放。

应用softmax函数得到注意力权重。

用注意力权重乘以值矩阵 ( V ) 得到输出矩阵 ( Z )。

5. 拼接结果矩阵

  • 描述:将得到的 ( Z ) 矩阵拼接起来,然后用权重矩阵 ( W^O ) 乘以拼接后的矩阵,得到层的输出。
  • 解释:将所有头的输出矩阵 ( Z ) 拼接成一个矩阵,然后用输出权重矩阵 ( W^O ) 乘以这个拼接后的矩阵,得到最终的输出矩阵 ( Z )。

额外说明

  • 查询、键、值和输出向量的形状:在加载查询、键、值和输出向量时,注意到它们的形状分别是 [4096x4096]、[1024x4096]、[1024x4096]、[1024x4096] 和 [4096x4096]。
  • 并行化注意力头的乘法:将它们捆绑在一起有助于并行化注意力头的乘法。

这张图展示了Transformer模型中多头注意力机制的实现过程,从输入句子的嵌入开始,经过多头分割、注意力计算,最后拼接结果并生成输出。每个步骤都详细说明了如何从输入矩阵 ( X ) 生成最终的输出矩阵 ( Z )。

当我们从模型中加载查询(query)、键(key)、值(value)和输出(output)向量时,我们注意到它们的形状分别是 [4096x4096]、[1024x4096]、[1024x4096]、[4096x4096]

乍一看这很奇怪,因为理想情况下我们希望每个头的每个q、k、v和o都是单独的

print(model["layers.0.attention.wq.weight"].shape,model["layers.0.attention.wk.weight"].shape,model["layers.0.attention.wv.weight"].shape,model["layers.0.attention.wo.weight"].shape)

手撕Llama3第1层: 从零开始实现llama3画像

クエリ重み行列 (wq.weight) の形状は [4096, 4096] です。キーの重み行列 (wk.weight) の形状は [1024, 4096] です。値の重み行列 (wv.weight) の形状は [1024, 4096] です。出力 (Output) 重み行列 (wo.weight) の形状は [4096, 4096] です。出力結果は、クエリ (Q) と出力 (O) の重み行列の形状が同じであることを示しています (両方とも [4096, 4096])。これは、入力フィーチャと出力フィーチャの両方のクエリと出力のディメンションが 4096 であることを意味します。キー (K) と値 (V) の重み行列の形状も同じです (両方とも [1024、4096])。これは、キーと値の入力特徴の次元は 4096 ですが、出力特徴の次元は 1024 に圧縮されていることを示しています。これらの重み行列の形状は、モデル設計者がアテンション メカニズムのさまざまな部分の寸法をどのように設定するかを反映します。特に、キーと値の次元は、おそらく計算の複雑さとメモリ消費量を削減するために削減されますが、クエリと出力の次元を高く保つことは、より多くの情報を保持するためである可能性があります。この設計の選択は、特定のモデル アーキテクチャとアプリケーション シナリオによって異なります
この図のアテンション メカニズムを説明する実装プロセスを簡略化するために、例として「李紅章を賞賛します」という文を使用してみましょう。文を入力してください: まず、「私は李紅章を賞賛します」という文があります。この文を処理する前に、文内の各単語を数学的に処理可能な形式、つまり単語ベクトルに変換する必要があります。このプロセスは単語の埋め込みと呼ばれます。
単語の埋め込み: 「私」、「感謝」、「李紅章」などの各単語は、固定サイズのベクトルに変換されます。これらのベクトルには単語の意味情報が含まれています。
複数のヘッドに分割: モデルがさまざまな視点から文を理解できるようにするために、各単語のベクトルを複数の部分に分割します。ここでは 8 つのヘッドを示します。各頭は文の異なる側面に焦点を当てています。
アテンションを計算する: 頭ごとに、アテンションと呼ばれるものを計算します。このプロセスには 3 つのステップが含まれます。「李紅章に感謝します」を例に挙げます。「感謝」という単語に焦点を当てたい場合は、「感謝」がクエリになり、「私」や「李紅章」などの他の単語がクエリになります。はキーです。 のベクトルは値です。
クエリ (Q): これは情報を見つけたい部分です。キー (K): 情報が含まれる部分です。値 (V): これは実際の情報コンテンツです。スプライシングと出力: 各ヘッドのアテンションを計算した後、これらの結果を連結し、重み行列 Wo を通じて最終出力を生成します。この出力は、次の処理層で、または最終結果の一部として使用されます。

図のコメントで言及されている形状の問題は、これらのベクトルをコンピューターで効率的に保存および処理する方法に関するものです。実際のコード実装では、効率を向上させるために、開発者は各ヘッダーを個別に処理するのではなく、複数のヘッダーのクエリ、キー、および値のベクトルをまとめてパッケージ化することがあります。これにより、最新のコンピューターの並列処理機能を利用して計算を高速化できます。

  • クエリ重み行列 (wq.weight) の形状は [4096, 4096] です。
  • キーの重み行列 (wk.weight) の形状は [1024, 4096] です。
  • Value 重み行列 (wv.weight) の形状は [1024, 4096] です。
  • 出力(Output)の重み行列(wo.weight)の形状は[4096, 4096]です。

出力結果は次のことを示しています:

  • クエリ (Q) と出力 (O) の重み行列の形状は同じで、どちらも [4096, 4096] です。これは、入力フィーチャと出力フィーチャの両方のクエリと出力のディメンションが 4096 であることを意味します。
  • キー (K) と値 (V) の重み行列の形状も同じで、どちらも [1024, 4096] です。これは、キーと値の入力特徴の次元は 4096 ですが、出力特徴の次元は 1024 に圧縮されていることを示しています。

これらの重み行列の形状は、モデル設計者がアテンション メカニズムのさまざまな部分の寸法をどのように設定するかを反映しています。特に、キーと値の次元は、おそらく計算の複雑さとメモリ消費量を削減するために削減されますが、クエリと出力の次元を高く保つことは、より多くの情報を保持するためである可能性があります。この設計の選択は、特定のモデル アーキテクチャとアプリケーション シナリオによって異なります

この図の注意メカニズムを説明する実装プロセスを簡略化するために、例として「李紅章を賞賛します」という文を使用してみましょう。

  • 文を入力してください: まず、「李紅章に感謝します」という文があります。この文を処理する前に、文内の各単語を数学的に処理可能な形式、つまり単語ベクトルに変換する必要があります。このプロセスは単語の埋め込みと呼ばれます。
  • 単語の埋め込み: 「私」、「感謝」、「李紅章」などの各単語は、固定サイズのベクトルに変換されます。これらのベクトルには単語の意味情報が含まれています。
  • 複数のヘッドに分割: モデルがさまざまな視点から文を理解できるようにするために、各単語のベクトルを複数の部分に分割します。ここでは 8 つのヘッドに分割します。各頭は文の異なる側面に焦点を当てています。
  • 注意力の計算: 頭ごとに、注意力と呼ばれるものを計算します。このプロセスには 3 つのステップが含まれます。「李紅章に感謝します」を例に挙げます。「感謝」という単語に焦点を当てたい場合は、「感謝」がクエリになり、「私」や「李紅章」などの他の単語がクエリになります。はキーです。 のベクトルは値です。

クエリ (Q): これは情報を見つけたい部分です。

キー (K): これは情報が含まれる部分です。

値 (V): これは実際の情報コンテンツです。

  • スプライシングと出力: 各頭のアテンションを計算した後、これらの結果を一緒にスプライスし、重み行列 Wo を通じて最終出力を生成します。この出力は、次の処理層で、または最終結果の一部として使用されます。

図のコメントで言及されている形状の問題は、これらのベクトルをコンピューターで効率的に保存および処理する方法に関するものです。実際のコード実装では、効率を向上させるために、開発者は各ヘッダーを個別に処理するのではなく、複数のヘッダーのクエリ、キー、および値のベクトルをまとめてパッケージ化することがあります。これにより、最新のコンピューターの並列処理機能を利用して計算を高速化できます。

引き続き「李紅章に感謝します」という文を使用して、重み行列 WQ、WK、WV、WO の役割を説明します。

Transformer モデルでは、各単語は単語埋め込みによってベクトルに変換されます。これらのベクトルは一連の線形変換を経て、注意スコアが計算されます。これらの線形変換は、重み行列 WQ、WK、WV、および WO を通じて実装されます。

  1. WQ (重み行列 Q): この行列は、各単語のベクトルを「クエリ」ベクトルに変換するために使用されます。この例では、「appreciation」という単語に注目したい場合、「appreciation」のベクトルに WQ を乗算してクエリ ベクトルを取得します。
  2. WK (重み行列 K): この行列は、各単語のベクトルを「キー」ベクトルに変換するために使用されます。同様に、「I」や「Li Honzhang」などの各単語のベクトルに WK を乗算して、キー ベクトルを取得します。
  3. WV (重み行列 V): この行列は、各単語のベクトルを「値」ベクトルに変換するために使用されます。各単語ベクトルに WV を乗算すると、値ベクトルが得られます。これら 3 つの行列 (WQ、WK、WV) は、ヘッダーごとに異なるクエリ、キー、および値のベクトルを生成するために使用されます。こうすることで、各頭が文章の異なる側面に集中できるようになります。
  4. WQ (重み行列 Q)、WK (重み行列 K)、WV (重み行列 V)、および WO (重み行列 O) これらの行列は、Transformer モデルのパラメーターであり、モデルのトレーニング中にバックプロパゲーション アルゴリズムを介して渡されます。勾配降下法などの最適化手法から学習されます。

プロセス全体で、WQ、WK、WV、WO は、モデルが入力単語ベクトルをさまざまな表現に変換する方法と、これらの表現を組み合わせて最終出力を取得する方法を決定します。これらの行列は、Transformer モデルのアテンション メカニズムの中核部分であり、モデルが文内の異なる単語間の関係を捉えることができるようになります。

WQ (重み行列 Q)、WK (重み行列 K)、WV (重み行列 V)、および WO (重み行列 O) これらの行列は、Transformer モデルのパラメーターであり、モデルのトレーニング プロセス中にバックプロパゲーションによって渡されます。アルゴリズムや勾配降下法などの最適化手法から学習します。

この学習プロセスがどのように機能するかを見てみましょう:

  1. 初始化:在训练开始之前,这些矩阵通常会被随机初始化。这意味着它们的初始值是随机选取的,这样可以打破对称性并开始学习过程。
  2. 前向传播:在模型的训练过程中,输入数据(如句子“我欣赏李鸿章”)会通过模型的各个层进行前向传播。在注意力机制中,输入的词向量会与WQ、WK、WV矩阵相乘,以生成查询、键和值向量。
  3. 计算损失:模型的输出会与期望的输出(通常是训练数据中的标签)进行比较,计算出一个损失值。这个损失值衡量了模型的预测与实际情况的差距。
  4. 反向传播:损失值会通过反向传播算法传回模型,计算每个参数(包括WQ、WK、WV和WO)对损失的影响,即它们的梯度。
  5. 参数更新:根据计算出的梯度,使用梯度下降或其他优化算法来更新这些矩阵的值。这个过程会逐渐减小损失值,使模型的预测更加准确。
  6. 迭代过程:这个前向传播、损失计算、反向传播和参数更新的过程会在训练数据上多次迭代进行,直到模型的性能达到一定的标准或者不再显著提升。

    通过这个训练过程,WQ、WK、WV和WO这些矩阵会逐渐调整它们的值,以便模型能够更好地理解和处理输入数据。在训练完成后,这些矩阵将固定下来,用于模型的推理阶段,即对新的输入数据进行预测。

四、展开查询向量

在本小节中,我们将从多个注意力头中展开查询向量,得到的形状是 [32x128x4096] 这里,32 是 llama3 中注意力头的数量,128 是查询向量的大小,而 4096 是令牌嵌入的大小。

q_layer0 = model["layers.0.attention.wq.weight"]head_dim = q_layer0.shape[0] // n_headsq_layer0 = q_layer0.view(n_heads, head_dim, dim)q_layer0.shape

手撕Llama3第1层: 从零开始实现llama3图片

这段代码通过对模型中第一层的查询(Q)权重矩阵进行重塑(reshape),将其分解为多个注意力头的形式,从而揭示了32和128这两个维度。

  1. q_layer0 = model["layers.0.attention.wq.weight"]:这行代码从模型中提取第一层的查询(Q)权重矩阵。
  2. head_dim = q_layer0.shape[0] // n_heads:这行代码计算每个注意力头的维度大小。它通过将查询权重矩阵的第一个维度(原本是4096)除以注意力头的数量(n_heads),得到每个头的维度。如果n_heads是32(即模型设计为有32个注意力头),那么head_dim就是4096 // 32 = 128。
  3. q_layer0 = q_layer0.view(n_heads, head_dim, dim):这行代码使用.view()方法重塑查询权重矩阵,使其形状变为[n_heads, head_dim, dim]。这里dim很可能是原始特征维度4096,n_heads是32,head_dim是128,因此重塑后的形状是[32, 128, 4096]。
  4. q_layer0.shape 输出:torch.Size([32, 128, 4096]):这行代码打印重塑后的查询权重矩阵的形状,确认了其形状为[32, 128, 4096]。

之所以在这段代码中出现了32和128这两个维度,而在之前的代码段中没有,是因为这段代码通过重塑操作明确地将查询权重矩阵分解为多个注意力头,每个头具有自己的维度。32代表了模型中注意力头的数量,而128代表了分配给每个头的特征维度大小。这种分解是为了实现多头注意力机制,其中每个头可以独立地关注输入的不同部分,最终通过组合这些头的输出来提高模型的表达能力。 

实现第一层的第一个头

访问了第一层第一个头的查询(query)权重矩阵,这个查询权重矩阵的大小是 [128x4096]。

q_layer0_head0 = q_layer0[0]q_layer0_head0.shape

手撕Llama3第1层: 从零开始实现llama3图片

我们现在将查询权重与令牌嵌入相乘,以获得令牌的查询

在这里,你可以看到结果形状是 [17x128],这是因为我们有17个令牌,每个令牌都有一个长度为128的查询(每个令牌在一个头上方的查询)。

br

手撕Llama3第1层: 从零开始实现llama3画像

このコードは、トークンの埋め込み (token_embeddings) と最初の層の最初のヘッドのクエリ重み行列 (q_layer0_head0) の転置 (.T) を比較する行列乗算演算を実行します。乗算して per を生成します。 -トークン クエリ ベクトル (q_per_token)。

  1. q_per_token = torch.matmul(token_embeddings, q_layer0_head0.T):

torch.matmul は、2 つのテンソルの乗算を処理できる PyTorch の行列乗算関数です。

token_embeddings は、形状 [17, 4096] のテンソルである必要があり、17 個のトークンを表し、それぞれが 4096 次元の埋め込みベクトルで表されます。

q_layer0_head0 は、最初のレイヤーの最初のヘッドのクエリ重み行列であり、その元の形状は [128, 4096] です。 .T は PyTorch の転置操作で、q_layer0_head0 の形状を [4096, 128] に転置します。

このように、token_embeddings と q_layer0_head0.T の行列乗算は [17, 4096] と [4096, 128] の乗算となり、結果は形状 [17, 128] のテンソルになります。

  1. q_per_token.shape と出力: torch.Size([17, 128]):

このコード行は、q_per_token テンソルの形状を出力し、それが [17, 128] であることを確認します。

これは、入力されたすべてのトークン (合計 17) に対して、128 次元のクエリ ベクトルが得られることを意味します。この 128 次元のクエリ ベクトルは、トークンの埋め込みとクエリ重み行列を乗算することによって取得され、後続のアテンション メカニズムの計算に使用できます。

要約すると、このコードは、行列の乗算を通じて各トークンの埋め込みベクトルをクエリ ベクトルに変換し、アテンション メカニズムを実装する次のステップの準備をします。各トークンにはそれに対応するクエリ ベクトルがあり、これらのクエリ ベクトルは他のトークンで注意スコアを計算するために使用されます。

以上がLlama3 レイヤー 1 を手動でティアリングする: llama3 を最初から実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。