Kiến trúc mạng học sâu: từ nền tảng đến LLM·Chương 3 — Vanilla RNN: trạng thái cho dữ liệu tuần tự

Chế độ xem

BPTT và dòng gradient trong RNN

Mục tiêu

Sau bài này, bạn cần:

  • hiểu lan truyền ngược qua thời gian - Backpropagation Through Time (BPTT) thực chất là backpropagation trên RNN đã mở cuộn;
  • biết vì sao gradient trong RNN là tổng đóng góp từ nhiều thời điểm;
  • giải thích được hiện tượng vanishing gradient và exploding gradient;
  • biết tác dụng của gradient clipping và truncated BPTT;
  • hiểu vì sao khó học phụ thuộc dài là vấn đề của quá trình tối ưu, không chỉ của khả năng biểu diễn.

1. RNN học như thế nào?

Ở lượt truyền xuôi, RNN tính lần lượt:

h1→h2→⋯→hTh_1 \rightarrow h_2 \rightarrow \cdots \rightarrow h_T

Giả sử ở mỗi thời điểm có loss Lt\mathcal{L}_t, tổng loss là:

L=∑t=1TLt\mathcal{L} = \sum_{t=1}^{T}\mathcal{L}_t

Hoặc với bài toán chỉ dự đoán ở cuối chuỗi, ta có thể chỉ dùng:

L=LT\mathcal{L} = \mathcal{L}_T

Mục tiêu huấn luyện vẫn như mạng thông thường: điều chỉnh các tham số để giảm loss.

Vấn đề là cùng một tham số, chẳng hạn WhhW_{hh}, được dùng ở mọi thời điểm. Vì vậy một thay đổi của WhhW_{hh} có thể ảnh hưởng đồng thời đến h1,h2,…,hTh_1,h_2,\ldots,h_T.

2. BPTT = backpropagation trên đồ thị đã mở cuộn

Ta có RNN:

ht=ϕ(Wxhxt+Whhht−1+bh)h_t = \phi(W_{xh}x_t + W_{hh}h_{t-1}+b_h)

Sau khi mở cuộn:

Bây giờ đồ thị không còn vòng lặp. Ta có thể áp dụng quy tắc dây chuyền giống hệt mạng feedforward.

Tên Backpropagation Through Time nhấn mạnh rằng gradient đang đi ngược qua các bước thời gian.

Werbos (1990) là tài liệu kinh điển hệ thống hóa BPTT cho các hệ động và mạng hồi tiếp.

3. Vì sao gradient của tham số phải cộng qua nhiều thời điểm?

Giả sử WhhW_{hh} xuất hiện tại mọi bước. Tổng gradient có dạng:

∂L∂Whh=∑t=1T∂L∂ht∂ht∂Whh\frac{\partial \mathcal{L}}{\partial W_{hh}} = \sum_{t=1}^{T} \frac{\partial \mathcal{L}}{\partial h_t} \frac{\partial h_t}{\partial W_{hh}}

Ý nghĩa:

  • lỗi tại bước 2 tạo một tín hiệu cập nhật;
  • lỗi tại bước 3 tạo thêm một tín hiệu;
  • …;
  • các đóng góp được cộng lại vì tất cả cùng sửa một ma trận WhhW_{hh}.

Đây là hệ quả trực tiếp của chia sẻ tham số theo thời gian.

4. Vấn đề xuất hiện khi gradient phải đi qua nhiều bước

Để một loss ở thời điểm tt ảnh hưởng tới trạng thái cũ hkh_k với k≪tk \ll t, gradient chứa một chuỗi tích:

∂ht∂hk=∏j=k+1t∂hj∂hj−1\frac{\partial h_t}{\partial h_k} = \prod_{j=k+1}^{t} \frac{\partial h_j}{\partial h_{j-1}}

Với Vanilla RNN:

∂hj∂hj−1=DjWhh\frac{\partial h_j}{\partial h_{j-1}} = D_j W_{hh}

trong đó DjD_j là ma trận chứa đạo hàm của hàm kích hoạt tại bước j.

Do đó:

∂ht∂hk=∏j=k+1tDjWhh\frac{\partial h_t}{\partial h_k} = \prod_{j=k+1}^{t} D_j W_{hh}

Ta không cần khai triển hết công thức để thấy vấn đề: nhiều ma trận được nhân liên tiếp.

5. Vanishing gradient: tín hiệu học nhỏ dần

Xét ví dụ vô hướng cực đơn giản. Nếu mỗi bước làm gradient nhỏ đi khoảng 0.5 lần thì sau 20 bước:

0.520≈9.54×10−70.5^{20} \approx 9.54 \times 10^{-7}

Gradient gần như biến mất.

