반응형

word vector로 기사 카테고리 분류하기 level 1

가설 ; 어떤 기사에 많이 나온 주요 단어들은 카테고리 키워드와의 유사도가 다른 것 보다 높을 것이다.

이 방식의 정확도가 높지는 않겠지만 그런대로 어느 정도는 될 것으로 예상된다.

하지만 장점으로는 언제든지 카테고리를 늘리거나 줄이거나 원하는대로 변경할 수 있다.


(먼저 워드벡터 학습모델이 있어야 되는데 이 전편에 나무위키로 워드벡터 만들기를 보면 된다. )


+알고리즘


기사를 읽고, 단어 분리

단어별 카운팅. 상위 탑 10 단어 추출.

카테고리명과 상위 탑10 단어의 유사도 계산

스코어 계산 (단어의 빈도를 가중치로하여 카테고리와의 유사도 곱으로 평균을 냄)

카테고리 중 가장 높은 스코어를 선정.


스코어 계산은 카테고리와의 유사도인 단어가 많이 나올수록 높도록 측정하였음.


'''
기사 분류기
나무위키로 학습한 워드 벡터를 이용.
주제별 단어와 기사의 유사성을 측정하여 max 분류로 한다.
기사와의 유사성 측정은?
기사내의 단어(조사 등 제외) 분포를 찾아 의미 있는 것을 찾아,스코어화.
'''
import codecs
from bs4 import BeautifulSoup
from konlpy.tag import Okt
from gensim.models import word2vec
import time
import numpy as np

category = ['정치', '경제', '사회', 'IT', '과학', '자동차', '부동산',
'생활', '세계', '의학', '인테리어', '예술', '연예']

filename='namu_test_article.txt'
filename4 = './hub/namu.model'


def make_DF(filename):
file = codecs.open(filename, 'r', encoding='utf-8')
text = file.read()

twitter = Okt()
word_dic = {}
lines = text.split('\r\n')
for line in lines:
malist = twitter.pos(line)
for taeso, pumsa in malist:
if pumsa == 'Noun':
if not (taeso in word_dic):
word_dic[taeso] = 0
word_dic[taeso] += 1
print(word_dic)
keys = sorted(word_dic.items(), key=lambda x: x[1], reverse=True)

top20_dic = {}
if len(keys)>20:
for word, count in keys[:20]:
top20_dic[word]=count
else:
for word, count in keys:
top20_dic[word]=count
return top20_dic


print('Model test')
t1 = time.time()
model = word2vec.Word2Vec.load(filename4)
t2 = time.time()
print('model load elapsed=', t2-t1)
top20_dic=make_DF(filename)
for ks in top20_dic.keys():
print(ks, top20_dic[ks], end=" ,")

# 카테고리별 단어의 유사도
cascores=[]
for ca in category:
sims = []
dfs = []
for ks in top20_dic.keys():
try:
v1 = model.similarity(ca, ks)
sims.append( v1 )
except KeyError:
sims.append( 0.0 )
v2 = top20_dic[ks]
dfs.append( v2 )
print(ca, ks, 'similarity=',v1, 'df=',v2)

sims = np.asarray(sims)
dfs = np.asarray(dfs)

# 단어출연 빈도를 가중치로 한 스코어
val = np.dot(sims, dfs)
print('wsum=', val)
sco=val/ np.sum(dfs)
print('scor=', sco)
cascores.append(sco)

cascores=np.asarray(cascores)
maxidx = np.argmax(cascores)

# print(category)
# print(cascores, maxidx)

categorydic = {
cate:scor for cate, scor in zip(category, cascores)
}
pc=sorted(categorydic, key=lambda k:categorydic[k], reverse=True)
print(pc)
print( sorted(cascores, reverse=True) )
print( 'predict=',pc[0],"/",pc[1] )


이렇게 하여 테스트 결과.

Model test

model load elapsed= 21.66822624206543

