Friday, September 29, 2017

Q&A - 다성음 인식 관련





아래의 질문을 받았습니다. 

* * *

저희는 전자 피아노를 연주 했을 때 다성(polyphony)음을 인식하여 맞았는지, 틀렸는지를 분석해 주는 연구를 진행중입니다. 다성음에 관한 자료가 많지 않아 다양한 논문을 리서치 하던 중, (..중략).. 음악 인식에 관련되어 몇가지 질문이 있어 이렇게 메일을 보내게 되었습니다.
1. (첨부파일 중 magnitudeSpectrum.py)
사전에 연주한 음악 파일(첨부한 파일 중 butterfly.wav)을 fft를 사용하여 frequency와 power(dB)로 분리해 특징을 추출하여 그 결과로 첨부한 사진과 같은 그래프를 얻을 수 있었습니다.
여기서 혹시 전처리와 특징값 추출이 올바르게 되었다는 사실을 검증할 수 있는 방법이 특별히 있는지 궁금합니다.

2. 그 후 NMF(non-negative matrix factorization, 비음수 행렬 인수분해)를 사용하여 다성음 인식을 가능하다는 정보를 얻게 되어 이를 분류기로 사용하여 학습을 진행할 예정입니다. 하지만 정확한 검증과정이 없어 혹시 NMF가 분류기로 사용하기에 적절한지 궁금합니다. 혹시 해당 방법이 적절하지 않다면 적절한 분류기를 추천해 주실 수 있으신가요?
실제 이렇게 하여 다성음간 비교를 하여 일정확률 이상으로 일치여부를 판별하고자 합니다. 방향성이나 구현방법이 맞는지 조언 부탁드리고, 추가로 해주실 말씀 있으시면 의견부탁드립니다.

* * *

받은 소스코드는 아래와 같습니다.


import scipy.io.wavfile as wavfile
import numpy as np
import pylab as pl

rate, data = wavfile.read("Butterfly.wav")
t = np.arange(len(data[:,0]))*1.0/rate

#Original Signal graph
fig = pl.figure()
g1 = fig.add_subplot(221)
g1.set_title("Original signal")
g1.plot(data)


for i in range(0,180778):
  if(data[i,1]>0):
      start = i
      break
print(start)
temp = np.abs(np.fft.rfft(data[start:180778,1]))

p = [20*np.log10(x) if x>=1 else 1 for x in temp]


f = np.linspace(0, rate/2.0, len(p))

g2 = fig.add_subplot(222)
g2.set_title("FFT")

g2.plot(f, p)
# g2.xlabel("Frequency(Hz)")
# g2.ylabel("Power(dB)")

pl.show()


그럼 간단히 첨삭해볼까요?

첨삭

파트 1


import scipy.io.wavfile as wavfile
import numpy as np
import pylab as pl

rate, data = wavfile.read("Butterfly.wav")
t = np.arange(len(data[:,0]))*1.0/rate

#Original Signal graph
fig = pl.figure()
g1 = fig.add_subplot(221)
g1.set_title("Original signal")
g1.plot(data)

좋습니다.

파트 2


for i in range(0,180778):
  if(data[i,1]>0):
      start = i
      break
print(start)
temp = np.abs(np.fft.rfft(data[start:180778,1]))


음악 파일에서 앞에있는 묵음 구간을 제외하려는 코드같네요. 그런데 실제 음원은 잡음이 껴있어서 이렇게 샘플 기반으로, 기준을 0으로 잡아서 하면 잘 작동하지 않습니다. 음성의 경우에 음성이 있는 구간을 탐지하는 (Voice activity detector, VAD) 아주 중요한 문제죠. 프로젝트에서 쓸법한 간단한 방법으로는 1. 신호를 프레임으로 나누고 (예) 2. 프레임마다 평균 에너지를 구하고 3. 그걸 plot해서 4. 눈으로 보고 적당한 기준값을 정하면 됩니다.

그 뒤엔 np.fft(rfft)를 하셨는데요, 이렇게 하면 신호 전체에 대해 FFT를 수행합니다. 하지만 실제로 필요한건 짧은 프레임에 대해 계속 주파수 분석을 하는 short-time Fourier transform입니다.

마지막으로 180778은 butterfly.wav의 샘플 개수인가요? 이건 곡마다 달라질테니 변수로 지정하는편이 좋겠죠.

파트 3 


p = [20*np.log10(x) if x>=1 else 1 for x in temp]

이 부분을 보니 오디오 샘플 x가 웨이브파일에서 그대로 값을 읽어온 int로 이루어진것같군요. 일단 계산 자체는 맞습니다.

하지만 이 연산을 이렇게 수행하는것보다 x=np.array(x)로 바꾸고 p=np.log10(np.maximum(x, 1))를 하는게 좋겠죠.

그리고 p는 좋은 변수 이름은 아니네요.

파트 4



f = np.linspace(0, rate/2.0, len(p))

g2 = fig.add_subplot(222)
g2.set_title("FFT")

g2.plot(f, p)
# g2.xlabel("Frequency(Hz)")
# g2.ylabel("Power(dB)")

pl.show()


맞습니다.

조언

위의 작업을 STFT로 수행해야 시간별로 어떤 일이 일어나고있는지 알 수 있습니다.

NMF와 다성음 인식

일단 쉬운 주제는 아닙니다. 다성음 인식은 아직 풀린 문제가 아니고 기존에 논문으로 나온 인식기를 구현하는 것도 배경지식이 필요합니다.

아주 간단한 작업부터 시작하시길 권합니다 (항상 마찬가지죠). 예를 들어 '도.wav'와 '솔.wav'를 구별할 수 있게 학습이 가능한지 해보시고, 그 뒤엔 이것을 도레미파솔라시 7개 음을 구별하는걸로 확장해보고, 그런 식이죠.

그리고 NMF는 여기서 분류기가 아니라 특징값 추출기입니다. NMF를 np.abs(np.log10(STFT))에 적용한 뒤 나온 값을 다시 분류기에 넣으셔야합니다.

마지막으로 모든 작업은 기본적인 머신러닝의 룰을 따라야합니다. 제 게시물 등을 참고하셔서 오버피팅, 학습/시험셋 나누기 등을 수행하셔야합니다.

Thursday, September 14, 2017

튜토리알 논문을 하나 썼습니다.

"Music information retrieval을 위한 딥러닝"이라는 제목으로 튜토리알 논문을 하나 썼습니다. 어제 아카이브에 올라왔네요.  논문 링크 | 코드 링크
핵심만 쉽게 설명하려고 애를 많이 썼습니다. 재미있게 읽어주세요.