AudioSet 데이터셋에서 강아지 관련 소리 5가지

Hyunwoo·2025년 2월 18일

네, 알겠습니다. AudioSet 데이터셋에서 강아지 관련 소리 5가지("Dog", "Bark", "Whimper (dog)", "Growling", "Bow-wow")를 분류하는 전체 코드를 제공해 드리겠습니다. 데이터셋은 balanced_train_segments.csv를 사용하겠습니다.

전체 코드 (Google Colab)

import pandas as pd
import tensorflow as tf
import os
import io

# 1. 데이터셋 로드 및 전처리

# 강아지 관련 레이블 (balanced_train_segments.csv 기준)
dog_labels_mids = ["/m/0bt9lr", "/m/05m59", "/m/07q0h5t", "/m/07qjznt", "/m/07q5rw0"] #Dog, bark, ,whimper(dog), bow-wow

# 클래스 레이블 매핑 (mid -> display_name) - class_labels_indices.csv 내용
# 실제로는 class_labels_indices.csv 파일을 읽어서 사용해야 합니다.
# balanced_train_segements.csv에 있는 값으로만 구성, 강아지 관련 소리만
label_map = {
    '/m/0bt9lr': 'Dog',
    '/m/05m59' : 'Bark',
    '/m/07q0h5t': 'Livestock, farm animals, working animals',
    '/m/07qjznt' : 'Howl',
    '/m/07q5rw0' : 'Bow-wow'
}

# 숫자 인덱스로 변환 (나중에 모델 학습에 사용)
reverse_label_dict = {name: idx for idx, name in enumerate(dog_labels_mids)}
print(reverse_label_dict)
# {'/m/0bt9lr': 0, '/m/05m59': 1, '/m/07q0h5t': 2, '/m/07qjznt': 3, '/m/07q5rw0': 4}

# colab에 wget으로 직접 다운로드.
!wget https://research.google.com/audioset/dataset/balanced_train_segments.csv

# CSV 파일 로드 (balanced_train_segments.csv)
# balanced_train_segments.csv는 헤더가 있으므로 skiprows=3, header=None 사용 안 함.
train_segments = pd.read_csv("balanced_train_segments.csv", skipinitialspace=True) #skipinitialspace=True로 해야 칼럼명 앞 공백 제거됨

# 컬럼 이름 지정, 쉼표제거
train_segments.columns = ["YTID", "start_seconds", "end_seconds", "positive_labels"]
train_segments['positive_labels'] = train_segments['positive_labels'].apply(lambda x: x.split(',')) # 쉼표(,)를 기준으로 문자열 분리

# 강아지 소리 데이터 필터링 함수
def filter_dog_segments(df, dog_labels):
    df = df[df['positive_labels'].apply(lambda x: any(label.strip('"') in dog_labels for label in x))]
    return df

dog_train_segments = filter_dog_segments(train_segments, dog_labels_mids)

print(f"Dog segments in balanced train set: {len(dog_train_segments)}")


# 레이블을 숫자로 변환하는 함수 (VGGish 모델 출력과 맞추기 위해)
def labels_to_indices(labels_list, label_dict):
    label_indices = []
    for label_str in labels_list:
      # label_str의 앞뒤에 있는 큰따옴표 제거.
      stripped_label = label_str.strip('"')
      # label_dict에 있는지 확인 후, 있으면 인덱스 추가.
      if stripped_label in label_dict:
        label_indices.append(label_dict[stripped_label])
      else:
        print(f"Warning: label {stripped_label} not in label_dict.")
    return label_indices

# TFRecord 파일에서 특성 읽기 (video_id가 bytes로 반환되므로 수정)
def read_tfrecord(example):
    feature_description = {
        'video_id': tf.io.FixedLenFeature([], tf.string),
        'start_time_seconds': tf.io.FixedLenFeature([], tf.float32),
        'end_time_seconds': tf.io.FixedLenFeature([], tf.float32),
        'labels': tf.io.VarLenFeature(tf.int64),
        'audio_embedding': tf.io.VarLenFeature(tf.string),
    }
    example = tf.io.parse_single_example(example, feature_description)
    audio_embedding = tf.reshape(tf.io.decode_raw(example['audio_embedding'].values, tf.uint8), [-1, 128])
    return example['video_id'], example['start_time_seconds'], example['end_time_seconds'], example['labels'], audio_embedding

