2016년 결산

2016년은 연말 회식도 그다지 많지 않아 2015년만큼이나 정신없이 지내지 않은 것 같다. 아마도 최근 최순실 사태 및 여러 마무리 되지 않은 사회적 이슈와 더불어 개인적으로도 마무리해야 되는 것들에 대한 정리로 바쁘게 연말이 지난 것 같다. 그래도 한해 소회 정도는 블로그에 올리는게 연례 행사인지라 간단하게 정리하고자 한다.

 

가족

두 아이의 아버지로서 그리고 한 여자의 남편으로서 개인적으론 충실했다고 생각하고, 아이들도 아내도 크게 다르지 않게 생각하는 것 같다. 이제 어느정도 상대방을 이해하고 그에 맞춰주는 배려의 여유가 우리 가족 내에서도 생겨난게 아닐까 하는 생각을 해보고 2016년은 그 배려의 여유를 확인한 한해가 된 것 같다. 한해동안 건강하고 행복해줘서 고맙고, 무엇보다 아이들의 순수함에 대해서 아이를 키우면서 느끼고 지킬 수 있는 기회를 가진 것을 매우 감사하게 생각하는 한해였다.

 


비즈니스 관련 중요한 의사결정에 쓰이는 회사의 백본과 같은 예측모델링 업무를 수행할 기회가 년초에 주어져 12월까지 수행했고, 지금까지도 마무리 작업을 하고 있다. 이 모형에 대한 모델링/적용/운영에 거의 70%이상의 시간을 할애했다. 이 진행 과정을 통해 예전의 ‘데이터 분석의 70% 이상은 데이터 전처리에 소요되고 나머지는 모델링과 통계분석이다’라는 통념이 깨지게 되었다. 비즈니스 모델링 혹은 분석의 70%는 업을 이해하고 관계자들과의 커뮤니케이션을 통해 어떻게 모형을 절충 적용할 것인지 그 방법을 찾는 과정에 소요되었고, 데이터 전처리 및 모델링은 2월부터 12월까지 해온 일 중에서 단 3개월에 불과했다. 나머지는 적용에 대한 A/B테스트와 현업과의 조정 그리고 성과 창출에 대한 고민에 들어갔다. 성공적으로 적용 및 성과창출이 되었고, 이에 대한 인정도 받았으며, 개인적으로도 매우 규모있고 리스크 있는 모델링 프로젝트를 잘 수행해서 의미가 컸다. 매달 월말이 되면 이 모형의 스코어 도출 때문에 신경이 곤두서고 담당자로부터 스코어 설명에 대한 요청이 올때마다 뭔가 잘못된 것인지 마음이 조마조마 하지만 이 또한 의미있는 경험이라 생각한다.

년초부터 업무를 하면서 딥러닝을 내가 하고 있는 업무에 적용 가능할까?라는 고민을 계속 해왔다. 사실 2016년초만 하더라도 부정적이었다. 왜냐면 딥러닝이 성과를 발휘하는 영역의 대부분은 이미지 인식이나 자연어처리쪽에 가까웠기 때문이고 내 업무 영역은 이 도메인하고는 거리가 있었기 때문이었다. 다행히도 입력 속성을 딥러닝에 맞게 변환하는 방식으로 10월쯤 일반 분류문제에서도 기존 모형보다 훨씬 좋은 퍼포먼스를 보여줄 수 있다는 것을 발견했고, 이를 이용해 신규 스코어링 모형을 구축/적용했다. 이 경험은 내가 딥러닝을 본격적으로 공부하게 하는 중요한 계기가 되었고, 지금도 중요한 관심사로 자리매김 하고 있다. 역시나 머신이 찾아내는 하이퍼 파라메터가 존재한다는 것을 이 과정을 통해서 충분히 공감하게 되었다. 물론 학습 및 튜닝에 시간 소요가 많이 되지만 의미있는 과정이고 추후 미션 크리티컬한 영역에서는 매우 큰 역할을 발휘할 수 있다고 생각한다.

 

개인

올해는 논문작업과 업무를 병행하는게 매우 어려운 일이라는 것을 뼈저리게 느낀 한해였다. 논문에 대한 변명은 하고싶지 않다. 다만 내가 개인적인 시간 컨트롤을 못했고 이 부분을 잘 하지 못하면 쓰지 못한다는 것도 충분히 이해하게 되었다. 하지만 논문 실험과 아웃라인을 잡고 관련 연구에 대한 리서치를 수행하면서 논문 읽기에 대한 습관과 연구 영역을 좁힌건 올해 매우 큰 수확이었다.