Trong RNN thật, hệ số không phải luôn là 0.5, nhưng cơ chế tương tự xảy ra khi chuẩn của các Jacobian lặp lại nhỏ hơn 1 trong nhiều bước.

Với tanh, đạo hàm có độ lớn không vượt quá 1 và rất nhỏ khi đầu vào nằm ở vùng bão hòa. Vì vậy một chuỗi dài các phép nhân dễ làm tín hiệu gradient suy giảm.

Hệ quả thực tế

Giả sử nhãn ở cuối câu phụ thuộc vào một từ nằm rất xa ở đầu câu. Nếu gradient từ cuối câu về đầu câu gần 0:

  • các trọng số ở đoạn đầu nhận tín hiệu cập nhật rất yếu;
  • mô hình khó học rằng thông tin sớm đó quan trọng;
  • trên lý thuyết trạng thái có thể mang thông tin xa, nhưng trên thực tế tối ưu bằng gradient có thể không học được cách làm đó.

Bengio, Simard và Frasconi (1994) đã phân tích rõ khó khăn của gradient descent khi phụ thuộc cần kéo dài qua nhiều bước thời gian.

6. Exploding gradient: tín hiệu học tăng quá nhanh

Ngược lại, nếu mỗi bước khuếch đại gradient khoảng 1.2 lần thì sau 20 bước:

1.220≈38.341.2^{20} \approx 38.34

Với chuỗi dài hơn hoặc hệ số lớn hơn, giá trị có thể tăng rất nhanh.

Hệ quả thường gặp:

  • loss nhảy đột ngột;
  • chuẩn gradient cực lớn;
  • trọng số thay đổi quá mạnh trong một bước;
  • xuất hiện NaN hoặc Inf;
  • huấn luyện mất ổn định.

Pascanu, Mikolov và Bengio (2013) phân tích cả hai hiện tượng vanishing và exploding gradient dưới nhiều góc nhìn và đề xuất gradient norm clipping như một biện pháp thực tế cho exploding gradient.

7. Gradient clipping: giới hạn độ lớn của bước cập nhật

Giả sử vector gradient là gg và ngưỡng là τ\tau. Một dạng clipping theo chuẩn là:

g←g⋅min⁡(1,τ∥g∥)g \leftarrow g \cdot \min\left(1,\frac{\tau}{\|g\|}\right)

Nếu ∥g∥≤τ\|g\| \le \tau, gradient giữ nguyên.

Nếu ∥g∥>τ\|g\| > \tau, toàn bộ gradient được co lại nhưng vẫn giữ hướng.

Gradient clipping giải quyết gì?

Chủ yếu là exploding gradient.

Nó không tự giải quyết vanishing gradient. Nếu gradient đã gần bằng 0 thì giới hạn giá trị lớn không giúp gì.

8. Truncated BPTT: không lan truyền ngược qua toàn bộ lịch sử

Với chuỗi rất dài, mở toàn bộ chuỗi có thể tốn nhiều bộ nhớ và thời gian.

Truncated BPTT chia chuỗi thành các đoạn ngắn hơn. Ví dụ chuỗi 1000 bước có thể được xử lý thành các đoạn 50 bước.

Ý tưởng phổ biến:

  1. truyền xuôi qua một đoạn;
  2. backprop trong đoạn đó;
  3. giữ giá trị trạng thái để tiếp tục đoạn sau;
  4. ngắt đồ thị gradient tại ranh giới giữa các đoạn.

Như vậy mô hình vẫn mang trạng thái sang đoạn sau, nhưng gradient không đi ngược vô hạn.

Đánh đổi

  • ưu điểm: giảm bộ nhớ, huấn luyện chuỗi dài khả thi hơn;
  • nhược điểm: tín hiệu học trực tiếp chỉ đi ngược trong một cửa sổ hữu hạn.

Truncated BPTT là biện pháp tính toán, không phải một kiến trúc mới.

9. Ví dụ: một phụ thuộc dài khó học thế nào?

Xét bài toán tổng hợp:

  • bước 1 chứa một bit cần nhớ;
  • các bước 2-99 chỉ là nhiễu;
  • bước 100 yêu cầu mô hình xuất lại bit ở bước 1.

Để học được nhiệm vụ này, loss ở bước 100 phải gửi tín hiệu đủ mạnh về cách xử lý thông tin ở bước 1.

Nếu gradient suy giảm mạnh sau vài chục phép nhân, bước 1 gần như không nhận được thông tin sửa lỗi.

Đây chính là loại bài toán đã thúc đẩy sự phát triển của các kiến trúc có cơ chế nhớ tốt hơn, nổi bật là LSTM.

10. Vanishing gradient không đồng nghĩa trạng thái luôn “quên”

Cần phân biệt hai khái niệm:

  1. truyền xuôi: thông tin trong hth_t có tồn tại đến bước xa hay không;
  2. học bằng gradient: mô hình có nhận được tín hiệu đủ tốt để học cách lưu thông tin đó hay không.

