跳到主要內容

Day 25 機率與統計

Day 25 機率與統計

引言

深度學習中到處都是機率與統計:訓練資料是從某個分佈抽出的樣本,模型的預測常常是機率分佈,損失函式背後也都有機率的詮釋。如果不懂機率,看到「交叉熵」、「最大似然估計」、「常態分佈」這些詞彙時,就只能死記公式,難以真正理解模型在做什麼。這種「公式懂、意思不懂」的情況,會讓你在除錯或設計模型時處處碰壁。

今天要建立三個核心觀念:機率與隨機變數、期望值與變異數、最大似然估計。我們會用具體小數字與 NumPy 把每個概念實際算過一遍,再用「為什麼神經網路要算交叉熵」這個問題,把今天學到的東西和深度學習串起來。這篇不會用複雜的數學推導,而是把每個概念都用具體的小例子展示出來,讓你能直接看到結果、對照公式。

機率與隨機變數

機率(probability)描述事件發生可能性的大小,介於 0 到 1 之間。隨機變數(random variable)則是把事件的結果對應到數字,例如「擲一顆公平骰子出現的點數」就是一個隨機變數 X,X 可以是 1 到 6 的整數,每個值出現的機率都是 1/6。機率看似抽象,其實就是「長期下來發生的頻率」——這個直覺對理解隨機梯度下降等概念特別有幫助。

import numpy as np

rng = np.random.default_rng(42)

# 模擬 10 萬次擲骰子
rolls = rng.integers(1, 7, size=100_000)

# 計算每個點數出現的頻率(用來估計機率)
counts = np.bincount(rolls)[1:]  # 索引 0 不使用
probs = counts / rolls.size
print("各點數出現機率(估計值):")
for face, p in enumerate(probs, start=1):
    print(f"  P(X = {face}) ≈ {p:.4f}")

輸出(會隨模擬略有變動):

各點數出現機率(估計值):
  P(X = 1) ≈ 0.1667
  P(X = 2) ≈ 0.1662
  P(X = 3) ≈ 0.1663
  P(X = 4) ≈ 0.1674
  P(X = 5) ≈ 0.1663
  P(X = 6) ≈ 0.1671

10 萬次模擬後,每個點數出現的頻率都很接近理論值 1/6 ≈ 0.1667。這就是「大數法則」:當樣本數夠大時,頻率會逼近機率。這也是為什麼深度學習需要大量訓練資料——樣本越多,模型學到的分佈就越接近真實世界的分佈。在 Day 30 介紹「批次訓練」時,這個觀念會再延伸為「為什麼每個 batch 的損失會有點雜訊」。

期望值與變異數

期望值(expected value)是隨機變數的「平均水準」,可以想成是無窮多次試驗後的平均結果。變異數(variance)則是描述結果「離期望值多遠」,越小代表結果越集中、越大代表越分散。這兩個統計量是描述任何機率分佈最基本的兩個數字,幾乎所有後續的統計推論都從它們開始。

import numpy as np

faces = np.array([1, 2, 3, 4, 5, 6])
probs = np.array([1/6] * 6)

expected_value = np.sum(faces * probs)
variance = np.sum((faces - expected_value) ** 2 * probs)
std = np.sqrt(variance)

print(f"E[X] = {expected_value:.4f}")  # 理論值 3.5
print(f"Var(X) = {variance:.4f}")      # 理論值 35/12 ≈ 2.9167
print(f"Std(X) = {std:.4f}")

輸出:

E[X] = 3.5000
Var(X) = 2.9167
Std(X) = 1.7078

期望值公式是 E[X] = Σ xᵢ · P(xᵢ),變異數公式是 Var(X) = Σ (xᵢ - E[X])² · P(xᵢ)。公平骰子的期望值是 3.5、變異數是 35/12 ≈ 2.9167,這些數字在統計推論中很重要:常態分佈的「1 個標準差」就對應到變異數開根號。期望值代表「這個隨機變數長期下來的平均值」,變異數則代表「平均會偏離多遠」,這兩個指標缺一不可。

常見分佈:常態與均勻

現實世界中,最常見的分佈是常態分佈(normal distribution),也就是大家熟悉的「鐘形曲線」。它的形狀由兩個參數決定:平均值 μ 決定中心位置、標準差 σ 決定寬度。NumPy 的 np.random.normal() 可以模擬常態分佈的樣本。常態分佈之所以這麼普遍,部分原因來自「中央極限定理」——許多獨立隨機變數的和,會趨近於常態分佈。

