저자
소속

1 음성인식 데이터셋

OpenSLR은 음성 인식을 위한 학습용 말뭉치, 음성 인식 관련 소프트웨어 등 음성 및 언어 자원을 제공하고 있다.

1.1 오디오 표본

대규모(1000시간) 영어 음성 읽기 말뭉치에서 영어 음성 읽기 하나를 추출해서 관련 사항을 정답문과 비교해보자.

영어 음성 대본

대규모(1000시간) 영어 음성 읽기 말뭉치에 포함된 영어음성대본에서 텍스트를 추출해서 다음에 영어 음성에서 텍스트 추출을 비교한다.

코드
library(tidyverse)
# fs::dir_ls(path="data/LibriSpeech/dev-clean/1993/147149")

trans_txt <- read_lines("data/LibriSpeech/dev-clean/1993/147149/1993-147149.trans.txt") 

# trans_0011_str <- trans_txt[str_detect(trans_txt, "0011")]
# trans_str <- str_extract_all(trans_0011_str, "[a-zA-Z].+")[[1]]

trans_0011 <- trans_txt %>% 
  enframe() %>% 
  separate(value, into = c("순번", "텍스트"), sep = "\\s", extra = "merge") %>% 
  mutate(텍스트 = str_to_lower(텍스트)) %>% 
  filter(str_detect(순번, "0011")) %>% 
  pull(텍스트)

then the mother lifted up her voice and wept

영어 음성

영어 음성을 들어보자. .flac 파일을 av 패키지 av_audio_convert() 함수로 .mp3 혹은 .wav 파일 변환이 가능하다.

코드
library(av)
library(embedr)

audio_file <- "data/LibriSpeech/dev-clean/1993/147149/1993-147149-0011.flac"

av::av_audio_convert(audio_file, output = "data/whisper_before.mp3", 
                     format = "mp3", sample_rate = 16000)
#> [1] "D:\\tcs\\chatGPT\\data\\whisper_before.mp3"

whisper_before_mp3 <- av::read_audio_bin("data/whisper_before.mp3")

embedr::embed_audio("data/whisper_before.mp3")

2 whisper STT

영어 음성에서 텍스트로 전환하는 작업을 STT(Speech-to-Text)라고 부르는데 최근 성능도 많이 좋아졌고 그중 챗GPT 인기를 얻고 있는 whisper API를 사용해서 영어 음성을 텍스트로 변환할 수도 있고, C/C++로 OpenAI의 Whisper 모델 이식한 whisper.cpp 모델을 사용하면 CPU로 무료로 사용가능하다.

audio.whisper 패키지가 최근에 출시되어 이를 사용하면 수월히 R에서도 STT 작업을 수행할 수 있다.

모형 언어 크기 필요 RAM 크기
tiny & tiny.en Multilingual & English only 75 MB 390 MB
base & base.en Multilingual & English only 142 MB 500 MB
small & small.en Multilingual & English only 466 MB 1.0 GB
medium & medium.en Multilingual & English only 1.5 GB 2.6 GB
large-v1 & large Multilingual 2.9 GB 4.7 GB

whisper() 입력 오디오는 16비트 .wav 파일형식만 가능하다. 따라서 av 패키지 av_audio_convert() 함수로 원본 파일(.flac)을 .wav 파일로 변환한 후에 STT 작업을 수행한다.

.flac.wav

.flac 파일을 .wav 파일로 변환시킨다.

코드
library(audio.whisper)

av::av_audio_convert(audio_file, output = "data/whisper_before.wav", 
                     format = "wav", sample_rate = 16000)
#> [1] "D:\\tcs\\chatGPT\\data\\whisper_before.wav"

STT 결과

base.en 모델을 사용해서 STT로 영어음성에서 텍스트를 추출한다.

