Bỏ qua

Lý Thuyết Thông Tin (Information Theory)

Lý thuyết thông tin định lượng thông tin, sự bất ngờ (surprise), và sự khác biệt giữa các phân bố xác suất. File này bao gồm entropy, entropy chéo (cross-entropy), phân kỳ KL, thông tin tương hỗ (mutual information), và surprisal — những khái niệm đằng sau mọi hàm mất mát phân lớp, mục tiêu VAE, và sơ đồ nén dữ liệu trong ML.

  • Lý thuyết thông tin, do Claude Shannon sáng lập năm 1948, cung cấp một khuôn khổ toán học để định lượng thông tin. Nó trả lời các câu hỏi như: bạn nên bất ngờ bao nhiêu về một biến cố? Một thông điệp mang bao nhiêu thông tin? Hai phân bố xác suất khác nhau thế nào?

  • Những câu hỏi này nghe có vẻ trừu tượng, nhưng chúng là nền tảng của các hàm mất mát ML, nén dữ liệu, và hệ thống truyền thông. Cross-entropy loss, hàm mất mát phổ biến nhất trong phân lớp, xuất phát trực tiếp từ lý thuyết thông tin.

  • Bắt đầu với câu hỏi đơn giản nhất: một biến cố đơn lẻ mang bao nhiêu thông tin?

  • Surprisal (còn gọi là tự thông tin, self-information) đo lường mức độ bất ngờ của một biến cố. Nếu một điều gì đó rất có thể xảy ra, bạn hầu như không học được gì. Nếu điều gì đó hiếm xảy ra, bạn học được rất nhiều.

  • Nếu bạn sống ở sa mạc và ai đó bảo bạn trời nắng, điều đó không nhiều thông tin. Nếu họ bảo bạn trời đang tuyết rơi, điều đó cực kỳ nhiều thông tin. Surprisal hình thức hóa trực giác này:

\[I(x) = \log_2 \frac{1}{p(x)} = -\log_2 p(x)\]
  • Đơn vị là bit khi ta dùng \(\log_2\). Một lần tung đồng xu công bằng có surprisal \(-\log_2(0.5) = 1\) bit. Một biến cố có xác suất \(1/8\) có surprisal \(\log_2(8) = 3\) bit.

  • Tại sao dùng logarithm thay vì chỉ \(1/p\)? Ba lý do:

    • Một biến cố chắc chắn (\(p = 1\)) nên cho thông tin bằng không: \(\log(1) = 0\) nhưng \(1/1 = 1\).
    • Các biến cố độc lập nên có thông tin cộng được: \(\log(1/p_1 p_2) = \log(1/p_1) + \log(1/p_2)\).
    • Chúng ta muốn một hàm mượt mà, có hành vi tốt. \(1/p\) bùng nổ; \(\log(1/p)\) tăng nhẹ nhàng.
  • Entropy là surprisal kỳ vọng, lượng thông tin trung bình bạn nhận được mỗi lần lấy mẫu từ một phân bố. Nó đo lường độ không chắc chắn hoặc "khả năng dự đoán được" của phân bố:

\[H(X) = E[I(X)] = -\sum_{x} p(x) \log_2 p(x)\]