{'디넷': 1, '코리아': 1, '봉삼': 1, '기자': 1, '방탄소년단': 3, '컴백': 2, '카카오': 2, '운영': 1, '음원': 1, '플랫폼': 1, '멜론': 13, '이': 1, '먹통': 1, '이용자': 3, '불편': 3, '오후': 1, '접속': 4, '문제': 2, '발생': 2, '오류': 3, '등': 2, '트래픽': 2, '번': 2, '불': 1, '안정': 1, '현상': 1, '것': 1, '추정': 1, '실행': 1, '인터넷': 2, '수': 2, '상태': 2, '확인': 1, '메시지': 1, '지속': 1, '검색': 1, '포털': 1, '실시간': 1, '급상승': 1, '검색어': 1, '단어': 1, '그': 1, '만큼': 1, '사용자': 1, '오류로': 1, '뜻': 1, '로고': 2, '팬': 1, '게시판': 1, '통해': 1, '호소': 1, '컴퓨터': 1, '겨우': 1, '몇': 1, '재생': 1, '목록': 1, '장난': 1, '말': 1, '다른': 1, '서버': 2, '위': 1, '관리': 1, '좀': 1, '측은': 1, '일시': 1, '면서': 1, '조속': 1, '정상화': 1, '고': 1}

멜론 13 ,접속 4 ,방탄소년단 3 ,이용자 3 ,불편 3 ,오류 3 ,컴백 2 ,카카오 2 ,문제 2 ,발생 2 ,등 2 ,트래픽 2 ,번 2 ,인터넷 2 ,수 2 ,상태 2 ,로고 2 ,서버 2 ,디넷 1 ,코리아 1 


  v1 = model.similarity(ca, ks)

정치 멜론 similarity= 0.10295023 df= 13

정치 접속 similarity= 0.14711404 df= 4

정치 방탄소년단 similarity= 0.14685516 df= 3

정치 이용자 similarity= 0.34247914 df= 3

정치 불편 similarity= 0.20862463 df= 3

정치 오류 similarity= 0.22307779 df= 3


... 중략....

연예 인터넷 similarity= 0.42752212 df= 2

연예 수 similarity= 0.11581904 df= 2

연예 상태 similarity= 0.17616633 df= 2

연예 로고 similarity= 0.3842223 df= 2

연예 서버 similarity= 0.114044465 df= 2

연예 디넷 similarity= 0.114044465 df= 1

연예 코리아 similarity= 0.3931188 df= 1

wsum= 14.374487452208996

scor= 0.2613543173128908

['연예', 'IT', '경제', '부동산', '사회', '정치', '예술', '과학', '세계', '자동차', '생활', '인테리어', '의학']

[0.2613543173128908, 0.24636683274399152, 0.22802816277200524, 0.22646296511996877, 0.20992791205644606, 0.2065649455243891, 0.19927997887134552, 0.18424408056519248, 0.17951921116222036, 0.17361825514923443, 0.15364200567657296, 0.14810618719255383, 0.13615213415839456]

predict= 연예 / IT

스코어 높은 순으로 카테고리를 분류하여 정렬 결과 

연예 기사로 잘 분류하였다. 

실제 여러 기사로 해 보면 종종 오탐이 발생한다. 

단순하게 카테고리명과의 단어 유사도만으로 측정해서 한계는 있다.

다음엔 좀 더 나은 방식으로 해보자.


반응형

나무위키 워드벡터(word2vec) 만들기


1. 나무위키 데이터베이스 다운로드

https://mu-star.net/wikidb

미러링 사이트에서 찾았다. 다운로드 안되는 곳이 많음. 

7z압축되어 있는 최신 파일 다운로드하니 1.2기가 정도 됨.


+ 압축해제 

우분투에 7z 압축해제 프로그램이 없어서 p7zip을 설치하고 다운받은 압축 파일을 풀었더니  무려 8.3기가였다.

설치

