#%% 라이브러리 호출
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from PIL import Image
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.optimizers import Adam
from tensorflow.keras import layers,models,Model
from keras.preprocessing.image import ImageDataGenerator
import tensorflow.keras.backend as K
from tensorflow.keras.applications import resnet50 # ResNet 모델 불러오기 50 layer 모델
from tensorflow.keras.layers import Input,GlobalAveragePooling2D, Conv2D, BatchNormalization, Activation, Add, MaxPooling2D, AveragePooling2D, Flatten, Dense, ZeroPadding2D
각 종 라이브러리를 선언해 주는데, PreTrained 모델을 위해서, 각종 레이어 툴을 한번에 선언하여주었습니다.
import random
os.environ['PYTHONHASHSEED']='1'
os.environ['TF_DETERMINISTIC_OPS']='1'
np.random.seed(5148)
random.seed(5148)
tf.random.set_seed(5148)
동일한 초기 조건에서 실험을 재현하거나 결과를 비교하기 위해 실험의 재현성을 위해 난수 생성을 제어하는 목적으로, Python, NumPy, TensorFlow의 난수 시드를 각각 5148로 설정합니다.
gpus = tf.config.list_physical_devices('GPU')
if gpus:
try:
tf.config.set_logical_device_configuration(
gpus[0],
[tf.config.LogicalDeviceConfiguration(memory_limit=4*1024)])
print("GPU를 사용합니다")
except RuntimeError as e:
# 프로그램 시작시에 가상 장치가 설정되어야만 합니다
print(e)
메모리는 기본적으로 4GB를 사용하나, VIT의 경우 4GB로는 진행되지 않아 6GB를 사용하는 등, 제한을 풀어줘야하는 경우가 생김.
#%% 이미지 경로와 라벨, encoding된 라벨을 data frame제작
path = "./Rice_Image_Dataset"
data = {"imgpath": [] , "labels": [] ,"encoded_labels":[]}
categories=["Arborio","Basmati","Ipsala","Jasmine","Karacadag"] # 순서대로 0,1,2,3,4
for idx,folder in enumerate(categories):
folderpath = os.path.join(path,folder)
filelist = os.listdir(folderpath)
for file in filelist:
fpath = os.path.join(folderpath, file)
data["imgpath"].append(fpath)
data["labels"].append(folder)
data["encoded_labels"].append(idx)
df = pd.DataFrame(data)
#%% train:test:validation=6:2:2 로 쪼개어 학습
train_xy,test_xy=train_test_split(df, train_size=0.6,shuffle=True)
test_xy,validation_xy=train_test_split(test_xy,train_size=0.5,shuffle=True)
이미지 폴더에서 경로, 클래스 라벨, 인코딩된 라벨을 이용해 데이터 프레임을 생성
주어진 이미지 경로 내에 있는 정수로 인코딩된 클래스의 라벨을 데이터 프레임의 각 열에 추가합니다. 이후 Train:Validation:test=6:2:2로 데이터를 쪼갬
#%% 학습 이미지 데이터를 전처리하는 데이터 제너레이터를 만드는 작업
img_size=(224,224)
generator = ImageDataGenerator(
rescale=1./255
)
train_images = generator.flow_from_dataframe(
dataframe=train_xy,
x_col='imgpath',
y_col='labels',
target_size=img_size,
color_mode='rgb',
class_mode='categorical',
batch_size=16,
shuffle=True,
seed=42,
)
val_images = generator.flow_from_dataframe(
dataframe=validation_xy,
x_col='imgpath',
y_col='labels',
target_size=img_size,
color_mode='rgb',
class_mode='categorical',
batch_size=16,
shuffle=False
)
test_images = generator.flow_from_dataframe(
dataframe=test_xy,
x_col='imgpath',
y_col='labels',
target_size=img_size,
color_mode='rgb',
class_mode='categorical',
batch_size=16,
shuffle=False
)
학습 이미지를 전처리 하기위한 Data Generator 생성.
주로 딥러닝 모델을 훈련할 때 사용되고, 이미지 데이터를 실시간으로 처리하고 배치 단위로 모델에 공급할 때 주로 사용하는데, 이미지의 픽셀 값을 0과 1 사이로 정규화하고, 이미지의 크기를 (224,224)로 앞서 설명한 모든 Network에 적절한 이미지 크기로 resize를 진행
def ResNet_50(input_shape=(224, 224, 3), num_classes=5):
# ResNet50 모델 불러오기 (pretrained weights를 사용)
base_model = resnet50.ResNet50(weights="imagenet", input_shape=(224, 224, 3), include_top=False)
# 기존의 마지막 레이어를 제외한 부분 가져오기
x=base_model.output
x=GlobalAveragePooling2D()(x)
x=Dense(num_classes, activation='softmax')(x)
model=Model(inputs=base_model.input,outputs=x)
return model
resnet_50=ResNet_50(input_shape=(224, 224, 3), num_classes=5)
# 모델 요약 출력
resnet_50.summary()
keras.applications
를 통해서 모델의 선언이 가능하다. weights=None은 Non-Pretrained로 가중치가 사전학습되지않은 모델을 이용하는 것이고, 학습을 하지 않는 경우에는 Included_top=True로 두고 num_classes=5 등 클래스의 개수를 바로 변환시켜 사용가능하지만, keras에서는 사전 학습 되는 경우에 num_classes를 조절할 수 없어, Included_top=False로 두고 FC(Fully Connected)Layer를 직접 설계해야하는 불편함이 있다.
예를 들어 Non_Pretrianed의 경우에는def ResNet_50(input_shape=(224, 224, 3), num_classes=5): # ResNet50 모델 불러오기 (pretrained weights를 사용하지 않음) base_model = resnet50.ResNet50(weights=None, input_shape=(224, 224, 3), include_top=True, classes=num_classes) # 기존의 마지막 레이어를 제외한 부분 가져오기 x=base_model.output model=Model(inputs=base_model.input,outputs=x) return model
다음과 같이 작성 가능하다.
#%% 모델 컴파일 및 학습
resnet_50.compile(optimizer=Adam(learning_rate=0.0003),loss='categorical_crossentropy',metrics=['accuracy'])
with tf.device('/GPU:0'):
history=resnet_50.fit(
train_images,
steps_per_epoch=len(train_images),
validation_data=val_images,
validation_steps=len(val_images),
epochs=3,
)
results = resnet_50.evaluate(test_images, verbose=0)
print(" Test Loss: {:.5f}".format(results[0]))
print("Test Accuracy: {:.2f}%".format(results[1] * 100))
train_acc = history.history['accuracy']
train_loss = history.history['loss']
valid_acc = history.history['val_accuracy']
valid_loss = history.history['val_loss']
모델을 compile하고 주어진 데이터 제너레이터를 사용하여 모델을 학습시킵니다. 학습된 모델을 Test Data로 평가하고 학습과정에서의 훈련 및 검증 정확도 및 손실을 기록하여 결과를 출력합니다.