본문 바로가기
Developer/Librosa

[Python 음성 데이터 분석] Librosa 라이브러리를 이용한 주파수 분석

by Doony 2020. 7. 23.

이번 포스팅에서는 지난 포스팅에 이어, 실제로 파이썬을 이용해 어떻게 음성 데이터를 불러오고 가공하는지에 대해 알아보겠습니다.

Librosa 라이브러리 만든이 칭찬해~~

파이썬은 배워두면 참 쓸모가 많은 언어인 것 같습니다. 찾아보면 라이브러리가 다 있으니까요. Librosa 라이브러리는 음성 데이터를 다루는 대표적인 라이브러리입니다. 간단하게 wav파일을 불러와서 파형을 직접 가공할 수도 있고, FFT나 MFCC 등 다양한 형태로 변환하는 기능들도 제공합니다. 상세히 살펴보겠습니다.


음원 데이터 불러오기

아래와 같이 wav 파일을 불러올 수 있습니다.

1
2
3
import librosa
audio_path = 'waveFile.wav'
y, sr = librosa.load(audio_path)
cs

y: 파형의 amplitude 값
sr: sampling rate


y의 그래프를 그려보면, wav파일에 담긴 파형 자체의 그래프가 나오게 됩니다. sr은 오디오 파일에 맞게 설정할 수 있으며, 설정하지 않으면 기본값인 22050으로 파형을 그리게 됩니다.
wave form
(https://stackoverflow.com/questions/41606185/audio-waveform-matching

지난 포스팅에서 nyquist frequency에 대해 간단히 다뤘었는데, 나이퀴스트 주파수는 그 절반값인 11025Hz가 됩니다. 즉, 이 파형을 주파수 분석(FFT)할 때 나올 수 있는 최대 주파수가 11025Hz가 됩니다. 그 이상의 주파수까지 분석이 필요할 경우, sr을 더 올려서 음원데이터를 불러오시면 됩니다.


주파수 분석

소리는 기본적으로 특정 주파수를 가지는 sin함수들의 합이라고 했습니다. 즉, 위에서 구한 y파형을 주파수 분석을 통해, 특정 시간에 주파수 성분이 어떻게 구성되어 있는지 확인할 수 있는데요. 음성 데이터 분석을 할 때 주파수 분석 기법을 많이 사용합니다. (파형 자체를 이용하기도 합니다!) 주파수 분석은 크게 3단계로 이루어지는데, 이번 포스팅에서는 그 중에서도 Fourier Transform에 대해서만 간단히 다뤄보겠습니다.

  • Fourier Transform: time-domain의 그래프를 frequency-domain으로 변환시켜주는 작업

푸리에변환
(https://upload.wikimedia.org/wikipedia/commons/7/72/Fourier_transform_time_and_frequency_domains_%28small%29.gif
그림으로 이해하는 것이 빠를 것 같습니다. 빨간 곡선이 있었을 때, 이것을 푸리에 변환을 거치게 되면 다양한 주파수와 위상을 가지는 sin함수들로 변환시키게 됩니다. 각 sin함수는 주파수와 위상이 정해져 있고, 이를 x축이 frequency, y축이 amplitude로 나타낼 수 있습니다. 핵심은, 어떤 파형이든지 간에, sin함수들로 쪼갤 수 있다는 점입니다. 수 많은 다양한 sin함수의 조합으로 모든 곡선을 만들 수 있다고 봐도 괜찮을 것 같습니다.


이제 해야할 일은, 분석하고자 하는 음원 데이터를 특정 시간 간격으로 쪼갠 뒤, 해당 간격에 있는 파형에 대해 퓨리에 변환을 적용함으로써 주파수 분석을 하는 것입니다. 원하는 아웃풋 형태는 다음과 같습니다.
주파수분석


보시는 것처럼 음원 파형이 있을 때, 이를 작은 시간 간격으로 쪼갠 뒤 푸리에 변환을 하게 되면 칼라맵이 나오게 됩니다. x축은 시간, y축은 Hz, 색상은 dB를 나타냅니다. 이런 일종의 이미지화된 어레이를 통해 데이터를 분석할 수 있게 됩니다. 이런 이미지, 칼라맵들은 Short Time Fourier Transform (STFT)을 통해 만들 수 있습니다.


Short Time Fourier Transform

위에서 설명한 방법과 동일합니다. 음원을 특정 시간 주기로 쪼갠 뒤, 해당 주기별로 Fourier Transform을 진행하면, 해당 주기만큼 주파수 분석 그래프를 얻게 됩니다. 이를 다시 시간 단위로 배열하면, 3차원 칼라맵이 나오게 되는데요. Librosa 라이브러리에서 제공하는 메소드가 있습니다. 상세 내용은 여기에서 확인하실 수 있습니다.

1
2
3
4
5
6
stft_result = librosa.stft(y, n_fft=4096, win_length = 4096, hop_length=1024)
= np.abs(stft_result)
S_dB = librosa.power_to_db(D, ref=np.max)
librosa.display.specshow(S_dB, sr=sr, hop_length = 1024, y_axis='linear', x_axis='time', cmap = cm.jet)
plt.colorbar(format='%2.0f dB')
plt.show()
cs

실행하시면 칼라맵이 생성되는 것을 확인하실 수 있습니다. 주목해야 할 점들은 parameter들입니다. 크게 아래 3가지 설정값이 있습니다.

  • win_length
  • hop_length
  • n_fft - window

짧은 시간 주기로 쪼갠 뒤, 퓨리에 변환을 한다고 서술했는데요. 이 때 사용하는 방식이 FFT 방식입니다. Fast Fourier Transform의 약자이며, 그냥 빠르게 변환하는 방식이라고 볼 수 있겠습니다.
이 때 변환하는 방식을 보면 다음 그림과 같습니다.

STFT
(https://kr.mathworks.com/help/dsp/ref/dsp.stft.html

win_length는 FFT를 할 때 참조할 그래프의 길이입니다.
hop_length는 얼마만큼 시간 주기를 이동하면서 분석을 할 것인지에 대한 파라미터입니다. 즉, 칼라맵의 시간 주기라고 볼 수 있습니다.
n_fftwin_length보다 길 경우 모두 zero padding해서 처리하기 위한 파라미터입니다. default는 win_length와 같습니다.

이런 파라미터들이 있는 이유는...
단순하게 생각해봤을 때, 분석하는 시간이 길수록 주파수를 더 잘 분석합니다. 그 만큼 데이터가 많으니까요. 대신 시간이 길어진다는 건, 시간 해상도는 떨어진다는 의미가 됩니다. 일종의 tradeoff 관계가 있기 때문에 보통 STFT를 수행할 때는 파라미터 설정을 통해 최적화하는 과정을 거치게 됩니다.

위 파라미터들을 보시면 이제 조금 이해가 가시나요? win_length는 FFT를 수행할 시간 간격이고, hop_length는 시간 해상도를 나타내는 값입니다. 이렇게 되면 약간의 overlap이 발생하게 됩니다. hop_length 이후의 시간 단위들에 해당하는 주파수까지 포함하게 된다는 의미입니다. 그럼에도 이렇게 사용하는 건 이점이 있기 때문일겁니다. (더 상세히는 잘 모르겠군요..)


n_fft는 얘기가 약간 다릅니다. 아래 그림을 보면서 설명하겠습니다.
original
(https://www.bitweenie.com/listings/fft-zero-padding/


위와 같은 신호를 FFT 분석을 하게 되면, 다음과 같이 나오게 됩니다.
FFT후
(https://www.bitweenie.com/listings/fft-zero-padding/


어떻게 보면 제대로 분석한 것처럼 보이는데요. 만약 다음과 같이 기본 음원파형에 zero를 이어 붙여서 시간 간격을 늘리면 어떻게 될까요?

zeros
(https://www.bitweenie.com/listings/fft-zero-padding/


시간 간격을 늘린다는 것은, 주파수 해상도를 높이는 것이라고 위에서 언급했습니다. FFT한 결과는 다음과 같습니다.
FFTzeros
(https://www.bitweenie.com/listings/fft-zero-padding/


원래의 FFT보다 더 섬세하게 주파수 분석을 한 것을 볼 수 있습니다. 이렇게 zero padding을 하면 주파수 해상도를 올릴 수 있습니다.
n_fftwin_length보다 클 경우, 큰 구간은 모두 zero padding으로 채우게 됩니다. 이유는 설명드린바와 같이, 주파수 해상도를 높이기 위해서입니다.


사실 설명안한 파라미터 중 window 도 있습니다. STFT 라이브러리에서 default 값은 hann으로 되어 있는데요.
FFT를 할 때 특정 주기 간격으로 한다고 말씀드렸는데, 그 주기가 무한번 반복한다는 가정하에 계산이 이뤄지게 됩니다. 그렇다면 다음과 같이 파형이 생겼을 경우, 연결되는 부분에서 문제가 발생하게 됩니다.

leakage
(https://www.tek.com/blog/window-functions-spectrum-analyzers


매끄럽지 않은 부분들까지 포함해서 주파수 분석을 하기 때문에, spectral leakage가 발생하게 됩니다.
leakage2
(https://www.tek.com/blog/window-functions-spectrum-analyzers


위 그림은 leakage가 발생한 그림이고, 이상적으로는 아래와 같이 나왔어야 합니다. (단일 주파수를 가진 sin함수일 때)
expect
(https://www.tek.com/blog/window-functions-spectrum-analyzers



즉 같은 sin함수라도, 구간을 어떻게 정의하느냐에 따라 주파수 spectral에 leakage가 발생하는 것입니다. 이러한 discontinuity 문제를 해결하기 위한 것이 바로 window 입니다. 대표적으로 hanninghamming이 있습니다.
hanning_hamming
(https://www.tek.com/blog/window-functions-spectrum-analyzers


그림처럼 일종의 필터링을 하는 것입니다. hanning의 경우, 그래프 구간의 양 끝을 0으로 맞춤으로써, periodic 상황에서의 discontinuity를 없애는 방식입니다. 이런식으로 핕터링을 하게 되면, 주파수 분석 결과는 다음과 같습니다.
hanning_FFT
(https://www.tek.com/blog/window-functions-spectrum-analyzers


데이터에 따라 다양한 윈도우를 적용할 수 있습니다.
여기에서 사용가능한 window 리스트를 볼 수 있습니다.


간단하게 STFT와 그 parameter 들에 대해 살펴보았습니다. 다음 포스팅에서는 핫한 음성 머신러닝 시 주로 활용하는 mel scalemel frequency, mel frequency cepstral coefficient등에 대해 알아보겠습니다.

댓글