[konlpy] 형태소 분석기별 품사 태깅(Pos-Tagging) 비교

    바로 이전 포스팅에서는 형태소 분석기별 가장 중요한 명사를 추출하는 성능(속도 측면)을 비교해 봤습니다. 품사 태깅(pos-tagging)은 우리가 흔히 형태소 분석기라는 말과 동의어로 생각하는 기능으로 명사만 추출하는게 아니라 문장의 모든 품사를 분석하게 됩니다.

     

    명사만 추출하는 것은 사실 어렵지 않습니다. 단어안에 명사를 그냥 찾으면 그만이니까요. 하지만 pos-tagging은 한국어 문법을 제대로 이해해야 하고, 분석 방법과 퀄리티에 따라 속도에 큰 영향을 주게 됩니다. 즉, 명사만 추출하는 것으로 속도가 오래 걸리지 않지만 대다수 pos-tagging 작업으로 인해서 형태소 분석기가 오래 걸리게 되는 것이죠.

     

    예를 들어, 자모 단위로 분리해서 분석을 한다면 상당히 큰 cpu 자원을 사용할 수도 있으며 이렇게 분석을 하면 매우 훌륭한 퀄리티의 결과가 나오게 되는 것이죠. 이는 정확히 트레이드 오프(trade-off) 관계이며 속도가 빠르다고 무조건 좋은 형태소 분석기라고 말을 할 수 없습니다.

     

    전반적으로 소스 코드는 이전 포스팅에서 다룬 명사 속도 비교와 상당히 유사합니다. 사실 nouns 하나와 morph(품사명을 제외한 형태소만 추출) 혹은 pos(형태소와 품사 모두 추출) 메소드 하나의 차이 정도로 인지하면 그만일 정도이죠. 

     

    형태소 분석기, 품사태깅 비교


    전체 소스

    import urllib.request
    import time
    from konlpy.tag import Okt
    from konlpy.tag import Komoran
    from konlpy.tag import Kkma
    
    import matplotlib.pyplot as plt
    
    #urllib.request.urlretrieve("https://raw.githubusercontent.com/e9t/nsmc/master/ratings.txt", filename="ratings.txt")
    
    def set_review_data(file_nm):
        with open(file_nm, "r", encoding="utf8") as f:
            data = [line.split('\t') for line in f.read().splitlines()]
        return data[1:20001]
    
    
    start_time = time.time()
    print('[BEGIN] 리뷰 데이터를 읽는다.')
    review_data = set_review_data('ratings.txt')
    print('review_data size->', len(review_data))
    print('[END] 리뷰 데이터를 읽는다. (', time.time() - start_time, ')sec')
    
    kolnpy_perform = {}
    print(review_data[1][1])
    
    start_time = time.time()
    kkma = Kkma()
    morph_data = [kkma.pos(datas[1]) for datas in review_data]
    print(morph_data[1])
    kolnpy_perform['kkma'] = time.time() - start_time
    print('꼬꼬마 품사태깅 (', kolnpy_perform['kkma'], ')sec')
    
    start_time = time.time()
    komoran = Komoran()
    morph_data = [komoran.pos(datas[1]) for datas in review_data]
    print(morph_data[1])
    kolnpy_perform['komoran'] = time.time() - start_time
    print('코모란 품사태깅 (', kolnpy_perform['komoran'], ')sec')
    
    start_time = time.time()
    okt = Okt()
    morph_data = [okt.pos(datas[1]) for datas in review_data]
    print(morph_data[1])
    kolnpy_perform['okt'] = time.time() - start_time
    print('OKT 품사태깅 (', kolnpy_perform['okt'], ')sec')
    
    
    # 그래프 출력
    plt.bar(*zip(*kolnpy_perform.items()))
    plt.show()

     

    파이썬(python)을 잘 몰라서 소스가 혹시 잘 이해가 되지 않는다면 자세한 설명은 아래에 링크를 올린 이전에 작성한 명사 분석 속도 비교 포스팅을 보시고 이해를 하시는게 좋을 것 같습니다.

     

     

    [kolnpy] 형태소 분석기별 명사(noun) 분석 속도 비교

    최근 kolnpy 사이트에 적혀 있는 형태소 분석기의 분석 속도와 실제로 느꼈던 kolnpy의 개별 형태소 분석기의 성능이 맞지 않는 것 같아서 비교적 용량이 많은 문장을 기반으로 형태소 분석기의 성

    needjarvis.tistory.com

     

    리뷰 2만개를 읽어와서, 3개의 형태소 분석기의 pos 메소드의 속도를 비교하며 비교적 긴 문장인 2번째 리뷰의 형태소 품질까지 분석해보도록 하겠습니다. 형태소 분석은 너무 품질이 좋아서 개발자가 어떻게 사용하느냐에 따라서 오히려 독이 될 수도 있으니 그 부분에 대해서도 적어보겠습니다.

     

     

    결과

    [BEGIN] 리뷰 데이터를 읽는다.
    review_data size-> 20000
    [END] 리뷰 데이터를 읽는다. ( 0.35003018379211426 )sec
    디자인을 배우는 학생으로, 외국디자이너와 그들이 일군 전통을 통해 발전해가는 문화산업이 부러웠는데. 사실 우리나라에서도 그 어려운시절에 끝까지 열정을 지킨 노라노 같은 전통이있어 저와 같은 사람들이 꿈을 꾸고 이뤄나갈 수 있다는 것에 감사합니다.
    [('디자인', 'NNG'), ('을', 'JKO'), ('배우', 'VV'), ('는', 'ETD'), ('학생', 'NNG'), ('으로', 'JKM'), (',', 'SP'), ('외국', 'NNG'), ('디자이너', 'NNG'), ('와', 'JKM'), ('그', 'NP'), ('들', 'XSN'), ('이', 'JKS'), ('일구', 'VV'), ('ㄴ', 'ETD'), ('전통', 'NNG'), ('을', 'JKO'), ('통하', 'VV'), ('어', 'ECS'), ('발전', 'NNG'), ('하', 'XSV'), ('어', 'ECS'), ('갈', 'VV'), ('는', 'ETD'), ('문화', 'NNG'), ('산업', 'NNG'), ('이', 'JKS'), ('부럽', 'VA'), ('었', 'EPT'), ('는데', 'ECD'), ('.', 'SF'), ('사실', 'NNG'), ('우리나라', 'NNG'), ('에서', 'JKM'), ('도', 'JX'), ('그', 'MDT'), ('어렵', 'VA'), ('ㄴ', 'ETD'), ('시절', 'NNG'), ('에', 'JKM'), ('끝', 'NNG'), ('까지', 'JX'), ('열정', 'NNG'), ('을', 'JKO'), ('지키', 'VV'), ('ㄴ', 'ETD'), ('노', 'VV'), ('라', 'ECD'), ('노', 'NNG'), ('같', 'VA'), ('은', 'ETD'), ('전통', 'NNG'), ('이', 'JKS'), ('있', 'VV'), ('어', 'ECD'), ('저', 'NP'), ('와', 'JKM'), ('같', 'VA'), ('은', 'ETD'), ('사람', 'NNG'), ('들', 'XSN'), ('이', 'JKS'), ('꿈', 'NNG'), ('을', 'JKO'), ('꾸', 'VV'), ('고', 'ECE'), ('이루', 'VV'), ('어', 'ECS'), ('나가', 'VV'), ('ㄹ', 'ETD'), ('수', 'NNB'), ('있', 'VV'), ('다는', 'ETD'), ('것', 'NNB'), ('에', 'JKM'), ('감사', 'NNG'), ('하', 'XSV'), ('ㅂ니다', 'EFN'), ('.', 'SF')]
    꼬꼬마 품사태깅 ( 391.3187165260315 )sec
    [('디자인', 'NNG'), ('을', 'JKO'), ('배우', 'VV'), ('는', 'ETM'), ('학생', 'NNG'), ('으로', 'JKB'), (',', 'SP'), ('외국', 'NNP'), ('디자이너', 'NNP'), ('와', 'JC'), ('그', 'NP'), ('들', 'XSN'), ('이', 'JKS'), ('일구', 'VV'), ('ㄴ', 'ETM'), ('전통', 'NNG'), ('을', 'JKO'), ('통하', 'VV'), ('아', 'EC'), ('발전', 'NNG'), ('하', 'XSV'), ('아', 'EC'), ('가', 'VX'), ('는', 'ETM'), ('문화', 'NNG'), ('산업', 'NNG'), ('이', 'JKS'), ('부럽', 'VA'), ('었', 'EP'), ('는데', 'EF'), ('.', 'SF'), ('사실', 'NNG'), ('우리나라', 'NNP'), ('에서', 'JKB'), ('도', 'JX'), ('그', 'MM'), ('어렵', 'VA'), ('ㄴ', 'ETM'), ('시절', 'NNG'), ('에', 'JKB'), ('끝', 'NNG'), ('까지', 'JX'), ('열정', 'NNG'), ('을', 'JKO'), ('지키', 'VV'), ('ㄴ', 'ETM'), ('노라', 'NNP'), ('노', 'NNP'), ('같', 'VA'), ('은', 'ETM'), ('전통', 'NNG'), ('이', 'JKS'), ('있', 'VV'), ('어', 'EC'), ('저', 'NP'), ('와', 'JKB'), ('같', 'VA'), ('은', 'ETM'), ('사람', 'NNG'), ('들', 'XSN'), ('이', 'JKS'), ('꿈', 'NNG'), ('을', 'JKO'), ('꾸', 'VV'), ('고', 'EC'), ('이루', 'VV'), ('어', 'EC'), ('나가', 'VX'), ('ㄹ', 'ETM'), ('수', 'NNB'), ('있', 'VV'), ('다는', 'ETM'), ('것', 'NNB'), ('에', 'JKB'), ('감사', 'NNG'), ('하', 'XSV'), ('ㅂ니다', 'EF'), ('.', 'SF')]
    코모란 품사태깅 ( 24.937052488327026 )sec
    [('디자인', 'Noun'), ('을', 'Josa'), ('배우는', 'Verb'), ('학생', 'Noun'), ('으로', 'Josa'), (',', 'Punctuation'), ('외국', 'Noun'), ('디자이너', 'Noun'), ('와', 'Josa'), ('그', 'Noun'), ('들', 'Suffix'), ('이', 'Josa'), ('일군', 'Noun'), ('전통', 'Noun'), ('을', 'Josa'), ('통해', 'Noun'), ('발전', 'Noun'), ('해가는', 'Verb'), ('문화', 'Noun'), ('산업', 'Noun'), ('이', 'Josa'), ('부러웠는데', 'Adjective'), ('.', 'Punctuation'), ('사실', 'Noun'), ('우리나라', 'Noun'), ('에서도', 'Josa'), ('그', 'Noun'), ('어려운', 'Adjective'), ('시절', 'Noun'), ('에', 'Josa'), ('끝', 'Noun'), ('까지', 'Josa'), ('열정', 'Noun'), ('을', 'Josa'), ('지킨', 'Verb'), ('노라노', 'Noun'), ('같은', 'Adjective'), ('전통', 'Noun'), ('이', 'Josa'), ('있어', 'Adjective'), ('저', 'Noun'), ('와', 'Josa'), ('같은', 'Adjective'), ('사람', 'Noun'), ('들', 'Suffix'), ('이', 'Josa'), ('꿈', 'Noun'), ('을', 'Josa'), ('꾸고', 'Verb'), ('이뤄', 'Verb'), ('나갈', 'Verb'), ('수', 'Noun'), ('있다는', 'Adjective'), ('것', 'Noun'), ('에', 'Josa'), ('감사합니다', 'Verb'), ('.', 'Punctuation')]
    OKT 품사태깅 ( 56.301979541778564 )sec

     

    Pos-tagging 속도

    pos-tagging 속도

     

    속도는 명사와 유사하게 각각 391초, 24초, 56초 입니다. 즉 pos-tagging과 명사 추출 메소드(nouns)는 완전히 동일한 로직이며, 형태소 분석을 완벽히 수행 후 명사를 추출한다는 것을 알 수 있습니다. noun만 추출할 때에는 다른 로직으로 좀 더 속도를 빠르게 할거라고도 생각 했는데 그런거 없이 동일한 로직으로 돌고 있습니다.

     

    제가 전에 자바(Java)로 형태소 분석기를 만들때에는 오로지 명사만 제대로 추출해보자라는 생각에 명사 이외의 품사는 뭉뚱그려서 분석하곤 했습니다. 예를 들어, 위의 예시의 "감사합니다"의 경우 감사(명사) 이후의 합니다는 "하+ㅂ니다"로 분석하지 않고 그냥 "합니다=어미"로 퉁쳐서 분석했었습니다. 오로지 명사만 제대로 뽑으면 되기 때문에 나머지는 속도의 문제로 제대로 분석을 안해버리는 식으로 진행을 하는 것이죠.

     

    Pos-Tagging 품질

    각설하고 형태소 분석기별로 품질을 평가해보겠습니다. 

    디자인을 배우는 학생으로, 외국디자이너와 그들이 일군 전통을 통해 발전해가는 문화산업이 부러웠는데. 사실 우리나라에서도 그 어려운시절에 끝까지 열정을 지킨 노라노 같은 전통이있어 저와 같은 사람들이 꿈을 꾸고 이뤄나갈 수 있다는 것에 감사합니다.

     

    꼬꼬마

    [('디자인', 'NNG'), ('을', 'JKO'), ('배우', 'VV'), ('는', 'ETD'), ('학생', 'NNG'), ('으로', 'JKM'), (',', 'SP'), ('외국', 'NNG'), ('디자이너', 'NNG'), ('와', 'JKM'), ('그', 'NP'), ('들', 'XSN'), ('이', 'JKS'), ('일구', 'VV'), ('ㄴ', 'ETD'), ('전통', 'NNG'), ('을', 'JKO'), ('통하', 'VV'), ('어', 'ECS'), ('발전', 'NNG'), ('하', 'XSV'), ('어', 'ECS'), ('갈', 'VV'), ('는', 'ETD'), ('문화', 'NNG'), ('산업', 'NNG'), ('이', 'JKS'), ('부럽', 'VA'), ('었', 'EPT'), ('는데', 'ECD'), ('.', 'SF'), ('사실', 'NNG'), ('우리나라', 'NNG'), ('에서', 'JKM'), ('도', 'JX'), ('그', 'MDT'), ('어렵', 'VA'), ('ㄴ', 'ETD'), ('시절', 'NNG'), ('에', 'JKM'), ('끝', 'NNG'), ('까지', 'JX'), ('열정', 'NNG'), ('을', 'JKO'), ('지키', 'VV'), ('ㄴ', 'ETD'), ('노', 'VV'), ('라', 'ECD'), ('노', 'NNG'), ('같', 'VA'), ('은', 'ETD'), ('전통', 'NNG'), ('이', 'JKS'), ('있', 'VV'), ('어', 'ECD'), ('저', 'NP'), ('와', 'JKM'), ('같', 'VA'), ('은', 'ETD'), ('사람', 'NNG'), ('들', 'XSN'), ('이', 'JKS'), ('꿈', 'NNG'), ('을', 'JKO'), ('꾸', 'VV'), ('고', 'ECE'), ('이루', 'VV'), ('어', 'ECS'), ('나가', 'VV'), ('ㄹ', 'ETD'), ('수', 'NNB'), ('있', 'VV'), ('다는', 'ETD'), ('것', 'NNB'), ('에', 'JKM'), ('감사', 'NNG'), ('하', 'XSV'), ('ㅂ니다', 'EFN'), ('.', 'SF')]

    꼬꼬마는 기본적으로 품사를 매우 디테일하게 분석합니다. 예를 들어 일군 이라는 동사를 일구 + ㄴ 으로 한다던지 통해를 통하+어로 한다던지, 감사합니다도 감사 + 하 + ㅂ니다로 분석을 하는 정말 언어학자가 형태소 분석을 하는 수준으로 진행을 합니다.

     

    이렇게 뛰어난 퀄리티는 매우 디테일한 서비스에 효과적입니다. 예를 들어 챗봇(Chatbot)에서 단순히 인텐트(Intent)만 사용하는 것이 아니라 품사의 조합에 따라 과거형, 긍정 지정사 등을 분석하여 보다 적절하고 스무스한 대화를 유도하는 등의 서비스를 수행하려면 이와 같은 수준으로 품사를 분석해야 합니다.

     

    코모란

    [('디자인', 'NNG'), ('을', 'JKO'), ('배우', 'VV'), ('는', 'ETM'), ('학생', 'NNG'), ('으로', 'JKB'), (',', 'SP'), ('외국', 'NNP'), ('디자이너', 'NNP'), ('와', 'JC'), ('그', 'NP'), ('들', 'XSN'), ('이', 'JKS'), ('일구', 'VV'), ('ㄴ', 'ETM'), ('전통', 'NNG'), ('을', 'JKO'), ('통하', 'VV'), ('아', 'EC'), ('발전', 'NNG'), ('하', 'XSV'), ('아', 'EC'), ('가', 'VX'), ('는', 'ETM'), ('문화', 'NNG'), ('산업', 'NNG'), ('이', 'JKS'), ('부럽', 'VA'), ('었', 'EP'), ('는데', 'EF'), ('.', 'SF'), ('사실', 'NNG'), ('우리나라', 'NNP'), ('에서', 'JKB'), ('도', 'JX'), ('그', 'MM'), ('어렵', 'VA'), ('ㄴ', 'ETM'), ('시절', 'NNG'), ('에', 'JKB'), ('끝', 'NNG'), ('까지', 'JX'), ('열정', 'NNG'), ('을', 'JKO'), ('지키', 'VV'), ('ㄴ', 'ETM'), ('노라', 'NNP'), ('노', 'NNP'), ('같', 'VA'), ('은', 'ETM'), ('전통', 'NNG'), ('이', 'JKS'), ('있', 'VV'), ('어', 'EC'), ('저', 'NP'), ('와', 'JKB'), ('같', 'VA'), ('은', 'ETM'), ('사람', 'NNG'), ('들', 'XSN'), ('이', 'JKS'), ('꿈', 'NNG'), ('을', 'JKO'), ('꾸', 'VV'), ('고', 'EC'), ('이루', 'VV'), ('어', 'EC'), ('나가', 'VX'), ('ㄹ', 'ETM'), ('수', 'NNB'), ('있', 'VV'), ('다는', 'ETM'), ('것', 'NNB'), ('에', 'JKB'), ('감사', 'NNG'), ('하', 'XSV'), ('ㅂ니다', 'EF'), ('.', 'SF')]

    코모란의 경우도 꼬꼬마와 분석의 퀄리티가 상당히 유사합니다. 아마도 비슷한 품사체계(형태소 분석기마다 품사 체계가 다릅니다)로 분석을 하는 것으로 예상되며, 약간의 차이는 품사 사전의 차이 정도와 제대로 분석이 안되었을 때 어떤 선택을 하는지에 따른 차이정도만 있지 않을까도 생각합니다.

     

     

    OKT(Open Korean Text)

    [('디자인', 'Noun'), ('을', 'Josa'), ('배우는', 'Verb'), ('학생', 'Noun'), ('으로', 'Josa'), (',', 'Punctuation'), ('외국', 'Noun'), ('디자이너', 'Noun'), ('와', 'Josa'), ('그', 'Noun'), ('들', 'Suffix'), ('이', 'Josa'), ('일군', 'Noun'), ('전통', 'Noun'), ('을', 'Josa'), ('통해', 'Noun'), ('발전', 'Noun'), ('해가는', 'Verb'), ('문화', 'Noun'), ('산업', 'Noun'), ('이', 'Josa'), ('부러웠는데', 'Adjective'), ('.', 'Punctuation'), ('사실', 'Noun'), ('우리나라', 'Noun'), ('에서도', 'Josa'), ('그', 'Noun'), ('어려운', 'Adjective'), ('시절', 'Noun'), ('에', 'Josa'), ('끝', 'Noun'), ('까지', 'Josa'), ('열정', 'Noun'), ('을', 'Josa'), ('지킨', 'Verb'), ('노라노', 'Noun'), ('같은', 'Adjective'), ('전통', 'Noun'), ('이', 'Josa'), ('있어', 'Adjective'), ('저', 'Noun'), ('와', 'Josa'), ('같은', 'Adjective'), ('사람', 'Noun'), ('들', 'Suffix'), ('이', 'Josa'), ('꿈', 'Noun'), ('을', 'Josa'), ('꾸고', 'Verb'), ('이뤄', 'Verb'), ('나갈', 'Verb'), ('수', 'Noun'), ('있다는', 'Adjective'), ('것', 'Noun'), ('에', 'Josa'), ('감사합니다', 'Verb'), ('.', 'Punctuation')]

    OKT는 형태소 처리기를 표방하는 것처럼 다른 형태소 분석기와는 차이가 많이 납니다. 특히 "일군(일구다)"이라는 단어를 명사로 추출했는데 공백 기준으로 한 토큰이 명사 사전에 존재한다면 명사로 뽑는다는 것을 알 수 있습니다. 

     

    결론적으로 품사 태깅의 퀄리티를 본다면 Kkma > Komoran > Okt로 보입니다. 하지만 이렇게 품사태깅이 정확하다고 무조건 꼬꼬마와 코모란을 사용하는 것도 방법은 아닙니다. 

     

    형태소 분석에 따른 프로젝트

    명사 + @ 정도의 품사를 사용하여 심층신경망(Deep Neural Network, DNN)이나 순환신경망(Recurrent Neural Network, RNN) 등으로 문서를 분류한다고 가정을 해보겠습니다. 그런데 꼬꼬마와 코모란 정도의 수준으로 품사를 잘게잘게 잘라서 분석을 한다면 임베딩(Embedding) 퀄리티가 상당히 구릴 수도 있습니다. 

     

    위에서 설명한 "일군"이라는 단어를 보자면, 이를 그대로 뽑은 결과와 "일구+ㄴ"으로 뽑을 경우 임베딩을 수행을 위해 "ㄴ"을 버리고 "일구"라는 단어만 추출하게 될 것입니다. 그러나 이렇게 되면 품사 자체의 느낌이 사라지기 때문에 더욱 정확한 결과가 나오지 않을 수도 있습니다. 

     

    어떤 한문장이 어떤 의미를 가지는가?(ex: 챗봇)의 경우는 최대한 디테일하게 품사를 태깅하는 것이 유리할지 모르지만 퉁명스럽게 단어를 뽑고 임베딩을 진행해야 하는 경우 좀 더 의미있게 형태소를 뽑아내는 것이 유리할 겁니다. 그래서 어느정도 품사를 분석하는 수준이 내가 만든 서비스 혹은 사이트에 맞는지 체크하는 것도 중요하며, 내부적으로 성능 테스트를 꼭 진행하거나 후처리의 로직을 넣어서 작업하시는 것이 좋을 겁니다.

     

    그런데 문제는 이과 출신이 대다수인 개발자들이 이런 품사를 분석하여 후처리를 하는 경우가 상당히 흔치 않고 어렵기 때문에 자연어처리를 제대로 할 줄 알고 전문으로 하는 개발자 혹은 엔지니어들은 상당히 드문 것 같습니다.

     

    연관포스팅

    [kolnpy] 형태소 분석기별 명사(noun) 분석 속도 비교

    형태소 분석기, Okt(Open Korean Text) (구)트위터 형태소분석기

    형태소 분석의 개념과 konlpy로 사용 하기

     

    댓글

    Designed by JB FACTORY