Nhập môn Trí tuệ Nhân tạo·Neural Networks - Mạng neural

Chế độ xem

Recurrent Neural Networks - RNN và dữ liệu tuần tự

Bài này trả lời vì sao cùng một bộ trọng số phải chạy lại trên từng bước của một chuỗi, thay vì xem cả chuỗi như một vector không có thứ tự. Mạng làm vậy gọi là mạng neural hồi tiếp, viết tắt RNN, từ recurrent neural network. Trạng thái ẩn là một số, hoặc một vector số, mang phần tóm tắt của các bước trước sang bước hiện tại.

Bạn có thể đọc khi chỉ nhớ rằng huấn luyện sửa trọng số theo đạo hàm của một loss. Bài tính hai bước với trạng thái ban đầu, hai đầu vào, và các trọng số cho sẵn, toàn số giả định. Bài nói vì sao một chuỗi dài làm tín hiệu sửa trọng số teo dần, ở mức một tích các hệ số. Vài đoạn sau chỉ nói cổng nào của LSTM và GRU quyết định giữ hay quên, rồi trỏ sang khóa kiến trúc học sâu. Bài không khai triển đạo hàm qua thời gian, không viết đủ công thức các cổng, và không xây Transformer.

Các giá trị h1=1h_1 = 1 và h2=0.4h_2 = 0.4 bên dưới là kết quả của các phép cộng đã viết. Chúng không phải độ chính xác của một bộ gõ tiếng Việt hay của một mô hình điểm danh thật.

Đường đọc nhanh: mục 1, mục 2, mục 3, mục Khi cách này thất bại, mục 4, rồi phần Mang đi. Các mục Đào sâu nằm ngoài đường đó.

1. Chuỗi có thứ tự, và tầng không vòng thì không giữ bước trước

Hai câu dưới đây dùng gần cùng các từ. Đổi thứ tự là đổi điều người nói đang hỏi.

Sinh viên không đỗ môn AI.
Sinh viên đỗ môn AI, không?

Một túi từ, tức một bản đếm số lần mỗi từ xuất hiện, có thể gần giống nhau cho hai câu nếu chỉ nhìn tập từ. Thứ tự “không” đứng trước hay sau cụm “đỗ môn” không còn là một trục của vector. Mạng nối đầy đủ trên vector đó vì vậy không có chỗ để viết “từ này đứng sau từ kia”, trừ khi người thiết kế đã gắn thêm các đặc trưng vị trí bằng tay.

Với một dãy đo, cùng chuyện đó. Dãy giả định 35,36,37,39,4235, 36, 37, 39, 42 không chỉ là năm số. Khoảng tăng ở cuối lớn hơn khoảng tăng ở đầu. Một mô hình nhận cả năm số cùng lúc vẫn có thể học một trọng số riêng cho từng vị trí, và lúc đó độ dài chuỗi bị khóa theo số cột. RNN làm một việc khác: một bước tính được viết một lần, rồi dùng lại cho bước một, bước hai, bước ba, với cùng trọng số, và với trạng thái đổi.

Mạng đi tới, không có vòng, tính y=f(x)y = f(x) cho từng mẫu độc lập. Đưa lần lượt các từ “hôm”, “nay”, “trời”, “mưa” vào một mạng như vậy, mỗi từ là một mẫu riêng, thì từ “mưa” không nhận được một đường tính từ “hôm”. RNN thêm đường đó bằng trạng thái ẩn.

2. Ví dụ tính tay: từ h0h_0 sang h1h_1 rồi h2h_2

Ví dụ này cố ý dùng trạng thái là một số, không phải một vector, để từng tích hiện trên trang. Mạng thật thường để hh là nhiều số. Cách dùng lại trọng số thì cùng kiểu. Công thức một bước: nhân đầu vào hiện tại với wxw_x, nhân trạng thái trước với whw_h, cộng bias, rồi uốn bằng ReLU. ReLU giữ số dương và đổi số âm thành 00. Kết luận của một bước là trạng thái mới chỉ phụ thuộc đầu vào bước này, trạng thái ngay trước, và bộ số dùng chung:

