'Math' 카테고리의 다른 글
감마 함수 (0) | 2019.05.05 |
---|---|
가우스적분 (0) | 2019.05.05 |
[이상한 수학] pi = 0? (1) | 2019.04.03 |
root i, 허수 i의 루트. 이게 말이 돼? (0) | 2019.04.01 |
월리스(Wallis)의 곱. 이젠 이해할 수 있다. (1) | 2019.04.01 |
감마 함수 (0) | 2019.05.05 |
---|---|
가우스적분 (0) | 2019.05.05 |
[이상한 수학] pi = 0? (1) | 2019.04.03 |
root i, 허수 i의 루트. 이게 말이 돼? (0) | 2019.04.01 |
월리스(Wallis)의 곱. 이젠 이해할 수 있다. (1) | 2019.04.01 |
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
스코어 높은 순으로 카테고리를 분류하여 정렬 결과
연예 기사로 잘 분류하였다.
실제 여러 기사로 해 보면 종종 오탐이 발생한다.
단순하게 카테고리명과의 단어 유사도만으로 측정해서 한계는 있다.
다음엔 좀 더 나은 방식으로 해보자.
Jupyter Lab/ TensorBoard 원격접속/방화벽 (1) | 2019.08.09 |
---|---|
[keras] categorical_crossentropy vs sparse_categorical_crossentropy (0) | 2019.07.24 |
나무위키 데이터베이스 워드벡터(word2vec) 만들기 (2) | 2019.04.12 |
SVM 간단 예제 (0) | 2018.09.04 |
tensorflow 강좌6. Linear Regression with multi-variables (0) | 2018.07.18 |
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으로 줘서 그렇다. 값이 클 수록 표현하는 정보가 큰데, 학습 시간이 더 오래 걸린다.
그런대로 재미가 있다.
다음엔 이걸로 무엇을 할 수 있을지 생각해 봐야겠다.
[keras] categorical_crossentropy vs sparse_categorical_crossentropy (0) | 2019.07.24 |
---|---|
워드벡터를 사용하여 기사분류하기1 (0) | 2019.04.15 |
SVM 간단 예제 (0) | 2018.09.04 |
tensorflow 강좌6. Linear Regression with multi-variables (0) | 2018.07.18 |
tensorflow 강좌5. linear regression (0) | 2018.07.18 |
이차원상에서 직선이라면 기울기가 다르기만 하면 즉, 평행하지만 않으면 언젠가는 만나게 된다.
그러나 직선이 아니라 선분이면 길이가 정해져 있다.
선분1과 선분2가 있을 때, 각각 직선으로 확장한 것을 직선1, 직선2라고 하자.
직선1로 선분2의 양 끝점을 양분한다면 선분2와 직선1이 만나게 된다. (이 조건만 만족한다면, 직선1 대신 선분1로 했을때 선분1이 확장되지 않아 선분2와 만나지 않을 수도 있다. 즉 선분2이 직선2로 봤을 때 한쪽에 존재하는 경우이다.)
반대로 직선2로 선분1의 양 끝점을 양분한다면 선분1과 직선2가 만나게 된다. ( 이 조건도 만족한다면, 선분1이 직선2의 한쪽에 있지 않아서 위 걱정이 사라지게 된다.)
위 두 조건을 만족한다면, 선분1과 선분2가 만나게 된다.
모든 경우의 수를 따져보면 위 가정이 맞다는 것을 확인 할 수 있다.
먼저, 직선이 두 점을 양분하는지 검사하려면?
f(x)=0라는 직선이 있을 때, 두 점을 각각 넣어서 부호가 반대인 것을 의미한다.
y=mx+b 라는 직선이라고 본다면, 검사할 점(x1,y1)의 x좌표를 넣어서 나온 y값(mx1+b. 직선위의 점)이 검사할 점의 y좌표(y1)보다 크냐 작냐에 따라 직선 아래 또는 위에 있을지 결정된다. 따라서 y1-(mx1+b)를 하게 된 부호가 중요하다. 다른 점 (x2,y2)도 마찬가지로 넣으면
y1-(mx1+b)
y2-(mx2+b)
위 두 값의 부호가 다르면 양분된다. (y=mx+b 직선이 두 점을 양분한다.)
즉, 위 두 값을 곱하여 0보다 작으면 위 조건을 만족하게 된다
직선 1의 기울기를 구하고 방정식을 구하면
y = m1 (x-x11) + y11
직선2의 기울기를 구하고 방정식을 구하면
y=m2(x-x21)+y21
조건1 ;직선1이 두점(x21, y21), (x22, y22)를 양분하는지 검사
f1=y21- (m1 x21 - m1 x11 + y11 )
f2=y22 - (m1 x22 - m1 x11 + y11 )
f1*f2 < 0 이면 양분 된다.
좀 더 정리하면
f1=y21 - ( x21 (y12-y11)/(x12-x11) - x11 (y12-y11)/(x12-x11) + y11)
= { 1/(x12-x11) } * { y21(x12-x11) - x21(y12-y11) +x11(y12-y11) -y11(x12-x11) }
= { 1/(x12-x11) } * { (y21-y11)(x12-x11) - (x21-x11)(y12-y11) }
이렇게 된다.
f2=위와 동일하다 (y21대신 y22, x21대신 x22를 대입)
f2={1/(x12-x11)} * { (y22-y11)(x12-x11) - (x22-x11)(y12-y11) }
f1*f2의 부호만 관심있으므로 분수 부분을 없애기 위해 (x12-x11)^2을 곱해도 부호는 변함없다.
따라서 분수를 없애면
f1 = (y21-y11)(x12-x11) - (x21-x11)(y12-y11)
f2 = (y22-y11)(x12-x11) - (x22-x11)(y12-y11)
if f1*f2 <0 then 양분됨.
이를 파이썬 모듈로 작성하면 간단하다.
def is_divide_pt(x11,y11, x12,y12, x21,y21, x22,y22):
'''input: 4 points
output: True/False
'''
# // line1 extension이 line2의 두 점을 양분하는지 검사..
# 직선의 양분 판단
f1= (x12-x11)*(y21-y11) - (y12-y11)*(x21-x11)
f2= (x12-x11)*(y22-y11) - (y12-y11)*(x22-x11)
if f1*f2 < 0 :
return True
else:
return False
두 선분이 교차하는지 검사하려면 이것을 바꿔서 두 번하면 된다.
def is_cross_pt(x11,y11, x12,y12, x21,y21, x22,y22):
b1 = is_divide_pt(x11,y11, x12,y12, x21,y21, x22,y22)
b2 = is_divide_pt(x21,y21, x22,y22, x11,y11, x12,y12)
if b1 and b2:
return True
return False
랜덤하게 점을 찍어서 그리고, 위 조건 모듈을 써서 판정해 본 그래프
잘 판정되었다. 위 모듈에서 주의해야 할 것은 수평이나 수직인 경우는 별도로 처리해 주어야 한다. divide by zero등도 신경써서 에러 처리를 해 주어야 할 것이다. 테스트해보니 수직, 수평에서도 잘 작동하였다. ( divide 부분을 미리 처리해 두어서 그런지 별 이상없음.)
Fourier Transform Python (1) | 2019.08.02 |
---|---|
[UI] Qt5 (0) | 2019.05.17 |
두 선분의 교점 찾기 (0) | 2019.04.11 |
모든 성분에 따옴표붙여서 컴마 리스트로 출력하기 (0) | 2019.04.11 |
[Python] enum (0) | 2019.03.28 |
선분1 ; (x11,y11) to (x12, y12)
선분2 ; (x21,y21) to (x22, y22)
위 두 선분이 주어졌을 때 교점의 좌표를 파이썬 함수로 만들어 보자.
선분 1의 기울기 ; m1
선분2의 기울기 ; m2
따라서 선분1의 직선은 기울기가 m1이고 (x11, y11)점을 지난다.
y = m1 (x-x11) + y11
선분2의 직선은 기울기가 m2이고 (x21,y21)점을 지난다.
y=m2(x-x21)+y21
위 두 직선의 교차점은 x, y가 같게 되므로 위 연립 방정식의 해가 된다.
m1x - m1 x11 + y11 = m2x - m2x21 + y21
(m1-m2)x = m1x11-y11-m2x21+y21
x = (m1x11-y11-m2x21+y21) / (m1-m2)
x를 구했으면 위 아무 방정식에 넣으면 y가 나온다.
y = m1(x-x11)+y11
import numpy as np
import matplotlib.pyplot as plt
def get_crosspt(x11,y11, x12,y12, x21,y21, x22,y22):
if x12==x11 or x22==x21:
print('delta x=0')
return None
m1 = (y12 - y11) / (x12 - x11)
m2 = (y22 - y21) / (x22 - x21)
if m1==m2:
print('parallel')
return None
print(x11,y11, x12, y12, x21, y21, x22, y22, m1, m2)
cx = (x11 * m1 - y11 - x21 * m2 + y21) / (m1 - m2)
cy = m1 * (cx - x11) + y11
return cx, cy
x11 =np.random.randint(1,50)
y11 =np.random.randint(1,50)
x12 =np.random.randint(50,100)
y12 =np.random.randint(50,100)
x21 =np.random.randint(1,50)
y21 =np.random.randint(50,100)
x22 =np.random.randint(50,100)
y22 =np.random.randint(1,50)
plt.figure()
plt.plot([x11,x12], [y11,y12], c='r')
plt.plot([x21,x22], [y21,y22], c='b')
cx, cy = get_crosspt(x11, y11, x12, y12, x21, y21, x22, y22)
plt.plot(cx, cy, 'ro')
plt.show()
선분이 수직, 수평인 경우는 기울기가 0가 되어 문제가 될 수 있다.
이 경우는 따로 처리한다.
# line1:(x11,y11)-(x12,y12) , line2:(x21,y21)-(x22,y22)
def get_crosspt(x11,y11, x12,y12, x21,y21, x22,y22):
if x12==x11 or x22==x21:
print('delta x=0')
if x12==x11:
cx = x12
m2 = (y22 - y21) / (x22 - x21)
cy = m2 * (cx - x21) + y21
return cx, cy
if x22==x21:
cx = x22
m1 = (y12 - y11) / (x12 - x11)
cy = m1 * (cx - x11) + y11
return cx, cy
m1 = (y12 - y11) / (x12 - x11)
m2 = (y22 - y21) / (x22 - x21)
if m1==m2:
print('parallel')
return None
print(x11,y11, x12, y12, x21, y21, x22, y22, m1, m2)
cx = (x11 * m1 - y11 - x21 * m2 + y21) / (m1 - m2)
cy = m1 * (cx - x11) + y11
return cx, cy
나중에 필요에 따라 겹치거나 평행한 경우도 검사해 주어야 한다. 이것은 다음에 직선이 두 점을 양분하는지 체크하면 된다.
[UI] Qt5 (0) | 2019.05.17 |
---|---|
두 선분의 교차여부 체크 (0) | 2019.04.11 |
모든 성분에 따옴표붙여서 컴마 리스트로 출력하기 (0) | 2019.04.11 |
[Python] enum (0) | 2019.03.28 |
랜덤스트링 만들기 (0) | 2019.03.22 |
[mysql] 쿼리시 스트링을 수타입으로 정렬하기
mysql에서 쿼리시 특정 필드로 소팅 조회할 때, 숫자가 스트링인 필드일 경우, 스트링 타입으로 정렬이 된다.
이럴 경우, 1, 10, 100이 2보다 작게 된다. (스트링 대소 비교)
따라서 넘버 타입으로 캐스팅하여 정렬하는 것이 필요하다.
select * from MyTable order by cast(Num as unsigned) ;
or
select * from MyTable order by Num+0 ;
Mysql5.7 설치(ubuntu) (0) | 2019.11.08 |
---|---|
Mysql 두 개의 테이블(또는 자신)에서 다른 값(불일치) 찾기 (0) | 2019.08.26 |
mysql 사용자 정의 변수 사용 (0) | 2018.07.13 |
mysql select 한 데이터를 insert/update하기 (0) | 2018.07.12 |
only_full_group_by 에러 해결 (0) | 2018.07.04 |
모든 성분에 따옴표붙여서 컴마 리스트로 출력하기
a=["abc", "def", "ghi"]
위 리스트의 요소들을 각각 따옴표 씌워 컴마를 구분자로 한 스트링을 생성하려면?
b=",".join( list( map(lambda x: '"'+x+'"', a) ) )
Out[70]: '"abc","def","ghi"'
좀 더 간단하게
b=",".join('"'+x+'"' for x in a)
Out[72]: '"abc","def","ghi"'
두 선분의 교차여부 체크 (0) | 2019.04.11 |
---|---|
두 선분의 교점 찾기 (0) | 2019.04.11 |
[Python] enum (0) | 2019.03.28 |
랜덤스트링 만들기 (0) | 2019.03.22 |
matplotlib subplot 화면분할 grid로 분할하기 (0) | 2019.03.22 |
[이상한 수학]
유명한 오일러 공식
양변을 제곱하고
양변에 로그를 취하면
정리하면
무엇이 잘못 되었을까요?
가우스적분 (0) | 2019.05.05 |
---|---|
퓨리에시리즈. 스텝함수 (0) | 2019.05.05 |
root i, 허수 i의 루트. 이게 말이 돼? (0) | 2019.04.01 |
월리스(Wallis)의 곱. 이젠 이해할 수 있다. (1) | 2019.04.01 |
적분팁 (Integration Tip). 이것만 알아도 적분속도향상. (3) | 2019.03.29 |
허수 i의 루트값은 대체 무엇일까?
일단 복소수이기 때문에 수를 복소수 까지 확장하면 어떤 복소수도 복소평면에 있기 때문에 a+bi의 형태로 나타낼 수 있다. (a,b는 실수)
양변을 제곱하여 a, b를 구한다.
위 두 복소수는 같으므로 허수부와 실수부가 같아야 한다. (a,b는 실수임을 염두한다.)
여기서 이고, 이것을 위에 식에 대입하면
( b는 실수이므로 -1/2은 될 수 없다.)
따라서 a는 위 b에 따라 각 각
결국 답은 아래 두 개가 될 수 있다.
퓨리에시리즈. 스텝함수 (0) | 2019.05.05 |
---|---|
[이상한 수학] pi = 0? (1) | 2019.04.03 |
월리스(Wallis)의 곱. 이젠 이해할 수 있다. (1) | 2019.04.01 |
적분팁 (Integration Tip). 이것만 알아도 적분속도향상. (3) | 2019.03.29 |
integral sec(x) (0) | 2019.03.28 |
월리스의 곱은 홀수, 짝수를 두 번씩 분모, 분자에 써서 무한 곱을 하는 형태입니다. (분모 1은 한 번만)
이 값은 놀랍게도 수렴합니다. 그리고 이 값을 계산하기 위해서는 이것이 sin의 n제곱 적분과 관련이 있다는 것을 알아야 합니다. (이것을 처음 생각한 사람은 대단하지요.)
먼저 sin의 적분을 통해 공식을 만들고 W와의 관련성을 생각해 봅시다. sin의 n승의 적분을 계산합니다.
(부분적분으로 왼쪽 sin을 적분하고 오른쪽 sin^(n-1)을 미분하는 파트로 정함)
오른쪽끝 부분을 왼쪽으로 넘겨 공통부분으로 묶어주면,
(가장 앞에 마이너스가 빠졌네요. 추가..)
드디어 sin의 n제곱의 적분의 일반형으로 공식같은 것을 만들었습니다. 그런데 이 공식과 월리스의 곱이랑은 언듯보기에 아무 관계없어 보입니다. 좀 더 정리해 볼까요?
이렇게 놓으면
(가장 앞에 마이너스가 빠졌네요. 추가..)
위 적분 공식은 적분구간에 관계없이 항상 성립합니다. 그렇다면 적분 구간을 적당히 조절하면 왼쪽의 복잡한 식을 없애고, 분자,분모 1씩 차이나는 분수들의 연속적인 곱 형태로 바꿀 수 있을 것 같습니다.
이번엔 이렇게 적분 구간을 주어 I를 다시 정의해 봅시다. 이렇게 되면 왼쪽 파트가 항상 0이되어 아래와 같이됩니다.
이렇게 정리됩니다.
분자,분모가 1씩 차이나고 n이 2씩 감소되면서 반복되는 형태입니다. 홀수, 짝수로 나누어 이것도 공식으로 만들어봅시다.
이러한 공식이 나옵니다.
을 각각 구해보면,
따라서 대입하여 정리하면
이렇게 정리해 놓고 보니, 월리스의 곱과 아주 유사하게 전개됩니다.
분자를 짝수만 놓게 해보고 곱하면 뭔가 나올 것 같습니다.
에 역수를 구합니다.
위 두 개를 곱하면, W의 형태가 보이게 됩니다.
위와 같이 월리스의 곱이 정리되어 나옵니다. 여기서 또 오른쪽 부분의 극한값을 구해야 합니다.
와
은 sin(x)배를 더 한 쪽이 더 작습니다. (왜냐면 0~pi/2 구간에서 sin(x)는 0과 1사이의 값입니다. 0과1사이 값을 곱하면 곱할수록 원래값보다 작아집니다.) 그리고 적분값은 각각 그래프를 그리고 적분구간에 해당되는 넓이와 같기 때문에 이 함수들을 적분해도 함수의 대소관계와 같습니다. 따라서
은
보다 작거나 같게 됩니다. 마찬가지로
는
보다도 작거나 같습니다. (왜 같다고도 했는지는 극한값이라서 그렇습니다.)
그런데
는 공식에 따라서
여기서 n을 극한으로 보내면
=
이 됩니다.
위 대소관계
에서 보게되면
=
이므로 가운데 끼인
도
과 같게 되는 것입니다. (샌드위치 정리)
이렇게 월리스의 곱은 파이의 반이됩니다. 유리수들로 이루어진 값들의 무한 곱이 무리수가 되는 것이지요.
cf) 팁
sin의 n승의 적분을 적분구간
인 경우 아래 공식을 이용하면 쉽게 계산이 됩니다.
n을 분모로 놓고, 분자는 하나 적은 n-1 곱하기 분모 n-2, 분자 n-3 곱하기... 반복. 계속 1씩 감소하면서 적어주면 됩니다. 마지막에만 주의해 주면 됩니다.
ex2)
+파이썬으로 돌려보기
i값이 1, 2, ... n 까지 변해가면서 W값을 계산합니다.
v1 = 2i / (2i-1) = 2/1, 4/3, 6/5, ..., 2n/(2n-1)
v2= 2i / (2i+1 ) = 2/3, 4/5, 6/7, ..., 2n/(2n+1)
# wallis.py
import numpy as np
def wall(n):
W=1
for i in range(1,n+1):
v1=(2*i)/(2*i-1)
v2=(2*i)/(2*i+1)
W=W*v1*v2
return W
print('Wallis loop(10)=', wall(10))
print('Wallis loop(50)=', wall(50))
print('Wallis loop(100)=', wall(100))
print('Wallis loop(1000)=', wall(1000))
print('Wallis loop(10000)=', wall(10000))
print('Wallis loop(100000)=', wall(100000))
print('pi/2=', np.pi/2)
결과 확인
# python3 wallis.py
Wallis loop(10)= 1.5338519033217486
Wallis loop(50)= 1.5630394501077045
Wallis loop(100)= 1.5668937453140788
Wallis loop(1000)= 1.5704038730151892
Wallis loop(10000)= 1.5707570593409275
Wallis loop(100000)= 1.5707923998284614
pi/2= 1.5707963267948966
점점 pi/2에 수렴해 갑니다.
[이상한 수학] pi = 0? (1) | 2019.04.03 |
---|---|
root i, 허수 i의 루트. 이게 말이 돼? (0) | 2019.04.01 |
적분팁 (Integration Tip). 이것만 알아도 적분속도향상. (3) | 2019.03.29 |
integral sec(x) (0) | 2019.03.28 |
integral csc(x) (0) | 2019.03.27 |