2025. 2. 20. 16:42ㆍ머신러닝 수난기 (프로젝트)
Dogs vs. Cats (1) : https://one-plus-one-is-two.tistory.com/3
Dogs vs. Cats (2) : https://one-plus-one-is-two.tistory.com/4
이번 포스팅에서는 2편에서 작성한 코드의 에러 원인을 분석하고 올바르게 고치는 작업을 할 것이다.
4학년이나 됐으면서 간단한 모델 하나 만드는 것도 어려워 하는 내가 참 답답하지만 이 또한 성장 과정이다 생각하고 열심히 공부해보려고 한다.
에러 리뷰
전편 끝부분에 올려놓은 에러를 다시 한 번 살펴보자.
그러니까 이게 뭔 소린지는 알겠다. target은 1차원인데 output은 2차원이라서 에러가 생긴 것이다.
그러면 마지막 밀집 계층에서 units를 2가 아니라 1로 하면 해결이 될까? 일단 다른건 차치하고 단순히 차원을 맞추기 위해 값을 1로 바꿔보았다.
그랬더니 실행이 되기는 한다! 그런데... 정확도가 0.5 주변에서 맴돌고 있다.
개 아니면 고양이 둘 중 하나가 정답인 문제니까 내가 사진을 안 보고 찍어도 정확도가 50% 언저리는 될텐데, 지금 이 모델이 딱 그 수준이다. 정확도는 백 번 양보해서 그렇다 치더라도 손실값은 왜 계속 0으로 뜨는 것인가? 결과적으로 이건 정확도가 낮은 수준이 아니라 아예 분류를 안 하는 모델이라고 할 수 있다.
지금부터 뭐가 문제인지 한 번 분석해보고, 정확도가 높진 않을 수 있어도 적어도 '예측을 하는' 모델을 만들어 보기로 하자.
- 먼저, 분류 문제이기 때문에 마지막 밀집 계층의 활성화 함수는 sigmoid 또는 softmax가 적합하다. 하지만 일반적으로 softmax 함수는 타깃값이 3개 이상인 다중 분류 문제에서 많이 사용하고, 지금 같은 이진 분류 문제에서는 sigmoid 함수를 더 많이 사용한다고 한다. 따라서 마지막 밀집 계층의 활성화 함수를 'sigmoid'로 바꾸겠다.
- 그리고 이진 분류 문제가 다른 다중 분류 문제에 비해 특별한 점은, "A가 아니면 반드시 B이다."라는 판단이 가능하다는 것이다. 예를 들어 어떤 사진을 골랐는데 그 사진이 개가 아니라고 하자. 그러면 그 사진은 무조건 고양이 사진이다. 만약에 개, 고양이 외에 돼지라는 동물이 하나 더 있어서 삼중 분류 문제가 됐다면, 어떤 사진이 개가 아니라고 했을 때 그 사진을 고양이라고 단정할 수 없었을 것이다.
따라서 여기서 하고 싶은 말은, 마지막 밀집 계층의 units을 위 코드처럼 2로 설정할 필요가 없다는 것이다. 활성화 함수를 sigmoid 함수로 했을 때 출력값은 0 이상 1 이하의 실수값일 것이기 때문에, 하나의 출력값만 보고 0.5 이상이면 개로, 0.5 미만이면 고양이로 분류하는 식으로 예측해도 된다는 것이다. 따라서 units를 2에서 1로 바꾸도록 하겠다. - model.compile() 부분에서도 마찬가지로 분류 문제이기 때문에 손실 함수를 Cross Entropy로 쓴 것은 잘했지만, 일반적으로 이진 분류 문제에서는 Categorical Cross Entropy보다 Binary Cross Entropy를 더 많이 사용한다고 한다. 그래서 컴파일 할 때 손실 함수를 "binary_crossentropy"로 바꾸겠다.
그리고 ChatGPT가 짜줬다는 이미지 데이터세트 코드도 리뷰해보기로 하자.
이미지 데이터세트 코드 리뷰
from tensorflow.keras.preprocessing.image import ImageDataGenerator
datagen = ImageDataGenerator(rescale=1/255.0, validation_split=0.2) # 80% 훈련, 20% 검증
df["label"] = df["label"].map({0: "cat", 1: "dog"})
# X_train, y_train을 만드는 과정 (훈련 데이터)
train_generator = datagen.flow_from_dataframe(
dataframe=df,
directory="train/", # 이미지가 들어 있는 폴더 경로
x_col="filename", # 이미지 파일명 컬럼
y_col="label", # 라벨 컬럼
target_size=IMAGE_SIZE, # 모델 입력 크기와 맞춤
batch_size=32,
class_mode="binary", # 다중 클래스 분류인 경우 (이진 분류면 'binary')
subset="training"
)
# 검증 데이터 생성
valid_generator = datagen.flow_from_dataframe(
dataframe=df,
directory="train/",
x_col="filename",
y_col="label",
target_size=IMAGE_SIZE,
batch_size=32,
class_mode="binary",
subset="validation"
)
# 모델 학습
history = model.fit(train_generator, epochs=10, validation_data=valid_generator)
ImageDataGenerator 클래스
찾아보니까 deprecated된 클래스라고 한다. ChatGPT가 왜 deprecated된걸로 코드를 짜줬나 다시 봤는데... 내가 구글링으로 ImageDataGenerator 주워 들어가지고는 그걸로 코드 짜달라고 ChatGPT에게 부탁했었더라...
TensorFlow 2.9 버전부터는 ImageDataGenerator()가 deprecated되었고, 대신에 tensorflow.keras.utils.image_dataset_from_directory()를 사용한다고 한다. 그래서 이후에는 코드를 image_dataset_from_directory()를 사용한 코드로 바꿀 것이다.
deprecated된 클래스를 굳이 리뷰해야 할 필요가 있나 싶다. 그래도 공식 문서가 남아있기는 하니 필요하면 다음 공식 문서를 참조하기로 하고 여기서는 따로 리뷰하지 않으려고 한다.
ImageDataGenerator : https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/image/ImageDataGenerator
tf.keras.preprocessing.image.ImageDataGenerator | TensorFlow v2.16.1
DEPRECATED.
www.tensorflow.org
아 그래도 한 가지 짚고 넘어가자면, 처음에 에러가 발생했던 원인은 class_mode를 "binary"로 한 것에 있었다.
구체적으로 말하자면 내가 만든 모델의 출력 결과는 (None, 2)이라는 2차원 형태로 나오는데, class_mode를 "binary"로 하면 이는 (None,)이라는 1차원 형태가 된다. 이거 GPT가 원래 짜준건 다음과 같았다.
class_mode="categorical", # 다중 클래스 분류인 경우 (이진 분류면 'binary')
근데 내가 주석 보고 "아 이건 이진 분류문제니까 binary로 고쳐야겠네~"하고 "binary"로 고쳐놨던 것이다. "binary"로 고쳤으면 모델도 그에 맞게 고쳐야 했는데 모델은 안 고치고 저것만 고쳤으니 출력 형식이 맞지 않아서 에러가 난 것이었다.
만약에 모델도 안 고치고 class_mode도 "categorical"로 놔뒀으면 그것도 에러 없이 학습이 진행된다. 이게 비록 이진 분류 문제긴 하지만 다중 분류처럼 코드를 짜도 전혀 문제가 없다. 문제가 생긴 지점은 이진 분류와 다중 분류를 혼용한 데에 있었다.
어쨌든 데이터세트 코드는 나중에 고칠 것이긴 한데, 만들어 놓은거 아깝긴 하니까(내가 만든건 아니고 GPT가 만들어준거긴 하지만) 일단 이걸로 학습을 시켜보기로 하자.
모델 학습 코드 리뷰
모델은 model.fit() 함수로 학습시킬 수 있다.
Model.fit(
x=None,
y=None,
batch_size=None,
epochs=1,
verbose="auto",
callbacks=None,
validation_split=0.0,
validation_data=None,
shuffle=True,
class_weight=None,
sample_weight=None,
initial_epoch=0,
steps_per_epoch=None,
validation_steps=None,
validation_batch_size=None,
validation_freq=1,
)
매개변수에 대한 설명은 다음 문서에 나와있다.
Model training APIs : https://keras.io/api/models/model_training_apis/
내 코드에서 전달한 인자는 x, y, epochs, validation_data로 총 4개이다.
엥 근데 아까 보니까 3개만 전달하던데?
GPT의 답변에 따르면 제너레이터 객체가 x와 y의 역할을 모두 수행하기 때문에, train_generator만 전달해도 x와 y를 모두 전달한 것과 같은 효과를 낸다고 한다. 다음은 GPT의 답변 내용이다.
이렇게 모델을 학습할 준비까지 마쳤다. 그러면 이제 모델을 실제로 학습해보자.
드디어..! 아까와 달리 학습이 제대로 되고 있는것 같다. 마지막인 10번째 에폭에서는 검증 정확도(val_accuracy)가 76.62%로, 정확도가 높지는 않지만 적어도 예측을 한다고 할 수 있는 모델이 완성되었다.
그런데 이 결과에서 성능 개선 아이디어를 얻을 수 있을 것 같다. 보면 테스트 정확도(accuracy)는 거의 1에 초근접할 정도로 올라가고 있다. 만약에 에폭 수를 더 늘렸으면 0.99도 넘어갔을 것 같다. 하지만 val_accuracy는 0.77쯤에서 더 이상 올라가지 못한다. 이게 머신러닝 공부했으면 많이 들어봤을법한 '과적합' 현상인 것 같다. 모델이 테스트 데이터에만 지나치게 적합되어 외부 데이터에 대한 정확도는 정체되거나 오히려 떨어지는 현상이다. 이는 나중에 모델에 Dropout 계층을 추가하거나 하는 방식으로 해결할 수 있을 것 같다.
오늘은 '돌아가기는 하는 모델 만들기' 목표를 달성했으니 여기까지만 포스팅하려고 한다.
다음 포스팅의 목표는
- 모델 성능 개선해보기
- ImageDataGenerator()를 사용한 코드를 image_dataset_from_directory()를 사용한 코드로 바꿔보기
로 하자.
'머신러닝 수난기 (프로젝트)' 카테고리의 다른 글
Dogs vs. Cats (6) [PyTorch] (0) | 2025.02.25 |
---|---|
Dogs vs. Cats (5) (0) | 2025.02.23 |
Dogs vs. Cats (4) (0) | 2025.02.21 |
Dogs vs. Cats (2) (0) | 2025.02.19 |
Dogs vs. Cats (1) (0) | 2025.02.16 |