AI Core cho lập trình viên/Mini-challenge: đọc sơ đồ kiến trúc LLM
35/54
Bài 35 / 54~15 phútTransformer & attentionMiễn phí lượt xem

Mini-challenge: đọc sơ đồ kiến trúc LLM

Cầm sơ đồ kiến trúc và model card của một LLM thật: chỉ ra khối transformer, giải nghĩa layers/heads/params/context — bằng chính vốn từ vừa học trong module.

TL;DR: Bài này đưa cho bạn model card và hai sơ đồ kiến trúc của một LLM giả định (VietLM-7B) — bạn tự đọc sơ đồ, giải nghĩa các con số layers/heads/params/context window bằng đúng vốn từ vừa học trong module, rồi suy luận đánh đổi khi tăng context hay tăng một trục scaling laws. Bài không dạy khái niệm mới; kỹ năng cần luyện là tự đọc một tài liệu kiến trúc thật (như trang model Hugging Face) mà không cần ai diễn giải lại.

Đến đây bạn đã có đủ vốn từ để đọc một tài liệu thật: model card kèm sơ đồ kiến trúc của một LLM — đúng dạng bạn sẽ gặp trên trang model của Hugging Face hay README một checkpoint mở. Bài này đưa cho bạn một bảng số và hai sơ đồ, rồi hỏi ba câu. Cầm bút, thử trả lời cả ba trước khi mở phần Lời giải — kỹ năng cần luyện là tự đọc ra ý nghĩa, không phải nhận ra đáp án khi nhìn thấy nó.

🎯 Đề bài — model card của VietLM-7B

VietLM-7B là một cái tên giả định, dựng riêng cho bài này — không phải model của hãng nào. Các con số dưới đây được chọn đúng bậc độ lớn của một model 7 tỷ tham số thật ngoài đời, để bạn luyện đọc trên một trường hợp an toàn trước khi tự tra model card thật.

Thông sốGiá trị
Kiến trúcDecoder-only Transformer
Số lớp (layers)32
Số attention head mỗi lớp (heads)32
Chiều embedding (d_model)4096
Tổng tham số (params)~7 tỷ (7B)
Độ dài ngữ cảnh (context window)8.192 token (~8k)
Kích thước từ vựng (vocab)~64.000 token

Sơ đồ dưới vẽ đường đi toàn cục của một chuỗi token qua model, từ lúc vào tới lúc ra một phân phối xác suất cho token kế tiếp:

Chuỗi token vào, qua token embedding rồi cộng positional encoding, đi hết khung 32 transformer block xếp chồng, tới final layer norm và LM head, ra phân phối xác suất cho token kế tiếp

Và đây là bên trong một trong 32 block ở trên, phóng to:

Bên trong một block: đầu vào đi qua self-attention 32 head song song rồi tới bước cộng residual và layer norm, tiếp đó là feed-forward network với bước cộng residual và layer norm thứ hai; hai đường residual vẽ vòng bên ngoài luồng chính

🧭 Bộ công cụ — ôn nhanh bốn khái niệm

Bốn khái niệm dưới đây bạn đã học ở các bài trước. Đây là bản ôn tự-chứa — không cần mở lại bài cũ để bắt đầu.

Mỗi token phát ra một truy vấn (Q) và mỗi token khác (kể cả chính nó) trưng ra một khoá (K). Độ khớp giữa Q và từng K quyết định trọng số, dùng để pha trộn các giá trị (V) tương ứng thành ra biểu diễn attention của token đó — một phép tra cứu mềm, có trọng số, không phải tra khớp tuyệt đối như tra từ điển.

Trước block đầu tiên, positional encoding được cộng một lần vào embedding. Mỗi block sau đó xử lý: self-attention → cộng residual (+ layer norm) → FFN → cộng residual (+ layer norm). Xếp chồng N block như vậy — mỗi block nhận đầu ra của block trước làm đầu vào — tạo thành toàn bộ mạng.

Loss giảm trơn theo ba trục params, data, compute — nhưng theo đường cong bão hoà, không tuyến tính. Chinchilla chỉ ra: tăng params mà không tăng data cân xứng thì model bị undertrained, phí compute đã bỏ ra.

