01. 텍스트 감성 분석 (Sentiment Analysis)
1. 개요 및 시나리오
상황: 수천 건의 리뷰가 매일 쏟아집니다. 별점은 5점인데 리뷰 내용은 “배송이 너무 늦어서 화가 나지만 제품은 좋아요”라면, 이걸 긍정으로 봐야 할까요? 텍스트 속에 숨겨진 **고객의 감정(Sentiment)**을 수치화하여 분석해봅시다.
2. 데이터 준비
raw_reviews_relabeled 테이블의 리뷰 텍스트(review_body)를 분석합니다.
BigQuery (SQL)
from google.cloud import bigquery
import pandas as pd
client = bigquery.Client()3. 키워드 기반 감성 분석
가장 단순한 방법은 “좋은 단어”와 “나쁜 단어”가 있는지 확인하는 것입니다.
❓ 문제 1: 긍정/부정 키워드 찾기
Q. 리뷰 텍스트에 bad, poor, terrible, late 등이 포함되면 ‘Negative’, good, great, excellent 등이 포함되면 ‘Positive’, 나머지는 ‘Neutral’로 분류하세요.
BigQuery (SQL)
Hint: REGEXP_CONTAINS를 사용하면 여러 패턴을 한 번에 찾을 수 있습니다.
정답 코드 보기
SELECT
review_id,
review_body,
CASE
WHEN REGEXP_CONTAINS(LOWER(review_body), r'bad|poor|terrible|late|slow|worst') THEN 'Negative'
WHEN REGEXP_CONTAINS(LOWER(review_body), r'good|great|excellent|love|best') THEN 'Positive'
ELSE 'Neutral'
END as sentiment_category
FROM `your-project-id.retail_analytics_us.raw_reviews_relabeled`
LIMIT 10;4. NLP 라이브러리 활용 (TextBlob)
단순 키워드 매칭은 “Not good”을 “Positive”로 잘못 분류할 수 있습니다. 전문 NLP 라이브러리를 사용하면 문맥을 어느 정도 파악하여 점수()를 줍니다.
❓ 문제 2: 감성 점수 계산 (Polarity Score)
Q. Python의 TextBlob (혹은 BigQuery Remote Function)을 사용하여 리뷰의 감성 점수를 계산하세요.
Pandas (TextBlob)
Pandas: TextBlob(text).sentiment.polarity를 사용합니다.
정답 코드 보기
from textblob import TextBlob
# 감성 점수 계산 함수
def get_sentiment(text):
return TextBlob(str(text)).sentiment.polarity
reviews['sentiment_score'] = reviews['review_body'].apply(get_sentiment)
# 결과 확인
print(reviews[['review_body', 'sentiment_score']].head())
# 평균 점수
print(f"평균 감성 점수: {reviews['sentiment_score'].mean():.4f}")5. 카테고리별 감성 분석 및 시각화
어떤 제품군이 고객 불만이 가장 많은지 찾아봅시다.
❓ 문제 3: 최악의 카테고리 찾기
Q. 계산된 감성 점수를 바탕으로, 카테고리별 평균 감성 점수와 부정적 리뷰(점수 < 0) 비율을 집계 및 시각화하세요.
Pandas (Visualization)
정답 코드 보기
import matplotlib.pyplot as plt
# 집계
cat_summary = reviews.groupby('relabeled_category').agg({
'sentiment_score': 'mean',
'review_id': 'count'
}).reset_index()
# 부정 리뷰 비율 계산
neg_reviews = reviews[reviews['sentiment_score'] < 0].groupby('relabeled_category').size()
total_reviews = reviews.groupby('relabeled_category').size()
cat_summary['neg_ratio'] = (neg_reviews / total_reviews * 100).fillna(0).values
# 시각화 (Scatter Plot)
plt.figure(figsize=(10, 6))
plt.scatter(cat_summary['neg_ratio'], cat_summary['sentiment_score'],
s=cat_summary['review_id']/10, alpha=0.5)
for i, txt in enumerate(cat_summary['relabeled_category']):
plt.annotate(txt, (cat_summary['neg_ratio'][i], cat_summary['sentiment_score'][i]))
plt.xlabel('Negative Review Ratio (%)')
plt.ylabel('Average Sentiment Score')
plt.title('Sentiment Analysis by Category')
plt.axvline(x=30, color='r', linestyle='--') # 30% 이상이면 위험
plt.show()💡 요약
- 키워드 매칭: 빠르고 비용이 없지만 정확도가 떨어짐. “Not bad”를 구분 못함.
- Rule-based NLP (TextBlob): 문법을 고려하여 조금 더 정확함.
- LLM (다음 챕터): “비꼬는 말투”까지 이해하는 최고 수준의 성능.
다음 챕터에서는 Gemini를 사용하여 진짜 사람처럼 텍스트를 이해하고 분류해보겠습니다.