Biểu đồ cột minh hoạ các biến cố xác suất cao có surprisal thấp và ngược lại; entropy là trung bình có trọng số

  • Một đồng xu công bằng có entropy \(H = -0.5\log_2(0.5) - 0.5\log_2(0.5) = 1\) bit. Độ không chắc chắn cực đại.

  • Một đồng xu thiên lệch với \(p = 0.9\) có entropy \(H = -0.9\log_2(0.9) - 0.1\log_2(0.1) \approx 0.469\) bit. Ít không chắc chắn hơn, nên ít entropy hơn.

  • Một biến cố xác định (\(p = 1\)) có entropy \(H = 0\). Không có chút không chắc chắn nào.

  • Entropy đạt cực đại khi tất cả các kết quả có khả năng như nhau. Với \(n\) kết quả có khả năng như nhau, \(H = \log_2 n\). Một xúc xắc công bằng có entropy \(\log_2 6 \approx 2.585\) bit.

  • Ý nghĩa thực tế của entropy là nén (compression). Định lý mã hóa nguồn của Shannon nói bạn không thể nén dữ liệu dưới mức tốc độ entropy của nó mà không mất thông tin. Một hình ảnh mà mọi pixel đều có khả năng như nhau (entropy cực đại) không thể nén. Một hình ảnh phần lớn là trắng (entropy thấp) nén tốt.

  • Để có cảm giác về quy mô: một pixel mức xám (256 giá trị) có entropy cực đại là 8 bit. Một hình ảnh mức xám 1080p có nhiều nhất \(1920 \times 1080 \times 8 \approx 16,6\) triệu bit. Các hình ảnh thực có entropy thấp hơn nhiều vì các pixel kề nhau có tương quan, đó là lý do nén JPEG hoạt động.

  • Với các biến ngẫu nhiên liên tục, tổng rời rạc trở thành tích phân. Entropy vi phân (differential entropy) là:

\[h(X) = -\int_{-\infty}^{\infty} f(x) \log f(x)\, dx\]
  • Một phân bố Gauss với phương sai \(\sigma^2\) có entropy vi phân \(h = \frac{1}{2}\log_2(2\pi e \sigma^2)\). Trong số tất cả các phân bố có cùng phương sai, phân bố Gauss có entropy cực đại. Đây là một lý do phân bố Gauss phổ biến trong mô hình hóa: nó giả định ít nhất ngoài kỳ vọng và phương sai đã chỉ định.

  • Thông tin tương hỗ (mutual information) đo lường việc biết một biến cho bạn biết bao nhiêu về biến kia. Nó là sự giảm độ không chắc chắn về \(X\) khi bạn quan sát \(Y\):

\[I(X; Y) = H(X) - H(X|Y) = H(Y) - H(Y|X)\]
  • Tương đương:
\[I(X; Y) = \sum_{x,y} p(x,y) \log_2 \frac{p(x,y)}{p(x) p(y)}\]
  • Nếu \(X\)\(Y\) độc lập, \(p(x,y) = p(x)p(y)\) và thông tin tương hỗ bằng không. Chúng càng phụ thuộc, thông tin tương hỗ càng cao.

  • Trong ML, thông tin tương hỗ được dùng trong chọn đặc trưng (chọn các đặc trưng có MI cao với mục tiêu), trong các phương pháp nút thắt thông tin (information bottleneck), và trong đánh giá chất lượng phân cụm.

  • Entropy chéo (cross-entropy) đo lường số bit trung bình cần thiết để mã hóa các biến cố từ phân bố \(p\) bằng một mã được tối ưu hóa cho phân bố \(q\):

\[H(p, q) = -\sum_{x} p(x) \log_2 q(x)\]
  • Nếu \(q\) khớp hoàn hảo với \(p\), entropy chéo bằng entropy: \(H(p, p) = H(p)\). Nếu \(q\) là một xấp xỉ tồi, entropy chéo cao hơn. Các "bit thừa" đến từ sự không khớp.

  • Đây chính là lý do entropy chéo là hàm mất mát chuẩn cho phân lớp trong ML. Các nhãn thực tế xác định \(p\) (một phân bố one-hot), và xác suất dự đoán của mô hình xác định \(q\). Cực tiểu hóa entropy chéo đẩy \(q\) về phía \(p\):

\[\mathcal{L} = -\sum_{c} y_c \log \hat{y}_c\]
  • Với một mẫu đơn với lớp thực \(c\), điều này đơn giản thành \(\mathcal{L} = -\log \hat{y}_c\). Mất mát là surprisal của lớp thực dưới các dự đoán của mô hình. Nếu mô hình gán xác suất cao cho lớp đúng, mất mát thấp.

  • Phân kỳ KL (KL divergence) (phân kỳ Kullback-Leibler, còn gọi là entropy tương đối) đo lường một phân bố khác biệt với phân bố kia bao nhiêu:

\[D_{\text{KL}}(p \| q) = \sum_{x} p(x) \log \frac{p(x)}{q(x)} = H(p, q) - H(p)\]
  • Phân kỳ KL là "chi phí thừa" của việc dùng phân bố \(q\) thay vì phân bố thực \(p\). Nó luôn không âm (\(D_{\text{KL}} \ge 0\)) và chỉ bằng không khi \(p = q\).

Hai phân bố p và q với khoảng cách giữa chúng đại diện cho phân kỳ KL

  • Phân kỳ KL không đối xứng: \(D_{\text{KL}}(p \| q) \ne D_{\text{KL}}(q \| p)\). Sự không đối xứng này quan trọng. \(D_{\text{KL}}(p \| q)\) phạt \(q\) vì đặt xác suất thấp ở nơi \(p\) có xác suất cao (vì \(\log(p/q)\) bùng nổ). \(D_{\text{KL}}(q \| p)\) phạt chiều ngược lại.

  • Sự không đối xứng này dẫn đến hai phong cách xấp xỉ:

    • Cực tiểu hóa \(D_{\text{KL}}(p \| q)\) tạo ra hành vi khớp moment (moment-matching): \(q\) bao phủ tất cả các mode của \(p\) nhưng có thể quá trải rộng.
    • Cực tiểu hóa \(D_{\text{KL}}(q \| p)\) tạo ra hành vi tìm mode (mode-seeking): \(q\) tập trung vào một mode của \(p\) nhưng có thể bỏ sót các mode khác. Đây là những gì suy luận biến phân dùng.
  • \(H(p)\) không đổi đối với mô hình, cực tiểu hóa entropy chéo \(H(p, q)\) tương đương với cực tiểu hóa \(D_{\text{KL}}(p \| q)\). Đây là lý do ta có thể dùng cross-entropy loss và biết rằng ta cũng đang cực tiểu hóa phân kỳ KL giữa phân bố thực và phân bố dự đoán.

  • Phân kỳ KL đóng vai trò trung tâm trong cập nhật Bayes. Hậu nghiệm \(P(\theta | D)\) là phân bố gần nhất với tiên nghiệm \(P(\theta)\) (theo nghĩa phân kỳ KL) mà vẫn nhất quán với dữ liệu quan sát. Mỗi quan sát mới cập nhật hậu nghiệm, giảm độ không chắc chắn về \(\theta\).

  • Trong bộ mã hóa tự động biến phân (VAE), hàm mất mát có hai số hạng: một mất mát tái thiết (cross-entropy) và một số hạng phân kỳ KL chính quy hóa không gian tiềm ẩn (latent space) để giữ gần với phân bố chuẩn tắc.

  • Để kết nối mọi thứ: entropy cho bạn biết độ không chắc chắn nội tại trong một phân bố, entropy chéo cho bạn biết mô hình xấp xỉ hiện thực tốt đến đâu, và phân kỳ KL cho bạn biết khoảng cách giữa hai đại lượng đó. Ba đại lượng này tạo thành xương sống của tối ưu hóa ML hiện đại.

