Xác suất (Probability).
Updated Mar 12, 2026

Tags: machine learning data science math

Bài 3: Xác suất (Probability) — AI đoán mò với độ chắc chắn cao

Series: Toán học trong AI/ML & Deep Learning

Topics: probability bayes distribution MLE VAE diffusion


🎯 Tại sao Xác suất quan trọng?

Trong giới thực không chắc chắn. Cùng một bức ảnh chó, con người cũng có thể nhầm sang cáo. Cùng một email, người này thấy spam, người khác không.

Thay vì trả lời “đây là chó”, một AI tốt phải nói: “87% là chó, 10% là cáo, 3% là thứ khác” — và đó là ngôn ngữ của xác suất.

Model quyết định cứng:        Model xác suất:
                              
Input → [Black Box] → "Chó"   Input → [Model] → P(chó)  = 0.87
                                               → P(cáo)  = 0.10
                                               → P(mèo)  = 0.03
                                               Σ = 1.00 ✓

the only one certainty is that nothing is certain - Pliny the Elder


1. Xác suất Cơ bản

\[P(A \cup B) = P(A) + P(B) - P(A \cap B) \quad \text{(Quy tắc cộng)}\] \[P(A \cap B) = P(A) \cdot P(B|A) \quad \text{(Quy tắc nhân)}\] \[P(A|B) = \frac{P(A \cap B)}{P(B)} \quad \text{(Xác suất có điều kiện)}\]
import numpy as np
from scipy import stats

# Ví dụ: Email spam detection
# P(spam) = 0.3, P(có từ "free" | spam) = 0.8, P(có từ "free" | ham) = 0.1

p_spam = 0.3
p_free_given_spam = 0.8
p_free_given_ham  = 0.1
p_ham = 1 - p_spam

# P(có từ "free")
p_free = p_free_given_spam * p_spam + p_free_given_ham * p_ham
print(f"P(có 'free') = {p_free:.2f}")   # 0.31

# P(spam | có "free") — dùng Bayes (xem phần 2)
p_spam_given_free = p_free_given_spam * p_spam / p_free
print(f"P(spam | 'free') = {p_spam_given_free:.2f}")  # 0.77

2. Định lý Bayes — Cập nhật niềm tin

confidence level

bayes theory

\[P(\theta | \text{data}) = \frac{P(\text{data} | \theta) \cdot P(\theta)}{P(\text{data})}\]
Thành phần Ý nghĩa Trong ML
$P(\theta)$ Prior — niềm tin ban đầu Trước khi thấy dữ liệu
$P(\text{data}|\theta)$ Likelihood — bằng chứng Model fit dữ liệu tốt đến đâu
$P(\theta|\text{data})$ Posterior — niềm tin cập nhật Sau khi thấy dữ liệu
# Xét nghiệm COVID:
# - Độ nhạy (sensitivity): P(test+| có COVID) = 0.95
# - Độ đặc hiệu: P(test-| không COVID) = 0.99 → P(test+|không) = 0.01
# - Tỉ lệ mắc trong dân (prevalence): P(COVID) = 0.001 (0.1%)

p_covid    = 0.001
p_pos_covid    = 0.95   # sensitivity
p_pos_no_covid = 0.01   # false positive rate

p_no_covid = 1 - p_covid
p_pos = p_pos_covid * p_covid + p_pos_no_covid * p_no_covid

p_covid_given_pos = (p_pos_covid * p_covid) / p_pos
print(f"P(có COVID | test dương) = {p_covid_given_pos:.1%}")
# → 8.7%  ← chỉ 8.7%! dù test sensitivity 95%
# Vì prevalence rất thấp → hầu hết "dương tính" là false positive

# Kết luận: P(A|B) ≠ P(B|A) ← lỗi phổ biến nhất!

3. Biến Ngẫu nhiên & Phân phối

PDF, CDF, PMF

random variable

Khái niệm Dùng khi Công thức
PMF Biến rời rạc $P(X = x)$
PDF Biến liên tục $f(x)$, diện tích = xác suất
CDF Cả hai $F(x) = P(X \leq x)$
import numpy as np
from scipy import stats
import matplotlib

# === Phân phối Normal (Gaussian) ===
mu, sigma = 0, 1
dist = stats.norm(mu, sigma)

x = np.linspace(-4, 4, 100)
pdf = dist.pdf(x)      # PDF: f(x) = (1/σ√2π) exp(-(x-μ)²/2σ²)
cdf = dist.cdf(x)      # CDF: P(X ≤ x)

