본문 바로가기
Study/ML&DL

8. 감성분석에 머신 러닝 적용

by GodKim 2020. 3. 3.

 감성 분석(Sentiment Analysis)에 머신 러닝을 적용하여 글쓴이의 성향에 따라 문서를 분류하기.

감성 분석이랑 자연어처리(Natural Language Processing, NLP)의 하위 분야로 의견 분석(Opinion Mining)이라고도 한다. 이 분야는 주로 문서의 성향을 분석하는 것을 주로 다룬다. 예컨데, 특정 상품에 대한 리뷰의 성향을 글쓴이의 감정을 기반으로 문서를 분류하는 것과 같은 작업이다.

 

  • 텍스트 데이터의 정제와 준비하기
  • 텍스트 문서로부터 특성 벡터 구축하기
  • 영화 리뷰를 긍정 또는 부정으로 분류하는 머신 러닝 모델을 훈련하기
  • 외부 메모리 학습을 사용한 대용량 텍스트 데이터셋 다루기
  • 문서를 카테고리로 묶기 위한 문서의 토픽 추론하기

8.1 텍스트 데이터 정제

 8.1.1 타볼 파일 압축 풀기(window)

  7Zip(http://www.7-zip.org)에서 프로그램을 다운받아 압축해제가 가능하다. 혹은 python에서 

import tarfile
with tarfile.open(r'E:\Programming\python\aclImdb_v1\aclImdb_v1.tar.gz', 'r:gz') as tar:
    tar.extractall()

로 압축을 풀 수 있다.

 

 8.1.2 데이터 프레임화하기

  더 간편한 사용을 위해 자료를 데이터 프레임화와 csv파일로 변환 시켜준다.

 

 8.1.3 텍스트 정제하기

  현재의 데이터는 HTML 마크업은 물론 구두점과 같이 글자가 아닌 문자가 다수 포함 되어있다. 이는 감성 분석에 유용하지 않은 데이터이기에 제거를 요한다. 하지만 이모티콘과 같은 경우에는 감성 분석에 유용한 자료가 될 수 있으므로 이모티콘을 제외하고 텍스트 데이터를 정제해보자. 이를 정제하기 위해선 정규 표현식을 사용하면 된다. 

import re
def preprocessor(text):
    text = re.sub('<[^>]*>', '', text) #html 마크업을 삭제하는 정규표현식
    emoticons = re.findall('(?::|;|=)(?:-)?(?:\)|\(|D|P)', text)
    text = (re.sub('[\W]+', ' ', text.lower()) + ' '.join(emoticons).replace('-', ''))
    return text
    
df['review'] = df['review'].apply(preprocessor)

 

8.1.4 문서를 토큰으로 나누기

 문서를 토큰화하는데는 여러가지 방법이 있다. 가장 간단한 방법은 공백을 기준으로 개별 단어를 나누는 방법이다. 하지만 이는 언어학적으로 정확하게 단어를 토큰화했다고 할 수 없다. 예를 들어 'runners like running and thus they run'이라는 문장을 위의 방식대로 공백 기준으로 나누면 running과 run은 같은 뜻이지만 두 가지 토큰으로 분류되는 사태가 발생한다. 이를 방지하기 위해 단어를 어간으로 바꾸는 어간 추출(stemming) 방법이 있다. 어간 추출 알고리즘은 다양한 패키지가 존재한다. 이 책에서는 가장 오래된 PorterStemmer를 사용했지만 그 외에도 LancasterStemmer, SnowBall 등이 존재한다.

 그리고 문서들을 보면 가장 흔히 등장하는 단어들이 자주 나타난다. 이를 불용어(stop-word)라 하며 예컨대 영어에는 is, and, has 등이 있다. 불용어 제거는 tf-idf보다 기본 단어 빈도나 정규화된 단어 빈도를 사용할 때 더 유용하다.

8.2 BoW 모델

 BoW(Bag of Word)란 전체 문서에 대해 고유한 토큰으로 어휘 사전을 생성한 후 특정 문서에 해당 토큰이 얼마나 자주 등장하는지를 헤아려 문서의 특성 벡터를 만드는 것이다. BoW는 단어의 순서들을 고려하지 않고 오로직 빈도 수에만 집중한 것이다. 쉽게 말해 가방 안에 단어들을 넣고 흔들어 섞어 준 다음, 하나씩 꺼내 보는 것이다. 가방에서 특정 단어가 n번 등장했다면 그 가방에는 해당 단어가 총 n번 인 것이다. 코드를 보면서 이해해 보자.

###BoW 만들기
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer

#CountVectorizer은 문서 또는 문장으로 이루어진 텍스트 데이터 배열을 입력 받아 BoW 모델 생성
count = CountVectorizer()
docs = np.array([
        'The sun is shining',
        'The weather is sweet',
        'The sun is shining, the weather is sweet, and one and one is two'])
bag = count.fit_transform(docs)

print(count.vocabulary_)
 => {'and': 0, 'is': 1, 'one': 2, 'shining': 3, 'sun': 4, 'sweet': 5, 'the': 6, 'two': 7, 'weather': 8}

print(bag.toarray())
 => [[0 1 0 1 1 0 1 0 0]
     [0 1 0 0 0 1 1 0 1]
     [2 3 2 1 1 1 2 1 1]]

count.vocabulary_의 결과를 보면 dictionary 형태로 각 단어마다 정수 인덱스가 부여된 것을 확인 할 수 있다. 이를 바탕으로 아래의 bag.toarray()의 값을 확인하면 각 index마다 해당 단어가 문서에 등장하는 빈도수가 얼마나 되는지를 나타내고 있다. 예를 들어 첫 줄은 첫 문장(The sun is shining)을 나타내며 첫 번째 숫자는 and의 빈도수, 두 번째 숫자는 is의 빈도수 ... 이런식으로 나타나는 것이다. 특성 벡터의 이러한 값들을 단어 빈도(term frequency)라 하며 문서 d에 등장한 단어 t의 횟수를 tf(t, d)로 쓴다.

 8.2.1 Tf-Idf

일반적으로 문서에서 자주 등장하는 단어는 판별에 필요한 정보를 가지고 있지 않다. 이 때 특성 벡터의 가중치를 낮추는 기법인 tf-idf(단어 빈도와 역문서 빈도의 곱)을 사용한다. 사이킷런에는 이러한 기법을 사용할 수 있는 TfidfTransformer이라는 클래스가 구현되어있다. 다음 코드를 통해 살펴 보자. 

from sklearn.feature_extraction.text import TfidfTransformer
tfidf = TfidfTransformer(use_idf=True,
                         norm='l2',
                         smooth_idf=True)
np.set_printoptions(precision=2)

print(tfidf.fit_transform(count.fit_transform(docs)).toarray())
  => [[0.   0.43 0.   0.56 0.56 0.   0.43 0.   0.  ]
      [0.   0.43 0.   0.   0.   0.56 0.43 0.   0.56]
 	  [0.5  0.45 0.5  0.19 0.19 0.19 0.3  0.25 0.19]]

결과 값을 확인해 보면 기존에 BoW와는 다르게 3번째 문장에서 가장 많이 등장했던 is의 값이 비교적 낮은 값을 가지는 것을 파악할 수 있다. 

 

8.3 문서 분류를 위한 로지스틱 회귀 모델 훈련

영화 리뷰를 긍정과 부정으로 분류하는 로지스틱 회귀 모델을 훈련 시켜 볼 것이다. 정제된 텍스트 데이터를 2만 5천개는 훈련 세트, 나머지 반은 테스트 세트로 나눈다. 그 후 GridSearchCV를 사용하여 최적의 매개변수를 찾는다.

 찾은 최적의 매개변수를 바탕으로 정확도와 테스트 세트에 대한 분류의 정확도를 출력한다.

 

8.4 외부 메모리 학습

  실제로 데이터를 다루때에는 컴퓨터 메모리를 초과하는 대량의 데이터셋을 다루는 경우가 빈번하다. 외부 메모리 학습(out of core learning)은 데이터셋을 작은 배치(batch)로 나누어 분류기를 점진적으로 학습시키는 방법이다. 이는 이전의 GridSearchCV보다 시간이 훨씬 단축된다. 이 방법에선 전체 어휘 사전을 갖고 있어야 하는 CountVectrorizer나 TfidfVectorizer를 사용할 수 없다. 따라서 데이터 종류에 상관없이 사용할 수 있는 HasingVectorizer를 사용한다. 이를 통해 모델을 훈련 시키면 이전에 오래걸렸던 시간이 1분도 안돼는 시간으로 단축된 것을 확인 할 수있다. 하지만 그리드 서치로 하이퍼파라미터를 튜닝해 달성한 정확도보다 좀 낮은 것을 확인 할 수 있다. 

 

8.5 토픽 모델링(Topic Modeling)

토픽 모델링이란 레이블이 없는 텍스트 문서에 토픽을 할당하는 분야이다. 즉, 문서 집합의 추상적인 주제를 발견하기 위한 통계적 모델 중 하나로, 텍스트 본문의 숨겨진 의미 구조를 발견하기 위해 사용되는 텍스트 마이닝 기법이다.

  8.5.1 잠재 디리클레 할당(Latent Dirichlet Allocation, LDA)

  LDA는 모든 문서가 작성될 때 작성자는  '나는 이 문서를 작성하기 위해서 이런 주제들을 넣을거고, 이런 주제들을 위해서는 이런 단어들을 넣을 거야.' 라는 가정을 바탕으로 작성한다는 것을 전제하고 있다. LDA는 위 전제를 바탕으로 작성된 문서를 역공학적인 방법을 통하여 주제를 뽑아내는 것이다. 

 LDA는 빈도수 기반 표현 방법인 BoW를 사용하기에 단어의 순서는 신경 쓰지 않는 다는 것을 알 수 있다. 

 LDA의 수행 방식은 아래와 같다.

  1. 알고리즘에 토픽 개수(n)를 부여
  2. 모든 단어를 n개 중 하나의 토픽을 랜덤으로 할당
  3. 어떤 문서의 단어의 토픽이 자신은 잘못된 토픽이 할당되있지만 다른 단어의 토픽은 모두 올바르다고 가정한다. 이를 바탕으로 두 가지 기준 (1. 문서에 해당하는 단어들 중  토픽에 해당하는 단어의 비율 / 2. 단어를 갖고 있는 모든 문서들 중 토픽이 할당된 비율)을 참고하여 어떤 토픽을 할당할지를 결정한다. 
  4. 3번을 모든 문서의 모든 단어에 적용시킨다.

 

반응형

댓글