深度学习中卷积与互相关

发布于:2025-08-06 ⋅ 阅读:(23) ⋅ 点赞:(0)

1. 互相关运算 (cross-correlation)

  • 输出位置 (m, n) 的值:
    Y[m,n]=∑i=0Hk−1∑j=0Wk−1X[m+i,n+j]⋅K[i,j]Y[m,n] = \sum_{i=0}^{H_k-1} \sum_{j=0}^{W_k-1} X[m+i, n+j] \cdot K[i,j]Y[m,n]=i=0Hk1j=0Wk1X[m+i,n+j]K[i,j]
  • 输出尺寸公式:
    output_height=Hx−Hk+1\text{output\_height} = H_x - H_k + 1output_height=HxHk+1
    output_width=Wx−Wk+1\text{output\_width} = W_x - W_k + 1output_width=WxWk+1
    (其中 Hx,WxH_x, W_xHx,Wx 是输入尺寸,Hk,WkH_k, W_kHk,Wk 是卷积核尺寸)
  • $ X[m+i, n+j] $ 是输入图像的局部区域。
  • $ K[i,j] $ 是核的对应位置元素。

2. 卷积运算 (convolution)

  • 数学定义要求先翻转卷积核:
    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[Hk1i,Wk1j]
  • 卷积输出:
    Y[m,n]=∑i=0Hk−1∑j=0Wk−1X[m+i,n+j]⋅Kflipped[i,j]Y[m,n] = \sum_{i=0}^{H_k-1} \sum_{j=0}^{W_k-1} X[m+i, n+j] \cdot K_{\text{flipped}}[i,j]Y[m,n]=i=0Hk1j=0Wk1X[m+i,n+j]Kflipped[i,j]
  • 等价形式(直接使用原始核):
    Y[m,n]=∑i=0Hk−1∑j=0Wk−1X[m+i,n+j]⋅K[Hk−1−i,Wk−1−j]Y[m,n] = \sum_{i=0}^{H_k-1} \sum_{j=0}^{W_k-1} X[m+i, n+j] \cdot K[H_k-1-i, W_k-1-j]Y[m,n]=i=0Hk1j=0Wk1X[m+i,n+j]K[Hk1i,Wk1j]
3. 工作流程图示

卷积计算详细过程(结合公式)

输入数据:
输入 X:       卷积核 K:      翻转核 K_flipped:
[1, 2, 3]     [0, 1]        [3, 2]
[4, 5, 6]     [2, 3]        [1, 0]
[7, 8, 9]
卷积公式:

数学定义的卷积运算:
Y[m,n]=∑i=0Hk−1∑j=0Wk−1X[m+i,n+j]⋅Kflipped[i,j]Y[m,n] = \sum_{i=0}^{H_k-1} \sum_{j=0}^{W_k-1} X[m+i, n+j] \cdot K_{\text{flipped}}[i,j]Y[m,n]=i=0Hk1j=0Wk1X[m+i,n+j]Kflipped[i,j]

其中:

  • Hk=2H_k = 2Hk=2(核高度)
  • Wk=2W_k = 2Wk=2(核宽度)
  • 输出尺寸:(3−2+1)×(3−2+1)=2×2(3-2+1) \times (3-2+1) = 2 \times 2(32+1)×(32+1)=2×2
计算步骤:
  1. 位置 (0,0) 的计算

    • 输入窗口:
      [1, 2]
      [4, 5]
      
    • 翻转核:
      [3, 2]
      [1, 0]
      
    • 计算过程:
      1*3 = 3
      2*2 = 4
      4*1 = 4
      5*0 = 0
      ---------
      总和 = 3 + 4 + 4 + 0 = 11
      
    • 公式表示:
      Y[0,0]=∑i=01∑j=01X[i,j]⋅Kflipped[i,j]Y[0,0] = \sum_{i=0}^{1}\sum_{j=0}^{1} X[i,j] \cdot K_{\text{flipped}}[i,j]Y[0,0]=i=01j=01X[i,j]Kflipped[i,j]
  2. 位置 (0,1) 的计算

    • 输入窗口:
      [2, 3]
      [5, 6]
      
    • 翻转核:
      [3, 2]
      [1, 0]
      
    • 计算过程:
      2*3 = 6
      3*2 = 6
      5*1 = 5
      6*0 = 0
      ---------
      总和 = 6 + 6 + 5 + 0 = 17
      
  3. 位置 (1,0) 的计算

    • 输入窗口:
      [4, 5]
      [7, 8]
      
    • 翻转核:
      [3, 2]
      [1, 0]
      
    • 计算过程:
      4*3 = 12
      5*2 = 10
      7*1 = 7
      8*0 = 0
      ---------
      总和 = 12 + 10 + 7 + 0 = 29
      
  4. 位置 (1,1) 的计算

    • 输入窗口:
      [5, 6]
      [8, 9]
      
    • 翻转核:
      [3, 2]
      [1, 0]
      
    • 计算过程:
      5*3 = 15
      6*2 = 12
      8*1 = 8
      9*0 = 0
      ---------
      总和 = 15 + 12 + 8 + 0 = 35
      
最终输出:
Y = [[11, 17],
     [29, 35]]
