한글 형태소 분석기 KOMORAN 0.6 버전을 공개합니다.


네이밍과 관련하여 몇몇 분의 문의를 주셨는데, KOMORAN은


KOrean MORphological ANalyzer의 약자입니다.


버전업 주요 히스토리


ver 0.6 

- 기존 0.5 버전 ( 링크 )에 기분석 사전을 추가하였습니다.

- 기분석 사전 추가로 인해 분석 속도가 2배 가량 빨라졌습니다. (sec 당 200kb 분석)

- VM는 최소 512mb를 필요로 합니다. ( -Xms512m -Xmx512m )


  • 형태소 분석 Core 라이브러리 파일
  • 형태소 분석 라이브러리 내 사용되는 유틸성 라이브러리

  • 필요 라이브러리 파일


사용법은 ver 0.5와 같습니다. ( 링크 )


본 형태소 분석기는 상업적 용도의 사용을 금지합니다. (안정성 검증 필요, 별도 연락)


개인 및 비상업적 용도로만 사용을 하실 수 있습니다.


사용중 나타나는 버그 및 오류는 댓글이나 메일 shin285 AT 네이버.com 으로 알려주시기 바랍니다.


※ 현재 자연어처리와 관련된 라이브러리 및 엔진들을 계속적으로 연구하고 만들고자 합니다. 함께 하시길 원하시는 분은 연락부탁드립니다.


========== 2013.04.02 10:45분 추가==========

현재 데이터 경로와 관련된 에러가 있습니다. 금일 저녁에 다시 업로드 하겠습니다. 잠시 링크는 삭제하겠습니다.


========== 2013.04.02 21:09분 추가==========

데이터 경로와 관련된 에러를 수정하였습니다. 에러를 알려주신 "hare"님께 감사드립니다^^


==========[2013.05.20. 11:51분 추가]==========

- 1.0 버전이 릴리즈 되었습니디다. 링크 참고 하시기 바랍니다. ( KOMORAN 1.0 )



  1. hare 2013.04.02 10:33 신고

    안녕하세요. 만드신 형태소분석기를 사용해봤습니다.
    결과는 잘 나오는데
    "java.io.FileNotFoundException: \Users\shin285\Documents\6. Data\5. Morphology Model\irrDic.txt (지정된 경로를 찾을 수 없습니다)" 에러메시지가 나오는데요. 다른 설정을 해야 하는건가요?
    파일은 정상적으로 들어가 있습니다.

    • shine_ing 2013.04.02 21:39 신고

      아..죄송합니다..그 부분을 손봤어야 했는데..테스트용 라이브러리가 그대로 올라간 것 같습니다. 금일 저녁에 바로 손봐서 다시 업데이트 하겠습니다. 죄송합니다.
      ============이하 추가 내용============
      오류 수정하였습니다. 감사합니다^^

  2. pch 2013.04.30 22:00 신고

    java.io.FileNotFoundException: 형태소 분석용 파일들이 저장된 루트 폴더observation.obj (지정된 파일을 찾을 수 없습니다)

    이런식으로 줄줄이 오류가 뜨는데 해결방법 알려주시면 감사하겠습니다..

    • shine_ing 2013.05.01 01:15 신고

      저장된 루트 폴더의 가장 뒤에 "/"를 붙여주셔야 합니다^^;;
      예를 들어 "komoran_datas"라는 루트 폴더에 있다면 "komoran_datas/"로 표기해야합니다.
      조만간 유연하게 사용가능 할 수 있도록 수정하겠습니다. 죄송합니다 ㅠㅠ

  3. pch 2013.05.01 12:59 신고

    0.5의 예제코드에서 보면 폴더를 지정해주는 부분은 없던데, 폴더를 어떤식으로 지정해주나요?

    • shine_ing 2013.05.01 22:33 신고

      0.5 예제코드에서 객체 생성시에 경로를 지정해주는 부분이 있었는데, 포스팅의 문맥과 잘 맞지 않아 이해가 어려웠던 것 같습니다. 죄송합니다. 자세한 내용은 스크린샷을 첨부하였습니다. 감사합니다.

자바로 만든 한글 형태소 분석기를 공개합니다.


이름은 KOMORAN입니다.


현재 자체 테스트는 완료하였습니다.


기본적인 어절 단위 형태소 분석기를 따르고 있습니다. (sec 당 약 100kb 분석)


사용 중 발생하는 에러는 댓글로 부탁드립니다.


필요한 라이브러리 파일은 아래와 같습니다.

    • 형태소 분석 라이브러리 내 사용된 유틸성 라이브러리


    • 분석에 필요한 모델 파일들은 아래와 같습니다.



사용법은 아래와 같습니다.


public static void main(String[] args) {

MorphologyAnalyzer analyzer = new MorphologyAnalyzer("형태소 분석용 파일들이 저장된 루트 폴더");

List<List<Pair<String,String>>>reslut =  analyzer.analyze("감기는 자주 걸리는 병이다.");

for (List<Pair<String, String>> wordResult : reslut) {

for (Pair<String, String> pair : wordResult) {

System.out.println(pair);

}

System.out.println();

}

}


