무회blog

200713,-gensim_LSI-002,LSI방식,문서유사도 본문

Python

200713,-gensim_LSI-002,LSI방식,문서유사도

최무회 2020. 7. 13. 18:17
200713,-gensim_LSI-002,LSI,문서유사도
In [1]:
from gensim import models
from collections import defaultdict
from gensim import corpora
from gensim import similarities
import pandas as pd
import time, timeit, os, sys , re , math
from datetime import datetime
from nltk import sent_tokenize, word_tokenize
from konlpy.tag import Hannanum
hannanum = Hannanum()
from kiwipiepy import Kiwi
kiwi = Kiwi()
kiwi.prepare()


# BM25 를 사용하여 코사인 유사도에서 측정된 단어의 문장을 뽑아 낸다. 
start_now = int(time.time())
td = datetime.today().strftime("%Y%m%d")
tdd = datetime.today().strftime("%m%d")
now = datetime.now()
tdnow = now.strftime('%Y%m%d%H%M%S')
In [2]:
class Cleaning_Text:
    def tokenize_kiwi(sent):
        res, score = kiwi.analyze(sent)[0] # 첫번째 결과를 사용
        return [word
                for word, tag, _, _ in res
                if not tag.startswith('E') 
                and not tag.startswith('J') 
                and not tag.startswith('S')] # 조사, 어미, 특수기호는 제거      
    
    
    def token_word(documents): ## param List
        texts = [
            [word for word in document.split() if len(word) > 1]
            for document in documents
        ]
        return texts
    
    def token_sent(text):
        text = sent_tokenize(text)
        if len(text) > 1:
            text = text
        else:
            pass
        text_token = text
        return text_token        
    
    def listToText(inputList):
        returnText = ''
        for i in inputList:
            returnText = returnText + i
        rt2 = Cleaning_Text.text_cleaning(returnText)
        return rt2

    ##  # step_4,   공통 코드 , 텍스트 클리닝 
    def text_cleaning(text): 
    ##################################  gensim 사용을 위한 정규표현식 200624 
        hangul_path9 = '[가-힣]+\.'                  # 한글로 포함되다 . 

        hangul_path0 = '[가-힣]+\.[가-힣]{1}'        # 한글 . + 한글 처리 
        hangul_path1 = '[가-힣]+\.[\d]{1}'           # 한글 . + 숫자 처리 [0-9]
        hangul_path2 = '[가-힣]+\.[a-z]{1}'          # 한글 . + 영어 소문자 
        hangul_path3 = '[가-힣]+\.[A-Z]{1}'          # 한글 . + 영어 대문자 
        hangul_path4 = '[가-힣]+\.[\S]{1}'           # 한글 . + 비공백 [^ \t\n\r\f\v]와 같다.    
        hangul_path5 = '[가-힣]+\.[\s]{1}'           # 한글 . + 공백 [ \t\n\r\f\v]와 같다.
        hangul_path6 = '[가-힣]+\.[\W]{1}'           # 한글 . + 숫자 + 문자가 아닌 것 [^a-zA-Z0-9]와 같다.
        hangul_path7 = '[가-힣]+\.[\w]{1}'           # 한글 . + 숫자 + 문자 [a-zA-Z0-9]와 같다.
        hangul_path8 = '[가-힣]+\.[\b]{1}'           # 한글 . + 단어 경계 (`\w`와 `\W`의 경계)

        reg_path = hangul_path0 + '|' + hangul_path1 + '|'+ hangul_path2 + '|'+ hangul_path3 + '|'+ hangul_path4+ '|'+ hangul_path5
        hangul = re.compile(reg_path)              # 한글 + 모모로 포함되다 . 

        result = hangul.findall(text)                                                   # 정규식에 일치되는 부분을 리스트 형태로 저장, 단어 반환 
        result = list(set(result))    
        for x in result:
            text = text.replace(x, x[:-1] + '\n' + x[-1:])

        ### 줄단위 좌우 공백 지우기 , 
        text = text.replace('\n','_').split('_')
        text = [x.strip() for x in text]
        tts = ''
        for i in text:
            tts = tts + i + '\n'
            text = tts        
        ##################################  gensim 사용을 위한 정규표현식 200624
        text = re.sub('\[.+?\]','', text)         # 대괄호 [] 이내 모든 문자 삭제     

        #이모티콘 제거
        EMOJI = re.compile('[\U00010000-\U0010ffff]', flags=re.UNICODE)
        text= EMOJI.sub(r'', text)
        #이메일 주소 제거
        email =re.compile('([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)')
        text = email.sub('', text) 
        #URL 제거
        url =re.compile('(http|ftp|https)://(?:[-\w.]|(?:%[\da-fA-F]{2}))+')
        text = url.sub('', text) 
        #HTML 제거
        html =re.compile('<[^>]*>')
        text = html.sub('', text) 

        #특수문자를 공백으로 대체(문장을 살리기위헤 마침표는 남겨둠)
        #special =re.compile('[^\w\s]')
        #text = special.sub(' ', text) 
        special= ['*', '{', ',', ':', ']', '$', '+', '[', '#', '(', '%', '&', '}', '`', '‘', '’','·',
                    '=', ';', '>','>', '/', '"', '“', '”', '\\', '?', '~', "'", '<', ')', '^', '!', '_',
                    '|', '@','@','©','ⓒ', '℗','®','①', '-','▶','…','☞','▲','◆','■'] #'.', 빼고
        for chr in special :
            text=text.replace(chr,' ')

            #특수문자 제거 후 생기는 중복된 공백 제거
            while text.find('  ') > 0:
                text = text.replace('  ',' ' ) # 중복된 공백 제거

            #특수문자 제거 후 생기는 중복된 개행 제거
            while text.find('\n\n') > 0:
                text = text.replace('\n\n','\n' ) # 중복된 개행 제거

            #좌우측 공백 삭제
            text = text.strip()
        return text 
    
