Danh sách bài viết

Bài 9: Attention mechanism — ý tưởng cốt lõi

Attention là cơ chế cho phép model "tập trung" vào phần input liên quan thay vì xử lý đều mọi token. Bài này đi qua intuition, lịch sử từ Bahdanau 2014 đến Vaswani 2017, 3 thành phần Query / Key / Value, công thức Scaled Dot-Product Attention, lý do scale theo \( \sqrt{d_k} \), causal / padding mask, implementation PyTorch ngắn, computation / memory cost \( O(n^2 d) \), và preview các biến thể hiện đại (Flash Attention, MQA, GQA, Sliding Window).

25/05/2026
15 phút đọc
2 lượt xem
1

Mục tiêu bài học

Sau bài học, bạn sẽ:

  • Phát biểu được attention là gì và ý nghĩa của "trọng số importance".
  • Biết ba mốc lịch sử: Bahdanau 2014, Luong 2015, Vaswani 2017.
  • Giải thích được vai trò của ba ma trận Query, Key, Value và liên hệ với search engine.
  • Viết ra và hiểu công thức Scaled Dot-Product Attention từng bước.
  • Giải thích vì sao phải chia cho \( \sqrt{d_k} \).
  • Phân biệt causal mask và padding mask, viết được công thức causal mask.
  • Implement attention bằng PyTorch trong ~10 dòng và đọc hiểu code.
  • Ước lượng được cost \( O(n^2 d) \) và memory \( O(n^2) \), thấy được bottleneck với context dài.
  • Biết tên (chưa cần chi tiết) các tối ưu hiện đại: Flash Attention, MQA, GQA, Sliding Window.

Bài này là nền tảng cho cả Module 2. Bài 10 (Self-Attention) sẽ áp ngay công thức này vào trường hợp \( Q = K = V \) cùng từ một sequence; Bài 11 (Multi-Head) chạy nhiều attention song song; Bài 13 ráp tất cả lại thành Transformer block.

2

Attention là gì

Attention là cơ chế cho phép model gán cho mỗi phần của input một trọng số importance, rồi tính output như một tổ hợp tuyến tính có trọng số của các phần đó. Thay vì xử lý đều mọi token, model "tập trung" vào phần liên quan với truy vấn hiện tại.

Ở dạng toán học cô đọng, attention nhận một query và một tập (key, value), trả về:

\[ \text{output} = \sum_{i} \alpha_i \cdot v_i, \quad \text{với} \quad \alpha_i = \frac{\exp(\text{score}(q, k_i))}{\sum_{j} \exp(\text{score}(q, k_j))} \]

Trong đó \( \alpha_i \in [0, 1] \) là trọng số softmax giữa query \( q \) và từng key \( k_i \), còn \( v_i \) là value tương ứng. Tổng các \( \alpha_i \) bằng 1, nên output là một convex combination của các value.

Khác biệt cốt lõi với RNN (Bài 8): RNN xử lý input theo thứ tự tuần tự và nén tất cả vào một hidden state cố định; attention cho phép query truy cập trực tiếp mọi vị trí cùng lúc.

3

Intuition

Tương tự khi đọc một paragraph dài: não bộ không xử lý đều mọi từ. Khi đang trả lời câu hỏi "ai đã ký hợp đồng", bạn nhìn lướt qua các từ nối ("the", "of", "and") và tập trung vào các danh từ riêng, động từ ký, mốc thời gian. Trọng số chú ý không đồng đều — đó là attention.

Một ví dụ cụ thể trong NMT (machine translation): để dịch từ thứ 5 của câu tiếng Việt, mô hình cần "nhìn" vài từ cụ thể trong câu tiếng Anh nguồn (thường gần vị trí 5, đôi khi xa hơn do trật tự từ khác nhau). Trước attention, encoder-decoder RNN phải nén cả câu nguồn vào một vector cố định — thông tin chi tiết bị mất khi câu dài. Bahdanau 2014 đề xuất: với mỗi bước decoder, tính một phân phối trọng số trên tất cả hidden state của encoder, rồi dùng vector trọng số đó. Đây là attention bản gốc.

Diễn đạt ngắn: attention = "trọng số importance", còn output = "trung bình có trọng số của các value".

4

