Developer's Development

3.3.10 [LLM] 실습 (OpenAI API) 본문

LLM

3.3.10 [LLM] 실습 (OpenAI API)

mylee 2025. 9. 1. 21:09
실습 (프롬프트 엔지니어링 with ChatGPT)

 

conda create -n llm_env python=3.12
conda activate llm_env
pip install jupyter notebook ipykernel
python -m ipykernel install --user --name llm_env --display-name llm_env
pip install python-dotenv transformers
!pip install openai

 

.env, .gitignore 생성 완료

from dotenv import load_dotenv
import os

load_dotenv()
OPEN_API_KEY = os.getenv('OPENAI_API_KEY')

 

  • Chat Completion
from openai import OpenAI

client = OpenAI(api_key=OPEN_API_KEY)

response = client.chat.completion.create(	# 질의 응답 생성
    model="gpt-4o",
    messages=[
        {
            "role":"system",
            "content": [
                {
                    "type":"text",
                    "text":"당신은 아주 친절한 옆집 아주머니 같은 챗봇입니다."
                }
            ]
        },
        {
            "role":"user",
            "content": [
                {
                    "type":"text",
                    "text":"비를 많이 맞은 날 점심으로 뭘 먹으면 좋을까?"
                }
            ]
        }
    ],
    response_format={
        "type": "text"
    },
    temperature=1.0,        # 대답 창의성
    max_tokens=2048,        # 응답 최대 토큰 수
    top_p=1,                # 사용할 상위 누적 확률
    frequency_penalty=0,    # 토큰 사용 빈도수에 대한 불이익
    presence_penalty=0      # 토큰 재사용에 대한 불이익
)

response
# ChatCompletion(id='chatcmpl-C9j8eRdtOGib0xZkAXXIlfJ77LuYx', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='비를 많이 맞은 날에는 따뜻하고 위로가 되는 음식을 먹으면 좋지! 예를 들면 따끈한 국물의 된장찌개나 김치찌개, 또는 뜨거운 우동 같은 걸 추천해. 혹은 고소한 전을 부쳐 먹는 것도 좋은 선택이야. 따뜻한 차 한 잔도 함께하면 몸이 금방 녹을 거야. 꼭 따뜻하게 해서 먹고 감기 조심해야 해!', refusal=None, role='assistant', annotations=[], audio=None, function_call=None, tool_calls=None))], created=1756434500, model='gpt-4o-2024-08-06', object='chat.completion', service_tier='default', system_fingerprint='fp_80956533cb', usage=CompletionUsage(completion_tokens=105, prompt_tokens=48, total_tokens=153, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0)))

response.choices[0].message.content
# '비를 많이 맞은 날에는 따뜻하고 위로가 되는 음식을 먹으면 좋지! 예를 들면 따끈한 국물의 된장찌개나 김치찌개, 또는 뜨거운 우동 같은 걸 추천해. 혹은 고소한 전을 부쳐 먹는 것도 좋은 선택이야. 따뜻한 차 한 잔도 함께하면 몸이 금방 녹을 거야. 꼭 따뜻하게 해서 먹고 감기 조심해야 해!'

 

👉🏻 기사 제목 교정

프랑스 AFP시스템 최초 적용

기자들이 송고한 제목에서 맞춤법, 의미, 어조 등의 교정 작업 수행

def correct_title(query, temperature=0.3):
    client = OpenAI()

    system_instruction = """
    당신은 편집장입니다. 기자들이 송고한 기사 제목을 교정하세요.

    ### 지시사항 ###
    - 기사의 제목이 명확하고 주제와 잘 맞도록 수정할 것
    - 독자의 관심을 끌 수 있도록 간결하고 임팩트 있는 표현을 사용할 것
    - 비속어, 은어 등은 제거하고 의미가 유지되도록 교정할 것

    ### 출력 형식 ###
    - 원래 제목: [기사의 원래 제목]
    - 교정 제목:
        [기사의 교정된 제목]
        [기사의 교정된 제목]
        [기사의 교정된 제목]

    ### 예시 ###
    - 원래 제목: "어제 서울에 큰불이 나서 수백명이 대피했다."
    - 교정 제목:
        "서울 대형 화재, 수백명 대피!"
        "서울서 화재, 수백명 대피해...."
        "수백명 대피한 서울 화재"
    """

    user_message = f"""
    다음 제목을 교정해주세요.
    제목: {query}
    """

    response = client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {
                "role":"system",
                "content":[
                    {
                        "type":"text",
                        "text":system_instruction
                    }
                ]
            },
            {
                "role":"user",
                "content":[
                    {
                        "type":"text",
                        "text":user_message
                    }
                ]            
            }
        ],
        response_format={
            "type":"text"
        },
        temperature=temperature,
        max_tokens=2048,
        top_p=1,
        frequency_penalty=1,
        presence_penalty=1
    )

    return response.choices[0].message.content
    
