1. 자연어의 특성¶
- 자연어를 기계가 처리하도록 하기 위해서는 먼저 자연어를 기계가 이해할 수 있는 언어로 바꾸는 방법을 알아야 함
- 토큰화 작업의 결과인 단어 사전을 기계가 이해할 수 있는 언어로 표현하는 과정이고, 단어 사전 내 단어 하나를 어떻게 표현할까의 문제로 볼 수 있음
1-1. 단어의 유사성과 모호성¶
- 단어의 의미는 유사성과 모호성을 가지고 있는데 단어는 겉으로 보이는 형태인 표제어안에 여러가지 의미를 담고 있음
- 사람은 주변 정보에 따라 숨겨진 의미를 파악하고 이해할 수 있으나, 기계는 학습의 부재 또는 잘못된 데이터로 의미를 파악하지 못하는 경우가 많음
- 한 가지 형태의 단어에 여러 의미가 포함되어 생기는 중의성 문제는 자연어 처리에서 매우 줌요
- 동형어: 형태는 같으나 뜻이 서로 다른 단어(예: 배)
- 다의어: 하나의 형태가 여러 의미를 지니면서도 그 의미들이 서로 관련이 없는 단어(예: 머리)
- 동의어: 서로 다른 형태의 단어들이 동일한 의미를 가지는 단어(예: 춘추, 나이)
- 상의어: 상위 개념을 가리키는 단어(예: 동물)
- 하의어: 하위 개념을 가리키는 단어(예: 강아지)
1-2. 언어의 모호성 해소¶
- 동형어나 다의어처럼 여러 의미를 가지는 단어들이 하나의 형태로 공유, 동의어처럼 하나의 형태를 가지는 단어들이 서로 같은 의미를 공유
- 단어 중의성 해소(WSD) 알고리즘 방법을 통해 단어의 의미를 명확히 함
- 지식 기반 단어 중의성 해소
- 컴퓨터가 읽을 수 있는 사전이나 어휘집 등을 바탕으로 단어의 의미를 추론하는 접근방식
- 구축에 많은 리소스가 필요함
- 데이터 편향이 생길 수 있음
- 지도 학습 기반 단어 중의성 해소
- 지도 학습은 데이터에 정답이 있다는 의미로, 각종 기계 학습 알고리즘을 통해 단어 의미를 분류해내는 방법
- 좋은 성능을 위해서는 질 높은 레이블을 가진 많은 데이터가 필요
- 데이터가 충분할 경우 일반화된 환경에서도 좋은 성능을 낼 수 있음
- 비지도 학습 기반 단어 중의성 해소
- 문장에 등장하는 각 단어의 의미를 사전적인 의미에 연결하지 않고, 세부 의미가 같은 맥락을 군집화하는 데에 초점을 맞춤
- 대규모 자연어 코퍼스로부터 추가 작업 없이 자동적으로 학습을 수행할 수 있어서 활용 가능성이 높음
- 사람이 직접 제작한 학습 데이터를 사용하지 않기 때문에 성능을 내기 어려움
- 지식 기반 단어 중의성 해소
2. 임베딩 구축 방법¶
2-1. 임베딩¶
- 자연어처리 작업에서 특징 추출을 통해 자연어를 수치화하는 과정이 필요하고 이런 벡터화의 과정이자 결과
- 토큰화 작업의 목표는 임베딩을 만들기 위한 단어 사전을 구축하는것
2-2. 임베딩의 역할¶
- 자연어의 의미적인 정보 함축
- 자연어의 중요한 특징들을 추출하여 벡터로 압축하는 과정
- 임베딩으로 표현된 문장은 실제 자연어의 주요 정보들을 포함하고 있음
- 벡터인 만큼 사칙연산이 가능하여 단어 벡터간 덧셈/뺄셈을 통해 단어들 사이의 의미적 문법적 관계를 도출
- 임베딩의 품질을 평가하기 위해 사용되는 단어 유추 평가(https://word2vec.kr/search/)
- 자연어 간 유사도 계산
- 자연어를 벡터로 표현하면 두 벡터 간 유사도를 계산할 수 있음(코사인 유사도, 유클리디안 거리 기반 유사도, 맨하탄 거리 기반 유사도 ..)
- 코사인 유사도는 -1이상 1이하의 값을 가지며, 값이 1에 가까울수록 유사도가 높다고 판단함
- 전이 학습
- 이미 만들어진 임베딩을 다른 작업을 학습하기 위한 입력값으로 쓰임
- 품질이 좋은 임베딩을 사용할수록 목표로하는 자연어처리 작업의 학습 속도와 성능이 향상됨
- 매번 새로운 것을 배울때 scratch 부터 시작한다면 매 학습이 오래 걸림
- 파인 튜닝: 학습하는데 전이 학습에 의한 임베딩을 초기화하여 사용하면 새로운 작업을 학습함에도 빠르게 학습할 수 있고 성능도 좋아짐
2-3. 단어 출현 빈도에 기반한 임베딩 구축 방법¶
- 원 핫 인코딩
- 자연어를 0과 1로 구별하겠다는 인코딩 방법
- 표현하고 싶은 단어의 인덱스에 1의 값을 부여하고, 나머지 인덱스에는 0을 부여하는 벡터 표현 방식
- ".. 오늘 날씨가 참 좋다.." -> { ...5:"좋다", 6:"날씨", ...13:"오늘", ...} (일반적으로 빈도수로 정렬하고 사용, 인덱스의 순서가 의미가 없음)
- 순서가 없는 카테고리컬 피쳐인 경우 클래스 갯수가 3개 이상일 때 원 핫 인코딩을 함
- "오늘 좋아 내일 싫어"
- 오늘 -> [1, 0, 0, 0]
- 좋아 -> [0, 1, 0, 0]
- 내일 -> [0, 0, 1, 0]
- 싫어 -> [0, 0, 0, 1]
- 단어 사전의 크기가 10,000이라면, 총 10,000개 중 현재 단어를 표현하는 1개의 차원에만 1을, 나머지 9,999개의 차원은 0으로 표현
- 대부분의 값들이 0인 행렬을 희소행렬이라 하는데, 단어가 늘어날수록 행렬의 크기는 계속 증가하나 증가하는 크기에 비해 표현의 호율성이 떨어짐
- 단어의 유사도를 표현하지 못함
- Bag of Words
- 단어들의 순서를 전혀 고려하지 않고 단어들의 출현빈도에 집중하는 자연어 코퍼스의 데이터 수치화 방법
- 각 단어에 고유한 정수 인덱스를 부여하여 단어 집합을 생성하고 각 인덱스의 위치에 단어 토큰의 등장 횟수를 기록한 벡터를 듦
- 단어 단위 압축 방식이기 때문에 희소 문제와 단어 순서를 반영하지 못함
- 문장을 표현하는 방법 -> 원 핫 인코딩을 모두 더함
- 오늘 좋아: [1, 1, 0, 0]
- 오늘 좋아 좋아: [1, 2, 0, 0]
- TF-IDF
- 단어의 빈도와 문서의 빈도를 사용하여 문서-단어 행렬 내 각 단어들의 중요한 정도를 가중치로 주는 표현 방법
- 문서의 유사도를 구하는 작업, 검색 시스템에서 검색 결과의 중요도를 정하는 작업, 문서내에서 특정 단어의 중요도를 구하는 작업 등에서 효과적으로 쓰일 수 있음
2-4. 단어의 순서¶
- 통계 기반 언어 모델
- 단어가 n개 주어졌을 때 언어 모델은 n개의 단어가 동시에 나타날 확률을 반환
- 문장은 어순을 고려하여 여러 단어로 이루어진 단어 시퀀스를 n개의 단어로 구성된 단어 시퀀스를 확율적으로 표현
- 딥러닝 기반 언어 모델
- 통계 기반 언어 모델에서는 빈도 라는 통계량을 활용하여 확률을 추산하여 나열하지만, 딥러닝 기반 언어 모델들이 등장하면서 입력과 출력 사이의 관계를 유연하게 정의할 수 있게 되고, 그 자체로 확률 모델로 동작할 수 있음
- MLM(MAsked Language Modeling)
- 문장 중간에 마스크를 씌워서 해당 마스크에 어떤 단어가 올지 예측하는 과정으로 학습을 진행
- 문장 전체를 다 보고 중간에 있는 단어를 예측하기 때문에 양방향 학습이 가능
- 대표적으로 BERT 모델이 있음
- Next Token Prediction
- 주어진 단어 시퀀스를 가지고 다음 단어로 어떤 단어가 올지 예측하는 과정으로 학습
- 단어를 순차적으로 입력 받은 뒤 다음 단어를 맞춰야하기 때문에 한방향 학습을 함
- 대표적으로 GPT, ELMo 모델이 있음
3. 텍스트 유사도¶
- 두 개의 자연어 텍스트가 얼마나 유산한지를 나타내는 방법
- 유사도를 정의하거나 판단하는 척도가 주관적이기 때문에, 최대한 정량화하는 방법을 찾는것이 중요함
3-1. 유클리디안 거리 기반 유사도¶
- 두 점 사이의 거리를 측정하는 유클리디안 거리 공식을 사용하여 문서의 유사도를 구하는 방법으로 거리가 가까올수록 유사도가 높다고 판단함
- 자연어처리 분야뿐 아니라 다른 분야에서도 범용적으로 사용되는 거리 측정 기법
3-2. 맨하탄 거리 기반 유사도¶
- 맨하탄 거리 공식을 사용하여 문서의 유사도를 구하는 방법
- 유클리드 거리 공식과 유사하나 각 차원의 차를 곱해서 사용하는 대신 절대값을 바로 합산함
- 유클리드 거리 공식보다 값이 크거나 같음
- 다차원 공간 상에서 두 좌표 간 최단거리를 구하는 방법이 아니다 보니 특별한 상황이 아니면 잘 사용되지 않음
3-3. 코사인 유사도¶
- 두 개의 벡터값에서 코사인 각도를 이용하여 구할 수 있는 두 벡터의 유사도를 의미
- 두 벡터의 방향이 완전히 동일한경우는 1의 값을 가지며, 90도의 각을 이루면 0, 180도로 반대의 방향을 가지면 -1의 값을 가짐
- -1이상 1이하의 값을 가지며, 값이 1에 가까울수록 유사하다는 것을 의미
- 두 벡터가 가리키는 방향이 얼마나 유사한가를 의미하기 때문에 자연어 내 유사도 계산에 적합함
3-4. 자카드 유사도¶
- 두 문장을 각각 단어의 집합으로 만든 뒤 두 집합을 통해 유사도를 측정하는 방식
- 수치화된 벡터 없이 단어 집합만으로 계산할 수 있음
- 두 집합의 교집함인 공통된 단어의 개수를 두 집합의 합집합을 전체 단어의 개수로 나눈 것
- 전체 합집합 중 공통의 단어의 개수에 따라 0과 1사이의 값을 가지며, 1에 가까울수록 유사도가 높음
4. 유사도 측정 실습¶
sen_1 = '오늘 점심에 배가 너무 고파서 밥을 너무 많이 먹었다'
sen_2 = '오늘 점심에 배가 고파서 밥을 많이 먹었다'
sen_3 = '오늘 배가 너무 고파서 점심에 밥을 너무 많이 먹었다'
sen_4 = '오늘 점심에 배가 고파서 지하철을 많이 먹었다'
sen_5 = '어제 저녁에 밥을 너무 많이 먹었더니 배가 부르다'
sen_6 = '이따가 오후 6시에 출발하는 비행기가 3시간 연착이 되었다고 하네요'
training_documents = [sen_1, sen_2, sen_3, sen_4, sen_5, sen_6]
for text in training_documents:
print(text)
오늘 점심에 배가 너무 고파서 밥을 너무 많이 먹었다 오늘 점심에 배가 고파서 밥을 많이 먹었다 오늘 배가 너무 고파서 점심에 밥을 너무 많이 먹었다 오늘 점심에 배가 고파서 지하철을 많이 먹었다 어제 저녁에 밥을 너무 많이 먹었더니 배가 부르다 이따가 오후 6시에 출발하는 비행기가 3시간 연착이 되었다고 하네요
from sklearn.feature_extraction.text import CountVectorizer
vectorlizer = CountVectorizer()
vectorlizer.fit(training_documents)
CountVectorizer()In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
CountVectorizer()
word_idx = vectorlizer.vocabulary_
word_idx
{'오늘': 14, '점심에': 18, '배가': 9, '너무': 3, '고파서': 2, '밥을': 8, '많이': 5, '먹었다': 6, '지하철을': 19, '어제': 12, '저녁에': 17, '먹었더니': 7, '부르다': 10, '이따가': 16, '오후': 15, '6시에': 1, '출발하는': 20, '비행기가': 11, '3시간': 0, '연착이': 13, '되었다고': 4, '하네요': 21}
# word_idx를 idx 순서대로 정렬
for key, idx in sorted(word_idx.items()):
print(f'word: {key}, idx: {idx}')
word: 3시간, idx: 0 word: 6시에, idx: 1 word: 고파서, idx: 2 word: 너무, idx: 3 word: 되었다고, idx: 4 word: 많이, idx: 5 word: 먹었다, idx: 6 word: 먹었더니, idx: 7 word: 밥을, idx: 8 word: 배가, idx: 9 word: 부르다, idx: 10 word: 비행기가, idx: 11 word: 어제, idx: 12 word: 연착이, idx: 13 word: 오늘, idx: 14 word: 오후, idx: 15 word: 이따가, idx: 16 word: 저녁에, idx: 17 word: 점심에, idx: 18 word: 지하철을, idx: 19 word: 출발하는, idx: 20 word: 하네요, idx: 21
# word_idx에 따라 dataframe을 생성
# 컬럼: key, 인덱스: 문장idx, 값: 빈도수
# 오늘 점심에 배가 너무 고파서 ...
# 0 1 1 2 1 ...
import pandas as pd
result = []
vocab = list(word_idx.keys())
for i in range(len(training_documents)):
result.append([])
d = training_documents[i]
for j in range(len(vocab)):
target = vocab[j]
result[-1].append(d.count(target))
tf = pd.DataFrame(result, columns=vocab)
tf
오늘 | 점심에 | 배가 | 너무 | 고파서 | 밥을 | 많이 | 먹었다 | 지하철을 | 어제 | ... | 부르다 | 이따가 | 오후 | 6시에 | 출발하는 | 비행기가 | 3시간 | 연착이 | 되었다고 | 하네요 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 2 | 1 | 1 | 1 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 1 | 1 | 1 | 2 | 1 | 1 | 1 | 1 | 0 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3 | 1 | 1 | 1 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | ... | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
4 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 1 | ... | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | ... | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
6 rows × 22 columns
vector_sen_1 = vectorlizer.transform([sen_1]).toarray()[0]
vector_sen_2 = vectorlizer.transform([sen_2]).toarray()[0]
vector_sen_3 = vectorlizer.transform([sen_3]).toarray()[0]
vector_sen_4 = vectorlizer.transform([sen_4]).toarray()[0]
vector_sen_5 = vectorlizer.transform([sen_5]).toarray()[0]
vector_sen_6 = vectorlizer.transform([sen_6]).toarray()[0]
print(vector_sen_1)
print(vector_sen_2)
print(vector_sen_3)
print(vector_sen_4)
print(vector_sen_5)
print(vector_sen_6)
[0 0 1 2 0 1 1 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0] [0 0 1 0 0 1 1 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0] [0 0 1 2 0 1 1 0 1 1 0 0 0 0 1 0 0 0 1 0 0 0] [0 0 1 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 1 1 0 0] [0 0 0 1 0 1 0 1 1 1 1 0 1 0 0 0 0 1 0 0 0 0] [1 1 0 0 1 0 0 0 0 0 0 1 0 1 0 1 1 0 0 0 1 1]
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))
sen_1 = '오늘 점심에 배가 너무 고파서 밥을 너무 많이 먹었다'
sen_2 = '오늘 점심에 배가 고파서 밥을 많이 먹었다'
sen_3 = '오늘 배가 너무 고파서 밥을 너무 많이 먹었다'
sen_4 = '오늘 점심에 배가 고파서 지하철을 많이 먹었다'
sen_5 = '어제 저녁에 밥을 너무 많이 먹었더니 배가 부르다'
sen_6 = '이따가 오후 6시에 출발하는 비행기가 3시간 연착이 되었다고 하네요'
- sen_1, sen_2: 의미가 유사한 문장 간 유사도 계산(조사 생략) -> 0.7977240352174656
- sen_1, sen_3: 의미가 유사한 문장 간 유사도 계산(순서변경) -> 1.0
- sen_2, sen_4: 문장 내 단어를 임의의 단어로 치환한 문장과 원 문장 간 유사도 계산 -> 0.857142857142857
- sen_1, sen_5: 의미는 다르지만 비슷한 주제를 가지는 문장 간 유사도 계산 -> 0.5330017908890261
- sen_1, sen_6: 의미가 서로 다른 문장 간 유사도 계산 -> 0.0
print(f'sen_1, sen_2: {cos_sim(vector_sen_1, vector_sen_2)}')
print(f'sen_1, sen_3: {cos_sim(vector_sen_1, vector_sen_3)}')
print(f'sen_2, sen_4: {cos_sim(vector_sen_2, vector_sen_4)}')
print(f'sen_1, sen_5: {cos_sim(vector_sen_1, vector_sen_5)}')
print(f'sen_1, sen_6: {cos_sim(vector_sen_1, vector_sen_6)}')
sen_1, sen_2: 0.7977240352174656 sen_1, sen_3: 1.0 sen_2, sen_4: 0.857142857142857 sen_1, sen_5: 0.5330017908890261 sen_1, sen_6: 0.0
# TF-IDF기반 행렬을 활용한 문장 간 유사도 측정
from sklearn.feature_extraction.text import TfidfVectorizer
tfidfv = TfidfVectorizer().fit(training_documents)
for key, idx in sorted(tfidfv.vocabulary_.items()):
print(f'{key}: {idx}')
3시간: 0 6시에: 1 고파서: 2 너무: 3 되었다고: 4 많이: 5 먹었다: 6 먹었더니: 7 밥을: 8 배가: 9 부르다: 10 비행기가: 11 어제: 12 연착이: 13 오늘: 14 오후: 15 이따가: 16 저녁에: 17 점심에: 18 지하철을: 19 출발하는: 20 하네요: 21
tf_idf = tfidfv.transform(training_documents).toarray()
tf_idf
array([[0. , 0. , 0.28941449, 0.67547293, 0. , 0.24993256, 0.28941449, 0. , 0.28941449, 0.24993256, 0. , 0. , 0. , 0. , 0.28941449, 0. , 0. , 0. , 0.28941449, 0. , 0. , 0. ], [0. , 0. , 0.39248775, 0. , 0. , 0.33894457, 0.39248775, 0. , 0.39248775, 0.33894457, 0. , 0. , 0. , 0. , 0.39248775, 0. , 0. , 0. , 0.39248775, 0. , 0. , 0. ], [0. , 0. , 0.28941449, 0.67547293, 0. , 0.24993256, 0.28941449, 0. , 0.28941449, 0.24993256, 0. , 0. , 0. , 0. , 0.28941449, 0. , 0. , 0. , 0.28941449, 0. , 0. , 0. ], [0. , 0. , 0.34642121, 0. , 0. , 0.29916243, 0.34642121, 0. , 0. , 0.29916243, 0. , 0. , 0. , 0. , 0.34642121, 0. , 0. , 0. , 0.34642121, 0.58392899, 0. , 0. ], [0. , 0. , 0. , 0.29913919, 0. , 0.22136971, 0. , 0.43208699, 0.25633956, 0.22136971, 0.43208699, 0. , 0.43208699, 0. , 0. , 0. , 0. , 0.43208699, 0. , 0. , 0. , 0. ], [0.33333333, 0.33333333, 0. , 0. , 0.33333333, 0. , 0. , 0. , 0. , 0. , 0. , 0.33333333, 0. , 0.33333333, 0. , 0.33333333, 0.33333333, 0. , 0. , 0. , 0.33333333, 0.33333333]])
# TF-IDF 행렬에서 얻어지는 유사도의 값을 0 ~ 1로 스케일하기 위해 L1정규화를 진행
def l1_nomalize(v):
norm = np.sum(v)
return v / norm
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix_l1 = tfidf_vectorizer.fit_transform(training_documents)
tfidf_norm_l1 = l1_nomalize(tfidf_matrix_l1)
tfidf_norm_l1
<6x22 sparse matrix of type '<class 'numpy.float64'>' with 47 stored elements in Compressed Sparse Row format>
tf_sen_1 = tfidf_norm_l1[0:1]
tf_sen_2 = tfidf_norm_l1[1:2]
tf_sen_3 = tfidf_norm_l1[2:3]
tf_sen_4 = tfidf_norm_l1[3:4]
tf_sen_5 = tfidf_norm_l1[4:5]
tf_sen_6 = tfidf_norm_l1[5:6]
tf_sen_1.toarray()
array([[0. , 0. , 0.01788756, 0.04174829, 0. , 0.01544734, 0.01788756, 0. , 0.01788756, 0.01544734, 0. , 0. , 0. , 0. , 0.01788756, 0. , 0. , 0. , 0.01788756, 0. , 0. , 0. ]])
# 유클리디안 거리 기반 유사도 측정
from sklearn.metrics.pairwise import euclidean_distances
euclidean_distances(tf_sen_1, tf_sen_2)
array([[0.04479254]])
def euclidean_distances_value(vec_1, vec_2):
return round(euclidean_distances(vec_1, vec_2)[0][0], 3)
print(f'sen_1, sen_2: {euclidean_distances_value(tf_sen_1, tf_sen_2)}')
print(f'sen_1, sen_3: {euclidean_distances_value(tf_sen_1, tf_sen_3)}')
print(f'sen_2, sen_4: {euclidean_distances_value(tf_sen_2, tf_sen_4)}')
print(f'sen_1, sen_5: {euclidean_distances_value(tf_sen_1, tf_sen_5)}')
print(f'sen_1, sen_6: {euclidean_distances_value(tf_sen_1, tf_sen_6)}')
sen_1, sen_2: 0.045 sen_1, sen_3: 0.0 sen_2, sen_4: 0.044 sen_1, sen_5: 0.068 sen_1, sen_6: 0.087
from sklearn.metrics.pairwise import manhattan_distances
def manhattan_distances_value(vec_1, vec_2):
return round(manhattan_distances(vec_1, vec_2)[0][0], 3)
print(f'sen_1, sen_2: {manhattan_distances_value(tf_sen_1, tf_sen_2)}')
print(f'sen_1, sen_3: {manhattan_distances_value(tf_sen_1, tf_sen_3)}')
print(f'sen_2, sen_4: {manhattan_distances_value(tf_sen_2, tf_sen_4)}')
print(f'sen_1, sen_5: {manhattan_distances_value(tf_sen_1, tf_sen_5)}')
print(f'sen_1, sen_6: {manhattan_distances_value(tf_sen_1, tf_sen_6)}')
sen_1, sen_2: 0.085 sen_1, sen_3: 0.0 sen_2, sen_4: 0.077 sen_1, sen_5: 0.207 sen_1, sen_6: 0.347
from sklearn.metrics.pairwise import cosine_similarity
def cosine_similarity_value(vec_1, vec_2):
return round(cosine_similarity(vec_1, vec_2)[0][0], 3)
print(f'sen_1, sen_2: {cosine_similarity_value(tf_sen_1, tf_sen_2)}')
print(f'sen_1, sen_3: {cosine_similarity_value(tf_sen_1, tf_sen_3)}')
print(f'sen_2, sen_4: {cosine_similarity_value(tf_sen_2, tf_sen_4)}')
print(f'sen_1, sen_5: {cosine_similarity_value(tf_sen_1, tf_sen_5)}')
print(f'sen_1, sen_6: {cosine_similarity_value(tf_sen_1, tf_sen_6)}')
sen_1, sen_2: 0.737 sen_1, sen_3: 1.0 sen_2, sen_4: 0.747 sen_1, sen_5: 0.387 sen_1, sen_6: 0.0
# 언어 모델을 활용한 문장 간 유사도 측정
!pip install transformers
Requirement already satisfied: transformers in /usr/local/lib/python3.10/dist-packages (4.41.2) Requirement already satisfied: filelock in /usr/local/lib/python3.10/dist-packages (from transformers) (3.15.3) Requirement already satisfied: huggingface-hub<1.0,>=0.23.0 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.23.4) Requirement already satisfied: numpy>=1.17 in /usr/local/lib/python3.10/dist-packages (from transformers) (1.25.2) Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.10/dist-packages (from transformers) (24.1) Requirement already satisfied: pyyaml>=5.1 in /usr/local/lib/python3.10/dist-packages (from transformers) (6.0.1) Requirement already satisfied: regex!=2019.12.17 in /usr/local/lib/python3.10/dist-packages (from transformers) (2024.5.15) Requirement already satisfied: requests in /usr/local/lib/python3.10/dist-packages (from transformers) (2.31.0) Requirement already satisfied: tokenizers<0.20,>=0.19 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.19.1) Requirement already satisfied: safetensors>=0.4.1 in /usr/local/lib/python3.10/dist-packages (from transformers) (0.4.3) Requirement already satisfied: tqdm>=4.27 in /usr/local/lib/python3.10/dist-packages (from transformers) (4.66.4) Requirement already satisfied: fsspec>=2023.5.0 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub<1.0,>=0.23.0->transformers) (2023.6.0) Requirement already satisfied: typing-extensions>=3.7.4.3 in /usr/local/lib/python3.10/dist-packages (from huggingface-hub<1.0,>=0.23.0->transformers) (4.12.2) Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (3.3.2) Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (3.7) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (2.0.7) Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.10/dist-packages (from requests->transformers) (2024.6.2)
from transformers import AutoTokenizer, AutoModel, BertTokenizer
MODEL_NAME = 'bert-base-multilingual-cased'
model = AutoModel.from_pretrained(MODEL_NAME)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
config.json: 0%| | 0.00/625 [00:00<?, ?B/s]
model.safetensors: 0%| | 0.00/714M [00:00<?, ?B/s]
tokenizer_config.json: 0%| | 0.00/49.0 [00:00<?, ?B/s]
vocab.txt: 0%| | 0.00/996k [00:00<?, ?B/s]
tokenizer.json: 0%| | 0.00/1.96M [00:00<?, ?B/s]
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')
bert_sen_1
{'input_ids': tensor([[ 101, 9580, 118762, 9668, 71013, 10530, 9330, 11287, 9004, 32537, 8888, 46150, 12424, 9327, 10622, 9004, 32537, 47058, 9266, 17706, 102]]), 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
sen_1_outputs = model(**bert_sen_1)
# pooler_output: BERT 모델에서 나오는 마지막 레이어의 출력 값
sen_1_pooler_output = sen_1_outputs.pooler_output
sen_1_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)
print(f'sen_1, sen_2: {cos_sim(sen_1_pooler_output, sen_2_pooler_output)}')
print(f'sen_1, sen_3: {cos_sim(sen_1_pooler_output, sen_3_pooler_output)}')
print(f'sen_2, sen_4: {cos_sim(sen_2_pooler_output, sen_4_pooler_output)}')
print(f'sen_1, sen_5: {cos_sim(sen_1_pooler_output, sen_5_pooler_output)}')
print(f'sen_1, sen_6: {cos_sim(sen_1_pooler_output, sen_6_pooler_output)}')
sen_1, sen_2: tensor([0.9920], grad_fn=<SumBackward1>) sen_1, sen_3: tensor([0.9959], grad_fn=<SumBackward1>) sen_2, sen_4: tensor([0.9840], grad_fn=<SumBackward1>) sen_1, sen_5: tensor([0.9608], grad_fn=<SumBackward1>) sen_1, sen_6: tensor([0.9342], grad_fn=<SumBackward1>)
'코딩 > 자연어 처리' 카테고리의 다른 글
워드 임베딩 (0) | 2024.07.18 |
---|---|
LSTM과 GRU (0) | 2024.07.18 |
문장 임베딩 (0) | 2024.07.18 |
CNN text classification (0) | 2024.07.18 |
자연어 처리 프로젝트 진행 순서 (1) | 2024.07.17 |