FE 개발자를 위한 안드로이드 후려치기 #4 바텀시트 만들기
TensorFlow와 Keras를 사용해서 딥러닝을 해보자 #1 시행착오
2024-08-04
Explanation
오늘은 대 AI의 시대 속에서 한번 직접 TensorFlow와 Keras를 사용해서 간단하게 학습을 시키고 직접 확인해 보려 합니다!
여담으로, 올해 공부하려고 했던 두 가지 중 하나가 웹 브라우저 개발이고 나머지 하나가 AI 였는데요. 그런데 생각보다 웹 브라우저 개발이 오래걸려서.. 조금 많이 밀렸습니다.
지금 이 글은 그 시작으로 아주 간단한 첫 걸음이지만, 목표는 GAN(Generative Adversarial Networks) 이미지에 대한 학습을 시키고 개발하고 있는 웹 브라우저에 적용해서 웹 페이지에 GAN 이미지가 있을때, 해당 이미지에 알림를 띄우는 기능을 넣는 것이랍니다!
틈틈이 공부한 내용이나 개발에 진척이 있다면 블로그를 통해서 공유하도록 하겠습니다!
이번에 해볼 것은 여러 장의 고양이 이미지와 그 밖의 동물들의 이미지를 가지고 학습을 시킨 후에 특정 이미지가 주어졌을 때, 이 이미지가 고양이인지 아닌지 알아내는 api 서버를 만드는 것까지 입니다.
(언어는 파이썬을 사용하고 api 서버는 플라스크를 사용합니다.)
저는 딥러닝도 머신러닝도 AI도 잘 모른답니다. (파이썬도 한 5년 만에 써보는 거 같아요.)
이 글은 아주 짧게 공부하면서 작성하는 글이기 때문에, 부족한 부분이 많습니다. 그냥 어떤 순서로 어떻게 동작하는지 가볍게 보는 정도로만 봐주시면 좋을 것 같습니다 :)
일단 기본적으로 학습을 시키려면, 공부거리가 있어야겠죠?! 그래서 고양이 이미지와 고양이가 아닌 이미지를 인터넷에서 수집하였습니다. 이 대목에서 당연히 숙련된 개발자라면 스크래핑 서버를 만들어서 인터넷에서 싹싹 긁어 오겠지만, 저는 미숙한 개발자이기 때문에 저작권까지 생각해서 https://unsplash.com 에서 한땀 한땀 수집하였습니다.
총 고양이 이미지 60개, 그밖의 동물 이미지 60개를 만들었는데, 이 정도로 될지 모르겠네요.
일단 해보고 잘 안되면 추가하는 쪽으로 하겠습니다.
늘 그렇듯, 이 글에 작성된 코드는 깃허브 저장소를 통해서 모두 확인할 수 있으며 위 데이터 셋 이미지도 확인할 수 있습니다.
https://github.com/falsy/blog-post-example/tree/master/tensorflow-keras
앞서 이야기한대로 TensorFlow와 Keras를 사용해서 학습 코드를 만들어 줄 건데요.
우선 필요한 패키지를 먼저 정의하고 설치해 줍니다.
개발 환경은 macOS이고 Python은 v3.9.7 를 사용하고 있습니다.
1 2 3 4 5 6 |
// requirements.txt flask tensorflow pillow scipy matplotlib |
가상 환경을 만들고 활성화해주고, 패키지를 설치해 줍니다.
1 2 |
$ python3 -m venv .venv $ source .venv/bin/activate |
가상환경은 ‘deactivate’ 명령어로 종료할 수 있습니다.
1 |
$ pip install -r requirements.txt |
flask 는 후에 만들 api 서버의 프레임워크이고 tensorflow는 딥러닝 라이브러리이고 pillow는 파이썬 이미지 라이브러리의 유지 보수 버전이고 scipy는 학습시키려고 하니까 설치하라고 하더라고요?! 뭔지는 잘 모르겠네요.
이제 본격적으로 코드를 볼까요
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
// train_model.py import tensorflow as tf from tensorflow.keras.preprocessing.image import ImageDataGenerator from tensorflow.keras.applications import ResNet50 from tensorflow.keras.layers import Dense, GlobalAveragePooling2D from tensorflow.keras.models import Model from tensorflow.keras.optimizers import Adam # 데이터 경로 설정 train_dir = 'dataset/train' val_dir = 'dataset/validation' # 데이터 증강 및 전처리 train_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True, fill_mode='nearest' ) val_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( train_dir, target_size=(150, 150), batch_size=32, class_mode='binary' ) val_generator = val_datagen.flow_from_directory( val_dir, target_size=(150, 150), batch_size=32, class_mode='binary' ) # 사전 학습된 ResNet50 모델 로드 base_model = ResNet50(weights='imagenet', include_top=False) # 모델 아키텍처 수정 x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(1024, activation='relu')(x) predictions = Dense(1, activation='sigmoid')(x) model = Model(inputs=base_model.input, outputs=predictions) # 모델 컴파일 model.compile(optimizer=Adam(learning_rate=0.0001), loss='binary_crossentropy', metrics=['accuracy']) # 모델 학습 model.fit( train_generator, validation_data=val_generator, epochs=10 ) # 모델 저장 model.save('cat_detector_model.keras') |
짜잔! 뭔가 생각보다 간단하고 그럴싸해 보이죠?!
사실 ChatGPT가 만들어 준 코드라 뭔지 저도 잘 모릅니다..
그래도 코드를 좀 알아야 뭘 할 수 있겠죠?
1 2 3 |
# 데이터 경로 설정 train_dir = 'dataset/train' val_dir = 'dataset/validation' |
주석 그대로 데이터 셋 이미지가 있는 디렉토리 경로 입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# 데이터 증강 및 전처리 train_datagen = ImageDataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, rotation_range=40, width_shift_range=0.2, height_shift_range=0.2, horizontal_flip=True, fill_mode='nearest' ) val_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory( train_dir, target_size=(150, 150), batch_size=32, class_mode='binary' ) val_generator = val_datagen.flow_from_directory( val_dir, target_size=(150, 150), batch_size=32, class_mode='binary' ) |
ImageDataGenerator는 이미지의 크기가 일정하지 않을때, 이미지의 크기를 동일하게 만들어주고 여러가지 옵션의 이미지의 양을 늘려서 모델의 성능을 올려주는 역할을 합니다. 위 코드를 짧게 살펴보면 ‘shear_range’ 무작위로 기울기를 변형한 이미지를 추가하고(찌그러트림), ‘zoom_range’ 무작위 이미지를 확대하거나 축소한 이미지를, 그리고 ‘horizontal_flip’는 무작위 이미지를 좌우반전한 이미지가 전달됩니다. 그리고 ‘target_size’로 이미지의 사이즈는 150×150으로 동일하게 합니다.
1 2 |
# 사전 학습된 ResNet50 모델 로드 base_model = ResNet50(weights='imagenet', include_top=False) |
‘ResNet50’라는 모델을 기본 학습 모델로 사용하고 있는데요. ‘ResNet50’는 ResNet 계열의 딥러닝 모델로 이미지 인식 및 분류 작업에 많이 사용되는 모델입니다.
그리고 ‘ResNet50’의 메커니즘이나 특징을 이해하는 건..
저의 프로젝트 의지를 현격하게 저하시킬 가능성이 농후하기 때문에 여기까지..
1 2 3 4 5 |
# 모델 아키텍처 수정 x = base_model.output x = GlobalAveragePooling2D()(x) x = Dense(1024, activation='relu')(x) predictions = Dense(1, activation='sigmoid')(x) |
‘x = GlobalAveragePooling2D()(x)’
– 전역 평균 풀링 레이어를 추가하여 공간적 크기를 줄입니다.
‘x = Dense(1024, activation=’relu’)(x)’
– 1024개의 뉴런과 ReLU 활성화 함수를 가진 완전 연결 레이어를 추가합니다.
‘predictions = Dense(1, activation=’sigmoid’)(x)’
– 1개의 뉴런과 sigmoid 활성화 함수를 가진 출력 레이어를 추가합니다. 이 레이어는 이진 분류를 위해 사용됩니다.
무슨 말인지 하나도 모르겠습니다.
괜찮습니다. 차차 알아가기면 되니까요.
1 2 3 4 5 6 |
# 모델 학습 model.fit( train_generator, validation_data=val_generator, epochs=10 ) |
train_generator: 학습 데이터 생성기입니다.
validation_data=val_generator: 검증 데이터 생성기입니다.
epochs=10: 에포크 수를 10으로 설정합니다.
여기서 에포크는 학습 데이터 셋을 몇번 반복해서 학습시킬지를 나타냅니다. 예를 들어서 학습 데이터 셋이 100개고 epochs 수가 10이라면 총 1000개의 이미지를 학습합니다. 근데 이게 또 너무 많으면 훈련 데이터가 과적합해서 안좋을 수도 있어서 적당한 수치를 맞추는게 중요하다고 합니다.
이제 한번 코드를 실행해 볼까요?
1 |
$ python3 train_model.py |
학습을 하면 아래와 같이 로그가 찍히는데요.
1 |
accuracy: 0.9926 - loss: 0.0643 - val_accuracy: 0.5000 - val_loss: 0.7365 |
각각 ‘accuracy’ 학습 데이터의 정확도, ‘loss’ 학습 데이터의 손실, ‘val_accuracy’ 검증 데이터의 모델 정확도, ‘val_loss’ 검증 데이터셋의 모델 손실값으로, 대략 위 데이터는 모델이 데이터셋을 잘 예측했지만, 예측은 50%에다가 검증 데이터 손실도 0.7365로 굉장히 높은데요.
이렇게 되면 검증 데이터 손실이 너무 커서 잘 맞추지 못합니다..
처음이라 뭐가 문제인지조차 모르지만, 아마도 데이터 셋 이미지가 너무 적어서 그런 게 아닐까 싶어요..
조금 더 테스트 해봐야 하는데, 오늘 제가 사용하고 있는 서버를 업데이트를 해줘야해서..
포스팅은 여기까지하고 내일 이어서 진행하도록 하겠습니다!