무회blog

0003-scikit-lear,방식으로 텍스트 ,코사인 유사도, 구하기-004-01 본문

Python

0003-scikit-lear,방식으로 텍스트 ,코사인 유사도, 구하기-004-01

최무회 2020. 7. 14. 18:21
0003-scikit-lear,방식으로 텍스트 ,코사인 유사도, 구하기-004-01

0003-scikit-lear,방식으로 텍스트 ,코사인 유사도, 구하기-004

scikit-learn 001 방식 체크

In [1]:
from sklearn.metrics.pairwise import cosine_similarity

a=[[1,3,2],[2,2,1]]
cosine_similarity(a) # 余弦相似度
Out[1]:
array([[1.        , 0.89087081],
       [0.89087081, 1.        ]])
In [2]:
from sklearn.metrics.pairwise import pairwise_distances
pairwise_distances(a,metric="cosine")   # 注意该方法返回的是余弦距离
Out[2]:
array([[0.        , 0.10912919],
       [0.10912919, 0.        ]])
In [3]:
from numpy import dot
from numpy.linalg import norm
import numpy as np
def cos_sim(A, B):
       return dot(A, B)/(norm(A)*norm(B))
    
# doc1 = np.array(['바나나','사과','키위'])
# doc2 = np.array(['바나나','사과','딸기'])

doc1=np.array([0,1,1,1])
doc2=np.array([0,0,2,1])
print(cos_sim(doc1,doc2))
0.7745966692414834
In [4]:
# 엑셀 vlookup 방식으로 title 과 content 내용을 출력 하기 
import pandas as pd

data = pd.read_excel('./../test_data/test_Newsdata-100.xlsx')
data = data[['subMenu','title','content','yoyag']]
data['content'].isnull().sum()  # 널값이 몇개있는지 체크 
# data['overview'] = data['overview'].fillna('')  # overview에서 Null 값을 가진 경우에는 값 제거


from sklearn.feature_extraction.text import TfidfVectorizer
tfidf = TfidfVectorizer(stop_words='english')
tfidf_matrix = tfidf.fit_transform(data['content'])       #   #第一个fit_transform是计算tf-idf,# 第二个fit_transform是将文本转为词频矩阵

# content 대해서 tf-idf 수행
print(tfidf_matrix.shape)  # (100, 12038) -> 100 행을표현하기 위하여 , 12038 개의 단어를 사용 



from sklearn.metrics.pairwise import linear_kernel ,cosine_similarity
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)  # 코사인 유사도를 구합니다.

print(type(cosine_sim))
# print(cosine_sim[99])  # 100 개 문서에 대한 코사인 유사도 
print(len(cosine_sim))
print(cosine_sim)


# scosine_similarity =cosine_similarity(tfidf_matrix,tfidf_matrix)
# print(type(scosine_similarity))
# print(scosine_similarity)
(100, 12038)
<class 'numpy.ndarray'>
100
[[1.         0.00409371 0.00255311 ... 0.04224467 0.02979325 0.01701856]
 [0.00409371 1.         0.00278238 ... 0.0026723  0.00900035 0.00871025]
 [0.00255311 0.00278238 1.         ... 0.0199744  0.01092612 0.02228726]
 ...
 [0.04224467 0.0026723  0.0199744  ... 1.         0.4208833  0.0062815 ]
 [0.02979325 0.00900035 0.01092612 ... 0.4208833  1.         0.01733702]
 [0.01701856 0.00871025 0.02228726 ... 0.0062815  0.01733702 1.        ]]
In [5]:
indices = pd.Series(data.index, index=data['title']).drop_duplicates()
print(indices.head())

# target_title = '6ㆍ25 70주년 애국가 ‘아무리 봐도 북한 애국가’ 논란 ' 
target_title = '남한 기대 수명 83세 세계 9위, 북한은 얼마일까            '
target_title = target_title.strip()
idx = indices[target_title]
print(idx)


