Hard skills/Keras (12)

Early Stopping 이란 무엇인가? 


딥러닝을 비롯한 머신러닝 모델의 한 가지 중요한 딜레마는 다음과 같다. 


너무 많은 Epoch 은 overfitting 을 일으킨다. 하지만 너무 적은 Epoch 은 underfitting 을 일으킨다. 


이런 상황에서 Epoch 을 어떻게 설정해야하는가? 

Epoch 을 정하는데 많이 사용되는 Early stopping 은 무조건 Epoch 을 많이 돌린 후, 특정 시점에서 멈추는 것이다. 


그 특정시점을 어떻게 정하느냐가 Early stopping 의 핵심이라고 할 수 있다. 일반적으로 hold-out validation set 에서의 성능이 더이상 증가하지 않을 때 학습을 중지시키게 된다. 본 포스팅에서는 Keras 를 이용하여 Early stopping 을 구현하는 법과 성능이 더 이상 증가하지 않는다는 것은 어떤 기준으로 정하는 것인지를 중점으로 정리해보고자 한다.  


Early Stopping in Keras


Keras 의 Early stopping 을 구현하는 Early stopping 함수를 통해 구현할 수 있다. 


from keras.callbacks import EarlyStopping


Earlystopping 클래스의 구성 요소

  • Performance measure: 어떤 성능을 monitoring 할 것인가?
  • Trigger: 언제 training 을 멈출 것인가?

Earlystopping 객체는 초기화될 때 두개의 요소를 정의하게 된다. 


아래와 같이 지정하면 validation set 의 loss 를 monitoring 한다는 뜻이다. 


es = EarlyStopping(monitor='val_loss')


만약 performance measure가 최소화 시켜야하는 것이면 mode를 min 으로, 최대화 시켜야하는 것이면 mode를 max로 지정한다. loss 의 경우, 최소화 시키는 방향으로 training 이 진행되므로 min 을 지정한다. 


es = EarlyStopping(monitor='val_loss', mode='min')


mode 의 default 는 auto 인데, 이는 keras 에서 알아서 min, max 를 선택하게 된다. 여기까지가 가장 기본적인 Early stopping 의 사용법이다. performance measure를 정의하고, 이것을 최대화 할지, 최소화 할지를 지정하는 것이다. 그러면 keras 에서 알아서 적절한 epoch 에서 training 을 멈춘다. verbose=1 로 지정하면, 언제 keras 에서 training 을 멈추었는지를 화면에 출력할 수 있다. 


성능이 증가하지 않는다고, 그 순간 바로 멈추는 것은 효과적이지않을 수 있다. patience 는 성능이 증가하지 않는 epoch 을 몇 번이나 허용할 것인가를 정의한다. partience 는 다소 주관적인 기준이다. 사용한 데이터와 모델의 설계에 따라 최적의 값이 바뀔 수 있다. 


es = EarlyStopping(monitor='val_loss', mode='min', verbose=1, patience=50)


만약 performance measure 를 practical 하게 설정한 경우 성능의 증가의 기준을 직접 정의할 수 있다. 예를 들어 아래 코드는 validation accuracy 가 1% 증가하지 않는 경우, 성능의 증가가 없다고 정의한다. 


특정값에 도달했을 때, 더 이상 training 이 필요하지 않은 경우가 있다. 이 경우 baseline 파라미터를 통해 정의할 수 있다. 


es = EarlyStopping(monitor='val_loss', mode='min', baseline=0.4)


최종적으로 mode.fit 함수의 callback 으로 early stopping 객체를 넣어주면 early stopping 을 적용할 수 있다. 


hist = model.fit(train_x, train_y, nb_epoch=10,  

                 batch_size=10, verbose=2, validation_split=0.2,   

                 callbacks=[early_stopping])  


Model Choice


Early stopping 객체에 의해 트레이닝이 중지되었을 때, 그 상태는 이전 모델에 비해 일반적으로 validation error 가 높은 상태일 것이다. 따라서, Earlystopping 을 하는 것은 특정 시점에 모델의 트레이닝을 멈춤으로써, 모델의 validation error 가 더 이상 낮아지지 않도록 조절할 수는 있겠지만, 중지된 상태가 최고의 모델은 아닐 것이다. 따라서 가장 validation performance 가 좋은 모델을 저장하는 것이 필요한데, keras 에서는 이를 위해 ModelCheckpoint 라고 하는 객체를 존재한다. 이 객체는 validation error 를 모니터링하면서, 이전 epoch 에 비해 validation performance 가 좋은 경우, 무조건 이 때의 parameter 들을 저장한다. 이를 통해 트레이닝이 중지되었을 때, 가장 validation performance 가 높았던 모델을 반환할 수 있다. 


from keras.callbacks import ModelCheckpoint


mc = ModelCheckpoint('best_model.h5', monitor='val_loss', mode='min', save_best_only=True)


위 ModelCheckpoint instance를 callbacks 파라미터에 넣어줌으로써, 가장 validation performance 가 좋았던 모델을 저장할 수 있게된다.


hist = model.fit(train_x, train_y, nb_epoch=10,  

                 batch_size=10, verbose=2, validation_split=0.2,   

                 callbacks=[early_stopping, mc])  

참고

https://machinelearningmastery.com/how-to-stop-training-deep-neural-networks-at-the-right-time-using-early-stopping/

https://machinelearningmastery.com/early-stopping-to-avoid-overtraining-neural-network-models/

  • song 2021.02.17 00:15

    이해하기 쉬운 설명 감사합니다! 잘 배워갑니다ㅎㅎ


Keras 멀티 GPU 이용하기


keras 2.0.9 버전부터 멀티 GPU를 손쉽게 활용할 수 있게 업데이트되었습니다. 이번 포스팅은 기존 모델을 멀티 GPU를 사용하여 트레이닝하는 예제입니다.


1. keras 2.0.9 이상에서 지원하므로 2.0.9 이상으로 업데이트

pip install --upgrade keras

2. keras 모델 생성


일반적인 keras CNN을 예로 들어 설명하겠습니다. 대략 이런 cnn model이 있다고 합시다. (패키지 임포트문 생략)

def cnn_model(): # create model model = Sequential() model.add(Convolution2D(16, 3, 3, border_mode='same', activation='relu', input_shape=(256,256,1))) model.add(BatchNormalization()) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Convolution2D(32, 3, 3, border_mode='same', activation='relu')) model.add(BatchNormalization()) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Convolution2D(32, 3, 3, border_mode='same', activation='relu')) model.add(BatchNormalization()) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Convolution2D(64, 3, 3, border_mode='same', activation='relu')) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Convolution2D(64, 3, 3, border_mode='same', activation='relu')) model.add(BatchNormalization()) model.add(MaxPooling2D(pool_size=(2, 2))) model.add(Flatten()) model.add(Dense(64, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(64, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(3, activation='softmax')) return model


2.0.9 버전 이후부터 제공하는 multi_gpu_model을 임포트

from keras.utils.training_utils import multi_gpu_model

모델을 만들고 multi_gpu_model을 통해서 multi gpu를 활용한다는 선언을 합니다. gpus 파라미터에 사용하고 싶은 gpu 수를 설정합니다. 모델을 compile하고 fit하면 트레이닝이 됩니다.