Bài tập lập trình (dùng CoLab hoặc notebook)

  1. Tính entropy của các phân bố khác nhau và kiểm tra rằng phân bố đều có entropy cực đại cho một số lượng kết quả cho trước.

    import jax.numpy as jnp
    
    def entropy(p):
        """Compute entropy in bits. Filter out zero-probability events."""
        p = p[p > 0]
        return -jnp.sum(p * jnp.log2(p))
    
    # Fair die
    fair = jnp.ones(6) / 6
    print(f"Fair die entropy:   {entropy(fair):.4f} bits (max = log2(6) = {jnp.log2(6.):.4f})")
    
    # Loaded die
    loaded = jnp.array([0.1, 0.1, 0.1, 0.1, 0.1, 0.5])
    print(f"Loaded die entropy: {entropy(loaded):.4f} bits")
    
    # Deterministic
    det = jnp.array([0.0, 0.0, 0.0, 0.0, 0.0, 1.0])
    print(f"Deterministic:      {entropy(det):.4f} bits")
    
    # Fair coin
    coin = jnp.array([0.5, 0.5])
    print(f"Fair coin entropy:  {entropy(coin):.4f} bits")
    

  2. Tính entropy chéo và phân kỳ KL giữa một phân bố thực và một số xấp xỉ. Kiểm tra \(D_{\text{KL}}(p \| q) = H(p, q) - H(p)\).

    import jax.numpy as jnp
    
    def cross_entropy(p, q):
        return -jnp.sum(p * jnp.log2(jnp.clip(q, 1e-10, 1.0)))
    
    def kl_divergence(p, q):
        mask = p > 0
        return jnp.sum(jnp.where(mask, p * jnp.log2(p / jnp.clip(q, 1e-10, 1.0)), 0.0))
    
    def entropy(p):
        p = p[p > 0]
        return -jnp.sum(p * jnp.log2(p))
    
    p = jnp.array([0.4, 0.3, 0.2, 0.1])  # true distribution
    
    for name, q in [("perfect match", p),
                    ("slight mismatch", jnp.array([0.35, 0.30, 0.25, 0.10])),
                    ("big mismatch", jnp.array([0.1, 0.1, 0.1, 0.7]))]:
        h_p = entropy(p)
        h_pq = cross_entropy(p, q)
        kl = kl_divergence(p, q)
        print(f"{name:20s}: H(p)={h_p:.4f}, H(p,q)={h_pq:.4f}, "
              f"KL={kl:.4f}, H(p,q)-H(p)={h_pq-h_p:.4f}")
    

  3. Chỉ ra phân kỳ KL không đối xứng bằng cách tính \(D_{\text{KL}}(p \| q)\)\(D_{\text{KL}}(q \| p)\) cho hai phân bố khác nhau.

    import jax.numpy as jnp
    
    def kl_div(p, q):
        mask = p > 0
        return float(jnp.sum(jnp.where(mask, p * jnp.log2(p / jnp.clip(q, 1e-10, 1.0)), 0.0)))
    
    p = jnp.array([0.9, 0.1])
    q = jnp.array([0.5, 0.5])
    
    print(f"D_KL(p || q) = {kl_div(p, q):.4f}")
    print(f"D_KL(q || p) = {kl_div(q, p):.4f}")
    print(f"Not the same! KL divergence is asymmetric.")
    

  4. Mô phỏng cross-entropy loss trong quá trình huấn luyện. Tạo một nhãn one-hot "thực" và cho thấy mất mát giảm thế nào khi xác suất dự đoán của mô hình cải thiện.

    import jax.numpy as jnp
    import matplotlib.pyplot as plt
    
    # True label: class 2 out of 4
    true_label = jnp.array([0, 0, 1, 0])
    
    # Simulate improving predictions
    steps = []
    losses = []
    for confidence in jnp.linspace(0.25, 0.99, 50):
        # Model becomes more confident in class 2
        remaining = (1 - confidence) / 3
        pred = jnp.array([remaining, remaining, confidence, remaining])
        loss = -jnp.sum(true_label * jnp.log(jnp.clip(pred, 1e-10, 1.0)))
        steps.append(float(confidence))
        losses.append(float(loss))
    
    plt.figure(figsize=(8, 4))
    plt.plot(steps, losses, color="#e74c3c", linewidth=2)
    plt.xlabel("Model confidence in true class")
    plt.ylabel("Cross-entropy loss")
    plt.title("Cross-entropy loss decreases as predictions improve")
    plt.grid(alpha=0.3)
    plt.show()