[2주차] 딥러닝 기반 음성인식 기초

Tobigs Sound Seminar·2021년 10월 30일
1

DL

목록 보기
1/1

1. Audio Auto Tagging, Speech Recognition (STT)

1.1 Audio Auto Tagging

Audio Autio Tagging (음향 이벤트 인식)은 오디오 신호에서 발생하는 이벤트 종류를 찾는 문제이다. 아기의 웃음이나 기타의 소리가 동시에 있는 소리는 독특해서 즉시 인식됩니다. 하지만 전기 톱 소리와 믹서기 소리처럼 유사한 소리가 동시에 포함된 오디오는 해당 소리들을 인식하기 어렵다. 따라서 특정 오디오에 대해서 해당 오디오가 포함하는 여러 이벤트들을 tagging할 수 있는 Multi-Label Classification이 가능한 Audio Auto Tagging System이 필요하다.

이미지 출처: https://www.kaggle.com/c/freesound-audio-tagging-2019

1.1.1 Audio Auto Tagging Metric : label-weighted label-ranking average precision

보통 Audio Autio Tagging을 평가할 때는 Average Precision, Rank-Average Precision과 같이 IR(Information Retrieval)에서 사용되는 metric이 많이 사용됩니다. (딥러닝 모델들을 활용해 Multi-Label Classification을 진행하면, 각 레이블에 대한 점수가 확률벡터로 나오게 될텐데, 실제 1인 Label에 대해서 얼만큼 높은 Rank로 모델이 예측하였는가가 결국 모델이 잘 예측했냐를 평가할 수 있기 때문에 IR쪽에서 사용되는 metric이 자주 사용되는 것 같네요)

Freesound Audio Auto Tagging 2019에서는 LRAP(Label-Weighted-Label-Ranking-Average-Precision)을 활용해 모델의 성능을 평가했습니다. LRAP는 Label=1인 값에 대해서만 Rank Precision을 계산합니다. (yij=1y_{ij} = 1)

LRAP(y,f^)=1nsamplesi=0nsamples11yi0j:yij=1LijrankijLRAP(y, \hat{f}) = \frac{1}{n_{\text{samples}}} \sum_{i=0}^{n_{\text{samples}} - 1} \frac{1}{||y_i||_0} \sum_{j:y_{ij} = 1} \frac{|\mathcal{L}_{ij}|}{\text{rank}_{ij}}

where,Lij={k:yik=1,f^ikf^ij}rankij={k:f^ikf^ij}\text{where}, \mathcal{L}_{ij} = \left\{k: y_{ik} = 1, \hat{f}_{ik} \geq \hat{f}_{ij} \right\} \text{rank}_{ij} = \left|\left\{k: \hat{f}_{ik} \geq \hat{f}_{ij} \right\}\right|

예시를 들어볼까요?

Label : [Cat, Dog, Bird]
1st Ground Truth : [1,0,1]
1st Model Output : [.8, .2, .9]
1st Model Score : [1(2/2),1(1/1)] = [Cat,Bird]
2nd Ground Truth : [1, 1, 0]
2nd Model Output : [.1, .2, .8]
2nd Model Score : [2/3,1/2] = [Cat,Dog]
One Final Score : Cat Score = (1+0.66)/2 = 0.83. Dog Score =0.50, Bird Score = 1.0. Final Score = (2 Cats/4 Labels)0.83 + (1 Dog/4 Labels)0.50 + (1 Bird/4 Labels)*1.0 = 0.79. 결국에 각 Label별 평균 스코어와 동일함으로 (1 + 1 + 0.66 + 0.50)/4 = 0.79.

참고: How to interpret: Label Ranking Average Precision Score

Code: LRAP