def get_recommendations(title, cosine_sim=cosine_sim):
    # 선택한 타이틀로부터 해당되는 인덱스를 받아옵니다. 이제 선택한 것 가지고 연산할 수 있습니다.
    idx = indices[title]

    # 모든 타이틀 대해서 해당 타이틀 유사도를 구합니다.
    sim_scores = list(enumerate(cosine_sim[idx]))

    # 유사도에 따라 타이틀들을 정렬합니다.
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)

    # 가장 유사한 10개의 타이틀를 받아옵니다.
    sim_scores = sim_scores[1:11]

    # 가장 유사한 10개의 타이틀의 인덱스를 받아옵니다.
    title_indices = [i[0] for i in sim_scores]

    # 가장 유사한 10개의 타이틀의 제목을 리턴합니다.
    return data['content'].iloc[title_indices]

print('target_title : ',target_title)
get_recommendations(target_title)
title
"일본, 한국·중국·대만 입국규제 완화 교섭 검토"               0
남한 기대 수명 83세 세계 9위, 북한은 얼마일까               1
하태경 "윤석열, 박근혜도 감옥에 넣었는데…秋, 윤석열 선대본부장같다"    2
北 선전매체 "반민족분열광신자" vs 진중권 "왜 나한테 ZR?"       3
6ㆍ25 70주년 애국가 ‘아무리 봐도 북한 애국가’ 논란           4
dtype: int64
1
target_title :  남한 기대 수명 83세 세계 9위, 북한은 얼마일까
Out[5]:
17    북한의 평균 기대수명이 남한보다 11년 짧은 것으로 나타났다.  합계출산율은 1.9...
81    마스크를 착용한 북한 주민들의 모습.  평양 노동신문=뉴스1    코로나19 확진자...
61    신종 코로나바이러스 감염증(코로나19)으로 중국을 거쳐 국내로 들어오는 탈북루트가 ...
4     달 6ㆍ25 전쟁 70주년 추념식 애국가가 북한의 애국가 전주와 비슷해 논란이다. ...
26    [서울신문]코로나19 확산 때문에 중국을 거쳐 국내로 들어오는 탈북 루트가 사실상 ...
58    국내 입국 탈북민 수가 올해 2분기 사상 최저를 기록했다. 신종 코로나바이러스 감염...
76    국내 입국 탈북민 수가 올해 2분기 사상 최저를 기록했다.     신종 코로나바이러...
73    "우리당에 대권주자가 있나?" 김종인 미래통합당 비상대책위원장이 이 물음을 자주 던...
25    북한에서 신종 코로나바이러스 감염증(코로나19)과 관련해 주민 255명이 격리 중이...
77    수면 위 떠오른 주한미군 감축론 / 트럼프, 駐獨미군 9500명 감축 발표하며 / ...
Name: content, dtype: object

scikit-learn 002 방식 체크

In [6]:
import pandas as pd
pd.options.mode.chained_assignment = None
import numpy as np
np.random.seed(0)  # 랜덤 난수를 지정하여 사용
# test = np.random.permutation(10)
# print(test)
from konlpy.tag import Twitter
twitter = Twitter()
 
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer, HashingVectorizer
from sklearn.metrics.pairwise import linear_kernel, cosine_similarity 
D:\appd\lib\site-packages\konlpy\tag\_okt.py:16: UserWarning: "Twitter" has changed to "Okt" since KoNLPy v0.4.5.
  warn('"Twitter" has changed to "Okt" since KoNLPy v0.4.5.')
In [7]:
# tokenizer : 문장에서 색인어 추출을 위해 명사,동사,알파벳,숫자 정도의 단어만 뽑아서 normalization, stemming 처리하도록 함
def tokenizer(raw, pos=["Noun","Alpha","Verb","Number"], stopword=[]):
    return [
        word for word, tag in twitter.pos(
            raw, 
            norm=True,   # normalize 그랰ㅋㅋ -> 그래ㅋㅋ
            stem=True    # stemming 바뀌나->바뀌다
            )
            if len(word) > 1 and tag in pos and word not in stopword
        ]
 
# 테스트 문장
rawdata = [
    '남북 고위급회담 대표단 확정..남북 해빙모드 급물살',
    '[남북 고위급 회담]장차관만 6명..판 커지는 올림픽 회담',
    '문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰',
    '1987 본 문 대통령.."그런다고 바뀌나? 함께 하면 바뀐다"',
    '이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨'
]
rawdata
Out[7]:
['남북 고위급회담 대표단 확정..남북 해빙모드 급물살',
 '[남북 고위급 회담]장차관만 6명..판 커지는 올림픽 회담',
 '문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰',
 '1987 본 문 대통령.."그런다고 바뀌나? 함께 하면 바뀐다"',
 '이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨']
