반응형
Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

Learner's Log님의 블로그

음성 데이터 EDA 분석 본문

데이터 분석 및 실습

음성 데이터 EDA 분석

Learner's Log 2025. 3. 18. 10:01

음성 데이터 EDA 분석

1. 데이터 로드 및 준비

  • librosa, numpy, matplotlib, seaborn 등의 라이브러리를 사용하여 데이터 분석 진행
  • voice_data 폴더에서 .wav 파일을 불러옴
  • 감정별로 데이터를 정리하고, 무작위로 하나씩 샘플링하여 분석을 진행함
import os
import librosa
import librosa.display
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.fftpack import fft
voice_data_path = "voice_data"
wav_files = [f for f in os.listdir(voice_data_path) if f.endswith(".wav")]
# 무작위로 감정별 1개씩 선택
selected_files = {
    "Angry": next((f for f in wav_files if "-A" in f), None),
    "Happy": next((f for f in wav_files if "-H" in f), None),
    "Neutral": next((f for f in wav_files if "-N" in f), None),
    "Sad": next((f for f in wav_files if "-S" in f), None)
}
# 감정별 파일 리스트 정리(평균값 계산을 위해)
emotion_files = {
    "Angry": [f for f in wav_files if "-A" in f],
    "Happy": [f for f in wav_files if "-H" in f],
    "Neutral": [f for f in wav_files if "-N" in f],
    "Sad": [f for f in wav_files if "-S" in f]
}

2. 감정별 음성 파형(Waveform) 분석

개별 샘플 파형 분석
  • 감정별로 하나씩 랜덤하게 선택하여 파형을 시각화
  • 시간에 따른 신호의 진폭 변화
selected_files = {emotion: file for emotion, file in selected_files.items() if file}

plt.figure(figsize=(12, 8))

for i, (emotion, file) in enumerate(selected_files.items()):
    file_path = os.path.join(voice_data_path, file)
    y, sr = librosa.load(file_path, sr=None)

    plt.subplot(2, 2, i+1)
    librosa.display.waveshow(y, sr=sr)
    plt.title(f"Waveform - {emotion} ({file})")
    plt.xlabel("Time (s)")
    plt.ylabel("Amplitude")

plt.tight_layout()
plt.show()

png


감정별 평균 파형 분석

  • 같은 감정 카테고리 내의 여러 파일을 평균 내어 감정별 특성을 확인
  • 파형의 특징:
    • Angry: 초반에 진폭이 매우 크며 다른 파형들의 비해 큰 폭을 유지
    • Happy: 진폭 변화가 뚜렷하며 점진적으로 감쇠
    • Neutral: 다른 감정보다 전반적으로 낮은 진폭, 균일한 패턴 유지
    • Sad: 초기 진폭이 크지만, 천천히 감소하며 부드러운 흐름을 가짐
# 감정별 평균 파형 계산
plt.figure(figsize=(12, 8))

for i, (emotion, files) in enumerate(emotion_files.items()):
    waveforms = []
    sr = None

    for file in files:
        file_path = os.path.join(voice_data_path, file)
        y, sr = librosa.load(file_path, sr=None)
        waveforms.append(y)

    avg_waveform = np.mean(np.array([librosa.util.fix_length(w, size=max(map(len, waveforms))) for w in waveforms]), axis=0)

    plt.subplot(2, 2, i+1)
    librosa.display.waveshow(avg_waveform, sr=sr)
    plt.title(f"Average Waveform - {emotion}")
    plt.xlabel("Time (s)")
    plt.ylabel("Amplitude")

plt.tight_layout()
plt.show()

png

# 감정별 스펙트로그램 분석
plt.figure(figsize=(12, 8))

for i, (emotion, file) in enumerate(selected_files.items()):
    file_path = os.path.join(voice_data_path, file)
    y, sr = librosa.load(file_path, sr=None)

    # 스펙트로그램 계산
    D = librosa.amplitude_to_db(librosa.stft(y), ref=np.max)

    # 스펙트로그램 시각화
    plt.subplot(2, 2, i+1)
    librosa.display.specshow(D, x_axis='time', y_axis='log', sr=sr)
    plt.colorbar(format='%+2.0f dB')
    plt.title(f"Spectrogram - {emotion} ({file})")

plt.tight_layout()
plt.show()
C:\Users\Public\Documents\ESTsoft\CreatorTemp\ipykernel_3408\2353304294.py:9: UserWarning: amplitude_to_db was called on complex input so phase information will be discarded. To suppress this warning, call amplitude_to_db(np.abs(S)) instead.
  D = librosa.amplitude_to_db(librosa.stft(y), ref=np.max)

png

감정별 평균 길이 비교

  • 감정에 따라 발화 시간의 차이가 존재 가능성(미미한 차이..)
emotion_lengths = {"Angry": [], "Happy": [], "Neutral": [], "Sad": []}

for file in wav_files:
    file_path = os.path.join(voice_data_path, file)
    y, sr = librosa.load(file_path, sr=None)
    duration = librosa.get_duration(y=y, sr=sr)

    if "-A" in file:
        emotion_lengths["Angry"].append(duration)
    elif "-H" in file:
        emotion_lengths["Happy"].append(duration)
    elif "-N" in file:
        emotion_lengths["Neutral"].append(duration)
    elif "-S" in file:
        emotion_lengths["Sad"].append(duration)


# 평균 길이 계산
emotion_avg_lengths = {emotion: np.mean(lengths) for emotion, lengths in emotion_lengths.items()}
# 감정별 평균 길이 시각화
plt.figure(figsize=(8, 5))
sns.barplot(x=list(emotion_avg_lengths.keys()), y=list(emotion_avg_lengths.values()))
plt.xlabel("Emotion")
plt.ylabel("Average Duration (seconds)")
plt.title("Average Duration of Emotions")
plt.show()

png

감정별 평균 특징 분석 MFCC

# 감정별 특징 저장
emotion_features = {"Angry": [], "Happy": [], "Neutral": [], "Sad": []}

for emotion, files in emotion_files.items():
    for file in files:
        file_path = os.path.join(voice_data_path, file)
        y, sr = librosa.load(file_path, sr=None)

        # 특징 추출
        mfcc = np.mean(librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13), axis=1)
        spectral_centroid = np.mean(librosa.feature.spectral_centroid(y=y, sr=sr))
        spectral_bandwidth = np.mean(librosa.feature.spectral_bandwidth(y=y, sr=sr))
        spectral_contrast = np.mean(librosa.feature.spectral_contrast(y=y, sr=sr))
        rms = np.mean(librosa.feature.rms(y=y))
        zero_crossing_rate = np.mean(librosa.feature.zero_crossing_rate(y))

        feature_vector = np.hstack([mfcc, spectral_centroid, spectral_bandwidth, spectral_contrast, rms, zero_crossing_rate])
        emotion_features[emotion].append(feature_vector)

# 감정별 평균 특징 계산
emotion_avg_features = {emotion: np.mean(features, axis=0) for emotion, features in emotion_features.items()}
# 감정별 평균 MFCC 시각화
plt.figure(figsize=(12, 5))
for emotion, features in emotion_avg_features.items():
    plt.plot(features[:13], label=emotion)
plt.legend()
plt.xlabel("MFCC Coefficient")
plt.ylabel("Average Value")
plt.title("Emotion-wise Average MFCC Features")
plt.show()

png

이 그래프는 각 감정의 평균 MFCC 특성을 비교한 것
X축은 MFCC 계수(Index), Y축은 평균 MFCC 값(Average Value)

  • 거의 유사한 패턴을 그린다..
반응형