Chapter 3: 프로젝트 후반부

박해극·2024년 4월 20일
0

A. U-Net Autoencoder 학습


U-Net 을 활용한 비지도 학습

지금까지 배워온 비지도학습은 Clustering 기법이나 PCA 같은 Dimension Reduction 기법이었다. 그래서 Encoder Decoder를 MSE로 학습시키는 방법은 생소하긴 했다. 하지만 나름 직관적으로 이해할 수 있었기 때문에 팀에서도 접근성이 좋은 방법 중에 하나였다.

데이터 셋

데이터 셋 비율은 다음과 같다.

  1. AudioSet 60%
  2. CryCeleb 30%
  3. Kaggle Data 10%
    a. https://www.kaggle.com/datasets/raiyanjahangir1939/infant-cry-sounds

CryCeleb 같은 경우 18000개의 오디오 데이터가 있지만 대부분 1초가량의 데이터가 많아 1초를 5번 루프를 돌려 재구성했다. 그래서 CryCeleb이 비율이 높았지만 루프에 대한 Feature 즉 음색 보단 Temporal Feature에 집중이 될 우려가 있어 비율을 낮춰서 데이터 셋을 구성했다.

전처리

우선 멘토님께서 말했듯이 Mel Spectrogram 을 input으로 써보기로 했다.

Mel Spectrogram 에 대해 다시 설명을 보고 싶으시면 아래 링크를 클릭해주세요!
Mel-spectrogram 이란?

아기 울음소리를 Mel Spectrogram으로 변환 후 (128, 128) 로 모든 데이터를 맞춰주었다. 여기서 첫 번째 128은 Mel Filter Bank의 Filter 개수이다. 두 번째 128은 Time Step 이다. 자세한 설명은 아래 링크에 들어가서 영상과 설명을 보는 걸 추천한다. 영어로 되어 있어 한글 자료를 찾아 이해하고 링크에 있는 영상을 꼭 보는 걸 추천한다.

https://learn.flucoma.org/reference/melbands/

전처리 코드

# Mel Spectrogram 패딩 혹은 자르기
reshaped = []

for mspc in mel_spec_re:
    h, w = mspc.shape
    
    if w < h:
        left_width = (h - w) // 2
        pad_remainder = (h - w) % 2
        padded = np.pad(mspc, pad_width = ((0, 0), (left_width, left_width + pad_remainder)), mode = 'constant', constant_values=0)
        reshaped.append(padded)
    else:
        trunc_mspc = mspc[:, :h]
        reshaped.append(trunc_mspc)

모델링

이번에도 팀장님이 솔선수범하게 U-net을 직접 구현하였다.

U-net 코드

from tensorflow.keras.layers import LeakyReLU

def build_unet(input_shape):
    # Define the input layer
    inputs = tf.keras.Input(shape=input_shape)

    # Encoder Layer
    # [1]
    conv0 = layers.Conv2D(32, activation='relu', kernel_size = 3, padding='same')(inputs) # 104  52 26
    conv1 = layers.Conv2D(32, activation='relu', kernel_size = 3, padding='same')(conv0)  # Skip connection으로 Expanding path로 이어질 예정, 
    conv2 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(conv1) # 64

    # [2]
    conv3 = layers.Conv2D(64, activation='relu', kernel_size = 3, padding='same')(conv2) # 
    conv4 = layers.Conv2D(64, activation='relu', kernel_size = 3, padding='same')(conv3) # 
    conv5 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(conv4) # 32

    # [3]
    conv6 = layers.Conv2D(128, activation='relu', kernel_size=3, padding='same')(conv5) # 
    conv7 = layers.Conv2D(128, activation='relu', kernel_size=3, padding='same')(conv6) # 
    conv8 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(conv7) # 16

    # [4]
    conv9 = layers.Conv2D(256, activation='relu', kernel_size=3, padding='same')(conv8) # 
    conv10 = layers.Conv2D(256, activation='relu', kernel_size=3, padding='same')(conv9) # 
    conv11 = layers.MaxPooling2D(pool_size=(2, 2), strides=(2, 2))(conv10) # 8

    # [5]
    conv12 = layers.Conv2D(512, activation='relu', kernel_size=3, padding='same')(conv11) # 
    conv13 = layers.Conv2D(512, activation='relu', kernel_size=3, padding='same')(conv12) # 

    # Contracting path

    # The output of the last encoder layer
    encoder_output = conv10

    # Decoder Layer
    # [6]
    trans01 = layers.Conv2DTranspose(256, kernel_size=2, strides=(2, 2), activation='relu')(conv13) # 
    # crop01 = layers.Cropping2D(cropping=(4, 4))(conv4) 
    concat01 = layers.concatenate([trans01, conv10], axis=-1) # 

    # [7]
    conv14 = layers.Conv2D(256, activation='relu', kernel_size=3, padding='same')(concat01) # 
    conv15 = layers.Conv2D(256, activation='relu', kernel_size=3, padding='same')(conv14)  # 

    trans02 = layers.Conv2DTranspose(128, kernel_size=2, strides=(2, 2), activation='relu')(conv15)
    # crop02 = layers.Cropping2D(cropping=(16, 16))(conv1) # 
    concat02 = layers.concatenate([trans02, conv7], axis=-1)

    # [8]
    conv16 = layers.Conv2D(128, activation='relu', kernel_size=3, padding='same')(concat02) # 
    conv17 = layers.Conv2D(128, activation='relu', kernel_size=3, padding='same')(conv16)  # 

    trans03 = layers.Conv2DTranspose(64, kernel_size=2, strides=(2, 2), activation='relu')(conv17)
    # crop02 = layers.Cropping2D(cropping=(16, 16))(conv1) # 
    concat03 = layers.concatenate([trans03, conv4], axis=-1)

    # [9]
    conv18 = layers.Conv2D(64, activation='relu', kernel_size=3, padding='same')(concat03) # 
    conv19 = layers.Conv2D(64, activation='relu', kernel_size=3, padding='same')(conv18)

    trans04 = layers.Conv2DTranspose(32, kernel_size=2, strides=(2, 2), activation='relu')(conv19)
    # crop02 = layers.Cropping2D(cropping=(16, 16))(conv1) # 
    concat04 = layers.concatenate([trans04, conv1], axis=-1)

    # [10]
    conv20 = layers.Conv2D(32, activation='relu', kernel_size=3, padding='same')(concat04) # 
    conv21 = layers.Conv2D(32, activation='relu', kernel_size=3, padding='same')(conv20)

    # Expanding path

    # Define the output layer for the U-Net
    outputs = layers.Conv2D(1, kernel_size=1)(conv21)

    # Full U-Net model
    unet_model = tf.keras.Model(inputs=inputs, outputs=outputs)

    # Return the full model and the encoder part
    encoder_model = tf.keras.Model(inputs=inputs, outputs=encoder_output)
    return unet_model, encoder_model

# Define your input shape based on your mel-spectrogram dimensions
input_shape = (128, 128, 1)  # Example shape
unet, encoder = build_unet(input_shape)

optimizer 설정

손실함수는 MSE로 설정하여 훈련을 진행했다.


👉👉 다음 챕터 읽으러 가기

profile
안뇽하세요

0개의 댓글