All Articles

파이썬 머신러닝 #4 - 스팀잇 글 감정 분류하기

안녕하세요. @anpigon 입니다.

이번에는 간단한 텍스트 감정 분류기를 만들어 보겠습니다. 참고로 구글 검색하면 파이썬 머신러닝 관련 자료가 넘치도록 많습니다. 구글에서 마음에 드는 예제를 하나 가져왔습니다.

아래 예제는 https://stevenloria.com/simple-text-classification/를 참고 했습니다.



TextBlob 라이브러리 설치

Textblob는 텍스트 정보를 처리하는 파이썬 라이브러리다. 품사 태깅, 명사구 추출, 감정 분석 및 분류, 번역 등과 같은 일반적인 자연 언어 처리(Natural Language Processing, NLP)를 위한 간단한 API를 제공한다.

설치하기
$ pip install -U textblob nltk



TextBlob 라이브러리가 제공하는 기능 중에 어썸(Awesome)한 기능 하나를 소개한다. 나는 언어를 알아내는 기능과 번역기능이 정말 마음에 들었다. 아래 예제를 보자. 나머지 기능들은 [여기]에서 볼 수 있다.

Detect Language(언어 알아내기)

어떤 국가의 언어로 작성된 텍스트인지 판단한다. 한글은 ko로 나온다. 언어 코드는 구글에서 제공하는 Adwords Api 문서에서 확인 가능하다.


Translate(번역하기)

한글로 작성된 텍스트를 영어로 번역한다.




텍스트 감정 분류기 만들기

간단한 문장을 학습하여 긍정(pos)과 부정(neg)을 분류하는 단순한 분석기를 구현해보자. 먼저 textblob.classifiers를 임포트(import)하고 학습 데이터 세트과 테스트 데이터 세트를 만든다.

from textblob.classifiers import NaiveBayesClassifier

train = [
    ('I love this sandwich.', 'pos'),
    ('This is an amazing place!', 'pos'),
    ('I feel very good about these beers.', 'pos'),
    ('This is my best work.', 'pos'),
    ("What an awesome view", 'pos'),
    ('I do not like this restaurant', 'neg'),
    ('I am tired of this stuff.', 'neg'),
    ("I can't deal with this", 'neg'),
    ('He is my sworn enemy!', 'neg'),
    ('My boss is horrible.', 'neg')
]
test = [
    ('The beer was good.', 'pos'),
    ('I do not enjoy my job', 'neg'),
    ("I ain't feeling dandy today.", 'neg'),
    ("I feel amazing!", 'pos'),
    ('Gary is a friend of mine.', 'pos'),
    ("I can't believe I'm doing this.", 'neg')
]


NaiveBayesClassifier 객체에 학습 데이터를 입력하고 분류기 cl를 생성한다.

cl = NaiveBayesClassifier(train)


이제 학습된 분류기에서 classify(text) 함수를 사용하면 텍스트를 분류할 수 있다. 학습 데이터에 없는 텍스트를 입력하여 분류해보자.

cl.classify("Their burgers are amazing")  # "pos"
cl.classify("I don't like their pizza.")  # "neg"


아래와 같이 TextBlob 객체를 사용하여 여러 문장으로 구성된 텍스트도 분류할 수 있다.

from textblob import TextBlob
blob = TextBlob("The beer was amazing. "
                "But the hangover was horrible. My boss was not happy.",
                classifier=cl)
blob.classify()  # "neg"          


TextBlob에서 문장을 분리하여 각 문장을 개별적으로 분류 할 수도 있다.

for sentence in blob.sentences:
    print(sentence)
    print(sentence.classify())
# "pos", "neg", "neg"


테스트 데이터 세트로 정확도를 확인해보자. 테스트 데이터의 정확도가 83%로 나왔다. 사실 테스트 데이터양이 적어서 높은 정확도가 나왔다.

cl.accuracy(test)
# 0.8333333333333334


show_informative_features를 실행하여 분류기가 어떤 기준으로 분류하는지 살펴보자.

cl.show_informative_features(5)
# Most Informative Features
#             contains(my) = True              neg : pos    =      1.7 : 1.0
#             contains(an) = False             neg : pos    =      1.6 : 1.0
#             contains(my) = False             pos : neg    =      1.3 : 1.0
#          contains(place) = False             neg : pos    =      1.2 : 1.0
#             contains(of) = False             pos : neg    =      1.2 : 1.0

텍스트에 “my” 단어를 포함(True)하고 있으면 부정(neg)일 확률이 1.7이다. 그리고 “place”라는 단어를 포함하지 않으면(False) 부정(neg)일 확률이 1.2이다.




한글 텍스트 감정 분류기 만들기

위에서 만든 분류기를 한글에도 적용해보자. TextBlob는 형태소 분석 기능을 포함하고 있지만, 한글 형태소 분석은 하지 못한다. 그래서 한글 형태소 분석을 위해 은전한닢(Mecab)을 사용하였다. 한글 형태소 분석기를 사용하지 않아도 분류기가 작동은 한다. 하지만 형태소 분석을 하지 않으면 조사, 명사, 동사 구분이 되지 않아 분류기의 정확도가 떨어진다.