model = cnn_model()
model = multi_gpu_model(model, gpus=4)
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
model.fit(train_x, train_y, validation_data=(test_x, test_y), nb_epoch=20, batch_size=32, verbose=1)

트레이닝이 잘됩니다.

Train on 215 samples, validate on 107 samples
Epoch 1/20
215/215 [==============================] - 1s - loss: 2.0448 - acc: 0.4465 - val_loss: 1.0884 - val_acc: 0.2710
Epoch 2/20
215/215 [==============================] - 0s - loss: 1.5974 - acc: 0.4791 - val_loss: 1.1431 - val_acc: 0.1963
Epoch 3/20
215/215 [==============================] - 0s - loss: 1.2704 - acc: 0.5302 - val_loss: 1.2524 - val_acc: 0.4486
Epoch 4/20
215/215 [==============================] - 0s - loss: 1.1777 - acc: 0.4512 - val_loss: 1.3994 - val_acc: 0.5327
Epoch 5/20
215/215 [==============================] - 0s - loss: 1.0600 - acc: 0.5953 - val_loss: 1.4960 - val_acc: 0.5888
Epoch 6/20
215/215 [==============================] - 0s - loss: 1.0486 - acc: 0.6512 - val_loss: 1.5189 - val_acc: 0.5888
Epoch 7/20
215/215 [==============================] - 0s - loss: 0.9968 - acc: 0.6186 - val_loss: 1.5151 - val_acc: 0.5888
Epoch 8/20
215/215 [==============================] - 0s - loss: 1.0375 - acc: 0.6000 - val_loss: 1.4286 - val_acc: 0.5888
Epoch 9/20
215/215 [==============================] - 0s - loss: 0.9987 - acc: 0.6093 - val_loss: 1.3102 - val_acc: 0.5701
Epoch 10/20


서버에서 nvidia-smi를 쳐보면 이렇게 모든 gpu가 모두 활용되는 것을 볼 수 있습니다.




참고


https://www.pyimagesearch.com/2017/10/30/how-to-multi-gpu-training-with-keras-python-and-deep-learning/

  • 나그네 2019.07.04 17:11

    너무나도 감사합니다. 궁금증이 쉽게 해결됐어요 ㅠㅠ.

  • 나그네2 2019.08.02 09:35

    backend가 뭔지 궁금합니다. 언급이 없으신거 보면 왠만하면 tensorflow 같긴한데 혹시나 해서요.

    • Deepplay 2019.08.02 16:15 신고

      안녕하세요 backend는 tensorflow 입니다 :)

Keras- Tensorflow Backend에서 특정 디바이스 사용법


Keras에 tensorflow backend를 사용할 때, 기본적으로 gpu를 사용하여 돌아갑니다. gpu가 있는데 cpu를 사용할 필요는 없겠지만, gpu의 성능을 측정하거나 하는 목적을 위해 강제로 cpu를 사용하도록 하는 방법에 대해 알아보겠습니다.


keras + tensorflow에서 사용가능한 하드웨어 자원보기


아래와 같이 tensorflow의 device_lib이라는 함수를 통해 현재 사용가능한 하드웨어 디바이스들의 리스트를 볼 수 있습니다. 여기서 name을 통해 해당 하드웨어에 접근할 수 있습니다. 제가 사용하는 환경의 경우 1080ti 4개를 사용하는데, 이 디바이스들의 리스트가 나오는 것을 볼 수 있습니다.