图解计算过程:
输入 X 和滑动窗口:
[1, 2, 3]    [1,2] [2,3] 
[4, 5, 6] →  [4,5] [5,6] → 输出 Y[0,0] 和 Y[0,1]
[7, 8, 9]    [4,5] [5,6] 
             [7,8] [8,9] → 输出 Y[1,0] 和 Y[1,1]

每个窗口与翻转核进行元素乘加:
[1,2] ⊙ [3,2] = 1*3 + 2*2 + 4*1 + 5*0 = 11
[4,5]   [1,0]

关键点:数学卷积需要先翻转卷积核(180°旋转),然后执行互相关运算(滑动点积)。深度学习框架通常省略翻转步骤,直接进行互相关操作。

code

import torch
import numpy as np

# 手动实现的卷积函数
def flip_kernel1(K):
    """180° 翻转卷积核"""
    # 上下翻转
    K_ud = K[::-1]
    # 左右翻转
    K_flipped = [row[::-1] for row in K_ud]
    return K_flipped

def flip_kernel(K):
    """180° 翻转卷积核(完全基础实现)"""
    # 1. 获取卷积核尺寸
    height = len(K)       # 总行数
    width = len(K[0])     # 总列数
    
    # 2. 创建空的结果矩阵(尺寸相同)
    K_flipped = []
    
    # 3. 逐行处理(从最后一行到第一行)
    for row_index in range(height):
        # 创建新行
        new_row = []
        
        # 4. 逐列处理(从最后一列到第一列)
        for col_index in range(width):
            # 计算翻转后的位置
            flipped_row = height - 1 - row_index
            flipped_col = width - 1 - col_index
            
            # 5. 获取原始核对应位置的元素
            original_value = K[flipped_row][flipped_col]
            
            # 6. 添加到新行
            new_row.append(original_value)
        
        # 7. 将完整新行添加到结果矩阵
        K_flipped.append(new_row)
    
    return K_flipped

def corr2d(X, K):
    h = len(K)  # 核的高度 H_k
    w = len(K[0])  # 核的宽度 W_k

    # 输出矩阵的尺寸
    output_height = len(X) - h + 1
    output_width = len(X[0]) - w + 1

    Y = [[0 for _ in range(output_width)] for _ in range(output_height)]

    for m in range(output_height):  # 外部循环:输出位置 m
        for n in range(output_width):  # 外部循环:输出位置 n
            current_sum = 0.0
            for i in range(h):  # 内部循环:卷积核行索引 i
                for j in range(w):  # 内部循环:卷积核列索引 j
                    x_val = X[m + i][n + j]  # 输入矩阵的对应位置 X[m+i, n+j]
                    k_val = K[i][j]  # 卷积核的对应位置 K[i, j]
                    current_sum += x_val * k_val  # 乘积累加
            Y[m][n] = current_sum  # 存储结果 Y[m,n]
    return Y

def conv2d(X, K):
    """二维卷积运算(先翻转核,再调用互相关)"""
    K_flipped = flip_kernel(K)
    return corr2d(X, K_flipped)

# 构造输入数据和卷积核
X =[[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]]

K = [[8, 7],
       [6, 5]]
# 手动实现的互相关结果
manual_result = corr2d(X, K)
manual_conv_result = conv2d(X, K)


# 转换为 PyTorch 张量
X_tensor = torch.tensor(X, dtype=torch.float32).unsqueeze(0).unsqueeze(0)  # [1, 1, 5, 5]
K_tensor = torch.tensor(K, dtype=torch.float32).unsqueeze(0).unsqueeze(0)  # [1, 1, 3, 3]

# 使用 PyTorch 的 Conv2d 进行卷积
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=(3, 3), stride=1, padding=0, bias=False)
conv_layer.weight.data = K_tensor

with torch.no_grad():
    pytorch_result = conv_layer(X_tensor).squeeze().tolist()

# 打印结果对比
print("手动实现卷积结果:")
for row in manual_conv_result:
    print(row)

print("手动实现互相关结果:")
for row in manual_result:
    print(row)

print("\nPyTorch 卷积结果:")
for row in pytorch_result:
    print(row)

# 计算误差
manual_flat = [item for row in manual_result for item in row]
pytorch_flat = [item for row in pytorch_result for item in row]

error = [abs(m - p) for m, p in zip(manual_flat, pytorch_flat)]
max_error = max(error)
mean_error = sum(error) / len(error)

print("\n误差分析:")
print(f"最大误差:{max_error:.6f}")
print(f"平均误差:{mean_error:.6f}")

结果:

手动实现卷积结果:
[8.0, 7.0, 16.0, 14.0]
[6.0, 5.0, 12.0, 10.0]
[24.0, 21.0, 32.0, 28.0]
[18.0, 15.0, 24.0, 20.0]
手动实现互相关结果:
[5.0, 6.0, 10.0, 12.0]
[7.0, 8.0, 14.0, 16.0]
[15.0, 18.0, 20.0, 24.0]
[21.0, 24.0, 28.0, 32.0]

PyTorch 卷积结果:
[5.0, 6.0, 10.0, 12.0]
[7.0, 8.0, 14.0, 16.0]
[15.0, 18.0, 20.0, 24.0]
[21.0, 24.0, 28.0, 32.0]

误差分析:
最大误差:0.000000
平均误差:0.000000

网站公告

今日签到

点亮在社区的每一天
去签到