zt=wxxt+whht−1+bz_t = w_x x_t + w_h h_{t-1} + b ht=max⁡(0,zt)h_t = \max(0, z_t)

Các số cho sẵn, không phải số học được: h0=1h_0 = 1, x1=2x_1 = 2, x2=0.5x_2 = 0.5, wx=0.4w_x = 0.4, wh=0.5w_h = 0.5, b=−0.3b = -0.3.

Bước t=1t = 1. Tích với đầu vào: 0.4⋅2=0.80.4 \cdot 2 = 0.8. Tích với trạng thái trước: 0.5⋅1=0.50.5 \cdot 1 = 0.5. Cộng bias: 0.8+0.5+(−0.3)=1.00.8 + 0.5 + (-0.3) = 1.0. Vậy z1=1.0z_1 = 1.0. Vì 1.0>01.0 > 0, ReLU cho h1=1.0h_1 = 1.0.

Bước t=2t = 2. Tích với đầu vào: 0.4⋅0.5=0.20.4 \cdot 0.5 = 0.2. Tích với trạng thái trước, lúc này là h1h_1: 0.5⋅1.0=0.50.5 \cdot 1.0 = 0.5. Cộng bias: 0.2+0.5+(−0.3)=0.40.2 + 0.5 + (-0.3) = 0.4. Vậy z2=0.4z_2 = 0.4. Vì 0.4>00.4 > 0, ReLU cho h2=0.4h_2 = 0.4.

Hai kết quả: h1=1h_1 = 1 và h2=0.4h_2 = 0.4. Cùng bộ wx,wh,bw_x, w_h, b đã chạy hai lần. Lần thứ hai không có trọng số mới; nó nhận h1h_1 thay cho h0h_0, và nhận x2x_2 thay cho x1x_1. Phép tính cho thấy trạng thái đã đổi từ 11 thành 11 ở bước một, rồi từ 11 thành 0.40.4 ở bước hai, dưới các số này. Nó không cho thấy một RNN sẽ dự đoán đúng từ tiếp theo của một câu thật.

Có thể tách phần x1x_1 còn lại trong h2h_2, khi cả hai ReLU đang mở, tức z>0z > 0 và độ dốc ReLU bằng 11. Độ dốc của h1h_1 theo x1x_1 là wx=0.4w_x = 0.4. Độ dốc của h2h_2 theo h1h_1 là wh=0.5w_h = 0.5. Tích hai độ dốc là 0.4⋅0.5=0.20.4 \cdot 0.5 = 0.2. Trên đoạn tuyến tính này, một thay đổi nhỏ của x1x_1 đi vào h2h_2 với hệ số 0.20.2, sau khi đã đi qua h1h_1. Đó là một tích hai thừa số, chưa phải đạo hàm của một chuỗi dài.

Hình dưới đây trải vòng lặp thành các bước cạnh nhau. Các hộp là cùng một bộ trọng số, vẽ lại cho dễ nhìn, không phải ba mạng khác nhau.

RNN được unroll theo nhiều time step

x1 -> [RNN] -> h1
        |
        v
x2 -> [RNN] -> h2
        |
        v
x3 -> [RNN] -> h3

Nếu có một đầu ra tại mỗi bước, đầu ra đó là một hàm của hth_t, ví dụ một tầng tuyến tính rồi một softmax khi ta cần phân phối trên các từ. Bài này không tính đầu ra đó, vì câu hỏi đang là trạng thái được cập nhật ra sao. Many-to-one nghĩa là cả chuỗi đi vào và một nhãn đi ra, thường đọc từ trạng thái cuối: một câu thành một nhãn cảm xúc. One-to-many nghĩa là một đầu vào tạo một chuỗi đầu ra. Many-to-many nghĩa là một chuỗi sang một chuỗi, như mỗi khung hình một nhãn, hoặc một câu sang một câu. Cùng một bước trạng thái phục vụ cả ba kiểu; chỗ khác nhau là ta gắn loss vào những bước nào.