from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())
[name: "/cpu:0"
device_type: "CPU"
memory_limit: 268435456
locality {
}
incarnation: ....
, name: "/gpu:0"
device_type: "GPU"
memory_limit: 295043072
locality {
  bus_id: 1
}
incarnation: .....
physical_device_desc: "device: 0, name: GeForce GTX 1080 Ti, pci bus id: 0000:02:00.0"
, name: "/gpu:1"
device_type: "GPU"
memory_limit: 366346240
locality {
  bus_id: 1
}
...

특정 device 사용하여 모델 구축, 트레이닝하기


아래와 같이 keras.backend.tensorflow_backend를 K 라는 이름으로 불러오고 with 문을 통해 해당 환경에서 모델을 구축후, fitting을 하면 해당 디바이스 환경에서 모델을 트레이닝 할 수 있습니다. 주의할 점은 model = Sequential()을 통해 모델을 생성할 때부터 with문을 통해 해당 device를 사용한다는 것을 알려줘야 한다는 것입니다. 단순히 model.fit 만 with 아래에서 돌리면 with 문이 먹히지 않더군요..

import keras.backend.tensorflow_backend as K
with K.tf.device('/gpu:0'):
    model = Sequential()
    model.add(Dense(512, input_dim=28*28, activation='relu'))
    model.add(Dense(256, activation='relu'))
    model.add(Dense(128, activation='relu'))
    model.add(Dense(32, activation='relu'))
    model.add(Dense(10, activation='softmax'))

    model.compile(loss='categorical_crossentropy',
                  optimizer='rmsprop',
                  metrics=['accuracy'])
    h = model.fit(X_train, y_train_cat, batch_size=128*4, epochs=10, verbose=1, validation_split=0.3)
Train on 42000 samples, validate on 18000 samples
Epoch 1/10
42000/42000 [==============================] - 0s - loss: 0.5832 - acc: 0.8240 - val_loss: 0.4741 - val_acc: 0.8254
Epoch 2/10
42000/42000 [==============================] - 0s - loss: 0.2057 - acc: 0.9379 - val_loss: 0.1414 - val_acc: 0.9566
Epoch 3/10
42000/42000 [==============================] - 0s - loss: 0.1359 - acc: 0.9585 - val_loss: 0.1454 - val_acc: 0.9548
Epoch 4/10
42000/42000 [==============================] - 0s - loss: 0.0919 - acc: 0.9717 - val_loss: 0.0964 - val_acc: 0.9716
Epoch 5/10

gpu를 사용하면 아래와 같이 한 epoch 당 1초도 안되어서 트레이닝이되는 것을 볼 수 있습니다. 이번엔 cpu로 한 번 모델을 구축해보겠습니다. 아래와 같이 device 이름을 /cpu:0으로 변경한 후 모델을 구축, fitting을 하면, 한 epoch 당 2초가 걸리는 것을 확인할 수 있습니다.

import keras.backend.tensorflow_backend as K
with K.tf.device('/cpu:0'):
    model = Sequential()
    model.add(Dense(512, input_dim=28*28, activation='relu'))
    model.add(Dense(256, activation='relu'))
    model.add(Dense(128, activation='relu'))
    model.add(Dense(32, activation='relu'))
    model.add(Dense(10, activation='softmax'))

    model.compile(loss='categorical_crossentropy',
                  optimizer='rmsprop',
                  metrics=['accuracy'])
    h = model.fit(X_train, y_train_cat, batch_size=128*4, epochs=10, verbose=1, validation_split=0.3)
Train on 42000 samples, validate on 18000 samples
Epoch 1/10
42000/42000 [==============================] - 2s - loss: 0.6009 - acc: 0.8115 - val_loss: 0.3323 - val_acc: 0.8966
Epoch 2/10
42000/42000 [==============================] - 2s - loss: 0.2008 - acc: 0.9404 - val_loss: 0.3604 - val_acc: 0.8832
Epoch 3/10
42000/42000 [==============================] - 2s - loss: 0.1293 - acc: 0.9605 - val_loss: 0.1384 - val_acc: 0.9578
Epoch 4/10
42000/42000 [==============================] - 2s - loss: 0.0914 - acc: 0.9716 - val_loss: 0.1376 - val_acc: 0.9590
Epoch 5/10
42000/42000 [==============================] - 2s - loss: 0.0676 - acc: 0.9796 - val_loss: 0.1145 - val_acc: 0.9674
Epoch 6/10
42000/42000 [==============================] - 2s - loss: 0.0494 - acc: 0.9842 - val_loss: 0.0903 - val_acc: 0.9742


  • 부럽다 2020.02.09 10:07

    1080샤 4개요? 저도 잠깐...

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장씩 넣어서 돌려봐도 않나오는데 해결법 알수있을까요?


Keras를 통한 LSTM 구현


/* 2017.8.29 by. 3months */


- LSTM에 대한 개념 학습

http://blog.naver.com/kmkim1222/221069000196

https://www.youtube.com/watch?v=6niqTuYFZLQ&list=PL3FW7Lu3i5JvHM8ljYj-zLfQRF3EO8sYv


- 코드 및 데이터 출처  -https://github.com/Dataweekends/zero_to_deep_learning_udemy


LSTM 개념


LSTM은 기존 vanilla RNN을 개선한 모델로써 데이터의 long-term dependency를 학습하는데 효과적인 모델입니다. 또한 내부적으로 구성요소간에 additive, elementwise interaction을 통해 backpropagation 할 때, gradient explosion, vanishing 문제를 기존 RNN에 비해 매우 줄인 것이 중요한 개선점입니다.


코드 실습을 통해 LSTM을 학습하기 위해서는 어떠한 데이터 구조가 필요한지, 즉, INPUT은 어떤 모양이어야 하고, OUTPUT이 어떻게 생성되며, 네트워크 구조를 코드 상으로 어떻게 정의하는지 알아보겠습니다.


사용할 데이터


cansim-0800020-eng-6674700030567901031.csv


데이터 임포트


데이터를 임포트합니다. 데이터는 날짜별로 특정한 값이 있는 데이터입니다. Unadjusted이 Raw 값이고, Seaonally adjusted는 Season의 효과를 보정한 값입니다. 보통 주식과 같은 시계열 데이터는 Season에 의한 효과가 있는데, Seasonally adjusted는 이 Season에 의한 효과를 보정하여 전체적인 추세만 보고자할 때 사용하는 변수입니다. 우리가 학습할 변수는 Season에 의한 효과를 보정하지 않은 Unadjusted입니다.

import pandas as pd
import numpy as np
%matplotlib inline
import matplotlib.pyplot as plt

df = pd.read_csv('data/cansim-0800020-eng-6674700030567901031.csv',
                 skiprows=6, skipfooter=9,
                 engine='python')
df.head()

Adjustments Unadjusted Seasonally adjusted
0 Jan-1991 12588862 15026890
1 Feb-1991 12154321 15304585
2 Mar-1991 14337072 15413591
3 Apr-1991 15108570 15293409
4 May-1991 17225734 15676083



전처리

from pandas.tseries.offsets import MonthEnd df['Adjustments'] = pd.to_datetime(df['Adjustments']) + MonthEnd(1) df = df.set_index('Adjustments') print(df.head()) df.plot()

우선 문자열로된 Adjustments 변수를 datetime 객체로 만들어주고, MonthEnd(1)을 더하면 해당 월의 맨 끝 날짜가 지정되게 됩니다. 그리고 이 날짜를 index로 만들어줍니다. 이후에 plot을 하면 날짜를 index로 해서 Unadjusted와 Seasonally adjusted 변수의 그래프를 그릴 수 있습니다.



Unadjusted Seasonally adjusted
Adjustments

1991-01-31 12588862 15026890
1991-02-28 12154321 15304585
1991-03-31 14337072 15413591
1991-04-30 15108570 15293409
1991-05-31 17225734 15676083


인덱스가 0,1,2,3 ... 에서 날짜로 변했음을 알 수 있습니다. (가장 왼쪽열. 또한 index의 이름이 Adjustements가 되었음을 알 수 있음.)



우리가 학습할 변수는 파란색 선 Unadjusted 입니다.



트레이닝셋 테스트셋 SPLIT


2011/1/1 을 기준으로 트레이닝셋과 테스트셋으로 구분합니다. 그리고 학습과 테스트에 사용할 Unadjusted 변수만 남겨놓습니다.

split_date = pd.Timestamp('01-01-2011')
# 2011/1/1 까지의 데이터를 트레이닝셋.
# 그 이후 데이터를 테스트셋으로 한다.

train = df.loc[:split_date, ['Unadjusted']]
test = df.loc[split_date:, ['Unadjusted']]
# Feature는 Unadjusted 한 개

ax = train.plot()
test.plot(ax=ax)
plt.legend(['train', 'test'])



변수 Scaling


MinMax Scaling을 통해 변수를 Scaling합니다. MinMax Scaling : http://3months.tistory.com/167

그러면 아래와 같이 Scaling 된 것을 볼 수 있고, scaling의 결과는 2차원의 numpy ndarray 타입으로 변환이 되게 됩니다.

from sklearn.preprocessing import MinMaxScaler

sc = MinMaxScaler()

train_sc = sc.fit_transform(train)
test_sc = sc.transform(test)

train_sc
(240, 1)
[[ 0.01402033]
 [ 0.        ]
 [ 0.0704258 ]
 [ 0.09531795]
 [ 0.16362761]
 [ 0.13514108]
 [ 0.12395846]
 [ 0.12617398] 
......


Pandas Dataframe으로 변환


이를 다시 pandas dataframe 데이터 타입으로 변환해봅시다. Dataframe에서 작업을 해야지 Window를 만들기 유용하기 때문입니다. Window는 RNN을 트레이닝 하기위한 단위로 Window size는 Timestep과 같다고 생각하시면 됩니다.

train_sc_df = pd.DataFrame(train_sc, columns=['Scaled'], index=train.index)
test_sc_df = pd.DataFrame(test_sc, columns=['Scaled'], index=test.index)
train_sc_df.head()



Scaled
Adjustments
1991-01-31 0.014020
1991-02-28 0.000000
1991-03-31 0.070426
1991-04-30 0.095318
1991-05-31 0.163628



pandas shift를 통해 Window 만들기


shift는 이전 정보 다음 row에서 다시 쓰기 위한 pandas의 함수입니다. 이를 통해 아래와 같이 과거의 값들을 shift_s 와 같은 형태로 저장할 수 있습니다. 과거값은 총 12개를 저장하며, timestep은 12개가 됩니다. 우리의 목적은 과거값 shift1~12를 통해 현재값 Scaled를 예측하는 것입니다.

for s in range(1, 13):
    train_sc_df['shift_{}'.format(s)] = train_sc_df['Scaled'].shift(s)
    test_sc_df['shift_{}'.format(s)] = test_sc_df['Scaled'].shift(s)

train_sc_df.head(13)



Scaled shift_1 shift_2 shift_3 shift_4 shift_5 shift_6 shift_7 shift_8 shift_9 shift_10 shift_11 shift_12
Adjustments












1991-01-31 0.014020 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1991-02-28 0.000000 0.014020 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1991-03-31 0.070426 0.000000 0.014020 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1991-04-30 0.095318 0.070426 0.000000 0.014020 NaN NaN NaN NaN NaN NaN NaN NaN NaN
1991-05-31 0.163628 0.095318 0.070426 0.000000 0.014020 NaN NaN NaN NaN NaN NaN NaN NaN
1991-06-30 0.135141 0.163628 0.095318 0.070426 0.000000 0.014020 NaN NaN NaN NaN NaN NaN NaN
1991-07-31 0.123958 0.135141 0.163628 0.095318 0.070426 0.000000 0.014020 NaN NaN NaN NaN NaN NaN
1991-08-31 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426 0.000000 0.014020 NaN NaN NaN NaN NaN
1991-09-30 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426 0.000000 0.014020 NaN NaN NaN NaN
1991-10-31 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426 0.000000 0.014020 NaN NaN NaN
1991-11-30 0.131738 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426 0.000000 0.014020 NaN NaN
1991-12-31 0.200913 0.131738 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426 0.000000 0.01402 NaN
1992-01-31 0.030027 0.200913 0.131738 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426 0.00000 0.01402



트레이닝셋과 테스트셋 만들기


dropna를 통해 NaN이 있는 데이터를 제거하고, shift_1 ~ shift_12는 X로 Scaled는 Y로 지정을 합니다.

X_train = train_sc_df.dropna().drop('Scaled', axis=1)
y_train = train_sc_df.dropna()[['Scaled']]

X_test = test_sc_df.dropna().drop('Scaled', axis=1)
y_test = test_sc_df.dropna()[['Scaled']]


최종 트레이닝셋과 테스트셋의 모양은 아래와 같습니다.


X_train.head()



shift_1 shift_2 shift_3 shift_4 shift_5 shift_6 shift_7 shift_8 shift_9 shift_10 shift_11 shift_12
Adjustments











1992-01-31 0.200913 0.131738 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426 0.000000 0.014020
1992-02-29 0.030027 0.200913 0.131738 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426 0.000000
1992-03-31 0.019993 0.030027 0.200913 0.131738 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318 0.070426
1992-04-30 0.065964 0.019993 0.030027 0.200913 0.131738 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628 0.095318
1992-05-31 0.109831 0.065964 0.019993 0.030027 0.200913 0.131738 0.111395 0.092309 0.126174 0.123958 0.135141 0.163628


y_train.head()



Scaled
Adjustments
1992-01-31 0.030027
1992-02-29 0.019993
1992-03-31 0.065964
1992-04-30 0.109831
1992-05-31 0.149130



다시 ndarray로 변환하기


데이터구조를 다시 ndarray로 변환시켜줍니다. 실제 deep learning 모델의 트레이닝과 테스트로 사용되는 데이터는 일반적으로 numpy의 ndarray입니다. 아래와 같이 .values를 통해 ndarray 값을 얻을 수 있습니다. 트레이닝 데이터는 timestep=12, size = 228 입니다.

X_train = X_train.values
X_test= X_test.values

y_train = y_train.values
y_test = y_test.values
print(X_train.shape)
print(X_train)
print(y_train_shape)
print(y_train)
(228, 12)
[[ 0.20091289  0.13173822  0.11139526 ...,  0.0704258   0.          0.01402033]
 [ 0.03002688  0.20091289  0.13173822 ...,  0.09531795  0.0704258   0.        ]
 [ 0.01999285  0.03002688  0.20091289 ...,  0.16362761  0.09531795
   0.0704258 ]
 ..., 
 [ 0.79916654  0.81439355  0.86398323 ...,  0.92972161  0.71629034
   0.77368724]
 [ 0.80210057  0.79916654  0.81439355 ...,  0.59734863  0.92972161
   0.71629034]
 [ 0.81482896  0.80210057  0.79916654 ...,  0.53166512  0.59734863
   0.92972161]]
(228, 1)
[[ 0.03002688]
 [ 0.01999285]
 [ 0.06596369]
 [ 0.10983126]
 [ 0.14912986]
....


최종 트레이닝셋과 테스트셋의 X 만들기


이부분이 중요한데, keras에서는 RNN 계열의 모델을 트레이닝할 대 요구되는 데이터의 형식이 있습니다. 바로 3차원 데이터여야하며 각각의 차원은 (size, timestep, feature) 을 순서대로 나타내주어야하는 것입니다. 따라서 이 형태로 데이터를 reshape 해주어야합니다. 일반적인 MLP 모델에서는 size와 feature만 있기 때문에 2차원이지만, RNN에서는 "시간" 이라는 개념이 있기 때문에 차원이 한 차원 늘어나게 된 것입니다. 합리적인 데이터 구조라고 볼 수 있습니다.


아래 코드를 통해 Training set과 Test set의 X를 RNN 학습에 맞는 형태로 reshape 해줍니다.

X_train_t = X_train.reshape(X_train.shape[0], 12, 1) X_test_t = X_test.reshape(X_test.shape[0], 12, 1)

print("최종 DATA")
print(X_train_t.shape)
print(X_train_t)
print(y_train)
최종 DATA
(228, 12, 1)
[[[ 0.20091289]
  [ 0.13173822]
  [ 0.11139526]
  ..., 
  [ 0.0704258 ]
  [ 0.        ]
  [ 0.01402033]]

 [[ 0.03002688]
  [ 0.20091289]
  [ 0.13173822]
  ..., 
  [ 0.09531795]
  [ 0.0704258 ]
  [ 0.        ]]


LSTM 모델 만들기


아래와 같이 keras를 통해 LSTM 모델을 만들 수 있습니다. input_shape=(timestep, feature)으로 만들어줍니다. size는 모델 설계시에는 중요하지 않으므로, feature, timestep만 모델에 알려주면 됩니다. 또 예측하고자하는 target의 갯수가 1이므로 마지막에 Dense(1)을 하나 추가해줍니다. 또한 실제 연속적인 값을 예측하는 것이기 때문에 loss function은 mean squared error가 됩니다. 또한 일반적으로 optimizer는 adam을 자주 사용합니다.

from keras.layers import LSTM from keras.models import Sequential from keras.layers import Dense import keras.backend as K from keras.callbacks import EarlyStopping K.clear_session() model = Sequential() # Sequeatial Model model.add(LSTM(20, input_shape=(12, 1))) # (timestep, feature) model.add(Dense(1)) # output = 1 model.compile(loss='mean_squared_error', optimizer='adam') model.summary()


모델 Fitting


모델을 Fitting한다는 것은 Training data set으로 optimization 과정을 통해 모델의 weight를 찾는 것입니다. early stopping 객체를 이용해 epoch마다 early stopping을 체크합니다. 아래와 같은 결과를 내면서 트레이닝이 잘 되는 것을 확인할 수 있습니다.

early_stop = EarlyStopping(monitor='loss', patience=1, verbose=1)

model.fit(X_train_t, y_train, epochs=100,
          batch_size=30, verbose=1, callbacks=[early_stop])
Epoch 1/100
228/228 [==============================] - 0s - loss: 0.3396     
Epoch 2/100
228/228 [==============================] - 0s - loss: 0.2958     
Epoch 3/100
228/228 [==============================] - 0s - loss: 0.2551     
Epoch 4/100
228/228 [==============================] - 0s - loss: 0.2175   
...


학습된 모델을 통해 테스트셋 Test 하기


테스트셋은 2011/1/1 이후의 데이터를 나타냅니다. 트레이닝에는 1991년~ 2010년 까지 데이터가 이용되었기 때문에, 이 실습은 1991년~2010년 데이터를 통해 2011년 이후의 데이터를 예측하는 것으로 볼 수 있습니다.


테스트셋의 모양은 아래와 같이 생겼습니다. 당연한 이야기지만 트레이닝셋과 구조가 같습니다.

print(X_test_t)
[[[ 1.06265011]
  [ 0.87180554]
  [ 0.84048091]
  [ 0.86220767]
  [ 0.88363094]
  [ 0.89302107]
  [ 0.92552046]
  [ 0.89993326]
  [ 0.83505683]
  [ 0.77259579]
  [ 0.56926634]
  [ 0.61423187]]


....


model.predict를 통해 테스트셋의 X에 대한 예측값 y_hat을 얻을 수 있습니다.

y_pred = model.predict(X_test_t)
print(y_pred)
[[ 0.82410765]
 [ 0.85221326]
 [ 0.88795143]
 [ 0.90008551]
 [ 0.90281236]
 [ 0.90063977]
 [ 0.89379823]
 [ 0.88957471]
 [ 0.88779473]

...


  • 이민호 2018.03.28 23:10

    안녕하세요 저번에 질문 드렸던 학생입니다. 제가 모르는 부분이있어서 그러는데 질문좀 할게요 ㅠㅠㅠㅠㅠ 죄송해요

    마지막에 예측값 y_hat까지 얻으셨는데 예측값과 실제 테스트 값과 차이가 얼마나 있고 확인하기 위해서 위에 matplotlib함수를 이용해서 그래프를 나타낸것처럼 확인하는 방법이 있을까요? 하려면 데이터 전처리 한 부분을 다시 복구해야하는건가요?

    • Deepplay 2018.03.30 01:49 신고

      예측값 y_hat에 대한 실제 정답값은 y_train 변수에 저장되어있으니 이 두개를 matplotlib를 통해 그래프로 나타내고 비교하시면 될 것 같습니다~ㅎㅎ

  • 이민호 2018.03.30 19:15

    안녕하세요 Deepp님
    y_train인가요? y_test에 실제 정답값이 저장되어있는거 아닌가요?? 그리고 에측값이 y_pred변수에 저장되어있는거 아닌가요?
    제가 해보았는데
    plt.figure()
    plt.plot(y_pred)
    plt.plot(y_test)
    2개의 그래프를 해보았는데 차이가 많이나고 일단 y축의 범위가 다르게 나옵니다.
    혹시 제가 잘못 비교한것은 아닌지 모르겠습니다

    • Deepplay 2018.04.01 04:33 신고

      아 y_test가 맞습니다. 잘못적었네요.

      음 그런경우에는 optimization이 잘 안됐을 수 있어서 우선은 실제 값을 프린트해봐서 비슷하게 나오는지 확인해보면 좋을 것 같습니다. 그리고 maplotlib를 이용해 두 데이터가 하나의 그래프에 겹쳐서 나오도록 비교하시고 그래도 차이가 많이난다면 모델 튜닝을 잘 해보셔야합니다. (epoch을 늘리거나 learning rate를 조정하거나 등) 위 예제는 예제이기 때문에 optimization이 잘 안되었을 수도 있습니다.

    • 지나가다 2020.12.15 23:07

      y_hat값은 MinMax scaling이 적용된 예측값입니다. inverse_transform으로 스케일을 원래대로 돌리셔야 됩니다. 그리고 Early Stop patience를 10 정도로 높이고 epoch을 200쯤 잡고 학습하면 거의 train값에 가깝게 수렴합니다.

  • 절간스님 2018.05.15 17:33

    y_pred = model.predict(X_test_t)
    y_pred = sc.inverse_transform(y_pred)
    y_pred_df = pd.DataFrame(y_pred, columns=['prediction'], index=test.index[:-12])

    y_pred_df.plot(ax=ax)
    plt.show()
    이민호님은 inverse_transform을 생략해서 그런 게 아닐까 싶습니다.
    이렇게 해보니 prediction이 매우 잘됬다고 보기 어려운데
    epoch나 learning rate 말고 다른 방법으로 optimization시키는 방법이 있을까요?

    • Deepplay 2018.10.15 03:46 신고

      안녕하세요. 말씀해주신 방법 이외에도 LSTM 모델 자체의 파라미터를 변경하거나 (cell의 수), optimizer 변경 등을 해볼 수 있습니다.

  • 2018.08.31 20:49

    비밀댓글입니다

    • 2018.10.15 04:00

      비밀댓글입니다

  • 김종화 2018.10.13 18:20

    안녕하세요 예제 따라하는 중간에
    X_train_t = X_train.reshape(X_train.shape[0], 12, 1)

    ---------------------------------------------------------------------------
    ValueError Traceback (most recent call last)
    <ipython-input-24-994d220ffde8> in <module>()
    ----> 1 X_train_t = X_train.reshape(X_train.shape[0], 12, 1)

    ValueError: cannot reshape array of size 357 into shape (119,12,1)
    이런 에러가 뜨는 어떻게 수정해야 할까요?

    • Deepplay 2018.10.15 04:00 신고

      안녕하세요. 앞부분에서 데이터 핸들링이 제대로 되지 않은듯 합니다. X_train이 (119,3)의 모양을 갖고 있어서 이를 (119,12,1)로 바꾸려고하니 numpy array의 element 갯수 차이 때문에 에러가 난 것입니다.

      앞선 데이터 처리 과정을 다시 하시어 X-train의 shape를 (228,12) 모양으로 만든 후, X_train.reshape(X_train.shape[0], 12, 1) 를 실행하시면 (228,12,1)의 모양이 될 듯합니다!

  • 학생 2019.07.22 00:36

    안녕하세요! 글 잘 읽었습니다. 다름이 아니라 데이터셋을 Test와 Train으로 나누는 과정에서 오류가 계속 발생해서 여쭤봅니다.
    저는 원래는 아래와같은 샘플 코드를 실행하려했습니다.

    datas = df['inputs'].value
    labels = df['outputs'].value

    training_size = int(0.8* datas.shape[0])
    training_datas = datas[:training_size,:]
    training_labels = labels[:training_size,:,0]
    validation_datas = datas[training_size:,:]
    validation_labels = labels[training_size:,:,0]

    그런데 csv파일에서 불러온 데이터프레임 형태의 df가 3가지 인덱스로 구성되어있지않아 datas[:training_size,:] 를 할때 계속 오류가 났었습니다.
    혹시 이때 어떻게 해야하는지 혹시 아시나요?

    즉 제 질문은 데이터 프레임을 values함수를 통해 np.array로 만들고 shape함수를 해보면 (1150, 7) 이런식으로 나오는데
    이것을 3차원으로 reshape하는 방법을 조언 주시면 감사하겠습니다!

  • Jun 2019.10.26 00:43

    LSTM관련 서적을 아직 구매하지 않아서 해당 포스팅 덕분에 좋은 공부를 하였습니다.

    하지만 해당 예제 결과는 저같은경우 실제값과 예측값의 차이가 매우 크게 나오는데 다른분들은 어떤신지 궁금하네요

    제 개인적인 판단으로는 EarlyStopping의 patience가 1로 잡혀서

    적은 Epoch 횟수에 학습을 멈추기에 성능이 좋지 못하게 나오는게 아닌가 생각됩니다.

    만약 보다 좋은 성능을 보고싶은분이 계신다면 patience를 3~7 정도 잡고 돌리면 괜찮치 않을까 싶네요

    • 123 2020.09.14 20:49

      저도 예측성능이 좋지 못한거 같아 이것저것 변경해 봤는데 patience가 문제였군요 ! 성능이 바로 좋아졌습니다 ! 감사합니다

  • Hong 2020.04.28 17:13

    안녕하세요 포스팅 감사드립니다. 큰 도움이 되었습니다.
    RNN을 주먹구구식으로 이해하고 있는데 어려움이 많네요..ㅠㅠ 간단한 거 하나만 여쭤보겠습니다.
    LSTM(20,~)으로 모델을 설정하셨는데 여기서 20이 의미하는 바가 궁금합니다.
    DNN의 경우 dense를 이용해 layer를 쌓는 과정에서 dense(20,~)으로 입력하면 각 layer의 node의 수를 의미하는 것으로 알고 있습니다.
    하지만 RNN의 경우에는 node의 수가 뭘 의미하는지 와닿지가 않네요...
    예제를 통해 생각해보면 LSTM(20,~)모형에서는 288개의 obs중 20개를 input으로 해서 예측값을 얻는 거라고 이해해도 될까요?

    • WJ 2020.09.08 16:02

      LSTM(20,~) : 20메모리 쉘을 가진 LSTM 레이어 1개 인걸로 알고 있습니다.

  • Neural net 2020.09.11 10:31

    안녕하세요 LSTM을 공부하다가 궁금한게 생겨서 질문드립니다.

    (다시 ndarray로 변환하기)
    부분을 보면 현재의 값일수록 앞에 있습니다.
    여기 문제를 예로 들면 time step이 12이고
    [t-1. t-2, t-3, ...t-12] 를 통해 [t]를 예측하는 경우입니다.

    하지만 보통 LSTM을 사용할때는 (many-to-one) 과거의 값일수록 앞에 있지 않나요??
    셋팅을 [t-12, t-11, ...., t-1] 로 예측 [t]

    데이터프레임을
    shift_1 shift_2 shift_3 ...shift_12 / Scaled [y] 로 구성하고 해보는건 어떨지요

    • 지나가다^^ 2020.12.27 16:21

      잘못 보셨습니다.
      즉 말씀 주신 대로 잘 되어 있는데 헷갈리신 듯합니다.
      저것 ndarray변환된 것을 보면
      가장 오래된 것이 가장 위에(즉 앞에) 있습니다.
      말씀 대로 잘 변환되어 있습니다.

      그리고 제가 알기에는 말씀주신 것은 데이터 시퀀스 상 LSTM에서 그 규칙을 안 지키면 아예 안되는 규칙입니다.

      아예 안되기에 너무 기초적인 것이라 ㅂ블로그 작성자님이 그런 실수를 하실리는 없구요:)

  • Kim 2020.12.05 17:28

    안녕하세요~공부하다가 궁금한게 생겨서 질문드립니다.

    제가 데이터 파일에서 값을 다른 값을 넣어서 돌려보았는데 아래와 같은 오류가 떴는데 문제가 무엇일까요??ㅠ

    ParserError: day is out of range for month: Jan-00

    • user2 2021.07.07 11:18

      수고많으십니다.
      날짜포맷에 문제가 있는듯보였습니다.
      혹여 편집을 어떤 툴을 이용하셨는지 알수 있을까요?
      제경우에 exel을 사용하여 에러가 났었지만 셀서식을 변경하고
      df = pd.read_csv 구문에서 skiprow도 모두 지웠습니다.
      물론 데이터 위 아래 주석도 모두 지웠구요
      데이터 상에 Jan-00으로 표기가 되어있는지 확인 부탁드립니다. 1900년으로 읽어 생기는 오류같습니다. 표기방식을 변경하거나 데이터를 yyyy-mm-dd 형식으로 표기되게끔 수정하여 현재까지는 에러없습니다.

  • user2 2021.07.07 11:23

    Jason shin 본인이시오? 코드 및 데이터 출처를 밝혔으나 그대로 배껴와 실행만 해보면 무슨 의미가 있습니까?
    당신은 개발자입니까 아니면 앵무새입니까
    당신이 Jason shin이라면 업로드용 코드와 학습용 코드를 구분해야 할 것임
    당신이 코드를 퍼온 것이라면 응당 이해하고 공부한 후에 올리도록 하시오
    당신의 알량한 거짓말이 내 소중한 시간을 낭비했소 당신은 무언가를 배운적도 앞으로도 배울자격도 없는 인간이오

Keras 모델 저장하고 불러오기


/* by 3months. 2017.7.19 */


keras를 통해 MLP, CNN 등의 딥러닝 모델을 만들고, 이를 학습시켜서 모델의 weights를 생성하고 나면 이를 저장하고 싶을 때가 있습니다. 특히 weights 같은 경우는 파일 형태로 저장해놓는 것이 유용한데, 파이썬 커널을 내리는 순간 애써 만든 weights 가 모두 메모리에서 날라가 버리기 때문입니다. keras에서는 모델과 weights의 재사용을 위해 이를 파일형태로 저장하는 라이브러리를 제공하며, 이를 통해 모델과 weights를 파일 형태로 저장하고 불러올 수가 있습니다.


Keras에서 만든 모델을 저장할 때는 다음과 같은 룰을 따릅니다.

  • 모델은 JSON 파일 또는 YAML 파일로 저장한다.
  • Weight는 H5 파일로 저장한다.


아래는 모델을 저장하고 불러오는 실습 코드입니다.



라이브러리 임포트

from keras.models import Sequential
from keras.layers import Dense
from keras.callbacks import ModelCheckpoint
import matplotlib.pyplot as plt
import numpy
import pandas as pd


데이터 로드

url = "https://archive.ics.uci.edu/ml/machine-learning-databases/pima-indians-diabetes/pima-indians-diabetes.data"
names = ['preg', 'plas', 'pres', 'skin', 'test', 'mass', 'pedi', 'age', 'class']

dataframe = pd.read_csv(url, names=names)
array = dataframe.values

X = array[:,0:8]
Y = array[:,8]


모델 생성

# create model
model = Sequential()
model.add(Dense(12, input_dim=8, kernel_initializer='uniform', activation='relu'))
model.add(Dense(8, kernel_initializer='uniform', activation='relu'))
model.add(Dense(1, kernel_initializer='uniform', activation='sigmoid'))

# Compile model
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])


모델을 JSON 파일 형식으로 만들어 저장하기

model_json = model.to_json()
with open("model.json", "w") as json_file : 
    json_file.write(model_json)


Weight를 h5 파일 포맷으로 만들어 저장하기

model.save_weights("model.h5")
print("Saved model to disk")


저장된 JSON 파일로 부터 모델 로드하기

from keras.models import model_from_json json_file = open("model.json", "r") loaded_model_json = json_file.read() json_file.close() loaded_model = model_from_json(loaded_model_json)


로드한 모델에 Weight 로드하기

loaded_model.load_weights("model.h5") print("Loaded model from disk")


모델 컴파일 후 Evaluation

loaded_model.compile(loss="binary_crossentropy", optimizer="rmsprop", metrics=['accuracy'])

# model evaluation
score = loaded_model.evaluate(X,Y,verbose=0)

print("%s : %.2f%%" % (loaded_model.metrics_names[1], score[1]*100))


  • 어른아이 2019.09.09 17:31

    감사합니다!!!

  • 2020.03.24 16:10

    model.save_weights("model.h5")


    여기에서 int로 index를 파일이름에 넣는 방법이 있을까요?

    • f-string 2020.03.30 16:22

      f-string을 이용하셔서 f'model_{index}.h5' 처럼 넣으시면 될것 같습니다.

오늘 모 워크샵에서 강의를 듣던 중 아래 github의 가상환경이 굉장히 잘 되어있어서 공유합니다. zero_to_deep_learning 이라는 외국 강의가 있는데 이곳에서 사용하는 것이라고 합니다. 이외에도 다양한 예제파일이 있는데 매우 유용하네요.


https://github.com/Dataweekends/zero_to_deep_learning_udemy


방법은 해당 깃허브 폴더를 clone한 후, yaml 파일에 정의된 아나콘다 가상환경을 만들고 이 가상환경을 activate 시킨다음에 파이썬을 사용하시면 됩니다.

Keras에서는 Theano Backend와 Tensorflow Backend 둘 중 하나를 골라서 사용할 수 있습니다. 둘 중 어느것을 Backend로 사용하더라도 별 차이는 없습니다. 아주 약간의 차이가 있는데 예를 들어 이미지를 input으로 줄 때 차원값의 순서가 바뀌기도 합니다.


Windows를 사용하는 유저의 경우, Keras를 설치하였다면 사용자 폴더 아래에 .keras 폴더가 있습니다. 이 폴더 아래에 keras.json 파일이 있는데 이것을 수정하시면 Backend를 바꿀 수 있습니다. 물론 Backend도 설치가 되어있어야합니다. 즉 Theano나 Tensorflow를 설치하여야합니다.


파일 경로

C:\Users\(사용자이름)\.keras\keras.json


Theano

{
    "image_dim_ordering": "th",
    "epsilon": 1e-07,
    "floatx": "float32",
    "backend": "theano"
}


Tensorflow

{
    "image_dim_ordering": "channels_last",
    "epsilon": 1e-07,
    "floatx": "float32",
    "backend": "tensorflow"
}


keras.json 파일을 위와같이 수정하면 각각 Theano backend와 tensorflow backend로 구동되어집니다.

  • dkdlzosen 2019.01.20 14:36

    keras.json 파일 열어서 수정하라는 말씀인가요? 어떻게 열죠...?

    • Deepplay 2019.01.21 16:58 신고

      윈도우의 경우
      C:\\Users\\유저이름\\.keras\\keras.json'

      리눅스의 경우
      /사용자홈폴더/.keras\\keras.json'

      에 위치해 있고, json 파일은 메모장 같은 텍스트 에디터를 이용해서 여시면 됩니다. :)

/**

날짜 : 2017.1.22

작성자 : 3개월

제목 : MNIST 데이터로 CNN Training 해보기

*/



1. 패키지 로드 & 데이터 읽기


""" Simple Convolutional Neural Network for MNIST """
import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Flatten
from keras.layers.convolutional import Convolution2D
from keras.layers.convolutional import MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
K.set_image_dim_ordering('th')
import cPickle, gzip, numpy

# Load the dataset
f = gzip.open('mnist.pkl.gz', 'rb')
train_set, valid_set, test_set = cPickle.load(f)
f.close()


필요한 패키지들을 불러오고 데이터들을 불러옵니다.

mnist.pkl.gz는 

https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/data/mnist.pkl.gz 

에서 다운로드 후 코드와 같은 폴더에 붙여 넣으시면 됩니다.


# train_set 은 튜플로 두 개의 ndsarray를 원소로 가지고 있음
print train_set
print type(train_set)
print len(train_set)

# 튜플을 이렇게 둘로 나눌 수 있음
X_train, y_train = train_set
X_test, y_test = test_set

print type(X_train)
print len(X_train)
print len(y_train)
print X_train
print y_train


이제 MNIST 데이터를 읽어들였고, training set과 test set이 준비 완료되었습니다. 하지만 cnn에서는 x vector의 형태를 알맞게 고치는 preprocessing 작업이 필요합니다.  


2. 데이터 preprocessing


""" In Keras, the layers used for two-dimensional convolutions expect pixel values with the dimensions [pixels][width][height]. """
X_train = X_train.reshape(X_train.shape[0], 1, 28, 28).astype('float32') X_test = X_test.reshape(X_test.shape[0], 1, 28, 28).astype('float32') # normalize inputs from 0-255 to 0-1 X_train = X_train / 255 X_test = X_test / 255 # one hot encode outputs y_train = np_utils.to_categorical(y_train) y_test = np_utils.to_categorical(y_test) num_classes = y_test.shape[1] # fix random seed for reproducibility seed = 7 numpy.random.seed(seed)


위와 같이 numpy의 reshape함수를 이용하여 X vector를 [pixels][width][height] 형태로 바꿔줍니다. (이 때 X_train.shape = (50000L, 784L) 라서 X_train.shape[0] = 50000 입니다.)


  • keras에서 cnn을 구현할 때 [pixels][width][height] 형태로 input을 받습니다. (Theano Backend의 경우)

piexels는 흑백이므로 1을 입력하고 MNIST데이터는 28x28 손글씨 이미지이므로 width = 28, height = 28을 입력합니다. 만약 컬러라면 3을 넣어야합니다. 이제 모든 training 데이터와 test 데이터의 준비는 끝났습니다.


3. CNN 모델 


def cnn_model():
    # create model
    model = Sequential()
    model.add(Convolution2D(32, 5, 5, border_mode='valid', input_shape=(1, 28, 28), activation='relu'))
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.2))
    model.add(Flatten())
    model.add(Dense(127, activation='relu'))
    model.add(Dense(num_classes, activation='softmax'))
    # Compile model
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model