Model chỉ nhìn được một cửa sổ token giới hạn mỗi request. Vượt cửa sổ đó, thông tin bị cắt — với model tại thời điểm sinh token tiếp theo, phần bị cắt coi như không tồn tại.

✍️ Phần việc của bạn

Ba nhiệm vụ độc lập, dùng đúng model card và hai sơ đồ ở trên:

Nhiệm vụ 1 — Đọc sơ đồ. Với từng khối trong hai sơ đồ (Token embedding, Positional encoding, Self-attention, hai khối Residual, Layer norm (x2 mỗi block), Feed-forward network, Final layer norm, LM head), viết một câu nói nó làm gì. Sau đó trả lời riêng: Token embeddingPositional encoding khác nhau ở chỗ nào — tại sao cộng thêm bước thứ hai là bắt buộc? Và vì sao hai mũi tên Residual được vẽ vòng qua self-attention/FFN thay vì nối thẳng vào luồng chính?

Nhiệm vụ 2 — Giải nghĩa model card. Với mỗi dòng trong bảng thông số, viết một câu giải thích con số đó đo cái gì — dùng đúng từ đã học ở module này (không tra định nghĩa ngoài, không chép nguyên văn bảng). Ví dụ về hình thức một câu cần có (không phải đáp án): "layers là ___, đo bằng cách ___".

Nhiệm vụ 3 — Suy luận đánh đổi. Trả lời hai câu hỏi: (a) Hai model cùng 7B tham số, một có context window 8k, một có 128k — cái gì phải trả giá để có context dài hơn? (b) Muốn một model "khôn hơn" theo nghĩa scaling laws đã học, bạn tăng trục nào, và mỗi lựa chọn trả giá bằng gì?

💡 Gợi ý

Chỉ là câu hỏi định hướng — không có con số chốt hay câu kết luận nào ở đây. Nếu chưa viết xong phần việc ở trên, quay lại làm trước.

  • Nhiệm vụ 1: Token embedding biến một token (một con số ID rời rạc) thành cái gì? Nếu chỉ có bước đó, self-attention có cách nào tự biết token nào đứng trước token nào không — hay nó chỉ thấy một tập token không thứ tự? Với residual: nhìn kỹ dấu + — nó cộng đầu ra của khối nào với đầu vào của khối nào, và luồng chính (không qua residual) có bị bỏ đi không?
  • Nhiệm vụ 2: layers liên hệ thế nào tới "xếp chồng N lớp" ở bài transformer block? heads liên hệ thế nào tới việc chạy song song nhiều "góc nhìn" attention? context window liên hệ gì tới khái niệm "bộ nhớ làm việc hữu hạn"? params là loại con số được cập nhật lúc nào — lúc train hay lúc chạy inference?
  • Nhiệm vụ 3(a): Self-attention so mỗi token với mọi token khác trong cửa sổ context. Khi độ dài chuỗi tăng, số cặp token cần so sánh tăng theo bậc gì — tuyến tính hay nhanh hơn?
  • Nhiệm vụ 3(b): Nhớ lại ba trục của scaling laws. Trục nào ảnh hưởng tới tốc độ/bộ nhớ lúc phục vụ (serving)? Trục nào cần dữ liệu chất lượng cao hơn là chỉ cần dữ liệu nhiều hơn? Đường cong loss theo compute có tuyến tính không?

✅ Lời giải

Nhiệm vụ 1 — Đọc sơ đồ

Từng khối làm gì:

  • Token embedding: biến mỗi token (một ID rời rạc, số nguyên) thành một vector số thực dài d_model (ở đây là 4096 chiều). Đây là bước duy nhất "phiên dịch" danh tính của token sang không gian số mà mạng có thể tính toán.
  • Positional encoding: cộng thêm vào vector embedding một tín hiệu mã hoá vị trí của token trong chuỗi.
  • Self-attention (trong mỗi block): mỗi token phát Q, mọi token phát K/V; 32 head chạy song song, mỗi head học một kiểu quan hệ khác nhau (ví dụ một head chuyên bắt quan hệ chủ ngữ-động từ, head khác chuyên bắt đồng tham chiếu), rồi ghép kết quả 32 head lại.
  • Residual (cả hai chỗ): cộng thẳng đầu vào của một khối con (self-attention hoặc FFN) với đầu ra của chính khối con đó.
  • Layer norm (x2 mỗi block): đi kèm ngay sau mỗi phép cộng residual, đưa giá trị vector về một khoảng ổn định — khác với Final layer norm (chỉ chạy một lần, sau block 32).
  • Feed-forward network (FFN): biến đổi phi tuyến áp dụng riêng cho từng vị trí token, không nhìn sang token khác — khác hẳn self-attention là bước duy nhất cho token "nhìn" lẫn nhau.
  • Final layer norm: chuẩn hoá vector đầu ra sau khi đã đi hết 32 block, trước khi chiếu sang không gian từ vựng.
  • LM head: một phép chiếu tuyến tính (linear) từ d_model chiều sang vocab chiều (~64.000), rồi softmax biến kết quả thành một phân phối xác suất hợp lệ (tổng bằng 1) cho token kế tiếp.