# 텍스트 정제 함수 : 분석에 불필요한 문자는 전부 제거합니다. 
# 형태소 분석해서 용언(P), 체언(N)만 남김
def hannanum_get_infoText(read_text):
    #resList = list()
    resList=[]
    #GetWordSet = set(['N'])
    GetWordSet = set(['N','P'])
    for read_text_line in read_text:
        res=""
        if len(read_text_line) > 0:
            pos = hannanum.pos(read_text_line,ntags=9)
            for keyword, type in pos:
                # 키워드가 한글자 이상일 경우만
                if len(keyword) > 1 :
                    # 용언(P), 체언(N)만 남김
                    if (type in GetWordSet):
                        if type == 'P': #용언일 경우 '다'를 붙여줌
                            keyword=keyword+'다'
                        resList.append(keyword)  
    return resList    

001 - LSI 방식으로 gensim _ similarities 코사인 유사도 구하기

In [3]:
# doc = "하태경 윤석열 잘하고 있어요?"
# doc = "코로나19 경제가 얼어 붙다"

input_Text0 = input()
input_Text = Cleaning_Text.tokenize_kiwi(input_Text0)
input_Text = [x for x in input_Text if len(x) > 1]



df_list = [pd.read_excel('./../test_data/test_Newsdata-100.xlsx', sheet_name = x) for x in range(6)]
df_list = [df_list[x][['subMenu', 'newsFrom', 'title', 'content', 'yoyag']] for x in range(6)]
df_list = [df_list[x].head(17) for x in range(6)]
df_ = pd.concat(df_list, axis=0)
df_ = df_.head(100)
ct_list = []

for i in df_['content'].tolist():
    ts = i
    ts = ts.strip()
    ct_list.append(ts)
documents = ct_list
documents = [Cleaning_Text.text_cleaning(documents[x]) for x in range(len(documents))]