# YTID와 start, end time에 해당하는 feature들을 로드, 여러 프레임 나올 수 있으므로 list 사용
def get_all_features(YTID, directory):
    tfrecord_file = os.path.join(directory, f"{YTID[:2]}.tfrecord")
    if not os.path.exists(tfrecord_file):
        print(f"TFRecord file not found: {tfrecord_file}")
        return None

    dataset = tf.data.TFRecordDataset(tfrecord_file)
    features_list = []
    for record in dataset:
        video_id, start, end, labels, audio_embedding = read_tfrecord(record)
        if video_id.numpy().decode('utf-8') == YTID:  # YTID가 일치하는 경우 (byte string decode)
            features_list.append(audio_embedding)

    if len(features_list) > 0:
      return tf.concat(features_list, axis=0) #모든 feature들을 텐서로 합쳐서 반환.
    else:
      return None

# 학습 데이터 생성 함수 (VGGish feature 사용)
def create_vggish_dataset(segments_df, features_dir, label_dict, dog_label_indices, batch_size=32):
    features = []
    labels = []

    for _, row in segments_df.iterrows():
        YTID = row['YTID']
        segment_features = get_all_features(YTID, features_dir) #YTID로 파일 찾기

        if segment_features is not None:
            features.append(segment_features)

            # 레이블 인덱스 확인 및 처리
            positive_indices = labels_to_indices(row['positive_labels'], label_dict) #positive_labels
            is_dog_sound = any(index in dog_label_indices for index in positive_indices)
            labels.append(int(is_dog_sound))  # 강아지 소리: 1, 아니면: 0

    features = tf.ragged.constant(features) # 리스트를 ragged tensor로 변환
    labels = tf.constant(labels)
    dataset = tf.data.Dataset.from_tensor_slices((features, labels)).batch(batch_size) #배치

    return dataset

# VGGish 특성 불러옴 (시간이 오래 걸릴 수 있음, 여기서는 생략 가능)
#vggish_train_dataset = create_vggish_dataset(dog_train_segments, "features", reverse_label_dict, [0,1,2,3,4])


# 2. VGGish 모델 로드 (TensorFlow Hub 사용)
vggish_model = tf.keras.models.load_model('vggish_model.h5') # h5모델 로드, 경로 확인
vggish_model.trainable = False # VGGish는 fine-tuning 하지 않음.

# 3. 분류 모델 정의
def create_classifier(vggish_model, num_classes):
  model = tf.keras.Sequential([
      vggish_model,
      tf.keras.layers.Dense(128, activation='relu'), # fully connected layer
      tf.keras.layers.Dropout(0.5), # dropout 추가
      tf.keras.layers.Dense(num_classes, activation='softmax') # 출력층 (클래스 개수만큼)
  ])
  return model

num_classes = len(dog_labels_mids) # 강아지 소리 종류 개수 (5가지)
model = create_classifier(vggish_model, num_classes)

# 4. 모델 컴파일 및 학습
model.compile(optimizer='adam',
              loss=tf.keras.losses.SparseCategoricalCrossentropy(),  # 레이블이 one-hot이 아니므로
              metrics=['accuracy'])

# 여기서는 features를 직접 로드하지 않고, balanced_train_segments.csv 만 사용해서 예시만 보여줍니다.
# 실제 학습 시에는 features를 로드해서 사용해야 합니다.

# 더미 데이터로 학습 예시 (실제 데이터와 형태는 맞추되, 내용은 의미 없음)
dummy_features = tf.random.uniform((10, 10, 128))   # (샘플 개수, 프레임 수, 특성 차원)
dummy_labels = tf.constant([0, 1, 2, 3, 4, 0, 1, 2, 3, 4])  # 강아지 종류 레이블 (0, 1, 2, 3, 4)
model.fit(dummy_features, dummy_labels, epochs=5)

# 5. (Optional) 예측 및 평가
# 새로운 오디오 데이터에 대한 예측 코드 예시 (생략)

