일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 구글 BERT의 정석
- 텐서플로2와 머신러닝으로 시작하는 자연어처리
- 은준아 화이팅
- F분포
- rnn
- 모두의 딥러닝
- 기초통계
- 히스토그램
- Pandas
- 차원축소
- 다층 퍼셉트론
- 오래간만에 글쓰네
- 코사인 유사도
- 밑바닥부터 시작하는 딥러닝2
- word2vec
- 최소자승법
- 텍스트 분류
- 밑바닥부터 시작하는 딥러닝
- Django
- 머신러닝
- 가설검정
- 회귀분석
- 자연어 처리
- numpy
- 군집화
- 파이썬 pandas
- 기술통계학
- 감성분석
- student t분포
- 결정계수
- Today
- Total
데이터 한 그릇
텍스트 분석(4) - 감성 분석 본문
- 지도 학습 기반
- 비지도 학습 기반
지도학습 기반 감성 분석 실습 - IMDB 영화평
캐글에 있는 IMDB 영화평을 기반으로 실습을 진행해 보겠다.
import pandas as pd
review_df = pd.read_csv('C:\\Users\\user\\Desktop\\labeledTrainData.tsv\\labeledTrainData.tsv', header = 0, sep="\t", quoting =3)
review_df.head()
print(review_df['review'][0])
먼저 데이터를 불러오고 데이터의 전반적인 모습을 살펴보겠다.
import re
review_df['review'] = review_df['review'].str.replace('<br />',' ')
review_df['review'] = review_df['review'].apply(lambda x : re.sub("[^a-zA-Z]"," ",x))
review 글을 살펴보면, HTML 태그가 담겨 있음을 확인할 수 있다. 이는 크롤링을 통해서 가져올 때 HTML 을 이용하기 때문이다. 따라서 분석에 필요없는 HTML 을 제거해준다. 또한 특수문자에 대해서도 공백으로 바꾸는 클렌징 작업을 진행한다.
(정규식을 사용)
from sklearn.model_selection import train_test_split
class_df = review_df['sentiment']
feature_df = review_df.drop(['id','sentiment'], axis = 1, inplace=False)
X_train, X_test, y_train, y_test = train_test_split(feature_df, class_df, test_size = 0.3, random_state = 156)
X_train.shape, X_test.shape
X_train
데이터를 살펴보면 sentiment 칼럼이 존재한다. 이는 긍정적인 글인지 부정적인 글인지 결과 값이 나타나 있는 칼럼이다. 따라서 이를 target 데이터로 지정한다.
또한 train 데이터를 설정하기 위해서 필요없는 칼럼인 id 와 sentiment 를 제거해준다.
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, roc_auc_score
pipeline = Pipeline([
('cnt_vect', CountVectorizer(stop_words = 'english', ngram_range=(1,2))),
('lr_clf',LogisticRegression(C = 10))])
pipeline.fit(X_train['review'], y_train)
pred = pipeline.predict(X_test['review'])
pred_probs = pipeline.predict_proba(X_test['review'])[:,1]
print(accuracy_score(y_test, pred), roc_auc_score(y_test, pred_probs))
train 데이터들에 대해서 피처 벡터화를 한 이후에 벡터화된 데이터를 모델에 학습시켜야 한다. 이 둘 작업은 fit_transform 작업이 수반되는데 따로 각각의 코드를 작성하지 않고 pipeline 으로 연결하여 학습시키고 변형한다.
먼저 bow 피처 벡터화 방식 중 count 방식을 사용한다.
그리고 최종적으로 모델의 성능을 정확도와 roc_auc_score 을 통해서 살펴본다.
pipeline = Pipeline([
('tfidf_vect',TfidfVectorizer(stop_words = 'english', ngram_range=(1,2)) ),
('lr_clf',LogisticRegression(C= 10))])
pipeline.fit(X_train['review'], y_train)
pred = pipeline.predict(X_test['review'])
pred_probas = pipeline.predict_proba(X_test['review'])[:,1]
print(accuracy_score(y_test, pred), roc_auc_score(y_test,pred_probas))
다음으로는 bow 방식 중, tf-idf 방식을 사용한다. tf-idf 방식도 count 방식과 마찬가지로 빈도 횟수가 높은 단어를 중요도가 높은 단어로 선정하지만, 보편적으로 빈도가 높은 수 밖에 없는 단어에 대해서는 패널티를 부여한다.
성능이 카운트 방식보다 조금 더 높아졌음을 확인할 수 있다.
비지도학습 기반 감성 분석 소개
비지도 감성 분석은 "Lexicon" 을 기반으로 한다. Lexicon 은 일반적으로 어휘집을 의미하지만 더 나은 의미는 감성 사전이다. 감성 사전은 각 단어에 대해서 긍정 또는 부정에 관한 수치를 가지고 있다. 이를 감성 지수라고 부른다. 종합하자면 Lexicon 은 감성 사전을 의미하며, 감성 사전은 어떤 단어에 대한 긍정 지수와 부정 지수 즉, 감성 지수를 가지고 있다.
이러한 Lexicon 의 감성 지수는 단어의 위치나 문맥 그리고 품사에 의해서 결정된다.
=> 비지도 학습 감성 분석은 감성 사전을 통해서 진행된다.
이러한 작업을 위해서 주로 사용되는 도구는 NLTK 패키지이다.
NLTK 패키지의 WORDNET 은 시맨틱 분석을 제공하는 어휘 패키지이다. 시맨틱이란 단어의 문맥상 의미를 의미한다. 예를 들어서 PRESENT 는 현재라는 의미도 있지만 선물이라는 의미도 가지고 있다. WORDNET 은 이런 다양한 시맨틱적 요소들을 정리하여 분석해두었다.
정리해둔 시맨틱적 요소들은 SYNSET 을 통해서 표현된다. SYNSET 은 단순한 하나의 단어가 아니라 그 단어가 가지는 문맥, 시맨틱 정보를 제공하는 WORDNET 의 핵심 개념이다.
=> NLTK WORDNET 은 감성 사전을 제공해준다. 이 사전은 시맨틱적 요소를 가지고 있는데 이는 SYNSET 으로 표현이 된다.
import nltk
from nltk.corpus import wordnet as wn
nltk.download('all')
term = 'present'
synsets = wn.synsets(term)
print(type(synsets))
print(len(synsets))
print(synsets)
#synset 은 단어의 시맨틱적 요소를 표현하기 위한 수단
SYNSET 에 대해서 살펴보기 위해서 단어 PRESENT 에 대해 WORDNET 감성 사전을 통해서 살펴본다.
for synset in synsets:
print('#####Synset name : ', synset.name(), '####') #synset 이름
print('POS : ', synset.lexname()) #단어의 품사
print('Definition : ', synset.definition()) #단어의 시맨틱적 의미
print('Lemmas:', synset.lemma_names()) #단어의 부명제, 다른 대체 단어
한 단어에 대해서 시맨틱적 요소를 표현하기 위해서 다양한 synsets들이 존재한다. 이 synset 하나 하나 품사와 정의 그리고 대체 단어들을 살펴볼 수 있다.
tree = wn.synset('tree.n.01')
lion = wn.synset('lion.n.01')
tiger = wn.synset('tiger.n.02')
cat = wn.synset('cat.n.01')
dog = wn.synset('dog.n.01')
entities = [tree, lion, tiger, cat, dog]
similarities = []
entity_names = [entity.name().split('.')[0] for entity in entities]
for entity in entities:
similarity = [round(entity.path_similarity(compared_entity) ,2) for compared_entity in entities]
similarities.append(similarity)
similarity_df = pd.DataFrame(similarities, columns=entity_names, index = entity_names)
similarity_df
또한 단어간 유사성을 비교할 수 있다.
import nltk
from nltk.corpus import sentiwordnet as swn
#sentiwordnet 은 감성지수(긍정,부정), 객관성지수가 들어있음
senti_synsets = list(swn.senti_synsets('slow'))
print(type(senti_synsets))
print(len(senti_synsets))
print(senti_synsets)
wordnet 사전도 있지만 sentiwordnet 사전을 더 많이 사용한다.
sentiwordnet 사전은 감성 지수와 객관성 지수를 가지고 있다. 감성 지수는 긍적 지수와 부정 지수가 포함되어 있다.
father = swn.senti_synset('father.n.01')
print('긍정 : ', father.pos_score())
print('부정 : ', father.neg_score())
print('객관 : ', father.obj_score())
print('\n')
fabulous = swn.senti_synset('fabulous.a.01')
print('긍정 : ', fabulous.pos_score())
print('부정 : ', fabulous.neg_score())
print('객관 : ', fabulous.obj_score())
명사 father 와 형용사 fabulous 에 대해서 sentiwordnet 을 통해서 감성 지수를 살펴본다. father 는 객관성 지수가 1로 나오고 감성 지수들이 0으로 나왔다. 반대로 fabulous 는 객관성 지수가 0으로 나왔고 나머지 지수들은 긍정이 높에 나왔다.
다음으로 sentiwordnet 을 통해서 위의 영화 감상평에 대한 감성 분석을 진행하도록 하겠다.
하기 전에, 영화 감상평 하나 하나에 대해서, 문장 토큰화와 단어 토큰화를 진행해야만 한다. 그리고 각 문장의 단어들이 어떤 품사를 가지고 있는지 도출해야만 한다. 왜냐하면 이후에 진행할 어근을 찾는 과정에 사용되기도 하며 synset 을 정의할 때 품사를 적어야만 한다. 마지막으로 모든 단어들의 수치를 구한 후에 이를 모두 합산해서 특정 임계값을 넘기면 긍정 넘기지 않으면 부정으로 판단한다. (0,1)
from nltk.corpus import wordnet as wn
#단어의 품사를 반환
def penn_to_wn(tag):
if tag.startswith('J'):
return wn.ADJ
elif tag.startswith('N'):
return wn.NOUN
elif tag.startswith('R'):
return wn.ADV
elif tag.startswith('V'):
return wn.VERB
먼저 단어의 품사를 결정짓는 함수를 구현한다.
from nltk.stem import WordNetLemmatizer
from nltk.corpus import sentiwordnet as swn
from nltk import sent_tokenize, word_tokenize, pos_tag
def swn_polarity(text):
sentiment = 0.0
tokens_count = 0
lemmatizer = WordNetLemmatizer()
raw_sentences = sent_tokenize(text)
for raw_sentence in raw_sentences:
tagged_sentence = pos_tag(word_tokenize(raw_sentence))
# print(tagged_sentence)
for word, tag in tagged_sentence:
wn_tag = penn_to_wn(tag)
if wn_tag not in (wn.NOUN, wn.ADJ, wn.ADV):
continue
lemma = lemmatizer.lemmatize(word, pos=wn_tag)
if not lemma:
continue
synsets = wn.synsets(lemma, pos = wn_tag)
if not synsets:
continue
synset = synsets[0]
swn_synset = swn.senti_synset(synset.name())
sentiment += (swn_synset.pos_score() - swn_synset.neg_score())
tokens_count += 1
if not tokens_count:
return 0
if sentiment >= 0:
return 1
return 0
그리고 데이터를 토큰화 시키고, 어근 추출을 한 이후에 품사 태깅을 하고, 이것들을 바탕으로 synset 을 검색한다. 그리고 검색된 synset 의 감성 지수를 이용하여 최종 감성 지수를 계산한다.
import numpy as np
from sklearn.metrics import accuracy_score, recall_score, precision_score
review_df['preds'] = review_df['review'].apply(lambda x :swn_polarity(x))
y_target = review_df['sentiment'].values()
preds = review_df['preds'].values()
print(accuracy_score(y_target, preds))
print(precision_score(y_target, preds))
print(recall_score(y_target, preds))
앞서 비지도 학습에서 사용된 review_df 를 살펴보면 감정에 대한 결과값들이 존재한다. 이를 이용해서 생성된 모델을 평가한다.
우리가 예측한 결과 값은 preds 칼럼을 생성하여 넣어주고, 타겟 데이터를 본래의 sentiment 칼럼을 이용해 추출한다. 이이처럼 결과 값과 실제 값을 설정했으니 모델을 평가해준다.
'머신러닝 > 텍스트 분석' 카테고리의 다른 글
텍스트 분석(3) - Bag of Words (BOW) (0) | 2021.09.02 |
---|---|
텍스트 분석(2) - 텍스트 전처리, 정규화 (0) | 2021.09.01 |
텍스트 분석(1) (0) | 2021.09.01 |