코드
model <- whisper("base.en")
#> whisper_init_from_file: loading model from 'D:/tcs/chatGPT/ggml-base.en.bin'
#> whisper_model_load: loading model
#> whisper_model_load: n_vocab       = 51864
#> whisper_model_load: n_audio_ctx   = 1500
#> whisper_model_load: n_audio_state = 512
#> whisper_model_load: n_audio_head  = 8
#> whisper_model_load: n_audio_layer = 6
#> whisper_model_load: n_text_ctx    = 448
#> whisper_model_load: n_text_state  = 512
#> whisper_model_load: n_text_head   = 8
#> whisper_model_load: n_text_layer  = 6
#> whisper_model_load: n_mels        = 80
#> whisper_model_load: f16           = 1
#> whisper_model_load: type          = 2
#> whisper_model_load: mem required  =  215.00 MB (+    6.00 MB per decoder)
#> whisper_model_load: kv self size  =    5.25 MB
#> whisper_model_load: kv cross size =   17.58 MB
#> whisper_model_load: adding 1607 extra tokens
#> whisper_model_load: model ctx     =  140.60 MB
#> whisper_model_load: model size    =  140.54 MB

trans <- predict(model, newdata = "data/whisper_before.wav", language = "en", n_threads = 2)
#> Processing data/whisper_before.wav (49920 samples, 3.12 sec), lang = en, translate = 0, timestamps = 0
#> 
#> [00:00:00.000 --> 00:00:03.000]   Then the mother lifted up her voice and wept.

trans$data
#>   segment         from           to
#> 1       1 00:00:00.000 00:00:03.000
#>                                             text
#> 1  Then the mother lifted up her voice and wept.

1, 00:00:00.000, 00:00:03.000, Then the mother lifted up her voice and wept.

3 챗GPT와 오정보

2023년 서울 R 미트업에서 “챗GPT와 오정보(ChatGPT and Misinformation)”를 주제로 워싱턴 대학 제빈 웨스트 교수님을 모시고 특강을 진행했다. 한정된 예산으로 직접 모시지는 못하고 줌(Zoom) 녹화로 대신했다.

약 1시간 분량의 녹화분량 중 일부 불필요한 부분은 오픈샷 비디오 편집기(OpenShot Video Editor)를 가지고 잘라낸다.

결국 유튜브 동영상으로 올려 한글 자막을 입히는 것이 최종 목적이기 때문에 .mp4 파일에서 이미지 정보 대신 오디오 정보만 .wav, .mp3 파일로 추출해서 뽑아낸다.

STT(Speech-to-Text) 영어 음성을 영어 텍스트로 전사(transcribe)해야 하는 작업이 필요하기 때문에 다양한 모델이 있지만 성능이 좋다고 인정받는 OpenAI Whisper 를 사용한다.

OpenAI Whisper API는 OpenAI의 API를 통해 사용할 수 있는 음성-텍스트 변환 모델이며 Whisper의 오픈 소스 버전은 Github에서 사용할 수 있다. 오픈 소스 버전의 Whisper와 OpenAI의 API를 통해 제공되는 버전은 차이가 없지만, OpenAI의 Whisper API를 사용할 경우, 시스템 전반에 걸친 일련의 최적화를 통해 OpenAI는 12월부터 ChatGPT의 비용을 90% 저렴하게 이용할 수 있으며,개발자는 API에서 오픈 소스 Whisper 대형-v2 모델을 사용하여 훨씬 빠르고 비용 효율적인 결과를 얻을 수 있다.

3.1 동영상에서 오디오 추출

ffmpeg 프로그램을 사용하면 오디오를 추출할 수 있다. MP4 파일에서 16비트 깊이와 16kHz 샘플링 레이트로 오디오를 추출하여야 whisper에 입력값으로 넣을 수 있는 .wav 파일이 된다. 두가지 조건(16비트 깊이와 16kHz 샘플링)이 충족되지 않을 경우 Whisper에서 처리할 수 없다는 오류가 나온다.

.mp4에서 .wav 추출

ffmpeg -i misinformation.mp4 -acodec pcm_s16le -ar 16000 -ac 2 misinformation_16.wav

.mp4에서 .mp3 추출

.mp3 확장자를 줄 경우 오디오를 mp3 파일로 추출하여 파일크기를 크게 줄일 수 있다.

ffmpeg -i misinformation.mp4 -acodec pcm_s16le -ar 16000 -ac 2 misinformation.mp3