CNN 모델을 구축하여보겠습니다. 

위 모델은 Convolution Layer-ReLU-Pooling Layer-(Dropout)-Fully Connected Network로 이어지는 구조이며 기본적인 CNN의 구조입니다.


  • Convolution2D(32,5,5...)에서 32는 feature map의 갯수라 할 수 있고 5,5는 5x5 필터 사이즈를 의미합니다. 
  • border_mode는 zero-padding에 관한 세팅입니다. valid는 valid convolution을 의미하여 padding을 주지 않습니다. 만약 same인 경우 same convolution이며 zero-padding을 넣어 convolution 결과의 사이즈과 인풋과 같게 합니다.


4. Training & Test


# build the model
model = cnn_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), nb_epoch=10, batch_size=200, verbose=2)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))



이제 CNN을 트레이닝해보겠습니다. validation set은 test set으로 대체하였고, epoch=10, batch_size=200으로 하였습니다. verbose는 로깅에 관한 세팅으로 개념을 이해할 때는 무시하셔도 좋습니다.





error는 validation error을 기준으로 하며 마지막 epoch의 validation error에서 (1-0.9370)*100을 하여 6.3%가 됩니다. 앞서 MLP로 training하였을때는 7% 대에 error를 보였지만 CNN을 통해서 error rate를 낮출 수 있었습니다.






