diff --git a/notebooks/ko/_toctree.yml b/notebooks/ko/_toctree.yml index d420590c..04852cb9 100644 --- a/notebooks/ko/_toctree.yml +++ b/notebooks/ko/_toctree.yml @@ -24,4 +24,9 @@ isExpanded: false sections: - local: faiss_with_hf_datasets_and_clip - title: 유사성 검색을 위한 멀티모달 데이터 임베딩 \ No newline at end of file + title: 유사성 검색을 위한 멀티모달 데이터 임베딩 + - title: 검색 레시피 + isExpanded: false + sections: + - local: vector_search_with_hub_as_backend + title: 허깅페이스에서 허브를 백엔드로 사용한 벡터 검색 diff --git a/notebooks/ko/index.md b/notebooks/ko/index.md index c68982d8..820f08a8 100644 --- a/notebooks/ko/index.md +++ b/notebooks/ko/index.md @@ -13,6 +13,7 @@ - [다중 에이전트 계층 구조에서 여러 에이전트가 협업하도록 하기](multiagent_web_assistant) - [유사성 검색을 위한 멀티모달 데이터 임베딩](faiss_with_hf_datasets_and_clip) +- [허깅페이스에서 허브를 백엔드로 사용한 벡터 검색](vector_search_with_hub_as_backend) 더 다양한 노트북을 확인하고 싶다면 Cookbook's [GitHub 리포지토리](https://github.com/huggingface/cookbook)에 방문해보세요. diff --git a/notebooks/ko/vector_search_with_hub_as_backend.ipynb b/notebooks/ko/vector_search_with_hub_as_backend.ipynb new file mode 100644 index 00000000..954d5eeb --- /dev/null +++ b/notebooks/ko/vector_search_with_hub_as_backend.ipynb @@ -0,0 +1,329 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "ksUdu7H7qBig" + }, + "source": [ + "# 허깅페이스에서 허브를 백엔드로 사용한 벡터 검색\n", + "\n", + "_작성자: [Martin Elstner](https://github.com/MartinEls) , 번역: [최용빈](https://github.com/whybe-choi)_\n", + "\n", + "허깅페이스의 데이터셋은 파켓(parquet) 파일에 의존합니다. 우리는 빠른 인메모리 데이터베이스 시스템인 [DuckDB를 사용하여 이 파일들과 상호작용](https://huggingface.co/docs/hub/en/datasets-duckdb)할 수 있습니다. DuckDB의 기능 중 하나는 [벡터 유사도 검색](https://duckdb.org/docs/extensions/vss.html)으로, 인덱스 유무에 관계없이 사용할 수 있습니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Nwziazq7qBig" + }, + "source": [ + "## 의존성 설치" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "_10SP4H4qBih" + }, + "outputs": [], + "source": [ + "!pip install datasets duckdb sentence-transformers model2vec -q" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "E8DN3ihKqBih" + }, + "source": [ + "## 데이터셋에 대한 임베딩 생성\n", + "\n", + "먼저, 검색할 데이터셋에 대한 임베딩을 생성해야 합니다. 우리는 `sentence-transformers` 라이브러리를 사용하여 데이터셋에 대한 임베딩을 생성할 것입니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ecQmlrkyqBih" + }, + "outputs": [], + "source": [ + "from sentence_transformers import SentenceTransformer\n", + "from sentence_transformers.models import StaticEmbedding\n", + "\n", + "static_embedding = StaticEmbedding.from_model2vec(\"minishlab/potion-base-8M\")\n", + "model = SentenceTransformer(modules=[static_embedding])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pOxpuYpxqBih" + }, + "source": [ + "이제 허브에서 [ai-blueprint/fineweb-bbc-news](https://huggingface.co/datasets/ai-blueprint/fineweb-bbc-news) 데이터셋을 로드해 보겠습니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Zbo4T4edqBih" + }, + "outputs": [], + "source": [ + "from datasets import load_dataset\n", + "\n", + "ds = load_dataset(\"ai-blueprint/fineweb-bbc-news\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rXZDtCytqBii" + }, + "source": [ + "이제 데이터셋에 대한 임베딩을 생성할 수 있습니다. 일반적으로 정밀도(precision)을 잃지 않기 위해 데이터를 더 작은 배치로 나누고 싶을 수 있지만, 이 예제에서는 데이터셋의 전체 텍스트에 대한 임베딩만 생성하겠습니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "Y7QClFshqBii" + }, + "outputs": [], + "source": [ + "def create_embeddings(batch):\n", + " embeddings = model.encode(batch[\"text\"], convert_to_numpy=True)\n", + " batch[\"embeddings\"] = embeddings.tolist()\n", + " return batch\n", + "\n", + "ds = ds.map(create_embeddings, batched=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "BeM-bRRhqBii" + }, + "source": [ + "이제 임베딩이 포함된 데이터셋을 허브에 다시 업로드할 수 있습니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "rCRDC_pUqBii" + }, + "outputs": [], + "source": [ + "ds.push_to_hub(\"ai-blueprint/fineweb-bbc-news-embeddings\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "pFE5laHqqBii" + }, + "source": [ + "## 허깅페이스 허브에서 벡터 검색\n", + "이제 `duckdb`를 사용하여 데이터셋에서 벡터 검색을 수행할 수 있습니다. 이때 인덱스를 사용하거나 사용하지 않을 수 있습니다. 인덱스를 **활용하지 않고** 검색하는 것은 더 느리지만 더 정확하고, 인덱스를 **활용하여** 검색하는 것은 더 빠르지만 덜 정확합니다.\n", + "\n", + "### 인덱스를 활용하지 않고 검색하기\n", + "인덱스를 활용하지 않고 검색하려면 `duckdb` 라이브러리를 사용하여 데이터셋에 연결하고 벡터 검색을 수행할 수 있습니다. 이는 느린 작업지만 일반적으로 약 10만 행까지의 작은 데이터셋에서는 충분히 빠르게 동작합니다. 즉 우리가 사용하는 데이터셋에 대해서 쿼리를 날리는 것은 다소 느릴 것입니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "BTeqWPCNqBii" + }, + "outputs": [], + "source": [ + "import duckdb\n", + "from typing import List\n", + "\n", + "def similarity_search_without_duckdb_index(\n", + " query: str,\n", + " k: int = 5,\n", + " dataset_name: str = \"ai-blueprint/fineweb-bbc-news-embeddings\",\n", + " embedding_column: str = \"embeddings\",\n", + "):\n", + " # 인덱스를 위해 사용한 모델과 동일한 모델을 사용\n", + " query_vector = model.encode(query)\n", + " embedding_dim = model.get_sentence_embedding_dimension()\n", + "\n", + " sql = f\"\"\"\n", + " SELECT\n", + " *,\n", + " array_cosine_distance(\n", + " {embedding_column}::float[{embedding_dim}],\n", + " {query_vector.tolist()}::float[{embedding_dim}]\n", + " ) as distance\n", + " FROM 'hf://datasets/{dataset_name}/**/*.parquet'\n", + " ORDER BY distance\n", + " LIMIT {k}\n", + " \"\"\"\n", + " return duckdb.sql(sql).to_df()\n", + "\n", + "similarity_search_without_duckdb_index(\"What is the future of AI?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Y4W1wkkRqBij" + }, + "source": [ + "### 인덱스를 활용하여 검색하기\n", + "\n", + "이 접근법은 데이터셋의 로컬 복사본을 생성하고 이를 사용하여 인덱스를 생성합니다. 약간의 경미한 오버헤드가 있지만 한번 생성한 후에는 검색 속도가 상당히 향상될 것입니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5ewJ7Ns8qBij" + }, + "outputs": [], + "source": [ + "import duckdb\n", + "\n", + "def _setup_vss():\n", + " duckdb.sql(\n", + " query=\"\"\"\n", + " INSTALL vss;\n", + " LOAD vss;\n", + " \"\"\"\n", + " )\n", + "def _drop_table(table_name):\n", + " duckdb.sql(\n", + " query=f\"\"\"\n", + " DROP TABLE IF EXISTS {table_name};\n", + " \"\"\"\n", + " )\n", + "\n", + "def _create_table(dataset_name, table_name, embedding_column):\n", + " duckdb.sql(\n", + " query=f\"\"\"\n", + " CREATE TABLE {table_name} AS\n", + " SELECT *, {embedding_column}::float[{model.get_sentence_embedding_dimension()}] as {embedding_column}_float\n", + " FROM 'hf://datasets/{dataset_name}/**/*.parquet';\n", + " \"\"\"\n", + " )\n", + "\n", + "def _create_index(table_name, embedding_column):\n", + " duckdb.sql(\n", + " query=f\"\"\"\n", + " CREATE INDEX my_hnsw_index ON {table_name} USING HNSW ({embedding_column}_float) WITH (metric = 'cosine');\n", + " \"\"\"\n", + " )\n", + "\n", + "def create_index(dataset_name, table_name, embedding_column):\n", + " _setup_vss()\n", + " _drop_table(table_name)\n", + " _create_table(dataset_name, table_name, embedding_column)\n", + " _create_index(table_name, embedding_column)\n", + "\n", + "create_index(\n", + " dataset_name=\"ai-blueprint/fineweb-bbc-news-embeddings\",\n", + " table_name=\"fineweb_bbc_news_embeddings\",\n", + " embedding_column=\"embeddings\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rPv0DiO8qBij" + }, + "source": [ + "이제 인덱스를 사용하여 벡터 검색을 수행할 수 있으며, 결과는 즉시 반환됩니다." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "ZT0732nmqBij" + }, + "outputs": [], + "source": [ + "def similarity_search_with_duckdb_index(\n", + " query: str,\n", + " k: int = 5,\n", + " table_name: str = \"fineweb_bbc_news_embeddings\",\n", + " embedding_column: str = \"embeddings\"\n", + "):\n", + " embedding = model.encode(query).tolist()\n", + " return duckdb.sql(\n", + " query=f\"\"\"\n", + " SELECT *, array_cosine_distance({embedding_column}_float, {embedding}::FLOAT[{model.get_sentence_embedding_dimension()}]) as distance\n", + " FROM {table_name}\n", + " ORDER BY distance\n", + " LIMIT {k};\n", + " \"\"\"\n", + " ).to_df()\n", + "\n", + "similarity_search_with_duckdb_index(\"What is the future of AI?\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XVT4VYknqBij" + }, + "source": [ + "이 쿼리는 응답시간을 30초에서 1초 미만으로 줄이며, 무거운 벡터 검색 엔진을 배포할 필요가 없고 저장소는 허브에서 처리됩니다." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "AQf6IxFGqBij" + }, + "source": [ + "## 결론\n", + "\n", + "우리는 `duckbd`를 사용하여 허브에서 벡터 검색을 수행하는 방법을 보았습니다. 10만 행 미만의 작은 데이터셋의 경우, 허브를 벡터 검색 벡엔드로 사용하여 인덱스 없이 벡터 검색을 수행할 수 있지만, 더 큰 데이터셋의 경우 `vss` 확장 프로그램으로 인덱스를 생성하면서 로컬 검색을 수행하고 허브를 스토리지 백엔드로 사용해야 합니다.\n", + "\n", + "## 더 알아보기\n", + "\n", + "- [허깅페이스에서의 벡터 검색](https://huggingface.co/docs/hub/en/datasets-duckdb)\n", + "- [DuckDB를 사용한 벡터 검색 인덱싱](https://duckdb.org/docs/extensions/vss.html)" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +}