Soy Library

[VectorDB] 특징벡터 본문

Study/GenAI

[VectorDB] 특징벡터

Soy_Hwang 2024. 8. 22. 18:10

특징 벡터의 유사도 & 거리

벡터의 관계를 숫자로 수치화하여 비교

  • L1 distance
a = np.array([2, 5])
b = np.array([4, 6])
c = np.array([6, 2])

# numpy를 이용한 계산
distance = np.sum(np.abs(a - b))
distance = np.linalg.norm(a - b, 1)
# torch를 이용한 계산
tensor_a = torch.from_numpy(a).float()
tensor_b = torch.from_numpy(b).float()
distance = torch.norm(tensor_a - tensor_b, 1)
  • L2 distance
# numpy를 이용한 계산
distance = np.sqrt(np.sum(np.square(a - b)))
distance = np.linalg.norm(a - b, 2)
# torch를 이용한 계산
tensor_a = torch.from_numpy(a).float()
tensor_b = torch.from_numpy(b).float()
distance = torch.norm(tensor_a - tensor_b, 2)
  • Dot-product
# numpy를 이용한 계산
similarity = np.dot(a, b)
similarity = a @ b
# torch를 이용한 계산
tensor_a = torch.from_numpy(a).float()
tensor_b = torch.from_numpy(b).float()
similarity = torch.dot(tensor_a, tensor_b)
  • Cosine 유사도
# 각도 & cosine 유사도 확인
theta = np.arccos(similarity)
theta, np.cos(theta)

# 단위 벡터의 내적
unit_a = a / np.linalg.norm(a, 2)
unit_b = b / np.linalg.norm(b, 2)
similarity = unit_a @ unit_b

# torch를 이용한 계산 
tensor_a = torch.tensor(a).float()
tensor_b = torch.tensor(b).float()
torch.nn.functional.cosine_similarity(tensor_a, tensor_b, dim=0)

NLP: 빈도수 기반 벡터

NLP: 빈도수 기반 벡터

  • 한국어의 특징: 교착어 & 띄어쓰기가 복잡하고 어려움
  • 접사가 존재하지 않는 영어와 달리, 한국어는 접사를 분리하는 작업이 필요
  • 형태소 분석기를 통해 어간과 접사 분할
    • MeCab: 가장 빠름
    >> mecab.morphs('영등포구청역에 있는 맛집 좀 알려주세요.')
    ['영등포구청역', '에', '있', '는', '맛집', '좀', '알려', '주', '세요', '.']
    
    • KoNLPy: 자바 설치 필요

BOW (Bag Of Words)

  • 단어들의 순서는 고려하지 않고 단어들의 출현 빈도에만 집중하는 텍스트의 수치화 표현 방법

TF-IDF (Term Frequency - Inverse Document Frequencty)

  • 어떤 문서 내에서 얼마나 중요한지 나타내는 지표
    • TF: 단어의 문서 내 출현 횟수, 자주 발생하는 단어
    • IDF: 단어가 출현한 문서 숫자의 역수
    • $tf-idf(d, w) = tf(d, w) \times \log\frac{N}{df(w)}$
  • 특정 문서에서 자주 나타나는 단어는 중요한 단어일 수 있음
  • 모든 문서에서 나타나는 단어는 중요하지 않은 단어
  • 문서의 TF-IDF 값을 문서의 특징 벡터로 볼 수 있음
# TF-IDF 이해를 위한 짧은 문서
corpus = [
    '먹고 싶은 사과',
    '먹고 싶은 바나나',
    '길고 노란 바나나 바나나',
    '저는 과일이 좋아요'
]

# make vocab
words = list(set(w for doc in corpus for w in doc.split()))
words.sort()
vocab = {w:i for i, w in enumerate(words)}
vocab

## 함수 정의 ##
# tf
def tf(w, d):  # w: 단어, d: 문서
  return d.count(w)

# tdf
def idf(w, corpus):
  df = 0
  for doc in corpus:
    df += w in doc
  return np.log(N/df)

# tf-idf
def tfidf(w, d, corpus):
  return tf(w, d)* idf(w, corpus)
  

## 연산 계산 ## 
# tf 계산
result = []
for i in range(N):
  result.append([])
  d = corpus[i]
  for w in vocab.keys():
    result[-1].append(tf(w, d))
tf_ = pd.DataFrame(result, columns = vocab)

# idf 계산
result = []
for w in vocab.keys():
    result.append(idf(w, corpus))
idf_ = pd.DataFrame(result, index=vocab, columns=["IDF"]).T

# tfidf 계산
result = []
for i in range(N):
  result.append([])
  d = corpus[i]
  for w in vocab.keys():
    result[-1].append(tfidf(w, d, corpus))

tfidf_ = pd.DataFrame(result, columns = vocab)
tfidf_

 

BM25

  • TF-IDF의 스코어를 보안하기 위한 파라미터 추가
  • TF의 영향을 감소: TF는 단어 빈도가 많아질수록 점수가 지속적으로 증가. BM25는 특정 값으로 수렴
  • IDF의 영향이 증가: BM25는 불용어가 점수에 영향을 덜 미치도록 DF가 높아지면 점수가 0으로 급격히 수렴
  • ElasticSearch 유사도
# 형태소 분석기를 이용한 tokeinizer 선언
# 조사 등 일부 품사를 제거
EXCLUDE = set(['JKS', 'JKC', 'JKG', 'JKO', 'JKB',
               'JKV', 'JKQ', 'JX', 'JC', 'EP',
               'EF', 'EC', 'ETN', 'ETM',
               'SF', 'SSC', 'SSO', 'SY'])

def tokenizer(sent):
    tokens = []
    for w, t in mecab.pos(sent):
        if t not in EXCLUDE:
            tokens.append(w)
    return tokens

의미 없는 단어(조사 등)를 제거하며 문장을 토큰화해주는 tokenizer 생성

# tokenize
tokenized_chunks = [tokenizer(chunk) for chunk in corpus]
print(tokenized_chunks[0])

# bm25 class 생성
bm25 = BM25Okapi(tokenized_chunks)
# tf
bm25.doc_freqs
# idf
bm25.idf

새로운 쿼리가 입력되면, 기존의 문서와 비교하여 bm25 유사도 score가 높은 순으로 출력

# query tokenize
query = "수학의 정의가 뭐야?"
tokenized_query = tokenizer(query)

# score 계산
# 기존의 다섯 개의 문서와 비교하여 score 계산
scores = bm25.get_scores(tokenized_query)

# score 순서로 정렬
rank = np.argsort(-scores)

# 입력 쿼리와 score가 높은 상위 두 개 문서 출력
bm25.get_top_n(tokenized_query, corpus, n = 2)

NLP: 단어 Embedding

빈도수 기반의 벡터의 한계

  • 문자에는 특징을 표현하는 아무런 정보가 있지 않음

Word2Vec

  • 주변에 같은 단어가 나타나는 중심 단어일수록 비슷한 벡터 값을 가져야 함
  • 즉, 주변 단어가 중심 단어의 의미를 표현
    • Skip-gram: 하나의 중심 단어를 통해 주변 단어를 예측 I went to the school library
    • CBOW: 여러 주변 단어를 통해 중심 단어를 예측 I went to the school library
  • 코드에서는 Skip-gram만 진행해봄
# 이미 학습된 model download
# wv = api.load('word2vec-google-news-300') # 1.6G
wv = api.load('glove-wiki-gigaword-100') # 128M

# vocab 개수 및 최초 20개 출력
print(f"len: {len(wv.index_to_key)}")
for i, word in enumerate(wv.index_to_key):
    if i >= 20:
        break
    print(f"{i:2d}: {word}")

# 입력 단어의 embedding vector 추출
wv['apple']

# 입력 단어에 대해 가장 유사한 단어 리스트를 추출해줌
wv.most_similar('banana')

 

# 단어 간 연산 가능
# p1 - n1 + p2
def analogy(p1, n1, p2):
    result = wv.most_similar(positive=[p2, p1], negative=[n1])
    return result

# king - man + woman
analogy('king', 'man', 'woman')

NLP: 문맥을 고려한 단어 Embedding

단어 임베딩의 한계

  • 동일한 글자의 단어도 문맥에 따라서 뜻이 다를 수 있음
  • 단순히 글자(심볼)을 특징 벡터로 변환하는 것 만으로는 문맥을 고려한 특징 벡터를 얻기 어려움

RNN

  • 입력의 시작과 끝을 BOS, EOS로 고정
  • 자신의 이전 단어들을 보고 다음 단어를 예측

ELMo

  • 대용량 말뭉치로 언어모델 학습
  • 정방향, 역방향 언어모델 동시 학습
  • 양방향 LM으로부터 나온 출력을 임베딩 벡터 대신 특징 벡터로 사용

RNN vs. Attenti

  • RNN은 이전 step의 hidden 벡터를 사용하여 다음 step의 hidden 벡터를 계산
  • 문장이 길어지면 과거 문장 전체 정보를 담기 어려움
  • Attention은 모든 step의 정보를 이용해서 hidden 벡터를 계산
    • $h_{t-1}$만 사용하는 RNN에 비해 모든 정보를 사용하므로 문맥을 더 잘 고려한 hidden 벡터 연산 가능
     
  • GPT: RNN 대신에 Attention (Transformer) 사용

BERT(Bidirectional Encoder Representations from Transformers)

  • 주변 단어들을 보고 중심 단어를 예측하는 방식으로 사전학습
  • 양방향 언어모델
  • MLM(Masked Language Model) - 현재 time step의 토큰을 예측
  • NSP(Next Sentence Prediction) - 문장의 관계를 이해하는 task에서 성능 개선

GPT Text Generation

# GPT 모델 로드
import torch
from transformers import GPT2LMHeadModel
from transformers import PreTrainedTokenizerFast

# Gradient False
# Pytorch에서 동작을 확안하기 위해서 Gradient 계산을 하지 않도록 설정
torch.set_grad_enabled(False)

# tokenizer loading
# skt에서 만든 kogpt2-base-v2 tokenizer 로드
tokenizer = PreTrainedTokenizerFast.from_pretrained("skt/kogpt2-base-v2",
                                                    bos_token='',
                                                    eos_token='',
                                                    unk_token='',
                                                    pad_token='',
                                                    mask_token='')
# 51,200개의 사전

# gpt2 model loading
# <https://github.com/SKT-AI/KoGPT2>
model = GPT2LMHeadModel.from_pretrained('skt/kogpt2-base-v2')                                                    

입력된 prompt를 tokenizer를 통해 토큰화 하고, 아래와 같이 input_id 형태로 변환

# 시작 문장 입력
prompt = '근육이 커지기 위해서는'
# 문장을 ID로 변환
input_ids = tokenizer.encode(prompt, return_tensors='pt')
input_ids

# tensor([[33245, 10114, 12748, 11357]])

tokenizer.convert_ids_to_tokens([33245, 10114, 12748, 11357])
# ['▁근육이', '▁커', '지기', '▁위해서는']

tokenizer.decode([33245, 10114, 12748, 11357])
# '근육이 커지기 위해서는'

미리 학습된 모델을 이용하여 입력한 프롬프트 뒤에 올 말을 생성함

# 입력 문장 뒤에올 문장 생성
gen_ids = model.generate(input_ids,
                         max_length=256,
                         repetition_penalty=2.0,
                         pad_token_id=tokenizer.pad_token_id,
                         eos_token_id=tokenizer.eos_token_id,
                         bos_token_id=tokenizer.bos_token_id,
                         use_cache=True)
gen_ids
# tensor([[33245, 10114, 12748, 11357, 23879, 39306,  9684,  7884, 10211, 15177,
#          26421,   387, 17339,  7889,  9908, 15768,  6903, 15386,  8146, 12923,
#           9228, 18651, 42600,  9564, 17764,  9033,  9199, 14441,  7335,  8704,
# ...

# 생성된 ID를 문자로 변환
generated = tokenizer.decode(gen_ids[0])
print(generated)

# 근육이 커지기 위해서는 무엇보다 규칙적인 생활습관이 중요하다.
# 특히, 아침식사는 단백질과 비타민이 풍부한 과일과 채소를 많이 섭취하는 것이 좋다.
# 또한 하루 30분 이상 충분한 수면을 취하는 것도 도움이 된다.
# 아침 식사를 거르지 않고 규칙적으로 운동을 하면 혈액순환에 도움을 줄 뿐만 아니라 신진대사를 촉진해 체내 노폐물을 배출하고 혈압을 낮춰준다.
# 운동은 하루에 10분 정도만 하는 게 좋으며 운동 후에는 반드시 스트레칭을 통해 근육량을 늘리고 유연성을 높여야 한다.
# 운동 후 바로 잠자리에 드는 것은 피해야 하며 특히 아침에 일어나면 몸이 피곤해지기 때문에 무리하게 움직이면 오히려 역효과가 날 수도 있다.
# 운동을 할 때는 몸을 따뜻하게 하고 땀은 잘 흡수하도록 해야 한다.</d> 지난달 30일 오후 서울 종로구 세종로 정부중앙청사 별관. 이낙연 국무총리가 주재하던 국무회의가 열렸다.
# 국무위원들이 모두 참석한 가운데 열린 이날 회의에서는 ‘경제혁신 3개년 계획’ 등 주요 국정 현안에 대한 논의가 이뤄졌다.
# 김동연(사진) 경제부총리 겸 기획재정부 장관은 “올해는 우리 경제가 회복세를 보일 것으로 예상된다”며 “이런 상황에서 정부가 추진하는 각종 정책들을 차질 없이 추진하기 위해 최선을 다하겠다”고 말했다.
# 그는 이어 “우리 경제의 성장세가 지속되고 있는 만큼 내년에도 경기회복을