/**

Keras Mnist Learning Using Multi Layer Perceptron

작성자 : 3개월

날짜 : 2017.1.20

코드하이라이터 : http://markup.su/highlighter/

*/


1. 데이터 읽기


import cPickle, gzip, numpy

# Load the dataset
f = gzip.open('mnist.pkl.gz', 'rb')
train_set, valid_set, test_set = cPickle.load(f)
f.close()


mnist.pkl.gz는

https://github.com/mnielsen/neural-networks-and-deep-learning/blob/master/data/mnist.pkl.gz 

에서 다운로드 후 코드와 같은 폴더에 붙여 넣으시면 됩니다.


cPickle.load(f)를 하면 train_set, valid_set, test_set이 알아서 나뉘어집니다.


# train_set 은 튜플로 두 개의 ndsarray를 원소로 가지고 있음
print train_set
print type(train_set)
print len(train_set)

# 튜플을 이렇게 둘로 나눌 수 있음
X_train, y_train = train_set
X_test, y_test = test_set

print type(X_train)
print len(X_train)
print len(y_train)
print X_train
print y_train


train_set을 위와 같이 X_train, y_train 으로 나눌 수 있습니다.

train_set은 2개의 원소(X, Y)를 갖고 있는 '튜플' 인데 튜플인 경우 저러한 문법이 가능합니다.


