본문 바로가기

ML&DL/PyTorch

[PyTorch] 시계열 데이터를 위한 1D convolution과 1x1 convolution

 

오늘은 시계열 데이터처리에 많이 사용되는 1D convolution이 PyTorch에 어떻게 구현되어 있는지와 어떤 파라미터가 존재하는지 차원은 어떻게 계산하는 지를 정리해 보려고 한다. 자꾸 까먹는 나 자신을 위한 포스팅이 될 것 같다. 시작해보자!

간단한 1D convolution 예시

2D convolution이 가로,세로로 모두 이동하면서 output이 계산되는 것과 다르게 1D convolution 연산은 가로로만 이동하면서 output을 계산해낸다. 우리가 알고있는 신호처리에서의 convolution과 다르게 사실은 cross correlation 연산과 같다. 아래의 예시를 계산해보면 쉽게 이해할 수 있다. 직접 계산해보면 금방 이해할 것이다. (여기서 kernel은 filter라고도 부르며 이것이 학습되는 파라미터이다.)

 

1D convolution

먼저 1D convolution을 PyTorch에서 어떻게 사용하는지 그리고 파라미터는 무엇이 있는 지 하나씩 알아보자. 

 

import torch.nn as nn

conv1 = nn.Conv1d(in_channels=4, out_channels=64, kernel_size=5, 
                  stride=3, padding=0, dilation=1, 
                  groups=1, bias=True, padding_mode='zeros')

# default setting
# you have to set in_channels, output_channels and kernel_size
conv2 = nn.Conv1d(in_channels=4, out_channels=64, kernel_size=5)

Parameters

  • in_channels: input의 feature dimension
  • out_channels: 내가 output으로 내고싶은 dimension
  • kernel_size: time step을 얼마만큼 볼 것인가(=frame size = filter size)
  • stride: kernel을 얼마만큼씩 이동하면서 적용할 것인가 (Default: 1) -> 아래 추가 설명
  • dilation: kernel 내부에서 얼마만큼 띄어서 kernel을 적용할 것인가 (Default: 1) -> 아래 추가 설명
  • padding: 한 쪽 방향으로 얼마만큼 padding할 것인가 (그 만큼 양방향으로 적용) (Default: 0)
  • groups: kernel의 height를 조절 (Default: 1) -> 아래 추가 설명
  • bias: bias term을 둘 것인가 안둘 것인가 (Default: True)
  • padding_mode: 'zeros', 'reflect', 'reflect', 'replicate', 'circular' (Default: 'zeros')  

Input

input은 [Batch_size, Feature_dimension, Time_step] 순이다. 즉 time step(K) 마다 feature dimension(N)이 존재하는 2차원 배열이 들어온다. batch size(B)까지 고려하면 3차원이지만 알아서 계산해주므로 아래 그림에선 생략한다.

 

Operation

2차원 이미지의 경우 (Length, Width, Channel) 이렇게 3개의 차원이 존재하지만 1차원 시계열 데이터의 경우 (Feature dimension, Time_step) 이렇게 2개의 차원이 존재하므로 두 번째 차원인 feature dimension이 채널이 된다. 그래서 in_channel은 위의 예시에서 N이 된다. 여기에 기본적인 1D convolution을 적용해보자.

2D convolution의 경우 kernel size를 설정하면 length와 width가 같은 정사각형의 kernel이 생성되지만, 1D convolution은 width가 kernel size가 되고 length는 input channel과 같아진다. 그리고 그런 kernel이 out channel만큼 생성된다. 위의 예시를 보면, out channel B=3일 때 각각 주황, 초록, 파랑 커널을 이용해 convolution 연산을 진행하면 된다.

Output

output은 [Batch size, Feature dimension(Channel_dimension), kernel로 변경된 Time_step] 순이다. output은 아래의 그림으로 표현할 수 있다.

 

Code example (Input, kernel, output size)

import torch
import torch.nn as nn

batch_size = 3
feature_dim = 256
time_step = 16000

x = torch.rand(batch_size, feature_dim, time_step)
print('input_size:', x.shape)

conv1d = nn.Conv1d(256, 128, kernel_size=3)
print('kernel_size:', conv1d.weight.shape)

out = conv1d(x)
print('output_size:',out.shape)

 

 

위와 같이 kernel의 size는 [128, 256, 3]이 되고 모델 파라미터는 128X256X3 = 98304로 계산가능하다.

