0. 서론
gensim 라이브러리를 이용하여 doc2vec모델을 만들어보았다. 위키피디아 데이터를 이용하여 doc2vec모델을 학습시켰다.
처음으로 시도한 방법은 로컬에서 클래스를 만들고, 한줄씩 yield하면서 모델을 학습시키는 것이였다. (소스코드1)
1. 소스코드1 (한번에 학습하는 방법.)
class Doc2VecSentences:
def __init__(self,path):
self.path=path
self.keyDict={}
def clean_str(self,text):
pattern = '([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)' # E-mail제거
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '(http|ftp|https)://(?:[-\w.]|(?:%[\da-fA-F]{2}))+' # URL제거
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '([ㄱ-ㅎㅏ-ㅣ]+)' # 한글 자음, 모음 제거
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '<[^>]*>' # HTML 태그 제거
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '[^\w\s]' # 특수기호제거
text = re.sub(pattern=pattern, repl='', string=text)
return text
def __iter__(self):
# 파일을 다시 처음부터 읽음.
result = []
keyId=0
fread = open(self.path, encoding="utf8")
print("!")
while (1):
line = fread.readline() #한 줄씩 읽음.
if not line:
break # 모두 읽으면 while문 종료.
lineL=line.split()
if lineL:
#새문서 시작
if lineL[0]=="</doc>":
fread.readline()
title=fread.readline().rstrip("\n")
self.keyDict[keyId]=title
keyId+=1
elif lineL[0]=='<doc':
title=fread.readline().rstrip("\n")
self.keyDict[keyId]=title
keyId+=1
#그냥문장
else:
lineL=list(map(self.clean_str,lineL))
yield TaggedDocument(words=lineL, tags=['#%d'%keyId])
fread.close()
path='wiki_data.txt'
doc2vec_corpus=Doc2VecSentences(path)
print("?")
doc2vec_model = Doc2Vec(doc2vec_corpus)
2. 문제
하지만 문제가 생겼다. 200만줄정도를 [형태소분해]->[태그]->[학습] 하는데 시간이 너무 오래걸리는 것이였다. 그래서 아래와 같은 3가지 정도의 방법을 생각해봤지만 모두 또다른 문제가 생겼다.
(1) 로컬에서 돌린다 -> 높은 부하를 몇십시간이나 컴퓨터가 견뎌야 한다.
(2) 코랩에서 돌린다 -> 최대 12시간의 러닝타임만을 보장한다.
(3) 코랩에서 모델을 12시간마다 저장하면서 돌린다. -> 모델의 학습이 doc2vec객체로 지정함과 동시에 iter 함수로 yield되면서 진행되는데, 중간에 모델을 어떻게 저장해야 되는지 아무리 찾아봐도 안나왔다.
3. 해결
따라서 생각해낸 방법이 일단 먼저 데이터를 형태소 분해하여 tag까지 한 상태로 리스트에 저장하고, 이를 pickle형식의 파일로 저장해놓는것이였다.
10만개씩 잘라서 pickle형식의 데이터로 저장해놓으면 중간에 끊겨도 저장된 데이터 다음부터 학습시킬 수 있었다.
형태소 분해하는데 시간이 많이 걸리는 것이였기 때문에 형태소 분해된 데이터를 그대로 불러와 학습만 시키는 것은 그렇게 오래 걸리지 않았다.
과정은 아래와 같다
(1) txt파일을 한줄씩 읽어서 형태소 분해한 후 10만개씩 pickle형식의 파일로 저장
import re
from gensim.models.doc2vec import TaggedDocument
from konlpy.tag import Okt
import pickle
def clean_str(text):
pattern = '([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)' # E-mail제거
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '(http|ftp|https)://(?:[-\w.]|(?:%[\da-fA-F]{2}))+' # URL제거
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '([ㄱ-ㅎㅏ-ㅣ]+)' # 한글 자음, 모음 제거
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '<[^>]*>' # HTML 태그 제거
text = re.sub(pattern=pattern, repl='', string=text)
pattern = '[^\w\s]' # 특수기호제거
text = re.sub(pattern=pattern, repl='', string=text)
text=text.rstrip('\n')
return text
def saveP(n):
fileName=str(n)[:-5]+'.p'
with open(fileName, 'wb') as file:
pickle.dump(result, file)
# startPoint : 추가시작할 행을 적어
# cheka : 몇개단위로 저장할 것인가??
path='data_all.txt'
fread = open(path, encoding="utf8")
keyId=-1
n=0
result=[]
okt=Okt()
cheak=100000
startPoint=0
while (1):
line = fread.readline() #한 줄씩 읽음.
# [모두 읽으면 while문 종료]
if not line:
saveP("9900000")
break
# [줄바꿈이면 다음껄로]
if line=="\n":
continue
# [새문서 시작]
if line[:6]=="</doc>":
fread.readline()
fread.readline()
keyId+=1
# [새문서 시작2]
elif line[:4]=='<doc':
fread.readline()
keyId+=1
#그냥문장
else:
if startPoint<=n:
line=clean_str(line)
lineL=okt.morphs(line)
taggedL=TaggedDocument(words=lineL, tags=['%d'%keyId])
result.append(taggedL)
n+=1
#cheak개마다 저장하고 리셋
if n%cheak==0:
saveP(n)
result=[]
print(n)
else:
n+=1
fread.close()
(2) 형태소 분해된 데이터가 들어있는 pickle 파일을 불러와서 학습
import os
import pickle
from gensim.models import Doc2Vec
class Doc2VecSentences:
def __init__(self,path):
self.path=path
def __iter__(self):
print("학습!")
# 파일을 다시 처음부터 읽음.
folder=self.path #디렉토리 내에 폴더
subdir_names=os.listdir(folder) #subdir_names에 폴더내의 모든 파일이름이 들어간다
for now_pickle in subdir_names:
#pickle 오픈
if now_pickle!='.DS_Store':
print(now_pickle)
with open(path+now_pickle, 'rb') as file:
L=pickle.load(file)
for taggedLine in L:
yield taggedLine
#test_folder에 모든 pickle파일을 다 넣어둔다
path='test_folder/'
doc2vec_corpus=Doc2VecSentences(path)
doc2vec_model = Doc2Vec(doc2vec_corpus)