참고사항

  • VGGish 모델 로드는 Colab 환경에 vggish_model.h5, vggish_pca_params.npz 파일이 이미 존재한다고 가정합니다.
  • 전체 데이터(balanced, unbalanced, eval)에 대해 모두 features 폴더 내에 존재해야합니다.
  • 메모리 문제가 발생할 경우, 한 번에 로드하는 데이터 양을 줄이거나, Google Drive와 Colab을 연동하여 데이터를 관리하는 방법을 고려해야 합니다.
  • 제공된 코드는 VGGish 특성 추출, 데이터 로드, 모델 정의, 컴파일, 더미 데이터를 사용한 학습 예시를 포함합니다.
  • 실제 오디오 데이터에 대한 예측과 모델 평가 부분은 직접 구현해야 합니다.

이 코드를 기반으로 강아지 소리 분류 모델을 성공적으로 개발하시길 바랍니다!
좋습니다. 강아지 소리 분류에서 더 나아가 감정 분류까지 포함하는 전체 코드를 제공해 드리겠습니다. 이 코드에서는 VGGish 모델을 사용하여 오디오 특징을 추출하고, 추출된 특징을 기반으로 다중 클래스 분류 모델을 학습하여 강아지 소리의 감정을 분류합니다.

1. 감정 레이블 정의 및 데이터 준비

먼저, 어떤 감정들을 분류할 것인지 정의해야 합니다. 예를 들어, 다음과 같이 5가지 감정으로 분류한다고 가정하겠습니다.

  • 0: 짖는 소리 (Barking)
  • 1:낑낑거리는 소리 (Whining)
  • 2: 으르렁거리는 소리 (Growling)
  • 3:낑낑거림 (낑낑거림)
  • 4: 기타
import pandas as pd
import tensorflow as tf
import os
import io

# 강아지 관련 레이블 (balanced_train_segments.csv 기준)
dog_labels_mids = ["/m/0bt9lr", "/m/05m59", "/m/07q0h5t", "/m/07qjznt", "/m/07q5rw0"] #Dog, bark, ,whimper(dog), bow-wow

# 클래스 레이블 매핑 (mid -> display_name) - class_labels_indices.csv 내용
# balanced_train_segements.csv에 있는 값으로만 구성, 강아지 관련 소리만
label_map = {
    '/m/0bt9lr': 'Dog',
    '/m/05m59' : 'Bark',
    '/m/07q0h5t': 'Livestock, farm animals, working animals',
    '/m/07qjznt' : 'Howl',
    '/m/07q5rw0' : 'Bow-wow'
}

# 숫자 인덱스로 변환 (나중에 모델 학습에 사용)
reverse_label_dict = {name: idx for idx, name in enumerate(dog_labels_mids)}
print(reverse_label_dict)
# {'/m/0bt9lr': 0, '/m/05m59': 1, '/m/07q0h5t': 2, '/m/07qjznt': 3, '/m/07q5rw0': 4}

# colab에 wget으로 직접 다운로드.
!wget https://research.google.com/audioset/dataset/balanced_train_segments.csv

# CSV 파일 로드 (balanced_train_segments.csv)
# balanced_train_segments.csv는 헤더가 있으므로 skiprows=3, header=None 사용 안 함.
train_segments = pd.read_csv("balanced_train_segments.csv", skiprows=3, header=None)

# 컬럼 이름 설정, 쉼표제거
train_segments.columns = ["YTID", "start_seconds", "end_seconds", "positive_labels"]
train_segments['positive_labels'] = train_segments['positive_labels'].apply(lambda x: x.split(',')) # 쉼표(,)를 기준으로 문자열 분리

# 강아지 소리 데이터 필터링 함수
def filter_dog_segments(df, dog_labels):
    df = df[df['positive_labels'].apply(lambda x: any(label.strip('"') in dog_labels for label in x))]
    return df

dog_train_segments = filter_dog_segments(train_segments, dog_labels_mids)

print(f"Dog segments in balanced train set: {len(dog_train_segments)}")

