一、 兩大框架之核心設計哲學與物理映射表
「動態計算圖(PyTorch)」與「靜態/宣告式 API(Keras/TensorFlow)」。
【 深度學習框架設計哲學對立面 】
| 物理維度 | PyTorch (Imperative / Dynamic) | Keras (Declarative / Functional) |
|---|---|---|
| 物理本質 | 動態計算圖 (Eager Execution) | 宣告式封裝 (Engine Abstraction) |
| 控制核心 | 明確參數導向 (Tensor-level) | 層級抽象導向 (Layer-level) |
| 執行流程 | 手動控制 Loop, 自由度高 | 高階 fit() 封裝, 速度與易用平衡 |
二、 核心對照變數物理映射表 (Scientific Alignment Map)
下表為 PyTorch 與 Keras 在模型建構、參數控制與訓練上的 1-to-1 科學對照:
| 比較維度 | PyTorch (物件/參數導向) | Keras / TensorFlow (層級/宣告導向) |
|---|---|---|
| 線性映射層 | nn.Linear(in_features, out_features) |
layers.Dense(units) |
| 卷積層 | nn.Conv2d(in_channels, out_channels, ...) |
layers.Conv2D(filters, kernel_size, ...) |
| 池化層 | nn.MaxPool2d(kernel_size) |
layers.MaxPooling2D(pool_size) |
| 結構容器 | nn.Sequential 或繼承 nn.Module |
Sequential 或 Functional API (Model) |
| 凍結核心屬性 | 參數張量屬性 parameter.requires_grad = False |
層級/模型屬性 layer.trainable = False |
| 訓練循環 | 手動撰寫 loss.backward() 與 opt.step() |
宣告式 model.compile() + model.fit() |
| 權重獲取 | model.state_dict() 或 model.parameters() |
model.get_weights() |
三、 深入對位解構 (Deep Architectural Alignment)
1. 模型結構與線性層物理映射 (nn.Linear vs layers.Dense)
線性層的數學本質為矩陣相乘與偏置相加: Y = X x W + B
🔬 PyTorch 介面實作:
- 物理特性:權重矩陣 W 的形狀是
(out_features, in_features),在內部進行矩陣乘法時,PyTorch 會自動將輸入 X 與 W 的轉置相乘:X x W.T + B。 - 類別導向定義: (Neural Network建築模型材料庫)
import torch
import torch.nn as nn
# 宣告線性層:輸入維度 50,輸出維度 10
py_linear = nn.Linear(in_features=50, out_features=10)
# 直接存取權重參數張量
print(py_linear.weight.shape) # 輸出: torch.Size([10, 50])
print(py_linear.bias.shape) # 輸出: torch.Size([10])
🔬 Keras 介面實作:
- 物理特性:權重矩陣 W 的形狀是
(in_features, out_features)。Keras 直接執行X x W + B。它不需要在初始化時顯式指定in_features,而是會在第一次前向傳播(傳入數據)時自動推導輸入維度。 - 類別導向定義:
from tensorflow import keras
from tensorflow.keras import layers
# 宣告 Dense 層:僅需指定輸出單元數 10
keras_dense = layers.Dense(units=10)
# 注意:在未建立/未傳入數據前,Keras 的權重並未初始化 (避免維度硬編碼)
# 傳入虛擬數據進行建置
keras_dense.build(input_shape=(None, 50))
print(keras_dense.weights[0].shape) # 輸出: (50, 10) -> 權重矩陣
print(keras_dense.weights[1].shape) # 輸出: (10,) -> 偏置向量
2. 凍結語句與參數控制 ( requires_grad vs trainable )
這就是造成 AttributeError 的核心根源。
【 凍結機制之物理路徑對比 】
◆ PyTorch (從 Tensor 屬性阻斷)
model.features (nn.Sequential) ➔ parameters() 生成器 ➔ [Parameter Tensor (Weight)] ➔ requires_grad = False (Autograd 引擎跳過梯度計算)
◆ Keras (從 Layer 容器狀態標記)
model.layers (Layer List) ➔ layer.trainable = False ➔ [Layer Object] ➔ 在 compile() 時,將該層所有權重移出可訓練變數列表 (trainable_weights)
❌ 混淆陷阱除錯 (Error Diagnostics)
當我們嘗試在 PyTorch 的 Conv2d 物件上直接寫 layer.trainable = False 時:
- PyTorch 執行期反饋:
AttributeError: 'Conv2d' object has no attribute 'trainable' - 根因分析:PyTorch 的
nn.Module(Conv2d 的父類別)只維護一個_parameters字典和一個_modules字典,它沒有設計trainable這個屬性來管理狀態。 - PyTorch 正確凍結語法:
for param in model.features.parameters():
param.requires_grad = False
❌ 反向混淆陷阱 (Keras 側)
若在 Keras 的 Layer 上嘗試修改屬性 layer.requires_grad = False:
- 運行回饋:Keras 的 Layer 對象並不會報錯,但完全不起作用。因為 Keras 的底層引擎在呼叫
model.compile()時,只會去檢查各層的.trainable屬性。
3. 訓練核心與優化器控制 (Autograd vs Compile)
🔬 PyTorch 的「顯式物理控制」:
PyTorch 將前向傳播、損失計算、反向傳播與參數更新完全暴露給開發者。優化器只負責更新傳入給它的那組參數。
# 1. 篩選出未被凍結的參數,送入優化器
optimizer = torch.optim.SGD(
filter(lambda p: p.requires_grad, model.parameters()),
lr=0.01
)
# 2. 訓練迴圈手動控制
optimizer.zero_grad() # 1. 清空舊梯度
outputs = model(inputs) # 2. 前向傳播
loss = criterion(outputs, y) # 3. 計算損失
loss.backward() # 4. 反向傳播 (僅對 requires_grad=True 的參數計算梯度)
optimizer.step() # 5. 更新權重
🔬 Keras 的「宣告式引擎控制」:
Keras 將底層細節隱藏在 C++ 引擎中。開發者只需宣告優化器與損失函數,並由 fit() 執行高階優化。
# 1. 凍結特定層
model.get_layer("block1_conv1").trainable = False
# 2. 編譯模型 (此步驟會鎖定哪些權重可以被更新)
model.compile(
optimizer=keras.optimizers.SGD(learning_rate=0.01),
loss='sparse_categorical_crossentropy'
)
# 3. 啟動訓練,引擎內部自動處理梯度與更新
model.fit(x_train, y_train, epochs=5)
四、 總結:Feynman 專家級記憶口訣
- PyTorch 是「泥水匠」:你得親自走到每個「水泥磚(Tensor)」面前,把「要不要抹水泥(
requires_grad)」的開關關掉。你對磚頭(參數)直接發言。 - Keras 是「設計師」:你只需對「房間(Layer)」掛上「請勿施工(
trainable = False)」的牌子,施工隊(compile)在進場時看到牌子,就會自動繞過整個房間。你對房間(層)發言。