Lịch sử ngắn

  • Bahdanau et al., 2014Neural Machine Translation by Jointly Learning to Align and Translate. Lần đầu đưa attention vào seq2seq cho NMT. Score được tính bằng một MLP nhỏ (additive attention).
  • Luong et al., 2015Effective Approaches to Attention-based Neural Machine Translation. Đưa ra các biến thể: dot product, general, concat; phân biệt global vs local attention.
  • Vaswani et al., 2017Attention Is All You Need. Bỏ hẳn recurrence, chỉ dùng attention. Đề xuất Scaled Dot-Product Attention và Multi-Head Attention — kiến trúc nền tảng của mọi LLM modern.

Trước Bahdanau 2014, ý tưởng "trọng số có học" đã xuất hiện rải rác trong cộng đồng (alignment trong statistical MT, soft addressing trong Neural Turing Machine). Nhưng bản hiện đại được công nhận rộng rãi bắt đầu từ ba paper trên.

5

Query, Key, Value

Vaswani 2017 chuẩn hoá attention thành ba ma trận. Cho một query đang xét và một tập (key, value):

  • Query \( Q \) — "tôi đang tìm gì?". Là vector đại diện cho nhu cầu truy vấn của vị trí hiện tại.
  • Key \( K \) — "tôi đại diện cho cái gì?". Mỗi vị trí trong nguồn có một key dùng để so khớp với query.
  • Value \( V \) — "thông tin thực sự của tôi". Mỗi vị trí có một value chứa nội dung sẽ được tổ hợp lại sau khi tính trọng số.

Analogy search engine — cách dễ nhớ nhất:

  • Query = từ khoá bạn gõ vào ô search.
  • Key = tiêu đề / metadata của từng document trong index.
  • Value = nội dung thực của document được trả về.

Search engine kinh điển trả về đúng 1 document có key match nhất. Attention "soft" hơn: trả về trung bình có trọng số của tất cả value, trọng số đến từ độ giống nhau giữa query và key.

Trong Transformer thực tế, \( Q, K, V \) đều được sinh ra từ input bằng ba phép linear projection riêng biệt:

\[ Q = X W^Q, \quad K = X W^K, \quad V = X W^V \]

với \( X \) là embedding của các token và \( W^Q, W^K, W^V \) là tham số học được. Bài 10 (Self-Attention) sẽ chi tiết hoá phép projection này.

6

Scaled Dot-Product Attention

Đây là dạng attention được dùng trong Transformer. Công thức:

\[ \text{Attention}(Q, K, V) = \text{softmax}\!\left( \frac{Q K^T}{\sqrt{d_k}} \right) V \]

Shape của các ma trận:

  • \( Q \in \mathbb{R}^{n \times d_k} \) — \( n \) query, mỗi query \( d_k \) chiều.
  • \( K \in \mathbb{R}^{m \times d_k} \) — \( m \) key, cùng chiều với query.
  • \( V \in \mathbb{R}^{m \times d_v} \) — \( m \) value, có thể khác chiều với key.
  • Output \( \in \mathbb{R}^{n \times d_v} \) — mỗi query trả về một vector \( d_v \) chiều.

Trong self-attention (Bài 10), \( n = m \) và thường \( d_k = d_v \). Trong cross-attention (encoder-decoder), \( n \) là độ dài decoder, \( m \) là độ dài encoder — hai con số có thể khác nhau.

7

Step-by-step