# 레이블을 숫자로 변환하는 함수 (VGGish 모델 출력과 맞추기 위해)
def labels_to_indices(labels_list, label_dict):
    label_indices = []
    for label_str in labels_list:
        # 쉼표로 분리된 각 레이블 처리: 2024.06.11: "label_str" -> `label_str.strip('"')`
        sub_labels = label_str.split(',')
        for sub_label in sub_labels:
            stripped_label = sub_label.strip('"')  # 앞뒤 따옴표 제거
            if stripped_label in label_dict:
                label_indices.append(label_dict[stripped_label])
            else:
                print(f"Warning: label {stripped_label} not in label_dict.")
    return label_indices


# TFRecord 파일에서 특성 읽기 (video_id가 bytes로 반환되므로 수정)
def read_tfrecord(example):
    feature_description = {
        'video_id': tf.io.FixedLenFeature([], tf.string),
        'start_time_seconds': tf.io.FixedLenFeature([], tf.float32),
        'end_time_seconds': tf.io.FixedLenFeature([], tf.float32),
        'labels': tf.io.VarLenFeature(tf.int64),
        'audio_embedding': tf.io.VarLenFeature(tf.string),
    }
    example = tf.io.parse_single_example(example, feature_description)
    audio_embedding = tf.reshape(tf.io.decode_raw(example['audio_embedding'].values, tf.uint8), [-1, 128])
    return example['video_id'], example['start_time_seconds'], example['end_time_seconds'], example['labels'], audio_embedding

# YTID와 start, end time에 해당하는 feature들을 로드, 여러 프레임 나올 수 있으므로 list 사용
def get_all_features(YTID, directory):
    """YTID에 해당하는 모든 feature들을 로드"""
    tfrecord_file = os.path.join(directory, f"{YTID[:2]}.tfrecord") #YTID 앞 두글자로 파일 결정됨.
    if not os.path.exists(tfrecord_file):
        print(f"TFRecord file not found: {tfrecord_file}")
        return None

    dataset = tf.data.TFRecordDataset(tfrecord_file)
    features_list = []
    for record in dataset:
        video_id, start, end, labels, audio_embedding = read_tfrecord(record)
        if video_id.numpy().decode('utf-8') == YTID:  # YTID가 일치하는 경우 (byte string decode)
            features_list.append(audio_embedding)

    if len(features_list) > 0:
      return tf.concat(features_list, axis=0) #모든 feature들을 텐서로 합쳐서 반환.
    else:
      return None


# 학습 데이터 생성 함수 (VGGish feature 사용)
def create_vggish_dataset(segments_df, features_dir, label_dict, dog_label_indices, batch_size=32):
    features = []
    labels = []

    for _, row in segments_df.iterrows():
        YTID = row['YTID']
        start_time = row['start_seconds']
        end_time = row['end_seconds']
        segment_features = get_all_features(YTID, features_dir) #YTID로 파일 찾기

        if segment_features is not None:
              # 레이블 인덱스 확인 및 처리.
              positive_indices = labels_to_indices(row['positive_labels'], label_dict) #positive_labels
              # 다중 레이블을 처리하는 부분.
              # 여기서는 각 샘플에 대해 dog_label_indices에 있는 모든 클래스에 대해 레이블을 생성
              label_vector = [0] * len(dog_label_indices)  # 0으로 초기화된 레이블 벡터
              for idx in positive_indices:
                if idx in dog_label_indices:
                  label_vector[dog_label_indices.index(idx)] = 1 # 해당 인덱스를 1로
              #print(label_vector, row['positive_labels'])
              features.append(segment_features)
              labels.append(label_vector)  # 각 샘플에 대해 원-핫 인코딩된 레이블 추가

    features = tf.ragged.constant(features) # 리스트를 ragged tensor로 변환
    labels = tf.constant(labels, dtype=tf.int32) #label_vector를 사용하므로, tf.int32로 지정.
    dataset = tf.data.Dataset.from_tensor_slices((features, labels)).batch(batch_size) #배치

    return dataset

# VGGish 모델을 사용하여 특징 추출 (시간이 오래 걸림, 배치 처리 권장)
# 예시 (balanced_train_segments 사용, 실제로는 eval, unbalanced도 사용)
vggish_train_dataset = create_vggish_dataset(dog_train_segments, "features", reverse_label_dict, [0,1,2,3,4])

