GRU: reset gate, update gate và trạng thái ẩn
Mục tiêu
Sau bài này, bạn cần:
- giải thích vì sao GRU được xem là một recurrent unit gọn hơn LSTM;
- phân biệt vai trò của reset gate và update gate ;
- đọc được các phương trình của GRU và theo dõi dòng thông tin qua một cell;
- hiểu vì sao GRU chỉ cần một trạng thái thay vì cặp như LSTM;
- hiểu ở mức ý tưởng vì sao phép nội suy giữa trạng thái cũ và trạng thái ứng viên giúp học phụ thuộc dài;
- nhận ra khác biệt nhỏ giữa công thức GRU gốc và cách
torch.nn.GRUtính candidate state; - tránh kết luận sai kiểu “GRU luôn nhanh hơn LSTM” hoặc “GRU giải quyết hoàn toàn vanishing gradient”.
1. Từ Vanilla RNN và LSTM đến GRU
Vanilla RNN cập nhật trạng thái bằng một phép biến đổi kiểu:
Cấu trúc rất gọn, nhưng mỗi bước gần như tạo lại toàn bộ trạng thái mới. Khi cần học quan hệ cách nhau nhiều bước thời gian, gradient phải đi qua một chuỗi dài các phép biến đổi và có thể suy giảm hoặc tăng mạnh.
LSTM xử lý vấn đề này bằng một cơ chế bộ nhớ có cổng. Phiên bản LSTM hiện đại thường có:
- cell state ;
- hidden state ;
- forget gate;
- input gate;
- output gate;
- candidate state.
GRU - Gated Recurrent Unit - giữ ý tưởng cốt lõi “hãy để mạng tự học khi nào cần giữ và khi nào cần cập nhật”, nhưng gộp bớt thành phần.
GRU xuất phát từ recurrent hidden unit được Cho et al. đề xuất năm 2014 trong mô hình RNN Encoder-Decoder cho dịch máy thống kê. Thiết kế này được mô tả là lấy cảm hứng từ LSTM nhưng đơn giản hơn về tính toán và triển khai.
Điểm cần nhớ ngay từ đầu:
GRU không phải “LSTM bị cắt bớt tùy ý”. Nó dùng một cách tổ chức trạng thái khác.
2. GRU chỉ truyền một trạng thái
LSTM truyền hai vector:
Trong khi GRU chủ yếu truyền:
Không có cell state riêng.
Điều đó không có nghĩa GRU “không có bộ nhớ”. Trong RNN, chính trạng thái hồi tiếp mang thông tin từ quá khứ. GRU điều khiển việc giữ hay thay đổi trạng thái đó bằng các cổng.
Hai cổng chính là:
- reset gate - điều khiển lượng thông tin quá khứ được dùng để tạo nội dung ứng viên;
- update gate - điều khiển mức giữ trạng thái cũ so với cập nhật bằng nội dung ứng viên.
3. Bốn phương trình cốt lõi
Ta dùng dạng gần với công thức gốc của Cho et al. (2014).
Reset gate
Update gate
Trạng thái ứng viên
Trạng thái mới
Trong đó:
- là sigmoid;
- là phép nhân từng phần tử;
- là các vector;
- các ma trận xử lý đầu vào hiện tại;
- các ma trận xử lý trạng thái hồi tiếp.
4. Toàn bộ một GRU cell
Có thể đọc cell theo hai câu hỏi:
- Khi tạo một phương án trạng thái mới, cần nhìn lại quá khứ bao nhiêu? - .
- Sau khi đã có phương án mới, nên giữ trạng thái cũ hay chuyển sang trạng thái mới bao nhiêu? - .
5. Reset gate: quá khứ nào cần dùng để tạo trạng thái ứng viên?
Reset gate được tính từ cả đầu vào hiện tại và trạng thái trước đó:
Vì sigmoid cho giá trị giữa 0 và 1, từng chiều của có thể điều tiết từng chiều của .
Trong công thức gốc:
Nếu một phần tử của gần 0, phần tương ứng của trạng thái cũ gần như bị loại khỏi quá trình tạo candidate.
Nếu gần 1, candidate có thể dùng mạnh thông tin cũ ở chiều đó.
Ví dụ ý tưởng
Giả sử mô hình đọc chuỗi sự kiện:
Sinh viên A -> đăng nhập -> làm quiz -> đăng xuất -> Sinh viên B -> đăng nhập
Tại ranh giới sang “Sinh viên B”, một số thông tin gắn riêng với sinh viên A có thể không còn hữu ích khi tạo trạng thái mới. Reset gate cho phép candidate giảm phụ thuộc vào các chiều trạng thái đó.
Không nên hiểu rằng toàn bộ GRU “reset về 0”. Gate là vector và chỉ tham gia vào cách tính candidate.
6. Update gate: giữ trạng thái cũ hay nhận trạng thái mới?
Phương trình quan trọng nhất của GRU là:
Đây là phép nội suy từng phần tử giữa:
- trạng thái cũ ;
- trạng thái ứng viên .
Khi gần 1
Mô hình chủ yếu giữ trạng thái cũ.
Khi gần 0
Mô hình chủ yếu cập nhật bằng candidate mới.
Khi ở giữa
Ví dụ :
Trạng thái thay đổi mềm thay vì bị thay hoàn toàn.
7. Cẩn thận với tên gọi và quy ước của update gate
Trong công thức gốc của Cho et al.:
Do đó lớn nghĩa là giữ nhiều trạng thái cũ.
Một số tài liệu khác có thể viết công thức với ký hiệu hoặc quy ước ngược lại. Vì vậy khi đọc paper hay code:
Đừng suy luận từ tên biến. Hãy nhìn trực tiếp phương trình cập nhật .
Đây là một thói quen đọc paper quan trọng vì cùng tên “update gate” nhưng ký hiệu trong từng nguồn có thể khác nhau.
8. Reset gate và update gate không làm cùng một việc
Hai gate thường bị nhầm.
Reset gate
Tác động vào câu hỏi:
Candidate mới có cần dựa nhiều vào quá khứ không?
Update gate
Tác động vào câu hỏi:
Trạng thái cuối cùng ở bước này nên giống trạng thái cũ hay candidate mới?
Một reset gate nhỏ không đồng nghĩa trạng thái cũ chắc chắn biến mất khỏi , vì update gate vẫn có thể giữ trực tiếp .
9. Ví dụ số một chiều
Giả sử một chiều trạng thái có:
Sau khi tính reset gate và candidate, ta có:
Giả sử update gate:
Khi đó:
Ta có thể đọc kết quả như sau:
- 75% đường cập nhật trực tiếp ưu tiên trạng thái cũ;
- 25% ưu tiên candidate mới;
- candidate đã được tạo sau khi reset gate giảm ảnh hưởng của một phần quá khứ.
Đây chỉ là cách diễn giải ở một chiều để hiểu cơ chế. Trong mô hình thật, các giá trị là vector và chúng được học từ dữ liệu.
10. Một ví dụ nhiều chiều
Giả sử:
và:
Ta chưa cần biết candidate cụ thể để thấy rằng:
- chiều 1 có xu hướng giữ mạnh trạng thái cũ;
- chiều 2 sẵn sàng cập nhật mạnh;
- chiều 3 pha trộn tương đối cân bằng.
Vì gate là vector, một GRU cell có thể đồng thời giữ thông tin dài hạn ở một số chiều và thay đổi nhanh ở những chiều khác.
Cho et al. mô tả chính khả năng này như việc các hidden unit có thể học những thang thời gian khác nhau.
11. Vì sao update gate có thể giúp học phụ thuộc dài?
Vanilla RNN luôn có dạng biến đổi mạnh:
Trong GRU, tồn tại một đường cộng trực tiếp:
Nếu tại nhiều bước liên tiếp mô hình học tương đối lớn, thông tin trong có thể được truyền tiếp với mức thay đổi nhỏ.
Đây là khác biệt quan trọng so với việc buộc toàn bộ trạng thái phải đi qua một phép biến đổi phi tuyến hoàn toàn mới ở mỗi bước.
Nhưng không nên nói quá mức
Không thể viết đơn giản:
và coi đó là toàn bộ gradient.
Lý do:
- phụ thuộc vào ;
- phụ thuộc vào ;
- cũng phụ thuộc vào .
Gradient thật còn nhiều nhánh khác.
Điểm đúng ở mức ý tưởng là GRU tạo đường cập nhật cộng và có cổng giúp trạng thái cũ có thể được truyền trực tiếp hơn. GRU làm việc học phụ thuộc dài dễ hơn Vanilla RNN trong nhiều trường hợp, nhưng không đảm bảo loại bỏ hoàn toàn vanishing hay exploding gradient.
12. GRU liên hệ với LSTM như thế nào?
Một cách so sánh cơ chế:
| Thành phần | LSTM | GRU |
|---|---|---|
| Trạng thái hồi tiếp | và | |
| Cổng chính | forget, input, output | reset, update |
| Candidate | ||
| Đường giữ thông tin | qua cell state | trực tiếp trong |
| Đầu ra của cell | được điều tiết bởi output gate | chính là |
GRU không có một output gate riêng. Trạng thái sau khi cập nhật vừa là bộ nhớ hồi tiếp vừa là biểu diễn đầu ra của recurrent unit.
13. Vì sao gọi GRU là “gọn hơn”?
Với input dimension và hidden dimension , bỏ qua chi tiết bias:
Vanilla RNN
Cần một nhóm phép biến đổi chính:
GRU
Có ba nhóm affine chính:
- reset gate;
- update gate;
- candidate.
Do đó số trọng số lõi xấp xỉ:
LSTM
Có bốn nhóm:
- forget gate;
- input gate;
- output gate;
- candidate.
Do đó:
Với cùng và , phần recurrent affine của GRU có số trọng số bằng khoảng:
so với LSTM.
Nói cách khác, ít hơn khoảng 25% trọng số recurrent trong cấu hình cơ bản cùng kích thước.
Điều này không đồng nghĩa mô hình chắc chắn chạy nhanh hơn đúng 25%. Tốc độ thực tế còn phụ thuộc:
- implementation;
- kernel GPU/CPU;
- batch size;
- sequence length;
- memory access;
- backend như cuDNN.
14. Một chi tiết implementation quan trọng: paper gốc và PyTorch không hoàn toàn giống nhau
Công thức candidate trong paper gốc có dạng:
Tức là reset gate được nhân với hidden state trước khi qua recurrent matrix .
Trong torch.nn.GRU, tài liệu PyTorch mô tả candidate theo dạng:
Tức là recurrent linear transformation được tính trước, sau đó mới nhân reset gate.
PyTorch ghi rõ đây là khác biệt có chủ đích để tăng hiệu quả implementation.
Hệ quả khi học và đọc code
- về mặt ý tưởng, reset gate vẫn điều tiết ảnh hưởng của quá khứ lên candidate;
- nhưng nếu bạn tự cài GRU theo paper rồi so từng số với
nn.GRU, không nên kỳ vọng kết quả trung gian giống hệt chỉ vì dùng cùng tên tham số; - khi tái lập paper, phải kiểm tra đúng biến thể/công thức của framework.
15. Tự viết một GRU cell nhỏ để nhìn gate
Ví dụ sau không nhằm thay nn.GRU, mà để quan sát các giá trị bên trong.
import torch
import torch.nn as nn
class SimpleGRUCell(nn.Module):
def __init__(self, input_size, hidden_size):
super().__init__()
self.hidden_size = hidden_size
self.reset = nn.Linear(input_size + hidden_size, hidden_size)
self.update = nn.Linear(input_size + hidden_size, hidden_size)
self.candidate = nn.Linear(input_size + hidden_size, hidden_size)
def forward(self, x_t, h_prev):
joined = torch.cat([x_t, h_prev], dim=-1)
r_t = torch.sigmoid(self.reset(joined))
z_t = torch.sigmoid(self.update(joined))
candidate_input = torch.cat([x_t, r_t * h_prev], dim=-1)
h_tilde = torch.tanh(self.candidate(candidate_input))
h_t = z_t * h_prev + (1 - z_t) * h_tilde
return h_t, r_t, z_t, h_tilde
torch.manual_seed(7)
cell = SimpleGRUCell(input_size=3, hidden_size=4)
x_t = torch.tensor([[0.4, -0.2, 0.7]])
h_prev = torch.tensor([[0.5, 0.1, -0.3, 0.8]])
h_t, r_t, z_t, h_tilde = cell(x_t, h_prev)
print("reset gate :", r_t.detach())
print("update gate :", z_t.detach())
print("candidate :", h_tilde.detach())
print("new state h_t :", h_t.detach())
Khi chạy, tập trung vào quan hệ:
h_prev -----> giữ theo z ------\
+ ---> h_t
candidate ---> nhận theo 1-z -/
Không cần cố gán nghĩa cố định cho từng hidden dimension. Các chiều ẩn là biểu diễn do mạng tự học.
16. Unroll GRU qua nhiều bước
Một GRU layer chỉ lặp lại cùng một cell theo thời gian với cùng bộ tham số.
Các hộp GRU cell trên sơ đồ không phải ba bộ tham số khác nhau. Chúng là cùng một hàm được áp dụng ở các thời điểm khác nhau.
Đây vẫn là recurrent neural network, vì:
và hidden state tiếp tục tạo đường phụ thuộc theo thời gian.
17. GRU vẫn được huấn luyện bằng BPTT
Có gate không làm thay đổi nguyên lý huấn luyện cơ bản.
Ta vẫn:
- unroll mô hình qua các bước;
- tính loss;
- backpropagate gradient qua computational graph theo thời gian;
- cập nhật các ma trận của reset gate, update gate và candidate.
Các gate được học bằng gradient descent như các tham số neural network khác. Ta không tự viết luật “nếu gặp dấu chấm thì reset” hay “nếu câu dài thì giữ”.
18. Reset gate có bắt buộc để GRU hoạt động tốt không?
Về mặt lịch sử, GRU gốc có cả reset và update gate. Nhưng các nghiên cứu về kiến trúc recurrent cho thấy có nhiều biến thể có thể hoạt động tốt, và không nên xem một cấu trúc là tối ưu tuyệt đối cho mọi nhiệm vụ.
Jozefowicz, Zaremba và Sutskever (2015) khảo sát hơn mười nghìn kiến trúc recurrent và tìm thấy các kiến trúc vượt LSTM/GRU trên một số nhiệm vụ nhưng không phải mọi nhiệm vụ.
Ý nghĩa sư phạm:
Các gate là một thiết kế học được để hỗ trợ dòng thông tin. Hiệu quả cuối cùng vẫn phải được kiểm chứng trên dữ liệu cụ thể.
19. Các lỗi hiểu thường gặp
Sai 1 - “Reset gate bằng 0 thì hidden state mới bằng 0”
Không đúng.
Reset gate chỉ tác động mạnh vào cách tạo candidate. Hidden state cuối còn phụ thuộc update gate:
Sai 2 - “Update gate gần 1 nghĩa là cập nhật nhiều”
Không đúng với quy ước công thức đang dùng ở đây.
Với:
gần 1 nghĩa là giữ cũ nhiều.
Sai 3 - “GRU không có cell state nên không học dài hạn”
Không đúng. Hidden state chính là trạng thái hồi tiếp mang thông tin. Update gate tạo đường giữ thông tin qua nhiều bước.
Sai 4 - “GRU luôn tốt hơn Vanilla RNN”
Không có đảm bảo như vậy cho mọi dữ liệu, mọi budget và mọi metric. Gated units thường thuận lợi hơn trên nhiệm vụ có phụ thuộc thời gian phức tạp, nhưng baseline đơn giản vẫn cần thiết.
Sai 5 - “GRU giải quyết xong exploding gradient”
Không đúng. Gradient clipping vẫn có thể cần thiết.
20. Kiểm tra nhanh
Câu 1
Với một chiều:
Tính .
Đáp án
Trạng thái mới vẫn gần trạng thái cũ vì update gate ưu tiên đường giữ.
Câu 2
Nếu rất nhỏ nhưng rất lớn, điều gì có thể xảy ra?
Gợi ý
Candidate mới ít dựa vào trạng thái cũ, nhưng hidden state cuối vẫn có thể giữ phần lớn qua nhánh .
Câu 3
Vì sao không nên nói gradient của GRU qua một bước đơn giản bằng ?
Gợi ý
Vì các gate và candidate cũng phụ thuộc vào , nên đạo hàm đầy đủ có thêm nhiều nhánh.
21. Bài tập
Bài 1 - theo dõi một cell
Cho:
Tính và giải thích chiều nào giữ trạng thái cũ mạnh hơn.
Bài 2 - giải thích reset gate
Một sinh viên nói:
“Reset gate quyết định trực tiếp bao nhiêu phần trăm hidden state cũ được copy sang hidden state mới.”
Hãy chỉ ra vì sao câu này chưa chính xác và viết lại cho đúng.
Bài 3 - đọc công thức framework
Đối chiếu công thức candidate của paper Cho et al. (2014) với tài liệu torch.nn.GRU.
Trả lời:
- phép nhân với reset gate nằm trước hay sau recurrent linear transformation ở mỗi nguồn;
- PyTorch giải thích lý do thay đổi là gì;
- sự khác biệt này có làm thay đổi ý tưởng chung của reset gate không.
Bài 4 - tự visualize gate
Chạy SimpleGRUCell ở trên cho một chuỗi 10 bước. Lưu r_t và z_t, sau đó vẽ heatmap có:
- trục ngang: timestep;
- trục dọc: hidden dimension;
- giá trị: gate activation.
Quan sát xem có hidden dimension nào thường giữ cao hơn các chiều khác hay không.
22. Những điều cần nhớ
- GRU là gated RNN với một hidden state chính .
- Reset gate điều tiết quá khứ khi tạo candidate.
- Update gate nội suy giữa trạng thái cũ và candidate mới.
- Với công thức gốc đang dùng, lớn nghĩa là giữ trạng thái cũ nhiều.
- GRU có ít nhóm affine hơn LSTM: ba nhóm so với bốn nhóm trong cấu hình cơ bản.
- Đường cập nhật cộng giúp thông tin cũ có thể được truyền qua nhiều bước thuận lợi hơn Vanilla RNN, nhưng không loại bỏ mọi vấn đề gradient.
torch.nn.GRUcó khác biệt nhỏ, có chủ đích, ở vị trí áp dụng reset gate so với công thức paper gốc.- GRU vẫn được huấn luyện bằng BPTT và vẫn cần đánh giá, debug, masking và quản lý trạng thái như các RNN khác.
Tài liệu tham khảo
- Cho, K., van Merrienboer, B., Gulcehre, C., Bahdanau, D., Bougares, F., Schwenk, H., & Bengio, Y. (2014). Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation. EMNLP 2014, 1724-1734. https://aclanthology.org/D14-1179/
- Chung, J., Gulcehre, C., Cho, K., & Bengio, Y. (2014). Empirical Evaluation of Gated Recurrent Neural Networks on Sequence Modeling. NIPS 2014 Deep Learning and Representation Learning Workshop. https://arxiv.org/abs/1412.3555
- Jozefowicz, R., Zaremba, W., & Sutskever, I. (2015). An Empirical Exploration of Recurrent Network Architectures. ICML 2015, PMLR 37, 2342-2350. https://proceedings.mlr.press/v37/jozefowicz15.html
- Bengio, Y., Simard, P., & Frasconi, P. (1994). Learning Long-Term Dependencies with Gradient Descent Is Difficult. IEEE Transactions on Neural Networks, 5(2), 157-166. https://doi.org/10.1109/72.279181
- Pascanu, R., Mikolov, T., & Bengio, Y. (2013). On the difficulty of training recurrent neural networks. ICML 2013, PMLR 28(3), 1310-1318. https://proceedings.mlr.press/v28/pascanu13.html
- PyTorch documentation. torch.nn.GRU. https://docs.pytorch.org/docs/stable/generated/torch.nn.GRU.html