낼름낼름 동동이

[ASAC 0529] CNN_Optuna & Image_Intro 본문

데이터분석/머신&딥러닝

[ASAC 0529] CNN_Optuna & Image_Intro

인죠인간 2024. 5. 29. 09:23

5월 29일의 기록


어제 저녁에 뷰티 광고 유튜브들의 음성 데이터의 푸리에 변환까지 해두었는데, 이제 변환된 각 특성들이 무엇을 의미하는지, 어떻게 활용해야 할지를 공부해야겠다. 오늘은 CNN과 image의 구조를 배우다보니까 시각적으로 보이는 것 덕분에 이해가 잘되서 기분이 좋다.

 

목차

  1. Fashion_MNIST 데이터셋을 활용한 CNN 구조의 Optuna
  2. Image_intro( 이미지의 활용 방법 학습)

 

 

1. Fashion_MNIST 데이터셋을 활용한 CNN 구조의 Optuna

 

CNN 구조의 최적화를 자동으로 해주는 Optuna를 활용해보자.

 

Optuna 설치

!pip install optuna

패키지 import & 데이터셋 불러오기

import optuna
import time

import tensorflow as tf
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropout

## fashion_mnist 활용
fashion_mnist = tf.keras.datasets.fashion_mnist
(train_X, train_y),(test_X, test_y) = fashion_mnist.load_data()
print(train_X.shape)
print(train_y.shape)
print(test_X.shape)
print(test_y.shape)

# 전처리 : 이미지 전처리 --> 1/255.0으로 정규화
train_X = train_X/255.0
test_X = test_X /255.0

# 목적 모델의 구조도에 맞춰서 데이셋을 변경해준다.
train_X = train_X.reshape(-1 ,28,28,1)  # 3d ---> 4d
test_X  = test_X.reshape(-1, 28,28,1)

기존 모델 확인

  • CNN 기존에 사용했던 모델 구조를 확인해본다.
model_vgg_fashion = tf.keras.Sequential(
    [
        # 원본 입력을 기준으로 설계.... : 224 224 RGB
        # --> 28 28 1
        Conv2D( input_shape=(28,28,1), kernel_size=(3,3),
               filters=64, padding ="same", activation="relu"),
        Conv2D(  kernel_size=(3,3),
               filters=64, padding ="same", activation="relu"),
        MaxPool2D( pool_size=(2,2), strides=(2,2)),

        Conv2D(  kernel_size=(3,3),
               filters=128, padding ="same", activation="relu"),
        Conv2D(  kernel_size=(3,3),
               filters=128, padding ="same", activation="relu"),
        MaxPool2D( pool_size=(2,2), strides=(2,2)),

        Conv2D(  kernel_size=(3,3),
               filters=256, padding ="same", activation="relu"),
        Conv2D(  kernel_size=(3,3),
               filters=256, padding ="same", activation="relu"),
        Conv2D(  kernel_size=(3,3),
               filters=256, padding ="same", activation="relu"),
        MaxPool2D( pool_size=(2,2), strides=(2,2)),
        
        
        # ===> 기본적인 이미지가 가지고 있는 특징 추출!!

        # 분류를 위한 NN
        # 1) 특징을 분류 Faltten
        Flatten(),
        # 2) 분류용 HL 적층!!!!
        Dense( units=4096, activation="relu"),
        Dense( units=4096, activation="relu"),
        # 3) 출력용 --> 내 모델의 목적에 맞춰야함!!
        #    나는 10개만 하면 됨...수정..
        Dense( units=10, activation="softmax")
    ]
)
model_vgg_fashion

Model 생성 함수

  • optuna로 최적해를 찾기 위해 model 생성함수를 정의해준다.
def create_model(
    num_layers_conv1, num_filters_conv1,
    num_layers_conv2, num_filters_conv2,
    dnn_dropout_rate, dnn_num_node):
    # 여기서는 모델을 설계하는 방식 중
    # 기존의 리스트에 레이어를 적층하는 구조 방식이 아니라..
    # 리스트의 append를 이용해서 직접 추가
    model = tf.keras.Sequential()

    # 1번 Conv Block
    model.add( Conv2D( input_shape = (28,28,1),
                       kernel_size =(3,3), filters= 32,
                       padding = "same", activation = "relu"))

    for i in range(num_layers_conv1): # 숫자만큼 conv 레이어 추가
        model.add( Conv2D(kernel_size =(3,3), filters= num_filters_conv1,
                         padding = "same", activation = "relu"))
    model.add( MaxPool2D(pool_size=(2,2), strides=(2,2)))

    ## 2번 Block
    model.add( Conv2D( kernel_size =(3,3), filters= 32,
                       padding = "same", activation = "relu"))

    for i in range(num_layers_conv2): # 숫자만큼 conv 레이어 추가
        model.add(Conv2D(kernel_size =(3,3), filters= num_filters_conv2,
                         padding = "same", activation = "relu"))
    model.add(MaxPool2D(pool_size=(2,2), strides=(2,2)))
    # --> 여기까지 특징 추출을 잘했다고 한다면,,,

    # 분류를 위한 설계를 하자
    model.add(Flatten())
    # 참고로 분류를 위한 HL의 숫자도 위의 for문 이용해서
    # 실험이 가능하다. HL의 Depth에 대한 HPT
    model.add(Dense(units = dnn_num_node, activation = "relu"))
    model.add(Dropout(rate = dnn_dropout_rate))

    model.add(Dense(units = dnn_num_node, activation = "relu"))
    model.add(Dropout(rate = dnn_dropout_rate))

    # => 하고 싶다고 하면 + 직접 HL별로 units 변수로 처리해도 된다.

    # 출력층을 설계... 고정...
    model.add(Dense(units = 10, activation = "softmax"))
    # 함수 최종 출력은 : 모델 구조도!
    return model

