언어 전공자의 NLP 로그
감정 태그 추출 코드 구현 본문
1. 문제 인식
요즘 프로젝트를 진행하느라 코딩 테스트를 준비할 시간이 없었다. 코딩 연습을 하는 과정 자체가 프로젝트와 직접적인 연관이 없다고 생각해서 다소 소홀했던 것이 사실이다. 그런데 앱에 사용되는 함수를 구현하는 과정에서 알고리즘적인 접근이 필요한 순간이 발생하면서, '아, 이래서 기본기가 중요하구나'라는 깨달음을 얻었다.
내가 구현하려고 하는 부분은 감정 분석의 결과를 일기장에 보여주는 과정에서 최빈 감정 태그 2개를 추출하는 코드였다. 그런데 단순히 빈도수를 기준으로 내림차순으로 정렬하여 상위 2개 태그를 도출하게 되면 빈도수가 겹치는 경우를 온전히 반영하지 못한다는 문제가 발생한다. 예컨대,
[('기쁨', 5),
('애정', 4),
('혐오', 4),
('중립', 4),
('기대', 3),
('슬픔', 3),
('열정', 1),
('분노', 0),
('우울', 0)]
위와 같은 결과에서는 '기쁨'과 '애정' 2개의 결과를 항상 동일하게 반환하는 것이 정확하지 않을 수 있다. 한 번의 대화 세션을 짧게는 10번, 길게는 15~20번의 유저 input으로 설정한 우리 프로젝트의 성격 상, 이처럼 멀티 태깅으로 나누었을 때 빈도수가 겹치는 경우가 생각보다 자주 발생할 텐데, 조금 더 정확한 결과가 나오려면 적어도 빈도수가 중첩되는 태그에 한해서는 임의 추출로 결과를 반환하는 것이 조금 더 합리적이라는 생각이 들었다.
2. 경우의 수
그렇다면 위처럼 중첩되는 경우는 어떤 것들이 있을까?
1) 최빈 태그 2개 초과
2) 최빈 태그 2개
3) 최빈 태그 1개, 차빈 태그 1개
4) 최빈 태그 1개, 차빈 태그 2개 초과
2번과 3번의 경우는 그냥 내림차순으로 정렬해서 반환해도 큰 문제가 없을 것이다. 문제는 1번과 4번의 경우인데, 특히 4번의 경우, 차빈 태그에서 1개를 임의추출한 결과를 최빈 태그와 함께 반환해주어야 한다는 점 때문에 코드가 쓸 데 없이 길어질 수밖에 없다.
그렇게 주피터 노트북에서 이런저런 테스트를 거친 결과 다음과 같은 코드를 얻어낼 수 있었다.
# 기본적으로 sentiment는 아래와 같이 (감정 태그:빈도수) 쌍으로 이뤄진 dict로 다룬다.
sent = [10, 5, 3, 1, 4, 3, 0, 0, 4, 4]
sentiment = {"total": int(sent[0]), '기쁨':int(sent[1]), '기대':int(sent[2]),
'열정':int(sent[3]), '애정': int(sent[4]), '슬픔':int(sent[5]),
'분노':int(sent[6]), '우울': int(sent[7]), '혐오':int(sent[8]),
'중립':int(sent[9])}
tags = ['total', '기쁨', '기대', '열정', '애정', '슬픔', '분노', '우울', '혐오', '중립']
sent_dict = dict((tags[i], sent[i]) for i in range(1, 10))
sortedSent = sorted(sent_dict.items(), key=lambda x: x[1], reverse=True)
import random
max_val = sortedSent[0][1]
result = []
final = []
# 1차적으로 태그 추출
for tag, val in sortedSent:
if val == max_val:
result.append(tag)
print(result)
# 만약 그 결과가 2개 초과라면 2개 임의 추출
if len(result)>2 :
final = random.sample(result, 2)
# 만약 그 결과가 2개라면 그대로 반환
elif len(result) == 2:
final = result
# 나머지의 경우, 최빈 태그가 1개만 나왔다는 의미이다.
else :
max_val = sortedSent[1][1]
for tag, val in sortedSent[1:]:
if val == max_val:
result.append(tag)
if len(result)>2:
final = [sortedSent[0][0], random.sample(result[1:], 1)[0]]
print(final)
임의 추출을 구현한 부분에서 뭔가 재귀 호출을 사용해볼 수 있을 것도 같은데, 일단은 brute-force로 해결했다. 함수 형태로 깔끔하게 나타낼 수 있다면 좋았겠지만, 그러지 못한 부분이 아쉽다.
다만, 이렇게 최종적으로 2개의 태그를 보여준다 하더라도 유저가 보기에 정확하지 않을 가능성이 있으므로, 추후에는 태그를 변경할 수 있는 기능도 구현하고자 한다.
3. 결과물


위 예시에서도 분노/슬픔이 1개씩 겹쳤지만 임의로 하나를 잘 뽑아냈다.
'프로젝트 - 하루의 일기장' 카테고리의 다른 글
| 텍스트 증강 (EDA)에 따른 분류 모델 성능 비교 (0) | 2023.09.22 |
|---|---|
| KoBERT 8-class sentiment analysis (1) | 2023.09.22 |
| 모델 서빙을 위한 솔루션 탐색 (0) | 2023.09.14 |
| GPT-4 기반 감성 분류 예시 (0) | 2023.09.13 |
| KoBERT + GPT4 생성 문장 감정 분석 (0) | 2023.09.12 |