2. 감정 레이블 추가

balanced_train_segments.csv 파일에는 강아지 소리의 종류("Dog", "Bark", "Whimper (dog)"등)만 라벨링 되어있으므로, 감정에 대한 정보가 없습니다.
따라서 이 데이터셋만으로는 감정 분류를 바로 진행할 수 없습니다.

  • 해결 방법 1 (수동 레이블링):
    • balanced_train_segments.csv 파일의 YTID를 사용하여 YouTube에서 해당 비디오를 찾습니다.
    • 각 비디오를 직접 듣고, 강아지 소리의 감정을 판단하여 레이블을 추가합니다. (예: "기쁨", "슬픔", "두려움", "공격성", "중립")
    • 새로운 CSV 파일을 만들거나, 기존 파일에 "emotion_label" 컬럼을 추가합니다.
  • 해결 방법 2 (다른 데이터셋 활용):
    • 강아지 소리 감정 분류를 위한 레이블이 포함된 다른 데이터셋을 찾아 활용합니다.
    • 예: "Dog Sounds Dataset" (Kaggle 등에서 검색)
    • 두 데이터셋을 결합하거나, 별도로 모델을 학습시킨 후 결합하는 방법을 고려할 수 있습니다.

3. 모델 정의

def create_classifier(vggish_model, num_classes):
  model = tf.keras.Sequential([
      vggish_model,
      tf.keras.layers.Dense(128, activation='relu'),
      tf.keras.layers.Dropout(0.5),
      tf.keras.layers.Dense(num_classes, activation='softmax') # 다중분류: 출력층 (클래스 개수만큼)
  ])
  return model

num_classes = 5 # 강아지 감정 종류 개수.
model = create_classifier(vggish_model, num_classes)
model.summary()

4.모델 컴파일

model.compile(optimizer='adam',
              loss=tf.keras.losses.CategoricalCrossentropy(), #label이 원핫 인코딩 되었으므로.
              metrics=['accuracy'])

주의사항:

  • 데이터 불균형: 감정 레이블 역시 불균형할 가능성이 높습니다. 학습 시 주의가 필요합니다.
  • 데이터 품질: 수동 레이블링의 경우, 레이블의 일관성을 유지하는 것이 중요합니다. 여러 명이 레이블링을 진행한다면, 가이드라인을 만들고, 서로 검토하는 과정이 필요합니다.
  • 모델 선택: VGGish는 오디오 특징 추출에 효과적이지만, 감정 분류를 위해서는 추가적인 모델링이 필요할 수 있습니다. (예: LSTM, Transformer 등 시계열 모델). 또한, 2진분류가 아니므로, loss function도 적절하게 변경해야 합니다.

5.모델 훈련

  • 데이터 증강:
    • 오디오 데이터 증강 기법 (time stretching, pitch shifting, noise addition 등)을 적용하여 데이터를 늘립니다.
  • 하이퍼파라미터 튜닝:
    • 배치 사이즈, 학습률, 에폭 수, 은닉층의 뉴런 수, 드롭아웃 비율 등을 조정하여 최적의 모델을 찾습니다.
  • TFRecord파일을 직접 활용:
    • get_all_features에서 반환된 features는 ragged tensor입니다.
    • tf.data.Dataset.from_tensor_slices((features, labels))는 ragged tensor를 지원하므로, 바로 사용 가능합니다.
  • labels_to_indices의 수정: label_dict, dog_label_indices 필요.

6.모델 평가

  • 평가 지표: Accuracy, Precision, Recall, F1-score 등 다중 클래스 분류에 적합한 지표를 사용합니다.
  • Confusion Matrix: 어떤 감정끼리 혼동이 많이 발생하는지 시각화하여 분석합니다.

7. 감정 인식 시스템 구현
* 학습된 모델과 전처리 함수를 사용하여 새로운 오디오 입력에 대한 감정 분류를 수행하는 함수를 만듭니다.

