728x90

이번에 Review할 논문은 "Voice Impersonation using Generative Adversarial Networks" 이다. Carnegie Mellon University의 "Yang Gao" et al. 이 작성하였으며, 2018.02.19에 Arxiv에 출간되었고, ICASSP 2018에 실렸다. 해당 논문은 https://arxiv.org/abs/1802.06840 에서 볼 수 있다.

 

1. Overview

 해당 논문은, 음성 변환, 변조, 흉내 (Voice Conversion, Impersonation)을 다루는 내용이다. Voice Impersonation에서 결과 음성은 target 스피커에서 자연스럽게 생성된 느낌을 확실하게 전달해야하며, 피치 및 기타 인식 가능한 신호 품질뿐만 아니라 target 스피커의 style을 모방해야한다. 이 논문에서는 Impersonation된 음성 합성을 위한 신경망 기반의 음성 품질 및 스타일 모방 프레임 워크인 VoiceGAN을 제안하며, 이 프레임워크는 GAN을 기반으로 한다.

 모델의 입력은 Spectrogram을 받고 있고, 제안한 VoiceGAN 네트워크의 output은 Griffin-Lim 방법을 사용하여 time 도메인 신호를 재구성하여 합성된 Spectrogram을 내보낸다. 논문에서 주장하는 Voice style transfer에 대한 내용은, 실제로 성별을 바꿔가며 pair-dataset 기반으로 결과를 보여주고 있다.

 변환 결과는 https://nbviewer.jupyter.org/github/Yolanda-Gao/Spectrogram-GAN/blob/master/VoiceGAN%20result.ipynb?flush_cache=true 에서 들어볼 수 있다.

 

2. Intro

 한 사람이 다른 사람의 목소리를 모방하여 다른 사람과 같은 소리를 내려고 시도하는 음성 가장(Voice Impersonation)은 복잡한 현상이다. Impersonation 한 사람은(X) Impersonation 한 사람의 목소리(x)에서 가장 두드러진 측면을 직관적으로 일치시켜 변환을 수행한다. 이 프로세스에서 Impersonation을 통해 변경한 음성의 가장 일반적인 특성이 음성 품질 요소와 스타일 요소이다. 

 하지만 음성은 설명하기 어려운 개체이다. Nasality(콧소리), 거칠기, 숨가쁨 등과 같은 일부 측면이 기존 연구를 통해 확인되었지만, 이들 중 대부분은 화자간에 의미 있는 비교를 허용하는 방식으로 정량화 하기가 힘들다. target 화자의 말하기 속도 등의 특징들을 시간적 피치 및 에너지 패턴(magnitude)을 포함하는 스타일 요소는 정확한 정의와 비교 가능성 측면에서 유사하게 모호하다.

 (개인 생각: 그래서 우리는 MOS를 측정하거나, 합성된 음질의 SNR(Signal-to-Noise Ratio)등을 통해 비교를 하기도 한다. 하지만 가장 직관적인 것은 실제로 사람이 들어보는 것이다.)

 이 논문에서는, 다른 컨텐츠를 수정하지 않고 음성의 특정 측면만 변환하는 것이 목표이다. 이상적으로 스타일 특징을 결정하는 음성의 측면을 식별, 격리 및 명시적으로 측정한 뒤 이를 사용하여 대상 음성을 수정하기도 하지만, 이러한 특징들은 주관적/객관적으로 식별이 가능하지만 위에 언급한 것 처럼 정량화 할 수 없기 때문에 일반적으로 어렵다고 주장한다. 

 저자의 목표는 식별할 수 있는 스타일의 측면을 전달하는 매커니즘을 개발하는 것이다. 이러한 맥락에서 가장 관련성이 높은 선행 연구는 소스 음성을 대상 음성으로 변환하는 특정 문제를 다루는 음성 변환과 관련이 있다. 일반적으로 음성 변환은 Pitch 및 Spectrul Envelope과 같은 신호의 순간 특성을 수정한다. 여기에 codebook based conversion (Voice Conversion through Vector Quantization)과 minimum-mean-squared error linear estimator (Voice Conversion Based on Maximum-Likelihood Estimation of Spectral Parameter Trajectory), Neural Networks (Voice conversion using deep neural networks with layerwise generative training) 등 여러 기법들을 사용한다. 위 방법들은 모두 신호의 순간적인 특성 변화에 매우 효과적이며, 심지어 일부 운율 신호를 mapping 할 수 있지만, 일반적으로 linguistic한 의미를 측정할 수 없다. 

 또한 음성 변환을 효과적으로 하기 위해서 train/test set간의 녹음이 완벽하게 시간이 정렬되어 있어야 한다. 이는 일반적으로 녹음을 서로 맞추기 위해 녹음 시간을 왜곡하여 충족시키는 요구 사항인데, 이렇게 녹음하기에는 비현실적이다. 또한 목적이 음성의 도메인 변환을 수행하는 것이 아니라 스타일을 변형시키는 것만 배울 경우, 위의 정렬은 부적절 할 수 있다.

 이러한 맥락에서 저자는 GAN을 주장한다. Discriminator는 병렬 신호들 사이의 mapping을 배우기보다는 원하는 스타일 특징을 가진 데이터와 그렇지 않은 데이터를 구별한다. Generator는 Discriminator를 어리석게 만드는 방법을 배워야한다. D와 G 모두 Deep Learning에 의해 모델링되며, 적절한 설계와 충분한 훈련 데이터로 변환을 모델링 할 수 있다. 기의 음성 변환 방법에 필요한 것과 같은 병렬 데이터는 필요하지 않다. (하지만... 결과를 들어보면 pair dataset에 대한 결과만 나오며, 뒤에서 소개될 Loss에는 supervised로서, label이 주어져 있음)

 음성 신호 변환에 GAN을 사용하기에는 여러 문제가 있다. GAN에서 널리 사용되는 이미지와는 달리 음성은 크기가 고정되어 있지 않으며(즉, 지속 시간이 고정되어 있지 않음), 축소 될 때 스타일 특성이 많이 손실된다. 음성과 같은 sequencial 데이터 생성은 이미지와 비교하여 더 어려운 문제이다. 프로세스를 순진하게 구현하면 언어, 스타일 또는 이해하기 어려운 내용을 잃은 데이터가 생성 될 수 있다.(매우 공감...) 이 연구에서는 음성 변환 문제에 대한 여러 GAN 모델을 소개하고, 저자의 모델과 해당 학습 알고리즘은 언어 고유의 특정 문제를 고려하도록 설계되었다. 구체적으로, 저자는 모델 구조와 학습 알고리즘을 적절히 선택하고 GAN 프레임워크에 적절한 Discriminator를 도입하여 다른 측면을 모방하기 위해 다른 언어를 수정하거나 Linguistic 정보를 잃지 않고 음성의 특정 특성 유지 방법을 소개한다.

 

3. Related Works

3-1. GAN

Fig. 1. The original GAN model

 Original GAN 모델은 generator $G(z)$와 Discriminator $D(x)$로 이루어져 있다.

Generator $G$는 일부 표준 확률 분포 함수 $P_z$ 로부터 도출 된 랜덤 변수 $z$ 를 입력으로 취하며, 표준 정규 분포를 따르는 출력 벡터 $x_z$ 를 생성한다.

 Discriminator $D$는 $P_x$에서 추출한 표본 $x~P_x$, 즉 모델링하려는 실제 분포와 Generator $G$에서 생성한 표본을 구별한다. T는 벡터 $x$가 $P_X$으로부터 도출된 사건이라고 가정하면, $P_x$에서 Discriminator는 $T$의 사후 확률, 즉 $D(x) = P(T|X)$를 계산한다.

  GAN의 수식은 아래와 같다.

GAN
GAN Discriminator Loss
GAN Generator Loss

부가적으로 설명을 달아보겠다.

Discriminator의 역할인 $max_D V(D)$ 는 실제 이미지들을 더 잘 인식하는 것이 목적이고,

Generator의 역할인 $min_G V(G)$ 는 가짜 이미지들이 Discriminator를 속이는 것이 목적이다.

 

여기에서,

