본문 바로가기
LLM 생성할레나

[STT/Whisper] Speech To Text 빠른 시도와 작은 실패

by LENA-cfg 2024. 5. 20.

얼마전, 대표님께서 내자리에 오시더니 장황하게 다른 회사의 서비스를 우리도 진행해보고 싶다고 하셨다. 

내용을 들어보니, stt, tts, llm 모델을 사용하는 것...

비전 공부를 집중해서 한지 이제 6개월 가량 되었는데 새로운 도메인을 공부하려니 정말 많은 생각이 들었다.

그래도 후회만 할 수 없으니 빠르게 stt 먼저 테스트 해봤다.


시도해본것은 3가지로 추릴 수 있다.

 

1. 한국어로 학습한 STT 모델 테스트

2. ETRI open api 테스트

3. open ai Whisper 테스트 

 

순차적으로 시도 방법, 결과, 느낀점 순으로 블로그가 작성될 것 같다.


1. 한국어로 학습한 STT 모델 테스트

 

최근에 음성 모델은 asr이라는 사람의 음성을 텍스트로 변환하는 기술을 사용한다. 우리 주변에도 많이 서비스로 출시됬는데 음성 비서, 유튜브 자막 생성등이 있다. 과거에는 asr 기술을 사용하기 위해서 다양한 기술들이 복합적이고 순차적으로 진행됬는데 이제는 Speech To Text 딥러닝 모델을 사용하여 편하게 end-to-end로 사용할 수 있다.

 

내가 찾는 STT 모델은 한국어를 인식하고 텍스트로 변환해야하기떄문에 한국어를 학습한 오픈 모델을 허깅 페이스에서 찾았다.

 

사용한 모델: speechbrain/asr-conformer-transformerlm-ksponspeech

링크: https://huggingface.co/speechbrain/asr-conformer-transformerlm-ksponspeech

 

해당 모델은 SpeechBrain이라는 파이토치 오픈소스 툴킷을 만든 회사에서 학습시킨 모델이다. 음성 인식 이외에 다양한 모델이 제공된다. 

 

위에 내가 사용한 모델은 ASR 시스템으로 3개의 블록을 가진다.

1) Tokenizer

2) Transformer LM

3) 컨포머 인코더 + CTC, 트랜스포머로 만들어진 디코더가 있는 음성 모델

 

인풋 데이터는 16kHz로 샘플링된 녹음 데이터이다.

 

테스트는 코렙에서 진행했으며 빠르게 확인 해볼 수 있었다.

테스트 데이터: AIhub_민원(콜센터) 질의-응답 데이터

링크: https://www.aihub.or.kr/aihubdata/data/view.do?currMenu=115&topMenu=100&aihubDataSe=data&dataSetSn=98

 

테스트 모델 이미지

!pip install speechbrain
!pip install pydub

from pydub import AudioSegment
from speechbrain.inference.ASR import EncoderDecoderASR


# 데이터 포맷 변경 mwa > wav
m4a_file = '/content/쇼핑_642.m4a'
wav_file = '/content/쇼핑_642.wav'

track = AudioSegment.from_file(m4a_file, format="m4a")
file_handle = track.export(wav_file, format="wav")

# 인풋 데이터 크기 변경 1분 > 5초
audio = AudioSegment.from_wav('/content/쇼핑_642.wav')

duration_in_milliseconds = 5000  # 5초
if len(audio) > duration_in_milliseconds:
    audio = audio[:duration_in_milliseconds]

output_file = "/content/trimmed_audio.wav"
audio.export(output_file, format="wav")

# asr 모델 테스트
asr_model = EncoderDecoderASR.from_hparams(source="speechbrain/asr-conformer-transformerlm-ksponspeech", savedir="pretrained_models/asr-conformer-transformerlm-ksponspeech",  run_opts={"device":"cuda"})
asr_model.transcribe_file('/content/trimmed_audio.wav')

 

우선 1분 이상의 음성 데이터를 사용할 경우 쿠다 메모리 에러 발생한다. 그러면 인풋 데이터 사이즈를 줄여야하는데 나는 5초로 줄여서 테스트를 해봤다.

 

깊은 모델 구조 대비 결과는 좋지 않았다. 허깅페이스 한글 데모는 괜찮았던 거 같은데 실제 사용하기에는 너무 무겁고 성능이 떨어진다. 추가 학습을 한다고 해도 로컬로 모델을 내려 받아서 하기가 부담스러웠다.


2. ETRI open api 테스트

 

두번째 시도는 무료 api를 사용하는 것이다. ETRI에서 무료 음성인식 api를 제공해서 사용해봤다.

링크: https://aiopen.etri.re.kr/guide/Recognition

다양한 언어를 제공하였고 rest api로 음성 인식 결과를 쉽고빠르게 사용할 수 있었다.

하지만, 하루에 총 5시간 사용할 수 있고 음성 데이터를 20초를 최대로 사용할 수 있었다.

 

테스트 시에는 20초씩 분할하여 저장한 후 사용하였다.

