dbpedia 데이터셋
dbpedia 데이터셋
위키백과 기사 100만 개와 그 벡터 임베딩을 담은 데이터셋입니다. 벡터 임베딩, 유사도 검색(semantic search), 생성형 AI를 이해하기 위한 훌륭한 시작 데이터셋으로, ClickHouse의 근사 최근접 이웃 검색(ANN)과 Q&A 애플리케이션을 실습할 수 있어요.
출처: 문서
본문
dbpedia 데이터셋은 위키백과 기사 100만 개와 OpenAI의 text-embedding-3-large 모델로 생성된 벡터 임베딩을 포함합니다.
이 데이터셋은 벡터 임베딩, 벡터 유사도 검색, 생성형 AI를 이해하기 위한 훌륭한 시작 데이터셋입니다. 이 데이터셋으로 ClickHouse의 근사 최근접 이웃 검색과 간단하지만 강력한 Q&A 애플리케이션을 시연합니다.
데이터셋 상세 (Dataset details)
이 데이터셋은 huggingface.co에 있는 26개의 Parquet 파일을 포함합니다. 파일 이름은 0.parquet, 1.parquet, …, 25.parquet입니다. 데이터셋의 몇 가지 예제 행을 보려면 이 Hugging Face 페이지를 방문하세요.
테이블 생성 (Create table)
기사 id, 제목, 본문, 임베딩 벡터를 저장할 dbpedia 테이블을 만듭니다:
CREATE TABLE dbpedia
(
id String,
title String,
text String,
vector Array(Float32) CODEC(NONE)
) ENGINE = MergeTree ORDER BY (id);
테이블 로드 (Load table)
모든 Parquet 파일에서 데이터셋을 로드하려면 다음 셸 명령을 실행하세요:
for i in $(seq 0 25); do
echo "Processing file ${i}..."
clickhouse client -q "INSERT INTO dbpedia SELECT _id, title, text, \"text-embedding-3-large-1536-embedding\" FROM url('https://huggingface.co/api/datasets/Qdrant/dbpedia-entities-openai3-text-embedding-3-large-1536-1M/parquet/default/train/${i}.parquet') SETTINGS max_http_get_redirects=5,enable_url_encoding=0;"
echo "File ${i} complete."
done
또는 아래와 같이 개별 SQL 문을 실행해 26개 Parquet 파일 각각을 로드할 수 있습니다:
INSERT INTO dbpedia SELECT _id, title, text, "text-embedding-3-large-1536-embedding" FROM url('https://huggingface.co/api/datasets/Qdrant/dbpedia-entities-openai3-text-embedding-3-large-1536-1M/parquet/default/train/0.parquet') SETTINGS max_http_get_redirects=5,enable_url_encoding=0;
INSERT INTO dbpedia SELECT _id, title, text, "text-embedding-3-large-1536-embedding" FROM url('https://huggingface.co/api/datasets/Qdrant/dbpedia-entities-openai3-text-embedding-3-large-1536-1M/parquet/default/train/1.parquet') SETTINGS max_http_get_redirects=5,enable_url_encoding=0;
...
INSERT INTO dbpedia SELECT _id, title, text, "text-embedding-3-large-1536-embedding" FROM url('https://huggingface.co/api/datasets/Qdrant/dbpedia-entities-openai3-text-embedding-3-large-1536-1M/parquet/default/train/25.parquet') SETTINGS max_http_get_redirects=5,enable_url_encoding=0;
dbpedia 테이블에 100만 행이 있는지 확인합니다:
SELECT count(*)
FROM dbpedia
┌─count()─┐
1. │ 1000000 │
└─────────┘
유사도 검색 (Semantic search)
권장 자료: "Vector embeddings" OpenAPI 가이드
벡터 임베딩을 사용하는 유사도 검색(또는 유사도 검색, similarity search)은 다음 단계를 포함합니다:
- 사용자로부터 자연어 검색 쿼리를 받습니다. 예: "Tell me about some scenic rail journeys", "Suspense novels set in Europe" 등
- LLM 모델로 검색 쿼리의 임베딩 벡터를 생성합니다
- 데이터셋에서 검색 임베딩 벡터의 최근접 이웃을 찾습니다
*최근접 이웃(nearest neighbours)*은 사용자 쿼리와 관련된 결과인 문서, 이미지 또는 콘텐츠입니다. 검색된 결과는 생성형 AI 애플리케이션에서 Retrieval Augmented Generation (RAG)의 핵심 입력입니다.
브루트포스 벡터 유사도 검색 실행 (Run a brute-force vector similarity search)
KNN(k-Nearest Neighbours) 검색 또는 브루트포스 검색은 데이터셋의 각 벡터와 검색 임베딩 벡터 사이의 거리를 계산한 뒤, 거리를 정렬해 최근접 이웃을 구하는 방식입니다. dbpedia 데이터셋에서 유사도 검색을 시각적으로 관찰하는 빠른 기법은 데이터셋 자체의 임베딩 벡터를 검색 벡터로 사용하는 것입니다. 예:
Query
SELECT id, title
FROM dbpedia
ORDER BY cosineDistance(vector, ( SELECT vector FROM dbpedia WHERE id = '<dbpedia:The_Remains_of_the_Day>') ) ASC
LIMIT 20
Response
┌─id────────────────────────────────────────┬─title───────────────────────────┐
1. │ <dbpedia:The_Remains_of_the_Day> │ The Remains of the Day │
2. │ <dbpedia:The_Remains_of_the_Day_(film)> │ The Remains of the Day (film) │
3. │ <dbpedia:Never_Let_Me_Go_(novel)> │ Never Let Me Go (novel) │
4. │ <dbpedia:Last_Orders> │ Last Orders │
5. │ <dbpedia:The_Unconsoled> │ The Unconsoled │
6. │ <dbpedia:The_Hours_(novel)> │ The Hours (novel) │
7. │ <dbpedia:An_Artist_of_the_Floating_World> │ An Artist of the Floating World │
8. │ <dbpedia:Heat_and_Dust> │ Heat and Dust │
9. │ <dbpedia:A_Pale_View_of_Hills> │ A Pale View of Hills │
10. │ <dbpedia:Howards_End_(film)> │ Howards End (film) │
... (원문에 20개 결과 전체 포함)
└───────────────────────────────────────────┴─────────────────────────────────┘
20 rows in set. Elapsed: 0.261 sec. Processed 1.00 million rows, 6.22 GB (3.84 million rows/s., 23.81 GB/s.)
쿼리 지연 시간을 기록해 두어 ANN(벡터 인덱스 사용) 쿼리 지연 시간과 비교하세요. 또한 cold OS 파일 캐시 상태와 max_threads=1에서의 쿼리 지연 시간을 기록해 실제 컴퓨팅 사용량과 저장 대역폭 사용량을 파악하세요(수백만 벡터가 있는 프로덕션 데이터셋으로 외삽해 보세요!).
벡터 유사도 인덱스 구축 (Build a vector similarity index)
다음 SQL을 실행해 vector 컬럼에 벡터 유사도 인덱스를 정의하고 구축하세요:
ALTER TABLE dbpedia ADD INDEX vector_index vector TYPE vector_similarity('hnsw', 'cosineDistance', 1536, 'bf16', 64, 512);
ALTER TABLE dbpedia MATERIALIZE INDEX vector_index SETTINGS mutations_sync = 2;
인덱스 생성과 검색의 매개변수 및 성능 고려사항은 문서에 설명되어 있습니다.
인덱스 구축과 저장은 사용 가능한 CPU 코어 수와 저장 대역폭에 따라 몇 분이 걸릴 수 있습니다.
ANN 검색 수행 (Perform ANN search)
근사 최근접 이웃(Approximate Nearest Neighbours) 또는 ANN은 (그래프·랜덤 포레스트 같은 특수 데이터 구조 같은) 정확한 벡터 검색보다 훨씬 빠르게 결과를 계산하는 기법 그룹을 말합니다. 결과 정확도는 실제 사용에 보통 "충분히 좋습니다". 많은 근사 기법은 결과 정확도와 검색 시간 사이의 트레이드오프를 조정할 매개변수를 제공합니다.
벡터 유사도 인덱스가 구축되면 벡터 검색 쿼리가 자동으로 인덱스를 사용합니다:
Query
SELECT
id,
title
FROM dbpedia
ORDER BY cosineDistance(vector, (
SELECT vector
FROM dbpedia
WHERE id = '<dbpedia:Glacier_Express>'
)) ASC
LIMIT 20
Response
┌─id──────────────────────────────────────────────┬─title─────────────────────────────────┐
1. │ <dbpedia:Glacier_Express> │ Glacier Express │
2. │ <dbpedia:BVZ_Zermatt-Bahn> │ BVZ Zermatt-Bahn │
3. │ <dbpedia:Gornergrat_railway> │ Gornergrat railway │
... (원문에 20개 결과 전체 포함)
└─────────────────────────────────────────────────┴───────────────────────────────────────┘
20 rows in set. Elapsed: 0.025 sec. Processed 32.03 thousand rows, 2.10 MB (1.29 million rows/s., 84.80 MB/s.)
검색 쿼리용 임베딩 생성 (Generating embeddings for search query)
지금까지 본 유사도 검색 쿼리는 dbpedia 테이블에 있는 기존 벡터 중 하나를 검색 벡터로 사용합니다. 실제 세계 애플리케이션에서는 자연어일 수 있는 사용자 입력 쿼리에 대해 검색 벡터를 생성해야 합니다. 검색 벡터는 데이터셋의 임베딩 벡터를 생성하는 데 사용된 것과 같은 LLM 모델로 생성해야 합니다.
아래 예제 Python 스크립트는 text-embedding-3-large 모델로 임베딩 벡터를 생성하기 위해 OpenAI API를 프로그래밍 방식으로 호출하는 방법을 보여줍니다. 생성된 검색 임베딩 벡터는 SELECT 쿼리의 cosineDistance() 함수에 인자로 전달됩니다.
스크립트를 실행하려면 환경 변수 OPENAI_API_KEY에 OpenAI API 키가 설정되어 있어야 합니다. OpenAI API 키는 https://platform.openai.com에 등록한 후 얻을 수 있습니다.
import sys
from openai import OpenAI
import clickhouse_connect
ch_client = clickhouse_connect.get_client(compress=False) # Pass ClickHouse credentials
openai_client = OpenAI() # Set OPENAI_API_KEY environment variable
def get_embedding(text, model):
text = text.replace("\n", " ")
return openai_client.embeddings.create(input = [text], model=model, dimensions=1536).data[0].embedding
while True:
# Accept the search query from user
print("Enter a search query :")
input_query = sys.stdin.readline();
# Call OpenAI API endpoint to get the embedding
print("Generating the embedding for ", input_query);
embedding = get_embedding(input_query,
model='text-embedding-3-large')
# Execute vector search query in ClickHouse
print("Querying clickhouse...")
params = {'v1':embedding, 'v2':10}
result = ch_client.query("SELECT id,title,text FROM dbpedia ORDER BY cosineDistance(vector, %(v1)s) LIMIT %(v2)s", parameters=params)
for row in result.result_rows:
print(row[0], row[1], row[2])
print("---------------")
Q&A 데모 애플리케이션 (Q&A demo application)
위 예제는 ClickHouse를 사용한 유사도 검색과 문서 검색을 보여주었습니다. 이어서 매우 단순하지만 잠재력이 큰 생성형 AI 예제 애플리케이션을 소개합니다.
이 애플리케이션은 다음 단계를 수행합니다:
- 사용자로부터 *주제(topic)*를 입력받습니다
- 모델
text-embedding-3-large로 OpenAI API를 호출해 주제에 대한 임베딩 벡터를 생성합니다 dbpedia테이블에서 벡터 유사도 검색으로 관련성 높은 위키백과 기사/문서를 검색합니다- 주제와 관련된 자유 형식의 자연어 질문을 사용자로부터 받습니다
- OpenAI
gpt-3.5-turboChat API로 #3 단계에서 검색한 문서의 지식을 기반으로 질문에 답합니다. #3 단계에서 검색한 문서는 Chat API에 컨텍스트로 전달되며 생성형 AI의 핵심 연결 고리입니다.
Q&A 애플리케이션 실행의 대화 예시를 먼저 나열한 뒤, Q&A 애플리케이션 코드를 보여줍니다. 애플리케이션을 실행하려면 환경 변수 OPENAI_API_KEY에 OpenAI API 키가 설정되어 있어야 합니다. OpenAI API 키는 https://platform.openai.com에 등록한 후 얻을 수 있습니다.
$ python3 QandA.py
Enter a topic : FIFA world cup 1990
Generating the embedding for 'FIFA world cup 1990' and collecting 100 articles related to it from ClickHouse...
Enter your question : Who won the golden boot
Salvatore Schillaci of Italy won the Golden Boot at the 1990 FIFA World Cup.
Enter a topic : Cricket world cup
Generating the embedding for 'Cricket world cup' and collecting 100 articles related to it from ClickHouse...
Enter your question : Which country has hosted the world cup most times
England and Wales have hosted the Cricket World Cup the most times, with the tournament being held in these countries five times - in 1975, 1979, 1983, 1999, and 2019.
$
Code:
import sys
import time
from openai import OpenAI
import clickhouse_connect
ch_client = clickhouse_connect.get_client(compress=False) # Pass ClickHouse credentials here
openai_client = OpenAI() # Set the OPENAI_API_KEY environment variable
def get_embedding(text, model):
text = text.replace("\n", " ")
return openai_client.embeddings.create(input = [text], model=model, dimensions=1536).data[0].embedding
while True:
# Take the topic of interest from user
print("Enter a topic : ", end="", flush=True)
input_query = sys.stdin.readline()
input_query = input_query.rstrip()
# Generate an embedding vector for the search topic and query ClickHouse
print("Generating the embedding for '" + input_query + "' and collecting 100 articles related to it from ClickHouse...");
embedding = get_embedding(input_query,
model='text-embedding-3-large')
params = {'v1':embedding, 'v2':100}
result = ch_client.query("SELECT id,title,text FROM dbpedia ORDER BY cosineDistance(vector, %(v1)s) LIMIT %(v2)s", parameters=params)
# Collect all the matching articles/documents
results = ""
for row in result.result_rows:
results = results + row[2]
print("\nEnter your question : ", end="", flush=True)
question = sys.stdin.readline();
# Prompt for the OpenAI Chat API
query = f"""Use the below content to answer the subsequent question. If the answer cannot be found, write "I don't know."
Content:
\"\"\"
{results}
\"\"\"
Question: {question}"""
GPT_MODEL = "gpt-3.5-turbo"
response = openai_client.chat.completions.create(
messages=[
{'role': 'system', 'content': "You answer questions about {input_query}."},
{'role': 'user', 'content': query},
],
model=GPT_MODEL,
temperature=0,
)
# Print the answer to the question!
print(response.choices[0].message.content)
print("\n")