Danh sách bài viết

Bài 3: Multi-Layer Perceptron (MLP) — chồng nhiều layer

MLP là mạng chồng nhiều layer perceptron, mỗi layer có nhiều neuron fully connected với layer trước. Cấu trúc input / hidden / output, notation W, b, a, z, công thức số tham số, cách MLP giải XOR, Universal Approximation Theorem, width vs depth, output layer cho regression / classification, batch processing, hạn chế, code NumPy forward 784→128→10.

24/05/2026
14 phút đọc
2 lượt xem
1

Mục tiêu bài học

Sau bài học, bạn sẽ:

  • Hiểu MLP là chồng nhiều layer perceptron, mỗi layer là một fully connected (dense) layer cộng activation.
  • Phân biệt 3 loại layer: input, hidden, output.
  • Viết được công thức forward cho 1 layer: \( \mathbf{z}^{(l)} = W^{(l)} \mathbf{a}^{(l-1)} + \mathbf{b}^{(l)} \), \( \mathbf{a}^{(l)} = f(\mathbf{z}^{(l)}) \).
  • Đếm chính xác số tham số của một MLP cho trước.
  • Giải thích vì sao MLP với 1 hidden layer giải được XOR (mà perceptron đơn không).
  • Phát biểu được Universal Approximation Theorem (Hornik 1989, Cybenko 1989) ở mức trực giác.
  • Biết chọn output layer + loss cho 3 task: regression, binary classification, multi-class classification.
  • Tự viết được forward pass MLP 784–128–10 bằng NumPy thuần cho 1 mini-batch.

Bài này nối tiếp Bài 2 (Perceptron — đơn vị tính toán cơ bản nhất). Bài 4 (Forward Propagation) sẽ chính thức hoá luồng dữ liệu đi qua MLP nhiều layer; bài này tập trung vào kiến trúc tĩnh của MLP.

2

MLP là gì — chồng nhiều layer perceptron

Bài 2 đã định nghĩa perceptron: 1 neuron nhận \( \mathbf{x} \), tính \( z = \mathbf{w}^\top \mathbf{x} + b \), rồi cho qua một hàm activation \( f \) để ra \( a = f(z) \). Một neuron chỉ biểu diễn được hàm phân loại tuyến tính.

Multi-Layer Perceptron (MLP) là kiến trúc tổng quát hoá:

  • Xếp nhiều neuron cạnh nhau thành một layer.
  • Chồng nhiều layer nối tiếp, output của layer trước là input của layer sau.
  • Giữa 2 layer có activation phi tuyến — đây là phần khiến MLP mạnh hơn nhiều so với "chồng nhiều phép nhân ma trận".
      x1   x2   x3   x4            ← input (4 feature)
       ●    ●    ●    ●
        \  /|\  /|\  /|\  /
         \/ | \/ | \/ | \/
         /\ | /\ | /\ | /\
        /  \|/  \|/  \|/  \
       ●    ●    ●    ●    ●        ← hidden layer 1 (5 neuron)
        \   |    |    |   /
         \  |    |    |  /
       ●    ●    ●    ●             ← hidden layer 2 (4 neuron)
              \  |  /
               \ | /
                ●                   ← output layer (1 neuron)
                ŷ

Tên gọi "Multi-Layer Perceptron" có tính lịch sử — perceptron gốc của Rosenblatt 1958 chỉ có 1 layer. Mạng nhiều layer được Minsky & Papert (1969) bàn tới như mở rộng cần thiết để vượt qua giới hạn của 1 layer (XOR). Bộ ba Rumelhart, Hinton, Williams (1986) cho thấy có thể train MLP bằng backpropagation, mở đường cho thời kỳ neural network thứ hai.

Trong tài liệu hiện đại, "MLP" thường ngụ ý: feedforward network gồm các fully connected layer + activation, không có recurrence, không có convolution. Đây là kiến trúc cơ bản nhất của deep learning và vẫn là building block bên trong CNN, Transformer (mỗi block Transformer đều có một MLP con).

3

Cấu trúc 3 loại layer

Một MLP luôn có 3 vai trò layer:

Input layer

  • Không "tính" gì — chỉ là chỗ đặt feature input \( \mathbf{x} \).
  • Số neuron = số feature. Ảnh 28×28 grayscale flatten ra 784 ⇒ input layer 784 neuron. Vector embedding 768 chiều ⇒ input layer 768 neuron.
  • Vì không có trọng số, một số tài liệu không đếm input vào "số layer của mạng".

Hidden layer(s)

  • Layer ở giữa, học representation trung gian.
  • Có thể có 1 hidden layer, hoặc nhiều (deep network).
  • Số neuron mỗi hidden layer là hyperparameter do bạn chọn (thường thử 64, 128, 256, 512).
  • Mỗi neuron trong hidden layer là một perceptron đầy đủ: tính \( z \), qua activation \( f \).