In [8]:
# 문서목록에서 각 문서의 feature(문장의 특징) 노출수를 가중치로 설정한 BOW 벡터를 만든다.
vectorize = CountVectorizer(
    tokenizer=tokenizer, 
    min_df=2    # 예제로 보기 좋게 1번 정도만 노출되는 단어들은 무시하기로 했다
                # min_df = 0.01 : 문서의 1% 미만으로 나타나는 단어 무시
                # min_df = 10 : 문서에 10개 미만으로 나타나는 단어 무시
                # max_df = 0.80 : 문서의 80% 이상에 나타나는 단어 무시
                # max_df = 10 : 10개 이상의 문서에 나타나는 단어 무시
)

# 문장에서 노출되는 feature(특징이 될만한 단어) 수를 합한 Document Term Matrix(이하 DTM) 을 리턴한다
X = vectorize.fit_transform(rawdata)  # 인덱스 , tf-idf 기준의 DTM 값을 리턴한다. 


print(
    'fit_transform, (sentence {}, feature {})'.format(X.shape[0], X.shape[1])
)

dtm_bindo = X.toarray()   # 5 행 7 열 에 대한 피쳐 
features = vectorize.get_feature_names()  # ['1987', '고위', '남북', '대통령', '동반', '여사', '회담']
print(features)
[print(x) for x in rawdata]
df_ = pd.DataFrame(dtm_bindo, columns=features)   # feature or topic 기준 , DTM 테이블 구성하기 
df_
fit_transform, (sentence 5, feature 7)
['1987', '고위', '남북', '대통령', '동반', '여사', '회담']
남북 고위급회담 대표단 확정..남북 해빙모드 급물살
[남북 고위급 회담]장차관만 6명..판 커지는 올림픽 회담
문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰
1987 본 문 대통령.."그런다고 바뀌나? 함께 하면 바뀐다"
이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨
Out[8]:
1987 고위 남북 대통령 동반 여사 회담
0 0 1 2 0 0 0 1
1 0 1 1 0 0 0 2
2 1 0 0 2 1 1 0
3 1 0 0 1 0 0 0
4 0 0 0 3 1 1 0
In [9]:
# 검색 문장에서 feature를 뽑아냄
input_text = '1987 관람한 대통령 인터뷰 기사'

srch=[t for t in tokenizer(input_text) if t in features]
print(srch) # ['1987', '대통령']

# dtm 에서 검색하고자 하는 feature만 뽑아낸다.
srch_dtm = np.asarray(X.toarray())[:, [
    # vectorize.vocabulary_.get 는 특정 feature 가 dtm 에서 가지고 있는 컬럼의 인덱스값 을 리턴한다
    vectorize.vocabulary_.get(i) for i in srch  
]]
['1987', '대통령']
In [10]:
score = srch_dtm.sum(axis=1)
print(score)
# array([0, 0, 3, 2, 3], dtype=int64) 문장별 feature 합계 점수

    
for i in score.argsort()[::-1]: # argsort-큰값부터 순서대로 데이터의 위치를 반환
    if score[i] > 0:  # score[i] 는 feature 합계의 인덱스 , i 는 디센딩으로 들어오는 스코어 인덱스 
        print('{} 번째 문장에 디센딩 순서문장은: , {} / score : {}'.format(i, rawdata[i], score[i]))
        
# # 이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨 / score : 3
# # 문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰 / score : 3
# # 1987 본 문 대통령.."그런다고 바뀌나? 함께 하면 바뀐다" / score : 2
[0 0 3 2 3]
4 번째 문장에 디센딩 순서문장은: , 이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨 / score : 3
2 번째 문장에 디센딩 순서문장은: , 문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰 / score : 3
3 번째 문장에 디센딩 순서문장은: , 1987 본 문 대통령.."그런다고 바뀌나? 함께 하면 바뀐다" / score : 2
In [11]:
## TfidfVectorizer  방식으로 가중치를 주어서 Bow 를 만들다 
vectorize = TfidfVectorizer(
    tokenizer=tokenizer,
    min_df=2,
    
    sublinear_tf=True    # tf값에 1+log(tf)를 적용하여 tf값이 무한정 커지는 것을 막음
)
X = vectorize.fit_transform(rawdata)

