└── README.md /README.md: -------------------------------------------------------------------------------- 1 | ### Rouzeta 2 | - 설명 3 | - FST에 기반한 한국어 형태소 분석기 [Rouzeta](https://shleekr.github.io/)의 사용법 4 | 5 | - Rouzeta 설치시 필요한 패키지 6 | - [foma](https://code.google.com/archive/p/foma/) 7 | ``` 8 | # stack full을 피하기 위해서, int_stack.c를 수정하고 다시 컴파일해야함. 9 | $ cd foma-0.9.18 10 | $ vi int_stack.c 11 | ... 12 | #define MAX_STACK 2097152*10 13 | #define MAX_PTR_STACK 2097152*10 14 | 15 | static int a[MAX_STACK]; 16 | static int top = -1; 17 | ... 18 | $ make ; sudo make install 19 | ``` 20 | - [xerces-c](http://xerces.apache.org/xerces-c/download.cgi) 21 | ``` 22 | $ cd xerces-c-3.1.4 23 | $ ./configure ; make ; sudo make install 24 | ``` 25 | - [openfst](http://www.openfst.org/twiki/bin/view/FST/WebHome) 26 | ``` 27 | # <주의> 최신 버전으로 하면, kyfd 컴파일시 오류가 발생한다. 28 | $ openfst-1.3.2 29 | $ ./configure ; make ; sudo make install 30 | ``` 31 | - [kyfd](http://www.phontron.com/kyfd/) 32 | ``` 33 | $ cd kyfd-0.0.5 34 | $ ./configure ; make ; sudo make install 35 | ``` 36 | 37 | - Rouzeta 다운로드 & 소스 설명 38 | - [rouzeta](https://shleekr.github.io/public/data/rouzeta.tar.gz) 39 | ``` 40 | $ tar -zxvf rouzeta.tar.gz 41 | $ ls -R 42 | .: 43 | Rouzeta Tagger 44 | 45 | ./Rouzeta: 46 | korean.lexc kormoran.script morphrules.foma splithangul.foma 47 | 48 | ./Tagger: 49 | koreanuni.xml korfinaluni.fst korinvert.sym kydf testme.txt worduniprob.sym 50 | ``` 51 | - 소스 설명 52 | ``` 53 | - Rouzeta 54 | - korean.lexc 55 | 형태소들과 각 형태소들의 접속 네트워크가 있는 사전 56 | 예를 들어, 57 | 58 | LEXICON Root 59 | ncLexicon ; ! 보통명사 60 | ... 61 | acLexicon ; ! 접속부사 62 | 63 | LEXICON acLexicon ! 접속부사 64 | 고로/ac acNext ; 65 | 곧/ac acNext ; 66 | 그나/ac acNext ; 67 | 하지만/ac acNext ; 68 | ... 69 | 70 | LEXICON acNext 71 | finLexicon ; 72 | srLexicon ; 73 | pxLexicon ; 74 | nnLexicon ; 75 | ... 76 | 77 | 위와 같이 기술하면, '고로/ac #', '곧/ac "/sr', '하지만/ac 은/px', '하지만/ac 첫째/nn' 78 | 등의 형태소 sequence가 valid하다는 의미이다. 79 | 일반적으로 어떤 태그와 어떤 태그가 결합가능한지 여부를 여기에 기술한다. 80 | 81 | - morphrules.foma 82 | 어휘형과 표층형간의 규칙이 적혀 있는 룰 파일 83 | 예를 들어, 84 | 85 | define IrrConjD %_ㄷ -> %_ㄹ || _ %/irrd %/vb ㅇ ; 86 | 87 | IrrConjD 규칙은 'ㄷ-불규칙'을 기술해서 아래와 같이 변환가능하게 한다. 88 | 'ㄷ ㅏ %_ㄷ irrd vb ㅇ' -> 'ㄷ ㅏ %_ㄹ irrd vb ㅇ' 89 | 90 | - splithangul.foma 91 | utf-8 한글 음절을 자소로 분리하는 규칙 92 | 예를 들어, 93 | 94 | '형 -> ㅎ ㅕ %_ㅇ' 95 | 96 | 음절을 자소단위로 분리하는 규칙이다. '%_ㅇ'에서 '%_'는 종성을 의미한다. 97 | 98 | - kormoran.script 99 | 위 3가지를 가지고 하나의 유한 상태 변환기 (finite state transducer)를 만드는 파일 100 | 즉, 형태소 분석용 FST(Rouzeta FST)를 만든다. 101 | 예를 들어, 102 | 103 | define SingleWordPhrase Lexicon .o. SplitHangul .o. ! 사전으로부터 자소 분리 104 | AlternationRules .o. ! 변환규칙 적용 105 | RemoveTags .o. ! 품사/불규칙 태그 삭제 106 | SplitHangul.i .o. ! 자소를 한글로 바꿈 107 | ~$[Syllable] .o. ~$[KorChar] .o. ~$[CodaOnly] ; ! 자소열, 자모만, 종성 글자 제거 108 | 109 | 이 코드는 korean.lexc에 기술된 Lexicon을 자소단위로 분리하고 110 | morphrules.foma의 변환규칙을 적용한 다음, 이 결과를 다시 음절단위로 변환하는 111 | FST를 구성한다. 112 | 113 | korean.lexc에 있는 엔트리를 가지고 설명하면 아래와 같다. 114 | 115 | LEXICON vbNext 116 | ... 117 | epLexicon 118 | ... 119 | LEXICON epLexicon 120 | ... 121 | efLexicon 122 | 123 | 깨닫/irrd/vb vbNext ; ! vbLexicon, 동사 124 | 았/ep epNext ; ! epLexicon, 선어말어미 125 | 다/ef efNext ; ! efLexicon, 어말어미 126 | 127 | vbLexicon epLexicon efLexicon 128 | --------------------------------------------- 129 | {깨 닫 /irrd /vb} -> {았 /ep} -> {다 /ef} 130 | 131 | Lexicon 네트웍을 구성하면 vbLexicon의 모든 엔트리와 epLexicon의 모든 엔트리의 연결이 생성되어 있을 것이고 132 | epLexicon과 efLexicon도 마찬가지다. 133 | 여기서 음절을 자소로 분리한다음 IrrConjD 규칙을 적용하면 134 | 135 | ㄲ ㅐ ㄷ ㅏ %_ㄷ /irrd /vb ㅇ ㅏ %_ㅆ /ep ㄷ ㅏ /ef 136 | => 137 | ㄲ ㅐ ㄷ ㅏ %_ㄹ /irrd /vb ㅇ ㅏ %_ㅆ /ep ㄷ ㅏ /ef 138 | 139 | 이 결과에서 '품사/불규칙 태그'등을 삭제해야 다시 자소를 음절로 바꿀 수 있으므로, 삭제하면 140 | 141 | ㄲ ㅐ ㄷ ㅏ %_ㄹ /irrd /vb ㅇ ㅏ %_ㅆ /ep ㄷ ㅏ /ef 142 | => 143 | ㄲ ㅐ ㄷ ㅏ %_ㄹ ㅇ ㅏ %_ㅆ ㄷ ㅏ 144 | 145 | 이것을 다시 음절단위로 변환하자. 146 | 147 | ㄲ ㅐ ㄷ ㅏ %_ㄹ ㅇ ㅏ %_ㅆ ㄷ ㅏ 148 | => 149 | 깨 달 았 다 150 | 151 | composition 결과물에서 불필요한 노이즈(자소열, 자모만, 종성 글자 등등)는 제거한다. 152 | 153 | 154 | - Tagger 155 | - korfinaluni.fst 156 | 형태소 분석용 FST를 inverse한 FST와 와 unigram FST를 composition한 FST 157 | (openfst를 사용해서 빌드한 결과) 158 | - korinvert.sym 159 | kyfd의 input symbol 정의 160 | - worduniprob.sym 161 | kyfd의 output symbol 정의 162 | - koreanuni.xml 163 | kyfd를 사용하기 위한 설정파일 164 | - kyfd 165 | 컴파일된 바이너리인데, 별도로 소스를 다운받고 설치해서 사용하자. 166 | ``` 167 | 168 | - 형태소분석 169 | ``` 170 | $ cd KFST/Rouzeta 171 | $ foma 172 | foma[0]: source kormoran.script 173 | .... 174 | defined AlternationRules: 26.7 MB. 360257 states, 1750440 arcs, Cyclic. 175 | defined SingleWordPhrase: 42.9 MB. 127415 states, 2797946 arcs, Cyclic. 176 | defined Delim: 202 bytes. 2 states, 1 arc, 1 path. 177 | defined NormalizeDelim: 364 bytes. 2 states, 4 arcs, Cyclic. 178 | defined WPWithMark: 42.9 MB. 127417 states, 2798456 arcs, Cyclic. 179 | defined SentenceWithMark: 42.9 MB. 127417 states, 2798457 arcs, Cyclic. 180 | defined DeleteMarkUp: 376 bytes. 1 state, 3 arcs, Cyclic. 181 | defined DeleteMarkDown: 376 bytes. 1 state, 3 arcs, Cyclic. 182 | defined Sentence: 43.0 MB. 127416 states, 2806708 arcs, Cyclic. 183 | 43.0 MB. 127416 states, 2806708 arcs, Cyclic. 184 | foma[1]: up 185 | apply up> 형태소분석은 186 | 형태소/nc분석/nc은/nc 187 | 형태소/nc분석/nc은/pt 188 | 형태소/nc분/nc석/nc은/nc 189 | 형태소/nc분/nc석/nc은/pt 190 | .... 191 | ``` 192 | 193 | - 형태소분석용 FST를 save & load 194 | ``` 195 | ... 196 | foma[1]: save stack kor.stack 197 | foma[1]: quit 198 | $ foma 199 | foma[0]: load stack kor.stack 200 | foma[1]: up 201 | apply up> 형태소분석은 202 | ... 203 | ``` 204 | 205 | - 커맨드라인에서 형태소 분석 206 | ``` 207 | $ flookup kor.stack 208 | 형태소분석은 209 | 형태소분석은 형/nc태/nc소/nc분/nc석/nc은/nc 210 | 형태소분석은 형/nc태/nc소/nc분/nc석/nc은/pt 211 | 형태소분석은 형/nc태/nc소/nc분/nc석/nd은/nc 212 | 형태소분석은 형/nc태/nc소/nc분/nc석/nd은/nr 213 | .... 214 | ``` 215 | 216 | - 태깅 217 | ``` 218 | $ cd KFST/Tagger 219 | # 시스템에 설치한 kyfd를 사용한다. 220 | $ rm kyfd 221 | $ cat testme.txt | kyfd koreanuni.xml 222 | -------------------------- 223 | -- Started Kyfd Decoder -- 224 | -------------------------- 225 | 226 | Loaded configuration, initializing decoder... 227 | Loading fst korfinaluni.fst... 228 | Done initializing, took 0 seconds 229 | Decoding... 230 | 나 /np 는 /pt 학 교 /nc 에 서 /pa 공 부 /na 하 /xv _ㅂ 니 다 /ef . /sf 231 | .선 /nc 을 /po 긋 /irrs /vb 어 /ex 버 리 /vx 었 /ep 다 /ef . /sf 232 | .고 맙 /irrb /vj 었 /ep 다 /ef . /sf 233 | .나 /np 는 /pt 답 /nc 을 /po 모 르 /irrl /vb 아 /ec . /sf 234 | .지 나 /vb _ㄴ /ed 1 8 /nb 일 /nc 하 오 /nc 3 /nb 시 /nc 경 남 /nr 마 산 시 /nr 235 | .색 /nc 이 /ps 하 얗 /irrh /vj 어 서 /ef 예 쁘 /vj 었 /ep 다 /ef . /sf 236 | .일 찍 /ad 일 어 나 /vb 는 /ed 새 /nc 가 /ps 피 곤 /ns 하 /xj 다 /ef ( /sl 웃 음 /nc ) /sr . /sf 237 | .꽃 /nc 이 /ps 핀 /nc 곳 /nc 을 /po 알 /vb 고 /ec 있 /vj 다 /ef . /sf 238 | .이 것 /nm 은 /pt 사 과 /nc 이 /pp 다 /ef . /sf 239 | .상 자 /nc 를 /po 연 /nc 사 람 /nc 은 /pt 그 /np 이 /pp 다 /ef . /sf 240 | .사 과 /nc _ㄹ /po 먹 /vb 겠 /ep 다 /ef . /sf 241 | .향 약 /nc 은 /pt 향 촌 /nc 의 /pd 교 육 /nc 과 /pc 경 제 /nc 를 /po 관 장 /nc 해 /nc 서 원 /nc 을 /po 운 영 /na 하 /xv 면 서 /ef 중 앙 /nc 정 부 /nc 등 용 문 /nc 인 /nc 대 과 /nc 응 시 자 격 /nc 을 /po 부 여 /na 하 /xv 는 /ed 향 시 /nc 를 /po 주 관 /nc 하 고 /pq 흉 년 /nc 이 /ps 들 /vb 면 /ex 곡 식 /nc 을 /po 나 누 /vb 는 /ed 상 호 부 조 /nc 와 /pc 작 황 /nc 에 /pa 따 르 /vb _ㄴ /ed 소 작 료 /nc 연 동 적 용 /nc 을 /po 정 하 /vb 는 가 /ef 하 /vb 면 /ex 풍 속 사 범 /nc 에 /pa 대 하 /vb 어 /ex 형 벌 /nc 을 /po 가 하 /vb 는 /ed 사 법 부 /nc 역 할 /nc 까 지 /px 담 당 /na 하 /xv 었 었 /ep 다 /ef . /sf 242 | . Done decoding, took 0 seconds 243 | 244 | # 문장을 캐릭터단위로 분리해서 공백구분자로 입력해야한다. 원래 공백문자는 로 치환한다. 245 | ``` 246 | 247 | - `SingleWordPhrase` 규칙을 보면 알겠지만, Rouzeta FST는 입력이 어휘형(자소단위,기호 등등)이고 출력이 표층형(어절 등)이다. 그런데, 형태소분석기는 이를 역으로 처리하는 프로그램이므로 실제 사용시에는 inverse 연산으로 FST를 뒤집어서 사용해야한다. `korfinaluni.fst` FST binary 파일은 이와 같이 inverse 연산한 결과와 unigram FST를 composition한 결과물이다. 어떻게 생겼는지 직접 보려면 `fstprint`를 사용해서 출력해보면 된다. 248 | ``` 249 | $ fstprint --isymbols=korinvert.sym --osymbols=worduniprob.sym korfinaluni.fst > korfinaluni.fst.txt 250 | # fst의 마지막 필드는 weight인데, unigram FST를 composition하면서 들어온 값이다. 251 | $ more korfinaluni.fst.txt 252 | ... 253 | 572 3373 /nc 2.7578125 254 | 572 3375 /nr 1.61328125 255 | 573 4265 /vb 6.78320312 256 | 573 3378 /nc 2.7578125 257 | 573 3378 /nr 1.61328125 258 | 574 13160 · · 7.18847656 259 | 574 13161 가 가 4.54980469 260 | 574 13162 간 간 6.78320312 261 | 574 3696 거 거 6.27246094 262 | 574 13163 게 게 7.18847656 263 | 574 13164 경 경 6.49609375 264 | ... 265 | 133579 114136 길 기 266 | 133579 114137 긴 기 267 | 133579 114138 기 기 268 | ... 269 | 270 | $ fstinfo korfinaluni.fst 271 | fst type vector 272 | arc type standard 273 | input symbol table none 274 | output symbol table none 275 | # of states 230911 276 | # of arcs 2993031 277 | ... 278 | ``` 279 | 280 | - `korfinaluni.fst`를 만드는 방법 281 | - [유니그램 한국어 형태소 분석기를 만들기 위한 자료들](https://shleekr.github.io/) 포스트 참조 282 | 283 | - 바이그램 태거 284 | - [바이그램 한국어 품사 태거](https://shleekr.github.io/) 285 | - 지금까지 언급한 태거는 유니그램 확률만을 사용했지만, 바이그램 태거는 생성확률과 전이확률을 이용한다. fst의 사이즈는 좀 많이 커지지만(600M) 태깅 정확률은 좋아진다. 286 | - 사용법 287 | ``` 288 | # 앞서 기술한 유니그램 태거의 상용법과 동일하다. 289 | $ curl -OL https://shleekr.github.io/public/data/bitagger_aa 290 | $ curl -OL https://shleekr.github.io/public/data/bitagger_ab 291 | $ cat bitagger_aa bitagger_ab > bitaggerfile.tar.gz 292 | $ tar -zxvf bitaggerfile.tar.gz 293 | $ cd BiTagger/ 294 | $ ls . 295 | koreanbi.xml korinvert.sym korinvertwordbifinal.fst testme.txt wordbiprob.sym 296 | # 시스템에 설치된 kyfd를 사용 297 | $ rm kyfd 298 | $ cat testme.txt | kyfd koreanbi.xml 299 | -------------------------- 300 | -- Started Kyfd Decoder -- 301 | -------------------------- 302 | Loaded configuration, initializing decoder... 303 | Loading fst korinvertwordbifinal.fst... 304 | Done initializing, took 6 seconds 305 | Decoding... 306 | 나 /np 는 /pt 학 교 /nc 에 서 /pa 공 부 /na 하 /xv _ㅂ 니 다 /ef . /sf 307 | 선 /nc 을 /po 긋 /irrs /vb 어 /ex 버 리 /vx 었 /ep 다 /ef . /sf 308 | 고 맙 /irrb /vj 었 /ep 다 /ef . /sf 309 | ... 310 | ``` 311 | 312 | ### Kyfd 313 | - 입력 문자열을 linear FST로 만들고 이것과 Tagger FST(`korfinaluni.fst`)을 composition한 다음, begin -> end까지 shortest path를 찾으면, 그 path가 바로 tagging 결과가 된다. 이런 과정을 라이브러리로 구성해둔 decoder가 kyfd이다. 이 소스를 수정하면 좀더 편리한 API를 만들 수 있을 것 같다. 314 | - kyfd를 fork해서 사용하기 편하게 수정한 버전, [kyfd](https://github.com/dsindex/kyfd) 315 | - 이것을 가지고 c, python interface를 개발, [ckyfd](https://github.com/dsindex/ckyfd) 316 | - 형태소 분석결과를 정리해서 볼 수 있다. 317 | ``` 318 | $ cd ckyfd/wrapper/python 319 | $ python test_rouzeta.py -c koreanuni.xml 320 | Loading fst korfinaluni.fst... 321 | 나는 학교에서 공부합니다. 322 | 0 나는 323 | 1 학교에서 324 | 2 공부합니다. 325 | 나 np None NP 0 0 326 | 는 pt None JX 0 1 327 | 학교 nc None NNG 1 2 328 | 에서 pa None JKB 1 3 329 | 공부 na None NNG 2 4 330 | 하 xv None XSV 2 5 331 | _ㅂ니다 ef None EF 2 6 332 | . sf None SF 2 7 333 | 나는 답을 몰라. 334 | 0 나는 335 | 1 답을 336 | 2 몰라. 337 | 나 np None NP 0 0 338 | 는 pt None JX 0 1 339 | 답 nc None NNG 1 2 340 | 을 po None JKO 1 3 341 | 모르 vb irrl VV 2 4 342 | 아 ec None EC 2 5 343 | . sf None SF 2 6 344 | ``` 345 | 346 | ### Foma 347 | - 영어에서 foma를 이용한 형태소분석기 만들기, [morpological analysis with FSTs](http://foma.sourceforge.net/dokuwiki/doku.php?id=wiki:morphtutorial) 348 | - 이것을 읽어 보면, Rouzeta에 있는 korean.lexc, morphrules.foma, kormoran.script 등을 더 잘 이해할 수 있을 것이다. 349 | 350 | ### Problems on Rouzeta and Kyfd 351 | - 미등록어 352 | ``` 353 | 예) 츠 카 그 룹 이 발 매 한 ' 츠 카 S ' 라 는 이 름 의 실 버 폰 . 354 | 355 | 여기서 '츠 카'가 사전에 존재하지 않는 단어이므로 path가 존재하지 않아서 분석이 실패한다.( WARNING, no path found ) 356 | 만약 입력문장을 단어별로 분리해서 형태소분석하고 그 결과물들을 붙여서 태깅을 하는 구조라면 357 | 중간에 어느 하나가 오분석되어도 큰 문제는 안될 것 같다. 하지만, 현재 모델에서는 one-path로 358 | 형태소분석과 태깅을 동시에 수행하기 때문에 이런 문제가 생긴다. 359 | 현재 rouzeta는 이렇게 path를 찾지 못하는 경우가 매우 많기 때문에 어떤 방식으로 360 | 개선해야 할 지 생각해봐야할 것 같다. 361 | ``` 362 | - 미등록 심벌 363 | ``` 364 | 예) 가 벼 운 문 구 수 정 은 제 외 ) 기 본 질 문 365 | 366 | 여기서 '▲'는 unknown symbol인데, kyfd에서 "-unknown ''" 이렇게 지정해주면 아래와 같이 분석된다. 367 | 368 | 가 볍 /irrb /vj _ㄴ /ed 문 구 /nc 수 정 /nc 은 /pt 제 외 /nc ) /sr 기 본 /nc 질 문 /nc 369 | 370 | unknown을 ''로 지정했기 때문에, 미등록 심벌은 무조건 사라지게 된다. 371 | unknown을 지정하지 않거나, 분석대상 문자열에 존재하는 심벌로 지정한 경우는 오류가 발생한다. 372 | 373 | 정확하게 처리하려면 rouzeta에 '' 심벌을 추가할 필요가 있어 보인다. 374 | ``` 375 | - 메모리 증가 376 | ``` 377 | kyfd에 보면 'reload' 옵션이 있는데, 예를 들어 '500'으로 설정하면 매 500 문장을 분석한 이후 378 | fst를 다시 로딩하게 된다. 이것은 입력을 fst로 만들어서 compose할때마다 메모리가 늘어나기 때문에, 379 | 적당한 시점에 이렇게 늘어난 메모리를 초기화 시켜주려고 만든 옵션인 것 같다. 380 | 381 | "Reload the model after a certain number of sentences. Can be used 382 | to flush the memory of expanded states for dynamically composed models 383 | that become unmanagably large after a time." 384 | 385 | 대량의 문서를 입력해서 메모리 증가를 모니터링한 결과 그렇게 큰 변화는 없었다. 386 | 만약, 메모리가 꾸준히 증가한다면 kyfd를 그대로 서비스에 사용하기에는 적합하지 않을 것이다. 387 | (메모리가 늘어나지 않도록 수정하거나 별도의 fst decoding 모듈을 개발해야 함) 388 | ``` 389 | - 분석 속도 390 | ``` 391 | 적절한 길이의 문장단위로 입력했을 때, fst 기반 분석기의 속도는 다른 형태소분석기보다 빠를 것이다. 392 | 하지만, 문장의 길이가 더 길어지거나 입력이 문서인 경우는 문제가 된다. 문장이 길수록 탐색해야하는 composed fst의 크기도 393 | 커지기 때문이다. 따라서, 문서로 입력되거나 문장의 길이가 매우 긴 경우를 처리하는 별도의 방법이 필요하다. 여기에는 394 | 문장분리기를 사용하거나, 윈도우를 이동시키며 분석하는 방법 등이 있을 수 있다. 이런 방법을 사용하면 미등록어로 인해 395 | 분석이 실패할때도 해당하는 문장이나 어절만 제외하고 나머지를 분석할 수 있게 되는 효과도 얻을 수 있다. 396 | 397 | 또 하나의 문제는 매번 입력 문장을 linear fst로 만들고 composition한 다음, shortest path를 탐색한다는 데서 기인한다. 398 | composition과 shortest path에 들어가는 비용은 생각보다 매우 크다. decoding까지 포함했을 경우, 일반 형태소분석기보다 399 | 분석 속도가 느린 경우도 많다. 따라서, composition 없이 단순히 fst를 탐색하면서 분석을 끝내는 방법이 필요하다. 400 | 이렇게 되면 위 '메모리 증가'에서 언급된 문제도 사라질 것이다. 401 | ``` 402 | 403 | --------------------------------------------------------------------------------