Output layer

  • Layer cuối, cho ra prediction.
  • Số neuron và activation phụ thuộc task (xem mục 12): regression dùng linear, binary dùng sigmoid, multi-class dùng softmax.
  • Output layer thường đặt tên theo task: "regression head", "classification head".

Quy ước đánh số layer trong cả series: \( l = 0 \) là input, \( l = 1, 2, \ldots, L-1 \) là các hidden, \( l = L \) là output. Tổng số layer "có trọng số" là \( L \).

4

"Deep" — bao nhiêu layer thì gọi là sâu

"Deep learning" theo nghĩa lịch sử chỉ đơn giản là neural network với nhiều hidden layer. Không có ngưỡng cứng, nhưng quy ước hay gặp:

  • Shallow: 0 hoặc 1 hidden layer (logistic regression hoặc MLP 1 hidden).
  • Deep: từ 2–3 hidden layer trở lên.
  • Các kiến trúc hiện đại đi xa hơn nhiều: ResNet-50 có ~50 layer có trọng số, ResNet-152 có 152, Transformer LLM có 24 → 96+ layer.

Vì sao "sâu" lại có ích? Trực giác: mỗi hidden layer học một mức biểu diễn cao hơn của input. Với ảnh, các nghiên cứu visualization (Zeiler & Fergus 2014, arXiv:1311.2901) cho thấy: layer thấp học edge/cạnh, layer giữa học texture, layer cao học part/đối tượng. Đây là hierarchical representation.

Lưu ý: "deep" không tự động "tốt hơn". Mạng quá sâu sẽ gặp vanishing gradient, khó train; cần các kỹ thuật như residual connection (He et al. 2015, arXiv:1512.03385), batch normalization (sẽ học ở Module 3). Đối với MLP thuần, đi quá 3–5 hidden layer thường lợi ích biên không nhiều so với chi phí.

5

Fully Connected (Dense) layer

Định nghĩa: layer \( l \) là fully connected (FC, còn gọi là dense trong Keras / TensorFlow, nn.Linear trong PyTorch) nếu mỗi neuron của layer đó nối tới mọi neuron của layer \( l-1 \).

layer l-1: 4 neuron        layer l: 3 neuron
   a1 ●──────────────────● n1   (n1 nhận từ a1, a2, a3, a4)
       \\\\               /│\
        \\\\             / │ \
   a2 ●──\\\\───────────●  │  n2  (n2 nhận từ a1, a2, a3, a4)
         \\\\           /  │
          \\\\         /   │
   a3 ●────\\\\───────●    │  n3  (n3 nhận từ a1, a2, a3, a4)
            \\\\     /     │
             \\\\   /      │
   a4 ●───────\\\\─/───────┘
              4×3 = 12 kết nối → 12 trọng số + 3 bias

Tên gọi:

  • "Fully connected" / "Dense" / "Linear" thường được dùng thay thế nhau, đều chỉ cùng phép biến đổi tuyến tính \( \mathbf{z} = W \mathbf{a} + \mathbf{b} \).
  • PyTorch: torch.nn.Linear(in_features, out_features).
  • Keras / TF: tf.keras.layers.Dense(units).

Trong toàn bài này, "MLP" mặc định là chuỗi các fully connected layer xen kẽ activation. Kiến trúc khác (Conv, RNN, Attention) không phải MLP.

6

Notation chuẩn: W, b, a, z theo layer

Cả series sẽ dùng cùng một bộ ký hiệu. Tại layer \( l \):

  • \( \mathbf{a}^{(l-1)} \) — vector activation đầu vào của layer \( l \), kích thước \( n_{l-1} \).
  • \( W^{(l)} \) — ma trận trọng số của layer \( l \), kích thước \( n_l \times n_{l-1} \).
  • \( \mathbf{b}^{(l)} \) — vector bias, kích thước \( n_l \).
  • \( \mathbf{z}^{(l)} \) — pre-activation (trước khi qua hàm \( f \)).
  • \( \mathbf{a}^{(l)} \) — activation đầu ra của layer \( l \).

Phép tính của 1 layer:

\[ \mathbf{z}^{(l)} = W^{(l)} \, \mathbf{a}^{(l-1)} + \mathbf{b}^{(l)} \]

\[ \mathbf{a}^{(l)} = f\!\left(\mathbf{z}^{(l)}\right) \]

Trong đó:

  • Với input layer, đặt \( \mathbf{a}^{(0)} = \mathbf{x} \).
  • Với layer cuối, \( \mathbf{a}^{(L)} = \hat{\mathbf{y}} \) là output / prediction của mạng.
  • Hàm \( f \) là activation (Sigmoid, ReLU, Tanh… — bài 5–7). Hàm có thể khác nhau theo layer; thông thường mọi hidden dùng cùng \( f \) (ReLU), output layer dùng activation phù hợp task.