3. Cùng một bộ số, vì bước nào cũng là một bước

Chia sẻ theo thời gian giống chia sẻ theo vị trí của một kernel trên ảnh, nhưng trục là thứ tự chứ không phải lưới pixel. Hệ quả thứ nhất: số tham số không tăng theo độ dài chuỗi. Bộ wx,wh,bw_x, w_h, b của ví dụ vẫn là ba số dù chuỗi dài mười hay một trăm, trong mô hình một trạng thái vô hướng này. Hệ quả thứ hai: cùng một kiểu chuyển trạng thái được dùng ở mọi bước, nên mạng có cơ hội học “làm gì với đầu vào tiếp theo” mà không học lại từ đầu tại vị trí thứ bốn mươi. Hệ quả thứ ba nằm ở chiều ngược: vì cùng một trọng số xuất hiện nhiều lần trong chuỗi đã trải ra, đạo hàm của loss theo trọng số đó là tổng đóng góp từ các bước. Bài này không viết tổng đó. Chỉ cần thấy một hệ số whw_h sẽ bị nhân lặp lại khi tín hiệu đi ngược, và mục thất bại tính các tích đó.

Huấn luyện vẫn là forward, loss, đạo hàm, cập nhật. Forward bây giờ là vòng lặp các bước như mục 2. Loss có thể đặt ở bước cuối, hoặc là tổng loss từng bước nếu mỗi bước có nhãn. Cập nhật sửa wxw_x, whw_h và bb, tức các số dùng chung, không sửa một bản sao riêng cho t=2t = 2.

Một chuỗi ngắn trong ví dụ từ vựng giúp thấy trạng thái là ngữ cảnh. Các xác suất dưới đây là số giả định để đọc, không phải đầu ra của mạng vừa tính:

Hôm -> h1
nay + h1 -> h2
trời + h2 -> h3

Từ h3h_3, một tầng ra có thể cho điểm các từ tiếp theo. Điều cần giữ là điểm của từ đứng sau “trời” được tính từ h3h_3, mà h3h_3 đã trộn xx tại “trời” với h2h_2, và h2h_2 đã trộn các từ trước. Cắt trạng thái, chỉ nhìn từ “trời”, là một mô hình khác.

Khi cách này thất bại

Lấy tiếp các trọng số của mục 2 và thêm một đầu vào thứ ba bằng 00, tức x3=0x_3 = 0, với h2=0.4h_2 = 0.4. Tích đầu vào: 0.4⋅0=00.4 \cdot 0 = 0. Tích trạng thái: 0.5⋅0.4=0.20.5 \cdot 0.4 = 0.2. Cộng bias: 0+0.2−0.3=−0.10 + 0.2 - 0.3 = -0.1. Vậy z3=−0.1z_3 = -0.1. ReLU của một số âm là 00, nên h3=0h_3 = 0.

Trạng thái h3h_3 bằng 00, dù x1x_1 từng là 22 và đã làm h1=1h_1 = 1. Trên các số này, một bước với đầu vào 00 đã kéo tổng về âm và ReLU xóa trạng thái. Phép tính cho thấy giá trị x1x_1 không còn hiện trong h3h_3. Nó không cho thấy mọi RNN quên sau đúng ba bước; bộ whw_h và bias khác sẽ ra z3z_3 khác. Với đúng bộ này, bước thứ ba đã tắt.