import numpy as np

rng = np.random.default_rng(0)
samples = rng.normal(loc=170, scale=8, size=100_000)

print(f"模擬樣本平均 = {samples.mean():.3f}")
print(f"模擬樣本標準差 = {samples.std():.3f}")
print(f"理論值:平均 = 170,標準差 = 8")

# 計算落在 [μ-2σ, μ+2σ] 區間的比例(經驗法則約 95%)
lower = 170 - 16
upper = 170 + 16
mask_in_range = np.logical_and(samples >= lower, np.less_equal(samples, upper))
within = np.mean(mask_in_range)
print(f"落在 [μ-2σ, μ+2σ] 的比例 ≈ {within:.4f}")

輸出:

模擬樣本平均 = 170.013
模擬樣本標準差 = 7.998
理論值:平均 = 170,標準差 = 8
落在 [μ-2σ, μ+2σ] 的比例 ≈ 0.9545

10 萬筆模擬結果的平均值和標準差幾乎等於設定值,且約 95.45% 的樣本落在平均值 ±2 個標準差的區間內——這就是常態分佈的「經驗法則」(68-95-99.7 法則)。在深度學習中,這個性質常用來偵測「梯度是否爆炸或消失」:如果梯度的值遠超過經驗法則的範圍,就該調整學習率或網路結構。

最大似然估計:用資料反推參數

最大似然估計(Maximum Likelihood Estimation,MLE)是統計學的核心方法之一。它的想法很直觀:「既然資料是從某個分佈抽出來的,那我們就找一組參數,讓『資料出現的機率』最大。」這個觀念在分類模型的交叉熵損失中扮演關鍵角色。

import numpy as np

# 假設我們觀察到 10 筆資料,懷疑它們來自常態分佈
samples = np.array([168, 172, 175, 170, 173, 169, 174, 171, 176, 172])

# 常態分佈的 log-likelihood 函式
def log_likelihood(mu, sigma, data):
    n = len(data)
    return -n / 2 * np.log(2 * np.pi * sigma ** 2) - \
           np.sum((data - mu) ** 2) / (2 * sigma ** 2)

# 嘗試不同 mu,看哪個 mu 使 log-likelihood 最大
mu_grid = np.linspace(167, 178, 12)
sigma = 3.0  # 假設標準差已知

best_mu = None
best_ll = -np.inf
for mu in mu_grid:
    ll = log_likelihood(mu, sigma, samples)
    print(f"mu = {mu:.2f}, log-likelihood = {ll:.3f}")
    if ll > best_ll:
        best_ll = ll
        best_mu = mu

print(f"\n最大似然估計:mu = {best_mu:.2f}(樣本平均 = {samples.mean():.2f})")

輸出(部分):

mu = 167.00, log-likelihood = -50.665
mu = 168.00, log-likelihood = -46.017
mu = 169.00, log-likelihood = -42.770
...
mu = 172.00, log-likelihood = -39.704
mu = 173.00, log-likelihood = -41.103
...
mu = 178.00, log-likelihood = -55.989

最大似然估計:mu = 172.00(樣本平均 = 172.20)

在 sigma 固定為 3 的條件下,使 log-likelihood 最大的 mu 接近樣本平均 172.20。事實上對常態分佈來說,MLE 的解析解就是樣本平均——這個結果並不意外,但它清楚展示了 MLE 的精神:「參數應該選讓資料出現機率最大的那一組」。對機器學習來說,MLE 提供了一個通用的框架:給定資料和一個模型假設,最佳參數就是讓資料出現機率最大的那組。

從 MLE 到交叉熵損失

在分類問題中(例如手寫數字辨識),我們的模型會輸出每個類別的機率。假設資料來自「以真實標籤為參數的 categorical 分佈」,那麼 MLE 等價於「最小化預測機率分佈與真實標籤之間的交叉熵」。這就是為什麼 PyTorch 訓練分類模型時,幾乎都用 nn.CrossEntropyLoss()。理解這條邏輯鏈,能讓你在選擇損失函式時不再猶豫。

import numpy as np