아래와 같이 학습 데이터와 테스트 데이터를 준비한다.

from konlpy.tag import Mecab
pos_tagger = Mecab()

train = [
    ('나는 이 샌드위치를 정말 좋아해.', '긍정'),
    ('정말 멋진 곳이에요!', '긍정'),
    ('나는 이 맥주들이 아주 좋다고 생각해요.', '긍정'),
    ('이것은 나의 최고의 작품입니다.', '긍정'),
    ("정말 멋진 광경이다", "긍정"),
    ('난 이 식당 싫어', '부정'),
    ('난 이게 지겨워.', '부정'),
    ("이 문제는 처리할 수 없습니다.", "부정"),
    ('그는 나의 불구대천의 원수이다.', '부정'),
    ('내 상사는 끔찍해.', '부정')
]

test = [
    ('맥주가 좋았습니다.', '긍정'),
    ('난 내 일을 즐기지 않는다', '부정'),
    ('오늘은 기분이 안 좋아요.', '부정'),
    ('놀라워요!', '긍정'),
    ('네드는 나의 친구입니다.', '긍정'),
    ('제가 이렇게 하고 있다니 믿을 수가 없어요.', '부정')
]


우선 차이를 확인하기 위해서 형태소 분석없이 분류기를 사용해보자.

정확도를 확인해보자. 확률이 50%다. 거의 찍는 수준이다.


이제 형태소 분석기를 적용하고 다시 정확도를 확인해보자.

train 데이터의 텍스트를 형태소를 분석하여 train_data를 생성한다. 한글 단어 뒤에 태그를 붙여줘야 분류기가 정확하게 분류할 수 있다.

train_data = [(['/'.join(token) for token in pos_tagger.pos(sentence)], result) for [sentence, result] in train]
train_data
결과


테스트 데이터를 사용하여 정확도를 다시 확인해보자. 66%가 나왔다. 형태소 분석을 하고 나니 정확도가 높아졌다.

test_data = [(['/'.join(token) for token in pos_tagger.pos(sentence)], result) for [sentence, result] in test]

cl.accuracy(test_data)
결과


show_informative_features를 실행하여 분류 기준을 살펴보자.

cl.show_informative_features()
결과

“정말”이라는 부사가 있으면 부정일 확률이 2.2이다. 그리고 “좋” 형용사가 있으면 긍정일 확률이 1.6이다.




스팀잇 감정 분류하기

마지막으로 스팀잇에 작성한 글 10개를 가져와서 긍정/부정을 분류해보자.

from steem import Steem 
from steem.blog import Blog

# 스팀잇에서 작성한 글 10개 가져오기
username = 'anpigon'
b = Blog(username)
posts = b.take(10)

for post in posts:
  if post.body != "":
    author = post.author
    permlink = post.permlink
    title = post.title
    body = post.body.replace('\n', '')
    
    # 분류기로 분류하기
    result = cl.classify(['/'.join(token) for token in pos_tagger.pos(body)])
    print("(%s) %s(/@%s/%s)" % (result, title, author, permlink));
결과

결과가 좋지 않다. 긍정보다는 부정으로 분류가 많이 되었다.ㅠㅠ


분류기에 학습 데이터를 추가로 더 입력하고 정확도를 계산해보자.

new_train = [
  ('나는 내 꿈을 믿는다', '긍정'),
  ('나는 매일 최선을 다하고 있다', '긍정'),
  ('나는 있는 그대로의 나를 사랑한다', '긍정'),
  ('나는 내 삶을 100% 책임진다', '긍정'),
  ('가장 좋은 일은 아직 생기지 않았다', '긍정'),
  ('나는 매일 나의 삶에 감사한다', '긍정'),
  ('새로나온 휴대폰은 배터리 교체가 되지 않아 불편하다', '부정'),
  ('이번에 나온 영화 너무 재밌다. 주말에 또 보고 싶다.', '긍정'),
  ('나의 아버지는 이해가 안된다', '부정'),
	('나는 어머니와 있을 때 퉁명해진다', '부정'),
	('나는 어머니와 있을 때 불편할 때가 있다.', '부정')
]
new_train_data = [(['/'.join(token) for token in pos_tagger.pos(sentence)], result) for [sentence, result] in test]
cl.update(new_train_data)

cl.accuracy(test_data)
결과

정확도는 66%로 변함이 없다. 하지만 학습 데이터가 많아지면 정확도가 올라갈 것이다.


마지막으로 학습 데이터가 추가된 분류기로 스팀잇 글을 다시 분류해보자.

결과

긍정으로 분류된 글의 비율이 늘었다. 하지만 여전히 부정으로 분류된 글이 많다.ㅠ


여기까지 읽어주셔서 감사합니다.


이전글
Published 19 Sep 2018

안피곤의 블로그입니다.