#apt-get install p7zip

파일압축해제

#7zr x [파일명].7z


+파일이 너무 커서, 조금만 읽어보고 형태를 보자.

압축을 풀면 확장자가 JSON 포맷임.

# wc -l  namuwiki_20180326.json

0

줄 구분없이 한 줄로 구성된 텍스트 파일이다. more로 한 페이지만 보자.


# head namuwiki_20180326.json | more

다음과 같이 나온다.

[{"namespace":"0","title":"!","text":"#redirect \ub290\ub08c\ud45c\n","contributors":["namubot","R:hoon12560"]},{"nam

espace":"0","title":"!!\uc544\uc557!!","text":"[[\ud30c\uc77c:3444050440.jpg]]\n([[\uc2e0 \uc138\uacc4\uc218\uc758 \u

bbf8\uad81 2]]\uc5d0\uc11c \ub72c !!\uc544\uc557!!)\n{{{+1 \uff01\uff01\u3042\u3042\u3063\u3068\uff01\uff01 }}}\n\n[[

\uc138\uacc4\uc218\uc758 \ubbf8\uad81 \uc2dc\ub9ac\uc988]]\uc5d0 \uc804\ud1b5\uc73c\ub85c \ub4f1\uc7a5\ud558\ub294 \u

b300\uc0ac. [[\uc138\uacc4\uc218\uc758 \ubbf8\uad81 2 \uc81c\uc655\uc758 \uc131\ubc30|2\ud3b8 \uc81c\uc655\uc758 \uc1

31\ubc30]]\ubd80\ud130 \ub4f1\uc7a5\ud588\uc73c\uba70, \ud6cc\ub96d\ud55c [[\uc0ac\ub9dd \ud50c\ub798\uadf8]]\uc758 \

uc608\uc2dc\uc774\ub2e4.\n\n\uc138\uacc4\uc218\uc758 \ubaa8\ud5d8\uac00\ub4e4\uc774 \ud0d0\ud5d8\ud558\ub294 \ub358\u

c804\uc778 \uc218\ud574\uc758 \uad6c\uc11d\uad6c\uc11d\uc5d0\ub294 \ucc44\ucde8\/\ubc8c\ucc44\/\ucc44\uad74 \ud3ec\uc

778\ud2b8\uac00 \uc788\uc73c\uba70, \uc774\ub97c \uc704\ud55c \ucc44\uc9d1 \uc2a4\ud0ac\uc5d0 \ud22c\uc790\ud558\uba7

4 \uc81c\ud55c\ub41c \ucc44\uc9d1 \uae30\ud68c\uc5d0 \ubcf4\ub2e4 \ud070 \uc774\ub4dd\uc744 \ucc59\uae38 \uc218 \uc78

8\ub2e4. \uadf8\ub7ec\ub098 \ubd84\ubc30\ud560 \uc218 \uc788\ub294 \uc2a4\ud0ac \ud3ec\uc778\ud2b8\ub294 \ud55c\uc815

\ub41c \ub9cc\ud07c \ucc44\uc9d1 \uc2a4\ud0ac\uc5d0 \ud22c\uc790\ud558\ub294 \ub9cc\ud07c \uc804\ud22c \uc2a4\ud0ac \

ub808\ubca8\uc740 \ub0ae\uc544\uc9c0\uac8c \ub41c\ub2e4.\n\n 1. \ucc44\uc9d1\uc6a9 \uce90\ub9ad\ud130\ub4e4\ub85c \uc

774\ub8e8\uc5b4\uc9c4 \uc57d\ud55c \ud30c\ud2f0(ex: [[\ub808\uc778\uc800(\uc138\uacc4\uc218\uc758 \ubbf8\uad81 2)|\ub

808\uc778\uc800]] 5\uba85)\uac00 \uc218\ud574\uc5d0 \uc785\uc7a5\ud55c\ub2e4.\n 1. \ud544\ub4dc \uc804\ud22c\ub97c \u

d68c\ud53c\ud558\uba74\uc11c \ucc44\uc9d1 \ud3ec\uc778\ud2b8\uc5d0 \ub3c4\ucc29\ud574 \uc5f4\uc2ec\ud788 \uc544\uc774

\ud15c\uc744 \uce90\ub294 \uc911\uc5d0...\n ..............................

유니코드 인코딩인 것으로 보인다. title, text 필드가 제목과 내용인 것 같다.

JSON 파일이 너무 커서 한 번에 읽을 수도 없다. python은 ijson 패키지에서 처리한다고 하니 살펴본다.

https://pypi.org/project/ijson/


+시험삼아 데이터 몇개만 꺼내 확인해 본다.

import ijson


def load_json(filename):
count=0
with open(filename, 'r') as fd:
parser = ijson.parse(fd)
for prefix, event, value in parser:
if prefix.endswith('.title'):
print('index=', count+1)
print("\nTITLE: %s" % value)
elif prefix.endswith('.text'):
print("\nCONTENT: %s" % value)
count += 1
# debug
if count==10 :
break
if __name__ == "__main__":
print('load_json start')
load_json('/home/psychic/download/namuwiki_20180326.json')
print('load_json end')

글 제목과 내용이 번갈아가면서 출력된다. 알아서 디코딩되어 한글이 바로 잘 출력되었다.

데이터가 너무 많기 때문에 10개만 출력되게 디버깅코드를 추가하였다. 

얼마나 많은 데이터가 있는지 카운트를 해 보았다. (출력문이 있으면 상당히 느려지기 때문에 카운트만 계산한다. ) 카운트만 해도 오래 걸린다. 565985 개가 있다. (약 56만건)


+용이한 개발 및 디버깅 테스트를 위해 일부만 자르자. 원본 갖고 바로 작업하는 것은 상당히 비효율적이다. 개발시에는 디버깅용으로 라이트한 데이터만 다루고, 이후 개발이 다 되고 난 후에 전체데이터로 학습을 시키면 된다. 물론, DB가 크다는 가정하에 개발해야 될 것이다. (메모리 관리)


+디버깅용 DB 만들기 / 미니나무위키DB

일부만 자른다. 10메가로 하자.

# head -c 10000000 > mini_namu.json

파일을 열고 마지막 부분을 편집하여 JSON 형식을 맞춘다.

vi로 편집하려했더니 느려서 안되겠다. 그냥 커맨드로 append해야 겠다.

tail -c 800 mini_namu.json  | grep -E "title|text"

:"#redirect \uc81c18\ub300 \uad6d\ud68c\uc758\uc6d0 \uc120\uac70\n","contributors":["namubot","R:ttgrgt24","211.187.44.56"]},{"namespace":"0","title":"18\ub85c \uc81c\ud6c4","text":"[include(\ud2c0:18\ub85c \uc81c\ud6c4)]\n\n\u5341\u516b\u8def\u8af8\u4faf\n\n[\ubaa9\ucc28]\n== \uc124\uba85 ==\n[[\ud6c4\ud55c]]\ub9d0 [[190\ub144]], [[\uc5ed\uc801]] [[\ub3d9\ud0c1#s-1|\ub3d9\ud0c1]]\uc744 \ud1a0\ubc8c\ud558\uace0 [[\ud55c\ub098\ub77c#s-1.2|\ud55c\ub098\ub77c]] \ud669\uc2e4\uc744 \uad73\uac74\ud788 \ubc1b\ub4e0\ub2e4\ub294 \uac83\uc744 \ub300\uc758 \uba85\ubd84\uc73c\ub85c \ud558\uc5ec \uc77c\uc5b4\uc120 [[\uc81c\ud6c4]]\ub4e4\uc758 [[\uc5f0\ud569]]. \uc774\ub978\ubc14 '[[\ubc18\ub3d9\ud0c1 \uc5f0\ud569]]'\uc774\ub77c\uace0 \ubd80\ub974\uba70, \ud568\uace1\uad00\uc758 \ub3d9\ucabd \uc9c0\ubc2

#echo -n "0\"}]" >> mini_namu.json

#tail -c 100 mini_namu.json 

ud569]]'\uc774\ub77c\uace0 \ubd80\ub974\uba70, \ud568\uace1\uad00\uc758 \ub3d9\ucabd \uc9c0\ubc20"}] 



