Họ LLaMA: RoPE, huấn luyện song song
This content is not available in your language yet.
Mục tiêu
Phần tiêu đề “Mục tiêu”Sau bài này, bạn sẽ:
- Kể được bốn chỗ họ Llama khác GPT của bài trước, và lý do: RMSNorm, SwiGLU, RoPE (rotary position embedding, mã hóa vị trí xoay) có từ LLaMA 1, GQA (grouped-query attention, attention truy vấn theo nhóm) thêm từ Llama 2.
- Tự dẫn được vì sao với RoPE, điểm attention giữa vị trí và chỉ phụ thuộc vị trí qua độ lệch , và kiểm lại bằng NumPy.
- Tính được cỡ KV cache và hiểu vì sao GQA làm nó nhỏ đi.
- Phân biệt được song song dữ liệu (data parallelism), song song tensor (tensor parallelism), song song pipeline (pipeline parallelism) và ZeRO/FSDP.
Cần biết trước
Phần tiêu đề “Cần biết trước”- Building Your First LLM, in Large Language Models(Not translated yet) (Learned)
Trực giác
Phần tiêu đề “Trực giác”Cùng bộ khung, thay vài linh kiện
Phần tiêu đề “Cùng bộ khung, thay vài linh kiện”Năm 2023, Meta công bố LLaMA (Touvron và cộng sự, 2023). Theo bài báo, bản 13 tỉ tham số vượt GPT-3 (175 tỉ) trên phần lớn các benchmark được thử, dù nhỏ hơn trên 10 lần, và chạy được trên một GPU.
Bí quyết không nằm ở một kiến trúc mới. LLaMA vẫn là GPT của bài trước, chỉ thay ba linh kiện bằng những loại đã được thử tốt ở các bài báo khác (từ Llama 2 thêm linh kiện thứ tư là GQA), rồi cho mô hình học lâu hơn trên nhiều dữ liệu hơn mức thường dùng. Lập luận của nhóm tác giả: chi phí mỗi lần dùng tính theo cỡ mô hình, nên một mô hình nhỏ học lâu thì lợi về lâu dài.
| Linh kiện | GPT ở bài trước | Họ Llama |
|---|---|---|
| Chuẩn hóa | Chuẩn hóa lớp (LayerNorm) | RMSNorm: bỏ bước trừ trung bình |
| Mạng truyền thẳng | GELU, 2 ma trận, chiều ẩn | SwiGLU, 3 ma trận, chiều ẩn khoảng |
| Vị trí | Vectơ vị trí cộng vào embedding ở đầu vào | RoPE: xoay và trong attention, ở mọi lớp |
| Attention | Mỗi đầu một bộ K, V | GQA (từ Llama 2): vài đầu truy vấn dùng chung một bộ K, V |
Bài báo đầu viết “LLaMA”, các thế hệ sau viết “Llama”.
Vị trí: nhãn cố định hay độ lệch?
Phần tiêu đề “Vị trí: nhãn cố định hay độ lệch?”Trong câu “con mèo đuổi con chuột”, “mèo” đứng trước “đuổi” một token, dù câu nằm ở đầu văn bản hay sau 1 000 token khác. Điều quan trọng là độ lệch giữa hai vị trí. Nhưng GPT ở bài trước cộng vào mỗi vị trí một vectơ riêng, nên cặp vị trí (3, 2) và cặp (1003, 1002) là hai đầu vào khác hẳn nhau, và mô hình phải tự học rằng chúng “giống nhau”.
RoPE đưa điều đó vào ngay phép tính. Hình dung mỗi cặp chiều của truy vấn và khóa là một mặt đồng hồ. Token ở vị trí có kim quay nấc, nên hai token cách nhau 2 vị trí luôn lệch nhau đúng 2 nấc, ở đâu cũng vậy. Mỗi cặp chiều quay một tốc độ, như kim giây, kim phút, kim giờ: kim nhanh phân biệt các token gần nhau, kim chậm phân biệt các token xa nhau.
Một GPU không đủ
Phần tiêu đề “Một GPU không đủ”Bản 65 tỉ tham số của LLaMA được huấn luyện trên 2 048 GPU A100 80 GB, khoảng 380 token mỗi giây trên mỗi GPU, và 1.4 nghìn tỉ token mất khoảng 21 ngày. Khi huấn luyện, mỗi tham số cần khoảng 16 byte (mục Lý thuyết sẽ đếm), tức khoảng 1 040 GB cho 65 tỉ tham số (tự tính): bằng bộ nhớ của khoảng 13 GPU 80 GB, chưa kể kích hoạt (activation: giá trị trung gian của các lớp, phải giữ lại cho lan truyền ngược).
Phải chia việc, như một bếp nấu 100 suất ăn: mở nhiều bếp giống nhau; cho hai đầu bếp cùng làm một món; hoặc làm dây chuyền sơ chế, nấu, bày đĩa. Đó là ba kiểu song song của bài.
Bài toán của bài này là: LLaMA thay những gì trong GPT và vì sao, nhất là cách RoPE làm phần vị trí trong điểm attention chỉ phụ thuộc độ lệch ? Và khi mô hình lớn hơn bộ nhớ của một GPU, có những cách nào để chia việc huấn luyện ra nhiều GPU?
Lý thuyết
Phần tiêu đề “Lý thuyết”Khối Transformer của LLaMA
Phần tiêu đề “Khối Transformer của LLaMA”Một khối của LLaMA làm hai bước, mỗi bước có kết nối tắt như GPT:
Chuẩn hóa đặt trước mỗi khối con (pre-norm) như GPT-2; bài báo LLaMA nói chọn vậy để huấn luyện ổn định hơn. Đầu vào là embedding của token, không cộng vectơ vị trí.
RMSNorm: chỉ chia, không trừ
Phần tiêu đề “RMSNorm: chỉ chia, không trừ”Chuẩn hóa lớp trừ trung bình các thành phần của , chia cho độ lệch chuẩn, rồi co giãn và dịch bằng hai vectơ học được. Zhang và Sennrich (2019) đặt giả thuyết: phần có ích là việc chia, còn việc trừ trung bình thì không cần. Bỏ phép trừ trung bình và cả vectơ dịch, ta có RMSNorm:
là căn của trung bình bình phương (root mean square), là số rất nhỏ để không chia cho 0, là hệ số co giãn học được.
Vì sao vẫn ổn định? Thay bằng với hằng , bỏ qua :
Đẳng thức đầu đưa ra ngoài tổng, đẳng thức sau dùng vì . Vậy : đầu vào to hay nhỏ bao nhiêu lần, đầu ra vẫn vậy. Trước khi nhân , vectơ ra luôn có RMS bằng 1. Ít phép tính hơn nên nhanh hơn.
SwiGLU: thêm một cái cổng
Phần tiêu đề “SwiGLU: thêm một cái cổng”Mạng truyền thẳng (feed-forward network, FFN; chính là khối MLP ở bài trước) của GPT có hai ma trận, và : , với là vectơ hàng. SwiGLU (Shazeer, 2020) thêm một nhánh và nhân hai nhánh từng phần tử ():
Ở đây , , là hàm sigmoid. Nhánh qua Swish là một cái cổng (gate): ở mỗi thành phần, nó quyết định nhánh tuyến tính được đi qua nhiều hay ít.
Vì sao chiều ẩn là ? Ba ma trận thì nhiều tham số hơn hai. Muốn giữ số tham số như cũ, cho hai bên bằng nhau:
Llama 3 không theo đúng quy tắc này: bản 8B có và chiều ẩn 14 336, khoảng (tự tính). Trong thí nghiệm của Shazeer, SwiGLU thuộc nhóm cho perplexity tốt nhất; tác giả nói thẳng là không giải thích được vì sao, chỉ ghi nhận kết quả.
RoPE: xoay thay vì cộng
Phần tiêu đề “RoPE: xoay thay vì cộng”Cách cũ. GPT cộng vectơ vị trí vào embedding trước khi tính truy vấn và khóa. Khai triển tích vô hướng, có những số hạng trộn nội dung với riêng vị trí hay riêng , nên điểm phụ thuộc cả vị trí tuyệt đối (ô NumPy ở mục Ví dụ code cho thấy bằng số).
Ý của RoPE (Su và cộng sự, 2021). Embedding không mang vị trí. Tính , từ nội dung như thường, rồi xoay một góc tỉ lệ với vị trí của nó, xoay một góc tỉ lệ với vị trí của nó. Trong bài này , là vị trí token (theo RoFormer), không phải số mẫu và số đặc trưng như quy ước chung của site.
Trường hợp hai chiều. Ma trận xoay một góc là:
Nó quay vectơ hai chiều ngược chiều kim đồng hồ một góc , độ dài giữ nguyên. Ta cần hai tính chất:
- . Chuyển vị đổi chỗ hai ô . Vì và , ma trận thu được đúng là : phép xoay ngược lại.
- . Xoay rồi xoay tiếp bằng xoay một lần . Ô trên trái của tích là , theo công thức cộng góc; ba ô còn lại tương tự.
Gọi là góc xoay cho mỗi vị trí. Truy vấn ở vị trí thành , khóa ở vị trí thành . Điểm của cặp này là:
Dòng đầu dùng quy tắc chuyển vị của tích, . Dòng hai dùng tính chất 1. Dòng ba dùng tính chất 2 với , .
Kết quả phụ thuộc nội dung (, ) và chỉ phụ thuộc vị trí qua . Dịch cả hai vị trí thêm cùng một đoạn thì , điểm không đổi. Thứ tự vẫn có nghĩa: cặp cho , cặp cho , hai điểm thường khác nhau.
Trường hợp chiều. Mỗi đầu attention có chiều, chẵn. Ghép các chiều thành cặp liền kề ; cặp thứ xoay một góc với:
Tích vô hướng là tổng điểm của cặp. Theo phép dẫn trên, điểm của mỗi cặp chỉ phụ thuộc vị trí qua , nên tổng cũng vậy.
Đây là các kim đồng hồ. Cặp đầu có radian mỗi token, quay một vòng sau khoảng 6 token (tự tính). Với , cỡ một đầu của Llama 3 8B (), cặp cuối có , quay một vòng sau khoảng 54 nghìn token (tự tính).
Cài đặt. Không cần dựng ma trận. Với mỗi cặp và góc :
Python đếm chỉ số từ 0, nên cặp là x[0::2] và x[1::2]. Một số thư viện ghép chiều với chiều (nửa đầu với nửa sau): chỉ là xếp lại thứ tự các chiều, cùng một ý.
RoPE áp lên và ở mọi lớp, không áp lên : điểm attention là tích nên mới cần độ lệch vị trí, còn chỉ là nội dung được lấy ra. Bài báo RoFormer còn đưa ra một cận trên của độ lớn điểm và cho thấy nó giảm dần khi tăng. Đó chỉ là cận, không bảo đảm cho từng cặp.
GQA: dùng chung khóa và giá trị
Phần tiêu đề “GQA: dùng chung khóa và giá trị”KV cache. Khi sinh văn bản, mỗi bước cần khóa và giá trị của mọi token trước đó, ở mọi lớp. Nhờ mặt nạ nhân quả, chúng không đổi khi có token mới, nên ta tính một lần rồi lưu lại: đó là KV cache. Cỡ của nó cho mỗi token:
Hệ số 2 vì có cả K lẫn V; là số lớp, là số đầu K/V, là số chiều mỗi đầu, là số byte mỗi số. Llama 3 8B có , , (tự tính), lưu ở BF16 nên : mỗi token cần byte (128 KiB), một chuỗi 8 192 token cần đúng 1 GiB (tự tính). Nếu mỗi đầu truy vấn có bộ K, V riêng () thì là 4 GiB, và còn nhân với số chuỗi đang phục vụ.
Ba cách chia đầu. Attention nhiều đầu (multi-head attention, MHA) có . Multi-query attention (MQA; Shazeer, 2019) dùng một bộ K, V cho mọi đầu: cache nhỏ nhất, nhưng có thể giảm chất lượng. GQA (Ainslie và cộng sự, 2023) nằm giữa: chia đầu truy vấn thành nhóm, mỗi nhóm chung một bộ K, V. Llama 3 8B có 32 đầu truy vấn, 8 đầu K/V, nên cứ 4 đầu truy vấn chung một bộ. Bài báo GQA báo cáo chất lượng gần MHA, tốc độ gần MQA. Số đầu truy vấn giữ nguyên, và tham số giảm không nhiều: chỉ hai ma trận chiếu K, V nhỏ đi.
Ba thế hệ Llama
Phần tiêu đề “Ba thế hệ Llama”Bảng chỉ ghi số có trong ba bài báo. Số của Llama 3 là của đợt Llama 3.1, như bài báo ghi.
| Thông số | Llama 1 (2023) | Llama 2 (2023) | Llama 3 (2024) |
|---|---|---|---|
| Cỡ (tham số) | 6.7B, 13.0B, 32.5B, 65.2B (gọi tắt 7B, 13B, 33B, 65B) | 7B, 13B, 70B (bản 34B không phát hành) | 8B, 70B, 405B |
| Token huấn luyện | 1.0T (hai cỡ nhỏ), 1.4T (hai cỡ lớn) | 2.0T | 15.6T (bản 405B) |
| Ngữ cảnh | 2 048 (theo bài báo Llama 2) | 4 096 | 8K khi pre-train, mở rộng tới 128K |
| Vị trí | RoPE ở mọi lớp | RoPE | RoPE, cơ số 500 000 |
| Attention | Nhiều đầu | GQA ở 34B, 70B | GQA, 8 đầu K/V ở mọi cỡ |
Cả ba dùng RMSNorm và SwiGLU. Bài báo Llama 3 tự nhận kiến trúc “không khác nhiều” so với trước; tiến bộ đến chủ yếu từ chất lượng, độ đa dạng của dữ liệu và quy mô huấn luyện. Số token tăng khoảng 7.8 lần so với Llama 2 (tự tính). Dữ liệu là chủ đề của bài Chuẩn bị dữ liệu pre-train.
Trọng số Llama 2 và Llama 3 đi kèm giấy phép riêng của Meta, không phải giấy phép mã nguồn mở thông dụng như MIT hay Apache 2.0: đọc giấy phép gốc trước khi dùng.
Huấn luyện song song
Phần tiêu đề “Huấn luyện song song”Bộ nhớ đi đâu? Khi huấn luyện bằng Adam với độ chính xác hỗn hợp (mixed precision), bài báo ZeRO (Rajbhandari và cộng sự, 2020) đếm cho mỗi tham số: 2 byte cho tham số FP16, 2 byte cho gradient, 12 byte cho trạng thái của bộ tối ưu (optimizer state: bản sao FP32 của tham số, momentum, phương sai). Tổng 16 byte. Mô hình tham số cần byte, chưa kể kích hoạt; với tỉ, đó là 120 GB.
Song song dữ liệu. Mỗi GPU giữ cả mô hình, xử lý một phần khác nhau của lô. Sau lượt lan truyền ngược, các GPU cộng gộp gradient (phép all-reduce) nên cùng cập nhật giống hệt nhau. Nhanh hơn, nhưng không giảm bộ nhớ trên mỗi GPU.
Song song tensor. Megatron-LM (Shoeybi và cộng sự, 2019) chia ma trận trong một lớp. Lấy khối MLP (tức FFN) trên hai GPU:
- Chia theo cột, , nên : mỗi GPU tính một nửa số cột.
- GELU tác động từng phần tử, nên với . Chưa cần trao đổi gì.
- Chia theo hàng thành (nửa trên) và (nửa dưới). Theo quy tắc nhân ma trận khối, . Mỗi GPU tính một số hạng, rồi một phép all-reduce cộng lại.
Với SwiGLU, chia và theo cùng cách chia cột, theo hàng. Với attention, mỗi GPU lo một số đầu. Lớp nào cũng phải trao đổi, nên cần đường truyền rất nhanh, thường là các GPU trong cùng một máy.
Song song pipeline. Chia mô hình theo chiều sâu: GPU 1 giữ các lớp đầu, GPU 2 các lớp tiếp theo… Mỗi phần là một giai đoạn (stage). Đưa cả lô vào một lần thì GPU 2 phải chờ GPU 1, nên GPipe (Huang và cộng sự, 2019) cắt lô thành micro-batch nối đuôi nhau: giai đoạn 1 chuyển micro-batch 1 đi rồi làm ngay micro-batch 2.
Vẫn còn bong bóng (pipeline bubble): lúc đầu các giai đoạn sau chờ, lúc cuối các giai đoạn đầu chờ. Đếm theo lượt, mỗi lượt là lúc một giai đoạn xử lý một micro-batch, với giai đoạn. Micro-batch cuối vào giai đoạn 1 ở lượt , tới giai đoạn ở lượt . Vậy chiều xuôi kéo dài lượt, mỗi giai đoạn bận lượt, rảnh lượt:
Chiều ngược cũng vậy. Với : cho , cho (tự tính). GPipe ghi bong bóng không đáng kể khi . Giữa hai giai đoạn chỉ chuyền kích hoạt, nên pipeline chịu được đường truyền chậm hơn tensor.
ZeRO và FSDP. Song song dữ liệu lãng phí: GPU giữ bản giống hệt nhau. ZeRO chia chúng ra theo ba mức, gọi là stage 1, 2, 3. Ví dụ trong bài báo, tỉ, :
| Cách chia | Byte mỗi GPU | Ví dụ |
|---|---|---|
| Song song dữ liệu thường | 120 GB | |
| ZeRO stage 1: chia trạng thái bộ tối ưu | 31.4 GB | |
| ZeRO stage 2: chia thêm gradient | 16.6 GB | |
| ZeRO stage 3: chia thêm tham số | 1.9 GB |
Ở stage 3, GPU cần tham số của lớp nào thì gom các mảnh từ GPU khác, dùng xong thì bỏ; theo bài báo, lưu lượng trao đổi gấp khoảng 1.5 lần song song dữ liệu thường. Mỗi GPU vẫn xử lý dữ liệu riêng, nên đây vẫn là song song dữ liệu. FSDP (Fully Sharded Data Parallel) là cách PyTorch cài đặt ý này.
Ghép lại: Llama 3. Bản 405B dùng bốn kiểu cùng lúc: tensor, context (chia chuỗi đầu vào thành nhiều đoạn, cho ngữ cảnh rất dài), pipeline và dữ liệu (FSDP), lồng từ trong ra ngoài theo thứ tự đó. Tensor nằm trong cùng, trên các GPU có đường truyền nhanh nhất; FSDP ở ngoài cùng vì chịu được độ trễ lớn nhất.
Giới hạn
Phần tiêu đề “Giới hạn”Song song chỉ ở mức khái niệm, và hai ô code chỉ kiểm tính chất toán trên vectơ nhỏ. Chạy mô hình lớn trên nhiều GPU khi phục vụ người dùng thuộc bài Deploy LLM: nhiều GPU, phân tán; mixture of experts thuộc bài Họ DeepSeek.
Minh họa
Phần tiêu đề “Minh họa”Hình dưới đặt ba kiểu song song cạnh nhau, trên cùng một mô hình 4 lớp và 2 GPU. Hãy nhìn phần tô đậm ở mỗi hàng: thứ bị chia là lô dữ liệu, ma trận trong một lớp, hay các lớp.
Ví dụ code
Phần tiêu đề “Ví dụ code”Code trong bài chạy thử lần cuối tháng 10/2026: ô RoPE với Python 3.14 và NumPy 2.4 (bản có trong Pyodide), ô PyTorch với Python 3.14 và PyTorch 2.14 trên CPU. Ô RoPE chạy ngay trên trang, ô PyTorch chạy trên máy bạn.
RoPE bằng NumPy
Phần tiêu đề “RoPE bằng NumPy”Ô dưới đây cài RoPE với (4 cặp), lấy ngẫu nhiên , (hạt giống 0), rồi tính điểm ở nhiều cặp vị trí theo hai cách: xoay bằng RoPE (cột rotate) và cộng vectơ vị trí sin/cos (cột add). Ô add cộng thẳng vectơ vị trí vào , cho gọn; Transformer gốc cộng vào embedding, nhưng ý vẫn vậy.
RoPE: điểm chỉ phụ thuộc độ lệch vị trí
# RoPE: xoay từng cặp chiều liền kề, rồi so với cách cộng vectơ vị tríimport numpy as np
def angles(d, base=10000.0): return base ** (-np.arange(0, d, 2) / d) # d/2 tần số theta_i
def rope(x, pos): # xoay cặp (x[2i], x[2i+1]) một góc pos * theta_i cos, sin = np.cos(pos * angles(x.size)), np.sin(pos * angles(x.size)) out = np.empty_like(x) out[0::2] = x[0::2] * cos - x[1::2] * sin out[1::2] = x[0::2] * sin + x[1::2] * cos return out
def add_pos(x, pos): # cách của Transformer gốc: cộng vectơ sin/cos của vị trí vào x pe = np.empty_like(x) pe[0::2] = np.sin(pos * angles(x.size)) pe[1::2] = np.cos(pos * angles(x.size)) return x + pe
rng = np.random.default_rng(0)q, k = rng.normal(size=8), rng.normal(size=8) # d = 8, tức 4 cặpprint(f"no position: q.k = {q @ k:.6f}")print(" m n m-n rotate add")for m, n in [(5, 2), (105, 102), (1005, 1002), (5, 3), (2, 5)]: s_rot = rope(q, m) @ rope(k, n) # RoPE s_add = add_pos(q, m) @ add_pos(k, n) # vị trí tuyệt đối, cộng vào print(f"{m:4d} {n:5d} {m - n:4d} {s_rot:10.6f} {s_add:10.6f}")# xoay không đổi độ dài vectơprint(f"|q| = {np.linalg.norm(q):.6f}")print(f"|rope(q, 1000)| = {np.linalg.norm(rope(q, 1000)):.6f}")The first run downloads Python, about 10 MB.
Đọc kết quả:
- Không có vị trí: .
- Cột
rotate: ba dòng đầu cùng ở vị trí 5, 105 và 1005 đều cho . Độ lệch 2 cho . Cặp có độ lệch cho : thứ tự trước sau vẫn có nghĩa. - Cột
add: cùng ba cặp có mà điểm nhảy giữa , và : điểm phụ thuộc cả vị trí tuyệt đối. - Hai dòng cuối: độ dài của trước và sau khi xoay đều là .
“Bằng nhau” ở đây là bằng tới 6 chữ số được in ra; khi so số thực trong code, dùng np.isclose thay vì ==.
RMSNorm, SwiGLU và RoPE bằng PyTorch
Phần tiêu đề “RMSNorm, SwiGLU và RoPE bằng PyTorch”File dưới đây viết ba linh kiện bằng PyTorch rồi kiểm: RMSNorm đưa RMS về 1 và bỏ qua việc nhân đầu vào với 3; SwiGLU có đúng số tham số của FFN thường; và với cùng một , một đặt ở 5 vị trí, ma trận điểm (hàng , cột ) có mỗi đường chéo cùng một số. Hàm rope nhận tensor dạng (batch, L, số đầu, d) như truy vấn và khóa trong attention. Cách cài PyTorch bản CPU có ở bài Xây dựng LLM đầu tiên.
Run on your machine
Ba linh kiện của khối LLaMA
- Install
pip install torch- Hardware
- CPU là đủ. Dưới 10 giây trên máy phát triển, phần lớn là thời gian nạp PyTorch
# Ba mảnh của khối LLaMA bằng PyTorch: RMSNorm, SwiGLU, RoPEimport torchimport torch.nn as nn
torch.manual_seed(0)
class RMSNorm(nn.Module): def __init__(self, dim, eps=1e-6): super().__init__() self.eps = eps self.g = nn.Parameter(torch.ones(dim)) # hệ số co giãn học được
def forward(self, x): # không trừ trung bình, chỉ chia cho RMS rms = (x.pow(2).mean(-1, keepdim=True) + self.eps).sqrt() return x / rms * self.g
class SwiGLU(nn.Module): def __init__(self, dim, hidden): super().__init__() self.w1 = nn.Linear(dim, hidden, bias=False) # cổng, qua Swish (silu) self.w3 = nn.Linear(dim, hidden, bias=False) # nhánh tuyến tính self.w2 = nn.Linear(hidden, dim, bias=False) # chiếu về dim
def forward(self, x): return self.w2(nn.functional.silu(self.w1(x)) * self.w3(x))
def rope(x, base=10000.0): # x: (batch, L, số đầu, d); vị trí là chỉ số 0..L-1 theo trục 1 _, L, _, d = x.shape theta = base ** (-torch.arange(0, d, 2) / d) # (d/2,) ang = torch.arange(L)[:, None] * theta # (L, d/2) cos, sin = ang.cos()[:, None, :], ang.sin()[:, None, :] x1, x2 = x[..., 0::2], x[..., 1::2] # cặp liền kề out = torch.empty_like(x) out[..., 0::2] = x1 * cos - x2 * sin out[..., 1::2] = x1 * sin + x2 * cos return out
D = 48x = torch.randn(2, 5, D) * 7 # 2 chuỗi, L = 5 tokennorm = RMSNorm(D)rms = norm(x).pow(2).mean(-1).sqrt()[0].tolist()print("RMS after norm:", [round(r, 4) for r in rms])print("norm(3x) == norm(x):", torch.allclose(norm(3 * x), norm(x)))mlp = nn.Sequential(nn.Linear(D, 4 * D, bias=False), nn.GELU(), nn.Linear(4 * D, D, bias=False)) # FFN hai ma trậnffn = sum(p.numel() for p in mlp.parameters())glu = sum(p.numel() for p in SwiGLU(D, 2 * 4 * D // 3).parameters())print(f"params: FFN 4D = {ffn}, SwiGLU 2/3*4D = {glu}")# cùng một q và một k ở mọi vị trí: điểm (m, n) chỉ phụ thuộc m - nq = torch.randn(1, 1, 1, 8).expand(1, 5, 1, 8)k = torch.randn(1, 1, 1, 8).expand(1, 5, 1, 8)scores = rope(q)[0, :, 0] @ rope(k)[0, :, 0].T # hàng m, cột nfor row in scores.tolist(): print(" ".join(f"{v:6.2f}" for v in row))Sample output
From one real run.
RMS after norm: [1.0, 1.0, 1.0, 1.0, 1.0]norm(3x) == norm(x): Trueparams: FFN 4D = 18432, SwiGLU 2/3*4D = 18432 0.82 0.87 -0.13 -1.17 -1.19 -0.31 0.82 0.87 -0.13 -1.17 -1.54 -0.31 0.82 0.87 -0.13 -1.79 -1.54 -0.31 0.82 0.87 -0.85 -1.79 -1.54 -0.31 0.82Đọc kết quả:
- RMSNorm: đầu vào đã nhân 7, vậy mà RMS sau chuẩn hóa của cả 5 token đều là 1.0 ( lúc đầu toàn số 1). Nhân thêm 3 thì đầu ra không đổi (
True). - SwiGLU: với , FFN thường có tham số; SwiGLU có chiều ẩn 128 và tham số.
- RoPE: đường chéo chính () toàn là 0.82, chính là chưa xoay. Đường ngay dưới () toàn là , đường kế tiếp toàn là . Ma trận không đối xứng: ngay trên đường chéo () là 0.87. Với mặt nạ nhân quả, mô hình chỉ dùng nửa dưới ().
Lỗi hay gặp
Phần tiêu đề “Lỗi hay gặp”Kiểm tra nhanh
Phần tiêu đề “Kiểm tra nhanh”Tóm tắt và đọc thêm
Phần tiêu đề “Tóm tắt và đọc thêm”Ý chính:
- Họ Llama là GPT với bốn thay đổi: LLaMA 1 có RMSNorm, SwiGLU (chiều ẩn khoảng ) và RoPE; GQA thêm từ Llama 2. Từ Llama 1 tới Llama 3, kiến trúc gần như giữ nguyên; tiến bộ đến chủ yếu từ dữ liệu và quy mô.
- RoPE xoay cặp chiều thứ của , một góc . Vì , điểm chỉ phụ thuộc vị trí qua độ lệch.
- KV cache mỗi token là byte. GQA giảm mà giữ số đầu truy vấn: Llama 3 8B cần 1 GiB cho 8 192 token thay vì 4 GiB.
- Song song dữ liệu chia lô, tensor chia ma trận trong lớp (cần đường truyền nhanh), pipeline chia các lớp (bong bóng ), ZeRO/FSDP chia trạng thái của song song dữ liệu.
Đọc thêm:
- Su, J., Lu, Y., Pan, S., Murtadha, A., Wen, B., Liu, Y. (2021). RoFormer: Enhanced Transformer with Rotary Position Embedding, arXiv:2104.09864, mục 3.1 tới 3.4. Gốc của RoPE và phép dẫn tính chất tương đối.
- Touvron, H. và cộng sự (2023). LLaMA: Open and Efficient Foundation Language Models, arXiv:2302.13971, mục 2; và Touvron, H. và cộng sự (2023). Llama 2: Open Foundation and Fine-Tuned Chat Models, arXiv:2307.09288, mục 2 và phụ lục A.2.1. Mục 2.2 của LLaMA ngắn, đủ ba thay đổi kiến trúc; GQA nằm ở phụ lục A.2.1 của Llama 2.
- Grattafiori, A., Dubey, A., Jauhri, A. và cộng sự (2024). The Llama 3 Herd of Models, arXiv:2407.21783, mục 3.2 và 3.3. Kiến trúc và bốn kiểu song song.
- Zhang, B., Sennrich, R. (2019). Root Mean Square Layer Normalization, arXiv:1910.07467; Shazeer, N. (2020). GLU Variants Improve Transformer, arXiv:2002.05202; Ainslie, J. và cộng sự (2023). GQA: Training Generalized Multi-Query Transformer Models from Multi-Head Checkpoints, arXiv:2305.13245. Ba bài báo gốc của RMSNorm, SwiGLU và GQA.
- Shazeer, N. (2019). Fast Transformer Decoding: One Write-Head is All You Need, arXiv:1911.02150. Bài báo gốc của MQA.
- Shoeybi, M. và cộng sự (2019). Megatron-LM: Training Multi-Billion Parameter Language Models Using Model Parallelism, arXiv:1909.08053, mục 3; Huang, Y. và cộng sự (2019). GPipe: Easy Scaling with Micro-Batch Pipeline Parallelism, arXiv:1811.06965 (tên bản v5), mục 2; Rajbhandari, S., Rasley, J., Ruwase, O., He, Y. (2020). ZeRO: Memory Optimizations Toward Training Trillion Parameter Models, arXiv:1910.02054, mục 5. GPipe viết cho số giai đoạn, chỗ bài này viết .
Your browser is blocking storage. Your progress will be lost when you reload the page.
Content licensed under CC BY-NC-SA 4.0. Code under the MIT license.About