Создание семантический поисковика за 5 минут#

Обзор#

Данное руководство предназначено для новичков и содержит сценарий создания семантической поисковой системы для научно-фантастических книг. После настройки, можно задать системе вопрос о надвигающейся угрозе инопланетного вторжения. Творение предложит книги, чтобы подготовиться к потенциальной атаке из космоса.

Перед началом убедитесь, что установлена новая версия Python. Если неизвестно как запустить этот код в виртуальной среде, сначала ознакомьтесь с документацией по Python для создания виртуальных сред.

В этом руководстве предполагается, что пользователь находитесь в оболочке Bash. Используйте документацию Python, чтобы активировать виртуальную среду командами вроде следующих:

source tutorial-env/bin/activate

Шаг 1. Установка#

Необходимо обработать данные так, чтобы поисковая система могла работать с ними. Фреймворк Sentence Transformers предоставляет доступ к распространенным большим языковым моделям, преобразующим исходные данные в эмбеддинги.

pip install -U sentence-transformers

После кодирования данные нужно где-то хранить. Platform V Vector DB (далее - Vector DB) позволяет сохранять данные в виде эмбеддингов. Также можно использовать Vector DB для выполнения поисковых запросов по этим данным. Это означает, что можно попросить движок предоставить релевантные ответы, выходящие далеко за рамки простого сопоставления ключевых слов.

pip install -U qdrant-client

Импорт моделей#

Как только два основных фреймворка определены, необходимо указать точные модели, которые будет использовать эта поисковая машина.

from qdrant_client import models, QdrantClient
from sentence_transformers import SentenceTransformer

Фреймворк Sentence Transformers содержит множество моделей эмбеддинга. Возьмите модель all-MiniLM-L6-v2, поскольку она обладает хорошим балансом между скоростью и качеством эмбеддингов для текущего примера.

encoder = SentenceTransformer("all-MiniLM-L6-v2")

Шаг 2. Добавление набора данных#

Модель all-MiniLM-L6-v2 закодирует предоставленные данные. Будут перечислен все научно-фантастические книги используемой библиотеки. Каждая книга имеет метаданные — название, автора, год публикации и краткое описание.

documents = [
    {
        "name": "The Time Machine",
        "description": "A man travels through time and witnesses the evolution of humanity.",
        "author": "H.G. Wells",
        "year": 1895,
    },
    {
        "name": "Ender Game",
        "description": "A young boy is trained to become a military leader in a war against an alien race.",
        "author": "Orson Scott Card",
        "year": 1985,
    },
    {
        "name": "Brave New World",
        "description": "A dystopian society where people are genetically engineered and conditioned to conform to a strict social hierarchy.",
        "author": "Aldous Huxley",
        "year": 1932,
    },
    {
        "name": "The Hitchhiker Guide to the Galaxy",
        "description": "A comedic science fiction series following the misadventures of an unwitting human and his alien friend.",
        "author": "Douglas Adams",
        "year": 1979,
    },
    {
        "name": "Dune",
        "description": "A desert planet is the site of political intrigue and power struggles.",
        "author": "Frank Herbert",
        "year": 1965,
    },
    {
        "name": "Foundation",
        "description": "A mathematician develops a science to predict the future of humanity and works to save civilization from collapse.",
        "author": "Isaac Asimov",
        "year": 1951,
    },
    {
        "name": "Snow Crash",
        "description": "A futuristic world where the internet has evolved into a virtual reality metaverse.",
        "author": "Neal Stephenson",
        "year": 1992,
    },
    {
        "name": "Neuromancer",
        "description": "A hacker is hired to pull off a near-impossible hack and gets pulled into a web of intrigue.",
        "author": "William Gibson",
        "year": 1984,
    },
    {
        "name": "The War of the Worlds",
        "description": "A Martian invasion of Earth throws humanity into chaos.",
        "author": "H.G. Wells",
        "year": 1898,
    },
    {
        "name": "The Hunger Games",
        "description": "A dystopian society where teenagers are forced to fight to the death in a televised spectacle.",
        "author": "Suzanne Collins",
        "year": 2008,
    },
    {
        "name": "The Andromeda Strain",
        "description": "A deadly virus from outer space threatens to wipe out humanity.",
        "author": "Michael Crichton",
        "year": 1969,
    },
    {
        "name": "The Left Hand of Darkness",
        "description": "A human ambassador is sent to a planet where the inhabitants are genderless and can change gender at will.",
        "author": "Ursula K. Le Guin",
        "year": 1969,
    },
    {
        "name": "The Three-Body Problem",
        "description": "Humans encounter an alien civilization that lives in a dying system.",
        "author": "Liu Cixin",
        "year": 2008,
    },
]