이러한 단계를 거쳐 강아지 소리 감정 인식 시스템을 개발할 수 있습니다.
맞습니다. Google Colab에서 Google Drive를 활용하면 다음과 같은 장점이 있습니다.

  1. 데이터 저장 및 로드:
    • AudioSet과 같은 대용량 데이터셋을 Colab 로컬 환경에 매번 다운로드받을 필요 없이, Google Drive에 한 번 저장해두고 필요할 때마다 마운트하여 사용할 수 있습니다.
    • 전처리된 데이터(특징 벡터 등)도 Drive에 저장하여 반복적인 전처리 과정을 줄일 수 있습니다.
  2. 모델 저장 및 로드:
    • 학습된 모델 (가중치 포함)을 Google Drive에 저장하여, 나중에 다시 로드하여 사용할 수 있습니다. (모델 재학습 시간 절약)
    • 체크포인트(checkpoint)를 주기적으로 저장하여, 학습 중단 시 복구할 수 있습니다.
  3. Colab 세션 관리:
    • Colab 런타임 세션이 종료되어도 (예: 12시간 제한) Google Drive에 저장된 데이터와 모델은 보존됩니다.

Google Drive 연동 및 활용 코드 (수정)

import pandas as pd
import tensorflow as tf
import os
import io
from google.colab import drive  # Google Drive 연동

# 1. Google Drive 마운트
drive.mount('/content/drive')

# 2. 작업 디렉토리 설정 (Google Drive 내)
#   - '/content/drive/MyDrive/'는 기본 경로입니다.
#   - 필요에 따라 하위 폴더를 만들어서 사용할 수 있습니다 (예: '/content/drive/MyDrive/AudioSet_Project').
WORK_DIR = '/content/drive/MyDrive/AudioSet_Project' # 예시 경로
os.makedirs(WORK_DIR, exist_ok=True)  # 폴더가 없으면 생성
os.chdir(WORK_DIR) # 작업 경로 변경
print(f"Current working directory: {os.getcwd()}") #현재 경로 확인

# 3. 데이터 다운로드 (처음 한 번만)
#   - 이미 Drive에 데이터가 있다면 이 부분은 건너뜁니다.
#   - balanced_train_segments.csv, features.tar.gz 다운
if not os.path.exists("balanced_train_segments.csv"): #파일 없으면 다운
  !wget -P {WORK_DIR} https://research.google.com/audioset/dataset/balanced_train_segments.csv
if not os.path.exists("features.tar.gz"): #파일 없으면 다운
  !wget -P {WORK_DIR} https://storage.googleapis.com/us_audioset/youtube_corpus/v1/features/features.tar.gz

#압축 해제. 이미 해제 했으면 생략.
if not os.path.isdir("features"): # features 폴더 없으면 압축 풀기
  !tar -xzf features.tar.gz
if not os.path.exists("vggish_model.h5"):
    !wget https://storage.googleapis.com/audioset/vggish_model.ckpt
    !wget https://storage.googleapis.com/audioset/vggish_pca_params.npz

# 4. 강아지 관련 레이블 (balanced_train_segments.csv 기준)
dog_labels_mids = ["/m/0bt9lr", "/m/05m59", "/m/07q0h5t", "/m/07qjznt", "/m/07q5rw0"] #Dog, bark, ,whimper(dog), bow-wow

# 클래스 레이블 매핑 (mid -> display_name) - class_labels_indices.csv 내용
# balanced_train_segements.csv에 있는 값으로만 구성, 강아지 관련 소리만
label_map = {
    '/m/0bt9lr': 'Dog',
    '/m/05m59' : 'Bark',
    '/m/07q0h5t': 'Livestock, farm animals, working animals',
    '/m/07qjznt' : 'Howl',
    '/m/07q5rw0' : 'Bow-wow'
}

# 숫자 인덱스로 변환 (나중에 모델 학습에 사용)
reverse_label_dict = {name: idx for idx, name in enumerate(dog_labels_mids)}
print(reverse_label_dict)

# 5.  CSV 파일 로드 (balanced_train_segments.csv)
train_segments = pd.read_csv("balanced_train_segments.csv", skipinitialspace=True, skiprows=[1,2])  # 헤더 건너뛰기
train_segments.columns = ["YTID", "start_seconds", "end_seconds", "positive_labels"]
train_segments['positive_labels'] = train_segments['positive_labels'].apply(lambda x: x.split(','))