전체 .mp3 파일이 너무 길어 ffmpeg를 사용해서 30분부터 30초만 추출하는 코드를 작성해서 들어보자.

ffmpeg -i misinformation.mp3 -ss 00:30:30 -t 00:00:30 -vn -codec:a libmp3lame -qscale:a 2 misinformation_short.mp3
코드
embedr::embed_audio("data/LibriSpeech/misinformation_short.mp3")

3.2 STT

16비트 깊이와 16kHz 샘플링 조건을 갖춘 .wav 파일이 준비되면 다음 단계로 음성-텍스트 변환 Whisper 모델을 선정하여 텍스트 전사 작업을 수행한다. 윈도우 10 에서 2.6 GB 영어 medium.en 모델은 알 수 없는 오류로 인해 base.en 모델을 사용하여 영어 음성에서 텍스트를 추출했다.

코드
library(audio.whisper)

medium_model <- whisper("base.en")

misinformation_trans <- predict(medium_model, newdata = "data/LibriSpeech/misinformation_16.wav", 
                                language = "en", n_threads = 2)

Whisper 모델을 통해 나온 음성-텍스트 변환 결과를 로컬 파일에 저장하여 점검한다.

코드
library(tidyverse)
misinformation_trans$data %>% 
  mutate(data = glue::glue("{from} ==> {to} {text}")) %>% 
  pull(data) %>% 
  write_lines("data/LibriSpeech/misinformation_oneline.txt")

3.3 SRT

몇번의 시행착오를 거쳐 순번, 시작시각, 종료시각, 텍스트로 구성된 파일을 .srt 파일 형태로 변환하여 영어 자막작업을 마무리한다.

코드
mis_srt_raw <- read_lines("data/LibriSpeech/misinformation_proof_reading.txt")

mis_srt_tbl <- mis_srt_raw %>% 
  enframe() %>% 
  separate(value, into = c("start", "end"),   sep = "\\s==>\\s", extra = "merge") %>% 
  separate(end,   into = c("end", "subtitle"), sep = "\\s", extra = "merge") %>% 
  mutate(subtitle = str_trim(subtitle))
 
mis_srt_tbl %>% 
  mutate(srt = glue::glue("{name} \n {start} --> {end} \n {subtitle}\n\n")) %>% 
  pull(srt) %>% 
  write_lines("data/LibriSpeech/misinformation_proof_reading_srt.srt")

유튜브 동영상 영문 자막으로 사용될 .srt 자막 파일을 불러읽어와서 최종 작업결과를 살펴본다.

코드
library(tidyverse)
mis_srt <- read_lines("data/LibriSpeech/misinformation.srt")

mis_srt %>% 
  head(20) 
#>  [1] "1 "                                                                                          
#>  [2] "00:00:00.000 --> 00:00:27.520 "                                                              
#>  [3] "Welcome everybody to our Seoul R Online seminar where we explore various ideas and"          
#>  [4] ""                                                                                            
#>  [5] "2 "                                                                                          
#>  [6] "00:00:27.520 --> 00:00:33.360 "                                                              
#>  [7] "practices of data science. We are really happy today to have Jevin West from the University" 
#>  [8] ""                                                                                            
#>  [9] "3 "                                                                                          
#> [10] "00:00:33.360 --> 00:00:43.040 "                                                              
#> [11] "of Washington with us to talk about ChatGPT and misinformation. I'm sorry, I'll begin again."
#> [12] ""                                                                                            
#> [13] "4 "                                                                                          
#> [14] "00:00:43.040 --> 00:00:46.480 "                                                              
#> [15] "Okay, no problem. No, I do this a lot of times."                                             
#> [16] ""                                                                                            
#> [17] "5 "                                                                                          
#> [18] "00:00:46.480 --> 00:00:53.680 "                                                              
#> [19] "Welcome everybody to our Seoul R Online seminar. We explore various ideas and"               
#> [20] ""
코드
library(magick)

mis_mp4 <- image_read_video("data/LibriSpeech/misinformation.mp4",  fps = 1)