LSTM: bộ nhớ, cell state và các cổng
Mục tiêu
Sau bài này, bạn cần:
- giải thích vì sao LSTM được đề xuất để xử lý khó khăn học phụ thuộc dài của RNN;
- phân biệt trạng thái ẩn và trạng thái bộ nhớ ;
- hiểu chức năng của cổng quên, cổng ghi và cổng đầu ra;
- đọc được các phương trình của LSTM hiện đại;
- hiểu vì sao đường cập nhật của giúp dòng gradient thuận lợi hơn Vanilla RNN;
- tránh một số cách diễn giải quá đơn giản như “LSTM giải quyết hoàn toàn vanishing gradient”.
1. Từ Vanilla RNN đến LSTM
Vanilla RNN cập nhật trạng thái bằng:
Mỗi bước, toàn bộ trạng thái cũ đi qua một phép biến đổi phi tuyến mới. Khi gradient đi ngược qua nhiều bước, nó phải đi qua chuỗi dài các phép nhân Jacobian. Đây là nguồn gốc của khó khăn khi học phụ thuộc dài.
Hochreiter và Schmidhuber (1997) đề xuất Long Short-Term Memory - LSTM để tạo một cơ chế lưu trữ có đường truyền lỗi ổn định hơn.
Một chi tiết lịch sử quan trọng
Bản LSTM năm 1997 không giống hoàn toàn LSTM chuẩn trong thư viện hiện nay.
- LSTM 1997 có cơ chế bộ nhớ, input gate và output gate.
- Forget gate được Gers, Schmidhuber và Cummins bổ sung năm 2000 để mạng có thể tự học khi nào cần xóa bớt trạng thái cũ.
Trong bài này, ta học LSTM hiện đại phổ biến, gồm ba cổng: forget, input và output.
2. LSTM có hai trạng thái
Vanilla RNN chủ yếu truyền một trạng thái:
LSTM truyền hai trạng thái:
- trạng thái bộ nhớ - đường lưu thông tin dài hạn;
- trạng thái ẩn - phần thông tin được đưa ra để tính bước tiếp theo và đầu ra.
Có thể hiểu đơn giản:
- là “dòng bộ nhớ nội bộ”;
- là “phần bộ nhớ được lộ ra ngoài ở bước hiện tại”.
Hai vector này có liên hệ chặt chẽ nhưng không phải cùng một thứ.
3. Các phương trình của LSTM hiện đại
Trước tiên ghép thông tin hiện tại và trạng thái trước đó để các cổng cùng xem:
Cổng quên
Cổng đầu vào (cổng ghi)
Nội dung ứng viên
Cập nhật bộ nhớ
Cổng đầu ra
Trạng thái ẩn mới
Trong đó là phép nhân từng phần tử.
4. Toàn bộ LSTM cell nhìn như thế nào?
Điểm nổi bật nằm ở dòng:
Bộ nhớ mới được tạo bằng giữ lại một phần bộ nhớ cũ + thêm một phần nội dung mới.
5. Vì sao dùng sigmoid cho các cổng?
Hàm sigmoid:
cho đầu ra trong khoảng:
Do đó mỗi phần tử của cổng có thể được xem như hệ số điều tiết:
- gần 0: gần như chặn thông tin;
- gần 1: gần như cho đi qua;
- ở giữa: giữ/ghi một phần.
Quan trọng: cổng là vector, không phải một công tắc duy nhất cho cả cell. Các chiều khác nhau của bộ nhớ có thể được giữ hoặc cập nhật với mức khác nhau.
6. Cổng quên làm gì?
và:
Ví dụ một chiều của lưu thông tin “chủ thể đang nói là sinh viên A”.
Nếu sang một đoạn hội thoại mới, mô hình có thể học đặt phần tử tương ứng trong gần 0 để giảm ảnh hưởng của thông tin cũ.
Nếu thông tin vẫn còn cần thiết, có thể gần 1.
Vì sao forget gate được thêm sau?
Gers et al. (2000) chỉ ra rằng khi xử lý luồng liên tục không có ranh giới reset rõ ràng, trạng thái có thể tích lũy không phù hợp. Forget gate cho phép LSTM học cách tự “reset mềm” từng thành phần bộ nhớ.
7. Cổng ghi và nội dung ứng viên
LSTM không chỉ quyết định có ghi hay không. Nó tách thành hai phần:
- viết bao nhiêu: ;
- viết nội dung gì: .
Sau đó:
Ví dụ với văn bản, nếu xuất hiện một thực thể mới quan trọng, một số chiều trong có thể mở để cập nhật bộ nhớ tương ứng.
8. Cổng đầu ra: bộ nhớ không nhất thiết lộ ra toàn bộ
Sau khi cập nhật , LSTM tính:
và:
Điều này cho phép mô hình giữ một số thông tin trong bộ nhớ mà chưa cần dùng ngay ở đầu ra hiện tại.
Một cách phân biệt hữu ích:
- : mô hình đang giữ gì;
- : ở bước này mô hình cho phần nào của trạng thái đó tham gia vào xử lý tiếp.
9. Ví dụ số một chiều
Giả sử tại một chiều bộ nhớ:
và các cổng cho:
Bộ nhớ mới:
Nghĩa là:
- phần lớn bộ nhớ cũ được giữ lại;
- một lượng nhỏ thông tin mới có giá trị âm được thêm vào.
Nếu:
thì:
Qua ví dụ này có thể thấy và có giá trị khác nhau.
10. Vì sao LSTM giúp dòng gradient tốt hơn?
Xét đường cập nhật trực tiếp của bộ nhớ:
Nếu chỉ nhìn nhánh trực tiếp từ sang :
Nếu một số chiều của được giữ gần 1, gradient trên đường bộ nhớ đó có thể truyền qua nhiều bước mà không bị nhân liên tiếp với một ma trận hồi tiếp đầy đủ như Vanilla RNN.
Đây là một trong những lý do quan trọng giúp LSTM học phụ thuộc dài tốt hơn RNN đơn giản.
Nhưng không nên nói quá mức
LSTM không bảo đảm gradient luôn ổn định.
- nếu liên tục nhỏ hơn 1, gradient vẫn có thể suy giảm;
- các nhánh khác của mạng vẫn chứa phép nhân phi tuyến;
- exploding gradient vẫn có thể xảy ra;
- chuỗi rất dài vẫn khó tối ưu và tốn tính toán.
Cách diễn đạt chính xác hơn:
LSTM tạo một cơ chế truyền và cập nhật trạng thái giúp việc học phụ thuộc dài thuận lợi hơn đáng kể so với Vanilla RNN.
11. Cổng của LSTM được học, không được viết bằng luật thủ công
Ta không lập trình:
nếu gặp dấu chấm → quên 80%
Thay vào đó, tham số của các cổng:
được học bằng gradient descent từ dữ liệu và loss.
Vì vậy LSTM không có “quy tắc nhớ” cố định. Mô hình học cách điều khiển bộ nhớ sao cho hữu ích cho nhiệm vụ.
12. LSTM có bao nhiêu tham số so với Vanilla RNN?
Vanilla RNN cần một phép biến đổi chính cho trạng thái.
LSTM hiện đại tính bốn nhóm giá trị:
- forget gate;
- input gate;
- candidate memory;
- output gate.
Do đó với cùng input_size và hidden_size, LSTM thường có nhiều tham số và nhiều phép tính hơn Vanilla RNN.
Đổi lại, nó có cơ chế kiểm soát bộ nhớ tốt hơn.
13. Những hiểu nhầm phổ biến
“Forget gate xóa toàn bộ ký ức”
Không đúng. là vector, nên LSTM có thể quên một số chiều và giữ các chiều khác.
” là long-term memory, là short-term memory”
Cách nói này có thể giúp nhớ tên, nhưng quá đơn giản. Cả hai đều là trạng thái của hệ thống và cùng tham gia vào động lực học. Tốt hơn nên gọi:
- : trạng thái bộ nhớ/cell state;
- : trạng thái ẩn/hidden state.
“LSTM không bị vanishing gradient”
Không chính xác. LSTM giảm khó khăn và tạo đường gradient thuận lợi hơn, chứ không loại bỏ mọi trường hợp vanishing gradient.
“Cổng quên có ngay trong paper LSTM 1997”
Không. Forget gate là cải tiến được công bố năm 2000.
14. Từ công thức sang cách triển khai thư viện
Trong PyTorch, nn.LSTM thực hiện cùng họ phương trình với các cổng:
- input ;
- forget ;
- candidate ;
- output .
Nó trả về cả trạng thái ẩn và trạng thái bộ nhớ cuối:
output, (h_n, c_n) = lstm(x)
Điều này khác nn.RNN, chỉ trả trạng thái ẩn.
Ta sẽ dùng chi tiết này ở bài thực hành tiếp theo.
15. Những điều cần nhớ
- LSTM được thiết kế để việc học phụ thuộc dài khả thi hơn Vanilla RNN.
- LSTM truyền hai trạng thái: và .
- Cổng quên điều khiển phần bộ nhớ cũ được giữ lại.
- Cổng ghi điều khiển lượng nội dung mới được thêm vào.
- Cổng đầu ra điều khiển phần trạng thái bộ nhớ được lộ ra thành .
- Cập nhật có đường cộng trực tiếp, thuận lợi hơn cho dòng gradient.
- Forget gate là cải tiến năm 2000, không phải thành phần của bản LSTM 1997 ban đầu.
Câu hỏi tự kiểm tra
- Vì sao LSTM cần cả và ?
- Nếu một phần tử của gần 1 và gần 0, điều gì xảy ra với chiều bộ nhớ tương ứng?
- Vì sao sigmoid phù hợp để làm cổng?
- Forget gate khác thao tác reset toàn bộ trạng thái như thế nào?
- LSTM có hoàn toàn loại bỏ exploding gradient không?
Tài liệu tham khảo
- Hochreiter, S., & Schmidhuber, J. (1997). Long Short-Term Memory. Neural Computation, 9(8), 1735-1780. https://doi.org/10.1162/neco.1997.9.8.1735
- Gers, F. A., Schmidhuber, J., & Cummins, F. (2000). Learning to Forget: Continual Prediction with LSTM. Neural Computation, 12(10), 2451-2471. https://doi.org/10.1162/089976600300015015
- 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
- Greff, K., Srivastava, R. K., Koutnik, J., Steunebrink, B. R., & Schmidhuber, J. (2017). LSTM: A Search Space Odyssey. IEEE Transactions on Neural Networks and Learning Systems, 28(10), 2222-2232. https://arxiv.org/abs/1503.04069
- PyTorch documentation. torch.nn.LSTMCell. https://docs.pytorch.org/docs/stable/generated/torch.nn.LSTMCell.html