[opencv] 05. 개인 토이프로젝트 -2 전처리

이지연·2020년 12월 20일

사용한 라이브러리

  • numpy
  • matplotlib
  • keras

모델을 만들기 위해서 필요한 Input(입력층), Activation(활성화 함수 - relu 음수 0, 양수는 그래도 표현해준다), Conv2D(이미지 필터링해준다),Flatten(1차원 배열을 다차원 배열로 재구성/재배열 ),Dense(모든 입력이 가중치에 의해 모든 출력에 연결되는 선형 연산),MaxPooling2D(polling층 가장 자극이 강한 것만 다시 재추출)를 import한다.

from keras.layers import Input, Activation, Conv2D, 
Flatten, Dense, MaxPooling2D

케라스 모델 import

from keras.models import Model, load_model

가상의 이미지 데이터를 생성하기 위해서 ImageDataGenerator를 씀

from keras.preprocessing.image import ImageDataGenerator

가장 validation performance 가 좋았던 모델을 저장

from keras.callbacks import ModelCheckpoint, ReduceLROnPlateau

전처리

1단계 : 해당 눈 영역 라벨링 작업

left_x_train : 왼쪽 눈 영역의 이미지
left_x_val : 왼쪽 눈 영역을 라벨링 데이터 0(감은눈) 또는 1(뜬눈)

2단계 : Data Augmentation(데이터 증가)

(1)
훈련용 데이터를 증가시키고 성능을 향상시키기 위해 하는 방식이다.
이 방식은 훈련 성능을 향상시키고, 훈련용 데이터에만 적응하는 과적합을 막기 위해서 필요하다.

과적합이란, 말그대로 과도하게 적합한 것을 의미한다. 너무 훈련을 잘해서 정해진 값만 정확하게 판단하려고 파라미터의 값들을 맞추는 것이다. 예를 들어, 학습 이미지가 2개뿐인데, 그 두개 이외에 눈 이미지 데이터를 볼 때 정확하게 판단을 하지 못해 분명 눈을 떴는데 학습한 이미지와 다르게 눈이 작아서 판단을 미스해 음악을 재생시키는 문제가 발생할 수 있다.

train_datagen에는 훈련 데이터로 회전, 넓이/높이 등의 범위를 정해주어 다양한 경우를 만들어 주었다. 하지만 검증 데이터는 굳이 변형할 필요가 없다고 판단해 생성만 했다.

(2)
왼쪽, 오른쪽 트레이닝 이미지를 생산하는데, batch_size에는 한 횟수의 훈련 및 검증에 사용될 데이터의 양을 매번 32개씩 하도록 정해주었고,shuffle=True를 해 순서를 섞어주었다.

(1)
train_datagen = ImageDataGenerator(
    rotation_range=10,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2
)

(2)
print("왼쪽")
left_train_generator = train_datagen.flow(
    x=left_x_train, y=left_y_train,
    batch_size=32, 
)

left_val_generator = val_datagen.flow(
    x=left_x_val, y=left_y_val,
    batch_size=32,
    shuffle=False
)

print("오른쪽")
right_train_generator = train_datagen.flow(
    x=right_x_train, y=right_y_train,
    batch_size=32,
    shuffle=True
)

right_val_generator = val_datagen.flow(
    x=right_x_val, y=right_y_val,
    batch_size=32,
    shuffle=False
)

3단계 :Build Model, 모델 만들기

cnn모델은 인풋레이어(인풋 정보 정의), 히든레이어(conv갯수에 따라 몇층인지), 아웃풋레이어(3차원데이터를 분류하기 편한 1차원으로 변경 및 활성화함수로 분류)로 진행된다.

(1) input층으로 26*34 사이즈의 이미지의 크기이며, 채널을 1로 정해 흑백으로 정보를 넣어줍니다.

(2)convolutional 층을 쌓아 filter 크기(3*3), strides filter가 몇칸씩 이동하는지, padding same은 공간 정보 유지를 원하게 했습니다.
activation 활성화함수인 relu 음수 0, 양수는 그대로 표현해서 쓰이기 편하게 만들어주었습니다.