Tách công thức Vaswani thành bốn bước riêng — phù hợp khi debug hoặc viết lại bằng tay:

  1. Compute score. \( S = Q K^T \in \mathbb{R}^{n \times m} \). Mỗi phần tử \( S_{ij} = q_i \cdot k_j \) là dot product giữa query thứ \( i \) và key thứ \( j \) — đo độ tương đồng.
  2. Scale. \( S' = S / \sqrt{d_k} \). Lý do giải thích ở Bước 8.
  3. Softmax. Áp softmax theo từng hàng của \( S' \): \( A_{ij} = \frac{\exp(S'_{ij})}{\sum_{k} \exp(S'_{ik})} \). Sau bước này, mỗi hàng của \( A \) là một phân phối xác suất tổng bằng 1.
  4. Weighted sum. Output \( = A \cdot V \in \mathbb{R}^{n \times d_v} \). Hàng \( i \) của output là tổ hợp tuyến tính các hàng của \( V \) với trọng số là hàng \( i \) của \( A \).

Lưu ý: softmax áp theo hàng (axis cuối), không phải cho cả ma trận. Đây là lỗi sai phổ biến khi tự implement.

8

Vì sao scale √d_k

Giả sử các thành phần của \( q, k \) là biến ngẫu nhiên độc lập, kỳ vọng 0, phương sai 1. Khi đó:

\[ q \cdot k = \sum_{i=1}^{d_k} q_i k_i, \qquad \mathbb{E}[q \cdot k] = 0, \qquad \mathrm{Var}(q \cdot k) = d_k \]

Phương sai của dot product tăng tuyến tính theo \( d_k \). Với \( d_k = 64 \), độ lệch chuẩn của \( q \cdot k \) đã là 8; với \( d_k = 512 \) là khoảng 22. Score bị "phình" ra rất nhanh.

Hệ quả khi đưa qua softmax với score lớn:

  • Một phần tử lớn hẳn → softmax bão hoà về 0/1 (gần one-hot).
  • Đạo hàm của softmax \( \frac{\partial \alpha_i}{\partial s_j} = \alpha_i (\delta_{ij} - \alpha_j) \). Khi \( \alpha_i \to 0 \) hoặc \( \alpha_i \to 1 \), gradient \( \to 0 \) — model gần như không học.

Chia cho \( \sqrt{d_k} \) đưa phương sai về \( O(1) \) — gradient ổn định bất kể \( d_k \) lớn nhỏ. Đây là điều chỉnh kỹ thuật nhỏ nhưng quan trọng để Transformer train được ở scale lớn.

9

Attention weight visualization

Sau softmax, \( A \in \mathbb{R}^{n \times m} \) là ma trận trọng số attention. Cách trực quan hoá phổ biến: heatmap.

  • Hàng = query (vị trí đang truy vấn).
  • Cột = key (vị trí được truy vấn).
  • Mỗi ô \( A_{ij} \in [0, 1] \) — màu càng đậm, trọng số càng lớn.
  • Tổng từng hàng = 1 (do softmax).

Một số pattern hay gặp khi nhìn heatmap của self-attention trên text:

  • Đường chéo sáng — token attend mạnh tới chính nó hoặc lân cận.
  • Ô off-diagonal sáng — cross-position attention (ví dụ đại từ "it" attend tới danh từ ở vị trí xa).
  • Cột sáng cố định — một token đặc biệt (BOS, dấu chấm, từ chức năng) bị nhiều query attend tới — thường liên quan tới "attention sink".

Bài 10 sẽ có ví dụ cụ thể heatmap; ở đây chỉ cần hình dung dạng \( n \times m \) ma trận.

10

Bahdanau attention (additive)

Trước khi Vaswani 2017 chuẩn hoá dot product, attention được tính qua một MLP nhỏ. Công thức Bahdanau (đôi khi gọi là additive attention):

\[ \text{score}(h_i, s_j) = v^\top \tanh\!\big(W_1 h_i + W_2 s_j\big) \]

Với \( h_i \) là hidden state encoder, \( s_j \) là hidden state decoder, \( W_1, W_2, v \) là tham số học được. Output là một scalar — đo độ tương đồng giữa hai hidden state.

Ưu / nhược:

  • Có thể xử lý trường hợp \( h_i \) và \( s_j \) khác chiều mà không cần linear projection riêng.
  • Chậm hơn dot product: mỗi cặp \( (i, j) \) cần một forward qua MLP — không vector hoá đẹp như matrix multiplication.

Trong seq2seq RNN giai đoạn 2014-2016, Bahdanau attention dùng rất nhiều. Khi Transformer ra đời, dot product chiếm ưu thế vì tính song song trên GPU hiệu quả hơn.

11

Luong attention (dot product)

Luong 2015 đề xuất ba biến thể score đơn giản hơn Bahdanau:

  • Dot: \( \text{score}(h, s) = h^\top s \).
  • General: \( \text{score}(h, s) = h^\top W s \).
  • Concat: tương tự Bahdanau.

Dot product không có tham số học, chỉ là phép nhân vector — rất nhanh trên GPU. Vaswani 2017 mở rộng dạng này thành scaled dot product (chia \( \sqrt{d_k} \)) để ổn định gradient ở scale lớn — và đó là công thức mọi Transformer hiện đại dùng.

Tổng kết ngắn: Bahdanau = MLP score, Luong = dot product, Vaswani = scaled dot product + chuẩn hoá thành Q / K / V.

12

Cross-attention vs Self-attention

Cùng công thức Scaled Dot-Product, nhưng nguồn của \( Q, K, V \) khác nhau dẫn tới hai dạng:

  • Cross-attention: \( Q \) đến từ một sequence (ví dụ decoder), \( K, V \) đến từ một sequence khác (ví dụ encoder). Dùng trong encoder-decoder Transformer (T5, BART) để decoder "nhìn" sang encoder. Cũng dùng trong các model multimodal khi một modality query sang modality khác.
  • Self-attention: \( Q, K, V \) đều xuất phát từ cùng một sequence. Mỗi token vừa là query (đang tìm thông tin), vừa là key (sẵn sàng để được hỏi), vừa là value (cung cấp nội dung). Là thành phần chính của decoder-only LLM. Bài 10 sẽ deep-dive.

Cả hai dạng dùng cùng công thức; chỉ khác đầu vào của \( Q \), \( K \), \( V \).

13

Masking

Trong thực tế, không phải mọi cặp (query, key) đều hợp lệ. Trước khi softmax, ta cộng thêm một mask để loại bỏ một số vị trí. Hai loại mask phổ biến:

  • Causal mask (look-ahead mask): trong decoder LLM, token thứ \( i \) chỉ được nhìn token \( \leq i \). Mục đích: đảm bảo tính autoregressive — khi sinh token tiếp theo, model không "nhìn trộm" tương lai.
  • Padding mask: khi batch chứa các câu độ dài khác nhau, các câu ngắn được pad bằng token \( \texttt{[PAD]} \) cho đủ chiều dài. Mask này đảm bảo token thực không attend tới [PAD] và ngược lại.

Trong nhiều framework (PyTorch nn.functional.scaled_dot_product_attention, Hugging Face), hai loại mask được truyền dưới dạng tensor boolean hoặc tensor cộng (giá trị \( -\infty \) ở vị trí bị mask, 0 ở vị trí hợp lệ).

14

Causal mask

Định nghĩa hình thức cho causal mask trong decoder:

\[ \text{mask}[i, j] = \begin{cases} 0 & \text{nếu } j \leq i \\ -\infty & \text{nếu } j > i \end{cases} \]

Sau khi cộng mask vào score, các vị trí \( -\infty \) đi qua softmax cho ra trọng số bằng 0 — coi như không đóng góp gì vào output. Vị trí 0 không bị ảnh hưởng.

Hệ quả về shape: nếu score là \( S \in \mathbb{R}^{n \times n} \) (self-attention với chính sequence của mình), mask là một ma trận tam giác dưới của 0 và tam giác trên của \( -\infty \).

Vì sao bắt buộc causal mask trong LLM decoder: training chạy teacher forcing — toàn bộ chuỗi target được đưa vào song song để tính loss. Nếu không mask, vị trí thứ \( i \) có thể nhìn thấy token thứ \( i+1, i+2, \dots \) — model học cách "copy" từ tương lai thay vì học predict. Khi inference, vì sinh từng token một nên không có future để copy → kết quả tệ. Mask đảm bảo training và inference khớp nhau.

15

PyTorch implementation

Implementation tối giản (PyTorch 2.x):

import torch
import torch.nn.functional as F

def attention(Q, K, V, mask=None):
    d_k = Q.size(-1)
    scores = Q @ K.transpose(-2, -1) / (d_k ** 0.5)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, float("-inf"))
    attn = F.softmax(scores, dim=-1)
    return attn @ V

Giải thích từng dòng:

  • Q @ K.transpose(-2, -1) — \( Q K^T \), shape \( (\dots, n, m) \). Hai chiều cuối được swap, các chiều batch giữ nguyên.
  • / (d_k ** 0.5) — chia cho \( \sqrt{d_k} \).
  • masked_fill(mask == 0, -inf) — quy ước mask = 1 cho vị trí hợp lệ, mask = 0 cho vị trí cấm. Vị trí cấm bị set \( -\infty \) trước softmax.
  • F.softmax(scores, dim=-1) — softmax theo chiều key (chiều cuối).
  • attn @ V — weighted sum, shape \( (\dots, n, d_v) \).

Trong production, dùng torch.nn.functional.scaled_dot_product_attention (PyTorch \( \ge 2.0 \)) — nó tự chọn Flash Attention backend khi điều kiện đủ (CUDA, dtype hợp lệ). Hàm tự viết ở trên dùng cho mục đích học và debug.

16

Computation cost

Phân tích cost cho self-attention với \( n \) token, hidden dim \( d \) (giả sử \( d_k = d_v = d \)):

  • \( Q K^T \): hai ma trận \( n \times d \) nhân nhau ra \( n \times n \) — \( O(n^2 d) \).
  • Softmax trên \( n \times n \): \( O(n^2) \).
  • \( A V \): \( n \times n \) nhân \( n \times d \) — \( O(n^2 d) \).

Tổng: \( O(n^2 d) \). Thành phần \( n^2 \) là bottleneck quan trọng — khi context dài, cost tăng bậc hai theo độ dài chuỗi. So sánh với RNN: \( O(n d^2) \) — tuyến tính theo \( n \) nhưng tuần tự (không song song được).

Với LLM thực tế, \( n \) có thể là 4K, 32K, 128K, hoặc 1M. Cost \( n^2 d \) tăng khủng khiếp ở phía cao. Đây là lý do trực tiếp cho hàng loạt biến thể attention efficient ở Bước 18.

17

Memory cost

Bên cạnh compute, vấn đề thứ hai là memory. Naive implementation lưu ma trận attention \( A \in \mathbb{R}^{n \times n} \) ở float32 (4 byte / số).

Tính nhanh:

  • Context 4K: \( 4{,}000^2 = 1.6 \times 10^7 \) số \( \approx 64 \) MB.
  • Context 32K: \( 32{,}000^2 \approx 10^9 \) số \( \approx 4 \) GB.
  • Context 128K: \( 128{,}000^2 \approx 1.6 \times 10^{10} \) số \( \approx 64 \) GB.

Đó mới chỉ là một head, một layer. Multi-head, nhiều layer còn nhân lên thêm. Memory thường "đụng trần" trước cả compute — đây là lý do dài context không chỉ là chuyện train lâu hơn, mà có thể không train được.

Flash Attention (Dao et al., 2022) giải quyết đúng vấn đề này: tổ chức tính toán theo tile để không bao giờ phải materialize toàn bộ ma trận \( n \times n \) trong HBM, chỉ giữ một block nhỏ trong SRAM. Memory từ \( O(n^2) \) xuống \( O(n) \), throughput tăng 2-4 lần nhờ giảm I/O. Hiện đã có FlashAttention-2 (2023) và FlashAttention-3 (2024) tối ưu thêm cho H100.

18

Modern optimizations

Bốn hướng tối ưu chính được áp dụng ở LLM modern — chỉ nhắc tên ở đây, sẽ chi tiết trong module / series sau:

  • Flash Attention (Dao et al., 2022): implementation IO-aware, không thay đổi công thức toán; giảm memory \( O(n^2) \to O(n) \) và tăng throughput. Hầu hết mọi LLM modern train với FlashAttention.
  • Multi-Query Attention — MQA (Shazeer, 2019): các head chia sẻ chung một bộ \( K, V \). Giảm bộ nhớ KV-cache khi inference; nhanh hơn nhưng có thể giảm chất lượng nhẹ.
  • Grouped-Query Attention — GQA (Ainslie et al., 2023): trung dung giữa MHA và MQA — chia head thành nhóm, mỗi nhóm dùng chung K, V. Llama 2 / 3, Mistral, Claude và phần lớn LLM modern dùng GQA.
  • Sliding Window Attention (Mistral, 2023): mỗi query chỉ attend cửa sổ cố định (ví dụ 4096 token gần nhất). Cost xuống \( O(n \cdot w) \) với \( w \) là window. Phối hợp tốt với attention sink và streaming inference.
  • Sparse Attention: dạng tổng quát hơn — chỉ tính attention cho một subset các cặp (q, k). Longformer, BigBird, Sparse Transformer là các ví dụ. Ít phổ biến trong LLM decoder hơn ba dạng trên.

Đây mới chỉ là preview ở mức "biết tên" — Module 2 và Series 5 (Deployment) sẽ quay lại từng kỹ thuật khi liên quan trực tiếp đến serving / fine-tune.

19

Vì sao attention mạnh

Bốn tính chất khiến attention phù hợp cho sequence modeling ở scale lớn:

  • Direct access: mỗi token có thể nhìn trực tiếp mọi token khác trong cùng một bước, không qua trung gian.
  • Long-range dependency: khoảng cách hiệu dụng giữa hai vị trí là \( O(1) \) trong attention, so với \( O(n) \) ở RNN. Vì vậy phụ thuộc xa (đại từ đứng cách danh từ vài trăm token) được học dễ hơn nhiều.
  • Parallelizable: \( Q K^T \) chỉ là phép nhân ma trận — GPU làm rất nhanh, batch toàn bộ chuỗi cùng lúc. RNN buộc tuần tự nên không tận dụng được điều này.
  • Interpretable một phần: ma trận \( A \) là phân phối xác suất quan sát được. Cộng đồng đã có nhiều phân tích attention head (induction head, attention sink…) — không hoàn hảo nhưng có cấu trúc để khảo sát.

Không có nghĩa attention là tối ưu mọi mặt. Cost \( O(n^2) \) là điểm yếu cố hữu; một số kiến trúc mới (Bước 20) tìm cách thay thế hoặc kết hợp.

20

Vị trí hiện tại

Tính đến 2026, phần lớn LLM tổng quát thị trường dùng kiến trúc decoder-only Transformer với attention biến thể (MHA hoặc GQA), kết hợp Flash Attention cho efficiency. Đây là quy ước de facto, không phải định luật.

Có một số challenger không dùng attention thuần:

  • Mamba (Gu & Dao, 2023) — state space model với cost \( O(n) \), không dùng attention.
  • RWKV — kiến trúc lai giữa RNN và Transformer, hỗ trợ chuỗi dài với memory hằng số.
  • Hybrid (Jamba, Zamba…) — kết hợp attention với SSM cho cân bằng giữa long context và quality.

Tính đến nay (2026), các challenger này chưa thay thế attention ở mọi benchmark; thường mạnh ở long context nhưng còn khoảng cách với Transformer SOTA ở các benchmark reasoning. Series này tập trung vào attention-based architecture vì đó vẫn là nền tảng chính.

21

Code Python

Tự implement Scaled Dot-Product Attention cho một sequence ngắn, visualize attention matrix, áp causal mask:

import torch
import torch.nn.functional as F

torch.manual_seed(0)

n, d_k, d_v = 4, 8, 8           # 4 token, d_k = d_v = 8
Q = torch.randn(n, d_k)
K = torch.randn(n, d_k)
V = torch.randn(n, d_v)

def scaled_dot_product(Q, K, V, mask=None):
    d_k = Q.size(-1)
    scores = Q @ K.transpose(-2, -1) / (d_k ** 0.5)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, float("-inf"))
    attn = F.softmax(scores, dim=-1)
    return attn @ V, attn