$E_{x~P_x}$ 는 $x$ 가 real data로부터 도출된 sample data

$E_{z~P_Z}$ 는 $z$ 가 fake data(noise)로 부터 도출된 sample data

$D(x)$ 는 실제 이미지가 들어갔을 때의 D(real)의 확률

$1-D(x_z)$ 는 가짜 이미지가 들어갔을 때의 D(fake)의 확률

$[logD(x)]$ 는 D(real)의 likelihood

$[log(1-D(x_z))]$ 는 D(fake)의 likelihood

이다.

 

더 좋은 품질의 GAN output을 얻기 위해서는

$Generator: D(x_z)$ 를 minimize 시켜야 하고,

$Discriminator:D(x)$ 를 maximize 시켜야 한다.

즉 결론적으로 $G$ 와 $D$는 적대적(Adversarial)이다. 우리는 종종 GAN을 미니 맥스 게임으로 정의하는데, G는 V를 최소화하고 D는 V를 최대화하기를 원하기 때문이다.

 

3-2. Style transfer GAN

Fig. 2. Style transfer by GAN

 2번째로 논문에서 소개하는 것은 Style transfer를 GAN을 통해서 한다는 것이다. $P_A$분포로부터 가져온 input data $x_A$ 를 $Generator$를 통해 $transformed$ 하여 $x_{AB}$를 만든다. 이것을 Transformer $G_{AB}$ 라고 부른다. (Attention is all you need논문에 소개된 Transformer 아닙니다..) $G_{AB}$의 목적은 $x_A$를 분포 $P_B$에서 기본적으로 발생하는 변수 $x_B$의 스타일로 변환하는 것이다.

 Discriminator $D_B$는 $x_B$에서 $P_B$의 실제 드로우와 $P_A$에서 $x_A$의 드로우를 변환하여 얻은 인스턴스 $x_{AB}$ 중 누가 진짜인지를 구별하는 역할을 한다. Style transfer 최적화는 아래의 공식과 같다.

 

 

Style transfer Generator Loss and Discriminator Loss

generator $G$는 generator loss인 $L_G$을 최소화하여 업데이트가 되는 반면, discriminator $D$는 discriminator loss인 $L_D$를 최소화하여 업데이트한다.

 

3-3. DiscoGAN

Fig. 3. The DiscoGAN model

DiscoGAN은 카테고리 두 가지 범주의 데이터인 $A$ 및 $B$를 서로 변환하려고 시도하는 대칭 모델이다. DiscoGAN은 2개의 Generator를 포함하고 있는데,

- $G_{AB}$ = A의 분포 $P_A$로부터 얻은 $x_A$를 $B$로 변환 ==> $x_AB$ ==> $G_{AB}(x_A)$

- $G_{BA}$ = B의 분포 $P_B$로부터 얻은 $x_B$를 $A$로 변환 ==> $x_BA$ ==> $G_{BA}(x_B)$

두 Generator는 서로 역의 관계를 갖고 있다.

 $G_{AB}$의 목표는 $B$의 style을 모방한 $G_{AB}(x_{AB})$의 output이 $B$ 데이터 분포인 $P_B$와 구별되지 않도록 하는 것이다. $G_{BA}$ 도 역의 관계로 생각하면 될 것이다.

 

다음은 DiscoGAN의 loss 부분이다.

DiscoGAN loss 부분 설명

빨간색 그림은 내가 표시한 것이다.

위에서 언급한 GAN loss와 Style transfer loss를 확실히 이해하였다면 이 부분도 어렵지 않게 이해할 수 있다.

2개의 Generator Loss

$L_{G_A}$는 given data $B$를 $A$로 변환한 뒤의 real일 확률의 negative likelihood 값이며,

$L_{G_B}$는 given data $A$를 $B$로 변환한 뒤의 real일 확률의 negative likelihood 값이다.

즉 둘다 작을수록 좋다.

 

또한 그림에 보이는 Reconstruction Loss가 2개가 있다. DiscoGAN에서는 $d$를 두 이미지의 거리로 계산하였으며, 이때 $MSE$loss를 사용하였다.

$L_{CONST_A} = d(G_{BA}(G_{AB}(x_A)),x_A)$ 

Reconstruction A loss는 $x_A$와 $B$로 변환하였다가 다시 $A$로 변환한 이미지와의 $MSE$loss 값이며,

$L_{CONST_B} = d(G_{AB}(G_{BA}(x_B)),x_B)$

Reconstruction B loss는 $x_B$와 $A$로 변환하였다가 다시 $B$로 변환한 이미지와의 $MSE$loss 값이다.

 

DiscoGAN GAN Loss

이제 전체 loss를 정리해보겠다.

2개의 Generator loss는 다음과 같이 정의된다

$L_{GAN_{AB}} = L_{G_B} + L_{CONST_A}$

$L_{GAN_{BA}} = L_{G_A} + L_{CONST_B}$

그러므로,

$L_G = L_{G_{AB}} + L_{G_{BA}}$

$ ==> L_{G_B} + L_{CONST_A} + L_{G_A} + L_{CONST_B}$

 

2개의 Discriminator loss들은 다음과 같이 정의된다.

$L_{D_A} = -E_{x_A~P_A}[logD_A(x_A)] -E_{x_B~P_B}[log(1-D_A(G_{BA}(x_B)))]$

$L_{D_B} = -E_{x_B~P_B}[logD_B(x_B)] -E_{x_A~P_A}[log(1-D_B(G_{AB}(x_A)))]$

따라서 전체 $L_D$ 는

$L_D = -E_{x_A~P_A}[logD_A(x_A)]-E_{x_B~P_B}[log(1-D_A(G_{BA}(x_B)))] - E_{x_B~P_B}[logD_B(x_B)] - E_{x_A~P_A}[log(1-D_B(G_{AB}(x_A)))]$ 가 된다.

 

4. Proposed Model: VoiceGAN

 DiscoGAN은 원래 이미지의 스타일을 변형할 수 있도록 설계되었다. 저자는 이 모델을 음성에 적용하기위해 음성을 Spectrogram으로 변환하여 진행하였다. (단순히 STFT한 Spectrogram인지, Mel-Spectrogram인지 정확히 논문에 언급은 안되었지만, Mel-Spectrogram으로 변환한 뒤 Linear Prediction을 한 후 Griffin-Lim Method를 적용한 것 처럼 보임) 

위에 언급한 것처럼, 보통의 GAN은 이미지에서 많이 사용되고 있고, 음성은 적용하기가 어려운 문제점을 저자는 아래와 같이 주장한다.

1. Original DiscoGAN은 고정 크기의 이미지에서 작동하도록 설계되었다. 본질적으로 가변 크기의 음성 신호와 함께 작동하려면 새로운 디자인에서 이 제약 조건을 완화해야한다.

2. 음성 신호의 언어 정보가 손실되지 않도록 하는 것이 중요하다.

3. 저자의 목표는 연설의 특정 측면을 수정하는 것이다. 예를 들어 스타일을 구현하기 위해 모델에 추가 구성 요소를 추가한다.

 

저자는 위의 문제점을 개선할 VoiceGAN을 제안한다. 그럼 이제 VoiceGAN에 대하여 알아볼 시간이다.

 

Voice GAN의 Generator와 Discriminator

모델 아키텍쳐는 위와 같은데, DiscoGAN의 Generator와 VoiceGAN의 Generator 부분은 같다. 차별점은, Discriminator인데,

1. Adaptive Pooling layer는 CNN layer 이후 및 MLP 계층 이전에 추가된다.

2. 채널 별 풀링이 포함 된다.

3. 가변 크기의 feature map을 고정 된 차원의 벡터로 변환한다.

아쉽지만 여기에서 Hyperparameter에 대한 언급은 생략되어 있다. 코드라도 공개 했으면 좋았을텐데..

 

우선 VoiceGAN의 reconstruction loss를 보면 아래와 같다.

$L_{CONST_A} = \alpha d(X_{ABA},x_A) + \beta d(x_{AB},x_A)$

$L_{CONST_B} = \alpha d(X_{BAB},x_B) + \beta d(x_{BA},x_B)$

식의 뒷 편을 자세히 보면