ㄷㄷㄷㄷㄷ

ㄷㅌㅌㅌㄷ

ㄷㅌㅌㅌㄷ -> 3*3을 5*5변경

ㄷㅌㅌㅌㄷ

ㄷㄷㄷㄷㄷ

(3) polling층 가장 자극이 강한 것만 다시 재추출하는 maxpolling, poll_size = 2

1 19 ---> 55(가장 강함)

55 34

숫자가 강할 수록 가장 자극이 강하고, 보통 polling은 maxpolling을 사용하면 된다고 알고 있습니다.

자, 이렇게 하면 1층 쌓는 것은 끝입니다. 보통 conv와 polling은 1개의 층으로 칩니다.

이걸 3번 반복해서 층을 3번 쌓았습니다. 층을 높이 쌓을 수록 세밀하게 할 수 있고, 모델 성능도 높일 수 있습니다.

(4) 3차원데이터를 1차원으로 변경하는 아웃풋레이어을 진행하게 됩니다. 활성화 함수인 relu 음수 이면 0 ,양수 이면 1로 변경시킵니다.

output에 결과를 도출하는 층, 활성화함수 sigmoid 사용합니다. 이진분류로 0 혹은 1로 결과가 나오고, if 다중분류라면 softmax 사용하고 %(퍼센트)로 결과가 나옵니다.

(1)
inputs = Input(shape=(26, 34, 1)) #input층

(2)
net = Conv2D(32, kernel_size=3, strides=1, padding='same', activation='relu')(inputs) 

net = MaxPooling2D(pool_size=2)(net) 
#1층 끝

net = Conv2D(64, kernel_size=3, strides=1, padding='same', activation='relu')(net)
net = MaxPooling2D(pool_size=2)(net)
#2층 끝

net = Conv2D(128, kernel_size=3, strides=1, padding='same', activation='relu')(net)
net = MaxPooling2D(pool_size=2)(net)
#3층 끝

(4)
net = Flatten()(net) 

net = Dense(512)(net)
net = Activation('relu')(net) 
net = Dense(1)(net)
outputs = Activation('sigmoid')(net) 

left_model = Model(inputs=inputs, outputs=outputs)

left_model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])

left_model.summary()

right_model = Model(inputs=inputs, outputs=outputs)

right_model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])

