AI Core cho lập trình viên/Transformer block: ghép attention, FFN, residual thành khối
32/54
Bài 32 / 54~13 phútTransformer & attentionMiễn phí lượt xem

Transformer block: ghép attention, FFN, residual thành khối

Attention tự nó không biết thứ tự — cần positional encoding. Ghép encoding, self-attention, FFN và residual thành một block, xếp chồng N lớp thành model.

TL;DR: Self-attention thuần không biết token nào đứng trước, đứng sau — nó chỉ so khớp nội dung. Positional encoding vá lỗ hổng đó bằng cách cộng một vector vị trí vào embedding, đúng một lần, trước khi vào block đầu tiên. Một transformer block gồm ba mảnh: self-attention (trộn thông tin giữa các token), FFN (xử lý từng token riêng), và residual connection bao quanh cả hai (giữ input gốc, giúp xếp sâu không "quên" và gradient chảy được). Xếp chồng N block, mỗi lớp xây trên cụm thông tin lớp trước đã gom, biến quan hệ gần thành quan hệ trừu tượng hơn.

Xếp nhiều lớp cùng hàm phi tuyến xen giữa cho phép mạng học hàm phức tạp hơn hẳn một lớp đơn — sức mạnh đến từ việc CHỒNG (compose) nhiều lớp, không phải từ một lớp to hơn. Bài này dùng lại nguyên lý đó, nhưng đơn vị xếp chồng bây giờ là cả một transformer block, không phải một lớp neuron đơn lẻ.

1. Analogy — một cuộc họp nhóm, lặp lại N vòng

Hình dung một cuộc họp brainstorm 3 người: Chó, Cắn, Mèo (đúng ba từ bạn sẽ theo dõi xuyên suốt bài này). Trước khi họp, mỗi người được gắn thẻ ghi thứ tự phát biểu — "Người #1", "#2", "#3" — để phân biệt ai nói trước ai nói sau, dù nội dung phát biểu giống nhau.

Họp lặp lại hai bước: nghe (mỗi người nghe cả ba người, kể cả chính mình, rồi tổng hợp thành ý hiểu mới) và tự suy nghĩ riêng (gặm nhấm ý vừa tổng hợp, không trao đổi thêm). Ở cả hai bước, mỗi người đều giữ lại ý kiến ban đầu, cộng thêm phần vừa xử lý — không ai xoá sạch ý cũ để thay hoàn toàn bằng ý mới.

Đời thường (họp nhóm)Khái niệm trong transformer block
Thẻ số thứ tự gắn trước khi họpPositional encoding — cộng vào embedding, một lần, trước block đầu tiên
Nghe phát biểu cả ba người rồi tổng hợpSelf-attention — mỗi token "nghe" mọi token khác, lấy tổng có trọng số
Giữ ý ban đầu, cộng thêm phần vừa ngheResidual connection quanh self-attention
Tự gặm nhấm riêng, không trao đổi thêmFFN — xử lý từng token độc lập, không tương tác giữa token
Giữ ý trước khi nghĩ riêng, cộng thêm kết quảResidual connection quanh FFN
Lặp lại N vòng, ý mỗi vòng tinh chỉnh sâu hơnXếp chồng N block
Cách nhớ

Một block chỉ làm đúng hai việc theo thứ tự: "nghe người khác" (self-attention) rồi "tự suy nghĩ một mình" (FFN) — và sau mỗi việc, luôn cộng lại ý kiến gốc (residual) trước khi qua bước tiếp theo.

2. Vì sao self-attention một mình không biết ai đứng trước ai?

Bài 02–03 đã giải thích cơ chế self-attention: mỗi token tính một vector query, so khớp với key của mọi token khác (kể cả chính nó), rồi lấy trung bình có trọng số của các vector value. Phép so khớp đó dựa hoàn toàn vào nội dung của vector — token nào giống token nào — chứ không dùng tới "token này đứng ở vị trí thứ mấy".

Thử đoán trước khi đọc tiếp