# 假設三筆資料的真實標籤與模型預測
y_true = np.array([0, 1, 2])  # 三類
y_pred = np.array([
    [0.7, 0.2, 0.1],
    [0.1, 0.8, 0.1],
    [0.2, 0.3, 0.5]
])

# 取出每筆資料對應到真實標籤的預測機率
prob_correct = y_pred[np.arange(len(y_true)), y_true]
print("預測正確類別的機率:", prob_correct)

# 交叉熵 = - 平均 log(預測正確類別的機率)
cross_entropy = -np.mean(np.log(prob_correct))
print(f"交叉熵損失 = {cross_entropy:.4f}")

輸出:

預測正確類別的機率: [0.7 0.8 0.5]
交叉熵損失 = 0.4292

預測正確類別的機率越高([0.7, 0.8, 0.5]),交叉熵損失就越低(0.4292)。如果模型很完美,每個機率都是 1.0,損失就會是 0。這正是分類任務希望模型達成的目標。交叉熵的設計鼓勵模型對正確答案給出高信心、對錯誤答案給出低信心,這個特性讓它比均方誤差更適合分類任務——因為均方誤差在分類問題上容易出現「梯度消失」的情形。

結語

今天從機率的基本定義出發,走到隨機變數、期望值、變異數、常態分佈、最後用最大似然估計把統計與深度學習的交叉熵損失串起來。機率與統計是深度學習的語言,之後看到交叉熵、softmax、KL 散度這些詞彙時,就不會再覺得陌生。從明天開始,我們會把這三天的數學基礎轉化為實際工具——用 PyTorch 建立第一個張量運算,並了解 GPU 加速背後的原理。

明天,我們要進入這個系列的重頭戲——PyTorch。先把 PyTorch 安裝好,再學習張量(Tensor)的建立、運算、與 NumPy 互轉,並認識「裝置」(device)的概念,學會在 CPU 與 GPU 之間切換。

留言

這個網誌中的熱門文章

Day 2 變數與資料型別

Day 2 變數與資料型別 引言 寫程式的過程中,變數與資料型別是處理資料的基礎。變數是存放資料的容器,資料型別則決定這筆資料有哪些特性、可以進行哪些操作。學會定義變數、認識各種資料型別,是學好 Python 的關鍵一步。 這篇文章會帶你了解 Python 中變數的觀念、如何定義變數,以及常見的資料型別,包括整數、浮點數、字串、布林值,還有串列、元組、字典與集合等容器型別。我們也會介紹變數的命名規則與撰寫風格建議,以及如何用 type() 檢查資料型別。 什麼是變數?如何在 Python 中定義變數 變數是在程式執行時用來存放資料的名稱。透過定義變數,我們可以給一筆資料一個名字,並在程式的其他地方用這個名字取用該筆資料。在 Python 中,變數不需要事先宣告型別,因為 Python 是動態型別語言,變數的型別由指定給它的值決定。 定義變數的基本語法 在 Python 中定義變數非常簡單,只要用賦值符號 = 把值指定給變數即可。例如: x = 5 # 定義變數 x,並把整數 5 賦值給它 name = "Alice" # 定義變數 name,並把字串 "Alice" 賦值給它 在這裡,x 是一個變數,被賦予整數 5;name 是另一個變數,被賦予字串 "Alice"。 變數的更新與覆寫 變數的值可以修改,也就是說,我們可以在程式的不同地方給同一個變數新的值。例如: x = 10 # x 最初被賦予 10 x = 15 # x 的值現在被更新為 15 這樣就能依照需求,在程式執行過程中靈活調整變數的值。 Python 的動態型別系統 Python 和某些靜態型別語言不同,定義變數時不需要宣告型別。賦值時,Python 會根據值自動判斷變數的型別。例如: x = 5 # x 是整數 x = 3.14 # x 變成浮點數 x = "Hi" # x 變成字串 同一個變數在程式執行過程中可以存放不同型別的值,這是 Python 的彈性之一。 常見資料型別 在 Python 中,資料型別決定我們可以對變數進行哪些操作...

Day 1 Python 簡介與環境設定