2. Multi Layer Perceptron 구현


CNN을 활용하기 전에 Multi Layer Perceptrons을 이용하여 데이터셋을 러닝해봅니다. universal approximation theorem에 의해 사실 이 MLP로도 이미지 classification을 포함한 이 세상에 존재하는 모든 문제를 해결할 수 있습니다. (다만 데이터가 부족할뿐) 


# multi-layer perceptron
import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from keras.utils import np_utils


패키지를 임포트합니다.


seed = 7
numpy.random.seed(seed)


계속 실행하더라도 동일한 결과를 갖기 위해 numpy random seed를 설정해줍니다.


print type(X_train)
print type(y_train)


타입은 두 개 모두 ndarray입니다. X_train은 784*50000 2차원 배열, y_train은 길이가 50000인 1차원 배열입니다. 784는 MNIST 데이터가 28*28 =784 픽셀 흑백 손글씨 이미지이기 때문에 나온 숫자이고 이를 일렬로 쭉 늘어놓은 것입니다. 또한 50000은 데이터의 갯수입니다. 


# normalize inputs from 0-255 to 0-1
X_train = X_train / 255
X_test = X_test / 255


흑백 이미지 데이터는 픽셀 하나당 0-255까지의 숫자값을 가지므로 이를 255로 나누면 0-1 사이로 normalize됩니다.