def _one_sample_positive_class_precisions(scores, truth):
    """단일 샘플에 대한 각 실제 클래스의 정밀도를 계산합니다.
    Args:
      scores: np.array of (num_classes,) giving the individual classifier scores.
      truth: np.array of (num_classes,) bools indicating which classes are true.
    Returns:
      pos_class_indices: np.array of indices of the true classes for this sample.
      pos_class_precisions: np.array of precisions corresponding to each of those
        classes.
    """
    num_classes = scores.shape[0]
    pos_class_indices = np.flatnonzero(truth > 0)
    if not len(pos_class_indices):
        return pos_class_indices, np.zeros(0)
    retrieved_classes = np.argsort(scores)[::-1]
    class_rankings = np.zeros(num_classes, dtype=np.int)
    class_rankings[retrieved_classes] = range(num_classes)
    retrieved_class_true = np.zeros(num_classes, dtype=np.bool)
    retrieved_class_true[class_rankings[pos_class_indices]] = True
    retrieved_cumulative_hits = np.cumsum(retrieved_class_true)
    precision_at_hits = (
            retrieved_cumulative_hits[class_rankings[pos_class_indices]] /
            (1 + class_rankings[pos_class_indices].astype(np.float)))
    return pos_class_indices, precision_at_hits


def calculate_per_class_lwlrap(truth, scores):
    """Calculate label-weighted label-ranking average precision.

    Arguments:
      truth: np.array of (num_samples, num_classes) giving boolean ground-truth
        of presence of that class in that sample.
      scores: np.array of (num_samples, num_classes) giving the classifier-under-
        test's real-valued score for each class for each sample.

    Returns:
      per_class_lwlrap: np.array of (num_classes,) giving the lwlrap for each
        class.
      weight_per_class: np.array of (num_classes,) giving the prior of each
        class within the truth labels.  Then the overall unbalanced lwlrap is
        simply np.sum(per_class_lwlrap * weight_per_class)
    """
    assert truth.shape == scores.shape
    num_samples, num_classes = scores.shape
    precisions_for_samples_by_classes = np.zeros((num_samples, num_classes))
    for sample_num in range(num_samples):
        pos_class_indices, precision_at_hits = (
            _one_sample_positive_class_precisions(scores[sample_num, :],
                                                  truth[sample_num, :]))
        precisions_for_samples_by_classes[sample_num, pos_class_indices] = (
            precision_at_hits)
    labels_per_class = np.sum(truth > 0, axis=0)
    weight_per_class = labels_per_class / float(np.sum(labels_per_class))
    
    per_class_lwlrap = (np.sum(precisions_for_samples_by_classes, axis=0) /
                        np.maximum(1, labels_per_class))
    
    return per_class_lwlrap, weight_per_class

1.1.2 Audio Auto Tagging Procedure

Wave Form 형태의 오디오를 Feature Extraction을 통해 모델의 Input으로 들어갈 수 있게 만들어 주고 (Mel Spectogram) 우리의 가정으로 만들어진 모델(CNN, RNN)을 통해 Feature Extraction을 진행해준다. 이후 Feature Space에서 Classifier를 학습해주면 된다.

오디오마다 Time Sequence가 다르면?

모델이 동일한 Time Sequence를 처리하도록 Train Data를 Chunk로 나누어 주고 Label을 복사하면 된다.

Mel-Power Spectogram

1.2 Speech Recognition (STT)

음성 인식이란 사람이 말하는 음성 언어를 컴퓨터가 해석해 그 내용을 문자 데이터로 전환하는 처리를 말한다. STT라고도 한다. 키보드 대신 문자를 입력하는 방식으로 주목을 받고 있다.
출처 : https://ko.wikipedia.org/wiki/%EC%9D%8C%EC%84%B1_%EC%9D%B8%EC%8B%9D

2. CTC Loss

2.1 Deep Speech2

이미지 참조 : http://proceedings.mlr.press/v48/amodei16.pdf

2.2 CTC (Connectionist Temporal Classification) Loss (Speech Recognition Loss Function)

Why we use CTC Loss?

