차밍이
[R, kaggle실습] 영화 평점 감성 분석 - 텍스트 전처리에서 베이지안 필터기 적용까지 본문
R을 사용한 감성분석
-
케글의 영화 평점 분석 데이터를 사용하여 감성 분석을 진행하겠습니다. 텍스트 전처리, 데이터 전처리, 자연어 처리(NLP), 텍스트 마이닝을 진행한 후 정제된 데이터를 바탕으로 베이지안 필터기와 의사결정나무 모델을 사용해서 데이터를 분류해보겠습니다. 그 결과 어느 정도의 정확도를 보여줄 것인지 확인해보겠습니다.
-
작업환경은 구글의 코랩(Colab)에서 진행하였습니다. 데이터의 크기가 커서 좀 더 나은 환경을 위해서 코랩에서 진행했습니다. 집 컴퓨터의 메모리와 cpu가 좋다면 로컬에서 작업하시는 것이 좋을 수도 있습니다.
(Colab에서 R 사용하기)
라이브러리 및 환경설정
# 파이썬에서 R 사용하는 환경설정
import rpy2
%load_ext rpy2.ipython
# 구글 드라이브와 연동하기
from google.colab import drive
drive.mount('/content/gdrive')
# 폴더 이동
cd /content/gdrive/My Drive/R_class
# 필요한 패키지 설치
%%R
install.packages('readr')
install.packages("tidytext")
install.packages("stringr")
install.packages("tm")
install.packages("tidyr")
install.packages("SnowballC")
install.packages("textdata")
install.packages('e1071')
install.packages('gmodels')
install.packages('stats')
install.packages('arules')
install.packages('C50')
# 라이브러리 불러오기
library(readr)
library(gmodels)
library(e1071)
library(stringr)
library(tm)
library(tidytext)
library(tidyr)
library(textdata)
library(SnowballC)
library(tidytext)
library(arules)
library(stats)
library(C50)
# 데이터 불러오기
imdb <- read_csv("IMDB Dataset.csv")
# 데이터가 너무 많아 10000개만 랜덤 추출해서 진행
set.seed(123)
num <- 10000
db_index <- sample(nrow(imdb), num)
imdb <- imdb[db_index,]
# label을 범주화 Negative와 Positive로 구분
imdb$sentiment <- factor(imdb$sentiment)
원본 데이터가 50,000개 정도 되는데 전부 돌리게 되면 속도도 매우 느린 데다가 중간에 메모리 부족으로 뻑나기도 합니다. 20,000개 까지 진행해봤는데 나중에 필터기 돌릴 때, 진짜 느려요. 연습 삼아하는 것이므로 5,000~10,000개 정도가 좋은 것 같습니다.
데이터 전처리
데이터 전처리는 2번에 나눠서 진행했습니다.
- 텍스트 자체에서 기본적인 전처리를 진행
- 벡터화와 Corpus(말뭉치) 단위로 나눈 후 전처리 진행
텍스트 자체 데이터 전처리
%%R
imdb$review <- sapply(imdb$review, FUN = function(x){
# 축약어 전처리
x <- str_replace_all(x,"'ll",' will')
x <- str_replace_all(x,"'m",' am')
x <- str_replace_all(x,"n't",' not')
x <- str_replace_all(x,"'ve",' have')
x <- str_replace_all(x,"'s",' ')
# Be 동사 어원처리
x <- str_replace_all(x,"( am)|( are)|( is)|( were)|( been)|( was)",' be')
# br태그 제거
x <- str_replace_all(x, "<[[:graph:]]{0,} /{0,}>", " ")
x <- tolower(x)
# 이상글자 제거
x <- str_replace_all(x, "\\u0096", " ")
x <- str_replace_all(x, "\\u0085", " ")
})
- 축약어 제거
I've been there. I'll be there later soon. I'm so sorry. Please Don't be upset. It's not my fault.
이러한 문장이 있다고 할 때, 그냥 특수문자를 제거하면 I've
->
Ive
또는 I ve
형태가 되어 의미 없는 garbage들을 만들어냅니다. 따라서 이런 축약된 부분들을 모두 풀어서 쓸 수 있도록 전처리를 했습니다. 이후 Be
동사의 어원으로 만들어 주었습니다. 사실 Be
동사 자체에는 큰 의미가 없으므로 제거해도 무방할 것이라? 조심스럽게 예측해봅니다. 아마 어차피 불용어 처리할 때, stopwords
에서 걸러집니다.
<br />
태그 제거
데이터가 파싱 해온 데이터인 것으로 생각됩니다. html
태그들이 같이 달려있는 것이 있어서 제거해주었습니다. 다른 태그들이 들어오는 경우도 있으니 일반화해서 제거할 수도 있겠습니다.
- 이상 글자 제거
인코딩 문제 같은데 계속 해결하는데 결국 실패했습니다. 스페인어 같은 데서 나오는 문자 위에 점찍어진 단어들 혹시 아시나요? Résumé *요론 단어들, *café 이런 단어들이 읽어올 때 펣, 쉪, 팩 이런 식으로 깨져서 나오는 경우가 있습니다. 저는 결국 해결하지 못하고 해당 단어들이 출력되는 방식을 찾아서 직접 제거했습니다.
말뭉치 단위 전처리
%%R
# 벡터화 후 코퍼스 생성
corpus <- VCorpus(VectorSource(imdb.c$review))
# 2차 전처리에 필요한 함수
replacePunctuation <- function(x){
x <- gsub(pattern = "[[:punct:]]+", " ", x)
}
replaceNumber <- function(x){
x <- gsub(pattern = "[[:digit:]]+", " ", x)
}
# tm_map 함수로 각 말뭉치들에 함수 적용
# 숫자 제거
corpus <- tm_map(corpus,content_transformer(replaceNumber))
# 특수문자 제거
corpus <- tm_map(corpus,content_transformer(replacePunctuation))
# 불용어 제거
corpus <- tm_map(corpus,removeWords, stopwords('en'))
# 불필요 빈칸 제거
corpus <- tm_map(corpus, stripWhitespace)
# 어근 동일화
corpus <- tm_map(corpus, stemDocument)
# DTM 생성
dtm <- DocumentTermMatrix(corpus)
- 특수문자 제거, 숫자 제거
tm_map
에서 removePunctuation
등의 함수를 사용할 수 있지만, 그런 경우에 Umm....No good
이런 단어가 UmmNo good
이런 방식으로 붙는 경우가 발생합니다. 그래서 공백으로 대체했습니다.
- DTM 생성
전처리를 모두 마친 후 DocumentTermMatrix를 생성합니다. DTM을 만들면 Column에 각 단어들이 모두 들어가고 Matrix의 데이터 값으로는 해당 열(해당 문장)에 해당 열의 단어가 몇 번 쓰였는지가 들어갑니다. 이로써 준비는 끝났습니다.
데이터셋 분류
%%R
# sample 비율 70%
smp_ratio <- 0.7
smp_size <- smp_ratio * num
# train/test data set 분리
set.seed(123)
train_index <- sample(num, smp_size)
train <- dtm[train_index,]
test <- dtm[-train_index,]
# 빈도수 10회 이상인 단어들로 제한
freq_words <- findFreqTerms(train,10)
train <- train[,freq_words]
test <- test[,freq_words]
# label 설정
train_labels <- imdb[train_index,]$sentiment
test_labels <- imdb[-train_index,]$sentiment
# 분류기 적용을 위한 카테고리화
convert_counts<- function(x){
x<-ifelse(x>0, 'Y', "N")}
train <- apply(train,MARGIN = 2,FUN = convert_counts)
test <- apply(test,MARGIN = 2,FUN = convert_counts)
- Train_data_split
sampling을 통해서 70%의 데이터를 뽑아 train데이터로 사용하고 나머지 30%를 결과를 확인하는 test데이터로 사용하였습니다.
- 빈도수 10회 제한
각 단어들의 빈도수가 최소 10회 이상 사용되는 단어들로 제한하였습니다. 그렇지 않으면 생긴 단어들의 수만큼의 열의 수가 증가합니다. 그만큼 차원의 수가 증가하는 것입니다. 단어 자체가 10번도 나오지 않았다는 것은 10000개의 행에서 0.001% 의 확률도 안 되는 것이므로 모델에 영향을 미치지 않으면서도 전체적인 성능을 높일 것이라 생각했습니다.
- 카테고리화
나이브 베이즈 필터를 적용하기 전 데이터를 펙터화, 카테고리화 해야 하니다. train과 test에 있는 데이터는 dtm으로부터 발생된 데이터로 value들은 모두 int 형태의 숫자입니다. 해당 단어가 몇 번 나왔는지 말해주는 값입니다. 분류기에서는 해당 값이 몇 인 지보 다는 해당 단어가 나왔을 때 그 문장이 positive였는지 혹은 negative였는지가 중요하죠. 이것이 베이즈 이론의 핵심이기 때문입니다. 따라서, 펙터화를 위한 함수를 만들고 Y or N
로 구분하였습니다.
나이브 베이즈 분류기 적용
# 나이브 베이즈 분류기 모델 생성
NB.classifier <- naiveBayes(train, train_labels, laplace = 1)
# test set에 적용해서 예측
pred <- predict(NB.classifier, test)
# 예측 결과 확인
sum(pred == test_labels)*100/length(test_labels)
# CrossTable 확인
CrossTable(pred, test_labels, prop.t=FALSE, prop.r = FALSE, dnn=c('predicted','actual'))
- 모델 생성
train
데이터와 train_labels
정답을 가지고 분류기를 생성합니다. laplace = 1
로 설정합니다. 만약 특정 단어가 만약 한 번도 등장하지 않았을 경우를 생각해보겠습니다. 모델에서 해당 단어가 나올 확률을 0으로 확정 지어버릴 경우 test 데이터에 적용할 때, 그 단어가 나오면 무조건 0으로 판단하게 될 것입니다. 각 단어들 간의 사건이 독립이라고 생각하면 각 확률들이 곱해질 것입니다. 그러면 0이 있으면 무조건 결과값이 0이 나오겠죠. 따라서, 기본적으로 모든 단어가 1건은 있었다는 전제를 두고 들어가는 것입니다. 이를 두고 라플라스 추정량이라고 합니다.
- 모델 적용 및 결과 확인
나이브 베이지안 분류기가 만들어지면 해당 모델을 test데이터에 적용한 후 결과를 얻습니다. 이후 test_labels
와 결과값을 비교하여 Accuracy를 확인합니다.
정확도는 83.6%로 측정되었습니다. negative를 negative로 판단하는 정답률은 85.7%, positive를 positive로 판단하는 정답률은 81.5%의 정답률을 보였습니다. 부정적인 결과를 더 잘 파악하는 것을 확인할 수 있었습니다. 더욱 성능을 높이기 위해서는 전처리 과정이 더 많이 필요할 것으로 생각됩니다. 형태소 분석기가 python에서 사용하는 것 만큼의 성능이 나오지 않는 것같습니다. dtm 내부의 columns를 살펴보면 어떤 단어들로 구성되어 있는지 확인할 수 있었는데, 인터넷 상의 언어이다보니 오타가 많은데 이런 경우를 잘잡지 못하는 것 같습니다.
지금까지 베이즈 필터를 사용해서 영화 댓글의 긍정과 부정을 판별하는 필터기를 만들었습니다. 다음으로는 negative와 positive를 넘어서 감성분석 사전을 활용해서 더 자세한 텍스트 감성분석을 진행해보겠습니다.
베이지안 필터 적용 실습 사례
2020/02/11 - [R 프로그래밍] - [R] 나이브 베이즈를 활용한 스팸메일 분류와 텍스트 마이닝
2020/02/11 - [R 프로그래밍] - [R] 나이브 베이즈 분류(Naive Bayes Classifier) 활용 데이터 분석 및 실습 - 독버섯 분류하기
'R > 데이터 사이언스' 카테고리의 다른 글
[R] 선형회귀 모델과 상관관계를 통한 의료비 예측모델 : P-value와 R-squared (0) | 2020.02.25 |
---|---|
[R] 딥러닝 neuralnet 라이브러리 활용 인공신경망 모델 생성 및 예측 (2) | 2020.02.19 |
[R] 나이브 베이즈를 활용한 스팸메일 분류와 텍스트 마이닝 (1) | 2020.02.11 |
[R] 나이브 베이즈 분류(Naive Bayes Classifier) 활용 데이터 분석 및 실습 - 독버섯 분류하기 (2) | 2020.02.11 |
[R] 머신러닝 - KNN을 사용한 암, 악성 종양 진단모델 (0) | 2020.02.06 |
[R] 연관 분석 Association analysis 과 Pruning (0) | 2020.02.06 |
[R프로그래밍] 실제 데이터를 사용한 데이터프레임과 그래프 활용 (0) | 2020.02.04 |