Ghi đầy đủ cho một MLP \( L \) layer áp lên input \( \mathbf{x} \):

\[ \hat{\mathbf{y}} = f^{(L)}\!\left( W^{(L)} \, f^{(L-1)}\!\left( W^{(L-1)} \cdots f^{(1)}\!\left( W^{(1)} \mathbf{x} + \mathbf{b}^{(1)} \right) \cdots + \mathbf{b}^{(L-1)} \right) + \mathbf{b}^{(L)} \right) \]

Đây là hàm hợp. Bài 4 sẽ trình bày forward propagation — cách lập trình bước-từng-bước để tính biểu thức này.

7

Shape của weight và bias

Gọi \( n_0 \) là số neuron input, \( n_l \) là số neuron layer \( l \). Quy tắc shape (cần thuộc lòng):

  • Input \( \mathbf{a}^{(l-1)} \in \mathbb{R}^{n_{l-1}} \).
  • Weight \( W^{(l)} \in \mathbb{R}^{n_l \times n_{l-1}} \) — số neuron layer mới ở hàng, số neuron layer cũ ở cột.
  • Bias \( \mathbf{b}^{(l)} \in \mathbb{R}^{n_l} \).
  • Output \( \mathbf{z}^{(l)}, \mathbf{a}^{(l)} \in \mathbb{R}^{n_l} \).

Kiểm tra phép nhân ma trận: \( W^{(l)} \) shape \( (n_l, n_{l-1}) \) nhân với \( \mathbf{a}^{(l-1)} \) shape \( (n_{l-1},) \) cho \( \mathbf{z}^{(l)} \) shape \( (n_l,) \). Cộng tiếp \( \mathbf{b}^{(l)} \) cùng shape \( (n_l,) \). Hợp lệ.

Ví dụ MLP nhỏ với 3 hidden layer: \( n_0 = 100, n_1 = 64, n_2 = 32, n_3 = 16, n_4 = 4 \). Shape mỗi ma trận:

W(1): (64, 100)    b(1): (64,)
W(2): (32, 64)     b(2): (32,)
W(3): (16, 32)     b(3): (16,)
W(4): (4,  16)     b(4): (4,)

Quy ước nhân ngược (input ở hàng, output ở cột — \( W^{(l)} \in \mathbb{R}^{n_{l-1} \times n_l} \) và \( \mathbf{z} = \mathbf{a}^\top W + \mathbf{b} \)) cũng thấy trong vài framework. PyTorch nn.Linear(in, out) tham số được lưu shape (out, in), khớp quy ước trong bài này. TensorFlow / Keras Dense lưu kernel shape (in, out) — ngược lại. Khi đọc tài liệu, kiểm tra quy ước trước khi viết công thức.

8

Đếm tham số: ví dụ MLP 784–128–10

"Tham số" (parameter) của mạng là tổng số số thực mà mạng học: tất cả phần tử trong \( W^{(l)} \) và \( \mathbf{b}^{(l)} \) gộp lại. Activation function không có tham số học được (trừ ngoại lệ như PReLU).

Công thức cho 1 layer \( l \):

\[ \text{params}(l) = n_l \cdot n_{l-1} + n_l = n_l (n_{l-1} + 1) \]

Phần \( n_l \cdot n_{l-1} \) là số phần tử của \( W^{(l)} \); phần \( +\, n_l \) là số bias.

Tổng tham số toàn mạng:

\[ \text{params}(\text{net}) = \sum_{l=1}^{L} n_l (n_{l-1} + 1) \]

Ví dụ thực tế: MLP 1 hidden cho MNIST (digit 0–9). Ảnh 28×28 grayscale flatten thành vector 784 chiều. Kiến trúc 784 → 128 → 10:

  • Layer 1 (784 → 128): \( 128 \times 784 + 128 = 100\,352 + 128 = 100\,480 \) tham số.
  • Layer 2 (128 → 10): \( 10 \times 128 + 10 = 1\,280 + 10 = 1\,290 \) tham số.
  • Tổng: \( 100\,480 + 1\,290 = 101\,770 \) tham số.

Số tham số quyết định:

  • Bộ nhớ chứa model (mỗi tham số 4 byte với float32, 2 byte với float16). 101.770 × 4 ≈ 400 KB.
  • Lượng tính toán mỗi forward pass (xấp xỉ \( 2 \times \text{params} \) FLOP).
  • Khả năng overfit: tham số càng nhiều, mạng càng dễ "thuộc lòng" data nhỏ.

Để so sánh: ResNet-50 có ~25 triệu tham số, GPT-2 small ~124 triệu, Llama 2 7B ~7 tỉ, GPT-4 ước tính hàng nghìn tỉ. Quy mô tham số là một chỉ số (không phải duy nhất) của khả năng model.