print(
    'fit_transform, (sentence {}, feature {})'.format(X.shape[0], X.shape[1])
)
# fit_transform, (sentence 5, feature 7)

print(X.toarray())

# ([[0.        , 0.40824829, 0.81649658, 0.        , 0.        , 0.        , 0.40824829],
# [0.        , 0.40824829, 0.40824829, 0.        , 0.        , 0.        , 0.81649658],
# [0.41680418, 0.        , 0.        , 0.69197025, 0.41680418, 0.41680418, 0.        ],
# [0.76944707, 0.        , 0.        , 0.63871058, 0.        , 0.        , 0.        ],
# [0.        , 0.        , 0.        , 0.8695635 , 0.34918428, 0.34918428, 0.        ]])

# 문장에서 뽑아낸 feature 들의 배열
features = vectorize.get_feature_names()
print(features)

df_tfi = pd.DataFrame(X.toarray(), columns=features)
df_tfi
fit_transform, (sentence 5, feature 7)
[[0.         0.45329466 0.76749457 0.         0.         0.
  0.45329466]
 [0.         0.45329466 0.45329466 0.         0.         0.
  0.76749457]
 [0.44832087 0.         0.         0.63009934 0.44832087 0.44832087
  0.        ]
 [0.76944707 0.         0.         0.63871058 0.         0.
  0.        ]
 [0.         0.         0.         0.77637396 0.44566999 0.44566999
  0.        ]]
['1987', '고위', '남북', '대통령', '동반', '여사', '회담']
Out[11]:
1987 고위 남북 대통령 동반 여사 회담
0 0.000000 0.453295 0.767495 0.000000 0.000000 0.000000 0.453295
1 0.000000 0.453295 0.453295 0.000000 0.000000 0.000000 0.767495
2 0.448321 0.000000 0.000000 0.630099 0.448321 0.448321 0.000000
3 0.769447 0.000000 0.000000 0.638711 0.000000 0.000000 0.000000
4 0.000000 0.000000 0.000000 0.776374 0.445670 0.445670 0.000000
In [12]:
# 검색 문장에서 feature를 뽑아냄
srch=[t for t in tokenizer('1987 관람한 대통령 인터뷰 기사') if t in features]
print(srch)
# ['1987', '대통령']

# dtm 에서 검색하고자 하는 feature만 뽑아낸다.
srch_dtm = np.asarray(X.toarray())[:, [
    # vectorize.vocabulary_.get 는 특정 feature 가 dtm 에서 가지고 있는 index값을 리턴한다
    vectorize.vocabulary_.get(i) for i in srch
]]

#       1987    대통령
# 0     0.000000    0.000000
# 1     0.000000    0.000000
# 2     0.416804    0.691970
# 3     0.769447    0.638711
# 4     0.000000    0.869563

score = srch_dtm.sum(axis=1)
print(score)
# array([0.         0.         1.10877443 1.40815765 0.8695635 ], dtype=int64) 문장별 feature 합계 점수

for i in score.argsort()[::-1]:
    if score[i] > 0:
        print('{} / score : {}'.format(rawdata[i], score[i]))

# 1987 본 문 대통령.."그런다고 바뀌나? 함께 하면 바뀐다" / score : 1.408157650537996
# 문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰 / score : 1.1087744279177436
# 이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨 / score : 0.869563495264799
['1987', '대통령']
[0.         0.         1.07842022 1.40815765 0.77637396]
1987 본 문 대통령.."그런다고 바뀌나? 함께 하면 바뀐다" / score : 1.408157650537996
문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰 / score : 1.0784202177170614
이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨 / score : 0.7763739568837855
In [13]:
## HashingVectorizer 방식으로 테스트 해보기 , 
##  대용량 텍스트를 벡터화 할때 많이 쓰인다,  CountVectorizer, TfidfVectorizer 와 달리 벡터화 할때 모든 feature 에 대해 사전을 만들지 않고, 해싱함수를 통해 벡터안의 인덱스를 특정하도록 한다. 큰 사전을 만들 필요가 없어 메모리 소모가 적어 대용량 텍스트를 벡터화 할때 많이 쓰인다.
vectorize = HashingVectorizer(
    tokenizer=tokenizer,
    n_features=7               # 기본 feature 수를 설정하며 기본값이 2의 20승이다. 아래 예시를 위해 feature 를 7로 한정했으나, 아래 유사문장을 찾을때는 다시 n_features 주석처리 했다.
)
X = vectorize.fit_transform(rawdata)