일반적인 Speech Recongition에서 우리는 데이터셋으로 오디오 클립과, Transcript를 받게 됩니다. 하지만 우리는 어떤 단어의 Character가 Audio와 Alignment가 맞는지 알수 없죠 (2초 동안 조용하다가 Hello를 말하거나 바로 Hello를 말하거나 둘다 Transcript은 Hello다). 이러한 Alignment 없이, 어떤 Audio와 Text 사이의 규칙을 정의하기 힘듭니다. 또한 사람들의 언어사용을 하는것은 다양하기 떄문에 단일한 Rule로 그들을 정의하기는 쉽지 않습니다.

이미지 참조 : https://distill.pub/2017/ctc

우리가 input과 output사이의 정확한 Alignment가 labeling되어있는 데이터셋이 필요하지는 않습니다. 그러나 주어진 input에 대해서 output의 확률값은 필요하죠. CTC는 둘 사이의 가능한 모든 alignment의 가능성을 합산하여 작용합니다. (CTC works by summing over the probability of all possible alignments between the two). 이 말은 즉, Input도 output의 가능한 Align을 모두 뽑아서 Marginalize하자! 라는 뜻입니다.

Hello가 등장할 수 있는 모든 Path(Time Sequence)안에서 모델이 H,E,L,L,O를 예측하도록 하자!!

Motivation

모든 Time Step(25ms)에 대해서 labeling을 할 수 없다!

아래의 Notation을 활용하겠습니다.
x:audiox:audio
l:transcript(l=hello)l:transcript(l=hello)
l:blank가 추가된 transcript (l=hello)l^{'}:blank가 \ 추가된\ transcript\ (l^{'}=-h-e-l-l-o-)
ϵ(=):blank(silence,글자구분)\epsilon(=-):blank(silence,글자구분)
B:marginalize function B(AAAϵBBB)=ABB:marginalize\ function\ B(AAA\epsilon BBB)=AB
B1:inverse of marginalize functionB^{-1}:inverse\ of\ marginalize\ function
t:timestep(t=8)t:time-step(t=8)
s:음소(character)s:음소(character)
yst:timestep t에 state s가 등장할 확률y_s^{t}:timestep\ t에\ state\ s가\ 등장할\ 확률

이미지 참조 : https://distill.pub/2017/ctc

All Possible Path

CTC Rule
1. selp-loop : 자기 자신을 반복합니다.
2. left-to-right : non-blank 레이블을 순방향으로 하나씩 전이합니다. 역방향은 허용하지 않습니다. non-blank를 두 개 이상 건너뛰는 것 역시 허용이 안 됩니다.
3. blank 관련 : blank에서 non-blank, non-blank에서 blank로의 전이를 허용합니다.

이미지 참조 : https://distill.pub/2017/ctc

언어 조음 규칙상 절대로 발음될 수 없는 일부 경로들을 제거해 컴팩트하게 다시 그리면

이미지 참조 : https://distill.pub/2017/ctc

CTC 기법에서는 각 상태가 조건부 독립(conditional independence)라고 가정한다. 즉 time-step t의 output은 이전/이후 time-step의 상태가 어떻든 그 값이 변하지 않는다고 가정한다.

특정 Path의 등장 확률

p(πx)=t=1Tyπttp\left( \pi |\mathbf{x} \right) =\prod _{ t=1 }^{ T }{ { y }_{ { \pi }_{ t } }^{ t } }

가능한 모든 Path의 등장 확률

p(lx)=πB1(l)p(πx)p( \mathbf{l} | \mathbf{x} )=\sum _{ \pi \in {B}^{ -1 } \left( \mathbf{l} \right) }^{ }{ p\left( \pi | \mathbf{x} \right) }

이제 ysty_s^{t}일 때 Path의 등장확률을 구해보자

Forward Computation

전방 확률은 1:t 시점에 걸쳐 레이블 시퀀스가 1:s 가 나타날 확률
(3번째 time-step에서 e가 등장할 때, time-step 1:3까지 가능한 모든 Path의 경로인 1:e가 등장할 확률은?)

이미지 참조 : https://distill.pub/2017/ctc

Forward Probability