Xét hai câu dùng đúng ba token giống hệt nhau, chỉ đổi thứ tự: "Chó cắn mèo." (chó là chủ thể) và "Mèo cắn chó." (mèo là chủ thể). Nếu self-attention chỉ so khớp theo NỘI DUNG vector, không hề biết vị trí — bạn đoán: vector đầu vào của "chó" ở hai câu này có giống hệt nhau không? Và nếu giống hệt, self-attention có cách nào phân biệt "ai đang cắn ai" giữa hai câu không? Viết ra dự đoán trước khi đọc tiếp.

Không có gì phân biệt vị trí, vector đầu vào của "chó" ở cả hai câu giống hệt nhau — cùng embedding, không mang dấu vết "đứng trước" hay "đứng sau" từ cắn. Self-attention khi đó tính ra cùng một tập điểm tương đồng cho cả hai câu — không có cách nào phân biệt "ai đang cắn", vì phép tính chưa từng nhìn vào vị trí.

Pitfall — attention nghe 'thông minh' không có nghĩa nó tự biết thứ tự

Vaswani và cộng sự (2017) nói thẳng trong bài báo gốc: vì Transformer không có recurrence (đọc tuần tự như RNN) và không có convolution (cửa sổ trượt theo thứ tự), nên phải chủ động bơm thêm thông tin vị trí — nếu không, mô hình mù hoàn toàn về thứ tự.

3. Positional encoding — dán thẻ thứ tự trước khi vào họp

Positional encoding (mã hoá vị trí) là một vector số học đại diện cho "vị trí thứ mấy trong câu", được cộng thẳng vào embedding của token — giống thẻ số thứ tự phát biểu ở phần analogy. Điểm cần nhớ: bước cộng này chỉ diễn ra một lần duy nhất, ngay tại đầu vào, trước khi vào block đầu tiên — không cộng lại ở mỗi block. Vector kết quả (embedding cộng vị trí) là thứ được đưa vào toàn bộ chuỗi N block phía sau.

Ở mức trực giác: mỗi token mang hai lớp thông tin chồng lên nhau trong cùng một vector — "tôi LÀ từ gì" (embedding) và "tôi ĐỨNG ở đâu" (positional encoding). Nhờ lớp thứ hai, hai token "chó" ở vị trí 1 và 3 của hai câu khác nhau mang vector đầu vào khác nhau dù cùng là từ "chó" — đủ để self-attention phân biệt chủ thể hay tân ngữ dựa trên vị trí tương đối.

Bài này không đi vào công thức

Bản gốc dùng hàm sin/cos để tính vector vị trí — bài này chỉ cần bạn nắm được VÌ SAO cần bước này và NÓ ĐƯỢC CỘNG Ở ĐÂU. Công thức cụ thể nằm ngoài phạm vi bài, đọc thêm ở phần Đào sâu nếu tò mò.

Phiên bản — đây là cách của kiến trúc GỐC, không phải mọi LLM

Cộng một lần trước block đầu tiên là thiết kế của Transformer gốc (Vaswani et al., 2017, sinusoidal). Nhiều LLM hiện đại (ví dụ họ Llama) dùng RoPE: thông tin vị trí được áp LẠI ngay trong self-attention, ở MỖI layer, không phải cộng một lần ở đầu vào. Nguyên tắc chung không đổi — attention thuần không tự biết thứ tự, phải tiêm thông tin vị trí vào — chỉ khác CHỖ tiêm.

4. Một block transformer gồm những gì

Sau khi token đã mang vị trí, nó đi qua một block — đơn vị lặp lại N lần để tạo thành cả model. Một block gồm hai sub-layer, mỗi sub-layer bọc trong một residual connection:

Token embedding cộng positional encoding đúng một lần, bên ngoài khung lặp; trong khung là self-attention rồi FFN, mỗi cái có một đường residual vòng qua và cộng lại kèm layer norm

Viết lại bằng pseudocode cho toàn bộ N block, áp dụng cho mọi token trong câu cùng lúc:

for each token t in sequence:
    x[t] <- embedding(t) + positional_encoding(vi tri cua t)   -- cong 1 lan duy nhat, truoc block dau tien