10월 논문작업을 잠시중단하게 한 일은 KoNLP의 개선에 쓸 시간이 부족했기 때문이었다. 그 동안 3년 넘게 방치해둔 패키지 이슈들과 사용자들의 요청을 외면할 수 없었는데, 다행히 정보화진흥원 에서 좋은 기회를 제공해 줘 개인적으로 만족할 만한 수준까지의 패키지 성능 개선을 하게 되었다. KoNLP는 개인적으로 업무로서 거리가 생겨버린 텍스트 마이닝의 연결점이라는 의미가 있고, 사용자에게는 좋은 텍스트 기반 연구도구로서 잘 사용될 수 있도록 지속적인 관심과 연구와 업데이트를 해갈 예정이다. 120만 형태소 사전을 활용해 분석을 수행하게 만들면서 기존의 메모리 낭비 이슈라든지 퍼포먼스 이슈를 함께 해결해야 했으며 문장 분리기 및 자동 띄어쓰기 등의 플러그인을 개발해야 했다. 약 3개월동안 주말 및 일과 이외 시간을 KoNLP에 썼으며 지금 생각해보면 2016년에 가장 의미있게 수행한 개인 업무였다 생각한다.

 

2017년

2017년은 좀더 다른 경험을 하는 한해가 되었으면 한다. 이미 작년 초중반부터 내가 가지고 있는 기술이 이미 과거 기술이 되고 있는게 아닌가 하는 생각을 했었고, 2017년 초가 되면서 이 생각은 거의 확실화 되고 있다. 지금 가지고 있는 기술과 능력에 대해서 반복 사용하면서 지낼 것인가 아니면 지금의 경험을 기반으로 새로운 기술에 대해서 경험을 쌓을 수 있는 기회를 만들 것인가의 고민을 계속하고 있다. 다만 확실한 건 이미 이런 고민을 하고 있는 사이에 세상은 변하고 있다는 것이다. 따라서 나도 보폭을 맞춰야 되겠다는 생각을 해본다. 보폭을 맞출 수 있고, 새로운 경험을 할 수 있는 한해가 되길 기대해 본다.

 


 

페이스북 포스트 워드클라우로 돌아보는 2016년

이런 워드클라우드를 그려보는건 거의 4년만인거 같다. KoNLP 초기 버전을 개발하고 나서 가장 먼저 했던 일이 워드클라우드를 그려보는 일이었고, 최근엔 이 워드클라우드를 그리는게 R을 배울때 'hello world!'격의 예제가 되어 버렸다.

필자가 가끔씩 페이스북 API로 이런 저런 분석작업을 하는데, 연말이고 하니 1년간 페이스북에 올린 포스트를 기반으로 워드클라우드를 그리는 작업을 해봤다.

올해 wordcloud2 패키지까지 나와줘서 아주 예쁘게 워드클라우드가 그려진다.

library(Rfacebook)
library(KoNLP)
library(data.table)
library(wordcloud2)
#token을 얻는 방법은 "https://developers.facebook.com/tools/explorer"에서
fb_page <- getPage(page="me", token=token, n=2000, since='2016/01/01', until='2016/12/15')


commentslist <- list()
for(id in fb_page$id){
  post <- data.table(getPost(post=id, token=token, comments = F,likes=F))
  #print(id)
  commentslist[[id]] <- post
}

dtlist <- sapply(commentslist, function(x){x$V1})


comm <- rbindlist(dtlist)
#98만 형태소 사전...
useNIADic()

#문장에 NA가 포함된 경우 특정 환경에서 에러가 생길 수 있다. 
#NA를 직접 제거해 주던지, devtools::install_github('haven-jeon/KoNLP') 
#명령어로 보완된 패키지를 설치해 사용한다. 
nouns <- extractNoun(comm$message)

nouns_norm <- Map(function(x){if(!is.na(x) && is.ascii(x)) toupper(x) else x},unlist(nouns))

cnts <- table(unlist(nouns_norm))

cnts_ <- cnts[cnts > 2 & nchar(names(cnts)) > 1]


wordcloud2(data.frame(word=names(cnts_), freq=as.numeric(cnts_)),  color = "random-light", backgroundColor = "grey", shape="star")

페이스북을 활발하게 활용한다면 한번쯤 그려보는것도 한해를 회고하는데 도움이 될 거라 생각한다. 위 코드를 사용하면 5분내에 그려볼 수 있다.

KoNLP v.0.80.0 버전 업(on CRAN now)

KoNLP v.0.80.0 릴리즈 on CRAN