9

Vì sao MLP giải được XOR

Bài 2 đã chỉ ra perceptron đơn (1 neuron) không thể phân loại XOR vì XOR không phân tách được bằng 1 đường thẳng. Cách MLP với 1 hidden layer 2 neuron giải bài này:

  • Neuron hidden 1 học một đường thẳng phân tách (ví dụ: chia điểm \( (1,0) \) sang một bên, ba điểm còn lại sang bên kia).
  • Neuron hidden 2 học một đường thẳng khác (chia điểm \( (0,1) \) sang một bên).
  • Output neuron combine hai feature mới \( h_1, h_2 \) — đây không còn là không gian input gốc, mà là không gian biểu diễn đã được "uốn".
Không gian input (x1, x2)         Không gian hidden (h1, h2)
                                    (sau khi qua 2 neuron + activation)

  x2                                  h2
   ^                                   ^
   │  ●(0,1)─────●(1,1)               │      ●─────●
   │  │ y=1       y=0                 │
   │  │                               │           ●
   │  │                               │
   │  ●(0,0)─────●(1,0)               │      ●
   │    y=0       y=1                 │
   └──────────────────> x1            └──────────────────> h1
   không tách được bằng 1 đường        có thể tách bằng 1 đường

Trực giác chính: hidden layer "biến đổi không gian". Không gian mới có thể làm cho các lớp tách rời nhau tuyến tính, dù không gian gốc không như vậy. Activation phi tuyến là điều kiện cần — nếu cả hai layer đều linear thì hàm hợp vẫn linear (xem mục 14).

Tổng quát hoá, hai bài báo gần như đồng thời năm 1989 chứng minh kết quả mạnh: với đủ neuron, MLP 1 hidden layer có thể xấp xỉ bất kỳ hàm continuous nào trên một tập compact — không chỉ XOR.

10

Universal Approximation Theorem

Phát biểu (rút gọn, không hình thức) — phiên bản Cybenko 1989 và Hornik 1989:

Với mọi hàm continuous \( g : [0,1]^n \to \mathbb{R} \) và mọi \( \varepsilon > 0 \), tồn tại một MLP với 1 hidden layer sử dụng activation sigmoid (Cybenko) hoặc một activation phi tuyến bị chặn (Hornik), với số neuron đủ lớn, sao cho mạng đó xấp xỉ \( g \) với sai số \( < \varepsilon \) trên toàn miền.

Điều theorem nói:

  • MLP về mặt biểu diễn là một universal approximator. Không có hàm continuous nào nằm ngoài tầm với của MLP — cho đủ neuron.
  • 1 hidden layer (về lý thuyết) đã đủ. Không bắt buộc phải sâu để "diễn tả được mọi thứ".

Điều theorem không nói:

  • Không nói có thể tìm được trọng số đúng bằng SGD — đây là vấn đề optimization.
  • Không nói số neuron cần là khả thi — có thể cần số neuron khổng lồ.
  • Không nói model sẽ generalize tốt trên data mới — đây là vấn đề statistics.

Vì sao thực tế DL hiện đại đi sâu thay vì rộng? Các kết quả lý thuyết và thực nghiệm (Telgarsky 2016 arXiv:1602.04485, Eldan & Shamir 2016 arXiv:1512.03965) cho thấy: có những hàm mà deep + narrow network biểu diễn được với số neuron đa thức, trong khi shallow + wide (1 hidden) cần số neuron tăng theo cấp luỹ thừa. Deep network "kinh tế" hơn về tham số cho cùng độ biểu diễn.

Vai trò của Universal Approximation Theorem: cho ta yên tâm về khả năng biểu diễn của MLP. Mọi thử thách còn lại (optimization, generalization, kiến trúc tốt cho từng dạng data) là kỹ thuật.

11

Width vs Depth

Hai chiều "kích cỡ" của một MLP:

  • Width (rộng): số neuron mỗi layer (\( n_l \)).
  • Depth (sâu): số layer (\( L \)).

So sánh 2 MLP cùng ~100K tham số trên MNIST (784 input → 10 output):

A) "Wide & Shallow": 784 → 128 → 10
   Layer 1: 128 × 784 + 128 = 100,480
   Layer 2:  10 × 128 +  10 = 1,290
   Tổng: 101,770 tham số,  L = 2 layer

B) "Narrow & Deep":  784 → 100 → 100 → 100 → 10
   Layer 1: 100 × 784 + 100 = 78,500
   Layer 2: 100 × 100 + 100 = 10,100
   Layer 3: 100 × 100 + 100 = 10,100
   Layer 4:  10 × 100 +  10 = 1,010
   Tổng: 99,710 tham số,  L = 4 layer