Một RNN về mặt biểu diễn có thể chứa trạng thái rất lâu. Tuy nhiên, nếu gradient bị suy giảm, quá trình huấn luyện có thể không tìm ra bộ trọng số cần thiết.

Vì vậy câu nói chính xác hơn là:

Vanilla RNN khó học các phụ thuộc dài bằng gradient descent thông thường.

Không nên phát biểu tuyệt đối rằng Vanilla RNN “không thể nhớ dài”.

11. Một số biện pháp trước thời LSTM và bên cạnh LSTM

Ngoài kiến trúc có cổng, người ta đã nghiên cứu nhiều cách cải thiện việc huấn luyện RNN:

  • khởi tạo trọng số phù hợp;
  • gradient clipping;
  • truncated BPTT;
  • các biến đổi trực giao/unitary cho ma trận hồi tiếp;
  • optimizer và lịch learning rate tốt hơn;
  • chuẩn hóa hoặc kiến trúc residual trong các RNN sâu.

Tuy nhiên, trong mạch kiến thức của khóa học này, bước chuyển quan trọng nhất là:

Vanilla RNN → nhận ra khó khăn của dòng gradient → LSTM tạo đường truyền bộ nhớ có kiểm soát bằng cổng.

12. Liên hệ với LSTM

Vanilla RNN cập nhật trạng thái bằng một phép biến đổi phi tuyến hoàn toàn mới ở mỗi bước:

ht=tanh⁡(Wxhxt+Whhht−1+b)h_t=\tanh(W_{xh}x_t + W_{hh}h_{t-1}+b)

LSTM thêm một trạng thái bộ nhớ riêng và cập nhật nó theo kiểu gần với phép cộng:

ct=ft⊙ct−1+it⊙c~tc_t = f_t \odot c_{t-1} + i_t \odot \tilde{c}_t

Đường cộng này giúp gradient có một lộ trình thuận lợi hơn để đi qua nhiều bước, đồng thời các cổng quyết định thông tin nào cần giữ hay cập nhật.

Bài sau sẽ phân tích từng thành phần của cơ chế này.

13. Checklist khi Vanilla RNN huấn luyện bất thường

Nếu bạn đang dùng Vanilla RNN và thấy loss không ổn định, kiểm tra theo thứ tự:

  1. Gradient norm có quá lớn không? Nếu có, thử gradient clipping.
  2. Learning rate có quá cao không? Giảm learning rate trước khi kết luận kiến trúc sai.
  3. Chuỗi có quá dài không? Cân nhắc truncated BPTT.
  4. Có phụ thuộc rất dài không? Vanilla RNN có thể không phù hợp; thử LSTM/GRU.
  5. Có quên reset trạng thái giữa các mẫu độc lập không? Điều này có thể làm dữ liệu bị trộn.
  6. Loss có bị tính trên padding không? Với chuỗi độ dài khác nhau phải mask đúng.

14. Những điều cần nhớ

  1. BPTT là backpropagation trên RNN đã mở cuộn qua thời gian.
  2. Vì tham số được dùng chung, gradient của tham số nhận đóng góp từ nhiều bước.
  3. Gradient qua nhiều bước chứa tích của nhiều Jacobian.
  4. Tích này có thể làm gradient nhỏ dần hoặc lớn nhanh.
  5. Gradient clipping chủ yếu xử lý exploding gradient.
  6. Truncated BPTT giới hạn độ dài đường backprop để giảm chi phí tính toán.
  7. Khó học phụ thuộc dài là động lực trực tiếp cho các kiến trúc có cổng như LSTM.

Câu hỏi tự kiểm tra

  1. Vì sao BPTT không phải một “loại backprop hoàn toàn khác”?
  2. Nếu chuẩn gradient là 100 và ngưỡng clipping là 5, hướng gradient có bị đổi khi dùng norm clipping không?
  3. Truncated BPTT có giữ được trạng thái truyền xuôi sang đoạn kế tiếp không?
  4. Gradient clipping có giải quyết vanishing gradient không?
  5. Vì sao tanh ở vùng bão hòa có thể làm vấn đề vanishing gradient nặng hơn?

Tài liệu tham khảo

  1. Werbos, P. J. (1990). Backpropagation through time: what it does and how to do it. Proceedings of the IEEE, 78(10), 1550-1560. https://doi.org/10.1109/5.58337
  2. 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
  3. Pascanu, R., Mikolov, T., & Bengio, Y. (2013). On the difficulty of training recurrent neural networks. ICML. https://arxiv.org/abs/1211.5063
  4. Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning, Sections 10.7, 10.10 and 10.11. MIT Press. https://www.deeplearningbook.org/contents/rnn.html