🔥알림🔥
① 테디노트 유튜브 - 구경하러 가기!
② LangChain 한국어 튜토리얼 바로가기 👀
③ 랭체인 노트 무료 전자책(wikidocs) 바로가기 🙌
④ RAG 비법노트 LangChain 강의오픈 바로가기 🙌
⑤ 서울대 PyTorch 딥러닝 강의 바로가기 🙌

4 분 소요

이번 튜토리얼은 OpenAI 의 Whisper API 를 사용하여 음성을 텍스트로 변환하는 STT, 그리고 텍스트를 음성으로 변환하는 방법에 대해 알아보겠습니다.

튜토리얼 진행시 참고사항

# 토큰 정보로드를 위한 라이브러리
# 설치: pip install python-dotenv
from dotenv import load_dotenv

# 토큰 정보로드
load_dotenv()
True

📍 Client 생성

  • client 는 OpenAI 모듈로 생성된 인스턴스 입니다.

[주의] 아래의 코드에서 오류가 난다면 API 키의 오류일 가능성이 높습니다.

from openai import OpenAI

client = OpenAI()

① Text To Speech(TTS)

  • TTS는 컴퓨터 프로그램이나 기기가 텍스트를 인간의 음성처럼 들리는 오디오로 변환하는 과정입니다.

  • 이 기술은 음성 합성을 통해 텍스트 데이터를 자연스러운 음성으로 바꿉니다.

  • 사용 예시: 오디오북, 음성 안내 시스템, 음성 기반 가상 어시스턴트 등.

[참고]

  • 공식문서: https://platform.openai.com/docs/guides/text-to-speech

주요 파라미터

  • model: 사용 가능한 TTS 모델 중 하나를 지정합니다. tts-1 또는 tts-1-hd.

    • 최신 지원모델 확인: https://platform.openai.com/docs/models/tts
  • input: 오디오를 생성할 텍스트입니다. 최대 길이는 4096자입니다.

  • voice: 오디오를 생성할 때 사용할 음성입니다. 지원되는 음성은 alloy, echo, fable, onyx, nova, and shimmer 입니다. 음성의 미리듣기는 여기 에서 확인할 수 있습니다.

  • response_format: 오디오를 입력할 형식입니다. 지원되는 형식은 mp3, opus, aacflac 입니다.

  • speed: 생성된 오디오의 속도입니다. 0.25 에서 4.0 사이의 값을 선택합니다. 기본값은 1.0 입니다.

speech_file_path = "tts_audio.mp3"

response = client.audio.speech.create(
    model="tts-1",
    input="아~ 오늘 파이썬 배우기 정말 좋은 날이네~",
    voice="alloy",
    response_format="mp3",
    speed=1.1,
)

response.stream_to_file(speech_file_path)

저장한 오디오 파일을 재생합니다.

from IPython.display import Audio

Audio(speech_file_path)

② Speech To Text(STT)

  • STT는 사람의 말소리를 텍스트로 변환하는 기술입니다.

  • 이는 음성 인식을 통해 구어체 언어를 캡처하고 이를 기록 가능한 형태의 텍스트로 변환합니다.

  • 사용예시: 음성 명령 입력, 자동 회의록 작성, 음성 기반 검색 시스템 등.