αt(s)=πNT:B(π1:t)=l1:st=1tyπtt{ \alpha }_{ t }\left( s \right) =\sum _{ \pi \in { N }^{ T }: B \left( { \pi }_{ 1:t } \right) ={ \mathbf{l} }_{ 1:s } }^{ }{ \prod _{ t'=1 }^{ t }{ { y }_{ { \pi }_{ t' } }^{ t' } } }

Dynamic Programming

Case 01 : ls=ϵ or ls2=ls{ \mathbf{l}' }_{ s }=\epsilon \text{ or } { \mathbf{l}' }_{ s - 2 }={ \mathbf{l}' }_{ s }

Case 02 : otherwiseotherwise

Forward Flow

Forward Computation

Backward Computation

후방 확률은 t:T 시점에 걸쳐 레이블 시퀀스가 s:|l|일 확률
(6번째 time-step에서 ϵ\epsilon이 등장할 때, time-step t:T까지 가능한 모든 Path의 경로인 ϵ\epsilon:|l|가 등장할 확률은?)

이미지 참조 : https://distill.pub/2017/ctc

Backward Probability

βt(s)=πNT:B(πt:T)=ls:lt=tTyπtt{ \beta }_{ t }\left( s \right) =\sum _{ \pi \in { N }^{ T }: B \left( { \pi }_{ t:T } \right) ={ \mathbf{l} }_{ s:| \mathbf{l} | } }^{ }{ \prod _{ t'=t }^{ T }{ { y }_{ { \pi }_{ t' } }^{ t' } } }

Dynamic Programming

Backward Computation

Complete Path Calculation

특정 시점, 특정 상태를 지나는 경로에 대한 등장 확률을 구해보겠습니다. 이는 앞서 구한 전방확률과 후방확률로 간단히 계산할 수 있습니다. t=3 시점에 상태가 h(s=2)일 때 모든 경로에 대한 확률은?

Forward Path Probability

Backward Path Probability

Complete Path Probability Calculation
Complete Path Probability는 Forward Path Probability와 Backward Path Probability의 곱으로 표현할 수 있다.

Likelihood & Gradient Computation

Likelihood

시점(time)을 고정해 놓고 상태(state)에 대해 모두 합을 취하면 우도가 됩니다.
(t=3 을 기준으로 우도를 구한다고 하면 파란색으로 칠한 다섯 칸에 해당하는 Complete Path 확률을 모두 더한 값이 됩니다)

Likelihood Computation

p(lx)=s=1lαt(s)βt(s)ylstp\left( \mathbf{l} | \mathbf{x} \right) =\sum _{ s=1 }^{ | \mathbf{l}' | }{ \frac { { \alpha }_{ t }\left( s \right) \cdot \beta _{ t }\left( s \right) }{ { y }_{ { \mathbf{l}' }_{ s } }^{ t } } }

Gradient

우리는 우도, 즉 p(l|x) 를 최대화하는 모델 파라메터를 찾고자 합니다. 이를 위해서는 우도에 대한 그래디언트(gradient)를 구해야 합니다. 이 그래디언트를 모델 전체 학습 파라메터에 역전파(backpropagation)하는 것이 CTC 기법을 적용한 모델의 학습(train)이 되겠습니다. 우도 계산은 보통 로그 우도(log-likelihood)로 수행하는데요. t 번째 시점 k 번째 상태(음소)에 대한 로그 우도의 그래디언트는 아래와 같습니다. ln(x) 를 x 로 미분하면 1/x 이고 우도에 로그를 취한 것을 합성 함수라고 이해하면 체인룰(chain)에 의해 우변처럼 정리할 수 있습니다.

Example

참고 ㅣ Connectionist Temporal Classification, ratsgo님의 Speech Book

2.3 Levenshtein distance (Speech Recognition Metric)

러시아 과학자 블라디미르 리벤슈테인(Vladimir Levenshtein)가 고안한 알고리즘입니다. 편집 거리(Edit Distance) 라는 이름으로도 불립니다. Levenshtein Distance는 두 개의 문자열 A, B가 주어졌을 때 두 문자열이 얼마나 유사한 지를 알아낼 수 있는 알고리즘입니다. 그러니까, 문자열 A가 문자열 B와 같아지기 위해서는 몇 번의 연산을 진행해야 하는 지 계산할 수 있습니다.