사용법과 관련하여 문의 사항이 있어서 스크린샷을 첨부하였습니다.

datas 밑에 필요한 파일들을 위치시켜 놓습니다.



형태소 분석기 객체 생성시에 위에 위치한 폴더 경로인 "datas/"를 지정해줍니다.




출력 결과는 아래와 같습니다.


Pair [first=감기, second=VV]

Pair [first=는, second=ETM]


Pair [first=자주, second=MAG]


Pair [first=걸리, second=VV]

Pair [first=는, second=ETM]


Pair [first=병, second=NNG]

Pair [first=이, second=VCP]

Pair [first=다, second=EF]

Pair [first=., second=SF]


본 형태소 분석기는 상업적 용도의 사용을 금지합니다. (안정성 검증 필요, 별도 연락)


개인 및 비상업적 용도로만 사용을 하실 수 있습니다.


※ 현재 자연어처리와 관련된 라이브러리 및 엔진들을 계속적으로 연구하고 만들고자 합니다. 함께 하시길 원하시는 분은 연락부탁드립니다.


==========[2013.03.20. 14:50분 추가]==========

- 현재 성능 테스트, 오류 및 버그 분석을 위해서 기분석 사전(FWD)을 사용하지 않고 있습니다.


==========[2013.03.22. 14:50분 추가]==========

- 현재 내용은 4월 1일부터 새로운 버전으로 릴리즈 예정입니다. 이용에 참고 하시기 바랍니다.


==========[2013.04.01. 23:50분 추가]==========

- 기분석 사전이 포함된 새로운 버전으로 릴리즈 되었습니다. 링크 참고 하시기 바랍니다. ( KOMORAN 0.6 )

- 현재 버전은 기존에 사용중이신 분들을 위해 그대로 유지하겠습니다.


==========[2013.05.20. 11:51분 추가]==========

- 1.0 버전이 릴리즈 되었습니디다. 링크 참고 하시기 바랍니다. ( KOMORAN 1.0 )


  1. 안녕하세요 2013.03.20 13:53 신고

    저는 프로그램같은 건 전혀 모르지만 예시문장부터 오류길래 남겨요. 감기는은 감기/NNG+는/JX 일텐데 프로그램은 동사 감기다로 분석했네요 ^^;

    • shine_ing 2013.03.20 14:48 신고

      안녕하세요^^ 사실 감기가 NNG가 맞지만 현재 어절 내 확률기반으로 계산을 하고 있기 때문에 VV로 나오고 있습니다~ 물론 어절 간의 정보를 이용하면 감기/NNG로 나올 수는 있습니다만, 전체적인 성능은 오히려 떨어지는 단점이 있습니다~ 지적 감사드립니다~

      결론 : 데이터 추가 또는 어절 정보 사용으로 수정 가능.
      조치 : 조만간 업데이트 예정.

  2. 유총재 2013.04.01 14:22 신고

    안녕하세요.

    분석기 잘 사용하고 있습니다.

    Clien에서 졸업작품에 사용한다고 글을 남겼는데..

    데이터가 좀 많은 것을 for문으로 돌리면서 넣어봤더니...

    Heap space 오류를 뱉어내는군요 ㅠ

    다른 부분에서의 Memory Leak은 다 확인 해 보았으나, 형태소 분석기를 추가하면 Memory 오류가 발생합니다.

    혹, 분석기 내에서 제가 인풋으로 집어넣은 String이나..String을 통해 분석된 형태소가 계속 저장되어 있는 건 아닌지 싶어 글 남깁니다.

    항상 감사하고 있습니다!

    • shine_ing 2013.04.02 00:42 신고

      안녕하세요.
      내부적으로 init을 해주어서 메모리 부족문제는 최대한 안나도록 해놓았습니다만, 한번 더 확인해보겠습니다. 혹시 그 데이터를 제가 테스트 해볼 수 있을까요?
      ========= 이하 추가 내용 =========
      현재 1G정도의 문서를 분석해봤는데 memory 오류가 나질 않네요..실험하신 데이터를 제가 직접 돌려봐야할 것 같네요. 데이터 좀 부탁드리겠습니다 ^^

  3. 유총재 2013.04.02 10:50 신고

    직접 테스트 해 보셨다니 감사합니다.ㅠㅠ

    실험 데이터 보기 쉽게 정리해서 올리겠습니다!

    소스코드를 코멘트로 올리는 게 편할까요..메일로 따로 보내드릴까요??

    • shine_ing 2013.04.02 11:13 신고

      용량이 크지 않으면 메일로 보내주셨으면 합니다 ^^
      shin285 AT naver.com 으로 부탁드리겠습니다~

  4. shine_ing 2013.04.05 09:18 신고

    "유총재"님이 알려 주신 Heap space 오류는 형태소 분석기 문제가 아닌 것으로 확인 됐습니다.

  5. casionwoo 2013.05.07 12:29 신고

    질문이 있는데요 혹시 Pair에서 second가 그 형태소의 의미인거같은데 설명을 찾을수가 없어요 혹시 각각이 무슨 뜻인지 알수 있을까요?

    • shine_ing 2013.05.08 00:53 신고

      안녕하세요. KOMORAN 업데이트와 문서 정리를 동시에 하다보니 정신이 없어서 기본적인 설명도 빠트렸네요..ㅠㅠ 기본적으로 형태소 정보는 세종 품사 셋을 따르고 있습니다. http://nlp.kangwon.ac.kr/~nlpdemo/KACTEIL-KMA/right.html 를 참조하시면 될 것 같습니다^^ 감사합니다~

  6. casionwoo 2013.05.08 11:34 신고

    제가 사용해서 분석을 해 보려하는데 사용은 잘됩니다만 "싸이콘서트"
    라는 말을 싸, 이콘서트, 콘서트 이렇게 인식하더라고요

    혹시 싸이라는 말을 인식을 못하는거 같은데 사전을 제가 따로 고치는 방법이 있을까요?

    원하는 단어를 사전에 등록 시키는 방법이 있는지를 알고 싶습니다.

    • shine_ing 2013.05.08 14:46 신고

      문의주신 내용은 현재 업데이트 되고있는 버전에 반영하였습니다. 금주내로 업데이트 예정이오니 조금만 기다려주세요^^

  7. casionwoo 2013.05.08 20:07 신고

    혹시 제가 사전을 수정하여서 사용할 수 잇는 방법에는 없을까요?
    이상한 에러가 생길때마다 바로바로 수정하여서 사용하고싶어서요.ㅠ
    참고로 사용하는 것은 졸업프로젝트에 사용하고 있습니다.

    • shine_ing 2013.05.09 02:55 신고

      사전을 추가 할 수는 있지만 사전을 직접 수정하는 방법은 상당히 위험합니다. 형태소 분석기를 어떤 요소에 사용하고 계신지는 모르겠지만, 사전을 바로바로 수정 하시길 원하신다면 형태소 분석기보다는 정규표현식을 이용한 일반 사전 매칭 방법을 사용하시는 방법이 더 적합할 것 같습니다.

