관리 메뉴

개발자의 스터디 노트

CNN 하이퍼 파라미터 본문

파이썬/파이토치 자연어처리

CNN 하이퍼 파라미터

박개발씨 2022. 2. 22. 00:43

CNN은 공간상의 부분 구조를 감지하는 데 적합한( 그래서 의미 있는 공간상의 부분 구조를 만드는) 신경망입니다. CNN 은 소수의 가중치를 사용해 입력 데이터 텐서를 스캔하는 식으로 이를 수행합니다. 스캔하면서 부분 구조의 유무를 표현하는 출력 텐서를 만듭니다.

2차원 합성곱 연산. 입력 행렬이 커널 하나와 합성곱 연산을 수행하여 출력 행렬(또는 특성 맵)을 만듭니다. 합성곱은 입력 행렬의 위치마다 커널을 적용합니다. 합성곱 연산을 수행할 때마다 커널과 입력 행렬의 각 원소를 곱한 후 모두 더합니다.

 

1. 합성곱 연산의 차원

 - 파이토치에서는 1차원, 2차원, 3차원 합성곱이 가능하며 각각 Conv1d, Conv2d, Conv3d 클래스로 구현되었습니다.

 - 1차원 합성곱은 각 타임 스텝에 특성 벡터가 있는 시계열에 잘 맞습니다. 이런 경우 시퀀스 차원을 따라 패턴을 학습할 수 있습니다. NLP에서 합성곱 연산은 대부분 1차원입니다.

 - 2차원 합성곱은 데이터의 두 방향을 따라 시공간 패턴을 감지합니다. 예를 들어 이미지의 높이와 너비 차원입니다. 이 때문에 2차원 합성곱은 이미지 처리 분야에서 많이 사용합니다.

 - 3차원 합성곱은 데이터의 세 방향을 따라 패턴을 감지합니다. 예를 들어 비디오 데이터는 차원 3개에 정보가 놓여있습니다. (두 차원은 이미지 프레임을 나타내고 시간 차원은 프레임의 시퀀스를 나타냅니다.)

 

2. 채널

채널은 입력의 각 포인트에 있는 특성 차원을 의미합니다.

 - 이미지에는 픽셀마다 RGB에 해당하는 차원이 3개 있습니다.

 - 텍스트 문서의 픽셀이 단어라면 채널 개수는 어휘 사전의 크기입니다. 더 세분화하여 문자에 대한 합성곱을 수행한다면 채널 개수는 문자 집합의 크기가 됩니다.(이 집합이 어휘 사전이 됩니다.)

파이토치의 합성곱 구현에서 입력 채널 개수는 in_channels 매개변수입니다. 주어진 문제에 적절한 출력 채널 개수를 바로 알기는 힘듭니다. 1과 1024 사이로 한계를 정한다면, 합성곱 층에는 채널이 최소 1개에서 최대 1024개까지 있습니다. 다음으로 고려할 사항은 입력 채널 개수입니다. 일반적인 디자인 패턴에서는 합성곱 층에서 다음 층으로 채널 개수를 2배 이상 줄이지 않습니다. 이는 고정불변의 법칙은 아니지만 적절한 out_channels의 크기가 얼마인지 감을 잡을 수 있습니다.

입력 행렬이 2개인 합성곱 연산. 해당 커널에도 채널이 2개 있습니다. 각 채널에 독립적으로 곱한 다음 결과를 모두 더합니다. 하이퍼파라미터 설정은 다음과 같습니다. input_channels=2. output_channels=1, kernel_size=2, stride=1, padding=0, dilation=1

 

 

입력 행렬 1개(입력 채널 1개)와 합성곱 커널 2개(출력 채널 2개)가 있는 합성곱 연산, 커널이 독립적으로 입력 행렬에 적용되어 출력 텐서에 차례대로 쌓입니다. 하이퍼파라미터 설정은 다음과 같습니다. input_channels=1, output_channels=2, kernel_size=2, stride=1, padding=0, dilation=1

 

3. 커널 크기

 - 커널 행렬의 너비를 커널 크기(kernel size)라고 부릅니다. 

 - 합성곱마다 얻어지는 정보의 양은 커널 크기로 조절됩니다. 하지만 커널 크기를 늘리면 출력의 크기가 줄어듭니다.

입력 행렬에 적용한 kernel_size=3인 합성곱, 결과에는 장단점이 있습니다. 커널이 행렬에 적용될 때 더많은 국부적인 정보가 사용됩니다. 하지만 출력 크기는 작아집니다.

커널의 크기가 작을수록 작고 자주 등장하는 패턴을 감지합니다. 커널 크기가 커지면 큰 패턴을 감지합니다. 이는 의미 있는 패턴이지만 발생 빈도가 적습니다. 작은 커널은 상세한 특성을 출력하고 큰 커널은 성긴 특성을 만듭니다.

 

4. 스트라이드

 - 스트라이드(stride)는 합성곱 간의 스텝 크기를 제어합니다. 스트라이드가 커널 크기와 같으면 커널 연산이 겹치지 않습니다. 반면 스트라이드가 1이면 커널이 가장 많이 겹칩니다.

스트라이드 1로 입력에 적용된 kernel_size=2인 합성곱 커널, 커널이 더 큰 스트라이드를 사용해서 출력 행렬을 작게 만드는 효과를 냅니다. 이는 입력 행렬을 듬성듬성 샘플링하는 데 유용합니다.

 

 

