차밍이

[R] 나이브 베이즈를 활용한 스팸메일 분류와 텍스트 마이닝 본문

R/데이터 사이언스

[R] 나이브 베이즈를 활용한 스팸메일 분류와 텍스트 마이닝

2020. 2. 11. 20:15
반응형

나이브 베이즈 분류를 사용해서 미국 고등학생들의 sms 데이터를 분석 해보겠습니다. sms데이터를 분석하기 위해서는 우선 텍스트 데이터를 정리하는 전처리 과정이 필요합니다.

1. 데이터 전처리

  • 기본 데이터 셋팅

    sms_raw<-read.csv('Data/dataset_for_ml/sms_spam_ansi.txt',
                    stringsAsFactors = FALSE)
    sms_raw$type <- factor(sms_raw$type)
    # 데이터를 불러올 떄, 문자메세지는 Factor로 받아오지 않도록하고
    # label은 다시 factor로 변경해줍니다.
    # ham과 spam의 비율을 확인합니다.
    table(sms\_raw$type)
    > ham spam  
    > 4812 747
    
  • 데이터 마이닝

텍스트를 분석하기 위해서 문장을 Corpus로 만들어 주어야합니다. 말뭉치 또는 코퍼스(영어: corpus, 복수형: corpora)는 자연언어 연구를 위해 특정한 목적을 가지고 언어의 표본을 추출한 집합이다 from 위키백과. 간단하게 단어 집합이라고 생각하면됩니다.

코퍼스가 만들어지면 메모리에 데이터가 저장됩니다. 따라서 너무 많은 데이터는 메모리 터짐현상을 발생시킬 수 있습니다. 주의

VCorpus를 사용해서 코퍼스를 만들기 위해서는 먼저 벡터화가 필요합니다. VectorSource함수를 사용해서 text문서를 벡터화합니다.

library(tm) # 텍스트 마이닝을 위한 라이브러리
#           코퍼스 객체생성 <- 벡터화 <- 텍스트 내용
sms_corpus <- VCorpus(VectorSource(sms_raw$text))

 

  • 데이터 전처리를 위한 일련의 과정
# tm_map 함수를 사용해서 각 corpus들에 함수를 적용
# 소문자화
sms_corpus_clean <- tm_map(sms_corpus, tolower)
# 벡터에 문제가 생기는 경우가 간혹 있습니다.
# 그러한 경우에는 `content_transformer`를 적용합니다.
sms_corpus_clean <- tm_map(sms_corpus, content_transformer(tolower))

# 숫자 제거
sms_corpus_clean <- tm_map(sms_corpus_clean, removeNumbers)

# 구두점 
sms_corpus_clean <- tm_map(sms_corpus_clean, removePunctuation)

# 불용어 제거stop words
stopwords()
sms_corpus_clean <- tm_map(sms_corpus_clean,removeWords, stopwords())

# 형태소 분석
library(SnowballC) # 형태소 분석 함수를 위한 패키지
sms_corpus_clean <- tm_map(sms_corpus_clean,stemDocument)

# 추가 여백 제거
sms_corpus_clean <- tm_map(sms_corpus_clean,stripWhitespace)

# 단어 토큰화
sms_dtm <- DocumentTermMatrix(sms_corpus_clean)

이러한 긴 과정은DocumentTermMatrix함수의 옵션을 사용해서 한번에 처리할 수 있습니다.

sms_dtm <- DocumentTermMatrix(sms_corpus,
                   control=list(
                     tolower = TRUE,
                     removeNumbers = TRUE,
                     stopwords = TRUE,
                     removePunctuation = TRUE,
                     stemming = TRUE))

 

 

  • 해당 데이터에 필요한 함수를 만들어서 적용하기
# 적용하고 싶은 함수 제작
replacePunctuation <- function(x){
  gsub(pattern = "[[:punct:]]+", " ", x)
}

# tm_map 함수를 사용해서 만든 함수를 적용
sms_corpus_clean <- tm_map(sms_corpus_clean, content_transformer(replacePunctuation))

 

 

  • 개인적인 생각

전체적인 데이터를 직접 보면서 데이터 전처리를 진행해야 하는 것 같습니다. 실제 데이터를 돌렸을때, removePuctuation을 적용하면 . 마침표가 전부 ""처리가 되어서 없어집니다. 그런 경우 kk..oops!이런 문장이 kkoops가 되어버립니다. 원하지 않는 잘못된 전처리가 발생될 수 있습니다. 따라서 저는 특수문자나 숫자를 제거할 때는 ' '공백으로 대체해 주는 것으 좋은 것 같습니다.