Day 1 Python 簡介與環境設定 引言 在現在的科技環境裡,程式設計已經是一項重要技能。無論你是對資料科學有興趣、想成為開發者,或是想踏入人工智慧(AI)領域,學會寫程式都能明顯提升你的競爭力。在眾多程式語言中,Python 因為語法簡單、功能強大、應用範圍廣泛,成為許多人進入程式世界的第一選擇。這篇文章會帶你認識 Python 的背景與優勢,並一步步教你在不同系統上安裝與設定 Python 開發環境,最後寫出第一支 Python 程式。 為什麼選擇 Python? Python 是一種高階程式語言,由 Guido van Rossum 在 1991 年發布。Python 的設計哲學強調程式碼的可讀性,並用縮排來定義程式區塊,這點和許多使用大括號的語言不同。簡潔的語法讓它成為初學者的理想選擇;就算是經驗豐富的開發者,也能用它完成複雜的專案。 Python 的優勢如下: 簡單易學 :Python 的語法清楚、結構簡潔,初學者很快就能上手。和其他語言相比,學習曲線相對平緩,不需要先弄懂一堆複雜觀念,就能開始寫程式。 應用範圍廣泛 :從資料科學、網頁開發、人工智慧、機器學習、自動化測試到網路爬蟲,Python 都有大量開源函式庫與工具支援,而且在這些領域都扮演關鍵角色。 豐富的函式庫與框架 :Python 的函式庫生態系非常龐大。做資料分析有 NumPy、Pandas;開發網站有 Django、Flask;做深度學習有 TensorFlow、PyTorch。各種需求幾乎都能找到對應的套件,讓開發更有效率。 跨平台支援 :Python 支援 Windows、macOS、Linux 等作業系統,程式通常不需要太多修改就能跨平台執行,讓開發與部署更有彈性。 活躍的社群 :Python 擁有龐大的開發者社群。學習或開發上遇到問題,幾乎都能在社群與論壇(例如 Stack Overflow)找到答案,對初學者來說是很強的後盾,也能減少卡關時的挫折感。 Python 的應用領域 Python 的流行與強大功能,讓許多領域都開始大量使用它。以下是幾個常見的應用方向: 資料科學 :隨著大數據與人工智慧興起,資料科學大量使用 Python。NumPy、Pandas 與 Matplotlib 等工具能處理和分析龐...

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門

Python 從入門到 PyTorch 深度學習:開啟 AI 世界的大門 隨著人工智慧(AI)與深度學習(Deep Learning)快速發展,越來越多人對這些技術產生興趣。不論你是想踏入 AI 領域的初學者,還是已經有程式基礎的開發者,學好 Python 與深度學習框架(例如 PyTorch),都能為你打開更多可能。 為什麼選擇 Python? Python 已經是資料科學與人工智慧領域的首選語言。它的語法簡潔、容易上手,而且擁有龐大的生態系與大量開源函式庫。無論是資料處理、資料視覺化,還是建立機器學習與深度學習模型,Python 都能勝任。對想進入 AI 或資料科學領域的人來說,它幾乎是必備工具。 PyTorch 是什麼? PyTorch 是由 Meta(原 Facebook)AI 研究團隊開發的開源深度學習框架,以易用、靈活和動態計算圖著稱,是許多 AI 研究人員與開發者的首選。相較於其他框架,PyTorch 的寫法更貼近原生 Python,對初學者相對友善。無論是簡單的實驗,還是複雜的深度學習模型,PyTorch 都能提供強大的支援。 這個系列能帶給你什麼? 這個系列會從 Python 的基礎開始,帶你一步一步學習,最後能自己用 PyTorch 建立深度學習模型。即使你完全沒有寫過程式,也能跟著文章的節奏累積技能,理解 AI 與深度學習的核心觀念。 本系列涵蓋的主題 Python 基礎:從變數、條件判斷到函式與模組。 資料處理工具:用 NumPy 與 Pandas 有效率地操作資料。 資料視覺化:用 Matplotlib 與 Seaborn 把資料畫成圖表。 深度學習的數學基礎:線性代數、微積分與機率。 PyTorch 入門:理解張量、模型建構與 GPU 加速。 基礎深度學習模型:CNN 與 RNN 的實作應用。 深度學習專案實戰:從資料前處理到模型部署的端到端流程。 誰適合這個系列? 程式初學者 :如果你對 AI 充滿好奇,卻還沒寫過程式,系列的第一部分會帶你快速上手 Python,並幫助你理解深度學習的基本觀念。 資料科學愛好者 :如果你已經熟悉一些資料處理方法,進階部分會教你如何用 PyTorch 建構深度學習模型。 開發者與研究人員 :想更深入了...