+미니 DB를 사용하여 워드 벡터 생성.

import ijson
import codecs
import sys, os
import time

import nltk

from gensim.models import word2vec
from konlpy.tag import Okt

import pickle


'''
JSON 나무위키 파싱

'''


def load_json(filename):
count=0
with open(filename, 'r') as fd:
# parser = ijson.parse(fd)
# for prefix, event, value in parser:
# print ( 'prefix=', prefix, 'event=', event )

# if prefix.endswith('.title'):
# # print('index=', count+1)
# # print("\nTITLE: %s" % value)
# elif prefix.endswith('.text'):
# # print("\nCONTENT: %s" % value)
# count += 1
# print ('total cnt=', count)
for item in ijson.items(fd, 'item'):
print(item['title'])
# print(item['text'])

def load_and_write_content(filename, filename2):
count=0
file = codecs.open(filename2, 'w', encoding='utf-8')
with open(filename, 'r') as fd:
for item in ijson.items(fd, 'item'):
count+=1
file.write('[[제목]]: ')
file.write(item['title'])
file.write('\n')
file.write('[[내용]]: \n')
file.write(item['text'])
file.write("\n")
file.close()
print('contents count=', count)


def make_wakati(f1, f2, f3):
file = codecs.open(f1, 'r', encoding='utf-8')
text = file.read()

