Text to Speech
OpenAI: Text to Speech
import os
from dotenv import load_dotenv
# 토큰 정보로드
api_key = os.getenv("OPENAI_API_KEY")
load_dotenv()
True
1. 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
, andshimmer
입니다. 음성의 미리듣기는 여기 에서 확인할 수 있습니다.response_format
: 오디오를 입력할 형식입니다. 지원되는 형식은mp3
,opus
,aac
및flac
입니다.speed
: 생성된 오디오의 속도입니다.0.25
에서4.0
사이의 값을 선택합니다. 기본값은1.0
입니다.
Client 생성
client
는 OpenAI 모듈로 생성된 인스턴스 입니다.
[주의] 아래의 코드에서 오류가 난다면 API 키의 오류일 가능성이 높습니다.
from openai import OpenAI
client = OpenAI()
speech_file_path = "tts_audio.mp3"
response = client.audio.speech.create(
model="tts-1",
input="아~ 오늘 파이썬 배우기 정말 좋은 날이네~",
voice="shimmer",
response_format="mp3",
speed=2.0,
)
response.stream_to_file(speech_file_path)
/tmp/ipykernel_4084825/1531316983.py:11: DeprecationWarning: Due to a bug, this method doesn't actually stream the response content, `.with_streaming_response.method()` should be used instead
response.stream_to_file(speech_file_path)
from IPython.display import Audio
Audio(speech_file_path)
Your browser does not support the audio element.
2. 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)
지금 생각해보면 가장 기억에 남는 것이 외환위기 때의 경험입니다. 외환위기 때 나라뿐 아니라 회사에서는 달러가 많이 부족했고 우리는 수출을 하기 위해서 원자재라든지 대금지급 혹은 또 우리가 수출한 물건에 대한 대금을 받아야 되는 상황이었습니다. 일단은 우리가 해외로부터 받아야 될 때는 최대한 그것을 달러로 받았고 반대로 우리가 지불해야 될 것은 가능한 한 원자재를 통해서 지급을 했습니다. 그 원자재라는 것이 결국은 반도체 쪽이었는데 우리나라에서 가장 나름 손쉽게 구하면서도 꼭 필요한 제품인 반도체를 원자재값 대응으로 물건을 주면서 반대로 해외에서 우리가 받아야 될 것은 달러로 받으면서 그 환율차를 가장 줄일 수가 있었습니다. 그것으로 인해서 약 6개월 동안에 나름 회사에서는 달러 지출을 막을 수 있었고 그때 그나마 달러를 회사에서 확보를 해서 나름의 위기를 극복할 수 있었던 것으로 생각합니다.
3. 더욱 긴 오디오 입력 대한 처리
기본적으로 Whisper API는 25MB 미만의 파일 만 지원
이보다 긴 오디오 파일이 있는 경우 25MB 이하의 청크로 나누거나 압축된 오디오 형식을 사용
최상의 성능을 얻으려면 문장 중간에 오디오를 분할하면 일부 문맥이 손실될 수 있으므로 가급적 분할 사용에 주의
이를 처리하는 한 가지 방법은
PyDub
오픈 소스 Python 패키지를 사용하여 오디오를 분할
샘플 데이터셋(채용면접 인터뷰 데이터)
링크: https://aihub.or.kr/aihubdata/data/view.do?currMenu=115&topMenu=100&aihubDataSe=data&dataSetSn=71592
#%pip install pydub
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 # 전체 길이를 밀리초로 변환
/home/kubwa/anaconda3/lib/python3.11/site-packages/pydub/utils.py:170: RuntimeWarning: Couldn't find ffmpeg or avconv - defaulting to ffmpeg, but may not work
warn("Couldn't find ffmpeg or avconv - defaulting to ffmpeg, but may not work", RuntimeWarning)
# 전체 길이를 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},
],
stream=True,
)
final_answer = []
# 스트림 모드에서는 completion.choices 를 반복문으로 순회
for chunk in response:
# chunk 를 저장
chunk_content = chunk.choices[0].delta.content
# chunk 가 문자열이면 final_answer 에 추가
if isinstance(chunk_content, str):
final_answer.append(chunk_content)
# 토큰 단위로 실시간 답변 출력
print(chunk_content, end="")
return "".join(final_answer)
hr_opinion = generate_HR_opinion(0, example_prompt, final_output)
#긍정적인면
- 한 가지 일에 꽂히면 끝을 본다는 집중력
- 행동력이 좋음
- 호기심이 많고, 새로운 것을 시도하는 것을 두려워하지 않음
#부정적인면
- 자기 비하하는 경향이 있음
- 외적인 컴플렉스를 스스로 느끼고 있음
- 스스로를 지나치게 엄격하게 평가하는 경향이 있음
Last updated