このシリーズの記事では、llama3 を最初から実装します。
Llama3 の全体的なアーキテクチャ:
写真
Llama3 のモデルパラメータ:
LlaMa 3 モデルにおけるこれらのパラメータの実際の値を見てみましょう。
写真
LlaMaクラスをインスタンス化する際、変数max_seq_lenはコンテキストウィンドウを定義します。クラスには他にもパラメータがありますが、このパラメータは変圧器モデルに最も直接関係しています。ここでの max_seq_len は 8K です。
Pictures
Transformer クラスは語彙とレイヤー数を定義するモデルです。ここでの語彙とは、モデルが認識して処理できる単語 (およびトークン) のセットを指します。アテンション レイヤーは、モデルで使用されるトランスフォーマー ブロック (アテンション レイヤーとフィードフォワード レイヤーの組み合わせ) を指します。
写真
これらの数字によると、LlaMa 3 の語彙数は 128K で、これは非常に多くなります。さらに、32個のトランスブロックを備えています。
[3] フィーチャー次元とアテンションヘッド
フィーチャー次元とアテンションヘッドがセルフアテンション モジュールに導入されます。特徴次元は、埋め込み空間内のトークンのベクトル サイズを指します (特徴次元は、入力データまたは埋め込みベクトルの次元サイズを指します)。一方、アテンションヘッドには、トランスフォーマーのセルフ アテンション メカニズムを駆動する QK モジュールが含まれます。
写真
隠れ次元とは、フィードフォワードニューラルネットワーク(Feed Forward)の隠れ層の次元サイズを指します。フィードフォワード ニューラル ネットワークには通常 1 つ以上の隠れ層が含まれており、これらの隠れ層の次元によってネットワークの容量と複雑さが決まります。 Transformer モデルでは、モデルの表現能力を高めるために、フィードフォワード ニューラル ネットワークの隠れ層の次元は通常、特徴次元の倍数になります。 LLama3 では、隠れ次元は特徴次元の 1.3 倍です。隠れ層と隠れ次元は 2 つの概念であることに注意してください。
隠れ層の数が多いほど、ネットワークはより豊富な表現を内部で作成および操作してから、より小さな出力次元に投影し直すことができます。
Picture
最初の行列は入力特徴行列で、これはアテンション レイヤーによって処理されてアテンション加重特徴を生成します。この画像では、入力特徴行列のサイズは 5 x 3 のみですが、実際の Llama 3 モデルでは 8K x 4096 まで大きくなります。
次はフィードフォワード ネットワークの隠れ層で、5325 まで成長し、最後の層で 4096 に戻ります。
写真
LlaMa 3 は上記の 32 個のトランスフォーマー ブロックを結合し、出力は最後のブロックに到達するまで 1 つのブロックから次のブロックに渡されます。
写真
上記のすべての部分を開始したら、それらを組み合わせて、LlaMa エフェクトがどのように作成されるかを確認します。
写真
ステップ 1: まず、サイズ 8K(コンテキスト ウィンドウ) x 128K(語彙サイズ) の入力行列を用意します。この行列は、この高次元行列を低次元行列に変換するための埋め込みプロセスを受けます。
ステップ 2: この場合、この低次元の結果は 4096 になります。これは、前に見た LlaMa モデルの特徴の指定された次元です。
ニューラル ネットワークでは、次元の強化と次元の削減は一般的な操作であり、それぞれに異なる目的と効果があります。
次元の強化は通常、より複雑な特徴やパターンを捕捉できるようにモデルの容量を増やすことです。入力データが高次元空間にマッピングされると、モデルによってさまざまな特徴の組み合わせをより簡単に区別できるようになります。これは、モデルがより複雑な決定境界を学習するのに役立つため、非線形問題を扱う場合に特に役立ちます。
次元削減は、モデルの複雑さと過学習のリスクを軽減することです。特徴空間の次元を減らすことにより、モデルはより洗練された一般化された特徴表現を学習するように強制できます。さらに、次元削減を正則化方法として使用して、モデルの汎化能力を向上させることができます。場合によっては、次元削減により計算コストが削減され、モデルの操作効率が向上することもあります。
実際のアプリケーションでは、次元を増やしてから次元を減らすという戦略は、特徴抽出と変換のプロセスとみなすことができます。このプロセスでは、モデルはまず次元を増やすことでデータの本質的な構造を調査し、次に次元を減らすことで最も有用な特徴とパターンを抽出します。この方法は、十分な複雑さを維持しながら、モデルがトレーニング データへの過剰適合を回避するのに役立ちます。
ステップ 3: この機能は、Transformer ブロックを通じて、最初にアテンション層によって、次に FFN 層によって処理されます。アテンション層はフィーチャ全体を水平方向に処理し、FFN 層はディメンション全体を垂直方向に処理します。
ステップ 4: Transformer ブロックの 32 レイヤーに対してステップ 3 を繰り返します。最後に、結果として得られる行列の次元は、特徴の次元に使用されるものと同じになります。
ステップ 5: 最後に、モデルが語彙内で使用可能な単語を選択してマッピングできるように、この行列は元の語彙行列サイズ (128K) に変換されて戻されます。
これが、LlaMa 3 がこれらのベンチマークで高いスコアを獲得し、LlaMa 3 効果を生み出す方法です。
いくつかの混同されやすい用語を簡単にまとめます:
これは、モデルが 1 回の処理で受け入れることができるトークンの最大数です。
LlaMa 3-8B モデルでは、このパラメーターは 8,000 トークン、つまりコンテキスト ウィンドウ サイズ = 8K に設定されています。これは、モデルが 1 回の処理で考慮できるトークンの最大数が 8,000 であることを意味します。これは、長いテキストを理解したり、長期の会話のコンテキストを維持したりするために重要です。
これは、モデルが認識できるすべての異なるトークンの数です。これには、考えられるすべての単語、句読点、特殊文字が含まれます。モデルの語彙は 128,000 で、Vocabulary-size = 128K として表されます。これは、モデルがさまざまな単語、句読点、特殊文字を含む 128,000 の異なるトークンを認識して処理できることを意味します。
Transformer モデルの主要コンポーネント。これは主に、入力データのどの部分が最も重要であるか (つまり、どのトークンが「出席」しているか) を学習することによって入力データを処理する役割を果たします。モデルにはそのようなレイヤーが複数ある場合があり、それぞれが異なる観点から入力データを理解しようとします。
LlaMa 3-8B モデルには 32 の処理レイヤーが含まれています。つまり、レイヤー数 = 32 です。これらの層には、複数のアテンション層と他のタイプのネットワーク層が含まれており、それぞれが異なる観点から入力データを処理して理解します。
複数の異なるレイヤーのモジュールが含まれており、通常は少なくとも 1 つのアテンション レイヤーとフィードフォワード ネットワークが含まれます。モデルには複数のトランスフォーマー ブロックを含めることができ、これらのブロックは順番に接続され、各ブロックの出力は次のブロックの入力になります。トランスフォーマーブロックはデコーダー層とも呼ばれます。
Transformer モデルのコンテキストでは、通常、モデルには「32 レイヤー」があると言いますが、これはモデルに「32 の Transformer ブロック」があると言うのと同じです。各 Transformer ブロックには通常、セルフ アテンション レイヤーとフィードフォワード ニューラル ネットワーク レイヤーが含まれており、これら 2 つのサブレイヤーが合わせて完全な処理ユニットまたは「レイヤー」を形成します。
つまり、モデルに 32 個の Transformer ブロックがあると言うとき、実際にはモデルが 32 個のそのような処理ユニットで構成されており、それぞれがデータのセルフアテンション処理とフィードフォワード ネットワーク処理が可能であることを説明しています。このプレゼンテーションでは、モデルの階層構造と各レベルでのその処理能力を強調します。
要約すると、Transformer モデルの構造を説明する場合、「32 レイヤー」と「32 Transformer ブロック」は基本的に同義であり、どちらもモデルに 32 の独立したデータ処理サイクルが含まれており、各サイクルには自己注意とフィードフォワード ネットワーク操作が含まれていることを意味します。
これは、入力トークンがモデル内でベクトルとして表現されるときの各ベクトルの次元です。
各トークンは、モデル内の 4096 個の特徴を含むベクトル、つまり、Feature-dimension = 4096 に変換されます。この高次元により、モデルはより豊富な意味情報と文脈上の関係をキャプチャできるようになります。
各アテンション レイヤーには複数のアテンション ヘッドがあり、各ヘッドは異なる視点から入力データを個別に分析します。
各アテンション レイヤーには 32 個の独立したアテンション ヘッドが含まれています。つまり、アテンション ヘッドの数 = 32 です。これらのヘッドは入力データをさまざまな側面から分析し、より包括的なデータ分析機能を共同で提供します。
これは通常、フィードフォワード ネットワークの層の幅、つまり各層のニューロンの数を指します。通常、非表示ディメンションはフィーチャ ディメンションよりも大きくなり、モデルが内部的により豊富なデータ表現を作成できるようになります。
フィードフォワード ネットワークでは、隠れ層の次元は 5325、つまり隠れ次元 = 5325 です。これは特徴の次元よりも大きいため、モデルは内部層間でより深い特徴の変換と学習を実行できます。
アテンション レイヤーとアテンション ヘッドの関係: 各アテンション レイヤーには複数のアテンション ヘッドを含めることができます。
数値関係: モデルには複数のトランスフォーマー ブロックが含まれる場合があり、各ブロックにはアテンション レイヤーと 1 つ以上の他のレイヤーが含まれます。各アテンション レイヤーには複数のアテンション ヘッドがある場合があります。このようにして、モデル全体がさまざまなレイヤーとヘッドで複雑なデータ処理を実行します。
Llama3 モデルの公式リンク スクリプトをダウンロードします: https://llama.meta.com/llama-downloads/
次のコードは、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!"))
画像
ロードされたモデルファイルに含まれる最初の 20 個のパラメータまたは重みの名前を表示します。
model = torch.load("Meta-Llama-3-8B/consolidated.00.pth")print(json.dumps(list(model.keys())[:20], indent=4))
Pictures
写真
全体として、この出力は、Transformer アーキテクチャに基づく深層学習モデルの主要なコンポーネントを明らかにします。このモデルは、テキスト分類、機械翻訳、質問応答システムなどの自然言語処理タスクで広く使用されています。アテンション メカニズム、フィードフォワード ネットワーク、正規化層など、各層の構造はほぼ同じであり、モデルが複雑な入力シーケンスの特徴を捉えるのに役立ちます。
Llama3 モデルのパラメーター構成を表示します:
with open("Meta-Llama-3-8B/params.json", "r") as f:config = json.load(f)config
图片
我们使用这个配置来推断模型的细节,比如:
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"])
图片
代码如下:
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。
图片
代码如下:
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
图片
我们接着使用 RMS 归一化对嵌入进行归一化,也就是图中这个位置:
图片
使用公式如下:
图片
代码如下:
# 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 是归一化时使用的权重。
函数的工作原理如下:
在进行归一化处理后,我们的数据形状仍然保持为 [17x4096],这与嵌入层的形状相同,只不过数据已经过归一化。
token_embeddings = rms_norm(token_embeddings_unnormalized, model["layers.0.attention_norm.weight"])token_embeddings.shape
图片
图片
接下来,我们介绍注意力机制的实现,也就是下图中的红框标注的位置:
图片
图片
计算 ( Q ) 和 ( K ) 的点积。
对点积结果进行缩放。
应用softmax函数得到注意力权重。
用注意力权重乘以值矩阵 ( V ) 得到输出矩阵 ( Z )。
这张图展示了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)
画像
クエリ重み行列 (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 を通じて最終出力を生成します。この出力は、次の処理層で、または最終結果の一部として使用されます。
図のコメントで言及されている形状の問題は、これらのベクトルをコンピューターで効率的に保存および処理する方法に関するものです。実際のコード実装では、効率を向上させるために、開発者は各ヘッダーを個別に処理するのではなく、複数のヘッダーのクエリ、キー、および値のベクトルをまとめてパッケージ化することがあります。これにより、最新のコンピューターの並列処理機能を利用して計算を高速化できます。
出力結果は次のことを示しています:
これらの重み行列の形状は、モデル設計者がアテンション メカニズムのさまざまな部分の寸法をどのように設定するかを反映しています。特に、キーと値の次元は、おそらく計算の複雑さとメモリ消費量を削減するために削減されますが、クエリと出力の次元を高く保つことは、より多くの情報を保持するためである可能性があります。この設計の選択は、特定のモデル アーキテクチャとアプリケーション シナリオによって異なります
この図の注意メカニズムを説明する実装プロセスを簡略化するために、例として「李紅章を賞賛します」という文を使用してみましょう。
クエリ (Q): これは情報を見つけたい部分です。
キー (K): これは情報が含まれる部分です。
値 (V): これは実際の情報コンテンツです。
図のコメントで言及されている形状の問題は、これらのベクトルをコンピューターで効率的に保存および処理する方法に関するものです。実際のコード実装では、効率を向上させるために、開発者は各ヘッダーを個別に処理するのではなく、複数のヘッダーのクエリ、キー、および値のベクトルをまとめてパッケージ化することがあります。これにより、最新のコンピューターの並列処理機能を利用して計算を高速化できます。
引き続き「李紅章に感謝します」という文を使用して、重み行列 WQ、WK、WV、WO の役割を説明します。
Transformer モデルでは、各単語は単語埋め込みによってベクトルに変換されます。これらのベクトルは一連の線形変換を経て、注意スコアが計算されます。これらの線形変換は、重み行列 WQ、WK、WV、および WO を通じて実装されます。
プロセス全体で、WQ、WK、WV、WO は、モデルが入力単語ベクトルをさまざまな表現に変換する方法と、これらの表現を組み合わせて最終出力を取得する方法を決定します。これらの行列は、Transformer モデルのアテンション メカニズムの中核部分であり、モデルが文内の異なる単語間の関係を捉えることができるようになります。
WQ (重み行列 Q)、WK (重み行列 K)、WV (重み行列 V)、および WO (重み行列 O) これらの行列は、Transformer モデルのパラメーターであり、モデルのトレーニング プロセス中にバックプロパゲーションによって渡されます。アルゴリズムや勾配降下法などの最適化手法から学習します。
この学習プロセスがどのように機能するかを見てみましょう:
在本小节中,我们将从多个注意力头中展开查询向量,得到的形状是 [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
图片
这段代码通过对模型中第一层的查询(Q)权重矩阵进行重塑(reshape),将其分解为多个注意力头的形式,从而揭示了32和128这两个维度。
之所以在这段代码中出现了32和128这两个维度,而在之前的代码段中没有,是因为这段代码通过重塑操作明确地将查询权重矩阵分解为多个注意力头,每个头具有自己的维度。32代表了模型中注意力头的数量,而128代表了分配给每个头的特征维度大小。这种分解是为了实现多头注意力机制,其中每个头可以独立地关注输入的不同部分,最终通过组合这些头的输出来提高模型的表达能力。
访问了第一层第一个头的查询(query)权重矩阵,这个查询权重矩阵的大小是 [128x4096]。
q_layer0_head0 = q_layer0[0]q_layer0_head0.shape
图片
在这里,你可以看到结果形状是 [17x128],这是因为我们有17个令牌,每个令牌都有一个长度为128的查询(每个令牌在一个头上方的查询)。
br
画像
このコードは、トークンの埋め込み (token_embeddings) と最初の層の最初のヘッドのクエリ重み行列 (q_layer0_head0) の転置 (.T) を比較する行列乗算演算を実行します。乗算して per を生成します。 -トークン クエリ ベクトル (q_per_token)。
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] のテンソルになります。
このコード行は、q_per_token テンソルの形状を出力し、それが [17, 128] であることを確認します。
これは、入力されたすべてのトークン (合計 17) に対して、128 次元のクエリ ベクトルが得られることを意味します。この 128 次元のクエリ ベクトルは、トークンの埋め込みとクエリ重み行列を乗算することによって取得され、後続のアテンション メカニズムの計算に使用できます。
要約すると、このコードは、行列の乗算を通じて各トークンの埋め込みベクトルをクエリ ベクトルに変換し、アテンション メカニズムを実装する次のステップの準備をします。各トークンにはそれに対応するクエリ ベクトルがあり、これらのクエリ ベクトルは他のトークンで注意スコアを計算するために使用されます。
以上がLlama3 レイヤー 1 を手動でティアリングする: llama3 を最初から実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。