twitter = Okt()
lines = text.split('\r\n')
results = []
for line in lines:
r = []
malist = twitter.pos(line) # part of speech (POS)
# pumsa : Noun, Josa, Verb, Eomi, Punctuation, Number, KoreanParticle,
for word, pumsa in malist:
if not pumsa in ['Josa', 'Eomi', 'Punctuation']:
r.append(word)
results.append((" ".join(r)).strip())
output = (" ".join(results)).strip()

with open(f2, "w", encoding="utf-8") as fp:
fp.write(output)

print('make word2vec')
data = word2vec.LineSentence(f2)
model = word2vec.Word2Vec(data, size=200, window=10, hs=1, min_count=5, sg=1, workers=3)
model.save(f3)
print('end word2vec')


if __name__ == "__main__":
print('load_json start')
print('curdir=', os.getcwd())
filedir = os.path.dirname( os.path.realpath(__file__))
os.chdir(filedir)
print('filedir=', filedir)

filename ='mini_namu.json'
filename2='mini_namu.txt'
filename3='mini_namu.wakati'
filename4='mini_namu.model'
filename5='mini_namu.pkl'

# load_json(filename)
# 나무위키 JSON DB에서 제목과 컨텐트를 스트링으로 기록한 파일 생성. (txt)
if True:
print('Create WordTxT')
t1=time.time()
load_and_write_content(filename, filename2)
t2=time.time()
print('End WordTxt ', 'time=',t2-t1)

if True:
print('Create Wakati')
t1=time.time()
make_wakati(filename2, filename3, filename4)
t2=time.time()
print('End Wakati', 'time=',t2-t1)

if True:
print('Model test')
t1=time.time()
model = word2vec.Word2Vec.load(filename4)
t2=time.time()
print(model.most_similar(positive=["조선", "일본"]))
print(model.most_similar(positive=["고려"]))
print(model.most_similar(positive=["고려"], negative=["왕"]))
print(model.most_similar(positive=["고려", "경제"]))
print(model.most_similar(positive=["RPG", "게임"]))
print(model.most_similar(positive=["임진왜란"]))
print(model.most_similar(positive=["왕", "여자"], negative=["남자"]))