여기서의 연산이란, 삽입(Insertion), 삭제(Deletion), 대체(Replacement)를 말합니다.

Example01
A : Delegate, B : Delete
A > 'g'삭제 > 'a'삭제 > B
Levenshtein distance=2

Example02
A : Process, B : Professor
A > 'c'를'f'로 대체 > 'o'삽입 > 'r'삽입 > B
Levenshtein distance=3

참고 ㅣ Levenshtein distance

3. LAS (Listen & Attend & Spell)

이미지 참조 : https://jybaek.tistory.com/793

Listener는 피라미드 형식으로 구성된 bidirectional LSTM(BLSTM) 인코더이며 입력 시퀀스 x로부터 특징을 뽑아냅니다. Speller는 attention-based 디코더에서 h와 s로부터 컨텍스트 벡터 c를 생성하고 이를 기반으로 Grapheme 캐릭터 y를 뽑아냅니다. 이런 과정에 <\sos>와 <\eos>를 특별한 토큰으로 사용하는데 각각 special start-of-sentence, end-of-sentence를 나타냅니다.

작성자 : 16기 장준원

References
출처의 검색일은 모두 2021.10.30.-2021.10.31이다.

profile
투빅스 15&16 음성세미나 입니다.

5개의 댓글

comment-user-thumbnail
2021년 11월 2일

[16기 김윤혜]
1. CTC의 alignment는 음성의 길이에 맞춰서 모든 time sequence에 텍스트와 Blank가 서로 독립적으로 채워지는 것이 맞나요? 그렇다면 CTC는 전부 RNN 기반의 모델을 사용하는 것인가요?
2. LAS 모델의 Listener를 피라미드 형식으로 구성하는 이유가 무엇인지 궁금합니다.

답글 달기
comment-user-thumbnail
2021년 11월 2일

[15기 안민준]
1. CTC의 엡실론 토큰이 음성 공백을 채워주기 위한 의도라고 강의에서 설명되었습니다. 혹시 음성 인식 중 '띄어쓰기' 등을 인식하고자 하는 시도가 있었을지, 그리고 있었다면 이 경우엔 엡실론 토큰을 사용했을지 궁금합니다.
2. LAS 의 인코더는 피라미드 형식으로 인코딩 된다고 되었는데, 이 경우 음소가 붙어 있는 등 인접한 음 끼리의 정보가 약간 소실되는 문제가 없는지 궁금합니다.

답글 달기
comment-user-thumbnail
2021년 11월 3일

[15기 황보진경]

Audio Auto Tagging이란 오디오 신호에 존재하는 다양한 이벤트들을 detect하는 multi-label classification task이다. 이 때 사용하는 metric이 LRAP이다. rank 정보를 이용하여 확률을 계산한다.

CTC Loss

모든 타임스텝에 대해 라벨링을 할 수 없기 때문에 alignment가 필요한 seq-to-seq task에 사용할 수 있는 Loss이다.

  • CTC rule
  1. self loop: 자기자신을 반복
  2. left-to-right: 다음 label로 엡실론 혹은 순방향인 바로 다음 토큰만 가능하다.
  3. blank: blank -> non-blank나 non-blacnk -> blank가 가능하다.
    조건부 독립을 가정하여 주어진 label이 얻어질 수 있는 모든 Path들의 등장 확률을 계산한다. 특정 시점 t에 s라는 토큰이 등장할 확률은 forward path probability와 backward path probability를 곱하여 구할 수 있다.
  • forward path probability
    앞에 나올 수 있는 token을 고려하여 현재 시점 t에서 s라는 토큰이 등장하는 Path의 확률을 의미한다.
    DP를 이용하면 계산 효율성을 높일 수 있다. CTC rule에 따라 다음 두 가지 경우로 나눌 수 있다.
    case 1) 현재 상태가 blank이거나 현재 상태가 두번째 전 상태와 일치하는 경우, t-1번째 스텝은 blank이거나 s-1 번째 label이어야 한다.
    case 2) 그 외의 경우에는 t-1번째 step이 s이거나 s-1(blank)이거나 s-2가 된다.
  • backward path probability
    현재 시점 t에서 s라는 토큰이 등장했을 때, 뒤에 나올 수 있는 Path의 확률을 의미하며 계산과정은 위와 동일하다.

