안녕하세요. 오늘은 ILSVRC-2014에서 2등한 모델인 VGGNet의 VGG16 모델을 keras로 구현을 해보고자 합니다.

출처:https://arxiv.org/pdf/1409.1556v6.pdf

위 테이블은 VGGNet 논문에 있는 테이블로, 오늘 구현해보고자 하는 VGG16은 빨간 사각형 내에 있는 D에 해당하는 모델입니다.

 

모델을 간략하게 설명해드리자면 다음과 같습니다.

  • 13개의 conv layer, 5개의 maxpooling layer, 3개의 FC layer로 구성되어 있습니다.
  • Input shape은 224 x 224 RGB image => (224,224,3) 입니다.
  • conv layer의 kernel size = 3, filter size = 64 ~ 512 , padding = 1, stride = 1
  • maxpooling layer의 stride = 2
  • activation function(활성화함수) => relu
import tensorflow
from tensorflow import keras

# VGG 16 model의 input => 224 x 224 RGB image => (224 x 224 x 3)
# VGG16은 16 weight layer로 13개의 conv layer와 3개의 FC layer로 구성되어 있습니다.
# convolution layer는 3x3 size 이며, filter size는 block 마다 64에서 512까지 2의 배수로 증가합니다.
# activation function(활성화함수)로는 relu 함수를 사용합니다.
def VGG16():
  model = keras.Sequential()

  # First convolution block
  # the number of convolution=2, filter size=64, kernel size=3, activation function=relu, padding=1=same
  # the number of maxpooling=1, stride 2
  model.add(keras.layers.Conv2D(input_shape=(224,224,3), filters=64, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.Conv2D(filters=64, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.MaxPool2D(strides=(2,2)))

  # Second convolution block
  # filter size=128
  model.add(keras.layers.Conv2D(filters=128, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.Conv2D(filters=128, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.MaxPool2D(strides=(2,2)))

  # Third convolution block
  # the number of convolution=3, filter size=256
  model.add(keras.layers.Conv2D(filters=256, kernel_size=3, padding='same', activation= 'relu'))
  model.add(keras.layers.Conv2D(filters=256, kernel_size=3, padding='same', activation= 'relu'))
  model.add(keras.layers.Conv2D(filters=256, kernel_size=3, padding='same', activation= 'relu'))
  model.add(keras.layers.MaxPool2D(strides=(2,2)))

  # Forth convolution block
  # filter size = 512
  model.add(keras.layers.Conv2D(filters=512, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.Conv2D(filters=512, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.Conv2D(filters=512, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.MaxPool2D(strides=(2,2)))

  # Fifth convolution block
  model.add(keras.layers.Conv2D(filters=512, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.Conv2D(filters=512, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.Conv2D(filters=512, kernel_size=3, padding='same', activation='relu'))
  model.add(keras.layers.MaxPool2D(strides=(2,2)))

  # FC layer & softmax layer
  model.add(keras.layers.Flatten())
  model.add(keras.layers.Dense(4096, activation='relu'))
  model.add(keras.layers.Dense(4096, activation='relu'))
  model.add(keras.layers.Dense(1000, activation='softmax'))

  return model

'인공지능 > 구현' 카테고리의 다른 글

[파이토치] 보스턴 집값 데이터로 신경망 구현하기  (0) 2022.09.06
[파이토치] CNN  (0) 2022.08.06

안녕하세요.

오늘은 사이킷런(scikit-learn)에서 제공해주는 보스턴 집값 데이터를 통해 신경망을 코드로 구현해보려고 합니다.

구글 코랩을 사용하였으며, 파이토치 기반으로 작성하였습니다.

 

크게 신경망을 구축하는데 있어서 필요한 요소는 다음과 같습니다.

 

1) 데이터 호출

2) 데이터 전처리

3) 신경망 모델 구현

4) 학습에 필요한 하이퍼파라미터, 손실함수 그리고 옵티마이저 세팅 및 학습 데이터셋 구성

5) 순전파와 역전파 과정을 통한 학습


 

각 과정에 맞춰 코드를 한번 살펴보겠습니다.

 

1) 데이터 호출

 

신경망에 있어 필요한 라이브러리 호출

먼저 신경망을 구성하는데 있어 필요한 라이브러리들을 호출해주었습니다.

 

이 부분은 난수 발생에 관련한 랜덤 시드를 고정시키는 부분입니다.

머신러닝에서는 랜덤성에 의하여 결과 값이 달라지기 때문에 모델에 따른 결과 값의 변화를 확인하기 위해 랜덤 시드를 고정하였습니다.

 

(다만, 현재 코드에서 DataLoader의 랜덤성을 고정시키는 방법을 찾지 못해 코드를 돌릴 때마다 다른 결과가 나올 수 있습니다.

추후, 방법을 찾게 된다면 정리하여 포스팅 하도록 하겠습니다.)

데이터를 호출하여 저장하는 코드입니다.

집값에 영향을 미치는 변수들은 총 13가지의 독립변수로 구성되어 있으며, 다음과 같습니다.

 

  • CRIM: 범죄율을 나타내는 수치
  • INDUS: 비소매상업지역 면적 비율을 나타내는 수치
  • NOX: 일산화질소 농도를 나타내는 수치
  • RM: 주택당 방 수를 나타내는 수치
  • LSTAT: 인구 중 하위 계층 비율을 나타내는 수치
  • B: 인구 중 흑인 비율을 나타내는 수치
  • PTRATIO: 학생/교사 비율을 나타내는 수치
  • ZN: 25,000 평방피트를 초과 거주지역 비율을 나타내는 수치
  • CHAS: 찰스강의 경계에 위치해 있는지 여부를 나타내는 수치
  • AGE: 1940년 이전에 건축된 주택의 비율을 나타내는 수치
  • RAD: 방사형 고속도로까지의 거리를 나타내는 수치
  • DIS: 직업센터의 거리를 나타내는 수치
  • TAX: 재산세율을 나타내는 수치

Target 데이터는 종속변수로 1978년 506개의 도시의 집값의 중앙값 입니다. (단위 1,000 달러)


2) 데이터 전처리

 

다음은 데이터를 전처리 하는 과정입니다.

 

먼저, 학습을 하기 위해 자료형은 Tensor이어야 하기 때문에 Tensor형으로 자료형을 변환해주었습니다.

 

그리고 학습을 위해서는 훈련을 위한 데이터와 훈련이 잘되었는지 검증하기 위한 검증 데이터가 필요합니다.

따라서, 주어진 입력데이터를 train_test_split 함수를 통해 데이터의 80%를 훈련용 데이터로, 20%를 검증용 데이터로 나누어 주었습니다.

 

데이터 자료형을 확인해 볼때, 13개의 독립변수(집값에 영향을 미치는 데이터)와 이에 따른 1개의 종속변수(집값)로 구성되어 있음을 알 수 있었습니다.

데이터의 shape에서도 확인할 수 있는데요.

x에 해당하는 데이터들은 13의 길이를 갖고 있고, y에 해당하는 데이터는 1의 길이를 갖고 있음을 확인할 수 있습니다.


위 코드는, 구글 코랩의 gpu를 사용하기 위한 코드입니다.

 


3) 신경망 모델 구현하기

다음은 신경망 모델을 구현한 코드입니다.

 

파이토치는 일반적으로 torch.nn.Module을 기본클래스로 지정하여 사용합니다.

따라서, 구현하고자 하는 신경망은 torch.nn.Module을 상속받는 클래스로 정의합니다.

 

이제 신경망을 어떻게 구현했는지 보겠습니다.

 

먼저, Input Layer - Hidden Layer - Output Layer로 이어지는 층을 구성하였습니다.

13개의 변수에 대하여 1개의 target 값을 예측하는 방향이므로,

Input dimension을 13으로, Hidden Layer를 거쳐 나오는 데이터의 Output dimension을 1로 설정해주었습니다.

 

또한, 각 층 사이 사이에 비선형성을 주기 위하여 ReLU를 사용하였습니다.


4) 학습에 필요한 하이퍼파라미터, 손실함수 그리고 옵티마이저 세팅 및 학습 데이터셋 구성