$\beta d(x_{AB},x_A)$ 와 $\beta d(x_{BA},x_B)$ 가 추가 되었다. 추가한 이유를 저자는 "For retain the linguistic information" 라고 소개하고 있다.

$d(x_{AB}, x_A)$ 는 $x_A$로 변환 된 후에도 $x_A$의 언어적 정보를 유지하려고 시도한다고 설명하고 있다.

 

$\alpha \beta$에 대해서는, 변환 후 언어 정보의 정확한 변환 및 보존을 하기 위한 매개변수라고 설명하고 있다. 하지만, 아쉬운 점으로는 가장 중요한 hyperparameter에 대해서 정확한 값에 대해 공개를 하지 않았다.ㅜㅜ 예측하건데 굉장히 작은 값으로 multiple 한 것이 아닐까 싶다. 논문에서는 "Careful choice of $\alpha, \beta$ ensures both" 라고만 공개하고 있다.

 

두번째로는 Style Embedding Model $D_S$ 이다.

저자는 음성 레이블이 있는 Style Discriminator를 추가하였다. 

$L_{D_STYLE-A} = d(D_S(x_A), label_A) + d(D_S(x_{AB}, label_B) + d(D_S(x_{ABA}, label_A)$

$L_{D_STYLE-B} = d(D_S(x_B), label_B) + d(D_S(x_{BA}, label_A) + d(D_S(x_{BAB}, label_B)$

$L_{D_STYLE} = L_{D_{STYLE-A}} + L_{D_{STYLE-B}}$ 

 

위의 Loss들을 정리하여 DiscoGAN과의 차별점은,

DiscoGAN Final Loss

Generator: $L_{G_B} + L_{CONST_A} + L_{G_A} + L_{CONST_B}$

Discriminator: $L_{D_A} + L_{D_B}$

 

VoiceGAN Final Loss

Generator: $L_{G_B} + L_{CONST_A} + L_{G_A} + L_{CONST_B}$

Discriminator: $L_{D_A} + L_{D_B} + L_{D_{STYLE}}$

 

즉 언어적 정보를 잃지 않기 위해 Discriminator 부분만 추가되었다.

 

5. Experiment

저자는 TIDIGITS dataset을 사용하였다.  TIDIGITS dataset은 326의 speaker들이 있으며(111 men, 114 women, 50 boys, 51 girls) 각 각의 speaker들은 77개의 문장을 녹음하였다. Sampling rate는 16kHz이고, 논문에서 주장하는 Style transfer는 Gender 로 설정하였다.

사용된 문장은 숫자를 읽는 문장이다. 아랫쪽에 나오겠지만, 5 4 3 2 1 과 같은 음성을 사용하였다. 다르게 말하면, 연속적 대화보다는 쉬운 task로 볼 수 있고, 고립 단어 변환으로 볼 수도 있을 것 같다.

위의 그림은 음성 변환 그림이다. 맨 왼쪽 위가 남자 $x_A$이고, 위쪽 가운데가 변환한 여자 $x_{AB}$, 위쪽 오른쪽이 다시 원래 남자로 변환한 $x_{ABA}$이다.

 

몇 가지 음성에 대한 결과를 랜덤으로 뽑아서 저자가 github에 공개를 해놓았다. 여기서 들어볼 수 있다.

 

6. Conclusion

개인적인 생각으로는, 기존에 음성 변환 및 생성에 있어서 GAN이 잘 작동하지 않았던 연구사례들이 많다. 이 부분을 저자는 언어학적 정보를 잃지 않기 위해 Style Embedding Discriminator를 추가하였다. 간단한 아이디어지만 이 부분은 Novel하다 볼 수 있으며, 더 발전시킬 수 있을 것 같다.

아쉬운 점으로는

1. 코드 공개가 없다는 점

2. Hyperparameter 언급이 없다는 점

3. 결과가 paired lingustic만 나온다는 점

으로 꼽아볼 수 있다.

 

- 주관적인 생각이 들어있으므로 관련 논문에 대해 잘못된 부분이나 comment는 언제나 환영합니다.

728x90
728x90

파일 이름: this_file

폴더 이름: this_folder

 

다른 서버내의 id: other_server_id

다른 서버의 ip: 192.168.0.0

다른 서버내의 옮길 디렉토리: /home/junewoo

 

1. 다른 서버로 복사할 때,

scp file id@ip:/dir

ex) scp this_file other_server_id@192.168.0.0:/home/junewoo/

==> 다른 서버내의 /home/junewoo/ 내에서 this_file 만 옮겨짐

2. 다른 서버로 폴더안에 포함된 모든것들 복사할 때,

scp -r folder id@ip:/dir

ex) scp -r this_folder other_server_id@192.168.0.0:/home/junewoo/

==> 다른 서버내의 /home/junewoo/ 내에서 this_folder 폴더가 옮겨짐

 

3. 같은 서버내의 파일/폴더 이동 및 이름 변경

mv file /dir

ex) mv ./this_file ./not_this_file

==> 현재 커맨드라인이 있는 위치(pwd)에서 같은 디렉토리 내의 this_file 파일이 not_this_file로 변경됨

 

ex) mv ./this_folder /test/dir/mnt/dataset/

==> 현재 커맨드라인이 있는 위치(pwd)에서 /test/dir/mnt/dataset/ 으로 파일을 옮김

 

728x90
728x90

한 개의 압축파일을 풀었는데, 그 안에 여러개의 압축파일이 있을 수 있다.

 

5개 이하면 직접 하나씩 unzip이나 tar -xvf 하면 되는데,

 

10개, 20개 넘어가면...이게 좀 귀찮다.

 

다음과 같이 커맨드 창에서 해결할 수 있다.

 

zip 파일일 경우

find . -name '*.zip' -exec unzip {} \;

 

tar 파일일 경우

find . -name '*.tar' -exec tar xvf {} \;

 

tar.gz파일일 경우

find . -name '*tar.gz' -exec tar xvf {} \;

 

bz2 파일일 경우

find . -name '*.bz2' -exec tar xvf {} \;

 

 

728x90
728x90

오늘은 Matlab에서의 Stft(short time fourier transform)와 python library인 librosa의 stft의 dimension 결과 및 각 vector값의 차이에 대해서 알아보겠다.

 

실제로 나는 Matlab에서 DTW 알고리즘으로 테스트할 것이 있어서 stft로 변환해본 뒤 python 결과와 비교해보았는데 dimension 크기가 달랐었다. 그리하여 그 이유를 정리하여 여기에 올리겠다.

 

우선 Matlab에서 spectrogram 을 변환하기 위한 코드는 아래와 같다.

 

>> [y, Fs] = audioread("filename1.wav");
>> [x, Fs] = audioread("filename2.wav");

 

음성은 16kHz 이고, nfft=512, hop=256으로 진행하겠다 ==> frame length를 32ms, 겹치는 hop을 16ms로 진행

 

>> s1 = spectrogram(x, 512, 256);
>> s2 = spectrogram(y, 512, 256);

 

matlab data 결과

 

첨부한 그림처럼,

s1: (257, 271)

s2: (257, 256) 으로, s1이 s2보다 길이가 크다고 볼 수 있다.

 

이번에는 python에서 librosa를 사용해서 진행하겠다.

 

nfft=512

hop_length=256

wavfile_male = '경로'

wavfile_female = '경로'

male_stft = librosa.stft(y_male, n_fft=nfft, hop_length=hop_length)
female_stft = librosa.stft(y_female, n_fft=nfft, hop_length=hop_length)
print("male_stft shape {} female_stft shape {}".format(np.shape(male_stft), np.shape(female_stft)))

 

첨부한 그림처럼

male(s1): 257, 273

female(s2): 257, 258

으로 나온다. s1이 s2보다 크다.

 

근데 여기서 차원이 다르다.

 

정확히 말하면 matlab 결과물이 python librosa 결과물보다 크다. 왜 그럴까?

 

계속 찾던 도중, 잘 설명해주는 글을 발견했다.

https://stackoverflow.com/questions/55474581/difference-between-output-of-python-librosa-core-stft-and-matlab-spectrogramx

 