# # 불용어 삭제 처리 ?  안할거임 , 그리고 1글자 이상 만 삭제 처리 
# # stoplist = set('for a of the and to in'.split())
texts = Cleaning_Text.token_word(documents)
texts
# remove words that appear only once
frequency = defaultdict(int)
for text in texts:
    for token in text:
        frequency[token] += 1
dictionary = corpora.Dictionary(texts)
# [(0, 1), (1, 1), (2, 1)] (단어사전id, 해당단어 빈도수, 순서 상관 없음)
# doc2bow fangrudao danci daizilia 
corpus = [dictionary.doc2bow(text) for text in texts]          

# remove words that appear only once
frequency = defaultdict(int)
for text in texts:
    for token in text:
        frequency[token] += 1
print(type(frequency))
# print(sorted(frequency))
# print(frequency)

dictionary = corpora.Dictionary(texts)
corpus = [dictionary.doc2bow(text) for text in texts]          # [(0, 1), (1, 1), (2, 1)] (단어사전id, 해당단어 빈도수, 순서 상관 없음)

# 빈도수가 1개 이상인 토큰만 저장 
texts = [
    [token for token in text if frequency[token] > 1]
    for text in texts
]

# lsi = models.TfidfModel(corpus)                                # TfidfModel(num_docs=9, num_nnz=28)
lsi = models.LsiModel(corpus, id2word=dictionary, num_topics=2)  # LsiModel(num_terms=12, num_topics=2, decay=1.0, chunksize=20000)

vec_bow = dictionary.doc2bow(input_Text)
print(input_Text)
vec_lsi = lsi[vec_bow]                       # convert the query to LSI space

print(vec_lsi)

index = similarities.MatrixSimilarity(lsi[corpus])  # transform corpus to LSI space and index it
# [print(x) for x in index]

sims = index[vec_lsi]  # perform a similarity query against the corpus
sims = sorted(enumerate(sims), key=lambda items: -items[1])

scoreIndex = []
scoreList = []
textList = []
# print(sims)  ## 인덱스, 확률 
for i, s in enumerate(sims):
#     print(s[0],s[1])
#     print(documents[s[0]])
    scoreIndex.append(s[0])
    scoreList.append(round(s[1],2))
    textList.append(documents[s[0]])

    
# 하태경 미래통합당 의원 은 내일 주식을 사겠다고 한다.
    
미래통합당 의원
<class 'collections.defaultdict'>
['미래', '통합', '의원']
[(0, 0.04616785127841069), (1, 0.008900636817216312)]
In [4]:
remarks = '''LSI 방식
 입력데이터':input_Text
'출력데이터':textList
'점수'      :scoreList
'유사도문장':textList
'소요시간'  :ends
'비고'      :remarks
'''

'''
LSI 방식으로 
1. 스코어  뽑기 
2. n개 코사인 유사도 문장 뽑기
3. 돌아가는 시간 체크하기 
4. 테스트 결과 기록하기
  - 입력데이터
  - 출력데이터
  
    scoreIndex.append(s[0])
    scoreList.append(s[1])
    textList.append(documents[s[0]])
    print('-'*100)  
'''

cnt = len(input_Text)
# 1. 스코어  뽑기 
scoreList = scoreList[:cnt]
print(scoreList)

# 2. n개 코사인 유사도 문장 뽑기
textList = textList[:cnt]
# print(textList[0])

# 3. 돌아가는 시간 체크하기 
ends = int(time.time()) - start_now
ends = '{} 초'.format(ends)

# 4. 테스트 결과 기록하기
dic = {'입력데이터': input_Text
       ,'출력데이터':input_Text
       ,'점수':scoreList
       ,'유사도문장':textList
       ,'소요시간':ends
       ,'비고':remarks
      }

df_LSI = pd.DataFrame(dic)
df_LSI.to_excel('./'+td+'_LSI방식_코사인유사도.xlsx')
df_LSI
print('성공')
print(ends)
[1.0, 0.94, 0.9]
성공
6 초
Comments