# 특수문자 제거용 함수
replacePunctuation <- function(x){
  gsub(pattern = "[[:punct:]]+", " ", x)
}
# 숫자 제거용 함수
replaceNumber <- function(x){
  gsub(pattern = "[[:digit:]]+", " ", x)
}
# 만든 함수를 적용할 떄는 content_transformer 적용하는 것이 좋음
# 데이터에 적용
sms_corpus_clean <- tm_map(sms_corpus_clean, content_transformer(replacePunctuation))
sms_corpus_clean <- tm_map(sms_corpus_clean, content_transformer(replaceNumber))
# 나머지 전처리는 토큰화 할 때, 옵션으로 적용
sms_dtm<-DocumentTermMatrix(sms_corpus_clean,
                   control=list(
                     tolower = TRUE, # 소문자화
                     stopwords = TRUE, # 불용어 제거
                     stemming = TRUE # 형태소 분석
                   ))

 

 

  • 토큰화 : DocumentTermMatrix

text 메세지의 코퍼스를 토큰화하여 Matrix 형태로 만들어줍니다. 각 단어들의 숫자 만큼의 열이 생성됩니다. 각 행이 문자에 해당되므로, 각 문자에 대해서 모든 text의 단어들 중 해당되는 단어 부분이 숫자로 0 or 1, 2 등으로 나타내어 집니다.

 

 

2. 데이터 나누기

# train data와 test 데이터 나누기
sms_dtm_train <- sms_dtm[1:4167,]
sms_dtm_test <- sms_dtm[4167:5559,]

sms_train_labels <- sms_raw[1:4167,]$type
sms_test_labels <- sms_raw[4167:5559,]$type

너무 많은 종류의 단어들이 만들어지면 그만큼 feature들도 많아집니다. 그러면 베이즈 이론에 따라 확률을 계산할 때, 불필요한 연산들이 많이 들어갑니다. 따라서, 무의미한 단어를 제거하기 위해서 최소 5회 이상 등장한 단어들로 필터를 적용하였습니다.

# 5번 이상 나온 feature 선택
sms_freq_words <- findFreqTerms(sms_dtm_train,5)
# 해당 feature에 해당되는 열만 선택
sms_dtm_freq_train <- sms_dtm_train[,sms_freq_words]
sms_dtm_freq_test <- sms_dtm_test[,sms_freq_words]

3. 나이브 베이즈 분류 적용

 

  • 범주형으로 바꾸기

분류기를 적용하기에 앞서, 해당 값들을 범주화로 바꾸어줍니다. 현재 데이터 값들은 단어가 쓰인 횟수가 들어있습니다. 0이 대부분이고 나머지는 1, 2, ...등으로 다양할 것입니다. 해당 단어가 해당 문장에서 몇 번 사용되었는지 알고싶은 것이 아닙니다. 해당 단어가 사용된 문장이 SPAM or HAM만 궁금합니다.

게다가 분류를 위해서는 int자료형 보다는 factor인 범주형이 분류에 적합하므로 데이터를 범주형으로 바꿔주겠습니다.

# 범주형으로 바꾸기위한 함수 생성
convert_counts<- function(x){ x <- ifelse(x>0, 'YES', "NO")}

# MARGIN=2 열을 기준으로 함수 적용
sms_train <- apply(sms_dtm_freq_train,MARGIN = 2,FUN = convert_counts)
sms_test <- apply(sms_dtm_freq_test,MARGIN = 2,FUN = convert_counts)

 

  • 나이브 베이지안 필터기 적용
library('e1071') # 베이지안 필터 사용을 위한 알고리즘 라이브러리

# 필터 모델 생성
sms_classifier <- naiveBayes(sms_train, sms_train_labels)

# 예측 결과 확인
library(gmodels)
CrossTable(sms_test_pred, sms_test_labels,
           prop.t=FALSE, prop.r = FALSE,
           dnn=c('predicted','actual'))
# 예측 점수 확인
sum(sms_test_pred == sms_test_labels)*100/length(sms_test_labels)
>> 98.20531

98%의 높은 정확도를 가지는 분류기가 완성되었습니다. 꽤 좋은 점수를 받아 기분이 좋습니다. 아무래도 데이터의 상태가 좋다보니 분류가 잘 되는 것 같습니다.

이전에 Dacon에서 KB금융 14회 금융문자 분석 대회에 참여해서 71등 정도 했었습니다. 좋은 등수는 아니지만 처음 데이터 분석을 진행하는 저에게 의미있는 활동이었습니다. 그떄도 베이지안필터를 사용해서 스팸메일 분류를 해보았었는데 좋은 점수가 나오지는 않아서 다른 방식으로 진행하였었습니다.

텍스트 분석은 아무래도 데이터 전처리가 가장 중요한 것 같습니다. 더 성능을 높이기위한 방향을 생각해봐야할 것 같습니다.

 

2020/02/11 - [R 프로그래밍] - [R] 나이브 베이즈 분류(Naive Bayes Classifier) 활용 데이터 분석 및 실습 - 독버섯 분류하기

2020/02/06 - [R 프로그래밍] - [R] 머신러닝 - KNN을 사용한 암, 악성 종양 진단모델

2020/02/06 - [R 프로그래밍] - [R] 연관 분석 Association analysis 과 Pruning

반응형

관련된 글 보기

Comments