요약하자면, matlab은 spectrogram의 마지막 window를 생략하면서, 각 window 가운데에 해당하는 시간 벡터를 출력한다. 예를 들어 3샘플 윈도우와 1개의 겸칩(overlap) 샘플을 갖는 10개의 샘플 길이의 신호는 다음과 같은 4개의 윈도우를 생성한다.

1:3

3:5

5:7

7:9,

m:n은 n번째 샘플을 포함하여 m에서 n까지의 샘플을 포함하는 윈도우를 나타낸다. 그러므로 window의 center가 2,4,6,8이다.

matlab은 (윈도우 크기-1) * 홉 길이 + 윈도우 사이즈 <= 샘플들의 개수로 되는 최대의 윈도우 크기를 필요로 하기 때문이다.

 

 

반면 python librosa stft 각 프레임의 첫 번째 샘플이 시간이고, 프레임은 입력 신호보다 많은 부분을 다룬다.

 

위의 matlab의 공식에 반해, librosa는

윈도우의 개수 * 홉 길이 > 샘플개수에 되는 최소한의 윈도우 크기를 요구한다.

 

정리

- MATLAB은 최대한의 윈도우 크기 요구

- Librosa는 최소한의 윈도우 크기 요구

 

그럼 MATLAB과 동일한 차원을 갖고 싶다면?

librosa.stft(y, nfft=, hop=, center=False)

위의 코드처럼 맨 뒤에 center=False를 해주면 된다.

 

자세한 내용은 librosa 공식 사이트를 확인해보자 

https://librosa.github.io/librosa/generated/librosa.core.stft.html

 

center:boolean

  • If True, the signal y is padded so that frame D[:, t] is centered at y[t * hop_length].
  • If False, then D[:, t] begins at y[t * hop_length]

위의 center=False로 돌려보면,

(257, 271) (257, 256) 으로 Matlab과 동일한 차원을 갖는 Spectrogram이 나오게 된다.

 

하지만 Matlab과 librosa의 각 spectrogram의 벡터값들이 모두 같진 않다.

얼핏 보기엔 유사하지만 같지 않다.

아무래도 각 팀들에서 Nomalize등의 기법을 다르게 하여 그런 것 같다.

 

 

 

Matlab 코드

[y, Fs] = audioread("filename1.wav"); #여자
[x, Fs] = audioread("filename2.wav"); #남자

s1 = spectrogram(x, 512, 256); 
s2 = spectrogram(y, 512, 256);

Python 코드

import numpy as np

nfft=512
hop_length=256
male_name = 'masch0'
female_name = 'fekmh0'
import pickle
import scipy
from scipy.spatial.distance import euclidean

wavfile_female = '/mnt/junewoo/speech/KAIST_wav/new_Korean_Voice_DB/public/falhj0/PBSG001.wav'

wavfile_male = '/mnt/junewoo/speech/KAIST_wav/new_Korean_Voice_DB/public/masch0/PBSG001.wav'

y_female, sr = librosa.load(wavfile_female, sr=16000)
y_male, sr = librosa.load(wavfile_male, sr=16000)
male_stft = librosa.stft(y_male, n_fft=nfft, hop_length=hop_length, center=False)
female_stft = librosa.stft(y_female, n_fft=nfft, hop_length=hop_length, center=False)
print("male_stft shape {} female_stft shape {}".format(np.shape(male_stft), np.shape(female_stft)))
print('Done')
728x90
728x90

딥러닝을 이용하여 음성 인식, 음성 처리, 화자 인식, 감정 인식 등에서 많이 쓰이는 음성의 특징 추출 방법은 1.Mel-Spectrogram, 2. MFCC가 있다. 오늘은 Mel-Spectrogram에 대하여 어떻게 추출하여 쓸 수 있는지 적어보겠다.

 

사용할 라이브러리는 librosa 이다.

https://librosa.github.io/librosa/generated/librosa.feature.melspectrogram.html

 

librosa.feature.melspectrogram — librosa 0.6.3 documentation

Parameters: y : np.ndarray [shape=(n,)] or None audio time-series sr : number > 0 [scalar] sampling rate of y S : np.ndarray [shape=(d, t)] spectrogram n_fft : int > 0 [scalar] length of the FFT window hop_length : int > 0 [scalar] number of samples betwee

librosa.github.io

위 사이트에서 자세히 설명이 나와있다. 그대로 따라해도 되지만, 이왕 하는김에 조금 자세히 알아보자.

사이트의 doc 을 보면 아래처럼 설명이 있다.

 

Parameters:Returns:

y:np.ndarray [shape=(n,)] or None

audio time-series

sr:number > 0 [scalar]

sampling rate of y

S:np.ndarray [shape=(d, t)]

spectrogram

n_fft:int > 0 [scalar]

length of the FFT window

hop_length:int > 0 [scalar]

number of samples between successive frames. See librosa.core.stft

power:float > 0 [scalar]

Exponent for the magnitude melspectrogram. e.g., 1 for energy, 2 for power, etc.

kwargs:additional keyword arguments

Mel filter bank parameters. See librosa.filters.mel for details.

S:np.ndarray [shape=(n_mels, t)]

Mel spectrogram

1. y

Mel-Spectrogram을 뽑기 위해서는 librosa.load 로 음성 데이터를 load하여 얻은 y를 넣으면 된다. 이렇게 나머지를 지정하지 않고 추출하였을 경우 default 값으로 추출이된다.

2. sr

y, sr = librosa.load(음성데이터) 를 하게 될 경우, 음성의 sr을 얻을 수 있다. 단 여기에서 중요한 것은, sr을 따로 설정해주지 않으면 librosa는 default인 22050을 주게 된다. 나는 이러한 이유로 soundfile을 사용하여 처음 보는 음성 데이터에 대하여 sr을 체크한 뒤, 그대로 아래처럼 설정해서 sr을 얻는 방법을 사용한다.

y, sr = librosa.load(wav_file, sr=16000)

3. S

S는 librosa.stft(y)를 하면 얻을 수 있다. 즉 Short-Time Fourier Transform을 하여 얻어진 magnitude 와 phase의 값인 것이다. 그렇지만 S는 사용하지 않고 음성 데이터인 y만 넣어도 되므로 pass하겠다.

 

4. n_fft

우리가 보유한 음성 데이터는 현재 time-magnitude domain이다. 이걸 왜 frequency로 바꾸냐면, 주파수 관점에서 바라보았을 때 얻을 수 있는 정보가 많기 때문이다.

음성은 연속적인 신호이다. 그러므로 연속적인 신호를 우리는 frequency로 바꿔야 한다.

 

n_fft는 이 때 사용되는데, 음성의 길이를 얼마만큼으로 자를 것인가? 라고 이해하면 될 것 같다. 이를 window라고 부른다.

사람의 목소리는 대부분 16kHz안에 포함이 된다. 만약 n_fft를 512라고 가정해보겠다. 이렇게 될 경우 1개의 n_fft는 16000/512 = 약 31.25가 된다. 이것이 자연수로 떨어지기 위해 올림을 해주어 32로 설정해준다. 총 음성 데이터의 길이가 500이라고 가정하면, 32만큼 잘라서 1칸을 그리겠다는 것으로 이해하면 된다. 

보유한 음성 데이터의 Sampling Rate가 16000이고, 우리는 n_fft를 512개만 사용할 것이라면, 아래의 공식을 이용하여 구할 수 있다.

 

input_nfft = int(round(sr*frame_length))
frame_length = input_nfft/sr

2번 째 줄을 보면 되겠다.

 

frame_length = 512/16000 = 0.032 => 32ms가 나온다.

 

즉 1개의 window의 크기를 32ms로 잡으면 되고, 이것을 window_length 라고도 부른다.

 

5. hop_length

hop_length는 음성의 magnitude를 얼만큼 겹친 상태로 잘라서 칸으로 보여줄 것인가? 라고 생각하면 편할 것 같다. 즉 window_length - frame_stride 만큼 겹쳐서 1칸을 뽑겠다는 뜻이다. 

 

