tidytext
와 caret
tidytext
와 caret
을 활용하여 자연어 텍스트 분류기를 제작하여 본다. 이를 위해서 Disasters on Social Media에 올라온 데이터를 바탕으로 예측모형 개발을 시작한다. 더불어 figure eight 웹사이트에 흥미로운 데이터셋도 많이 올라와 있다.
관련 데이터를 재난(disaster)와 관련이 있는지 없는지 분류하기 위해서 예측변수와 텍스트에 대한 전처리 작업을 수행한다.
library(tidyverse)
library(tidytext)
library(caret)
raw_dat <- read_csv("data/socialmedia-disaster-tweets-DFE.csv")
raw_dat %>%
count(choose_one)
# A tibble: 3 x 2
choose_one n
<chr> <int>
1 Can't Decide 16
2 Not Relevant 6187
3 Relevant 4673
clean_df <- raw_dat %>%
filter(choose_one != "Can't Decide") %>%
mutate(id = `_unit_id`,
disaster = choose_one == "Relevant",
text = str_replace_all(text, " ?(f|ht)tp(s?)://(.*)[.][a-z]+", "")) %>%
select(id, disaster, text)
clean_df %>%
count(disaster) %>%
mutate(pcnt = n / sum(n))
# A tibble: 2 x 3
disaster n pcnt
<lgl> <int> <dbl>
1 FALSE 6187 0.570
2 TRUE 4673 0.430
count_df <- clean_df %>%
unnest_tokens(word, text) %>%
anti_join(stop_words, by = "word") %>%
count(id, word, sort = TRUE)
words_10 <- count_df %>%
group_by(word) %>%
summarise(n = n()) %>%
filter(n >= 10) %>%
select(word)
dtm_df <- count_df %>%
right_join(words_10, by = "word") %>%
bind_tf_idf(word, id, n) %>%
cast_dtm(id, word, tf_idf)
meta <- tibble(id = as.numeric(dimnames(dtm_df)[[1]])) %>%
left_join(clean_df[!duplicated(clean_df$id), ], by = "id")
caret
예측모형caret
팩키지를 활용하여 훈련/시험 데이터로 분할시키고 나서 설명변수와 예측변수로 나누는 작업을 수행하고 3가지 모형 아키텍처에 적합시켜 정확도를 기준으로 가장 성능이 좋은 예측모형을 선정한다.
library(caret)
library(extrafont)
loadfonts()
# 2. 예측모형 -----
## 2.1. 훈련/시험 데이터 분할 ------
y_index <- createDataPartition(meta$disaster, times =1, p=0.7, list=FALSE)
train_df <- dtm_df[y_index, ] %>% as.matrix() %>% as.data.frame()
test_df <- dtm_df[-y_index, ] %>% as.matrix() %>% as.data.frame()
response_train <- meta$disaster[y_index]
## 2.2. 모형 개발/검증 데이터셋 준비 ------
trctrl <- trainControl(method = "none")
## 2.2. 모형 개발/검증 데이터셋 준비 ------
library(doSNOW)
library(tictoc)
# 실행시간
tic()
cl <- makeCluster(4, type = "SOCK")
registerDoSNOW(cl)
svm_mod <- train(x = train_df,
y = as.factor(response_train),
method = "svmLinearWeights2",
trControl = trctrl,
tuneGrid = data.frame(cost = 1,
Loss = 0,
weight = 1))
nb_mod <- train(x = train_df,
y = as.factor(response_train),
method = "naive_bayes",
trControl = trctrl,
tuneGrid = data.frame(laplace = 0,
usekernel = FALSE,
adjust = FALSE))
rf_mod <- train(x = train_df,
y = as.factor(response_train),
method = "ranger",
trControl = trctrl,
tuneGrid = data.frame(mtry = floor(sqrt(dim(train_df)[2])),
splitrule = "gini",
min.node.size = 1))
Growing trees.. Progress: 51%. Estimated remaining time: 30 seconds.
65.87 sec elapsed
# 4. 모형 비교평가-----
svm_pred <- predict(svm_mod, newdata = test_df)
svm_cm <- confusionMatrix(svm_pred, as.factor(meta[-y_index, ]$disaster))
nb_pred <- predict(nb_mod, newdata = test_df)
nb_cm <- confusionMatrix(nb_pred, as.factor(meta[-y_index, ]$disaster))
rf_pred <- predict(rf_mod, newdata = test_df)
rf_cm <- confusionMatrix(rf_pred, as.factor(meta[-y_index, ]$disaster))
mod_results <- rbind(svm_cm$overall,
nb_cm$overall,
rf_cm$overall) %>%
as.data.frame() %>%
mutate(model = c("SVM", "Naive-Bayes", "Random forest"))
mod_results %>%
ggplot(aes(model, Accuracy)) +
geom_point() +
scale_y_continuous(labels = scales::percent, limits=c(0,1)) +
geom_hline(yintercept = mod_results$AccuracyNull[1], color = "red") +
labs(x="", y="정확도") +
theme_minimal(base_family = "NanumGothic")