# one hot encode outputs
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)

# y_test 의 column을 클래스의 갯수 지정 : 10개
num_classes = y_test.shape[1]
num_pixels = X_train.shape[1]


위와 같이 y 값들을 one hot encoding 합니다.

또 아래 MLP 모델을 구축할 때 쓰는 변수들 num_classes, num_pixels를 정의합니다.


# define baseline model
def baseline_model():
    # create model
    model = Sequential()
    model.add(Dense(num_pixels, input_dim=num_pixels, init='normal', activation='relu'))
    model.add(Dense(num_classes, init='normal', activation='softmax'))
    # Compile model
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model

def good_model():
    # create model
    model = Sequential()
    model.add(Dense(400, input_dim=num_pixels, init='normal', activation='relu'))
    model.add(Dense(100, init='normal', activation='relu'))
    model.add(Dense(num_classes, init='normal', activation='softmax'))
    # Compile model
    model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
    return model


위와 같이 두 개의 MLP를 만들었습니다.

baseline_model은 히든 레이어가 한 개인 네트워크이고, good_model은 히든 레이어가 두 개인 네트워크입니다.


  • keras의 경우 input layer는 생략하며 첫 번째 히든 레이어에 "input_dim=인풋 레이어의 노드 수"를 지정하는 식으로 네트워크를 구성합니다.
  • 히든 레이어가 많고, 한 레이어당 노드 수가 적을 수록 overfitting이 적어져 generalization이 잘된다고 알려져있습니다.