목적함수 설계

  • 어떤 목적으로 어떤 파라미터를 최적화하며 찾아갈지 설계한다.
  • 이번에는 val_accuacy를 극대화하는 목표로 함수를 설계
from tensorflow import keras

def objective_vgg(trail):
  # 혹시 뒤에서 keras 백단에서 살아 있을 수 있으므로
  # ==> 클리어해서 정리하고 시작하자..!
  keras.backend.clear_session()

  # 내가 테스트할 파라미터들을 제안 받는 것들 세팅
  num_layers_conv1 = trail.suggest_int("num_layers_conv1", 1, 4)
  num_filters_conv1 = trail.suggest_int("num_filters_conv1", 32, 128)
  num_layers_conv2 = trail.suggest_int("num_layers_conv2", 1, 4)
  num_filters_conv2 = trail.suggest_int("num_filters_conv2", 182, 256)
  dnn_dropout_rate = trail.suggest_float("dnn_dropout_rate", 0.1, 0.4)
  dnn_num_node = trail.suggest_int("dnn_num_node", 128, 2048)

  # =========== 여기까지는 모델의 설계도와 관련된 파라미터

  # +++ 경우에 따라서는 모델의 학습하는 영향을 미치는 파라미터도 중요하다!!
  # Learning과 관련된 HPT
  # ==> 여기서는 간단하게 최적화 방법만 선택
  #     + learning rate 학습에서 테스트도 하는 것이 중요하다.
  #     + batch_size etc 테스트의 영역!!

  optimizer = trail.suggest_categorical("optimizer", ["sgd", "adam"])

  model = create_model(num_layers_conv1, num_filters_conv1,
                        num_layers_conv2, num_filters_conv2,
                        dnn_dropout_rate, dnn_num_node)

  model.compile(optimizer = optimizer,
                loss = "sparse_categorical_crossentropy",
                metrics = ["accuracy"])

  # 제안 받은 모델 구조도 + 학습 방식에 을 적용
  # + 시간 관계상 확 줄여서 우선적으로 해보겠음
  # + callback ( early stopping을 주로 같이..)
  history = model.fit( train_X, train_y, epochs = 2, batch_size=1024,
                      validation_split = 0.25)

  # objective function의 타겟!
  # ==> val_accuracy

  score = history.history["val_accuracy"][-1]

  return score

최종 test

#실제 Test
study_my_vgg = optuna.create_study(direction = "maximize")

# 원래는 엄청 시도해야 하는데... 시도 횟수도 줄여서.. 우선 확인용으로만 한다.
study_my_vgg.optimize(objective_vgg, n_trials = 2, n_jobs = -1)

 

 

2. image_intro
  • 컴퓨터가 바라보는 이미지 파일에 대한 이해를 해보자.
  • 일반적인 이미지 데이터를 가지고 핸들링 하는 연습
  • opencv는 다양한 언어로 제작되어 있고 파이썬에서는 cv2로 세팅되어 있다.
import matplotlib.pyplot as plt
%matplotlib inline
import matplotlib.cm as cm
import numpy as np
##############
import cv2
import os
##############

 

CV2의 버전 확인

  • 실습은 colab으로 진행을 하고 있으므로 colab 기준
  • 프로젝트를 하다보면 cv를 사용할 때 참조하는 코드가 예전 코드일 수 있다.
  • cv는 버전을 타기 때문에 항상 버전 체크와 돌아가는 방식으로 세팅하는것이 중요하다!
cv2.__version__

 

이미지 파일과 색 공간

  • 이미지는 0~255 사이의 값으로 밝기가 표현되며
  • color는 3차원으로 RED, GREEN, BLUE 3가지 차원에서 동작하게 된다.

이미지는 3차원으로 RGB가 구성되고 255의 개수를 가진다.

Gray scale: 2차원

  • 0~255의 값을 통해 밝기를 표현
  • 0으로 갈수록 어둡고 255로 갈수록 밝아진다.

흑과 백 2차원의 형태로 0~255까지 밝기가 표현된다.
Gray sacle로 만들어낸 이미지

 

이미지 파일의 형식

