💻 Mochi 程式白話講堂
中級科目二・科目三的 26 題程式題,每一行都講——非本科系也看得懂。
🐼 pandas
🔬 sklearn
🔢 NumPy
🧠 Keras/PyTorch
🧰 方法速查卡
程式題占中級科目二、科目三各 25%(13 題=26 分),而且全是固定套路——不用會寫程式,只要看懂在做什麼就能拿分!我把衝刺站裡每一題程式題逐行拆開講,看不懂的行就慢慢讀;最下面還有套件方法速查卡,衝刺計畫裡的「💻 逐行白話詳解」連結都會跳到這裡 🐾
🐍 Python 讀題基本功3 分鐘
考試不會叫你寫程式,只會叫你「看懂」。掌握這五件事,後面 26 題都讀得動。
import numpy as npimport=把工具箱搬進來。「as np」是幫它取小名,之後寫 np.xxx 就是在用這個工具箱裡的工具。pandas 慣用 pd、seaborn 慣用 sns。
x = np.array([1, 2, 3])「=」是把右邊的東西貼上名牌,不是數學的相等。這行做出一個陣列,貼上名牌 x。
df['Revenue']df 是一張表格(DataFrame),方括號+欄名=抽出那一整欄,像 Excel 點選整欄。
model.fit(X, y)「物件.方法(參數)」=叫這個東西做事。fit=「學」:讓 model 從特徵 X 與答案 y 學出規則。點(.)唸成「的」:model 的 fit。
for xb, yb in loader:
pred = model(xb)for=逐批重複做;冒號之後縮排的行都屬於這個迴圈。Python 靠縮排分層,不用大括號。
🎯 口訣:import 搬工具、= 貼名牌、[] 抽欄位、.method() 叫它做事、縮排=誰屬於誰。
📗 科目二程式題逐行解
大數據處理分析與應用:pandas 資料操作 → 統計計算 → sklearn 建模評估,13 題全拆。
① list 參照綁定:b = a 不是複製
情境:工程師想「備份」特徵清單再加新特徵,結果原本的清單也被改了。
base_features = ['age', 'income']做一個清單(list),裡面兩個字串,貼上名牌 base_features。
new_features = base_features關鍵行:這不是影印一份!只是把同一個清單再貼一張新名牌。現在兩個名字指向同一個東西——像同一間房子有兩支鑰匙。
new_features.append('region')append=在清單尾端加一項。因為兩個名字是同一間房子,用哪支鑰匙進去擺東西,另一支鑰匙開門看到的都一樣。
print(base_features)印出來:['age', 'income', 'region']——「沒動過」的 base_features 也多了 region。
🎯 這題在考:Python 的「=」是綁定參照。要真正複製一份,寫 base_features.copy() 或 list(base_features)。
⚠️ 陷阱選項:「append 只作用在 new_features」——聽起來合理,但兩個名字根本是同一個物件。
② 虛擬程式碼認演算法:聚合式階層分群
情境:給一段 pseudocode(假程式),問這是哪個分群演算法。每屆固定 1~2 題。
1: 將每一個資料點各自初始化為一個叢集第一個指紋:每個點自己一群(100 個點=100 群)。這是「由下而上(Bottom-up)」的起手式。
2: 計算所有叢集兩兩之間的距離算出每一對群之間的距離,準備找「誰跟誰最近」。
3: while 叢集數量 > k:while=「只要條件成立就一直重複」。群數還太多,就繼續合併。
4: 找出距離最近的兩個叢集 Ci, Cj
5: 將 Ci 與 Cj 合併為一個新叢集第二個指紋:反覆「合併最近的兩群」——群數每輪少 1,一路併到剩 k 群。
6: 依連結法(linkage)更新距離兩群併成一群後,它跟其他群的距離要重算;怎麼算由 linkage 決定(single=取最近點、complete=取最遠點、average、Ward)。
7: 回傳叢集結果結束。整個過程可以畫成樹狀圖(Dendrogram),從哪裡「剪一刀」就得到幾群。
🎯 三大分群的指紋:「各自一群→合併最近」=階層分群;「指定 k→指派點→搬群心」=K-means;「密度夠就擴張、不夠標雜訊」=DBSCAN。
③ scipy 二項分佈:pmf 恰好、cdf 累積
情境:點擊率 0.2、投放 10 人,算「幾人點擊」的機率。X 服從二項分佈 B(10, 0.2)。
from scipy.stats import binom從 scipy 的統計模組裡,只搬出「二項分佈」這一個工具。
p1 = binom.pmf(2, 10, 0.2)pmf=「恰好」:恰好 2 人點擊的機率 P(X=2)。參數順序:(幾次成功, 總次數, 單次成功率)。
p2 = binom.cdf(2, 10, 0.2)cdf=「累積到」:0 人+1 人+2 人的機率總和 P(X≤2)。c 想成 cumulative(累積)。
p3 = 1 - binom.cdf(2, 10, 0.2)全部(1)扣掉「≤2」剩下的就是「≥3」:P(X≥3)。不是「至少 2」——至少 2 要寫 1−cdf(1)。
🎯 口訣:pmf 恰好、cdf 累積、1−cdf(k)=「至少 k+1」。「至少」題先想清楚要扣到哪。
⚠️ 差一陷阱:選項最愛把 1−cdf(2) 說成「至少 2 人」——那是 ≥3!
④ 向量化運算:一行贏過一萬圈迴圈
情境:500 萬筆訂單要算「數量 × 單價」,比較四種寫法的效率。
df['Revenue'] = df['Quantity'] * df['UnitPrice']正解(向量化):抽出兩整欄直接相乘——pandas 把運算丟給底層 C 語言「整欄一次算完」,再把結果貼成新欄 Revenue。
for i in range(len(df)):
df.loc[i,'Revenue'] = ...逐列迴圈:一次處理一筆、跑 500 萬圈,每圈都有 Python 的「翻譯成本」——慢數百倍。
df.apply(lambda r: ..., axis=1)apply(axis=1)=對每一列呼叫一次小函式,本質還是逐列,只是包裝得漂亮——照樣慢。
for _, row in df.iterrows(): ...iterrows 逐列取出、還要把每列轉成物件,是四種裡最慢的之一。
🎯 效率排序:向量化 ≫ apply ≫ iterrows/逐列 .loc。看到「欄與欄直接運算」就是最佳解。
⑤ pd.to_datetime:錯誤格式不炸掉
情境:日期欄是字串、還混了 '2026/13/45' 這種怪值,要轉日期型態且不中斷。
df['OrderDate'] = pd.to_datetime(
df['OrderDate'], errors='coerce')to_datetime=把字串欄「翻譯」成日期型態。errors='coerce'=翻不動的就填 NaT(日期版的缺值)繼續走,程式不會停。翻好的整欄貼回原欄位。
# errors='raise'(預設)預設遇到怪格式直接拋錯停機。適合「資料應該要乾淨、髒了我要知道」的場景。
# .astype('datetime64')astype 是硬轉型:遇到怪格式一樣報錯,也沒有「錯誤變缺值」的選項。
# .str.replace('/', '-')只是字串取代,換完型態還是字串,日期運算(排序、相減)都做不了。
🎯 同門師兄弟:pd.to_numeric(..., errors='coerce') 把「翻不動的數字」變 NaN——考點一模一樣。
⑥ pivot_table:兩個維度交叉的樞紐表
情境:想要「列=月份、欄=商品分類、格子=營收總和」的交叉分析表。
df.pivot_table(index='Month',pivot_table=Excel 的樞紐分析表。index=哪個欄位當「列」——每個月份一列。
columns='Category',columns=哪個欄位當「欄」——每個商品分類一欄。列×欄就是交叉。
values='Revenue', aggfunc='sum')values=格子裡放什麼、aggfunc=怎麼彙總:把落在「該月×該分類」的所有營收加總(也可用 mean、count)。
# 等價寫法
df.groupby(['Month','Category'])['Revenue']
.sum().unstack()groupby 兩個欄位加總後,用 unstack 把第二個維度「翻」成欄——結果相同,考題兩種都可能出。
🎯 判別:一個維度彙總→groupby;兩個維度交叉→pivot_table(或 groupby+unstack)。merge 是「兩張表合併」、cumsum 是「累加」,都不是交叉表。
⑦ seaborn boxplot:比較分佈用箱型圖
情境:比較四個地區訂單營收的「分佈」——中位數、四分位距、離群值一次看。
sns.boxplot(x='Region', y='Revenue',
data=df)sns 是 seaborn 的小名。x=分組欄位(四個地區各畫一個箱子)、y=要看分佈的數值。一個箱子=五數摘要:箱底 Q1、箱中線=中位數、箱頂 Q3,鬍鬚外的點=離群值。
# lineplot:看「趨勢」(時間變化)折線圖是給時間序列用的;把類別放 x 軸畫折線沒有意義。
# countplot:只「數筆數」countplot 數每區有幾筆訂單,完全沒看金額。
# scatterplot:類別軸上會疊成一直線散佈圖適合兩個連續變數;x 是類別時點全部疊在一起、難以判讀。
🎯 口訣:比分佈→箱型圖(更華麗用 violinplot);看趨勢→折線;數個數→countplot;兩連續變數→散佈圖。
⑧ train_test_split+stratify:分層切資料
情境:流失率只有 8%,切訓練/測試集時要讓兩邊比例一致。
X_train, X_test, y_train, y_test =
train_test_split(X, y,train_test_split=把特徵 X 和答案 y「一刀切成兩份」,一次回傳四包:訓練特徵、測試特徵、訓練答案、測試答案(順序固定,背起來)。
test_size=0.2,測試集佔 20%、訓練集 80%——常見比例。
stratify=y,關鍵參數:依「答案 y 的類別比例」分層抽樣——切完兩邊都維持 8%:92%。不寫的話,8% 的稀有類可能幾乎沒被分到測試集,指標會亂跳。
random_state=42)固定亂數種子:每次執行切出一模一樣的結果,實驗才可重現(42 只是慣例數字)。
🎯 分清楚:stratify 是「保持比例」;把類別「變平衡」是 SMOTE/class_weight 的事——兩者常被出成混淆選項。
⑨ 標準化防洩漏:fit 只在訓練集
情境:數值特徵要標準化再訓練,哪種寫法不會資料洩漏?
scaler = StandardScaler()做一台「標準化機器」:它之後會學「平均值 μ、標準差 σ」,把數值轉成 (x−μ)/σ。
X_train_s = scaler.fit_transform(X_train)fit_transform=學+轉一次做完:只從訓練集學 μ 和 σ,馬上把訓練集轉換。考試重點:「學」這個動作只准發生在訓練集。
X_test_s = scaler.transform(X_test)測試集只 transform:套用剛剛從訓練集學到的 μ、σ。測試集要假裝是「未來才出現的資料」——未來的統計量你不可能先知道。
# 錯誤一:合併全部資料再 fitμ、σ 混到了測試集資訊=偷看考卷,離線分數虛高。
# 錯誤二:對測試集重新 fit_transform兩邊用不同的尺,模型看到的量尺不一致;而且照樣用到測試分佈。
🎯 鐵律:fit(學)=只在訓練集;transform(套用)=兩邊都可以。順序反了直接報 NotFittedError。
⑩ predict_proba:要機率不要標籤
情境:想拿到每位客戶「流失」的機率,之後才能自己調門檻。
model.predict_proba(X_test)回傳一個 (筆數 × 2) 的機率表:每一列是一位客戶,兩欄分別是「不流失的機率、流失的機率」,兩欄相加=1。
[:, 1]切片語法:「所有列(:),第 1 欄」——欄位從 0 開始數,第 1 欄就是「正類=流失」的機率。欄位順序跟著 model.classes_(通常 [0, 1])。
# model.predict(X_test)回傳的是已經用 0.5 門檻切好的 0/1 標籤——機率資訊被丟掉了,想調門檻就來不及了。
# model.score(X_test, y_test)回傳一個整體 Accuracy 分數,不是逐筆機率。
🎯 記法:proba=probability(機率);要「調門檻、算 AUC、排序名單」都必須用它。
⑪ classification_report:不平衡報表怎麼讀
情境:流失類只佔 8%,報表整體 Accuracy 0.94 看起來很棒——到底該看哪裡?
precision recall f1 support
0 0.95 0.99 0.97 1840「0」這一列=不流失類的成績。support 1840=測試集裡實際有 1840 筆不流失(不是被預測的次數)。多數類幾乎怎樣都漂亮。
1 0.72 0.35 0.47 160先看這一列(少數類=流失):recall 0.35=真的會流失的客戶只抓到 35%、65% 漏掉;precision 0.72=被點名流失的人有 72% 真的流失。
accuracy 0.94 2000整體答對率 0.94——但 92% 都是多數類,全猜「不流失」也有 0.92,這個數字被灌水。
macro avg 0.84 0.67 0.72macro=兩類各算一次再平均(一視同仁)——少數類爛就會拉低它,不平衡場景看這個。
weighted avg 0.93 0.94 0.93weighted=依樣本數加權——被 1840 筆多數類主導,看起來永遠很美,別被騙。
🎯 判讀順序:少數類的 recall/f1 → macro avg → 才看整體。support=實際筆數。
⑫ 門檻 0.5→0.3:寧可錯殺的取捨
情境:主管說「寧可誤攔正常客戶,也不能漏掉會流失的」。工程師把門檻降到 0.3。
model.predict_proba(X_test)[:, 1]由內往外讀:先拿到每位客戶的「流失機率」(0~1 的小數)。
>= 0.3跟 0.3 比大小,得到一串 True/False:機率超過 0.3 就當作會流失——比預設 0.5 寬鬆很多,更多人被點名。
.astype(int)把 True/False 轉成 1/0,變成正式的預測標籤。
🎯 效果:門檻降→判正變多→Recall↑(漏掉的變少)、Precision↓(誤報變多)。「零容忍漏抓」=降門檻;「怕誤殺」=升門檻。兩者不可能同時上升。
⑬ StratifiedKFold+F1:不平衡的交叉驗證
情境:流失率 8%,要用 5 折交叉驗證把評估做穩。
cross_val_score(model, X, y,cross_val_score=自動做交叉驗證:把資料切成幾折、輪流當驗證集、回傳每折分數。你不用自己寫迴圈。
cv=StratifiedKFold(n_splits=5,
shuffle=True, random_state=42),cv=怎麼切。StratifiedKFold=每一折都維持 8%:92% 的類別比例;shuffle=True 先洗牌避免資料原本按順序排;random_state 讓結果可重現。普通 KFold 可能有些折幾乎沒有少數類。
scoring='f1')scoring=用什麼指標打分。F1 兼顧少數類的查準與查全;用 accuracy 會被 92% 的多數類灌水。
🎯 不平衡雙保險:切法用 Stratified、指標用 F1/PR-AUC——兩個都對才拿分,考題常只錯其中一半。
📘 科目三程式題逐行解
機器學習技術與應用:NumPy 數學 → sklearn 流程 → Keras/PyTorch 深度學習,13 題全拆。
① NumPy 廣播:(1,3)+(3,1) 變 (3,3)
情境:兩個形狀不同的陣列相加,會發生什麼事?
import numpy as np搬進 NumPy 工具箱,取小名 np。
a = np.array([[1, 2, 3]]) # shape (1, 3)雙層方括號=二維陣列:1 列 3 欄(橫的一條)。shape 就是(列數, 欄數)。
b = np.array([[10], [20], [30]]) # (3, 1)三個小括號各裝一個數=3 列 1 欄(直的一條)。
c = a + b廣播(Broadcasting)上場:維度「相等、或其中一方是 1」就相容。a 沿著列方向複製成 3 列、b 沿著欄方向複製成 3 欄,兩個都被「拉成」(3,3) 再逐格相加。
print(c.shape) # (3, 3)結果 c=[[11,12,13],[21,22,23],[31,32,33]]。驗算 c[0,2](第 0 列第 2 欄)=3+10=13。
🎯 廣播規則:從尾端維度比對,「相等或一方為 1」即可播;(1,3)+(3,1)→(3,3) 是經典題。橫條+直條=攤開成一張表。
② @ 是矩陣乘法、* 是逐元素
情境:同樣兩個 2×2 矩陣,@ 和 * 算出來完全不同。
A = np.array([[1, 2],
[3, 4]])2×2 矩陣 A:第一列 [1,2]、第二列 [3,4]。
B = np.array([[2, 0],
[1, 2]])2×2 矩陣 B。
m1 = A @ B # 矩陣乘法@=線性代數的矩陣乘法:結果第 (i,j) 格=A 的第 i 「列」與 B 的第 j 「欄」做內積。算給你看:左上=1×2+2×1=4、右上=1×0+2×2=4、左下=3×2+4×1=10、右下=3×0+4×2=8 → [[4,4],[10,8]]。
m2 = A * B # 逐元素乘法*=同位置的格子各自相乘:[[1×2, 2×0],[3×1, 4×2]]=[[2,0],[3,8]]。跟 @ 完全是兩回事。
# np.dot(A, B) 等同 A @ B(二維時)np.dot 對二維陣列就是矩陣乘法——「dot=逐元素」是錯誤選項的慣用話術。
🎯 口訣:@=列×欄內積(神經網路的權重運算都是它);*=對位相乘。維度規則:(m,k)@(k,n)→(m,n),內側要相等。
③ 蒙地卡羅估 π:撒一百萬個點
情境:在 1×1 正方形裡隨機撒點,用「落在四分之一圓內的比例」反推 π。
N = 1_000_000要撒一百萬個點(底線只是千分位隔線,幫人眼讀數字,Python 會無視它)。
x = np.random.rand(N)
y = np.random.rand(N)rand(N)=產生 N 個 0~1 之間的均勻亂數。x、y 各一百萬個=一百萬個隨機座標點。
inside = (x**2 + y**2) <= 1**=次方。x²+y²≤1 就是「離原點距離 ≤1」=落在四分之一圓內。結果是一百萬個 True/False 的陣列。
pi_est = 4 * inside.mean()妙招:布林陣列取 .mean()=True 的比例(True 當 1、False 當 0)。比例≈四分之一圓面積÷正方形面積=(π/4)÷1,所以乘 4 還原成 π。
print(pi_est) # 約 3.1416點越多越準——這就是蒙地卡羅:解析解懶得算(或算不出來),用大量隨機模擬逼近答案。
🎯 兩個積木:rand 產生均勻亂數、布林.mean()=比例。inside.mean() 本身≈0.785(π/4),忘記乘 4 是最愛考的空格。
④ 前處理洩漏:先標準化再切分=偷看考卷
情境:KNN 分類流程看起來很正常,但行 1 有方法論大忌。
scaler = StandardScaler()做一台標準化機器(會學 μ、σ)。到這裡沒問題。
X_all = scaler.fit_transform(X) # 行 1問題行:拿「全部資料」去 fit——μ 和 σ 裡混進了之後要當測試集的那 20% 的資訊。測試集應該假裝是未來資料,未來的平均值你不可能先知道。
X_tr, X_te, ... = train_test_split(
X_all, y, test_size=0.2, ...) # 行 2切分本身沒錯——錯在「先處理、後切分」的順序。洩漏已經發生了。
model.fit(X_tr, y_tr) # 行 3訓練:模型在「已被測試集資訊污染過的尺度」上學習。
print(model.score(X_te, y_te)) # 行 4評估分數會系統性偏高——離線漂亮、上線見光死。
🎯 正確順序:先 split → scaler.fit_transform(訓練集) → scaler.transform(測試集)。「任何會『學東西』的前處理」都適用這條鐵律(標準化、編碼、SMOTE、特徵篩選)。
⑤ Pipeline+交叉驗證:官方防洩漏寫法
情境:把前處理和模型「綁成一條生產線」,交叉驗證時就不會洩漏。
pipe = Pipeline([
('scaler', StandardScaler()),Pipeline=生產線:把步驟依序排好。第一站取名 'scaler',負責標準化。
('knn', KNeighborsClassifier(
n_neighbors=5))
])第二站是 KNN 模型(看最近 5 個鄰居投票)。之後對 pipe 喊 fit,它會「第一站學完轉完 → 交給第二站學」。
scores = cross_val_score(pipe, X, y, cv=5)妙處:把整條生產線丟進交叉驗證——每一折都「只用該折的訓練部分」重新 fit 整條線(含 scaler),驗證部分只被 transform 與預測。每折各自防洩漏,全自動。
🎯 兩個考點:①Pipeline+cross_val_score=每折獨立 fit、徹底防洩漏;②KNN 靠距離吃飯、對尺度極敏感,scaler 絕不是多餘的。
⑥ stratify=y 填空:讓 4% 的正例兩邊都有
情境:疾病正例僅 4%,切分後要讓訓練與測試的正例比例都和原始一致。
X_tr, X_te, y_tr, y_te = train_test_split(
X, y,老朋友 train_test_split:X 特徵、y 標籤,回傳四包。
test_size=0.2,
random_state=42,測試集 20%;固定種子保證可重現。
stratify=y
)空格答案:依「標籤 y」的比例分層——兩邊正例都維持約 4%。stratify=X 語意錯誤(要按答案分層,不是按特徵);shuffle=False 只是不洗牌、保證不了比例;class_weight 是「模型」的參數、切分函數根本沒有這個選項。
🎯 記法:「照『答案』的比例分」→ stratify=y。四個選項各踩一個典型誤解,全部認得就穩。
⑦ Keras 輸出層與損失配對:sigmoid+binary
情境:客戶流失二元分類(標籤 0/1 單一欄位),(A)(B) 該填什麼?
model = keras.Sequential([Sequential=「一層接一層」疊起來的網路容器,資料從上往下流。
keras.layers.Dense(64, activation='relu',
input_shape=(20,)),Dense=全連接層,64 個神經元;relu 給非線性;input_shape=(20,)=每筆輸入有 20 個特徵。
keras.layers.Dense(32, activation='relu'),再疊一層 32 個神經元的隱藏層,繼續萃取特徵。
keras.layers.Dense(1, activation='(A)')
])輸出層只有 1 個神經元=輸出一個數字。二元分類要把它壓成 0~1 的「流失機率」→ (A)=sigmoid。
model.compile(optimizer='adam',
loss='(B)',compile=設定訓練方式:optimizer 用 adam(穩定的預設款);loss=訓練要最小化的誤差。單節點+sigmoid 的標準搭檔 → (B)=binary_crossentropy。
metrics=['accuracy'])metrics=訓練過程順便回報的指標,不影響訓練本身。
🎯 配對表(必背):二元單節點=sigmoid+binary_crossentropy;多類 One-Hot=softmax+categorical_crossentropy;整數標籤多類=softmax+sparse_categorical;迴歸=linear+MSE。
⑧ PyTorch 訓練迴圈:忘了 zero_grad 會爆炸
情境:損失越訓練越大、無法收斂——梯度在批次之間不斷累加。
for epoch in range(epochs):外圈:整份資料要看幾輪(epoch)。
for xb, yb in train_loader:內圈:每次拿一小批(batch)特徵 xb 與答案 yb。
pred = model(xb) # 位置 1前向傳播:把這批資料丟進模型得到預測。缺的那行要加在這之前:optimizer.zero_grad()。
loss = criterion(pred, yb) # 位置 2算損失:預測跟正確答案差多少。
loss.backward() # 位置 3反向傳播:從損失往回算每個參數的梯度(該往哪修、修多少)。PyTorch 的設計是把新梯度「加到」舊梯度上,不是覆蓋。
optimizer.step() # 位置 4依梯度更新權重。因為前面沒歸零,這裡用的是「歷史梯度總和」——越疊越大,損失自然爆炸。
🎯 標準五步(必背):zero_grad → forward → loss → backward → step。「梯度累加」是為了支援大 batch 分次算的刻意設計,所以歸零責任在你。
⑨ model.eval() 與 no_grad():推論雙保險
情境:部署前推論,這兩行各做什麼?可以只用一個嗎?
model.eval()把模型切到「考試模式」:Dropout 停止隨機關神經元(否則同一筆輸入每次預測都不同)、BatchNorm 改用訓練期存下的移動平均統計量(不再看當下這批)。不動權重、不碰梯度,之後呼叫 model.train() 就能回到訓練模式。
with torch.no_grad():告訴 autograd:「接下來不用記怎麼算梯度」——不建計算圖,記憶體省一大截、速度也快。with 區塊內的程式都適用。
preds = model(X_test)安心推論:行為確定(eval)+不浪費資源(no_grad)。
🎯 分工:eval() 管「層的行為」、no_grad() 管「梯度引擎」——作用不同、要一起用。只用 eval 會白白建圖耗記憶體;只用 no_grad 則 Dropout 還在亂關、輸出不穩。
⑩ 遷移學習凍結:只訓練最後一層
情境:拿 ImageNet 預訓練的 ResNet18 改成 5 類分類器,只想訓練分類頭。
model = torchvision.models.resnet18(
weights='IMAGENET1K_V1')下載「已經在 ImageNet 百萬張圖上練好」的 ResNet18——它的淺層已會抓邊緣、紋理等通用特徵。
for param in model.parameters():
param.requires_grad = False # 行 A凍結全部參數:requires_grad=False=「不用幫它算梯度」=訓練時不會被更新。整個骨幹變成純特徵抽取器。
model.fc = nn.Linear(
model.fc.in_features, 5) # 行 B換頭:把最後的全連接層(fc)換成輸出 5 類的新層。關鍵:新建的層預設 requires_grad=True——它出生在凍結之後,不受行 A 影響,天生可訓練。
optimizer = torch.optim.Adam(
model.fc.parameters(), lr=1e-3) # 行 C優化器只收分類頭的參數——雙重保險:就算有參數沒凍到,優化器也只更新 fc。
🎯 這叫「特徵萃取(Feature Extraction)」策略:凍骨幹、訓新頭,小資料也能快速收斂。順序很重要:先凍結、再換頭,新頭才會是可訓練的。
⑪ 卷積參數量:(3×3×3)×32+32=896
情境:算出這個卷積層有幾個可訓練參數。計算題每屆穩定出現。
conv = nn.Conv2d(in_channels=3,輸入 3 個通道(彩色圖的 R、G、B)。
out_channels=32,要學 32 個不同的濾鏡(每個濾鏡抓一種特徵),輸出就有 32 個通道。
kernel_size=3, stride=1, padding=1,每個濾鏡是 3×3 的小窗;stride/padding 只影響輸出圖的大小,跟參數量無關。
bias=True)每個濾鏡附一個偏置項(+b),32 個濾鏡=32 個 bias。
# 參數量 = (3×3×3)×32 + 32 = 896一個濾鏡要同時看 3 個輸入通道:3×3×3=27 個權重;乘 32 個濾鏡=864;加 32 個 bias=896。
🎯 公式:(k×k×C_in)×C_out+C_out。與輸入影像大小無關(濾鏡到處共用=參數共享,CNN 省參數的原因)。錯項解碼:864=忘 bias、288=忘乘 32、9248=下一層 (3×3×32)×32+32。
⑫ 學習曲線判讀:驗證反轉就該早停
情境:訓練準確率升到 0.98,驗證在第 10 epoch 到頂 0.85 後掉到 0.78。
plt.plot(history.history['accuracy'],
label='train')history 是 Keras 訓練時的「日記」;這行畫出每個 epoch 的訓練準確率曲線。
plt.plot(history.history['val_accuracy'],
label='val')再畫驗證準確率曲線——兩條線的相對走勢就是診斷依據。
plt.legend(); plt.xlabel('Epoch');
plt.ylabel('Accuracy')加圖例與軸標籤(好圖表的基本禮貌)。
🎯 判讀:訓練一直進步+驗證到頂後反轉=過擬合教科書曲線——最佳模型在驗證峰值附近,用 Early Stopping(搭 patience)在那裡收手,輔以正則化/Dropout/資料增強。繼續硬練只會更糟;把驗證集併入訓練=拆掉儀表板。
⑬ ROC-AUC 要餵「機率」不是 0/1
情境:兩種算 AUC 的寫法,哪個才對?
y_pred = model.predict(X_test)predict 回傳已經用 0.5 門檻切好的 0/1 硬標籤——「誰比較有信心」的資訊全丟了。
auc1 = roc_auc_score(y_test, y_pred)拿 0/1 去算 AUC:整條 ROC 曲線只剩一個門檻點,算出來低估且失真。
y_prob = model.predict_proba(X_test)[:, 1]正確做法:拿正類的連續機率([:,1] 取第 1 欄)。
auc2 = roc_auc_score(y_test, y_prob)AUC 的本質=「隨機抽一正一負,正樣本分數比較高的機率」=排序能力——需要連續分數才能掃出整條曲線。
🎯 鐵律:算 AUC/畫 ROC/調門檻,一律用 predict_proba(或 decision_function);用 predict 是實務最常見的評估 bug。
🧰 套件方法速查卡
衝刺計畫裡的「💻 逐行白話詳解」會跳到這裡——每張卡=語法模板+口訣+出現在哪幾題。
list 參照綁定b = a 不是複製
Python 的「=」只是幫同一個物件多貼一張名牌:改一邊、兩邊都變。要獨立副本得明講。
b = a # 同一間房子、兩支鑰匙
b = a.copy() # 真正影印一份
binom.pmf / cdfscipy.stats 二項分佈
口訣:pmf 恰好、cdf 累積;「至少 k」=1−cdf(k−1),差一就中陷阱。
binom.pmf(k, n, p) # P(X=k) 恰好
binom.cdf(k, n, p) # P(X≤k) 累積
1 - binom.cdf(k, n, p) # P(X≥k+1)
向量化運算Vectorization
欄與欄直接運算,整欄一次算完(底層 C 速度);比逐列迴圈快數十~數百倍,是 Pandas 第一慣例。
df['Revenue'] = df['Quantity'] * df['UnitPrice']
出現在:
科二④・效率排序:向量化 ≫ apply ≫ iterrows
errors='coerce'pd.to_datetime / to_numeric
轉型別時「翻不動的值」變缺值(NaT/NaN)不中斷;預設 raise=遇錯即停。
pd.to_datetime(s, errors='coerce') # 壞日期→NaT
pd.to_numeric(s, errors='coerce') # 壞數字→NaN
groupby 分組彙總df.groupby()
「依某欄分組 → 各組彙總」。一個維度用它;兩個維度交叉改用 pivot_table。
df.groupby('Month')['Revenue'].sum()
df.groupby(['Month','Cat'])['Revenue'].sum().unstack() # =樞紐表
pivot_table 樞紐表df.pivot_table()
index=列、columns=欄、values+aggfunc=格子怎麼算——兩維交叉分析一行搞定。
df.pivot_table(index='Month', columns='Category',
values='Revenue', aggfunc='sum')
sns.boxplot 箱型圖seaborn
跨類別比較「分佈」的首選:五數摘要+離群點一次呈現。
sns.boxplot(x='Region', y='Revenue', data=df)
出現在:
科二⑦・比分佈→箱型;趨勢→折線;數個數→countplot
stratify / StratifiedKFold分層抽樣
切分與交叉驗證都「照標籤比例分」,不平衡資料的標配;注意它是保持比例、不是變平衡。
train_test_split(X, y, stratify=y, ...)
StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
Pipelinesklearn 生產線
前處理+模型綁成一條線丟進 cross_val_score:每折只用訓練部分 fit——官方防洩漏寫法。
pipe = Pipeline([('scaler', StandardScaler()),
('knn', KNeighborsClassifier())])
cross_val_score(pipe, X, y, cv=5)
predict_proba要機率不要標籤
回傳 (n,2) 機率表、[:,1] 取正類機率;predict() 是 0.5 門檻切過的 0/1。調門檻、算 AUC、排名單都靠它。
p = model.predict_proba(X_test)[:, 1]
classification_report報表判讀
不平衡先看少數類 recall/f1、再看 macro avg;weighted 被多數類灌水;support=實際樣本數。
print(classification_report(y_test, y_pred))
決策門檻調整Threshold Tuning
降門檻→判正變多→Recall↑、Precision↓。「寧可誤攔不可漏抓」=降門檻。
y_pred = (model.predict_proba(X)[:, 1] >= 0.3).astype(int)
NumPy 廣播與 @ vs *Broadcasting
維度「相等或一方為 1」即可播:(1,3)+(3,1)→(3,3)。@=矩陣乘法(列×欄內積)、*=逐元素。
c = a + b # 廣播
m1 = A @ B # 矩陣乘法 (m,k)@(k,n)→(m,n)
m2 = A * B # 對位相乘
np.random 與布林平均隨機模擬技巧
rand(N) 產生 0~1 均勻亂數;布林陣列 .mean() 直接算「比例」——蒙地卡羅的兩塊積木。
x = np.random.rand(N)
inside = (x**2 + y**2) <= 1
pi_est = 4 * inside.mean() # True 的比例 × 4
輸出層與損失配對Keras
二元單節點=sigmoid+binary_crossentropy;多類 One-Hot=softmax+categorical;迴歸=linear+MSE。
Dense(1, activation='sigmoid')
model.compile(loss='binary_crossentropy', ...)
optimizer.zero_grad()PyTorch 梯度歸零
PyTorch 預設「累加」梯度:每個 batch 開頭不歸零,梯度越疊越大直接炸掉。
optimizer.zero_grad() # ①歸零
pred = model(xb) # ②forward
loss = criterion(pred, yb) # ③loss
loss.backward() # ④backward
optimizer.step() # ⑤step
model.eval() + torch.no_grad()推論雙保險
eval() 切層行為(Dropout 停用、BN 用移動平均);no_grad() 不建計算圖省記憶體——作用不同、一起用。
model.eval()
with torch.no_grad():
preds = model(X_test)
requires_grad 凍結遷移學習
先全凍結→再換新分類頭(新層預設可訓練)→優化器只收分類頭=特徵萃取策略。
for p in model.parameters(): p.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, 5)
optimizer = Adam(model.fc.parameters())
卷積參數量Conv2d
(k×k×C_in)×C_out+C_out(bias),與輸入影像大小無關——參數共享的威力。
Conv2d(3, 32, kernel_size=3, bias=True)
# (3×3×3)×32 + 32 = 896
學習曲線判讀Learning Curve
訓練低+驗證高=過擬合(加資料/正則化/早停);兩條都高且近=欠擬合(加容量/特徵);驗證峰值後反轉→Early Stopping 收手。
plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
讀完一輪就會發現:
26 題其實只有七個套路——pandas 資料操作、統計計算、切分防洩漏、sklearn 流程、Keras 配對、PyTorch 五步、評估指標。回
中級衝刺站把不熟的方法按進計畫,考前再回來掃一次速查卡就穩了 🐾