Ngay khi ReLU không tắt, hệ số wh=0.5w_h = 0.5 vẫn nhân vào mỗi bước lùi. Tám lần nhân 0.50.5 là 1/2561/256, tức 0.003906250.00390625. Hai mươi lần nhân 0.50.5 là 1/10485761/1048576. Nếu một loss ở cuối chuỗi gửi tín hiệu ngược và mỗi bước đang ở đoạn độ dốc 0.50.5, tín hiệu tới bước đầu sau hai mươi bước nhỏ hơn một phần triệu nếu tín hiệu xuất phát bằng 11. Trọng số gần như không được sửa bởi phụ thuộc giữa hai đầu xa nhau. Đây là gradient biến mất theo thời gian, nói bằng một tích, không bằng khai triển đạo hàm của cả đồ thị tính toán. Hệ số 0.50.5 trùng whw_h của ví dụ chỉ khi độ dốc ReLU bằng 11. Tại bước 33 của chính ví dụ, độ dốc ReLU bằng 00 vì z3<0z_3 < 0, nên tín hiệu ngược qua bước đó còn bị chặn hẳn, mạnh hơn việc nhân 0.50.5.

Chiều bùng dùng cùng logic với một hệ số lớn hơn 11. Nếu mỗi bước nhân 22, mười bước ra 210=10242^{10} = 1024. Cập nhật trừ tốc độ học nhân một số cỡ đó sẽ nhảy rất xa, hoặc thành giá trị không dùng được trong số thực máy tính. Người ta gọi đó là gradient bùng. Dấu hiệu lúc chạy là loss thành một giá trị không hữu hạn sau một vài bước trải chuỗi dài. Cách làm thực hành thường gặp là cắt ngưỡng độ lớn gradient trước khi cập nhật. Bài này không tính một ngưỡng cắt.

Thất bại về mặt bài toán, khác thất bại về mặt tích, là khi dữ liệu không có thứ tự đáng giữ. Một bảng các cột đã tính sẵn, mỗi hàng một sinh viên, không cần trạng thái hth_t trừ khi bạn tự ý xếp các cột thành một chuỗi giả. RNN khi đó phải học lại rằng thứ tự do bạn bịa không quan trọng, và tốn một vòng lặp không tương ứng cấu trúc hàng. Dấu hiệu chọn sai là hoán vị các cột, tức đổi thứ tự không có thật, làm dự đoán đổi mạnh. Với chuỗi thật, hoán vị các bước phải đổi ý nghĩa, như hai câu mở bài.

Chuỗi rất dài còn một giới hạn dung lượng: trạng thái một số, như ví dụ, chỉ giữ được những gì các phép nhân-cộng chưa xóa. h3=0h_3 = 0 là một lần xóa thấy được. Trạng thái vector dài hơn chỉ trì hoãn cùng kiểu vấn đề nếu các hệ số lặp lại vẫn nhỏ hơn 11 về thang độ dốc. Đó là lý do các kiến trúc sau thêm một đường nhớ có cổng, thay vì chỉ một hh bị nhân whw_h mỗi bước.

4. LSTM và GRU: cổng nào giữ, cổng nào quên

LSTM, từ long short-term memory, thêm một đường giá trị gọi là cell state, song song với trạng thái ẩn đưa ra ngoài. Các cổng là các hệ số từ 00 đến 11, thường là đầu ra sigmoid, nhân vào đường đó. Bài này không viết phương trình từng cổng. Khóa kiến trúc học sâu có bài riêng cho công thức và cho việc đọc gradient qua cell: LSTM: cell state và các cổng.

Ở mức quyết định, forget gate, cổng quên, nhân với cell state cũ. Hệ số gần 00 nghĩa là quên phần nhớ đó. Hệ số gần 11 nghĩa là giữ. Input gate, cổng vào, quyết định phần nào của một ứng viên mới được cộng vào cell. Output gate, cổng ra, quyết định phần nào của cell được đưa ra thành trạng thái ẩn ở bước này. Nhờ cell có thể đi qua một cổng gần 11 trong nhiều bước, tín hiệu không bắt buộc phải nhân với một whw_h nhỏ tại mỗi bước. Đó là ý “nhớ dài hơn” ở mức cơ chế, không phải một đo đạc rằng mọi LSTM đều nhớ được một câu dài tùy ý.