crontab 한글이 깨질 시 실행되는 .sh 파일 위에 아래와 같은 내용을 추가해줘야 한다.


export LANG=ko_KR.UTF-8


무조건 UTF-8로 설정해서는 안되고 리눅스의 환경에 맞게끔 설정해줘야한다.

리눅스 언어의 인코딩은 

env | grep LANG

로 확인 가능.

'Tip > Linux' 카테고리의 다른 글

^M 삭제  (0) 2013.05.02
vi 문자열 치환  (0) 2013.04.30
잘 되던 파일 처리 프로그램에서 한글이 물음표로 표시 될 때  (0) 2013.04.25
리눅스 crontab 한글 깨짐 현상  (0) 2013.03.13
리눅스 shell로 파일 전송하기  (2) 2013.01.07

저 같은 경우에는 윈도우에서 개발을 하고 리눅스에서 프로토타입을 테스트하는 경우가 잦습니다.


이걸 모를때는 ftp를 번거롭게 켜고 끄고 했었는데, 더 쉬운 방법이 있었습니다.


바로 lrzsz를 이용하는 방법입니다.


그냥 간단하게 파일을 드래그해서 리눅스 쉘프로그램(xshell이나 putty 등)에 던져 넣으면 파일이 전송됩니다.


주의하셔야 될 점은 시간초과(?)가 있으니 비교적 용량이 짧은 파일을 이동하는데 자주 사용하고 있습니다.


lrzsz는 리눅스에 따라서 기본적으로 설치되어있는 놈들이 있고 그렇기 않은 놈들이 있습니다.


적어도 현재 제가 사용중인 cent 6.0에서는 기본적으로 설치되어 있지 않네요~


이 방법을 사용하기 위해서는 먼저


lrzsz를 인스톨 해줘야 합니다.


root 계정으로 로그인 한뒤에 아래와 같이 합니다.


- lrzsz 인스톨

yum install lrzsz


중간에 나오는건 당연히 y 버튼 살포시..


인스톨이 완료되면 그냥 파일을 집어던지면 리눅스에 파일이 올라가지는 것을 확인하실 수 있을 것입니다^^


========== [2013.06.13 추가]==========


위에서 언급한 방식은 시작에도 말씀 드렸듯이 "프로토 타입"으로 일회성일 때 유용한 방법입니다. 그러나 빈번하게 사용해야 하는 경우에 있어서 훨씬 더 좋은 방법이 있네요. 윈도우로 마운트하여 마치 드라이브처럼 사용하는 방법이 있습니다. 아래 링크를 참조하셔서 사용해보시면 좋을 것 같습니다^^