right_model.summary()
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_4 (InputLayer)         [(None, 26, 34, 1)]       0         
_________________________________________________________________
conv2d_9 (Conv2D)            (None, 26, 34, 32)        320       
_________________________________________________________________
max_pooling2d_9 (MaxPooling2 (None, 13, 17, 32)        0         
_________________________________________________________________
conv2d_10 (Conv2D)           (None, 13, 17, 64)        18496     
_________________________________________________________________
max_pooling2d_10 (MaxPooling (None, 6, 8, 64)          0         
_________________________________________________________________
conv2d_11 (Conv2D)           (None, 6, 8, 128)         73856     
_________________________________________________________________
max_pooling2d_11 (MaxPooling (None, 3, 4, 128)         0         
_________________________________________________________________
flatten_3 (Flatten)          (None, 1536)              0         
_________________________________________________________________
dense_6 (Dense)              (None, 512)               786944    
_________________________________________________________________
activation_6 (Activation)    (None, 512)               0         
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 513       
_________________________________________________________________
activation_7 (Activation)    (None, 1)                 0         
=================================================================
Total params: 880,129
Trainable params: 880,129
Non-trainable params: 0
_________________________________________________________________
Model: "functional_13"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_4 (InputLayer)         [(None, 26, 34, 1)]       0         
_________________________________________________________________
conv2d_9 (Conv2D)            (None, 26, 34, 32)        320       
_________________________________________________________________
max_pooling2d_9 (MaxPooling2 (None, 13, 17, 32)        0         
_________________________________________________________________
conv2d_10 (Conv2D)           (None, 13, 17, 64)        18496     
_________________________________________________________________
max_pooling2d_10 (MaxPooling (None, 6, 8, 64)          0         
_________________________________________________________________
conv2d_11 (Conv2D)           (None, 6, 8, 128)         73856     
_________________________________________________________________
max_pooling2d_11 (MaxPooling (None, 3, 4, 128)         0         
_________________________________________________________________
flatten_3 (Flatten)          (None, 1536)              0         
_________________________________________________________________
dense_6 (Dense)              (None, 512)               786944    
_________________________________________________________________
activation_6 (Activation)    (None, 512)               0         
_________________________________________________________________
dense_7 (Dense)              (None, 1)                 513       
_________________________________________________________________
activation_7 (Activation)    (None, 1)                 0         
=================================================================
Total params: 880,129
Trainable params: 880,129
Non-trainable params: 0

4단계 train : 훈련 단계

모델이 가장 validation performance 가 좋았던 모델을 저장시키
epochs는 훈련 루틴 횟수입니다. 왼쪽 눈과 오른쪽 둘다 모두 해줍니다.

model.fit_generator(
    left_train_generator, epochs=50, validation_data=left_val_generator,
    callbacks=[
        ModelCheckpoint('datas/models/left_eye.h5', monitor='val_acc', save_best_only=True, mode='max', verbose=1),
        ReduceLROnPlateau(monitor='val_acc', factor=0.2, patience=10, verbose=1, mode='auto', min_lr=1e-05)
    ]
)

그러면 돌면서 손실과 정확도가 나옵니다. 또, 검증데이터의 손실과 정확도 도 함께 출력되는 것을 볼 수 있습니다.

이 두개가 차이로 과적합인지 아닌지를 판단할 수 있습니다.

5단계 : Seaborn으로 시각화

sklearn과 Seaborn을 이용했습니다.

먼저, sklearn에서 accuracy_score, confusion_matrix를 사용하는데,
accuracy_score는 전체 대비 정확하게 예측한 개수의 비율이고,
분류결과표(Confusion Matrix)는 타겟의 원래 클래스와 모형이 예측한 클래스가 일치하는지는 갯수로 센 결과를 표나 나타낸 것입니다. 정답 클래스는 행(row)으로 예측한 클래스는 열(column)로 나타냅니다.

더 자세한 내용은 아래 사이트에서 보시면 됩니다.
https://gaussian37.github.io/ml-concept-ml-evaluation/

두번째로, Seaborn은 변수간의 상관관계를 찾기 위해 쓰는 시각화 라이브러리 중 하나입니다.

그 중 저는 heatmap으로 그렸는데, 이건 열을 의미하는 heat과 지도를 뜻하는 map을 합친 단어로 데이터들의 배열을 색상으로 표현해주는 그래프입니다. heatmap을 사용하면 두 개의 카테고리 값에 대한 값 변화를 한 눈에 알기 쉽습니다. 저는 눈을 감은지, 떠있는지를 라벨링 0과 1로 만들었는데 이 두값이 정확히 나오는지 확인하기 위해 사용할 것입니다.

(1) 전처리한 왼쪽 눈 모델을 갖고옵니다. 그런 다음 왼쪽 눈 이미지를 예측하는데, 모델을 아래와 같이 예측 하고, 그 값이 0.5보다 크면 행렬연산으로 int로 타입을 변경해줍니다.

그리고 분류결과표는 원래 모형과 예측한 값이 일치하는 지 갯수로 나타내 표로 나타내도록 했습니다.

from sklearn.metrics import accuracy_score, confusion_matrix
import seaborn as sns

(1)
model = load_model('datas/models/left_eye.h5')

y_pred = model.predict(left_x_val/255.)
y_pred_logical = (y_pred > 0.5).astype(np.int)

print ('test acc: %s' % accuracy_score(left_y_val, y_pred_logical))
cm = confusion_matrix(left_y_val, y_pred_logical)
sns.heatmap(cm, annot=True) #annot : 각 cell의 값 표기 유무

다음을 보면 x축이 1, y축이 1일때 값이 16진수로 나오는 것과 0,0일때도 마찬가지입니다.

그렇지만, x축이 0일때 y축이 1인 것도 2개라고 나오는 것을 볼 수 있습니다.

여기까지 전처리 과정을 끝냈습니다.

profile
Everyday STEP BY STEP

0개의 댓글