개고양이_new.py
개와 고양이 이미지 분류 - CNN + 데이터 증강
핵심 개념
- 데이터 증강 (Data Augmentation): RandomFlip, RandomRotation, RandomZoom
- Conv2D + MaxPooling2D 조합
- Dropout으로 과적합 방지
- 모델 저장/로드 (.keras 형식)
- 이진 분류 (binary_crossentropy)
#conda env config vars unset deeplearning
#conda deactivate
#conda activate 환경이름
#conda info --envs : 환경변수 확인
#conda clean --all
#conda update conda
#conda create -n mytensorflow python=3.11 -c conda-forge #conda-forge:새로운 레포지토리
#conda activate mytensorflow
#conda install -c conda-forge tensorflow numpy scikit-learn scipy pandas matplotlib # 여기에 당신의 프로젝트에 필요한 다른 라이브러리도 추가하세요 (예: scikit-learn, opencv 등)
#주의사항 : pip랑 conda 섞지 말것, conda 로 설치
#https://www.tensorflow.org/tutorials/images/classification
import os
import tensorflow as tf
import shutil
# Suppress TensorFlow INFO and WARNING messages
# Set TF_CPP_MIN_LOG_LEVEL to:
# 0 = Show all messages (default)
# 1 = Filter out INFO messages
# 2 = Filter out INFO and WARNING messages
# 3 = Filter out INFO, WARNING, and ERROR messages
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
# If you also want to suppress Python's warnings module messages (less common for these specific TF warnings)
import warnings
warnings.filterwarnings('ignore') # Or 'default' or 'always' for specific types of warnings
import pickle
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras import models
# 이미지 전처리 유틸리티 모듈
from tensorflow.keras.preprocessing import image
from tensorflow.keras.models import load_model
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as np
import random
import PIL.Image as pilimg
import imghdr
import pandas as pd
import pickle
import keras
#tf.compat.v1.disable_eager_execution()
# 원본 데이터셋을 압축 해제한 디렉터리 경로-원래 이미지 있는 폴더
original_dataset_dir = './dataset/cats_and_dogs/train'
#옮길 위치
base_dir = './dataset/cats_and_dogs_small'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')
model_save_path_keras = 'cats_and_dogs_model.keras'
history_filepath = 'cats_and_dogs_history.pkl'
batch_size = 16 #한번에 불러오는 이미지 개수
img_height = 180 #이미지의 높이
img_width = 180 #이미지의 넓이
#-------------------------------------
train_cats_dir = os.path.join(train_dir, 'cats')
train_dogs_dir = os.path.join(train_dir, 'dogs')
test_cats_dir = os.path.join(test_dir, 'cats')
test_dogs_dir = os.path.join(test_dir, 'dogs')
validation_cats_dir = os.path.join(validation_dir, 'cats')
validation_dogs_dir = os.path.join(validation_dir, 'dogs')
#이미지 복사
def ImageCopyMove():
#디렉토리내의 파일 개수 알아내기
totalCount = len(os.listdir(original_dataset_dir))
print("전체개수 : ", totalCount)
# 소규모 데이터셋을 저장할 디렉터리
# 반복적인 실행을 위해 디렉토리를 삭제합니다.
if os.path.exists(base_dir): #기존에 디렉토리가 존재하면 전부 지우기
shutil.rmtree(base_dir, ignore_errors=True, onerror=None)
#새로폴더만들기
os.mkdir(base_dir) #없으면 만들기
os.mkdir(train_dir)
os.mkdir(validation_dir)
os.mkdir(test_dir)
os.mkdir(train_cats_dir)
print( train_cats_dir)
os.mkdir(train_dogs_dir)
os.mkdir(validation_cats_dir)
os.mkdir(validation_dogs_dir)
os.mkdir(test_cats_dir)
os.mkdir(test_dogs_dir)
# 처음 1,000개의 고양이 이미지를 train_cats_dir에 복사합니다
fnames = ['cat.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(train_cats_dir, fname)
shutil.copyfile(src, dst)
# 다음 500개 고양이 이미지를 validation_cats_dir에 복사합니다
fnames = ['cat.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(validation_cats_dir, fname)
shutil.copyfile(src, dst)
# 다음 500개 고양이 이미지를 test_cats_dir에 복사합니다
fnames = ['cat.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(test_cats_dir, fname)
shutil.copyfile(src, dst)
# 처음 1,000개의 강아지 이미지를 train_dogs_dir에 복사합니다
fnames = ['dog.{}.jpg'.format(i) for i in range(1000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(train_dogs_dir, fname)
shutil.copyfile(src, dst)
# 다음 500개 강아지 이미지를 validation_dogs_dir에 복사합니다
fnames = ['dog.{}.jpg'.format(i) for i in range(1000, 1500)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(validation_dogs_dir, fname)
shutil.copyfile(src, dst)
# 다음 500개 강아지 이미지를 test_dogs_dir에 복사합니다
fnames = ['dog.{}.jpg'.format(i) for i in range(1500, 2000)]
for fname in fnames:
src = os.path.join(original_dataset_dir, fname)
dst = os.path.join(test_dogs_dir, fname)
shutil.copyfile(src, dst)
def DataIncrease():
#레이어들이 train_ds에서 읽어온 각 이미지에 실시간으로 무작위 변형을 적용하여,
#모델이 매 에포크마다 원본 이미지와 약간씩 다른 버전의 이미지를 보게 만듭니다.
#이를 통해 모델의 일반화 성능을 높이고 과적합(overfitting)을 줄일 수 있습니다.
data_augmentation = keras.Sequential(
[
layers.RandomFlip("horizontal", input_shape=(img_height, img_width, 3)),
layers.RandomRotation(0.1),
layers.RandomZoom(0.1),
]
)
model = models.Sequential()
model.add(layers.Rescaling(1./255))
model.add(data_augmentation)
model.add(layers.Conv2D(32, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Flatten())
model.add(layers.Dropout(0.5)) #중간에 데이터 절반을 없애버림 - 과대적합을 막기 위해서
model.add(layers.Dense(512, activation='relu'))
model.add(layers.Dense(1, activation='sigmoid'))
#원핫인코딩 안할때 - 이진분류일때 가능
model.compile(optimizer='adam',
loss='binary_crossentropy',
metrics=['accuracy'])
train_ds = tf.keras.utils.image_dataset_from_directory(
train_dir,
validation_split=0.2,
subset="training",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
val_ds = tf.keras.utils.image_dataset_from_directory(
train_dir,
validation_split=0.2,
subset="validation",
seed=123,
image_size=(img_height, img_width),
batch_size=batch_size)
epochs=100
history = model.fit(
train_ds,
validation_data=val_ds,
epochs=epochs
)
print("----------------------------")
# #모델 저장하기
try:
model.save(model_save_path_keras)
print(f"모델이 TensorFlow SavedModel (.keras) 형식으로 '{model_save_path_keras}'에 성공적으로 저장되었습니다.")
except Exception as e:
print(f"TensorFlow SavedModel 저장 중 오류 발생: {e}")
try:
with open(history_filepath, 'wb') as file:
pickle.dump(history.history, file)
print(f"학습 히스토리가 '{history_filepath}'에 성공적으로 저장되었습니다.")
except Exception as e:
print(f"학습 히스토리 저장 중 오류 발생: {e}")
import numpy as np
def Predict():
#1.학습된 모듈을 불러온다
loaded_model_keras = None
try:
loaded_model_keras = keras.models.load_model(model_save_path_keras)
print("모델 부르기 성공")
except Exception as e:
print(f"모델 로딩중 실패 : {e}")
return
#예측데이터셋
val_ds = keras.utils.image_dataset_from_directory(
train_dir,
validation_split=0.2,
seed=123,
subset="validation",
image_size=(180,180),
batch_size=16
)
print("----- 라벨링 ------")
class_names = val_ds.class_names
print(class_names)
total_match_count =0
total_samples_proceed=0
max_samples_to_process = 500
for input_batch, labels_batch in val_ds:
total_samples_proceed += len(labels_batch)
#예측하기
prdictions_batch = loaded_model_keras.predict(input_batch, verbose=2)
print(prdictions_batch)
predicted_classes = (prdictions_batch>0.5).astype(int)
print("예측 : ", predicted_classes.flatten())
print("라벨 : ", labels_batch.numpy())
match_count = np.sum(predicted_classes.flatten()== labels_batch.numpy() )
total_match_count+= match_count
print("전체데이터개수 ", total_samples_proceed)
print("맞춘개수 ", total_match_count)
print("못맞춘개수 ", total_samples_proceed-total_match_count)
def LoadModels():
print("\n--- 저장된 모델 로드 테스트 ---")
loaded_model_keras = None
try:
loaded_model_keras = keras.models.load_model(model_save_path_keras)
print(f"모델이 '{model_save_path_keras}'에서 성공적으로 로드되었습니다.")
except Exception as e:
print(f"모델 로드 중 오류 발생: {e}")
try:
with open(history_filepath, 'rb') as file:
history = pickle.load(file)
print(f"학습 히스토리가 '{history_filepath}'에 성공적으로 불러왔습니다.")
except Exception as e:
print(f"학습 히스토리 읽는 중 오류 발생: {e}")
return False
acc = history['accuracy']
val_acc = history['val_accuracy']
loss = history['loss']
val_loss = history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
if __name__=="__main__":
while(True):
print("1. 이미지 복사")
print("2. 데이터 학습 ")
print("3. 모듈 로딩하기")
print("4. 예측하기 ")
sel = input("선택 : ")
if sel=="1":
ImageCopyMove()
elif sel=="2":
DataIncrease()
elif sel=="3":
LoadModels()
elif sel=="4":
Predict()
else:
break