Token embedding vs Positional encoding: embedding chỉ mã hoá token đó là gì — nó không mang thông tin token đứng ở vị trí nào trong câu. Self-attention, tự bản chất, là một phép tổng có trọng số qua các token khác — phép tổng đó không tự nhiên phân biệt được "token đứng trước" với "token đứng sau" nếu không có thêm dữ kiện. Vì vậy cộng thêm positional encoding là bắt buộc: thiếu bước này, đảo ngược thứ tự cả câu đầu vào sẽ cho ra một biểu diễn attention gần như giống hệt câu gốc — model mất khả năng phân biệt "chó cắn người" với "người cắn chó".

Vì sao residual vẽ vòng qua: dấu + trong sơ đồ nghĩa là đầu ra được cộng với đầu vào của chính khối con đó, chứ không phải thay thế nó. Luồng chính (đầu vào gốc) vẫn đi tiếp song song, không bị bỏ đi — đường vòng residual là một "lối tắt" giữ lại nguyên vẹn tín hiệu gốc, để qua 32 lớp xếp chồng, thông tin ban đầu không bị pha loãng hay mất dần.

Nhiệm vụ 2 — Giải nghĩa model card

  • layers = 32: số block transformer xếp chồng lên nhau — đầu ra của block 1 là đầu vào của block 2, cứ vậy tới block 32. Đây chính là "N lớp" mà bài transformer block mô tả.
  • heads = 32: trong mỗi lớp, phép self-attention không chạy một lần mà chạy song song 32 lần độc lập ("góc nhìn" khác nhau), mỗi lần học bắt một kiểu quan hệ riêng, rồi ghép kết quả 32 head lại thành một vector.
  • d_model = 4096: độ dài vector dùng để biểu diễn mỗi token, giữ nguyên xuyên suốt toàn mạng (embedding, qua từng block, tới trước LM head) — có thể hiểu là "độ rộng băng thông" nơi thông tin của một token được lưu tại mỗi bước.
  • params ~7 tỷ: tổng số trọng số (weight) nằm trong toàn bộ 32 lớp cộng với embedding và LM head. Đây là những con số được học (cập nhật dần qua gradient descent) trong lúc train — khi chạy inference, chúng cố định, không đổi theo từng request.
  • context window = 8k: số token tối đa mà self-attention có thể nhìn thấy cùng lúc trong một request. Vượt ngưỡng này, token cũ bị đẩy ra ngoài cửa sổ — với model tại thời điểm sinh token kế tiếp, phần bị đẩy ra coi như không tồn tại.
  • vocab ~64.000: số token khác nhau mà tokenizer có thể sinh ra — kích thước "bảng chữ cái" của model, và cũng chính là số chiều đầu ra của LM head trước bước softmax.

Nhiệm vụ 3 — Suy luận đánh đổi

