컴퓨터 구조에 대해 공부하던 중 다음과 같은 내용을 접하였다.

 

컴퓨터 프로그램에서 저장하는 장치로 레지스터와 메모리를 사용하고, 레지스터의 개수는 제한적이기 때문에 컴파일러는 자주 사용되는 변수를 가능한 한 많이 레지스터에 저장하고 나머지 변수는 메모리에 저장한 후 활용한다는 내용이었다.

 

해당 내용을 읽으면서 문득 논문리뷰를 하며 들었던 의문이 하나 생각났다.

바로, '파라미터 증가가 모델 성능에 크게 영향을 미치는 이유가 무엇일까?' 라는 것이었다.

 

그 이유가 바로 메모리에 관련되어 있을 수 있겠다는 생각이 들었다.

레지스터의 개수는 32개이다. 

 

예를 들어, VGG-19 처럼 4096짜리 FC layer가 있다고 가정한다면, 해당 FC layer에 파라미터 개수는 (4096+1) * (4096) 이다.

약, 1.68M개의 파라미터를 사용하는 것인데 결국 이 많은 파라미터가 메모리에 저장되는 것이다. 

 

단순히 1.68M개의 파라미터를 메모리에 저장하고 불러오는 것만으로도 cost가 어마어마하게 들 것이다.

 

이렇기에 파라미터 수가 모델 성능에 많은 영향을 미치는 것이 아닐까 라는 생각을 하였다.

MNIST Classification을 CNN을 통해 간단하게 구현해보고자 합니다.

 

CNN은 Convolution Neural Network의 약자로, 필터링 기법을 신경망에 적용하여 이미지를 효과적으로 처리할 수 있는 심층 신경망 입니다.

 

CNN의 신경망은 크게 Convolution layer - Max Pooling layer - Fully-Connected layer로 구성됩니다.

 

Convolution layer는 Fliter를 통해 Feature Map을 만들어 내는 계층으로, 이미지의 특징을 추출하는 계층입니다.

Max Pooling layer는 Convolution layer의 Feature Map의 크기를 줄이는 계층입니다.

마지막으로 Fully-Connected layer는 앞선 두 계층을 통해 나온 결과물을 바탕으로 분류를 하는 계층입니다.

 

그럼 코드를 한번 봐볼까요?

 

1. 필요한 Library Import

이제 코드를 구현하기 위해 필요한 Library들을 import 해줍니다.

 

2. 필요한 Hyper Parameter 정의하기

 

<하이퍼 파라미터 설명>

· batch_size: 기계를 학습시킬 때 사용하는 데이터 크기는 보통 1000만 이상이라고 합니다.

이러한 데이터를 한번에 학습시키면 메모리에 부담이 될 뿐만 아니라 너무나도 긴 시간의 연산 시간이 걸리기 때문에 데이터를 나눠서 학습시키게 됩니다. 

이때 연산 한번에 들어가게 할 데이터 크기를 'Batch Size'라고 합니다.

 

· epochs: batch_size 만큼 나누어진 모든 데이터 셋을 학습시키는 횟수를 의미합니다.

예를 들어 1 Epoch 란, 모든 데이터 셋을 신경망을 통해 한번의 순전파와 역전파 과정을 거친 것을 말합니다.

즉, 한번의 학습과정을 마친 것을 말합니다.

 

· learning_rate: 'learnig rate'란, 학습 속도 또는 학습량을 의미합니다. 한번 학습할 때마다 학습시킬 양을 정의하는 변수입니다.

 

추가적으로, iteration은 batch로 나누어진 데이터를 뜻합니다.

총 데이터가 100개, batch size = 10 이면

1 Epoch = 10 iteration이 됩니다.

 

 

3. MNIST Dataset Load

다음은 학습에 활용할 데이터셋을 가져오는 코드입니다.

 

< torchvision.datasets 함수 인자 알아보기>

· root: ' . / '과 같이 우리가 데이터를 저장하고 사용할 경로를 지정하는 인자입니다.

· train: 정의하는 데이터가 학습용인지 테스트용인지 지정하는 인자입니다.

· download: True로 설정할 경우, 지정된 경로에 해당 데이터가 없을 시 다운로드 하도록 하는 인자입니다.

· transform: 데이터를 어떤 형태로 변형할 것인지 지정하는 인자 입니다. 위 코드와 같이 transforms.ToTensor()로 지정하게 되면,

데이터를 텐서로 변형해주는 것을 의미합니다.

 

datasets 함수를 통해, 이번 코드에서 사용할 MNIST 데이터를 불러옵니다.

 

데이터 자료형을 보면 다음과 같습니다.

위 출력을 보면 Train 데이터들은 넓이와 높이가 각각 28임을 알 수 있습니다.  넓이와 높이는 뒤에서 합성곱 계층할 때 중요하니 잘 봐두시면 좋습니다.

 

4. Loader 정의하기

DataLoader는 주어진 데이터셋을 순회 가능(iterable)하도록 만들어 주며,

각 batch의 size를 정의할 수 있고 데이터 셔플 / 병렬 처리 등을 간단하게 처리할 수 있도록 해주는 함수입니다.

 