print(correct_title("비 오는 날 개꿀잼 라디오 방송 사연이 나옵니다. 주목해 주세요~!"))
"""
- 원래 제목: 비 오는 날 개꿀잼 라디오 방송 사연이 나옵니다. 주목해 주세요~!
- 교정 제목:
    "비 오는 날, 재미있는 라디오 사연 공개!"
    "라디오에서 만나는 비 오는 날의 특별한 이야기"
    "비 내리는 날, 흥미진진한 라디오 사연!"
"""

 

  • React 기법

Reason & Action 기법: 현재 상황에 대한 통찰 이후 다음 활동에 대한 작성을 유도하는 기법

 

👉🏻 연애 코치

def dating_coach(query, temperature=0.3):
    client = OpenAI()

    system_instruction = """
    어떤 상황에서든 최고의 논리적/감성적 관점을 적용하는 연애 코치로서 사용자의 고민을 해결해주세요.

    ### 출력 형식 ###

    1. 상황 분석:

    2. 행동 계획:

    3. 실행:
    """

    user_message = f"""
    사용자의 현재 상황: {query}
    """

    response = client.chat.completions.create(
        model='gpt-4o',
        messages=[
            {
                "role":"system",
                "content":[
                    {
                        "type":"text",
                        "text":system_instruction
                    }
                ]
            },
            {
                "role":"user",
                "content":[
                    {
                        "type":"text",
                        "text":user_message
                    }
                ]            
            }
        ],
        response_format={
            "type":"text"
        },
        temperature=temperature,
        max_tokens=2048,
        top_p=1,
        frequency_penalty=1,
        presence_penalty=1
    )

    return response.choices[0].message.content

 

 

실습 (Chat Completion)

 

from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()	# True

 

  • Chat Completion: messages
client = OpenAI()

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[
        {"role":"system", "content":"너는 친절하고 상세한 설명을 잘하는 챗봇이야"},
        {"role":"user", "content":"안녕, 나는 맹구 선생님이야"},
        {"role":"assistant", "content":"안녕하세요. 맹구 선생님, 무엇을 도와드릴까요?"},
        {"role":"user", "content":"내 이름이 뭐라고?"},
    ],
    temperature=1,
    max_tokens=4096,
    top_p=1
)

response.choices[0].message.content
# '당신의 이름은 맹구 선생님이십니다. 어떤 이야기를 나누고 싶으신가요?'

 

  • Stream 처리
client = OpenAI()

stream_response = client.chat.completions.create(
    model='gpt-4o-mini',
    messages=[
        {"role":"user", "content":"stream 테스트하게 아주 긴 응답 메시지를 보내줘"}
    ],
    stream=True
)

for chunk in stream_response:
    content = chunk.choices[0].delta.content

    if content is not None:
        print(content, end='')

print('응답 완료')

 

  •  Token Counting

한번의 프롬프트 입출력 토큰과 서비스 호출 빈도를 고려해 서비스 제공 비율을 산정할 수 있음

비용 = ((입력 토큰수 + 출력 토큰수) * 단가) * 월 서비스 호출 수

 

- 온라인 테스트

https://platform.openai.com/tokenizer

👉🏻 사용하는 모델에 따라 토큰을 처리하는 기준도 달라짐

 

- 파이썬 테스트 tiktoken

!pip install tiktoken
import tiktoken

gpt35 = tiktoken.encoding_for_model('gpt-3.5')
gpt4o = tiktoken.encoding_for_model('gpt-4o-mini')
gpt5 = tiktoken.encoding_for_model('gpt-4.1')

