kakao / khaiii

Kakao Hangul Analyzer III
Apache License 2.0
1.41k stars 284 forks source link

설치 완료 후 ctest에서 오류 #105

Closed sorryhyun closed 3 years ago

sorryhyun commented 3 years ago

안녕하세요~ 카카오 형태소 분석기를 사용하려고 설치중에 있는데 설치를 분명 다 완료한것 같은데 ctest에서 오류가 나네요 $ ctest --output-on-failure

Test project /home/aa/khaiii-master/build
    Start 1: test_khaiii
1/1 Test #1: test_khaiii ......................***Failed    0.04 sec
[==========] Running 13 tests from 4 test cases.
[----------] Global test environment set-up.
[----------] 1 test from ErrPatchTest
[ RUN      ] ErrPatchTest.apply
[2021-07-27 11:03:14.395] [ErrPatchTest] [warning] error not found: '지저스크라이스트' => E:'지저스크라이스/NNP + 트/NNG' vs A:'지저스/NNG + 크라이스트/NNP'
[2021-07-27 11:03:14.398] [ErrPatchTest] [warning] error not found: '지저스 크라이스트' => E:'지저스/NNP + _ + 크라이스/NNP + 트/NNG' vs A:'지저스/NNP + _ + 크라이스트/NNP'
[2021-07-27 11:03:14.406] [ErrPatchTest] [warning] error not found: '무함마드압둘라' => E:'무함마드압/NNP + 둘/NR + 라/NNP' vs A:'무함마드압둘라/NNP'
[       OK ] ErrPatchTest.apply (15 ms)
[----------] 1 test from ErrPatchTest (15 ms total)

[----------] 7 tests from KhaiiiApiTest
[ RUN      ] KhaiiiApiTest.version
[       OK ] KhaiiiApiTest.version (0 ms)
[ RUN      ] KhaiiiApiTest.open_close
[       OK ] KhaiiiApiTest.open_close (0 ms)
[ RUN      ] KhaiiiApiTest.analyze
[       OK ] KhaiiiApiTest.analyze (4 ms)
[ RUN      ] KhaiiiApiTest.free_results
[       OK ] KhaiiiApiTest.free_results (4 ms)
[ RUN      ] KhaiiiApiTest.last_error
[       OK ] KhaiiiApiTest.last_error (0 ms)
[ RUN      ] KhaiiiApiTest.restore_true
/home/aa/khaiii-master/src/test/cpp/khaiii/KhaiiiApiTest.cpp:65: Failure
Expected equality of these values:
  expected.c_str()
    Which is: "\xEB\xB3\xB4\xEC\x9D\xB4/VV + \xEC\x96\xB4/EC + \xEC\xA3\xBC/VX + \xE3\x84\xB9/ETM"
  oss.str().c_str()
    Which is: "\xEB\xB3\xB4\xEC\x9D\xB4/VV + \xEC\x96\xB4/EC + \xEC\xA4\x84/NNG"
[  FAILED  ] KhaiiiApiTest.restore_true (4 ms)
[ RUN      ] KhaiiiApiTest.restore_false
/home/aa/khaiii-master/src/test/cpp/khaiii/KhaiiiApiTest.cpp:65: Failure
Expected equality of these values:
  expected.c_str()
    Which is: "\xEB\xB3\xB4\xEC\x97\xAC/VV + \xEC\xA4\x84/VX"
  oss.str().c_str()
    Which is: "\xEB\xB3\xB4\xEC\x97\xAC/VV + \xEC\xA4\x84/NNG"
[  FAILED  ] KhaiiiApiTest.restore_false (3 ms)
[----------] 7 tests from KhaiiiApiTest (15 ms total)

[----------] 3 tests from KhaiiiDevTest
[ RUN      ] KhaiiiDevTest.analyze_bfr_errorpatch
[       OK ] KhaiiiDevTest.analyze_bfr_errorpatch (4 ms)
[ RUN      ] KhaiiiDevTest.set_log_level
[       OK ] KhaiiiDevTest.set_log_level (0 ms)
[ RUN      ] KhaiiiDevTest.set_log_levels
[       OK ] KhaiiiDevTest.set_log_levels (1 ms)
[----------] 3 tests from KhaiiiDevTest (5 ms total)

