감정 시스템 로드맵 현실화 - 이미 학습된 7개 감정 모델 반영
This commit is contained in:
parent
cd0f9f5648
commit
f46bdfe399
@ -12,31 +12,35 @@
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: 최소 기능 구현
|
||||
## Phase 1: 7개 감정 기본 구현 (이미 학습 완료)
|
||||
|
||||
### 목표
|
||||
"5개 기본정서로 감정 인식이 작동하는 최소 시스템"
|
||||
"이미 학습된 7개 한국어 감정 모델을 skill-embedding에 통합"
|
||||
|
||||
### 구현 범위
|
||||
```python
|
||||
# 기본정서만 구현
|
||||
BASIC_EMOTIONS = [
|
||||
"joy", # 기쁨
|
||||
"sadness", # 슬픔
|
||||
"anger", # 분노
|
||||
"fear", # 두려움
|
||||
"disgust" # 혐오
|
||||
# AI Hub 데이터로 학습 완료된 7개 감정
|
||||
EMOTIONS = [
|
||||
"fear", # 공포 (기본정서)
|
||||
"surprise", # 놀람 (기본정서)
|
||||
"anger", # 분노 (기본정서)
|
||||
"sadness", # 슬픔
|
||||
"neutral", # 중립
|
||||
"happiness", # 행복
|
||||
"disgust" # 혐오
|
||||
]
|
||||
# 모델 성능: F1 56.3%, Temperature Scaling 1.232
|
||||
|
||||
# 단순 엔트로피 계산
|
||||
# 엔트로피 계산 (7개 감정)
|
||||
def calculate_entropy(probs: List[float]) -> float:
|
||||
"""5개 확률값으로 엔트로피 계산"""
|
||||
"""7개 감정 확률값으로 엔트로피 계산"""
|
||||
return -sum(p * log(p) for p in probs if p > 0)
|
||||
```
|
||||
|
||||
### 기술 스택
|
||||
- **임베딩**: 기존 skill-embedding 서비스 활용 (포트 8502)
|
||||
- **저장**: 기존 ChromaDB 활용
|
||||
- **감정 모델**: klue/bert-base 기반 (이미 학습 완료)
|
||||
- **임베딩**: 기존 skill-embedding 서비스 확장 (포트 8515)
|
||||
- **저장**: 기존 ChromaDB 활용 (메타데이터에 감정 추가)
|
||||
- **의사결정**: ε-greedy (ε=0.1)
|
||||
- **기존 코드**: rb10508_micro의 memory/storage.py 재사용
|
||||
|
||||
@ -45,10 +49,11 @@ def calculate_entropy(probs: List[float]) -> float:
|
||||
- 정확도: 사용자 평가 3.5/5.0
|
||||
- 메모리: 200MB 이내
|
||||
|
||||
### 데이터 준비
|
||||
- 감정당 100개 샘플 (총 500개)
|
||||
- Gemini로 초기 라벨 생성
|
||||
- 수동 검증 20%
|
||||
### 데이터 준비 (완료)
|
||||
- AI Hub 한국어 대화 데이터셋 38,594개 샘플
|
||||
- 7개 감정 균형 분포
|
||||
- 학습/검증/테스트 분할 완료
|
||||
- 클래스 가중치 적용
|
||||
|
||||
### 검증 방법
|
||||
```bash
|
||||
@ -63,18 +68,19 @@ curl -w "@curl-format.txt" http://localhost:8503/analyze
|
||||
```
|
||||
|
||||
### 산출물
|
||||
- [ ] skill-embedding 서비스에 감정 분석 엔드포인트 추가
|
||||
- [ ] 5개 감정 프로토타입 정의
|
||||
- [ ] 기본 엔트로피 계산기
|
||||
- [ ] 기존 ChromaDB 통합 코드
|
||||
- [ ] 최소 테스트 데이터 (100개)
|
||||
- [x] 7개 감정 모델 학습 완료 (training_emotion)
|
||||
- [ ] skill-embedding 서비스에 /analyze_emotion 엔드포인트 추가
|
||||
- [ ] Temperature Scaling 적용 (1.232)
|
||||
- [ ] 엔트로피 계산기 구현
|
||||
- [ ] ChromaDB 메타데이터 통합
|
||||
- [ ] rb10508_micro 연동
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: 성능 최적화
|
||||
## Phase 2: 성능 최적화 및 통합
|
||||
|
||||
### 목표
|
||||
"응답시간 200ms 달성 및 캐싱 시스템 구축"
|
||||
"ONNX 변환, 캐싱 구현, rb10508_micro 완전 통합"
|
||||
|
||||
### 최적화 전략
|
||||
```python
|
||||
@ -115,77 +121,84 @@ stats.sort_stats('cumulative').print_stats(10)
|
||||
- 캐시 적중률: 30%
|
||||
|
||||
### 산출물
|
||||
- [ ] LRU 캐시 시스템
|
||||
- [ ] ONNX 모델 변환 (442MB → 150MB)
|
||||
- [ ] LRU 캐시 시스템 (5분 TTL)
|
||||
- [ ] 배치 처리 API
|
||||
- [ ] ChromaDB 인덱스 최적화
|
||||
- [ ] 성능 모니터링 대시보드
|
||||
- [ ] 프로파일링 리포트
|
||||
- [ ] ChromaDB 감정 메타데이터 인덱싱
|
||||
- [ ] 성능 모니터링 (Grafana)
|
||||
- [ ] rb10508_micro 감정 기반 응답 톤 조정
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: 사회기능 감정 추가
|
||||
## Phase 3: 감정 패턴 분석 및 개인화
|
||||
|
||||
### 목표
|
||||
"9개 감정으로 확장하고 2헤드 구조 도입"
|
||||
"장기 감정 패턴 추적, 사용자별 감정 프로파일 구축"
|
||||
|
||||
### 확장 감정
|
||||
### 감정 패턴 분석
|
||||
```python
|
||||
# 사회기능 추가
|
||||
SOCIAL_EMOTIONS = [
|
||||
"anxiety", # 불안
|
||||
"envy", # 질투
|
||||
"embarrassment", # 당혹
|
||||
"ennui" # 권태
|
||||
]
|
||||
|
||||
# 2헤드 병렬 처리
|
||||
async def two_head_analysis(text: str, context: dict):
|
||||
basic_task = analyze_basic(text) # 100ms 목표
|
||||
social_task = analyze_social(text, context) # 300ms 목표
|
||||
# 시간별 감정 추적
|
||||
class EmotionTracker:
|
||||
def __init__(self, user_id: str):
|
||||
self.user_id = user_id
|
||||
self.history = [] # 시계열 감정 데이터
|
||||
|
||||
basic, social = await asyncio.gather(basic_task, social_task)
|
||||
def track(self, emotion_result: dict):
|
||||
"""감정 결과를 시계열로 저장"""
|
||||
self.history.append({
|
||||
"timestamp": datetime.now(),
|
||||
"emotions": emotion_result["emotions"],
|
||||
"dominant": emotion_result["dominant"],
|
||||
"entropy": emotion_result["entropy"]
|
||||
})
|
||||
|
||||
# 동적 가중치 계산
|
||||
w = calculate_weight(len(text), context)
|
||||
return w * basic + (1-w) * social
|
||||
def get_pattern(self, period: str = "day"):
|
||||
"""일/주/월 단위 감정 패턴 분석"""
|
||||
# 시간대별 주요 감정
|
||||
# 감정 변화 추이
|
||||
# 엔트로피 패턴
|
||||
return analyze_temporal_pattern(self.history, period)
|
||||
```
|
||||
|
||||
### 데이터 확장
|
||||
- 새 감정당 200개 샘플 추가
|
||||
- 총 1,300개 라벨 데이터
|
||||
- 크라우드소싱 활용 검토
|
||||
### 개인화 전략
|
||||
- 사용자별 감정 프로파일 생성
|
||||
- 감정 응답 히스토리 학습
|
||||
- 개인별 감정 임계값 조정
|
||||
- 엔트로피 특이점 활용 (창발적 응답)
|
||||
|
||||
### Thompson Sampling 도입
|
||||
### 엔트로피 기반 의사결정
|
||||
```python
|
||||
class ThompsonSampler:
|
||||
class EntropyBasedDecision:
|
||||
def __init__(self):
|
||||
self.alpha = np.ones(9) # 성공 횟수
|
||||
self.beta = np.ones(9) # 실패 횟수
|
||||
self.entropy_threshold = 2.5 # 특이점 임계값
|
||||
|
||||
def sample(self):
|
||||
"""베타 분포에서 샘플링"""
|
||||
return np.random.beta(self.alpha, self.beta)
|
||||
def should_be_creative(self, entropy: float) -> bool:
|
||||
"""높은 엔트로피일 때 창의적 응답"""
|
||||
return entropy > self.entropy_threshold
|
||||
|
||||
def update(self, action, reward):
|
||||
"""결과에 따라 파라미터 업데이트"""
|
||||
if reward > 0:
|
||||
self.alpha[action] += 1
|
||||
else:
|
||||
self.beta[action] += 1
|
||||
def adjust_response(self, response: str, emotion_result: dict):
|
||||
"""감정에 따른 응답 톤 조정"""
|
||||
if emotion_result["dominant"] == "sadness":
|
||||
return make_empathetic(response)
|
||||
elif emotion_result["dominant"] == "anger":
|
||||
return make_calm(response)
|
||||
elif self.should_be_creative(emotion_result["entropy"]):
|
||||
return make_creative(response)
|
||||
return response
|
||||
```
|
||||
|
||||
### 성능 목표
|
||||
- 기본정서: 100ms
|
||||
- 사회기능: 300ms
|
||||
- 통합 응답: 350ms
|
||||
- 정확도: 4.0/5.0
|
||||
- 패턴 분석: 일 1회 배치 처리
|
||||
- 프로파일 업데이트: 실시간
|
||||
- 감정 히스토리: 30일 보관
|
||||
- 개인화 정확도: 70% 이상
|
||||
|
||||
### 산출물
|
||||
- [ ] 9개 감정 프로토타입
|
||||
- [ ] 2헤드 병렬 처리 시스템
|
||||
- [ ] Thompson Sampling 구현
|
||||
- [ ] 1,300개 라벨 데이터
|
||||
- [ ] A/B 테스트 결과
|
||||
- [ ] 감정 패턴 분석기
|
||||
- [ ] 사용자 감정 프로파일 DB
|
||||
- [ ] 엔트로피 기반 의사결정 모듈
|
||||
- [ ] 시계열 감정 시각화
|
||||
- [ ] 개인화 응답 전략
|
||||
|
||||
---
|
||||
|
||||
|
||||
292
plans/250812_감정시스템_Phase1_실행계획.md
Normal file
292
plans/250812_감정시스템_Phase1_실행계획.md
Normal file
@ -0,0 +1,292 @@
|
||||
# 감정 시스템 Phase 1 실행 계획
|
||||
|
||||
작성일: 2025년 8월 12일
|
||||
작성자: Claude (51123 서버)
|
||||
상태: 실행 준비 완료
|
||||
|
||||
## 1. 현황 분석 결과
|
||||
|
||||
### 1.1 기존 자산
|
||||
- **학습 완료 모델**: training_emotion에 7개 감정 모델 (klue/bert-base)
|
||||
- **모델 성능**: F1 56.3%, Temperature Scaling 1.232
|
||||
- **인프라**: skill-embedding 서비스 운영 중 (FastAPI, ONNX Runtime)
|
||||
- **여유 자원**: CPU 0.05%, 메모리 873MB/2GB
|
||||
|
||||
### 1.2 기술 스택 확인
|
||||
```
|
||||
✅ transformers 4.45.2 설치됨
|
||||
✅ FastAPI 구조 확립
|
||||
✅ ONNX Runtime 지원
|
||||
✅ ChromaDB 메타데이터 저장 가능
|
||||
```
|
||||
|
||||
## 2. 감정 모델 상세
|
||||
|
||||
### 2.1 7개 감정 체계
|
||||
```python
|
||||
emotions = [
|
||||
'fear', # 공포 (즉발적 기본정서)
|
||||
'surprise', # 놀람 (즉발적 기본정서)
|
||||
'anger', # 분노 (즉발적 기본정서)
|
||||
'sadness', # 슬픔 (감정적 반응)
|
||||
'happiness', # 행복 (감정적 반응)
|
||||
'disgust', # 혐오 (감정적 반응)
|
||||
'neutral' # 중립 (균형 상태)
|
||||
]
|
||||
```
|
||||
|
||||
### 2.2 로빙 철학과의 연결
|
||||
- **기본정서 (100ms)**: fear, surprise, anger → 즉발 반응
|
||||
- **사회기능 (500ms)**: sadness, happiness, disgust → 숙고된 반응
|
||||
- **엔트로피 특이점**: 높은 엔트로피 시 창발적 응답
|
||||
|
||||
## 3. 구현 아키텍처
|
||||
|
||||
### 3.1 서비스 확장 (Option A - 선택)
|
||||
```
|
||||
skill-embedding (포트 8515)
|
||||
├── /embed (기존)
|
||||
└── /analyze_emotion (신규)
|
||||
├── 입력: text, user_id (optional)
|
||||
├── 처리: 7개 감정 분석
|
||||
└── 출력: emotions, dominant, entropy, confidence
|
||||
```
|
||||
|
||||
### 3.2 API 설계
|
||||
```json
|
||||
// Request
|
||||
POST /analyze_emotion
|
||||
{
|
||||
"text": "오늘 프로젝트가 실패했어요",
|
||||
"user_id": "optional_for_caching"
|
||||
}
|
||||
|
||||
// Response
|
||||
{
|
||||
"emotions": {
|
||||
"fear": 0.15,
|
||||
"surprise": 0.05,
|
||||
"anger": 0.25,
|
||||
"sadness": 0.35,
|
||||
"neutral": 0.10,
|
||||
"happiness": 0.05,
|
||||
"disgust": 0.05
|
||||
},
|
||||
"dominant": "sadness",
|
||||
"entropy": 2.31,
|
||||
"confidence": 0.35,
|
||||
"processing_time_ms": 87
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 실행 계획
|
||||
|
||||
### Phase 1-A: 기본 통합 (Day 1-2)
|
||||
|
||||
#### 51123 서버 작업
|
||||
```bash
|
||||
# 1. 모델 준비
|
||||
sudo mkdir -p /opt/models/emotion
|
||||
sudo cp -r /path/to/training_emotion/outputs/aihub-7emotions-complete/* \
|
||||
/opt/models/emotion/
|
||||
sudo chown -R admin:admin /opt/models/emotion
|
||||
```
|
||||
|
||||
#### 로컬 개발자 작업
|
||||
```python
|
||||
# 2. emotion_analyzer.py 작성
|
||||
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
||||
import torch
|
||||
import numpy as np
|
||||
|
||||
class EmotionAnalyzer:
|
||||
def __init__(self, model_path="/opt/models/emotion"):
|
||||
self.model = AutoModelForSequenceClassification.from_pretrained(model_path)
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(model_path)
|
||||
self.temperature = 1.232 # from calibration
|
||||
self.emotions = ['fear', 'surprise', 'anger', 'sadness',
|
||||
'neutral', 'happiness', 'disgust']
|
||||
|
||||
async def analyze(self, text: str) -> dict:
|
||||
inputs = self.tokenizer(text, return_tensors="pt",
|
||||
max_length=512, truncation=True)
|
||||
|
||||
with torch.no_grad():
|
||||
logits = self.model(**inputs).logits
|
||||
probs = torch.softmax(logits / self.temperature, dim=-1)
|
||||
|
||||
emotion_scores = {
|
||||
emotion: float(probs[0][i])
|
||||
for i, emotion in enumerate(self.emotions)
|
||||
}
|
||||
|
||||
dominant = max(emotion_scores, key=emotion_scores.get)
|
||||
entropy = self._calculate_entropy(list(emotion_scores.values()))
|
||||
|
||||
return {
|
||||
"emotions": emotion_scores,
|
||||
"dominant": dominant,
|
||||
"entropy": entropy,
|
||||
"confidence": emotion_scores[dominant]
|
||||
}
|
||||
|
||||
def _calculate_entropy(self, probs):
|
||||
probs = np.array(probs)
|
||||
probs = probs[probs > 0]
|
||||
return -np.sum(probs * np.log(probs))
|
||||
```
|
||||
|
||||
#### 51124 서버 작업
|
||||
```yaml
|
||||
# 3. docker-compose.yml 수정
|
||||
services:
|
||||
skill-embedding:
|
||||
volumes:
|
||||
- /opt/models:/opt/models:ro
|
||||
environment:
|
||||
- EMOTION_MODEL_PATH=/opt/models/emotion
|
||||
```
|
||||
|
||||
### Phase 1-B: 최적화 (Day 3-4)
|
||||
|
||||
#### ONNX 변환
|
||||
```bash
|
||||
# 로컬 개발자
|
||||
python convert_to_onnx.py \
|
||||
--model_path /opt/models/emotion \
|
||||
--output_path /opt/models/emotion-onnx \
|
||||
--optimize
|
||||
```
|
||||
|
||||
#### 캐싱 구현
|
||||
```python
|
||||
from functools import lru_cache
|
||||
import hashlib
|
||||
|
||||
class CachedEmotionAnalyzer(EmotionAnalyzer):
|
||||
@lru_cache(maxsize=1000)
|
||||
def _analyze_cached(self, text_hash: str):
|
||||
# 실제 분석 로직
|
||||
pass
|
||||
|
||||
async def analyze(self, text: str, user_id: str = None):
|
||||
text_hash = hashlib.md5(text.encode()).hexdigest()
|
||||
cache_key = f"{user_id}:{text_hash}" if user_id else text_hash
|
||||
return self._analyze_cached(cache_key)
|
||||
```
|
||||
|
||||
### Phase 1-C: 통합 (Day 5)
|
||||
|
||||
#### rb10508_micro 연동
|
||||
```python
|
||||
# Slack 메시지 처리 시
|
||||
async def process_slack_message(text: str, user_id: str):
|
||||
# 1. 감정 분석
|
||||
emotion_result = await call_emotion_api(text, user_id)
|
||||
|
||||
# 2. ChromaDB 메타데이터 추가
|
||||
metadata = {
|
||||
"user_id": user_id,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"emotions": emotion_result["emotions"],
|
||||
"dominant_emotion": emotion_result["dominant"],
|
||||
"emotional_entropy": emotion_result["entropy"]
|
||||
}
|
||||
|
||||
# 3. 응답 톤 조정
|
||||
response_tone = adjust_tone_by_emotion(emotion_result["dominant"])
|
||||
|
||||
return generate_response(text, tone=response_tone)
|
||||
```
|
||||
|
||||
## 5. 성능 목표 및 측정
|
||||
|
||||
### 5.1 목표 지표
|
||||
- **응답시간**: < 200ms (캐시 미스), < 50ms (캐시 히트)
|
||||
- **정확도**: 체감 정확도 > 70%
|
||||
- **메모리**: < 1.5GB 총 사용량
|
||||
- **처리량**: > 10,000 요청/일
|
||||
|
||||
### 5.2 측정 방법
|
||||
```bash
|
||||
# 성능 테스트
|
||||
curl -w "@curl-format.txt" \
|
||||
-X POST http://localhost:8515/analyze_emotion \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"text":"테스트 메시지"}'
|
||||
|
||||
# 부하 테스트
|
||||
locust -f tests/load_test.py --users 10 --spawn-rate 2
|
||||
```
|
||||
|
||||
## 6. 리스크 관리
|
||||
|
||||
### 6.1 잠재 위험
|
||||
| 위험 요소 | 영향도 | 대응 방안 |
|
||||
|---------|-------|----------|
|
||||
| 모델 크기 (442MB) | 중 | ONNX 변환으로 150MB로 축소 |
|
||||
| 메모리 부족 | 중 | 모델 공유, 캐싱 최적화 |
|
||||
| 낮은 정확도 (56.3%) | 낮 | confidence 임계값 설정 |
|
||||
| Cold start | 낮 | 서비스 시작 시 프리로드 |
|
||||
|
||||
### 6.2 폴백 전략
|
||||
- confidence < 0.3일 때 neutral로 분류
|
||||
- 엔트로피 > 2.8일 때 "복잡한 감정" 표시
|
||||
- 오류 시 감정 분석 스킵하고 진행
|
||||
|
||||
## 7. 작업 분담
|
||||
|
||||
### 51123 서버 (시스템 관리)
|
||||
- [ ] 모델 파일 /opt/models 배치
|
||||
- [ ] 배포 파이프라인 설정
|
||||
- [ ] 시스템 모니터링
|
||||
- [ ] 문서화
|
||||
|
||||
### 로컬 개발자 (코드 구현)
|
||||
- [ ] emotion_analyzer.py 작성
|
||||
- [ ] FastAPI 엔드포인트 추가
|
||||
- [ ] ONNX 변환 스크립트
|
||||
- [ ] rb10508_micro 연동
|
||||
|
||||
### 51124 서버 (서비스 운영)
|
||||
- [ ] Docker 이미지 빌드
|
||||
- [ ] 서비스 배포
|
||||
- [ ] 로그 모니터링
|
||||
- [ ] 성능 측정
|
||||
|
||||
## 8. 검증 계획
|
||||
|
||||
### 8.1 단위 테스트
|
||||
```python
|
||||
def test_emotion_analysis():
|
||||
analyzer = EmotionAnalyzer()
|
||||
result = analyzer.analyze("정말 기쁜 하루였어요!")
|
||||
assert result["dominant"] == "happiness"
|
||||
assert result["confidence"] > 0.5
|
||||
```
|
||||
|
||||
### 8.2 통합 테스트
|
||||
- Slack 메시지 → 감정 분석 → ChromaDB 저장
|
||||
- 다양한 감정 텍스트 100개 테스트
|
||||
- 응답 시간 측정
|
||||
|
||||
## 9. 다음 단계 (Phase 2 준비)
|
||||
|
||||
- 감정 기반 대화 전략 수립
|
||||
- 장기 감정 패턴 분석
|
||||
- 사용자별 감정 프로파일
|
||||
- 멀티모달 감정 분석 (이미지, 음성)
|
||||
|
||||
## 10. 성공 기준
|
||||
|
||||
✅ Phase 1 완료 조건:
|
||||
1. 7개 감정 분석 API 정상 작동
|
||||
2. 평균 응답시간 < 200ms
|
||||
3. rb10508_micro 통합 완료
|
||||
4. ChromaDB 메타데이터 저장 확인
|
||||
5. 100개 테스트 케이스 통과
|
||||
|
||||
---
|
||||
|
||||
**시작일**: 2025년 8월 13일 (예정)
|
||||
**완료일**: 2025년 8월 17일 (목표)
|
||||
Loading…
x
Reference in New Issue
Block a user