for block b = 1 to N:
    for each token t:
        attn[t] <- self_attention(x[t], toan bo x)   -- tron thong tin GIUA cac token
    for each token t:
        y[t] <- LayerNorm(x[t] + attn[t])            -- residual + norm quanh self-attention
    for each token t:
        ffn[t] <- FFN(y[t])                          -- xu ly TUNG token rieng, KHONG tron giua cac token
        x[t] <- LayerNorm(y[t] + ffn[t])              -- residual + norm quanh FFN
// x[t] sau block N la vector cuoi cung cua token t

Ba mảnh ghép, theo đúng trình tự pseudocode trên:

  • Self-attention — sub-layer duy nhất trộn thông tin giữa các token, đúng cơ chế đã học ở bài 02–03.
  • FFN (feed-forward network): mạng nơ-ron nhỏ gần giống MLP ở bài 02 module Neural Net — nhưng áp dụng độc lập cho từng token, cùng một bộ trọng số cho mọi vị trí. Đây là bước "tiêu hoá" điều vừa gom từ self-attention.
  • Residual connection (đường tắt): cộng thẳng đầu vào một sub-layer vào đầu ra của chính nó. Mỗi block có hai đường tắt — quanh self-attention và quanh FFN.

Layer normalization đi kèm ngay sau mỗi phép cộng residual, đưa giá trị vector về khoảng ổn định — bài này không đào sâu cơ chế, chỉ cần biết vị trí của nó trong sơ đồ trên. Cộng-rồi-chuẩn-hoá (Post-LN) là thiết kế gốc 2017; từ GPT-2 trở đi, phần lớn LLM hiện đại dùng Pre-LN — chuẩn hoá TRƯỚC self-attention/FFN thay vì sau — để train ổn định hơn khi xếp sâu. Nguyên tắc residual + layer norm quanh mỗi sub-layer không đổi, chỉ khác thứ tự.

Pitfall — nhầm FFN với một bước trộn thông tin khác

FFN KHÔNG trộn thông tin giữa các token — nó xử lý mỗi token hoàn toàn tách biệt. Trong toàn bộ block, self-attention là sub-layer DUY NHẤT cho phép token này "nhìn thấy" token kia.

5. Dự đoán: thiếu bước nào thì hỏng gì?

Thử đoán trước khi đọc tiếp

Giả sử một kỹ sư dựng lại đúng sơ đồ block ở mục 4, nhưng VÔ Ý bỏ mất bước cộng positional encoding ở đầu vào — self-attention, FFN, residual vẫn y nguyên. Sau khi huấn luyện xong, đưa mô hình này dịch cả hai câu "Chó cắn mèo." và "Mèo cắn chó." sang tiếng Anh. Bạn đoán điều gì xảy ra, và vì sao?

Đúng như mục 2: thiếu positional encoding, vector đầu vào của "chó" giống hệt nhau ở cả hai câu. FFN xử lý từng token độc lập, residual chỉ cộng dồn chứ không tạo thông tin vị trí mới — không bước nào bù lại lỗ hổng đó. Hệ quả: model không phân biệt được "ai đang cắn" — đây là lý do Vaswani và cộng sự (2017) coi positional encoding là bắt buộc, không phải tuỳ chọn.

6. Trace một token cụ thể qua block

Bám sát ví dụ "Chó cắn mèo." — theo chân token "cắn" (vị trí thứ 2) đi qua một block:

  1. Đầu vào: vector của "cắn" = embedding cộng positional encoding vị trí 2.
  2. Self-attention: "cắn" tạo query, so khớp key cả ba token. Nhờ vị trí đã gắn từ bước 1, model học được token đứng NGAY TRƯỚC động từ thường là chủ thể, token đứng SAU là tân ngữ — trọng số của "cắn" nghiêng đúng "chó là chủ thể, mèo là tân ngữ".
  3. Residual + layer norm quanh self-attention: vector mới = vector bước 1 CỘNG kết quả attention, rồi chuẩn hoá. Ý nghĩa gốc "cắn là động từ" không bị ghi đè — chỉ bồi thêm ngữ cảnh chủ thể/tân ngữ.
  4. FFN: vector mang ngữ cảnh qua một MLP nhỏ, xử lý MỘT MÌNH — không còn nhìn "chó" hay "mèo".
  5. Residual + layer norm quanh FFN: vector cuối = vector bước 3 cộng kết quả FFN, chuẩn hoá lần nữa — là output của "cắn" tại block này, thành đầu vào ở block kế tiếp.

