LSTM và BiLSTM
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 vai trò của trạng thái ô (cell state) và ba cổng của LSTM (Long Short-Term Memory): cổng quên (forget gate), cổng vào (input gate), cổng ra (output gate).
- Giải thích được vì sao đường cộng giúp gradient đi xa hơn RNN thường.
- Cài đặt được một bước LSTM bằng NumPy, đếm được số tham số, và so LSTM với GRU.
- Nhận ra khi nào dùng được LSTM hai chiều (BiLSTM) và khi nào không, và dùng LSTM nhiều tầng.
Cần biết trước
Phần tiêu đề “Cần biết trước”- GRU, in Deep Learning(Not translated yet) (Learned)
Trực giác
Phần tiêu đề “Trực giác”Đọc câu này và đoán chữ còn thiếu:
Chiếc xe máy mà ông nội mua từ năm 1995, sau bao nhiêu lần sửa, thay lốp, sơn lại và một trận ngập nước năm ngoái, đến giờ vẫn ___ được.
Bạn đoán “chạy”, không phải “nấu” hay “đọc”. Từ quyết định đáp án là “xe máy”, nằm cách chỗ trống 26 chữ. Trong khoảng giữa có rất nhiều thứ mà bạn đọc xong là quên ngay (năm 1995, lốp, sơn). Bạn chỉ giữ lại một ghi chú nhỏ: “chủ ngữ là một cái xe”.
RNN thường không giữ ghi chú kiểu đó được lâu. Mỗi bước nó viết lại toàn bộ trạng thái : nhân với ma trận, cộng đầu vào mới, rồi qua . Giống như chụp lại bản photo của bản photo: sau vài chục lần, nét chữ ban đầu mờ hẳn. Về mặt huấn luyện, như bài RNN đã chỉ ra, tín hiệu lỗi đi ngược 26 bước cũng bị nhân 26 lần với cùng một ma trận, nên thường tắt trước khi tới được chữ “xe máy”.
Một băng chuyền chạy suốt câu
Phần tiêu đề “Một băng chuyền chạy suốt câu”LSTM thêm một đường riêng cho ghi chú, gọi là trạng thái ô . Hãy hình dung một băng chuyền chạy ngang qua mọi bước thời gian. Mặc định, thứ gì đặt lên băng chuyền thì cứ thế trôi đi, không bị nhân với ma trận nào. Ở mỗi bước có ba người gác, mỗi người cầm một cái van:
- Người gác quên: nhìn đầu vào mới và quyết định bỏ bớt món nào trên băng. Đọc tới một chủ ngữ mới thì xóa chủ ngữ cũ.
- Người gác vào: quyết định đặt thêm món mới nào lên băng. Đọc “xe máy” thì ghi lại “chủ ngữ là một cái xe”.
- Người gác ra: quyết định lúc này lấy món nào trên băng ra dùng. Ở chỗ trống, cần lấy đúng ghi chú về chủ ngữ.
Phần lớn các bước, cả ba van gần như không động tới ghi chú “xe máy”, nên nó đi qua 26 chữ mà gần như nguyên vẹn.
Khi cả câu đã có sẵn
Phần tiêu đề “Khi cả câu đã có sẵn”Giờ đổi sang một việc khác: gắn nhãn cho từng từ. Từ “Hồ” ở đầu hai câu sau là tên người hay một phần tên địa danh?
Hồ Chí Minh đọc bản Tuyên ngôn Độc lập năm 1945.
Hồ Chí Minh là thành phố đông dân nhất Việt Nam.
Bên trái từ “Hồ” không có gì cả. Chỉ phần bên phải (“đọc bản Tuyên ngôn” hay “là thành phố”) mới quyết định. Một LSTM đọc từ trái sang phải phải đưa ra nhãn cho “Hồ” khi chưa thấy chữ nào phía sau. Nếu cả câu đã nằm sẵn trong tay, sao không đọc thêm một lượt từ phải sang trái?
Bài toán của bài này là: thiết kế một ô hồi tiếp giữ được thông tin (và gradient) qua hàng chục bước, rồi ghép hai ô như vậy chạy ngược chiều nhau để mỗi vị trí nhìn được cả hai phía của câu.
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”Đầu vào là chuỗi , mỗi (ví dụ embedding của một từ). Khác RNN, LSTM mang theo hai vectơ qua mỗi bước, cùng kích thước :
- Trạng thái ô : băng chuyền, bộ nhớ dài hạn. Nó không đi thẳng ra ngoài.
- Trạng thái ẩn : phần được đọc ra ở bước , dùng để dự đoán và để tính các cổng ở bước sau.
Ban đầu . Mỗi bước nhận , , và trả ra , .
Bốn nhánh: ba cổng và một ứng viên
Phần tiêu đề “Bốn nhánh: ba cổng và một ứng viên”Mỗi bước, LSTM tính bốn vectơ từ cùng một đầu vào . Mỗi vectơ có bộ trọng số riêng, cùng khuôn với RNN thường: nhân với đầu vào, nhân với trạng thái cũ, và một vectơ bias.
- là cổng quên, là cổng vào, là cổng ra. Cả ba qua sigmoid , nên mỗi thành phần nằm trong : 0 là đóng hẳn, 1 là mở hết.
- là ứng viên (candidate): nội dung mới có thể ghi lên băng. Nó qua , nên nằm trong và có thể âm hay dương. Nhánh này chính là công thức của RNN thường.
Vì sao cổng dùng sigmoid còn ứng viên dùng ? Cổng là một hệ số nhân, cần nằm giữa “chặn hết” và “cho qua hết”. Ứng viên là nội dung, cần được tăng lẫn giảm giá trị của ô.
Cập nhật trạng thái ô và trạng thái ẩn
Phần tiêu đề “Cập nhật trạng thái ô và trạng thái ẩn”Hai dòng quan trọng nhất của bài:
Đọc dòng đầu theo từng thành phần . Thành phần mới bằng phần giữ lại của giá trị cũ, , cộng phần ghi thêm của ứng viên, . Vài trường hợp đầu mút:
| Ô làm gì | |||
|---|---|---|---|
| 1 | 0 | Giữ nguyên, bỏ qua đầu vào | |
| 0 | 1 | Xóa cũ, ghi mới hoàn toàn | |
| 1 | 1 | Cộng dồn | |
| 0 | 0 | 0 | Xóa sạch |
Hai cổng độc lập, nên LSTM có cả trường hợp “cộng dồn” mà GRU không có: ở GRU, tỉ lệ giữ cái cũ và tỉ lệ lấy cái mới luôn cộng lại bằng 1.
Dòng thứ hai đọc ô ra ngoài. ép giá trị của ô (có thể lớn hơn 1 do cộng dồn) về , rồi cổng ra chọn thành phần nào được lộ ra. Vì vậy ô có thể giữ một thông tin rất lâu mà không để nó ảnh hưởng tới , cho tới lúc cần.
Vì sao đường cộng giúp gradient đi xa
Phần tiêu đề “Vì sao đường cộng giúp gradient đi xa”Nhắc lại vì sao RNN thường gặp khó. Với , đạo hàm của theo là
( là ma trận đường chéo có các phần tử của trên đường chéo; là đạo hàm của , tính từng phần tử). Đi ngược bước thì gradient bị nhân với ma trận như vậy, cùng một lặp lại. Tích đó thường co về 0 hoặc phình ra theo cấp số nhân.
Giờ xét LSTM, và chỉ nhìn đường thẳng trên băng chuyền: từ tới , coi các cổng như những hệ số đã biết. Lấy đạo hàm dòng theo : số hạng thứ hai không chứa nên bỏ đi, số hạng đầu là nhân từng phần tử với , nên
Đi ngược từ bước về bước , theo quy tắc dây chuyền ta nhân ma trận đường chéo. Tích các ma trận đường chéo vẫn là ma trận đường chéo, với phần tử là tích các phần tử tương ứng:
So với RNN, có ba khác biệt:
- Không có ma trận trọng số và không có đạo hàm trên đường này. Gradient chỉ bị nhân với các giá trị cổng.
- Hệ số do mạng tự chọn, ở từng bước và từng thành phần. Thành phần nào đang giữ thông tin quan trọng thì mạng học cách để ở đó, và gradient đi qua gần như nguyên vẹn. Ở RNN, cùng một phải dùng cho mọi bước.
- Không bùng nổ trên đường này, vì mỗi nằm trong .
Đây chưa phải toàn bộ gradient: các cổng cũng phụ thuộc , nên còn những đường khác đi qua ma trận trọng số. Nhưng chỉ cần một đường đi xa được là tín hiệu lỗi tới được chữ “xe máy”. Ý tưởng “cộng thay vì viết lại” bạn sẽ gặp lại ở kết nối tắt (residual connection) của ResNet và Transformer.
LSTM cũng không phải phép màu. Với ở mọi bước, sau 100 bước còn ; với thì còn . Mạng phải học được để rất gần 1. Gradient qua các đường khác vẫn có thể bùng nổ, nên cắt chuẩn gradient (gradient clipping) vẫn hay dùng khi huấn luyện LSTM.
Một chút lịch sử
Phần tiêu đề “Một chút lịch sử”LSTM do Sepp Hochreiter và Jürgen Schmidhuber công bố năm 1997, nhằm giải quyết đúng vấn đề gradient tiêu biến của RNN. Bản gốc chưa có cổng quên: ô cập nhật bằng , tức luôn bằng 1. Các tác giả gọi đường này là “vòng quay lỗi không đổi” (constant error carousel): lỗi chạy vòng qua ô mà không to lên hay nhỏ đi.
Vấn đề: với một luồng dữ liệu dài không có điểm ngắt, ô chỉ cộng mà không bao giờ xóa, nên giá trị cứ phình ra. Năm 2000, Felix Gers, Jürgen Schmidhuber và Fred Cummins thêm cổng quên để ô tự xóa khi cần. Dạng có cổng quên là dạng mà ngày nay mọi người gọi là LSTM, và là dạng nn.LSTM của PyTorch cài đặt. Một nghiên cứu lớn (Greff và cộng sự, 2017) so tám biến thể của LSTM và thấy cổng quên là một trong hai thành phần quan trọng nhất.
Đếm tham số, và so với GRU
Phần tiêu đề “Đếm tham số, và so với GRU”Mỗi nhánh có ( số), ( số) và bias ( số), tức , đúng bằng một RNN thường. LSTM có bốn nhánh nên:
GRU có ba nhánh (hai cổng và một ứng viên), nên bằng 3 lần RNN. Ví dụ với , (ô NumPy dưới đây): RNN có tham số, GRU có 96, LSTM có 128.
So sánh LSTM và GRU:
| Tiêu chí | LSTM | GRU |
|---|---|---|
| Vectơ mang qua mỗi bước | Hai: và | Một: |
| Số nhánh | 4 (quên, vào, ra, ứng viên) | 3 (reset, update, ứng viên) |
| Số tham số | 4 lần RNN | 3 lần RNN |
| Giữ cũ và ghi mới | Hai cổng độc lập, được cộng dồn | Một cổng, nội suy giữa cũ và mới |
| Cổng ra | Có, ô có thể giữ kín thông tin | Không, toàn bộ lộ ra |
| Năm | 1997 (cổng quên: 2000) | 2014 |
Còn chạy thực tế thì sao? Chung và cộng sự (2014) so hai loại trên dữ liệu nhạc và tiếng nói, và thấy GRU tương đương LSTM. Jozefowicz và cộng sự (2015) thử hơn mười nghìn kiến trúc hồi tiếp. Với khởi tạo thông thường, GRU hơn LSTM ở hầu hết các bài, trừ mô hình ngôn ngữ; LSTM có bias cổng quên bằng 1 thì gần đuổi kịp GRU, và khi có dropout thì LSTM tốt nhất ở bài mô hình ngôn ngữ. Không loại nào thắng hẳn. Lời khuyên thực dụng: GRU nhẹ hơn khoảng một phần tư, thử trước nếu cần nhanh; LSTM là lựa chọn mặc định quen thuộc; còn tốt hơn thì phải đo trên dữ liệu của bạn.
LSTM nhiều tầng
Phần tiêu đề “LSTM nhiều tầng”Một tầng LSTM biến chuỗi thành chuỗi , cùng độ dài. Vậy có thể đưa chuỗi đó làm đầu vào cho một tầng LSTM thứ hai, rồi thứ ba. Viết là trạng thái ẩn của tầng tại bước , với :
Mỗi tầng có bộ tham số và băng chuyền riêng. Cũng như các lớp của một MLP, xếp chồng cho mạng thêm khả năng biểu diễn: tầng trên làm việc với những đặc trưng mà tầng dưới đã rút ra, thay vì với đầu vào thô. Trong PyTorch chỉ cần nn.LSTM(..., num_layers=2, dropout=0.2); dropout áp lên đầu ra giữa các tầng. Hai đến bốn tầng là phổ biến. Mô hình seq2seq của Sutskever và cộng sự (2014) dùng LSTM 4 tầng (xem bài Mô hình chuỗi sang chuỗi), còn hệ dịch máy GNMT của Google (Wu và cộng sự, 2016) dùng 8 tầng cho encoder và 8 tầng cho decoder.
BiLSTM: đọc theo cả hai chiều
Phần tiêu đề “BiLSTM: đọc theo cả hai chiều”Ý tưởng RNN hai chiều (bidirectional RNN) có từ Schuster và Paliwal (1997). Graves và Schmidhuber (2005) ghép nó với LSTM thành BiLSTM. Cách làm rất đơn giản: chạy hai LSTM riêng biệt, tham số khác nhau, trên cùng chuỗi.
- Chiều xuôi đọc từ tới , cho tóm tắt .
- Chiều ngược đọc từ về , cho tóm tắt .
Tại mỗi vị trí, ghép (concatenate) hai vectơ thành một:
Vậy biết cả phần trước lẫn phần sau vị trí . Với từ “Hồ” ở mục Trực giác, nửa ngược đã đọc “đọc bản Tuyên ngôn” hay “là thành phố” trước khi tới “Hồ”. BiLSTM có gấp đôi tham số của một LSTM cùng , và lớp phía sau nhận vectơ chiều.
Dùng được khi cả chuỗi có sẵn từ đầu:
- Gán nhãn từng từ: từ loại, nhận dạng thực thể có tên (named entity recognition, NER). Huang, Xu và Yu (2015) ghép BiLSTM với CRF cho các bài này, một cấu hình rất phổ biến trước Transformer.
- Phân loại cả câu, cả đoạn.
- Encoder của mô hình dịch: câu nguồn đã có đủ. Tầng dưới cùng của encoder GNMT là hai chiều.
Không dùng được khi:
- Sinh văn bản từ trái sang phải. Khi sinh từ thứ , các từ phía sau chưa được viết ra, nên chiều ngược không có gì để đọc. Tệ hơn, nếu cố huấn luyện mô hình ngôn ngữ bằng BiLSTM, đã đọc , đúng là nhãn cần dự đoán: mô hình chép đáp án thay vì học. Decoder của seq2seq vì vậy luôn một chiều.
- Xử lý theo thời gian thực, như nhận dạng giọng nói trực tiếp: muốn chạy chiều ngược thì phải chờ hết câu.
Giới hạn
Phần tiêu đề “Giới hạn”- Vẫn tuần tự. Bước phải đợi bước xong, nên không tận dụng được GPU để xử lý song song theo chiều dài chuỗi. Huấn luyện trên chuỗi dài chậm.
- Vẫn khó với chuỗi rất dài. Gradient trên băng chuyền là tích các , chỉ đi xa khi rất gần 1. Thông tin từ hàng trăm bước trước vẫn phải đi qua từng bước một.
- Mọi thứ phải nén vào một vectơ chiều. Ở seq2seq, cả câu nguồn dồn vào trạng thái cuối, là nút thắt mà attention được sinh ra để gỡ.
Ba giới hạn này dẫn thẳng tới bài Transformers: thay vì chuyền thông tin qua từng bước, mỗi vị trí nhìn thẳng vào mọi vị trí khác bằng attention, và mọi vị trí được tính song song.
Minh họa
Phần tiêu đề “Minh họa”Hình dưới vẽ một ô LSTM. Hãy nhìn đường nằm ngang phía trên, từ tới : trên đường này chỉ có một phép nhân với cổng quên và một phép cộng, không có ma trận trọng số nào. Mọi thứ phức tạp (bốn nhánh sigmoid và ) nằm ở phía dưới và chỉ “chạm” vào băng chuyền qua hai điểm đó.
Bảng dưới cho thấy BiLSTM thấy gì ở vị trí đầu tiên của câu “Hồ Chí Minh là thành phố đông dân nhất Việt Nam”. Chiều xuôi mới đọc đúng một từ, chiều ngược đã đọc cả câu. Ở vị trí cuối thì ngược lại.
| Vị trí | Chiều xuôi đã đọc | Chiều ngược đã đọc |
|---|---|---|
| 1 (“Hồ”) | Hồ | Hồ Chí Minh là thành phố … Việt Nam |
| 4 (“là”) | Hồ Chí Minh là | là thành phố … Việt Nam |
| 11 (“Nam”) | Hồ Chí Minh là thành phố … Việt Nam | Nam |
Hàng cuối cũng giải thích một lỗi hay gặp: vectơ ở vị trí cuối không tóm tắt cả câu theo cả hai chiều (xem mục Lỗi hay gặp).
Ví dụ code
Phần tiêu đề “Ví dụ code”Các ô chạy độc lập. Ô thứ nhất chạy trên trang, chỉ dùng NumPy. Ô thứ hai cần PyTorch nên chạy trên máy bạn, CPU là đủ.
Ô thứ nhất cài đặt đúng các công thức ở mục Lý thuyết với , , chạy qua một chuỗi ngẫu nhiên bước, bias cổng quên khởi tạo bằng 1, rồi đếm tham số.
Một bước LSTM bằng NumPy
import numpy as np
rng = np.random.default_rng(0)d_x, d_h, L = 3, 4, 5 # chiều đầu vào, chiều trạng thái, độ dài chuỗi
def sigmoid(z): return 1 / (1 + np.exp(-z))
def init(bias): # Mỗi nhánh có W_x (d_h x d_x), W_h (d_h x d_h) và b (d_h) W_x = rng.normal(0, 0.5, (d_h, d_x)) W_h = rng.normal(0, 0.5, (d_h, d_h)) return W_x, W_h, np.full(d_h, bias)
# Bốn nhánh: cổng quên, cổng vào, cổng ra, ứng viên.# Mẹo: bias cổng quên khởi tạo bằng 1 để lúc đầu mạng "nhớ" nhiều.P = {"f": init(1.0), "i": init(0.0), "o": init(0.0), "c": init(0.0)}
def lstm_step(x, h, c): z = {k: W_x @ x + W_h @ h + b for k, (W_x, W_h, b) in P.items()} f, i, o = sigmoid(z["f"]), sigmoid(z["i"]), sigmoid(z["o"]) c_tilde = np.tanh(z["c"]) c = f * c + i * c_tilde # băng chuyền: giữ một phần, cộng phần mới h = o * np.tanh(c) # cổng ra chọn phần nào của ô được đưa ra return h, c, f
X = rng.normal(size=(L, d_x))h, c = np.zeros(d_h), np.zeros(d_h)keep = np.ones(d_h) # tích các f_t: phần c_0 còn lại trên đường cộngfor t in range(L): h, c, f = lstm_step(X[t], h, c) keep *= f print(f"t={t + 1} f={f.round(2)} c={c.round(2) + 0.0}")print("h_L =", h.round(3))print("prod f =", keep.round(3))
# Đếm tham số và so với công thứcn = sum(W_x.size + W_h.size + b.size for W_x, W_h, b in P.values())rnn = d_h * (d_x + d_h) + d_hprint("params:", n, " formula 4*RNN:", 4 * rnn)print("RNN:", rnn, " GRU:", 3 * rnn, " PyTorch LSTM:", 4 * (d_h * (d_x + d_h) + 2 * d_h))The first run downloads Python, about 10 MB.
Đọc kết quả:
- Cổng quên ở bước đầu có các giá trị 0.77, 0.74, 0.69, 0.73, quanh : đúng tác dụng của mẹo bias bằng 1. Các bước sau, khi khác 0, giá trị tản ra hơn (từ 0.48 tới 0.88).
- Trạng thái ô đổi dấu và lớn dần qua các bước (thành phần đầu: 0.13, 0.49, 0.19, , ), còn luôn nằm trong vì đi qua và cổng ra.
prod flà tích các của năm bước, tức đạo hàm theo đường thẳng trên băng chuyền : từ 0.103 tới 0.271. Gần với . Nếu bias cổng quên bằng 0, các quanh 0.5 và tích chỉ còn cỡ . Đây là mạng chưa huấn luyện; mạng đã học sẽ đẩy sát 1 ở những thành phần cần nhớ.- Đếm tham số: 128, khớp công thức . RNN có 32, GRU 96, còn PyTorch sẽ báo 144 vì có hai bias mỗi nhánh.
Ô thứ hai so LSTM một chiều với BiLSTM trên một bài gán nhãn tự sinh. Đầu vào là dãy 12 chữ số ngẫu nhiên. Nhãn tại vị trí là 1 nếu chữ số kế tiếp lớn hơn chữ số hiện tại, ngược lại là 0 (vị trí cuối không có nhãn). Nhãn phụ thuộc vào phía bên phải, giống từ “Hồ”.
Run on your machine
LSTM một chiều và BiLSTM gán nhãn từng vị trí
- Install
pip install torch- Hardware
- CPU là đủ, không cần GPU. Khoảng 30 tới 60 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
import torchfrom torch import nn
torch.manual_seed(0)L, D_H = 12, 32 # độ dài chuỗi, chiều trạng thái mỗi chiều
def make_batch(n): # Dãy chữ số ngẫu nhiên. Nhãn tại t: chữ số kế tiếp có lớn hơn không? x = torch.randint(0, 10, (n, L)) y = (x[:, 1:] > x[:, :-1]).long() # L - 1 nhãn, vị trí cuối bỏ qua return x, y
class Tagger(nn.Module): def __init__(self, bidirectional): super().__init__() self.emb = nn.Embedding(10, 16) self.lstm = nn.LSTM(16, D_H, batch_first=True, bidirectional=bidirectional) k = 2 if bidirectional else 1 # BiLSTM ghép hai trạng thái self.out = nn.Linear(k * D_H, 2)
def forward(self, x): H, _ = self.lstm(self.emb(x)) # H: (n, L, k * D_H) return self.out(H)[:, :-1] # một logit cho mỗi vị trí
x_test, y_test = make_batch(2000)for bi in (False, True): model = Tagger(bi) n_lstm = sum(p.numel() for p in model.lstm.parameters()) opt = torch.optim.Adam(model.parameters(), lr=0.01) for step in range(400): x, y = make_batch(64) loss = nn.functional.cross_entropy( model(x).reshape(-1, 2), y.reshape(-1)) opt.zero_grad() loss.backward() opt.step() with torch.no_grad(): acc = (model(x_test).argmax(-1) == y_test).float().mean() name = "BiLSTM" if bi else "LSTM " print(f"{name} params {n_lstm:5d} loss {loss.item():.3f}" f" test acc {acc.item():.3f}")Sample output
From one real run.
LSTM params 6400 loss 0.497 test acc 0.753BiLSTM params 12800 loss 0.000 test acc 1.000Đọc kết quả:
- Số tham số: LSTM có (embedding), , nên PyTorch báo , đúng công thức hai bias ở mục Lý thuyết. BiLSTM là hai LSTM như vậy, nên gấp đôi: 12 800.
- LSTM một chiều dừng ở 0.753. Đây không phải do huấn luyện chưa đủ. Tại vị trí , nó chưa đọc , nên chỉ đoán được theo : chữ số hiện tại là thì xác suất chữ số kế tiếp lớn hơn là . Đoán theo phía nhiều khả năng hơn thì đúng , trung bình trên 10 giá trị của là . Mô hình đã học được cách đoán tốt nhất có thể và chạm trần (0.753 hơi trên 0.75 chỉ do 2000 dãy kiểm tra là một mẫu ngẫu nhiên).
- BiLSTM đạt 1.000 trên 2000 dãy kiểm tra, loss về 0.000. Chiều ngược tại vị trí đã đọc , nên bài toán trở nên tầm thường.
Chênh lệch ở đây đến từ thông tin, không phải từ số tham số: cho LSTM một chiều thêm tầng hay thêm chiều thì nó vẫn không thấy được . Seed cố định nên chạy lại 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ố loss có thể lệch đôi chút. Nếu máy chạy chậm bất thường, đặt biến môi trường OMP_NUM_THREADS=1 trước khi chạy: mô hình nhỏ thế này không lợi gì từ nhiều luồng.
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:
- LSTM mang theo trạng thái ô (bộ nhớ) bên cạnh trạng thái ẩn (phần đọc ra). Ba cổng sigmoid điều khiển nó: và .
- Trên đường băng chuyền, gradient chỉ bị nhân với các giá trị cổng quên, không qua ma trận trọng số hay đạo hàm . Mạng học ở chỗ cần nhớ, nên gradient đi xa hơn RNN thường. Khởi tạo bias cổng quên bằng 1 giúp lúc đầu.
- LSTM có 4 lần số tham số của RNN, GRU có 3 lần. Trên thực tế hai loại ngang nhau, tùy bài. Xếp chồng nhiều tầng LSTM bằng cách lấy chuỗi của tầng dưới làm đầu vào tầng trên.
- BiLSTM chạy hai LSTM ngược chiều và ghép trạng thái, nên mỗi vị trí thấy cả hai phía. Dùng khi có cả chuỗi (gán nhãn, NER, phân loại, encoder); không dùng để sinh văn bản từ trái sang phải.
Đọc thêm:
- Christopher Olah (2015). Understanding LSTM Networks. Bài viết có hình vẽ từng cổng, nguồn của hình ảnh “băng chuyền”. Hình ảnh trực quan nhất để đọc cùng bài này.
- Aston Zhang, Zachary C. Lipton, Mu Li, Alexander J. Smola (2023). Dive into Deep Learning, mục 10.1 (LSTM); mục 10.3, 10.4 cho RNN nhiều tầng và hai chiều. Có code cài đặt từ đầu. Sách viết cho cổng và gọi là “input node”.
- Sepp Hochreiter, Jürgen Schmidhuber (1997). Long Short-Term Memory. Neural Computation 9(8). Bài gốc, chưa có cổng quên; phần đầu phân tích vì sao gradient tiêu biến.
- Felix A. Gers, Jürgen Schmidhuber, Fred Cummins (2000). Learning to Forget: Continual Prediction with LSTM. Neural Computation 12(10). Bài thêm cổng quên.
- Rafal Jozefowicz, Wojciech Zaremba, Ilya Sutskever (2015). An Empirical Exploration of Recurrent Network Architectures. ICML 2015. So LSTM, GRU và hơn mười nghìn biến thể; nguồn của mẹo bias cổng quên bằng 1.
- Alex Graves, Jürgen Schmidhuber (2005). Framewise Phoneme Classification with Bidirectional LSTM and Other Neural Network Architectures. Neural Networks 18(5–6). Bài giới thiệu BiLSTM, trên bài nhận dạng âm vị.
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