Word2Vec 만들어 보기 - BD-SEARCH/MLtutorial GitHub Wiki
Python을 이용해 한국어 Word2Vec을 만들어 봅시다. 대략적인 과정은 아래와 같습니다.
- 한국어로 된 글 모으기
- 형태소 분석 등 전처리하기
- gensim을 통해 Word2Vec 만들기
아래 튜토리얼을 따라하려면 아래와 같은 것이 설치되어 있어야 합니다.
- Python 2.7, 3.3 이상
- python의 pip으로 필요한 라이브러리 설치
- python -m pip install gensim konlpy
한국어로 된 글 모으기
웹 상에서 손쉽게 구할 수 있는 한국어로 된 글은 아래와 같습니다.
- Wikipedia: https://dumps.wikimedia.org/
- pages-articles.xml.bz2 파일을 받으시면 됩니다.
- (참고) 한국어판: https://dumps.wikimedia.org/kowiki/latest/kowiki-latest-pages-articles.xml.bz2
- 나무위키: https://namu.wiki/w/%EB%82%98%EB%AC%B4%EC%9C%84%ED%82%A4:%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4%20%EB%8D%A4%ED%94%84
- 기타 등등..
덤프들이 다양한 포맷으로 되어 있습니다. plain text로 바꿔 주면 word2vec을 만들기 쉽습니다. 각 덤프별로 plain text로 변환하는 방법은 아래와 같습니다.
Wikipedia
Wikiepdia 덤프는 MediaWiki 포맷으로 되어 있습니다. 이 포맷을 plain text로 바꾸는 Tool은 많이 있지만, 여기서는 wikiextractor를 이용하겠습니다. 이 Tool을 이용하면 json/xml 형태로 제목, 링크와 본문을 저장합니다. 여기서는 json 포맷으로 저장합니다.
- attardi/wikiextractor로 이동해서, WikiExtractor.py를 다운로드합니다.
- 위 파일을 임의의 디렉토리에 저장합니다. 그 디렉토리에 output이라는 디렉토리를 만듧니다.
- 아래와 같은 명령어를 입력합니다.
python WikiExtractor.py [덤프 파일 이름] -o step1 --json -b 50M -q
위 명령어의 의미는 아래와 같습니다.
- -o step1: step1 디렉토리에 plain text로 변환된 파일을 저장합니다.
- --json: 출력 파일은 JSON 포맷입니다.
- -b 50M: 파일 한 개의 크기를 50MB로 제한합니다. 출력이 50MB를 초과한다면 새 파일을 만듧니다.
- -q: 덤프에 있는 문서 목록을 출력하지 않습니다.
전처리
특히 한국어 자연어 처리를 할 때, 전처리가 매우 중요합니다. 이 예제에서 사용하는 전처리는 아래와 같습니다.
- Konlpy의 Hannanum 라이브러리를 이용해서 문장을 품사별로 분리
- 관계언과 특수문자 제거
전처리에 따라 Word2Vec의 품질이 달라질 수 있으므로 다양한 시도를 해 보는 것도 좋습니다.
- Hannanum에서 사용하는 품사 정보: http://semanticweb.kaist.ac.kr/research/morph/
아래 코드는 step1 디렉토리에 저장된 plain text를 읽어서 품사별로 분리한 뒤, 관계언과 특수문자를 제거하고 그 결과를 step2 디렉토리에 저장하는 예제입니다.
# -*- coding: utf-8 -*-
import os
import re
import multiprocessing
from konlpy.tag import Hannanum
# tag reference: http://semanticweb.kaist.ac.kr/research/morph/
def extract_keywords(han, sentence):
"""
품사 분석을 진행한 뒤 관계언(조사 등)이나 기호를 제거한다.
"""
tagged = han.pos(sentence)
result = []
# 관계언 제거 (조사 등)
for word, tag in tagged:
if tag in ['F', 'N', 'P', 'M', 'I', 'X']: # 관계언 or 기호 제외
result.append(word)
return result
def worker(data):
han = Hannanum()
remove_special_char = re.compile(r'[^가-힣^A-z^0-9^.^,^?^!^ ]') # 한글, 영어, 기본 기호를 제외한 문자들
path, file_name = data
print("process file: {}".format(file_name))
with open(os.path.join(path, file_name), 'rt', encoding='utf-8') as input:
with open(os.path.join(os.getcwd(), 'step2', file_name), 'wt', encoding='utf-8') as output:
# step1에 있는, plain text를 읽어는다
i = 0
for input_line in input:
# 진행률을 출력하기 위한 부분
i += 1
if i % 100 == 0:
print("{}] {} finished".format(file_name, i))
# 특수 문자 제거 후 품사 분석 진행, 파일에 기록
text = remove_special_char.sub(' ', input_line)
keyword = extract_keywords(han, text)
output.write(' '.join(keyword))
output.write('\n')
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=4)
print('loading multiprocessing pool...')
data = []
for path, dirs, files in os.walk('step1/'):
for file_name in files:
data.append( (path, file_name) )
pool.map(worker, data)
pool.close()
pool.join()
Word2Vec 만들기
같은 디렉토리에서 아래 script를 실행시키면 word2vec이 만들어집니다.
아래 script는 http://blog.theeluwin.kr/post/146591096133/%ED%95%9C%EA%B5%AD%EC%96%B4-word2vec 를 참고하여 만들었습니다.
# -*- coding: utf-8 -*-
import multiprocessing
import os
import gensim
class SentenceLoader(object):
def __init__(self, source_dir):
self.source_dir = source_dir
def __iter__(self):
for path, dirs, files in os.walk(self.source_dir):
for file in files:
with open(os.path.join(path, file), 'rt', encoding='utf-8') as f:
for line in f:
yield line.replace('\\n', '').replace(',', '').split(' ')
sentences_vocab = SentenceLoader('step2/')
sentences_train = SentenceLoader('step2/')
print("sentence loader loaded.")
config = {
'min_count': 5, # 등장 횟수가 5 이하인 단어는 무시
'size': 350, # 300차원짜리 벡터스페이스에 embedding
'sg': 1, # 0이면 CBOW, 1이면 skip-gram을 사용한다
'batch_words': 10000, # 사전을 구축할때 한번에 읽을 단어 수
'iter': 10, # 보통 딥러닝에서 말하는 epoch과 비슷한, 반복 횟수
'workers': multiprocessing.cpu_count(),
}
model = gensim.models.Word2Vec(**config) # Word2vec 모델 생성
model.build_vocab(sentences_vocab) # corpus 개수를 셈
print('model.corpus_count: {}'.format(model.corpus_count))
model.train(sentences_train, total_examples=model.corpus_count, epochs=config['iter']) # Word2Vec training
model.save('model') # 모델을 'model' 파일에 저장
Word2Vec 사용하기
from gensim.models import Word2Vec
# 모델 로딩
model = Word2Vec.load('model')
print(model.vw['컴퓨터']) # 컴퓨터의 word vector 출력
print(model.most_similar(positive=['서울', '일본'], negative=['한국']))
# "서울 - 한국 + 일본" vector와 가장 가까운 단어 출력