반응형

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

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

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

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

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

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


+ Recent posts