Skip to main content

문서 분류

감성 분석 (Sentiment Analysis)

  • 문장의 감성(sentiment) 을 예측하는 문제 (주로 긍정/부정 분류)
    • 감성의 예: 긍정/부정, 찬성/반대, 좋다/싫다 등
  • 기쁨, 슬픔, 분노 등 더 세분화된 감정은 정서(emotion) 분석이라고 함
  • 주요 방법:
    • 사전 기반 (Dictionary-based): 감성 단어 사전을 이용
    • 머신 러닝 기반 (Machine Learning-based): 분류 모델을 학습

사전 기반 감성 분석

  • 단어별로 긍정 또는 부정을 미리 분류하여 감성 사전(lexicon) 을 구축
    • 긍정 단어 예: 좋다, 만족한다, 뛰어나다, 최고, 추천
    • 부정 단어 예: 나쁘다, 불만이다, 뒤떨어진다, 최악, 비추
  • 문장 내 긍정 단어 수와 부정 단어 수를 세어서, 더 많은 쪽으로 문장의 감성을 결정
  • 장점:
    • 도메인 지식이 있다면 비교적 만들기 쉬움
    • 복잡한 통계/머신러닝 지식 없이 간단히 구현 가능
  • 단점:
    • 사전 구축에 많은 노력 필요 (도메인 특화 단어, 신조어 등)
    • 문맥, 어순, 반어법 등을 고려하기 어려움 (예: "전혀 나쁘지 않다")

머신 러닝을 이용한 감성 분석

  • 감성 레이블(긍정/부정)이 부착된 텍스트 데이터를 이용해 분류 모델을 학습
  • 장점:
    • 일반적으로 사전 기반보다 성능이 높음
    • 도메인 배경 지식이 덜 필요
    • 모델에 따라 문맥, 어순 등을 고려 가능 (예: RNN, Transformer)
    • (활용) 학습된 모델을 이용해 자동으로 감성 사전을 구축할 수도 있음
  • 단점:
    • 레이블된 학습 데이터가 대량으로 필요
    • 학습 데이터에 레이블을 붙이는 작업(annotation)에 많은 노력 필요
    • 적절한 모델 선택 및 학습에 통계/머신러닝 지식 필요

이항 분류 (Binary Classification)

  • 분류(Classification): 범주형 변수를 종속 변수(yy)로 하는 지도 학습
  • 이항 분류: 종속 변수가 두 개의 범주로만 구성된 경우
    • 예: (긍정/부정), (스팸/정상 메일), (합격/불합격), (고객 유지/이탈)
  • 데이터 공간에서 두 범주를 나누는 경계선(decision boundary) 을 찾는 문제로 볼 수 있음
  • 주요 기법: 로지스틱 회귀, 판별 분석, 서포트 벡터 머신(SVM), 나이브 베이즈 등
  • 다항 분류(Multiclass Classification): 종속 변수가 3개 이상의 범주로 구성된 경우. 경계선이 여러 개가 되므로 더 복잡.

감성분석 실습: 전처리

  • 파일 열기 (예: yelp 리뷰 데이터)
    import pandas as pd
    df = pd.read_excel('yelp.xlsx')
  • 문서 단어 행렬(DTM) 만들기 (CountVectorizer 사용)
    from sklearn.feature_extraction.text import CountVectorizer
    # max_features: 사용할 최대 단어 수, stop_words: 불용어 제거
    cv = CountVectorizer(max_features=1000, stop_words='english')
    dtm = cv.fit_transform(df.review) # review 컬럼 사용
  • 독립 변수(x)와 종속 변수(y) 지정
    X = dtm              # DTM을 입력 특성(x)으로 사용
    y = df.sentiment # sentiment 컬럼(긍정=1, 부정=0 가정)을 목표(y)로 사용