# 6. 강아지 소리 데이터 필터링 함수 (이전과 동일)
def filter_dog_segments(df, dog_labels):
     df = df[df['positive_labels'].apply(lambda x: any([label.strip('"') in dog_labels for label in x]))]
     return df

dog_train_segments = filter_dog_segments(train_segments, dog_labels_mids)
print(f"Dog segments in balanced train set: {len(dog_train_segments)}")

# 7. 레이블을 숫자로 변환하는 함수 (이전과 동일)
def labels_to_indices(labels_list, label_dict):
     label_indices = []
     for label_str in labels_list:
          stripped_label = label_str.strip('"')  # 앞뒤 따옴표 제거.
          if stripped_label in label_dict:
            label_indices.append(label_dict[stripped_label])
          else:
            print(f"Warning: label {stripped_label} not in label_dict.") # label_dict에 없는 라벨 경고
     return label_indices


# 8. TFRecord 파일에서 특성 읽기 (video_id가 bytes로 반환되므로 수정)
def read_tfrecord(example):
    feature_description = {
        'video_id': tf.io.FixedLenFeature([], tf.string),
        'start_time_seconds': tf.io.FixedLenFeature([], tf.float32),
        'end_time_seconds': tf.io.FixedLenFeature([], tf.float32),
        'labels': tf.io.VarLenFeature(tf.int64),
        'audio_embedding': tf.io.VarLenFeature(tf.string),
    }
    example = tf.io.parse_single_example(example, feature_description)
    audio_embedding = tf.reshape(tf.io.decode_raw(example['audio_embedding'].values, tf.uint8), [-1, 128])
    return example['video_id'].numpy().decode('utf-8'), example['start_time_seconds'].numpy(), example['end_time_seconds'].numpy(), example['labels'].values.numpy(), audio_embedding


# 9. YTID와 start, end time에 해당하는 feature들을 로드
def get_all_features(YTID, directory):
    tfrecord_file = os.path.join(directory, f"{YTID[:2]}.tfrecord")
    if not os.path.exists(tfrecord_file):
        print(f"TFRecord file not found: {tfrecord_file}")
        return None

    dataset = tf.data.TFRecordDataset(tfrecord_file)
    features_list = []
    for record in dataset:
        video_id, start, end, labels, audio_embedding = read_tfrecord(record)
        if video_id == YTID:
            features_list.append(audio_embedding)

    if len(features_list) > 0:
      return tf.concat(features_list, axis=0)
    else:
      return None


# 10. 학습 데이터 생성 함수 (VGGish feature 사용)
def create_vggish_dataset(segments_df, features_dir, label_dict, dog_label_indices, batch_size=32):
    features = []
    labels = []

    for _, row in segments_df.iterrows():
        YTID = row['YTID']
        segment_features = get_all_features(YTID, features_dir)

        if segment_features is not None:
            features.append(segment_features)

            # 레이블 인덱스 확인 및 처리
            positive_indices = labels_to_indices(row['positive_labels'], label_dict)
            is_dog_sound = any(idx in dog_label_indices for idx in positive_indices)
            #label_vector = [0] * len(dog_label_indices)  # 다중 레이블을 위한 리스트
            #for idx in positive_indices: #label_dict에 없는 라벨이 들어오는 경우.
            #    if idx < len(dog_label_indices):  # 인덱스 범위 검사 추가.
            #      label_vector[idx] = 1
            #labels.append(label_vector)
            #print(label_vector, row['positive_labels'])
            labels.append(int(is_dog_sound))  # 이진 분류

    features = tf.ragged.constant(features)
    labels = tf.constant(labels)
    dataset = tf.data.Dataset.from_tensor_slices((features, labels)).batch(batch_size)
    return dataset

# 11. VGGish 모델 로드
vggish_model = tf.keras.models.load_model('vggish_model.h5') # h5모델 로드
vggish_model.trainable = False  # VGGish는 fine-tuning 하지 않음.

