ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [이미지] 의류 직물 불량 검출하기
    머신러닝 & 딥러닝 2022. 2. 2. 21:55

    1. 문제 정의

    양산 초기 품질 확보를 위해공정 검사는 필수적으로 진행되고 있다. 공정 검사는 측정, 육안 검사, 기록, 검사 기기 세팅 등과 같이 분류 되며 대부분 작업자에 의해 수행된다. 하지만, 사람이 하는 일에는 언제나 리스크가 따르듯이, 측정 오차나 기록 실수로 인해 공정 자동화의 필요성이 대두되고 있다. 

    이 리스크를 줄이기 위해 머신 비전이 도입되고 있는 추세이다. 컴퓨터 비전 기술이나 영상 처리를 통해 공정 검사의 퀄리티를 높일 수 있다. 이 프로젝트는 육안 검사의 리스크를 줄이기 위해, 의류 직물 불량을 검출하는 딥러닝 프로그램 개발을 목적으로 진행되었다. 

     

    2. 데이터셋

    총 20가지 종류의 직물 데이터셋을 확보하였다. 불량 직물은 총 141개, 정상 직물은 106개로 다소 적은 수의 이미지 데이터임을 감안해야 한다. 이를 보완하기 위해, 데이터 증강을 하였으면 Image Data Generator를 사용하여, 불충분한 데이터 크기를 보완하였다. 

     

    AITEX FABRIC IMAGE DATABASE - Aitex

    The textile fabric database consists of 245 images of 7 different fabrics. There are 140 defect-free images, 20 for each type of  fabric. With different types

    www.aitex.es

    # 이미지 데이터 절대 경로
    PATH_DEFECT = '/content/drive/MyDrive/Colab Notebooks/data/fabric_factory/Defect_images/'
    PATH_NODEFECT = '/content/drive/MyDrive/Colab Notebooks/data/fabric_factory/Fail_Defect_Images/'
    
    # 이미지 데이터 가져오기 및 라벨링
    import os
    
    paths = []
    label_divide = []
    
    for dirname, _, filenames in os.walk(PATH_DEFECT):
      for filename in filenames:
        file_path = dirname + filename
        paths.append(file_path)
        label_divide.append('OK')
    
    for dirname, _, filenames in os.walk(PATH_NODEFECT):
      for filename in filenames:
        file_path = dirname+filename
        paths.append(file_path)
        label_divide.append('FAIL')

     

    path, label_divide 리스트를 하나의 데이터 프레임에 담았다. 

    # 데이터를 dataframe에 담기
    import pandas as pd
    
    pd.set_option('display.max_colwidth', 200)
    
    data_df = pd.DataFrame({'path':paths, 'label':label_divide})
    print('data_df shape:', data_df.shape)
    data_df.tail(5)

     

    정상(OK), 불량(FAIL)의 분포도를 보면 각각 106, 141이다.

    이 데이터셋을 다시 학습/검정용으로 분리한다.

    # 데이터 학습/검정용으로 분리하기
    
    from sklearn.model_selection import train_test_split
    
    # scikit learn의 train_test_split()을 이용하여 train용, validation용 DataFrame을 생성
    # stratify를 이용하여 label 값을 균등하게 분할 
    tr_df, val_df = train_test_split(data_df, test_size=0.15, stratify=data_df['label'], random_state=2021)
    print('tr_df shape:', tr_df.shape, 'val_df shape:', val_df.shape)
    print('tr_df label distribution:\n', tr_df['label'].value_counts())
    print('val_df label distributuion:\n', val_df['label'].value_counts())

     

    3. 이미지 확인

    정상 제품과 불량 제품의 이미지를 확인해보자.

    import matplotlib.pyplot as plt
    import cv2
    %matplotlib inline
    
    def show_grid_images(image_path_list, ncols=5, augmentor=None, title=None):
        figure, axs = plt.subplots(figsize=(22, 6), nrows=1, ncols=ncols)
        for i in range(ncols):
            image = cv2.cvtColor(cv2.imread(image_path_list[i]), cv2.COLOR_BGR2RGB)
            axs[i].imshow(image)
            axs[i].set_title(title)  
            
    ok_image_list = data_df[data_df['label']=='OK']['path'].iloc[:6].tolist()
    show_grid_images(ok_image_list, ncols=6, title='OK')
    
    fail_image_list = data_df[data_df['label']=='FAIL']['path'].iloc[:6].tolist()
    show_grid_images(fail_image_list, ncols=6, title='FAIL')

    사실 육안으로 확인하기 어렵다. 그만큼, 직물 불량품을 검출하는 딥러닝 프로그램이 매우 필요하다.

     

    4. 이미지 생성

    현재 갖고 있는 이미지 데이터의 수가 워낙 작기 때문에 유의미한 결과를 도출하기 위해서는 이미지 증강(Image Augmentation)이 필요하다. 머신러닝/딥러닝은 데이터를 이용해서 모델이 학습하기 때문에 데이터 확보하는 것이 모델의 성능 개선과 직접적으로 연관되어 있다. 그렇기 때문에 한 이미지를 가지고, 좌우반전, 상하반전, 밝기 조절, 회전 등과 같이 변환을 주어 새로운 이미지로 만드는 것이 중요하다. 이 프로젝트에서는 좌우 반전 (Horizontal_flip) 기법을 사용하였다.

    # IMAGE 크기와 BATCH 크기를 위한 환경 변수 설정. 
    IMAGE_SIZE = 224
    BATCH_SIZE = 32
    
    from tensorflow.keras.preprocessing.image import ImageDataGenerator
    
    tr_generator = ImageDataGenerator(horizontal_flip=True, rescale=1/255.)
    
    tr_flow_gen = tr_generator.flow_from_dataframe(dataframe=tr_df # image file이 있는 디렉토리
                                          ,x_col='path'
                                          ,y_col='label'
                                          ,target_size=(IMAGE_SIZE, IMAGE_SIZE) # 원본 이미지를 최종 resize할 image size
                                          ,class_mode='binary' # 문자열 label을 자동 Encoding. 
                                          ,batch_size=BATCH_SIZE
                                          ,shuffle=True
                                          )
                                          
    images_array = next(tr_flow_gen)[0]
    labels_array = next(tr_flow_gen)[1]

     

    ImageDataGenerator는 fit(), flow()를 통해서 입력된 image array(numpy)를 변환 동작시킬 수 있으며, 실제 변환은 next()등으로 iteration 호출해야 한다. horizontal_flip=True 라고 설정했지만 매번 좌우반전이 되는 것이 아니라 랜덤하게 적용된다. 

     

    5. 모델 생성

    모델을 생성하는 함수를 정의한다.

    from tensorflow.keras.models import Sequential, Model
    from tensorflow.keras.layers import Input, Dense , Conv2D , Dropout , Flatten , Activation, MaxPooling2D , GlobalAveragePooling2D
    from tensorflow.keras.optimizers import Adam , RMSprop 
    from tensorflow.keras.layers import BatchNormalization
    from tensorflow.keras.callbacks import ReduceLROnPlateau , EarlyStopping , ModelCheckpoint , LearningRateScheduler
    from tensorflow.keras.applications.vgg16 import VGG16
    from tensorflow.keras.applications import ResNet50V2
    from tensorflow.keras.applications import Xception
    
    def create_model(model_name='vgg16', verbose=False):
        
        input_tensor = Input(shape=(IMAGE_SIZE, IMAGE_SIZE, 3))
        if model_name == 'vgg16':
            base_model = VGG16(input_tensor=input_tensor, include_top=False, weights='imagenet')
        elif model_name == 'resnet50':
            base_model = ResNet50V2(input_tensor=input_tensor, include_top=False, weights='imagenet')
        elif model_name == 'xception':
            base_model = Xception(input_tensor=input_tensor, include_top=False, weights='imagenet')
        
        bm_output = base_model.output
    
        x = GlobalAveragePooling2D()(bm_output)
        if model_name != 'vgg16':
            x = Dropout(rate=0.5)(x)
        x = Dense(50, activation='relu', name='fc1')(x)
        # 최종 output 출력을 softmax에서 sigmoid로 변환. 
        output = Dense(1, activation='sigmoid', name='output')(x)
    
        model = Model(inputs=input_tensor, outputs=output)
        
        if verbose:
            model.summary()
            
        return model
    model = create_model(model_name='xception')
    # 최종 output 출력을 softmax에서 sigmoid로 변환되었으므로 binary_crossentropy로 변환 
    model.compile(optimizer=Adam(0.001), loss='binary_crossentropy', metrics=['accuracy'])
    
    # 3번 iteration내에 validation loss가 향상되지 않으면 learning rate을 기존 learning rate * 0.2로 줄임.  
    rlr_cb = ReduceLROnPlateau(monitor='val_loss', factor=0.2, patience=3, mode='min', verbose=1)
    # 5번 iteration내에 validation loss가 향상되지 않으면 더 이상 학습하지 않고 종료
    ely_cb = EarlyStopping(monitor='val_loss', patience=5, mode='min', verbose=1)
    # 학습과 검증용 steps_per_epoch 계산 
    N_EPOCHS = 5
    
    history = model.fit(tr_flow_gen, epochs=N_EPOCHS,
              steps_per_epoch=int(np.ceil(tr_df.shape[0]/BATCH_SIZE)),
              validation_data=val_flow_gen, 
              validation_steps=int(np.ceil(val_df.shape[0]/BATCH_SIZE)), 
              callbacks=[rlr_cb, ely_cb])

    epoch의 값을 30 이상으로 주고 싶었지만, 시간 상의 문제로 간단하게 5로 설정하였다. 추후에 다시 30 이상을 설정해서 진행해보자.

    결과는 다음과 같다. 파란색이 validation, 빨간색이 loss이다. 학습데이터와 검정 데이터 모두 에포크가 2 이상이 되면서 loss 값이 증가하는 것을 확인하였다. 또한 정확도의 경우 학습데이터는 0.89, 검정 데이터는 0.81을 기록하였다.

     

    6. 보완할 점

    우선 데이터셋의 크기가 작기 때문에 좀 더 높은 수준의 성능을 구현하지 못하였다. 이를 위해 좌우 반전 뿐만 아니라 좀 더 다양한 형태의 데이터 증강이 필요할 것으로 사료된다. 

    또한, Early stopping 이라던가, Dropout과 같이 과적합 방지를 위한 장치를 진행하지 않았다. 때문에, 특정 피처를 과도하게 학습하는 과적합 문제가 발생할 수 있으므로, 이를 방지하기 위한 단계를 추가해야 한다. 

    전체 코드는 다음 깃헙에서 공개하고 있다. 

     

    GitHub - DAWUNHAN/deeplearning-fundamental: Deeplearning을 공부하며 정리한 레포지터리입니다.

    Deeplearning을 공부하며 정리한 레포지터리입니다. Contribute to DAWUNHAN/deeplearning-fundamental development by creating an account on GitHub.

    github.com

     

    '머신러닝 & 딥러닝' 카테고리의 다른 글

    PyTorch의 DP와 DDP  (0) 2023.12.25
    [CNN] ResNet  (0) 2022.01.23
    [CNN] GoogLeNet  (0) 2022.01.23
    [CNN] VGG  (0) 2022.01.23
    [CNN] AlexNet  (0) 2022.01.22

    댓글