GRU, từ gated recurrent unit, gọn hơn: nó không tách một cell state riêng khỏi trạng thái ẩn. Update gate trộn trạng thái cũ và một ứng viên mới: nghiêng về trạng thái cũ là giữ, nghiêng về ứng viên là viết đè. Reset gate quyết định trạng thái cũ được đưa vào việc tính ứng viên nhiều hay ít; reset gần 00 nghĩa là ứng viên ít nhìn quá khứ. Công thức và chỗ so với LSTM nằm ở GRU: cổng reset và update. Vài đoạn này đủ để đọc tên cổng. Chúng không đủ để cài đặt hay để chỉnh một LSTM đang phân kỳ.

Trên các bài ngôn ngữ rất lớn, nhiều hệ thống hiện nay dùng attention trong kiến trúc Transformer thay cho vòng lặp từng bước. Bài này không mô tả attention, không mô tả truy vấn và khóa, và không suy ra Transformer từ RNN. Chỗ đó thuộc bài Language của khóa nhập môn và các bài tương ứng trong khóa kiến trúc học sâu. RNN, LSTM và GRU vẫn là công cụ đúng khi chuỗi được đọc từng bước vì dữ liệu đến dần, khi bộ nhớ máy nhỏ, hoặc khi chuỗi ngắn và một trạng thái là đủ. Bảng ở mục đào sâu chỉ là bản đồ chọn, không phải thứ tự thời gian của “cái nào thắng”.

Đào sâu: bản đồ MLP, CNN, RNN trên cùng một câu hỏi dữ liệu

Ba họ mạng trong phần nhập môn này khác nhau ở chỗ lân cận được định nghĩa thế nào. MLP, mạng nối đầy đủ nhiều tầng, coi đầu vào là một vector: không có láng giềng sẵn. CNN coi láng giềng là các ô kề trên lưới, và dùng chung kernel. RNN coi láng giềng là bước thời gian trước, và dùng chung bước nhảy trạng thái. Chọn sai họ là chọn sai láng giềng, rồi mới là chuyện số tầng.

Kiến trúcLáng giềng mà công thức nhìnDữ liệu hợp dạng nàyMột ví dụ giả định
MLPcác tọa độ của một vectorbảng vài cột đã có nghĩađiểm giữa kỳ và số bài nộp
CNNcác ô kề trên lướiảnh, bản đồảnh trang bài làm
RNNbước ngay trước, qua ht−1h_{t-1}chuỗiđiểm danh theo tuần, câu chữ

Điểm chung là cả ba đều có tham số, đều có lượt đi tới, đều có loss, và đều có thể được sửa bằng đạo hàm nếu các hàm trên đường đi còn độ dốc. Điểm riêng là công thức hth_t buộc xtx_t đứng sau xt−1x_{t-1} trong tính toán. Hoán vị thời gian mà không hoán vị cách cập nhật hh sẽ ra trạng thái khác, như mục 2 sẽ ra số khác nếu đổi x1x_1 với x2x_2.

Hãy đảo nhanh hai đầu vào của ví dụ để thấy thứ tự không phải trang trí. Giữ h0=1h_0 = 1, wx=0.4w_x = 0.4, wh=0.5w_h = 0.5, b=−0.3b = -0.3, nhưng đưa 0.50.5 trước và 22 sau. Bước một: 0.4⋅0.5+0.5⋅1−0.3=0.2+0.5−0.3=0.40.4 \cdot 0.5 + 0.5 \cdot 1 - 0.3 = 0.2 + 0.5 - 0.3 = 0.4, ReLU cho 0.40.4. Bước hai: 0.4⋅2+0.5⋅0.4−0.3=0.8+0.2−0.3=0.70.4 \cdot 2 + 0.5 \cdot 0.4 - 0.3 = 0.8 + 0.2 - 0.3 = 0.7, ReLU cho 0.70.7. Trạng thái sau hai bước là 0.70.7, trong khi thứ tự cũ cho h2=0.4h_2 = 0.4. Cùng đa tập hai số, hai thứ tự, hai trạng thái. Phép tính cho thấy bước RNN này không hoán vị được. Một hàm chỉ phụ thuộc tổng x1+x2x_1 + x_2 sẽ không tách hai thứ tự; tổng 2+0.5=2.52 + 0.5 = 2.5 ở cả hai cách xếp.