프로젝트를 하다보면, 수집은 여러곳에서 하게되고 양식이 다양해진다. 이미지 포맷에 대한 통일도 언제나 필요하다. (사이즈, 양식, 기타 등등)

  • BMP
    • 픽셀 데이터를 압축하지 않은 상태로 저장
    • 파일 구조 간단하지만 용량이 매우 큼
  • JPG(JPEG)
    • 손실 압축(lossy compression) 사용
    • 원본 영상으로부터 픽셀값이 미세하게 달라짐
    • 파일 용량 크기가 크게 감소하는 점에서 장점
    • 디지털 카메라
  • GIF
    • 무손실 압축(losses compression)
    • 움직이는 그림인 Animation GIF 지원
    • 256 이하의 색상을 가진 영상만을 저장하고, 화질이 매우 떨어짐
  • PNG
    • Portable Network Graphics
    • 무손실 압축 사용
    • 용량은 큰 편이지만 픽셀값이 변경되지 않음
    • 𝛼 채널을 지원하여 일부분을 투명하게 설정 가능

실습 : 이미지 파일 불러오기

# 직접 아무 파일을 불러와서 사용해도 된다.
!gdown 1cHcUDb4ziI6UKr4Xq1aLRqS8JCNM-aHf

image의 shape 확인

  • 손흥민 선수의 이미지를 활용
img_path = '/content/sohn.jpg'
image = cv2.imread(img_path)
print(image.shape)

 

이미지 찍어보기

  • colab은 기본적인 RGB 구조로 안가고 BGR 구조로 인식을 해서 푸르딩딩한 형태(스머프…?)의 사진이 나오게 된다.
  • 따라서, 일반적인 cv를 Colab에서 열면 다르게 나온다.
plt.imshow(image)

스머프 흥민..

 

Colab 기준으로 수정

  • 일반적인 vscode 등에서는 cv2.imshow(image)도 먹지만, colab에서는 작동하지 않아서 아래와 같이 작성했다.
  • 다른곳에서 활용한다면 구글링해서 확인해보길 바란다.
## 내가 원하는대로 채널의 순서를 바꿔주자
imgRGB = cv2.cvtColor(image, cv2.COLOR_BGR2RGB )
plt.imshow(imgRGB)

정상으로 바뀐 모습

 

 

Colab 전용으로 패키지 설치해서 사용

  • 아래 코드를 사용하면 이렇게 확인할 수 있을 것이다.
from google.colab.patches import cv2_imshow
#cv2.imshow 함수를 cv2_imshow로 변경한다.
# cv2_imshow(img)

img = cv2.imread(img_path)
print(img.shape)
cv2_imshow(img)

plt 대신 cv2의 imshow를 사용하면 xy 축의 부분이 사라진다

 

이미지 정보 확인 : 모양/채널/채널분리 및 병합

  • 이미지를 확인해보면 array 형식이다.
  • 벡터 연산이나 슬라이싱을 해서 분리할 수 있다.

RGB에서 하나만 가져오기

  • 흑백을 만드는 과정은 채널 하나만 들고 올 수 있다.
  • 선택한 채널만 특성이 반영이 되는 형태다.
  • 컬러를 흑백으로 변환하는 룰을 통해 진행해보자.
cv2_imshow(img[:,:,0])

cv2_imshow(img[:,:,1])

 

cv2_imshow(img[:,:,2])

 

인덱스를 사용하지 않고 분리 split

# 참고) 인덱스를 사용하지 않고, 분리 split만들어 둠
r,g,b = cv2.split(img)
print(cv2_imshow(r))
print(cv2_imshow(g))
print(cv2_imshow(b))

각각 R흥민, G흥민, B흥민

 

위의 분리 처리된 이미지 채널을 다시 합쳐보기

# cv2.merge
temp = cv2.merge([r,g,b])
cv2_imshow(temp)

합체된.. 합흥민

 

colab처럼 바꿔보면 BGR

temp = cv2.merge([b,g,r])
cv2_imshow(temp)

 

컬러 이미지를 흑백으로 변환할 때 FM 방법

g = cv2.cvtColor( img, cv2.COLOR_RGB2GRAY)
# --> RGB ---> Gray
print("Org RGB:", img.shape)
print("Gray:", g.shape)
# ===> 차원을 흑백으로 두면 3D --> 2D 차원이 변경

실행 결과

cv2_imshow(g)

 

사이즈 변환 실습

옆으로 이미지 밀기

tx = 600
ty = 800
translation_matrix = np.float32([[1, 0, tx], [0, 1, ty]])
translated_image = cv2.warpAffine(imgRGB, translation_matrix, (image.shape[1], image.shape[0]))

plt.imshow(translated_image)

밀려나면서 잘려있는 이미지가 확인된다.

 

45각도로 기울여서 확인

angle = 45
height, width = imgRGB.shape[:2]
center = (width // 2, height // 2)
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated_image = cv2.warpAffine(imgRGB, rotation_matrix, (width, height))
plt.imshow(rotated_image)

 

45도 기울어진 모습

 

scale을 0.05로 줄이기(해상도 줄이기)

scale_width = 0.05
scale_height = 0.05

new_width = int(imgRGB.shape[1] * scale_width)
new_height = int(imgRGB.shape[0] * scale_height)

scaled_image = cv2.resize(imgRGB, (new_width, new_height))

plt.imshow(scaled_image)