2 분 소요

code 다운로드: 📥 conv2d.ipynb 다운로드

어느 날 PyTorch 문서를 둘러보던 중 재밌는 사실을 알게 되었다. nn.Conv2d 문서에 따르면, nn.Conv2d는 사실 convolution을 하지 않는다” 라는 것이다. 문서에 의하면 “where ⋆ is the valid 2D cross-correlation operator” 라고 하는데, cross-correlation와 convolution의 차이는 무엇일까?

import torch
import torch.nn as nn

nn.Conv2d는 무엇일까?

우선 이 포스트를 통해 nn.Conv2d가 무엇인지 고민해본다. 결국 nn.Conv2d는 파라미터(nn.Parameter)이다. 파라미터는 텐서지만, 텐서는 파라미터가 아니다. 둘의 차이는 무엇일까?

파라미터는 .requires_grad=True이며 model.parameters()에 자동으로 등록되는 텐서라고 생각하면 편하다. 아래 예제를 통해 알아보자. 즉 파라미터는 학습 가능한 텐서이다.

아래 코드에서는 rnd라는 변수를 (5,5) 사이즈의 표준 정규 분포를 따르는 텐서로 초기화한다.

torch.randn의 output이 애초에 tensor긴 하지만 본 예제에서는 명시적으로 표현하기 위해 torch.tensor로 감싼다.

rnd = torch.randn(5, 5)
t = torch.tensor(rnd)
t

출력값은 다음과 같다.

/tmp/ipykernel_723/1694594144.py:2: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.detach().clone() or sourceTensor.detach().clone().requires_grad_(True), rather than torch.tensor(sourceTensor).
  t = torch.tensor(rnd)





tensor([[-0.7679,  0.3281,  0.8054,  1.1955, -0.6240],
        [ 0.5000, -0.1930, -0.0020, -1.0173, -0.0083],
        [-0.7387,  0.9879,  0.8556,  0.8847,  0.2356],
        [ 0.4543,  1.8703,  1.0567,  0.2428,  1.7751],
        [ 0.5252,  0.1668, -0.9017,  0.5592, -0.6604]])

이번에는 nn.Parameterrnd 변수를 감싼 뒤 결과를 출력했다.당연하게도 두 값은 모두 같다.



param = nn.Parameter(rnd)
param
Parameter containing:
tensor([[-0.7679,  0.3281,  0.8054,  1.1955, -0.6240],
        [ 0.5000, -0.1930, -0.0020, -1.0173, -0.0083],
        [-0.7387,  0.9879,  0.8556,  0.8847,  0.2356],
        [ 0.4543,  1.8703,  1.0567,  0.2428,  1.7751],
        [ 0.5252,  0.1668, -0.9017,  0.5592, -0.6604]], requires_grad=True)

텐서와 파라미터의 차이는 .requires_grad가 True인지 아닌지와 nn.Module에서 자동으로 학습 가능한 파라미터로 포함하는지 아닌지의 차이가 있다.

print(t.requires_grad)
print(param.requires_grad)
False
True
class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.w = param
        self.t = t

model = Model()
list(model.parameters())
[Parameter containing:
 tensor([[-0.7679,  0.3281,  0.8054,  1.1955, -0.6240],
         [ 0.5000, -0.1930, -0.0020, -1.0173, -0.0083],
         [-0.7387,  0.9879,  0.8556,  0.8847,  0.2356],
         [ 0.4543,  1.8703,  1.0567,  0.2428,  1.7751],
         [ 0.5252,  0.1668, -0.9017,  0.5592, -0.6604]], requires_grad=True)]

Linear와 Conv2d

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv = nn.Conv2d(3, 128, (3, 3))
        self.fc = nn.Linear(128, 10)

    def forward(self, x):
        x = self.conv(x)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x

model = Model()
print(model)
print(type(model.conv.weight), model.conv.weight.shape)
print(type(model.conv.bias), model.conv.bias.shape)

print(type(model.fc.weight), model.fc.weight.shape)
print(type(model.fc.bias), model.fc.bias.shape)
Model(
  (conv): Conv2d(3, 128, kernel_size=(3, 3), stride=(1, 1))
  (fc): Linear(in_features=128, out_features=10, bias=True)
)
<class 'torch.nn.parameter.Parameter'> torch.Size([128, 3, 3, 3])
<class 'torch.nn.parameter.Parameter'> torch.Size([128])
<class 'torch.nn.parameter.Parameter'> torch.Size([10, 128])
<class 'torch.nn.parameter.Parameter'> torch.Size([10])

결국 nn.Conv2d의 weight는 학습 가능한 파라미터이고, 이 파라미터가 cross-correlation 연산에 사용된다.

nn.Conv2d는 사실 convolution을 하지 않는다.

잠시 신호처리의 관점에서 CNN을 바라보자 그러면 CNN이 왜 필터 혹은 커널로 불리는지 이해할 수 있다.

convolution 이란 어떤 함수 $f$와 커널이라는 함수 $g$중 한 함수를 뒤집고(flip) → 슬라이딩하면서 → 원소별 곱의 합(내적)을 하는 것인데, 이를 하게 되면 어떤 $f$와 $g$가 비슷한 방향을 가질 때 값이 커지게 된다. 이게 신호처리에서 필터링의 원리이고 이 때문에 CNN의 파라미터를 커널, 필터 등으로 부르는 것이다.

\[(f * g)(t) = \int f(\tau) \cdot g(t - \tau) \, d\tau\]

근데 torch의 문서에서 볼 수 있듯이 CNN은 사실 convolution을 하지 않는다. cross-correlation과 convolition의 차이는 무엇일까? 우선, cross-correlation의 수식은 다음과 같다.

\[(f \star g)(t) = \int f(\tau) \cdot g(t + \tau) \, d\tau\]

cross-correlation을 convolution 대신 사용하면 (1) 두 함수 중 하나를 굳이 뒤집을 필요가 없어서 연산이 단순해지고, (2) 커널이 입력과 같은 방향으로 슬라이딩하기 때문에 좀 더 직관적이다. CNN은 어차피 커널 값을 학습으로 찾으므로 flip 여부가 결과에 영향을 주지 않아 cross-correlation을 사용해도 무방하다.

아래는 어떤 필터들에 대하여 convolition과 cross-correlation의 결과 차이이다.

png

결과에서 볼 수 있듯이 결과는 크게 변하지 않고 한번 뒤집고 말고의 차이이다. 역사적으로 신호처리에서 convolution을 먼저 사용했고, CNN도 그 이름을 그대로 차용했기 때문에 관습적으로 convolution이라 부른다.