좋은 방법을 알려주신 "Keith Kim" 님께 진심으로 감사드립니다 ^^


http://code.google.com/p/win-sshfs/

http://www.swish-sftp.org/

'Tip > Linux' 카테고리의 다른 글

^M 삭제  (0) 2013.05.02
vi 문자열 치환  (0) 2013.04.30
잘 되던 파일 처리 프로그램에서 한글이 물음표로 표시 될 때  (0) 2013.04.25
리눅스 crontab 한글 깨짐 현상  (0) 2013.03.13
리눅스 shell로 파일 전송하기  (2) 2013.01.07
  1. Keith Kim 2013.06.12 23:13 신고

    SSH를 윈도우에 드라이브로 마운트 하는것은 어떨까요?
    http://www.swish-sftp.org/
    http://code.google.com/p/win-sshfs/

    • shine_ing 2013.06.13 00:43 신고

      이런 방법도 있군요! 좋은 정보 진심으로 감사드립니다. 이러한 방법을 찾아서 들어오실 분들을 위해서 포스트 내용을 수정하겠습니다. 감사합니다^^

나는 지난 5년동안 한글의 감정 분류와 관련된 연구를 계속해오고 있다.


최근에 이와 관련된 연구에 대한 평가(?)를 객관적으로 볼 수 있는 기회가 생겼다.


그.런.데....


내가 지난 5년간 해온 연구이고 다른 연구들과 비교실험해가면서 열심히 고민하고 진지한 연구로 객관성까지 부여한 연구였는데..


쉽게 인정해주지 않는것 같았다.


감정 분류는 자연어처리 기술들을 사용하는데 왜 당신은 그 기술들을 사용하지 않았습니까? 와 같은 의도였다.


자연어처리 기술이 들어가는건 당연한 것이지만, 최고의 퍼포먼스를 내기 위해서 최고의 자연어처리 기술을 도입해야 하는 것은 아니라도 생각한다.


나에게는 문서 100개라도 최고의 검색엔진을 만들어야하지 않겠느냐? 라는 질문으로 받아들여졌고,


그에 대한 나의 답변은 굳이 100개인데 왜 검색엔진을 만들어서 써야합니까? 대신 더 쉽고 퍼포먼스가 좋은 방법을 제안하겠습니다. 라는 답변으로 대답했다.


내가 인정받지 못한 것에 대한 분노가 조금 섞여 있긴 하다. 


어찌보면 자격지심이긴 하지만,


방법은 하나 밖에 없는것 같다.


자연어처리 관련된 모든 기술들을 직접 손수 개발하고.


그렇게 해봤는데 안돼서 제가 저런 방법을 썼습니다. 라고 얘기해야겠다.


그러기 위해서는 각 엔진들에서 최고 성능을 낼 수 있게 해야한다.


현재는 형태소 분석기부터 시작한다.


하나씩 하나씩.. 하다보면 내 연구가 빛을 볼 수 있곘지..

'Life' 카테고리의 다른 글

개인 사정으로 인한 휴재  (4) 2013.08.01
감정분류 연구를 하면서..  (6) 2012.11.22
동반자  (0) 2012.10.30
새로운 블로그 시작.  (0) 2012.10.27
  1. 오산돌구 2012.12.05 15:08 신고

    안녕하세요. 우연하게 방문했는데 관심사가 저랑 많이 비슷하시네요 : )
    저도 검색엔진, 형태소분석기, 기계학습에 대해 관심이 많아요. 관심만......-_-;;
    포스트들 잘보았습니다. : )

    하시는것과 비슷한게 있는데 참고하시면 도움이 될것같아 링크 남겨요.
    https://github.com/need4spd/crescent

    • shine_ing 2012.12.07 12:10 신고

      안녕하세요~ 이번에 공개소프트웨어에 참가하신다죠? :) 저도 분산 검색엔진으로 참여하고 싶었으나 오산돌구님께서 만드시는걸 보고 재빠르게 포기했습니다 ㅎㅎ 좋은 자료 포스팅 계속 해주세요~ :) 방문해 주셔서 감사합니다~

  2. 오산돌구 2012.12.07 15:45 신고

    그러셨군요 ㅎㅎㅎ
    아~ 그리고 오해하시는게 하나있는데 제가 참가한게 아니라
    용식님이(블로그: devyongsik.tistory.com ) 참가하셨고,
    저는 용식님이 github에 공개한걸 일부 기능에 관심이 있어서 pull request한거 밖에 없어요.
    나중에라도 하려는게 비슷하면 같이하면 좋을것같아요 : ) 황금 금요일 잘 보내세요~ ㅎㅎ

    • shine_ing 2012.12.10 14:27 신고

      제가 댓글을 조금 늦게 확인했네요^^;; 제가 오해를 했었군요..ㅎㅎ월요일인데 힘내서 한주 잘 보내시길 바랄게요~

  3. 해리s 2013.05.28 01:01 신고

    어떤 식으로 전체 알고리즘을 구성하시려고 하시는지 궁금해요 ㅎㅎ
    저도 자연어 처리에 관심이 좀 생겨서요. 전 현재 전기공학부 대학원을 다니고 있는 학생이에요~

    • shine_ing 2013.05.28 09:46 신고

      음 전체 알고리즘이라고 하기에는 조금 거시기한데..분류 문제로 접근을 하되 여러 방법을 사용한 다양한 엔진들을 만들어서 많이 분류한 쪽으로 처리를 할까 생각중입니다^^ 반갑습니다~ ㅎㅎ