print(model.similarity('고려','공민왕'))
print(model.doesnt_match("아침 점심 저녁 조선 밥".split() ) )
print(model.doesnt_match("총 무기 칼 게임 하늘".split() ) )

print('time=',t2-t1)

gensim.model.word2vec을 사용하여 간단하게 만들어서 테스트해서 아래의 결과가 나왔다.

[('1536년', 0.7473468780517578), ('음력', 0.7256854772567749), ('이이', 0.7070702910423279), ('도요토미', 0.7009462118148804), ('1552년', 0.6966617703437805), ('정조', 0.6919983625411987), ('문인', 0.6826009750366211), ('메이지', 0.6784789562225342), ('덴노', 0.6729492545127869), ('1337년', 0.6693580150604248)]

[('폐위', 0.7308624982833862), ('공민왕', 0.7183995246887207), ('정종', 0.7026641368865967), ('충숙왕', 0.7024295926094055), ('인종', 0.6962566375732422), ('즉위', 0.6933885216712952), ('충혜왕', 0.6890701651573181), ('명종', 0.6795019507408142), ('무신정권', 0.6763734817504883), ('원나라', 0.6668024659156799)]

[('마진', 0.2932665944099426), ('국호', 0.28764602541923523), ('발전', 0.27793973684310913), ('인종', 0.26443612575531006), ('말기', 0.2635525166988373), ('부족하다는', 0.2609822154045105), ('경향', 0.2581954002380371), ('1.6', 0.2573755383491516), ('줄어든', 0.2572077512741089), ('시기', 0.25421619415283203)]

[('천도', 0.6886128783226013), ('무신정권', 0.6836416721343994), ('인종', 0.6704342365264893), ('요나라', 0.6599131226539612), ('공민왕', 0.6575653553009033), ('권력자', 0.6571816205978394), ('도모', 0.6510674357414246), ('신변', 0.6495651006698608), ('폐위', 0.646056056022644), ('몽골', 0.6421409845352173)]

[('동봉', 0.6660709381103516), ('FPS', 0.6493582129478455), ('플래시', 0.6356097459793091), ('슈팅', 0.6346113681793213), ('로더', 0.629481852054596), ('플레이스테이션', 0.6227577924728394), ('TECHNIKA', 0.6164207458496094), ('DJMAX', 0.6132534742355347), ('온라인게임', 0.6100178360939026), ('오어', 0.606633186340332)]

[('1598년', 0.9288400411605835), ('이순신', 0.9017009735107422), ('1618년', 0.7677931189537048), ('1821년', 0.7676668167114258), ('도요토미', 0.7628185749053955), ('1865년', 0.7540280222892761), ('1839년', 0.7511029243469238), ('1868년', 0.7489482760429382), ('문인', 0.7481677532196045), ('1863년', 0.7473239898681641)]

[('속특', 0.4879986345767975), ('왕비', 0.48508623242378235), ('성종', 0.4771915674209595), ('선조', 0.4723415970802307), ('샤', 0.46159034967422485), ('세조', 0.4432491660118103), ('충혜왕', 0.43805891275405884), ('후궁', 0.4356191158294678), ('철종', 0.4354616701602936), ('문종', 0.43034279346466064)]

0.7184

조선

게임

데이터가 적기 때문에 오차가 좀 있지만 그런대로 결과는 나온다.




+실제 나무위키DB 텍스트 변환 (대용량 변환)

파일명들을 실제 나무위키 원본으로 바꾸고 실행해보자.


1단계. create wordtxt에서 encoding 에러 발생함.

open에 옵션으로 errors='ignore'로 무시하고 진행하도록 설정.

참고로 작업 서버 사양은... 

CPU : intel i7-7800X CPU 3.5GHz 12core

메모리 : 64기가

GPU: GeForce GTX 1080


