numpy memory animation 03

NumPy MSE 程式在記憶體裡怎麼跑

這支動畫把 MSE 範例拆成「變數紙條、函式物件、list 物件、ndarray 物件、暫存運算結果」來看。重點不是只算出 0.375,而是看懂 Python 每一行讓哪些物件被建立、哪些變數改指向新物件。
MSE 範例:y_true / y_pred → residual → squared error → sum / len → return
Mean Squared Error
MSE = sum((y_true - y_pred)^2) / n
114 年第二次 AI 應用規劃師-中級-第三科:機器學習技術與應用
第38題,由於題目只提供片段程式,這邊模擬還原完整程式分析
程式碼
mse_example.py
1import numpy as np
2 
3def mse(y_true, y_pred):
4    y_true = np.array(y_true)
5    y_pred = np.array(y_pred)
6 
7    return np.sum((y_true - y_pred) ** 2) / len(y_true)
8 
9 
10y_true = [3, -0.5, 2, 7]
11y_pred = [2.5, 0.0, 2, 8]
12 
13print("MSE =", mse(y_true, y_pred))
STEP 00

準備開始

點「下一步」後,右邊會逐步畫出記憶體裡的變化。

BUILT-IN FUNCTION — print():內建函式物件;這裡只把結果輸出到終端機
terminal output
MSE = 0.375
電腦記憶體(一座大倉庫)
variables / heap / call frame
VARIABLES / CALL FRAME — 變數紙條 HEAP — Python 物件主要存放區(物件倉庫) class(設計圖) numpy module np → 0xA1·0000 MODULE — <module 'numpy'> @ 0xA1·0000 .array(...) .sum(...) .mean(...) ... 這支程式會用到 array 與 sum def statement 編譯 code object 建立 function object mse → 0xF0·1000 FUNCTION — mse(y_true, y_pred) @ 0xF0·1000 function object:可呼叫物件,包住 code object 與全域環境 __code__ → 編譯後的函式內容;__globals__ → 所在 module 的全域命名空間 現在還沒有執行函式內容;等到 mse(...) 被呼叫才建立 call frame。 class(設計圖) list: [3, -0.5, 2, 7] y_true → 0xL1·3000 LIST 物件 — y_true @ 0xL1·3000 3 -0.5 2 7 Python list,稍後會被轉成 ndarray class(設計圖) list: [2.5, 0, 2, 8] y_pred → 0xL2·4000 LIST 物件 — y_pred @ 0xL2·4000 2.5 0.0 2 8 另一個 Python list class(設計圖) call frame 區域 CALL FRAME — mse() local y_true → 0xL1·3000 local y_pred → 0xL2·4000 y_true → 0xL1·3000 y_pred → 0xL2·4000 傳入參數位址 0xL1·3000 / 0xL2·4000 np.array(y_true) 讀 0xL1·3000 list 的元素 ndarray class(設計圖) 建新物件 3.0 -0.5 2.0 7.0 NDARRAY 實例 — new object @ 0xN1·5000 3.0 -0.5 2.0 7.0 shape: (4,) dtype: float64 由 np.array(0xL1 list) 複製資料後建立 → 0xN1·5000 np.array(y_pred) 讀 0xL2·4000 list 的元素 ndarray class(設計圖) 建新物件 2.5 0.0 2.0 8.0 NDARRAY 實例 — new object @ 0xN2·6000 2.5 0.0 2.0 8.0 shape: (4,) dtype: float64 由 np.array(0xL2 list) 複製資料後建立 → 0xN2·6000 向量化相減:同一欄 y_true - y_pred 逐元素運算後組成 residual 3.0 -0.5 2.0 7.0 2.5 0.0 2.0 8.0 3.0 - 2.5 = 0.5 -0.5 - 0 = -0.5 2.0 - 2.0 = 0.0 7.0 - 8.0 = -1.0 TEMP NDARRAY — residual = y_true - y_pred 0.5 -0.5 0.0 -1.0 實際 - 預測 = 模型錯多少 讀 residual 逐格平方 0.5 -0.5 0.0 -1.0 0.5² = 0.25 (-0.5)² = 0.25 0.0² = 0.0 (-1.0)² = 1.0 TEMP NDARRAY — squared error = residual ** 2 0.25 0.25 0.0 1.0 平方後不分正負,而且大錯會被放大 NumPy module 函式:np.sum(squared_error) 讀 squared error ndarray 的四個值,回傳 sum = 1.5 Python built-in:len(local y_true) 此時 local y_true 是 ndarray,長度 = 4 0.25 0.25 0.0 1.0 len(local y_true) shape (4,) → 4 1.5 / 4 = 0.375 SCALAR — return value (NumPy scalar) np.sum(squared_error) = 1.5;len(local y_true ndarray) = 4 MSE = 1.5 / 4 = np.float64(0.375) return value 0.375 交給左側 print() 終端機輸出 此值沒有變數接住,印完可回收 CLEANUP — mse() 結束後可回收 已離開函式:CALL FRAME — mse() 消失 local y_true / local y_pred 這兩張函式內紙條不再存在 沒有其他參考時,以下物件可被回收 NDARRAY — local y_true、NDARRAY — local y_pred TEMP NDARRAY — residual、TEMP NDARRAY — squared error SCALAR — np.float64(0.375)(print 用完後也沒被變數接住) 仍保留:np、mse、外面的 y_true list、y_pred list。

📝 iPAS 考點提醒

這頁還原 114 年第二次中級科目三第 38 題的 MSE 程式:def 定義函式、y_true - y_pred 逐元素相減、平方、np.sum 加總、除以 n。中級科目三程式題常給這種「指標的 NumPy 實作」片段,問它算的是哪個指標、輸出多少、或哪一行寫錯。常見考法:MSE = Σ(y−ŷ)²/n,對大誤差特別敏感;RMSE 是 MSE 開根號、單位與原值相同;MAE 取絕對值、對離群值穩健。易混點:**2 是逐元素平方;函式要 return 才有值。情境題長相:「y_true=[1,2,3]、y_pred=[1,2,5],MSE 為?」答 (0+0+4)/3≈1.33。

想練情境題與詳解 → AI 學習與考證地圖

❓ 常見問題

這段程式每一行在做什麼?

y_true - y_pred 算每筆殘差(逐元素),**2 逐元素平方,np.sum 全部相加,再除以筆數 n 取平均,就是均方誤差 MSE。

MSE、RMSE、MAE 怎麼分?

MSE 平方後平均,重罰大誤差;RMSE 是 MSE 開根號,單位回到原值好解讀;MAE 取絕對值平均,對離群值較不敏感。考題常問「哪個對離群值最敏感」:MSE/RMSE。

為什麼損失函數要平方而不直接相減?

直接相減正負會互相抵消;平方讓誤差都是正值、可微分、又放大大誤差,方便用梯度下降最佳化。

函式沒有 return 會怎樣?

Python 函式預設回傳 None;若只在函式裡 print 而沒 return,m = mse(...) 拿到的是 None,後續運算會出錯。這是程式題常見陷阱。

np.sum(...)/len(...) 和 np.mean 一樣嗎?

對一維陣列結果相同;np.mean 直接算平均,更簡潔。多維陣列時 len 只取第一維長度,要用 .size 或 np.mean。

呼叫函式時記憶體發生什麼?

def 先建立函式物件;呼叫時建立新的區域命名空間,參數紙條指向傳入的陣列,計算完回傳結果,區域變數隨即釋放。

🧭 相關主題

← 返回 AI 學習與考證地圖