[----------] 2 tests from PreanalTest
[ RUN      ] PreanalTest.apply_exact
[       OK ] PreanalTest.apply_exact (0 ms)
[ RUN      ] PreanalTest.apply_prefix
[       OK ] PreanalTest.apply_prefix (0 ms)
[----------] 2 tests from PreanalTest (0 ms total)

[----------] Global test environment tear-down
[==========] 13 tests from 4 test cases ran. (35 ms total)
[  PASSED  ] 11 tests.
[  FAILED  ] 2 tests, listed below:
[  FAILED  ] KhaiiiApiTest.restore_true
[  FAILED  ] KhaiiiApiTest.restore_false

 2 FAILED TESTS

0% tests passed, 1 tests failed out of 1

Total Test time (real) =   0.04 sec

The following tests FAILED:
      1 - test_khaiii (Failed)
Errors while running CTest

라는 식으로 오류가 뜹니다.. 이상하게 ctest 과정에서만 오류가 나는 거라서 $ ./bin/khaiii --rsc-dir=./share/khaiii 를 실행시켰을 땐 무난하게 동작하기도 하고 이걸 그대로 설치해서 파이썬 패키지로 바인딩해서 사용할수도 있긴 한데 (분명 한 10000번 반복해서 사용할 때는 문제없이 동작합니다) 나중에 신나게 형태소 분석을 수행하다 보면 또 이런 오류가 뜹니다.

`terminate called after throwing an instance of 'khaiii::Except' what(): fail to close memory mapped file:

Process finished with exit code 134 (interrupted by signal 6: SIGABRT)`

파이참 환경에서 돌리고 있는데 ide 메모리도 넉넉하게 설정해줬고 시스템 메모리도 넉넉한데 뭐가 문제인걸까요 ㅜㅜ

krikit commented 3 years ago

1) 첫번째 ctest 과정에서 나는 오류는 형태소 분석 결과가 기대와 다른 결과를 보이기 때문인데요. 사전 설치 과정이 제대로 되었는지, base 모델과 large 모델을 섞어서 사용하셨는지 등을 한번 확인해 보셔야 할 것 같습니다.

2) 두번째 $ ./bin/khaiii --rsc-dir=./share/khaiii 를 실행시켰을 때 나는 오류는 아무래도 프로그램이 종료되기 전에 memory mapped file을 닫으면서 발생하는 것 같습니다. double close 등을 의심할 수 있을 것 같은데요. 정확히 재현이 되는 케이스를 말씀해 주시면 디버깅에 도움이 될 것 같습니다.

sorryhyun commented 3 years ago

설치는 똑같이 따라한것 같은데 이상하네요 아무튼 2번에 대해 코드를 보여드리자면

ko = None

def morphinit():
    global ko
    ko = KhaiiiApi()

def tokenize(sentence):
    # 형태소 분석기를 활용하여 매 문장을 형태소분석, 리스트 형태로 리턴
    # 대체어 추출할 단어만을 리스트 형태로 출력.
    global ko
    reslist = []
    result = []
    templist = []
    for word in ko.analyze(sentence):
        for morph in word.morphs:
            templist.append((morph.lex, morph.tag))
    reslist = [tempword[0] for tempword in templist]

    if reslist:  # 만약 이번에 읽은 데이터에 명사가 존재할 경우에만
        result= reslist 
    return result

def output_preprocessed_corpus(self, fout_path):
    result = []
    fin = open('./dataset/train/train.txt', 'r+')
    fout = open(fout_path, 'w')
    lines = fin.readlines() 

    import multiprocessing
    pool = multiprocessing.Pool(self.num_cores, initializer=morphinit)  # 8 core CPU
    import time
    start = time.time()
    result = pool.map(tokenize, lines)
    print("총 " + str(time.time() - start) + "초 소요")
    pool.close()
    pool.join()

    for list in result:
        fout.write(",".join(list))
        fout.write('\n')
    fout.close()

    return result #[ [문장1에대한 토큰리스트1], [문장2에대한 토큰리스트2], [...] ]