[참고]

  • 공식문서: https://platform.openai.com/docs/guides/speech-to-text

  • 파일 업로드는 현재 25MB로 제한되어 있으며, 지원되는 입력 파일 형식은 mp3, MP4, MPEG, MPGA, M4A, WAV, WEBM 입니다.

  • 지원언어

    • 아프리칸스어, 아랍어, 아르메니아어, 아제르바이잔어, 벨라루스어, 보스니아어, 불가리아어, 카탈로니아어, 중국어, 크로아티아어, 체코어, 덴마크어, 네덜란드어, 영어, 에스토니아어, 핀란드어, 프랑스어, 갈리시아어, 독일어, 그리스어, 히브리어, 힌디어, 헝가리어, 아이슬란드어, 인도네시아어, 이탈리아어, 일본어, 인도네시아어, 칸나다어, 카자흐어, 한국어, 라트비아어, 리투아니아어, 마케도니아어, 말레이어, 마라티어, 마오리어, 네팔어, 노르웨이어, 페르시아어, 폴란드어, 포르투갈어, 루마니아어, 러시아어, 세르비아어, 슬로바키아어, 슬로베니아어, 스페인어, 스와힐리어, 스웨덴어, 타갈로그어, 타밀어, 태국어, 터키어, 우크라이나어, 우르두어, 베트남어 및 웨일스어.

주요 파라미터

  • file: 변환할 오디오 파일 개체(파일 이름이 아님)로, 다음 형식 중 하나입니다: FLAC, MP3, MP4, MPEG, MPGA, M4A, OGG, WAV 또는 WEBM.

  • model: 현재는 whisper-1 모델만 지정 가능합니다.

  • language: 입력 오디오의 언어입니다. 입력 언어를 ISO-639-1 형식으로 제공하면 정확도와 지연 시간이 개선됩니다.

  • prompt: (선택 사항) 모델의 스타일을 안내하거나 이전 오디오 세그먼트를 계속하기 위한 텍스트입니다. 프롬프트는 오디오 언어와 일치해야 합니다.

  • response_format: 변환된 결과물 출력 형식입니다. 가능한 지정 옵션은 json, text, srt, verbose_json 또는 vtt 입니다.

  • temperature: 0에서 1 사이의 샘플링 temperature 입니다. 0.8과 같이 값이 높을수록 출력은 더 무작위적이고, 0.2와 같이 값이 낮을수록 출력은 더 집중적이고 결정론적입니다. 0으로 설정하면 모델은 로그 확률을 사용하여 특정 임계값에 도달할 때까지 자동으로 temperature 을 높입니다.

audio_file = open("data/채용면접_샘플_01.wav", "rb")
transcript = client.audio.transcriptions.create(
    file=audio_file,
    model="whisper-1",
    language="ko",
    response_format="text",
    temperature=0.0,
)
# 결과물 출력
print(transcript)

③ 더욱 긴 오디오 입력 대한 처리

기본적으로 Whisper API는 25MB 미만의 파일 만 지원합니다.

이보다 긴 오디오 파일이 있는 경우 25MB 이하의 청크로 나누거나 압축된 오디오 형식을 사용 해야 합니다.

최상의 성능을 얻으려면 문장 중간에 오디오를 분할하면 일부 문맥이 손실될 수 있으므로 분할을 피하는 것이 좋습니다.

이를 처리하는 한 가지 방법은 PyDub 오픈 소스 Python 패키지를 사용하여 오디오를 분할 하는 것입니다.

샘플 데이터셋(채용면접 인터뷰 데이터)

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

아래의 코드는 오디오 파일을 정해진 시간에 따라 분절하여 별도의 파일로 저장하는 코드입니다.

from pydub import AudioSegment

filename = "data/채용면접_샘플_02.wav"
myaudio = AudioSegment.from_mp3(filename)

