KoNLP - 한글 자연어 처리 툴킷

R에는 {tm}이 있어서 텍스트 마이닝을 손쉽게 할 수 있다. 그렇지만 각종 처리를 위해 단어를 분절하는 일 등을 할 때는 참 어렵다. 뿐만 아니라 collocation 혹은 concordance 문제를 처리하기도 쉽지 않다.

전희원씨가 만든 KoNLP는 이러한 문제를 좀 더 잘 해결할 수 있도록 여러 함수를 제공한다. 이 패키지를 쓰려면 {rJava} 패키지가 있어야 한다. 만약 이 패키지를 제대로 설치하지 못하면 KoNLP를 사용할 수 없으니 본 블로그의 다른 포스트를 참고하여 반드시 잘 설치하자.

R> install.packages("KoNLP")
R> library(KoNLP)

메모리에 문제가 있다면 다음을 추가한다.

R> options(java.parameters = "-Xmx2G") #2GB할당

**임시 추가 2016-01-01**
ignore.case()함수가 더 이상 사용되지 않아 KoNLP의 concordance_str()함수에 오류가 발생한다. 이 문제를 직접 해결해야 예제 코드가 정상 작동된다. 일단 다음과 같이 사용자 함수를 만들어 사용하자.

concordance_str <- function (string, pattern, span = 5) 
{
    res <- str_match_all(string, regex(sprintf(".{0,%d}%s.{0,%d}", 
                                                     span, pattern, span),ignore_case=TRUE))
    return(Filter(function(x) {
        length(x) != 0
    }, res))
}
concordance_file <- function (filename, pattern, encoding = getOption("encoding"), 
          span = 5) 
{
    f = file(filename, "r", encoding = encoding)
    on.exit(close(f), add = TRUE)
    retu <- c()
    while (TRUE) {
        next_line = readLines(f, n = 1, warn = FALSE)
        if (length(next_line) == 0) {
            break
        }
        ret <- concordance_str(next_line, pattern, span)
        if (length(ret) != 0) {
            retu <- c(retu, unlist(ret))
        }
    }
    return(retu)
}


다음과 같은 test.txt 파일이 있다고 하자.

님은 갔습니다. 아아, 사랑하는 나의 님은 갔습니다.
푸른 산빛을 깨치고 단풍나무 숲을 향하여 난 작은 길을 걸어서 차마 떨치고 갔습니다.
황금의 꽃같이 굳고 빛나던 옛 맹세는 차디찬 티끌이 되어서 한숨의 미풍에 날아갔습니다.
날카로운 첫 키스의 추억은 나의 운명의 지침을 돌려 놓고 뒷걸음쳐서 사라졌습니다.
나는 향기로운 님의 말소리에 귀먹고 꽃다운 님의 얼굴에 눈멀었습니다.
사랑도 사람의 일이라 만날 때에 미리 떠날 것을 염려하고 경계하지 아니한 것은 아니지만, 이별은 뜻밖의 일이 되고 놀란 가슴은 새로운 슬픔에 터집니다.
그러나 이별은 쓸데없는 눈물의 원천을 만들고 마는 것은 스스로 사랑을 깨치는 것인 줄 아는 까닭에 걷잡을 수 없는 슬픔의 힘을 옮겨서 새 희망의 정수박이에 들어부었습니다.
우리는 만날 때에 떠날 것을 염려하는 것과 같이 떠날 때에 다시 만날 것을 믿습니다.
아아, 님은 갔지마는 나는 님을 보내지 아니하였습니다.
제 곡조를 못 이기는 사랑의 노래는 님의 침묵을 휩싸고 돕니다.



잘 아는 '님의 침묵'이다. 이 파일을 EUC-KR 인코딩으로 저장하고 Windows를 사용한다고 가정한다. Mac이나 Linux에서는 기본이 UTF-8이기 때문에 이런 식의 걱정은 별로 필요없지만... 윈도우에서는 인코딩을 항상 신경 써야 한다.

