Keras - CNN ImageDataGenerator 활용하기


keras에서는 이미지데이터 학습을 쉽게하도록 하기위해 다양한 패키지를 제공한다. 그 중 하나가 ImageDataGenerator 클래스이다. ImageDataGenerator 클래스를 통해 객체를 생성할 때 파라미터를 전달해주는 것을 통해 데이터의 전처리를 쉽게할 수 있고, 또 이 객체의 flow_from_directory 메소드를 활용하면 폴더 형태로된 데이터 구조를 바로 가져와서 사용할 수 있다. 이 과정은 매우 직관적이고 코드도 ImageDataGenerator를 사용하지 않는 방법에 비해 상당히 짧아진다. 환경은 keras tensorflow backend를 이용하였다.



1. 라이브러리 임포트

# Part 1 - Building the CNN # Importing the Keras libraries and packages from keras.models import Sequential from keras.layers import Conv2D from keras.layers import MaxPooling2D from keras.layers import Flatten from keras.layers import Dense

2. 모델 작성


ImageDataGenerator 클래스를 이용하는 것과 독립적으로 CNN 네트워크를 만들어준다. Input Image의 크기는 64x64이며, Conv-Pool-Conv-Pool-Fully Connected Layer로 이어지는 아주 간단한 구조의 CNN 모델이다. 데이터는 binary 데이터이기 때문에 activation function은 sigmoid함수 이며 손실 함수로 binary_crossentropy, 최적화 함수로는 adam을 사용한다.

# Initialising the CNN
classifier = Sequential()

# Step 1 - Convolution
classifier.add(Conv2D(32, (3, 3), input_shape = (64, 64, 3), activation = 'relu'))

# Step 2 - Pooling
classifier.add(MaxPooling2D(pool_size = (2, 2)))

# Adding a second convolutional layer
classifier.add(Conv2D(32, (3, 3), activation = 'relu'))
classifier.add(MaxPooling2D(pool_size = (2, 2)))

# Step 3 - Flattening
classifier.add(Flatten())

# Step 4 - Full connection
classifier.add(Dense(units = 128, activation = 'relu'))
classifier.add(Dense(units = 1, activation = 'sigmoid'))

# Compiling the CNN
classifier.compile(optimizer = 'adam', loss = 'binary_crossentropy', metrics = ['accuracy'])

3. ImageDataGenerator를 통한 트레이닝, 테스트 데이터 만들기


ImageDataGenerator를 만들 때 아래와 같이 rescale, shear_range, zoom_range, horizontal_flip을 설정해줌으로써 해당 데이터의 전처리를 어떻게 할지를 정해준다. 이 때 파라미터가 무엇을 뜻하는지를 정리하면 아래와 같다. (https://keras.io/preprocessing/image/)

  • shear_range: Float. Shear Intensity (Shear angle in counter-clockwise direction as radians)
  • zoom_range: Float or [lower, upper]. Range for random zoom. If a float,
  • horizontal_flip: Boolean. Randomly flip inputs horizontally.
  • rescale: rescaling factor. Defaults to None. If None or 0, no rescaling is applied, otherwise we multiply the data by the value provided (before applying any other transformation).
# Part 2 - Fitting the CNN to the images
from keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale = 1./255,
                                   shear_range = 0.2,
                                   zoom_range = 0.2,
                                   horizontal_flip = True)

test_datagen = ImageDataGenerator(rescale = 1./255)

training_set = train_datagen.flow_from_directory('dataset/training_set',
                                                 target_size = (64, 64),
                                                 batch_size = 32,
                                                 class_mode = 'binary')

test_set = test_datagen.flow_from_directory('dataset/test_set',
                                            target_size = (64, 64),
                                            batch_size = 32,
                                            class_mode = 'binary')

classifier.fit_generator(training_set,
                         steps_per_epoch = 300,
                         epochs = 25,
                         validation_data = test_set,
                         validation_steps = 2000)