Hai mạng cùng ngân sách tham số. Quan sát thực tế:

  • Mạng B (sâu hơn) thường học được biểu diễn hierarchical: layer thấp → đặc trưng đơn giản, layer cao → đặc trưng phức tạp hơn. Trên ảnh, sequence, đây là điểm mạnh quyết định.
  • Mạng A (rộng hơn) dễ train hơn (ít vấn đề vanishing gradient), nhưng khó học representation phân tầng.
  • Với MNIST cụ thể, hai mạng có accuracy gần nhau (~98%). Trên data phức tạp hơn (ImageNet, ngôn ngữ), depth thắng width rõ rệt.

Quy tắc kinh nghiệm cho MLP thuần: bắt đầu với 1–3 hidden layer, mỗi layer 64–512 neuron, tăng dần nếu underfit, giảm hoặc thêm regularization nếu overfit. Quy tắc giảm dần neuron theo chiều sâu (ví dụ 512 → 256 → 128) hay được dùng nhưng không bắt buộc.

12

MLP cho task gì — output layer 3 dạng

Hidden layer của MLP gần như giống nhau giữa các task. Phần thay đổi là output layer + loss function tương ứng. Ba dạng chuẩn:

Regression — dự đoán giá trị thực

  • Output layer: \( n_L \) neuron, không activation (hoặc linear / identity).
  • \( \hat{\mathbf{y}} = W^{(L)} \mathbf{a}^{(L-1)} + \mathbf{b}^{(L)} \in \mathbb{R}^{n_L} \).
  • Loss: MSE (Mean Squared Error) — bài 9.
  • Ví dụ: dự đoán giá nhà (\( n_L = 1 \)), dự đoán toạ độ 3D (\( n_L = 3 \)).

Binary classification — phân loại 2 lớp

  • Output layer: 1 neuron, activation sigmoid.
  • \( \hat{y} = \sigma(z^{(L)}) \in (0, 1) \), được hiểu là \( P(y = 1 \mid \mathbf{x}) \).
  • Loss: Binary Cross-Entropy (BCE) — bài 10.
  • Ví dụ: spam / không spam, có ung thư / không, click / không click.

Multi-class classification — phân loại \( K \) lớp

  • Output layer: K neuron, activation softmax:

    \[ \hat{y}_k = \frac{e^{z^{(L)}_k}}{\sum_{j=1}^{K} e^{z^{(L)}_j}}, \qquad \sum_{k=1}^{K} \hat{y}_k = 1 \]

  • \( \hat{\mathbf{y}} \) là phân phối xác suất trên \( K \) lớp.
  • Loss: Categorical Cross-Entropy — bài 10.
  • Ví dụ: MNIST (\( K = 10 \) chữ số), CIFAR-10 (\( K = 10 \) loại ảnh).

Một dạng đặc biệt: multi-label classification (1 ảnh có thể có nhiều nhãn cùng lúc). Output \( K \) neuron, mỗi neuron dùng sigmoid riêng (không softmax), loss là tổng các BCE độc lập. Ví dụ: tag ảnh, dự đoán nhiều bệnh đồng thời.

Trong toàn series 3, mẫu này lặp đi lặp lại: hidden layer "giống nhau", chỉ phần đầu cuối thay đổi tuỳ task. Đây là điểm mạnh cấu trúc của neural network.

13

Batch processing — forward một mini-batch

Cho tới giờ ta viết forward cho một sample \( \mathbf{x} \). Thực tế training, ta forward cả một mini-batch \( B \) sample cùng lúc (xem Bài 27 series 2 về Mini-batch GD).

Đặt \( X \) là ma trận shape \( (B, n_0) \) — mỗi hàng là 1 sample. Forward layer 1:

\[ Z^{(1)} = X \, (W^{(1)})^\top + \mathbf{b}^{(1)}, \qquad A^{(1)} = f(Z^{(1)}) \]

  • \( X \in \mathbb{R}^{B \times n_0} \).
  • \( W^{(1)} \in \mathbb{R}^{n_1 \times n_0} \), nên \( (W^{(1)})^\top \in \mathbb{R}^{n_0 \times n_1} \).
  • Tích \( X (W^{(1)})^\top \in \mathbb{R}^{B \times n_1} \).
  • \( \mathbf{b}^{(1)} \in \mathbb{R}^{n_1} \) được broadcast sang shape \( (B, n_1) \) (cộng cùng bias vào mỗi hàng).
  • \( Z^{(1)}, A^{(1)} \in \mathbb{R}^{B \times n_1} \).

Tiếp tục tương tự cho các layer sau. Tất cả sample trong batch được forward đồng thời bằng một phép nhân ma trận duy nhất — đây là nơi GPU phát huy: GPU thực hiện phép nhân ma trận rất nhanh.