먼저 루씬은 참 쓰기 편하면서도 커스터마이징하기는 참 불편한 특징을 갖고 있습니다. (물론 제 수준상으로...)


그래서 루씬을 이용해서 쓰기 편하게(?) 나름대로 만들어봤습니다.


지금부터 루씬을 이용해서 기본적인 검색기를 뚝딱 만들어보겠습니다.


먼저 색인할 데이터를 다운 받습니다.


[트윗 데이터]


data.zip



위 파일은 JSON 형태의 트윗을 모아둔 파일로 압축을 풀면 약 45M 정도가 됩니다.


먼저 이 데이터를 원하시는 경로에 풀어둡니다.


저는 D:/tweet/ 에 압축을 풀겠습니다.


트윗을 담기 위한 객체를 만들어 둡니다.


객체 이름은 당연히 Tweet으로 하고 소스 내용은 아래와 같습니다.



package kr.peopleware.lucene.model;

import java.util.List;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericField;

public class Tweet extends BaseModel{
	private long tweetId;
	private String contents;
	private long createdAt;
	private long crawledAt;	
	private boolean linked;
	private List<String> urls;	
	public long getTweetId() {
		return tweetId;
	}
	public void setTweetId(long tweetId) {		
		this.tweetId = tweetId;
	}
	public String getContents() {
		return contents;
	}
	public void setContents(String contents) {
		this.contents = contents;
	}
	public long getCreatedAt() {
		return createdAt;
	}
	public void setCreatedAt(long createdAt) {
		this.createdAt = createdAt;
	}
	public long getCrawledAt() {
		return crawledAt;
	}
	public void setCrawledAt(long crawledAt) {
		this.crawledAt = crawledAt;
	}	
	public boolean isLinked() {
		return linked;
	}
	public void setLinked(boolean linked) {
		this.linked = linked;
	}
	public List<String> getUrls() {
		return urls;
	}
	public void setUrls(List<String> urls) {
		this.urls = urls;
	}
	@Override
	public Document convetDocument() {
		Document doc = new Document();
		NumericField createdAt = new NumericField("createdAt",Field.Store.YES,true);
		createdAt.setLongValue(this.getCreatedAt());		
		doc.add(createdAt);		
	
		
		NumericField tweetId = new NumericField("tweetId",Field.Store.YES,false);		
		tweetId.setLongValue(this.getTweetId());
		doc.add(tweetId);
		
		String type = "all";
		if(this.getUrls() == null || this.getUrls().size() == 0){
			type="link";
		}
		
		doc.add(new Field("type",type,Field.Store.NO,Field.Index.NOT_ANALYZED));
		if(this.getUrls() != null){
			doc.add(new Field("urls",this.getUrls().toString(),Field.Store.YES,Field.Index.NOT_ANALYZED));
		}		
		doc.add(new Field("contents", this.getContents(), Field.Store.YES, Field.Index.ANALYZED));
		return doc;
	}	
}


이때 BaseModel을 익스텐드 시켜서 사용했는데 BaseModel은 아래와 같습니다.


package kr.peopleware.lucene.model;

import org.apache.lucene.document.Document;

public abstract class BaseModel {
	public abstract Document convetDocument();
}

BaseModel은 Tweet이란 객체를 루씬 색인에 사용할 수 있게끔 Document로 변환시켜주는 역할을 합니다.


그럼 이제 데이터를 담아둘 객체는 생성이 끝났습니다.


이제 데이터를 가져오는 부분을 만들어 보겠습니다.


간단히 만들기 위해서 main 클래스 내에 getTweets라는 메소드를 만들겠습니다.


이 메소드는 주어진 경로에 따라서 데이터를 List 형태로 반환하는 역할을 합니다.





	private static List<Tweet> getTweets(String path) {
		List<Tweet> tweetList = new ArrayList<Tweet>();
		List<String> filenames = FileUtil.getFileNames(path);

		for (String filename : filenames) {
			List<String> lines = null;
			try {
				lines = FileUtil.load2List(filename);
			} catch (Exception e) {
				e.printStackTrace();
			}
			if(lines == null){
				continue;
			}
			for (String line : lines) {

				Tweet t = convertTweet((DBObject) JSON.parse(line));				

				tweetList.add(t);
			}
			//			System.out.println(filename);
		}
		return tweetList;
	}