3년 5개월만의 업데이트다. 금번 업데이트에서 가장 큰 변화는 기존 36만에서 약 120만 형태소 사전을 탑재했다는 것이고, 이들 사전을 사용자들이 원하는 형태로 사용할 수 있게 하는데 방점을 두었다. 총 66개의 카테고리 사전을 보유하고 있으며 사용자들의 분석 대상에 맞게 카테고리 사전을 선택해 조합하여 사용할 수 있게 하였다.

내부적으로는 0.80 이전까지는 압축된 텍스트 파일 사전을 기반으로 동작했으나 이번 릴리즈에는 사전의 종류와 양이 늘어 이들을 데이터베이스로 관리하게 하였으며, 기존의 로직도 최대한 이쪽을 바라볼 수 있게 코드를 대폭 수정하였다.

과거 버전에서 고질적으로 노출되었던 out of memory 이슈의 경우 최대 120만 사전을 메모리에 가지고 있게 하기 위해서는 선결해야 될 과제였다. 이를 위해 사용자 사용 패턴을 기반으로 가장 빈번하게 사용하는 패턴을 가장 빠르고 효율적으로 구동될 수 있게 수정 하였고, 이 과정을 통해 메모리를 효과적으로 관리할 수 있는 방안을 찾았다. 결과적으로 각각 서로 다른 분석함수(SimplePos22, extractNoun 등등)을 번갈아 가면서 호출할때는 다소 느리나 한 함수를 지속적으로 반복적으로 호출하는 경우엔 매우 빠른 동작을 수행하게 하였고, 이를 통해 메모리 사용 효율화도 이끌 수 있게 하였다(첫 실행에서 느리기 때문에 체감상 느리게 느낄 수 있으나 반복적인 작업에서는 그렇지 않는다는 걸 느낄 수 있을 것이다).

odroid
심지어는 120만 단어를 사용하는 example 코드가 휴대폰에 들어가는 CPU와 2G의 메모리만을 가진 손바닥만한 머신에서도 잘 동작한다(해당 머신은 필자가 마인크래프트 서버로 활용하는 머신인데, 이번에 리눅스에서 테스트 하는데 큰 역할을 수행했다).

최대 120만 사전을 로딩해 분석하기 위해서는 heap memory를 최소 700Mb 이상 잡아서 분석하는걸 추천한다. 따라서 별도의 메모리 설정을 사용자가 하지 않는다면 KoNLP는 -Xmx768M옵션을 잡게 하였다. 더 많은 사전을 직접 만들어 올리지 않는 이상 주어진 사전을 최대한 활용하기엔 부족함이 없을 것이다. 물론 768Mb보다 작은 힙 메모리를 설정상 보유하고 있으면 여기에 따른 안내 메시지도 도출되게끔 했다.

Tutorial

백문이 불여일견 인데.. 직접 사용을 해보도록 하자!

# 몇몇 마이너한 버그가 수정된 패키지를 원한다면 개발버전을 설치해도 좋다.
# 참고로 예시는 개발버전으로 수행하도록 한다. 
#devtools::install_github('haven-jeon/KoNLP', build_vignette=T)

install.packages("KoNLP")

금번 버전부터 Scala로 플러그인을 작성할 수 있게 환경을 만들었고, 실제 기존보다 더 나은 문장경계인식 플러그인과 비정상 어절 필터 같은 플로그인을 만들어 적용을 하였다. CRAN에서 미리 컴파일 된 버전을 제공하는 Windows, Mac에서는 런타임이 포함되어 있어 배포가 되며 만일 소스코드 그대로 설치를 하게 되면 별도의 Scala 런다임 라이브러리를 자동으로 다운받아 설치하는 모습을 볼 수 있을 것이다. 따라서 외부 네트웍이 되지 않는 곳에서는 안내되는 메시지를 따라 별도의 설정 작업이 필요하다.

library(KoNLP)
## Checking user defined dictionary!
library(ggplot2)
## Find out what's changed in ggplot2 at
## http://github.com/tidyverse/ggplot2/releases.
library(data.table)
## data.table 1.9.6  For help type ?data.table or https://github.com/Rdatatable/data.table/wiki
## The fastest way to learn (by data.table authors): https://www.datacamp.com/courses/data-analysis-the-data-table-way
#HTML Vignette 보기(한글이 포함된 vignette를 보게 될 줄이야.......@.@) 
vignette("KoNLP-API") 

#일단 시스템 사전만 사용해본다. 
useSystemDic()
## Backup was just finished!
## 283949 words dictionary was built.
txt <- '미국 싱크탱크 전략국제문제연구소의 빅터 차 한국석좌는 9일 미국의 제45대 대통령으로 당선된 도널드 트럼프가 전시작전통제권(전작권)을 한국에 조기에 넘길 가능성이 있다고 전망했다.'

system_res <- MorphAnalyzer(txt)