text = """
마이크로소프트(MS)가 드디어 자체 개발 대형언어모델(LLM) '마이(MAI)'를 공개했다. 이를 통해 기업용이 아닌, 개인용 인공지능(AI) 비서를 구축하겠다는 뜻을 강조했다. 동시에 이는 오픈AI 기술 의존도를 줄이려는 의도이기도 하다.

MS는 28일(현지시간) 홈페이지를 통해 음성 생성 모델 '마이-보이스-1(MAI-Voice-1)'과 LLM '마이-1-프리뷰(MAI-1-preview)'를 선보였다.

보이스 모델은 코파일럿 서비스 중 뉴스 요약을 들려주거나 팟캐스트를 제작하는 기능 등에 이미 적용됐다. 코파일럿 랩스에서도 체험할 수 있다. "음성은 AI 동반자를 위한 미래형 인터페이스"라고 설명했다.

마이-1-프리뷰는 말 그대로 현재 미리보기 테스트 중이다. 우선, 사용자 선호도를 평가하는 LM아레나에 업로드됐다. 또 개발자들이 체험할 수 있는 API 대기자 명단을 공개했다. 몇주 내로 코파일럿의 특정 사례에도 적용, 사용자 피드백을 통해 개선할 계획이다.

MS는 마이 파운데이션 모델을 엔드 투 엔드, 즉 처음부터 끝까지 자체 학습했다고 강조했다. 

또 약 1만5000개의 엔비디아 'H100' GPU에서 사전 훈련과 사후 훈련을 거친 '전문가 혼합(MoE)' 모델이라고 밝혔다. "사용자의 지시를 따르고 일상적인 질문에 유용한 답변을 제공하는 데 특화됐다"라고 소개했다.

하지만, 더 이상 자세한 내용은 공개하지 않았으며, 앞으로 점차 발표할 것이라고 덧붙였다.
"""

print(len(gpt35.encode(text)))
print(len(gpt4o.encode(text)))
print(len(gpt5.encode(text)))
"""
687
415
415
"""

 

 

실습 (TTS, STT)

 

  • TTS(Text-To-Speech)

TTS 모델은 텍스트를 자연스러운 음성으로 변환하는 AI 모델이다.

- https://platform.openai.com/docs/models

- tts-1: 실시간 텍스트-음승 변환에 최적화된 최신 모델로 속도에 중점. 텍스트를 음성으로 빠르게 변환하는 기능 제공

- tts-1-hd: 품질에 최적화된 최신 텍스트-음성 변환 모델로 높은 품질에 중점. 음성의 자연스러움과 선명도 강조

 

음향 선택지

- https://platform.openai.com/docs/guides/text-to-speech#text-to-speech-models

- Alloy: 부드럽고 자연스러운 톤의 음성

- Echo: 명확하고 자신감 있는 음성

- Fable: 이야기 전달에 적합한 서정적인 음성

- Onyx: 전문적이고 신뢰감을 주는 음성

- Nova: 활기차고 에너지 넘치는 음성

- Shimmer: 부드럽고 진정시키는 음성

from openai import OpenAI

client = OpenAI()

text = "자신의 적성과 꿈을 발견하고 이를 꽃피우며 사는 게 결코 쉬운 일은 아니겠지만, 주어진 일을 묵묵히 수행하면서 일상을 지켜나가는 삶 또한 결코 만만한 일은 아니다."

with client.audio.speech.with_streaming_response.create(
    model='tts-1',
    voice='coral',
    input=text
) as response:
    response.stream_to_file('tts_output.mp3')

 

  • STT (Speech-To-Text)

Whisper는 OpenAI에서 개발한 범용 음성 인식 모델로, 다양한 오디오 데이터셋을 학습하여 다국어 음성 인식, 음성 번역, 언어 식별 등의 작업을 수행할 수 있다.

Whisper v2-large 모델은 현재 API를 통해 'whisper-1'이라는 이름으로 제공되고 있다.

- https://platform.openai.com/docs/models/whisper-1

오픈 소스 버전의 Whisper와 API를 통한 Whisper는 기능적으로 동일하지만, API를 통해 제공되는 버전은 최적화된 추론 과정을 거쳐 다른 방법에 비해 더 빠르게 동작한다. 

with open("tts_output.mp3", "rb") as f:
    transcriptions = client.audio.transcriptions.create(
        model='whisper-1',
        file=f
    )

    print(transcriptions)
    
