├── .github
├── doc
│ ├── khaiii_for_space_error.pptx
│ └── network.pptx
├── img
│ ├── multi-task-learning.png
│ ├── network.png
│ ├── pull-request-to-develop.png
│ └── win_emb_f.png
└── pull_request_template.md
├── .gitignore
├── CMakeLists.txt
├── CONTRIBUTING.md
├── LICENSE
├── NOTICE.md
├── README.md
├── cmake
├── CodeCoverage.cmake
├── FindGperftools.cmake
├── FusedMultiplyAdd.cmake
├── Hunter
│ └── config.cmake
└── HunterGate.cmake
├── docker
└── Dockerfile
├── include
└── khaiii
│ ├── KhaiiiApi.hpp
│ ├── khaiii_api.h
│ └── khaiii_dev.h
├── munjong
├── apply_patch.py
├── convert_jamo_to_compat.py
├── detect_sejong_period_error.py
├── fix_final_symbol_error.py
├── make_patch.py
├── recover_english_case.py
├── recover_raw_morph_mismatch.py
├── recover_wide_quotation.py
└── remove_sejong_period_error.py
├── requirements.txt
├── rsc
├── Makefile
├── bin
│ ├── compile_errpatch.py
│ ├── compile_model.py
│ ├── compile_preanal.py
│ └── compile_restore.py
└── src
│ ├── base.config.json
│ ├── base.errpatch.auto
│ ├── base.errpatch.manual
│ ├── base.model.pickle
│ ├── char_align.map
│ ├── large.config.json
│ ├── large.errpatch.auto
│ ├── large.errpatch.manual
│ ├── large.model.pickle
│ ├── preanal.auto
│ ├── preanal.manual
│ ├── restore.dic
│ ├── vocab.in
│ ├── vocab.out
│ └── vocab.out.more
├── src
├── main
│ ├── cpp
│ │ ├── khaiii
│ │ │ ├── Config.cpp
│ │ │ ├── Config.hpp
│ │ │ ├── Embed.cpp
│ │ │ ├── Embed.hpp
│ │ │ ├── ErrPatch.cpp
│ │ │ ├── ErrPatch.hpp
│ │ │ ├── KhaiiiImpl.cpp
│ │ │ ├── KhaiiiImpl.hpp
│ │ │ ├── MemMapFile.hpp
│ │ │ ├── Morph.cpp
│ │ │ ├── Morph.hpp
│ │ │ ├── Preanal.cpp
│ │ │ ├── Preanal.hpp
│ │ │ ├── Resource.cpp
│ │ │ ├── Resource.hpp
│ │ │ ├── Restore.cpp
│ │ │ ├── Restore.hpp
│ │ │ ├── Sentence.cpp
│ │ │ ├── Sentence.hpp
│ │ │ ├── Tagger.cpp
│ │ │ ├── Tagger.hpp
│ │ │ ├── Trie.cpp
│ │ │ ├── Trie.hpp
│ │ │ ├── Word.cpp
│ │ │ ├── Word.hpp
│ │ │ ├── khaiii_api.cpp
│ │ │ ├── khaiii_dev.cpp
│ │ │ ├── nn
│ │ │ │ ├── Conv1d.cpp
│ │ │ │ ├── Conv1d.hpp
│ │ │ │ ├── Linear.cpp
│ │ │ │ ├── Linear.hpp
│ │ │ │ ├── tensor.cpp
│ │ │ │ └── tensor.hpp
│ │ │ └── util.hpp
│ │ └── main.cpp
│ └── python
│ │ ├── MANIFEST.in.in
│ │ ├── khaiii
│ │ ├── __init__.py
│ │ ├── __init__.py.in
│ │ ├── khaiii.py
│ │ ├── munjong
│ │ │ ├── __init__.py
│ │ │ ├── libpatch.py
│ │ │ └── sejong_corpus.py
│ │ ├── resource
│ │ │ ├── __init__.py
│ │ │ ├── char_align.py
│ │ │ ├── jaso.py
│ │ │ ├── morphs.py
│ │ │ ├── resource.py
│ │ │ ├── trie.py
│ │ │ └── vocabulary.py
│ │ └── train
│ │ │ ├── dataset.py
│ │ │ ├── embedder.py
│ │ │ ├── evaluator.py
│ │ │ ├── models.py
│ │ │ ├── sentence.py
│ │ │ ├── tagger.py
│ │ │ └── trainer.py
│ │ └── setup.py.in
└── test
│ ├── cpp
│ ├── khaiii
│ │ ├── ErrPatchTest.cpp
│ │ ├── KhaiiiApiTest.cpp
│ │ ├── KhaiiiApiTest.hpp
│ │ ├── KhaiiiDevTest.cpp
│ │ └── PreanalTest.cpp
│ └── test_main.cpp
│ └── python
│ └── test_khaiii
│ ├── __init__.py
│ └── test_khaiii.py
└── train
├── eval.py
├── extract_errpatch.py
├── extract_preanal.py
├── hd_validate_errpatch.bash
├── make_vocab.py
├── map_char_to_tag.py
├── pickle_model.py
├── requirements.txt
├── split_corpus.py
├── tag.py
├── train.py
├── transform_corpus.py
└── validate_errpatch.py
/.github/doc/khaiii_for_space_error.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakao/khaiii/3d0c8944374163b1107fd6690ccf3a408430a02d/.github/doc/khaiii_for_space_error.pptx
--------------------------------------------------------------------------------
/.github/doc/network.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakao/khaiii/3d0c8944374163b1107fd6690ccf3a408430a02d/.github/doc/network.pptx
--------------------------------------------------------------------------------
/.github/img/multi-task-learning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakao/khaiii/3d0c8944374163b1107fd6690ccf3a408430a02d/.github/img/multi-task-learning.png
--------------------------------------------------------------------------------
/.github/img/network.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakao/khaiii/3d0c8944374163b1107fd6690ccf3a408430a02d/.github/img/network.png
--------------------------------------------------------------------------------
/.github/img/pull-request-to-develop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakao/khaiii/3d0c8944374163b1107fd6690ccf3a408430a02d/.github/img/pull-request-to-develop.png
--------------------------------------------------------------------------------
/.github/img/win_emb_f.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakao/khaiii/3d0c8944374163b1107fd6690ccf3a408430a02d/.github/img/win_emb_f.png
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | 설명 (Description)
2 | ----
3 | _이 문구를 지우고 여기에 내용을 적어주세요. (Remove this sentence and describe here.)_
4 |
5 | ~~_겁내지 말아요, 저희는 한글을 사랑합니다._~~
6 |
7 |
8 | 개발자를 위한 가이드 (Developer's Guide)
9 | ----
10 | 만약 khaiii에 pull request가 처음이라면 [개발자를 위한 가이드](https://github.com/kakao/khaiii/wiki#%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B0%80%EC%9D%B4%EB%93%9C) 문서들을 한번 읽어보시길 권고드립니다.
11 |
12 | If this is your first pull request for khaiii, please see the [Developer's Guide](https://github.com/kakao/khaiii/wiki#%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B0%80%EC%9D%B4%EB%93%9C).
13 |
14 |
15 | 체크 리스트 (Checklist)
16 | ----
17 | pull request 전에 아래 체크 리스트들을 만족하는 지 확인한 후 체크('x') 표시를 해주시기 바랍니다.
18 |
19 | Before you submit pull requests, please check(set 'x') to the checklist below.
20 |
21 | - [ ] master 브랜치가 아니라 **develop** 브랜치에 머지하도록 pull request를 작성 중이신가요? (Did you merge into **develop** branch not master?)
22 | - [ ] `build/test/khaiii` 프로그램을 실행하여 **테스트**가 성공했나요? (Did all **tests** are passed when you ran as `build/test/khaiii`)
23 | - [ ] **PyLint** 툴을 실행하여 발생한 에러를 모두 수정하셨나요? (Did you fix all errors after running **PyLint**?)
24 | - [ ] **CppLint** 툴을 실행하여 발생한 에러를 모두 수정하셨나요? (Did you fix all errors after running **CppLint**?)
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | __pycache__/
3 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Submitting Pull Requests
2 |
3 | When you are sending a pull request, please sign the [CLA](https://cla-assistant.io/kakao/khaiii)(Contributor Licensing Agreement) for Individual.
4 | If you need a Contributor Licensing Agreement for Corporate, please [contact us](mailto:oss@kakaocorp.com).
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | khaiii
2 | ====
3 | khaiii는 "Kakao Hangul Analyzer III"의 첫 글자들만 모아 만든 이름으로 카카오에서 개발한 세 번째 형태소분석기입니다. 두 번째 버전의 형태소분석기 이름인 dha2 (Daumkakao Hangul Analyzer 2)를 계승한 이름이기도 합니다.
4 |
5 | 형태소는 언어학에서 일정한 의미가 있는 가장 작은 말의 단위로 발화체 내에서 따로 떼어낼 수 있는 것을 말합니다. 즉, 더 분석하면 뜻이 없어지는 말의 단위입니다. 형태소분석기는 단어를 보고 형태소 단위로 분리해내는 소프트웨어를 말합니다. 이러한 형태소분석은 자연어 처리의 가장 기초적인 절차로 이후 구문 분석이나 의미 분석으로 나아가기 위해 가장 먼저 이루어져야 하는 과정으로 볼 수 있습니다. (한국어 위키피디아에서 인용)
6 |
7 |
8 | 데이터 기반
9 | ----
10 | 기존 버전이 사전과 규칙에 기반해 분석을 하는 데 반해 khaiii는 데이터(혹은 기계학습) 기반의 알고리즘을 이용하여 분석을 합니다. 학습에 사용한 코퍼스는 국립국어원에서 배포한 [21세기 세종계획 최종 성과물](https://ithub.korean.go.kr/user/noticeView.do?boardSeq=1&articleSeq=16)을 저희 카카오에서 오류를 수정하고 내용을 일부 추가하기도 한 것입니다.
11 |
12 | 전처리 과정에서 오류가 발생하는 문장을 제외하고 약 85만 문장, 천만 어절의 코퍼스를 사용하여 학습을 했습니다. 코퍼스와 품사 체계에 대한 자세한 내용은 [코퍼스](https://github.com/kakao/khaiii/wiki/%EC%BD%94%ED%8D%BC%EC%8A%A4) 문서를 참고하시기 바랍니다.
13 |
14 |
15 | 알고리즘
16 | ----
17 | 기계학습에 사용한 알고리즘은 신경망 알고리즘들 중에서 Convolutional Neural Network(CNN)을 사용하였습니다. 한국어에서 형태소분석은 자연어처리를 위한 가장 기본적인 전처리 과정이므로 속도가 매우 중요한 요소라고 생각합니다. 따라서 자연어처리에 많이 사용하는 Long-Short Term Memory(LSTM)와 같은 Recurrent Neural Network(RNN) 알고리즘은 속도 면에서 활용도가 떨어질 것으로 예상하여 고려 대상에서 제외하였습니다.
18 |
19 | CNN 모델에 대한 상세한 내용은 [CNN 모델](https://github.com/kakao/khaiii/wiki/CNN-%EB%AA%A8%EB%8D%B8) 문서를 참고하시기 바랍니다.
20 |
21 |
22 | 성능
23 | ----
24 | ### 정확도
25 |
26 | #### v0.3
27 | CNN 모델의 주요 하이퍼 파라미터는 분류하려는 음절의 좌/우 문맥의 크기를 나타내는 win 값과, 음절 임베딩의 차원을 나타내는 emb 값입니다. win 값은 {2, 3, 4, 5, 7, 10}의 값을 가지며, emb 값은 {20, 30, 40, 50, 70, 100, 150, 200, 300, 500}의 값을 가집니다. 따라서 이 두 가지 값의 조합은 6 x 10으로 총 60가지를 실험하였고 아래와 같은 성능을 보였습니다. 성능 지표는 정확률과 재현율의 조화 평균값인 F-Score입니다.
28 |
29 | 
30 |
31 | win 파라미터의 경우 3 혹은 4에서 가장 좋은 성능을 보이며 그 이상에서는 오히려 성능이 떨어집니다. emb 파라미터의 경우 150까지는 성능도 같이 높아지다가 그 이상에서는 별 차이가 없습니다. 최 상위 5위 중 비교적 작은 모델은 win=3, emb=150으로 F-Score 값은 97.11입니다. 이 모델을 large 모델이라 명명합니다.
32 |
33 | #### v0.4
34 | [띄어쓰기 오류에 강건한 모델을 위한 실험](https://github.com/kakao/khaiii/wiki/%EB%9D%84%EC%96%B4%EC%93%B0%EA%B8%B0-%EC%98%A4%EB%A5%98%EC%97%90-%EA%B0%95%EA%B1%B4%ED%95%9C-%EB%AA%A8%EB%8D%B8%EC%9D%84-%EC%9C%84%ED%95%9C-%EC%8B%A4%ED%97%98)을 통해 모델을 개선하였습니다. v0.4 모델은 띄어쓰기가 잘 되어있지 않은 입력에 대해 보다 좋은 성능을 보이는데 반해 세종 코퍼스에서는 다소 정확도가 떨어집니다. 이러한 점을 보완하기 위해 base 및 large 모델의 파라미터를 아래와 같이 조금 변경했습니다.
35 |
36 | * base 모델: win=4, emb=35, F-Score: 94.96
37 | * large 모델: win=4, emb=180, F-Score: 96.71
38 |
39 |
40 | ### 속도
41 |
42 | #### v0.3
43 | 모델의 크기가 커지면 정확도가 높아지긴 하지만 그만큼 계산량 또한 많아져 속도가 떨어집니다. 그래서 적당한 정확도를 갖는 모델 중에서 크기가 작아 속도가 빠른 모델을 base 모델로 선정하였습니다. F-Score 값이 95 이상이면서 모델의 크기가 작은 모델은 win=3, emb=30이며 F-Score는 95.30입니다.
44 |
45 | 속도를 비교하기 위해 1만 문장(총 903KB, 문장 평균 91)의 텍스트를 분석해 비교했습니다. base 모델의 경우 약 10.5초, large 모델의 경우 약 78.8초가 걸립니다.
46 |
47 | #### v0.4
48 | 모델의 크기가 커짐에 따라 아래와 같이 base, large 모델의 속도를 다시 측정했으며 v0.4 버전에서 다소 느려졌습니다.
49 |
50 | * base 모델: 10.8 -> 14.4
51 | * large 모델: 87.3 -> 165
52 |
53 |
54 | 사용자 사전
55 | ----
56 | 신경망 알고리즘은 소위 말하는 블랙박스 알고리즘으로 결과를 유추하는 과정을 사람이 따라가기가 쉽지 않습니다. 그래서 오분석이 발생할 경우 모델의 파라미터를 수정하여 바른 결과를 내도록 하는 것이 매우 어렵습니다. 이를 위해 khaiii에서는 신경망 알고리즘의 앞단에 기분석 사전을 뒷단에 오분석 패치라는 두 가지 사용자 사전 장치를 마련해 두었습니다.
57 |
58 | ### 기분석 사전
59 | 기분석 사전은 단일 어절에 대해 문맥에 상관없이 일괄적인 분석 결과를 갖는 경우에 사용합니다. 예를 들어 아래와 같은 엔트리가 있다면,
60 |
61 | 입력 어절 | 분석 결과
62 | --------|--------
63 | 이더리움* | 이더리움/NNP
64 |
65 | 문장에서 `이더리움`으로 시작하는 모든 어절은 신경망 알고리즘을 사용하지 않고 `이더리움/NNP`로 동일하게 분석합니다.
66 |
67 | 세종 코퍼스에서 분석 모호성이 없는 어절들로부터 자동으로 기분석 사전을 추출할 경우 약 8만 개의 엔트리가 생성됩니다. 이를 적용할 경우 약간의 속도 향상도 있어서 base 모델에 적용하면 약 9.2초로 10% 정도 속도 향상이 있었습니다.
68 |
69 | 기분석 사전의 기술 방법 및 자세한 내용은 [기분석 사전 문서](https://github.com/kakao/khaiii/wiki/%EA%B8%B0%EB%B6%84%EC%84%9D-%EC%82%AC%EC%A0%84)를 참고하시기 바랍니다.
70 |
71 |
72 | ### 오분석 패치
73 | 오분석 패치는 여러 어절에 걸쳐서 충분한 문맥과 함께 오분석을 바로잡아야 할 경우에 사용합니다. 예를 들어 아래와 같은 엔트리가 있다면,
74 |
75 | 입력 텍스트 | 오분석 결과 | 정분석 결과
76 | ---------|-----------|---------
77 | 이 다른 것 | 이/JKS + _ + 다/VA + 른/MM + _ + 것/NNB | 이/JKS + _ + 다르/VA + ㄴ/ETM + _ + 것/NNB
78 |
79 | 만약 khaiii가 위 "오분석 결과"와 같이 오분석을 발생한 경우에 한해 바른 분석 결과인 "정분석 결과"로 수정합니다. 여기서 "\_"는 어절 간 경계, 즉 공백을 의미합니다.
80 |
81 | 오분석 패치의 기술 방법 및 자세한 내용은 [오분석 패치 문서](https://github.com/kakao/khaiii/wiki/%EC%98%A4%EB%B6%84%EC%84%9D-%ED%8C%A8%EC%B9%98)를 참고하시기 바랍니다.
82 |
83 |
84 | 빌드 및 설치
85 | ----
86 | khaiii의 빌드 및 설치에 관해서는 [빌드 및 설치 문서](https://github.com/kakao/khaiii/wiki/%EB%B9%8C%EB%93%9C-%EB%B0%8F-%EC%84%A4%EC%B9%98)를 참고하시기 바랍니다.
87 |
88 |
89 | Contributing
90 | ----
91 | khaiii에 기여하실 분들은 [CONTRIBUTING](CONTRIBUTING.md) 및 [개발자를 위한 가이드](https://github.com/kakao/khaiii/wiki#%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%A5%BC-%EC%9C%84%ED%95%9C-%EA%B0%80%EC%9D%B4%EB%93%9C) 문서를 참고하시기 바랍니다.
92 |
93 |
94 | License
95 | ----
96 | This software is licensed under the [Apache 2 license](LICENSE), quoted below.
97 |
98 | Copyright 2018 Kakao Corp.
99 |
100 | Licensed under the Apache License, Version 2.0 (the "License"); you may not
101 | use this project except in compliance with the License. You may obtain a copy
102 | of the License at http://www.apache.org/licenses/LICENSE-2.0.
103 |
104 | Unless required by applicable law or agreed to in writing, software
105 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
106 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
107 | License for the specific language governing permissions and limitations under
108 | the License.
109 |
--------------------------------------------------------------------------------
/cmake/FindGperftools.cmake:
--------------------------------------------------------------------------------
1 | # Tries to find Gperftools.
2 | #
3 | # Usage of this module as follows:
4 | #
5 | # find_package(Gperftools)
6 | #
7 | # Variables used by this module, they can change the default behaviour and need
8 | # to be set before calling find_package:
9 | #
10 | # Gperftools_ROOT_DIR Set this variable to the root installation of
11 | # Gperftools if the module has problems finding
12 | # the proper installation path.
13 | #
14 | # Variables defined by this module:
15 | #
16 | # GPERFTOOLS_FOUND System has Gperftools libs/headers
17 | # GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler)
18 | # GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers
19 |
20 | find_library(GPERFTOOLS_TCMALLOC
21 | NAMES tcmalloc
22 | HINTS ${Gperftools_ROOT_DIR}/lib)
23 |
24 | find_library(GPERFTOOLS_PROFILER
25 | NAMES profiler
26 | HINTS ${Gperftools_ROOT_DIR}/lib)
27 |
28 | find_library(GPERFTOOLS_TCMALLOC_AND_PROFILER
29 | NAMES tcmalloc_and_profiler
30 | HINTS ${Gperftools_ROOT_DIR}/lib)
31 |
32 | find_path(GPERFTOOLS_INCLUDE_DIR
33 | NAMES gperftools/heap-profiler.h
34 | HINTS ${Gperftools_ROOT_DIR}/include)
35 |
36 | set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_TCMALLOC_AND_PROFILER})
37 |
38 | include(FindPackageHandleStandardArgs)
39 | find_package_handle_standard_args(
40 | Gperftools
41 | DEFAULT_MSG
42 | GPERFTOOLS_LIBRARIES
43 | GPERFTOOLS_INCLUDE_DIR)
44 |
45 | mark_as_advanced(
46 | Gperftools_ROOT_DIR
47 | GPERFTOOLS_TCMALLOC
48 | GPERFTOOLS_PROFILER
49 | GPERFTOOLS_TCMALLOC_AND_PROFILER
50 | GPERFTOOLS_LIBRARIES
51 | GPERFTOOLS_INCLUDE_DIR)
52 |
--------------------------------------------------------------------------------
/cmake/FusedMultiplyAdd.cmake:
--------------------------------------------------------------------------------
1 | include(CheckCXXCompilerFlag)
2 | check_cxx_compiler_flag(-mfma fma_compiles)
3 | if(fma_compiles)
4 | include(CheckCXXSourceRuns)
5 | set(test_src
6 | "#include
7 | double fma_wrap(double x, double y, double z) { return fma(x, y, z); }
8 | int main() { double a = fma_wrap(1.2, 3.4, 5.6); return 0; }")
9 | set(CMAKE_REQUIRED_FLAGS -mfma)
10 | check_cxx_source_runs("${test_src}" fma_runs)
11 | if(fma_runs)
12 | message(STATUS "[khaiii] fused multiply add option enabled")
13 | add_definitions(-mfma)
14 | else()
15 | message(WARNING "[khaiii] cpu does not have fused multiply add instruction")
16 | endif()
17 | else()
18 | message(WARNING "[khaiii] compiler does not support fused multiply add option")
19 | endif()
20 |
--------------------------------------------------------------------------------
/cmake/Hunter/config.cmake:
--------------------------------------------------------------------------------
1 | hunter_config(Boost VERSION 1.68.0-p1)
2 | hunter_config(cxxopts VERSION 2.1.1-pre)
3 | hunter_config(Eigen VERSION 3.3.5)
4 | hunter_config(fmt VERSION 4.1.0)
5 | hunter_config(GTest VERSION 1.8.0-hunter-p11)
6 | hunter_config(nlohmann_json VERSION 3.3.0)
7 | hunter_config(spdlog VERSION 0.16.3-p1)
8 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM pytorch/pytorch:latest
2 | MAINTAINER nako.sung@navercorp.com
3 |
4 | RUN git clone https://github.com/kakao/khaiii.git
5 | WORKDIR /workspace/khaiii
6 |
7 | RUN pip install cython
8 | RUN pip install --upgrade pip
9 | RUN pip install -r requirements.txt
10 |
11 | RUN mkdir build
12 | WORKDIR /workspace/khaiii/build
13 |
14 | RUN cmake ..
15 | RUN make all
16 | RUN make resource
17 |
18 | RUN apt-get update -y
19 | RUN apt-get install -y language-pack-ko
20 | RUN locale-gen en_US.UTF-8
21 | RUN update-locale LANG=en_US.UTF-8
22 |
--------------------------------------------------------------------------------
/include/khaiii/KhaiiiApi.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2017-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #ifndef INCLUDE_KHAIII_KHAIIIAPI_HPP_
8 | #define INCLUDE_KHAIII_KHAIIIAPI_HPP_
9 |
10 |
11 |
12 | //////////////
13 | // includes //
14 | //////////////
15 | #include
16 | #include
17 | #include // NOLINT
18 | #include
19 |
20 | #include "khaiii/khaiii_api.h"
21 |
22 |
23 | namespace khaiii {
24 |
25 |
26 | class KhaiiiApi {
27 | public:
28 | /**
29 | * create khaiii api object
30 | * @return shared pointer of khaiii api object
31 | */
32 | static std::shared_ptr create();
33 |
34 | /**
35 | * open resources
36 | * @param rsc_dir resource directory
37 | * @param opt_str option string (JSON format)
38 | */
39 | virtual void open(std::string rsc_dir = "", std::string opt_str = "") = 0;
40 |
41 | /**
42 | * analyze input text
43 | * @param input input text
44 | * @param opt_str runtime option (JSON format)
45 | * @return results
46 | */
47 | virtual const khaiii_word_t* analyze(const char* input, const char* opt_str) = 0;
48 |
49 | /**
50 | * free memories of analyzed results
51 | * @param results results got from analyze() function
52 | */
53 | virtual void free_results(const khaiii_word_t* results) = 0;
54 |
55 | virtual void close() = 0; ///< close resources
56 | };
57 |
58 |
59 | /**
60 | * standard exception thrown by khaiii api
61 | */
62 | class Except: public std::exception {
63 | public:
64 | /**
65 | * @param msg error message
66 | * @param file source file (for debug)
67 | * @param line line number in source file (for debug)
68 | * @param func function name (for debug)
69 | */
70 | explicit Except(std::string msg, const char* file = nullptr, const int line = 0,
71 | const char* func = nullptr);
72 |
73 | virtual const char* what() const noexcept;
74 |
75 | std::string debug(); ///< message with some debug information
76 |
77 | private:
78 | std::string _msg; ///< error message
79 | const char* _file = nullptr; ///< source file
80 | const int _line = 0; ///< line number in source file
81 | const char* _func = nullptr; ///< function name
82 | };
83 |
84 |
85 | } // namespace khaiii
86 |
87 |
88 | #endif // INCLUDE_KHAIII_KHAIIIAPI_HPP_
89 |
--------------------------------------------------------------------------------
/include/khaiii/khaiii_api.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2017-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #ifndef INCLUDE_KHAIII_KHAIII_API_H_
8 | #define INCLUDE_KHAIII_KHAIII_API_H_
9 |
10 |
11 | ///////////////
12 | // constants //
13 | ///////////////
14 | #define KHAIII_VERSION_MAJOR 0
15 | #define KHAIII_VERSION_MINOR 4
16 | #define _MAC2STR(m) #m
17 | #define _JOIN_VER(x,y) _MAC2STR(x) "." _MAC2STR(y) // NOLINT
18 | #define KHAIII_VERSION _JOIN_VER(KHAIII_VERSION_MAJOR,KHAIII_VERSION_MINOR) // NOLINT
19 |
20 |
21 | #ifdef __cplusplus
22 | extern "C" {
23 | #endif
24 |
25 |
26 | /**
27 | * morpheme data structure
28 | */
29 | typedef struct khaiii_morph_t_ {
30 | const char* lex; ///< lexical
31 | const char* tag; ///< part-of-speech tag
32 | int begin; ///< morpheme begin position
33 | int length; ///< morpheme length
34 | char reserved[8]; ///< reserved
35 | const struct khaiii_morph_t_* next; ///< next pointer
36 | } khaiii_morph_t;
37 |
38 |
39 | /**
40 | * word data structure
41 | */
42 | typedef struct khaiii_word_t_ {
43 | int begin; ///< word begin position
44 | int length; ///< word length
45 | char reserved[8]; ///< reserved
46 | const khaiii_morph_t* morphs; ///< morpheme list
47 | const struct khaiii_word_t_* next; ///< next pointer
48 | } khaiii_word_t;
49 |
50 |
51 | /**
52 | * get version string
53 | * @return version string like "2.1"
54 | */
55 | const char* khaiii_version();
56 |
57 |
58 | /**
59 | * open resources
60 | * @param rsc_dir resource directory
61 | * @param opt_str option string (JSON format)
62 | * @return handle. -1 if failed
63 | */
64 | int khaiii_open(const char* rsc_dir, const char* opt_str);
65 |
66 |
67 | /**
68 | * analyze input text
69 | * @param handle handle got from open() function
70 | * @param input input text
71 | * @param opt_str runtime option (JSON format)
72 | * @return results. NULL if failed
73 | */
74 | const khaiii_word_t* khaiii_analyze(int handle, const char* input, const char* opt_str);
75 |
76 |
77 | /**
78 | * free memories of analyzed results
79 | * @param handle handle got from open() function
80 | * @param results results got from analyze() function
81 | */
82 | void khaiii_free_results(int handle, const khaiii_word_t* results);
83 |
84 |
85 | /**
86 | * close resources
87 | * @param handle handle got from open() function
88 | */
89 | void khaiii_close(int handle);
90 |
91 |
92 | /**
93 | * get last error
94 | * @param handle handle got from open() function
95 | * @return message
96 | */
97 | const char* khaiii_last_error(int handle);
98 |
99 |
100 | #ifdef __cplusplus
101 | }
102 | #endif
103 |
104 |
105 | #endif // INCLUDE_KHAIII_KHAIII_API_H_
106 |
--------------------------------------------------------------------------------
/include/khaiii/khaiii_dev.h:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2018-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #ifndef INCLUDE_KHAIII_KHAIII_DEV_H_
8 | #define INCLUDE_KHAIII_KHAIII_DEV_H_
9 |
10 |
11 | //////////////
12 | // includes //
13 | //////////////
14 | #include
15 |
16 |
17 | #ifdef __cplusplus
18 | extern "C" {
19 | #endif
20 |
21 |
22 | /**
23 | * 분석을 수행하고 오분석 패치를 실행하기 직전에 멈춘 다음 그 결과를 리턴한다.
24 | * @param handle handle got from open() function
25 | * @param input input text
26 | * @param opt_str runtime option (JSON format)
27 | * @param output output value for each character
28 | * @return output length. -1 if failed
29 | */
30 | int khaiii_analyze_bfr_errpatch(int handle, const char* input, const char* opt_str,
31 | int16_t* output);
32 |
33 | /**
34 | * 로그 레벨을 지정한다.
35 | * @param name 로거 이름. "all"인 경우 모든 로거
36 | * @param level 로거 레벨. trace, debug, info, warn, err, critical
37 | * @return 0 if success. -1 if failed
38 | */
39 | int khaiii_set_log_level(const char* name, const char* level);
40 |
41 |
42 | /**
43 | * 여러 로그 레벨을 한꺼번에 지정한다.
44 | * @param name_level_pairs 로거 (이름, 레벨) 쌍의 리스트.
45 | * "all:warn,console:info,Tagger:debug"와 같은 형식
46 | * @return 0 if success. -1 if failed
47 | */
48 | int khaiii_set_log_levels(const char* name_level_pairs);
49 |
50 |
51 | #ifdef __cplusplus
52 | }
53 | #endif
54 |
55 |
56 | #endif // INCLUDE_KHAIII_KHAIII_DEV_H_
57 |
--------------------------------------------------------------------------------
/munjong/apply_patch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | apply patch to original Sejong corpus
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2019-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser, Namespace
16 | import logging
17 | import os
18 | import shutil
19 |
20 | from khaiii.munjong import libpatch
21 |
22 |
23 | #############
24 | # functions #
25 | #############
26 | def run(args: Namespace):
27 | """
28 | run function which is the start point of program
29 | Args:
30 | args: program arguments
31 | """
32 | if not os.path.exists(args.modified):
33 | logging.info('creating modified corpus dir: %s', args.modified)
34 | os.mkdir(args.modified)
35 |
36 | for name in sorted(os.listdir(args.original)):
37 | if not name.endswith('.txt'):
38 | continue
39 | org_path = '%s/%s' % (args.original, name)
40 | mod_path = '%s/%s' % (args.modified, name)
41 | patch_path = '%s/%s.patch' % (args.patch, name[:-len('.txt')])
42 | if os.path.exists(patch_path):
43 | logging.info('[%s] + [%s] = [%s]', org_path, patch_path, mod_path)
44 | libpatch.apply(org_path, args.org_enc, patch_path, mod_path, args.mod_enc)
45 | else:
46 | logging.info('[%s] = [%s]', org_path, mod_path)
47 | shutil.copyfile(org_path, mod_path)
48 |
49 |
50 | ########
51 | # main #
52 | ########
53 | def main():
54 | """
55 | main function processes only argument parsing
56 | """
57 | parser = ArgumentParser(description='apply patch to original Sejong corpus')
58 | parser.add_argument('-o', '--original', help='original corpus dir', metavar='DIR',
59 | required=True)
60 | parser.add_argument('-p', '--patch', help='patch dir', metavar='DIR', required=True)
61 | parser.add_argument('-m', '--modified', help='modified corpus output dir', metavar='DIR',
62 | required=True)
63 | parser.add_argument('--org-enc', help='original corpus encoding ',
64 | metavar='ENCODING', default='UTF-16')
65 | parser.add_argument('--mod-enc', help='modified corpus encoding ',
66 | metavar='ENCODING', default='UTF-8')
67 | parser.add_argument('--debug', help='enable debug', action='store_true')
68 | args = parser.parse_args()
69 |
70 | if args.debug:
71 | logging.basicConfig(level=logging.DEBUG)
72 | else:
73 | logging.basicConfig(level=logging.INFO)
74 |
75 | run(args)
76 |
77 |
78 | if __name__ == '__main__':
79 | main()
80 |
--------------------------------------------------------------------------------
/munjong/convert_jamo_to_compat.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | 한글 자모 영역의 코드를 호환 영역으로 변환
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2019-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser
16 | import logging
17 | import sys
18 |
19 | from khaiii.munjong.sejong_corpus import WORD_ID_PTN
20 | from khaiii.resource.jaso import norm_compat
21 |
22 |
23 | #############
24 | # functions #
25 | #############
26 | def _norm(text: str) -> str:
27 | """
28 | 정규화를 수행하는 함수
29 | Args:
30 | text: 입력 텍스트
31 | Returns:
32 | 정규화된 텍스트
33 | """
34 | normalized = norm_compat(text)
35 | normalized = normalized.replace('ᆞ', 'ㆍ') # 0x119e -> 0x318d
36 | normalized = normalized.replace('ᄝ', 'ㅱ') # 0x111d -> 0x3171
37 | return normalized
38 |
39 |
40 | def run():
41 | """
42 | run function which is the start point of program
43 | """
44 | for line in sys.stdin:
45 | line = line.rstrip('\r\n')
46 | if not WORD_ID_PTN.match(line):
47 | print(line)
48 | continue
49 | wid, word, morph = line.split('\t')
50 | print('{}\t{}\t{}'.format(wid, _norm(word), _norm(morph)))
51 |
52 |
53 | ########
54 | # main #
55 | ########
56 | def main():
57 | """
58 | main function processes only argument parsing
59 | """
60 | parser = ArgumentParser(description='한글 자모 영역의 코드를 호환 영역으로 변환')
61 | parser.add_argument('--input', help='input file ', metavar='FILE')
62 | parser.add_argument('--output', help='output file ', metavar='FILE')
63 | parser.add_argument('--debug', help='enable debug', action='store_true')
64 | args = parser.parse_args()
65 |
66 | if args.input:
67 | sys.stdin = open(args.input, 'r', encoding='UTF-8')
68 | if args.output:
69 | sys.stdout = open(args.output, 'w', encoding='UTF-8')
70 | if args.debug:
71 | logging.basicConfig(level=logging.DEBUG)
72 | else:
73 | logging.basicConfig(level=logging.INFO)
74 |
75 | run()
76 |
77 |
78 | if __name__ == '__main__':
79 | main()
80 |
--------------------------------------------------------------------------------
/munjong/detect_sejong_period_error.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | detect period error of Sejong corpus
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2019-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser
16 | import logging
17 | import os
18 | import re
19 | import sys
20 | from typing import Iterator, TextIO, Tuple
21 |
22 | from khaiii.munjong.sejong_corpus import Morph, WORD_ID_PTN
23 |
24 |
25 | #############
26 | # functions #
27 | #############
28 | def _get_two_lines(fin: TextIO) -> Iterator[Tuple[str, str]]:
29 | """
30 | get two lines tuple from file (generator)
31 | Args:
32 | fin: input file
33 | Yields:
34 | current line
35 | next line
36 | """
37 | curr_line = fin.readline().rstrip('\r\n')
38 | for next_line in fin:
39 | next_line = next_line.rstrip('\r\n')
40 | yield curr_line, next_line
41 | curr_line = next_line
42 |
43 |
44 | def _is_correct_eos(line: str) -> bool:
45 | """
46 | whether correct end of sentence or not
47 | Args:
48 | line: line (word)
49 | Returns:
50 | whether correct or not
51 | """
52 | _, _, morphs_str = line.split('\t')
53 | if re.match(r'.+/EF \+ ./SF$', morphs_str):
54 | return True
55 | if re.match(r'.+/SF \+ [\'"’”」\]]/SS$', morphs_str):
56 | return True
57 | morphs = [Morph.parse(_) for _ in morphs_str.split(' + ')]
58 | tags_str = '+'.join([_.tag for _ in morphs])
59 | if tags_str.endswith('+SF+SS+JKQ') or tags_str.endswith('+SF+SS+VCP+ETM'):
60 | return True
61 | return False
62 |
63 |
64 | def run():
65 | """
66 | run function which is the start point of program
67 | """
68 | file_name = os.path.basename(sys.stdin.name)
69 | for line_num, (curr_line, next_line) in enumerate(_get_two_lines(sys.stdin), start=1):
70 | cols = curr_line.split('\t')
71 | if len(cols) != 3 or not WORD_ID_PTN.match(cols[0]):
72 | continue
73 | if '/SF + ' not in cols[2] or not next_line.startswith(''):
74 | continue
75 | if _is_correct_eos(curr_line):
76 | continue
77 | print('{}:{}\t{}'.format(file_name, line_num, curr_line))
78 |
79 |
80 | ########
81 | # main #
82 | ########
83 | def main():
84 | """
85 | main function processes only argument parsing
86 | """
87 | parser = ArgumentParser(description='detect period error of Sejong corpus')
88 | parser.add_argument('--input', help='input file ', metavar='FILE')
89 | parser.add_argument('--output', help='output file ', metavar='FILE')
90 | parser.add_argument('--debug', help='enable debug', action='store_true')
91 | args = parser.parse_args()
92 |
93 | if args.input:
94 | sys.stdin = open(args.input, 'rt')
95 | if args.output:
96 | sys.stdout = open(args.output, 'wt')
97 | if args.debug:
98 | logging.basicConfig(level=logging.DEBUG)
99 | else:
100 | logging.basicConfig(level=logging.INFO)
101 |
102 | run()
103 |
104 |
105 | if __name__ == '__main__':
106 | main()
107 |
--------------------------------------------------------------------------------
/munjong/fix_final_symbol_error.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | fix final symbol errors on Sejong corpus
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2019-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser
16 | import logging
17 | import os
18 | import sys
19 |
20 | from khaiii.munjong.sejong_corpus import Morph, Word, WORD_ID_PTN
21 |
22 |
23 | #############
24 | # functions #
25 | #############
26 | def _attach_missing_symbol(word: Word):
27 | """
28 | attach missing symbol
29 | Args:
30 | word: Word object
31 | """
32 | raw_word = word.raw
33 | raw_morph = ''.join([_.lex for _ in word.morphs])
34 | if not raw_word.startswith(raw_morph) or len(raw_word) != len(raw_morph)+1:
35 | return
36 | last_symbol = raw_word[-1]
37 | if last_symbol == '.' and word.morphs[-1].tag == 'EC':
38 | word.morphs.append(Morph('.', 'SF'))
39 | elif last_symbol == ',':
40 | word.morphs.append(Morph(',', 'SP'))
41 | elif last_symbol == '"':
42 | word.morphs.append(Morph('"', 'SS'))
43 |
44 |
45 | def run():
46 | """
47 | run function which is the start point of program
48 | """
49 | file_name = os.path.basename(sys.stdin.name)
50 | for line_num, line in enumerate(sys.stdin, start=1):
51 | line = line.rstrip('\r\n')
52 | if not WORD_ID_PTN.match(line):
53 | print(line)
54 | continue
55 | word = Word.parse(line, file_name, line_num)
56 | _attach_missing_symbol(word)
57 | print(word)
58 |
59 |
60 | ########
61 | # main #
62 | ########
63 | def main():
64 | """
65 | main function processes only argument parsing
66 | """
67 | parser = ArgumentParser(description='fix final symbol errors on Sejong corpus')
68 | parser.add_argument('--input', help='input file ', metavar='FILE')
69 | parser.add_argument('--output', help='output file ', metavar='FILE')
70 | parser.add_argument('--debug', help='enable debug', action='store_true')
71 | args = parser.parse_args()
72 |
73 | if args.input:
74 | sys.stdin = open(args.input, 'rt')
75 | if args.output:
76 | sys.stdout = open(args.output, 'wt')
77 | if args.debug:
78 | logging.basicConfig(level=logging.DEBUG)
79 | else:
80 | logging.basicConfig(level=logging.INFO)
81 |
82 | run()
83 |
84 |
85 | if __name__ == '__main__':
86 | main()
87 |
--------------------------------------------------------------------------------
/munjong/make_patch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | make patch from two Sejong corpora
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2019-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser, Namespace
16 | import logging
17 | import os
18 |
19 | from khaiii.munjong import libpatch
20 |
21 |
22 | #############
23 | # functions #
24 | #############
25 | def run(args: Namespace):
26 | """
27 | run function which is the start point of program
28 | Args:
29 | args: program arguments
30 | """
31 | if not os.path.exists(args.patch):
32 | logging.info('creating patch dir: %s', args.patch)
33 | os.mkdir(args.patch)
34 |
35 | for name in sorted(os.listdir(args.original)):
36 | if not name.endswith('.txt'):
37 | continue
38 | org_path = '%s/%s' % (args.original, name)
39 | mod_path = '%s/%s' % (args.modified, name)
40 | patch_path = '%s/%s.patch' % (args.patch, name[:-len('.txt')])
41 | logging.info('[%s] - [%s] = [%s]', org_path, mod_path, patch_path)
42 | patches = libpatch.make(org_path, args.org_enc, mod_path, args.mod_enc)
43 | if patches:
44 | logging.info('creating patch file: %s', patch_path)
45 | with open(patch_path, 'w', encoding='UTF-8') as fout:
46 | for patch in patches:
47 | print(patch, file=fout)
48 | elif os.path.exists(patch_path):
49 | logging.info('removing existing patch file: %s', patch_path)
50 | os.remove(patch_path)
51 |
52 |
53 | ########
54 | # main #
55 | ########
56 | def main():
57 | """
58 | main function processes only argument parsing
59 | """
60 | parser = ArgumentParser(description='make patch from two Sejong corpora')
61 | parser.add_argument('-o', '--original', help='original corpus dir', metavar='DIR',
62 | required=True)
63 | parser.add_argument('-m', '--modified', help='modified corpus dir', metavar='DIR',
64 | required=True)
65 | parser.add_argument('-p', '--patch', help='patch output dir', metavar='DIR', required=True)
66 | parser.add_argument('--org-enc', help='original corpus encoding ',
67 | metavar='ENCODING', default='UTF-16')
68 | parser.add_argument('--mod-enc', help='modified corpus encoding ',
69 | metavar='ENCODING', default='UTF-8')
70 | parser.add_argument('--debug', help='enable debug', action='store_true')
71 | args = parser.parse_args()
72 |
73 | if args.debug:
74 | logging.basicConfig(level=logging.DEBUG)
75 | else:
76 | logging.basicConfig(level=logging.INFO)
77 |
78 | run(args)
79 |
80 |
81 | if __name__ == '__main__':
82 | main()
83 |
--------------------------------------------------------------------------------
/munjong/recover_english_case.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | recover cases of English letters in Sejong corpus
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2019-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser
16 | import copy
17 | import logging
18 | import os
19 | import re
20 | import sys
21 |
22 | from khaiii.munjong.sejong_corpus import Word, WORD_ID_PTN
23 |
24 |
25 | #############
26 | # functions #
27 | #############
28 | def _recover(word: Word):
29 | """
30 | recover cases
31 | Args:
32 | word: Word object
33 | """
34 | word_letters = [_ for _ in word.raw if re.match(r'[a-zA-Z]', _)]
35 | letter_idx = -1
36 | is_recovered = False
37 | word_copy = copy.deepcopy(word)
38 | for morph in word_copy.morphs:
39 | for idx, char in enumerate(morph.lex):
40 | if not re.match(r'[a-zA-Z]', char):
41 | continue
42 | letter_idx += 1
43 | if word_letters[letter_idx] == char:
44 | continue
45 | morph.lex = morph.lex[:idx] + word_letters[letter_idx] + morph.lex[idx+1:]
46 | is_recovered = True
47 | if is_recovered:
48 | logging.info('%s => %s', str(word), word_copy.morph_str())
49 | word.morphs = word_copy.morphs
50 |
51 |
52 | def run():
53 | """
54 | run function which is the start point of program
55 | """
56 | file_name = os.path.basename(sys.stdin.name)
57 | for line_num, line in enumerate(sys.stdin, start=1):
58 | line = line.rstrip('\r\n')
59 | if not WORD_ID_PTN.match(line):
60 | print(line)
61 | continue
62 | word = Word.parse(line, file_name, line_num)
63 | try:
64 | _recover(word)
65 | except IndexError as idx_err:
66 | logging.error('%s(%d): %s: %s', file_name, line_num, idx_err, word)
67 | print(word)
68 |
69 |
70 | ########
71 | # main #
72 | ########
73 | def main():
74 | """
75 | main function processes only argument parsing
76 | """
77 | parser = ArgumentParser(description='recover cases of English letters in Sejong corpus')
78 | parser.add_argument('--input', help='input file ', metavar='FILE')
79 | parser.add_argument('--output', help='output file ', metavar='FILE')
80 | parser.add_argument('--debug', help='enable debug', action='store_true')
81 | args = parser.parse_args()
82 |
83 | if args.input:
84 | sys.stdin = open(args.input, 'r', encoding='UTF-8')
85 | if args.output:
86 | sys.stdout = open(args.output, 'w', encoding='UTF-8')
87 | if args.debug:
88 | logging.basicConfig(level=logging.DEBUG)
89 | else:
90 | logging.basicConfig(level=logging.INFO)
91 |
92 | run()
93 |
94 |
95 | if __name__ == '__main__':
96 | main()
97 |
--------------------------------------------------------------------------------
/munjong/recover_raw_morph_mismatch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | 어절의 원문과 형태소 분석 결과의 문자가 정규화하면 같지만 코드가 다른 경우 원문의 문자로 복원
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2019-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser
16 | import logging
17 | import os
18 | import sys
19 |
20 | from khaiii.munjong.sejong_corpus import Morph, ParseError, Word, WORD_ID_PTN
21 |
22 |
23 | #############
24 | # functions #
25 | #############
26 | def _recover(line: str) -> str:
27 | """
28 | 문자를 복원한다.
29 | Args:
30 | line: 어절 라인
31 | Returns:
32 | 복원된 라인
33 | """
34 | wid, raw, morphs_str = line.split('\t')
35 | raw_idx = 0
36 | morphs = []
37 | for token_str in morphs_str.split(' + '):
38 | morph = Morph.parse(token_str)
39 | lex = []
40 | for _ in range(len(morph.lex)):
41 | try:
42 | lex.append(raw[raw_idx])
43 | raw_idx += 1
44 | except IndexError as idx_err:
45 | logging.error(line)
46 | raise idx_err
47 | morph.lex = ''.join(lex)
48 | morphs.append(morph)
49 | morphs_new = ' + '.join([str(m) for m in morphs])
50 | logging.debug('%s\t%s\t%s => %s', wid, raw, morphs_str, morphs_new)
51 | return '{}\t{}\t{}'.format(wid, raw, morphs_new)
52 |
53 |
54 | def run():
55 | """
56 | run function which is the start point of program
57 | """
58 | file_name = os.path.basename(sys.stdin.name)
59 | for line_num, line in enumerate(sys.stdin, start=1):
60 | line = line.rstrip('\r\n')
61 | if not WORD_ID_PTN.match(line):
62 | print(line)
63 | continue
64 | try:
65 | Word.parse(line, file_name, line_num)
66 | except ParseError as par_err:
67 | if 'raw-morph mismatch' in str(par_err):
68 | line = _recover(line)
69 | else:
70 | raise par_err
71 | print(line)
72 |
73 |
74 | ########
75 | # main #
76 | ########
77 | def main():
78 | """
79 | main function processes only argument parsing
80 | """
81 | parser = ArgumentParser(description='어절의 원문과 형태소 분석 결과의 문자가 정규화하면 같지만 코드가 다른 경우'
82 | ' 원문의 문자로 복원')
83 | parser.add_argument('--input', help='input file ', metavar='FILE')
84 | parser.add_argument('--output', help='output file ', metavar='FILE')
85 | parser.add_argument('--debug', help='enable debug', action='store_true')
86 | args = parser.parse_args()
87 |
88 | if args.input:
89 | sys.stdin = open(args.input, 'r', encoding='UTF-8')
90 | if args.output:
91 | sys.stdout = open(args.output, 'w', encoding='UTF-8')
92 | if args.debug:
93 | logging.basicConfig(level=logging.DEBUG)
94 | else:
95 | logging.basicConfig(level=logging.INFO)
96 |
97 | run()
98 |
99 |
100 | if __name__ == '__main__':
101 | main()
102 |
--------------------------------------------------------------------------------
/munjong/recover_wide_quotation.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | recover wide char quotations in Sejong corpus
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2019-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser
16 | import logging
17 | import os
18 | import sys
19 |
20 | from khaiii.munjong.sejong_corpus import Word, WORD_ID_PTN
21 |
22 |
23 | #############
24 | # constants #
25 | #############
26 | _QUOT_NORM = {
27 | '"': '"',
28 | '“': '"',
29 | '”': '"',
30 | "'": "'",
31 | "‘": "'",
32 | "’": "'",
33 | "`": "'",
34 | }
35 |
36 |
37 | #############
38 | # functions #
39 | #############
40 | def _recover(word: Word):
41 | """
42 | recover wide char quotations
43 | Args:
44 | word: Word object
45 | """
46 | word_quots = [_ for _ in word.raw if _ in _QUOT_NORM]
47 | morph_quots = []
48 | for idx, morph in enumerate(word.morphs):
49 | if morph.tag != 'SS' or morph.lex not in _QUOT_NORM:
50 | continue
51 | morph_quots.append((idx, morph))
52 | quot_idx = len(morph_quots)-1
53 | if len(word_quots) <= quot_idx or _QUOT_NORM[word_quots[quot_idx]] != _QUOT_NORM[morph.lex]:
54 | logging.error('%d-th quots are different: %s', quot_idx+1, word)
55 | return
56 | if len(word_quots) != len(morph_quots):
57 | morph_quots = [_ for _ in word.morph_str() if _ in _QUOT_NORM]
58 | if word_quots != morph_quots:
59 | logging.error('number of quots are different: %s', word)
60 | return
61 | for word_char, (idx, morph) in zip(word_quots, morph_quots):
62 | if word_char == morph.lex:
63 | continue
64 | morph.lex = word_char
65 |
66 |
67 | def run():
68 | """
69 | run function which is the start point of program
70 | """
71 | file_name = os.path.basename(sys.stdin.name)
72 | for line_num, line in enumerate(sys.stdin, start=1):
73 | line = line.rstrip('\r\n')
74 | if not WORD_ID_PTN.match(line):
75 | print(line)
76 | continue
77 | word = Word.parse(line, file_name, line_num)
78 | _recover(word)
79 | print(word)
80 |
81 |
82 | ########
83 | # main #
84 | ########
85 | def main():
86 | """
87 | main function processes only argument parsing
88 | """
89 | parser = ArgumentParser(description='recover wide char quotations in Sejong corpus')
90 | parser.add_argument('--input', help='input file ', metavar='FILE')
91 | parser.add_argument('--output', help='output file ', metavar='FILE')
92 | parser.add_argument('--debug', help='enable debug', action='store_true')
93 | args = parser.parse_args()
94 |
95 | if args.input:
96 | sys.stdin = open(args.input, 'rt')
97 | if args.output:
98 | sys.stdout = open(args.output, 'wt')
99 | if args.debug:
100 | logging.basicConfig(level=logging.DEBUG)
101 | else:
102 | logging.basicConfig(level=logging.INFO)
103 |
104 | run()
105 |
106 |
107 | if __name__ == '__main__':
108 | main()
109 |
--------------------------------------------------------------------------------
/munjong/remove_sejong_period_error.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | """
6 | remove wrong sentence breaking marks after period error eojeol
7 | __author__ = 'Jamie (jamie.lim@kakaocorp.com)'
8 | __copyright__ = 'Copyright (C) 2017-, Kakao Corp. All rights reserved.'
9 | """
10 |
11 |
12 | ###########
13 | # imports #
14 | ###########
15 | from argparse import ArgumentParser
16 | import logging
17 | import os
18 | import re
19 | import sys
20 | from typing import TextIO, Tuple
21 |
22 | from khaiii.munjong.sejong_corpus import Morph, WORD_ID_PTN
23 |
24 |
25 | #############
26 | # functions #
27 | #############
28 | def _get_three_lines(fin: TextIO) -> Tuple[str, str, str]:
29 | """
30 | get three lines tuple from file (generator)
31 | Args:
32 | fin: input file
33 | Yields:
34 | prev. prev. line
35 | prev. line
36 | curr. line
37 | """
38 | prev_prev_line = fin.readline().rstrip('\r\n')
39 | prev_line = fin.readline().rstrip('\r\n')
40 | # print first two lines
41 | print(prev_prev_line)
42 | print(prev_line)
43 | for curr_line in fin:
44 | curr_line = curr_line.rstrip('\r\n')
45 | yield prev_prev_line, prev_line, curr_line
46 | prev_prev_line = prev_line
47 | prev_line = curr_line
48 |
49 |
50 | def _is_known_period_error_eojeol(line: str) -> bool:
51 | """
52 | 알려진 특정 문장분리 오류를 포함하는 어절인 지 여부
53 | Args:
54 | line: line (eojeol)
55 | Returns:
56 | whether has error or not
57 | """
58 | cols = line.split('\t')
59 | if len(cols) != 3 or not WORD_ID_PTN.match(cols[0]):
60 | return False
61 | if '/SF + ' not in cols[2] or re.match(r'.+/EF \+ ./SF$', cols[2]):
62 | return False
63 | if re.match(r'.+/SF \+ [\'"’”]/SS$', cols[2]):
64 | return False
65 | morphs = [Morph.parse(_) for _ in cols[2].split(' + ')]
66 | tags_str = '+'.join([_.tag for _ in morphs])
67 | if 'SN+SF+SN' in tags_str and not tags_str.endswith('+SF'):
68 | # 4.6판: 4/SN + ./SF + 6/SN + 판/NNB
69 | if 'XSN+SF+SN' not in tags_str:
70 | return True
71 | elif 'SL+SF+SL' in tags_str and not tags_str.endswith('+SF'):
72 | # S.M.오너: S/SL + ./SF + M/SL + ./SF + 오너/NNG
73 | return True
74 | return False
75 |
76 |
77 | def run():
78 | """
79 | run function which is the start point of program
80 | """
81 | file_name = os.path.basename(sys.stdin.name)
82 | for line_num, (prev_prev_line, prev_line, curr_line) in enumerate(_get_three_lines(sys.stdin),
83 | start=1):
84 | if curr_line == '
' and _is_known_period_error_eojeol(prev_line):
85 | continue
86 | elif prev_line == '' and curr_line == '' and \
87 | _is_known_period_error_eojeol(prev_prev_line):
88 | logging.info('%s:%d\t%s', file_name, line_num, prev_prev_line)
89 | continue
90 | print(curr_line)
91 |
92 |
93 | ########
94 | # main #
95 | ########
96 | def main():
97 | """
98 | main function processes only argument parsing
99 | """
100 | parser = ArgumentParser(description='remove wrong sentence breaking marks after'
101 | ' period error eojeol')
102 | parser.add_argument('--input', help='input file ', metavar='FILE')
103 | parser.add_argument('--output', help='output file ', metavar='FILE')
104 | parser.add_argument('--debug', help='enable debug', action='store_true')
105 | args = parser.parse_args()
106 |
107 | if args.input:
108 | sys.stdin = open(args.input, 'rt')
109 | if args.output:
110 | sys.stdout = open(args.output, 'wt')
111 | if args.debug:
112 | logging.basicConfig(level=logging.DEBUG)
113 | else:
114 | logging.basicConfig(level=logging.INFO)
115 |
116 | run()
117 |
118 |
119 | if __name__ == '__main__':
120 | main()
121 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | cmake>=3.10
2 |
--------------------------------------------------------------------------------
/rsc/Makefile:
--------------------------------------------------------------------------------
1 | HOME_DIR = .
2 | BIN_DIR = $(HOME_DIR)/bin
3 | SRC_PYTHON = $(HOME_DIR)/../src/main/python
4 | RSC_SRC = $(HOME_DIR)/src
5 | PREFIX = /usr/local
6 | RSC_DIR = $(PREFIX)/share/khaiii
7 | MODEL_SIZE = base
8 |
9 | MODEL = \
10 | $(RSC_DIR)/config.json \
11 | $(RSC_DIR)/embed.bin \
12 | $(RSC_DIR)/conv.2.fil \
13 | $(RSC_DIR)/conv.3.fil \
14 | $(RSC_DIR)/conv.4.fil \
15 | $(RSC_DIR)/conv.5.fil \
16 | $(RSC_DIR)/cnv2hdn.lin \
17 | $(RSC_DIR)/hdn2tag.lin
18 |
19 | RESTORE = \
20 | $(RSC_DIR)/restore.key \
21 | $(RSC_DIR)/restore.val \
22 | $(RSC_DIR)/restore.one
23 |
24 | PREANAL = \
25 | $(RSC_DIR)/preanal.tri \
26 | $(RSC_DIR)/preanal.val
27 |
28 | ERRPATCH = \
29 | $(RSC_DIR)/errpatch.tri \
30 | $(RSC_DIR)/errpatch.val \
31 | $(RSC_DIR)/errpatch.len
32 |
33 | all: $(MODEL) $(PREANAL) $(RESTORE) $(ERRPATCH)
34 |
35 | $(wordlist 2,100,$(MODEL)): $(firstword $(MODEL))
36 | $(firstword $(MODEL)): $(RSC_SRC)/$(MODEL_SIZE).config.json $(RSC_SRC)/$(MODEL_SIZE).model.pickle
37 | mkdir -p $(RSC_DIR)
38 | PYTHONPATH=$(SRC_PYTHON) python3 $(BIN_DIR)/compile_model.py --model-size $(MODEL_SIZE) --rsc-src $(RSC_SRC) --rsc-dir $(RSC_DIR)
39 |
40 | $(wordlist 2,100,$(PREANAL)): $(firstword $(PREANAL))
41 | $(firstword $(PREANAL)): $(RSC_SRC)/preanal.auto $(RSC_SRC)/preanal.manual
42 | mkdir -p $(RSC_DIR)
43 | PYTHONPATH=$(SRC_PYTHON) python3 $(BIN_DIR)/compile_preanal.py --rsc-src $(RSC_SRC) --rsc-dir $(RSC_DIR)
44 |
45 | $(wordlist 2,100,$(RESTORE)): $(firstword $(RESTORE))
46 | $(firstword $(RESTORE)): $(RSC_SRC)/restore.dic $(RSC_SRC)/vocab.out $(RSC_SRC)/vocab.out.more
47 | mkdir -p $(RSC_DIR)
48 | PYTHONPATH=$(SRC_PYTHON) python3 $(BIN_DIR)/compile_restore.py --rsc-src $(RSC_SRC) --rsc-dir $(RSC_DIR)
49 |
50 | $(wordlist 2,100,$(ERRPATCH)): $(firstword $(ERRPATCH))
51 | $(firstword $(ERRPATCH)): $(RSC_SRC)/$(MODEL_SIZE).errpatch.auto $(RSC_SRC)/$(MODEL_SIZE).errpatch.manual
52 | mkdir -p $(RSC_DIR)
53 | PYTHONPATH=$(SRC_PYTHON) python3 $(BIN_DIR)/compile_errpatch.py --model-size $(MODEL_SIZE) --rsc-src $(RSC_SRC) --rsc-dir $(RSC_DIR)
54 |
55 | clean:
56 | rm -rf $(RSC_DIR)
57 |
--------------------------------------------------------------------------------
/rsc/src/base.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "cutoff": 1,
3 | "embed_dim": 35,
4 | "hidden_dim": 320,
5 | "model_id": "munjong.cut1.win4.sdo0.1.emb35.lr0.001.lrd0.9.bs500",
6 | "rsc_src": "../rsc/src",
7 | "window": 4
8 | }
--------------------------------------------------------------------------------
/rsc/src/base.errpatch.manual:
--------------------------------------------------------------------------------
1 | # 아래 엔트리는 단위테스트에 사용되는 것으로 삭제하지 마시기 바랍니다.
2 | 지저스크라이스트 지저스크라이스/NNP + 트/NNG 지저스/NNP + 크라이스트/NNP
3 | 지저스 크라이스트 지저스/NNP + _ + 크라이스/NNP + 트/NNG 지저스/NNP + _ + 크라이스트/NNP
4 | 고타마싯다르타 | + 고타마싯다르타/NNP | + 고타마/NNP + 싯다르타/NNP
5 | 무함마드압둘라 무함마드압/NNP + 둘/NR + 라/NNP + | 무함마드/NNP + 압둘라/NNP + |
6 |
--------------------------------------------------------------------------------
/rsc/src/base.model.pickle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakao/khaiii/3d0c8944374163b1107fd6690ccf3a408430a02d/rsc/src/base.model.pickle
--------------------------------------------------------------------------------
/rsc/src/large.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "cutoff": 1,
3 | "embed_dim": 180,
4 | "hidden_dim": 610,
5 | "model_id": "munjong.cut1.win4.sdo0.1.emb180.lr0.001.lrd0.9.bs500",
6 | "rsc_src": "../rsc/src",
7 | "window": 4
8 | }
--------------------------------------------------------------------------------
/rsc/src/large.errpatch.auto:
--------------------------------------------------------------------------------
1 | 이름 석자 이름/NNG + _ + 석자/NNG 이름/NNG + _ + 석/MM + 자/NNG
2 | 채 썬다 채/MAG + _ + 썰/VV + ㄴ다/EF 채/NNG + _ + 썰/VV + ㄴ다/EF
3 | 중증급성호흡기증후군 중증/NNG + 급성호흡기/NNG + 증후군/NNG 중증/NNG + 급성/NNG + 호흡기/NNG + 증후군/NNG
4 | 한국교사휴양원 _ + 한국교사휴양/NNP + 원/NNG _ + 한국교사휴양원/NNP
5 | 모여 들 모이/VV + 어/EC + _ + 들/VV 모이/VV + 어/EC + _ + 들/VX
6 | 연탄가스 _ + 연탄/NNG + 가스/NNG _ + 연탄가스/NNG
7 | 실수요자 _ + 실수/NNG + 요자/NNG _ + 실수요자/NNG
8 | 너랑 나 너/NP + 랑/JKB + _ + 나/NP 너/NP + 랑/JC + _ + 나/NP
9 | 시아누크공 시아누크공/NNP 시아누크/NNP + 공/NNG
10 | 하리만치 하/XSA + 리만/EC + 치/MAG 하/XSA + 리만치/EC
11 | 그래선지 | + 그렇/VA + 어선지/EC | + 그렇/VA + 어서/EC + 이/VCP + ㄴ지/EC
12 | 대대적인 대대/NNG + 적/XSN + 이/VCP + ㄴ/ETM 대대적/NNG + 이/VCP + ㄴ/ETM
13 | 이른바 ` 이른/MAJ + 바/MAG + _ + `/SS 이른바/MAJ + _ + `/SS
14 | 산간벽지 산간/NNG + 벽지/NNG 산간벽지/NNG
15 | 미스 민을 미스/NNG + _ + 민/NNG + 을/JKO 미스/NNG + _ + 민/NNP + 을/JKO
16 | 무임승차 무임/NNG + 승차/NNG 무임승차/NNG
17 | 습니다그려. 습니다/EC + 그/JX + 려/IC + ./SF 습니다/EC + 그려/JX + ./SF
18 | 진두지휘 _ + 진두/NNG + 지휘/NNG _ + 진두지휘/NNG
19 | 한편 1997 한/MAG + 편/NNG + _ + 1997/SN 한편/NNG + _ + 1997/SN
20 | 한편 1997 | + 한/MAG + 편/NNG + _ + 1997/SN | + 한편/NNG + _ + 1997/SN
21 | 지식인이란 지식인/NNG + 이/VCP + 란/JX + _ 지식인/NNG + 이란/JX + _
22 | 시험공부 시험/NNG + 공부/NNG 시험공부/NNG
23 | 중증급성호흡기 중증/NNG + 급성호흡기/NNG 중증/NNG + 급성/NNG + 호흡기/NNG
24 | 기념행사 기념/NNG + 행사/NNG 기념행사/NNG
25 | 그래선지 그렇/VA + 어선지/EC + _ 그렇/VA + 어서/EC + 이/VCP + ㄴ지/EC + _
26 | 사 가지고 사/VV + 아/EC + _ + 가/VV + 지/VX + 고/EC 사/VV + 아/EC + _ + 가지/VX + 고/EC
27 | 한국교사휴양원 한국교사휴양/NNP + 원/NNG 한국교사휴양원/NNP
28 | 언어문화 언어/NNG + 문화/NNG 언어문화/NNG
29 | 간 쇠고기 가/VV + ㄴ/ETM + _ + 쇠고기/NNG 갈/VV + ㄴ/ETM + _ + 쇠고기/NNG
30 | 달래 주 달러/VV + 어/EC + _ + 주/VX 달래/VV + 어/EC + _ + 주/VX
31 | 기 일원론 기/NNG + _ + 일원/NNG + 론/XSN 기/NNG + _ + 일원론/NNG
32 | 돼지머리 돼지머리/NNG 돼지/NNG + 머리/NNG
33 | 제자리걸음 _ + 제자리/NNG + 걸음/NNG _ + 제자리걸음/NNG
34 | 전지훈련 전지/NNG + 훈련/NNG 전지훈련/NNG
35 | 진우 씬 진우/NNP + _ + 씬/NNG 진우/NNP + _ + 씨/NNB + ㄴ/JX
36 | 이 바람에 이/JKS + _ + 바/NNG + 람/NNB + 에/JKB 이/JKS + _ + 바람/NNG + 에/JKB
37 | 대대적인 _ + 대대/NNG + 적/XSN + 이/VCP + ㄴ/ETM _ + 대대적/NNG + 이/VCP + ㄴ/ETM
38 | 돼지머리 _ + 돼지머리/NNG _ + 돼지/NNG + 머리/NNG
39 | 반벌거숭이 _ + 반벌거숭이/NNG _ + 반/NNG + 벌거숭이/NNG
40 | 이나 있 이나/JX + _ + 있/VX 이나/JX + _ + 있/VV
41 | 도시가스 _ + 도시/NNG + 가스/NNG _ + 도시가스/NNG
42 | 그 반벌거숭이 그/MM + _ + 반벌거숭이/NNG 그/MM + _ + 반/NNG + 벌거숭이/NNG
43 | 제자리걸음 제자리/NNG + 걸음/NNG 제자리걸음/NNG
44 | 만나 보 만나/VV + 아/EC + _ + 보/VV 만나/VV + 아/EC + _ + 보/VX
45 | 세계정세 _ + 세계/NNG + 정세/NNG _ + 세계정세/NNG
46 | 가상공간 가상/NNG + 공간/NNG 가상공간/NNG
47 | 만병통치약 만병/NNG + 통치약/NNG 만병통치약/NNG
48 | 조선말기 _ + 조/NNP + 선말기/NNG _ + 조선/NNP + 말기/NNG
49 | 그래선지 그렇/VA + 어선지/EC 그렇/VA + 어서/EC + 이/VCP + ㄴ지/EC
50 | 해임건의안 해임/NNG + 건의/NNG + 안/NNG 해임/NNG + 건의안/NNG
51 | 생맥주집 생/XPN + 맥주집/NNG 생/XPN + 맥주/NNG + 집/NNG
52 | 다문화주의 _ + 다문화주의/NNG _ + 다문화/NNG + 주의/NNG
53 | 가족계획 가족/NNG + 계획/NNG 가족계획/NNG
54 | 세대교체 세대/NNG + 교체/NNG 세대교체/NNG
55 | 물항아리 물항아리/NNG 물/NNG + 항아리/NNG
56 | 비평용어 _ + 비평용어/NNG _ + 비평/NNG + 용어/NNG
57 | 반벌거숭이 반벌거숭이/NNG 반/NNG + 벌거숭이/NNG
58 | 수사본부 수사/NNG + 본부/NNG 수사본부/NNG
59 | 전기난로 전기난로/NNG 전기/NNG + 난로/NNG
60 | 원상회복 원상/NNG + 회복/NNG 원상회복/NNG
61 | 베이지색 _ + 베이지색/NNG + _ _ + 베이지/NNG + 색/NNG + _
62 | 이 바람 이/JKS + _ + 바/NNG + 람/NNB 이/JKS + _ + 바람/NNG
63 | 시기상조 _ + 시기/NNG + 상조/NNG _ + 시기상조/NNG
64 | 하리만치 하/XSA + 리만/EC + 치/MAG + _ 하/XSA + 리만치/EC + _
65 | 원상회복 _ + 원상/NNG + 회복/NNG _ + 원상회복/NNG
66 | 수공예품 수공/NNG + 예품/NNG 수공예품/NNG
67 | 베이지색 베이지색/NNG 베이지/NNG + 색/NNG
68 | 신용보증기금 신/NNG + 용보증기금/NNP 신용보증기금/NNP
69 | 도시가스 도시/NNG + 가스/NNG 도시가스/NNG
70 | 가상공간 _ + 가상/NNG + 공간/NNG _ + 가상공간/NNG
71 | 학력고사 학력/NNG + 고사/NNG 학력고사/NNG
72 | 사 가지 사/VV + 아/EC + _ + 가/VV + 지/VX 사/VV + 아/EC + _ + 가지/VX
73 | 시기상조 시기/NNG + 상조/NNG 시기상조/NNG
74 | 슬기슬기 슬기슬기/NNG 슬기/NNG + 슬기/NNG
75 | 전기난로 _ + 전기난로/NNG _ + 전기/NNG + 난로/NNG
76 | 동물학자 _ + 동물/NNG + 학자/NNG _ + 동물학자/NNG
77 | 오리고기 오리고기/NNG 오리/NNG + 고기/NNG
78 | 슬기슬기 _ + 슬기슬기/NNG _ + 슬기/NNG + 슬기/NNG
79 | 가족계획 _ + 가족/NNG + 계획/NNG _ + 가족계획/NNG
80 | 위기관리 _ + 위기/NNG + 관리/NNG _ + 위기관리/NNG
81 | 전지훈련 _ + 전지/NNG + 훈련/NNG _ + 전지훈련/NNG
82 | 습니다그려 습니다/EC + 그/JX + 려/IC 습니다/EC + 그려/JX
83 | 비평용어 비평용어/NNG 비평/NNG + 용어/NNG
84 | 지식인이란 지식인/NNG + 이/VCP + 란/JX 지식인/NNG + 이란/JX
85 | 동물학자 동물/NNG + 학자/NNG 동물학자/NNG
86 | 예술가촌 예술가촌/NNG 예술가/NNG + 촌/NNG
87 | 베이지색 베이지색/NNG + _ 베이지/NNG + 색/NNG + _
88 | 가 주는 가/JKS + _ + 주/VX + 는/ETM 가/JKS + _ + 주/VV + 는/ETM
89 | 담임교사 _ + 담임/NNG + 교사/NNG _ + 담임교사/NNG
90 | 네덜란드인 네/NNP + 덜란드인/NNG 네덜란드인/NNG
91 | 선불카드 선불/NNG + 카드/NNG 선불카드/NNG
92 | 다문화주의 다문화주의/NNG 다문화/NNG + 주의/NNG
93 | 어인 일 어/NNG + 이/VV + ㄴ/ETM + _ + 일/NNG 어인/MM + _ + 일/NNG
94 | 조선말기 조/NNP + 선말기/NNG 조선/NNP + 말기/NNG
95 | 진두지휘 진두/NNG + 지휘/NNG 진두지휘/NNG
96 | 베이지색 _ + 베이지색/NNG _ + 베이지/NNG + 색/NNG
97 | 개인연금 개인/NNG + 연금/NNG 개인연금/NNG
98 | 위기관리 위기/NNG + 관리/NNG 위기관리/NNG
99 | , 대파 ,/SP + _ + 대파/NNG ,/SP + _ + 대/XPN + 파/NNG
100 | 연탄가스 연탄/NNG + 가스/NNG 연탄가스/NNG
101 | 50퍼센트 50/SN + 퍼센/NNG + 트/NNB 50/SN + 퍼센트/NNG
102 | 담임교사 담임/NNG + 교사/NNG 담임교사/NNG
103 | 개인연금 _ + 개인/NNG + 연금/NNG _ + 개인연금/NNG
104 | 전문학교 전문/NNG + 학교/NNG 전문학교/NNG
105 | 기념행사 _ + 기념/NNG + 행사/NNG _ + 기념행사/NNG
106 | 실수요자 실수/NNG + 요자/NNG 실수요자/NNG
107 | 세계정세 세계/NNG + 정세/NNG 세계정세/NNG
108 | 아씨마님 아씨마님/NNG 아씨/NNG + 마님/NNG
109 | 미스 민 미스/NNG + _ + 민/NNG 미스/NNG + _ + 민/NNP
110 | 통신업체 통신/NNG + 업체/NNG 통신업체/NNG
111 | 소강상태 소강/NNG + 상태/NNG 소강상태/NNG
112 |
--------------------------------------------------------------------------------
/rsc/src/large.errpatch.manual:
--------------------------------------------------------------------------------
1 | # 아래 엔트리는 단위테스트에 사용되는 것으로 삭제하지 마시기 바랍니다.
2 | 지저스크라이스트 지/NNG + 저스크라이스/NNP + 트/NNG 지저스/NNP + 크라이스트/NNP
3 | 지저스 크라이스트 지저스/NNP + _ + 크라이스/NNP + 트/NNG 지저스/NNP + _ + 크라이스트/NNP
4 | 고타마싯다르타 | + 고타마싯다르타/NNP | + 고타마/NNP + 싯다르타/NNP
5 | 무함마드압둘라 무함마드압둘라/NNP + | 무함마드/NNP + 압둘라/NNP + |
6 |
--------------------------------------------------------------------------------
/rsc/src/large.model.pickle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kakao/khaiii/3d0c8944374163b1107fd6690ccf3a408430a02d/rsc/src/large.model.pickle
--------------------------------------------------------------------------------
/rsc/src/preanal.manual:
--------------------------------------------------------------------------------
1 | # 아래 두 엔트리는 단위테스트에 사용되는 것으로 삭제하지 마시기 바랍니다.
2 | 이더리움 이더리움/NNG
3 | 가즈아* 가/VV + 즈아/EC
4 |
--------------------------------------------------------------------------------
/rsc/src/vocab.out.more:
--------------------------------------------------------------------------------
1 | I-SS:I-MAG:0
2 | I-SS:I-VCP:0
3 |
--------------------------------------------------------------------------------
/src/main/cpp/khaiii/Config.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2018-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #include "khaiii/Config.hpp"
8 |
9 |
10 | //////////////
11 | // includes //
12 | //////////////
13 | #include
14 |
15 | #include "fmt/format.h"
16 | #include "nlohmann/json.hpp"
17 |
18 | #include "khaiii/KhaiiiApi.hpp"
19 |
20 |
21 | namespace khaiii {
22 |
23 |
24 | using std::exception;
25 | using std::ifstream;
26 | using std::make_shared;
27 | using std::shared_ptr;
28 | using std::string;
29 |
30 |
31 | /////////////
32 | // methods //
33 | /////////////
34 | void Config::read_from_file(string path) {
35 | try {
36 | ifstream ifs(path);
37 | nlohmann::json jsn;
38 | ifs >> jsn;
39 | set_members(jsn);
40 | } catch (const exception& exc) {
41 | throw Except(fmt::format("fail to parse config: {}", exc.what()));
42 | }
43 | }
44 |
45 |
46 | void Config::override_from_str(const char* opt_str) {
47 | if (opt_str == nullptr || opt_str[0] == '\0') return;
48 |
49 | try {
50 | auto jsn = nlohmann::json::parse(opt_str);
51 | override_members(jsn);
52 | } catch (const exception& exc) {
53 | throw Except(fmt::format("fail to parse option: {}\n{}", exc.what(), opt_str));
54 | }
55 | }
56 |
57 |
58 | Config* Config::copy_and_override(const char* opt_str) {
59 | if (opt_str == nullptr || opt_str[0] == '\0') return this;
60 |
61 | auto found = _cfg_cache.find(opt_str);
62 | if (found != _cfg_cache.end()) return found->second.get();
63 |
64 | auto cfg = copy();
65 | try {
66 | auto jsn = nlohmann::json::parse(opt_str);
67 | cfg->override_members(jsn);
68 | _cfg_cache[opt_str] = cfg;
69 | } catch (const exception& exc) {
70 | throw Except(fmt::format("fail to parse option: {}\n{}", exc.what(), opt_str));
71 | }
72 |
73 | return cfg.get();
74 | }
75 |
76 |
77 | void Config::set_members(const nlohmann::json& jsn) {
78 | class_num = jsn.value("class_num", class_num);
79 | if (class_num <= 0) throw Except(fmt::format("invalid 'class_num' value: {}", class_num));
80 |
81 | embed_dim = jsn.value("embed_dim", embed_dim);
82 | if (embed_dim <= 0) throw Except(fmt::format("invalid 'embed_dim' value: {}", embed_dim));
83 |
84 | hidden_dim = jsn.value("hidden_dim", hidden_dim);
85 | if (hidden_dim <= 0) throw Except(fmt::format("invalid 'hidden_dim' value: {}", hidden_dim));
86 |
87 | vocab_size = jsn.value("vocab_size", vocab_size);
88 | if (vocab_size <= 0) throw Except(fmt::format("invalid 'vocab_size' value: {}", vocab_size));
89 |
90 | window = jsn.value("window", window);
91 | if (window <= 0) throw Except(fmt::format("invalid 'window' value: {}", window));
92 |
93 | override_members(jsn);
94 | }
95 |
96 | void Config::override_members(const nlohmann::json& jsn) {
97 | preanal = jsn.value("preanal", preanal);
98 | errpatch = jsn.value("errpatch", errpatch);
99 | restore = jsn.value("restore", restore);
100 | }
101 |
102 | shared_ptr Config::copy() {
103 | auto that = make_shared();
104 | that->class_num = class_num;
105 | that->embed_dim = embed_dim;
106 | that->hidden_dim = hidden_dim;
107 | that->vocab_size = vocab_size;
108 | that->window = window;
109 | that->preanal = preanal;
110 | that->errpatch = errpatch;
111 | that->restore = restore;
112 | return that;
113 | }
114 |
115 |
116 | } // namespace khaiii
117 |
--------------------------------------------------------------------------------
/src/main/cpp/khaiii/Config.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2018-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #ifndef SRC_MAIN_CPP_KHAIII_CONFIG_HPP_
8 | #define SRC_MAIN_CPP_KHAIII_CONFIG_HPP_
9 |
10 |
11 | //////////////
12 | // includes //
13 | //////////////
14 | #include
15 | #include
16 | #include
17 |
18 | #include "nlohmann/json.hpp"
19 |
20 |
21 | namespace khaiii {
22 |
23 |
24 | /**
25 | * JSON format configuration file
26 | */
27 | class Config {
28 | public:
29 | int class_num = -1; ///< number of classes
30 | int embed_dim = -1; ///< embedding dimension
31 | int hidden_dim = -1; ///< hidden dimension
32 | int vocab_size = -1; ///< vocabulary size
33 | int window = -1; ///< context window size
34 |
35 | bool preanal = true; ///< whether apply preanal or not
36 | bool errpatch = true; ///< whether apply error patch or not
37 | bool restore = true; ///< whether restore morphemes or not
38 |
39 | Config() = default;
40 | Config(const Config&) = delete; ///< delete copy constructor
41 | Config& operator=(const Config&) = delete; ///< delete assignment operator
42 |
43 | /**
44 | * 파일로부터 설정을 읽어들인다.
45 | * @param path file path
46 | */
47 | void read_from_file(std::string path);
48 |
49 | /**
50 | * JSON 옵션을 이용해 설정을 override 한다.
51 | * @param opt_str option string (JSON format)
52 | */
53 | void override_from_str(const char* opt_str);
54 |
55 | /**
56 | * 객체를 복사하고 설정을 override 한다.
57 | * @param opt_str option string (JSON format)
58 | * @return 존재할 경우 그 옵션 객체
59 | */
60 | Config* copy_and_override(const char* opt_str);
61 |
62 | /**
63 | * 파싱된 JSON 객체를 이용해서 멤버를 세팅한다.
64 | * @param jsn JSON 객체
65 | */
66 | void set_members(const nlohmann::json& jsn);
67 |
68 | /**
69 | * 파싱된 JSON 객체를 이용해서 오버라이딩할 멤버만 세팅한다.
70 | * @param jsn JSON 객체
71 | */
72 | void override_members(const nlohmann::json& jsn);
73 |
74 | /**
75 | * 자기 자신을 복사한 객체를 생성한다.
76 | * @return 복사된 객체
77 | */
78 | std::shared_ptr copy();
79 |
80 | private:
81 | /**
82 | * 오버라이딩된 객체의 캐시
83 | */
84 | std::unordered_map> _cfg_cache;
85 | };
86 |
87 |
88 | } // namespace khaiii
89 |
90 |
91 | #endif // SRC_MAIN_CPP_KHAIII_CONFIG_HPP_
92 |
--------------------------------------------------------------------------------
/src/main/cpp/khaiii/Embed.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2018-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #include "khaiii/Embed.hpp"
8 |
9 |
10 | //////////////
11 | // includes //
12 | //////////////
13 | #include
14 | #include
15 |
16 | #include "khaiii/Config.hpp"
17 | #ifndef NDEBUG
18 | #include "khaiii/util.hpp"
19 | #endif
20 |
21 |
22 | namespace khaiii {
23 |
24 |
25 | using std::make_shared;
26 | using std::shared_ptr;
27 | using std::string;
28 |
29 |
30 | ////////////////////
31 | // static members //
32 | ////////////////////
33 | shared_ptr Embed::_log = spdlog::stderr_color_mt("Embed");
34 |
35 |
36 | /////////////
37 | // methods //
38 | /////////////
39 | void Embed::open(const Config& cfg, string dir) {
40 | _embed_mmf.open(fmt::format("{}/embed.bin", dir));
41 | _keys = reinterpret_cast(_embed_mmf.data());
42 | const float* val_start = reinterpret_cast(_keys + cfg.vocab_size);
43 | for (int i = 0; i < cfg.vocab_size; ++i) {
44 | const float* embed_start = val_start + i * cfg.embed_dim;
45 | _vals.emplace_back(embedding_t(const_cast(embed_start), cfg.embed_dim));
46 | SPDLOG_TRACE(_log, "[{}] {}", i, _vals[i]);
47 | }
48 | }
49 |
50 |
51 | void Embed::close() {
52 | _embed_mmf.close();
53 | }
54 |
55 |
56 | const embedding_t& Embed::operator[](wchar_t chr) const {
57 | const wchar_t* found = reinterpret_cast(
58 | bsearch(&chr, _keys, _vals.size(), sizeof(wchar_t), Embed::_key_cmp));
59 | int idx = 1; // unknown character index is 1
60 | if (found != nullptr) idx = found - _keys;
61 | #ifndef NDEBUG
62 | wchar_t wstr[2] = {chr, 0};
63 | SPDLOG_TRACE(_log, "'{}'({}) {}", wstr_to_utf8(wstr), idx, _vals.at(idx));
64 | #endif
65 | return _vals.at(idx);
66 | }
67 |
68 |
69 | const embedding_t& Embed::left_word_bound() const {
70 | return _vals.at(2);
71 | }
72 |
73 |
74 | const embedding_t& Embed::right_word_bound() const {
75 | return _vals.at(3);
76 | }
77 |
78 |
79 | const embedding_t& Embed::left_padding() const {
80 | return _vals.at(0); // padding index is 0 which is zero vector
81 | }
82 |
83 |
84 | const embedding_t& Embed::right_padding() const {
85 | return _vals.at(0); // padding index is 0 which is zero vector
86 | }
87 |
88 |
89 | int Embed::_key_cmp(const void* left, const void* right) {
90 | const wchar_t* left_ = reinterpret_cast(left);
91 | const wchar_t* right_ = reinterpret_cast(right);
92 | return *left_ - *right_;
93 | }
94 |
95 |
96 | } // namespace khaiii
97 |
--------------------------------------------------------------------------------
/src/main/cpp/khaiii/Embed.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2018-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #ifndef SRC_MAIN_CPP_KHAIII_EMBED_HPP_
8 | #define SRC_MAIN_CPP_KHAIII_EMBED_HPP_
9 |
10 |
11 | //////////////
12 | // includes //
13 | //////////////
14 | #include
15 | #include
16 | #include
17 |
18 | #include "Eigen/Dense"
19 | #include "spdlog/spdlog.h"
20 |
21 | #include "khaiii/MemMapFile.hpp"
22 | #include "khaiii/nn/tensor.hpp"
23 |
24 |
25 | namespace khaiii {
26 |
27 |
28 | using embedding_t = nn::vector_map_t;
29 | class Config;
30 |
31 |
32 | class Embed {
33 | public:
34 | /**
35 | * open resource with memory data
36 | * @param cfg config
37 | * @param dir base directory
38 | */
39 | void open(const Config& cfg, std::string dir);
40 |
41 | void close(); ///< 리소스를 닫는다.
42 |
43 | /**
44 | * get embedding vector with character
45 | * @param chr character
46 | * @return embedding vector
47 | */
48 | const embedding_t& operator[](wchar_t chr) const;
49 |
50 | const embedding_t& left_word_bound() const; ///< left word bound
51 | const embedding_t& right_word_bound() const; ///< right word bound
52 | const embedding_t& left_padding() const; ///< left padding
53 | const embedding_t& right_padding() const; ///< right padding
54 |
55 | private:
56 | static std::shared_ptr _log; ///< logger
57 |
58 | const wchar_t* _keys = nullptr; ///< keys (characters)
59 | std::vector _vals; ///< values (embedding vectors)
60 |
61 | static int _key_cmp(const void* left, const void* right); ///< key comparator for bsearch
62 |
63 | MemMapFile _embed_mmf; ///< model embedding memory mapping
64 | };
65 |
66 |
67 | } // namespace khaiii
68 |
69 |
70 | #endif // SRC_MAIN_CPP_KHAIII_EMBED_HPP_
71 |
--------------------------------------------------------------------------------
/src/main/cpp/khaiii/ErrPatch.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2018-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #include "khaiii/ErrPatch.hpp"
8 |
9 |
10 | //////////////
11 | // includes //
12 | //////////////
13 | #include
14 | #include
15 | #include
16 |
17 | #include "khaiii/KhaiiiApi.hpp"
18 | #include "khaiii/Sentence.hpp"
19 | #include "khaiii/Word.hpp"
20 |
21 |
22 | namespace khaiii {
23 |
24 |
25 | using std::dynamic_pointer_cast;
26 | using std::exception;
27 | using std::shared_ptr;
28 | using std::string;
29 | using std::vector;
30 |
31 |
32 | ////////////////////
33 | // static members //
34 | ////////////////////
35 | const wchar_t ErrPatch::WORD_DELIM_NUM = -1;
36 | const wchar_t ErrPatch::SENT_DELIM_NUM = -2;
37 |
38 | shared_ptr ErrPatch::_log = spdlog::stderr_color_mt("ErrPatch");
39 |
40 |
41 | ////////////////////
42 | // ctors and dtor //
43 | ////////////////////
44 | ErrPatch::~ErrPatch() {
45 | close();
46 | }
47 |
48 |
49 | /////////////
50 | // methods //
51 | /////////////
52 | void ErrPatch::open(string dir) {
53 | _trie.open(dir + "/errpatch.tri");
54 | _val_mmf.open(dir + "/errpatch.val");
55 | MemMapFile len_mmf;
56 | len_mmf.open(dir + "/errpatch.len"); // 각 value들의 길이 정보
57 | _vals.reserve(len_mmf.size());
58 | const uint8_t* lens = len_mmf.data();
59 | const int16_t* val_ptr = _val_mmf.data();
60 | for (int i = 0; i < len_mmf.size(); ++i) {
61 | // 길이 정보를 이용하여 int16_t 가변길이 배열인 값(_vals)을 세팅한다.
62 | _vals.emplace_back(val_ptr);
63 | val_ptr += lens[i] + 1; // 길이 + 마지막 0
64 | }
65 | assert(_vals.size() == len_mmf.size());
66 | assert(val_ptr - _val_mmf.data() == _val_mmf.size());
67 | _log->info("errpatch dictionary opened");
68 | }
69 |
70 |
71 | void ErrPatch::close() {
72 | _trie.close();
73 | _val_mmf.close();
74 | _log->debug("errpatch dictionary closed");
75 | }
76 |
77 |
78 | void ErrPatch::apply(shared_ptr sent) const {
79 | vector outputs; // 매칭된 패치의 정분석 결과 태그 값을 덮어쓸 출력 위치
80 | vector chars = _get_char_tag_mixes(sent, &outputs);
81 | for (int i = 0; i < chars.size(); ++i) {
82 | auto found = _trie.search_longest_prefix_match(&chars[i]);
83 | if (found == boost::none) continue;
84 | auto val = _vals[found->val];
85 | for (int j = 0; j < found->len; ++j) {
86 | if (outputs[i + j] == nullptr) {
87 | assert(val[j] == WORD_DELIM_NUM || val[j] == SENT_DELIM_NUM);
88 | continue;
89 | }
90 | *outputs[i + j] = val[j];
91 | }
92 | i += found->len - 1;
93 | }
94 | }
95 |
96 |
97 | vector ErrPatch::_get_char_tag_mixes(shared_ptr sent,
98 | vector* outputs) {
99 | vector chars;
100 | chars.reserve(2 + 2 * sent->words.size());
101 | outputs->reserve(2 + 2 * sent->words.size());
102 | chars.emplace_back(SENT_DELIM_NUM); // 문장 경계
103 | outputs->emplace_back(nullptr);
104 | for (auto& word : sent->words) {
105 | if (chars.size() > 1) {
106 | chars.emplace_back(WORD_DELIM_NUM); // 어절 경계
107 | outputs->emplace_back(nullptr);
108 | }
109 | for (int i = 0; i < word->wlength; ++i) {
110 | wchar_t char_tag_mix = (word->wbegin[i] << 12) | word->char_tags[i];
111 | _log->debug("{:5x}|{:3x} -> {:08x}", static_cast(word->wbegin[i]),
112 | word->char_tags[i], static_cast(char_tag_mix));
113 | chars.emplace_back(char_tag_mix);
114 | outputs->emplace_back(&word->char_tags[i]);
115 | }
116 | }
117 | chars.emplace_back(SENT_DELIM_NUM); // 문장 경계
118 | outputs->emplace_back(nullptr);
119 | chars.emplace_back(0); // 마지막 string termination
120 | outputs->emplace_back(nullptr);
121 | return chars;
122 | }
123 |
124 |
125 | } // namespace khaiii
126 |
--------------------------------------------------------------------------------
/src/main/cpp/khaiii/ErrPatch.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2018-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #ifndef SRC_MAIN_CPP_KHAIII_ERRPATCH_HPP_
8 | #define SRC_MAIN_CPP_KHAIII_ERRPATCH_HPP_
9 |
10 |
11 | //////////////
12 | // includes //
13 | //////////////
14 | #include
15 | #include
16 | #include
17 |
18 | #include "spdlog/spdlog.h"
19 |
20 | #include "khaiii/MemMapFile.hpp"
21 | #include "khaiii/Trie.hpp"
22 |
23 |
24 | namespace khaiii {
25 |
26 |
27 | class Sentence;
28 |
29 |
30 | class ErrPatch {
31 | public:
32 | static const wchar_t WORD_DELIM_NUM; ///< 어절 경계를 나타내는 가상 음절
33 | static const wchar_t SENT_DELIM_NUM; ///< 문장 경계를 나타내는 가상 음절
34 |
35 | virtual ~ErrPatch(); ///< dtor
36 |
37 | /**
38 | * 리소스를 연다.
39 | * @param dir 리소스 디렉토리
40 | */
41 | void open(std::string dir);
42 |
43 | void close(); ///< 리소스를 닫는다.
44 |
45 | /**
46 | * 오분석 패치를 적용한다.
47 | * @param sent 문장
48 | */
49 | void apply(std::shared_ptr sent) const;
50 |
51 | private:
52 | static std::shared_ptr _log; ///< logger
53 |
54 | Trie _trie;
55 | MemMapFile _val_mmf; ///< value memory mapping
56 | std::vector _vals; ///< actual values
57 |
58 | /**
59 | * 문장을 Trie 입력에 맞도록 음절과 태그의 비트 조합의 열로 만들고, 출력 위치를 기록한다.
60 | * @param sent 문장
61 | * @param outputs 출력 위치
62 | * @return 음절과 태그의 비트 조합한 열
63 | */
64 | static std::vector _get_char_tag_mixes(std::shared_ptr sent,
65 | std::vector* outputs);
66 | };
67 |
68 |
69 | } // namespace khaiii
70 |
71 |
72 | #endif // SRC_MAIN_CPP_KHAIII_ERRPATCH_HPP_
73 |
--------------------------------------------------------------------------------
/src/main/cpp/khaiii/KhaiiiImpl.hpp:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Jamie (jamie.lim@kakaocorp.com)
3 | * @copyright Copyright (C) 2018-, Kakao Corp. All rights reserved.
4 | */
5 |
6 |
7 | #ifndef SRC_MAIN_CPP_KHAIII_KHAIIIIMPL_HPP_
8 | #define SRC_MAIN_CPP_KHAIII_KHAIIIIMPL_HPP_
9 |
10 |
11 | //////////////
12 | // includes //
13 | //////////////
14 | #include
15 | #include