Mạng nơ-ron nhân tạo: perceptron, MLP, lan truyền xuôi, hàm kích hoạt
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 một nơ-ron nhân tạo (artificial neuron) tính gì: cộng có trọng số, cộng hệ số tự do, rồi qua hàm kích hoạt (activation function); và perceptron học bằng luật học perceptron (perceptron learning rule) ra sao.
- Tự chứng minh được vì sao một perceptron không giải được bài XOR, và vì sao thêm một lớp ẩn (hidden layer) thì giải được.
- Viết được lan truyền xuôi (forward propagation) của perceptron nhiều lớp (multilayer perceptron, MLP) ở dạng ma trận, đếm được số tham số, và cài đặt nó bằng NumPy.
- Biết các hàm kích hoạt hay dùng, vì sao chúng phải phi tuyến, cách chọn lớp ra cùng hàm mất mát cross-entropy, và định lý xấp xỉ phổ quát (universal approximation theorem) nói gì, không nói gì.
Cần biết trước
Phần tiêu đề “Cần biết trước”- Gradient Descent, in Machine Learning (Learned)
- Logistic Regression, in Machine Learning (Learned)
- Softmax Regression, in Machine Learning (Learned)
Trực giác
Phần tiêu đề “Trực giác”Đèn cầu thang
Phần tiêu đề “Đèn cầu thang”Cầu thang nhà bạn có hai công tắc, một ở chân cầu thang, một ở đầu cầu thang. Gạt công tắc nào cũng đổi trạng thái của đèn: đi lên thì bật ở dưới, lên tới nơi thì tắt ở trên. Ghi vị trí mỗi công tắc là 0 hoặc 1, lắp sao cho cả hai ở 0 thì đèn tắt. Bảng của đèn là:
| Công tắc dưới | Công tắc trên | Đèn |
|---|---|---|
| 0 | 0 | 0 (tắt) |
| 0 | 1 | 1 (sáng) |
| 1 | 0 | 1 (sáng) |
| 1 | 1 | 0 (tắt) |
Đèn sáng khi đúng một công tắc ở vị trí 1. Phép toán logic này gọi là XOR (exclusive or, “hoặc loại trừ”).
Thử một quy tắc “cộng điểm rồi so ngưỡng”
Phần tiêu đề “Thử một quy tắc “cộng điểm rồi so ngưỡng””Cách đơn giản nhất để máy ra quyết định từ hai số , : cho mỗi số một trọng số, cộng lại, rồi so với một ngưỡng. Điểm đạt ngưỡng thì đèn sáng. Với vài cổng logic quen thuộc, cách này chạy tốt:
- AND (sáng khi cả hai cùng bật): trọng số 1 và 1, ngưỡng 1.5. Chỉ có điểm 2 đạt ngưỡng.
- OR (sáng khi ít nhất một bật): trọng số 1 và 1, ngưỡng 0.5.
Với XOR thì loay hoay mãi không ra. Muốn và sáng, mỗi trọng số phải đạt ngưỡng. Muốn tắt, ngưỡng phải dương. Nhưng khi đó có điểm bằng tổng hai trọng số, còn lớn hơn nữa, nên cũng sáng. Nhìn bằng hình: đặt bốn điểm ở bốn góc một hình vuông, hai điểm “sáng” nằm trên một đường chéo, hai điểm “tắt” nằm trên đường chéo kia. Quy tắc “cộng điểm rồi so ngưỡng” chỉ chia mặt phẳng bằng một đường thẳng, mà không đường thẳng nào tách được hai đường chéo.
Ghép hai quy tắc
Phần tiêu đề “Ghép hai quy tắc”Nói bằng lời thì XOR rất dễ: đèn sáng khi “ít nhất một công tắc bật” và “không phải cả hai cùng bật”. Vế đầu là OR, vế sau là “không phải AND”. Mỗi vế là một quy tắc cộng điểm rồi so ngưỡng. Thêm một quy tắc thứ ba nhận kết quả của hai quy tắc đầu, và bạn có XOR. Đó là ý tưởng của mạng nơ-ron nhiều lớp: những đơn vị đơn giản, xếp thành lớp, lớp sau dùng kết quả của lớp trước.
Bài toán của bài này là: ghép những “nơ-ron” cộng điểm rồi quyết định thành nhiều lớp, để cả mạng biểu diễn được những quy tắc mà một nơ-ron không làm nổi, như đèn cầu thang; và viết phép tính của cả mạng thật gọn, để dùng được cho cả những bài lớn như đọc chữ số viết tay.
Lý thuyết
Phần tiêu đề “Lý thuyết”Nơ-ron nhân tạo
Phần tiêu đề “Nơ-ron nhân tạo”Nơ-ron sinh học nhận tín hiệu từ nhiều nơ-ron khác. Khi tổng kích thích đủ mạnh, nó “phát” một xung truyền đi. McCulloch và Pitts (1943) mô hình hóa việc này rất thô: cộng có trọng số các tín hiệu vào, rồi so với ngưỡng. Mạng nơ-ron (neural network) ngày nay chỉ giữ lại ý tưởng đó, không mô phỏng bộ não.
Một nơ-ron nhân tạo nhận vectơ gồm đầu vào và tính hai bước:
- là vectơ trọng số, là hệ số tự do. Hệ số tự do đóng vai “trừ đi ngưỡng”: “điểm ngưỡng ” chính là "" với .
- là tổng có trọng số, còn gọi là tiền kích hoạt (pre-activation).
- là hàm kích hoạt, và là đầu ra của nơ-ron, gọi là kích hoạt (activation).
Bạn đã gặp nơ-ron này rồi. Với , nó là hồi quy tuyến tính. Với , nó là hồi quy logistic. Tập các điểm có là một đường thẳng khi , một siêu phẳng (hyperplane) khi lớn hơn: đó là biên quyết định (decision boundary) của nơ-ron.
Perceptron và luật học perceptron
Phần tiêu đề “Perceptron và luật học perceptron”Perceptron (Rosenblatt, 1958) là nơ-ron có hàm kích hoạt là hàm bậc thang (step function):
Rosenblatt còn đưa ra một cách học , từ dữ liệu có nhãn . Duyệt từng mẫu, tính , rồi sửa:
Hiệu chỉ có ba giá trị, nên luật này đọc thành ba trường hợp:
- Đoán đúng (): không sửa gì.
- Đoán 0 nhưng đúng ra là 1 (): cộng vào và vào . Tính lại của chính mẫu này với tham số mới: . Vậy tăng, mẫu bị kéo về phía “1”.
- Đoán 1 nhưng đúng ra là 0 (): trừ đi, của mẫu giảm cùng một lượng, mẫu bị kéo về phía “0”.
Ví dụ với AND, , xuất phát từ , . Epoch đầu tiên:
| Mẫu | Sửa thành | |||
|---|---|---|---|---|
| 0 | 0 | 1 | sai: , | |
| 0 | 0 | đúng, giữ nguyên | ||
| 0 | 0 | đúng, giữ nguyên | ||
| 1 | 0 | sai: , |
Epoch này sai 2 mẫu. Lặp lại tới khi có một epoch không sai mẫu nào. Ô code đầu tiên chạy tiếp và dừng ở epoch thứ 6.
Khi nào luật học dừng? Dữ liệu gọi là tách được tuyến tính (linearly separable) nếu có một siêu phẳng chia đúng mọi mẫu của hai lớp. Định lý hội tụ perceptron (Novikoff, 1962) nói: nếu dữ liệu tách được tuyến tính, luật học chỉ sửa một số hữu hạn lần rồi dừng, với mọi thứ tự duyệt mẫu. Nếu dữ liệu không tách được, nó sửa mãi, và các trọng số cứ đổi qua đổi lại.
Vì sao một perceptron không giải được XOR
Phần tiêu đề “Vì sao một perceptron không giải được XOR”Phần Trực giác đã nói bằng hình. Giờ chứng minh chặt. Giả sử có để perceptron đúng cả bốn mẫu XOR. Với hàm bậc thang, khi , khi . Bốn mẫu cho bốn điều kiện:
Cộng điều kiện thứ hai và thứ ba được . Chuyển một sang vế phải: . Theo điều kiện đầu, nên . Vậy , trái với điều kiện cuối. Không có bộ tham số nào thỏa cả bốn: XOR không tách được tuyến tính.
Thay hàm bậc thang bằng sigmoid rồi quyết định “lớp 1 khi ” cũng không cứu được, vì đúng khi : biên quyết định vẫn là đường thẳng . Năm 1969, cuốn Perceptrons của Minsky và Papert phân tích kỹ những giới hạn kiểu này của perceptron một lớp, và góp phần làm giới nghiên cứu nguội đi với mạng nơ-ron trong khoảng một thập kỷ.
Perceptron nhiều lớp
Phần tiêu đề “Perceptron nhiều lớp”Trở lại ý “ghép hai quy tắc”. Dùng hai perceptron ở lớp đầu và một perceptron ở lớp sau:
- : bằng 1 khi ít nhất một công tắc bật (OR).
- : bằng 1 khi cả hai cùng bật (AND).
- : bằng 1 khi và .
Kiểm tra cả bốn mẫu:
| XOR | ||||
|---|---|---|---|---|
| 0 | 0 | |||
| 1 | 1 | |||
| 1 | 1 | |||
| 0 | 0 |
Để ý cột . Lớp đầu đã đổi cách biểu diễn dữ liệu: hai mẫu “sáng” cùng thành điểm , hai mẫu “tắt” thành và . Trong mặt phẳng , ba điểm này tách được bằng một đường thẳng, và nơ-ron cuối chỉ việc vẽ đường thẳng đó. Đây là điều mạng nhiều lớp làm được mà một nơ-ron không làm được: các lớp đầu học ra đặc trưng mới, lớp cuối giải bài toán dễ hơn trên đặc trưng đó.
Mạng như thế gọi là perceptron nhiều lớp:
- Lớp vào (input layer) chỉ là các đầu vào , không tính gì.
- Lớp ẩn là các lớp ở giữa. “Ẩn” vì dữ liệu không cho biết chúng phải ra giá trị gì.
- Lớp ra (output layer) cho dự đoán .
Mỗi nơ-ron nhận đầu vào từ mọi nơ-ron của lớp ngay trước, nên mỗi lớp như vậy gọi là lớp kết nối đầy đủ (fully connected layer, hay dense layer). Thông tin chỉ đi một chiều từ lớp vào tới lớp ra, không quay vòng, nên MLP còn gọi là mạng truyền thẳng (feed-forward network). Mạng ở trên viết tắt là 2-2-1 (số nơ-ron mỗi lớp) và được gọi là mạng 2 lớp: theo quy ước thường gặp, không đếm lớp vào.
Tên “perceptron nhiều lớp” là di sản lịch sử. MLP ngày nay không dùng hàm bậc thang mà dùng các hàm kích hoạt trơn hơn, lý do ở mục Hàm kích hoạt.
Lan truyền xuôi dạng ma trận
Phần tiêu đề “Lan truyền xuôi dạng ma trận”Với 2 đầu vào thì viết từng nơ-ron được. Bài đọc chữ số ở ô code thứ ba có 64 đầu vào và 64 nơ-ron ẩn: cần một cách viết gọn hơn.
Ký hiệu. Đánh số lớp , với lớp 0 là lớp vào và lớp là lớp ra. Lớp có nơ-ron; lớp vào có , bằng số đặc trưng. Chỉ số trong ngoặc vuông là số thứ tự lớp, khác với là số thứ tự mẫu.
Một lớp. Nơ-ron thứ của lớp có vectơ trọng số (dài ) và hệ số tự do . Nó nhận vectơ kích hoạt của lớp trước và tính . Xếp các vectơ thành các hàng của một ma trận , và các thành vectơ cột . Phép nhân ma trận với vectơ tính một lúc mọi tích vô hướng, mỗi hàng một tích, nên cả lớp viết được trong hai dòng:
Hàm kích hoạt áp lên từng phần tử của . Các lớp ẩn thường dùng chung một hàm ; lớp ra có thể dùng hàm khác (mục Lớp ra).
Kiểm tra kích thước. có hàng (mỗi nơ-ron của lớp một hàng) và cột (mỗi đầu vào một cột):
Cả mạng. Bắt đầu với , áp hai dòng trên cho , và đầu ra là . Tính lần lượt từ lớp vào tới lớp ra như vậy gọi là lan truyền xuôi.
Mạng XOR ở trên viết thành:
Với : , qua hàm bậc thang được . Tiếp theo , nên . Đúng với dòng thứ ba của bảng.
Nhiều mẫu một lúc. Xếp mẫu thành cột của ma trận , kích thước . Cột thứ của chính là nhân với cột thứ của , nên một phép nhân ma trận làm lan truyền xuôi cho cả mẫu:
Ở đây được cộng vào mọi cột; NumPy tự làm việc này (gọi là broadcasting). Ô code thứ hai làm đúng phép tính này. Máy tính nhân ma trận rất nhanh, nhất là trên GPU, nên viết dạng ma trận không chỉ gọn mà còn là cách mạng nơ-ron thật sự được tính.
Số tham số. Lớp có trọng số và hệ số tự do, tức tham số. MLP 64-64-10 ở ô code thứ ba có tham số.
Hàm kích hoạt
Phần tiêu đề “Hàm kích hoạt”Vì sao phải phi tuyến. Thử bỏ hàm kích hoạt ở lớp ẩn, tức . Thay vào lớp thứ hai:
Dòng thứ hai nhân phân phối vào trong ngoặc rồi gom lại. Đặt và : hai lớp gộp thành một lớp . Lặp lại lập luận này, xếp bao nhiêu lớp tuyến tính cũng chỉ bằng một lớp tuyến tính, và vẫn không giải được XOR. Chính hàm kích hoạt phi tuyến cho phép các lớp ẩn bẻ cong không gian, như cách lớp ẩn của mạng XOR gộp hai điểm “sáng” làm một.
Các hàm hay gặp. Bảng giá trị ở một vài điểm (Leaky ReLU với ):
| Hàm | |||||
|---|---|---|---|---|---|
| Bậc thang | 0 | 0 | 1 | 1 | 1 |
| Sigmoid | 0.119 | 0.378 | 0.5 | 0.622 | 0.881 |
| tanh | −0.964 | −0.462 | 0 | 0.462 | 0.964 |
| ReLU | 0 | 0 | 0 | 0.5 | 2 |
| Leaky ReLU | −0.02 | −0.005 | 0 | 0.5 | 2 |
| GELU | −0.046 | −0.154 | 0 | 0.346 | 1.954 |
- Bậc thang. Đồ thị nằm ngang ở mọi nơi trừ chỗ nhảy tại 0, nên đạo hàm bằng 0 ở mọi chỗ có đạo hàm. Gradient descent cần độ dốc để biết nên sửa tham số về phía nào, nên mạng nhiều lớp với hàm bậc thang không học bằng gradient được. Luật học perceptron chỉ dùng được cho một lớp, vì chỉ lớp ra mới có nhãn để so.
- Sigmoid , giá trị trong . Trơn, nhưng bão hòa (saturate) khi lớn: đồ thị gần như nằm ngang, đạo hàm gần 0. Ngày nay sigmoid chủ yếu dùng ở lớp ra của bài phân loại hai lớp, và trong các cổng của GRU, LSTM.
- tanh , giá trị trong . Là sigmoid co giãn lại cho đối xứng quanh 0, nên đầu ra có trung bình gần 0 hơn. Cũng bão hòa ở hai đầu. Hay dùng trong mạng hồi tiếp.
- ReLU (rectified linear unit) . Tính rất rẻ, không bão hòa ở phía dương, và thành lựa chọn mặc định cho lớp ẩn từ đầu thập niên 2010 (Nair và Hinton, 2010; Glorot và cộng sự, 2011). Điểm yếu: với , đầu ra và đạo hàm đều bằng 0. Nếu một nơ-ron rơi vào cảnh với mọi mẫu, nó không bao giờ học được nữa, gọi là nơ-ron chết (dying ReLU).
- Leaky ReLU với nhỏ, thường (Maas và cộng sự, 2013). Phía âm vẫn có một độ dốc nhỏ, nên nơ-ron không chết hẳn.
- GELU (Gaussian error linear unit, Hendrycks và Gimpel, 2016) , với là hàm phân phối tích lũy của phân phối chuẩn tắc: xác suất một biến chuẩn tắc nhỏ hơn . Khi lớn, nên GELU gần bằng ; khi rất âm, nên GELU gần 0. Ở giữa, đồ thị trơn và hơi trũng xuống dưới 0, thấp nhất khoảng tại . GELU là hàm kích hoạt của BERT và GPT; bài Xây dựng LLM đầu tiên và bài Họ LLaMA gặp lại nó trong khối MLP của Transformer.
Không có hàm nào tốt nhất cho mọi bài. Điểm khởi đầu an toàn: ReLU cho lớp ẩn của MLP và CNN, GELU cho Transformer, tanh và sigmoid trong mạng hồi tiếp. Bài Huấn luyện mạng nơ-ron giải thích vì sao sự bão hòa của sigmoid làm mạng sâu khó học.
Lớp ra và hàm mất mát
Phần tiêu đề “Lớp ra và hàm mất mát”Lớp ẩn học đặc trưng; lớp ra biến đặc trưng đó thành đúng loại dự đoán mà bài toán cần. Ba trường hợp hay gặp:
| Bài toán | Lớp ra | Hàm mất mát |
|---|---|---|
| Hồi quy (dự đoán một số) | 1 nơ-ron, | MSE |
| Phân loại hai lớp | 1 nơ-ron, sigmoid | Binary cross-entropy |
| Phân loại lớp | nơ-ron, softmax | Cross-entropy |
Softmax. Lớp ra có nơ-ron, cho logit . Softmax đổi chúng thành phân phối xác suất:
Hàm mũ làm mọi số thành dương, phép chia làm tổng bằng 1, và logit lớn hơn thì xác suất lớn hơn. Khác các hàm kích hoạt ở trên, softmax không tính từng phần tử riêng: mỗi phụ thuộc vào mọi logit. Ví dụ cho .
Cross-entropy. Với một mẫu có nhãn đúng là lớp , mất mát là xác suất mà mạng gán cho lớp . Viết nhãn thành vectơ one-hot (phần tử thứ bằng 1, còn lại bằng 0) thì công thức trung bình trên mẫu là:
Trong tổng theo , chỉ số hạng của lớp đúng còn lại (các khác bằng 0), nên mỗi mẫu góp đúng . Với ví dụ trên: nếu lớp đúng là lớp 1 thì mất mát là ; nếu là lớp 3 thì . Đoán đúng và tự tin thì mất mát gần 0; tự tin mà sai thì mất mát rất lớn. Với và một nơ-ron sigmoid cho , công thức thu về binary cross-entropy .
Nhìn lại cả mạng: lớp ra softmax cộng cross-entropy chính là hồi quy softmax, chỉ khác ở chỗ đầu vào của nó không phải mà là , các đặc trưng do lớp ẩn học ra.
Định lý xấp xỉ phổ quát
Phần tiêu đề “Định lý xấp xỉ phổ quát”Mạng XOR cần đúng một lớp ẩn. Vậy một lớp ẩn làm được tới đâu? Định lý xấp xỉ phổ quát trả lời: với một hàm kích hoạt phi tuyến phù hợp, một MLP một lớp ẩn, đủ nhiều nơ-ron, xấp xỉ được mọi hàm liên tục trên một miền bị chặn với sai số nhỏ tùy ý. Cybenko (1989) chứng minh cho sigmoid; Hornik, Stinchcombe và White (1989) cho một lớp hàm rộng hơn; Leshno và cộng sự (1993) chỉ ra mọi hàm kích hoạt liên tục không phải đa thức đều dùng được, gồm cả ReLU.
Bài này không chứng minh, nhưng ý chính dễ thấy với ReLU. Mỗi nơ-ron ReLU là một đường gãy khúc tại một điểm. Hiệu là một con dốc đi từ 0 lên 1 rồi nằm ngang. Cộng nhiều con dốc như vậy, mỗi cái đặt ở một chỗ với một độ cao, bạn ghép được một đường gấp khúc bám theo đường cong bất kỳ. Càng nhiều nơ-ron, càng nhiều khúc, càng bám sát.
Đọc kỹ những gì định lý không nói:
- Không nói cần bao nhiêu nơ-ron. Với nhiều hàm, số nơ-ron cần có thể rất lớn. Trên thực tế, mạng sâu thường biểu diễn cùng một hàm với ít tham số hơn nhiều so với mạng nông và rộng.
- Không nói tìm ra trọng số bằng cách nào. Định lý chỉ đảm bảo bộ trọng số tốt tồn tại. Gradient descent có tìm được nó không là chuyện khác.
- Không nói gì về dữ liệu mới. Khớp tốt tập huấn luyện vẫn có thể là overfitting.
Còn thiếu: học trọng số
Phần tiêu đề “Còn thiếu: học trọng số”Trọng số của mạng XOR được đặt bằng tay. Mạng chữ số có 4810 tham số, mạng thật có hàng triệu tới hàng tỉ: không thể đặt tay. Hướng đi đã rõ: chọn hàm mất mát (cross-entropy), rồi dùng gradient descent để giảm nó. Muốn vậy cần và cho mọi lớp, mà mỗi tham số ảnh hưởng tới qua tất cả các lớp sau nó. Cách tính các gradient này hiệu quả là lan truyền ngược (backpropagation), nội dung của bài Huấn luyện mạng nơ-ron.
Ngoài ra, MLP coi mỗi đầu vào là một con số độc lập: nó không biết hai điểm ảnh nằm cạnh nhau hay hai từ đứng liền nhau. Bài Mạng tích chập (CNN) và bài Mạng nơ-ron hồi tiếp (RNN) đưa cấu trúc của ảnh và chuỗi vào mạng.
Minh họa
Phần tiêu đề “Minh họa”Hình dưới đặt hai thứ cạnh nhau. Bên trái là một nơ-ron: nhìn theo mũi tên, các đầu vào được nhân với trọng số , cộng với thành , rồi qua thành . Bên phải là một MLP 2-3-1: mỗi nơ-ron của lớp ẩn chính là nơ-ron bên trái, và mỗi mũi tên giữa hai lớp là một trọng số, tức một phần tử của .
Bảng dưới là kết quả lan truyền xuôi của ô code thứ hai: mạng XOR 2-2-1, lần này dùng sigmoid thay cho hàm bậc thang, với trọng số lớn hơn để sigmoid gần giống bậc thang. Hãy nhìn cột lớp ẩn: hai mẫu “sáng” và cho cùng một điểm . Lớp ẩn đã gộp chúng lại, nên lớp ra chỉ còn phải tách ba điểm bằng một đường thẳng.
| Lớp ẩn | Lớp ra | XOR | |
|---|---|---|---|
| 0.011 | 0 | ||
| 0.983 | 1 | ||
| 0.983 | 1 | ||
| 0.011 | 0 |
Ví dụ code
Phần tiêu đề “Ví dụ code”Các ô dưới đây chạy độc lập, ô nào cũng tự tạo dữ liệu. Hai ô đầu chỉ dùng NumPy và chạy ngay trên trang. Ô thứ ba cần PyTorch nên chạy trên máy bạn.
Ô thứ nhất cài đặt perceptron và luật học perceptron, rồi cho nó học ba cổng AND, OR, XOR, mỗi cổng tối đa 20 epoch. Mỗi cổng in số epoch đã chạy, , , số mẫu sai trong từng epoch, và dự đoán cuối cùng.
Perceptron học AND, OR và XOR
import numpy as np
# Bốn đầu vào của một cổng logic hai đầu vào: mỗi hàng là một mẫu.X = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])gates = { "AND": np.array([0, 0, 0, 1]), "OR": np.array([0, 1, 1, 1]), "XOR": np.array([0, 1, 1, 0]),}
def step(z): return (z >= 0).astype(int) # hàm bậc thang: 1 nếu z >= 0
def train_perceptron(X, y, eta=1.0, max_epochs=20): w, b = np.zeros(X.shape[1]), 0.0 # xuất phát từ 0 history = [] # số mẫu đoán sai trong mỗi epoch for epoch in range(max_epochs): errors = 0 for x_i, y_i in zip(X, y): y_hat = step(w @ x_i + b) # Luật học perceptron: chỉ sửa khi đoán sai. w = w + eta * (y_i - y_hat) * x_i b = b + eta * (y_i - y_hat) errors += int(y_hat != y_i) history.append(errors) if errors == 0: # một lượt không sai mẫu nào: đã hội tụ break return w, b, history
for name, y in gates.items(): w, b, history = train_perceptron(X, y) print(f"{name}: epochs = {len(history)}, w = {w}, b = {b}") print(f" errors per epoch: {history}") print(f" predictions: {step(X @ w + b)}, targets: {y}")The first run downloads Python, about 10 MB.
- AND hội tụ sau 6 epoch (số mẫu sai 2, 3, 3, 2, 1, 0), dừng ở , . Bộ số này không đối xứng như “trọng số 1 và 1, ngưỡng 1.5” của mục Trực giác, nhưng cũng đúng: chỉ có . Dữ liệu tách được tuyến tính thì có vô số đường thẳng tách đúng, và perceptron dừng ở đường đầu tiên nó gặp.
- OR hội tụ sau 4 epoch, ở , .
- XOR sai 3, 3, rồi 4 mẫu ở mọi epoch cho tới epoch thứ 20. Các trọng số cứ đổi qua đổi lại; lúc dừng là , , dự đoán , chỉ đúng 2 trên 4 mẫu, không hơn đoán bừa. Đúng như chứng minh: không có bộ tham số nào để hội tụ tới.
Ô thứ hai là mạng 2-2-1 giải XOR với trọng số đặt tay, dạng sigmoid: lớp ẩn có , ; lớp ra có , . Bốn mẫu được xếp thành bốn cột của , và lan truyền xuôi chạy đúng hai dòng , cho mỗi lớp. Phần cuối bỏ hàm kích hoạt và kiểm tra rằng hai lớp tuyến tính bằng đúng một lớp .
MLP 2-2-1 giải XOR, lan truyền xuôi dạng ma trận
import numpy as np
def sigmoid(z): return 1 / (1 + np.exp(-z))
# Bốn mẫu XOR, xếp thành CỘT: A0 có 2 hàng (đặc trưng), 4 cột (mẫu).A0 = np.array([[0, 0, 1, 1], [0, 1, 0, 1]])y = np.array([0, 1, 1, 0])
# Lớp ẩn: 2 nơ-ron. Hàng 1 gần với OR, hàng 2 gần với AND.W1 = np.array([[6.0, 6.0], [6.0, 6.0]])b1 = np.array([[-3.0], [-9.0]]) # vectơ cột, cộng vào mọi cột của Z1# Lớp ra: 1 nơ-ron, tính "OR và không AND".W2 = np.array([[10.0, -10.0]])b2 = np.array([[-5.0]])
# Lan truyền xuôi: z = W a + b, rồi a = g(z), lớp nối lớp.Z1 = W1 @ A0 + b1A1 = sigmoid(Z1)Z2 = W2 @ A1 + b2A2 = sigmoid(Z2) # xác suất lớp 1, shape (1, 4)print("hidden A1:\n", A1.round(3))print("output A2:", A2.round(3), " target:", y)
# Bỏ hàm kích hoạt (g(z) = z): hai lớp gộp lại thành một lớp tuyến tính.W = W2 @ W1b = W2 @ b1 + b2two_layers = W2 @ (W1 @ A0 + b1) + b2one_layer = W @ A0 + bprint("linear W =", W, " b =", b.ravel())print("same output:", np.allclose(two_layers, one_layer))The first run downloads Python, about 10 MB.
Đầu ra là : lấy ngưỡng 0.5 thì khớp XOR. Hàng đầu của A1 gần với OR, hàng thứ hai gần với AND, như thiết kế. Phần bỏ hàm kích hoạt in same output: True: hai lớp tuyến tính cho đúng kết quả của một lớp với và . Ở đây bằng 0 vì hai hàng của giống nhau; với trọng số khác thì khác 0, nhưng vẫn chỉ là một lớp tuyến tính, nên vẫn không giải được XOR.
Ô thứ ba huấn luyện một MLP 64-64-10 có ReLU trên bộ chữ số viết tay load_digits của scikit-learn: 1797 ảnh xám 8×8, tức 64 đầu vào, nhãn từ 0 tới 9. Mạng được viết bằng nn.Sequential: nn.Linear(64, 64) là một lớp , nn.ReLU() là . Hàm mất mát nn.CrossEntropyLoss nhận thẳng logit, và bộ tối ưu là gradient descent mini-batch (torch.optim.SGD, cỡ batch 32, , 30 epoch). Dòng loss.backward() tính gradient của theo mọi tham số: tạm coi nó là một hộp đen, bài sau sẽ mở hộp này ra.
Run on your machine
MLP bằng PyTorch đọc chữ số viết tay
- Install
pip install torch scikit-learn- Hardware
- CPU là đủ, không cần GPU. Khoảng 30 giây tới 3 phút trên máy phát triển, tùy máy đang bận cỡ nào
import torchfrom torch import nnfrom sklearn.datasets import load_digitsfrom sklearn.model_selection import train_test_split
torch.manual_seed(0)
# 1797 ảnh chữ số viết tay 8x8, mỗi điểm ảnh từ 0 tới 16.X, y = load_digits(return_X_y=True)X_tr, X_te, y_tr, y_te = train_test_split( X, y, test_size=0.25, random_state=0, stratify=y)X_tr = torch.tensor(X_tr / 16, dtype=torch.float32) # đưa về [0, 1]X_te = torch.tensor(X_te / 16, dtype=torch.float32)y_tr = torch.tensor(y_tr)y_te = torch.tensor(y_te)
# MLP 64 -> 64 -> 10. Lớp ra trả về logit, KHÔNG có softmax:# nn.CrossEntropyLoss tự áp softmax rồi lấy -log xác suất lớp đúng.model = nn.Sequential( nn.Linear(64, 64), nn.ReLU(), nn.Linear(64, 10),)n_params = sum(p.numel() for p in model.parameters())print(f"parameters: {n_params}")
loss_fn = nn.CrossEntropyLoss()opt = torch.optim.SGD(model.parameters(), lr=0.2) # mini-batch GD
for epoch in range(1, 31): perm = torch.randperm(len(X_tr)) # xáo trộn mỗi epoch for i in range(0, len(X_tr), 32): # mini-batch cỡ 32 idx = perm[i:i + 32] logits = model(X_tr[idx]) # lan truyền xuôi loss = loss_fn(logits, y_tr[idx]) opt.zero_grad() loss.backward() # tính gradient: bài sau giải thích opt.step() # w <- w - lr * gradient if epoch in (1, 5, 10, 20, 30): with torch.no_grad(): full = loss_fn(model(X_tr), y_tr).item() print(f"epoch {epoch:2d} train loss {full:.4f}")
with torch.no_grad(): pred = model(X_te).argmax(dim=1) # lớp có logit lớn nhất acc = (pred == y_te).float().mean().item()print(f"test accuracy: {acc:.4f} ({len(y_te)} images)")Sample output
From one real run.
parameters: 4810epoch 1 train loss 1.4863epoch 5 train loss 0.2110epoch 10 train loss 0.2626epoch 20 train loss 0.0631epoch 30 train loss 0.0416test accuracy: 0.9756 (450 images)Đọc kết quả:
- Số tham số 4810, khớp phép đếm ở mục Lý thuyết.
- Loss trên tập huấn luyện đi từ 1.4863 sau epoch đầu xuống 0.0416 sau epoch 30. Mốc so sánh: mạng chưa học, đoán đều 10 lớp, có loss . Loss ở epoch 10 (0.2626) cao hơn ở epoch 5 (0.2110): mỗi bước chỉ dùng 32 mẫu nên đường đi có nhiễu, như SGD ở bài Gradient Descent.
- Độ chính xác trên tập kiểm tra là 0.9756, tức 439 trên 450 ảnh chưa thấy lúc huấn luyện. Để so, hồi quy softmax (
LogisticRegressioncủa scikit-learn) trên cùng cách chia đạt 0.9689 trong lần chạy thử của bài: bộ dữ liệu nhỏ và dễ, nên lớp ẩn chỉ giúp thêm một chút.
Seed cố định nên mỗi lần chạy 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, các số có thể lệch đôi chút. Hãy thử đổi nn.ReLU() thành nn.Tanh() hoặc nn.GELU(), hoặc đổi số nơ-ron ẩn, rồi chạy lại.
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:
- Một nơ-ron tính rồi . Perceptron dùng hàm bậc thang và luật học ; luật này chỉ hội tụ khi dữ liệu tách được tuyến tính, nên không học được XOR.
- MLP xếp nơ-ron thành các lớp kết nối đầy đủ. Lan truyền xuôi: , rồi , , với kích thước . Lớp ẩn học ra cách biểu diễn mới, trong đó bài toán dễ hơn.
- Hàm kích hoạt phải phi tuyến, nếu không cả mạng chỉ là một lớp tuyến tính. ReLU là mặc định cho lớp ẩn; sigmoid và tanh bão hòa; Leaky ReLU tránh nơ-ron chết; GELU dùng trong Transformer.
- Phân loại lớp dùng nơ-ron ra, softmax và cross-entropy . Định lý xấp xỉ phổ quát nói MLP một lớp ẩn đủ rộng xấp xỉ được mọi hàm liên tục, nhưng không nói cách tìm trọng số: đó là việc của lan truyền ngược, ở bài sau.
Đọc thêm:
- Vũ Hữu Tiệp (2017). Machine Learning cơ bản, Bài 9: Perceptron Learning Algorithm và Bài 14: Multi-layer Perceptron và Backpropagation. Tiếng Việt, có chứng minh hội tụ của perceptron và ví dụ XOR. Ký hiệu khác: nhãn là , và ma trận trọng số của lớp có kích thước , tức là chuyển vị của trong bài này.
- Zhang, A., Lipton, Z. C., Li, M., Smola, A. J. (2023). Dive into Deep Learning, mục 5.1: Multilayer Perceptrons. Có đồ thị và đạo hàm của ReLU, sigmoid, tanh, kèm code. Sách xếp mẫu theo hàng (), như PyTorch.
- Goodfellow, I., Bengio, Y., Courville, A. (2016). Deep Learning, chương 6: Deep Feedforward Networks. Mục 6.1 giải XOR bằng một mạng ReLU nhỏ; mục 6.4.1 bàn định lý xấp xỉ phổ quát và vì sao độ sâu có ích.
- Nielsen, M. (2015). Neural Networks and Deep Learning, chương 4: A visual proof that neural nets can compute any function. “Chứng minh bằng hình” của định lý xấp xỉ phổ quát, có hình tương tác.
- Các bài gốc. Rosenblatt, F. (1958). The perceptron: A probabilistic model for information storage and organization in the brain. Psychological Review 65(6), 386–408. Cybenko, G. (1989). Approximation by superpositions of a sigmoidal function. Mathematics of Control, Signals and Systems 2(4), 303–314. Hendrycks, D., Gimpel, K. (2016). Gaussian Error Linear Units (GELUs). arXiv:1606.08415.
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