여기서 주어진 path로부터 데이터를 한줄 한줄 가져오게 됩니다. 이 때 각 한줄 한줄을 Tweet이라는 객체로 변환 시켜야합니다. 그래서 convertTweet이라는 메소드를 생성하여 변환시켜줍니다. JSON 형태를 Tweet이라는 객체로 변환시켜주는 역할을 합니다. 그리고 JSON.parse는 몽고디비 라이브러리에서 제공하는 유틸리티입니다. 


convertTweet역시 귀찮기 때문에 main 클래스 내에 만들겠습니다.



	private static Tweet convertTweet(DBObject obj) {		
		Tweet tweet = new Tweet();
		tweet.setTweetId((Long) obj.get("tweetId"));
		tweet.setContents((String) obj.get("contents"));		
		tweet.setCreatedAt((Long) obj.get("createdAt"));
		tweet.setCrawledAt((Long) obj.get("crawledAt"));
		tweet.setLinked((Boolean)obj.get("linked"));
		@SuppressWarnings("unchecked")
		List<String> urls = (List<String>) obj.get("urls");
		if(urls != null){
			tweet.setUrls(urls);
		}				

		return tweet;
	}


그럼 여기까지 데이터를 가져와서 객체에 담는것 까진 완성이 되었습니다.


그럼 이제 실제 색인을 해보도록 하겠습니다. 


이 글에서는 루씬을 해부하는 것이 아니라 루씬을 이용한 검색기를 만드는 것이 중요하기 때문에 루씬 자체에 대한 사용방법은 이 글에서는 생략하겠습니다.


먼저 위에 getTweets와 convertTweet이 포함된 메인 클래스 입니다.



package kr.peopleware.lucene.index.test;

import java.util.ArrayList;
import java.util.List;

import kr.peopleware.lucene.index.Indexer;
import kr.peopleware.lucene.index.properties.PropertiesManager;
import kr.peopleware.lucene.model.Tweet;
import kr.peopleware.util.file.FileUtil;

import org.apache.lucene.analysis.cjk.CJKAnalyzer;
import org.apache.lucene.util.Version;

import com.mongodb.DBObject;
import com.mongodb.util.JSON;

public class IndexTester {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		PropertiesManager pm = new PropertiesManager("index.properties");
		Indexer indexer = new Indexer(new CJKAnalyzer(Version.LUCENE_36),"index.properties");		
		List<Tweet> tweetList = getTweets(pm.getProperty("TARGET_PATH"));			
		for (Tweet tweet : tweetList ) {
			indexer.index(tweet);
		}
		indexer.commit();
		indexer.close();
	}

	private static List<Tweet> getTweets(String path) {
		List<Tweet> tweetList = new ArrayList<Tweet>();
		List<String> filenames = FileUtil.getFileNames(path);

		for (String filename : filenames) {
			List<String> lines = null;
			try {
				lines = FileUtil.load2List(filename);
			} catch (Exception e) {
				e.printStackTrace();
			}
			if(lines == null){
				continue;
			}
			for (String line : lines) {

				Tweet t = convertTweet((DBObject) JSON.parse(line));				

				tweetList.add(t);
			}
			//			System.out.println(filename);
		}
		return tweetList;
	}
	private static Tweet convertTweet(DBObject obj) {		
		Tweet tweet = new Tweet();
		tweet.setTweetId((Long) obj.get("tweetId"));
		tweet.setContents((String) obj.get("contents"));		
		tweet.setCreatedAt((Long) obj.get("createdAt"));
		tweet.setCrawledAt((Long) obj.get("crawledAt"));
		tweet.setLinked((Boolean)obj.get("linked"));
		@SuppressWarnings("unchecked")
		List<String> urls = (List<String>) obj.get("urls");
		if(urls != null){
			tweet.setUrls(urls);
		}				

		return tweet;
	}

}


메인클래스에서 보면 "index.properties"라는 인자를 사용하게 됩니다.


이는 properties에서 색인 할 데이터의 경로, 색인이 저장될 경로등을 설정하기 위한 것입니다.


index.properties 파일 내용을 살펴보면




	SHUTDOWN	=	false	
	TARGET_PATH	=	D://tweet
	INDEX_PATH	=	D://tweetIndex
	
	#open mode (create,append,create_or_append)
	OPEN_MODE	=	create
	
	#merge setting
	MERGE_POLICY	=	true
	MAX_MERGE_AT_ONCE	=	100
	SEGMENTS_PER_TIER	=	100


아래 merge setting은 루씬과 직접적인 관계가 있는 설정입니다만, 기본적인 검색기를 만드는데는 그냥 저 값을 사용해도 무방합니다.


SHUTDOWN은 추후에 실시간 색인을 위한 옵션으로 현재는 사용하지 않습니다.


TARGET_PATH는 아까 data.zip의 압축을 푼 경로를 적습니다.


