헌법에서 특정 단어가 사용된 용례를 찾아보기 : konlpy와 nltk를 활용한 한국어 concordance
텍스트를 특정한 목적 아래 전산화하여 모아 둔 것을 코퍼스(말뭉치)라고 한다. 코퍼스 언어학은 이러한 코퍼스를 처리하는 방법론을 연구한다. 이러한 코퍼스 언어학의 기법 중 하나가 concordance이다. Concordance는 특정 코퍼스에서 지정한 단어가 등장할 때, 이 단어를 맥락과 함께 표시한 것이다. 위의 그림은 대한민국 헌법에서 뽑아낸 '국민(일반명사)'에 대한 concordance이다.
NLTK는 Python을 사용하여 자연어처리를 할 수 있도록 도와주는 패키지이다. 코퍼스 언어학이 아니라 자연어처리를 위한 패키지인 이유는 concordance와 같은 기능을 더불어 tokenization, lemmatization 등의 처리를 할 수 있도록 도와주기 때문이다. 문제는 이 패키지는 한국어를 지원하지 않는다는 것이다.
이 글에서는 NLTK를 사용해서 한국어 코퍼스의 concordance를 뽑아본다. 이 과정에서 konlpy의 도움을 받는다.
문제상황
영어에서는 띄어쓰기 단위가 대부분 단어 단위이다. "I study English"이라는 문장은 'i', 'study', 'english'라는 세 단어로 구성되었다고 볼 수 있다. 이에 대한 예외상황은 "'m", "'s"와 같이 "'"와 같이 쓰이는 표현들인데, 이 종류가 한정되어 있어서 처리하는데 크게 어려움은 없다. "I'm studying English"는 'i', ''m', 'studying', 'english'로 나뉘어 져야 하는데, 크게 어렵지는 않은 이 과정을 NLTK에서는 자체적으로 지원한다.
문제는 한국어는 영어와 달리 띄어쓰기 단위가 단어 단위가 아니라는 것이다. 띄어쓰기단위로 문장을 나누면 "나는 영어를 공부한다", "영어가 어렵다"와 같은 두 문장은 '나는', '영어를', '공부한다', '영어가', '어렵다'로 나뉘어 진다. 분명 '영어를'과 '영어가'는 '영어'라는 같은 뜻을 가지고 있는 다른 문장 성분에 불과하지만, 형태가 다르기 때문에 NLTK에서는 다른 단어로 처리된다. 즉, 의존형태소를 떨어뜨려서 형태소 단위로 문장을 분절할 필요가 있다.
목표
1. 헌법 전문을 불러와서
2. 형태소 단위로 쪼개준 뒤
3. concordance를 파일로 내보낸다
사용 패키지
konlpy == 0.5.2
nltk == 3.6.5
헌법 전문을 불러오기
헌법을 불러오는 방법은 많겠지만, 가장 직관적인 방법은 국가법령정보센터에서 다운로드 받는 것이다.
https://www.law.go.kr/lsEfInfoP.do?lsiSeq=61603#
대한민국헌법 | 국가법령정보센터 | 법령 > 본문
대한민국헌법 [시행 1988. 2. 25.] [헌법 제10호, 1987. 10. 29., 전부개정]
www.law.go.kr
오른쪽 위에 보면 작게 플로피 디스크 모양의 저장 버튼이 있다. 아쉽게도 txt로 내보내기 옵션이 없는데, 한글이나 워드로 내보낸 뒤 전문을 복사해 메모장 등에 붙여넣어서 txt 파일로 만들어 준다.
여기서는 '대한민국헌법.txt'로 저장한다.
파일은 다음과 같이 불러와서 content 변수로 저장한다.
with open("대한민국헌법.txt") as f:
content = f.read()
형태소 단위로 쪼개기
한국어로 형태소분석기라 불리는 프로그램들이 있다. 영어 등에서 사용되는 '품사 태깅 도구Part-of-Speech tagger'에 대응하는 프로그램인데, 크게 두 가지 역할을 한다: 1) 텍스트를 형태소 단위로 쪼갠다(즉, 의존형태소와 실질형태소를 분리한다); 2) 각 형태소의 기능을 붙인다. 이 과정을 거치면 '독립운동으로 건립된 대한민국임시정부의 법통' 이라는 표현은 '독립 + 운동 + 으로 + 건립 + 된 + 대한민국 + 임시 + 정부 + 의 + 법통'정도로 분절되며, 각각의 형태소에 대해 일반명사인지, 고유명사인지, 조사인지 등등의 정보가 붙는다.
형태소분석기로는 울산대의 UTagger가 정확도가 상당하다고 알려져 있는데, 아쉽게도 사용하기가 쉽지 않을 뿐더러 Python에서 사용하기가 어렵다. 여기서는 꼬꼬마, Mecab-Ko 등의 형태소분석기를 종합해서 제공하는 konlpy 패키지를 사용한다.
konlpy 자체의 설치는 매우 쉽다. PIP에 등록되어 있기 때문에 pip install konlpy 한 줄이면 설치가 완료된다. 다만, 여기서 사용하는 형태소분석기 Mecab-ko는 별도의 설치 과정이 필요하다. 윈도우의 경우에는 pip 이외의 설정도 필요한 듯 하다. Mecab 및 윈도우에서의 설치방법은 https://konlpy.org/ko/latest/install/ 페이지를 참조. Mecab은 윈도우에서는 사용할 수 없다. Apple Silicon(M1) 맥에서의 Mecab 설치 및 사용에 관해서는 https://izelstudy.tistory.com/entry/Apple-SiliconM1-맥에서-KoNLPy-사용하기-jdk에서-Mecab까지 참조
윈도우에서 사용할 수 없는 Mecab을 자주 쓰게 되는데, 정확도도 높을 뿐더러 무엇보다 빠르기 때문이다. 사실 헌법 전문 정도의 글은 짧기 때문에 어떤 형태소분석기를 써도 정확도만 높다면 크게 상관이 없다. 따라서 윈도우라면 Mecab을 예를 들어 꼬꼬마(Kkma)로 바꾸기만 하면 된다. 다만, 윈도우에서 큰 텍스트를 처리해야 한다면 코랩 등을 써서 Mecab을 쓰는 쪽이 편하다.
다른 형태소분석기로는 카카오의 KHAIII가 있는데, 별도의 설치가 필요하며, 마찬가지로 윈도우에서 사용할 수 없다. 윈도우에서 사용할 만한 정확도도 어느정도 높고 속도도 빠르며 Python에서 활용이 가능한 형태소분석기로는 Kiwi가 있다. Kiwi 자체는 독립적인 형태소 분석기인데, Python에서 사용하려면 https://github.com/bab2min/kiwipiepy 버전을 설치하면 된다.
분석은 다음과 같이 진행한다.
from konlpy.tag import Mecab
# from konlpy.tag import Kkma
tagger = Mecab()
# tagger = Kkma()
content_tokens = tagger.pos(content)
content_tagged = list()
for token in content_tokens:
content_tagged.append(token[0] + '/' + token[1])
konlpy에서 Mecab 혹은 Kkma를 불러와 tagger로 호출한다. tagger.pos(str)을 돌리면 형태소분석이 진행된다. 분석된 형태소는 (단어, 품사)라는 튜플의 리스트로 나오는데, 이를 NLTK로 처리하기 위해서는 단어의 리스트로 변환해야 한다. 여기서는 품사정보를 담아서 표현하기 위해 for문을 통해 (단어, 품사) 튜플을 "단어/품사" 형태로 바꾸어 content_tagged 리스트에 넣어주는데, for문 내용을 바꾸어 형식을 바꿀 수 있다.
참고로 여기까지 진행한 다음, content_tagged를 ' '.join(content_tagged) 등으로 단어가 띄어쓰기로 구분된 스트링으로 변환하면 gensim의 Word2Vec이나 LDA와 같은 다양한 알고리즘에 한국어를 적용할 수 있다. 여기서는 NLTK가 단어의 리스트를 요구하기 때문에 이 과정을 진행하지 않는다.
Concordance를 구해 파일로 내보내기
import nltk
content_text = nltk.Text(content_tagged)
content_text_conc_list = content_text.concordance_list("국민/NNG", lines=None)
with open("국민_대한민국헌법.txt", 'w') as file:
for conc in content_text_conc_list:
file.write(conc.line)
file.write('\n')
Concordance는 NLTK의 Text 클래스의 메소드다. 따라서, nltk.Text()로 Text 객체를 생성하는데, 이 안에 스트링을 넣으면 멋대로 단어로 분절한다. 한국어의 경우 문자 단위로 떨어뜨리기 때문에 전혀 도움이 안 된다. 다행히 리스트를 넣으면 단어 분절을 따로 진행하지 않는다.
Concordance 자체는 Text 객체에서 concordance 메소드를 불러오면 된다.
concordance 메소드는 키워드 말고도 크게 두 개의 키워드를 받는다. 키워드와 match한 경우의 좌우 맥락을 하나의 line이라 부르는데, 이 line의 길이(문자단위 길이)를 정하는 키워드가 width이다. 출력된 결과를 보면 '67개 match 중 25개를 표시한다'라는 말이 처음에 나오는데, 이는 lines 키워드로 조정된다. 따라서, '좌우 200문자를 표시하고, 50개까지 표시하라'를 위해서는 content_text.concordance("국민/NNG", width=200, lines=50)을 내면 된다.
이 결과는 따로 저장되지 않는다. 두고두고 참조하려면 결과를 파일로 저장하면 유용하다. 이럴 경우 concordance 대신 concordance_list를 사용하면 된다. 이유는 모르겠지만 concordance_list 메소드는 concordance 메소드처럼 25개 match만 내보낸다. 25개는 살펴보기는 쉽지만, 이래서는 올바른 해석이 힘들다. lines 키워드의 숫자를 늘려서 1,000개, 10,000개 표시할 수도 있지만 코퍼스가 크면 이 수치도 모자랄 수 있고, 애시당초 match가 몇 개 있는지 장담할 수도 없는 상황에 lines를 이에 맞출 수도 없을 것이다.
공개된 NLTK 코드를 보면 내부적으로는 전체 리스트를 만드는데, 메소드 결과를 반환할 때 list 쪼개기로 25개만 return하는 것을 볼 수 있다. 이 부분을 지우고 새로 컴파일 해도 되겠지만, 꼼수를 좀 쓰자면 None값을 lines 키워드 값으로 넘기면 된다. Python에서 [i:j]로 리스트를 쪼갤 때, i와 j값을 주지 않으면 리스트 전체를 반환한다라는 것을 이용한 것이다. NLTK 코드에 concordance 전체 리스트에 [:lines]로 쪼개서 나가는건데, lines에 None을 넘겨서 [:]과 실질적으로 같은 역할을 해 모든 값을 뽑아내는 것이다.
이 리스트는 concordance line 객체로 이루어진다. 실제 용례는 이 객체의 line 속성으로 접근할 수 있다(conc.line). 코드에서는 '국민_대한민국헌법.txt' 파일을 쓰기 모드로 열어 각 라인의 실제 용례를 저장한다.
전체 코드
import nltk
from konlpy.tag import Mecab
def main():
with open("대한민국헌법.txt") as f:
content = f.read()
tagger = Mecab()
content_tokens = tagger.pos(content)
content_tagged = list()
for token in content_tokens:
content_tagged.append(token[0] + '/' + token[1])
content_text = nltk.Text(content_tagged)
content_text_conc_list = content_text.concordance_list("국민/NNG", lines=None)
with open("국민_대한민국헌법.txt", 'w') as f:
for conc in content_text_conc_list:
f.write(conc.line)
f.write('\n')
if __name__ == "__main__":
main()
프로그래밍 없는 솔루션
위 과정들은 프로그래밍 없이 진행할 수도 있다.
형태소분석기 Kiwi는 윈도우에서 텍스트를 넣으면 형태소분석이 된 텍스트 파일을 내보내는 GUI 프로그램을 제공한다.
Concordance는 AntConc라는 프로그램(https://www.laurenceanthony.net/software/antconc/)을 무료로 받아 뽑아낼 수 있다. 여전히 많은 코퍼스언어학자들이 AntConc를 통해 Concordance를 뽑아낸다. 형태소 분석이 된 결과를 넣어서 concordance를 사용하면 된다.