Skip to content

검색

순차 검색

import pandas as pd
df = pd.read_csv('neurips.zip')
df.head()
query = {'natural', 'language'}

import re
def tokenize(text):
    text = text.lower() # 소문자로 변환
    return re.findall(r'\w{2,}', text) # 2글자 이상 단어 추출

표의 각 행에서 순서대로 검색어가 있는지 확인

%%time
results = []
for row in df.itertuples():
    words = set(tokenize(row.abstract))
    if query < words: # 검색어가 부분집합이면
        results.append(row.Index)
CPU times: user 215 ms, sys: 0 ns, total: 215 ms
Wall time: 215 ms

조건에 맞는 행 번호

results

조건에 맞는 행 보기

df.loc[results]

리스트와 사전

a = list(range(1000000))

리스트에서 999999를 검색하는데 걸리는 시간 측정 리스트의 뒤로 갈 수록 검색이 오래 걸림

%%time
a.index(999999)
CPU times: user 21 ms, sys: 0 ns, total: 21 ms
Wall time: 22.2 ms
999999
b = dict(zip(a, a))

검색 시간이 0에 가까움

%%time
b[999999]
CPU times: user 6 µs, sys: 1e+03 ns, total: 7 µs
Wall time: 11.4 µs
999999

인덱싱

from collections import defaultdict
index = defaultdict(set)

for row in df.itertuples():
    words = tokenize(row.abstract)
    for word in words:
        index[word].add(row.Index)
index['language']
%%time
results = list(index['natural'] & index['language'])
CPU times: user 33 µs, sys: 4 µs, total: 37 µs
Wall time: 41.7 µs

TF

from collections import Counter
idxs = list(index['natural'] & index['language'])
results = []
for row in df.iloc[idxs].itertuples():
    words = tokenize(row.abstract)
    cnt = Counter(words)
    tf = sum(cnt[w] for w in query)
    results.append((tf, row.Index))

점수의 역순으로 정렬

idx = [i for _, i in sorted(results, reverse=True)]

정렬된 문서 보기

df.iloc[idx]

TF-IDF

문서 빈도

{k: len(v) for k, v in index.items()}

전체 문서 수

N, _ = df.shape

역문서빈도(inverse document frequency)

import numpy as np
idf = {k: np.log(N / len(v)) for k, v in index.items()}

idxs = list(index['natural'] & index['language'])
results = []

for row in df.iloc[idxs].itertuples():
    words = tokenize(row.abstract)
    cnt = Counter(words)
    tfidf = sum(cnt[w] * idf[w] for w in query)
    results.append((tfidf, row.Index))

idx = [i for _, i in sorted(results, reverse=True)]

BM25

!pip install rank_bm25 kiwipiepy
import pandas as pd
books = pd.read_csv('science_books.csv')
from kiwipiepy import Kiwi
kiwi = Kiwi()


def tokenize(sent):
    for token in kiwi.tokenize(sent):
        if token.tag in {'NNG', 'NNP', 'SL', 'VV', 'VA'}:
            yield token.form, token.tag
tokenized_corpus = []
for title in books.제목:
    tokenized_corpus.append(list(tokenize(title)))
from rank_bm25 import BM25Okapi
bm25 = BM25Okapi(tokenized_corpus)
import pandas as pd
idf_table = pd.DataFrame(bm25.idf.items(), columns=['token', 'idf'])
idf_table.sort_values('idf')
query = list(tokenize('다정한 것이 살아남는다'))
bm25.get_top_n(query, books.제목, n=5)
['다정한 것이 살아남는다 : 친화력으로 세상을 바꾸는 인류의 진화에 관하여(10만부 기념 스페셜 에디션, 저자 친필 사인 인쇄본)',
 '낙타는 왜 사막으로 갔을까 : 살아남은 동물들의 비밀',
 '무엇이 우리를 다정하게 만드는가 : 타인을 도우려 하는 인간 심리의 뇌과학적 비밀(양장)',
 '우주에서 기다릴게 : 한국 첫 우주인이 펼치는 다정한 호기심의 기록',
 '다정한 물리학 : 거대한 우주와 물질의 기원을 탐구하고 싶을 때']

임베딩을 이용한 검색

!pip install sentence-transformers
from sentence_transformers import SentenceTransformer
sbert = SentenceTransformer(
    'snunlp/KR-SBERT-V40K-klueNLI-augSTS')
from sklearn.metrics.pairwise import cosine_similarity
emb = sbert.encode(books.제목)

