텍스트 분류(Text Classification)는 주어진 텍스트(e.g. 문장이나 문서)를 미리 정해진 카테고리(범주) 중 하나로 분류하는 작업을 의미합니다.
그 중 가장 대표적인 유즈 케이스가 감정 분석(Sentiment Analysis)입니다.
본 글에서는 Huggingface에 존재하는 모델 2가지로 테스트를 진행했습니다.
- Copycats/koelectra-base-v3-generalized-sentiment-analysis
- yangheng/deberta-v3-base-absa-v1.1
테스트를 위해 복합적인 감정이 섞인 문장을 작성했습니다.
- 음식 맛은 정말 훌륭한데, 가게가 너무 시끄럽고 직원이 불친절해요.
- 분위기나 인테리어는 정말 예쁜데, 가격에 비해 양이 너무 적었어요.
- 기능은 다양한데, 사용법이 너무 복잡해서 오히려 불편해요.
- 배우들 연기는 정말 최고였어요.
- 한번입었는데 옆에 봉제선 다 풀리고 실밥도 계속 나옵니다. 마감 처리 너무 엉망 아닌가요?
- 오랜만에 친구들을 만나서 반가웠지만, 헤어질 때는 너무 아쉬웠어요.
- 영상미는 화려하고 좋았지만, 결말이 너무 허무했습니다.
위 문장의 의미를 조금 더 자세히 살펴보면,
문장의 구조 (A-but-B)
A-but-B의 의미는 A(긍정)이지만 B(부정)이다라는 구조이고, 이런 문장에서 사람들은 보통 B에 더 강한 강조점이나 최종 결론을 둡니다.
- A(긍정): 음식 맛은 정말 훌륭한데
- B(부정): 가게가 너무 시끄럽고 직원이 불친절해요.
위 문장은 "맛은 좋았으나, 시끄럽고 불친절해서 (전반적으로) 별로였다." 라는 뉘앙스를 풍깁니다. (저만 그런가요?)
부정적인 요소의 "양"
단순하게 키워드만 보면,
- 긍정 키워드: 훌륭한데 (1개)
- 부정 키워드: 시끄럽고, 불친절해요. (2개)
일반적인 감정 분석 모델은 문장 전체를 보고 하나의 레이블(긍정/부정)을 결정해야 할 때, 부정적인 요소가 2가지나 명확하게 언급되면 부정으로 판단할 가능성이 큽니다.
감정 분석 모델의 한계
문장을 어떻게 분석하는지는 모델의 수준에 따라 다릅니다.
일반적인 감정 분석(Sentence-level)에서는 문장 전체의 "종합적인" 분위기를 판단하도록 학습됩니다.
이 경우, 긍정적인 면(맛)이 언급되었음에도 부정적인 면(소음, 불친절)이 경험을 망쳤다고 해석하여 "부정"으로 분류할 것입니다.
속성 기반 감정 분석(ABSA - Aspect-Based Sentiment Analysis)에서는 문장을 "주제" 별로 분리하여 각각의 감정을 분석합니다. 만약 위에 언급된 문장을 ABSA 모델로 분석한다면 아래의 결과가 나올 것 입니다.
- 음식 맛: 긍정
- 가게 분위기/소음: 부정
- 직원/서비스: 부정
위 관점에서 2개의 모델을 테스트 해보겠습니다.
Copycats/koelectra-base-v3-generalized-sentiment-analysis 모델 테스트
# import library
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification, TextClassificationPipeline
# load model
tokenizer = AutoTokenizer.from_pretrained("Copycats/koelectra-base-v3-generalized-sentiment-analysis")
model = AutoModelForSequenceClassification.from_pretrained("Copycats/koelectra-base-v3-generalized-sentiment-analysis")
sentiment_classifier = TextClassificationPipeline(tokenizer=tokenizer, model=model)
# target reviews
review_list = [
"음식 맛은 정말 훌륭한데, 가게가 너무 시끄럽고 직원이 불친절해요.",
"분위기나 인테리어는 정말 예쁜데, 가격에 비해 양이 너무 적었어요.",
"기능은 다양한데, 사용법이 너무 복잡해서 오히려 불편해요.",
"배우들 연기는 정말 최고였어요.",
'한번입었는데 옆에 봉제선 다 풀리고 실밥도 계속 나옵니다. 마감 처리 너무 엉망 아닌가요?',
"오랜만에 친구들을 만나서 반가웠지만, 헤어질 때는 너무 아쉬웠어요.",
"영상미는 화려하고 좋았지만, 결말이 너무 허무했습니다."
]
# predict
for idx, review in enumerate(review_list):
pred = sentiment_classifier(review)
print(f'{review}\n>> {pred[0]}')
수행 결과는 아래와 같습니다.
음식 맛은 정말 훌륭한데, 가게가 너무 시끄럽고 직원이 불친절해요.
>> {'label': '0', 'score': 0.9508960247039795}
분위기나 인테리어는 정말 예쁜데, 가격에 비해 양이 너무 적었어요.
>> {'label': '0', 'score': 0.6903115510940552}
기능은 다양한데, 사용법이 너무 복잡해서 오히려 불편해요.
>> {'label': '0', 'score': 0.9847258925437927}
배우들 연기는 정말 최고였어요.
>> {'label': '1', 'score': 0.9640251994132996}
한번입었는데 옆에 봉제선 다 풀리고 실밥도 계속 나옵니다. 마감 처리 너무 엉망 아닌가요?
>> {'label': '0', 'score': 0.9988909363746643}
오랜만에 친구들을 만나서 반가웠지만, 헤어질 때는 너무 아쉬웠어요.
>> {'label': '1', 'score': 0.9475674629211426}
영상미는 화려하고 좋았지만, 결말이 너무 허무했습니다.
>> {'label': '0', 'score': 0.9037595987319946}
yangheng/deberta-v3-base-absa-v1.1 모델 테스트
# import library
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification, TextClassificationPipeline
# load model
tokenizer = AutoTokenizer.from_pretrained("yangheng/deberta-v3-base-absa-v1.1")
model = AutoModelForSequenceClassification.from_pretrained("yangheng/deberta-v3-base-absa-v1.1")
sentiment_classifier = TextClassificationPipeline(tokenizer=tokenizer, model=model)
# target reviews
review_list = [
"음식 맛은 정말 훌륭한데, 가게가 너무 시끄럽고 직원이 불친절해요.",
"분위기나 인테리어는 정말 예쁜데, 가격에 비해 양이 너무 적었어요.",
"기능은 다양한데, 사용법이 너무 복잡해서 오히려 불편해요.",
"배우들 연기는 정말 최고였어요.",
'한번입었는데 옆에 봉제선 다 풀리고 실밥도 계속 나옵니다. 마감 처리 너무 엉망 아닌가요?',
"오랜만에 친구들을 만나서 반가웠지만, 헤어질 때는 너무 아쉬웠어요.",
"영상미는 화려하고 좋았지만, 결말이 너무 허무했습니다."
]
# predict
for idx, review in enumerate(review_list):
pred = sentiment_classifier(review)
print(f'{review}\n>> {pred[0]}')
수행 결과는 아래와 같습니다.
음식 맛은 정말 훌륭한데, 가게가 너무 시끄럽고 직원이 불친절해요.
>> {'label': 'Positive', 'score': 0.7525179386138916}
분위기나 인테리어는 정말 예쁜데, 가격에 비해 양이 너무 적었어요.
>> {'label': 'Positive', 'score': 0.5790202617645264}
기능은 다양한데, 사용법이 너무 복잡해서 오히려 불편해요.
>> {'label': 'Negative', 'score': 0.5635974407196045}
배우들 연기는 정말 최고였어요.
>> {'label': 'Positive', 'score': 0.9434264302253723}
한번입었는데 옆에 봉제선 다 풀리고 실밥도 계속 나옵니다. 마감 처리 너무 엉망 아닌가요?
>> {'label': 'Negative', 'score': 0.9397663474082947}
오랜만에 친구들을 만나서 반가웠지만, 헤어질 때는 너무 아쉬웠어요.
>> {'label': 'Positive', 'score': 0.8371114134788513}
영상미는 화려하고 좋았지만, 결말이 너무 허무했습니다.
>> {'label': 'Positive', 'score': 0.862045168876648}
결론
어떤 모델이 더 정확하다고 생각하시나요?
Copycats/koelectra-base-v3-generalized-sentiment-analysis 모델
이 모델이 복합 감정 문장을 훨씬 더 잘 이해한다고 생각합니다.
- 영상미는... 좋았지만, 결말이... 허무했습니다. -> Negative (정확)
- 음식 맛은... 훌륭한데, 직원이... 불친절해요. -> Negative (정확)
- 분위기나... 예쁜데, 양이... 적었어요. -> Negative (정확)
- 기능은... 다양한데, 사용법이... 불편해요. -> Negative (정확)
문장 앞부분의 긍정적인 칭찬에도 불구하고, 최종적으로 전달하려는 의도(불편함, 불친절함, 양 적음, 허무함)가 부정적이라는 것을 정확하게 포착했다고 생각합니다.
yangheng/deberta-v3-base-absa-v1.1 모델
이 모델은 "A이지만 B이다." 구조의 문맥을 잘 파악하지 못하는 것 같습니다.
- 영상미는... 좋았지만, 결말이... 허무했습니다. -> Positive (부정확)
- 음식 맛은... 훌륭한데, 직원이... 불친절해요. -> Positive (부정확)
- 분위기나... 예쁜데, 양이... 적었어요. -> Positive (부정확)
이 모델은 문장 앞부분에 나오는 훌륭한데, 예쁜데, 좋았지만 등과 같은 긍정적인 단어에 너무 큰 가중치를 두어, 뒤에 오는 부정적인 결론을 놓친다고 생각합니다.
테스트 코드는 https://github.com/monocleface/huggingface-tutorials/blob/main/text-classification/sample_text_sentiment_analysis.ipynb 에 올려두었습니다.
0 Comments:
댓글 쓰기