AI 탐구노트

Whisper를 이용한 실시간 음성인식 본문

DIY 테스트

Whisper를 이용한 실시간 음성인식

42morrow 2024. 10. 18. 09:22

 

배경

 

최근 아이폰을 사용하다보니 시리가 예전 대비해서는 많이 좋아졌다는 느낌을 받게 되었습니다. 제대로 말귀를 못 알아듣던 녀석이었는데 오호라... 하지만, 아직도 구글 어시스턴트나 ChatGPT와 같은 다른 서비스에 비하면 영 멀었다는 생각이 드는 건 어쩔 수 없습니다. 

 

최근 나오는 ASR (Auto Speech Recognition) 혹은 STT (Speech to Text) 기능을 이용하면 실시간 번역 혹은 통역이 가능할 것 같고, 최신 휴대전화에는 온디바이스 AI 기능으로 이런 것이 소개되기도 합니다. 장비 자체의 연산 자원의 제약으로 아주 훌륭한 통번역은 좀 더 지나야 나올 수 있을 것 같긴 하지만, 그래도 간단한 문장들은 쉽게 처리해 주고 있어 언치(언어치 -_-;)인 저한테는 큰 기대가 되고 있습니다. 그래서, 이번에는 마이크로 입력된 음성 신호를 분석해서 실시간을 텍스트로 변환하고 이를 다른 언어로 변환하는 기능을 구현해서 이런 기대가 곧 실현될 것이라는 믿음을 더 가져보려고 합니다. ^^; 

 

 

자료 조사

ASR, STT에 대해 먼저 조사해 봅니다. 

 

STT (Speech To Text)란?

음성을 텍스트로 변환하는 기술로, 사용자가 마이크나 녹음된 음성을 입력하면 파형 등을 분석해서 대응되는 텍스트로 변환해 줍니다. 애플의 시리, 아마존의 알렉사나 카카오 스피커 같은 것이나 최근 SKT 에이닷 서비스에서 전화 통화 내용을 텍스트로 바꿔주기도 하죠. 다 STT 기술을 적용한 것이라고 보시면 됩니다. 

 

STT 지원 모델에는 어떤 것이 있나?

Google의 Speech-To-Text, Mozilla의 DeepSpeech, 메타의 SeamlessM4T(STT, TTT, TTS 등 다 포함), 오픈소스 Kaldi 등도 있습니다. 하지만, 현재 시점에 가장 유명한 것은 OpenAI의 Whisper가 아닌가 싶습니다. OpenAI의 인지도 때문인 것 같기도 합니다.

 

OpenAI의 Whisper는 다음과 같은 특징을 가지고 있습니다. 

  • 다국어 지원  (한국어도 잘 됨)
  • 소음이 많은 상황이나 저품질의 음성, 불규칙한 발음 처리에 강점
  • 자막 생성 및 번역 기능도 함께 제공
  • 오픈 소스로 공개, MIT 라이선스

 

그림 : Whisper의 접근 방식

 

실시간을 지원하나?

Whisper 모델 자체는 처음부터 실시간 처리를 목적으로 만들어지진 않았습니다. 다만, 그 이후 처리 속도를 획기적으로 높인 Fast Whisper, Faster Whisper, Insanely Fast Whisper 등이 나오면서 거의 실시간 처리를 할 수 있지 않을까 하는 생각을 갖게 만들었습니다. 게다가 로컬 장비가 아닌 클라이언트/서버 개념으로 음성을 스트림으로 전송하고 AI로 처리한 결과를 되돌려주는  Whisper Streaming이란 기술도 나왔고 실시간 처리를 할 수 있게끔 하는 Whisper Live도 소개되었습니다. 

 

 

Whisper 변종들

 

이 가운데 이번에는 Whisper Live를 테스트 해 볼 생각입니다. 

 

환경 구성

Whisper Live 데모를 진행하기 위해서는 whisper-live 외에도 PyAudio, ffmpeg이 필요합니다. 

# github repository 복제
$ git clone https://github.com/collabora/WhisperLive
$ cd WhiserLive

# PyAudio, ffmpeg 설치
$ bash scripts/setup.sh

# 기타 필요 패키지 설치
$ bash setup.sh

 

GPU를 보유하고 있고 TensorRT 환경 구성이 되어 있다면 TensorRT로 실행 최적화된 백앤드를 이용할 수도 있습니다. 설명 페이지로 들어가 보면 TensorRT 환경은 nvidia에서 공식 지원하는 TensorRT docker를 기반으로 만들어진 docker container를 이용하고 있습니다. 그러므로 실행을 위해서는 일반 docker가 아닌 nvidia-docker 설치가 선행되어야 합니다. 이 부분은 아래 글을 참고하시면 됩니다. 기본 docker만 설치하면 Nvidia가 GPU 기반으로 지원하는 nvidia-docker와 다르기 때문에 안 됩니다. 

 

 