또한 flow_from_directory 메소드를 사용하면 폴더구조를 그대로 가져와서 ImageDataGenerator 객체의 실제 데이터를 채워준다. 이 데이터를 불러올 때 앞서 정의한 파라미터로 전처리를 한다. 마지막으로 fit_generator 함수를 실행함으로써 fitting이 이루어진다. 이 예제의 경우 ImageDataGenerator 객체를 사용하였기 때문에 fit_generator 함수를 통해 fitting한다. steps_per_epoch은 한 번 epoch 돌 때, 데이터를 몇 번 볼 것인가를 정해준다. [트레이닝데이터수/배치사이즈]를 사용하면 되는듯하다. validation_steps는 한 번 epoch 돌 고난 후, validation set을 통해 validation accuracy를 측정할 때 validation set을 몇 번 볼 것인지를 정해준다. 이 때도 마찬가지로 [validation data수/배치사이즈]를 사용하면 된다. 즉 ImageDataGenerator를 쓴 경우, fit_generator를 사용하면 된다고 기억하면 된다. 폴더 구조는 아래와 같이 하면 된다. flow_from_directory에 넣어준 경로(dataset/training_set) 밑에 이런식으로 class(cats, dogs) 별로 폴더를 만들고 폴더 밑에 이미지들을 넣어준다. 그러면 알아서 labeling을 하게 된다.





4. 테스트셋 예측하기