Các bài dùng chuỗi gần một lớp học, toàn tình huống giả định, gồm gợi ý từ khi gõ một câu tiếng Việt, gắn nhãn một câu nhận xét, dự đoán bước tiếp của một cảm biến, và đọc một đoạn video như một dãy khung. Trong mỗi bài, câu hỏi kiểm tra là: nếu xáo trộn thứ tự mà nhãn vẫn nên giữ nguyên, RNN đang bị ép học một thứ tự không có trong đầu bài. Nếu xáo trộn làm nhãn đổi nghĩa, trạng thái theo bước là đúng họ mô hình để thử, sau một baseline không có thứ tự để biết phần thứ tự có đang giúp hay không.

Tự kiểm tra

  1. Với h0=1h_0 = 1, x1=2x_1 = 2, wx=0.4w_x = 0.4, wh=0.5w_h = 0.5, b=−0.3b = -0.3, z1z_1 bằng bao nhiêu và h1h_1 bằng bao nhiêu?
  2. h2h_2 bằng bao nhiêu sau x2=0.5x_2 = 0.5?
  3. Với x3=0x_3 = 0, vì sao h3=0h_3 = 0?
  4. Tích độ dốc từ x1x_1 tới h2h_2 trên đoạn ReLU đang mở bằng bao nhiêu?
  5. Tám lần nhân 0.50.5 còn lại bao nhiêu?
  6. Cổng nào của LSTM nhân vào cell state cũ để quyết định quên, và cổng nào của GRU trộn trạng thái cũ với ứng viên mới?
  7. Đảo x1x_1 và x2x_2 trong ví dụ thì trạng thái sau hai bước bằng bao nhiêu, so với 0.40.4?

Mang đi

RNN chạy lại cùng trọng số tại mỗi bước, và đưa trạng thái trước vào bước sau. Với h0=1h_0 = 1, x1=2x_1 = 2, x2=0.5x_2 = 0.5, wx=0.4w_x = 0.4, wh=0.5w_h = 0.5, b=−0.3b = -0.3, ta được z1=1z_1 = 1, h1=1h_1 = 1, rồi z2=0.4z_2 = 0.4, h2=0.4h_2 = 0.4. Thêm x3=0x_3 = 0 thì z3=−0.1z_3 = -0.1 và h3=0h_3 = 0: đầu vào 22 không còn nằm trong trạng thái. Trên đoạn các ReLU đang mở, hệ số từ x1x_1 tới h2h_2 là 0.20.2. Nhân 0.50.5 đủ tám lần còn 1/2561/256; đủ hai mươi lần còn 1/10485761/1048576. LSTM dùng cổng quên để giữ hay xóa cell state, cổng vào để viết nhớ mới, cổng ra để lộ một phần cell; GRU dùng cổng cập nhật để trộn cũ với mới và cổng reset để ứng viên có nhìn quá khứ hay không. Công thức đầy đủ của các cổng nằm ở khóa kiến trúc học sâu, không nằm trong vài đoạn trên. Đảo thứ tự hai đầu vào của chính ví dụ đổi trạng thái cuối từ 0.40.4 thành 0.70.7.

Bài sau đi từ một câu hỏi giả định về ảnh bài làm viết tay đến kiến trúc được chọn, và kết bằng các dấu hiệu cho thấy lựa chọn đó đang sai.

Nguồn tham khảo

  • CS50 AI, Lecture 5 - Neural Networks: https://cs50.harvard.edu/ai/notes/5/
  • Hochreiter, S., Schmidhuber, J. (1997), Long Short-Term Memory, Neural Computation, DOI: 10.1162/neco.1997.9.8.1735.
  • Vaswani, A. et al. (2017), Attention Is All You Need, arXiv:1706.03762.