# 12. 분류 모델 정의 (여기서는 간단한 MLP 사용)
def create_classifier(vggish_model, num_classes):
    model = tf.keras.Sequential([
        vggish_model,
        tf.keras.layers.Dense(128, activation='relu'),
        tf.keras.layers.Dropout(0.5),
        tf.keras.layers.Dense(num_classes, activation='softmax') # 출력층 (클래스 개수만큼)
    ])
    return model

# 13. 모델 컴파일, 학습
# 강아지 감정 종류 수에 맞게 수정
num_classes = 5 # 예시: 짖기, 낑낑, 으르렁, 낑낑거림, 기타
model = create_classifier(vggish_model, num_classes)

model.compile(optimizer='adam',
              loss=tf.keras.losses.CategoricalCrossentropy(), #label이 원핫 인코딩 되었으므로.
              metrics=['accuracy'])

# features 폴더에서 VGGish feature 로드
# features_dir = './features'
# dog_train_dataset = create_vggish_dataset(dog_train_segments, features_dir, reverse_label_dict, dog_label_indices)

# 모델 학습 (시간이 오래 걸리므로 주의!)
#model.fit(vggish_train_dataset, epochs=10)  # epochs, batch_size 조정 필요

# 더미 데이터로 학습 예시 (실제 데이터와 형태는 맞추되, 내용은 의미 없음)
dummy_features = tf.random.uniform((10, 10, 128))   # (샘플 개수, 프레임 수, 특성 차원)
dummy_labels = tf.constant([[1,0,0,0,0], [0,1,0,0,0],[1,0,0,0,0],[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0]])  # 강아지 종류 레이블 (0, 1, 2, 3, 4)

#labels = tf.constant(labels, dtype=tf.int32)를 위에서 수행하지 않음
model.fit(dummy_features, dummy_labels, epochs=5, batch_size=2)

# 모델 저장
model.save(os.path.join(WORK_DIR, 'my_dog_classifier.h5'))

코드 주요 변경 사항:

  1. Google Drive 연동:
    • google.colabdrive 모듈을 사용하여 Google Drive를 마운트합니다.
    • 작업 디렉토리(WORK_DIR)를 Google Drive 내에 설정하여, 데이터와 모델을 해당 디렉토리에 저장하고 불러옵니다.
  2. TFRecord 파일 처리:
    • YTID가 일치하는 경우에 대한 조건을 추가
  3. 데이터 로드:
    • wget을 사용하여 CSV 파일을 다운로드합니다. (이미 다운로드한 경우 생략)
    • pd.read_csv를 사용하여 CSV 파일을 로드합니다. skiprows=3 옵션을 제거했습니다. (헤더 처리가 변경됨).
  4. 감정 라벨
  • label_to_indices 함수에서 label_str의 따옴표를 제거합니다.
  • create_vggish_dataset 함수 내에서, dog_labels와 감정 라벨을 이용하여 라벨을 생성하는 부분을 수정하였습니다.
  1. 모델 저장:
    • model.save()를 사용하여 학습된 모델을 Google Drive에 저장합니다.

실행 방법:

  1. Google Colab 노트북을 엽니다.
  2. 런타임 유형을 GPU로 설정합니다. (런타임 > 런타임 유형 변경 > 하드웨어 가속기 > GPU 선택)
  3. 제공된 코드를 복사하여 Colab 노트북에 붙여넣습니다.
  4. WORK_DIR 변수를 자신의 Google Drive 내 원하는 경로로 수정합니다.
  5. 필요에 따라 create_vggish_dataset함수 내 batch_size를 조절.
  6. 코드를 실행합니다.

참고:

  • 이 코드는 AudioSet의 balanced_train_segments.csv 만을 사용합니다. 더 많은 데이터를 사용하려면 unbalanced_train_segments.csv, eval_segments.csv도 로드하여 사용해야 합니다.
  • vggish_model.h5파일은 직접 다운받아서, 코랩 세션 저장소에 업로드하거나, 위 코드처럼 wget으로 다운로드 해야 합니다.

이 코드를 바탕으로 강아지 소리 감정 분류 모델을 성공적으로 개발하고, Google Drive를 활용하여 효율적으로 데이터를 관리하시기 바랍니다.

profile
현우

0개의 댓글