스트라이드 컨볼루션

  • 기존 컨볼루션 연산은 필터를 1칸씩 이동시키며 연산을 했는데, 스트라이드 컨볼루션은 여러칸씩 점프하며 연산하는 것
def conv2d(data: np.ndarray, filters: np.ndarray, stride: int = 1) -> np.ndarray:
    data_len, _ = data.shape
    filter_len, _ = filters.shape
    output_len = ((data_len - filter_len) // stride) + 1
    output = np.zeros((output_len, output_len))

    for i in range(output_len):
        for j in range(output_len):
            for k in range(filter_len):
                for l in range(filter_len):
                    output[i, j] += data[i * stride + k, j * stride + l] * filters[k, l]

    return output

data = np.array([
    [2, 3, 7, 4, 6, 2, 9],
    [6, 6, 9, 8, 7, 4, 3],
    [3, 4, 8, 3, 8, 9, 7], 
    [7, 8, 3, 6, 6, 3, 4],
    [4, 2, 1, 8, 3, 4, 6],
    [3, 2, 4, 1, 9, 8, 3],
    [0, 1, 3, 9, 2, 1, 4]  
])

kernel = np.array([
    [3, 4, 4],
    [1, 0, 2],
    [-1, 0, 3]
])

result = conv2d(data, kernel, stride=2)
print(result)
[[ 91. 100.  88.]
 [ 69.  91. 117.]
 [ 44.  72.  74.]]
  • stride 는 ss 로 표기한다.
  • 출력 행렬의 크기는 반올림 하여 [n+2pfs+1]×[n+2pfs+1][\frac{n+2p-f}{s}+1]\times [\frac{n+2p-f}{s}+1] 가 된다.

수학 교과서의 컨볼루션

  • 요소별 곱셈과 합을 하기 전에 필터를 좌/우로 뒤집는다.
kernel = np.array([
    [3, 4, 4],
    [1, 0, 2],
    [-1, 0, 3]
])
flip_kernel = np.flipud(np.fliplr(kernel))
print(flip_kernel)
[[ 3  0 -1]
 [ 2  0  1]
 [ 4  4  3]]
  • 원래 이런 미러링까지 도입 해야 진짜 컨볼루션 연산이라고 할 수 있지만 딥러닝에서 관례 상 그냥 컨볼루션 연산이라고 얘기를 한다.