Vanilla RNN: trạng thái cho dữ liệu tuần tự
Mục tiêu
Sau bài này, bạn cần:
- giải thích được vì sao dữ liệu tuần tự không nên luôn bị ép thành một vector cố định;
- hiểu vai trò của trạng thái ẩn trong RNN;
- đọc được công thức cập nhật của một Vanilla RNN;
- hiểu ý nghĩa của việc dùng chung tham số theo thời gian;
- phân biệt các dạng bài toán chuỗi phổ biến: nhiều-đến-một, nhiều-đến-nhiều và dự đoán bước tiếp theo;
- nhận ra giới hạn cơ bản của Vanilla RNN trước khi sang BPTT và LSTM.
Trong chương này, Vanilla RNN chỉ mạng hồi tiếp đơn giản kiểu Elman, không có các cổng như LSTM hay GRU.
1. Vì sao dữ liệu tuần tự khác dữ liệu độc lập?
Ở MLP, ta thường giả sử mỗi mẫu đầu vào đã được biểu diễn thành một vector có kích thước cố định. Ví dụ:
- một sinh viên:
[điểm chuyên cần, điểm giữa kỳ, điểm bài tập]; - một căn hộ:
[diện tích, số phòng, quận]; - một ảnh sau khi làm phẳng: một vector rất dài.
Nhưng nhiều loại dữ liệu có thứ tự và thứ tự đó mang thông tin:
- câu:
"không tốt"khác"tốt không"; - chuỗi thao tác trên hệ thống học tập:
đăng nhập → xem bài → nộp bàikhácnộp bài → đăng nhập → xem bài; - tín hiệu cảm biến: nhiệt độ hôm nay thường liên quan đến các thời điểm ngay trước đó;
- âm thanh: một khung tín hiệu chỉ có ý nghĩa khi đặt trong ngữ cảnh của các khung xung quanh.
Nếu xử lý từng phần tử hoàn toàn độc lập, mô hình mất thông tin về những gì đã xảy ra trước đó.
Đây là lý do RNN đưa thêm một biến trạng thái để mang thông tin từ bước trước sang bước hiện tại.
2. Ý tưởng cốt lõi: mỗi bước nhận cả dữ liệu mới và trạng thái cũ
Tại thời điểm t, RNN nhận:
- đầu vào hiện tại ;
- trạng thái ẩn trước đó .
Sau đó tạo ra trạng thái mới .
Điểm quan trọng là ba khối trong hình không phải ba mạng khác nhau. Chúng là cùng một phép biến đổi được dùng lặp lại cho nhiều bước thời gian.
Một cách đọc dễ hiểu
Có thể xem như một bản tóm tắt có kích thước cố định về phần chuỗi mà mô hình đã đọc đến thời điểm t.
Ví dụ với chuỗi:
"bài này không ..."
sau khi đọc từ "không", trạng thái ẩn có thể thay đổi để phản ánh rằng từ phía sau có khả năng đảo nghĩa của phần trước. Mô hình không lưu nguyên văn toàn bộ câu; nó học cách nén thông tin quá khứ thành các con số trong .
Đây là khác biệt chính so với MLP thông thường.
3. Công thức của Vanilla RNN
Một RNN đơn giản thường được viết:
Nếu cần tạo đầu ra ở mỗi bước:
Trong đó:
| Ký hiệu | Ý nghĩa |
|---|---|
đầu vào tại thời điểm t | |
| trạng thái ẩn từ bước trước | |
| trạng thái ẩn mới | |
| trọng số biến đổi đầu vào thành trạng thái | |
| trọng số hồi tiếp từ trạng thái cũ sang trạng thái mới | |
| bias của trạng thái ẩn | |
| trọng số từ trạng thái ẩn sang đầu ra |
Hàm đưa giá trị về khoảng , giúp trạng thái có miền giá trị bị chặn. Một số thư viện cũng cho phép dùng ReLU, nhưng tanh là lựa chọn kinh điển của Simple/Elman RNN.
Kích thước tensor
Giả sử:
- mỗi phần tử đầu vào có
dđặc trưng; - trạng thái ẩn có
mphần tử.
Khi đó:
và:
Điều này giải thích vì sao RNN có thể nhận chuỗi dài 10, 100 hay 1000 bước mà không phải tạo thêm một bộ trọng số mới cho từng vị trí.
4. “Mở cuộn” RNN để nhìn rõ quá trình tính toán
Trong sơ đồ mạng, RNN thường được vẽ như một khối có mũi tên quay lại chính nó. Để hiểu quá trình tính toán, ta mở khối đó theo trục thời gian.
Cách biểu diễn này được gọi là unrolling - mở đồ thị tính toán qua thời gian.
Sau khi mở cuộn, RNN trông gần giống một mạng rất sâu:
- mỗi bước thời gian tương ứng một “tầng” tính toán;
- cùng bộ trọng số được dùng lại ở mọi bước;
- trạng thái tại bước sau phụ thuộc vào trạng thái bước trước.
Ý tưởng mở cuộn đặc biệt quan trọng khi ta học lan truyền ngược qua thời gian ở bài tiếp theo.
5. Dùng chung tham số theo thời gian
Giả sử chuỗi dài . Một cách ngây thơ là tạo 100 mạng khác nhau cho 100 vị trí. Cách đó có hai vấn đề:
- số tham số tăng theo độ dài chuỗi;
- mô hình học riêng cho “vị trí 1”, “vị trí 2”, … nên khó khái quát sang chuỗi có độ dài khác.
RNN dùng cùng , và cho tất cả thời điểm.
Hệ quả: cùng một kiểu biến đổi có thể được áp dụng cho phần tử ở bất kỳ vị trí nào trong chuỗi.
Đây là một dạng chia sẻ tham số, tương tự việc CNN dùng cùng một kernel ở nhiều vị trí không gian.
6. Một ví dụ tính tay rất nhỏ
Giả sử RNN có một trạng thái duy nhất để dễ quan sát:
Cho:
và chuỗi đầu vào:
Ta có:
Điểm cần quan sát: ở bước 2, mặc dù , trạng thái vẫn không bằng 0 vì nó còn mang ảnh hưởng của .
Nói cách khác, quá khứ tiếp tục ảnh hưởng đến hiện tại thông qua trạng thái ẩn.
7. RNN có thể tạo đầu ra theo nhiều kiểu
Không phải bài toán chuỗi nào cũng cần một đầu ra ở mỗi bước.
7.1. Nhiều-đến-một: phân loại cả chuỗi
Ví dụ:
- phân loại một câu phản hồi thành tích cực/tiêu cực;
- dự đoán sinh viên có nguy cơ bỏ học dựa trên chuỗi hoạt động nhiều tuần;
- phân loại một đoạn tín hiệu ECG.
Thông thường ta lấy trạng thái cuối rồi đưa qua một tầng phân loại.
7.2. Nhiều-đến-nhiều cùng chiều dài
Ví dụ:
- gán nhãn từ loại cho từng từ;
- gán nhãn trạng thái cho từng khung âm thanh;
- phát hiện bất thường tại từng thời điểm cảm biến.
7.3. Dự đoán bước tiếp theo
Ví dụ:
- dự đoán ký tự tiếp theo;
- dự đoán giá trị cảm biến ở thời điểm kế tiếp;
- mô hình hóa chuỗi sự kiện.
Ở đây đầu ra tại thời điểm t thường được dùng để dự đoán .
8. Đầu vào của RNN không nhất thiết là “một số”
Chuỗi số
Với dữ liệu thời gian, mỗi có thể là:
với , , lần lượt là nhiệt độ, độ ẩm và lượng mưa.
Chuỗi từ
Máy không nhận trực tiếp từ "học" hay "thi". Mỗi token thường được đổi thành vector embedding:
Embedding và RNN có thể được học đồng thời.
Chuỗi đặc trưng đã trích xuất
Trong âm thanh hoặc video, cũng có thể là vector đặc trưng do một mạng khác tạo ra.
RNN chỉ yêu cầu đầu vào được sắp thành một dãy vector có thứ tự.
9. Trạng thái đầu lấy từ đâu?
Lựa chọn đơn giản nhất:
Trong một số hệ thống, có thể:
- là tham số học được;
- được sinh từ thông tin ngữ cảnh khác;
- được giữ lại từ đoạn chuỗi trước nếu bài toán thực sự liên tục theo thời gian.
Không nên giữ trạng thái giữa hai mẫu không liên quan. Nếu mẫu A và B là hai sinh viên khác nhau, truyền trạng thái từ A sang B sẽ làm lẫn thông tin.
10. RNN khác MLP ở đâu?
| MLP | Vanilla RNN |
|---|---|
| xử lý đầu vào cố định | xử lý chuỗi dài biến đổi |
| không có trạng thái thời gian | có trạng thái |
| mỗi mẫu thường xử lý độc lập | các bước trong cùng chuỗi phụ thuộc nhau |
| không chia sẻ trọng số theo vị trí thời gian | cùng trọng số được dùng ở mọi bước |
| không tự mô hình hóa thứ tự | thứ tự ảnh hưởng trực tiếp đến trạng thái |
RNN vẫn là mạng neural thông thường: vẫn có trọng số, bias, hàm kích hoạt, loss và tối ưu bằng gradient. Điểm khác nằm ở cấu trúc vòng lặp của trạng thái.
11. Vanilla RNN mạnh ở đâu, yếu ở đâu?
Điểm mạnh
- mô hình hóa được chuỗi có độ dài khác nhau;
- số tham số không tăng theo chiều dài chuỗi;
- trạng thái ẩn tạo cơ chế mang thông tin từ quá khứ;
- phù hợp để giải thích nền tảng của hầu hết họ mạng hồi tiếp sau này.
Điểm yếu
Vanilla RNN thường khó học quan hệ cách xa nhiều bước. Ví dụ:
"Mình mua chiếc laptop từ đầu học kỳ ... sau rất nhiều sự kiện ... chiếc máy **này** vẫn chạy tốt."
Để hiểu "này" đang nói tới gì, thông tin cũ phải tồn tại qua nhiều bước cập nhật.
Trong quá trình huấn luyện, gradient phải đi ngược qua một chuỗi dài các phép nhân. Nó có thể:
- nhỏ dần đến gần 0 - vanishing gradient;
- lớn nhanh và mất ổn định - exploding gradient.
Đó là nội dung của bài tiếp theo.
12. Những điều cần nhớ
- RNN xử lý chuỗi bằng cách duy trì một trạng thái ẩn.
- phụ thuộc cả và .
- Cùng bộ tham số được dùng lặp lại ở mọi thời điểm.
- Mở cuộn RNN giúp nhìn nó như một đồ thị tính toán sâu theo thời gian.
- RNN có thể dùng cho phân loại chuỗi, gán nhãn từng bước và dự đoán bước tiếp theo.
- Vanilla RNN gặp khó với phụ thuộc dài - nguyên nhân nằm ở dòng gradient khi huấn luyện.
Câu hỏi tự kiểm tra
- Nếu đổi thứ tự các phần tử của một chuỗi, RNN có nhất thiết cho cùng kết quả không? Vì sao?
- Vì sao số tham số của một RNN không tăng khi chuỗi dài hơn?
- khác như thế nào?
- Với bài toán phân loại một câu thành 3 chủ đề, bạn cần đầu ra ở mọi bước hay chỉ đầu ra cuối?
- Nếu hai chuỗi thuộc hai người dùng khác nhau, có nên truyền của chuỗi thứ nhất thành của chuỗi thứ hai không?
Tài liệu tham khảo
- Elman, J. L. (1990). Finding Structure in Time. Cognitive Science, 14(2), 179-211. https://doi.org/10.1207/s15516709cog1402_1
- Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning, Chapter 10: Sequence Modeling: Recurrent and Recursive Nets. MIT Press. https://www.deeplearningbook.org/contents/rnn.html
- Schuster, M., & Paliwal, K. K. (1997). Bidirectional recurrent neural networks. IEEE Transactions on Signal Processing, 45(11), 2673-2681. https://doi.org/10.1109/78.650093
- PyTorch documentation. torch.nn.RNN. https://docs.pytorch.org/docs/stable/generated/torch.nn.RNN.html