Token "chó" và "mèo" trải qua đúng năm bước tương tự song song, chỉ khác nội dung vector và trọng số attention.

7. Xếp chồng N block: từ quan hệ gần tới quan hệ trừu tượng

Output của block 1 trở thành input của block 2 — cùng cấu trúc lặp lại, nhưng self-attention ở block 2 không thao tác trên embedding "thô", mà trên vector đã được block 1 "tiền xử lý".

Xét câu dài hơn: "Con mèo mà tôi nuôi hôm qua đã ăn hết cá." Chủ thể thật của "ăn" là "con mèo", nhưng hai từ cách xa nhau. Ở block 1, self-attention dễ gom token liền kề thành cụm có nghĩa: "con" ghép "mèo" thành biểu diễn đậm nét hơn. Ở block 2, self-attention làm việc với cụm đã gom sẵn đó — nối "con mèo" với "ăn" ở xa dễ hơn, vì tiền xử lý cục bộ đã xong.

Xếp chồng thêm block chính là compose nhiều vòng xử lý lên nhau — đúng nguyên lý ở recall đầu bài: mỗi lớp xây trên đầu ra của lớp trước, nên cả chuỗi N lớp làm được điều một lớp đơn không làm được. Cụ thể: lớp dưới gom quan hệ giữa token gần nhau, lớp trên ghép các cụm đó thành quan hệ xa và trừu tượng hơn.

Pitfall — nghĩ residual chỉ là tối ưu tốc độ, không phải bắt buộc

Xếp chồng nhiều chục block nghĩa là gradient lan truyền ngược phải qua từng đó tầng phi tuyến liên tiếp. Không có residual, gradient dễ suy giảm hoặc bùng nổ, khiến block sâu khó học được gì. Residual giữ cho mỗi sub-layer chỉ cần học một "phần chỉnh sửa nhỏ" — điều kiện gần như bắt buộc để xếp sâu, không phải tinh chỉnh tốc độ phụ.

8. Pitfall tổng hợp

Nhầm 1: nghĩ attention "thông minh" nên tự khắc biết thứ tự câu. ✅ Self-attention chỉ so khớp nội dung vector — thứ tự phải được bơm vào tường minh qua positional encoding, cộng một lần trước block đầu tiên.

Nhầm 2: nghĩ FFN cũng trộn thông tin giữa các token, giống self-attention. ✅ FFN xử lý từng token hoàn toàn độc lập; self-attention mới là sub-layer duy nhất cho token này "nhìn thấy" token kia.

Nhầm 3: nghĩ residual connection chỉ là mẹo tăng tốc train, có thể bỏ qua nếu model nhỏ. ✅ Với model xếp chồng nhiều block, residual gần như bắt buộc — thiếu nó, gradient khó lan truyền ổn định qua nhiều tầng phi tuyến liên tiếp.

9. Đào sâu — nguồn gốc kiến trúc

Đào sâu — nguồn gốc kiến trúc

Vaswani, A., Shazeer, N., Parmar, N., Uszkoreit, J., Jones, L., Gomez, A. N., Kaiser, Ł., & Polosukhin, I. (2017). Attention Is All You Need. NeurIPS 2017. arXiv:1706.03762 — bài báo gốc đề xuất kiến trúc Transformer, bỏ hẳn recurrence và convolution. Đọc Section 3 để thấy sơ đồ block đầy đủ (Figure 1) và công thức sin/cos mà bài này cố tình bỏ qua.

10. Liên hệ các bài khác