과적합 (Overfitting) / 과소적합 (Underfitting)

  • 과대적합 (Overfitting): 모델이 학습 데이터의 노이즈까지 과도하게 학습하여, 실제 패턴(일반성)보다 복잡해지는 현상. 학습 데이터 성능은 좋지만 새로운 데이터(테스트 데이터) 성능은 나쁨.
  • 과소적합 (Underfitting): 모델이 너무 단순하여 학습 데이터의 실제 패턴조차 제대로 학습하지 못하는 현상. 학습 데이터 및 테스트 데이터 모두 성능이 나쁨.
  • (이미지: Underfitting(직선 경계), Appropriate-fitting(적절한 곡선 경계), Overfitting(매우 구불구불한 경계) 시각화)
  • (이미지: 배달앱 리뷰 예시 - 과도한 긍정 표현 -> 사장님 댓글 "평점 점수가 떨어집니다" -> 오버피팅 비유)

감성분석 실습: 데이터 분할

  • 모델의 일반화 성능을 평가하기 위해 데이터를 훈련(training) 데이터테스트(test) 데이터로 분할
  • 모델은 훈련 데이터만을 이용하여 학습(fit)
  • 학습된 모델의 성능은 테스트 데이터를 이용하여 평가(score)
    • 과소적합: 훈련 점수, 테스트 점수 모두 낮음
    • 과대적합: 훈련 점수는 높지만, 테스트 점수는 낮음
    from sklearn.model_selection import train_test_split

    # 데이터를 훈련 세트(80%)와 테스트 세트(20%)로 분할
    # random_state: 동일한 분할 결과를 얻기 위한 씨앗값
    X_train, X_test, y_train, y_test = train_test_split(
    X, y,
    test_size=0.2, # 테스트 세트 비율 = 20%
    random_state=42 # 결과 재현을 위한 씨앗값 설정
    )

조건부 확률 (Conditional Probability)

  • 어떤 사건 B가 일어났다는 조건 하에서 다른 사건 A가 일어날 확률 P(AB)P(A|B) : "B가 주어졌을 때 A의 확률"
  • 감성 분석 예시:
    • P("Good"XY=1)P(\text{"Good"} \in X | Y = 1): 문장이 긍정(Y=1Y=1)일 때, 그 문장에 "Good"이라는 단어가 포함될 확률
    • P(Y=1"Good"X)P(Y = 1 | \text{"Good"} \in X): 문장에 "Good"이라는 단어가 포함되었을 때, 그 문장이 긍정(Y=1Y=1)일 확률 (우리가 예측하고 싶은 것)

