본문 바로가기

머신러닝공부

Kaggle Surface Crack Detection data Example script

728x90
반응형

Kaggle surface crack detection data는 콘크리트 표면 결함을 발견하고 예측하기 위한 Kaggle의 공개 데이터로서, 평균적으로 227 x 227 크기를 가지는 color 이미지이며, crack 없는 Negative 데이터 2만개와 crack 발생한 Positive 데이터 2만개, 총 4만개의 이미지 데이터이다.

 

모델 아키텍처 및 데이터 전처리

Conv -> MaxPooling -> Conv -> MaxPooling -> Conv -> MaxPooling -> Flatten -> Dense -> Dense

 

데이터 다운로드

import tensorflow as tf
from tensorflow.keras.layers import Dropout, Dense, Conv2D, GlobalAveragePooling2D, MaxPool2D
from tensorflow.keras.models import Sequential
from tensorflow.keras.preprocessing.image import ImageDataGenerator

from google.colab import drive

drive.mount('/content/gdrive/')

import shutil
import os

try:
	dataset_path = 'content/gdrive/My Drive/Colab Notebooks/dataset'
    shutil.copy(os.path.join(dataset_path, 'surface_crack.zip'), '/content')
    
except Exception as err:
	print(str(err))
    
# ROOT_DIR, DATA_ROOT_DIR 등의 변수를 설정해두면, Colab이외 시스템에서는 디렉토리 설정 부분만 변경하면
# 프로그램 호한성을 높일 수 있음
ROOT_DIR = '/content'
DATA_ROOT_DIR = os.path.join(ROOT_DIR, 'surface_crack')
TRAIN_DATA_ROOT_DIR = os.path.join(DATA_ROOT_DIR, 'train')
TEST_DATA_ROOT_DIR = os.path.join(DATA_ROOT_DIR, 'test')

import zipfile
with zipfile.ZipFile(os.path.join(ROOT_DIR, 'surface_crack.zip'), 'r') as target_file:
	target_file.extractall(DATA_ROOT_DIR)

# Negative, Positive 디렉토리는 이미지 정답(label)을 나타내며, 해당 디렉토리에 정답에 해당하는 이미지가 들어가있음
# 즉, 총 2개의 정답을 가지는 이미지가 학습데이터임
label_name_list = os.listdir(DATA_ROOT_DIR)
print(label_name_list)
for label_name in label_name_list:
	label_dir = os.path.join(DATA_ROOT_DIR, label_name)
    print('train label : ' + label_name + ' => ', len(os.listdir(os.path.join(DATA_ROOT_DIR, label_name))))

데이터 전처리 (train dir / test dir 생성)

import shutil
try:
	shutil.copytree(DATA_ROOT_DIR, TRAIN_DATA_ROOT_DIR)
    
except EXxception as err:
	print(str(err))
    
if not os.path.exists(TEST_DATA_ROOT_DIR):
	os.mkdir(TEST_DATA_ROOT_DIR)
    
if not os.path.exists(os.path.join(TEST_DATA_ROOT_DIR, 'Positive')):
	os.mkdir(os.path.jjoin(TEST_DATA_ROOT_DIR, 'Positive'))
    
if not os.path.exists(os.path.join(TEST_DATA_ROOT_DIR, 'Negative')):
	os.mkdir(os.path.join(TEST_DATA_ROOT_DIR, 'Negative'))

데이터 전처리 (train data : test data = 8 : 2)

import os
import shutil
import random

MOVE_RATIO = 0.2 # train data로 부터 생성될 test data비율, 즉 8:2 비율로 train data와 test data를 만들 예정
label_name_list = os.listdir(TRAIN_DATA_ROOT_DIR)

for label_name in label_name_list:
	src_dir_path = os.path.join(TRAIN_DATA_ROOT_DIR, label_name)
    dst_dir_path = os.path.join(TEST_DATA_ROOT_DIR, label_name)
    
    # shuffle train data
    # 다양한 데이터를 확보하기 위해 train data list를 random하게 shuffle한 후에, numpy slice를 이용하여 test data list 생성
    train_data_file_list = os.listdir(src_dir_path)
    random.shuffle(train_data_file_list)
    split_num = int(MOVE_RATIO*len(train_data_file_list))
    test_data_file_list = train_data_file_list[0:split_num]
    
    # shutil.move 이용해서 train 디렉토리에서 20% 비율로 test 디렉토리로 data move
    for test_data_file in test_data_file_list:
    	try:
        	shutil.moe(os.path.join(src_dir_path, test_data_file),
            	os.path.join(dst_dir_path, test_data_file))
                
         except Exception as err:
         	print(str(err))

데이터 전처리

train_datagen = ImageDataGenerator(rescale=1./255, validation_split=0.20) # 20% 비율로 validation 생성
validation_datagen = ImageDataGenerator(rescale=1./255, validation_split=0.20)
test_datagen = ImageDataGenerator(rescale=1./255)

IMG_WIDTH = 128
IMG_HEIGHT = 128 # 데이터 전체 개수가 많기 때문에 이미지 사이즈 224 x 224 등으로 크게하면 Memory Fail 발생

train_generator = train_datagen.flow_from_directory(TRAIN_DATA_ROOT_DIR, batch_size=32, color_mode='rgb', class_mode='sparse', target_size=(IMG_WIDTH, IMG_HEIGHT), subset='training')
validation_generator = validation_datagen.flow_from_directory(TRAIN_DATA_ROOT_DIR, batch_size=32, color_mode='rgb', class_mode='sparse', target_size=(IMG_WIDTH, IMG_HEIGHT), subset='validation')
test_generator = test_datagen.flow_from_directory(TEST_DATA_ROOT_DIR, batch_size=32, color_mode='rgb', class_mode='sparse', target_size=(IMG_WIDTH, IMG_HEIGHT))

print(train_generator.class_indices, train_generator.num_classes)

모델 아키텍처

3개의 컨볼루션 레이어와 맥스풀링 레이어를 교차로 가지고 있는 구조로 설정

Conv -> MaxPooling -> Conv -> MaxPooling -> Conv -> MaxPooling -> Flatten -> Dense -> Dense

class_nums = train_generator.num_classes # class_nums = 2

model =Sequential()

model.add(Conv2D(kernel_size=(3,3), filters=32, activation='relu', padding='same', input_shape(IMG_WIDTH, IMG_HEIGHT, 3)))
model.add(MaxPool2D(pool_size=(2,3)))
model.add(Dropout(0.25))

model.add(Conv2D(kernel_size=(3,3), filters=64, activation='relu', padding='same'))
model.add(MaxPool2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(Conv2D(kernel_size=(3,3), filters=128, activation='relu' padding='same'))
model.add(MaxPool2D(pool_size=(2,2)))
model.add(Dropout(0.25))

model.add(GlobalAveragePooling2D())

# 정확도를 높이고 오버피팅을 줄이기 위해 2개의 Desne 레이어와 1개의 Dropout 레이어 구성
model.add(Dense(512, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(class_nums, activation='softmax'))

model.complie(loss='sparse_categorical_crossentropy', optimizer=tf.keras.optimizers.Adam(1e-4), metrics=['acc'])

데이터 학습

from tensorflow.keras.callbacks import EarlyStopping
earlystopping = EarlyStopping(monitor='val_loss', patience=5, verbose=1)
hist = model.fit(train_generator, epochs=30, validation_data=validation_generator, callbacks=[earlystopping])

model.evaluate(test_generator)
반응형