728x90
반응형
SMALL
✅ 예시 문장
sen_1 = '오늘 점심에 배가 너무 고파서 밥을 너무 많이 먹었다.'
sen_2 = '오늘 점심에 배가 고파서 밥을 많이 먹었다.'
sen_3 = '오늘 배가 너무 고파서 점심에 밥을 너무 많이 먹었다.'
sen_4 = '오늘 점심에 배가 고파서 비행기를 많이 먹었다.'
sen_5 = '어제 저녁에 밥을 너무 많이 먹었더니 배가 부르다.'
sen_6 = '이따 오후 9시에 출발하는 비행기가 3시간 연착 되었다고 하네요.'
sen_1, sen_2: 의미가 유사한 문장 간 유사도 계산(조사를 생략)
sen_1, sen_3: 의미가 유사한 문장 간 유사도 계산(순서 변경)
sen_2, sen_4: 문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산
sen_1, sen_5: 의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산
sen_1, sen_6: 의미가 서로 다른 문장 간 유사도 계산
✅ 학습 데이터로서 리스트로 묶어줌
training_documents = [sen_1, sen_2, sen_3, sen_4, sen_5, sen_6]
✅ 뉴스기사를 가져와 파싱하는 라이브러리
!pip install newspaper3k
from newspaper import Article
# 가져올 기사의 URL
URL = 'https://v.daum.net/v/20230628102530481'
article = Article(URL, language='ko')
# 기사 다운로드 및 파싱
article.download()
article.parse()
news_title = article.title
news_context = article.text
print('title', news_title)
print('context', news_context)
✅ 기사의 내용을 "\n"을 기준으로 split
news_context = article.text.split('\n') # 엔터를 기준으로 split
for text in news_context:
print(text)
✅ 한국어 문장 분리를 위해 kss 라이브러리 설치 및 임포트
!pip install kss
import kss
✅ 한 줄 단위로 단어를 분리하는 함수 정의
def sentence_seperator(processed_context):
splited_context = []
for text in processed_context:
text = text.strip() # 공백 제거
if text:
splited_text = kss.split_sentences(text)
splited_context.extend(splited_text) # 리스트에 다른 리스트를 덧붙일 때 사용(다른 시퀀스 형식(리스트, 튜플, 문자열 등)의 요소를 추가)
return splited_context
✅ 함수를 이용하여 뉴스기사를 한 줄 단위로 문장 분리
splited_context = sentence_seperator(news_context)
for text in enumerate(splited_context):
print(text)
✅ 학습 데이터 만들기(training_documents + splited_context)
augmented_training_documents = training_documents + splited_context
for text in augmented_training_documents:
print(text)
✅ Bag of Words 기반으로 문장 간 유사도 측정
# CountVectorizer: 단어의 출현 빈도를 기반으로 문서를 벡터화
from sklearn.feature_extraction.text import CountVectorizer
# 객체 생성
bow_vectorizer = CountVectorizer()
bow_vectorizer.fit(augmented_training_documents) # 벡터화 모델을 학습
✅ 단어와 인덱스 사이의 매핑 정보를 담고 있는 딕셔너리 생성
word_idxes = bow_vectorizer.vocabulary_ # 단어와 해당 인덱스 사이의 매핑 정보를 담고 있는 딕셔너리
for key, idx in sorted(word_idxes.items()): # items(): (키, 값) 쌍(tuple)으로 반환
print(f'{key}:{idx}')
✅ word_idxes의 key를 column으로 하고 training 6개의 문장을 index로 하는 dataframe 생성, training_document에 있는 문장들을 분리하여 해당 개수(빈도)만큼 저장
result = []
vocab= list (word_idxes.keys())
for i in range(len(augmented_training_documents)):
result.append([])
d = augmented_training_documents[i]
for j in range(len(vocab)):
target = vocab[j]
result[-1].append(d.count(target))
tf =pd.DataFrame(result, columns=vocab, index=augmented_training_documents)
tf
✅ 유사도를 측정할 문장들을 문장-단어 행렬 기반 임베딩으로 변환
bow_vector_sen_1 = bow_vectorizer.transform([sen_1]).toarray()[0] # 벡터화 하여 array화
bow_vector_sen_2 = bow_vectorizer.transform([sen_2]).toarray()[0]
bow_vector_sen_3 = bow_vectorizer.transform([sen_3]).toarray()[0]
bow_vector_sen_4 = bow_vectorizer.transform([sen_4]).toarray()[0]
bow_vector_sen_5 = bow_vectorizer.transform([sen_5]).toarray()[0]
bow_vector_sen_6 = bow_vectorizer.transform([sen_6]).toarray()[0]
print(bow_vector_sen_1)
print(bow_vector_sen_2)
print(bow_vector_sen_3)
print(bow_vector_sen_4)
print(bow_vector_sen_5)
print(bow_vector_sen_6)
✅ 코사인 기반 유사도를 계산하기 위해 함수 정의
import numpy as np
from numpy import dot
from numpy.linalg import norm
def cos_sim(A, B):
return dot(A, B) / (norm(A) * norm(B))
print(f'의미가 유사한 문장 간 유사도 계산(조사 생략): {cos_sim(bow_vector_sen_1, bow_vector_sen_2)}') # 0.7977, 1에 가까울수록 유사하다고 판단
print(f'의미가 유사한 문장 간 유사도 계산(순서 변경): {cos_sim(bow_vector_sen_1, bow_vector_sen_3)}')
print(f'문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: {cos_sim(bow_vector_sen_2, bow_vector_sen_4)}') # 0.8571
print(f'의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: {cos_sim(bow_vector_sen_1, bow_vector_sen_5)}')
print(f'의미가 서로 다른 문장 간 유사도 계산: {cos_sim(bow_vector_sen_1, bow_vector_sen_6)}')
>>>
의미가 유사한 문장 간 유사도 계산(조사 생략): 0.7977240352174656
의미가 유사한 문장 간 유사도 계산(순서 변경): 1.0
문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: 0.857142857142857
의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: 0.5330017908890261
의미가 서로 다른 문장 간 유사도 계산: 0.0
✅ TF-IDF 기반 문서-단어 행렬을 활용하여 문장 간 유사도 측정
from sklearn.feature_extraction.text import TfidfVectorizer
# 객체생성 및 학습
tfidfv = TfidfVectorizer().fit(augmented_training_documents)
# 단어사전에서 단어와 idx값
for key, idx in sorted(tfidfv.vocabulary_.items()):
print(f'{key}: {idx}')
# TF-IDF 변환기인 tfidfv를 사용하여 주어진 문서들을 TF-IDF 벡터로 변환 후 array형태로 변환
sk_tf_idf = tfidfv.transform(augmented_training_documents).toarray()
print(sk_tf_idf) # 0~ 1 사이의 값보다 클 수 있음
# TF-IDF 행렬에서 얻어지는 유사도의 값을 0 ~ 1로 스케일링하기 위해 L1 정규화를 진행
def l1_normalize(v):
norm = np.sum(v)
return v / norm
fidf_vectorizer = TfidfVectorizer() # 객체생성
fidf_matrix_l1 = fidf_vectorizer.fit_transform(augmented_training_documents) # 학습시키고 벡터화
fidf_norm_1 = l1_normalize(fidf_matrix_l1) # 정규화
fidf_norm_1 # 데이터가 있는 객체
>>> <18x147 sparse matrix of type '<class 'numpy.float64'>'
with 212 stored elements in Compressed Sparse Row format>
tf_sen_1 = fidf_norm_1[0:1]
tf_sen_2 = fidf_norm_1[1:2]
tf_sen_3 = fidf_norm_1[2:3]
tf_sen_4 = fidf_norm_1[3:4]
tf_sen_5 = fidf_norm_1[4:5]
tf_sen_6 = fidf_norm_1[5:6]
tf_sen_1 # 객체
>>> <1x147 sparse matrix of type '<class 'numpy.float64'>'
with 8 stored elements in Compressed Sparse Row format>
# array형태로 보기
tf_sen_1.toarray()
✅ 유클리디안 거리 기반 유사도 측정
from sklearn.metrics.pairwise import euclidean_distances
euclidean_distances(tf_sen_1, tf_sen_2)
>>> array([[0.01090025]])
print(f'의미가 유사한 문장 간 유사도 계산(조사 생략): {euclidean_distances_value(tf_sen_1, tf_sen_2)}')
print(f'의미가 유사한 문장 간 유사도 계산(순서 변경): {euclidean_distances_value(tf_sen_1, tf_sen_3)}')
print(f'문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: {euclidean_distances_value(tf_sen_2, tf_sen_4)}')
print(f'의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: {euclidean_distances_value(tf_sen_1, tf_sen_5)}')
print(f'의미가 서로 다른 문장 간 유사도 계산: {euclidean_distances_value(tf_sen_1, tf_sen_6)}')
>>> 의미가 유사한 문장 간 유사도 계산(조사 생략): 0.011
의미가 유사한 문장 간 유사도 계산(순서 변경): 0.0
문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: 0.011
의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: 0.018
의미가 서로 다른 문장 간 유사도 계산: 0.024
✅ 맨해튼 거리 기반 유사도 측정
from sklearn.metrics.pairwise import manhattan_distances, cosine_similarity
manhattan_distances(tf_sen_1, tf_sen_2)
>>> array([[0.01962577]])
# 맨해튼 거리 유사도를 나타내는 함수
def manhattan_distances_value(vec_1, vec_2):
return round(manhattan_distances(vec_1, vec_2)[0][0], 3)
print(f'의미가 유사한 문장 간 유사도 계산(조사 생략): {manhattan_distances_value(tf_sen_1, tf_sen_2)}')
print(f'의미가 유사한 문장 간 유사도 계산(순서 변경): {manhattan_distances_value(tf_sen_1, tf_sen_3)}')
print(f'문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: {manhattan_distances_value(tf_sen_2, tf_sen_4)}')
print(f'의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: {manhattan_distances_value(tf_sen_1, tf_sen_5)}')
print(f'의미가 서로 다른 문장 간 유사도 계산: {manhattan_distances_value(tf_sen_1, tf_sen_6)}')
>>> 의미가 유사한 문장 간 유사도 계산(조사 생략): 0.02
의미가 유사한 문장 간 유사도 계산(순서 변경): 0.0
문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: 0.017
의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: 0.054
의미가 서로 다른 문장 간 유사도 계산: 0.096
✅ 코사인 유사도 측정
cosine_similarity(tf_sen_1, tf_sen_2)
def cosine_similarity_value(vec_1, vec_2):
return round(cosine_similarity(vec_1, vec_2)[0][0], 3)
print(f'의미가 유사한 문장 간 유사도 계산(조사 생략): {cosine_similarity_value(tf_sen_1, tf_sen_2)}')
print(f'의미가 유사한 문장 간 유사도 계산(순서 변경): {cosine_similarity_value(tf_sen_1, tf_sen_3)}')
print(f'문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: {cosine_similarity_value(tf_sen_2, tf_sen_4)}')
print(f'의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: {cosine_similarity_value(tf_sen_1, tf_sen_5)}')
print(f'의미가 서로 다른 문장 간 유사도 계산: {cosine_similarity_value(tf_sen_1, tf_sen_6)}')
>>> 의미가 유사한 문장 간 유사도 계산(조사 생략): 0.791
의미가 유사한 문장 간 유사도 계산(순서 변경): 1.0
문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: 0.797
의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: 0.425
의미가 서로 다른 문장 간 유사도 계산: 0.0
✅ 언어 모델을 활용하여 문장 간 유사도 측정하기(BERT모델)
!pip install transformers
from transformers import AutoModel, AutoTokenizer, BertTokenizer
# BERT 모델
MODEL_NAME = 'bert-base-multilingual-cased'
model = AutoModel.from_pretrained(MODEL_NAME)
# 사전학습한 토크나이저 생성
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
bert_sen_1 = tokenizer(sen_1, return_tensors= 'pt') # 파이토치 텐서로
bert_sen_2 = tokenizer(sen_2, return_tensors= 'pt')
bert_sen_3 = tokenizer(sen_3, return_tensors= 'pt')
bert_sen_4 = tokenizer(sen_4, return_tensors= 'pt')
bert_sen_5 = tokenizer(sen_5, return_tensors= 'pt')
bert_sen_6 = tokenizer(sen_6, return_tensors= 'pt')
# 문장 임베딩
# pooler_output: 문장 임베딩을 나타내는 벡터
sen_1_outputs = model(**bert_sen_1) #**: 딕셔너리 형태로 매개변수를 집어넣음
sen_1_pooler_output = sen_1_outputs.pooler_output
sen_2_outputs = model(**bert_sen_2)
sen_2_pooler_output = sen_2_outputs.pooler_output
sen_3_outputs = model(**bert_sen_3)
sen_3_pooler_output = sen_3_outputs.pooler_output
sen_4_outputs = model(**bert_sen_4)
sen_4_pooler_output = sen_4_outputs.pooler_output
sen_5_outputs = model(**bert_sen_5)
sen_5_pooler_output = sen_5_outputs.pooler_output
sen_6_outputs = model(**bert_sen_6)
sen_6_pooler_output = sen_6_outputs.pooler_output
from torch import nn
cos_sim = nn.CosineSimilarity(dim=1, eps=1e-6) # eps: 분모 자체가 0이되는것을 방지하기 위해 작은 값을 입력
print(f'의미가 유사한 문장 간 유사도 계산(조사 생략): {cos_sim(sen_1_pooler_output, sen_2_pooler_output)}')
print(f'의미가 유사한 문장 간 유사도 계산(순서 변경): {cos_sim(sen_1_pooler_output, sen_3_pooler_output)}')
print(f'문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: {cos_sim(sen_2_pooler_output, sen_4_pooler_output)}')
print(f'의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: {cos_sim(sen_1_pooler_output, sen_5_pooler_output)}')
print(f'의미가 서로 다른 문장 간 유사도 계산: {cos_sim(sen_1_pooler_output, sen_6_pooler_output)}')
>>> 의미가 유사한 문장 간 유사도 계산(조사 생략): tensor([0.9901], grad_fn=<SumBackward1>)
의미가 유사한 문장 간 유사도 계산(순서 변경): tensor([0.9972], grad_fn=<SumBackward1>)
문장 내 단어를 임의의 단어로 치환한 문장과 원본 문장간의 유사도 계산: tensor([0.9916], grad_fn=<SumBackward1>)
의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산: tensor([0.9744], grad_fn=<SumBackward1>)
의미가 서로 다른 문장 간 유사도 계산: tensor([0.9619], grad_fn=<SumBackward1>)
# 1,3번의 유사도가 더 높다. -> BERT 모델에 한국어를 지원하지만 완벽하지 않기 때문에
728x90
반응형
LIST
'Python > NLP' 카테고리의 다른 글
[파이썬, Python] 워드 임베딩 구축하기! (0) | 2023.07.23 |
---|---|
[파이썬, Python] 워드 임베딩(Word Embedding) - Word2Vec(C-BOW, Skipgram), FastText, GloVe (0) | 2023.07.23 |
[파이썬, Python] 임베딩(Embedding) - 임베딩 이론 (0) | 2023.07.07 |
[파이썬, Python] 자연어처리 - 데이터 전처리 실습하기! (0) | 2023.07.06 |
[파이썬, Python] 자연어처리 - 자연어 데이터 전처리 이론 (0) | 2023.07.05 |