> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-mintlify-fbfa8bee.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Датасет, содержащий более 28 миллионов публикаций Hacker News и их векторные эмбеддинги

# Датасет для векторного поиска по Hacker News

<div id="introduction">
  ## Введение
</div>

[Датасет Hacker News](https://news.ycombinator.com/) содержит 28,74 миллиона
публикаций и соответствующие им векторные эмбеддинги. Эмбеддинги были сгенерированы с помощью модели [SentenceTransformers](https://sbert.net/) [all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2). Размерность каждого эмбеддинг-вектора — `384`.

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

<div id="dataset-details">
  ## Подробности о датасете
</div>

ClickHouse предоставляет полный датасет с векторными эмбеддингами в виде одного файла `Parquet`, размещённого в [S3 бакете](https://clickhouse-datasets.s3.amazonaws.com/hackernews-miniLM/hackernews_part_1_of_1.parquet)

Мы рекомендуем сначала оценить размер датасета, чтобы определить требования к хранилищу и памяти, ориентируясь на [документацию](/ru/reference/engines/table-engines/mergetree-family/annindexes).

<div id="steps">
  ## Шаги
</div>

<Steps>
  <Step>
    ### Создание таблицы

    Создайте таблицу `hackernews` для хранения публикаций, их эмбеддингов & связанных с ними атрибутов:

    ```sql theme={null}
    CREATE TABLE hackernews
    (
        `id` Int32,
        `doc_id` Int32,
        `text` String,
        `vector` Array(Float32),
        `node_info` Tuple(
            start Nullable(UInt64),
            end Nullable(UInt64)),
        `metadata` String,
        `type` Enum8('story' = 1, 'comment' = 2, 'poll' = 3, 'pollopt' = 4, 'job' = 5),
        `by` LowCardinality(String),
        `time` DateTime,
        `title` String,
        `post_score` Int32,
        `dead` UInt8,
        `deleted` UInt8,
        `length` UInt32
    )
    ENGINE = MergeTree
    ORDER BY id;
    ```

    `id` — это просто последовательно увеличивающееся целое число. Дополнительные атрибуты можно использовать в предикатах, чтобы разобраться,
    как поиск векторов по сходству сочетается с постфильтрацией/префильтрацией, как описано в [документации](/ru/reference/engines/table-engines/mergetree-family/annindexes)
  </Step>

  <Step>
    ### Загрузка данных

    Чтобы загрузить данные из файла в формате `Parquet`, выполните следующий оператор SQL:

    ```sql theme={null}
    INSERT INTO hackernews SELECT * FROM s3('https://clickhouse-datasets.s3.amazonaws.com/hackernews-miniLM/hackernews_part_1_of_1.parquet');
    ```

    Вставка 28,74 миллиона строк в таблицу займет несколько минут.
  </Step>

  <Step>
    ### Создайте индекс векторного сходства

    Выполните следующий SQL-запрос, чтобы определить и создать индекс векторного сходства на столбце `vector` таблицы `hackernews`:

    ```sql theme={null}
    ALTER TABLE hackernews ADD INDEX vector_index vector TYPE vector_similarity('hnsw', 'cosineDistance', 384, 'bf16', 64, 512);

    ALTER TABLE hackernews MATERIALIZE INDEX vector_index SETTINGS mutations_sync = 2;
    ```

    Параметры и особенности производительности при создании индекса и поиске описаны в [документации](/ru/reference/engines/table-engines/mergetree-family/annindexes).
    В приведенном выше операторе используются значения 64 и 512 соответственно для гиперпараметров HNSW `M` и `ef_construction`.
    Вам нужно тщательно подобрать оптимальные значения этих параметров, оценивая время построения индекса и качество результатов поиска,
    соответствующие выбранным значениям.

    Построение и сохранение индекса для полного набора данных из 28,74 млн записей может занять от нескольких минут до часа — в зависимости от количества доступных ядер CPU и пропускной способности хранилища.
  </Step>

  <Step>
    ### Выполните ANN-поиск

    После построения индекса векторного сходства запросы векторного поиска будут автоматически использовать этот индекс:

    ```sql title="Query" theme={null}
    SELECT id, title, text
    FROM hackernews
    ORDER BY cosineDistance( vector, <search vector>)
    LIMIT 10

    ```

    Первичная загрузка векторного индекса в память может занять от нескольких секунд до нескольких минут.
  </Step>

  <Step>
    ### Генерация эмбеддингов для поискового запроса

    [Sentence Transformers](https://www.sbert.net/) — это локальные, простые в использовании модели векторных представлений для захвата семантического смысла предложений и абзацев.

    Этот набор данных HackerNews содержит векторные эмбеддинги, сгенерированные моделью
    [all-MiniLM-L6-v2](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2).

    Ниже приведён пример Python-скрипта, демонстрирующего программную генерацию
    эмбеддинг-векторов с помощью пакета `sentence_transformers` для Python. Полученный эмбеддинг-вектор поиска затем передаётся как аргумент функции [`cosineDistance()`](/ru/reference/functions/regular-functions/distance-functions#cosineDistance) в запросе \`SELECT\`.

    ```python theme={null}
    from sentence_transformers import SentenceTransformer
    import sys

    import clickhouse_connect

    print("Initializing...")

    model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')

    chclient = clickhouse_connect.get_client() # ClickHouse credentials here

    while True:
        # Получить поисковый запрос от пользователя
        print("Enter a search query :")
        input_query = sys.stdin.readline();
        texts = [input_query]

        # Запустить модель и получить вектор поиска
        print("Generating the embedding for ", input_query);
        embeddings = model.encode(texts)

        print("Выполняется запрос к ClickHouse...")
        params = {'v1':list(embeddings[0]), 'v2':20}
        result = chclient.query("SELECT id, title, text FROM hackernews ORDER BY cosineDistance(vector, %(v1)s) LIMIT %(v2)s", parameters=params)
        print("Results :")
        for row in result.result_rows:
            print(row[0], row[2][:100])
            print("---------")
    ```

    Ниже приведён пример запуска указанного выше Python-скрипта и результаты поиска по сходству
    (выводятся только первые 100 символов из каждого из 20 наиболее релевантных постов):

    ```text theme={null}
    Инициализация...

    Введите поисковый запрос :
    Are OLAP cubes useful

    Генерация эмбеддинга для  "Are OLAP cubes useful"

    Запрос к ClickHouse...

    Результаты :

    27742647 smartmic:
    slt2021: OLAP Cube is not dead, as long as you use some form of:<p>1. GROUP BY multiple fi
    ---------
    27744260 georgewfraser:A data mart is a logical organization of data to help humans understand the schema. Wh
    ---------
    27761434 mwexler:&quot;We model data according to rigorous frameworks like Kimball or Inmon because we must r
    ---------
    28401230 chotmat:
    erosenbe0: OLAP database is just a copy, replica, or archive of data with a schema designe
    ---------
    22198879 Merick:+1 for Apache Kylin, it&#x27;s a great project and awesome open source community. If anyone i
    ---------
    27741776 crazydoggers:I always felt the value of an OLAP cube was uncovering questions you may not know to as
    ---------
    22189480 shadowsun7:
    _Codemonkeyism: After maintaining an OLAP cube system for some years, I&#x27;m not that
    ---------
    27742029 smartmic:
    gengstrand: My first exposure to OLAP was on a team developing a front end to Essbase that
    ---------
    22364133 irfansharif:
    simo7: I&#x27;m wondering how this technology could work for OLAP cubes.<p>An OLAP cube
    ---------
    23292746 scoresmoke:When I was developing my pet project for Web analytics (<a href="https:&#x2F;&#x2F;github
    ---------
    22198891 js8:It seems that the article makes a categorical error, arguing that OLAP cubes were replaced by co
    ---------
    28421602 chotmat:
    7thaccount: Is there any advantage to OLAP cube over plain SQL (large historical database r
    ---------
    22195444 shadowsun7:
    lkcubing: Thanks for sharing. Interesting write up.<p>While this article accurately capt
    ---------
    22198040 lkcubing:Thanks for sharing. Interesting write up.<p>While this article accurately captures the issu
    ---------
    3973185 stefanu:
    sgt: Interesting idea. Ofcourse, OLAP isn't just about the underlying cubes and dimensions,
    ---------
    22190903 shadowsun7:
    js8: It seems that the article makes a categorical error, arguing that OLAP cubes were r
    ---------
    28422241 sradman:OLAP Cubes have been disrupted by Column Stores. Unless you are interested in the history of
    ---------
    28421480 chotmat:
    sradman: OLAP Cubes have been disrupted by Column Stores. Unless you are interested in the
    ---------
    27742515 BadInformatics:
    quantified: OP posts with inverted condition: “OLAP != OLAP Cube” is the actual titl
    ---------
    28422935 chotmat:
    rstuart4133: I remember hearing about OLAP cubes donkey&#x27;s years ago (probably not far
    ---------
    ```

    ## Демонстрационное приложение для суммаризации

    В примере выше были показаны семантический поиск и извлечение документов с помощью ClickHouse.

    Далее представлен очень простой, но перспективный пример приложения на основе генеративного ИИ.

    Приложение выполняет следующие действия:

    1. Принимает от пользователя *топик* в качестве входных данных
    2. Создает эмбеддинг-вектор для *темы* с помощью `SentenceTransformers` и модели `all-MiniLM-L6-v2`
    3. Извлекает наиболее релевантные посты/комментарии с помощью поиска по векторному сходству в таблице `hackernews`
    4. Использует `LangChain` и Chat API OpenAI `gpt-3.5-turbo`, чтобы **кратко изложить** содержимое, полученное на шаге #3.
       Посты/комментарии, полученные на шаге #3, передаются в Chat API как *контекст* и служат ключевым связующим звеном в Generative AI.

    Ниже сначала приведён пример работы приложения для суммаризации, а затем — его исходный код.
    Для запуска приложения необходимо задать API-ключ OpenAI в переменной окружения
    `OPENAI_API_KEY`. API-ключ OpenAI можно получить после регистрации на [https://platform.openai.com](https://platform.openai.com).

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

    ```shell theme={null}
    $ python3 summarize.py

    Enter a search topic :
    ClickHouse performance experiences

    Generating the embedding for ---->  ClickHouse performance experiences

    Querying ClickHouse to retrieve relevant articles...

    Initializing chatgpt-3.5-turbo model...

    Summarizing search results retrieved from ClickHouse...

    Summary from chatgpt-3.5:
    The discussion focuses on comparing ClickHouse with various databases like TimescaleDB, Apache Spark,
    AWS Redshift, and QuestDB, highlighting ClickHouse's cost-efficient high performance and suitability
    for analytical applications. Users praise ClickHouse for its simplicity, speed, and resource efficiency
    in handling large-scale analytics workloads, although some challenges like DMLs and difficulty in backups
    are mentioned. ClickHouse is recognized for its real-time aggregate computation capabilities and solid
    engineering, with comparisons made to other databases like Druid and MemSQL. Overall, ClickHouse is seen
    as a powerful tool for real-time data processing, analytics, and handling large volumes of data
    efficiently, gaining popularity for its impressive performance and cost-effectiveness.
    ```

    Код для приведённого выше приложения:

    ```python theme={null}
    print("Initializing...")

    import sys
    import json
    import time
    from sentence_transformers import SentenceTransformer

    import clickhouse_connect

    from langchain.docstore.document import Document
    from langchain.text_splitter import CharacterTextSplitter
    from langchain.chat_models import ChatOpenAI
    from langchain.prompts import PromptTemplate
    from langchain.chains.summarize import load_summarize_chain
    import textwrap
    import tiktoken

    def num_tokens_from_string(string: str, encoding_name: str) -> int:
        encoding = tiktoken.encoding_for_model(encoding_name)
        num_tokens = len(encoding.encode(string))
        return num_tokens

    model = SentenceTransformer('sentence-transformers/all-MiniLM-L6-v2')

    chclient = clickhouse_connect.get_client(compress=False) # ClickHouse credentials here

    while True:
        # Получить поисковый запрос от пользователя
        print("Enter a search topic :")
        input_query = sys.stdin.readline();
        texts = [input_query]

        # Запустить модель и получить поисковый или эталонный вектор
        print("Generating the embedding for ----> ", input_query);
        embeddings = model.encode(texts)

        print("Querying ClickHouse...")
        params = {'v1':list(embeddings[0]), 'v2':100}
        result = chclient.query("SELECT id,title,text FROM hackernews ORDER BY cosineDistance(vector, %(v1)s) LIMIT %(v2)s", parameters=params)

        # Объединить все результаты поиска
        doc_results = ""
        for row in result.result_rows:
            doc_results = doc_results + "\n" + row[2]

        print("Initializing chatgpt-3.5-turbo model")
        model_name = "gpt-3.5-turbo"

        text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
            model_name=model_name
        )

        texts = text_splitter.split_text(doc_results)

        docs = [Document(page_content=t) for t in texts]

        llm = ChatOpenAI(temperature=0, model_name=model_name)

        prompt_template = """
    Write a concise summary of the following in not more than 10 sentences:

    {text}

    CONSCISE SUMMARY :
    """

        prompt = PromptTemplate(template=prompt_template, input_variables=["text"])

        num_tokens = num_tokens_from_string(doc_results, model_name)

        gpt_35_turbo_max_tokens = 4096
        verbose = False

        print("Summarizing search results retrieved from ClickHouse...")

        if num_tokens <= gpt_35_turbo_max_tokens:
            chain = load_summarize_chain(llm, chain_type="stuff", prompt=prompt, verbose=verbose)
        else:
            chain = load_summarize_chain(llm, chain_type="map_reduce", map_prompt=prompt, combine_prompt=prompt, verbose=verbose)

        summary = chain.run(docs)

        print(f"Summary from chatgpt-3.5: {summary}")
    ```
  </Step>
</Steps>