위와 같이 학습용 / 테스트용 데이터를 DataLoader를 통해 정의해주면, 학습 시 설정한 조건에 맞게 데이터를 Load 해줍니다.

<DataLoader 함수 인자 알아보기>

· dataset : DataLoader를 통해 처리할 dataset을 정의합니다.

· batch_size : 정의된 데이터를 처리할 batch size를 정의합니다.

· shuffle : 말 그대로 데이터를 섞어줄 것인지 설정합니다.

· drop_last : batch size만큼 데이터를 묶고 남은 데이터를 버릴지 설정합니다.

 
자 이제 필요한 Hyper Parameter와 데이터 셋 정의가 끝났습니다. 
본격적으로 CNN 모델 정의와 이를 바탕으로 학습 시켜보는 코드를 알아보겠습니다.
 
5. CNN Model 

 

<Model init>

init 함수는 3개의 레이어로 구성했습니다.

 

-layer1 , layer2 : Convolution layer로 Convolution 연산을 통해 특징을 추출하고, 이에 ReLU 함수를 통해 비 선형성을 부여하며, Max Pooling 을 통해 차원을 축소합니다.

 

-layer3: Fully-Connected layer로 추출된 특징을 바탕으로 분류를 진행합니다.

이때 첫번째 Linear함수의 파라미터가 4*4*64인 이유를 간단하게 설명하자면,

Convolution layer를 통과하면서 차원이 변화하게 됩니다.

 

Convolution 연산에서는 kernel의 size가 5, padding은 default 값이므로 0으로 설정되게 됩니다.

단일 채널의 차원을 계산해보면, (28,28) 차원의 채널을 위 커널로 convolution 연산을 진행해주면 output으로 나오게되는 채널의 차원이

(24,24)가 되고 max pooling을 거치면서 (12,12)가 됩니다.

 

이 과정을 한번 더 반복해주게 되면 (4,4)차원의 단일 채널이 생성됩니다.

 

우리가 2개의 Conv2d연산을 통해 output_channel 을 64개로 해주었으므로,

 총 FeatureMap의 차원은 64*4*4가 되는 것이지요. (4*4 채널이 64개)

 

따라서, Linear의 input_channel이 64*4*4가 되는 것입니다.

 

 

추가적으로 왜 비선형성을 부여해야하는지, Convolution layer에서 Max Pooling을 통해 차원 축소하는 이유는 무엇인지 간단하게 알아보겠습니다.

 

먼저 비선형성을 부여하는 이유는 선형성의 한계 때문입니다. 

f(x) = x+2 , g(x) = x^2 이라고 가정할 때, g(f(x)) = (x+2)^2 입니다.

즉, 여러개의 선형 연산은 하나의 선형 연산으로 표현이 가능하죠.

 

입력된 데이터에 아무리 많은 선형 변환을 적용하여 주어도 하나의 선형변환으로 표현할 수 있기 때문에 쌓은 레이어의 의미가 없어지는 것이죠. 복잡한 특징을 인식할 수 없는 것이죠. 

 

따라서 위와 같은 치명적인 선형성의 한계를 극복하기 위해 비선형성을 부여해주는 것입니다.

 

두번째로 Max pooling을 통해 차원을 축소하는 이유는 바로 '연산량을 감소 시켜주기 위함'입니다.

차원 축소가 없이 학습을 시키게 될 경우, parameter 수의 증가와 이를 통한 엄청난 연산량 때문에 OverFitting이 발생할 우려가 있습니다. 또한, 엄청난 연산량은 학습 속도에도 영향을 미치게 되죠.

 

따라서 Max Pooling은 차원을 축소를 바탕으로 OverFitting의 우려를 감소시키고, 학습 속도가 너무 느려지지 않도록 하기 위해 활용합니다.

 

<Model Forward>

- forward는 init에서 구현한 3개의 레이어를 순차적으로 통과하도록 구현하였습니다.

 

전결합층에는 1차원 데이터로 전달해주어야하기 때문에 layer3 호출 전에 flatten을 진행해줍니다.

 

 

6. 학습을 위한 Model, 손실함수, 최적화 함수 정의

 

손실함수는 CrossEntropy 함수를 사용할 것이고, 최적화 함수는 Adam을 사용하도록 하겠습니다.

 

7. 학습

 

각 Train data를 불러오면서, 순전파와 역전파를 수행해주었습니다.

CNN에 Data를 통과시키는 부분이 순전파

loss를 구하고, 최적화 함수를 적용시키는 부분이 역전파 과정입니다.

 

최종적으로 98%정도의 정확도가 출력되었습니다.

 

MNIST의 경우 외곽 데이터가 큰 의미를 갖지 않기에 Padding의 여부가 결과에는 크게 영향을 미치지 않습니다.

 

Conv2d 연산의 channel, kernel size, padding 등을 조정해가며 정확도가 어떻게 달라지는지 확인해보시면 좋을 것 같습니다.

 

읽어주셔서 감사합니다.

+ Recent posts