일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- ChatGPT
- OpenAI
- 이미지 생성
- 자연어 처리
- 멀티모달
- XAI
- 메타
- ai 챗봇
- tts
- 휴머노이드 로봇
- 일론 머스크
- 트랜스포머
- 실시간 렌더링
- 강화 학습
- 강화학습
- 티스토리챌린지
- LLM
- 우분투
- 다국어 지원
- 확산 모델
- PYTHON
- AI 기술
- 오픈소스
- 인공지능
- Stable Diffusion
- 딥러닝
- 오픈AI
- AI
- 오블완
- 생성형AI
- Today
- Total
AI 탐구노트
WhisperX를 이용한 간이 회의 녹취 프로그램 만들어보기 본문
전화통화 내용이나 회의 중 녹음한 내용을 다시 텍스트로 전환하는 것을 해 보겠습니다. 복수의 사람들이 말하는 것을 각각 구분할 수 있어야 하므로 '화자 분리'라는 기술이 사용됩니다.
대략적으로 구현하려는 기능은 다음과 같습니다.
1️⃣ 목표 기능
1.입력
- 오디오 파일 (mp3 또는 wav 파일)
2.처리 과정
- 화자 분리 (diarization) : 허깅페이스에 공개된 pyannote/speaker-diarization-3.1 를 이용
- 음성 텍스트 변환 (STT) : WhisperX 이용
3.추후 확장 (언제가 될까? -_-;)
- 화자 별 오디오 시각화
- 채팅창 형식의 Gradio 앱에 내용 표시
- 다국어 지원 (언어 자동 감지)
- 자동 번역 (EasyNMT, m2m100, opus-mt 또는 경량 LLM 이용)
2️⃣ 개발 환경 구성
화자 분리를 위한 필요 라이브러리 설치는 다음과 같이 진행합니다.
# 기본 환경 설정 (conda 추천)
$ conda create -n audio_chat python=3.10
$ conda activate audio_chat
# 필요 라이브러리 설치
$ pip install pyannote.audio
$ pip install whisperx
$ pip install torchaudio
$ pip install librosa
$ pip install pydub
$ pip install matplotlib seaborn pandas
$ pip install gradio # GUI용 (선택적)
허깅페이스 로그인 진행 (위 언급한 모델을 사용하기 위해서 API Key 도 필요)
$ huggingface-cli login
3️⃣ 기본적인 워크플로우
- 오디오 로딩 및 준비
- 화자 분리 진행
- STT 처리 (WhisperX 이용)
- 결과물 병합 (화자분리 + STT 텍스트)
화자 별 오디오 신호 시각화 (matplot 이용한 파형 표시)채팅창 GUI 구현 (Gradio 이용)
4️⃣ 실제 음성으로 진행한 테스트
이런 과정을 거쳐 나온 것은 다음과 같습니다.
우선 테스트는 한국어 영상과 영어 영상 2가지로 해 봤습니다.
1) 한국어 영상 테스트
한국어 영상의 경우, 유튜브에 공개된 방송 영상 중에 음원만 mp3로 일부(약 36초 정도) 가져와서 테스트를 진행해 봅니다. 총 4명이 예능 프로에서 두서없이 대화를 하는 방식으로 되어 있어 겹치는 구간이 다수 있으며 어떤 경우는 방송에서 음소거를 하는 경우도 있습니다.
한국어 영상의 경우, 대략 화자 구분과 각자의 대사는 다음과 같습니다.
- Speaker_00 : 홍진경
- Speaker_01 : 지석진
- Speaker_02 : 유재석
- Speaker_03 : 조세호
2) 영어 영상
영어 영상의 경우는 일론 머스크와 기자 간의 인터뷰 내용입니다. 총 2명이 등장하고 질문과 답변 형태로 대화를 이어나가는 방식으로 진행됐습니다. 겹치는 구간이 별로 없다는 얘기가 되겠죠.
이 영상의 경우, 대략 화자 분리와 대사는 다음과 같습니다. 아무래도 겹치는 부분이 별로 없다보니 그런지 깔끔합니다.
- Speaker_01 : Chris Anderson (TED)
- Speaker_02 : Elon Musk
5️⃣ 동시 발화 (Overlapping Speech) 문제
실제 방송 등에서는 여러 사람이 동시에 말하는 경우(overlap)이 자주 발생합니다. 화자 분리를 위해 사용한 pyannote.audio의 경우, 중첩(동시) 발화를 감지할 수 있으나 Whisper는 오디오의 일정 구간에 대해 하나의 텍스트만 출력할 수 있습니다. 그래서 위의 한국어 영상의 음성 분석과 같은 현상이 발생합니다.
해결할 수 있는 방안으로는 다음과 같은 것이 있습니다.
- WhisperX의 diarization 모드 이용 : WhisperX 사용 시 diarization_speaker_turns 옵션을 활용해 정확히 speaker turn 기반으로 구간을 나눕니다.
- 동시 발화 필터링 또는 정리 : 동일 구간에 여러 화자에게 같은 문장이 반복될 경우, 첫번째 화자만 남기고 나머지는 제겋하도록 합니다. 단 이 경우는 후처리로 진행됩니다.
사용한 코드는 다음과 같습니다.
import whisperx
import torch
import pandas as pd
device = "cuda" if torch.cuda.is_available() else "cpu"
audio_file = "test_ko.mp3"
# 1. Whisper로 ASR
model = whisperx.load_model("large-v2", device)
asr_result = model.transcribe(audio_file)
# 2. WhisperX로 alignment 모델 불러오기 (한국어 지원)
align_model, metadata = whisperx.load_align_model(language_code="ko", device=device)
aligned_result = whisperx.align(asr_result["segments"], align_model, metadata, audio_file, device)
# 3. WhispherX diarization 실행 (Annotation 객체 반환)
diarize_pipeline = whisperx.diarize.DiarizationPipeline(use_auth_token="hf_key", device=device)
annotation = diarize_pipeline(audio_file)
diarize_df = annotation # 이미 DataFrame이므로 변환 불필요
# 4. 단어별로 화자 정보 매핑 (인자 순서 주의!)
final_result = whisperx.assign_word_speakers(diarize_df, aligned_result)
# 5. 중복 제거 함수 (동일 구간 내 다수 화자 발화 시 처리)
def remove_duplicate_speaker_segments(segments):
seen = set()
filtered = []
for s in segments:
key = (round(s['start'], 2), round(s['end'], 2), s['text'].strip())
if key not in seen:
filtered.append(s)
seen.add(key)
return filtered
filtered_segments = remove_duplicate_speaker_segments(final_result["segments"])
# 6. 결과 출력
for segment in filtered_segments:
speaker = segment.get("speaker", "UNKNOWN")
start = float(segment["start"])
end = float(segment["end"])
print(f"[{start:3.1f} ~ {end:3.1f}s] {speaker}: {segment['text']}")
적용한 결과는 다음과 같습니다.
[0.0 ~ 6.8s] SPEAKER_01: 여기 뒤에 가짜의 삶.
[6.8 ~ 18.1s] SPEAKER_01: 일단 우리 가짜의 삶은 우리 조세호 씨, 우리 홍진경 씨, 우리 석삼이 형까지 해서 저에게 큰 웃음을 주시고 제가 항상 또 많이 또 좋아.
[18.2 ~ 22.6s] SPEAKER_02: 내가 뭐 두 분 기자회견 같은데.
[22.8 ~ 23.4s] SPEAKER_02: 우리 마주친 표정은.
[25.3 ~ 30.4s] SPEAKER_02: 존경하는 국민 여러분 안녕하십니까.
[31.9 ~ 33.4s] SPEAKER_02: 네 여러분들은 오해십니다.
[33.4 ~ 35.2s] SPEAKER_02: 오늘 가짜의 삶을 맞아요.
[35.2 ~ 37.1s] SPEAKER_02: 저희가 다 밝혀드리겠습니다.
[37.5 ~ 39.0s] SPEAKER_02: 모든 걸 밝히도록 하겠습니다.
[39.0 ~ 40.0s] SPEAKER_02: 오늘 왜 옷을 또 거부의 옷을 입고 왔죠.
대략 비슷하게 나온 것 같습니다. 하지만, 중복 제거를 실행할 경우, 같은 구간 내에 다수의 사람이 말하는 것 가운데 놓치는 것이 많이 생길 수 있다는 문제가 발생할 수 있습니다. 용도에 따라 해당 부분들을 선택적으로 적용되어야 할 것 같습니다.
그리고, 내용에서 Speaker_02가 너무 많이 나오는 부분은 발화자의 발화 내용이 너무 짧기도 했고 구간이 겹치는데 먼저 발화를 한 사람 기준으로 매핑이 되었기 때문으로 보입니다. 흠... 이것도 문제네요... 좀 더 섬세한 분리가 필요할 것 같은데... 이 부분은 난이도가 좀 있어 보이므로 향후 숙제로 넘겨야 할 듯 싶습니다.
이번 글에서는 WhisperX를 이용해 오디오 파일에서 화자분리와 STT를 함께 적용해 보는 것을 해 봤습니다.
인터뷰처럼 화자가 확실히 분리된 경우는 잘 되었지만 그렇지 않은 경우는 어려움이 있는 것 같습니다. 특히나 순서 가리지 않고 동일 구간에 아무말이나 막 하는 예능 프로그램의 경우는 더 그런 것 같습니다.
간단한 회의록이 목적이었기 때문에 일반적인 회사 내 회의 속성에는 충분히 활용할 수 있을 것 같긴 합니다. 매번 말싸움만 하는 곳이 아니라면 말이죠. ^^;
'DIY 테스트' 카테고리의 다른 글
[우분투] PDF 파일 용량 압축하기 - GhostScrpt 이용 (0) | 2025.06.09 |
---|---|
[Python] 영상 다 보지 말고, 핵심만 뽑아보자! -Youtube 영상에서 스크립트 추출하기 (1) | 2025.06.05 |
자신만의 폰트 제작 - 2) MX-Font를 이용해 자신만의 손글씨 만들기 (0) | 2025.05.13 |
영상 기반 강물의 표면 유속 측정 (0) | 2025.05.04 |
뉴스 타이틀 추출 및 워드 클라우드 생성기 만들기 (0) | 2025.04.28 |