여러 방법으로 Word2Vec을 만들 수 있습니다.
여기서는 gensim package를 활용해서 Word2Vec을 만들겠습니다.
Word2Vec을 사용해 보면 놀라울 정도로 잘 동작한다는 것으로 알 수 있습니다.
더 정확성을 높이려면 학습량을 늘리면 될 것입니다. 대신 시간이 오래 걸리죠.
다음과 같은 순서로 진행 합니다.
1) 한글 데이터 받기
1-1) Wiki 한글 데이터를 받습니다.
1-2) 받은 bz2 압축파일을 풀어서 Text 파일로 만듭니다.
2) 데이터 전처리
- 특수문자 제거 및 품사 처리
3) Word2Vec 학습
4) Word2Vec 사용
1-1) Wiki 한글 데이터 받기
- 아래 정리된 방법으로 받습니다.
[파이썬] 위키피디아(Wiki) 한글 자료 Dump (tistory.com)
[파이썬] 위키피디아(Wiki) 한글 자료 Dump
Wiki 데이터를 전부 Dump 받으려고 시도했습니다. WikiExtractor를 사용하는 방법이 있는데, 제 PC에서는 에러가 발생해서 사용할 수가 없었습니다. 그래서 다음과 같이 작업을 진행했습니다. 1) 아래 Wi
question99.tistory.com
- Wiki Dump 파일 : kowiki-20220820-pages-articles-multistream1.xml-p1p82407.bz2 (약 70 MB)
1-2) bz2 파일을 Text 파일로 변환합니다.
- step01/wiki_data.txt (약 300 MB) 만들어집니다
import bz2
# bz2 파일 읽기
with open("kowiki-20220820-pages-articles-multistream1.xml-p1p82407.bz2", "rb") as f:
data = f.read()
decom_data = bz2.decompress(data).decode()
print(decom_data[:500]) # 내용 확인 하기
# text 파일로 저장
f = open('wiki_data.txt', 'w', encoding='UTF-8')
f.write(decom_data)
f.close()
2) 데이터 전처리
- step02/wiki_data.txt(약 232 MB) 만들어집니다(소요시간 5시간 - PC 성능에 따라 달라지겠죠)
# -*- coding: utf-8 -*-
import os
import re
import multiprocessing
from konlpy.tag import Hannanum
# tag reference: http://semanticweb.kaist.ac.kr/research/morph/\n,
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(), 'E:/RnD/NLP/wikiextractor/step02', file_name), 'wt', encoding='utf-8') as output:
# step1에 있는, plain text를 읽어는다
i = 0
for input_line in input:
if not input_line:
break
print('run start=',i)
# 진행률을 출력하기 위한 부분
i += 1
if i % 100 == 0:
print('[{}] {} finished'.format(file_name, i))
# 특수 문자 제거 후 품사 분석 진행, 파일에 기록
print('for =',1)
text = remove_special_char.sub(' ', input_line)
chk = text.replace(' ','')
# text = text.replace('\n','')
print('for =',2,' / han=',han,' / text=',text,' /len(text)=',len(text))
if len(chk) == 0:
continue
keyword = extract_keywords(han, text)
print('for =',3)
output.write(' '.join(keyword))
print('for =',4)
output.write('\n')
print('run end=',i)
if __name__ == '__main__':
pool = multiprocessing.Pool(processes=4)
print('loading multiprocessing pool...')
data = []
for path, dirs, files in os.walk('E:/RnD/NLP/wikiextractor/step01/'):
for file_name in files:
data.append( (path, file_name) )
worker(data[-1])
pool.map(worker, data)
pool.close()
pool.join()
print('End!')
3) Word2Vec 학습 (약 15분 소요)
- - model 8,512 KB
- model.syn1neg.npy 355,454 KB
- model.wv.vectors.npy 355,454 KB
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('E:/RnD/NLP/wikiextractor/step02/')
sentences_train = SentenceLoader('E:/RnD/NLP/wikiextractor/step02/')
print('### sentence loader loaded.')
config = {
'min_count': 5, # 등장 횟수가 5 이하인 단어는 무시
# 'size': 350, # 300차원짜리 벡터스페이스에 embedding
'vector_size': 350, # 300차원짜리 벡터스페이스에 embedding
'sg': 1, # 0이면 CBOW, 1이면 skip-gram을 사용한다
'batch_words': 10000, # 사전을 구축할때 한번에 읽을 단어 수
# 'iter': 10, # 보통 딥러닝에서 말하는 epoch과 비슷한, 반복 횟수
'epochs': 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.train(sentences_train, total_examples=model.corpus_count, epochs=config['epochs']) # Word2Vec training
model.save('model') # 모델을 'model' 파일에 저장
4) Word2Vec 사용
### Word2Vec 사용하기
from gensim.models import Word2Vec
# 모델 로딩
model = Word2Vec.load('E:/RnD/NLP/wikiextractor/model')
- 김치와 관련이 있는 단어 찾기. positive로 지정 합니다.
# 김치와 관련이 있느 단어
rtn1 = model.wv.most_similar(positive=['김치'])
print(rtn1)
-
[('싸먹', 0.6364246010780334), ('풋고추', 0.6320699453353882), ('반찬', 0.6305665373802185), ('깍두기', 0.6203721165657043), ('[[피클]]', 0.6132300496101379), ('찌개', 0.6129357814788818), ('양념장', 0.6110677123069763), ('부침개', 0.6106680035591125), ('고추장', 0.6103342175483704), ('불고기', 0.6088910698890686)]
- 김치와 관련이 없는 단어 찾기. negative로 지정합니다.
# 김치와 관련이 없느 단어
rtn1 = model.wv.most_similar(negative=['김치'])
print(rtn1)
-
[('FlashPt\n', 0.15908798575401306), ('유자광\n', 0.1489071547985077), ('동물생명과학', 0.1481540948152542), ('doctoral_students\n', 0.14059396088123322), ('회심\n', 0.1364152878522873), ('정상순\n', 0.13528765738010406), ('지도너비\n', 0.13396240770816803), ('지도_크\n', 0.13077586889266968), ('list7\n', 0.13057848811149597), ('삼중점K\n', 0.12981094419956207)]
- 여러 단어를 지정할 수 있습니다.
rtn1 = model.wv.most_similar(positive=['서울','대한민국','특별시'])
print(rtn1)
[('강동구]]', 0.6355381011962891), ('강서구청장\n', 0.6267351508140564), ('중랑구\n', 0.6261865496635437), ('서울특별자유시', 0.626021146774292), ('숭인동]]', 0.6239762306213379), ('측백나무\n', 0.6221975684165955), ('중랑구]]', 0.6213183403015137), ('강동구\n', 0.6201136112213135), ('종로구\n', 0.6167482733726501), ('[[서울시의회]]', 0.6147496104240417)]
- positive와 negative를 같이 사용할 수 있습니다.
# "서울 + 일본 - 한국" vector와 가장 가까운 단어 출력
print(model.wv.most_similar(positive=['서울', '일본'], negative=['한국']))
[('[[도쿄]]', 0.4093792736530304), ('도쿄', 0.3986055850982666), ('교토', 0.393641859292984), ('보성중학교]]', 0.3928932249546051), ('인천', 0.39085716009140015), ('[[요코하마]]', 0.3900267481803894), ('성수중학교', 0.3894706666469574), ('[[보성중학교', 0.3870285749435425), ('양천구', 0.3832933306694031), ('동작구', 0.3815532624721527)]
print(model.wv.most_similar(positive=['서울', '중국'], negative=['한국']))
[('인천', 0.4297114610671997), ('남경', 0.42820557951927185), ('시안시', 0.41377440094947815), ('김포국제공항\n', 0.4102281630039215), ('베이징', 0.40923938155174255), ('[[톈진]]', 0.40780940651893616), ('뤄양', 0.3996354937553406), ('[[베이징]]', 0.3989080786705017), ('강동구', 0.39803001284599304), ('종로구', 0.39606156945228577)]
> 비슷한 단어 찾기
# 단어 비슷한 의미 단어 찾기
word1 = model.wv.n_similarity(['김치', '음식'], ['한국', '나라'])
word2 = model.wv.n_similarity(['김치', '음식'], ['빵', '음식'])
print(word1,'/',word2)
- 김치-음식/한국-나라는 0.2 이고, 김치-음식/빵-음식 은 0.8로 더 가깝게 판단합니다.
0.29520512 / 0.80048954
> 문장의 유사성 검사
# 문장의 유사성
s1 = '서울은 한국의 수도이다'
s2 = '서울은 한국의 수도이다' #corrected variable name
# s1.split() : ['서울은', '한국의', '수도이다.']
distance1 = model.wv.n_similarity(s1.split(), s2.split())
s1 = '서울은 한국의 수도이다'
s2 = '한국의 수도 서울이다'
distance2 = model.wv.n_similarity(s1.split(), s2.split())
s1 = '서울은 한국의 수도이다'
s2 = '도쿄는 일본의 수도이다' #corrected variable name
distance3 = model.wv.n_similarity(s1.split(), s2.split())
s1 = '제주도는 아름다운 섬 입니다'
s2 = '독도는 아름다운 섬 입니다' #corrected variable name
distance4 = model.wv.n_similarity(s1.split(), s2.split())
print(distance1,'/', distance2, '/', distance3, '/',distance4)
- 동일한 문장은 1 입니다.
- 단어의 위치만 바꾼 문장의 0.75,
- 서울-한국/도쿄-일본 은 0 인데, 이건 더 학습이 필요할 듯
- 제주도/독도는 1 로 나옵니다.
1.0 / 0.75130904 / 0.0 / 1.0
'프로그램' 카테고리의 다른 글
[파이썬] pandas 행열(Column, Row) 전환 (0) | 2022.08.25 |
---|---|
[파이썬] 엑셀 xls(html) 파일 읽기 (0) | 2022.08.25 |
[파이썬] gensim.models.Word2Vec 에러(__init__() got an unexpected keyword argument 'iter') (0) | 2022.08.23 |
[파이썬] gensim.models.Word2Vec 에러(__init__() got an unexpected keyword argument 'size') (0) | 2022.08.23 |
[파이썬] bz2 파일 text로 저장 하기 (0) | 2022.08.22 |
댓글