11. Tóm tắt

  • Self-attention chỉ so khớp nội dung vector, không tự biết thứ tự token trong câu.
  • Positional encoding cộng một vector vị trí vào embedding, đúng một lần, trước block đầu tiên — không cộng lại ở các block sau.
  • Một block gồm ba mảnh: self-attention (trộn thông tin giữa token), FFN (xử lý từng token riêng), residual bao quanh cả hai.
  • Residual giữ input gốc không bị "quên" và giúp gradient lan truyền qua nhiều block xếp chồng; layer norm đi kèm ngay sau mỗi residual.
  • Xếp chồng N block: lớp dưới gom quan hệ token gần nhau, lớp trên ghép các cụm đó thành quan hệ xa và trừu tượng hơn.
  • Bài này chưa nói causal mask hay cách decoder-only LLM sinh từng token — nội dung bài 05.

12. Tự kiểm tra

Tự kiểm tra
Q1
Vì sao self-attention một mình, không có positional encoding, không phân biệt được "Chó cắn mèo" và "Mèo cắn chó"?
Self-attention tính điểm tương đồng và trộn thông tin dựa trên nội dung vector (so khớp query với key), không dựa trên vị trí. Nếu vector đầu vào của "chó", "cắn", "mèo" giống hệt nhau ở cả hai câu vì không có positional encoding để phân biệt "đứng trước" hay "đứng sau", phép tính attention ra kết quả tương đương ở cả hai — model không có manh mối để biết ai đang cắn ai.
Q2
Positional encoding được cộng vào token ở bước nào — mỗi block, hay chỉ một lần? Residual connection giúp gì để thông tin đó không mất qua nhiều lớp?
Positional encoding chỉ cộng đúng một lần, vào embedding trước block đầu tiên — không cộng lại ở các block sau. Residual ở mỗi block cộng thẳng input (đã mang vị trí từ đầu) vào output của self-attention và FFN, nên dù vị trí không "làm mới" ở các block sau, thông tin đó vẫn mang xuyên suốt N block nhờ đường tắt cộng dồn liên tiếp.
Q3
Self-attention và FFN khác nhau thế nào về việc một token có "nhìn thấy" token khác hay không?
Self-attention là sub-layer duy nhất trong block trộn thông tin giữa các token — mỗi token nhìn vào mọi token khác để tính vector mới. FFN xử lý từng token hoàn toàn độc lập, dùng chung một bộ trọng số cho mọi vị trí, nhưng không có tương tác giữa token này với token kia.
Q4
Nếu bỏ residual connection ra khỏi một model xếp chồng nhiều chục block, bạn dự đoán điều gì xảy ra khi huấn luyện?
Model sẽ khó huấn luyện hơn hẳn. Không có đường tắt cộng thẳng input vào output mỗi sub-layer, gradient phải đi qua toàn bộ chuỗi phép biến đổi phi tuyến liên tiếp trước khi lan ngược về các lớp đầu, dễ suy giảm hoặc bùng nổ. Residual giữ cho mỗi sub-layer chỉ cần học một phần chỉnh sửa nhỏ, thay vì học lại từ đầu — gần như bắt buộc để xếp sâu nhiều block.
Q5
Vì sao xếp chồng nhiều block thường giúp model bắt được quan hệ xa và trừu tượng hơn, thay vì chỉ lặp lại đúng một điều nhiều lần?
Mỗi block nhận đầu vào là đầu ra đã được block trước tinh chỉnh — vector của một token ở block 2 mang ngữ cảnh cục bộ gom từ block 1. Self-attention ở block sau ghép các cụm đã "tiền xử lý" đó thành quan hệ xa hơn, đúng nguyên lý composition đã học ở module Neural Net: mỗi lớp xây trên đầu ra của lớp trước, nên cả chuỗi N lớp làm được điều một lớp đơn không làm được.
Q6
Bài này có giải thích causal mask hay cách LLM sinh từng token một không? Vì sao?
Không. Bài này chỉ nói kiến trúc một block dùng chung, chưa phân biệt encoder hay decoder. Causal mask (chặn token nhìn thấy token đứng sau) và vòng lặp sinh từng token của decoder-only LLM là nội dung bài 05 — cố tình để ngoài phạm vi bài này.

Bài tiếp theo: Từ transformer đến LLM

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ừ transformer đến LLM: causal mask và train song song