INDEX_PATH는 색인 데이터가 저장될 공간으로 원하는 곳에 경로를 적어주시면 됩니다.


OPEN_MODE는 색인기를 실행시에 기존 데이터를 새로 덮어쓸지(create), 기존 데이터에 추가 할 지(append)의 여부를 나타냅니다.


위 설정 파일의 설정을 마치면 이제 실행만 시키면 색인이 완료됩니다.




======================= 색인 끝 ======================




색인이 완료된 후 색인된 데이터를 검색해보도록 하겠습니다.


검색은 매우 심플합니다.



package kr.peopleware.lucene.search.test;

import java.util.Date;

import kr.peopleware.lucene.search.Searcher;
import kr.peopleware.lucene.util.QueryBuilder;

import org.apache.lucene.analysis.cjk.CJKAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.util.Version;

public class SearchTester {

	/**
	 * @param args
	 */
	public static void main(String[] args) {	
		
		//설정 파일을 세팅하여 Searcher 생성
		Searcher search = new Searcher("search.properties");
		
//		search.setSort("tweetId",SortField.LONG,true);
		
		//색인된 데이터의 contents에 "감기"라는 검색어를 넣어서 검색
		Document[] docs = search.getDocuments(QueryBuilder.makeQuery("contents", "감기",new CJKAnalyzer(Version.LUCENE_36)));

		int count = 0;
		for (Document document : docs) {
			if(count == 10)break;
			System.out.println("["+count+"]");			
			System.out.println(document.get("urls"));
			System.out.println(document.get("tweetId"));
			Date date = new Date(Long.parseLong(document.get("createdAt")));
			System.out.println(date);
			System.out.println(document.get("contents"));
			System.out.println();
			count++;
		}
		System.out.println(docs.length);
		search.close();
	}

}


역시 search.properties란 파일을 사용하는데 search.properties에는 아래와 같이 색인 파일의 경로만이 저장되어있습니다.



	INDEX_PATH	=	D://tweetIndex


또한 QueryBuilder라는 static 메소드가 있는데 이는 질의어를 만들어주기 위한 메소드입니다. 


소스 내용은 아래와 같습니다만, 굳이 보실 필요는 없을 것 같습니다.



package kr.peopleware.lucene.util;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryTermVector;
import org.apache.lucene.search.TermQuery;

public class QueryBuilder {
 	public static Query makeQuery(String field,String q,Analyzer analyzer)
	{
		BooleanQuery bq = new BooleanQuery();
		QueryTermVector qtv = new QueryTermVector(q, analyzer);
		for(int i=0;i<qtv.size();i++)
		{
			bq.add(new TermQuery(new Term(field,qtv.getTerms()[i])), BooleanClause.Occur.MUST);
		}
		return bq;
	}
 	public static Query makeQuery(String field, Object a,Object b){
		Query nq = null;
		if(a.getClass() == Integer.class){
			nq = NumericRangeQuery.newIntRange(field, (Integer)a, (Integer)b, true, true);
			
		}else if(a.getClass() == Double.class){
			nq = NumericRangeQuery.newDoubleRange(field, (Double)a, (Double)b, true, true);
			
		}else if(a.getClass() == Float.class){
			nq = NumericRangeQuery.newFloatRange(field, (Float)a, (Float)b, true, true);
			
		}else if(a.getClass() == Long.class){
			nq = NumericRangeQuery.newLongRange(field, (Long)a, (Long)b, true, true);
			
		}
		return nq;
	}
}


이제 검색을 해보면 아래와 같은 결과를 보실 수 있습니다.





이클립스 프로젝트 파일도 함께 올려두겠습니다.


[루씬 프로젝트 파일]


lucene.zip


[루씬 프로젝트를 사용하는데 필요한 라이브러리]


common.zip



기본적인 검색 엔진은 이제 만들 수 있습니다만,


한글 검색 엔진은 "색인어 추출기"가 핵심이라고 말씀드릴 수 있습니다.


시간이 나는대로 형태소 분석기를 마무리 지어서 공개하도록 하겠습니다.

'Opensource > Lucene' 카테고리의 다른 글