Stride

stride를 설정하면 kernel이 몇칸을 뛰면서 적용될지가 설정된다.

 

Dilation

dilation을 설정하면 kernel 내부에서 몇 칸을 띄면서 볼 것인가가 설정된다.

 

(추가) 1D Dilated convolution과 Receptive field

dilation은 time-series 데이터의 1D convolution 연산에서 많이 사용되는 option인데 이를 통해서 receptive field를 늘릴 수 있다. Receptive field는 현재 layer의 하나의 time step을 결정하는데 있어서 이전 layer의 몇개의 time step을 봤는지를 의미한다. dilation을 주면 기본적인 1D convolution연산에 비해 더 먼 time step까지 활용해서 다음 노드가 결정되고 이에 따라 receptive field가 늘어나는 효과를 준다. dilated convolution의 경우 왼쪽 그림과 같이 기본적인 dillated convolution뿐 아니라 미래의 정보를 활용하지 않고 과거의 정보만을 활용하는 오른쪽 그림의 causal dilated convolution도 존재한다. TCN [1] 논문에서는 dilation factor를 2의 지수승으로 증가시켜나가며 receptive field를 늘리는 dilated convolution 방식을 사용하였고 이 방식이 현재 널리 사용된다. dilation을 사용하면서 더 긴 sequential modeling이 가능해졌기 때문에 이를 RNN계열 [2]과 비교하는 실험들도 있다. 해당 논문을 참고해서 시계열모델링하길 바란다. (시간이 나면 residual block 코드도 추가해두겠다..)

 

(왼) 기본 dilated convolution(=atrous convolution), (오른) TCN의 causal dilated convolution

 

Receptive field의 계산은 다음과 같다.

 

receptive_field = (kernel_size - 1) * dilation + 1

Groups

Groups는 output channel과 input channel을 같게 맞추고 channel을 잘라서 convolution을 적용한다. 아래의 예제에서처럼 같은 색깔부분끼리만 group을 지어 convolution을 취한다. group수가 채널수와 같다면 depthwise convolution이라고 한다. 

 

모든 option을 적용했을 때, output length 계산

 

1x1 convolution

pointwise convolution이라고도 불리는 1x1 convolution은 kernel size가 1인 1D convolution을 뜻한다. 채널 수를 조절하며 비선형성을 주는 연산으로 알려져 있다. 1D convolution의 경우 채널이 feature dimension이 되기 때문에 feature를 줄였다 늘렸다 하며 feature에 bottleneck효과를 줄 수 있다. stride를 따로 조절하지 않는다면 하나의 kernel이 한 time step에만 적용되기 때문에 time step을 유지하며 feature dimension만 변경된다. 

 

stride를 따로 조절하지 않는다면 하나의 kernel이 한 time step에만 적용되기 때문에 time step을 유지하며 feature dimension만 변경된다.

 

마찬가지로 아래 예시로 input size, kernel size, output size를 확인할 수 있다. 중간의 feature dimension만 바뀌는 것을 확인할 수 있다.

 

import torch
import torch.nn as nn


x = torch.rand(3,256,16000)
print('input_size:', x.shape)
conv1d = nn.Conv1d(256, 128, kernel_size=1)
print('kernel_size:', conv1d.weight.shape)
out = conv1d(x)
print('output_size:',out.shape)

Depthwise separable convolution: 더 빠르고 가볍다!

기본적인 convolution은 depthwise convolution + pointwise convolution으로 대체될 수 있다. 이를 depthwise separable convolution이라고 하고 mobilenet에서 처음 제안되었다. [3] 이 기법은 모델의 파라미터가 줄어들면서도 성능은 유지하거나 더 좋다는 것이 알려져있다. 먼저 이미지에서 쓰였지만, 시계열 데이터에서도 동일하게 사용가능하다. 구현으로는, depthwise의 경우 groups라는 파라미터를 채널과 동일하게 설정하면 되고, pointwise convolution (=1X1 convolution)의 경우 kernel size를 1로 설정하면 된다. (이미지는 시간이 나면 1D로 수정하겠습니다..)

출처:https://coding-yoon.tistory.com/122

Reference

[1] Temporal convolutional networks for action segmentation and detection

[2] An empirical evaluation of generic convolutional and recurrent networks for sequence modeling

[3]  MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications