본문 바로가기

문돌이 존버/Speech&Language Processing

비전공자 문돌이가 설명해주는 MFCC 벡터(vector) 2탄

반응형

지난 시간에 이어 MFCC 벡터 2탄입니다. 우리는 소리(관찰값)를 듣고 이것의 근원인 단어들의 조합, 즉 문장을 알고 싶다고 이야기했습니다. 식으로 나타내면 아래와 같이 표현할 수 있었습니다. O는 observation의 이니셜, W는 words의 이니셜입니다. 

$\hat{W}=argmax\underset{W} P(W|O)$

위의 식을 풀려면 베이지안 룰을 사용해야 함을 기억하실 겁니다. 우리에게는 학습 과정을 통해 얻은 확률값 P(W), P(O|W)가 있기 때문에 아래의 식처럼 P(W|O)를 구할 수 있죠. 

저번에도 말씀드렸듯이, P(O)는 이미 관찰한 값으로 상수로 처리되기 때문에 최댓값을 구하는 데 아무런 영향을 끼치지 않습니다. 즉 "I love you"라는 문장이 들렸고, 이에 해당하는 source sentence 후보들의 확률값을 비교하여 최댓값을 찾는데 P("I love you")는 변하지 않는다는 것입니다. 그래서 과감하게 제외하고 아래 수식의 맨 오른쪽처럼 나타낼 수 있습니다. 

W나 O와 같은 표현이 어색하다면 이렇게 생각합시다. W는 단어들 $w_1, w_2, ... w_n$ 들의 조합이고 이는 문장을 가리킵니다. O는 $o_1, o_2, ... o_t$ 들의 조합으로 소리를 가리키는데 구체적으로 말하면 문장을 발음한 소리겠죠. 그런데 이상합니다. W는 단어들로 구분(slice)되는 것을 알겠는데, 소리를 구분한다는 것이 무엇을 의미할까요? 이후에 자세하게 설명하겠지만 간단히 말씀드리면, 소리를 실수값의 연속으로 보고 이들을 프레임(frame) 단위로 쪼개면 구간 하나에 해당하는 값이 $o_t$입니다. 이때 구간의 값이 바로 MFCC 벡터가 되는 것이죠. 

사실 소리는 본래 연속적 신호입니다. 영어로는 sound waveform이라고 하는데, 아래와 같은 그래프로 나타납니다. praat이란 프로그램을 사용하여 실제 제 목소리를 녹음한 파일입니다. 

저는 "안녕하세요"를 녹음했고, 위의 sound waveform은 "안"의 "아"까지 포착된 것입니다. 얼핏보면 "안녕하세요"를 모두 말한 것처럼 길게 보이지만, 실제로는 "ㅇ", "ㅏ" 처럼 자음, 모음만 하더라도 sound waveform이 (우리 눈에) 길게 그려집니다. 즉 요점은 소리는 연속이라는 것입니다. 하지만 주의해야 할 점이 있습니다. 실제 소리는 실수값의 연속이라고 할 수 있지만 컴퓨터에 입력되고, 음성인식기가 이를 처리하기 위해선 실수값이 아닌 이산적(discrete) 신호값으로 변환해주어야 합니다. 이 과정은 이후에 설명을 하도록 하고 MFCC 설명에선 이미 이산적 값으로 바뀌었다고 가정하겠습니다. 또 하나의 가정이 필요한 것은 이렇게 이산적 값으로 변환되어도 우리는 실수값 신호를 최대한 복원할 수 있다는 것입니다. 

본격적으로 MFCC 벡터를 어떻게 생성하는지 그 과정을 아래 순서에 따라 살펴보겠습니다. 

1. Preemphasis
2. Windowing
3. DFT
4. Mel Filter bank and log 
5. The Cepstrum: Inverse Discrete Fourier Transform
6. Deltas and Energy

MFCC 특징 추출(feature extraction)

1. 증폭(Preemphasis)

이산적 신호값을 받으면 가장 먼저 할 일은 이를 증폭시키는 것입니다. 이는 높은 주파수(high frequency)의 에너지 양을 증가시키는 것인데, 본래 낮은 주파수(low frequency)에 더 많은 에너지가 농축되어 있기 때문입니다. 다시 말해, 주파수가 높아질수록 에너지가 약해지는 것이죠. 이를 전문 용어로 spectral tilt라고도 부릅니다. 

아래 그림의 (a)는 증폭 전의 모습이고, (b)는 증폭 후의 모습입니다. x축은 주파수인데, 값이 커질수록 에너지에 해당하는 y축의 값이 점점 작아지다 아예 사라지죠. 하지만 (b)를 보면 전반적으로 y 값이 증가했음을 확인할 수 있습니다. 

<출처: Speech and language processing>

증폭을 구하는 공식이 궁금하신 분들을 위해 작성하긴 하지만 그냥 넘어가셔도 됩니다. 

$y(n) = x(n) - ax(n-1), 0.9\le a \le1$
참고:
사람의 목소리는 성문(glottis)의 진동으로부터 시작되는데, 일종의 자연적 현상으로 주파수가 높아질수록 에너지 즉 소리 크기가 작아지게 됩니다. 언어학을 공부하신 분들은 아시겠지만 소리 자체는 기관(trachea)에서 발생되고, 성문의 진동을 거쳐 "목"소리로 변하며, 성도(vocal track)라고 불리는 조음 기관들을 통과하면 언어의 의미를 가지는 "말"소리가 되는 것입니다. 이때 여러 고주파수, 저주파수에 해당하는 진폭(amplitude)들이 합처져(harmonic) 말소리가 됩니다. (해당 내용은 다른 글에서 별도로 설명하겠습니다.)

2. 윈도잉(Windowing)

윈도잉은 위에서 잠깐 언급했던 프레임을 소리 신호에 씌우는 과정입니다. 소리 신호를 한 번에 이해하기는 어려우니 여러 번 쪼개서 각 부분의 소리 특징을 캐치하겠다는 것입니다. 일반적으로 말소리는 불안정(non-stationary)한 신호라고 봅니다. 하지만 여러 부분으로 나눠서 본다면 각 부분은 그래도 안정적이지 않을까 하는 가정을 하게 됩니다. 

윈도잉에서 필요한 파라미터는 총 3개입니다.

1. 프레임 사이즈(=wide of the window)
2. 프레임 시프트(=offset between successive windows)
3. 프레임 모양(=shape of the window)

프레임 사이즈란, 프레임 넓이 즉 시간을 가리키는 것으로 보통 10~25ms를 기준으로 합니다. 프레임 시프트란, 프레임과 프레임 간의 간격을 말하며 보통 5~10ms를 기준으로 합니다. 프레임 모양은 2가지가 있는데, 하나는 사각형 모양(rectangular window), 다른 하나는 해밍 모양(hamming window)입니다. 아래 그림을 보면 직관적으로 알 수 있을 것입니다. 

<출처: Speech and language processing>

왼쪽 그래프의 양끝 모양이 어떤가요? 자세히 보시면 갑자기 끊겨버린 모습을 발견할 수 있을 것입니다. 반면, 오른쪽 그래프의 양끝 모양은 y축이 0에 가깝게 비교적 원만하게 끊긴 것을 볼 수 있습니다. 왼쪽 그래프가 사각형 프레임 모양, 오른쪽 그래프가 해밍 프레임 모양입니다. 아무래도 뚝 끊기는 사각형 모양보단 앞뒤 프레임의 연결성을 보장하여 안정적인 주파수 추출이 가능한 해밍 모양이 더 낫겠죠. 말소리는 인접한 자, 모음 간 서로 소리적 영향을 끼치게 되니까요. 

 

3. DFT(Discrete Fourier Transform)

이제 위에서 프레임에 따라 나눈 각 부분에서 spectral information을 추출할 차례입니다.(spectral information에 해당하는 한국어를 찾지 못했는데, 그냥 말소리가 가지고 있는 언어적 정보를 가리킨다고 보면 될 것 같습니다.) 우리는 서로 다른 주파수 영역에서 가지고 있는 에너지가 얼마인지 알고 싶습니다. 이때 주파수 영역 역시 이산적 값이고, 프레임에 따른 각 부분 역시 이산적 값입니다. 이들로부터 spectral information을 추출하는 툴이 바로 DFT 입니다. 

DFT에는 프레임 각 부분의 값이 입력 변수로 들어가고, 원래 소리 신호를 구성하는 주파수의 단계 및 진폭 크기를 나타내는 복잡한 숫자가 출력 변수가 됩니다. 아래 그림을 봅시다.

<출처: Speech and language processing>

(a)는 해밍 프레임 모양을 사용한 후의 한 신호 부분이고, (b)는 DFT를 적용한 후의 그래프인데요. 두 그래프 간의 차이점이 무엇일까요?  

(a)의 x축은 시간(time), y축은 진폭인 반면, (b)의 x축은 주파수로 측정 변수가 달라졌습니다. (b)의 y축은 단위가 데시벨(dB)이라 (a)의 y축과 다르게 표시되었지만 똑같이 진폭을 나타냅니다. 다시 말해, (a)는 시간에 따른 진폭의 크기 변화를 나타낸 그래프, (b)는 (a)에서 발견된 주파수들을 x축으로 옮긴 그래프입니다. 각 주파수 영역에 해당하는 진폭 크기를 알 수 있는 것이죠. 

참고:
(a) 그래프에서 구불구불한 모양을 cycle이라고 부릅니다. 1초에 몇 개의 cycle이 발생했는지가 바로 주파수를 나타냅니다. 예를 들어, 1초에 100개의 cycle이 발견되면 100Hz입니다. (Hz는 주파수 단위입니다.)

DFT 공식은 아래와 같습니다. DFT를 계산하는 알고리즘은 보통 FFT(Fast Fourier Transform)를 사용합니다.

x[n]: N point time-domain signal, real number 
X[k]: N point frequency-domain signal, complex number

4. 맬 필터 뱅크와 로그(Mel filter bank and log)

DFT를 통해 구한 값은 결국 각 주파수에 해당하는 에너지 양에 대한 정보입니다. 그런데 혹시 사람의 고막 특성을 아시나요? 연구에 따르면, 사람 고막은 높은 주파수에서는 덜 민감하다고 합니다. 쉽게 말해, 어떤 소리에서 주파수가 높은 부분은 잘 들리지 않는다는 의미입니다. 이를 바탕으로 DFT의 출력값에 맬 스케일(mel scale)을 적용해줍니다. 맬 스케일은 사람 고막의 특성에 맞게 DFT 값을 바꿔주는 역할로, 1000Hz 이상 범위에선 로그를 씌워 값 변화가 적게 한 것입니다. 

여기서 왜 1000Hz가 기준이 되었느냐, 그 이유 역시 사람 고막의 특성 때문입니다. 실제로 1000Hz 이전의 소리는 사람이 잘 듣고 구분한다는 것이죠. 정리하자면 맬 스케일을 적용한 DFT 값은 사람 고막이 듣는 수준을 그대로 따라간 것이라고 보면 됩니다. 

아래는 mel scale 공식입니다. 

맬 필터 뱅크란, 맬 스케일 값을 바탕으로 각 주파수 영역에서 에너지를 모으는 역할을 하는데요. 강둑 모양처럼 겹쳐져 있다고 하여 뱅크라고 이름을 붙인 것입니다. 1000Hz 이하에선 보통 10개의 필터를 사용하고, 1000Hz가 넘는 부분에 나머지 필터를 사용한다고 합니다. 아래 그림을 보면 1000Hz 이하에서 10개의 뱅크가 있고, 1000Hz~4000Hz 사이에 약 11개의 뱅크가 있네요. 

<출처: Speech and language processing>

마지막으로 맬 필터 뱅크를 통해 나오는 진폭값에 로그를 씌우게 되는데, 이는 또 다시 사람 고막의 특성 때문입니다. 사람은 보통 낮은 진폭보다 큰 진폭에서 소리를 잘 구분하지 못합니다. 따라서 로그를 취해주면 진폭값의 변동이 줄어드는 효과가 있어 안정된 진폭값을 유지할 수 있겠죠. 

 

5. The Cepstrum: Inverse Discrete Fourier Transform

점점 더 대응하는 한국어를 찾지 못하겠네요 ㅠㅠ 우선 영어를 사용하고 개념을 잘 설명해보겠습니다. Cepstrum은 소스(source)와 필터(filter)라는 개념을 생각해보면 이해하기 쉽습니다. 음성인식에서 말하는 소스는 말소리가 생성되는 근원인 성문을 가리킵니다. 흔히 glottal source라고 부르는데, 이는 음소(phone) 구별에 큰 영향을 미치지 않으므로 중요하지 않습니다. 중요한 것은 필터인데요, 필터는 성문에서 발생하는 소리를 말소리로 만들어주는 조음 기관(성문에서 입까지 이어지는 공간)을 가리킵니다. 따라서 효과적으로 음소를 구분하기 위해선 소스와 필터를 나누어 보여주는 것이죠. 이 역할을 Cepstrum이 하는 것입니다. 

아래 그림을 보면 (a)는 기본 스펙트럼, (b)는 (a)에 로그를 씌운 버전, (c)는 Cepstrum을 나타냅니다. (c)는 (b)의 진폭에서 소스와 필터에 따른 진폭 크기를 나누어 보여줍니다. 

(c)에서 또 다른 점을 발견하셨나요? (a)와 (b)의 x축은 주파수를 나타내는 반면, (c)는 샘플을 나타내고 있습니다. 사실 우리는 Cepstrum을 (b)의 스펙트럼이라고 생각할 수도 있습니다. 먼저, (b) 자체를 하나의 sound waveform이라고 생각해봅시다. sound waveform은 x축이 시간이였죠. (b)의 x축은 주파수를 나타내고 있습니다. 하지만 (b)의 x축을 시간으로 "상상"하고 여기에 다시 스펙트럼을 씌워준 것이 (c)입니다. 다만, 원본 sound waveform과는 달리 샘플(sample)로 시간을 대신 나타내는 것이죠.(원본 sound waveform은 실수값이지만 컴퓨터는 이산값만 취할 수 있기 때문입니다) 

(c)를 보시면 x축이 120 근처에서 피크(peak)가 보이는데, 이것이 바로 소스에 따른 진폭을 나타냅니다. 나머지는 모두 필터에 따른 진폭을 나타냅니다. MFCC 벡터를 위해선 일반적으로 첫 12개의 Cepstrum 값을 추출합니다. 이 12개 값은 소스가 아닌 필터에 의한 정보를 알려주게 됩니다. 또한, 이 12개의 값은 서로 다른 Cepstrum 값으로 상관성이 없다는 속성을 가집니다. 이는 이후에 GMM(Gaussiasn Mixture Model)을 사용할 때 서로 다른 MFCC 벡터 간에 공분산성(covariance)을 고려할 필요를 없애주어 계산이 용이해집니다.

Cepstrum을 형식적으로 정의하면 "inverse DFT of the log magnitude of the DFT of a signal"이라고 합니다. 주파수를 다시 시간 개념의 샘플로 표현했으니 inverse DFT가 되겠죠?

다음은 Cepstrum을 구하는 공식입니다. 마찬가지로 참고만 해주시기 바랍니다. 

6. 델타와 에너지(Deltas and Energy)

위에서 구한 12개의 Cepstrum 값 이외에 1개의 값을 더 추가해주어야 합니다. 바로 프레임의 에너지입니다. 에너지 역시 음소를 구별하는 데 중요한 역할을 하기 때문에 필요한 정보입니다.

에너지는 프레임 내 샘플들의 시간에 따른 소리 크기의 합이기 때문에 아래 공식을 통해 구할 수 있습니다.

t1: sample t1
t2: sample t2

또 하나 주의할 점은 말소리의 신호는 프레임마다 일정하지 않다는 것입니다. 이쪽 프레임에서 저쪽 프레임으로 가면 신호가 불안정하기 때문에 변화가 일어납니다. 이를 파악하기 위해 시간에 따른 Cepstrum 값 변화를 표현하는 특징을 추가해야 합니다. 이것을 델타(또는 velocity) 값, 그리고 더블 델타(또는 acceleration) 값이라고 합니다. 다시 말해, 기본 13개 값에 추가로 시간에 따른 변화를 나타내는 델타 값 13개, 또 델타 값에 따른 변화를 나타내는 더블 델타 값 13개를 추출하게 되며, 최종적으로 MFCC 39차원 벡터가 되는 것입니다. 

델타 값은 아래 공식으로 구할 수 있습니다.

c(t): particular cepstral value c(t) at time t

728x90
반응형