Ubuntu 22.04 (nvidia) Docker 설치하기

본 페이지에서는 Ubuntu 22.04에서 Docker를 설치하고, nvidia-docker를 설치까지 다루겠습니다.들어가기 전 본 페이지에서는 Docker Desktop이 아닌, Docker Engine을 설치합니다.Get Started | DockerInstall

velog.io

 

위의 과정이 완료되고나서 nvidia-docker 상에서 WhisperLive TensorRT를 실행합니다. 

docker run -p 9090:9090 --runtime=nvidia --gpus all --entrypoint /bin/bash -it ghcr.io/collabora/whisperlive-tensorrt:latest

 

참고로 TensorRT docker container 자체가 용량이 좀 됩니다. 게다가 docker hub 다운로드 속도도 생각보다 많이 안 나오네요...  아래 그림에서 시간만 흘러갑니다. 역시 인터넷은 기가 수준의 고속인터넷이 되어야 하는건데... 여튼 덕분에 커피 한잔 마실 여유가 생깁니다. -_-; 

 

아래 커맨드는 docker container 내부에서 진행됩니다. 개발자는 small.en(영어전용)과 smal (다국어)로 TensorRT 엔진을 빌드했다고 합니다. 그래서 저는 해당 container내에 engine 파일 (일반적으로 .bin 파일)이 이미 존재할 것으로 생각했는데, 그게 아니네요. 아래 커맨드를 실행하면 small.pt 파일을 다운받고 이걸 TensorRT를 이용해 엔진을 생성하는 과정을 거치도록 하고 있습니다. TensorRT의 최적화 및 엔진생성은 동작시키는 GPU 환경에 맞춰 되므로 미리 engine 파일 형태로 일괄 배포할 수 없다는 것을 잠시 잊고 있었습니다.  

# convert small multilingual model
bash build_whisper_tensorrt.sh /app/TensorRT-LLM-examples small

 

실행 결과 engine 파일이 정상적으로 생성됩니다. 

[10/16/2024-20:49:31] [TRT] [I] Total Activation Memory: 459497984 bytes
[10/16/2024-20:49:31] [TRT] [I] Total Weights Memory: 386863104 bytes
[10/16/2024-20:49:31] [TRT] [I] Compiler backend is used during engine execution.
[10/16/2024-20:49:31] [TRT] [I] Engine generation completed in 9.69378 seconds.
[10/16/2024-20:49:31] [TRT] [I] [MemUsageStats] Peak memory usage of TRT CPU/GPU memory allocators: CPU 153 MiB, GPU 1126 MiB
[10/16/2024-20:49:31] [TRT] [I] [MemUsageStats] Peak memory usage during Engine building and serialization: CPU: 5823 MiB
[10/16/2024-20:49:31] [TRT-LLM] [I] Total time of building Unnamed Network 0: 00:00:09
[10/16/2024-20:49:31] [TRT-LLM] [I] Config saved to whisper_small/decoder_config.json.
[10/16/2024-20:49:31] [TRT-LLM] [I] Serializing engine to whisper_small/whisper_decoder_float16_tp1_rank0.engine...
[10/16/2024-20:49:31] [TRT-LLM] [I] Engine serialized. Total time: 00:00:00
Whisper small TensorRT engine built.
=========================================
Model is located at: /app/TensorRT-LLM-examples/whisper/whisper_small


root@c4ddbd10e1c7:/app# ls -al /app/TensorRT-LLM-examples/whisper/whisper_small
total 555272
drwxr-xr-x 2 root root      4096 Oct 16 22:26 .
drwxr-xr-x 1 root root      4096 Oct 16 22:25 ..
-rw-r--r-- 1 root root      1704 Oct 16 22:26 decoder_config.json
-rw-r--r-- 1 root root      1460 Oct 16 22:25 encoder_config.json
-rw-r--r-- 1 root root 390305364 Oct 16 22:26 whisper_decoder_float16_tp1_rank0.engine
-rw-r--r-- 1 root root 178270308 Oct 16 22:25 whisper_encoder_float16_tp1_rank0.engine

 

 

참고로 기본은 small 모델이지만, build_whisper_tensorrt.sh에서 모델 크기를 변경한 다음 docker 이미지를 빌드하면 됩니다. 

 

아쉬운 것은 이미 TensorRT 환경을 구성하고 있는 사람들한테는 바로 쓸 수 있도록 하는 가이드가 추가되었으면 한다는 겁니다. docker가 여러 환경에서 독립적으로 동작할 수 있도록 하는 깔끔한 형태긴 하지만, 네트워크 환경이 안 좋다거나 하는 여러가지 불리한 이유를 가진 사람들한테는 추가적인 번거로운 작업이 필요하기 때문이죠. 흠... 커피를 한잔 제대로 하고 왔는데도 아직 800MB를 못 넘어서고 있습니다. 

 

 

테스트

 