Một số convention quan trọng khi đọc code:

  • "Batch first": shape \( (B, \text{features}) \) — chuẩn của PyTorch nn.Linear, NumPy, mọi tài liệu hiện đại.
  • "Feature first": shape \( (\text{features}, B) \) — đôi khi gặp trong tài liệu lý thuyết. Hai cách chỉ khác việc transpose.
  • Trong PyTorch, hàm nn.Linear nhận input shape (*, in_features) và trả về (*, out_features); phần * có thể là batch hoặc batch + sequence (cho RNN/Transformer).
14

MLP vs Linear Model — vai trò activation

Câu hỏi: nếu bỏ activation \( f \) khỏi mọi layer, MLP còn mạnh không?

Giả sử mạng có 3 layer, mọi activation là identity \( f(z) = z \). Forward:

\[ \hat{\mathbf{y}} = W^{(3)} \left( W^{(2)} \left( W^{(1)} \mathbf{x} + \mathbf{b}^{(1)} \right) + \mathbf{b}^{(2)} \right) + \mathbf{b}^{(3)} \]

Khai triển:

\[ \hat{\mathbf{y}} = \underbrace{W^{(3)} W^{(2)} W^{(1)}}_{=\,W'} \mathbf{x} + \underbrace{W^{(3)} W^{(2)} \mathbf{b}^{(1)} + W^{(3)} \mathbf{b}^{(2)} + \mathbf{b}^{(3)}}_{=\,\mathbf{b}'} \]

Đặt \( W' = W^{(3)} W^{(2)} W^{(1)} \) và \( \mathbf{b}' \) là phần const. Mạng trở thành:

\[ \hat{\mathbf{y}} = W' \mathbf{x} + \mathbf{b}' \]

— đúng bằng một linear layer. Chồng bao nhiêu fully connected layer mà không có activation phi tuyến cũng chỉ tương đương 1 layer.

Kết luận:

  • Activation phi tuyến là chìa khoá phân biệt MLP với linear model.
  • Linear regression / logistic regression chính là MLP 0 hidden layer với activation phù hợp ở output. Đây là trường hợp đặc biệt của MLP.
  • Bài 5–8 sẽ trình bày các activation phổ biến: Sigmoid, ReLU, Tanh, Softmax. Mỗi activation có ưu/nhược riêng nhưng tất cả đều mang vai trò "tiêm phi tuyến" vào giữa các phép biến đổi tuyến tính.
15

Hạn chế của MLP

MLP là kiến trúc tổng quát, dùng được cho mọi input dạng vector. Nhưng có 3 hạn chế bộc lộ khi gặp dữ liệu có cấu trúc:

  • Không khai thác cấu trúc spatial: với ảnh, hai pixel kề nhau có quan hệ chặt hơn hai pixel xa nhau. MLP flatten ảnh thành vector — thông tin "kề nhau" bị mất. CNN (Module 4) ra đời để giải quyết bằng convolution + weight sharing.
  • Không khai thác cấu trúc sequence: với text, audio, time-series, thứ tự token / sample là quan trọng. MLP không có cơ chế xử lý độ dài thay đổi hoặc lan truyền trạng thái. RNN / LSTM (Module 5) và Transformer (Series 4) phù hợp hơn.
  • Số tham số bùng nổ với input chiều cao: ảnh 224×224×3 = 150.528 pixel. MLP nối tới hidden 1024 neuron đã cần \( 150\,528 \times 1024 + 1024 \approx 154 \) triệu tham số chỉ riêng layer đầu — không thực tế, dễ overfit, không tận dụng spatial locality. CNN với cùng input nhưng chia sẻ kernel chỉ dùng vài chục nghìn tham số.

Dù vậy, MLP vẫn được dùng thường xuyên:

  • Làm baseline trước khi thử kiến trúc phức tạp hơn.
  • Làm classification head ở cuối CNN (sau Global Average Pooling) hoặc Transformer (sau CLS token).
  • Trong nội bộ block Transformer — mỗi block có một MLP 2 layer (gọi là "feed-forward network" hoặc "MLP block"), với chiều ẩn lớn gấp 4× chiều embedding.
  • Với data dạng tabular (số liệu, chỉ số) — MLP vẫn cạnh tranh tốt với mô hình cây như XGBoost (Gorishniy et al. 2021, arXiv:2106.11189).
16

Đặt tên architecture: quy ước đếm layer

Ký hiệu MLP thường thấy:

  • "MLP-784-128-64-10" hoặc "784 → 128 → 64 → 10": input 784, hidden lần lượt 128 và 64, output 10. Cách viết này không có thể nhầm — luôn rõ.
  • "3-layer MLP": không có quy ước thống nhất. Có tác giả đếm 3 = 1 input + 2 hidden, có người đếm 3 = 2 hidden + 1 output, có người ý là 3 hidden layer. Khi đọc paper hoặc viết của riêng mình, hãy nói rõ "3 hidden layer" hoặc liệt kê dimension đầy đủ để tránh hiểu lầm.