Шаг 3. Определение места хранения#

Необходимо сообщить Vector DB, куда сохранить эмбеддинги. Это простой демонстрационный пример, поэтому компьютер будет использовать память в качестве временного хранилища.

client = QdrantClient(":memory:")

Шаг 4. Создание коллекции#

Все данные в Vector DB организованы по коллекциям. В данном случае хранятся книги, поэтому используйте my_books в качестве имени.

client.create_collection(
    collection_name="my_books",
    vectors_config=models.VectorParams(
        size=encoder.get_sentence_embedding_dimension(),  # Vector size is defined by used model
        distance=models.Distance.COSINE,
    ),
)
  • Параметр vector_size определяет размер векторов для конкретной коллекции. Если их размеры различаются, невозможно вычислить расстояние между ними. Значение 384 соответствует выходной размерности энкодера. Также можно воспользоваться методом model.get_sentence_embedding_dimension(), чтобы получить размерность используемой модели.

  • Параметр distance позволяет указать функцию, которая используется для измерения расстояния между двумя точками.

Шаг 5. Загрузка данных в коллекцию#

Сообщите базе данных загрузить documents в коллекцию my_books. Каждому элементу будут присвоены идентификатор и полезная нагрузка (payload). Полезная нагрузка представляет собой просто метаданные из набора данных.

client.upload_points(
    collection_name="my_books",
    points=[
        models.PointStruct(
            id=idx, vector=encoder.encode(doc["description"]).tolist(), payload=doc
        )
        for idx, doc in enumerate(documents)
    ],
)

6. Создание запроса#

Теперь, когда данные хранятся в Vector DB можно задавать вопросы и получать результаты, соответствующие запросу по смыслу.

hits = client.query_points(
    collection_name="my_books",
    query=encoder.encode("alien invasion").tolist(),
    limit=3,
).points

for hit in hits:
    print(hit.payload, "score:", hit.score)

Ответ:

Поисковый движок показывает три наиболее вероятных ответа, связанных с вторжением пришельцев. Каждый ответ получает оценку, показывающую степень его близости к первоначальному запросу.

{'name': 'The War of the Worlds', 'description': 'A Martian invasion of Earth throws humanity into chaos.', 'author': 'H.G. Wells', 'year': 1898} score: 0.570093257022374
{'name': "The Hitchhiker Guide to the Galaxy", 'description': 'A comedic science fiction series following the misadventures of an unwitting human and his alien friend.', 'author': 'Douglas Adams', 'year': 1979} score: 0.5040468703143637
{'name': 'The Three-Body Problem', 'description': 'Humans encounter an alien civilization that lives in a dying system.', 'author': 'Liu Cixin', 'year': 2008} score: 0.45902943411768216

Уточнение запроса#

Зададим вопрос «А как насчет самой последней книги начала 2000-х годов?»

hits = client.query_points(
    collection_name="my_books",
    query=encoder.encode("alien invasion").tolist(),
    query_filter=models.Filter(
        must=[models.FieldCondition(key="year", range=models.Range(gte=2000))]
    ),
    limit=1,
).points

for hit in hits:
    print(hit.payload, "score:", hit.score)

Ответ:

Запрос был сужен до одного результата от 2008 года.

{'name': 'The Three-Body Problem', 'description': 'Humans encounter an alien civilization that lives in a dying system.', 'author': 'Liu Cixin', 'year': 2008} score: 0.45902943411768216