프롬프트에 입력된 이후 문장을 id로 생성한 후, 생성된 id를 문자로 변환함

BERT Sentence Embedding

# corpus
corpus = [
    "지미 카터\\n제임스 얼 “지미” 카터 주니어(, 1924년 10월 1일~)는 민주당 출신 미국의 제39대 대통령 (1977-81)이다.\\n생애.\\n어린 시절.\\n지미 카터는 조지아주 섬터 카운티 플레인스 마을에서 태어났다.\\n조지아 공과대학교를 졸업하였다. 그 후 해군에 들어가 전함·원자력·잠수함의 승무원으로 일하였다. 1953년 미국 해군 대위로 예편하였고 이후 땅콩·면화 등을 가꿔 많은 돈을 벌었다. 그의 별명이 \\"땅콩 농부\\" (Peanut Farmer)로 알려졌다.\\n정계 입문.\\n1962년 조지아주 상원 의원 선거에서 낙선하였으나, 그 선거가 부정선거 였음을 입증하게 되어 당선되고, 1966년 조지아 주지사 선거에 낙선하지만, 1970년 조지아 주지사 선거에서 당선됐다. 대통령이 되기 전 조지아주 상원의원을 두번 연임했으며, 1971년부터 1975년까지 조지아 지사로 근무했다. 조지아 주지사로 지내면서, 미국에 사는 흑인 등용법을 내세웠다.\\n대통령 재임.\\n1976년 미합중국 제39대 대통령 선거에 민주당 후보로 출마하여 도덕주의 정책을 내세워서 많은 지지를 받았으며 제럴드 포드 대통령을 누르고 당선되었다.\\n카터 대통령은 에너지 개발을 촉구했으나 공화당의 반대로 무산되었다.",
    "수학\\n수학(, , math)은 수, 양, 구조, 공간, 변화 등의 개념을 다루는 학문이다. 널리 받아들여지는 명확한 정의는 없으나 현대 수학은 일반적으로 엄밀한 논리에 근거하여 추상적 대상을 탐구하며, 이는 규칙의 발견과 문제의 제시 및 해결의 과정으로 이루어진다. 수학은 그 발전 과정에 있어서 철학, 과학과 깊은 연관을 맺고 있으며, 엄밀한 논리와 특유의 추상성, 보편성에 의해 다른 학문들과 구별된다. 특히 수학은 과학의 여느 분야들과는 달리 자연계에서 관측되지 않는 개념들에 대해서까지 이론을 추상화시키는 특징을 보이는데, 수학자들은 그러한 개념들에 대한 추측을 제시하고 적절하게 선택된 정의와 공리로부터 엄밀한 연역을 거쳐 그 진위를 파악한다.\\n수학의 개념들은 기원전 600년 경에 활동하며 최초의 수학자로도 여겨지는 탈레스의 기록은 물론, 다른 고대 문명들에서도 찾아볼 수 있으며 인류의 문명과 함께 발전해 왔다. 오늘날 수학은 자연과학, 사회과학, 공학, 의학 등 거의 모든 학문에서도 핵심적인 역할을 하며 다양한 방식으로 응용된다.\\n수학을 의미하는 mathematics라는 단어는 '아는 모든 것', '배우는 모든 것'이라는 뜻의 고대 그리스어 'máthēma'(μάθημα) 및 그 활용형 mathēmatikós(μαθηματικός)에서 유래되었다.",
    "수학 상수\\n수학에서 상수란 그 값이 변하지 않는 불변량으로, 변수의 반대말이다. 물리 상수와는 달리, 수학 상수는 물리적 측정과는 상관없이 정의된다.\\n수학 상수는 대개 실수체나 복소수체의 원소이다. 우리가 이야기할 수 있는 상수는 (거의 대부분 계산 가능한) 정의가능한 수이다.\\n특정 수학 상수, 예를 들면 골롬-딕맨 상수, 프랑세즈-로빈슨 상수, formula_1, 레비 상수와 같은 상수는 다른 수학상수 또는 함수와 약한 상관관계 또는 강한 상관관계를 갖는다.",
    "문학\\n문학(文學, )은 언어를 예술적 표현의 제재로 삼아 새로운 의미를 창출하여, 인간과 사회를 진실되게 묘사하는 예술의 하위분야이다. 간단하게 설명하면, 언어를 통해 인간의 삶을 미적(美的)으로 형상화한 것이라고 볼 수 있다. 문학은 원래 문예(文藝)라고 부르는 것이 옳으며, 문학을 학문의 대상으로서 탐구하는 학문의 명칭 역시 문예학이다. 문예학은 음악사학, 미술사학 등과 함께 예술학의 핵심분야로서 인문학의 하위범주에 포함된다.\\n일반적으로 문학의 정의는 텍스트들의 집합이다. 각각의 국가들은 고유한 문학을 가질 수 있으며, 이는 기업이나 철학 조류, 어떤 특정한 역사적 시대도 마찬가지이다. 흔히 한 국가의 문학을 묶어서 분류한다. 예를 들어 고대 그리스어, 성서, 베오울프, 일리아드, 그리고 미국 헌법 등이 그러한 분류의 범주에 들어간다. 좀 더 일반적으로는 문학은 특정한 주제를 가진 이야기, 시, 희곡의 모음이라 할 수 있다. 이 경우, 이야기, 시, 그리고 희곡은 민족주의적인 색채를 띨 수도 아닐 수도 있다. 문학의 한 부분으로서 특정한 아이템을 구분 짓는 일은 매우 어려운 일이다. 어떤 사람들에게 \\"문학\\"은 어떠한 상징적인 기록의 형태로도 나타날 수 있는 것이다. (이를테면 이미지나 조각, 또는 문자로도 나타날 수 있다.) 그러나 또다른 사람들에게 있어 문학은 오직 문자로 이루어진 텍스트로 구성된 것만을 포함한다. 좀 더 보수적인 사람들은 그 개념이 꼭 물리적인 형태를 가진 텍스트여야 하고, 대개 그러한 형태는 종이 등의 눈에 보이는 매체에서 디지털 미디어까지 다양할 수 있다.",
    "화학\\n화학(化學)은 물질의 성질, 조성, 구조, 변화 및 그에 수반하는 에너지의 변화를 연구하는 자연과학(自然科學)의 한 분야이다. 물리학(物理學)도 역시 물질을 다루는 학문이지만, 물리학이 원소(元素)와 화합물(化合物)을 모두 포함한 물체의 운동과 에너지, 열적·전기적·광학적·기계적 속성을 다루고 이러한 현상으로부터 통일된 이론을 구축하려는 것과는 달리 화학에서는 물질 자체를 연구 대상으로 한다. 화학은 이미 존재하는 물질을 이용하여 특정한 목적에 맞는 새로운 물질을 합성하는 길을 제공하며, 이는 농작물(農作物)의 증산, 질병의 치료 및 예방, 에너지 효율 증대, 환경오염(環境汚染) 감소 등 여러 가지 이점을 제공한다.\\n어원.\\n화학은 연금술사들이 물질을 섞으며 발전시켰기 때문에 화학을 뜻하는 영어 ‘케미스트리(chemistry)’는 연금술을 뜻하는 단어 ‘알케미(alchemy)’에서 비롯하였다. 이는 다시 아랍어 ‘알 키미야(, al-kīmiyāʾ)’에서 왔는데, 이 단어의 어원에 대해서는 여러 가지 설이 있다.\\n‘화학(化學)’이란 단어는 물질의 변화를 다루는 학문이라는 점에 착안한 번역어이다. 이 번역어는 의 《항해술기(航海述奇)》(1866), 의 자연과학 교과서 《격물입문(格物入門)》(1866) 등에서 처음 쓰였다.\\n역사.\\n고대 화학(古代化學)",
]

# 기존 학습된 SentenceBERT 모델 생성
model = SentenceTransformer('snunlp/KR-SBERT-V40K-klueNLI-augSTS')

# corpus embeddings
corpus_embeddings = model.encode(corpus, normalize_embeddings=True)
corpus_embeddings.shape
# (5, 768) -> 5개의 문서를 각각 768 size로 임베딩 

# query
query = "국무총리"
# query embedding
query_embedding = model.encode(query, normalize_embeddings=True)
query_embedding.shape
# (768,)

# dot score 계산
scores = np.dot(corpus_embeddings, query_embedding)
scores
# array([0.13264951, 0.02323388, 0.05651819, 0.06247297, 0.09593113], dtype=float32)
      
np.argsort(-scores)
# array([0, 4, 3, 2, 1], dtype=int64)
# '국무총리'와 가장 유사한 문서는 0번 째 문서

 

'Study > GenAI' 카테고리의 다른 글

[GenAI] LLM 종류  (1) 2024.11.13
[VectorDB] 벡터DB란  (0) 2024.08.22