무회blog

python: 200629-gensim_bm25-Copy1, BM25Okapi 본문

Python

python: 200629-gensim_bm25-Copy1, BM25Okapi

최무회 2020. 6. 30. 08:30

 

 

 

In [381]:
import codecs
from gensim.corpora.dictionary import Dictionary
# from gensim.summarization import bm25
# from gensim.summarization.bm25 import get_bm25_weights
import os
import re , pandas as pd
from nltk.corpus import stopwords
from nltk.tokenize import sent_tokenize ,word_tokenize
In [382]:
### %pip install rank-bm25
### BM25Okapi 방식 
from rank_bm25 import BM25Okapi

corpus = [
    "Hello there good man!",
    "It is quite windy in London",
    "How is the weather today?"
]

tokenized_corpus = [doc.split(" ") for doc in corpus]

bm25 = BM25Okapi(tokenized_corpus)

# query = "windy London"
query = "weather"
tokenized_query = query.split(" ")

print(bm25.get_scores(tokenized_query))

print(bm25.get_top_n(tokenized_query, corpus, n=1))
# ['It is quite windy in London']
 
[0.         0.         0.51082562]
['How is the weather today?']
In [384]:
##  # step_4,   공통 코드 , 텍스트 클리닝 
def text_cleaning(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,' ')
        
        ##################################  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 = re.sub('\[.+?\]','', text)         # 대괄호 [] 이내 모든 문자 삭제     
        ##################################  gensim 사용을 위한 정규표현식 200624
        
        #특수문자 제거 후 생기는 중복된 공백 제거
        while text.find('  ') > 0:
            text = text.replace('  ',' ' ) # 중복된 공백 제거

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

        #좌우측 공백 삭제
        text = text.strip()

        # 좌측 공백 삭제
        # text.lstrip()

        # 우측 공백 삭제
        #text.rstrip() 

        return text  
In [388]:
### gensim 의 소스 
import math
from six import iteritems
from six.moves import xrange


# BM25 parameters.
PARAM_K1 = 1.5
PARAM_B = 0.75
EPSILON = 0.25


class BM25(object):

    def __init__(self, corpus):
        self.corpus_size = len(corpus)
        self.avgdl = sum(map(lambda x: float(len(x)), corpus)) / self.corpus_size
        self.corpus = corpus
        self.f = []
        self.df = {}
        self.idf = {}
        self.initialize()

    def initialize(self):
        for document in self.corpus:
            frequencies = {}
            for word in document:
                if word not in frequencies:
                    frequencies[word] = 0
                frequencies[word] += 1
            self.f.append(frequencies)

            for word, freq in iteritems(frequencies):
                if word not in self.df:
                    self.df[word] = 0
                self.df[word] += 1

        for word, freq in iteritems(self.df):
            self.idf[word] = math.log(self.corpus_size - freq + 0.5) - math.log(freq + 0.5)

    def get_score(self, document, index, average_idf):
        score = 0
        for word in document:
            if word not in self.f[index]:
                continue
            idf = self.idf[word] if self.idf[word] >= 0 else EPSILON * average_idf
            score += (idf * self.f[index][word] * (PARAM_K1 + 1)
                      / (self.f[index][word] + PARAM_K1 * (1 - PARAM_B + PARAM_B * self.corpus_size / self.avgdl)))
        return score

    def get_scores(self, document, average_idf):
        scores = []
        for index in xrange(self.corpus_size):
            score = self.get_score(document, index, average_idf)
            scores.append(score)
        return scores


###  가중치를 구해준다 .     
def get_bm25_weights(corpus):
    bm25 = BM25(corpus)
    average_idf = sum(map(lambda k: float(bm25.idf[k]), bm25.idf.keys())) / len(bm25.idf.keys())

    weights = []
    for doc in corpus:
        scores = bm25.get_scores(doc, average_idf)
        weights.append(scores)

    return weights