이제 다음을 실행해 보자.

R> a <- concordance_file("c:/test.txt",pattern='?에')
R> a
[1] "숨의 미풍에 날아갔습" "의 말소리에 귀먹고 "  "님의 얼굴에 눈멀었습" " 만날 때에 미리 떠"
[5] "로운 슬픔에 터집니다" "아는 까닭에 걷잡을 "  " 정수박이에 들어부었" " 만날 때에 떠날 것"
[9] " 떠날 때에 다시 만"

보다시피 어떤 문서를 읽어서 패턴에 해당되는 구절을 찾아준다. 위의 예를 보면 pattern이 정규식임을 알 수 있다. "?에"는 에가 뒤에 붙는 두 문자로 이루어진 단어를 찾으라는 의미다.

이번에는 목적어에 해당되는 단어를 포함하여 그 전후의 맥락을 알 수 있게 적당히 추려보자.

R> a <- concordance_file("d:/test.txt",pattern='을')
 [1] "푸른 산빛을 깨치고 " "풍나무 숲을 향하여 " " 작은 길을 걸어서 "  "명의 지침을 돌려 놓" " 떠날 것을 염려하고"
 [6] "물의 원천을 만들고 " "스로 사랑을 깨치는 " "닭에 걷잡을 수 없는" "슬픔의 힘을 옮겨서 " " 떠날 것을 염려하는"
[11] " 만날 것을 믿습니다" " 나는 님을 보내지 "  "님의 침묵을 휩싸고 "


어떤 색인 구절을 만들기 위해 사용되는 concordance 함수를 적절히 활용하면 문서에서 흔히 사용되는 패턴들을 효과적이고 효율적으로 파악할 수 있다.

파일에서 데이터를 가져오지 않을 때는 concordance_str() 함수를 사용한다. 방법은 concordance_file()과 동일하다.


패턴을 확인하는 좋은 방법은 '키스트로크'를 분해하는 방법이다. 일단 분해하고 나면 유사성이 높은 단어패턴을 잘 뽑을 수 있다. 예를 들어 먹고 먹다 먹어 먹지만은 모두
"ㅁ ㅓ ㄱ"이라는 패턴이 포함되어 있다.

R> convertHangulStringToJamos("스스로 사랑을 깨치는")
 [1] "ㅅㅡ"   "ㅅㅡ"   "ㄹㅗ"   " "      "ㅅㅏ"   "ㄹㅏㅇ" "ㅇㅡㄹ" " "      "ㄲㅐ"   "ㅊㅣ"   "ㄴㅡㄴ"
R> convertHangulStringToKeyStrokes("스스로 사랑을 깨치는")
 [1] "tm"   "tm"   "fh"   " "      "tk"   "fkd" "dmf" " "      "Ro"   "cl"   "sms"


이번에는 명사(noun) 어구를 다 찾아보자.

R> library(foreach)
R> poem <- readLines("d:/test.txt")
R> foreach(p=poem, .combine='c') %do% extractNoun(p)
 [1] "님"             "사랑"           "나"             "님"             "산빛을"         "단풍나무"       "숲"             "길"          
 [9] "황금"           "꽃"             "맹세"           "티끌"           "한숨"           "미풍"           "첫"             "키스"        
[17] "추억"           "나"             "운명"           "나"             "향기"           "로운"           "님"             "말소리"      
[25] "귀먹고"         "꽃"             "님"             "얼굴"           "사랑"           "사람"           "일"             "때"          
[33] "것"             "염려"           "경계"           "것"             "이별"           "뜻밖"           "일"             "가슴"        
[41] "이별"           "눈물"           "원천"           "것"             "사랑"           "것"             "줄"             "까닭"        
[49] "수"             "힘"             "희망"           "정수"           "박이"           "들어부었습니다" "우리"           "때"          
[57] "것"             "염려"           "것"             "때"             "것"             "님"             "나"             "님"          
[65] "저"             "곡조"           "사랑"           "노래"           "님"             "침묵"    