먼저, 신경망 모델에 필요한 하이퍼파라미터는 3가지 입니다.

 

한번 연산에 입력할 데이터 크기를 의미하는 batch_size,

학습에 있어서 한번에 얼만큼 학습할 것인지를 의미하는 learning_rate,

총 몇번 학습할 것인지를 의미하는 epochs(에포크)가 있습니다.

 

손실함수로는 MSE Loss 함수를 사용하였고, 옵티마이저로는 Adam을 사용하였습니다.

 

* model = Model().to(device) 는 모델을 학습에 gpu를 사용하기 위해, 모델을 gpu 상으로 올려주는 코드입니다.

 

학습에 필요한 데이터 셋을 구성한 코드입니다.

 

먼저, 각 train데이터와 test데이터를 TensorDataset 함수를 통해 묶어주었습니다.

이를 바탕으로, DataLoader를 활용하여 batch_size로 데이터를 나누어 저장해주었습니다.


5) 순전파와 역전파 과정을 통한 학습
 
 
 

마지막으로 학습을 시키는 과정입니다. 

세팅한 epoch(에포크) 수 만큼 학습이 진행되며, 한번의 epoch 안에서는 DataLoader를 통해 나누어 놓은 모든 데이터에 대한 학습을 진행합니다.

 

먼저, batch_size별로 구성된 x 데이터를 꺼내와 구현해놓은 신경망을 통해 순전파를 진행합니다.

 