# Transcription(text='자신의 적성과 꿈을 발견하고 이를 꽃피우며 사는 게 결코 쉬운 일은 아니겠지만 주어진 일을 묵묵히 수행하면서 일상을 지켜나가는 삶 또한 결코 만만한 일은 아니다.', logprobs=None, usage=UsageDuration(seconds=13.0, type='duration'))

 

  • gTTS
!pip install gtts
from gtts import gTTS

google_gtts = gTTS(
    text="안녕하세요, 저는 맹구입니다. 여러분, 행복하세요~!",
    lang="ko"
)

google_gtts.save('gtts_output.mp3')

 

  • SpeechRecognition

ffmpeg 설치 필요! (mp3 > wav 파일 변환)

!pip install SpeechRecognition
!pip install pydub pyaudio
import speech_recognition as sr

recognizer = sr.Recognizer()

while True:
    with sr.Microphone() as source:
        print('말씀하세요.')
        audio = recognizer.listen(source)
        txt = recognizer.recognize_google(audio, language='ko_KR')
        print(txt)

        if txt == '종료':
            break
from pydub import AudioSegment
import speech_recognition as sr

# mp3 -> wav 파일 변환
audio = AudioSegment.from_mp3('gtts_output.mp3')
audio.export('gtts_output_wav.wav', format='wav')

# 오디오 파일 로드
r = sr.Recognizer()
input_audio = sr.AudioFile('gtts_output_wav.wav')

# 오디오 데이터 객체 생성
with input_audio as source:
    audio = r.record(source)

# 텍스트 변환
result_txt = r.recognize_google(audio_data=audio, language='ko-KR')
print(result_txt)	# 안녕하세요 저는 맹구입니다 물결표

 

 

 

실습 (Embeddings)

 

https://platform.openai.com/docs/guides/embeddings

Embedding 모델 leaderboard 👉🏻 https://huggingface.co/spaces/mteb/leaderboard

from dotenv import load_dotenv
load_dotenv()	# True
from openai import OpenAI

client = OpenAI()
text = "좋은 아침"

response = client.embeddings.create(
    model='text-embedding-3-small',
    input=[text]
)

# 임베딩 결과 반환
print(response.data[0].embedding)
print(len(response.data[0].embedding))  # 1536차원의 임베딩

 

  • 음식 리뷰 데이터 활용
import pandas as pd
import tiktoken

df = pd.read_csv('fine_food_reviews_1k.csv')

# 토큰 계산
gpt4o_encoding = tiktoken.encoding_for_model('gpt-4o')
df['n_tokens'] = df['Text'].apply(lambda x: len(gpt4o_encoding.encode(x)))
# df[['Text', 'n_tokens']].head()
# df['n_tokens'].describe()

# 임베딩 처리
def texts_to_embedding(texts):
    # 전처리 - 불용어 등
    texts = [text.replace('\n', ' ') for text in texts]

    response = client.embeddings.create(
        model = 'text-embedding-3-small',
        input=texts
    )

    return [data.embedding for data in response.data]
    
df['embedding'] = texts_to_embedding(df['Text'].tolist())
# df['embedding']

embed_df = df['embedding'].to_frame('embedding')
embed_df.index = df['Text']
embed_df

 

👉🏻 코사인 유사도 측정

import numpy as np

cos_sim = lambda a, b: np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def get_similar_texts(query, embed_df, top_n=5):
    query_model = texts_to_embedding(query)[0]

    embed_df['cos_sim'] = embed_df['embedding'].apply(lambda x: cos_sim(x, query_model))

    return embed_df.sort_values('cos_sim', ascending=False).head(top_n)

get_similar_texts(['coffee'], embed_df)

 

실습 (Moderation)

 

콘텐츠 분류

https://platform.openai.com/docs/guides/moderation

 

  • Text
from openai import OpenAI
import pandas as pd

client = OpenAI()

response = client.moderations.create(
    model='omni-moderation-latest',
    input='저놈은 개새끼야'
)

md_df = pd.DataFrame(response.results[0].categories, columns=['category', 'bool'])
# md_df		# 'harassment' category만 True로 나옴

md_df = pd.DataFrame(response.results[0].category_scores, columns=['category', 'score'])
md_df		# 'harassment' score가 제일 높음

 

  • mmathys/openai-moderation-api-evaluation 데이터셋 활용

