Notice
Recent Posts
Recent Comments
반응형
오늘도 공부
LLM Playground 구축 - 완벽 핸즈온 가이드 #1 본문
반응형
🎯 학습 목표
- LLM API 기본 사용법 이해
- 프롬프트 엔지니어링 기법 실습
- 토크나이제이션과 파라미터 조정
- 인터랙티브 웹 인터페이스 구축
📋 사전 준비
1. 개발 환경 설정
# Python 가상환경 생성
python -m venv llm-playground
source llm-playground/bin/activate # Windows: llm-playground\Scripts\activate
# 필수 패키지 설치
pip install openai anthropic streamlit python-dotenv tiktoken
pip install pandas numpy matplotlib
2. API 키 발급
- OpenAI: https://platform.openai.com/api-keys
- Anthropic: https://console.anthropic.com/settings/keys
3. 프로젝트 구조 생성
mkdir llm-playground
cd llm-playground
mkdir -p src utils data
touch .env app.py
🚀 Step 1: 기본 LLM 호출 (15분)
src/basic_llm.py
import os
from openai import OpenAI
from anthropic import Anthropic
from dotenv import load_dotenv
load_dotenv()
class LLMClient:
def __init__(self):
self.openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.anthropic_client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
def call_gpt(self, prompt, model="gpt-4o-mini", temperature=0.7, max_tokens=500):
"""OpenAI GPT 호출"""
try:
response = self.openai_client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=temperature,
max_tokens=max_tokens
)
return {
"response": response.choices[0].message.content,
"tokens": response.usage.total_tokens,
"model": model
}
except Exception as e:
return {"error": str(e)}
def call_claude(self, prompt, model="claude-3-5-sonnet-20241022", temperature=0.7, max_tokens=500):
"""Anthropic Claude 호출"""
try:
response = self.anthropic_client.messages.create(
model=model,
max_tokens=max_tokens,
temperature=temperature,
messages=[{"role": "user", "content": prompt}]
)
return {
"response": response.content[0].text,
"tokens": response.usage.input_tokens + response.usage.output_tokens,
"model": model
}
except Exception as e:
return {"error": str(e)}
# 테스트
if __name__ == "__main__":
client = LLMClient()
prompt = "안녕하세요! AI에 대해 한 문장으로 설명해주세요."
print("=== GPT-4 ===")
gpt_result = client.call_gpt(prompt)
print(gpt_result["response"])
print(f"토큰: {gpt_result['tokens']}\n")
print("=== Claude ===")
claude_result = client.call_claude(prompt)
print(claude_result["response"])
print(f"토큰: {claude_result['tokens']}")
.env 파일 생성
OPENAI_API_KEY=your_openai_key_here
ANTHROPIC_API_KEY=your_anthropic_key_here
실행 및 확인
python src/basic_llm.py
✅ 체크포인트: 두 모델 모두 응답이 나오면 성공!
🎨 Step 2: 프롬프트 엔지니어링 템플릿 (20분)
src/prompt_templates.py
class PromptTemplates:
"""다양한 프롬프트 엔지니어링 기법"""
@staticmethod
def zero_shot(task):
"""Zero-shot: 예시 없이 바로 질문"""
return f"{task}"
@staticmethod
def few_shot(task, examples):
"""Few-shot: 예시를 포함한 프롬프트"""
example_text = "\n\n".join([
f"입력: {ex['input']}\n출력: {ex['output']}"
for ex in examples
])
return f"""다음 예시를 참고하여 작업을 수행하세요.
{example_text}
입력: {task}
출력:"""
@staticmethod
def chain_of_thought(task):
"""CoT: 단계별 사고 유도"""
return f"""{task}
단계별로 생각해봅시다:
1. 먼저,
2. 그 다음,
3. 따라서,"""
@staticmethod
def react_prompt(task, tools):
"""ReAct: 추론과 행동을 결합"""
tools_desc = "\n".join([f"- {tool}" for tool in tools])
return f"""당신은 다음 도구들을 사용할 수 있습니다:
{tools_desc}
작업: {task}
다음 형식으로 생각하고 행동하세요:
Thought: [현재 상황 분석]
Action: [사용할 도구]
Observation: [도구 결과]
... (필요시 반복)
Final Answer: [최종 답변]
시작:"""
@staticmethod
def role_specific(role, task):
"""역할 기반 프롬프트"""
return f"""당신은 {role}입니다.
{task}
{role}의 관점에서 전문적으로 답변해주세요."""
# 테스트
if __name__ == "__main__":
from basic_llm import LLMClient
client = LLMClient()
templates = PromptTemplates()
task = "한국의 수도는 어디인가요?"
# 1. Zero-shot
print("=== Zero-shot ===")
prompt = templates.zero_shot(task)
result = client.call_gpt(prompt, max_tokens=100)
print(result["response"], "\n")
# 2. Few-shot
print("=== Few-shot ===")
examples = [
{"input": "일본의 수도는?", "output": "일본의 수도는 도쿄입니다."},
{"input": "프랑스의 수도는?", "output": "프랑스의 수도는 파리입니다."}
]
prompt = templates.few_shot(task, examples)
result = client.call_gpt(prompt, max_tokens=100)
print(result["response"], "\n")
# 3. Chain of Thought
print("=== Chain of Thought ===")
complex_task = "서울에서 부산까지 가는 가장 빠른 방법은?"
prompt = templates.chain_of_thought(complex_task)
result = client.call_gpt(prompt, max_tokens=200)
print(result["response"], "\n")
# 4. ReAct
print("=== ReAct ===")
tools = ["검색", "계산기", "날씨 조회"]
prompt = templates.react_prompt("오늘 서울 날씨는 어때?", tools)
result = client.call_gpt(prompt, max_tokens=200)
print(result["response"])
🔤 Step 3: 토크나이제이션 분석 (15분)
src/tokenizer_analyzer.py
import tiktoken
from anthropic import Anthropic
class TokenizerAnalyzer:
def __init__(self):
self.gpt_tokenizer = tiktoken.get_encoding("cl100k_base") # GPT-4, GPT-3.5-turbo
self.anthropic_client = Anthropic()
def analyze_gpt_tokens(self, text):
"""GPT 토크나이저 분석"""
tokens = self.gpt_tokenizer.encode(text)
token_texts = [self.gpt_tokenizer.decode([token]) for token in tokens]
return {
"token_count": len(tokens),
"tokens": tokens,
"token_texts": token_texts,
"char_count": len(text),
"tokens_per_char": len(tokens) / len(text) if len(text) > 0 else 0
}
def analyze_claude_tokens(self, text):
"""Claude 토큰 카운팅"""
# Anthropic의 count_tokens API 사용
token_count = self.anthropic_client.count_tokens(text)
return {
"token_count": token_count,
"char_count": len(text),
"tokens_per_char": token_count / len(text) if len(text) > 0 else 0
}
def compare_tokenizers(self, text):
"""두 토크나이저 비교"""
gpt_analysis = self.analyze_gpt_tokens(text)
claude_analysis = self.analyze_claude_tokens(text)
return {
"text": text,
"gpt": gpt_analysis,
"claude": claude_analysis,
"difference": abs(gpt_analysis["token_count"] - claude_analysis["token_count"])
}
# 테스트
if __name__ == "__main__":
analyzer = TokenizerAnalyzer()
test_texts = [
"Hello, world!",
"안녕하세요, 세계!",
"This is a longer sentence with more complex tokenization patterns.",
"한국어는 영어보다 토큰화가 어떻게 다를까요? 🤔"
]
for text in test_texts:
print(f"\n텍스트: {text}")
print("=" * 60)
gpt_result = analyzer.analyze_gpt_tokens(text)
print(f"GPT 토큰 수: {gpt_result['token_count']}")
print(f"토큰들: {gpt_result['token_texts'][:10]}...") # 처음 10개만
claude_result = analyzer.analyze_claude_tokens(text)
print(f"Claude 토큰 수: {claude_result['token_count']}")
print(f"문자당 토큰 비율: {claude_result['tokens_per_char']:.2f}")
🎛️ Step 4: 파라미터 실험실 (20분)
src/parameter_lab.py
from basic_llm import LLMClient
import json
class ParameterLab:
def __init__(self):
self.client = LLMClient()
def experiment_temperature(self, prompt, temps=[0.0, 0.5, 1.0, 1.5]):
"""Temperature 효과 실험"""
results = []
for temp in temps:
result = self.client.call_gpt(prompt, temperature=temp, max_tokens=100)
results.append({
"temperature": temp,
"response": result.get("response", "Error"),
"tokens": result.get("tokens", 0)
})
return results
def experiment_top_p(self, prompt, top_ps=[0.1, 0.5, 0.9, 1.0]):
"""Top-p (nucleus sampling) 실험"""
results = []
for top_p in top_ps:
# OpenAI API에 top_p 파라미터 추가
response = self.client.openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
top_p=top_p,
max_tokens=100
)
results.append({
"top_p": top_p,
"response": response.choices[0].message.content,
"tokens": response.usage.total_tokens
})
return results
def experiment_max_tokens(self, prompt, max_tokens_list=[50, 100, 200, 500]):
"""Max tokens 효과 실험"""
results = []
for max_tok in max_tokens_list:
result = self.client.call_gpt(prompt, max_tokens=max_tok)
results.append({
"max_tokens": max_tok,
"response": result.get("response", "Error"),
"actual_tokens": result.get("tokens", 0)
})
return results
# 테스트
if __name__ == "__main__":
lab = ParameterLab()
# 창의적 글쓰기 프롬프트
creative_prompt = "우주 여행에 대한 짧은 시를 써주세요."
print("=== Temperature 실험 ===")
temp_results = lab.experiment_temperature(creative_prompt)
for r in temp_results:
print(f"\nTemperature: {r['temperature']}")
print(f"응답: {r['response'][:100]}...")
print("\n\n=== Top-p 실험 ===")
topp_results = lab.experiment_top_p(creative_prompt)
for r in topp_results:
print(f"\nTop-p: {r['top_p']}")
print(f"응답: {r['response'][:100]}...")
🖥️ Step 5: Streamlit 웹 인터페이스 (30분)
app.py
import streamlit as st
import sys
sys.path.append('src')
from basic_llm import LLMClient
from prompt_templates import PromptTemplates
from tokenizer_analyzer import TokenizerAnalyzer
from parameter_lab import ParameterLab
# 페이지 설정
st.set_page_config(
page_title="LLM Playground",
page_icon="🤖",
layout="wide"
)
# 사이드바 - 모델 선택 및 파라미터
st.sidebar.title("⚙️ 설정")
model_type = st.sidebar.selectbox(
"모델 선택",
["GPT-4o-mini", "GPT-4o", "Claude Sonnet", "Claude Opus"]
)
temperature = st.sidebar.slider("Temperature", 0.0, 2.0, 0.7, 0.1)
max_tokens = st.sidebar.slider("Max Tokens", 50, 2000, 500, 50)
top_p = st.sidebar.slider("Top-p", 0.0, 1.0, 1.0, 0.1)
# 메인 탭
tab1, tab2, tab3, tab4 = st.tabs([
"💬 기본 채팅",
"📝 프롬프트 템플릿",
"🔤 토크나이저",
"🎛️ 파라미터 실험"
])
# 클라이언트 초기화
@st.cache_resource
def get_clients():
return LLMClient(), PromptTemplates(), TokenizerAnalyzer(), ParameterLab()
client, templates, analyzer, lab = get_clients()
# Tab 1: 기본 채팅
with tab1:
st.header("💬 LLM과 대화하기")
user_input = st.text_area(
"메시지를 입력하세요:",
height=150,
placeholder="예: 인공지능의 미래에 대해 설명해주세요."
)
col1, col2 = st.columns([1, 5])
with col1:
if st.button("🚀 전송", type="primary"):
if user_input:
with st.spinner("생각하는 중..."):
if "GPT" in model_type:
model_map = {
"GPT-4o-mini": "gpt-4o-mini",
"GPT-4o": "gpt-4o"
}
result = client.call_gpt(
user_input,
model=model_map[model_type],
temperature=temperature,
max_tokens=max_tokens
)
else:
model_map = {
"Claude Sonnet": "claude-3-5-sonnet-20241022",
"Claude Opus": "claude-opus-4-20250514"
}
result = client.call_claude(
user_input,
model=model_map[model_type],
temperature=temperature,
max_tokens=max_tokens
)
if "error" not in result:
st.success("✅ 응답 완료!")
st.markdown("### 📄 응답")
st.markdown(result["response"])
col_a, col_b, col_c = st.columns(3)
col_a.metric("모델", result["model"])
col_b.metric("토큰 사용", result["tokens"])
col_c.metric("Temperature", temperature)
else:
st.error(f"❌ 오류: {result['error']}")
else:
st.warning("메시지를 입력해주세요!")
# Tab 2: 프롬프트 템플릿
with tab2:
st.header("📝 프롬프트 엔지니어링 템플릿")
template_type = st.selectbox(
"템플릿 선택",
["Zero-shot", "Few-shot", "Chain of Thought", "ReAct", "Role-specific"]
)
task_input = st.text_input(
"작업/질문 입력:",
placeholder="예: 기후 변화의 주요 원인은 무엇인가요?"
)
if template_type == "Few-shot":
st.subheader("예시 입력 (Few-shot)")
num_examples = st.number_input("예시 개수", 1, 5, 2)
examples = []
for i in range(num_examples):
col1, col2 = st.columns(2)
with col1:
ex_input = st.text_input(f"예시 {i+1} - 입력", key=f"ex_in_{i}")
with col2:
ex_output = st.text_input(f"예시 {i+1} - 출력", key=f"ex_out_{i}")
if ex_input and ex_output:
examples.append({"input": ex_input, "output": ex_output})
elif template_type == "Role-specific":
role = st.text_input(
"역할 입력:",
placeholder="예: 경제학 교수"
)
if st.button("🎯 프롬프트 생성 및 실행"):
if task_input:
# 프롬프트 생성
if template_type == "Zero-shot":
prompt = templates.zero_shot(task_input)
elif template_type == "Few-shot":
prompt = templates.few_shot(task_input, examples)
elif template_type == "Chain of Thought":
prompt = templates.chain_of_thought(task_input)
elif template_type == "ReAct":
tools = ["검색", "계산기", "날씨 API"]
prompt = templates.react_prompt(task_input, tools)
elif template_type == "Role-specific":
prompt = templates.role_specific(role, task_input)
# 프롬프트 표시
st.markdown("### 생성된 프롬프트")
st.code(prompt, language="text")
# LLM 실행
with st.spinner("실행 중..."):
result = client.call_gpt(prompt, temperature=temperature, max_tokens=max_tokens)
if "error" not in result:
st.markdown("### 📄 결과")
st.markdown(result["response"])
# Tab 3: 토크나이저 분석
with tab3:
st.header("🔤 토크나이저 분석")
analyze_text = st.text_area(
"분석할 텍스트:",
height=150,
placeholder="예: Hello, world! 안녕하세요!"
)
if st.button("🔍 분석 시작"):
if analyze_text:
gpt_result = analyzer.analyze_gpt_tokens(analyze_text)
claude_result = analyzer.analyze_claude_tokens(analyze_text)
col1, col2 = st.columns(2)
with col1:
st.subheader("GPT 토크나이저")
st.metric("토큰 수", gpt_result["token_count"])
st.metric("문자 수", gpt_result["char_count"])
st.metric("문자당 토큰", f"{gpt_result['tokens_per_char']:.2f}")
with st.expander("개별 토큰 보기"):
for i, token_text in enumerate(gpt_result["token_texts"][:50]):
st.text(f"{i+1}. '{token_text}' (ID: {gpt_result['tokens'][i]})")
with col2:
st.subheader("Claude 토크나이저")
st.metric("토큰 수", claude_result["token_count"])
st.metric("문자 수", claude_result["char_count"])
st.metric("문자당 토큰", f"{claude_result['tokens_per_char']:.2f}")
st.info(f"ℹ️ 토큰 차이: {abs(gpt_result['token_count'] - claude_result['token_count'])} 토큰")
# Tab 4: 파라미터 실험
with tab4:
st.header("🎛️ 파라미터 효과 실험")
experiment_type = st.selectbox(
"실험 유형",
["Temperature", "Top-p", "Max Tokens"]
)
experiment_prompt = st.text_area(
"실험용 프롬프트:",
height=100,
value="우주 여행에 대한 짧은 시를 써주세요."
)
if st.button("🧪 실험 시작"):
if experiment_prompt:
with st.spinner("실험 진행 중..."):
if experiment_type == "Temperature":
results = lab.experiment_temperature(experiment_prompt)
for r in results:
with st.expander(f"Temperature: {r['temperature']}"):
st.markdown(r['response'])
st.caption(f"토큰 사용: {r['tokens']}")
elif experiment_type == "Top-p":
results = lab.experiment_top_p(experiment_prompt)
for r in results:
with st.expander(f"Top-p: {r['top_p']}"):
st.markdown(r['response'])
st.caption(f"토큰 사용: {r['tokens']}")
elif experiment_type == "Max Tokens":
results = lab.experiment_max_tokens(experiment_prompt)
for r in results:
with st.expander(f"Max Tokens: {r['max_tokens']}"):
st.markdown(r['response'])
st.caption(f"실제 토큰: {r['actual_tokens']}")
# Footer
st.sidebar.markdown("---")
st.sidebar.markdown("### 📚 학습 리소스")
st.sidebar.markdown("""
- [OpenAI 문서](https://platform.openai.com/docs)
- [Anthropic 문서](https://docs.anthropic.com)
- [프롬프트 엔지니어링 가이드](https://www.promptingguide.ai)
""")
실행
streamlit run app.py
🎓 Step 6: 고급 실습 과제
과제 1: 데이터 정제 (Data Cleaning)
# src/data_cleaning.py
# Common Crawl 데이터를 정제하는 파이프라인 구축
# - HTML 태그 제거
# - 중복 제거
# - 품질 필터링 (문장 길이, 특수문자 비율)
과제 2: SFT (Supervised Fine-Tuning) 데모
# src/sft_demo.py
# OpenAI Fine-tuning API를 사용한 간단한 fine-tuning
# - 데이터셋 준비 (JSONL 포맷)
# - Fine-tuning job 생성
# - 모델 평가
과제 3: RLHF 시뮬레이션
# src/rlhf_simulator.py
# 간단한 RLHF 프로세스 시뮬레이션
# - 여러 응답 생성
# - 사용자 선호도 수집 (thumbs up/down)
# - Reward model 학습 (간단한 분류기)
📊 평가 및 다음 단계
체크리스트
- [ ] 기본 LLM API 호출 성공
- [ ] 5가지 프롬프트 템플릿 모두 테스트
- [ ] 토크나이저 차이 이해
- [ ] Temperature/Top-p 효과 확인
- [ ] Streamlit 앱 실행 성공
다음 프로젝트 준비
Project 2를 위해 필요한 것:
- Vector Database 설치 (ChromaDB 또는 Pinecone)
- 고객 지원 데이터셋 수집
- Embeddings 개념 학습
반응형
'AI' 카테고리의 다른 글
| "Deep Research" Capability - 완벽 핸즈온 가이드 (0) | 2025.10.31 |
|---|---|
| Customer Support Chatbot 구축 - 완벽 핸즈온 가이드 #2 (0) | 2025.10.31 |
| CS 336: 언어 모델을 밑바닥부터 만들기 - 강의 요약 (0) | 2025.10.29 |
| AI 바이브 코딩에서 프로덕션 레벨까지 (0) | 2025.10.28 |
| NextJs 16에 새로 추가된 MCP 서버에 알아보자 (0) | 2025.10.28 |