Convention thực tế hay dùng (theo Goodfellow et al., Deep Learning Book):

  • "Depth của mạng" = số layer có trọng số = số phép biến đổi tuyến tính + activation. Input layer không tính.
  • MLP 784 → 128 → 10 = "MLP 2 layer" (1 hidden + 1 output).
  • MLP 784 → 256 → 128 → 64 → 10 = "MLP 4 layer" (3 hidden + 1 output).

Trong toàn series 3, ta theo convention này: đếm theo số layer có trọng số.

17

Code Python — forward MLP 784–128–10 bằng NumPy

Implement forward pass cho MLP 1 hidden layer, kiểm shape đúng với công thức ở mục 7. Activation hidden = ReLU, output = softmax (multi-class).

import numpy as np

# Cố định seed để kết quả tái lập
rng = np.random.default_rng(seed=0)

# Kiến trúc
n0 = 784    # input: ảnh 28x28 flatten
n1 = 128    # hidden layer
n2 = 10     # output: 10 lớp (chữ số 0-9)
B  = 64     # batch size

# Khởi tạo tham số (chưa quan tâm scheme khởi tạo — bài 20 sẽ làm)
W1 = rng.standard_normal((n1, n0)) * 0.01
b1 = np.zeros(n1)
W2 = rng.standard_normal((n2, n1)) * 0.01
b2 = np.zeros(n2)

# Đếm tham số
n_params = W1.size + b1.size + W2.size + b2.size
print(f"Số tham số: {n_params:,}")
# 128*784 + 128 + 10*128 + 10 = 101,770

# Activation
def relu(x):
    return np.maximum(0.0, x)

def softmax(x, axis=-1):
    # trừ max để ổn định số học
    x_shift = x - x.max(axis=axis, keepdims=True)
    e = np.exp(x_shift)
    return e / e.sum(axis=axis, keepdims=True)

# Một mini-batch input random
X = rng.standard_normal((B, n0))   # shape (64, 784)

# Forward — chú ý quy ước: W shape (n_out, n_in)
# nên với batch ở hàng, ta dùng X @ W.T
Z1 = X @ W1.T + b1                  # (B, n1)
A1 = relu(Z1)                       # (B, n1)

Z2 = A1 @ W2.T + b2                 # (B, n2)
Y_hat = softmax(Z2, axis=1)         # (B, n2), mỗi hàng sum = 1

print(f"X     shape: {X.shape}")
print(f"Z1    shape: {Z1.shape}")
print(f"A1    shape: {A1.shape}")
print(f"Z2    shape: {Z2.shape}")
print(f"Y_hat shape: {Y_hat.shape}")
print(f"Tổng xác suất hàng 0: {Y_hat[0].sum():.6f}")
print(f"Lớp dự đoán cho 5 sample đầu: {Y_hat[:5].argmax(axis=1)}")

Output điển hình:

Số tham số: 101,770
X     shape: (64, 784)
Z1    shape: (64, 128)
A1    shape: (64, 128)
Z2    shape: (64, 10)
Y_hat shape: (64, 10)
Tổng xác suất hàng 0: 1.000000
Lớp dự đoán cho 5 sample đầu: [3 7 9 0 1]

Quan sát:

  • Số tham số khớp tính tay: 101.770.
  • Shape đi qua mạng đúng quy ước "batch first": \( (B, n_l) \) ở mọi tầng activation.
  • Mỗi hàng của Y_hat là phân phối xác suất trên 10 lớp (sum = 1). Vì trọng số khởi tạo ngẫu nhiên nhỏ và chưa train, prediction còn vô nghĩa — model "đoán bừa".
  • Đây là toàn bộ forward pass của một MLP. Phần còn thiếu để training thật: loss + backward + optimizer.step() — sẽ học ở Bài 4 (Forward Propagation chi tiết), Bài 9–10 (Loss), Bài 11 (Backpropagation).
18

Bài tập

  1. Tính tay số tham số của MLP 100 → 50 → 50 → 10. Liệt kê \( W^{(l)} \), \( \mathbf{b}^{(l)} \) cho từng layer và shape của chúng.
  2. Một MLP có 3 hidden layer 256–128–64 cho input chiều 1000 và output 5 lớp (softmax). Tính tổng tham số. So sánh với MLP "shallow" 1000 → 1024 → 5 — bên nào nhiều tham số hơn?
  3. Implement forward pass cho mạng XOR kiến trúc 2 → 2 → 1 với sigmoid ở cả hidden và output. Khởi tạo trọng số cụ thể:
    W1 = np.array([[20.0, 20.0], [-20.0, -20.0]])
    b1 = np.array([-10.0, 30.0])
    W2 = np.array([[20.0, 20.0]])
    b2 = np.array([-30.0])
    Forward cho 4 input \( (0,0), (0,1), (1,0), (1,1) \). Output có khớp XOR (0, 1, 1, 0) không?
  4. Với mạng XOR ở bài 3, thử thay sigmoid bằng identity (linear). Forward lại 4 input. Output còn khớp XOR nữa không? Vì sao? (Liên hệ mục 14.)
  5. Một MLP cho ảnh ImageNet (input 224 × 224 × 3 = 150.528) chỉ 1 hidden layer 4096 và output 1000 lớp. Tính số tham số. So sánh với ResNet-50 (~25M params). Vì sao MLP cho ảnh "không kinh tế"?
  6. Code mục 17, đổi B = 1 rồi rerun. Có bị lỗi gì không? Tổng quát hoá: tại sao "batch first" lại tự nhiên với NumPy / PyTorch?