# PyDub 는 밀리초 단위로 시간을 계산합니다.
thirty_seconds = 1 * 30 * 1000  # (1초 * 30) * 1000
total_milliseconds = myaudio.duration_seconds * 1000  # 전체 길이를 밀리초로 변환
# 전체 길이를 30초로 나누어서 반복할 횟수를 계산합니다.
total_iterations = int(total_milliseconds // thirty_seconds + 1)
total_iterations
3
# 생성된 파일명을 저장할 리스트
output_filenames = []

for i in range(total_iterations):
    if i < total_iterations - 1:
        # 30초 단위로 오디오를 분할합니다.
        part_of_audio = myaudio[thirty_seconds * i: thirty_seconds * (i + 1)]
    else:
        # 마지막은 나머지 전체를 분할합니다.
        part_of_audio = myaudio[thirty_seconds * i:]

    output_filename = (
        # 예시: 채용면접_샘플_02-(1).mp3, 채용면접_샘플_02-(2).mp3 ...
        f"{filename[:-4]}-({i+1}).mp3"
    )

    # 분할된 오디오를 저장합니다.
    part_of_audio.export(output_filename, format="mp3")
    output_filenames.append(output_filename)
# 결과물(파일명) 출력
output_filenames
['data/채용면접_샘플_02-(1).mp3',
 'data/채용면접_샘플_02-(2).mp3',
 'data/채용면접_샘플_02-(3).mp3']
transcripts = []

for audio_filename in output_filenames:
    audio_file = open(audio_filename, "rb")  # audio file 을 읽어옵니다.

    # transcript 를 생성합니다.
    transcript = client.audio.transcriptions.create(
        file=audio_file,
        model="whisper-1",  # 모델은 whisper-1 을 사용
        language="ko",  # 한국어를 사용
        response_format="text",  # 결과물은 text 로 출력
        temperature=0.0,
    )

    # 생성된 transcript 를 리스트에 추가합니다.
    transcripts.append(transcript)

# 전체 transcript 출력(리스트를 문자열로 변환)
final_output = "---- 분할 ---- \n".join(transcripts)
print(final_output)
뭐 제가 평소에 제가 생각했던 것 말고 다른 사람들이 주로 저한테 말했던 걸 생각해보면은 일단은 되게 한가지에 꽂히면 그거를 끝을 보는 그런 성격을 장점이라고 해줬던 것 같습니다. 항상 실행을 하는 편이었고 주변에서 저한테 너 항상 행동력이 좋다 이런 말을 들었던 것 같아요.
---- 분할 ---- 
제가 하고 싶은 일이 생기고 호기심이 생기면 어쨌든 그거를 실행해봐야 다음으로 넘어갈 수 있다고 생각하고 있어요. 그래서 사실 일적인 부분 말고도 취미적인 부분에서도 되게 많은 것들을 시도했던 경험이 있고요. 단점을 말하자면 좀 주변에서 제가 많이 들었던 게 자기 비하였던 것 같아요. 제가 좀 외적으로 컴플렉스를 스스로
---- 분할 ---- 
느끼고 있는 부분들이 많은데 안 그래야지 라고 생각하면서도 스스로 좀 비하하는 말들을 많이 하게 되더라구요. 그래서 주변에서 그런 것들에 대해서 주의를 좀 많이 줬던 순간들이 있었어요. 그래서 저도 최대한 그런 것들을 좀 지양해야겠다고 스스로 좀 다 지고 있습니다.

example_prompt = """
당신은 채용 담당관입니다.
주어진 내용을 바탕으로, 면접자의 긍정적인 면과 부정적인 면을 나누어서 정리해 주세요.
결과물은 요약된 불렛포인트로 정리해 주세요.
한글로 작성해 주세요.
(예시)
#긍정적인면
- 
- 
-
#부정정인면
-
-  
"""


def generate_HR_opinion(temperature, prompt, transcript):
    response = client.chat.completions.create(
        model="gpt-4",
        temperature=temperature,
        messages=[
            {"role": "system", "content": prompt},
            {"role": "user", "content": transcript},
        ],
    )
    return response.choices[0].message.content


hr_opinion = generate_HR_opinion(0, example_prompt, final_output)
print(hr_opinion)
#긍정적인면
- 한 가지 일에 꽂히면 끝을 본다는 집중력
- 행동력이 좋음
- 호기심이 많고, 새로운 것을 시도하는 것을 두려워하지 않음

#부정적인면
- 자기 비하하는 경향이 있음
- 외적인 컴플렉스를 스스로 느끼는 경향이 있음
- 자신에 대한 부정적인 말을 자주 함

댓글남기기