보다시피 100% 정확도를 보이지는 않는다. 다음과 같은 문제를 보자.

  • 산빛을 -> 산빛
  • 로운: 제외
  • 귀먹고 -> 귀
  • 뜻밖: 제외
  • 들어부었습니다: 제외
71.4 % 정도의 정확성을 보이는 셈이다.


한글의 형태소 분석은 다음과 같이 수행할 수 있다.


R> MorphAnalyzer('스스로 사랑을 깨치는')
$스스로
[1] "스스로/mag" "스스로/npp"

$사랑을
[1] "사랑을/ncn"       "사랑/ncn+을/jco"  "사랑/ncpa+을/jco"

$깨치는
[1] "깨치/pvg+는/etm"

KoNLP 메뉴얼을 보면 한나눔 형태소 분석기를 바탕으로 한다는 점을 알 수 있다. 고맙다 KAIST! 더욱 더 자세한 내용은 다음 링크 참조: 한나눔


{KoNLP} 중에 쓸만한 것들은 이정도다. Part-of-Speech 부분은 많이 부족하다.

이에 대한 쓸만한 보완책이 없었는데 좋은 소식이 있어 첨부한다.
2014년 10월에 열린 26회 한글 및 한국어 정보처리 학술대회에서 KoNLPy가 공개되었다. 서울대학교 산업공학과의 박은정, 조성준이 작성한 Python 모듈이다.

홈페이지
학술논문

설치가 좀 까다롭다. 홈페이지에서 잘 확인하고 설치하자.

Part-of-Speech 부분에 관한 Python 예를 가져와서 보여주고 포스트를 끝내고자 한다.

>>> from konlpy.tag import Kkma
>>> from konlpy.utils import pprint
>>> kkma = Kkma()
>>> pprint(kkma.pos(u'오류보고는 실행환경, 에러메세지와함께 설명을 최대한상세히!^^'))
[(오류, NNG),
 (보고, NNG),
 (는, JX),
 (실행, NNG),
 (환경, NNG),
 (,, SP),
 (에러, NNG),
 (메세지, NNG),
 (와, JKM),
 (함께, MAG),
 (설명, NNG),
 (을, JKO),
 (최대한, NNG),
 (상세히, MAG),
 (!, SF),
 (^^, EMO)]

실행 속도는 그다지 만족스럽지 않다... 하지만 성능은 상당히 좋은데 무엇보다도 꼬꼬마, 한나눔, MeCab-ko 등 현존하는 거의 대부분의 형태소 분석기를 한 자리에서 모두 사용할 수 있다는 장점이 있다. 아직 베타 버전 단계의 물건이지만 계량을 거듭해서 반드시 더 좋아질 것이라고 믿는다.

