본문 바로가기
딥러닝으로 하루하루 씹어먹기

딥러닝 STT 모델 - ESPNet (6 - Inference, Predict 시작!)

by Yoo Sung Hyun 2021. 11. 15.
728x90

이번시간까지 정리를 하면, 얼추 STT는 종료가 되지 않을까 사료된다.

2021.10.25 - [딥러닝으로 하루하루 씹어먹기] - 딥러닝 STT 모델 - ESPNet (5 - 음성처리 도메인)

 

딥러닝 STT 모델 - ESPNet (5 - 음성처리 도메인)

지난 시간에는, 거의 딥러닝에 대한, ESPNet의 모델 아키텍쳐와 구조 흐름이 어떻게 흘러가고, 실제로 소스로 논문과 비슷하게 작성되어있는지 확인하는 작업까지 마쳤다. 글을 읽는 것 만으로는,

shyu0522.tistory.com

기본적인 지식들, 기술에 대한 소개부터, Training, 음성에 대한 도메인을 다루었고,

모델을 사용해서 어떻게 예측할 것인가?에 대해서만 짚어보면, 더 이상 소개할 자료는 없을 걸로 판단된다.

 

이미 여기까지 진행을 해오면서 얼추 감이 있는 독자라면 충분히 생각할 수 있을 것이다.

 

'내가 이미 Training을 위해 진행한 데이터 처리 과정을, 그대로 진행한 후 Numpy 형태로 Input 하면 되지 않을까?'

매우 지극히 맞는 이야기다. 소스를 Open 하고 싶긴 하지만, 비밀 유지에 대한 부분이 있으니, 소스를 공개하기는 좀 힘들것 같다.

대신 내가 유용하게 참고했던 소스라던가, 생각해봤던 노하우, 고민(?)들을 이야기해볼까 한다.

 

Data Pre-Processing

진행하는 방법은 크게 2가지가 있겠다. (C 사용 방식, Python 사용 방식)

 

C 소스를 그대로 활용하는 방식

1. kaldi 기반의 C 소스를 수행시켜, (Shell Scripts 등으로) MFCC 파일로 떨어트린 후, 해당 파일을 numpy로 읽어서 Inference 진행

    - 장점

        1) Training에서 사용한 Toolkit을 그대로 활용하므로, 신뢰도 있다.

        2) Kaldi를 사용하여, Multi-Processing 지원이 용이하다. 즉 속도가 빠르다.

    - 단점

        1) Kaldi를 잘 이해하지 못했다면, 사용하지 못할 가능성이 크다.

        2) Kaldi -> Python의 Pipeline을 획일화 시키는 추가적인 소스코딩 작업이 필요하다.

        3) 구동환경을 세팅할 시, 서비스 서버에도 무조건 Kaldi가 설치되어 있어야 하므로, 구동환경 세팅에 번거로움이 생긴다.

    - 평가

        소스가 Shell 스크립트 구동 (mfcc 파일 저장) -> Python으로 예측 의 흐름으로, 사실 단일화되고 매끄럽지는 못하나, 사용이 간편하고, 신뢰도 높고, 속도가 가장 빠를 가능성이 높다. 때문에 Batch등 Schedule 환경에서 사용하기 용이할 것으로 판단된다.

 

Python 소스를 활용하는 방식

2. MFCC 등 음성 전처리를 위한 Python Open Package 활용 (https://github.com/jameslyons/python_speech_features)

    - 장점

        1) Open Source라 신뢰도 있지만, Kaldi랑 완전히 똑같다고 보장하기 어려울 수 있다.

            => 이 부분은 내가 검증해봤는데 똑같다, 그냥 써도 됨. (강추)

        2) 구동환경만 세팅할 경우, 서비스 서버에는 Kaldi가 설치될 필요가 없어, 구동환경 세팅이 매우 가벼워진다.

    - 단점

        1) 소스 자체가 Multi-Processing은 고려되어있지 않아, 파이썬으로 추가 구현이 필요하다

        2) Python이 그렇듯, C Base(Kaldi)보다 속도가 느리다.

3. TensorFlow에서 제공하는 음성 전처리 Package 활용 (https://www.tensorflow.org/api_docs/python/tf/signal/mfccs_from_log_mel_spectrograms)

    - 장점

        1) GPU를 사용하여 전처리 가능함

    - 단점

        1) 개발과정이 조금 복잡하고, Debugging을 하는데 수고로움이 많음

            => 이건 나만 그런건지 모르겠는데, 이상하게 tf만 들어가면, 개발하기 조금 망설여진다. 익숙하지 않아서 그럴텐데, 사실상 써보면 2번안과 다를건 없다. (그리고 tf2를 사용하면, eager mode니까 print도 걱정할건 없지 사실.)