서버 구동

 

docker 환경이 구성되면 docker container 내부에서 Whisper  Live 서버를 구동합니다.

# Run Multilingual model
python3 run_server.py --port 9090 \
                      --backend tensorrt \
                      --trt_model_path "/app/TensorRT-LLM-examples/whisper/whisper_small" \
                      --trt_multilingual

 

클라이언트 코드 실행

아래 코드는 함께 포함되어 있는 JFK 연설음성을 이용해서 데모처럼 테스트 해봅니다. 

 

from whisper_live.client import TranscriptionClient

client = TranscriptionClient('localhost', 9090, model='small', lang="en")
client('assets/jfk.flac')

 

 

결과 확인

공개된 영상처럼 화면을 둘로 갈라봤습니다. 왼쪽은 TensorRT docker container 내부에서 서버가 구동된 부분이며, 오른쪽은 client로 jfk.flac 파일을 서버로 보내서 결과를 받아와 출력하는 장면입니다.

 

(주의) 언어를 en으로 설정하지 않으면 출력 결과 텍스트가 깨집니다. 

 

연설이 진행되는 동안 감지된 텍스트의 내용은 이후에 나오는 텍스트 내용을 반영해서 계속 업데이트됩니다. 아래는 최종으로 나온 결과물입니다. 

 

 

후기

Whisper Live의 경우, 클라이언트 / 서버 구조이며 둘 간은 websocket을 이용해 소통합니다. 즉, 서버에서 처리하고 추론 결과를 client로 전송해주는 방식입니다. 복수 처리를 위해 비동기 방식도 적용하고 있고, 추론 속도를 고려해 TensorRT 최적화도 적용되어 있죠. 이런 구조에서는 서버 구성만 제대로 해 두면 복수의 클라이언트들이 접속해서 원하는 처리를 할 수 있고 클라이언트 코드도 엄청 단순/간단해질 수 있습니다. 추론 속도는 small 모델인데다 TensorRT 최적화가 적용되어 그런지 상당히 빠릅니다. 예전에 Local에서 Whisper Streaming을 테스트해 봤을 때와 비슷한 정도로 말이죠. 

 

아직 한국어에 대해서는 테스트를 해 보진 못했지만 large 모델 정도를 이용하면 잘 나오지 않을까 생각되는데 이 부분은 다음 번에 테스트를 해 보고 추가적으로 공유 드리도록 하겠습니다. 

 

 

 

시행착오-1. libstdcxx 패키지 관련 오류 발생

 

client 코드를 실행시키면 conda 가상환경 상의 libstdc++.so.6 파일이 GLIBCXX_3.4.32를 지원하지 않는다는 오류가 나옵니다.

[Running] python -u "/hdd/git/WhisperLive/client01.py"
Could not import the PyAudio C module 'pyaudio._portaudio'.
Traceback (most recent call last):
  File "/hdd/git/WhisperLive/client01.py", line 1, in <module>
    from whisper_live.client import TranscriptionClient
  File "/hdd/git/WhisperLive/whisper_live/client.py", line 7, in <module>
    import pyaudio
  File "/home/sol/anaconda3/envs/whisper/lib/python3.10/site-packages/pyaudio/__init__.py", line 111, in <module>
    import pyaudio._portaudio as pa
ImportError: /home/sol/anaconda3/envs/whisper/lib/python3.10/site-packages/numpy/_core/../../../../libstdc++.so.6: version `GLIBCXX_3.4.32' not found (required by /lib/x86_64-linux-gnu/libjack.so.0)

 

 

그것에 대한 방안으로 아래와 같이 conda 패키지를 설치, 업그레이드하면 14.2.0 버전이 설치된다고 나오는데 설치를 해서 해당 버전이 지원하는 것을 확인해보면 그 버전에서는 GLIBCXX_3.4.29까지만 지원되는 것 같습니다. 

$ conda install -c conda-forge libstdcxx-ng
...
  libgcc             conda-forge/linux-64::libgcc-14.2.0-h77fa898_1 
  libstdcxx          conda-forge/linux-64::libstdcxx-14.2.0-hc0a3c3a_1 
..


$ strings /home/sol/anaconda3/envs/whisper/lib/python3.10/site-packages/numpy/_core/../../../../libstdc++.so | grep GLIBCXX | more
...
GLIBCXX_3.4.28
GLIBCXX_3.4.29
GLIBCXX_DEBUG_MESSAGE_LENGTH
...

 

다행인 것은 현재 시스템의 libstdcxx 파일은 3.4.33까지 지원하기에  symbolic 링크를 이용해 이를 이용하도록 걸어 줬습니다. 

$ sudo ln -sf /usr/lib/x86_64-linux-gnu/libstdc++.so.6 /home/sol/anaconda3/envs/whisper/lib/libstdc++.so.6

 

 

 

 

 

 

참고) 데모 영상, 코드(깃헙)