# Tính xác suất trong khoảng [-1, 1]
p = dist.cdf(1) - dist.cdf(-1)
print(f"P(-1 ≤ X ≤ 1) = {p:.1%}")   # 68.3% — quy tắc 68-95-99.7

# === Phân phối Bernoulli (flip coin) ===
p_head = 0.6
ber = stats.bernoulli(p_head)
print(f"P(head=1) = {ber.pmf(1):.1f}")  # 0.6
print(f"P(tail=0) = {ber.pmf(0):.1f}")  # 0.4

# === Phân phối Binomial (k lần head trong n lần) ===
n, p = 10, 0.6
binom = stats.binom(n, p)
print(f"P(X=6 heads in 10 flips) = {binom.pmf(6):.3f}")  # 0.251

Các phân phối quan trọng trong AI

Normal:    Model weights, noise trong diffusion
           μ=0, σ=1 → "standard normal"
           
Bernoulli: Output của binary classifier (0/1)
           p = sigmoid(logit)
           
Categorical: Output của multi-class classifier
             p = softmax(logits), Σpᵢ = 1

Beta:      Prior cho xác suất (ε [0,1])
           Dùng trong Bayesian A/B testing

Dirichlet: Prior cho phân phối categorical
           Topic modeling (LDA), Bayesian NLP

4. MLE — Maximum Likelihood Estimation

MLE trả lời: “Với dữ liệu đã quan sát, tham số nào làm cho dữ liệu đó có khả năng xảy ra cao nhất?”

\[\hat{\theta}_{MLE} = \arg\max_\theta P(\text{data} | \theta) = \arg\max_\theta \prod_i P(x_i | \theta)\]

Trong thực tế, ta dùng log-likelihood (tránh underflow):

\[\hat{\theta}_{MLE} = \arg\max_\theta \sum_i \log P(x_i | \theta)\]

Cross-entropy loss = Negative Log-Likelihood

import torch
import torch.nn.functional as F

# Classification với 3 classes
logits = torch.tensor([[2.0, 1.0, 0.1]])   # raw output của model
label  = torch.tensor([0])                  # ground truth: class 0

# Cross-entropy loss = -log P(correct class)
loss = F.cross_entropy(logits, label)
print(f"Cross-entropy loss: {loss.item():.4f}")

# Tương đương với:
probs = F.softmax(logits, dim=-1)
print(f"P(class 0) = {probs[0, 0]:.4f}")   # ~0.6590
manual_loss = -torch.log(probs[0, label])
print(f"Manual NLL : {manual_loss.item():.4f}")  # = cross_entropy ✓

# Kết luận:
# Train model = minimize cross-entropy = maximize log-likelihood
# → MLE và deep learning classification là MỘT!

5. MAP — Khi có Prior

MAP (Maximum A Posteriori) = MLE có thêm prior:

\[\hat{\theta}_{MAP} = \arg\max_\theta \underbrace{P(\text{data}|\theta)}_{\text{likelihood}} \cdot \underbrace{P(\theta)}_{\text{prior}}\]

Prior Gaussian trên weights → L2 regularization
Prior Laplace trên weights → L1 regularization

# MAP = MLE + regularization
# Loss = -log P(data|W) - log P(W)
#      = cross_entropy + λ·‖W‖²   ← L2 reg (Gaussian prior)
#      = cross_entropy + λ·‖W‖₁   ← L1 reg (Laplace prior)

# PyTorch: L2 regularization qua weight_decay
optimizer = torch.optim.AdamW(
    model.parameters(),
    lr=1e-3,
    weight_decay=1e-2   # ← đây chính là λ trong MAP
)

6. Ứng dụng Deep Learning

6.1 VAE — Variational Autoencoder

VAE học phân phối của dữ liệu, không chỉ encode/decode:

\[\mathcal{L}_{VAE} = \underbrace{\mathbb{E}[\log P(x|z)]}_{\text{Reconstruction}} - \underbrace{D_{KL}(q(z|x) \| p(z))}_{\text{Regularization}}\]
import torch
import torch.nn as nn

class VAEEncoder(nn.Module):
    def __init__(self, d_input, d_latent):
        super().__init__()
        self.fc = nn.Linear(d_input, 256)
        self.mu_head    = nn.Linear(256, d_latent)
        self.logvar_head = nn.Linear(256, d_latent)
    
    def forward(self, x):
        h = torch.relu(self.fc(x))
        mu     = self.mu_head(h)
        logvar = self.logvar_head(h)
        return mu, logvar