예를 들어 우리가 보유한 데이터가 7초이고, window_length를 0.025초, frame_stride를 0.010초(10ms)단위로 뽑는다고 가정하면, 1칸은 0.015초(15ms)가 겹치도록 하여 총 700칸을 얻을 수 있다.

 

6. 7은 넘어가도록 하겠다.

 

 

자 그럼 이제 실제로 코드를 돌려서 결과를 얻어 볼 것이다.

 

나는 window length 를 0.025로 두고, stride는 0.010로 진행하여 0.01초마다 frequency를 뽑을 것이다.

 

frame_length = 0.025
frame_stride = 0.010

def Mel_S(wav_file):
    # mel-spectrogram
    y, sr = librosa.load(wav_file, sr=16000)

    # wav_length = len(y)/sr
    input_nfft = int(round(sr*frame_length))
    input_stride = int(round(sr*frame_stride))

    S = librosa.feature.melspectrogram(y=y, n_mels=40, n_fft=input_nfft, hop_length=input_stride)
    
    print("Wav length: {}, Mel_S shape:{}".format(len(y)/sr,np.shape(S)))

 

아래처럼 함수를 선언하였다. 여기에서 중요한 것은, n_mels이다. 만약 16kHz를 Mel_S로 뽑는다면 약 8kHz에 해당하는 주파수를 얻을 수 있다. 이때, 8kHz를 n_mels 크기 만큼으로 나눠준다. 즉 1개의 n_mels의 height는 0.4kHz를 포함하고 있다. 

 

음성 데이터의 길이와 얻은 Mel_Spectrogram의 크기를 출력해보면 다음과 같다.

 

총 길이는 3.35초이고, 0.01 단위에서 올림을하여 3.36이 된다. 그리고 0.01에 1칸이므로 336칸을 얻는다.

 

이렇게 Mel_Spectrogram을 얻었고, 이것을 그려보도록 하겠다. 아래의 코드를 복사하면 된다.


    plt.figure(figsize=(10, 4))
    librosa.display.specshow(librosa.power_to_db(S, ref=np.max), y_axis='mel', sr=sr, hop_length=input_stride, x_axis='time')
    plt.colorbar(format='%+2.0f dB')
    plt.title('Mel-Spectrogram')
    plt.tight_layout()
    plt.savefig('Mel-Spectrogram example.png')
    plt.show()

얻을 Mel_Spectrogram 그림은 아래와 같다.

 

 

전체코드는 아래와 같다.

 

# -*- coding: utf-8 -*-
import librosa
import librosa.display
import numpy as np
import matplotlib.pyplot as plt

frame_length = 0.025
frame_stride = 0.010

def Mel_S(wav_file):
    # mel-spectrogram
    y, sr = librosa.load(wav_file, sr=16000)

    # wav_length = len(y)/sr
    input_nfft = int(round(sr*frame_length))
    input_stride = int(round(sr*frame_stride))

    S = librosa.feature.melspectrogram(y=y, n_mels=40, n_fft=input_nfft, hop_length=input_stride)

    print("Wav length: {}, Mel_S shape:{}".format(len(y)/sr,np.shape(S)))


    plt.figure(figsize=(10, 4))
    librosa.display.specshow(librosa.power_to_db(S, ref=np.max), y_axis='mel', sr=sr, hop_length=input_stride, x_axis='time')
    plt.colorbar(format='%+2.0f dB')
    plt.title('Mel-Spectrogram')
    plt.tight_layout()
    plt.savefig('Mel-Spectrogram example.png')
    plt.show()

    return S
man_original_data = '/mnt/junewoo/workspace/transform/test_wav/man_wav1.wav'
mel_spec = Mel_S(man_original_data)
728x90
728x90

음성 처리를 하다 보면 음성 데이터를 Down Sampling 하거나, Up sampling 할 경우가 있다.

예를 들어 보유한 데이터의 Sampling rate가 44.1kHz 인데, n_fft가 너무 크기 때문에 모든 것을 다 함축하여 Mel scale log spectrogram으로 담지 못할 경우가 있다.

이럴 때 자주 사용하는 것이 Down sampling이라고 한다.

오늘은 librosa 라이브러리를 이용하여 down sampling을 해보겠다.
우선 librosa 라이브러리를 설치해야 한다.

설치하였다고 가정하고,  함수를 하나 만들어서 진행하겠다. 함수 이름은 def down_sample이다.

def down_sample(input_wav, origin_sr, resample_sr):
    y, sr = librosa.load(input_wav, sr=origin_sr)
    resample = librosa.resample(y, sr, resample_sr)
    print("original wav sr: {}, original wav shape: {}, resample wav sr: {}, resmaple shape: {}".format(origin_sr, y.shape, resample_sr, resample.shape))

이 함수에서는 wav 파일과 기존 original sr, resample 할 sr을 받고 있다.

보유한 데이터의 음성은 16kHz이므로, original_sr = 16000, resample_sr = 8000으로 진행한다.

 

음성 처리에 있어서 librosa 라이브러리가 정말 잘 지원해주고 있다. resample을 하기 위해서는 3번째 줄의 resample = librosa.resample(y, sr, resample_sr)을 해주면 된다.

 

그 아래의 print 한 결과는 다음과 같다.

 

즉 기존의 y data는 53720이고, 그의 절반으로 하였기 때문에 26860으로 출력이 된다.

 

이렇게만 보면 이해하기가 어렵다. 아래의 코드를 활용하여 직접 그림을 그려보자.

 

    plt.figure(figsize=(10, 4)) # 10,4 크기로 그림
    plt.subplot(2, 1, 1) # 2개를 그릴건데, (총 개수, 가로, 세로) 순서로 그림
    time1 = np.linspace(0, len(y) / sr, len(y)) # 음성의 데이터를 이용하여 음성 총 길이를 구함
    plt.plot(time1, y) # 음성 flot
    plt.title('Original Wav')

    plt.subplot(2, 1, 2)
    time2 = np.linspace(0, len(resample) / resample_sr, len(resample))
    plt.plot(time2, resample)
    plt.title('Resampled Wav')

    plt.tight_layout()
    plt.savefig('compare_16k_vs_8k.png') # 저장

위의 코드에 자세히 주석을 달아놓았다. 위의 코드처럼 입력을 하면 다음의 그림을 얻을 수 있다.

 

 

코드에서 subplot은 2,1,1 -> 총 2개의 사진 중 맨 위를, 2,1,2 -> 총 2개의 사진 중 맨 아래에 그림을 그리겠다는 뜻이다.

 

육안으로 보았을 때 magnitude나 phase의 변화는 없다. 다만 자세히 들여다보면 data의 magnitude가 2개 중 1개씩 비어있다. 즉, 데이터가 총 100개면, resample 하였을 경우 (1/2로 하였기 때문에) 50개의 데이터만 남아있다. 이때, 데이터의 위치는 변화하지 않고 중간중간만 비어지는 것이다.

 

이제 음성 데이터를 저장하여 들어보겠다.

 

    sf.write('./' + '16k.wav' , y, origin_sr, format='WAV', endian='LITTLE', subtype='PCM_16')
    sf.write('./' + '8k.wav', resample, resample_sr, format='WAV', endian='LITTLE', subtype='PCM_16')

 

아래 글에 작성한 것처럼, sf.write를 사용하여 현재 directory에서 16k.wav 라고 저장하는 코드이다. 

 

실제로 음성 첨부를 하여 들어보면,

 

 

 

8kHz

 

16kHz

 

육안으로 들었을 때, 두 데이터의 차이는 크지 않다.

 

저장되는 크기는 어떨까? 예상한 것처럼, 절반으로 resampling 하였기 때문에 데이터의 크기 또한 절반으로 reduce 된다.

 

 

전체 코드는 아래와 같다.

import librosa
import numpy as np
import soundfile as sf
import matplotlib.pyplot as plt