output = classifier.predict_generator(test_set, steps=5)
print(test_set.class_indices)
print(output)
{'cats': 0, 'dogs': 1}
[[ 0.58327454]
 [ 0.08756679]
 [ 0.44407782]
 [ 0.66460747]
 [ 0.38810217]
 [ 0.52190965]
 [ 0.67696238]
 [ 0.67470866]
 [ 0.77020335]
 [ 0.68121213]
 [ 0.66256768]
 [ 0.82018822]
...


  • 이민호 2018.02.26 01:37

    안녕하세요 keras를 공부하고 있는 학생입니다. 블로그 정말 잘보고있습니다.
    제가 질문할게 있어서 댓글을 남깁니다.
    데이터셋 예측하기에서 cats 0 dogs 1 이렇게 되어있는데 이 의미가 무엇이며 밑에 퍼센트는 무엇을 의미하는건가요?

    • Deepplay 2018.02.26 15:16 신고

      안녕하세요. 이진분류 문제에서는 보통 0을 reference로 하여 1의 확률을 예측하게 됩니다. cats:0, dogs:1의 의미는 cat을 reference로 하여, 사진이 dog일 확률을 예측하겠다는 것입니다 그 아래 확률들은 test set의 각 이미지가 dog일 확률을 뜻합니다. test_set.filenames[0:10]을 통해 test set으로 사용된 파일들 이름들을 볼 수가 있습니다.

  • dory 2018.04.23 11:17

    안녕하세요
    작성해주신 코드를 돌려보면 epoch가 계속 도는데
    나오는 정보에서 acc가 정확도인거죠? 정확도가 계속 10프로언저리네요ㅠㅠ

    • Deepplay 2018.04.23 19:08 신고

      안녕하세요. 위에서 구현한 CNN은 예제를 위해 임의로 구현한 네트워크라 performance는 저도 측정해보지는 않았습니다..ㅠ
      CNN의 경우 네트워크 구조나 optimizing 방법에 performance가 의존적이기 때문에 네트워크 구조를 변경(ex. layer를 추가)하거나 optimizer를 변경해보시기 바랍니다.

    • dory 2018.04.24 12:54

      네 변경했습니다.

      추가 질문 드려도되나요?
      테스트셋으로 예측 결과를 출력해보니
      출력할때마다 무작위로 출력이됩니다.
      파일명을 출력해도 파일명은 순서대로나와서 예츨 결과랑 안맞구요 ㅠ
      예측 결과로 나온거 파일명이랑 맞춰보려면 어떻게 해야하나요?

  • 써얼 2019.03.25 14:30

    안녕하세요
    항상 유용한 포스트 감사합니다.
    궁금한점이있는데 코드 창보면 실제 코드 프로그램 처리되어있는데 어떻게 그럴 수 있나요
    그렇게 만들어주는 프로그램이 있나요 ??

    답변주시면 감사하겠습니다

    • Deepplay 2019.03.25 17:41 신고

      안녕하세요. 본 포스팅에서 code highlighting 은 이 사이트 (https://tohtml.com/python) 이용하였습니다.

  • 딥러닝 열공 2019.06.11 15:32

    안녕하세요.
    항상 유용한 포스트 감사합니다.
    다름이 아니라 궁금한 사항이 있어서 질문드립니다.
    현재 제가 가지고 있는 데이터는 시계열 데이터를 가지고 있습니다.
    시계열 데이터 -> 이미지 데이터 로 변환하여 전이학습(VGG16)을 진행해보고 싶습니다.
    시계열 데이터는 21(변수종류) x 24(시간) X 1
    의 Input size를 가지는데 이를 전이학습을 진행하라면 224x224x3 으로 확대 해야하는데
    ImageDatagenerator 함수를 쓰려고 하니 이함수는 이미지 데이터만 input으로 받아서 확대 할수 있는것 같더군요....
    어떻게 시작을 해야할지 막막합니다. 혹시 아시는 내용있으면 답변 부탁드립니다. 감사합니다.

    • Deepplay 2019.06.13 03:00 신고

      안녕하세요. ImageDataGenerator 은 본 포스팅 예제처럼 반드시 이미지 파일 형태의 데이터만 인풋으로 받진 않습니다. fit과 flow 를 통해 이미지 형태를 갖는 텐서라면 증강할 수 있습니다.

      만약 데이터 증강이 필요하지 않고, VGG16 인풋 형태로 resizing 만 하기 위함이라면 굳이 ImageDataGenerator 를 활용하지 않으셔도 되고 resizing 관련 라이브러리를 활용하시면 됩니다. 예를 들어 from skimage.transform import resize 를 이용해 할 수 있을 것 같습니다. 그리고 이후에는 fit_generator 가 아닌 그냥 fit 을 통해 학습하시면 됩니다.

  • 딥러닝! 2019.08.13 12:58

    안녕하세요
    좋은글 감사합니다
    맨끝에 테스트셋 예측하기 부분에서 소스가 이런데
    output = classifier.predict_generator(test_set, steps=5)
    print(test_set.class_indices)
    print(output)

    test_set 디렉토리 안에는 cat, dog폴더 안에 각각 사진이 들어져있는데
    공유하신 소스로 학습을 시켜서 돌려보면
    1.xxx 부터 9.xxx까지 결과값이 나옵니다
    그러면 고양이 부분은 어떻게 결과값이 나오나요?

    • 딥러닝! 2019.08.13 19:23

      {'cats': 0, 'dogs': 1}
      [[9.9994314e-01]
      [9.9992454e-01]
      [1.0000000e+00]
      [9.9999994e-01]
      [2.3841858e-07]
      [9.9998641e-01]
      [7.9116225e-04]
      [1.0000000e+00]
      [3.1081839e-08]
      [3.9390372e-07]
      [1.0000000e+00]
      [7.9116225e-04]
      [9.9998641e-01]
      [1.0000000e+00]
      [2.9802322e-08]
      [9.9994314e-01]
      [9.9999994e-01]
      [4.1723251e-07]
      [9.9992454e-01]
      [1.3033484e-07]
      [9.9994314e-01]
      [1.0000000e+00]
      [1.0000000e+00]
      [7.9116225e-04]
      [2.9802322e-08]
      [9.9999994e-01]
      [4.1723251e-07]
      [9.9998641e-01]
      [9.9992454e-01]
      [1.3033484e-07]
      [1.0000000e+00]
      [9.9994314e-01]
      [9.9999994e-01]
      [2.9802322e-08]
      [4.1723251e-07]
      [2.3841858e-07]
      [9.9998641e-01]
      [9.9992454e-01]
      [1.0000000e+00]
      [7.9116283e-04]
      [7.9116225e-04]
      [1.0000000e+00]
      [9.9994314e-01]
      [9.9999994e-01]
      [2.3841858e-07]
      [2.9802322e-08]
      [9.9992454e-01]
      [9.9998641e-01]
      [3.9390372e-07]
      [1.0000000e+00]]

      test_set폴더 안에는 cats폴더와 dogs폴더가 있습니다. 그리고 각각폴더안에 해당하는 사진이 5장씩 넣어서 돌려봐도 않나오는데 해결법 알수있을까요?