In [423]:
## 对目录下的所有文本进行预处理,构建字典
dirname = './news002/bm25/'
result = []
corpus = []
filenames = []
for root,dirs,files in os.walk(dirname):
    for f in files:
        f = root+f # 경로 + 파일명 
        with open(f, 'r', encoding='utf-8') as f:
            text = f.read()
            texts = text_cleaning(text)
            corpus.append(word_tokenize(texts))
            text = text_cleaning(text)
            text = sent_tokenize(text)
            print(text)
            print('-'*50)
            filenames.append(text)
        
dictionary = corpora.Dictionary(corpus)
print(dictionary)
 
['미래통합당 김종인 비상대책위원장이 대권 후보로 백종원 더본코리아 대표를 거명한 것과 관련 오세훈 전 시장은 24일 백종원 같은 취지의 인물을 찾아야 한다는 좋은 비유 라고 평가했다.', '일각에서 나온 김 위원장 자신이 대권에 도전하고 싶어 그러는 것 아니냐 는 관측에 대해선 그럴 가능성도 전혀 배제할 수 없다.', '연령이 뭐가 중요하냐 며 논의가 그렇게 흘러갈 가능성도 충분하다고 말했다.', '오세훈 전 서울시장.', '뉴시스 오 전 시장은 이날 CBS라디오 김현정의 뉴스쇼 에 출연해 그 얘기 김종인 위원장의 백종원 대표 언급 를 들으면서 아 그거 좋은 비유다.', '좋은 생각이다 하는 생각을 했다 며 그 정도로 국민적 거부감이 없고 많은 분과 스스럼없이 소통이 잘 되는 인물을 찾아야 한다 혹은 그런 인물이 되라고 하는 취지의 주문 이라고 해석했다.', '김 위원장이 당 내에 대선 주자가 안 보인다 고 한 발언과 관련해선 저는 굉장히 새겨듣고 있다.', '분발하라.', '지금 상태로는 도저히 정권 재탈환 불가능하다.', '더 노력하라 이런 메시지로 해석된다 고 설명했다.', '그러면서 진짜 대권 주자가 없을 리는 없다.', '언젠가는 선거를 치러야 하고.', '사람이 갑자기 나타나기는 현실적으로 어렵다 며 그런 의미에서 계속해서 준비가 필요하다는 말씀인 걸로 보인다 고 말했다.', '자신을 잠룡 후보로 보는 평가에 대해선 감사하다면서도 자신은 준비가 되려면 아직 멀었다고도 했다.', '김종인 미래통합당 비상대책위원장이 22일 오전 서울 여의도 국회 의원회관에서 열린 6.25전쟁 70주년 기념토론회에서 생각에 잠겨 있다.', '뉴스1 김 위원장 자신이 대권 후보로 나가고 싶어 백 대표를 언급한 것이라는 일각의 해석에 대해선 그럴 가능성도 전혀 배제할 수 없는 게 정치 라며 연령이 뭐가 그렇게 중요하겠나 지금 저렇게 활발하게 활동하시고 또 이슈 메이킹에 성공하는 걸 보면 충분한 자질은 갖추고 계신 분 이라고 강조했다.', '오 전 시장은 앞으로의 성과에 따라서는 충분히 논의가 그렇게 흘러갈 가능성도 배제할 수는 없다 고 관측했다.', '백종원 더본코리아 대표.', '연합뉴스 김 위원장은 지난 19일 비례대표 의원들과 오찬 간담회 자리에서 차기 대선 주자는 호감도가 높은 인물이 필요하다며 백 대표를 거론했다.', '백종원 대표는 여러 언론사에 대선은 꿈도 꿔본 적 없고 나는 지금 일이 제일 재밌고 좋다 라고 정치에 뜻이 없음을 밝혔다.', '한편 김 위원장의 발언이 화제가 되자 더불어민주당 정청래 의원은 백종원은 어떠냐 에이 백종원이 어떻게 음 그럼 김종인 이런 속셈인 것 같은데 라며 김종인 대망론을 그가 스스로 키우고 있다고 본다 고 해석하기도 했다.', '나진희 기자 세상을 보는 눈 세계일보']
--------------------------------------------------
['김태년 국회 정상화 노력 vs 주호영 새 제안 없어 與 오는 26일 국회 정상화 방침 마지막까지 협상 최선 김태년 더불어민주당 원내대표 왼쪽 와 주호영 미래통합당 원내대표가 23일 강원 고성의 화암사에서 만나 인근 식당에서 저녁 식사를 한 후 커피숍으로 자리를 옮겨 화기애애한 모습으로 대화를 나누고 있다.', '사진 연합뉴스 아시아경제 임주형 인턴기자 김태년 더불어민주당 원내대표와 주호영 미래통합당 원내대표가 23일 극적인 사찰 회동 으로 21대 국회 원 구성 협상을 이어갔으나 합의점을 찾지 못했다.', '민주당은 협상 타결 여부와 관계 없이 이번주 안에 원 구성을 매듭짓고 3차 추가경정예산 추경 안을 처리할 방침이며 통합당은 민주당이 법제사법위원장 자리를 내놓지 않으면 18개 상임위원장 모두 포기하겠다며 맞서고 있다.', '김 원내대표와 주 원내대표는 이날 전격 회동했다.', '두 사람의 만남은 주 원내대표가 지난 15일 사퇴 의사를 밝히고 전국 사찰을 돌며 칩거한 이후 8일 만에 처음 이뤄졌다.', '김 원내대표는 전국 여러 사찰을 옮겨다니던 주 원내대표의 거처를 수소문한 끝에 이날 오후 4시45분께 강원도 고성 화암사에서 극적으로 만날 수 있었다.', '두 사람의 회동은 이날 사찰에서 시작해 저녁 외부 만찬으로 이어지는 등 5시간 넘게 이뤄졌다.', '식사 후에는 커피숍으로 자리를 옮겨 어깨동무를 하고 사진을 찍는 등 화기애애한 모습을 연출하기도 했으며 국회 정상화 방안을 허심탄회하게 논의한 것으로 전해졌다.', '그러나 원 구성 협상의 핵심인 상임위원장 배분을 둘러싼 갈등에 대해서는 끝내 합의를 이끌어내지는 못했다.', '회동 직후 김 원내대표는 국회 정상화를 위해 노력하기로 했다 고 발표했고 주 원내대표는 김 원내대표가 국회 복귀만 호소했을 뿐 새로운 제안은 없었다 고 말했다.', '이날 만남은 3차 추경안 통과를 위해 국회를 정상화 해야 하는 김 원내대표 의지가 강하게 작용한 것으로 풀이된다.', '여당 일각에서는 국회 상임위원장 자리 18개 독식도 불사해야 한다는 목소리가 나오고 있으며 문재인 대통령도 이날 국무회의에서 추경안 처리가 늦어지면 늦어질수록 국민들 고통이 커질 것 이라며 추경안의 조속한 심사를 당부했다.', '김 원내대표는 통합당에 사실상 최후 통첩 을 하기도 했다.', '김 원내대표는 이날 오전 원내대책회의에서 통합당은 오늘까지 상임위 명단을 제출하고 국회 정상화에 협조할 것을 마지막으로 요청한다 며 3차 추경안 6월 내 통과는 국회 지상명령이다.', '6월 국회에서 추경을 마무리하기 위해 필요한 절차를 즉각 도입할 것 이라고 강조했다.', '김 원내대표와 주 원내대표가 커피숍에서 대화를 나누는 모습.', '사진 연합뉴스한편 지난 15일 통합당이 불참한 가운데 민주당 등 범여권 정당이 국회 본회의를 열어 법사위원장 등 6개 상임위원장을 단독 선출한 뒤 주 원내대표는 사퇴의사를 밝히고 전국 사찰을 돌며 칩거 중이다.', '이후 주 원내대표는 이번 주중 국회에 복귀하겠다는 의사를 밝혔으나 여전히 민주당이 가져간 법사위원장직을 제1야당인 통합당이 받지 않으면 추가 원 구성 협상 없이 상임위원장 18개 자리를 모두 포기하겠다는 입장을 고수하고 있다.', '주 원내대표는 23일 연합뉴스 와 통화에서 김종인 통합당 비상대책위원장도 거듭 복귀를 요청하는 상황이라 이번 주말까지 복귀를 해야 하지 않겠나 생각한다 며 복귀의 뜻을 재차 확인하면서도 민주당과의 상임위 협상에 대해서는 언급하고 싶지 않다.', '민주당 하고 싶은 대로 다 해보라 고 했다.', '두 정당 입장이 여전히 평행선을 달리는 가운데 민주당은 오는 26일 반드시 원 구성을 마무리하고 국회를 정상화 하겠다는 방침이다.', '김영진 민주당 원내수석부대표는 이와 관련해 이번주 목요일 금요일은 원구성을 마무리할테니 모든 의원들이 국회에서 한 시간 거리 내에 대기해 달라고 했다 고 박성준 원내대변인이 전했다.', '임주형 인턴기자 2020년 하반기 재물운 연애운 건강운 체크 네이버에서 아시아경제 뉴스를 받아보세요 놀 준비 되었다면 드루와 드링킷']
--------------------------------------------------
['트위터가 도널드 트럼프 미국 대통령의 트위트에 네 번째 경고 딱지를 붙였다.', '이번엔 폭력성을 조장해 플랫폼의 정책을 위반했다는 이유다.', '트럼프 대통령은 23일 트위터에 내가 대통령으로 있는 한 수도인 워싱턴은 자치 구역 Autonomous Zone 이 될 수 없다 며 시도를 한다면 그들은 상당한 물리적 힘에 부딪칠 것 이라고 공권력 투입을 시사했다.', '백인 경찰의 강압적인 체포로 흑인 시민이 사망한 후 미국 전역에서는 인종차별 항의 시위가 계속되고 있다.', '워싱턴주 시애틀에서는 시위대가 경찰서를 점거한 뒤 이곳을 경찰이 없는 자치 구역 으로 선포한 상태다.', '트럼프 대통령은 이 같은 시위대에 수차례 경고를 하고 강압적인 시위 해체 작업을 예고한 바 있다.', '이날 트위트 역시 이 연장선상에서 나온 셈이다.', '트위터는 그러나 트럼프 대통령의 트위트에 이 트위트는 폭력과 관련된 우리의 정책에 위반된다 며 특히 이는 식별 가능한 그룹에 대한 위협이다 고 해석했다.', '트럼프 대통령의 원본 트위트는 안내문 옆의 보기 를 눌러야만 확인할 수 있다.', '이날 트위트는 시위대가 백악관 앞에 위치한 앤드루 잭슨 전 대통령 동상의 철거를 시도한 직후 게시됐다.', '트럼프 대통령은 자신을 잭슨 전 대통령의 대단한 팬 이라고 소개할 정도로 여러 차례 그에 대한 존경을 표한 바 있다.', '박민철 기자 문화닷컴 바로가기 문화일보가 직접 편집한 뉴스 채널 모바일 웹 Copyright munhwa.com 대한민국 오후를 여는 유일석간 문화일보 무단 전재 및 재배포 금지 구독신청 02 3701 5555']
--------------------------------------------------
Dictionary(700 unique tokens: ['.', '19일', '22일', '24일', '6.25전쟁']...)
In [424]:
## doc2bow       , 모든 중복되는 단어의 빈도수를 구한다 . 
## vec1          , 첫번째 doc_vectors 의 중복되는 단어의 빈도수를 구한다 . 
## vec1_sorted   , vec1 의 디센딩을 구한다 . 
## for           , vec1_sorted 의 상위 5개를 뽑아낸다 . 