def down_sample(input_wav, origin_sr, resample_sr):
    y, sr = librosa.load(input_wav, sr=origin_sr)
    resample = librosa.resample(y, sr, resample_sr)
    print("original wav sr: {}, original wav shape: {}, resample wav sr: {}, resmaple shape: {}".format(origin_sr, y.shape, resample_sr, resample.shape))

    plt.figure(figsize=(10, 4))
    plt.subplot(2, 1, 1)
    time1 = np.linspace(0, len(y) / sr, len(y))
    plt.plot(time1, y)
    plt.title('Original Wav')

    plt.subplot(2, 1, 2)
    time2 = np.linspace(0, len(resample) / resample_sr, len(resample))
    plt.plot(time2, resample)
    plt.title('Resampled Wav')

    plt.tight_layout()
    plt.savefig('compare_16k_vs_8k.png')

    sf.write('./' + '16k.wav' , y, origin_sr, format='WAV', endian='LITTLE', subtype='PCM_16')
    sf.write('./' + '8k.wav', resample, resample_sr, format='WAV', endian='LITTLE', subtype='PCM_16')

man_original_data = '/mnt/junewoo/workspace/transform/test_wav/man_wav1.wav'
down_sample(man_original_data, 16000, 8000)

 

728x90
728x90

1강의 Introduction에 이어 전체 Audio Signal Processing에 필요한 간단한 Basic Mathematics들을 소개하고 있다. 아래의 수학적인 지식들에 대해 간단히 교수님께서 설명을 해주시는 강의이다.





먼저 Sinusoidal functions에 대해 보겠다. Sinusoidal functions은 sinewaves, 즉 사인파로 이해하면 될 것 같다. 사전적인 이름으로는 사인파형을 갖는 함수라고 나와있다.


위의 식은 아래와 같다. 


먼저 대문자 A는 진폭이다.

는 한국어로는 각진동수이다. 

이고, Hertz를 구하기 위해 오메가를 2pi로 나눈다.

이다. 실제 시간인 t는   이용하여 얻을 수 있다.

는 초기 각도이다.





위의 그래프는 위에서 배운 amplitude, angular frequency, time index, sampling period, initial phase, frequency in Hertz를 직접 대입하여 넣었을 때의 신호이다.


이 되고, 이 최종 신호인 X는 

가 된다.

 






다음은 복소수에 대해 보겠다.

Complex numbers는  로 표현되며 이때 a,b는 real 값, j는 imaginary 값으로, a는 real part, jb는 imaginary part라고 불리운다. 이 때 imaginary part를 표현하기 위해 j를 곱하는데 j는 허수이며이다. 그러므로 jb는 허수부로 구성되어 있다.

위의 그림 중 Complex plane은 우리말로 복소평면으로, 복소수를 기하학적으로 표현하기 위해 개발된 좌표평면으로 x축은 Re (real axis) 값으로 되어 있으며 horizontal axis이다. y축은 Im (imaginary axis) 값으로 되어 있으며 vertical axis이다. 위의 Complex plane의 magnitude는 1이다.








복소수는 2가지로 표현될 수 있는데, 1가지는 Rectangular form(직각좌표 형식)이며 나머지 1개는 Polar form(극좌표 형식)이다.

Rectangular form은 실수 부분인 a값과 허수 부분인 b값을 명시적으로 표현하는 가장 직접적인 형식이다. Complex Plane 내부의 2개 value의 교차점이 위의 그림에서 X표시가 되어 있는 가 actual complex number가 된다. 반면 Polar form은 위의 그림 중 아래의 원처럼 X 표시를 원점 벡터 (0, 0)의 끝으로 간주한다. 따라서 벡터로서 크기 A를 갖으며 를 이용하여 크기 A를 구할 수 있다. 또한 각도를 갖기 때문에, a에서 b의 역 탄젠트를 계산함으로써 a와 b로부터 계산 될 수 있다. Polar form은 복소수의 합계 및 곱셈 연산을 보다 직관적으로 보여줄 수 있다.








오일러의 공식은 인터넷에서 자세히 나와있다. 이 강의에서는 간단히만 소개하고 있는데 sine과 복소수를 결합하는 것을 설명하고 있다.

오일러 공식은 직사각형과 복소수간의 Polar co-ordinates(극좌표)를 매우 유용한 관계로 설립해준다.

는 Complex Exponential인데, real part인  와 imaginary part인 의 합으로 표현될 수 있다. 위의 공식을 이용하여 복잡한 지수함수로부터 시작하여 실수 부분인 를 얻을 수 있고, 복소수 부분인  를 얻을 수 있다.


이 방정식을 수학에서 가장 주목할만한 공식으로 교수님은 주장하고 계시므로 오일러 공식을 다시 찾아서 완벽히 이해할 필요가 있겠다. Complex plane에서 위의 식을 통하여 Complex Value인 magnitude가 1인 크기를 갖는다. 앞으로 배울 Discrete Fourier Transform을 이해하는데 가장 기초 개념이다. 

(결론 = 실수, = 허수)







아래의 그림은 복소수 사인파형에 대해서 간략히 설명하고 있다. 복소수 파형은 real part와 imaginary part로 구성되어 있는데, 아래의를 오일러 공식에 의해 풀면 real part가 되고,  imaginary part가 된다. 즉 두개의 part가 합쳐져 있고, 강의에서는 를 complex sinusoid (complex sinewave)라고 설명한다.

위의 그림에서 Real part sinewave를 구해보자. 먼저 는 amplitude times이며, 오메가 nT+phi의 코사인과 곱해져있는 사인파형이다. 즉 풀어서 말해보면, real sinusoid인 임의의 amplitude times 값을 갖는  는 Complex sinewave 들의 합인  로 표현될 수 있다. 그러므로 위의 2개의 합을 갖는 complex sinewave를 이용하여 real sinewave를 generate할 수 있다. 

강의에서 간단히 요약하는 부분이 나오는데, 위의 식에서  식을 가르키며, summing two complex sinewaves를 하는 것은 sinusoid에서 imaginary part를 제거하고 real part만을 유지시킬 수 있다고 한다. 위의 공식은 매우 복잡해보이지만 이는 Fourier Transform을 사용하기 위해 엄청나게 유용한 부분이므로 반드시 이해해야하는 부분이다.








아래의 그림은 real과 imaginary를 그림으로 표현하고 있다.

파랑색은 real part인 cosine 함수이며, 초록색은 imaginary part인 sine 함수이다.








다음은 스칼라 내적 곱에 대해서 설명한다.

강의에서는 앞으로 익숙해져야 할 개념 중 하나가 스칼라 내적 곱이라고 한다. 이것은 시퀀스간의 공통 대수 연산이다. 아래 그림의 첫번째 식은 2개의 길이가 같은 숫자 시퀀스들의 연산이다. 이 연산을 통해 single value를 return한다. Scalar dot은 기하학적 또는 대수적으로 정의할 수 있다.

Example에서, x[n]은 real 부분이고, y[n]은 conjugated 부분이다. 즉 complex sequences이다. 이 dot product는 point to point multiplication이다. 딥러닝에서 자주 말하는, otherwise multiplication으로 보면 되겠다. 즉 정리해보자면, Scalar product는 2개의 sequence의 연산을 통해 최종값인 single value를 얻는 것을 의미한다.







다음은 sequence들이 직교할때의 상황에 대해서 설명한다. 스칼라 내적 곱에서의 중요한 특성은 두 개의 sequence가 직교할때이다. 이 때, 곱의 값은 0이 된다. 아래의 ㅗ 표시는 x is orthogonal to y only if the scalar product of x times y is equal to 0 이라고 표현하고 있다.


위의 그래프는, 짧은 2개의 sequence인 x와 y가 있다. x는 (2,2)이고 y는 (2,-2)이다. 위의 그래프는 2차원 공간에서 서로 orthogonal 한 것을 보여준다. 즉 우리는 저 2개의 sequence들이 서로 수직적인것을 볼 수 있다.

위의 식을 통해 x와 y의 내적곱을 수행하면 0임을 증명 가능하다. 마찬가지로 이 부분도 DFT를 진행하기 위해 기본 정보이니 꼭 알아두도록 하자.





또다른 수학 개념 중 앞으로의 강의에서 사용 될 신호 분석 기법 중 하나는 Even 과 Odd 함수이다. 즉 짝수와 홀수이다.

아래의 식은, 만약 함수가 음수 part일 경우 이라고 부른다. 이 때,