시간이 오래걸릴 것으로 예상.  원본json이 8기가가 넘어서, 생성되는 것도 그 쯤 될 것이다.

7분정도 되니까 1기가까지 만들어짐. 

25분 걸려서 txt 파일 5기가 정도로 생성 완료하였다.


2단계.wakati 생성. 텍스트 파일을 읽어서 필요한 단어만 모아놓음.  (명사, 동사, 형용사, 부사..)  word2vec 클래스에서 에러가 발생할 것으로 생각됨. 메모리 부족??

VM 에러 발생. Memory.

# ulimit -c unlimited     ; 파일 사이즈 제한 해제도 미리 해 둠.

품사 정보 생성을 위해 Okt (구 Twitter)를 쓰는데, 거기서 발생한 것으로 보임. 라인 길이가 너무 크다. 2424660146 이정도된다.

메모리가 되는 선에서 적당한 크기로 잘라서 부분씩 처리하기로 함. 여기서는 20메가씩 스트링을 처리. 좀 더 크게 잡았더니 계속 실패해서 줄이고 줄임.  스트링을 자르는 방식도 여러가지인데, 그냥 구현하기로... 메모리를 상당히 내부적으로 많이 쓰는 듯 함. 

make_wakati 수정


# make wakati file
twitter = Okt(max_heap_size=1024*8) # heap size.. 8G
lines = text.split('\r\n')
t1=time.time()

print('making wakati start')
print('lines count=', len(lines))
fp = open(f2, "w", encoding="utf-8")
for line in lines:
linelen = len(line)
print("line length=", linelen)

# split.
# lineparts = map(''.join, zip(*[iter(line)] * 1000*1000*20)) # 20MB / 1line
blocksize = 1000*1000*20
if linelen % blocksize == 0:
blockcnt = int(linelen/blocksize)
else:
blockcnt = int(linelen / blocksize) + 1

for li in range(blockcnt):
if li==blockcnt-1:
linepart = line[li*blocksize:]
else:
linepart = line[li*blocksize:(li+1)*blocksize]
print('progress=', li, '/', blockcnt, len(linepart) )
malist = twitter.pos(linepart) # part of speech (POS)
# pumsa : Noun, Josa, Verb, Eomi, Punctuation, Number, KoreanParticle,
for word, pumsa in malist:
if not pumsa in ['Josa', 'Eomi', 'Punctuation']:
fp.write(word.strip())
fp.write(" ")
fp.close()
t2=time.time()
print('making wakati end time=', t1-t1)

다음 날 보니 wakati 파일 생성완료. 4G 크기.


3단계로 word vector 생성  진행. 얼마나 진행되었는지 알 수 없다. 언제 끝나지?

메모리 상황을 보면 가상 메모리로 파이썬이 66기가를 쓰고있다. cpu는 3개 쓰기로 설정해서 300% 풀로 나온다. 

기나긴 기다림 끝에 드디어 끝났다. 워드 벡터 생성에 걸린 시간은 41564 초. 시간으로 바꾸니 11시간 걸렸다.

전체 과정(와카티 생성, 워드 벡터 생성) 시간은 96503초. 시간으로는 26시간 걸렸다. 반드시 학습한 모델을 저장하는 코드를 넣어라. 저장을 깜빡했다면 이제까지 했던게 허사. 처음 부터 다시 ^^;;

모델 저장이 됐으니 이제, 필요할때 마다 로딩하여 질의를 하면 된다.

워드벡터 모델 파일 사이즈는 236메가. 그리고 넘파일파일이 3개 더 생긴다. 모델파일만 있으니 안돌아가는 걸로 봐서 넘파이 파일도 쓰는것 같다. 다 합쳐서 2기가 정도 된다.

모델 로딩에만 8초 정도 걸린다.


+ 나무위키 모델 테스트

