임베딩으로 의미론적 검색하기
임베딩으로 의미론적 검색하기
Embed 엔드포인트를 사용해 의미론적 검색을 수행하는 방법에 대한 예시 문서예요 (API v2).
출처: 문서
본문
이 섹션은 Embed 엔드포인트를 사용해 의미론적 검색을 수행하는 방법에 대한 예시를 제공해요.
의미론적 검색은 키워드 일치를 찾는 데 뛰어나지만 텍스트 조각의 맥락이나 의미를 포착하는 데 어려움을 겪는, 더 전통적인 어휘 검색(lexical search) 접근 방식이 직면한 문제를 해결합니다.
PYTHON
import cohere
import numpy as np
co = cohere.ClientV2(
api_key="YOUR_API_KEY"
) # Get your free API key: https://dashboard.cohere.com/api-keys
Embed 엔드포인트는 텍스트를 입력으로 받아 임베딩을 출력으로 반환해요.
의미론적 검색을 위해서는 임베딩으로 변환해야 할 두 가지 유형의 문서가 있어요.
- 검색할 문서 목록.
- 문서를 검색하는 데 사용될 쿼리.
1단계: 문서 임베딩
co.embed()를 사용해 Embed 엔드포인트를 호출하고 필요한 인자를 전달합니다:
texts: 텍스트 목록model: 여기서는embed-v4.0을 선택합니다input_type: 검색 시 이들을 문서로 취급하도록search_document를 선택해요embedding_types: 출력으로 float 배열을 얻기 위해float를 선택합니다
2단계: 쿼리 임베딩
다음으로, 쿼리를 추가하고 임베딩합니다. 검색 시 이 텍스트를 (문서가 아닌) 쿼리로 취급하도록 input_type으로 search_query를 선택해요.
3단계: 가장 유사한 문서 반환
다음으로, 쿼리와 문서 임베딩 사이의 유사성 점수를 계산하고 정렬한 다음, 상위 N개의 가장 유사한 문서를 표시해요. 여기서는 내적(dot product) 접근 방식을 사용해 유사성을 계산하기 위해 numpy 라이브러리를 사용합니다.
PYTHON
### STEP 1: Embed the documents
# Define the documents
documents = [
"Joining Slack Channels: You will receive an invite via email. Be sure to join relevant channels to stay informed and engaged.",
"Finding Coffee Spots: For your caffeine fix, head to the break room's coffee machine or cross the street to the café for artisan coffee.",
"Team-Building Activities: We foster team spirit with monthly outings and weekly game nights. Feel free to suggest new activity ideas anytime!",
"Working Hours Flexibility: We prioritize work-life balance. While our core hours are 9 AM to 5 PM, we offer flexibility to adjust as needed.",
]
# Constructing the embed_input object
embed_input = [
{"content": [{"type": "text", "text": doc}]} for doc in documents
]
# Embed the documents
doc_emb = co.embed(
inputs=embed_input,
model="embed-v4.0",
output_dimension=1024,
input_type="search_document",
embedding_types=["float"],
).embeddings.float
### STEP 2: Embed the query
# Add the user query
query = "How to connect with my teammates?"
query_input = [{"content": [{"type": "text", "text": query}]}]
# Embed the query
query_emb = co.embed(
inputs=query_input,
model="embed-v4.0",
input_type="search_query",
output_dimension=1024,
embedding_types=["float"],
).embeddings.float
### STEP 3: Return the most similar documents
# Calculate similarity scores
scores = np.dot(query_emb, np.transpose(doc_emb))[0]
# Sort and filter documents based on scores
top_n = 2
top_doc_idxs = np.argsort(-scores)[:top_n]
# Display search results
for idx, docs_idx in enumerate(top_doc_idxs):
print(f"Rank: {idx+1}")
print(f"Document: {documents[docs_idx]}\n")
cURL
# Step 1: Embed the documents
curl --request POST \
--url https://api.cohere.ai/v2/embed \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header "Authorization: bearer ***" \
--data '{
"model": "embed-v4.0",
"input_type": "search_document",
"embedding_types": ["float"],
"output_dimension": 1024,
"inputs": [
{
"content": [
{
"type": "text",
"text": "Joining Slack Channels: You will receive an invite via email. Be sure to join relevant channels to stay informed and engaged."
}
]
},
{
"content": [
{
"type": "text",
"text": "Finding Coffee Spots: For your caffeine fix, head to the break room'\''s coffee machine or cross the street to the café for artisan coffee."
}
]
},
{
"content": [
{
"type": "text",
"text": "Team-Building Activities: We foster team spirit with monthly outings and weekly game nights. Feel free to suggest new activity ideas anytime!"
}
]
},
{
"content": [
{
"type": "text",
"text": "Working Hours Flexibility: We prioritize work-life balance. While our core hours are 9 AM to 5 PM, we offer flexibility to adjust as needed."
}
]
}
]
}'
# Step 2: Embed the query
curl --request POST \
--url https://api.cohere.ai/v2/embed \
--header 'accept: application/json' \
--header 'content-type: application/json' \
--header "Authorization: bearer ***" \
--data '{
"model": "embed-v4.0",
"input_type": "search_query",
"embedding_types": ["float"],
"output_dimension": 1024,
"inputs": [
{
"content": [
{
"type": "text",
"text": "How to connect with my teammates?"
}
]
}
]
}'
다음은 예제 출력이에요:
Rank: 1
Document: Team-Building Activities: We foster team spirit with monthly outings and weekly game nights. Feel free to suggest new activity ideas anytime!
Rank: 2
Document: Joining Slack Channels: You will receive an invite via email. Be sure to join relevant channels to stay informed and engaged.
Embed v4로 콘텐츠 품질 측정하기
표준 텍스트 임베딩 모델은 쿼리와 후보 문서 사이의 주제 유사성만을 위해 최적화되어 있어요. 하지만 많은 실제 애플리케이션에서는 다양한 콘텐츠 품질을 가진 중복 정보가 있습니다.
예를 들어 사용자 쿼리인 "COVID-19 Symptoms"를 후보 문서인 "COVID-19 has many symptoms"와 비교해 보세요. 이 문서는 고품질이고 풍부한 정보를 제공하지 않아요. 하지만 일반적인 임베딩 모델에서는 쿼리와 매우 유사하기 때문에 검색 결과 상위에 나타납니다.
Embed v4 모델은 콘텐츠 품질과 주제 유사성 모두를 포착하도록 학습됐어요. 이 접근 방식을 통해 검색 시스템은 문서에서 더 풍부한 정보를 추출하고 노이즈에 견고할 수 있습니다.
아래 예시처럼 쿼리("COVID-19 Symptoms")가 주어지면, 가장 높은 품질의 문서("COVID-19 symptoms can include: a high temperature or shivering...")가 1위로 순위가 매겨집니다.
다른 문서("COVID-19 has many symptoms")는 포함한 정보를 기준으로 보면 쿼리와 더 유사하다고 볼 수 있지만, 그렇게 많은 정보를 포함하지 않아 더 낮게 순위가 매겨져요.
이것은 Embed v4가 주어진 쿼리에 대해 고품질 문서를 표면화하는 데 어떻게 도움이 되는지 보여줍니다.
PYTHON
### STEP 1: Embed the documents
documents = [
"COVID-19 has many symptoms.",
"COVID-19 symptoms are bad.",
"COVID-19 symptoms are not nice",
"COVID-19 symptoms are bad. 5G capabilities include more expansive service coverage, a higher number of available connections, and lower power consumption.",
"COVID-19 is a disease caused by a virus. The most common symptoms are fever, chills, and sore throat, but there are a range of others.",
"COVID-19 symptoms can include: a high temperature or shivering (chills); a new, continuous cough; a loss or change to your sense of smell or taste; and many more",
"Dementia has the following symptom: Experiencing memory loss, poor judgment, and confusion.",
"COVID-19 has the following symptom: Experiencing memory loss, poor judgment, and confusion.",
]
# Constructing the embed_input object
embed_input = [
{"content": [{"type": "text", "text": doc}]} for doc in documents
]
# Embed the documents
doc_emb = co.embed(
inputs=embed_input,
model="embed-v4.0",
output_dimension=1024,
input_type="search_document",
embedding_types=["float"],
).embeddings.float
### STEP 2: Embed the query
# Add the user query
query = "COVID-19 Symptoms"
query_input = [{"content": [{"type": "text", "text": query}]}]
# Embed the query
query_emb = co.embed(
inputs=query_input,
model="embed-v4.0",
input_type="search_query",
output_dimension=1024,
embedding_types=["float"],
).embeddings.float
### STEP 3: Return the most similar documents
# Calculate similarity scores
scores = np.dot(query_emb, np.transpose(doc_emb))[0]
# Sort and filter documents based on scores
top_n = 5
top_doc_idxs = np.argsort(-scores)[:top_n]
# Display search results
for idx, docs_idx in enumerate(top_doc_idxs):
print(f"Rank: {idx+1}")
print(f"Document: {documents[docs_idx]}\n")
다음은 샘플 출력이에요:
Rank: 1
Document: COVID-19 symptoms can include: a high temperature or shivering (chills); a new, continuous cough; a loss or change to your sense of smell or taste; and many more
Rank: 2
Document: COVID-19 is a disease caused by a virus. The most common symptoms are fever, chills, and sore throat, but there are a range of others.
Rank: 3
Document: COVID-19 has the following symptom: Experiencing memory loss, poor judgment, and confusion.
Rank: 4
Document: COVID-19 has many symptoms.
Rank: 5
Document: COVID-19 symptoms are not nice
다국어 의미론적 검색(Multilingual semantic search)
Embed 엔드포인트는 embed-v4.0 및 이전 embed-multilingual-... 모델을 통해 다국어 의미론적 검색도 지원해요. 이는 서로 다른 언어의 텍스트에 대해 의미론적 검색을 수행할 수 있다는 뜻입니다.
구체적으로, 단 하나의 모델로 다국어(multilingual) 및 교차 언어(cross-lingual) 검색을 모두 수행할 수 있어요.
PYTHON
### STEP 1: Embed the documents
documents = [
"Remboursement des frais de voyage : Gérez facilement vos frais de voyage en les soumettant via notre outil financier. Les approbations sont rapides et simples.",
"Travailler de l'étranger : Il est possible de travailler à distance depuis un autre pays. Il suffit de coordonner avec votre responsable et de vous assurer d'être disponible pendant les heures de travail.",
"Avantages pour la santé et le bien-être : Nous nous soucions de votre bien-être et proposons des adhésions à des salles de sport, des cours de yoga sur site et une assurance santé complète.",
"Fréquence des évaluations de performance : Nous organisons des bilans informels tous les trimestres et des évaluations formelles deux fois par an.",
]
# Constructing the embed_input object
embed_input = [
{"content": [{"type": "text", "text": doc}]} for doc in documents
]
# Embed the documents
doc_emb = co.embed(
inputs=embed_input,
model="embed-v4.0",
output_dimension=1024,
input_type="search_document",
embedding_types=["float"],
).embeddings.float
### STEP 2: Embed the query
# Add the user query
query = "What's your remote-working policy?"
query_input = [{"content": [{"type": "text", "text": query}]}]
# Embed the query
query_emb = co.embed(
inputs=query_input,
model="embed-v4.0",
input_type="search_query",
output_dimension=1024,
embedding_types=["float"],
).embeddings.float
### STEP 3: Return the most similar documents
# Calculate similarity scores
scores = np.dot(query_emb, np.transpose(doc_emb))[0]
# Sort and filter documents based on scores
top_n = 4
top_doc_idxs = np.argsort(-scores)[:top_n]
# Display search results
for idx, docs_idx in enumerate(top_doc_idxs):
print(f"Rank: {idx+1}")
print(f"Document: {documents[docs_idx]}\n")
다음은 샘플 출력이에요:
Rank: 1
Document: Travailler de l'étranger : Il est possible de travailler à distance depuis un autre pays. Il suffit de coordonner avec votre responsable et de vous assurer d'être disponible pendant les heures de travail.
Rank: 2
Document: Avantages pour la santé et le bien-être : Nous nous soucions de votre bien-être et proposons des adhésions à des salles de sport, des cours de yoga sur site et une assurance santé complète.
Rank: 3
Document: Fréquence des évaluations de performance : Nous organisons des bilans informels tous les trimestres et des évaluations formelles deux fois par an.
Rank: 4
Document: Remboursement des frais de voyage : Gérez facilement vos frais de voyage en les soumettant via notre outil financier. Les approbations sont rapides et simples.
멀티모달 PDF 검색(Multimodal PDF search)
텍스트, 이미지, 레이아웃 정보가 섞여 있는 경우가 많은 PDF 파일을 다루는 것은 전통적인 임베딩 방법에 도전 과제를 제시해요. 이는 보통 멀티모달 생성 모델이 임베딩 모델에 적합한 형식으로 문서를 사전 처리해야 합니다. 이 중간 텍스트 표현은 표나 복잡한 레이아웃의 구조와 정확한 콘텐츠가 정확하게 렌더링되지 않을 수 있는 등 중요한 정보를 잃을 수 있어요.
Embed v4는 혼합 모달리티 입력을 기본적으로 이해하도록 설계되어 이 문제를 해결합니다. Embed v4는 텍스트와 이미지를 포함한 PDF 콘텐츠를 단일 단계로 직접 처리할 수 있어요. 텍스트적 요소와 시각적 요소 모두에서 파생된 의미론적 의미를 포착하는 통합 임베딩을 생성합니다.
다음은 Embed 엔드포인트를 사용해 멀티모달 PDF 검색을 수행하는 방법의 예시예요.
먼저 필요한 라이브러리를 임포트합니다.
PYTHON
from pdf2image import convert_from_path
from io import BytesIO
import base64
import chromadb
import cohere
다음으로, PDF 파일을 페이지당 하나의 이미지 목록으로 변환합니다. 그런 다음 이 이미지들을 Embed 엔드포인트가 기대하는 콘텐츠 구조로 포맷해요.
PYTHON
pdf_path = "PDF_FILE_PATH" # https://github.com/cohere-ai/cohere-developer-experience/raw/main/notebooks/guide/embed-v4-pdf-search/data/Samsung_Home_Theatre_HW-N950_ZA_FullManual_02_ENG_180809_2.pdf
pages = convert_from_path(pdf_path, dpi=200)
input_array = []
for page in pages:
buffer = BytesIO()
page.save(buffer, format="PNG")
base64_str = base64.b64encode(buffer.getvalue()).decode("utf-8")
base64_image = f"data:image/png;base64,{base64_str}"
page_entry = {
"content": [
{"type": "text", "text": f"{pdf_path}"},
{"type": "image_url", "image_url": {"url": base64_image}},
]
}
input_array.append(page_entry)
다음으로, 이 페이지들에 대한 임베딩을 생성하고 벡터 데이터베이스(이 예시에서는 Chroma를 사용)에 저장합니다.
PYTHON
# Generate the document embeddings
embeddings = []
for i in range(0, len(input_array)):
res = co.embed(
model="embed-v4.0",
input_type="search_document",
embedding_types=["float"],
inputs=[input_array[i]],
).embeddings.float[0]
embeddings.append(res)
# Store the embeddings in a vector database
ids = []
for i in range(0, len(input_array)):
ids.append(str(i))
chroma_client = chromadb.Client()
collection = chroma_client.create_collection("pdf_pages")
collection.add(
embeddings=embeddings,
ids=ids,
)
마지막으로, 쿼리를 제공하고 문서에 대해 검색을 실행합니다. 이는 쿼리와 가장 유사한 페이지를 나타내는 정렬된 ID 목록을 반환합니다.
PYTHON
query = "Do the speakers come with an optical cable?"
# Generate the query embedding
query_embeddings = co.embed(
model="embed-v4.0",
input_type="search_query",
embedding_types=["float"],
texts=[query],
).embeddings.float[0]
# Search the vector database
results = collection.query(
query_embeddings=[query_embeddings],
n_results=5, # Define the top_k value
)
# Print the id of the top-ranked page
print(results["ids"][0][0])
22
가장 상위에 랭크된 페이지는 아래와 같아요:

참고(Note)
멀티모달 PDF 검색의 더 완전한 예시는 cookbook 버전을 참조하세요.