import os
import json
import time
import urllib3
import base64
from pydub import AudioSegment
from dotenv import load_dotenv

def split_wav_file(file_path, segment_length_ms):
    # 웨이브 파일 분할
    audio = AudioSegment.from_wav(file_path)
    for i in range(0, len(audio), segment_length_ms):
        segment = audio[i:i + segment_length_ms]
        segment.export(f"./wav_folder/segment_{i // segment_length_ms + 1}.wav", format="wav")


def m4a2wav(m4a_file):
    # M4A 파일을 WAV 파일로 변환
    track = AudioSegment.from_file(m4a_file, format="m4a")
    new_file_name = m4a_file.split('/')[-1].split('.')[0]+'.wav'
    file_handle = track.export('./'+new_file_name, format="wav")
    return split_wav_file(file_handle, 15000)


def process_audio_files(directory, open_api_url, language_code, access_key):
    # HTTP 클라이언트 초기화
    http = urllib3.PoolManager()
    headers = {
        "Content-Type": "application/json; charset=UTF-8",
        "Authorization": access_key
    }
    # 결과를 저장할 텍스트 파일 생성
    with open("결과경로/result_text/etri_api_results.txt", "w", encoding="utf-8") as result_file:
        # 지정된 디렉토리 내 모든 파일을 순회
        for filename in os.listdir(directory):
            if filename.endswith(".wav"):
                # 오디오 파일을 읽고 base64로 인코딩
                with open(os.path.join(directory, filename), "rb") as file:
                    audio_contents = base64.b64encode(file.read()).decode("utf8")

                # 요청 JSON 구성
                request_json = {
                    "argument": {
                        "language_code": language_code,
                        "audio": audio_contents
                    }
                }

                # 서버에 POST 요청
                response = http.request(
                    "POST",
                    open_api_url,
                    headers=headers,
                    body=json.dumps(request_json)
                )

                # 응답 처리
                if response.status == 200:
                    data = str(response.data, "utf-8")
                    print(data)
                    data = json.loads(data)
                    result_file.write(data['return_object']['recognized'] + "\n")  # JSON 응답의 형식에 따라 수정 필요
                else:
                    print(f"Error processing {filename}: {response.status}")


if __name__ == "__main__":
    start_time = time.time()
    m4a_file = '/원천데이터/쇼핑/업무처리/업무처리/쇼핑_642.m4a'
    m4a2wav(m4a_file)

    load_dotenv(verbose=True)
    accessKey = os.getenv('accessKey')
    directory = '/wav_folder/'
    open_api_url = "http://aiopen.etri.re.kr:8000/WiseASR/Recognition"
    language_code = "korean"
    process_audio_files(directory, open_api_url, language_code, accessKey)
    print(time.time()-start_time)

테스트 결과는 문장이 잘 변환되지 않았고 문장이 자연스럽게 이어지지 않았다. 

데모용으로도 사용할 수 없는 수준이었다. 1분 음성이 41초 가량 소요되었다. 

 


3. open ai Whisper 테스트 

마지막으로는 Whisper모델을 다운받아 사용하였다.

open ai의 오픈 소스 모델이었다. 

사이즈는 tiny, base, small, medium, large가 있었고 내 로컬 컴퓨터가 8기가 메모리를 가지고 있어 large는 사용할 수 없었다. 

당연한 얘기지만 모델 사이즈가 커질수록 정확도가 높아지나 요구되는 컴퓨터 사양도 비례하기 때문에 medium으로 테스트했다.

 

깃헙:  https://github.com/openai/whisper

import whisper
import torch
import time
from pydub import AudioSegment

start = time.time()

# 디바이스 설정
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

def m4a2wav(m4a_file):
    track = AudioSegment.from_file(m4a_file, format="m4a")
    new_file_name = m4a_file.split('/')[-1].split('.')[0]+'.wav'
    file_handle = track.export('./'+new_file_name, format="wav")
    return split_wav_file(file_handle, 15000)

if __name__ == "__main__":
    m4a_file = '/test/쇼핑_642.m4a'
    m4a2wav(m4a_file)

    # 모델 불러오기 tiny, base, small, medium, large, large는 8기가 메모리에서 사용 불가
    model = whisper.load_model("medium")

    model = model.to(device)

    audio = whisper.load_audio('test/쇼핑_642.wav', sr=16000)
    result = model.transcribe(audio, fp16=False, language='ko')

    with open("/test/result_text/whisper_results.txt", "w", encoding="utf-8") as result_file:
        result_file.write(result['text'])

    print(result['text'])

    tt = time.time() - start
    print("총 걸린 시간: ", tt)

 

테스트 결과는 의미가 있었다. 마침표부터 문장의 자연스러움까지 여기서 모델을 더 파인튜닝하면 될 것 같다.

근데 말하는 속도가 빨라지면 모델이 잘 인식을 못하는 경향이 있다. 그리고 실시간 까지는 아니더라도 모델의 추론 시간을 줄일 필요도 있었다.