print(X.shape)
# (5, 7)

print(X.toarray())

# ([[ 0.33333333,  0.33333333, -0.33333333,  0.33333333,  0.33333333, 0.66666667,  0.        ],
# [ 0.        ,  0.        , -0.57735027,  0.57735027,  0.57735027, 0.        ,  0.        ],
# [ 0.        ,  0.        ,  0.        ,  0.        , -0.21821789, -0.43643578,  0.87287156],
# [ 0.        ,  0.        ,  0.        ,  0.81649658,  0.        , -0.40824829,  0.40824829],
# [ 0.28867513,  0.28867513, -0.28867513,  0.28867513, -0.57735027, 0.        ,  0.57735027]])
(5, 7)
[[ 0.33333333  0.33333333 -0.33333333  0.33333333  0.33333333  0.66666667
   0.        ]
 [ 0.          0.         -0.57735027  0.57735027  0.57735027  0.
   0.        ]
 [ 0.          0.          0.          0.         -0.21821789 -0.43643578
   0.87287156]
 [ 0.          0.          0.          0.81649658  0.         -0.40824829
   0.40824829]
 [ 0.28867513  0.28867513 -0.28867513  0.28867513 -0.57735027  0.
   0.57735027]]
D:\appd\lib\site-packages\sklearn\feature_extraction\text.py:507: UserWarning: The parameter 'token_pattern' will not be used since 'tokenizer' is not None'
  warnings.warn("The parameter 'token_pattern' will not be used"
In [14]:
# search 문장 벡터
srch_vector = vectorize.transform([
    '1987 관람한 대통령 인터뷰 기사'
])

print(srch_vector.shape)
# (1, 7)
(1, 7)
In [15]:
from sklearn.metrics.pairwise import linear_kernel

# linear_kernel는 두 벡터의 dot product 이다.
cosine_similar = linear_kernel(srch_vector, X).flatten()
# cosine_similar = (srch_vector*X.T).toarray().flatten()

print(cosine_similar)
# [0.         0.         0.62017367 0.31622777 0.3]

print(cosine_similar.shape)
# (5,)

# 유사한 rawdata index
sim_rank_idx = cosine_similar.argsort()[::-1]
print(sim_rank_idx)
#[2 3 4 1 0]

for i in sim_rank_idx:
    if cosine_similar[i] > 0:
        print('{} / score : {}'.format(rawdata[i], cosine_similar[i]))

# 문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰 / score : 0.6201736729460423
# 1987 본 문 대통령.."그런다고 바뀌나? 함께 하면 바뀐다" / score : 0.3162277660168379
# 이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨 / score : 0.3
[-0.4472136  -0.25819889  0.58554004  0.          0.12909944]
(5,)
[2 4 3 1 0]
문재인 대통령과 대통령의 영부인 김정숙여사 내외의 동반 1987 관람 후 인터뷰 / score : 0.5855400437691198
이명박 전 대통령과 전 대통령의 부인 김윤옥 여사, 그리고 전 대통령의 아들 이시형씨의 동반 검찰출석이 기대됨 / score : 0.12909944487358058
In [16]:
# # a = [1, 2, 3, 4, 5] # a list
# # a = np.asarray(a)
# # a
# test1 = np.asarray(X.toarray())

# test = np.asarray(X.toarray())[:, [
#     vectorize.vocabulary_.get('1987')
# ]]
# # test = vectorize.vocabulary_.get('1987')
# print(test1)
# print(test)

# # print(test1[:,[0]])


# print(srch_dtm)
# score = srch_dtm.sum(axis=0)  # 내리방향으로 더하기 
# score = srch_dtm.sum(axis=1)  # 가로 방향으로 더하기 
# print(score)

# print(score.argsort()[::])    # argsort-작은값부터 순서대로 데이터의 위치를 반환
# print(score.argsort()[::-1])  # argsort-큰값부터 순서대로 데이터의 위치를 반환
Comments