그리고 역전파를 진행하기전, optimizer.zero_grad()를 통해 모델 매개변수 변화도를 조정합니다.

기본적으로 변화도는 계속해서 더해지기 때문에 변화도의 중복 계산을 막기 위하여 변화도를 0로 다시 세팅합니다.

 

순전파를 진행한 예측 값에 대하여, y 값과 손실함수를 통해 통해 손실을 구합니다.

이를 바탕으로, loss.backward() 함수를 통해 역전파를 진행하고 옵티마이저를 통해 매개변수 값을 조정합니다.

 


<학습된 모델의 손실도 및 정확도 확인 및 손실도 그래프 출력하기>

 

학습된 모델을 바탕으로, 테스트 데이터 셋을 이용하여 손실과 정확도를 구하는 부분입니다.

학습하는 부분과 다른 점은 with torch.no_grad() 입니다.

 

테스트 데이터 셋을 기반으로 손실과 정확도를 구할 때는 학습을 하는 것이 아니기에 더 이상 gradient를 계산할 필요가 없습니다.

따라서, 필요없는 연산을 수행하지 않게 하고 이를 바탕으로 연산 속도를 올려주기 위해 다음 코드를 활용합니다.

 

최종적으로 정확도는 약 74%가 나왔습니다.

마지막으로, 학습과정에서 각 epoch별 loss에 대해서 그래프를 그려보았습니다.

초반부에 loss가 확 떨어져, 전체적으로 감소하는 추이를 볼 수 있었습니다.

 

다만 정확도가 74% 정도로 나온 것은 조금 아쉬웠는데요.

 

데이터에 최적화된 신경망과 손실함수, 옵티마이저, 스케쥴러 등을 활용한다면 더 높은 정확도를 가진 모델을 구현할 수 있을 것 같습니다.

다만, 해당 포스팅은 정확도 보다는 신경망 자체를 코드로 보여드리는 것에 초점을 맞추었기에 이번 포스팅은 여기서 마무리 하도록 하겠습니다.

 

글에 대한 피드백은 언제나 감사드립니다. 

읽어주셔서 감사합니다.

 

'인공지능 > 구현' 카테고리의 다른 글

[케라스] VGG16 모델 구현  (0) 2022.09.21
[파이토치] CNN  (0) 2022.08.06

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