데이터를 통해 위 두 개의 네트워크를 훈련하고 테스트해보겠습니다.



3. Training & Test


히든 레이어 한 개짜리 MLP


# build the model
model = baseline_model()
# Fit the model
model.fit(X_train, y_train, validation_data=(X_test, y_test), nb_epoch=10, batch_size=200, verbose=2)
# Final evaluation of the model
scores = model.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))


첫 번째 모델인 baseline_model을 train하고 test하는 코드입니다. 

validation set은 test set으로 대체하였습니다. 

실행시 아래와 같이 10번 epoch을 돌면서, training accuracy, loss와 validation accuracy, loss를 출력합니다. 



error는 validation accuracy를 기준으로하며 (1-0.9232)*100 = 7.68% 입니다.



히든 레이어 두 개짜리 MLP (더 Deep 한 모델)


# build the model
model2 = good_model()
# Fit the model
model2.fit(X_train, y_train, validation_data=(X_test, y_test), nb_epoch=10, batch_size=200, verbose=2)
# Final evaluation of the model
scores = model2.evaluate(X_test, y_test, verbose=0)
print("Baseline Error: %.2f%%" % (100-scores[1]*100))


두 번째 모델인 good_model을 기준으로 training 과 test를 합니다.




위와 같이 Error가 7.54%로 첫 번째 네트워크보다 accuracy가 다소 높은 것을 알 수 있습니다.


4. 결론


MLP로 MNIST 데이터를 training하고 test 해보았습니다. MNIST와 같이 이미지 사이즈가 작고 흑백인 이미지에는 MLP도 꽤 괜찮은 accuracy를 내준다는 것을 알 수 있습니다. 하지만 이미지의 사이즈가 커지고 class의 갯수가 많아질 수록 CNN(Convolutional Neural Network)과 같은 이미지 처리에 최적화된 네트워크를 쓰는 것이 훨씬 효율적일 것입니다. 또한 training할 때 layer와 갯수와 layer당 node수를 적절히 선택하는 것이 중요하며 보통 이 사이즈는 try & error로 구하는 경우가 많습니다. 물론 네트워크를 구성하고 training 해본 경험이 많다면 어느정도의 layer와 node수를 선택해야하는지에 대해 어느정도 직감을 가질 수 있습니다.