딥러닝/자연어처리

코랩을 이용하여 doc2vec 모델 학습하기

씩씩한 IT블로그 2020. 10. 3. 16:25
반응형

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)
반응형