지금까지 배워온 비지도학습은 Clustering 기법이나 PCA 같은 Dimension Reduction 기법이었다. 그래서 Encoder Decoder를 MSE로 학습시키는 방법은 생소하긴 했다. 하지만 나름 직관적으로 이해할 수 있었기 때문에 팀에서도 접근성이 좋은 방법 중에 하나였다.
데이터 셋 비율은 다음과 같다.
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로 설정하여 훈련을 진행했다.
👉👉 다음 챕터 읽으러 가기