Spaces:
Sleeping
Sleeping
""" | |
🎭 AI 감정 분석기 | |
Hugging Face Spaces 배포용 완성 코드 | |
""" | |
import gradio as gr | |
from transformers import pipeline | |
import logging | |
import os | |
# 로깅 설정 | |
logging.basicConfig(level=logging.INFO) | |
logger = logging.getLogger(__name__) | |
# 전역 변수로 모델 파이프라인 저장 | |
sentiment_pipeline = None | |
def load_model(): | |
"""모델 로딩 함수 - 앱 시작시 한번만 실행""" | |
global sentiment_pipeline | |
try: | |
logger.info("🤖 감정 분석 모델을 로딩 중...") | |
# 다국어 지원 모델 사용 (한국어 포함) | |
sentiment_pipeline = pipeline( | |
"sentiment-analysis", | |
model="nlptown/bert-base-multilingual-uncased-sentiment", | |
return_all_scores=False | |
) | |
logger.info("✅ 모델 로딩 완료!") | |
return True | |
except Exception as e: | |
logger.error(f"❌ 모델 로딩 실패: {str(e)}") | |
# 기본 모델로 fallback | |
try: | |
sentiment_pipeline = pipeline("sentiment-analysis") | |
logger.info("✅ 기본 모델로 로딩 완료!") | |
return True | |
except Exception as e2: | |
logger.error(f"❌ 기본 모델도 로딩 실패: {str(e2)}") | |
return False | |
def analyze_sentiment(text): | |
""" | |
감정 분석 메인 함수 | |
Args: | |
text (str): 분석할 텍스트 | |
Returns: | |
str: 분석 결과 (마크다운 형식) | |
""" | |
# 입력 검증 | |
if not text or not text.strip(): | |
return """ | |
### ⚠️ 알림 | |
텍스트를 입력해주세요! 😊 | |
**사용법**: 아래 예시를 참고하거나 직접 문장을 입력해보세요. | |
""" | |
# 텍스트 길이 제한 | |
if len(text) > 1000: | |
return """ | |
### ❌ 오류 | |
텍스트가 너무 깁니다. 1000자 이하로 입력해주세요. | |
""" | |
try: | |
logger.info(f"📝 분석 시작: {text[:50]}...") | |
# AI 모델로 감정 분석 | |
result = sentiment_pipeline(text)[0] | |
# 결과 처리 | |
label = result['label'] | |
confidence = result['score'] | |
logger.info(f"🎯 분석 결과: {label} ({confidence:.3f})") | |
# 라벨 매핑 (다국어 모델 지원) | |
emoji, korean_label, description = map_sentiment_label(label, confidence) | |
# 결과 포맷팅 | |
result_markdown = f""" | |
### {emoji} 감정 분석 결과 | |
**🎭 감정**: {korean_label} | |
**📊 확신도**: {confidence:.1%} | |
**🔍 상세**: {description} | |
--- | |
**⚙️ 모델 정보**: {label} (원본) | |
**⏰ 분석 완료**: ✅ | |
""" | |
return result_markdown | |
except Exception as e: | |
logger.error(f"💥 분석 중 오류: {str(e)}") | |
return f""" | |
### ❌ 분석 오류 | |
죄송합니다. 분석 중 오류가 발생했습니다. | |
**오류 내용**: {str(e)} | |
**해결 방법**: | |
- 텍스트를 다시 확인해주세요 | |
- 잠시 후 다시 시도해주세요 | |
- 문제가 지속되면 새로고침 해주세요 | |
""" | |
def map_sentiment_label(label, confidence): | |
""" | |
모델 라벨을 한국어로 매핑하고 설명 추가 | |
Args: | |
label (str): 모델 출력 라벨 | |
confidence (float): 신뢰도 점수 | |
Returns: | |
tuple: (이모지, 한국어 라벨, 설명) | |
""" | |
label_str = str(label).upper() | |
# 긍정 감정 (POSITIVE, 4 STARS, 5 STARS) | |
if any(pos in label_str for pos in ['POSITIVE', '5', '4']): | |
if confidence > 0.8: | |
return "😍", "매우 긍정적", "아주 좋은 감정이 느껴집니다!" | |
else: | |
return "😊", "긍정적", "좋은 감정이 느껴집니다." | |
# 부정 감정 (NEGATIVE, 1 STAR, 2 STARS) | |
elif any(neg in label_str for neg in ['NEGATIVE', '1', '2']): | |
if confidence > 0.8: | |
return "😢", "매우 부정적", "강한 부정적 감정이 느껴집니다." | |
else: | |
return "😟", "부정적", "부정적 감정이 느껴집니다." | |
# 중립 감정 (3 STARS, NEUTRAL) | |
else: | |
return "😐", "중립적", "특별한 감정이 느껴지지 않습니다." | |
def create_interface(): | |
"""Gradio 인터페이스 생성""" | |
# CSS 스타일링 | |
custom_css = """ | |
.gradio-container { | |
font-family: 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif; | |
max-width: 900px; | |
margin: 0 auto; | |
} | |
.main-header { | |
text-align: center; | |
padding: 20px; | |
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
color: white; | |
border-radius: 10px; | |
margin-bottom: 20px; | |
} | |
.footer-info { | |
text-align: center; | |
margin-top: 20px; | |
padding: 15px; | |
background-color: #f8f9fa; | |
border-radius: 8px; | |
font-size: 14px; | |
color: #6c757d; | |
} | |
""" | |
# 인터페이스 생성 | |
with gr.Blocks( | |
css=custom_css, | |
theme=gr.themes.Soft( | |
primary_hue="blue", | |
secondary_hue="purple", | |
neutral_hue="gray" | |
), | |
title="🎭 AI 감정 분석기" | |
) as demo: | |
# 헤더 | |
gr.HTML(""" | |
<div class="main-header"> | |
<h1>🎭 AI 감정 분석기</h1> | |
<p>트랜스포머 기반 다국어 감정 분석 도구</p> | |
</div> | |
""") | |
# 메인 인터페이스 | |
with gr.Row(): | |
with gr.Column(scale=1): | |
text_input = gr.Textbox( | |
placeholder="감정을 분석할 텍스트를 입력하세요...\n예: '오늘 정말 좋은 하루였어요!'", | |
lines=4, | |
label="📝 텍스트 입력", | |
max_lines=8 | |
) | |
analyze_btn = gr.Button( | |
"🔍 감정 분석하기", | |
variant="primary", | |
size="lg" | |
) | |
clear_btn = gr.Button( | |
"🗑️ 초기화", | |
variant="secondary" | |
) | |
with gr.Column(scale=1): | |
result_output = gr.Markdown( | |
value=""" | |
### 👋 환영합니다! | |
왼쪽에 텍스트를 입력하고 **'감정 분석하기'** 버튼을 클릭하면 | |
AI가 텍스트의 감정을 분석해드립니다. | |
**✨ 특징**: | |
- 🌍 다국어 지원 (한국어 최적화) | |
- 🎯 높은 정확도 | |
- ⚡ 빠른 분석 속도 | |
""", | |
label="🎯 분석 결과" | |
) | |
# 예시 데이터 | |
gr.Markdown("### 📚 예시 문장들") | |
examples = gr.Examples( | |
examples=[ | |
["오늘 정말 기분이 좋아요! 🌟"], | |
["이 영화는 정말 지루하고 재미없어요 😴"], | |
["그냥 평범한 하루였어요."], | |
["허깅페이스 워크숍 너무 재미있어요! 🚀"], | |
["AI 기술이 정말 신기하고 놀라워요 🤖"], | |
["이번 프로젝트는 완전 실패했어요... 😞"], | |
["새로운 도전이 기대되고 설레어요!"], | |
["날씨가 흐리고 우울해요"], | |
["친구들과 함께한 시간이 행복했어요"], | |
["시험 결과가 걱정돼요"] | |
], | |
inputs=text_input, | |
label="클릭하면 자동 입력됩니다" | |
) | |
# 이벤트 핸들러 | |
analyze_btn.click( | |
fn=analyze_sentiment, | |
inputs=text_input, | |
outputs=result_output | |
) | |
clear_btn.click( | |
fn=lambda: ("", """ | |
### 👋 환영합니다! | |
왼쪽에 텍스트를 입력하고 **'감정 분석하기'** 버튼을 클릭하면 | |
AI가 텍스트의 감정을 분석해드립니다. | |
"""), | |
outputs=[text_input, result_output] | |
) | |
# Enter 키로도 분석 가능 | |
text_input.submit( | |
fn=analyze_sentiment, | |
inputs=text_input, | |
outputs=result_output | |
) | |
# 푸터 정보 | |
gr.HTML(""" | |
<div class="footer-info"> | |
<p>🤖 <strong>Powered by</strong>: Hugging Face Transformers + Gradio</p> | |
<p>📊 <strong>Model</strong>: BERT Multilingual Sentiment Analysis</p> | |
<p>💡 <strong>Tips</strong>: 한국어, 영어, 기타 언어 모두 지원합니다!</p> | |
</div> | |
""") | |
return demo | |
def main(): | |
"""메인 실행 함수""" | |
print("🎭 AI 감정 분석기 시작!") | |
# 모델 로딩 | |
if not load_model(): | |
print("❌ 모델 로딩 실패! 앱을 시작할 수 없습니다.") | |
return | |
# 인터페이스 생성 | |
demo = create_interface() | |
# 개발 환경 vs 배포 환경 설정 | |
if os.getenv("SPACE_ID"): # Hugging Face Spaces에서 실행 중 | |
print("🚀 Hugging Face Spaces에서 앱을 시작합니다...") | |
demo.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=False # Spaces에서는 자동으로 공개됨 | |
) | |
else: # 로컬에서 실행 중 | |
print("💻 로컬 환경에서 앱을 시작합니다...") | |
demo.launch( | |
share=True, # 로컬에서는 공개 링크 생성 | |
server_port=7860 | |
) | |
if __name__ == "__main__": | |
main() |