루씬 기본적인 검색 엔진  (7) 2012.11.06
루씬 한글 검색 엔진 제작  (0) 2012.11.06
  1. darkly0828@naver.com 2013.01.15 14:59 신고

    글 잘 보았습니다. 많은 도움이 되네요~

  2. 안상준 2014.07.14 11:53 신고

    죄송하지만 Common page 를 어떻게 임포트 해야하는지 ;;;;;;;;;;;;;;;; 다른놈들은 에러없이 다 그냥 나오는데 kr.peopleware.util.file.FileUtil;
    이놈이 임포트 에러 뜨네요 폴더 자체를 Libarary 추가 해줫는데 해결이 안되네요 ㅠㅠ

    • shine_ing 2014.07.21 12:28 신고

      common도 별개의 프로젝트입니다. 그렇기 때문에 프로젝트 형식으로 import 해주시고 이클립스(를 사용하신다는 가정 하에)에서 빌드패스 부분에서 프로젝트를 통째로 잡아주시면 됩니다. 그래도 안되신다면 댓글 부탁드립니다~

  3. 2014.11.25 01:28

    비밀댓글입니다

  4. 아롱사태남 2014.11.25 01:29 신고

    안녕하세요 ^^ 요즘 오프소스에 관심이 많은 신입 개발자입니다.
    오픈소스 검색엔진에 대하여 검색하다가 루씬과 국내에서 개발한 패스트캣서치가 있던데
    신입인 저에게는 어떤걸로 먼저 뛰어드는게 좋을까요?
    루씬 단점은 한글 분석기가 지원이 안되고 초보자는 만들기 어렵다고 하더라구요 ㅜ
    패스트캣서치 단점은 국내에서 개발한지 별로되지않아 컴폼 받기가 힘들구요...
    사용해보시면서 루씬의 단점이라고 느겼던점과 장점을 가르쳐주셨으면 좋겠어요 그리고
    신입이라면 루씬을 도전해야할지 패스트캣서치를 도전해야할지 가르쳐주세요 !!

    • shine_ing 2014.11.25 09:51 신고

      저도 사실 루씬과 패스트캣 서치에 대해서 잘 알지는 못합니다만, 패스트캣서치가 루씬 베이스로 동작하기 때문에 아마 루씬을 먼저 보는게 낫지 않을까요? 그러나 업무상 적용할 용도라면 패스트캣서치가 훨씬 편리할 듯 보이네요

소스코드를 그냥 copy & paste를 해버리면 소스 자체를 읽기가 힘들다.


그래서 오픈소스로 SyntaxHighlighter 라는 라이브러리(?)가 있다.


이것을 설치해주면 티스토리에서도 소스코드 하이라이터를 사용할 수 있다.


먼저 다운로드를 한다. (다운로드)


먼저 admin에서 좌측 꾸미기에서 스킨을 클릭후 아래 화면과 같이 파일 업로드를 클릭한다.





이어 나오는 화면에서 하단에 있는 "추가" 버튼을 누른 뒤 아까 받아둔 SyntaxHighlighter 폴더의 styles와 scripts에 있는 파일들을 업로드 시킨다.






상단에 있는 HTML/CSS 버튼을 누른 후


skin.html에 있는 head 부분에 아래 내용을 집어 넣는다.


<p><script src="./images/shCore.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushBash.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushCpp.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushCSharp.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushCss.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushDelphi.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushDiff.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushGroovy.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushJava.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushJScript.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushPhp.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushPlain.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushPython.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushRuby.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushScala.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushSql.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushVb.js" type="text/javascript"></script></p>

<p><script src="./images/shBrushXml.js" type="text/javascript"></script></p>

<p> <link href="./images/shCore.css" type="text/css" rel="stylesheet"><link href="./images/shThemeDefault.css" type="text/css" rel="stylesheet"></p>

<p><script type="text/javascript"> SyntaxHighlighter.config.clipboardSwf = './images/clipboard.swf'; SyntaxHighlighter.all();</script></p>

<p></p>



최종으로 "저장" 버튼을 누른다.


이제 소스코드 하이라이터를 사용할 준비는 끝났다.


글쓰기 페이지에서 글을 잘 쓴 다음에 소스 코드는 가장 나중에 입력하는 편이 좋다.


왜냐하면 HTML 문법을 잘 모르는 사용자들에게 문단이니 뭐니 직접 다 처넣어야 하기 때문에....


그래서 소스가 들어갈 부분을 잘 확인한 다음에


글을 쓸 때 우측 상단에 있는 HTML을 누른다. 


그 다음에


<pre class="brush:java">

소스 코드 내용

</pre>


와 같이 사용한다.


실제 사용하게 되면 아래와 같이 화면에 출력된다.




package kr.peopleware.util.test;

import java.util.List;

import kr.peopleware.util.common.file.FileUtil;

public class FileUtilTester {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		List<String> lines = FileUtil.load2List("www.com.001", "EUC-KR");
		for (String line : lines) {
			System.out.println(line);
		}
	}

}


여기서 주의 사항이 하나 있다.


"<"를 사용할 때는 "&lt;"로 대체해야한다.


List<String>의 경우에는 List&lt;String>으로 대체하여야 한다. 여간 불편한게 아니지만..그래도 이렇게 해줘야 된다..아직까지는..ㅠㅠ


실제로 이 라이브러리를 제작자도 큰 이슈로 다루고 있다..조만간 해결책이 나오겠지..





'Tip > Blog' 카테고리의 다른 글

티스토리에 소스코드 집어넣기  (0) 2012.11.06
티스토리 카테고리 펼치기  (0) 2012.10.27

+ Recent posts