sum(sapply(system_res, length))
## [1] 102

이제 NIADic을 기반으로 다양한 사전 데이터를 활용해보자!

#추가된 NIADic 사전 패키지
useNIADic()
## Backup was just finished!
## 983012 words dictionary was built.
niadic_res <- MorphAnalyzer(txt)

sum(sapply(niadic_res, length))
## [1] 288
morph_cnts <- rbind(data.table(eojeol=names(system_res), cnts=sapply(system_res, length), kind='시스템 사전'),
                    data.table(eojeol=names(niadic_res), cnts=sapply(niadic_res, length), kind='NIA 사전'))

ggplot(morph_cnts, aes(eojeol, cnts)) + geom_bar(stat='identity', aes(fill=kind), position='dodge') + coord_flip() + ggtitle("어절별 형태소 분석 결과 빈도")

plot of chunk unnamed-chunk-3

기본 시스템 사전을 사용하는 것보다 NIADic을 사용해서 분석하는게 약 2.5배 더 풍성한 형태소 분석 결과를 보여준다.

#자신의 머신이 -Xmx512m 정도의 힙 메모리를 쓰고 있다면,
# woorimalsam 사전만 올려서 아래와 같이 분석 가능함
# options('java.parameters') 명령어로 설정 확인 가능 

#category_dic_nms 파라메터로  '정치(political)' 관련 카테고리 사전을 더 부가할 수 있다. 
#?buildDictionary 메뉴얼 확인! 
buildDictionary(ext_dic = c('woorimalsam'), category_dic_nms=c('political'),
                user_dic=data.frame(term='전작권', tag='ncn'))
## 631100 words dictionary was built.
woori_res <- MorphAnalyzer(txt)

사전 데이터 활용

NIADic 패키지의 사전은 사실 KoNLP만을 위한 사전은 아니다. 따라서 아래와 같은 방식으로 형태소 사전 파일을 텍스트 형태로 내려받을 수 있고, 수정해 다른 형태소 분석기에 활용을 할 수 있다.

다운로드 가능한 사전은 ‘sejong’, ‘woorimalsam’, ‘insighter’이다.

사전의 Copy Right은 CC BY이며, 저작자 표기만 하면 상업적으로 활용이 가능하다. 인용에 대한 정보는 citation('NIADIc')을 참고하면 된다.

library(NIADic)
## Successfully Loaded NIADic Package.
citation('NIADic')
## 
## NIA(National Information Society Agency) and Jeon H (2016).
## _NIADic: NIA(National Information Society Agency) Korean
## Dictionaries_. R package version 0.0.1, <URL:
## https://github.com/haven-jeon/NIADic>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {NIADic: NIA(National Information Society Agency) Korean Dictionaries},
##     author = {{NIA(National Information Society Agency)} and Heewon Jeon},
##     year = {2016},
##     note = {R package version 0.0.1},
##     url = {https://github.com/haven-jeon/NIADic},
##     licence = {CC BY},
##   }
woori_dic <- get_dic("woorimalsam")

woori_dic <- data.table(woori_dic)

#품사별 빈도
woori_dic_cnt  <- woori_dic[,.N,tag][order(-N)]
ggplot(woori_dic_cnt, aes(reorder(tag,N),N)) + geom_bar(stat='identity')

plot of chunk unnamed-chunk-5

#카테고리별 빈도 
woori_dic_cnt  <- woori_dic[,.N,category][order(-N)]
ggplot(woori_dic_cnt, aes(reorder(category,N),N)) + geom_bar(stat='identity') + coord_flip()

plot of chunk unnamed-chunk-6

write.csv(woori_dic, file='woori_dic.csv', row.names=F, fileEncoding='UTF-8')

좀더 자세한 내용은 KoNLP-APIvignette을 참고하기 바란다.

차주 초나 금주 말에 몇몇 마이너 버그를 해결하고 다시한번 CRAN에 업데이트를 할 예정이다. 혹시 사용중 이슈가 있으면 이곳에 올려주면 감사하겠다(물론 한글로 올려도 좋다).

ps.
마지막으로 3년 5개월만에 KoNLP를 업그레이드 할 수 있던 동기를 부여해 주셨던 NIA 빅 데이터 센터에 감사의 말을 전하고 싶다. 이곳에서 마련해준 사전 데이터가 아니였으면 KoNLP의 버그수정/기능개발은 커녕 방향도 고민하지 못했을 거라는 생각을 다시한번 해보게 된다. 다시 코드를 보고 개발을 시작하면서 피곤하지만 굉장한 재미가 있었고, KoNLP의 목적과 방향을 다시 설정할 수 있었던 소중한 시간이었다.