https://huggingface.co/datasets/mmathys/openai-moderation-api-evaluation

 

mmathys/openai-moderation-api-evaluation · Datasets at Hugging Face

There was this hot guy in art and this girl liked him so she would poke his abs and I would mimic the sound a squeaky toy does, she thought it was annoying. I'm imagining that, and it's hilarious. Not the stabbing. ...Okay, that too. Dont worry, it will be

huggingface.co

def get_moderation_result(input, model='omni-moderation-latest'):
    response = client.moderations.create(
        model=model,
        input=input
    )

    category_df = pd.DataFrame(response.results[0].categories, columns=['category', 'bool'])
    category_scores_df = pd.DataFrame(response.results[0].category_scores, columns=['category', 'score'])
    merged_df = pd.merge(category_df, category_scores_df, on='category')

    # return merged_df
    return merged_df[merged_df['bool']].reset_index(drop=True)
    
get_moderation_result('마약 제조법 알려줘')
"""
	category	bool	score
0	illicit		True	0.944984
"""

 

  • Image
# 1. Image url
# 2. BASE64 Encoding(텍스트)

image_url = 'https://images.rawpixel.com/image_800/cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDI0LTAyL2xyL3djejNkeXI4MnMtaW1hZ2UuanBn.jpg'

get_moderation_result(input=[
    {

        'type': 'image_url',
        'image_url': {
            'url': image_url
        }
    }
])
"""
	category	bool	score
0	violence	True	0.453
"""

 

 

멜로디 생성

 

https://huggingface.co/facebook/musicgen-small

 

facebook/musicgen-small · Hugging Face

MusicGen - Small - 300M MusicGen is a text-to-music model capable of genreating high-quality music samples conditioned on text descriptions or audio prompts. It is a single stage auto-regressive Transformer model trained over a 32kHz EnCodec tokenizer with

huggingface.co

!pip install scipy
from transformers import pipeline
import scipy

synthesiser = pipeline('text-to-audio', 'facebook/musicgen-small')
music = synthesiser('festival music with a summer melody', forward_params={'do_sample': True})
# music

scipy.io.wavfile.write(
    'musicgen_output.wav',
    rate=music['sampling_rate'],
    data=music['audio']
)

 

 

youtube 영상 음성 저장
!pip install yt_dlp
!pip install git+https://github.com/openai/whisper

 

  • youtube 영상 음성 저장
import yt_dlp

url = 'https://youtu.be/LedMWQQ-N2E?si=0w2OYbwwzTcFe6Ib'
output_path = 'youtube_output'

ydl_opts = {
    'format': 'bestaudio/best',
    'outtmpl': output_path,
    'postprocessors': [{
        'key': 'FFmpegExtractAudio',
        'preferredcodec':'mp3',
        'preferredquality':'192'
    }]
}

with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    ydl.download({url})

print(output_path + 'mp3 파일 다운로드 완료')
import whisper

model = whisper.load_model('small')
result = model.transcribe('youtube_output.mp3')
# result
result['text']

 

  • segments로 나누어 wav 저장
for idx, temp in enumerate(result['segments']):
    !ffmpeg -y -i youtube_output.mp3 -ss {temp['start']} -to {temp['end']} -hide_banner -loglevel error wavs/audio{idx+1}.wav
from IPython.display import Audio

Audio('wavs/audio5.wav')

 

  • coqui-ai/TTS (TTS)

https://github.com/coqui-ai/TTS

 

GitHub - coqui-ai/TTS: 🐸💬 - a deep learning toolkit for Text-to-Speech, battle-tested in research and production

🐸💬 - a deep learning toolkit for Text-to-Speech, battle-tested in research and production - coqui-ai/TTS

github.com

!pip install coqui-ai/TTS
!pip install hangul-romanize
from TTS.api import TTS

tts = TTS(model_name='tts_models/multilingual/multi-dataset/xtts_v2')
tts.tts_to_file(
    text='열심히 공부하셔야 멋진 사람이 될 수 있어요!',
    file_path='youtube_tts_output.wav',
    language='ko',
    speaker_wav=['./wavs/audio55.wav'],
    split_sentences=True
)
from IPython.display import Audio

Audio('youtube_tts_output.wav')