[R] 감성사전 만들기

*본 포스팅은 마인드 스케일에 있는 코드를 그대로 사용한 것입니다.

 

감성사전 만들기

 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

 kor_pos_reg_sent_2016-10-27.csv

 kor_pos_ridge_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를 사용하여 형태소 분석하기

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

댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다