4. Kaldi를 Python으로 소스 변환하여 사용. (https://github.com/pykaldi/pykaldi)

    - 장점

        1) Cython으로 변환하던, pybind로 변환하던, 위의 git을 활용하던 방법이 여러가지 있고, Kaldi Base를 그대로 Python Conversion만 진행하는 것으로, Python 방식 중 가장 신뢰도 높다.

    - 단점

        1) 속도가 뭐 여전히 느리다. (Cython 쓰면 좀 나을지도...?)

 

말고도 사실 Open Source라는 것이, 많은 방법들이 있을 것 같아서, 해당 방법들을 사용해서 전처리한 후, Inference 과정을 거치면된다.

 

Inference

위에서 Data-Preprocess 작업을, Kaldi Base로 진행했다면, 아마 작업이 처리된 파일들이 바이너리로 떨어지고, 해당 파일들을 read해서 처리해야 할 것이고,

Python Base로 진행했다면, 아마 메소드 호출 방식으로 진행했을 것이니, 이제 Inference 데이터로 활용하는 일만 남았을 것이다.

 

Inference는 사실 설명하기도 좀 애매하긴 하다, 본인이 어떻게 ESPNet을 Training 했느냐에 따라서, 그에 맞는 Inference 방법을 채택해야 되기 때문이다. 나는 그냥 무지성으로 돌려보고, 나온 모델 값을 활용하는데 초점을 맞췄던지라, 내 기준에서의 노하우를 몇가지 적어놓도록 하겠다.

 

모델을 어떻게 작성하냐에 따라서, 활용방법은 좀 달라질 수 있겠는데, (Layer 정보까지 전부 저장했는지, 혹은 가중치 값들만 저장했는지.) 필자가 했던 방법으로는, Layer의 구성은 저장되지 않고, 가중치와 같은 Hyper Parameter만 저장되는 방식이었다. (그냥 별 세팅을 하지 않고 돌렸고, 뭔가 Layer 정보까지 다 저장되어, 그냥 심플하게 Keras 사용하듯이 model.load 한 후 predict가 가능한지 '모르겠다.')

때문에, torch load를 해야할때, 실제로 Layer에 대한 정보는 따로 Class로 선언한 뒤, 거기에 model의 Hyper Parameter를 매칭해야 하는 방식으로 진행해야해서, 매우 골치가 아팠다. (Layer를 내가 구성하질 않았는데, 어떻게 찾아서 쓰라는거야?)

 

ESPNet 개발자가이드와 git의 Readme를 찾아봐도, 뚜렷한 내용은 찾기 어려웠고, 레퍼런스조차 존재하지 않았다.

(학습만 시켜놓고 예측을 못하는것이냐...!? 따흐흑...ㅠㅠ)

 

하지만, 학습을 시켰다는 것은 결국에 라이브러리에 Layer 정보가 남아있다는 얘기가 될 것이며,

               Decoder                     -> 가장 Best라고 생각하는 N개의 토큰 가중값

Encoder ->                                                                                             -> 합친 후 Best Token 추출

               CTC Score(BeamSearch) -> 가장 Best라고 생각하는 N개의 토큰 가중값

의 과정으로 구해나가면 된다.

 

조금 수직적으로 생각해보자.

1. Model을 Load하여, Layer에 적용한다.

    -> ESPNet Package에서 Import 하여, 해당 Layer를 Config로 Model을 Load한다.

    -> 참고, https://github.com/espnet/espnet/blob/master/espnet/nets/pytorch_backend/e2e_asr_transformer.py

 

GitHub - espnet/espnet: End-to-End Speech Processing Toolkit

End-to-End Speech Processing Toolkit. Contribute to espnet/espnet development by creating an account on GitHub.

github.com

    -> 여기에 Encoder, Decoder, CTC, Beamsearch에 대한 내용이 전부 들어있으며, Training 관련해서 ESPNet 소스를 한번 정독해봤다면, 익숙한 내용의 소스들일 것으로 판단된다. 소스를 읽어보고, Prediction을 진행하면 된다.

 

2. Encoder 값을 구한다.

    -> 이제 model은 e2e_asr_trasformer의 E2E class에 종속되니, 소스를 보고 해당 메소드를 사용하면 된다. model.encode()

 

3. Decoder 값을 구한다.

    -> model.decode() 써도 되긴된다. 다만, 이렇게 할 경우, CTC를 따로 구해서 합쳐주는 일련의 뒷작업을 직접 작성해야 하는데, 소스를 잘보면 scorers()가 존재하여, 소스에 보면, Decoder와 CTC 준비작업이 수반된다.

# espnet.nets.pytorch_backend.e2e_asr_trasformer.py 발췌