질의 내용
print(model.most_similar(positive=["조선", "일본"]))
print(model.most_similar(positive=["고려"]))
print(model.most_similar(positive=["고려"], negative=["왕"]))
print(model.most_similar(positive=["고려", "경제"]))
print(model.most_similar(positive=["RPG", "게임"]))
print(model.most_similar(positive=["임진왜란"]))
print(model.most_similar(positive=["왕", "여자"], negative=["남자"]))

print(model.similarity('고려','공민왕'))
print(model.doesnt_match("아침 점심 저녁 조선 밥".split() ) )
print(model.doesnt_match("총 무기 칼 게임 하늘".split() ) )


Model test

[('대한제국', 0.6866917610168457), ('중국', 0.6801478862762451), ('우리나라', 0.6566961407661438), ('청나라', 0.6511589288711548), ('일제강점기', 0.6480269432067871), ('한국', 0.6324470043182373), ('조선국', 0.6295247077941895), ('경술국치', 0.6277073621749878), ('임진왜란', 0.623056173324585), ('역사', 0.6140305995941162)]

[('조선', 0.6692752838134766), ('고려왕조', 0.6515953540802002), ('공민왕', 0.6487005949020386), ('문종', 0.6360877752304077), ('현종', 0.6323516368865967), ('무신정권', 0.6292731761932373), ('태조', 0.6256691217422485), ('감안', 0.6252619624137878), ('冊封', 0.6246188879013062), ('충렬왕', 0.6229778528213501)]

[('감안', 0.4361225962638855), ('염두', 0.3996908664703369), ('제승방략', 0.39954277873039246), ('유념', 0.3989650011062622), ('바람직하지', 0.39659619331359863), ('해보아야', 0.39381834864616394), ('갖추자는', 0.39022499322891235), ('추세였음', 0.38095635175704956), ('타당', 0.3788178563117981), ('받아들여지기에', 0.37214767932891846)]

[('정책', 0.7264151573181152), ('동북아', 0.702474057674408), ('사회', 0.7002162933349609), ('정치', 0.6759592294692993), ('불평등', 0.6736841797828674), ('노동시장', 0.6685125827789307), ('경제정책', 0.668291449546814), ('한반도', 0.6653428673744202), ('민생', 0.6610323190689087), ('발전', 0.6543104648590088)]

[('TPS', 0.757465124130249), ('롤플레잉', 0.7494930028915405), ('로그라이크', 0.7455687522888184), ('FPS', 0.7437580823898315), ('플랫포머', 0.7379695177078247), ('RTS', 0.7236455082893372), ('모바일', 0.7205138206481934), ('MOBA', 0.718673586845398), ('SRPG', 0.7134039402008057), ('MMORPG', 0.7068561911582947)]

[('정유재란', 0.8903812170028687), ('병자호란', 0.8541854619979858), ('임란', 0.8498165607452393), ('왜란', 0.8400564193725586), ('정묘호란', 0.7854044437408447), ('1592년', 0.7841730117797852), ('의병장', 0.781627893447876), ('류성룡', 0.7787343263626099), ('왜군', 0.7751662731170654), ('광해군', 0.7695599794387817)]

[('왕인', 0.6398370265960693), ('준왕', 0.6377674341201782), ('왕중', 0.5966447591781616), ('극', 0.5917797088623047), ('최후', 0.5905873775482178), ('선왕', 0.5905437469482422), ('왕전', 0.588803231716156), ('中山王', 0.5885385274887085), ('楚穆王', 0.5835995674133301), ('南平王', 0.5762388706207275)]

0.648701

조선

게임

model.wv.get_vector('노트북') 을 해보니 200개의 수가 나온다. 200차원. 모델 생성시에 사이즈를 200으로 줘서 그렇다. 값이 클 수록 표현하는 정보가 큰데, 학습 시간이 더 오래 걸린다.

그런대로 재미가 있다.

다음엔 이걸로 무엇을 할 수 있을지 생각해 봐야겠다.
















+ Recent posts