koalanlp / python-support

Python wrapper for KoalaNLP (Korean NLP with Java/Scala)
MIT License
31 stars 9 forks source link

finalize 후 initialize()를 다시 하려는데 안되네요. #4

Closed kwonmha closed 4 years ago

kwonmha commented 5 years ago

finalize() 후에, 다시 initialize()하려고 하는데, 아래처럼 에러가 납니다.

py4j.java_gateway Socket listening on ('127.0.0.1', 25334)
jip JVM initialization procedure is completed.
py4j.java_gateway Callback Server Shutting Down
False
py4j.java_gateway Callback Server Starting
py4j.java_gateway Socket listening on ('127.0.0.1', 25334)
Traceback (most recent call last):
  File "/home/user01/.local/lib/python3.5/site-packages/koalanlp/Util.py", line 188, in initialize
    check_jvm()
  File "/home/user01/.local/lib/python3.5/site-packages/koalanlp/jvm.py", line 34, in check_jvm
    class_of('java.lang.String')('123')
  File "/home/user01/.local/lib/python3.5/site-packages/py4j/java_gateway.py", line 1552, in __call__
    answer = self._gateway_client.send_command(command)
  File "/home/user01/.local/lib/python3.5/site-packages/py4j/java_gateway.py", line 1012, in send_command
    connection = self._get_connection()
  File "/home/user01/.local/lib/python3.5/site-packages/py4j/java_gateway.py", line 956, in _get_connection
    raise Py4JNetworkError("Gateway is not connected.")
py4j.protocol.Py4JNetworkError: Gateway is not connected.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "jvm_test.py", line 17, in <module>
    initialize(java_options="-Xmx10g -Xms10g -Dfile.encoding=utf-8", HNN='2.0.3')
  File "/home/user01/.local/lib/python3.5/site-packages/koalanlp/Util.py", line 190, in initialize
    raise Exception("JVM test failed because %s" % str(e))
Exception: JVM test failed because Gateway is not connected.`

왜 그런 건지 혹시 알 수 있을까요?

그리고 아래 코드 링크처럼 exception 메세지에 보니, koalanlp.Util.done()을 하라고 하는데, done()이란 함수가 빠진 건가요, 아니면 finalize()를 의미하는 건가요?

https://github.com/koalanlp/python-support/blob/20cd7ded80fb79a4606b2fd830d9b5a7b062ad27/koalanlp/Util.py#L202

kwonmha commented 5 years ago

문제점을 파악하여 말씀드립니다.

Util.initialize() 코드상에서 start_jvm()이후, check_jvm()을 하는데요,

check_jvm()class_of('java.lang.String')('123') 코드로 Java String class를 만들고 JavaClass.__call()__까지 실행합니다.

그 후, finalize()를 해도, 'java.lang.String_CLASS_DIC 에 남아 있어서

새로운 initialize() 과정에서 class_of('java.lang.String')('123') 코드를 실행하면 class_of()level = level.__getattr__() 를 실행하면서 __init__ 하고, __call__ 을 하는 게 아니라,

이미 'java.lang.String'이 '_CLASS_DIC' 안에 남아있기 때문에 바로 리턴이 되고, __call__이 실행됩니다. 이 JavaClass 인스턴스는 이전 jvm을 finalize()하면서 is_connected = False가 된 GatewayClient를 갖고 있습니다.

그래서

def check_jvm():
    class_of('java.lang.String')('123')
    del _CLASS_DIC['java.lang.String']

로 수정해봤습니다.

kwonmha commented 5 years ago

완벽한 해결책은 아닌 것 같아요. 다른 부분들에서도 문제가 생겨서 좀 더 알아보고 있습니다.

kwonmha commented 5 years ago

finalize -> initialize를 하려는 이유는, 10기가 정도 되는 코퍼스를 대상으로 문장분리 작업을 하고 있는데, 일정한 위치에서 문장 분리가 멈추는 현상이 일어나기 때문입니다. 한 번에 처리하는 텍스트 양을 바꾸면 또 전혀 상관없는 위치에서 문장 분리가 멈춰 버립니다. 코드는 대략 아래와 같습니다.

initialize(java_options="-Xmx10g -Xms10g -Dfile.encoding=utf-8", HNN='2.0.3')
splitter = SentenceSplitter('hnn')

while i < len(corpus):
  corpus_segment = corpus[i: min(i+char_count, len(corpus))]
  sentences.extend(splitter(corpus_segment))

그래서 재시작하면 멈추는 현상이 안 일어날 것 같아 재시작을 해보려고 했는데, 그것도 여의치 않네요.

kwonmha commented 5 years ago
  1. 위에 말씀드린 Java.lang.String 관련 문제가 다른 클래스들에서도 일어납니다. 그래서 shutdown_jvm()을 아래와 같이 수정했습니다.

    def shutdown_jvm():
    global GATEWAY
    global _CLASS_DIC
    GATEWAY.shutdown()
    GATEWAY = None
    _CLASS_DIC = {}
    return is_jvm_running()
  2. 한 프로세스 안에서 jvm을 다시 시작하면 initialize()_resolve_artifacts_modified() 를 실행할 때 이미 필요한 라이브러리가 이미 있다고 생각해서 그런지 다시 다운받지 않습니다. 그래서 리턴값을 받는 down_list가 비어 있고, 결과적으로 start_jvm(java_options, classpaths)classpaths가 없습니다. 그래서 필요한 SentenceSplitter()를 다시 생성하지 못합니다. 일단 저는 글로벌 변수 하나를 만들어서 해결했습니다.

def initialize(): 
        ...
        down_list.sort(key=lambda a: a.repos.uri)

        if len(down_list) == 0:
            global _DOWN_LIST
            down_list = _DOWN_LIST
        else:
            _DOWN_LIST = down_list

        for artifact in down_list:
        ...
bgnkim commented 4 years ago

@kwonmha 인사가 늦었습니다. 말씀하신 대로 재초기화 관련 코드에 문제가 있습니다. 알려주신 방법으로 임시 조치가 가능하지만, 더 좋은 방법이 있어 수정하고 push하였으니 변경사항을 확인하실 수 있을 것 같습니다. 지금은 몇 가지 더 고칠 사항이 있어서, 새 버전 업로드 때에 다시 알려드리겠습니다.

kwonmha commented 4 years ago

네 감사합니다.

bgnkim commented 4 years ago

KoalaNLP PyPi 패키지 2.1.3부터 적용되었습니다. 감사합니다.