베이즈 정리 (Bayes' Theorem)

  • 두 확률 변수의 사전 확률과 사후 확률 사이의 관계를 나타내는 정리
  • 조건부 확률의 정의로부터 유도됨: P(AB)=P(AB)P(B)P(A|B) = \frac{P(A \cap B)}{P(B)}
  • 베이즈 정리: P(AB)=P(BA)P(A)P(B)P(A|B) = \frac{P(B|A)P(A)}{P(B)}
    • P(AB)P(A|B): 사후 확률 (사건 B 발생 후 A의 확률)
    • P(A)P(A): 사전 확률 (사건 B 발생 전 A의 확률)
    • P(BA)P(B|A): 가능도 (Likelihood, 사건 A가 주어졌을 때 B의 확률)
    • P(B)P(B): 증거 (Evidence, 사건 B의 확률)

나이브 베이즈 (Naïve Bayes) 분류기

  • 베이즈 정리에 기반한 확률적 분류 알고리즘
  • 주어진 문서 dd가 특정 클래스 c1c_1에 속할 확률 P(c1d)P(c_1|d)를 계산
  • 베이즈 정리 적용: P(c1d)=P(dc1)P(c1)P(d)P(c_1|d) = \frac{P(d|c_1)P(c_1)}{P(d)}
  • 분류 규칙: P(c1d)P(c_1|d)P(c2d)P(c_2|d) 등을 비교하여 가장 확률이 높은 클래스로 분류
    • P(d)P(d)는 모든 클래스에 대해 동일하므로, P(dc1)P(c1)P(d|c_1)P(c_1) 를 최대화하는 클래스를 찾으면 됨
  • 나이브(Naïve, 순진한) 가정: 문서 dd를 구성하는 각 단어 w1,w2,...,wnw_1, w_2, ..., w_n 들이 주어진 클래스 c1c_1 하에서 서로 조건부 독립(conditionally independent) 이라고 가정 P(dc1)=P(w1,w2,...,wnc1)P(w1c1)P(w2c1)...P(wnc1)P(d|c_1) = P(w_1, w_2, ..., w_n|c_1) \approx P(w_1|c_1)P(w_2|c_1)...P(w_n|c_1)
    • 이 가정 덕분에 계산이 매우 간단해짐 (실제로는 단어 간 독립 가정이 틀릴 수 있지만 종종 잘 작동함)
  • 최종적으로 비교할 값: P(c1d)P(c1)i=1nP(wic1)P(c_1|d) \propto P(c_1) \prod_{i=1}^{n} P(w_i|c_1)

사이킷런을 이용한 나이브 베이즈 분류

  • Bernoulli Naive Bayes 임포트 (DTM이 0 또는 1 값(출현 여부)일 때 주로 사용, Count 기반일 때도 사용 가능)
    from sklearn.naive_bayes import BernoulliNB
    # MultinomialNB는 단어 빈도(count)에 더 적합
    # from sklearn.naive_bayes import MultinomialNB
  • 모델 객체 생성
    model = BernoulliNB()
    # model = MultinomialNB()
  • 훈련 데이터로 모델 학습
    model.fit(X_train, y_train)

나이브 베이즈 평가

  • 학습된 모델을 이용해 훈련 데이터에 대한 정확도(accuracy) 평가
    train_accuracy = model.score(X_train, y_train)
    print(f"Train Accuracy: {train_accuracy:.4f}")
  • 학습된 모델을 이용해 테스트 데이터에 대한 정확도 평가 (일반화 성능)
    test_accuracy = model.score(X_test, y_test)
    print(f"Test Accuracy: {test_accuracy:.4f}")

단어별 확률 비율 계산

  • 나이브 베이즈 모델이 학습한 각 단어별 로그 확률 정보를 이용
  • 특정 단어가 긍정 문장에 나올 로그 확률 vs 부정 문장에 나올 로그 확률 비교 logP(wordpositive)P(wordnegative)=logP(wordpositive)logP(wordnegative)\log \frac{P(word | \text{positive})}{P(word | \text{negative})} = \log P(word | \text{positive}) - \log P(word | \text{negative})
  • 이 값이 크면 긍정 문장에서 상대적으로 더 자주 등장하는 단어, 작으면(음수면) 부정 문장에서 더 자주 등장하는 단어
    # model.feature_log_prob_[0]: 각 단어가 부정(클래스 0)에서 나올 로그 확률
    # model.feature_log_prob_[1]: 각 단어가 긍정(클래스 1)에서 나올 로그 확률
    log_ratio = model.feature_log_prob_[1] - model.feature_log_prob_[0]

    prob_df = pd.DataFrame({
    '단어': cv.get_feature_names_out(), # CountVectorizer에서 단어 목록 가져오기
    '비율': log_ratio
    })

긍부정 단어 보기

  • 계산된 로그 확률 비율을 기준으로 정렬하여 각 클래스를 대표하는 단어 확인
  • 긍정 문장에서 상대적으로 많이 나오는 단어 (비율 값이 큰 단어)
    # 비율 기준 내림차순 정렬 후 상위 10개
    positive_words = prob_df.sort_values('비율', ascending=False).head(10)
    print("Top 10 Positive words:\n", positive_words)
  • 부정 문장에서 상대적으로 많이 나오는 단어 (비율 값이 작은 단어)
    # 비율 기준 오름차순 정렬 후 상위 10개
    negative_words = prob_df.sort_values('비율', ascending=True).head(10)
    print("Top 10 Negative words:\n", negative_words)