文章目錄
- 轉置卷積的三種等價實現方法:原理、公式與等價性分析
- 數學定義與核心公式
- 方法一:零填充+翻轉核卷積(數學定義方法)
- 原理與公式
- 等價性說明
- 方法二:直接位置映射(pytorch框架高效實現)
- 原理與公式
- 等價性說明
- 方法三:矩陣轉置法(數學本質實現)
- 原理與公式
- 等價性說明
- 轉置卷積三種方法詳細計算過程(使用輸入X和卷積核K)
- 輸入參數
- 方法一:零填充+翻轉核卷積(數學定義方法)
- 計算步驟:
- 方法二:直接位置映射(框架高效實現)
- 計算步驟:
- 方法三:矩陣轉置法(數學本質實現)
- 計算步驟:
- 三種方法統一結果
- code
轉置卷積的三種等價實現方法:原理、公式與等價性分析
數學定義與核心公式
轉置卷積的數學本質是卷積運算的伴隨算子(adjoint operator)。給定輸入 X∈RHin×WinX \in \mathbb{R}^{H_{in} \times W_{in}}X∈RHin?×Win? 和卷積核 K∈RHk×WkK \in \mathbb{R}^{H_k \times W_k}K∈RHk?×Wk?,輸出 Y∈RHout×WoutY \in \mathbb{R}^{H_{out} \times W_{out}}Y∈RHout?×Wout? 滿足:
Hout=(Hin?1)×stride+Hk?2×paddingH_{out} = (H_{in} - 1) \times \text{stride} + H_k - 2 \times \text{padding}Hout?=(Hin??1)×stride+Hk??2×padding
Wout=(Win?1)×stride+Wk?2×paddingW_{out} = (W_{in} - 1) \times \text{stride} + W_k - 2 \times \text{padding}Wout?=(Win??1)×stride+Wk??2×padding
三種方法都實現了相同的線性變換:
Y=T(X;K,stride,padding)Y = \mathcal{T}(X; K, \text{stride}, \text{padding})Y=T(X;K,stride,padding)
其中 T\mathcal{T}T 表示轉置卷積操作。
方法一:零填充+翻轉核卷積(數學定義方法)
原理與公式
-
輸入擴展:
Xexpanded[i,j×s]=X[i,j]X_{\text{expanded}}[i, j \times s] = X[i, j]Xexpanded?[i,j×s]=X[i,j]
其中 sss 為步長,元素間插入 (s?1)(s-1)(s?1) 個零值 -
邊界填充:
Xpadded=pad(Xexpanded,p)X_{\text{padded}} = \text{pad}(X_{\text{expanded}}, p)Xpadded?=pad(Xexpanded?,p)
p=max?(padding,k?1)p = \max(\text{padding}, k-1)p=max(padding,k?1) -
核翻轉:
Kflipped[i,j]=K[Hk?1?i,Wk?1?j]K_{\text{flipped}}[i,j] = K[H_k-1-i, W_k-1-j]Kflipped?[i,j]=K[Hk??1?i,Wk??1?j] -
互相關運算:
Y[m,n]=∑i=0Hk?1∑j=0Wk?1Xpadded[m+i,n+j]?Kflipped[i,j]Y[m,n] = \sum_{i=0}^{H_k-1} \sum_{j=0}^{W_k-1} X_{\text{padded}}[m+i, n+j] \cdot K_{\text{flipped}}[i,j]Y[m,n]=i=0∑Hk??1?j=0∑Wk??1?Xpadded?[m+i,n+j]?Kflipped?[i,j]
等價性說明
此方法直接實現數學定義:轉置卷積 = 翻轉核 + 擴展輸入 + 互相關
方法二:直接位置映射(pytorch框架高效實現)
原理與公式
-
位置映射:
For?each?(i,j)∈X:\text{For each } (i,j) \in X:For?each?(i,j)∈X:
Y[i?s+k,j?s+l]+=X[i,j]?K[k,l]Y[i \cdot s + k, j \cdot s + l] \mathrel{+}= X[i,j] \cdot K[k,l]Y[i?s+k,j?s+l]+=X[i,j]?K[k,l]
k∈[0,Hk?1],l∈[0,Wk?1]k \in [0, H_k-1], l \in [0, W_k-1]k∈[0,Hk??1],l∈[0,Wk??1] -
邊界處理:
僅當 i?s+k?p≥0i \cdot s + k - p \geq 0i?s+k?p≥0 且 j?s+l?p<Woutj \cdot s + l - p < W_{out}j?s+l?p<Wout? 時累加
等價性說明
此方法通過散射(scatter)操作實現:
- 每個輸入元素將核權重"放置"到輸出空間
- 重疊區域的值自動累加
- 數學上等價于方法一,但避免了顯式擴展
方法三:矩陣轉置法(數學本質實現)
原理與公式
-
構建卷積矩陣:
定義常規卷積算子 CCC,滿足:
vec(Yconv)=C?vec(X)\text{vec}(Y_{\text{conv}}) = C \cdot \text{vec}(X)vec(Yconv?)=C?vec(X) -
轉置卷積:
vec(Y)=CT?vec(X)\text{vec}(Y) = C^T \cdot \text{vec}(X)vec(Y)=CT?vec(X) -
矩陣元素:
C(m,n),(i,j)=K[k,l]if{i=?m/s?+kj=?n/s?+lC_{(m,n),(i,j)} = K[k,l] \quad \text{if} \quad \begin{cases} i = \lfloor m/s \rfloor + k \\ j = \lfloor n/s \rfloor + l \end{cases}C(m,n),(i,j)?=K[k,l]if{i=?m/s?+kj=?n/s?+l?
等價性說明
此方法直接實現線性代數的伴隨算子定義:
- CCC 是卷積的矩陣表示
- CTC^TCT 是轉置卷積的精確數學實現
轉置卷積三種方法詳細計算過程(使用輸入X和卷積核K)
輸入參數
- 輸入矩陣 X:
[[1, 2],[3, 4]]
- 卷積核 K:
[[5, 6],[7, 8]]
- 步長 stride:2
- 填充 padding:0
- 輸出尺寸:4×4(根據公式:
(2-1)×2 + 2 - 0 = 4
)
方法一:零填充+翻轉核卷積(數學定義方法)
計算步驟:
-
輸入矩陣 X零填充擴展輸入:
- 在元素間插入
(stride-1)=1
個零值 - 在行間插入
(stride-1)=1
行零行
[[1, 0, 2],[0, 0, 0],[3, 0, 4]]
- 在元素間插入
-
輸入矩陣 X邊界填充:
- 添加
(kernel_size-p-1)=1
圈零值
[[0, 0, 0, 0, 0],[0, 1, 0, 2, 0],[0, 0, 0, 0, 0],[0, 3, 0, 4, 0],[0, 0, 0, 0, 0]]
- 添加
-
核翻轉:
- 180度旋轉卷積核
原始核:[[5, 6],[7, 8]]翻轉核:[[8, 7],[6, 5]]
-
互相關計算:
- 使用翻轉后的核在填充矩陣上滑動計算
- 位置(0,0):
窗口:[[0,0],[0,1]] 計算:0×8 + 0×7 + 0×6 + 1×5 = 5
- 位置(0,1):
窗口:[[0,0],[1,0]] 計算:0×8 + 0×7 + 1×6 + 0×5 = 6
- 位置(0,2):
窗口:[[0,0],[0,2]] 計算:0×8 + 0×7 + 0×6 + 2×5 = 10
- 位置(0,3):
窗口:[[0,0],[2,0]] 計算:0×8 + 0×7 + 2×6 + 0×5 = 12
- 位置(1,0):
窗口:[[0,1],[0,0]] 計算:0×8 + 1×7 + 0×6 + 0×5 = 7
- 位置(1,1):
窗口:[[1,0],[0,0]] 計算:1×8 + 0×7 + 0×6 + 0×5 = 8
- 位置(1,2):
窗口:[[0,2],[0,0]] 計算:0×8 + 2×7 + 0×6 + 0×5 = 14
- 位置(1,3):
窗口:[[2,0],[0,0]] 計算:2×8 + 0×7 + 0×6 + 0×5 = 16
- 位置(2,0):
窗口:[[0,0],[0,3]] 計算:0×8 + 0×7 + 0×6 + 3×5 = 15
- 位置(2,1):
窗口:[[0,0],[3,0]] 計算:0×8 + 0×7 + 3×6 + 0×5 = 18
- 位置(2,2):
窗口:[[0,0],[0,4]] 計算:0×8 + 0×7 + 0×6 + 4×5 = 20
- 位置(2,3):
窗口:[[0,0],[4,0]] 計算:0×8 + 0×7 + 4×6 + 0×5 = 24
- 位置(3,0):
窗口:[[0,3],[0,0]] 計算:0×8 + 3×7 + 0×6 + 0×5 = 21
- 位置(3,1):
窗口:[[3,0],[0,0]] 計算:3×8 + 0×7 + 0×6 + 0×5 = 24
- 位置(3,2):
窗口:[[0,4],[0,0]] 計算:0×8 + 4×7 + 0×6 + 0×5 = 28
- 位置(3,3):
窗口:[[4,0],[0,0]] 計算:4×8 + 0×7 + 0×6 + 0×5 = 32
-
最終輸出:
[[ 5, 6, 10, 12],[ 7, 8, 14, 16],[15, 18, 20, 24],[21, 24, 28, 32]]
方法二:直接位置映射(框架高效實現)
計算步驟:
-
位置映射關系:
- 輸入元素位置 → 輸出起始位置
(0,0) → (0,0) (0,1) → (0,2) (1,0) → (2,0) (1,1) → (2,2)
-
核權重分配:
- 元素(0,0)=1 的貢獻:
[1×5, 1×6] → 位置(0,0)和(0,1) [1×7, 1×8] → 位置(1,0)和(1,1)
- 元素(0,1)=2 的貢獻:
[2×5, 2×6] → 位置(0,2)和(0,3) [2×7, 2×8] → 位置(1,2)和(1,3)
- 元素(1,0)=3 的貢獻:
[3×5, 3×6] → 位置(2,0)和(2,1) [3×7, 3×8] → 位置(3,0)和(3,1)
- 元素(1,1)=4 的貢獻:
[4×5, 4×6] → 位置(2,2)和(2,3) [4×7, 4×8] → 位置(3,2)和(3,3)
- 元素(0,0)=1 的貢獻:
-
輸出矩陣構建:
(0,0): 1×5 = 5 (0,1): 1×6 = 6 (0,2): 2×5 = 10 (0,3): 2×6 = 12(1,0): 1×7 = 7 (1,1): 1×8 = 8 (1,2): 2×7 = 14 (1,3): 2×8 = 16(2,0): 3×5 = 15 (2,1): 3×6 = 18 (2,2): 4×5 = 20 (2,3): 4×6 = 24(3,0): 3×7 = 21 (3,1): 3×8 = 24 (3,2): 4×7 = 28 (3,3): 4×8 = 32
-
完整輸出:
[[ 5, 6, 10, 12],[ 7, 8, 14, 16],[15, 18, 20, 24],[21, 24, 28, 32]]
方法三:矩陣轉置法(數學本質實現)
計算步驟:
-
構建卷積矩陣 C:
- 常規卷積:從4×4輸入 → 2×2輸出
- 矩陣維度:4行(輸出元素) × 16列(輸入元素)
-
填充卷積矩陣:
- 輸出位置(0,0)對應輸入位置:
[0,0]:5, [0,1]:6 [1,0]:7, [1,1]:8
- 矩陣行(0,0):
[5,6,0,0,7,8,0,0,0,0,0,0,0,0,0,0]
- 輸出位置(0,1)對應輸入位置:
[0,2]:5, [0,3]:6 [1,2]:7, [1,3]:8
- 矩陣行(0,1):
[0,0,5,6,0,0,7,8,0,0,0,0,0,0,0,0]
- 輸出位置(1,0)對應輸入位置:
[2,0]:5, [2,1]:6 [3,0]:7, [3,1]:8
- 矩陣行(1,0):
[0,0,0,0,0,0,0,0,5,6,0,0,7,8,0,0]
- 輸出位置(1,1)對應輸入位置:
[2,2]:5, [2,3]:6 [3,2]:7, [3,3]:8
- 矩陣行(1,1):
[0,0,0,0,0,0,0,0,0,0,5,6,0,0,7,8]
- 輸出位置(0,0)對應輸入位置:
-
轉置矩陣 CTC^TCT:
- 尺寸:16×4
- 每列對應一個輸出位置的影響
-
矩陣乘法:
- 輸入展平:
[1,2,3,4]
- 計算:
Y_flat = C^T × [1,2,3,4]^T
- 結果:
[5,6,10,12,7,8,14,16,15,18,20,24,21,24,28,32]
- 輸入展平:
-
重塑為4×4矩陣:
[[ 5, 6, 10, 12],[ 7, 8, 14, 16],[15, 18, 20, 24],[21, 24, 28, 32]]
三種方法統一結果
[[ 5, 6, 10, 12],[ 7, 8, 14, 16],[15, 18, 20, 24],[21, 24, 28, 32]]
code
import torch
import torch.nn as nn# 定義輸入張量和卷積核
input_tensor = torch.tensor([[[[1, 2],[3, 4]]]], dtype=torch.float32) # 形狀(1,1,2,2)
kernel = torch.tensor([[[[5, 6],[7, 8]]]], dtype=torch.float32) # 形狀(1,1,2,2)# 方法1:使用nn.ConvTranspose2d模塊(推薦)
def method_conv_transpose2d(input, kernel):# 創建轉置卷積層conv_trans = nn.ConvTranspose2d(in_channels=1, # 輸入通道數out_channels=1, # 輸出通道數kernel_size=2, # 卷積核尺寸stride=2, # 步長padding=0, # 輸入填充bias=False # 禁用偏置)# 手動注入卷積核權重with torch.no_grad():conv_trans.weight = nn.Parameter(kernel)# 執行計算return conv_trans(input)# 方法2:使用functional.conv_transpose2d函數
def method_functional(input, kernel):return torch.nn.functional.conv_transpose2d(input=input,weight=kernel,bias=None,stride=2,padding=0)# 方法3:手動實現轉置卷積(驗證原理)
def manual_transposed_conv(input, kernel):"""根據和的數學原理實現:1. 輸入插值:步長2需插入1行/列零2. 外部填充:卷積核2x2需在邊緣補1圈零3. 用旋轉180°的核執行普通卷積(步長1)"""# 輸入插值(插入行列零)interpolated = torch.zeros(1, 1, 3, 3)interpolated[0, 0, ::2, ::2] = input.squeeze()# 外部填充(四周各補1圈零)padded = torch.nn.functional.pad(interpolated, [1, 1, 1, 1])# 卷積核旋轉180°rotated_kernel = torch.rot90(kernel.squeeze(), 2, dims=(0, 1)).unsqueeze(0).unsqueeze(0)print("旋轉后的卷積核:\n", rotated_kernel.squeeze())# 執行普通卷積(步長1,無填充)return torch.nn.functional.conv2d(padded, rotated_kernel, padding=0)# 計算并驗證結果
output_module = method_conv_transpose2d(input_tensor, kernel)
output_functional = method_functional(input_tensor, kernel)
output_manual = manual_transposed_conv(input_tensor, kernel)print("nn.ConvTranspose2d 輸出:\n", output_module.squeeze())
print("F.conv_transpose2d 輸出:\n", output_functional.squeeze())
print("手動實現轉置卷積輸出:\n", output_manual.squeeze())
結果:
旋轉后的卷積核:tensor([[8., 7.],[6., 5.]])
nn.ConvTranspose2d 輸出:tensor([[ 5., 6., 10., 12.],[ 7., 8., 14., 16.],[15., 18., 20., 24.],[21., 24., 28., 32.]], grad_fn=<SqueezeBackward0>)
F.conv_transpose2d 輸出:tensor([[ 5., 6., 10., 12.],[ 7., 8., 14., 16.],[15., 18., 20., 24.],[21., 24., 28., 32.]])
手動實現轉置卷積輸出:tensor([[ 5., 6., 10., 12.],[ 7., 8., 14., 16.],[15., 18., 20., 24.],[21., 24., 28., 32.]])
參考:
00
0
1
2
3