Even(짝수) 함수는 일 경우이다. 이를 Symmetric function(대칭함수)라고 부른다.

Odd(홀수) 함수는 일 경우이다. 이를 Antisymmetric function(비대칭함수, 반대칭함수)라고 부른다.


위의 두 함수는 앞에서 말한 코사인과 사인의 매우 전형적인 예시가 된다.

왼쪽은 Symmetric(Even Function)이다. 0점을 기준으로 대칭이다.

오른쪽은 Antisymmetric(Odd function)이다. 0점을 기준으로 비대칭하다.




마지막으로 Convolution에 대해서 소개하고있다. 이 부분은 매우 간단히 넘어가고 있다. 이 수학적인 방법인 Convolution은 2개의 sequence를 이용하여 3번째 sequence를 만드는데, 1개의 원래 sequence의 모방된 버전처럼 될 수 있는 기법이라고 소개하고 있다.

위의 x1, x2는 서로 다른 shape을 갖는 sequence들이다. 3번째 파형인 결과 sequence인 y는 convolve가 되고난 이후이다. 결과적으로 보자면 더 부드러워진 함수가 되었다. Convolution은 cross correlation(상호 상관관계)과 비슷하다고 설명하고 있다. 사운드 필터링을 구현하는데 사용되는 일반적인 방법이다.






일단 여기까지가 Basic Mathematics의 강의가 끝났다.

강의자료들의 Reference들은 아래 그림과 같다. 교수님은 Reference for the mathematics of the DFT by Julius O. Smith 교수의 강의 자료를 보고 확실히 이해하라고 하셨다. 나도 이제 다시 월요일이 되면 course가 있지만... 듣고 확실히 이해하는 기회를 가져야겠다.




728x90
728x90

오늘부터 Coursera에서 Audio Signal Processing for Music Applications의 강의를 약 10주간에 걸쳐 듣기로 하였다. Coursera의 장점 중 하나는 매 주 강의가 나오고 그에 대한 시험 및 과제를 제출해야 수료하는 것으로 나오는데, 굉장히 잘 관리를 해주는 느낌이다.


강의는 Universitat Pompeu Fabra of Barcelona의 Dept. of Information and Communication Technologies의 Xavier Serra 교수님과 Stanfor University의 Electrical Engineering의 Prof Julius O Smith, III 교수님이 진행하신다. 강의 사이트는 https://www.coursera.org/learn/audio-signal-processing/home/info 에서 확인할 수 있다.


개인 공부 용도로 진행한 강의 내용을 정리할 예정이다.


첫번째 페이지에서는 Audio Signal Processing for Music Application 강의에 대해 소개하고 있다. 오디오 신호 처리의 개념을 소개하고 신호 처리의 일반적인 음악 응용 프로그램을 살펴볼 오디오 신호 처리의 기초 강의이다. 




아래 사진은 Audio Signal Processing에 대해 정의를 하는데, Intentional alteration of sound. 의도적인 소리 변경에 대하여 소개하고 있다. 강의에서는 audio signal processing을 "오디오 신호 처리가 의도적으로 소리를 변경하는 계산 방법에 초점을 맞춘 엔지니어링 분야"로 정의하고 있다. 


위의 블록 다이어그램에서, 오디오 신호를 입력으로 받아 인간 혹은 자동 시스템으로부터의 컨트롤에 의해 생성되는 오디오 신호 혹은 다른 유형의 정보를 오디오 신호 처리로 설명하고 있다. audio signals은 digital 또는 analog formats으로 electronically하게 표현 될 수 있으므로, signal processing은 digital 또는 analog 어느 영역에서나 발생할 수 있다. 아날로그 처리기는 전기 신호에서 직접 작동하지만 디지털 프로세서는 해당 신호의 binary 표현에서 수학적으로 작동한다.



아래의 그림을 통해 강의에서는 analog 신호 대 digital 신호의 차이에 대해 설명한다.


먼저 analog sound는 대부분 전기의 특성이다. 즉, 소리의 air pressure(기압) 파형을 나타내는 전압 레벨이다. 그림의 윗 plot에 표시된 것과 같은 연속적인 function이다. 

반면에 digital 표현은 압력을 이진수로 표현하므로 아래의 plot에 표시된 것과 같이 discrete function(이산 함수)으로 나타난다. digital 표현은 마이크로프로세서와 컴퓨터를 사용하여 나타낸다. analog에서 digital로 변환이 손실이 되기 쉽지만, 최근의 audio systems은 digital signal processing이 analog technique보다 더욱 powerful하고 efficient하기 때문에 digital signal processing 방식을 사용한다.



signal processing application은 데이터 압축 또는 audio coding이라고도 한다. 여기서의 목표는 digital audio 스트림의 bandwidth 요구 사항을 줄이는 것과 audio file의 저장소 크기를 줄이는 것이다. 


압축 기술에는 두 가지 유형이 있다. 하나는 정보를 잃지 않는 무손실(lossless)이라고하는 것이고, 다른 하나는 손실(loss)인데, 다행이도 정보 손실과는 관련이 없다. 여기서 WAV 파일을 입력 파일로 취하는 perceptual 오디오 코더의 예를 볼 수 있다. 그러므로 압축 없이 digital domain에서 사운드를 직접 표현한 파일만 사용한다. 이를 WAV 라고 부른다. => 압축 없이 디지털 도메인에서 사운드를 직접 표현하는 파일 형태

오디오 코더를 통해 perceptually-based 오디오 코더는 해당 파일을 훨씬 작은 파일로 변환이 가능한데, 그것이 바로 MP3이다. MP3 형식은 소리의 perceptual 특성에 기반하여 파일을 매우 압축하는 것이다.




위의 그림은 강의에서 소개하고 있는 Audacity라는 프로그램에서 플러그인을 여러 개 포함하는 두 개의 스크린 샷이다. 오디오 신호를 변환하는 여러 유형의 애플리케이션이 포함되어 있다. 왼쪽에는 dynamic 압축이 표시되고 오른쪽에는 reverb(잔향)이 표시된다. 

위의 그림은 audio signal processing 기술을 사용한 2종류의 변환이지만, 이 외에도 많은 변형이 있다. echo, equalizer, flanger, phaser, chorus, pitch shift, time stretching, voice effects, 3D audio effects, morphing 등이 포함된다. 



signal processing의 전통적인 용도 중 하나는 기존 사운드를 모방하거나 새로운 음색(timbres)을 생성하여 사운드를 생성하려는 목적으로, 사운드 합성과 관련된다. 아래의 그림에서는 세 가지의 각기 다른 합성 기술의 블록 다이어그램을 보여준다.


왼쪽 위의 그림은 Subtractive Synthesis(감산 합성)이다. 감산 합성은 풍부한 사운드에서 시작하여 풍부한 사운드의 구성 요소를 필터링하여 다른 사운드를 만든다. 

오른쪽 상단에있는 그림은 FM 합성이다. 하나의 발진기를 다른 발진기로 변조하고 발진기의 주파수를 변조하는 기술이다. 이 기술을 사용하여 다양한 소리를 얻을 수 있다.

아래쪽 있는 Additive Synthesis(추가 합성)이라는 그림은, 가장 직관적인 합성의 종류이다. 왜냐하면 일부 발진기를 추가하여 그로 인해 sinusoid(사인 곡선)가 추가가 되므로, 매우 간단한 사운드들을 추가하여 복잡한 사운드를 생성할 수 있기 때문이다.

이외에도 수년에 걸쳐 개발 된 다른 많은 합성 기술이 있다. 여기에는 granular synthesis, the idea of physical modeling, waveshaping, sampling, spectral synthesis이라고하는 개념이 포함된다. 


강의에서 마지막으로 언급하고자 하는 것은 사운드의 의미있는 특성을 설명하고 모델링하는 목표로 오디오 신호를 분석하는 기술을 사용하는 것이다. 위의 분석 기술은 지난 몇 년 동안 매우 확대된 주제인 정보 검색과 음악 정보 검색 분야와 관련있다. 