5. 패딩

stride 와 kernel_size가 계산된 특성 값의 범위를 제어하지만, 특성 맵(합성곱의 출력)의 전체 크기를 의도치 않게 줄이는 바람직하지 않은 부수효과가 발생합니다. 이에 대처하려면 입력 텐서의 길이, 높이, 깊이 차원의 앞뒤에 0을 추가하여 인공적으로 늘려줍니다. 이렇게 하면 CNN이 합성곱을 더 많이 수행할 수 있습니다. 원하는 커널 크기, 스트라이드, 다일레이션을 그대로 사용하면서 출력 크기를 조절할 수 있습니다.

높이와 너비가 2인 입력 행렬에 적용된 kernel_size=2인 합성곱. 하지만 패딩(주황색) 때문에 입력 행렬의 높이와 너비는 더 커집니다. 가장 널리 사용하는 커널 크기는 3이므로 출력 행렬이 입력 행렬의 크기와 정확히 같아집니다.

 

6. 다일레이션

다일레이션(dilation)은 합성곱 커널이 입력 행렬에 적용되는 방식을 제어합니다.

다일레이션을 1(기본값)에서 2로 늘리면 입력 행렬에 적용될 때 커널의 원소 사이에 공간이 생깁니다. 커널 원소 사이에 스텝이 있다거나 구멍 뚫린 커널을 적용한다고 말합니다. 파라미터 개수를 늘리지 않고 넓은 입력 공간을 요약하는 데 유용합니다. 다일레이션 합성곱은 합성곱 층을 쌓을 때 매우 유용하다고 입증되었습니다. 연속된 다일레이션 합성곱은 수용장(receptive field)의 크기를 기하급수적으로 늘려줍니다. 수용장은 신경망이 예측을 만들기 전에 바라보는 입력 공간의 크기입니다.

입력 행렬에 적용된 kernel_size=2인 합성곱. 다일레이션을 기본값보다 늘리면 커널 행렬의 원소를 입력행렬에 곱할 때 넓게 퍼집니다. 다일레이션을 증가시킬수록 더 많이 퍼집니다.

 

 

7. 파이토치로 CNN 구현하기

 

- 인공 데이터와 Conv1d 클래스

import torch

batch_size = 2
one_hot_size = 10
sequence_width = 7
data = torch.randn(batch_size, one_hot_size, sequence_width)
conv1 = torch.nn.Conv1d(in_channels=one_hot_size, out_channels=16, kernel_size=3)
intermediate1 = conv1(data)
print(data.size())
print(intermediate1.size())
torch.Size([2, 10, 7])
torch.Size([2, 16, 5])

 

- 데이터에 반복 적용한 합성곱

conv2 = torch.nn.Conv1d(in_channels=16, out_channels=32, kernel_size=3)
conv3 = torch.nn.Conv1d(in_channels=32, out_channels=64, kernel_size=3)

intermediate2 = conv2(intermediate1)
intermediate3 = conv3(intermediate2)

print(intermediate2.size())
print(intermediate3.size())
torch.Size([2, 32, 3])
torch.Size([2, 64, 1])
y_output=intermediate3.squeeze()
print(y_output.size())
torch.Size([2, 64])

합성곱 층을 더 만들어 차례대로 적용하여 출력 텐서의 크기를 줄였습니다. 위 예제 코드는 합성곱을 2개 추가하여 3번의 합송곱 후에 출력의 마지막 차원이 size=1이 되도록 구성하였습니다. 합성곱마다 채널 차원의 크기는 증가합니다. 채널 차원이 각 데이터 포인트의 특성 벡터가 되기 때문입니다. 텐서가 특성 벡터가 되는 최종 단계는 불필요한 size=1 차원을 제거하는 것입니다. squeeze() 메서드를 사용하면 size=1의 차원이 제거됩니다.

 

 

- 특성 벡터를 줄이는 두 가지 추가 방법

특성 벡터로 펼치는 방법과 특성 맵 차원을 따라 평균을 계산하는 방법입니다.

# 특성 벡터를 줄이는 방법 2
print(intermediate1.view(batch_size, -1).size())
# view()메서드를 사용해 하나의 벡터로 펼칩니다.

# 특성 벡터를 줄이는 방법 3
print(torch.mean(intermediate1, dim=2).size())
# print(torch.max(intermediate1, dim=2).size())
# print(torch.sum(intermediate1, dim=2).size())
# 수학 연산을 사용해 정보를 벡터로 요약합니다. 가장 많이 사용하는 연산은 산술 평균입니다.
torch.Size([2, 80])
torch.Size([2, 16])

펼침 방법은 모든 정보를 유지하지만 필요 이상으로 큰 특성 벡터를 만들기도 합니다. 평균은 특성 맵 차원의 크기에 상관없지만 일부 정보를 잃을 수 있습니다.

 

 

 

 

 

합성곱을 설계하는 방법은 경험을 기반으로 합니다. 먼저 예상하는 크기의 데이터에서 시작해 일련의 합성곱을 실험하다 보면 결국 문제에 잘 맞는 특성 벡터를 얻게 됩니다. 이런 방법은 실전에서 잘 작동하지만, 합성곱의 하이퍼파라미터와 입력 텐서가 주어지면 합성곱 연산의 수학 공식을 사용해 출력 텐서의 크기를 계산하는 방법도 있습니다.