Giao diện
Matrix Operations — Phép nhân ma trận và trực giác Neural Network
🎯 Mục tiêu
- Hiểu dot product như phép đo similarity giữa hai vector
- Hiểu matrix multiply như phép batch transformation — biến đổi nhiều điểm dữ liệu cùng lúc
- Kết nối hai phép toán trên với forward pass của neural network:
y = X @ W + b - Biết khi nào dùng transpose, khi nào (không) dùng inverse
1. Dot Product = Đo Similarity
1.1 Trực giác: Hai người dùng có giống nhau không?
Hãy tưởng tượng bạn có hai user và mỗi user được biểu diễn bằng một vector sở thích (preference vector):
User A: [5, 0, 3, 0, 2] ← thích Phim hành động, không thích Romance, ...
User B: [4, 0, 4, 0, 1] ← sở thích tương tự User A
User C: [0, 5, 0, 4, 0] ← sở thích hoàn toàn khácDot product giữa hai vector cho ta một con số đo mức độ "cùng hướng" của chúng:
a · b = a₁×b₁ + a₂×b₂ + ... + aₙ×bₙ
┌─────────────────────────────────────────────┐
│ Vector A: [5, 0, 3, 0, 2] │
│ ↓ dot product │
│ Vector B: [4, 0, 4, 0, 1] │
│ ↓ │
│ 5×4 + 0×0 + 3×4 + 0×0 + 2×1 = 34 │
│ │
│ Hai vector → MỘT con số (scalar) │
└─────────────────────────────────────────────┘- Dot product lớn → hai vector "cùng hướng" → hai user có sở thích giống nhau
- Dot product ≈ 0 → hai vector vuông góc → không liên quan
- Dot product âm → hai vector "ngược hướng" → sở thích trái ngược
1.2 Code với NumPy
python
import numpy as np
user_a = np.array([5, 0, 3, 0, 2])
user_b = np.array([4, 0, 4, 0, 1])
user_c = np.array([0, 5, 0, 4, 0])
# Cách 1: np.dot()
similarity_ab = np.dot(user_a, user_b)
print(f"A·B = {similarity_ab}") # 34 — rất giống nhau
# Cách 2: toán tử @ (Python 3.5+, khuyên dùng)
similarity_ac = user_a @ user_c
print(f"A·C = {similarity_ac}") # 0 — hoàn toàn khác nhau1.3 Cosine Similarity = Normalized Dot Product
Dot product thô bị ảnh hưởng bởi độ lớn (magnitude) của vector. Một user rate từ 1-5, user khác rate từ 1-100 — dot product sẽ thiên vị user rate lớn.
Cosine similarity chuẩn hóa bằng cách chia cho tích độ dài:
python
def cosine_similarity(a, b):
"""Dot product chia cho tích norm — kết quả từ -1 đến 1."""
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
print(cosine_similarity(user_a, user_b)) # ~0.98 → rất giống
print(cosine_similarity(user_a, user_c)) # 0.0 → không liên quan| Giá trị | Ý nghĩa |
|---|---|
1.0 | Hoàn toàn cùng hướng (identical direction) |
0.0 | Vuông góc (orthogonal — không liên quan) |
-1.0 | Hoàn toàn ngược hướng |
1.4 Ứng dụng thực tế: Document Similarity với TF-IDF
💼 Business Example
Trong search engine, mỗi document được biểu diễn bằng một TF-IDF vector (mỗi chiều = một từ, giá trị = tần suất × tầm quan trọng). Cosine similarity giữa hai vector TF-IDF cho biết hai document giống nhau đến mức nào — đây là core algorithm của nhiều recommendation system.
python
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
docs = [
"neural network deep learning",
"deep learning backpropagation",
"cooking recipe pasta italian"
]
tfidf = TfidfVectorizer()
vectors = tfidf.fit_transform(docs)
# Ma trận similarity giữa tất cả cặp documents
sim_matrix = cosine_similarity(vectors)
print(sim_matrix)
# doc0 vs doc1: ~0.45 (liên quan)
# doc0 vs doc2: ~0.00 (không liên quan)2. Matrix Multiply = Batch Transformation
2.1 Shape Rule — Quy tắc vàng
Quy tắc duy nhất cần nhớ:
(m, n) @ (n, p) → (m, p)
↑ ↑ ↑
│ MUST MATCH │
│ │
Giữ lại Giữ lại- Inner dimensions (n và n) phải bằng nhau — nếu không, lỗi
ValueError - Outer dimensions (m và p) tạo thành shape output
💡 Mẹo nhớ
Viết shape ra giấy theo hàng ngang: (m, n) @ (n, p). Hai số ở giữa "triệt tiêu" nhau, còn lại hai số ngoài cùng → output shape (m, p).
2.2 Trực giác: Mỗi hàng "gặp" mỗi cột
Matrix A (2×3) Matrix B (3×2) Result C (2×2)
┌─────────────┐ ┌─────────┐ ┌───────────┐
│ a₁₁ a₁₂ a₁₃│ │ b₁₁ b₁₂│ │ c₁₁ c₁₂ │
│ a₂₁ a₂₂ a₂₃│ @ │ b₂₁ b₂₂│ = │ c₂₁ c₂₂ │
└─────────────┘ │ b₃₁ b₃₂│ └───────────┘
└─────────┘
c₁₁ = ROW 1 of A · COL 1 of B (dot product!)
c₁₂ = ROW 1 of A · COL 2 of B
c₂₁ = ROW 2 of A · COL 1 of B
c₂₂ = ROW 2 of A · COL 2 of B
→ Mỗi phần tử output = MỘT dot productNhìn thấy chưa? Matrix multiply = rất nhiều dot products tổ chức thành lưới. Đây là lý do nó liên quan mật thiết đến similarity.
2.3 Trực giác: Batch Transformation
Thay vì tính dot product từng cặp một (chậm), matrix multiply cho phép biến đổi cả batch cùng lúc:
python
# 1000 users, mỗi user có 50 features
X = np.random.randn(1000, 50)
# Ma trận biến đổi: giảm từ 50 features xuống 10 latent factors
W = np.random.randn(50, 10)
# Batch transformation — 1 dòng code, xử lý TẤT CẢ 1000 users
result = X @ W # shape: (1000, 10)
print(f"Input: {X.shape}") # (1000, 50)
print(f"Weight: {W.shape}") # (50, 10)
print(f"Output: {result.shape}") # (1000, 10)💼 Business Example — Dimensionality Reduction
1000 users × 50 features → 1000 users × 10 latent factors
Đây chính là core idea của nhiều kỹ thuật: PCA, matrix factorization trong recommendation systems, và đặc biệt — neural network layers.
3. The Neural Network Forward Pass
3.1 Phương trình quan trọng nhất trong Deep Learning
python
y = X @ W + bChỉ một dòng này là nền tảng của mọi neural network layer. Hãy phân tích từng phần:
┌──────────────────────────────────────────────────────────────────┐
│ │
│ Input (batch × features) @ Weights (features × neurons) │
│ (32, 784) @ (784, 128) │
│ │
│ + Bias (neurons,) │
│ + (128,) │
│ │
│ = Output (32, 128) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────┐ ┌──────────┐ │
│ │ X │ │ W │ │ b │ │ y │ │
│ │ 32 ảnh │ @ │ 784→128 │ + │ 128 │ = │ 32 ảnh │ │
│ │ mỗi ảnh │ │ trọng số │ │ bias │ │ mỗi ảnh │ │
│ │ 784 pixel│ │ mỗi │ │ mỗi │ │ 128 │ │
│ │ │ │ neuron │ │neuron│ │ features │ │
│ └──────────┘ └──────────┘ └──────┘ └──────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘3.2 Giải thích từng thành phần
| Ký hiệu | Shape | Ý nghĩa |
|---|---|---|
X | (batch_size, n_features) | Dữ liệu đầu vào — mỗi hàng là một sample |
W | (n_features, n_neurons) | Trọng số — mỗi cột là một neuron |
b | (n_neurons,) | Bias — mỗi neuron có một bias riêng |
y | (batch_size, n_neurons) | Output — mỗi hàng là kết quả cho một sample |
3.3 Mỗi neuron = Một cột của W = Một dot product
Hãy zoom vào một neuron (cột thứ j của W):
python
# Neuron j nhìn TẤT CẢ features của input
# và tạo ra MỘT số cho mỗi sample
neuron_j_output = X @ W[:, j] + b[j]
# (32, 784) @ (784,) + scalar → (32,)
# Mỗi sample: dot product giữa input vector và weight vector của neuron j
# = đo "mức độ match" giữa input và pattern mà neuron j tìm kiếm💡 Trực giác sâu
Mỗi neuron là một pattern detector. Weight vector của nó định nghĩa pattern cần tìm. Dot product với input = đo mức độ input match với pattern đó. Đây là lý do neural network có thể nhận dạng hình ảnh, ngôn ngữ, ...
3.4 Kết nối với Broadcasting (bài trước)
Nhớ broadcasting từ Bài 02? Phép cộng bias chính là broadcasting:
python
# X @ W có shape (32, 128)
# b có shape (128,)
# Broadcasting: (32, 128) + (128,) → (32, 128)
# b được "copy" cho tất cả 32 samples trong batch
output = X @ W + b # broadcasting tự động xử lý!Không cần vòng lặp, không cần reshape thủ công — NumPy broadcasting làm hết.
3.5 Forward pass hoàn chỉnh (multi-layer)
python
import numpy as np
def relu(x):
"""Activation function — bỏ giá trị âm."""
return np.maximum(0, x)
# Dữ liệu: 32 ảnh MNIST, mỗi ảnh 784 pixels
X = np.random.randn(32, 784)
# Layer 1: 784 → 128
W1 = np.random.randn(784, 128) * 0.01
b1 = np.zeros(128)
z1 = X @ W1 + b1 # linear transform
a1 = relu(z1) # activation
# Layer 2: 128 → 64
W2 = np.random.randn(128, 64) * 0.01
b2 = np.zeros(64)
z2 = a1 @ W2 + b2
a2 = relu(z2)
# Output layer: 64 → 10 (10 digits)
W3 = np.random.randn(64, 10) * 0.01
b3 = np.zeros(10)
logits = a2 @ W3 + b3 # (32, 10) — scores cho 10 chữ số
print(f"Input: {X.shape}") # (32, 784)
print(f"Layer1: {a1.shape}") # (32, 128)
print(f"Layer2: {a2.shape}") # (32, 64)
print(f"Output: {logits.shape}") # (32, 10)Toàn bộ forward pass chỉ là chuỗi matrix multiply + activation. Không có phép toán nào khác ở core level.
4. Transpose — Đảo hàng và cột
4.1 Cơ bản
python
A = np.array([[1, 2, 3],
[4, 5, 6]])
print(A.shape) # (2, 3)
print(A.T.shape) # (3, 2)
# Hàng thành cột, cột thành hàng:
# ┌─────────┐ ┌─────┐
# │ 1 2 3 │ .T │ 1 4 │
# │ 4 5 6 │ ───→ │ 2 5 │
# └─────────┘ │ 3 6 │
# (2, 3) └─────┘
# (3, 2)4.2 Khi nào cần Transpose?
Trường hợp phổ biến nhất: điều chỉnh shape để matrix multiply hợp lệ.
python
# Bạn có X shape (100, 5) và muốn tính X^T @ X
# (100, 5).T @ (100, 5) → lỗi! (5, 100) @ (100, 5) → OK!
X = np.random.randn(100, 5)
# X^T @ X = Gram matrix (liên quan correlation/covariance)
gram = X.T @ X # (5, 100) @ (100, 5) → (5, 5)
print(gram.shape) # (5, 5) — ma trận vuông 5×54.3 Pattern quan trọng: X.T @ X
python
# Chuẩn hóa X (trừ mean)
X_centered = X - X.mean(axis=0)
# Covariance matrix (xấp xỉ)
cov_matrix = (X_centered.T @ X_centered) / (len(X_centered) - 1)
print(cov_matrix.shape) # (5, 5)
# Mỗi phần tử (i, j) = covariance giữa feature i và feature j
# Đường chéo = variance của mỗi feature💡 Ứng dụng
X.T @ X xuất hiện khắp nơi: PCA, linear regression (normal equation), regularization. Nhớ pattern này — bạn sẽ gặp nó liên tục.
5. Inverse — Khi nào và khi nào KHÔNG
5.1 Khái niệm
Ma trận nghịch đảo A⁻¹ thỏa mãn:
A⁻¹ @ A = A @ A⁻¹ = I (ma trận đơn vị — identity matrix)Giống như 5 × (1/5) = 1 cho số thực, nhưng cho ma trận.
5.2 Trong thực tế: ĐỪNG BAO GIỜ tính inverse trực tiếp
🚫 Anti-pattern
python
# ❌ ĐỪNG LÀM THẾ NÀY
x = np.linalg.inv(A) @ b
# ✅ LÀM THẾ NÀY
x = np.linalg.solve(A, b)Tại sao?
np.linalg.inv(A) @ b | np.linalg.solve(A, b) | |
|---|---|---|
| Tốc độ | Chậm hơn ~2-3x | Nhanh hơn |
| Bộ nhớ | Tạo ma trận inverse đầy đủ | Không cần |
| Độ chính xác | Sai số tích lũy (numerical instability) | Ổn định hơn nhiều |
| Khi A gần singular | Kết quả rác (garbage) | Cảnh báo hoặc lỗi rõ ràng |
📝 Khi nào cần biết về Inverse?
- Lý thuyết: Normal equation trong linear regression:
w = (X^T X)^{-1} X^T y - Thực tế: Dùng
np.linalg.lstsq(X, y)hoặcsklearn.linear_model.LinearRegression - Kết luận: Hiểu khái niệm, nhưng không tự tính
6. 🔥 GPU và Matrix Multiplication
🔥 Vì sao GPU nhanh hơn CPU cho Deep Learning?
Matrix multiplication là THE operation mà GPU được tối ưu hóa cho. NVIDIA's Tensor Cores có thể thực hiện phép nhân ma trận 4×4 trong một chu kỳ xung nhịp (single clock cycle).
Toàn bộ training loop của neural network cơ bản là chuỗi phép nhân ma trận:
- Forward pass:
y = X @ W + b(matrix multiply) - Loss computation: dot products và reductions
- Backward pass: gradient = matrix multiply với transpose
- Weight update: element-wise operations
Đây là lý do GPU training nhanh hơn CPU 10-100x:
| Thiết bị | Matrix Multiply (4096×4096) | Lý do |
|---|---|---|
| CPU (i7) | ~2 giây | Sequential cores |
| GPU (RTX 3090) | ~0.02 giây | 10,496 CUDA cores song song |
| GPU (A100) | ~0.005 giây | Tensor Cores chuyên dụng |
Khi bạn viết model.to('cuda') trong PyTorch, bạn đang chuyển tất cả matrix multiply sang GPU.
7. 🧠 Common Beginner Misconception
⚠️ "Tôi cần hiểu eigenvalues, eigenvectors, SVD mới làm ML được"
Sai. Đây là lầm tưởng phổ biến khiến nhiều người bỏ cuộc trước khi bắt đầu.
Cho 90% công việc ML engineering, bạn chỉ cần:
| Cần biết (bắt buộc) | Biết thì tốt (sau này) | Hiếm khi cần |
|---|---|---|
| Dot product | Eigenvalues/Eigenvectors | Jordan normal form |
| Matrix multiply | SVD (Singular Value Decomposition) | Tensor decomposition |
| Transpose | Determinant | Abstract algebra |
Shape rule (m,n)@(n,p) | Rank | Spectral graph theory |
| Broadcasting | Trace |
Spectral decomposition (eigenvalues, SVD) quan trọng cho:
- PCA (dimensionality reduction)
- Recommender systems (matrix factorization)
- NLP cổ điển (LSA/LSI)
Nhưng chúng không phải entry ticket để bắt đầu. Bạn có thể dùng PCA qua sklearn mà không cần tự implement SVD. Hãy học khi cần, không phải trước khi bắt đầu.
8. ⚡ Fast Exercise
⚡ Bài tập nhanh — Forward Pass thủ công
Đề bài: Cho feature matrix X shape (100, 5) và weight matrix W shape (5, 3), bias b shape (3,):
- Tính output
y = X @ W + b - Verify shape của output
- Giải thích ý nghĩa mỗi dimension
python
import numpy as np
np.random.seed(42)
# 100 samples, mỗi sample 5 features
X = np.random.randn(100, 5)
# Weight matrix: 5 features → 3 neurons
W = np.random.randn(5, 3) * 0.1
# Bias: 1 per neuron
b = np.array([0.1, -0.2, 0.05])
# Forward pass
y = X @ W + b
# Verify
print(f"X shape: {X.shape}") # (100, 5)
print(f"W shape: {W.shape}") # (5, 3)
print(f"b shape: {b.shape}") # (3,)
print(f"y shape: {y.shape}") # (100, 3)
# Giải thích:
# - 100 = số samples (giữ nguyên)
# - 5 features → 3 neurons (biến đổi qua W)
# - Mỗi hàng y[i] = dot product X[i] với từng cột W + biasTự kiểm tra:
y.shapecó phải(100, 3)không? ✅- Thử thay
Wthành shape(4, 3)— có lỗi gì? →ValueErrorvì inner dimensions không match (5 ≠ 4)
9. 🪤 Gotcha: np.dot() vs @
⚠️ Hành vi khác nhau tùy dimension!
python
import numpy as np
# === 1D arrays: cả hai đều = inner product (dot product) ===
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(np.dot(a, b)) # 32 (scalar)
print(a @ b) # 32 (scalar)
# === 2D arrays: cả hai đều = matrix multiply ===
A = np.array([[1, 2], [3, 4]])
B = np.array([[5, 6], [7, 8]])
print(np.dot(A, B)) # matrix multiply
print(A @ B) # matrix multiply — kết quả GIỐNG NHAU
# === NHƯNG: np.dot() với higher-dimensional arrays ===
# np.dot() trên 3D+ arrays có hành vi phức tạp khó đoán
# @ luôn là matrix multiply trên 2 dimensions cuốiQuy tắc thực hành:
| Trường hợp | Dùng gì | Lý do |
|---|---|---|
| Dot product 2 vector (1D) | a @ b hoặc np.dot(a, b) | Cả hai OK |
| Matrix multiply (2D) | A @ B | Rõ ràng, dễ đọc |
| Batch matrix multiply (3D+) | A @ B | np.dot có hành vi khác |
| Khi cần explicit | np.matmul(A, B) | Giống @, nhưng explicit |
10. 📊 Performance Note
📊 Hiệu suất Matrix Multiply
ĐỪNG BAO GIỜ implement matrix multiply bằng nested loops trong Python:
python
# ❌ O(n³) Python loops — CHẬM KHỦNG KHIẾP
def matmul_slow(A, B):
m, n = A.shape
n2, p = B.shape
C = np.zeros((m, p))
for i in range(m):
for j in range(p):
for k in range(n):
C[i, j] += A[i, k] * B[k, j]
return C
# ✅ NumPy/BLAS — tối ưu C/Fortran bên dưới
def matmul_fast(A, B):
return A @ BBenchmark (1000×1000 matrices):
| Method | Thời gian | Tỷ lệ |
|---|---|---|
| Python loops | ~120 giây | 1x |
np.dot / @ | ~0.02 giây | 6000x nhanh hơn |
| GPU (CuPy) | ~0.001 giây | 120,000x nhanh hơn |
NumPy gọi thư viện BLAS (Basic Linear Algebra Subprograms) bên dưới — cùng thư viện mà các supercomputer dùng. Đừng cố "tự viết" matrix multiply.
11. 🚫 Production Anti-pattern
🚫 Anti-pattern: Cosine Similarity trong Python Loop
python
# ❌ CHẬM — O(n²) Python loops cho n documents
def pairwise_cosine_slow(vectors):
n = len(vectors)
sim_matrix = np.zeros((n, n))
for i in range(n):
for j in range(n):
dot = np.dot(vectors[i], vectors[j])
norm_i = np.linalg.norm(vectors[i])
norm_j = np.linalg.norm(vectors[j])
sim_matrix[i, j] = dot / (norm_i * norm_j)
return sim_matrix
# ✅ NHANH — sử dụng matrix operations bên trong
from sklearn.metrics.pairwise import cosine_similarity
vectors = np.random.randn(10000, 300)
# sklearn vectorize toàn bộ phép tính:
# 1. Normalize tất cả vectors cùng lúc
# 2. Matrix multiply normalized vectors
sim_matrix = cosine_similarity(vectors) # (10000, 10000)Chênh lệch thực tế:
- 10,000 documents × 300 dimensions
- Python loop: ~45 phút
- sklearn: ~3 giây
Nguyên tắc: nếu có thể diễn đạt bằng matrix operation, hãy dùng matrix operation.
12. 🐛 Spot the Bug
🐛 Tìm lỗi trong đoạn code sau
python
import numpy as np
# Forward pass cho một layer
X = np.random.randn(64, 100) # 64 samples, 100 features
W = np.random.randn(50, 100) # muốn 50 neurons
b = np.zeros(50)
output = X @ W + b # 🐛 Lỗi ở đây!Lỗi gì?
Shape mismatch! X shape (64, 100) và W shape (50, 100).
Inner dimensions: 100 và 50 → không match!
(64, 100) @ (50, 100) → ValueError!Cách sửa — hai cách:
python
# Cách 1: Sửa shape W cho đúng convention (features × neurons)
W = np.random.randn(100, 50) # ✅ (100, 50)
output = X @ W + b # (64, 100) @ (100, 50) → (64, 50) ✅
# Cách 2: Transpose W nếu không muốn đổi cách khởi tạo
W = np.random.randn(50, 100) # giữ nguyên
output = X @ W.T + b # (64, 100) @ (100, 50) → (64, 50) ✅Bài học: Luôn kiểm tra shape TRƯỚC khi chạy matrix multiply. Viết comment shape bên cạnh mỗi phép tính.
13. 🏢 Scenario: Recommendation System
🏢 Scenario — Xây dựng User-Item Similarity
Bối cảnh: Bạn đang xây dựng recommendation system cho một e-commerce platform.
Dữ liệu:
- 5000 users, mỗi user có vector embedding 64 chiều
- 10000 products, mỗi product có vector embedding 64 chiều
- Mục tiêu: tìm top-10 products phù hợp nhất cho mỗi user
Giải pháp bằng matrix operations:
python
import numpy as np
# User embeddings và product embeddings (từ trained model)
users = np.random.randn(5000, 64) # (5000, 64)
products = np.random.randn(10000, 64) # (10000, 64)
# Normalize để cosine similarity = dot product
users_norm = users / np.linalg.norm(users, axis=1, keepdims=True)
products_norm = products / np.linalg.norm(products, axis=1, keepdims=True)
# Tính ALL similarity scores trong MỘT matrix multiply
# (5000, 64) @ (64, 10000) → (5000, 10000)
scores = users_norm @ products_norm.T
# Top-10 products cho mỗi user
top10_indices = np.argsort(scores, axis=1)[:, -10:][:, ::-1]
print(f"Score matrix: {scores.shape}") # (5000, 10000)
print(f"Top-10 per user: {top10_indices.shape}") # (5000, 10)Phân tích:
- Không loop nào cả — pure matrix operations
- 5000 × 10000 = 50 triệu dot products trong ~0.5 giây
- Nếu dùng Python loop: ~8 giờ cho cùng bài toán
Bài học: Khi nào thấy "tính similarity cho tất cả cặp", nghĩ ngay matrix multiply.
14. 🎮 Playground — Code hoàn chỉnh
🎮 Playground — Dot Product, MatMul, Forward Pass
Chạy code này từ đầu đến cuối để thực hành tất cả concept trong bài:
python
import numpy as np
np.random.seed(42)
print("=" * 60)
print("PHẦN 1: DOT PRODUCT — ĐO SIMILARITY")
print("=" * 60)
# Tạo user preference vectors
user_a = np.array([5.0, 0, 3, 0, 2]) # Action fan
user_b = np.array([4.0, 0, 4, 0, 1]) # Cũng Action fan
user_c = np.array([0.0, 5, 0, 4, 0]) # Romance fan
# Dot products
print(f"\nUser A · User B = {user_a @ user_b:.1f}") # Cao → giống
print(f"User A · User C = {user_a @ user_c:.1f}") # Thấp → khác
# Cosine similarity
def cosine_sim(a, b):
return (a @ b) / (np.linalg.norm(a) * np.linalg.norm(b))
print(f"\nCosine(A, B) = {cosine_sim(user_a, user_b):.4f}") # ~0.98
print(f"Cosine(A, C) = {cosine_sim(user_a, user_c):.4f}") # 0.00
print("\n" + "=" * 60)
print("PHẦN 2: MATRIX MULTIPLY — BATCH TRANSFORMATION")
print("=" * 60)
# 5 samples, 3 features → 5 samples, 2 output features
X = np.array([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[2, 3, 4],
[5, 6, 7]
], dtype=float)
W = np.array([
[0.1, 0.4],
[0.2, 0.5],
[0.3, 0.6]
])
result = X @ W
print(f"\nX shape: {X.shape}") # (5, 3)
print(f"W shape: {W.shape}") # (3, 2)
print(f"Result shape: {result.shape}") # (5, 2)
print(f"\nResult:\n{result}")
# Verify: hàng đầu tiên = dot product row 0 of X với từng cột W
row0_col0 = X[0] @ W[:, 0]
row0_col1 = X[0] @ W[:, 1]
print(f"\nVerify result[0, 0] = {row0_col0:.1f} (expected {result[0, 0]:.1f})")
print(f"Verify result[0, 1] = {row0_col1:.1f} (expected {result[0, 1]:.1f})")
print("\n" + "=" * 60)
print("PHẦN 3: NEURAL NETWORK FORWARD PASS")
print("=" * 60)
def relu(x):
return np.maximum(0, x)
# Mini neural network: 3 → 4 → 2
batch_size = 5
n_input = 3
n_hidden = 4
n_output = 2
# Khởi tạo weights (Xavier initialization đơn giản)
W1 = np.random.randn(n_input, n_hidden) * np.sqrt(2.0 / n_input)
b1 = np.zeros(n_hidden)
W2 = np.random.randn(n_hidden, n_output) * np.sqrt(2.0 / n_hidden)
b2 = np.zeros(n_output)
# Forward pass
print(f"\nInput X: {X.shape}")
z1 = X @ W1 + b1 # Linear layer 1
print(f"z1 = X @ W1 + b1: {z1.shape}")
a1 = relu(z1) # Activation
print(f"a1 = relu(z1): {a1.shape}")
z2 = a1 @ W2 + b2 # Linear layer 2
print(f"z2 = a1 @ W2 + b2: {z2.shape}")
print(f"\nFinal output (logits):\n{z2}")
print(f"\nShape journey: {X.shape} → {a1.shape} → {z2.shape}")
print("\n" + "=" * 60)
print("PHẦN 4: TRANSPOSE VÀ COVARIANCE")
print("=" * 60)
# X.T @ X = Gram matrix
X_centered = X - X.mean(axis=0)
cov = (X_centered.T @ X_centered) / (len(X) - 1)
print(f"\nCovariance matrix shape: {cov.shape}") # (3, 3)
print(f"Covariance matrix:\n{cov}")
print("\n" + "=" * 60)
print("PHẦN 5: SHAPE DEBUGGING")
print("=" * 60)
# Thử các shape combinations
shapes_to_test = [
((3, 4), (4, 2)), # ✅ Valid
((3, 4), (4, 5)), # ✅ Valid
((3, 4), (3, 2)), # ❌ Invalid
]
for shape_a, shape_b in shapes_to_test:
try:
A = np.random.randn(*shape_a)
B = np.random.randn(*shape_b)
C = A @ B
print(f" {shape_a} @ {shape_b} → {C.shape} ✅")
except ValueError as e:
print(f" {shape_a} @ {shape_b} → ❌ {e}")
print("\n✅ Playground hoàn thành!")15. Quiz nhanh
📝 Quiz — Tự kiểm tra
Q1: (64, 128) @ (128, 10) cho shape output gì?
Đáp án
(64, 10) — inner dimensions (128, 128) match, output = outer dimensions.
Q2: Dot product giữa [1, 0, 0] và [0, 1, 0] bằng bao nhiêu? Ý nghĩa?
Đáp án
0 — hai vector vuông góc (orthogonal), hoàn toàn không liên quan.
Q3: Trong y = X @ W + b, nếu X shape (32, 784) và ta muốn output 10 neurons, W và b phải có shape gì?
Đáp án
W:(784, 10)— features × neuronsb:(10,)— one bias per neurony:(32, 10)— batch_size × neurons
Q4: Tại sao dùng np.linalg.solve(A, b) thay vì np.linalg.inv(A) @ b?
Đáp án
solve nhanh hơn (2-3x), ít tốn bộ nhớ, và ổn định hơn về số học (numerical stability). inv có thể cho kết quả sai khi ma trận gần singular.
Q5: Một junior developer viết loop tính cosine similarity cho 10,000 documents. Bạn review code — suggest gì?
Đáp án
Dùng sklearn.metrics.pairwise.cosine_similarity() — nó vectorize toàn bộ thành matrix operations, nhanh hơn hàng nghìn lần. Normalize vectors rồi matrix multiply = cosine similarity matrix.
Tổng kết
| Concept | Trực giác | Code |
|---|---|---|
| Dot product | Đo similarity giữa 2 vector | a @ b |
| Matrix multiply | Batch transformation | X @ W |
| Forward pass | Neural network core | y = X @ W + b |
| Transpose | Đảo hàng ↔ cột | A.T |
| Inverse | Khái niệm, đừng tự tính | np.linalg.solve() |
🎯 Key Takeaway
Nếu bạn hiểu y = X @ W + b — bạn hiểu core của neural network. Mọi thứ khác (activation functions, backpropagation, optimizers) đều xây trên nền tảng này. Matrix multiply không chỉ là phép toán — nó là ngôn ngữ mà neural network "nói".
Tiếp theo: Bài 04 — EDA: Engineering Work, Không Phải Art — Áp dụng NumPy để phân tích dữ liệu thực tế.