위의 그림은 의미 있는 music 개념을 추출할 수 있는 알고리즘이다. 특히 음악의 각 부분에 대한 개념은 오디오 신호에서 얻을 수 있는 음악의 부분 중 일부의 조화 또는 화음과 관련이 있다. 따라서 오디오 신호에서부터 시작하여 다양한 분석 단계를 수행하며 결국에는 음악의 핵심을 식별 할 수 있다. 


앞으로의 강의에서는 오디오 신호에서 나오는 소리의 loudness, timbre, pitch와 같은 디스크립터를 참조한다. 또는 음악적으로 의미있는 디스크립터를 리듬, 하모니 또는 멜로디와 같은 개념에 대해 이야기 할 것이라고 한다.





728x90
728x90

Automatic Speech Recognition (ASR) 시스템에서 AM이라는 단어가 많이 보일 것이다. 그렇다면 AM이 무엇인지 간단히 알아보도록 하자.


AM은 Acoustic Model 로서, 음성 신호와(audio signal) 음소(phonemes) 또는 음성을 구성하는 다른 언어 단위간의 관계를 나타 내기 위해 음성 인식에 사용된다. AM은 오디오 녹음 및 해당 녹음의 transcription(음성의 text) 으로부터 학습된다. 녹음된 음성 신호, transcription, 그리고 소프트웨어를 사용하여 소리의 통계적 표현을 생성하여 각 단어를 구성한다.


최신 음성 인식 시스템은 음성의 통계적 특성(statistical properties of speech)을 나타내기 위해 AM과 언어 모델을(LM) 모두 사용한다. AM은 오디오 신호와 언어의 음성 단위 간의 관계를 모델링해주는 역할을 한다. LM은 언어의 단어단어 시퀀스를 모델링을 책임진다. 이 두개의 모델들은 결합되어 input으로 주어진 오디오 세그멘트에 해당되는 top-ranked 단어 시퀀스를 얻는 역할을 하게 된다.


대부분의 음성 인식 시스템은 대략적으로 입력 음성의 프레임 당 10ms의 duration을 갖는 프레임 단위로 계산한다. Mel Frequency Cepstrum을 적용하여 각 frame의 raw audio signal을 변환할 수 있다. (MFCC, Mel-Spectrogram 등에서 windows size, hop length 등, hop length 만큼 overlap 하여 대략적으로 10ms 당 계산하는 편이다.). 위의 변환 계수는 일반적으로 Mel Frequency Cepstral Coefficients (MFCC)로 알려져 있으며, 다른 특징과 함께 AM에 대한 입력으로 사용된다.


음성인식의 목적은 입력 waveform sequences를 해당 단어 또는 character sequences에 매핑하는 것이다. 그러므로 ASR를 구현하는 것은 채널 디코딩 또는 패턴 분류 문제로 간주 될 수 있다. 통계적 모델링은 아래의 식처럼 주어진 waveform sequence O에 대해 Posterior Bayesian Distribution (후미 베이지안 분포)에 기초한 "maximum a posteriori" (MAP) 추정을 사용하여 output sequence W*로 세운 뒤 해결할 수 있다.



Conditional Probability인 Likelihood p(W)는 정확한 sequence W가 발생하는 'priori probability' 이다. 우리는 이것을 LM (Language Model) 이라고 부른다. 즉, LM이 given된 상태에서 오른쪽의 p(O|W)는 정확한 observation sequence를 생성할 확률이다. W에 조건부인 ASR 시스템의 AM에 해당하는 확률이다. 




위의 그림은 주요 프론트 엔드 프로세싱 모듈, AM, LM 및 디코더를 포함하는 ASR 시스템의 구조를 보여준다. Backend processing의 Decoding 프로세스는 주로 train 된 AM 및 LM을 사용하여 최적의 output character sequence를 얻는 것이다.


AM의 목적은 p(O|W) 를 계산하는 것이다. 즉 모드에 대한 음성 파형을 생성할 확률을 계산하는 것이다. ASR 시스템의 중요한 부분인 AM은 계산 오버 헤드의 상당 부분을 차지하며 시스템 성능을 결정한다. GMM-HMM (Gaussian Mixture Model - Hidden Markov Model) 기반 AM은 전통적인 음성 인식 시스템에서 널리 사용된다. 아래의 그림을 보자.





이 모델에서, GMM은 음성의 음향 특성의 분포를 모델링하는데 사용되고, HMM은 음성 신호의 시간 시퀀스를 모델링하는데 사용된다. 2006 년 Deep Neural Networks이 시작된 이후로 DNN (deep neural networks)이 음성 AM에 적용되었다. 2009년, Geoffrey Hinton과 그의 학생들은 음성 인식 음향 모델링에서 feedforward fully-connected deep neural networks를 사용하였다. (Mohamed A, Dahl G, Hinton G. Deep belief networks for phone recognition[C]//Nips workshop on deep learning for speech recognition and related applications. 2009, 1(9): 39.)


최근에는, 기존의 GMM-HMM AM과 비교하여 DNN-HMM 기반 AM이 TIMIT dataset에서 우수한 성능을 보이고 있다. GMM과 비교할 때 DNN은 다음과 같은 점에서 유리하다

- DNN이 음성의 음향 특성의 posterior probability를 모델링 할 때 특성 분포에 대한 De-distribution이 필요하지 않다.
- GMM은 입력 특성에 대해 de-correlation processing이 필요하지만 DNN은 다양한 형태의 입력 특성을 사용할 수 있다.

- GMM은 single-frame 음성만 input으로 사용할 수 있지만, DNN은 인접한 프레임을 연결하여 valid한 context 정보를 캡처 할 수 있다.


최근 들어, Convolutional Neural Networks, Recurrent Neural Networks의 사용으로 AM modeling이 크게 향상되었다. 또한 d-vector를 LSTM 등 DNN으로 출력하여 ASR에 적용하는 편도 많아지고 있는 편이다.


References: Mohamed A, Dahl G, Hinton G. Deep belief networks for phone recognition[C]//Nips workshop on deep learning for speech recognition and related applications. 2009, 1(9): 39.


728x90
728x90

Keras를 사용하다보면, 간단하게 구현된 라이브러리를 사용할 때가 있다.

그래서 더 사랑을 받는게 아닐까. 너무 간결하고 간단한 라이브러리들이 많다.


최근에 사용했던 간단한 라이브러리 Transformer 모델의 Self-Attention이다.


이 Function을 사용하기 위해서는 따로 import를 해줘야하는데 아래와 같이 하면 된다.


from keras_self_attention import SeqSelfAttention


자 이제 CNN 몇개와 RNN 몇개를 쌓고, 그 아래에 Self-Attention을 쌓았다고 가정해보자.


이 모델을 Fine-tune 하기 위해 일정 learning rate로 돌려놓고, 어느정도 loss를 깎은 다음 일정 이상 loss가 떨어지지 않으면 early stopping을 하여 learning rate를 기존보다 0.1배 정도로 낮춰서 fine-tune 할 예정이다. 이를 위해 모델을 save해야한다.


그럼 모델을 load할 때 종속성 문제가 생길 수 있다. 위의 SeqSelfAttention 인식을 못 할 수 있는 것이다.


내가 안되었던 경우는 다음과 같다. 

from keras.models import load_model, Model
import tensorflow as tf
import os
import time
from keras_self_attention import SeqSelfAttention
global graph, model
graph = tf.get_default_graph()
### GPU Setting : Not Using GPU
os.environ["CUDA_VISIBLE_DEVICES"]="-1"

# Load Model
model = load_model('/data/Auditory_Emotion_Recognition/model_attention/fianl_emotion_model.h5')


코드를 보면 단지 model을 load하는데, Unknown layer라고 뜬다. 아래의 사진은 그 현상을 얘기한다.


즉 SeqSelfAttention을 import 하였는데도 이러한 문제가 생긴다.



이를 해결 하기 위해 model load시 다음과 같은 줄을 선언해주면 쉽게 해결할 수 있다.


model = load_model('/data/Auditory_Emotion_Recognition/model_attention/fianl_emotion_model.h5',custom_objects={'SeqSelfAttention':SeqSelfAttention})


custom_objects={'name':import_library_name}


위와 같이 custom_objects 를 선언해주면 사용 가능하다.




728x90

+ Recent posts