(a) 7B/8k so với 7B/128k: cùng 7 tỷ tham số nghĩa là hai model có cùng dung lượng "kiến thức" đã học được qua train — khác biệt duy nhất là cửa sổ attention nhìn được xa tới đâu. Nhưng cái giá phải trả tách thành hai trục khác nhau. Compute: self-attention phải so sánh mỗi token với mọi token khác trong cửa sổ, nên số phép so sánh cần làm tăng theo bậc hai (quadratic) theo độ dài chuỗi — context 128k dài gấp 16 lần 8k, nên compute cho attention có thể tăng tới xấp xỉ 256 lần (16²) nếu tính thuần quadratic (các kỹ thuật tối ưu attention trong thực tế giảm bớt phần này nhưng không loại bỏ được xu hướng tăng nhanh). Bộ nhớ lúc phục vụ (serving) thì khác hẳn: phần chiếm RAM chủ yếu là KV cache, và với mỗi token thêm vào, model chỉ lưu đúng một cặp key/value kích thước cố định — nên bộ nhớ KV cache tăng tuyến tính, đúng 16 lần khi context dài gấp 16 lần, không phải quadratic. Context dài không tự động làm model "giỏi" hơn, nó chỉ mở rộng phạm vi model được phép nhìn — và cái giá runtime lệch hẳn nhau giữa hai trục: compute tăng nhanh hơn nhiều so với bộ nhớ KV cache.

(b) Tăng trục nào để "khôn hơn": theo scaling laws đã học, có ba trục — params, data, compute. Tăng params: model có nhiều trọng số hơn để biểu diễn quan hệ phức tạp hơn, nhưng trả giá bằng việc lớn hơn, chậm hơn và tốn RAM hơn cả lúc train lẫn lúc serving. Tăng data: cần nhiều dữ liệu chất lượng hơn (không chỉ nhiều hơn về số lượng) và thời gian train dài hơn tương ứng — theo Chinchilla, tăng params mà không tăng data cân xứng thì phần tham số dư ra bị undertrained, lãng phí compute đã bỏ vào. Tăng compute: chi phí tiền bạc, năng lượng, thời gian train đều tăng, và loss giảm theo một đường cong bão hoà chứ không tuyến tính — càng về sau, cùng một lượng compute thêm vào mang lại cải thiện loss càng nhỏ dần. Không có trục nào "miễn phí": mỗi lựa chọn tăng một trục đều đánh đổi bằng chi phí ở một khâu khác trong vòng đời model.

🎓 Mở rộng

  • Tự tra một model card thật. Mở trang Hugging Face của một model mở (ví dụ dòng Llama, Mistral, Qwen) và tìm đúng bảy con số vừa đọc ở đây (layers, heads, d_model, params, context window, vocab, kiến trúc). Con số nào khớp bậc độ lớn với VietLM-7B, con số nào lệch hẳn — và bạn có đoán được vì sao model đó chọn khác không? Lưu ý: sơ đồ bài này theo kiến trúc gốc — positional encoding cộng một lần ở đầu vào; nhiều model thật (ví dụ họ Llama) dùng RoPE, nơi thông tin vị trí được áp lại ngay trong attention ở mỗi layer, nên đừng bối rối nếu model card thật không khớp y hệt chỗ này.
  • Encoder-decoder vs decoder-only. VietLM-7B trong bài chỉ có decoder (đúng kiến trúc phần lớn LLM chat hiện nay). Một số model dịch máy dùng kiến trúc encoder-decoder — thử tìm một model card như vậy và so sánh sơ đồ khối của nó với sơ đồ ở bài này khác nhau ở đâu.
  • Tính thử tổng tham số. Với d_model = 4096, layers = 32, thử tra công thức ước lượng tham số của một block transformer (chủ yếu nằm ở ma trận Q/K/V/output-projection của attention và hai ma trận của FFN) và xem con số bạn tính ra có gần ~7 tỷ không — đây là cách "re-derive" thay vì tin số liệu suông.

✨ Điều bạn vừa làm được

  • Đọc một sơ đồ kiến trúc transformer và gọi đúng tên chức năng từng khối, kể cả lý do residual được vẽ như một đường vòng chứ không phải bước biến đổi.
  • Giải nghĩa được toàn bộ bảy con số của một model card LLM bằng đúng vốn từ vừa học trong module (layers, heads, d_model, params, context window, vocab).
  • Suy luận được hai đánh đổi thực tế: chi phí quadratic của context dài, và ba trục scaling laws không có lựa chọn nào "miễn phí".

Bài tiếp theo: Tổng kết module — Transformer & attention

Bài này đáng gửi cho bạn học cùng?

Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.

Bài này có giúp bạn hiểu bản chất không?

Hỏi đáp về bài này

Chưa có câu hỏi

Đặt câu hỏi

Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).

Đặt câu hỏi đầu tiên

Bài tiếp theo

Tổng kết: Transformer & attention