def scorers(self):
    """Scorers."""
    return dict(decoder=self.decoder, ctc=CTCPrefixScorer(self.ctc, self.eos))

 

4. 우리는 CTC에서 가장 높은 N개의 값을 찾아야 하므로, BeamSearch는 추가적으로 진행해줘야한다.

    -> https://github.com/espnet/espnet/blob/master/espnet/nets/beam_search.py

 

GitHub - espnet/espnet: End-to-End Speech Processing Toolkit

End-to-End Speech Processing Toolkit. Contribute to espnet/espnet development by creating an account on GitHub.

github.com

    -> BeamSearch Class의 선언부에 Scorers를 넣게 되어있으니, 해당 model.scorers를 집어넣고, 선언한 뒤, beam_search() 메서드를 이용해서, 예측을 진행하면 된다.

    -> 사실상 BeamSearch에 Decoder와 CTC(BeamSearch)가 동봉되어 있는 셈이므로, X Input은 Encoder Output이 되겠다.

 

5. N개중에 가장 높은 값 1개를 정렬 후, vector to text매칭작업을 해주면 된다.

    -> log_softmax일 경우, 가장 낮은값을 찾아야 할 수도 있으니, 어떤 softmax가 사용되었는지 소스를 꼭 정독하고, 사용하도록 하자.

    -> vector to text 매칭작업은, https://github.com/espnet/espnet/blob/master/espnet/asr/asr_utils.py

 

GitHub - espnet/espnet: End-to-End Speech Processing Toolkit

End-to-End Speech Processing Toolkit. Contribute to espnet/espnet development by creating an account on GitHub.

github.com

      소스가 존재하니, 여기서 parse_hypothesis를 활용하면 조금 편하다.

 

이렇게 하면 예측이 전부 끝이나게 된다.

 

 사실 espnet package에 정말 많은 기능들이 함축되어있고, CTC나 BeamSearch는 꼭 espnet꺼를 사용하지 않아도, 구글에 찾아보면 관련된 git 레퍼런스도 많아서, 정말 어떻게 사용할 것인지에 따라 무궁무진한거같다.