# 1. No mask
out, A = scaled_dot_product(Q, K, V)
print("Attention matrix shape:", A.shape)   # (4, 4)
print("Row sums (expect ~1):", A.sum(dim=-1))

# 2. Causal mask
causal = torch.tril(torch.ones(n, n))       # tam giác dưới 1, trên 0
out_c, A_c = scaled_dot_product(Q, K, V, mask=causal)
print("Causal attention matrix:")
print(A_c.round(decimals=3))

Output kỳ vọng cho ma trận causal: hàng \( i \) chỉ có giá trị khác 0 ở các cột \( j \leq i \) (tam giác dưới), các cột \( j > i \) bằng 0.

Visualize bằng matplotlib (chạy trong notebook):

import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(8, 4))
axes[0].imshow(A.detach(), cmap="viridis")
axes[0].set_title("No mask")
axes[1].imshow(A_c.detach(), cmap="viridis")
axes[1].set_title("Causal mask")
for ax in axes:
    ax.set_xlabel("key")
    ax.set_ylabel("query")
plt.tight_layout()
plt.show()

Heatmap bên trái cho thấy attention đầy đủ, bên phải có dạng tam giác dưới — đây là pattern của decoder LLM.

22

Bài tập

Bài 1 — Tính tay attention. Cho \( n = 3 \) token với \( d_k = d_v = 4 \):