위에서 구한 확률을 이용하여 시간을 고정하고 나올 수 있는 token들의 complete path에 해당하는 확률을 모두 더하면 Likelihood를 계산할 수 있다.

STT Metric: Levenshtein distance

Edit distance라고도 불리며, 두개의 문자열이 같아지기 위해서 몇 번의 연산(삽입, 삭제, 대체)을 수행해야 하는지를 계산한다.

답글 달기

[15기 조효원]

  • Audio Auto Tagging이란 한 오디오 파일에 어떤 이벤트가 등장하는가를 찾는 task이다. 일반적으로 waveform에서 얻어낸 spectral feature를 딥러닝 모델을 거쳐 feature를 얻고, 이 feature들을 이용해 classifier를 학습한다.
  • CTC란 음성과 텍스트 사이의 alignment를 찾기 위한 기법이다. alignment를 찾기 위해, 특정 텍스트가 정해진 time sequence에서 등장할 수 있는 모든 가능성(all possible path)를 찾아내 확률값을 계산한다. 이때 가능한 경로를 찾기 위한 레이블 사이의 전이는 다음의 세 가지 경우로 한정한다. 이를 CTC rule이라고 칭한다.
1. self-loop : 자기 자신을 반복합니다.
2. left-to-right : non-blank 레이블을 순방향으로 하나씩 전이합니다. 역방향은 허용하지 않습니다. non-blank를 두 개 이상 건너뛰는 것 역시 허용이 안 됩니다.
3. blank 관련 : blank에서 non-blank, non-blank에서 blank로의 전이를 허용합니다.

해당 rule들로 구성된 all-path 중 특정 path의 등장확률은 Forward와 Backward Computation의 곱 fbf*b으로 구한다. ff는 t시점에 특정 레이블 k가 등장하는 path의 확률, backward는 뒤에 나올 수 있는 path의 확률을 의미한다. 이를 이용해 likelihood를 구할 수 있다.

답글 달기
comment-user-thumbnail
2021년 11월 8일

[15기 이성범]

  • Audio Auto Tagging : 오디오 신호에서 발생하는 이벤트 종류를 찾는 Task

    • 음성을 인식할 때 들어오는 음성이 무조건 하나의 음성이라는 보장이 없기 때문에 음성 인식에서 Audio Auto Tagging System을 구축하는 것은 중요하다. (noise를 필터링하기 위한 시스템)
    • 여러 이벤트 들을 tagging 한다는 관점이기 때문에 Multi-Label Classification으로 접근이 가능하다.
    • Multi-Label Classification으로 접근을 하기 때문에 Average Precision, Rank-Average Precision과 같이 IR(Information Retrieval)에서 사용되는 metric이 많이 사용된다.
    • Audio Representations (Mel Spectogram) -> Feature Extraction (인코더) -> Classifier (디코더)
  • Speech Recognition : 사람이 말하는 음성 언어를 컴퓨터가 해석해 그 내용을 문자 데이터로 전환하는 Task

    • 대표적인 모델이 Deep Speech2
    • Speech Recognition에서는 CTC (Connectionist Temporal Classification) Loss (Speech Recognition Loss Function) 가 많이 쓰인다.
    • 음성에 모든 Time step 마다 라벨링을 할 수 없기 때문에 CTC Loss를 사용한다.
    • CTC Loss는 예측하고자 하는 단어가 등장할 수 있는 Time Sequence 에서 예측하고자 하는 단어를 예측할 수 있도록 CTC Rule(selp-loop, left-to-right, blank)을 사용한 Loss이다.
답글 달기