def reparameterize(mu, logvar):
    """
    Reparameterization trick: z = μ + σ·ε,  ε ~ N(0,1)
    Cho phép gradient đi qua phép sampling!
    """
    std = torch.exp(0.5 * logvar)
    eps = torch.randn_like(std)        # sample from N(0,1)
    return mu + std * eps              # z ~ N(μ, σ²)

def kl_divergence(mu, logvar):
    """KL(N(μ,σ²) || N(0,1)) — có công thức analytic!"""
    return -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())

# VAE training loop skeleton
encoder = VAEEncoder(d_input=784, d_latent=16)
x = torch.randn(32, 784)

mu, logvar = encoder(x)
z = reparameterize(mu, logvar)
kl_loss = kl_divergence(mu, logvar)

print(f"z shape  : {z.shape}")       # (32, 16)
print(f"KL loss  : {kl_loss:.2f}")

6.2 Diffusion Model — Markov Chain

Diffusion model thêm noise dần dần (forward) rồi học denoising (reverse):

Forward:  x₀ → x₁ → x₂ → ... → xₜ    (thêm Gaussian noise)
Reverse:  xₜ → ... → x₂ → x₁ → x₀    (model học khử noise)

q(xₜ | xₜ₋₁) = N(√(1-βₜ)·xₜ₋₁, βₜI)   ← forward transition
p_θ(xₜ₋₁ | xₜ)                           ← model predict reverse
import torch

# Forward diffusion — thêm noise theo schedule
def forward_diffuse(x0, t, alphas_cumprod):
    """
    x0: ảnh gốc (batch, C, H, W)
    t: timestep (batch,)
    """
    alpha_t = alphas_cumprod[t].view(-1, 1, 1, 1)
    noise   = torch.randn_like(x0)
    
    # xt = √ᾱₜ·x₀ + √(1-ᾱₜ)·ε
    xt = torch.sqrt(alpha_t) * x0 + torch.sqrt(1 - alpha_t) * noise
    return xt, noise   # trả về cả noise để train

# Training: model đoán noise đã thêm vào
# Loss = ‖ε - ε_θ(xₜ, t)‖²    (MSE giữa real noise và predicted noise)

6.3 RLHF — Xác suất hành động trong RL

# Policy gradient: cải thiện hành động có reward cao
# L_RL = -E[log π_θ(a|s) · R]

# Trong LLM (PPO):
# - a = token được chọn
# - π_θ(a|s) = softmax(logits)[a]  ← xác suất chọn token a
# - R = reward từ reward model

log_prob = torch.log(policy_probs[chosen_token])
loss = -(log_prob * reward)   # maximize expected reward

7. Lỗi Phổ biến & Cách Phòng tránh

import torch
import numpy as np

# ❌ Lỗi 1: Nhầm P(A|B) với P(B|A)
# P(spam | "free") ≠ P("free" | spam) — như ví dụ COVID test ở trên

# ❌ Lỗi 2: Log của số âm hoặc 0
probs = torch.tensor([0.0, 0.5, 0.5])   # có 0 → log(0) = -inf!
log_probs = torch.log(probs)
print(log_probs)  # tensor([-inf, -0.6931, -0.6931])

# ✅ Luôn clamp probability trước log
eps = 1e-8
safe_log_probs = torch.log(probs.clamp(min=eps))

# ❌ Lỗi 3: Quên log khi tính MLE → underflow
# N=1000 data points, P(xᵢ|θ) = 0.5 mỗi điểm
# Product: 0.5^1000 = 9.33e-302 → underflow!
p = 0.5
n = 1000
product = p ** n   # → underflow
log_sum = n * np.log(p)   # → -693.1 ✓ (dùng log-likelihood)

# ❌ Lỗi 4: Dùng Normal distribution cho heavy-tail data
# Dùng Normal khi data có outliers lớn → fit kém
# → Thử Student-t distribution thay thế

8. Checklist “Đã hiểu Xác suất cho AI/ML chưa?”


📚 Tài nguyên

Tài nguyên Loại Ghi chú
Probability Theory: Logic of Science — Jaynes Sách Bayesian perspective sâu sắc
Seeing Theory Visual Probability tương tác, đẹp
Bishop — PRML Ch.1–2 Sách Nền tảng ML từ góc nhìn xác suất
Lil’Log — VAE Blog Giải thích VAE rõ nhất

🔗 Series

← Bài 2: Đại số Tuyến tính — Ngôn ngữ của Dữ liệu
→ Bài 4: Thống kê — Phân biệt Tín hiệu và Nhiễu