query_emb = sbert.encode(['다정한 것이 살아남는다'])
from sklearn.metrics.pairwise import cosine_similarity
import numpy as np
다정함의 과학 : 친절, 신뢰, 공감 속에 숨어 있는 건강과 행복의 비밀
다정한 것이 살아남는다 : 친화력으로 세상을 바꾸는 인류의 진화에 관하여(10만부 기념 스페셜 에디션, 저자 친필 사인 인쇄본)
모든 것은 그 자리에 : 첫사랑부터 마지막 이야기까지(양장)
이토록 다정한 기술 : 지구와 이웃을 보듬는 아이디어(〈희망의 이유〉 사쉐 증정 (포인트 차감))
무엇이 우리를 다정하게 만드는가 : 타인을 도우려 하는 인간 심리의 뇌과학적 비밀(양장)
%%time
sims = cosine_similarity(query_emb, emb)
ids = np.argsort(-sims[0])[:5]
CPU times: user 6.84 ms, sys: 7.09 ms, total: 13.9 ms
Wall time: 30.9 ms
books.iloc[ids]
제목
322 다정함의 과학 : 친절, 신뢰, 공감 속에 숨어 있는 건강과 행복의 비밀
6 다정한 것이 살아남는다 : 친화력으로 세상을 바꾸는 인류의 진화에 관하여(10만부 ...
618 모든 것은 그 자리에 : 첫사랑부터 마지막 이야기까지(양장)
392 이토록 다정한 기술 : 지구와 이웃을 보듬는 아이디어(〈희망의 이유〉 사쉐 증정 (...
111 무엇이 우리를 다정하게 만드는가 : 타인을 도우려 하는 인간 심리의 뇌과학적 비밀(양장)

nmslib

pip install nmslib
import nmslib
index = nmslib.init()
index.addDataPointBatch(emb)
index.createIndex()
%%time
ids, dist = index.knnQuery(query_emb, k=5)
CPU times: user 1.38 ms, sys: 0 ns, total: 1.38 ms
Wall time: 1.48 ms
books.iloc[ids]
제목
322 다정함의 과학 : 친절, 신뢰, 공감 속에 숨어 있는 건강과 행복의 비밀
6 다정한 것이 살아남는다 : 친화력으로 세상을 바꾸는 인류의 진화에 관하여(10만부 ...
618 모든 것은 그 자리에 : 첫사랑부터 마지막 이야기까지(양장)
392 이토록 다정한 기술 : 지구와 이웃을 보듬는 아이디어(〈희망의 이유〉 사쉐 증정 (...
111 무엇이 우리를 다정하게 만드는가 : 타인을 도우려 하는 인간 심리의 뇌과학적 비밀(양장)

chroma

!pip install chromadb
import chromadb
client = chromadb.Client()
collection = client.create_collection(
    name="science_books", 
    embedding_function=sbert.encode)
metadatas = books.제목.map(lambda x: {'length': len(x)}).tolist()
ids = books.index.map(str).tolist()
collection.add(
    documents=books.제목.tolist(),
    metadatas=metadatas,
    ids=ids
)
results = collection.query(
    query_texts=["다정한 것이 살아남는다"],
    n_results=5
)
CPU times: user 93.4 ms, sys: 973 µs, total: 94.3 ms
Wall time: 98.8 ms
%%time
results = collection.query(
    query_embeddings=[query_emb[0]],
    n_results=5
)
CPU times: user 3.76 ms, sys: 0 ns, total: 3.76 ms
Wall time: 3.96 ms
query_emb.shape
(1, 768)
results
{'ids': [['6', '322', '392', '111', '313']],
 'embeddings': None,
 'documents': [['다정한 것이 살아남는다 : 친화력으로 세상을 바꾸는 인류의 진화에 관하여(10만부 기념 스페셜 에디션, 저자 친필 사인 인쇄본)',
   '다정함의 과학 : 친절, 신뢰, 공감 속에 숨어 있는 건강과 행복의 비밀',
   '이토록 다정한 기술 : 지구와 이웃을 보듬는 아이디어(〈희망의 이유〉 사쉐 증정 (포인트 차감))',
   '무엇이 우리를 다정하게 만드는가 : 타인을 도우려 하는 인간 심리의 뇌과학적 비밀(양장)',
   'ADHD 2.0 : 산만하고 변덕스러운 ‘나’를 뛰어난 ‘창조자’로 바꾸는 특별한 여정!(포함 건강취미분야 2만원↑ 데일리 알약케이스 증정(택1, 포인트 차감))']],
 'metadatas': [[{'length': 71},
   {'length': 40},
   {'length': 54},
   {'length': 49},
   {'length': 90}]],
 'distances': [[305.2731018066406,
   332.41082763671875,
   351.1950988769531,
   361.7748718261719,
   384.9884033203125]]}
results = collection.query(
    query_texts=["다정한 것이 살아남는다"],
    n_results=5,
    where={"length": {'$lt': 75}},
    where_document={"$contains":"다정"}
)
results
{'ids': [['6', '322', '392', '111', '105']],
 'embeddings': None,
 'documents': [['다정한 것이 살아남는다 : 친화력으로 세상을 바꾸는 인류의 진화에 관하여(10만부 기념 스페셜 에디션, 저자 친필 사인 인쇄본)',
   '다정함의 과학 : 친절, 신뢰, 공감 속에 숨어 있는 건강과 행복의 비밀',
   '이토록 다정한 기술 : 지구와 이웃을 보듬는 아이디어(〈희망의 이유〉 사쉐 증정 (포인트 차감))',
   '무엇이 우리를 다정하게 만드는가 : 타인을 도우려 하는 인간 심리의 뇌과학적 비밀(양장)',
   '다정한 물리학 : 거대한 우주와 물질의 기원을 탐구하고 싶을 때']],
 'metadatas': [[{'length': 71},
   {'length': 40},
   {'length': 54},
   {'length': 49},
   {'length': 35}]],
 'distances': [[305.2731018066406,
   332.41082763671875,
   351.1950988769531,
   361.7748718261719,
   475.311279296875]]}