*본 포스팅은 마인드 스케일에 있는 코드를 그대로 사용한 것입니다.
감성사전 만들기
kor_neg_ela_sent_2016-10-27.csv
kor_neg_lasso_sent_2016-10-27.csv
kor_neg_reg_sent_2016-10-27.csv
kor_neg_ridge_sent_2016-10-27.csv
kor_pos_ela_sent_2016-10-27.csv
kor_pos_lasso_sent_2016-10-27.csv
실행 전 option 만지기
- Java option을 줍니다
- Xmx8g는 자바 힙 메모리, Dfile.encoding은 파일 인코딩입니다.
- scipen은 소수점 이하 자리수 출력입니다.
- default는 아마 소수점 이하 6자리수이면 e+06으로 나타납니다.
options(java.parameters=c("-Xmx8g", "-Dfile.encoding=UTF-8"))
options(scipen =100)
KoNLP를 사용하여 형태소 분석하기
- KoNLP는 KAIST의 한나눔 형태소 분석기를 사용합니다.
- 한나눔 형태소분석기 태그표 참조
library(glmnet)
## Loading required package: Matrix
## Loading required package: foreach
## Loaded glmnet 2.0-5
library(tm)
## Loading required package: NLP
library(stringr)
library(KoNLP)
## Loading required package: rJava
## Loading required package: hash
## hash-2.2.6 provided by Decision Patterns
## Loading required package: tau
## Loading required package: Sejong
## Successfully Loaded Sejong Package.
## Checking user defined dictionary!
##
## Attaching package: 'KoNLP'
## The following object is masked from 'package:tau':
##
## is.ascii
ko.words = function(doc){
d = as.character(doc)
d = str_split(d, ' ')[[1]] ## 띄어쓰기(' ')를 기준으로 한 문장을 여러 단어로 나눔
d = paste(d[nchar(d) <= 20], collapse = ' ') ## 각 단어들에서 20자 이하인 것만 선택하여 다시 문장으로 합침
pos = paste(SimplePos09(d))
extracted = str_match(pos, '([가-힣a-zA-Z]+)/[NP]')
keyword = extracted[,2]
keyword[!is.na(keyword)]
}
- mc.cores=1을 주는 이유는 감성사전 분석을 위한 함수가 이미 병렬 처리를 하여, 이중 병렬처리는 시간이 더 걸린다고 마인드스케일 강의에서 말했습니다.
options(mc.cores=1)
text <- read.delim("c:/users/suli91/Desktop/1. facebook messenger/com.facebook.orca_reviews_2.tsv",header=F,stringsAsFactors = F,sep="\t",quote="")
head(text)
## V1 V2 V3 V4
## 1 "2015년 12월 12일 조준모 51
## 2 "2015년 12월 12일 Google 사용자 55 ㅗ농어ㅓ
## 3 "2015년 12월 12일 김예도 51 전화가안되요
## 4 "2015년 12월 13일 la dong g. 55
## 5 "2015년 12월 12일 최혜은 55
## 6 "2015년 12월 12일 이예린 55 페메좋은데
## V5
## 1 업데이트하고나서 이모티콘 로딩이 안되서 안보이네요...... 제폰이 이상한건가요?? 전체 리뷰",,,
## 2 ㅗ농어ㅓ 전체 리뷰",,,
## 3 전화가안되요 원랴 뚜루루루룩 뚜루루루룩 되야되는데 뚝뚝뚝 뚝뚝뚝 이래요 소리가 커졌다가 작아졌다가 전체 리뷰",,,
## 4 자동 엎데이트 요청 전체 리뷰",,,
## 5 렉이 지리다든 전체 리뷰",,,
## 6 페메좋은데 암호걸수있게해주세요..♥ 전체 리뷰",,,
- 해당 파일이 제가 좀 잘못 수집해서 전처리를 해야 합니다.
text$V3<-text$V3-50
text$V5<-gsub("전체 리뷰","",text$V5)
text$V5<-gsub("ㅡ","",text$V5)
text$V5<-gsub("ㅠ","",text$V5)
text$V5<-gsub("\",,,","",text$V5)
text$V1<-gsub("\"","",text$V1)
head(text)
## V1 V2 V3 V4
## 1 2015년 12월 12일 조준모 1
## 2 2015년 12월 12일 Google 사용자 5 ㅗ농어ㅓ
## 3 2015년 12월 12일 김예도 1 전화가안되요
## 4 2015년 12월 13일 la dong g. 5
## 5 2015년 12월 12일 최혜은 5
## 6 2015년 12월 12일 이예린 5 페메좋은데
## V5
## 1 업데이트하고나서 이모티콘 로딩이 안되서 안보이네요...... 제폰이 이상한건가요??
## 2 ㅗ농어ㅓ
## 3 전화가안되요 원랴 뚜루루루룩 뚜루루루룩 되야되는데 뚝뚝뚝 뚝뚝뚝 이래요 소리가 커졌다가 작아졌다가
## 4 자동 엎데이트 요청
## 5 렉이 지리다든
## 6 페메좋은데 암호걸수있게해주세요..♥
- 긍/부정을 나누기 위해 평점이 3, 즉 중립인 리뷰를 제외합니다
- 이후 4,5점을 1로, 1,2점을 0으로 변환합니다.
- train/test 셋을 구분하기 위해 sample을 사용합니다.
- Weight는 TF-IDF를 사용합니다.
- 별도의 stopwords는 없습니다.
text <- subset(text,text$V3!=3)
text$V3<-ifelse(text$V3>3,1,0)
train.num<-sample(nrow(text),nrow(text)*0.9)
#regression sentiment dic
corpus1<- Corpus(VectorSource(text$V5[train.num]))
dtm <- DocumentTermMatrix(corpus1,
control=list(tokenize = ko.words,
tolower=T,
removePunctuation=T,
removeNumbers=T,
weighting=weightTfIdf))
X <- as.matrix(dtm)
Y <- text$V3[train.num]
- 일반 회귀 분석 방법
res.lm<-glmnet(X,Y,family="binomial",lambda=0)
coef.lm<-coef(res.lm)[,1]
pos.lm<-coef.lm[coef.lm>0]
neg.lm<-coef.lm[coef.lm<0]
pos.lm<-sort(pos.lm,decreasing=T)
neg.lm<-sort(neg.lm)
head(pos.lm)
## 메신저누르기전까지는 낮은점수주는건처음인데 데이터낭비
## 45.18993 44.54343 38.70563
## 빵빵하고 몇몇이모티콘이안떠요 입력창
## 27.42731 25.68217 25.33979
- lasso 분석
- 단어 수를 많이 줄여줌
- 의미가 비슷한 단어가 있을 수가 있음 -> 이 중 둘 중에 하나만 선택함
- alpha값이 1
set.seed(227)
res.lasso <- cv.glmnet(X,Y,family = "binomial",alpha=1,
nfolds = 4, type.measure = "class")
plot(res.lasso)
#최적 lambda 값
print(log(res.lasso$lambda.min))
## [1] -5.188857
plot(res.lasso$glmnet.fit, xvar = "lambda")
coef.lasso <- coef(res.lasso, s = "lambda.min")[,1]
pos.lasso <- coef.lasso[coef.lasso>0]
neg.lasso <- coef.lasso[coef.lasso<0]
pos.lasso <- sort(pos.lasso,decreasing = T)
neg.lasso <- sort(neg.lasso)
- ridge 분석
- 의미가 비슷한 단어가 있을 수가 있음 -> 둘다 선택
- alpha값이 0
set.seed(227)
res.ridge <- cv.glmnet(X,Y,family="binomial",alpha=0,
nfolds = 4, type.measure = "class")
plot(res.ridge)
#최적 lambda 값
print(log(res.ridge$lambda.min))
## [1] -0.2348106
plot(res.ridge$glmnet.fit, xvar = "lambda")
coef.ridge <- coef(res.ridge, s = "lambda.min")[,1]
pos.ridge <- coef.ridge[coef.ridge>0]
neg.ridge <- coef.ridge[coef.ridge<0]
pos.ridge <- sort(pos.ridge,decreasing = T)
neg.ridge <- sort(neg.ridge)
- elastic net 분석
- lasso와 ridge의 장점만 획득한다고 하
- alpha값을 0.1~0.9사이로, 마인드 스케일에서는 0.5
set.seed(227)
res.elastic <- cv.glmnet(X,Y,family="binomial",alpha=0.4,
nfolds = 4, type.measure = "class")
plot(res.elastic)
#최적 lambda 값
print(log(res.elastic$lambda.min))
## [1] -4.086499
plot(res.elastic$glmnet.fit, xvar = "lambda")
coef.elastic <- coef(res.elastic, s = "lambda.min")[,1]
pos.elastic <- coef.elastic[coef.elastic>0]
neg.elastic <- coef.elastic[coef.elastic<0]
pos.elastic <- sort(pos.elastic,decreasing = T)
neg.elastic <- sort(neg.elastic)
- 만들어진 사전 export
#### lasso,ridge,elastic 비교
###긍정
#pos.lm[1:5]
#pos.lasso[1:5]
#pos.ridge[1:5]
#pos.elastic[1:5]
##길이 비교
#length(pos.lm)
#length(pos.lasso)
#length(pos.ridge)
#length(pos.elastic)
###부정
#neg.lm[1:5]
#neg.lasso[1:5]
#neg.ridge[1:5]
#neg.elastic[1:5]
##길이비교
#length(neg.lm)
#length(neg.lasso)
#length(neg.ridge)
#length(neg.elastic)
write.csv(pos.lm,paste0("kor_pos_reg_sent_",Sys.Date(),".csv"))
write.csv(pos.lasso,paste0("kor_pos_lasso_sent_",Sys.Date(),".csv"))
write.csv(pos.ridge,paste0("kor_pos_ridge_sent_",Sys.Date(),".csv"))
write.csv(pos.elastic,paste0("kor_pos_ela_sent_",Sys.Date(),".csv"))
write.csv(neg.lm,paste0("kor_neg_reg_sent_",Sys.Date(),".csv"))
write.csv(neg.lasso,paste0("kor_neg_lasso_sent_",Sys.Date(),".csv"))
write.csv(neg.ridge,paste0("kor_neg_ridge_sent_",Sys.Date(),".csv"))
write.csv(neg.elastic,paste0("kor_neg_ela_sent_",Sys.Date(),".csv"))
- 감성사전을 사용하여 감성분석하기
- dictionary를 사용하여 제작한 감성사전에 있는 단어만 dtm으로 입력
- polarity함수로 감성분석
#devtools::install_github("mannau/tm.plugin.sentiment",force=T)
library(tm.plugin.sentiment)
## Loading required package: quantmod
## Loading required package: xts
## Loading required package: zoo
##
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Loading required package: TTR
## Version 0.4-0 included new data defaults. See ?getSymbols.
corpus5<- Corpus(VectorSource(text$V5[-train.num]))
dtm1 <- DocumentTermMatrix(corpus5,
control=list(tokenize = ko.words,
tolower=T,
removePunctuation=T,
removeNumbers=T,
weighting=weightTfIdf,
dictionary=Terms(dtm)))
- train set의 정확도 살펴보기
senti.lm <- polarity(dtm, names(pos.lm), names(neg.lm))
senti.lasso <- polarity(dtm, names(pos.lasso), names(neg.lasso))
senti.ridge <- polarity(dtm, names(pos.ridge),names(neg.ridge))
senti.elastic <- polarity(dtm, names(pos.elastic),names(neg.elastic))
library(rcv)
rcv("caret")
## Loading required package: lattice
## Loading required package: ggplot2
##
## Attaching package: 'ggplot2'
## The following object is masked from 'package:NLP':
##
## annotate
senti.lm.b <- ifelse(senti.lm>0,1,0)
senti.lasso.b <- ifelse(senti.lasso>0,1,0)
senti.ridge.b <- ifelse(senti.ridge>0,1,0)
senti.elastic.b <- ifelse(senti.elastic>0,1,0)
confusionMatrix(senti.lm.b,text$V3[train.num])
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 1856 629
## 1 142 617
##
## Accuracy : 0.7623
## 95% CI : (0.7473, 0.7769)
## No Information Rate : 0.6159
## P-Value [Acc > NIR] : < 0.00000000000000022
##
## Kappa : 0.4578
## Mcnemar's Test P-Value : < 0.00000000000000022
##
## Sensitivity : 0.9289
## Specificity : 0.4952
## Pos Pred Value : 0.7469
## Neg Pred Value : 0.8129
## Prevalence : 0.6159
## Detection Rate : 0.5721
## Detection Prevalence : 0.7660
## Balanced Accuracy : 0.7121
##
## 'Positive' Class : 0
##
confusionMatrix(senti.lasso.b,text$V3[train.num])
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 1985 572
## 1 13 674
##
## Accuracy : 0.8197
## 95% CI : (0.806, 0.8328)
## No Information Rate : 0.6159
## P-Value [Acc > NIR] : < 0.00000000000000022
##
## Kappa : 0.5837
## Mcnemar's Test P-Value : < 0.00000000000000022
##
## Sensitivity : 0.9935
## Specificity : 0.5409
## Pos Pred Value : 0.7763
## Neg Pred Value : 0.9811
## Prevalence : 0.6159
## Detection Rate : 0.6119
## Detection Prevalence : 0.7882
## Balanced Accuracy : 0.7672
##
## 'Positive' Class : 0
##
confusionMatrix(senti.ridge.b,text$V3[train.num])
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 1982 563
## 1 16 683
##
## Accuracy : 0.8215
## 95% CI : (0.8079, 0.8346)
## No Information Rate : 0.6159
## P-Value [Acc > NIR] : < 0.00000000000000022
##
## Kappa : 0.5888
## Mcnemar's Test P-Value : < 0.00000000000000022
##
## Sensitivity : 0.9920
## Specificity : 0.5482
## Pos Pred Value : 0.7788
## Neg Pred Value : 0.9771
## Prevalence : 0.6159
## Detection Rate : 0.6110
## Detection Prevalence : 0.7845
## Balanced Accuracy : 0.7701
##
## 'Positive' Class : 0
##
confusionMatrix(senti.elastic.b,text$V3[train.num])
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 1981 570
## 1 17 676
##
## Accuracy : 0.8191
## 95% CI : (0.8054, 0.8322)
## No Information Rate : 0.6159
## P-Value [Acc > NIR] : < 0.00000000000000022
##
## Kappa : 0.5827
## Mcnemar's Test P-Value : < 0.00000000000000022
##
## Sensitivity : 0.9915
## Specificity : 0.5425
## Pos Pred Value : 0.7766
## Neg Pred Value : 0.9755
## Prevalence : 0.6159
## Detection Rate : 0.6107
## Detection Prevalence : 0.7864
## Balanced Accuracy : 0.7670
##
## 'Positive' Class : 0
##
- test set의 정확도 살펴보기
###
lm_result <- polarity(dtm1, names(pos.lm),names(neg.lm))
lasso_result <- polarity(dtm1, names(pos.lasso), names(neg.lasso))
ridge_result <- polarity(dtm1, names(pos.ridge),names(neg.ridge))
elastic_result <- polarity(dtm1, names(pos.elastic),names(neg.elastic))
####
senti.lm.b <- ifelse(lm_result>0,1,0)
senti.lasso.b <- ifelse(lasso_result>0,1,0)
senti.ridge.b <- ifelse(ridge_result>0,1,0)
senti.elastic.b <- ifelse(elastic_result>0,1,0)
reg <- confusionMatrix(senti.lm.b,text$V3[-train.num])
ridge <- confusionMatrix(senti.lasso.b,text$V3[-train.num])
lasso <- confusionMatrix(senti.ridge.b,text$V3[-train.num])
ela <- confusionMatrix(senti.elastic.b,text$V3[-train.num])
comp <- data.frame(reg$overall[1],ridge$overall[1],lasso$overall[1],ela$overall[1])
print(comp)
## reg.overall.1. ridge.overall.1. lasso.overall.1. ela.overall.1.
## Accuracy 0.5706371 0.6315789 0.6177285 0.6232687