Đáp án ngắn
  1. Layer 1: \( W^{(1)} \in \mathbb{R}^{50 \times 100} \), \( b^{(1)} \in \mathbb{R}^{50} \) → 5.050 params. Layer 2: \( W^{(2)} \in \mathbb{R}^{50 \times 50} \), \( b^{(2)} \in \mathbb{R}^{50} \) → 2.550 params. Layer 3: \( W^{(3)} \in \mathbb{R}^{10 \times 50} \), \( b^{(3)} \in \mathbb{R}^{10} \) → 510 params. Tổng: 8.110 params.
  2. Sâu: \( (256 \cdot 1000 + 256) + (128 \cdot 256 + 128) + (64 \cdot 128 + 64) + (5 \cdot 64 + 5) = 256\,256 + 32\,896 + 8\,256 + 325 = 297\,733 \). Shallow: \( (1024 \cdot 1000 + 1024) + (5 \cdot 1024 + 5) = 1\,025\,024 + 5\,125 = 1\,030\,149 \). Shallow nhiều hơn ~3.4×.
  3. Mạng cho output gần 0 với input (0,0), (1,1) và gần 1 với (0,1), (1,0) — khớp XOR. Đây là một bộ trọng số "thủ công" cho XOR; tồn tại nhiều bộ khác.
  4. Mạng trở thành linear (mục 14): tương đương 1 layer. Không tách được XOR. Output sẽ là 4 giá trị nằm trên 1 đường thẳng, không tương ứng XOR.
  5. \( 4096 \cdot 150\,528 + 4096 + 1000 \cdot 4096 + 1000 = 616\,562\,688 + 4\,096 + 4\,096\,000 + 1\,000 \approx 620{,}7 \) triệu params — gấp 25× ResNet-50, nhưng accuracy kém xa. MLP không khai thác spatial locality và phải học redundant filter cho mỗi vị trí.
  6. Không lỗi: B = 1 vẫn cho shape (1, 784) → (1, 10). NumPy broadcast bias hoạt động bất kể B. "Batch first" tự nhiên vì matmul \( X W^\top \) hoạt động cho mọi \( B \geq 1 \), và hardware tối ưu cho phép tính ma trận này.
19

Tóm tắt

  • MLP = chồng nhiều fully connected layer + activation phi tuyến. 3 vai trò: input, hidden, output.
  • Forward 1 layer: \( \mathbf{z}^{(l)} = W^{(l)} \mathbf{a}^{(l-1)} + \mathbf{b}^{(l)} \), \( \mathbf{a}^{(l)} = f(\mathbf{z}^{(l)}) \).
  • Shape: \( W^{(l)} \in \mathbb{R}^{n_l \times n_{l-1}} \), \( \mathbf{b}^{(l)} \in \mathbb{R}^{n_l} \). Tham số mỗi layer \( = n_l (n_{l-1} + 1) \).
  • MLP 784 → 128 → 10 có 101.770 tham số.
  • MLP với 1 hidden layer giải được XOR — biến đổi không gian, làm cho 2 lớp tách rời tuyến tính trong không gian mới.
  • Universal Approximation Theorem (Hornik 1989, Cybenko 1989): MLP đủ rộng (1 hidden) có thể xấp xỉ mọi hàm continuous. Không nói về tính khả thi của training, generalization.
  • Deep + narrow thường biểu diễn hiệu quả hơn shallow + wide cho cùng số tham số.
  • 3 dạng output layer: linear (regression + MSE), sigmoid (binary + BCE), softmax (multi-class + cross-entropy).
  • Batch processing: shape \( (B, n_0) \) đi vào, mọi sample được forward đồng thời bằng matrix multiplication — tận dụng GPU.
  • Hạn chế MLP: không khai thác cấu trúc spatial / sequence, bùng nổ tham số với input chiều cao. Vẫn là baseline và là building block của CNN / Transformer.

Bài 4 (Forward Propagation) sẽ chính thức hoá thuật toán "forward" — viết thành vòng lặp theo layer, làm rõ thứ tự lưu trung gian cần thiết cho backpropagation ở Bài 11.