(이를 테면, https://github.com/HawkAaron)

 

HawkAaron - Overview

HawkAaron has 42 repositories available. Follow their code on GitHub.

github.com

 

 이렇게 절차적으로만 보면, 뭔가 예측도 잘 될 것 같고, 쉽게 진행 될 것같은데, 음성이라는 것이 순서에 따라 어떻게 text가 매칭될지 가늠하기 어려운 부분들도 있고, CTC나 BeamSearch가 크게 와닿지 않는 상태에서, 무작정 컴퓨터 공학 관점으로 소스 베이스의 접근을 하다보면, 에러는 나지 않지만, 절대로 예측이 잘 되지 않는 경우도 파다해서, 이번 프로젝트는 정말 너무 어려웠던 것 같다.

(사실 내 프로젝트도, Text가 어느정도 그럴싸하게 나오긴 하는데, 부정확하게 나오는 부분들에 대해서, 내가 뭔가 소스를 잘못 짠건지, 데이터가 부족해서 모델이 학습이 덜 된건지 현재 확실하진 않으며, 이제 차차 검증해 나가면서 조정해 봐야 한다고 생각한다.)

 

 사실 내가 지금 써놓은 Article을 보고도, 과연 몇 명이나 이걸 따라서 진행해서 실제로 예측값이 나오는걸 확인할 수 있을지도 의문이고, 나도 다른회사에 가서 새롭게 개발해야된다면, 꽤나 삽질하면서 진행하게 될 것 같다는 생각이다.

 

 도무지 ESPNet 개발자 가이드는 왜이렇게 어렵게 써놓은건지, 표준적인 개발자 가이드 하나라도 올려놔줬으면 좋았으련만, 야속하기만 하다.

 아쉬운 부분들은 이쯤에서 일단 뒤로 하기로 하고, 내가 실제로 겪으면서 느꼈던 Tip이라면 Tip, 고민이라면 고민을 정리해보자 한다.

 

Review & Worry & Tip

  1. 요즘은 사실 Client Base의 Not Server Based 모델이 선호되며, 때문에 더 가볍고, 더 실시간에 가까운 모델을 고객들이 선호하는 경향이 강하므로, 꼭 해당 아키텍쳐를 사용해야하는지 다시 한번 고민해보자.
  2. ESPNet의 기본 Package나, Kaldi, 혹은 해당 Idea로 더 신박하게 소스를 짜놓은 능력자 Git 사람들이 많으니, 최대한 기능 Base로 검색해서 필요한 부분들을 떼다 쓸 수 있도록 하자.
  3. ESPNet은 학습을 하는 것 보다, Inference를 하는 것이 더 어렵다. 모델을 어떻게 Train 할 것인지, 전략을 좀 더 공부하고, 내가 한 방법보다 쉽게 Inference를 할 수 있는 방법을 찾아보길 바란다. (있으면 공유좀....)
  4. Training에서도 나오는 부분이지만, 적절한 길이로 음성을 잘라서 처리해야 하며, 무작정 긴 음성을 처리하면, 내 생각에도 Attention에서의 행렬곱 연산 때문에, 속도 성능이 무척이나 떨어질 것으로 판단된다.
  5. 3번에서의 이유로, 음성을 학습시키는 것 보다, 음성을 어떻게 학습, 예측에 사용할지, Data 측면에서 다루는 부분이 훨씬 더 중요하고, 어렵다.
    • ESPNet은 사실상 내 생각에, STT 말고, DataSet만 구성되어 있으면, 파형 신호를 무언가에 매핑시키는 분류 Task에 모두 사용 가능하다고 본다.
    • 다만 Data의 기준을 어떻게 자를 것이냐? 어떻게 잘라야지만 학습율이 더 좋아질 것이냐?에 대한 의문이 생긴다. 왜냐하면, '아메리카노'의 '노'에서 음성이 짤려서, '아메리카' 만으로 학습이 진행되었다고 가정해보자. 그러면 아무리 '아메리카'라고 발음해도, '아메리카노'가 나올 것이다.
    • 길이로만 자르면 안된다. 그러면 어떻게 잘라야 할 것인가? 사람의 발성을 인식해서, 문장이던 단어던, 무조건 끊기는 위치를 찾아야만 하는데, 이거는 또 어떻게 찾느냐?
  6. 그렇다고 label은 편한가? label도 녹록치는 않다.
    • 단어 단위로 학습할 것인가? 단어 단위로 학습한다면, 형태소 단위로 떼어낼 것인가? 그러면 음성은 어떻게 잘라야하지?
    • 속편하게 글자로 학습해야 할까? 하지만 이 역시 매핑되는 음성의 길이를 정해야 하기는 마찬가지다.
  7. 나는 ESPNet과 Kaldi를 100% 이해하고 소스를 작성하였는가? 혹시 내가 찾아보지 못한 더 좋은 레퍼런스가 있는건 아닐까?
    • Package의 Wrapper가 너무 강하고, 옵션의 선택폭은 넓으나, 작성에 필요한 규칙성은 짙다. 이 말은 뭐냐면, 소스와 사용이 전혀 직관적이지 않으며, 그걸 Case별로 정리해놓은 Reference조차 보기 힘들고, 이 때문인지 Git은 중구난방 개발자들 마음대로, 자기네들 편한 식으로 작성하여 사용하고 있다.
    • 뭔가 반복적으로 사용되고, 정보가 공유되어, 선택과 사용에 있어서 안정감이 있어야 하는게 검증된 OpenSource의 기본이라고 생각되지만, ESPNet은 선을 넘어도 너무 넘었다.

 

 지금 와서 돌이켜보면, 너무 쉽게 생각해서 접근했던 오픈 딥러닝 아키텍쳐가 실제로 사용하려고 보니 너무 복잡하고 어려웠고, 내가 어떻게 여기까지 달려왔는지도 모르게 달려왔다.

(어떻게든 만들어진게 대단하다. WER 기준으로 약 70% 수준으로 나오긴한다. 소스를 좀 더 고도화하고, R&D하고, 데이터도 더 집어넣으면 조금 더 올라가지 않을까...?)

 

기간은 대략 3명이서, 8~9개월 정도 걸린 것 같다. 이렇게 갈아넣었는데도, 이 정도라니... 그래도 고생한 팀원들에게 경배를...!!

 

 가장 크게 얻어가는 것이라면, 이제 어떤 오픈 딥러닝 아키텍쳐를 마주하더라도, 일단 겁은 전혀 안날 것 같다는게 가장 큰 거 같고,

음성처리 자체가 무척이나 어렵다는 점.

기본적인 지식이 생겨서 눈이 뜨인다면, 데이터 전처리나, 모델 기능의 추가 등을 도모하여 고도화가 잘 될 수 있지 않을까?

 하는 점 등등이 있는 것 같긴한데, 맨 앞으로 돌아가보면 텍스트 분석 하고싶어서 접근했던 STT에서 너무 고생을 많이 하긴 했다...ㅋㅋㅋㅋㅋ

 

 그래도 정리해서, 나름 좋은 경험이 된 것 같고, 깔끔하게 일단은 BERT로 넘어가보자 한다.

(사실 오늘 기점으로 Pre-Training 까지는 리뷰 끝났고, 이제 앞으로 정리하고, Fine-Tunning 과제 정해서 나아가는 일만 남았다. 조금만 기다려주길 바란다.)

728x90

댓글