Keras에서 이미지 데이터셋을 불러오는 두가지 방법인 flow_from_directory와 flow_from_dataframe에 대한 간단한 예제코드입니다.
2022. 4. 30 최초작성
2024. 4. 21
데이터셋으로 다음 링크에 있는 것을 사용했습니다.
https://www.microsoft.com/en-us/download/details.aspx?id=54765
소스코드가 있는 폴더에 다음처럼 데이터셋이 위치하는 상태에서 코드를 실행해야 합니다.
이미지를 불러오는 두가지 방식을 테스트로 동작시켜보는 코드이기 때문에 CNN의 레이어는 단순하게 되어있고 에포크는 20번으로 되어있습니다.
데이터셋을 사용시 다음 에러가 발생하기 때문에 이 데이터셋을 사용하기 전에 오류가 있는 이미지 파일을 삭제합니다.
PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x17c6d8540>
다음 코드를 사용하여 오류있는 이미지를 제거합니다.
from PIL import Image import PIL import os def load_and_delete_invalid_images(root_directory): for dirpath, _, filenames in os.walk(root_directory): for filename in filenames: file_path = os.path.join(dirpath, filename) try: with Image.open(file_path) as img: img.verify() # 이미지 파일이 유효한지 확인 except PIL.UnidentifiedImageError: print(f"Invalid image file detected and deleted: {file_path}") os.remove(file_path) # 파일 삭제 root_directory = './PetImages' load_and_delete_invalid_images(root_directory) |
실행 결과입니다.
% /Users/webnautes/miniforge3/envs/tensorflow-dev/bin/python /Users/webnautes/Python_Example/check.py
Invalid image file detected and deleted: ./PetImages/.DS_Store
Invalid image file detected and deleted: ./PetImages/Cat/Thumbs.db
Invalid image file detected and deleted: ./PetImages/Cat/666.jpg
/Users/webnautes/miniforge3/envs/tensorflow-dev/lib/python3.10/site-packages/PIL/TiffImagePlugin.py:870: UserWarning: Truncated File Read
warnings.warn(str(msg))
Invalid image file detected and deleted: ./PetImages/Dog/Thumbs.db
Invalid image file detected and deleted: ./PetImages/Dog/11702.jpg
이제 이미지 데이터를 불러오는 다음 두 가지 메소드에 대해 다룹니다.
flow_from_directory
flow_from_dataframe
flow_from_directory
flow_from_directory를 사용하면 지정한 디렉토리에 있는 서브 디렉토리 이름으로 클래스를 구분하여 이미지를 가져옵니다.
import tensorflow as tf from tensorflow.python.keras.callbacks import EarlyStopping, ReduceLROnPlateau from sklearn.metrics import accuracy_score import matplotlib.pyplot as plt input_shape = (64, 64, 3) image_size = (64, 64) batch_size = 32 # Generator를 사용하여 특정 디렉토리에 있는 이미지 데이터를 가져옵니다. train_datagen = tf.keras.preprocessing.image.ImageDataGenerator( # 전체 데이터의 20%를 검증 데이터로 설정합니다. validation_split=0.2 ) train_generator = train_datagen.flow_from_directory( "PetImages", # 이미지 데이터셋은 이 경로에 디렉토리로 구분되어 저장되어 있습니다. color_mode="rgb", # 이미지를 rgb 포맷으로 불러옴 target_size=image_size, # 이미지 크기 batch_size=batch_size, # batch_size 단위로 데이터를 가져옴 class_mode='binary', # 2개의 클래스가 있음 subset='training') # train 데이터셋 validation_generator = train_datagen.flow_from_directory( "PetImages", color_mode="rgb", target_size=image_size, batch_size=batch_size, class_mode='binary', subset='validation') # CNN 모델을 구성합니다. model = tf.keras.Sequential() model.add(tf.keras.layers.Conv2D(256 , (3,3) , strides = 1 , padding = 'same' , activation = 'relu' , input_shape = input_shape)) model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.MaxPool2D((2,2) , strides = 2 , padding = 'same')) model.add(tf.keras.layers.Conv2D(64, (3,3) , strides = 1 , padding = 'same' , activation = 'relu')) model.add(tf.keras.layers.Conv2D(128 , (3,3) , strides = 1 , padding = 'same' , activation = 'relu' )) model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.MaxPool2D((2,2) , strides = 2 , padding = 'same')) model.add(tf.keras.layers.Conv2D(64, (3,3) , strides = 1 , padding = 'same' , activation = 'relu')) model.add(tf.keras.layers.Dropout(0.3)) model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.MaxPool2D((2,2) , strides = 2 , padding = 'same')) model.add(tf.keras.layers.Conv2D(32, (3,3) , strides = 1 , padding = 'same' , activation = 'relu')) model.add(tf.keras.layers.Dropout(0.3)) model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.MaxPool2D((2,2) , strides = 2 , padding = 'same')) model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(units = 64, activation = 'relu')) model.add(tf.keras.layers.Dropout(0.1)) model.add(tf.keras.layers.Dense(units = 2, activation = 'softmax')) model.compile(optimizer = 'adam' , loss = 'categorical_crossentropy' , metrics = ['accuracy']) model.summary() model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 검증 손실(val_loss)이 개선되지 않을 때 학습률을 동적으로 조정하여 모델의 학습을 개선합니다. reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=1, min_delta=0.0001) # 검증 손실이 특정 에포크 동안 개선되지 않을 경우 조기에 훈련을 중단하고, 가장 좋은 가중치를 복원합니다. early_stop= EarlyStopping(monitor = "val_loss", mode="min", min_delta=0.0001, restore_best_weights=True, patience=10, verbose=1) # 모델을 학습합니다. history=model.fit(train_generator, # train 데이터셋 epochs=20, # 전체 데이터셋을 몇번 학습에 사용할지 지정 steps_per_epoch=train_generator.samples//batch_size, validation_data=validation_generator, # validation 데이터셋 validation_steps=validation_generator.samples//batch_size, callbacks = [early_stop, reduce_lr]) # 콜백 함수 지정 # 학습 히스토리(accuracy/loss) fig, axs = plt.subplots(1,2,figsize=(12,5)) axs[0].plot(history.history["loss"], label="Training Loss") axs[0].plot(history.history["val_loss"], label="Validation Loss") axs[0].legend() axs[1].plot(history.history["accuracy"], label="Training Accuracy") axs[1].plot(history.history["val_accuracy"], label="Validation Accuracy") axs[1].legend() plt.suptitle("dog and cat") plt.show() # 검증 데이터셋을 사용하여 최종 정확도 계산하여 출력 loss,acc = model.evaluate(validation_generator) print(f'Accuracy : {acc}') |
학습 실행 결과입니다.
Epoch 1/20
625/625 [==============================] - 66s 103ms/step - loss: 0.8353 - accuracy: 0.6199 - val_loss: 0.6844 - val_accuracy: 0.5811 - lr: 0.0010
Epoch 2/20
625/625 [==============================] - 64s 103ms/step - loss: 0.5995 - accuracy: 0.7059 - val_loss: 0.5648 - val_accuracy: 0.7013 - lr: 0.0010
Epoch 3/20
625/625 [==============================] - 64s 103ms/step - loss: 0.5158 - accuracy: 0.7544 - val_loss: 0.5362 - val_accuracy: 0.7250 - lr: 0.0010
Epoch 4/20
625/625 [==============================] - 64s 103ms/step - loss: 0.4467 - accuracy: 0.7961 - val_loss: 0.5104 - val_accuracy: 0.7526 - lr: 0.0010
Epoch 5/20
625/625 [==============================] - 64s 102ms/step - loss: 0.4058 - accuracy: 0.8163 - val_loss: 0.5163 - val_accuracy: 0.7462 - lr: 0.0010
Epoch 6/20
625/625 [==============================] - 64s 102ms/step - loss: 0.3255 - accuracy: 0.8584 - val_loss: 0.3674 - val_accuracy: 0.8317 - lr: 1.0000e-04
Epoch 7/20
625/625 [==============================] - 64s 102ms/step - loss: 0.3022 - accuracy: 0.8681 - val_loss: 0.3584 - val_accuracy: 0.8395 - lr: 1.0000e-04
Epoch 8/20
625/625 [==============================] - 64s 102ms/step - loss: 0.2848 - accuracy: 0.8759 - val_loss: 0.3580 - val_accuracy: 0.8427 - lr: 1.0000e-04
Epoch 9/20
625/625 [==============================] - 65s 104ms/step - loss: 0.2780 - accuracy: 0.8820 - val_loss: 0.3369 - val_accuracy: 0.8568 - lr: 1.0000e-04
Epoch 10/20
625/625 [==============================] - 66s 106ms/step - loss: 0.2686 - accuracy: 0.8852 - val_loss: 0.3356 - val_accuracy: 0.8572 - lr: 1.0000e-04
Epoch 11/20
625/625 [==============================] - 66s 106ms/step - loss: 0.2620 - accuracy: 0.8888 - val_loss: 0.3286 - val_accuracy: 0.8634 - lr: 1.0000e-04
Epoch 12/20
625/625 [==============================] - 66s 105ms/step - loss: 0.2574 - accuracy: 0.8914 - val_loss: 0.3289 - val_accuracy: 0.8582 - lr: 1.0000e-04
Epoch 13/20
625/625 [==============================] - 65s 104ms/step - loss: 0.2415 - accuracy: 0.8993 - val_loss: 0.3262 - val_accuracy: 0.8590 - lr: 1.0000e-05
Epoch 14/20
625/625 [==============================] - 65s 104ms/step - loss: 0.2378 - accuracy: 0.9004 - val_loss: 0.3281 - val_accuracy: 0.8562 - lr: 1.0000e-05
Epoch 15/20
625/625 [==============================] - 67s 106ms/step - loss: 0.2369 - accuracy: 0.9010 - val_loss: 0.3263 - val_accuracy: 0.8594 - lr: 1.0000e-06
Epoch 16/20
625/625 [==============================] - 68s 108ms/step - loss: 0.2362 - accuracy: 0.9036 - val_loss: 0.3251 - val_accuracy: 0.8584 - lr: 1.0000e-07
Epoch 17/20
625/625 [==============================] - 66s 106ms/step - loss: 0.2348 - accuracy: 0.9021 - val_loss: 0.3259 - val_accuracy: 0.8584 - lr: 1.0000e-07
Epoch 18/20
625/625 [==============================] - 66s 105ms/step - loss: 0.2381 - accuracy: 0.9001 - val_loss: 0.3257 - val_accuracy: 0.8594 - lr: 1.0000e-08
Epoch 19/20
625/625 [==============================] - 66s 105ms/step - loss: 0.2343 - accuracy: 0.8979 - val_loss: 0.3249 - val_accuracy: 0.8602 - lr: 1.0000e-09
Epoch 20/20
625/625 [==============================] - 66s 105ms/step - loss: 0.2375 - accuracy: 0.9011 - val_loss: 0.3263 - val_accuracy: 0.8592 - lr: 1.0000e-09
157/157 [==============================] - 7s 43ms/step - loss: 0.3266 - accuracy: 0.8587
Accuracy : 0.8587434887886047
flow_from_dataframe
flow_from_dataframe는 이미지 파일 경로와 라벨을 데이터프레임에 저장하여 사용합니다.
import tensorflow as tf from tensorflow.python.keras.callbacks import EarlyStopping, ReduceLROnPlateau import pandas as pd import os import matplotlib.pyplot as plt # 이미지 경로와 라벨을 데이터프레임에 저장합니다. # csv 파일에 기록한 것을 로드하여 사용해도 됩니다. data = [] for root, subdirs, files in os.walk('./PetImages'): for d in subdirs: label = d fullpath = root + '/' + d if len(files) > 0: for f in files: # 디렉토리 이름을 라벨로 사용합니다. label = root.split('/')[-1] data.append([root + '/' + f, label]) df = pd.DataFrame(data=data, columns=['files', 'label']) print(df) input_shape = (64, 64, 3) image_size = (64, 64) batch_size = 32 # Generator를 사용하여 특정 디렉토리에 있는 이미지 데이터를 가져옵니다. train_datagen = tf.keras.preprocessing.image.ImageDataGenerator( # 전체 데이터의 20%를 검증 데이터로 설정합니다. validation_split=0.2 ) train_generator = train_datagen.flow_from_dataframe(dataframe=df, directory='.', # df_train에 있는 이미지 파일 경로의 root 디렉토리를 적으면 됩니다. 현재 폴더에 위치하므로 .을 사용합니다. x_col="files", # 데이터프레임에서 이미지 파일 경로가 저장된 컬럼 이름입니다. y_col="label", # 데이터프레임에서 라벨이 저장된 컬럼 이름입니다. class_mode="binary", # 2개의 클래스가 있음 target_size=image_size, # 이미지 크기 batch_size=batch_size, # batch_size 단위로 데이터를 가져옴 rescale=1.0/255, # 이미지의 픽셀값을 정규화함 seed=2024) # 랜덤시드 validation_generator = train_datagen.flow_from_dataframe(dataframe=df, directory='.', # df에 있는 이미지 파일 경로의 root 디렉토리를 적으면 됩니다. 현재 폴더에 위치하므로 .을 사용합니다. x_col="files", y_col="label", class_mode="binary", target_size=image_size, batch_size=batch_size, rescale=1.0/255, seed=2024) model = tf.keras.Sequential() model.add(tf.keras.layers.Conv2D(256 , (3,3) , strides = 1 , padding = 'same' , activation = 'relu' , input_shape = input_shape)) model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.MaxPool2D((2,2) , strides = 2 , padding = 'same')) model.add(tf.keras.layers.Conv2D(64, (3,3) , strides = 1 , padding = 'same' , activation = 'relu')) model.add(tf.keras.layers.Conv2D(128 , (3,3) , strides = 1 , padding = 'same' , activation = 'relu' )) model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.MaxPool2D((2,2) , strides = 2 , padding = 'same')) model.add(tf.keras.layers.Conv2D(64, (3,3) , strides = 1 , padding = 'same' , activation = 'relu')) model.add(tf.keras.layers.Dropout(0.3)) model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.MaxPool2D((2,2) , strides = 2 , padding = 'same')) model.add(tf.keras.layers.Conv2D(32, (3,3) , strides = 1 , padding = 'same' , activation = 'relu')) model.add(tf.keras.layers.Dropout(0.3)) model.add(tf.keras.layers.BatchNormalization()) model.add(tf.keras.layers.MaxPool2D((2,2) , strides = 2 , padding = 'same')) model.add(tf.keras.layers.Flatten()) model.add(tf.keras.layers.Dense(units = 64, activation = 'relu')) model.add(tf.keras.layers.Dropout(0.1)) model.add(tf.keras.layers.Dense(units = 2, activation = 'softmax')) model.compile(optimizer = 'adam' , loss = 'categorical_crossentropy' , metrics = ['accuracy']) model.summary() model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.001), loss='sparse_categorical_crossentropy', metrics=['accuracy']) # 검증 손실(val_loss)이 개선되지 않을 때 학습률을 동적으로 조정하여 모델의 학습을 개선합니다. reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=1, min_delta=0.0001) # 검증 손실이 특정 에포크 동안 개선되지 않을 경우 조기에 훈련을 중단하고, 가장 좋은 가중치를 복원합니다. early_stop= EarlyStopping(monitor = "val_loss", mode="min", min_delta=0.0001, restore_best_weights=True, patience=10, verbose=1) history=model.fit(train_generator, # train 데이터셋 epochs=20, # 전체 데이터셋을 몇번 학습에 사용할지 지정 steps_per_epoch=train_generator.n//train_generator.batch_size, validation_data=validation_generator, # validation 데이터셋 validation_steps=validation_generator.n//validation_generator.batch_size, callbacks = [early_stop, reduce_lr]) # 콜백 함수 지정 # 학습 히스토리(accuracy/loss) fig, axs = plt.subplots(1,2,figsize=(12,5)) axs[0].plot(history.history["loss"], label="Training Loss") axs[0].plot(history.history["val_loss"], label="Validation Loss") axs[0].legend() axs[1].plot(history.history["accuracy"], label="Training Accuracy") axs[1].plot(history.history["val_accuracy"], label="Validation Accuracy") axs[1].legend() plt.suptitle("dog and cat") plt.show() # 검증 데이터셋을 사용하여 최종 정확도 계산하여 출력 loss,acc = model.evaluate(validation_generator) print(f'Accuracy : {acc}') |
실행결과입니다.
Epoch 1/20
781/781 [==============================] - 110s 140ms/step - loss: 0.8120 - accuracy: 0.6206 - val_loss: 0.7061 - val_accuracy: 0.5659 - lr: 0.0010
Epoch 2/20
781/781 [==============================] - 104s 133ms/step - loss: 0.6482 - accuracy: 0.6749 - val_loss: 0.6020 - val_accuracy: 0.6648 - lr: 0.0010
Epoch 3/20
781/781 [==============================] - 104s 133ms/step - loss: 0.5964 - accuracy: 0.7046 - val_loss: 0.6558 - val_accuracy: 0.6077 - lr: 0.0010
Epoch 4/20
781/781 [==============================] - 104s 133ms/step - loss: 0.5187 - accuracy: 0.7451 - val_loss: 0.5329 - val_accuracy: 0.7248 - lr: 1.0000e-04
Epoch 5/20
781/781 [==============================] - 107s 136ms/step - loss: 0.4926 - accuracy: 0.7607 - val_loss: 0.4968 - val_accuracy: 0.7596 - lr: 1.0000e-04
Epoch 6/20
781/781 [==============================] - 108s 138ms/step - loss: 0.4789 - accuracy: 0.7696 - val_loss: 0.4728 - val_accuracy: 0.7756 - lr: 1.0000e-04
Epoch 7/20
781/781 [==============================] - 106s 135ms/step - loss: 0.4709 - accuracy: 0.7770 - val_loss: 0.4934 - val_accuracy: 0.7542 - lr: 1.0000e-04
Epoch 8/20
781/781 [==============================] - 102s 131ms/step - loss: 0.4529 - accuracy: 0.7850 - val_loss: 0.4703 - val_accuracy: 0.7761 - lr: 1.0000e-05
Epoch 9/20
781/781 [==============================] - 103s 131ms/step - loss: 0.4506 - accuracy: 0.7882 - val_loss: 0.4833 - val_accuracy: 0.7644 - lr: 1.0000e-05
Epoch 10/20
781/781 [==============================] - 102s 131ms/step - loss: 0.4499 - accuracy: 0.7878 - val_loss: 0.4744 - val_accuracy: 0.7726 - lr: 1.0000e-06
Epoch 11/20
781/781 [==============================] - 102s 131ms/step - loss: 0.4494 - accuracy: 0.7877 - val_loss: 0.4745 - val_accuracy: 0.7722 - lr: 1.0000e-07
Epoch 12/20
781/781 [==============================] - 102s 131ms/step - loss: 0.4482 - accuracy: 0.7907 - val_loss: 0.4768 - val_accuracy: 0.7701 - lr: 1.0000e-08
Epoch 13/20
781/781 [==============================] - 103s 131ms/step - loss: 0.4510 - accuracy: 0.7876 - val_loss: 0.4754 - val_accuracy: 0.7716 - lr: 1.0000e-09
Epoch 14/20
781/781 [==============================] - 103s 132ms/step - loss: 0.4488 - accuracy: 0.7886 - val_loss: 0.4754 - val_accuracy: 0.7713 - lr: 1.0000e-10
Epoch 15/20
781/781 [==============================] - 103s 132ms/step - loss: 0.4444 - accuracy: 0.7918 - val_loss: 0.4753 - val_accuracy: 0.7715 - lr: 1.0000e-11
Epoch 16/20
781/781 [==============================] - 103s 132ms/step - loss: 0.4479 - accuracy: 0.7910 - val_loss: 0.4753 - val_accuracy: 0.7715 - lr: 1.0000e-12
Epoch 17/20
781/781 [==============================] - 103s 132ms/step - loss: 0.4517 - accuracy: 0.7879 - val_loss: 0.4748 - val_accuracy: 0.7723 - lr: 1.0000e-13
Epoch 18/20
781/781 [==============================] - ETA: 0s - loss: 0.4502 - accuracy: 0.7873Restoring model weights from the end of the best epoch.
781/781 [==============================] - 103s 132ms/step - loss: 0.4502 - accuracy: 0.7873 - val_loss: 0.4752 - val_accuracy: 0.7714 - lr: 1.0000e-14
Epoch 00018: early stopping
782/782 [==============================] - 37s 47ms/step - loss: 0.4703 - accuracy: 0.7761
Accuracy : 0.7761421203613281
'Deep Learning & Machine Learning > Keras' 카테고리의 다른 글
Keras의 EfficientNet, EfficientNetV2 모델의 파라미터 개수 (0) | 2024.03.18 |
---|---|
Optuna를 사용한 Keras 분류 모델 하이퍼파라미터 최적화 - Mnist (0) | 2023.11.05 |
Optuna를 사용한 Keras 분류 모델 하이퍼파라미터 최적화 - iris (0) | 2023.11.05 |
Keras MNIST Image Classification 예제 (0) | 2023.10.30 |
Keras 모델 전체 파라미터 개수 세기 (0) | 2023.10.22 |
시간날때마다 틈틈이 이것저것 해보며 블로그에 글을 남깁니다.
블로그의 문서는 종종 최신 버전으로 업데이트됩니다.
여유 시간이 날때 진행하는 거라 언제 진행될지는 알 수 없습니다.
영화,책, 생각등을 올리는 블로그도 운영하고 있습니다.
https://freewriting2024.tistory.com
제가 쓴 책도 한번 검토해보세요 ^^
그렇게 천천히 걸으면서도 그렇게 빨리 앞으로 나갈 수 있다는 건.
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!