Mô hình chuỗi sang chuỗi (Seq2Seq)
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ẽ:
- Hiểu bài toán chuỗi sang chuỗi (sequence-to-sequence, Seq2Seq), khung encoder–decoder, và vì sao nén cả câu nguồn vào một vectơ cố định là một nút thắt.
- Giải thích được attention của Bahdanau và cross-attention trong Transformer: truy vấn từ decoder, khóa và giá trị từ encoder.
- Phân biệt teacher forcing khi huấn luyện với sinh tự do khi dùng, và nhận ra exposure bias.
- Cài đặt được beam search có phạt độ dài (length penalty) bằng NumPy, và đọc hiểu một Transformer encoder–decoder nhỏ viết bằng PyTorch.
Cần biết trước
Phần tiêu đề “Cần biết trước”- LSTM and BiLSTM, in Deep Learning(Not translated yet) (Learned)
- Transformers, in Deep Learning(Not translated yet) (Learned)
Trực giác
Phần tiêu đề “Trực giác”Câu ra không thẳng hàng với câu vào
Phần tiêu đề “Câu ra không thẳng hàng với câu vào”Hãy dịch hai câu tiếng Anh sang tiếng Việt:
| Câu nguồn | Câu đích | Số từ |
|---|---|---|
| Good morning | Chào buổi sáng | 2 → 3 |
| the black cat | con mèo đen | 3 → 3, nhưng “black cat” thành “mèo đen” |
Hai điều làm việc này khác hẳn phân loại. Độ dài khác nhau: câu ra có thể dài hơn hay ngắn hơn câu vào, và ta không biết trước nó dài bao nhiêu. Trật tự khác nhau: từ thứ hai của câu ra không nhất thiết ứng với từ thứ hai của câu vào. Vì vậy không thể gắn cho mỗi từ nguồn một nhãn đích, như khi gán nhãn từ loại.
Dịch máy chỉ là một ví dụ. Tóm tắt (văn bản dài vào, đoạn ngắn ra), hỏi đáp có sinh câu trả lời, hay sửa lỗi chính tả đều có cùng dạng: một chuỗi vào, một chuỗi ra, độ dài và trật tự của hai bên tự do.
Người phiên dịch và mẩu giấy ghi chú
Phần tiêu đề “Người phiên dịch và mẩu giấy ghi chú”Cách làm đầu tiên giống một người phiên dịch nghe hết câu, ghi ý vào một mẩu giấy nhỏ, rồi úp bản gốc xuống và dịch chỉ dựa vào mẩu giấy. Phần nghe là encoder (bộ mã hóa), phần nói là decoder (bộ giải mã). Câu ngắn thì ổn. Câu năm mươi từ thì mẩu giấy vẫn chỉ to chừng ấy, nên chi tiết rơi rớt.
Cách tốt hơn: để bản gốc nằm trên bàn. Mỗi khi viết một từ, người phiên dịch liếc lại đúng chỗ cần nhìn trong câu gốc. Viết “mèo” thì nhìn “cat”, viết “đen” thì nhìn “black”. Đó là ý tưởng của attention, và cũng là việc của cross-attention trong Transformer.
Còn một chuyện nữa: mỗi bước, mô hình có nhiều từ có thể viết tiếp. Chọn từ “tốt nhất” ở từng bước chưa chắc ra câu tốt nhất, vì một lựa chọn ban đầu hơi kém có khi mở ra phần sau rất tốt.
Bài toán của bài này là: từ các cặp câu nguồn và câu đích, học cách sinh câu đích từng token một, mỗi bước nhìn lại câu nguồn; rồi khi chỉ có câu nguồn, tìm một câu đích có xác suất cao.
Lý thuyết
Phần tiêu đề “Lý thuyết”Phát biểu bài toán
Phần tiêu đề “Phát biểu bài toán”Gọi chuỗi nguồn là và chuỗi đích là , với , là độ dài của hai chuỗi. Token cuối luôn là <EOS> (end of sequence): mô hình tự quyết định khi nào dừng bằng cách sinh ra token này, nên tính cả <EOS>.
Như bài Xây dựng LLM đầu tiên, ta tách xác suất của cả chuỗi bằng quy tắc nhân. Khác biệt duy nhất là mọi thừa số đều có thêm điều kiện :
Ở đây là các token đích đứng trước vị trí . Vậy mô hình seq2seq là một mô hình ngôn ngữ có điều kiện: nó đoán token kế tiếp của câu đích, khi biết các token đích đã có và cả câu nguồn.
Encoder–decoder bằng mạng hồi tiếp
Phần tiêu đề “Encoder–decoder bằng mạng hồi tiếp”Sutskever, Vinyals và Le (2014) và Cho cùng cộng sự (2014) đưa ra khung encoder–decoder bằng hai mạng hồi tiếp:
- Encoder đọc . Trạng thái ẩn cuối cùng là một vectơ có số chiều cố định, đại diện cho cả câu nguồn.
- Decoder sinh dựa trên , từng token một, mỗi bước nhận token vừa sinh làm đầu vào, cho tới khi sinh
<EOS>.
Hai mạng được huấn luyện cùng nhau để làm lớn trên các cặp câu. Mô hình của Sutskever và cộng sự dùng LSTM 4 lớp, mỗi lớp 1000 ô. Trên tập dịch Anh–Pháp WMT’14, tổ hợp 5 mô hình (ensemble) của họ đạt điểm BLEU 34.8, so với 33.3 của một hệ thống dịch dựa trên cụm từ. BLEU đo mức trùng các cụm từ ngắn (n-gram) giữa bản dịch máy và bản dịch tham chiếu của người; cao hơn là tốt hơn. Họ còn gặp một mẹo lạ: đảo ngược trật tự câu nguồn làm BLEU tăng từ 25.9 lên 30.6. Họ thừa nhận chưa giải thích được đầy đủ, và cho rằng khi đảo, nhiều cặp từ tương ứng giữa hai câu nằm gần nhau hơn, nên bài toán tối ưu dễ hơn.
Mẹo đó là một dấu hiệu: mọi thông tin của câu nguồn phải đi qua đúng một vectơ . Câu 5 từ hay 50 từ đều được nén vào cùng một số chiều. Bahdanau, Cho và Bengio (2015) đặt giả thuyết rằng chính vectơ cố định này là nút thắt (bottleneck), và đo được bằng chứng: mô hình encoder–decoder cơ bản tụt điểm mạnh khi câu dài ra, còn bản tốt nhất có attention của họ không tụt, kể cả với câu từ 50 từ trở lên.
Attention: mỗi bước nhìn lại câu nguồn
Phần tiêu đề “Attention: mỗi bước nhìn lại câu nguồn”Bahdanau và cộng sự bỏ yêu cầu “một vectơ cho cả câu”. Encoder là một RNN hai chiều, cho mỗi từ nguồn một vectơ mang thông tin của từ đó và các từ xung quanh. Gọi là trạng thái của decoder ngay trước khi sinh . Ở mỗi bước , decoder làm ba việc:
- Chấm điểm. đo “để sinh từ đích thứ , nên nhìn từ nguồn thứ bao nhiêu”. Hàm là một mạng nhỏ, học cùng cả mô hình.
- Chuẩn hóa. Softmax trên biến các điểm thành trọng số dương, cộng lại bằng 1 trên mọi từ nguồn.
- Trộn. là trung bình có trọng số của các . Đây là bản tóm tắt câu nguồn riêng cho bước , thay cho vectơ chung.
Encoder vẫn tạo vectơ cho câu nguồn; điều thay đổi là decoder không còn bị buộc chỉ dùng một vectơ. Bảng trọng số còn đọc được bằng mắt. Với “the black cat” sang “con mèo đen”, một mô hình học tốt có thể cho bảng như dưới (số tự đặt để minh họa, không phải số của mô hình thật):
| Từ đích | nhìn “the” | nhìn “black” | nhìn “cat” |
|---|---|---|---|
| con | 0.80 | 0.05 | 0.15 |
| mèo | 0.05 | 0.10 | 0.85 |
| đen | 0.02 | 0.90 | 0.08 |
Mỗi hàng cộng lại bằng 1. Hai hàng cuối đổi chỗ so với đường chéo, đúng chỗ tiếng Việt đặt tính từ sau danh từ. Bahdanau và cộng sự cho thấy các bản đồ như vậy từ mô hình thật trên câu Anh–Pháp: gần như theo đường chéo, lệch ra ở chỗ hai ngôn ngữ đổi trật tự.
Transformer encoder–decoder và cross-attention
Phần tiêu đề “Transformer encoder–decoder và cross-attention”Transformer (Vaswani và cộng sự, 2017) giữ khung encoder–decoder nhưng bỏ RNN, chỉ dùng attention. Bản gốc có 6 lớp encoder và 6 lớp decoder:
- Một lớp encoder gồm self-attention rồi mạng truyền thẳng, mỗi phần có kết nối tắt và chuẩn hóa lớp. Self-attention ở đây không có mặt nạ: mỗi từ nguồn nhìn được mọi từ nguồn, cả trước lẫn sau.
- Một lớp decoder có ba phần: self-attention có mặt nạ nhân quả trên các token đích, rồi cross-attention nhìn sang encoder, rồi mạng truyền thẳng.
Cross-attention dùng đúng công thức attention mà bạn đã gặp. Khác biệt nằm ở nguồn của ba ma trận. Gọi là đầu ra của lớp encoder cuối, mỗi hàng một vị trí nguồn, và là trạng thái của decoder (đầu ra của phần self-attention ngay trước), mỗi hàng một vị trí đích. Với các ma trận chiếu học được , , :
- Truy vấn từ decoder, khóa và giá trị từ encoder. Mỗi vị trí đích “hỏi”, các vị trí nguồn “trả lời”.
- Ma trận điểm có kích thước . Hàng là các điểm của vị trí đích trên mọi vị trí nguồn; softmax theo hàng cho đúng vai trò của ở trên.
- Không có mặt nạ nhân quả. Câu nguồn đã có đủ từ đầu, không có vị trí nào là “tương lai”. Chỉ self-attention của decoder mới cần mặt nạ.
- Mọi lớp decoder dùng cùng , nhưng mỗi lớp có ma trận chiếu riêng. Công thức trên là một đầu; Transformer dùng nhiều đầu song song. Với đầu, mỗi đầu chiếu xuống chiều và chia cho .
Vaswani và cộng sự viết rằng cross-attention bắt chước cơ chế attention encoder–decoder của các mô hình seq2seq trước đó. Hai khác biệt chính: điểm là tích vô hướng thay cho mạng nhỏ , và mọi vị trí được tính song song thay vì từng bước như RNN. Bản lớn của họ đạt BLEU 28.4 trên tập dịch Anh–Đức WMT 2014.
Huấn luyện bằng teacher forcing
Phần tiêu đề “Huấn luyện bằng teacher forcing”Khi huấn luyện, ta đã có câu đích thật. Vậy đầu vào của decoder là chính câu đích, dịch sang phải một vị trí (shift right), bắt đầu bằng token mở đầu <BOS>; còn nhãn là câu đích không dịch:
| Vị trí | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| Đầu vào decoder | <BOS> | con | mèo | đen |
| Nhãn cần đoán | con | mèo | đen | <EOS> |
Cách đưa đáp án thật làm đầu vào này gọi là teacher forcing. Nhờ mặt nạ nhân quả, vị trí chỉ thấy <BOS> và , nên một lần chạy cho cả dự đoán, như với GPT. Hàm mất mát là cross-entropy trung bình trên các vị trí:
Trong công thức này, là token thật. Khi lô có nhiều cặp câu, ta lấy trung bình thêm trên các cặp.
Exposure bias: lúc học và lúc dùng không giống nhau
Phần tiêu đề “Exposure bias: lúc học và lúc dùng không giống nhau”Khi dùng mô hình, không có câu đích thật. Decoder phải ăn lại token do chính nó sinh ra: trong công thức giờ là các token mô hình đã chọn. Ranzato và cộng sự (2016) gọi sự lệch này là exposure bias: lúc huấn luyện, mô hình chỉ được thấy tiền tố đúng, chưa từng tập tiếp tục từ một tiền tố có lỗi. Một token sai sớm làm tiền tố khác mọi thứ nó đã thấy, và lỗi có thể dồn lên nhau.
Teacher forcing không phải lỗi: nó nhanh, song song và ổn định. Vấn đề là khoảng cách giữa hai chế độ. Một hướng xử lý là scheduled sampling (Bengio và cộng sự, 2015): trong lúc huấn luyện, thỉnh thoảng thay token thật bằng token mô hình tự sinh, tỉ lệ thay tăng dần. Mức nặng nhẹ của exposure bias tùy bài toán: ở ô PyTorch bên dưới, tác vụ quá dễ nên không thấy nó.
Giải mã: tham lam và beam search
Phần tiêu đề “Giải mã: tham lam và beam search”Khi đã có mô hình, ta muốn câu có lớn nhất. Thử hết thì không thể: với từ vựng 32 000 token và câu 20 token, có chuỗi (tự tính).
Giải mã tham lam (greedy decoding) chọn token xác suất cao nhất ở mỗi bước. Nhanh, nhưng một lựa chọn sớm không bao giờ được xem lại.
Beam search giữ giả thuyết dở dang tốt nhất (gọi là chùm, beam), là độ rộng chùm (beam width). Điểm của một giả thuyết là tổng log-xác suất của các token của nó:
Đẳng thức thứ hai có được vì logarit của tích bằng tổng các logarit. Mỗi bước:
- Nối mỗi giả thuyết trong chùm với mọi token có thể, cộng thêm log-xác suất của token đó.
- Trong mọi ứng viên, giữ ứng viên điểm cao nhất.
- Ứng viên nào vừa sinh
<EOS>thì tách ra danh sách “đã xong”. Dừng khi chùm rỗng, hoặc khi đủ độ dài tối đa.
chính là giải mã tham lam. Sutskever và cộng sự thấy đã khá tốt và lấy được phần lớn lợi ích của beam search; Vaswani và cộng sự dùng . Beam search vẫn chỉ là xấp xỉ: không đảm bảo tìm ra câu có xác suất cao nhất.
Phạt độ dài. Mỗi token cộng thêm một số vào điểm, nên câu càng dài điểm càng âm. Beam search thô vì vậy thiên về câu ngắn, kể cả câu cắt cụt. Wu và cộng sự (2016) chia điểm cho một hàm tăng theo độ dài:
- là số token của câu đích. Bài này tính cả
<EOS>; bài báo không nói rõ điểm này, và các thư viện có thể đếm khác nhau. - cho : quay về beam search thô. càng lớn, câu dài càng được bù nhiều.
- Không có “đúng” chung. Nó được chỉnh trên tập kiểm định (validation set): Wu và cộng sự dùng cùng một khoản phạt khác tên là coverage penalty, Vaswani và cộng sự dùng .
T5: mọi việc là văn bản sang văn bản
Phần tiêu đề “T5: mọi việc là văn bản sang văn bản”Raffel và cộng sự (2020) đẩy ý seq2seq tới cùng với T5: mọi tác vụ đều viết thành “văn bản vào, văn bản ra”. Chỉ cần thêm một tiền tố tác vụ (task prefix) vào đầu câu vào. Các ví dụ trong bài báo:
| Đầu vào | Đầu ra |
|---|---|
| translate English to German: That is good. | Das ist gut. |
| cola sentence: The course is jumping well. | not acceptable |
| stsb sentence1: The rhino grazed on the grass. sentence2: A rhino is grazing in a field. | 3.8 |
Phân loại (câu có đúng ngữ pháp không) cho ra chữ “not acceptable”; chấm độ giống nhau của hai câu cho ra chuỗi “3.8”. Cùng một mô hình, cùng hàm mất mát, cùng cách giải mã cho mọi việc. Ý “nói việc cần làm bằng chữ ở đầu đầu vào” là họ hàng gần của prompt chỉ dẫn ở bài Prompt Engineering.
T5 dùng Transformer encoder–decoder chuẩn, huấn luyện bằng teacher forcing. Bản cơ sở có 12 khối ở mỗi bên, khoảng 220 triệu tham số. Trong thí nghiệm so kiến trúc của họ, với mục tiêu khử nhiễu (denoising) mà T5 chọn, bản encoder–decoder cho điểm cao nhất trên mọi tác vụ, hơn các biến thể decoder-only (chỉ có decoder) cùng chi phí tính toán (dù bản encoder–decoder có gấp đôi số tham số).
So với decoder-only
Phần tiêu đề “So với decoder-only”GPT và các họ mô hình như LLaMA là decoder-only: không có encoder, không có cross-attention. Câu vào và câu ra nằm nối nhau trong một chuỗi, và mọi vị trí đều dùng mặt nạ nhân quả. Giữa hai kiểu có prefix LM: vẫn một chồng khối, nhưng phần câu vào nhìn đầy đủ hai chiều, chỉ phần câu ra là nhân quả.
| Tiêu chí | Encoder–decoder (T5) | Prefix LM | Decoder-only (GPT) |
|---|---|---|---|
| Câu vào nhìn | Cả hai chiều, trong encoder | Cả hai chiều | Chỉ về trước (nhân quả) |
| Câu ra nhìn | Nhân quả, cộng cross-attention sang câu vào | Nhân quả, cả câu vào | Nhân quả, cả câu vào |
| Số chồng khối | Hai | Một | Một |
Decoder-only làm được việc seq2seq bằng cách đặt câu vào làm phần đầu của chuỗi, như khi bạn gửi prompt cho một LLM. Encoder–decoder vẫn là lựa chọn tự nhiên cho dịch và tóm tắt, nơi câu vào và câu ra tách bạch.
Giới hạn
Phần tiêu đề “Giới hạn”- Beam search tìm câu xác suất cao, không phải câu hay. Với sinh văn bản mở, Holtzman và cộng sự (2020) cho rằng giải mã theo xác suất cao nhất làm văn nhạt và lặp lạ thường; họ đề xuất top-p (bài Sử dụng API văn bản).
- Văn bản nguồn rất dài vẫn đắt. Self-attention của encoder tốn điểm, cross-attention tốn , cho mỗi đầu ở mỗi lớp.
Minh họa
Phần tiêu đề “Minh họa”Hình dưới đặt encoder và decoder cạnh nhau. Hãy nhìn các mũi tên đi vào khối cross-attention: truy vấn đến từ phía decoder, còn khóa và giá trị đến từ đầu ra của encoder.
Bảng dưới tóm lại ba kiểu attention trong một Transformer encoder–decoder. Hàng cuối là chỗ hai nửa gặp nhau.
| Kiểu attention | Ở đâu | từ | , từ | Mặt nạ nhân quả | Kích thước ma trận điểm |
|---|---|---|---|---|---|
| Self-attention | Encoder | Câu nguồn | Câu nguồn | Không | |
| Self-attention có mặt nạ | Decoder | Câu đích | Câu đích | Có | |
| Cross-attention | Decoder | Câu đích | Đầu ra encoder | Không |
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: ô beam search với Python 3.14 và NumPy 2.4 (bản có trong Pyodide), ô Transformer với Python 3.14 và PyTorch 2.14 trên CPU. Ô beam search chạy ngay trên trang. Ô Transformer cần PyTorch nên chạy trên máy bạn.
Tham lam và beam search bằng NumPy
Phần tiêu đề “Tham lam và beam search bằng NumPy”Để thấy cơ chế mà không cần huấn luyện gì, ô dưới dùng một “decoder giả” với bảng xác suất tự đặt. Hãy coi như decoder đã đọc xong câu nguồn. Để code ngắn, xác suất token kế chỉ phụ thuộc token ngay trước; decoder thật dùng cả câu nguồn và mọi token đã sinh.
| Token trước | Token kế và xác suất |
|---|---|
<s> (bắt đầu) | con 0.5, đàn 0.4, <eos> 0.1 |
| con | mèo 0.34, chó 0.33, chim 0.33 |
| đàn | chim 0.9, mèo 0.1 |
| mèo, chó, bay | <eos> 1.0 |
| chim | <eos> 0.52, bay 0.48 |
Trước khi chạy, hãy tự tính: câu “con mèo” có xác suất , còn câu “đàn chim” có . Câu nào lớn hơn, và giải mã tham lam sẽ chọn câu nào?
Ô chạy ba cấu hình: tham lam (), beam search không phạt độ dài, và với . Mỗi dòng in câu được chọn, xác suất của cả câu p, và điểm s đã chia cho .
Tham lam và beam search trên bảng xác suất giả lập
# Giải mã tham lam và beam search trên một bảng xác suất giả lập (số tự đặt)import numpy as np
# P[trước][kế] = p(token kế | token trước), như thể decoder đã đọc câu nguồnP = { "<s>": {"con": 0.5, "đàn": 0.4, "<eos>": 0.1}, "con": {"mèo": 0.34, "chó": 0.33, "chim": 0.33}, "đàn": {"chim": 0.9, "mèo": 0.1}, "mèo": {"<eos>": 1.0}, "chó": {"<eos>": 1.0}, "chim": {"<eos>": 0.52, "bay": 0.48}, "bay": {"<eos>": 1.0},}
def lp(n, alpha): # phạt độ dài của Wu và cộng sự (2016) return ((5 + n) / 6) ** alpha
def beam_search(width, alpha=0.0, max_len=6): beams, done = [(0.0, ["<s>"])], [] # (tổng log-xác suất, chuỗi) for _ in range(max_len): cand = [(score + np.log(p), seq + [tok]) # nối mọi token có thể for score, seq in beams for tok, p in P[seq[-1]].items()] cand.sort(key=lambda c: -c[0]) beams = [] for score, seq in cand[:width]: # chỉ giữ width ứng viên tốt nhất (done if seq[-1] == "<eos>" else beams).append((score, seq)) if not beams: break # chọn theo điểm đã chia lp; độ dài n tính cả <eos>, không tính <s> score, seq = max(done, key=lambda c: c[0] / lp(len(c[1]) - 1, alpha)) return " ".join(seq[1:-1]), score, score / lp(len(seq) - 1, alpha)
for width, alpha in [(1, 0.0), (2, 0.0), (2, 0.6)]: text, score, s = beam_search(width, alpha) print(f"B={width} alpha={alpha}: '{text}' p={np.exp(score):.3f} s={s:.3f}")The first run downloads Python, about 10 MB.
Đọc kết quả:
- Tham lam ra “con mèo”, . Bước đầu nó chọn “con” (0.5 > 0.4), rồi gặp một nhánh phẳng: “mèo”, “chó”, “chim” gần như ngang nhau, nên tích chỉ còn .
- Beam ra “đàn chim”, . Nó giữ cả “đàn” ở bước đầu, và nhánh đó có “chim” chắc tới 0.9. Đây là câu có xác suất cao hơn, mà tham lam bỏ lỡ chỉ vì một lựa chọn sớm.
- Thêm , beam đổi sang “đàn chim bay”, , dù xác suất thô thấp hơn “đàn chim” (0.187). Tính tay với độ dài tính cả
<eos>: “đàn chim” có , , chia được ; “đàn chim bay” có , , chia được , cao hơn.
Bảng này tự đặt để thấy cơ chế, không nói gì về việc phạt độ dài tốt hay xấu cho một bài dịch thật. Một chi tiết nữa của code: việc cắt chùm dùng log-xác suất thô, chỉ bước chọn cuối mới chia cho . Như vậy beam search không đảm bảo tìm ra câu có điểm cao nhất.
Transformer encoder–decoder nhỏ bằng PyTorch
Phần tiêu đề “Transformer encoder–decoder nhỏ bằng PyTorch”File dưới đây huấn luyện một Transformer encoder–decoder để đảo ngược dãy 6 chữ số: nguồn 9 7 9 6 0 9, đích 9 0 6 9 7 9. Tác vụ đồ chơi này có đủ mọi phần của seq2seq, và câu đích phụ thuộc vào cả câu nguồn: token đích đầu tiên là token nguồn cuối cùng, nên decoder buộc phải nhìn sang encoder.
- Mô hình:
nn.Transformercó 2 lớp encoder, 2 lớp decoder, , 4 đầu, mạng truyền thẳng 128 chiều, cộng một lớp nhúng token và một lớp nhúng vị trí học được. Từ vựng có 12 token:<BOS>,<EOS>và 10 chữ số. - Mặt nạ:
generate_square_subsequent_masktrả về ma trận số thực, ở các ô phía trên đường chéo, được cộng thẳng vào điểm của self-attention trong decoder. Encoder và cross-attention không nhận mặt nạ nào. - Teacher forcing: hàm
batchtạo đầu vào decoder là<BOS>cộng đáp án, nhãn là đáp án cộng<EOS>, đúng như bảng ở mục Lý thuyết. Mỗi bước sinh 64 dãy ngẫu nhiên mới; vòng lặp chạy 301 bước bằng AdamW. - Đánh giá trên 500 dãy mới, theo hai cách: tỉ lệ khớp hoàn toàn cả 7 token khi được đưa đáp án (teacher forcing), và khi tự sinh tham lam từ
<BOS>, mỗi bước nối token vừa chọn vào.
Cách cài PyTorch bản chỉ chạy CPU có ở bài Xây dựng LLM đầu tiên.
Run on your machine
Transformer encoder–decoder đảo ngược dãy số
- Install
pip install torch- Hardware
- CPU là đủ, không cần GPU. Khoảng 20 tới 40 giây trên máy phát triển, có thể vài phút trên máy chậm hoặc đang bận
# Transformer encoder-decoder nhỏ học đảo ngược một dãy 6 chữ sốimport torchfrom torch import nn
torch.manual_seed(0)BOS, EOS, L, D = 0, 1, 6, 64 # L: độ dài dãy, D: số chiềuTOK = ["<BOS>", "<EOS>"] + list("0123456789") # chữ số mang id 2..11V = len(TOK) # từ vựng 12 tokenCAUSAL = True # đổi thành False để thử bỏ mặt nạ của decoder
def batch(n): src = torch.randint(2, V, (n, L)) tgt = src.flip(1) # đáp án: câu nguồn viết ngược dec_in = torch.cat([torch.full((n, 1), BOS), tgt], 1) # dịch phải dec_out = torch.cat([tgt, torch.full((n, 1), EOS)], 1) # nhãn + <EOS> return src, dec_in, dec_out
class Seq2Seq(nn.Module): def __init__(self): super().__init__() self.emb, self.pos = nn.Embedding(V, D), nn.Embedding(L + 1, D) self.tf = nn.Transformer(D, nhead=4, num_encoder_layers=2, num_decoder_layers=2, dim_feedforward=128, dropout=0.0, batch_first=True) self.out = nn.Linear(D, V)
def embed(self, x): # nhúng token cộng nhúng vị trí return self.emb(x) + self.pos(torch.arange(x.size(1)))
def forward(self, src, dec_in): n = dec_in.size(1) mask = self.tf.generate_square_subsequent_mask(n) if CAUSAL else None h = self.tf(self.embed(src), self.embed(dec_in), tgt_mask=mask, tgt_is_causal=CAUSAL) return self.out(h) # (lô, n, V) logit
model = Seq2Seq()opt = torch.optim.AdamW(model.parameters(), lr=3e-3)for step in range(301): # teacher forcing: decoder ăn đáp án thật src, dec_in, dec_out = batch(64) z = model(src, dec_in).transpose(1, 2) # cross_entropy cần lớp ở chiều 1 loss = nn.functional.cross_entropy(z, dec_out) opt.zero_grad() loss.backward() opt.step() if step % 100 == 0: print(f"step {step:3d} loss {loss.item():.3f}")
model.eval()with torch.no_grad(): src, dec_in, dec_out = batch(500) # 500 dãy mới forced = (model(src, dec_in).argmax(-1) == dec_out).all(1).float().mean() seq = torch.full((500, 1), BOS) # sinh tự do: ăn lại token của chính nó for _ in range(L + 1): nxt = model(src, seq)[:, -1].argmax(-1, keepdim=True) # tham lam seq = torch.cat([seq, nxt], dim=1) free = (seq[:, 1:] == dec_out).all(1).float().mean()print(f"exact match, teacher forcing: {forced:.3f} greedy: {free:.3f}")print("src:", *[TOK[i] for i in src[0].tolist()])print("out:", *[TOK[i] for i in seq[0, 1:].tolist()])Sample output
From one real run.
step 0 loss 2.608step 100 loss 0.003step 200 loss 0.001step 300 loss 0.001exact match, teacher forcing: 1.000 greedy: 1.000src: 9 7 9 6 0 9out: 9 0 6 9 7 9 <EOS>Đọc kết quả:
- Bước 0: loss 2.608, gần , mức của mô hình đoán đều trên 12 token.
- Bước 100: loss đã xuống 0.003. Đảo dãy là việc rất hợp với cross-attention: vị trí đích chỉ cần nhìn đúng vị trí nguồn .
- Cả hai cách đo đều khớp hoàn toàn 1.000 trên 500 dãy chưa thấy, và dãy mẫu được đảo đúng, kết thúc bằng
<EOS>. Ở đây sinh tự do tốt ngang teacher forcing, nên không thấy exposure bias: tác vụ quá dễ để mô hình mắc lỗi đầu tiên. Đừng lấy kết quả này để kết luận exposure bias không có thật.
Seed cố định nên mỗi lần chạy trên cùng máy cho cùng kết quả; trên máy khác hoặc bản PyTorch khác, số có thể lệch đôi chút. Ở dòng 9, CAUSAL = True bật mặt nạ của decoder. Hộp đầu tiên ở mục dưới cho thấy chuyện gì xảy ra khi đổi nó thành False.
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:
- Seq2seq học : một mô hình ngôn ngữ có điều kiện theo câu nguồn, cho câu ra có độ dài và trật tự tự do, dừng khi sinh
<EOS>. - Encoder–decoder RNN nén câu nguồn vào một vectơ cố định, và tụt điểm với câu dài. Attention cho mỗi bước một trung bình có trọng số riêng của câu nguồn. Trong Transformer, đó là cross-attention: từ decoder, và từ encoder, ma trận điểm , không có mặt nạ nhân quả.
- Huấn luyện bằng teacher forcing (đầu vào là đáp án dịch phải, một lần chạy cho mọi vị trí), dùng bằng sinh tự do. Khoảng cách giữa hai chế độ là exposure bias; luôn đánh giá bằng cách cho mô hình tự sinh.
- Beam search giữ giả thuyết theo tổng log-xác suất, tốt hơn tham lam nhưng vẫn là xấp xỉ, và cần chia cho để không thiên vị câu ngắn. T5 biến mọi tác vụ thành văn bản sang văn bản bằng tiền tố tác vụ.
Đọc thêm:
- Sutskever, I., Vinyals, O., Le, Q. V. (2014). Sequence to Sequence Learning with Neural Networks, arXiv:1409.3215, mục 2 và 3. Bài gốc của khung seq2seq bằng LSTM, có mẹo đảo câu nguồn và thí nghiệm về độ rộng chùm.
- Bahdanau, D., Cho, K., Bengio, Y. (2015). Neural Machine Translation by Jointly Learning to Align and Translate, ICLR 2015, mục 3 và 5. Attention đầu tiên cho dịch máy, có đồ thị điểm theo độ dài câu và các bản đồ căn chỉnh. Bài báo dùng cho độ dài câu nguồn, chỗ bài này viết .
- Vaswani, A. và cộng sự (2017). Attention Is All You Need, NIPS 2017, mục 3.1 và 3.2.3. Transformer encoder–decoder và ba cách dùng attention. Bài báo dùng cho số chiều của khóa, chỗ bài này viết .
- Raffel, C. và cộng sự (2020). Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer, JMLR 21(140), mục 2.4 và 3.2. T5, tiền tố tác vụ, và phép so encoder–decoder với decoder-only và prefix LM.
- Wu, Y. và cộng sự (2016). Google’s Neural Machine Translation System: Bridging the Gap between Human and Machine Translation, arXiv:1609.08144, mục 7. Beam search có phạt độ dài và coverage penalty, cùng các mẹo cắt tỉa chùm trong một hệ thống dịch thậ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