인데, 멀티프로세싱 코드 사용 전에도 동일한 에러가 나오긴 했습니다.

krikit commented 3 years ago

khaiii는 여러 스레드 혹은 프로세스가 하나의 객체에 접근하면 내부적으로 lock을 사용하므로 효율이 좋지 않을 수 있습니다. khaiii가 사용하는 메모리가 그리 많지 않으므로, 굳이 global 객체로 사용하지 말고 각각 객체를 생성하여 사용하시길 추천드립니다.

sorryhyun commented 3 years ago
ko = None

def morphinit():
    global ko
    ko = KhaiiiApi()

def tokenize(sentence):
    # 형태소 분석기를 활용하여 매 문장을 형태소분석, 리스트 형태로 리턴
    # 대체어 추출할 단어만을 리스트 형태로 출력.
    reslist = []
    result = []
    templist = []
    for word in ko.analyze(sentence):
        for morph in word.morphs:
            templist.append((morph.lex, morph.tag))
    reslist = [tempword[0] for tempword in templist]

    if reslist:  # 만약 이번에 읽은 데이터에 명사가 존재할 경우에만
        result= reslist 
    return result

def output_preprocessed_corpus(self, fout_path):
    result = []
    fin = open('./dataset/train/train.txt', 'r+')
    fout = open(fout_path, 'w')
    lines = fin.readlines()
    import time
    start = time.time()
    import multiprocessing
    with multiprocessing.Pool(self.num_cores, initializer=morphinit) as pool:
        result = pool.map(tokenize, lines)

    print("총 " + str(time.time() - start) + "초 소요")
    pool.close()
    pool.join()

    for list in result:
        fout.write(",".join(list))
        fout.write('\n')
    fout.close()

    return result #[ [문장1에대한 토큰리스트1], [문장2에대한 토큰리스트2], [...] ]

음 이렇게 수정하면 각각 객체를 생성하는게 되나요? 일단 이렇게 수정한 이후로 에러가 해결된 상태입니다.

krikit commented 3 years ago

글쎄요 위에 써주신 코드에서 근본적인 차이는 사실 잘 모르겠습니다. initializer로 전달되는 morphinit 함수가 global variable을 쓰게 되는데 multiprocessing 모듈에 의해 생성된 child process에서 생성이 될지, parent process에서 생성이 될지 정확히 잘 모르겠네요.

물론 child process에서 독립적으로 각자 khaiii 객체를 생성하는 것이 문제의 소지가 가장 작고, 속도도 빠를 것으로 생각합니다. 저는 이런 경우 아래와 같이 khaiii 객체를 반환받는 함수를 하나 만들고, 그 안에서 lazy하게 생성하여 사용하는 것을 선호하는 편입니다.


ko: KhaiiiApi = None

def get_khaiii() -> KhaiiiApi:
    global ko
    if not ko:
        ko = KhaiiiApi()
    return ko

def tokenize_sentence():
    ko = get_khaiii()
    for word in ko.analyze(sentence):
        ...

이렇게 하면 child process에서 호출되는 tokenize_sentence 함수에서 khaiii 객체를 생성하는 get_khaiii 함수를 호출하게 되므로, 각 child process에서 별도로 khaiii 객체를 만들어 사용하게 될테니까요.

sorryhyun commented 3 years ago

좋은 코드 감사합니다. 막연하게 써본건데 남겨주신 코드가 좀더 확실해 보이네요!

sorryhyun commented 3 years ago

이번에 하드디스크를 새로 설치하면서 khaiii를 재설치하게 되었는데, ctest 오류가 발생하지 않고 오류를 재현하기가 어려워 이슈를 닫겠습니다.