\( Q = \begin{bmatrix} 1 & 0 & 1 & 0 \\ 0 & 1 & 0 & 1 \\ 1 & 1 & 0 & 0 \end{bmatrix} \), \( K = Q \), \( V = \begin{bmatrix} 1 & 2 & 3 & 4 \\ 5 & 6 & 7 & 8 \\ 9 & 10 & 11 & 12 \end{bmatrix} \).

  1. Tính \( S = Q K^T \) và \( S' = S / \sqrt{d_k} \).
  2. Softmax theo hàng để được \( A \).
  3. Tính output \( A V \). Đối chiếu với kết quả PyTorch.

Bài 2 — Implement causal mask. Viết hàm causal_mask(n) trả về tensor \( n \times n \) với 1 ở tam giác dưới (bao gồm đường chéo), 0 ở tam giác trên. So sánh với torch.tril(torch.ones(n, n)).

Bài 3 — Visualize attention pattern. Lấy một câu tiếng Anh ngắn (10-15 token), tự sinh \( Q, K, V \) bằng torch.randn, chạy attention không mask và có causal mask. Vẽ heatmap cả hai. Mô tả 2 quan sát.

Bài 4 — Memory cost. Giả sử attention matrix lưu ở fp16 (2 byte / số), tính bộ nhớ cần cho ma trận attention với các context length sau (một head, một layer):

  • 4K token.
  • 32K token.
  • 128K token.
  • 1M token.

Nhân thêm cho một model có 32 layer, 32 head: tổng memory là bao nhiêu cho context 128K? Đối chiếu với GPU memory thực tế (H100 — 80 GB) — vì sao naive attention không khả thi và Flash Attention cần thiết.

Gợi ý đáp án Bài 4
  • fp16 → 2 byte / số.
  • 4K: \( 4{,}000^2 \times 2 = 3.2 \times 10^7 \) byte \( \approx 32 \) MB.
  • 32K: \( 32{,}000^2 \times 2 \approx 2 \) GB.
  • 128K: \( 128{,}000^2 \times 2 \approx 32 \) GB.
  • 1M: \( 10^{12} \times 2 = 2 \) TB.
  • 128K × 32 layer × 32 head: \( 32 \times 32 \times 32 \) GB \( \approx 32{,}000 \) GB \( = 32 \) TB — vượt xa H100 80GB → naive không khả thi. Trên thực tế, Flash Attention không materialize ma trận, KV-cache nén bằng GQA / quantization để xuống mức chấp nhận được.
23

Tóm tắt

  • Attention = gán trọng số importance cho từng phần input, output là weighted sum của value.
  • Lịch sử: Bahdanau 2014 (additive, cho NMT) → Luong 2015 (dot product) → Vaswani 2017 (Scaled Dot-Product, Q/K/V chuẩn hoá).
  • Q = "tôi tìm gì", K = "tôi đại diện cho gì", V = "thông tin thực". Analogy: search engine.
  • Công thức Transformer: \( \text{Attention}(Q, K, V) = \text{softmax}(Q K^T / \sqrt{d_k}) V \).
  • Chia \( \sqrt{d_k} \) để dot product không phình theo \( d_k \), tránh softmax bão hoà và gradient triệt tiêu.
  • Mask: causal (decoder, không nhìn future), padding (bỏ qua [PAD]).
  • Cost: \( O(n^2 d) \) compute, \( O(n^2) \) memory cho naive — bottleneck với context dài.
  • Flash Attention (Dao 2022) giảm memory về \( O(n) \) qua tiling; MQA / GQA giảm KV-cache; Sliding Window giảm cost theo cửa sổ; Sparse Attention dùng subset cặp.
  • Lý do attention thắng: direct access, long-range \( O(1) \), parallel, interpretable một phần.
  • 2026: Transformer attention vẫn là chuẩn de facto; challenger không attention (Mamba, RWKV) đang phát triển nhưng chưa thay thế.
  • Bài 10 đi vào trường hợp đặc biệt quan trọng: self-attention — token "nhìn" các token khác trong cùng sequence.