doc_vectors = [dictionary.doc2bow(text) for text in corpus]
vec1 = doc_vectors[0]
vec1_sorted = sorted(vec1,reverse=True)
vec1_sorted

for term, freq in vec1_sorted[:5]:
    print(dictionary[term]) 
    
    
bm25Model = BM25(corpus)

# result = get_bm25_weights(corpus)
# print('result 가중치 : {}'.format(result))
 
흘러갈
후보로
활발하게
활동하시고
화제가
In [425]:
average_idf = sum(map(lambda k: float(bm25Model.idf[k]), bm25Model.idf.keys())) / len(bm25Model.idf.keys())

query_str = '흘러갈 활동하시고 활발하게'
query = []
for word in query_str.strip().split():
    print(word)
    query.append(word)
    
    
scores = bm25Model.get_scores(query,average_idf)
idx = scores.index(max(scores))    # 0 번재에 가장 높은 값이 있음 
fname = filenames[idx]
fname
 
흘러갈
활동하시고
활발하게
Out[425]:
['미래통합당 김종인 비상대책위원장이 대권 후보로 백종원 더본코리아 대표를 거명한 것과 관련 오세훈 전 시장은 24일 백종원 같은 취지의 인물을 찾아야 한다는 좋은 비유 라고 평가했다.',
 '일각에서 나온 김 위원장 자신이 대권에 도전하고 싶어 그러는 것 아니냐 는 관측에 대해선 그럴 가능성도 전혀 배제할 수 없다.',
 '연령이 뭐가 중요하냐 며 논의가 그렇게 흘러갈 가능성도 충분하다고 말했다.',
 '오세훈 전 서울시장.',
 '뉴시스 오 전 시장은 이날 CBS라디오 김현정의 뉴스쇼 에 출연해 그 얘기 김종인 위원장의 백종원 대표 언급 를 들으면서 아 그거 좋은 비유다.',
 '좋은 생각이다 하는 생각을 했다 며 그 정도로 국민적 거부감이 없고 많은 분과 스스럼없이 소통이 잘 되는 인물을 찾아야 한다 혹은 그런 인물이 되라고 하는 취지의 주문 이라고 해석했다.',
 '김 위원장이 당 내에 대선 주자가 안 보인다 고 한 발언과 관련해선 저는 굉장히 새겨듣고 있다.',
 '분발하라.',
 '지금 상태로는 도저히 정권 재탈환 불가능하다.',
 '더 노력하라 이런 메시지로 해석된다 고 설명했다.',
 '그러면서 진짜 대권 주자가 없을 리는 없다.',
 '언젠가는 선거를 치러야 하고.',
 '사람이 갑자기 나타나기는 현실적으로 어렵다 며 그런 의미에서 계속해서 준비가 필요하다는 말씀인 걸로 보인다 고 말했다.',
 '자신을 잠룡 후보로 보는 평가에 대해선 감사하다면서도 자신은 준비가 되려면 아직 멀었다고도 했다.',
 '김종인 미래통합당 비상대책위원장이 22일 오전 서울 여의도 국회 의원회관에서 열린 6.25전쟁 70주년 기념토론회에서 생각에 잠겨 있다.',
 '뉴스1 김 위원장 자신이 대권 후보로 나가고 싶어 백 대표를 언급한 것이라는 일각의 해석에 대해선 그럴 가능성도 전혀 배제할 수 없는 게 정치 라며 연령이 뭐가 그렇게 중요하겠나 지금 저렇게 활발하게 활동하시고 또 이슈 메이킹에 성공하는 걸 보면 충분한 자질은 갖추고 계신 분 이라고 강조했다.',
 '오 전 시장은 앞으로의 성과에 따라서는 충분히 논의가 그렇게 흘러갈 가능성도 배제할 수는 없다 고 관측했다.',
 '백종원 더본코리아 대표.',
 '연합뉴스 김 위원장은 지난 19일 비례대표 의원들과 오찬 간담회 자리에서 차기 대선 주자는 호감도가 높은 인물이 필요하다며 백 대표를 거론했다.',
 '백종원 대표는 여러 언론사에 대선은 꿈도 꿔본 적 없고 나는 지금 일이 제일 재밌고 좋다 라고 정치에 뜻이 없음을 밝혔다.',
 '한편 김 위원장의 발언이 화제가 되자 더불어민주당 정청래 의원은 백종원은 어떠냐 에이 백종원이 어떻게 음 그럼 김종인 이런 속셈인 것 같은데 라며 김종인 대망론을 그가 스스로 키우고 있다고 본다 고 해석하기도 했다.',
 '나진희 기자 세상을 보는 눈 세계일보']
In [426]:
list1 = ['physics', 'chemistry', 'maths']
print ('Index of chemistry', list1.index('chemistry'))
# print ('Index of C#', list1.index('C#'))
 
Index of chemistry 1
Comments