댓글

  1. 안녕하세요 몇가지 질문이 있습니다.

    1. 메모장에서 저 txt파일을 저장할때 (윈도우 기준) ANSI로 해야하나요? 아니면 UTF-8로 해야하나요?

    2. 1번 질문에 이어서 ANSI로 저장한 후 read.delim으로 읽었을때는 정상적으로 읽혀집니다. 하지만
    UTF-8로 저장된 파일은 "유효하지 않은 멀티바이트 문자열 1입니다"와같은 문구로 에러가 났습니다.
    혹시 해결책을 아시는지요?

    3. concordance_file 함수를 사용할 경우에는 두가지 코딩방식으로 저장된 메모장 모두
    아래와 같은 에러 메세지를 나타냅니다

    Please use (fixed|coll|regexp)(x, ignore_case = TRUE) instead of ignore.case(x)
    에러: Can only match regular expressions

    혹시 해결책을 아실까요?

    언제나 좋은 포스팅 감사합니다.
    연말 잘보내세요~

    답글삭제
    답글
    1. 한글 윈도우는 CP949를 사용하고, R은 기본적으로 시스템의 인코딩 체계를 준용합니다. 따라서 R콘솔은 CP949를 기준으로 문자를 표시하고 처리합니다. 제시한 문제의 극적인 해결책은 Mac이나 Linux와 같이 UTF-8 체계를 가지는 시스템을 사용하는 방법이지만, 혹시 UTF-8로 되어 있는 문서를 사용하려면 CP949로 encoding하면 됩니다. Notepad++라는 윈도우 프로그램에 변환 기능이 있고, R 내에서도 iconv()함수를 사용하면 할 수 있습니다. KoNLP 등은 윈도우에서 안정적으로 활용하기 어려운 측면이 있습니다.

      삭제
  2. ignore.case() 함수가 거절(depreciated)되었습니다. 그래서 이 함수를 사용하는 KoNLP가 현재 정상 작동되지 않습니다(2016년 1월 1일 현재). 전희웅씨에게 이메일을 보내 이 문제를 지적했고 GitHub에 folk도 했으니 조만간 해소되리라 생각합니다.

    답글삭제
  3. 작성자가 댓글을 삭제했습니다.

    답글삭제
  4. 안녕하세요~ 제가 KoNLP로 트위터 데이터 받아서 처리해보니 정말 정확도가 떨어지던데.. 비정형 데이터도 많고 맞춤법도 틀리게 해서 그렇겠지만 이런 형태소 분석 또한 R에서 할 수 있나요?? 근데 SNS분석 같은 경우는 경우의수가 너무 많아서 수동으로 하기는 힘들것 같은데...

    답글삭제
    답글
    1. 제 경험을 말씀드리겠습니다. 연구 목적으로 7억개 정도의 한글 트위터 메시지를 프로세싱했었지요. 말씀대로 비정형 데이터를 처리하기는 참으로 어렵습니다. 자연어처리에 앞서 전처리 과정이 중요한 이유이기도 합니다. 먼저 분석에 필요한 사전을 정의해서 트위터 데이터를 분석이 용이하도록 정제하는 과정을 거쳐야 한답니다. 그리고 수동으로 분석이 불가능하지는 않답니다. 돈이 좀 있으시면 아마존에서 제공하는 collective work 서비스(이름은 구글링해보십시오 ㅋㅋ) 사용하는 것이 어떨지요?

      삭제
  5. 안녕하십니까, 포스팅 잘읽었습니다.
    다름이 아니라 두 가지 여쭤보고 싶은 것이 있는데 저같은 경우에는 엑셀과 PDF 파일로 텍스트마이닝을 시도해 보았는데요, 엑셀 csv 파일의 경우에는 대부분의 과정이 잘되는데 TermDocumentMatrix를 하는 순간 한글이 깨져버리는 요상한 경우가 발생합니다. 문제점이 무엇일까요? 또 한가지, PDF 파일을 corpus(옵션 list(reader=Rpdf)로 호출하면 어떤 pdf는 되고 어떤 pdf는 안되는데 이것에 대한 해결 방법을 혹시 아실련지요..? 주위에 물어볼 곳이 없어 이렇게 조언을 구하게 된 점 양해 부탁드립니다.

    답글삭제
  6. 안녕하세요 저는 한글로 되어 있는 excel 파일 .csv
    를 분석 하려고 햇는데요 여러 방법으로 했는데
    유효 하지 않은 멀티바이트 문자열이 있다고만 나옵니다 어떻게 해야 할까요 ㅜㅜ

    답글삭제
  7. 안녕하세요 저는 한글로 되어 있는 excel 파일 .csv
    를 분석 하려고 햇는데요 여러 방법으로 했는데
    유효 하지 않은 멀티바이트 문자열이 있다고만 나옵니다 어떻게 해야 할까요 ㅜㅜ

    답글삭제

댓글 쓰기

이 블로그의 인기 게시물

R에서 csv 파일 읽는 법

xlwings tutorial - 데이터 계산하여 붙여 넣기