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。