🚀 Попробуйте Zilliz Cloud, полностью управляемый Milvus, бесплатно — ощутите 10-кратное увеличение производительности! Попробовать сейчас>

milvus-logo
LFAI
Главная
  • Интеграции
    • Другие
  • Home
  • Docs
  • Интеграции

  • Другие

  • Рука

Построение RAG на архитектуре Arm

ПроцессорыArm активно используются в широком спектре приложений, включая традиционные задачи машинного обучения (ML) и искусственного интеллекта (AI).

В этом руководстве вы узнаете, как построить приложение Retrieval-Augmented Generation (RAG) на инфраструктуре на базе Arm. Для хранения векторов мы используем Zilliz Cloud, полностью управляемую векторную базу данных Milvus. Zilliz Cloud доступна в таких крупных облаках, как AWS, GCP и Azure. В данной демонстрации мы используем Zilliz Cloud, развернутый на AWS с помощью машин Arm. Для LLM мы используем модель Llama-3.1-8B на процессоре сервера AWS на базе Arm с помощью llama.cpp.

Необходимые условия

Для выполнения этого примера мы рекомендуем использовать AWS Graviton, который обеспечивает экономически эффективный способ запуска рабочих нагрузок ML на серверах на базе Arm. Этот ноутбук был протестирован на экземпляре AWS Graviton3 c7g.2xlarge с системой Ubuntu 22.04 LTS.

Для выполнения этого примера вам потребуется не менее четырех ядер и 8 ГБ оперативной памяти. Настройте дисковое хранилище объемом не менее 32 ГБ. Мы рекомендуем использовать экземпляр с такими же или лучшими характеристиками.

После запуска экземпляра подключитесь к нему и выполните следующие команды для подготовки среды.

Установите python на сервер:

$ sudo apt update
$ sudo apt install python-is-python3 python3-pip python3-venv -y

Создайте и активируйте виртуальную среду:

$ python -m venv venv
$ source venv/bin/activate

Установите необходимые зависимости python:

$ pip install --upgrade pymilvus openai requests langchain-huggingface huggingface_hub tqdm

Автономная загрузка данных

Создание коллекции

Для хранения и получения векторных данных мы используем облако Zilliz Cloud, развернутое на AWS, с машинами на базе Arm. Чтобы быстро начать, просто бесплатно зарегистрируйте учетную запись на Zilliz Cloud.

В дополнение к Zilliz Cloud можно использовать и самостоятельный хостинг Milvus (более сложный в настройке). Мы также можем развернуть Milvus Standalone и Kubernetes на машинах на базе ARM. Более подробную информацию об установке Milvus можно найти в документации по установке.

Мы задаем uri и token в качестве публичной конечной точки и Api ключа в Zilliz Cloud.

from pymilvus import MilvusClient

milvus_client = MilvusClient(
    uri="<your_zilliz_public_endpoint>", token="<your_zilliz_api_key>"
)

collection_name = "my_rag_collection"

Проверьте, не существует ли уже коллекция, и удалите ее, если она существует.

if milvus_client.has_collection(collection_name):
    milvus_client.drop_collection(collection_name)

Создаем новую коллекцию с указанными параметрами.

Если мы не укажем информацию о полях, Milvus автоматически создаст поле по умолчанию id для первичного ключа и поле vector для хранения векторных данных. Зарезервированное поле JSON используется для хранения не определенных схемой полей и их значений.

milvus_client.create_collection(
    collection_name=collection_name,
    dimension=384,
    metric_type="IP",  # Inner product distance
    consistency_level="Strong",  # Strong consistency level
)

В качестве типа метрики по умолчанию мы используем внутреннее произведение расстояний. Для получения дополнительной информации о типах расстояний вы можете обратиться к странице Метрики сходства.

Подготовка данных

Мы используем страницы FAQ из Milvus Documentation 2.4.x в качестве частных знаний в нашей RAG, которая является хорошим источником данных для простого RAG-конвейера.

Скачайте zip-файл и распакуйте документы в папку milvus_docs.

$ wget https://github.com/milvus-io/milvus-docs/releases/download/v2.4.6-preview/milvus_docs_2.4.x_en.zip
$ unzip -q milvus_docs_2.4.x_en.zip -d milvus_docs

Мы загружаем все файлы разметки из папки milvus_docs/en/faq. Для каждого документа мы просто используем "# " для разделения содержимого в файле, что позволяет примерно разделить содержимое каждой основной части файла разметки.

from glob import glob

text_lines = []

for file_path in glob("milvus_docs/en/faq/*.md", recursive=True):
    with open(file_path, "r") as file:
        file_text = file.read()

    text_lines += file_text.split("# ")

Вставка данных

Мы подготовили простую, но эффективную модель встраивания all-MiniLM-L6-v2, которая может преобразовывать текст в векторы встраивания.

from langchain_huggingface import HuggingFaceEmbeddings

embedding_model = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")

Итерация по строкам текста, создание вкраплений, а затем вставка данных в Milvus.

Вот новое поле text, которое является неопределенным полем в схеме коллекции. Оно будет автоматически добавлено в зарезервированное динамическое поле JSON, которое на высоком уровне может рассматриваться как обычное поле.

from tqdm import tqdm

data = []

text_embeddings = embedding_model.embed_documents(text_lines)

for i, (line, embedding) in enumerate(
    tqdm(zip(text_lines, text_embeddings), desc="Creating embeddings")
):
    data.append({"id": i, "vector": embedding, "text": line})

milvus_client.insert(collection_name=collection_name, data=data)
Creating embeddings: 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 72/72 [00:18<00:00,  3.91it/s]

Запуск сервиса LLM на платформе Arm

В этом разделе мы создадим и запустим сервис llama.cpp на процессоре на базе Arm.

Модель Llama 3.1 и llama.cpp

Модель Llama-3.1-8B от Meta относится к семейству моделей Llama 3.1 и является бесплатной для использования в исследовательских и коммерческих целях. Прежде чем использовать модель, посетите сайт Llama и заполните форму для запроса доступа.

llama.cpp - это проект с открытым исходным кодом на языке C/C++, который обеспечивает эффективный вывод LLM на различном оборудовании - как локально, так и в облаке. Вы можете удобно разместить модель Llama 3.1 с помощью llama.cpp.

Загрузите и соберите llama.cpp

Выполните следующие команды, чтобы установить make, cmake, gcc, g++ и другие инструменты, необходимые для сборки llama.cpp из исходного кода:

$ sudo apt install make cmake -y
$ sudo apt install gcc g++ -y
$ sudo apt install build-essential -y

Теперь вы готовы приступить к сборке llama.cpp.

Клонируйте репозиторий исходных текстов для llama.cpp:

$ git clone https://github.com/ggerganov/llama.cpp

По умолчанию llama.cpp собирается только для CPU в Linux и Windows. Вам не нужно предоставлять никаких дополнительных переключателей, чтобы собрать его для процессора Arm, на котором вы его запускаете.

Запустите make для сборки:

$ cd llama.cpp
$ make GGML_NO_LLAMAFILE=1 -j$(nproc)

Проверьте правильность сборки llama.cpp, выполнив команду help:

$ ./llama-cli -h

Если llama.cpp был собран правильно, вы увидите отображение опции help. Выходной фрагмент выглядит следующим образом:

example usage:

    text generation:     ./llama-cli -m your_model.gguf -p "I believe the meaning of life is" -n 128

    chat (conversation): ./llama-cli -m your_model.gguf -p "You are a helpful assistant" -cnv

Теперь вы можете загрузить модель с помощью huggingface cli:

$ huggingface-cli download cognitivecomputations/dolphin-2.9.4-llama3.1-8b-gguf dolphin-2.9.4-llama3.1-8b-Q4_0.gguf --local-dir . --local-dir-use-symlinks False

Формат модели GGUF, представленный командой llama.cpp, использует сжатие и квантование для уменьшения точности весов до 4-битных целых чисел, что значительно снижает требования к вычислительным ресурсам и памяти и делает процессоры Arm CPU эффективными для вывода LLM.

Переквантование весов модели

Для повторного квантования выполните команду

$ ./llama-quantize --allow-requantize dolphin-2.9.4-llama3.1-8b-Q4_0.gguf dolphin-2.9.4-llama3.1-8b-Q4_0_8_8.gguf Q4_0_8_8

В результате будет создан новый файл dolphin-2.9.4-llama3.1-8b-Q4_0_8_8.gguf, содержащий перенастроенные веса, которые позволяют llama-cli использовать SVE 256 и поддержку MATMUL_INT8.

Эта реквантизация оптимальна именно для Гравитона3. Для Гравитона2 оптимальная реквантизация должна быть выполнена в формате Q4_0_4_4, а для Гравитона4 наиболее подходящим для реквантизации является формат Q4_0_4_8.

Запуск службы LLM

Вы можете использовать серверную программу llama.cpp и отправлять запросы через OpenAI-совместимый API. Это позволяет разрабатывать приложения, которые многократно взаимодействуют с LLM без необходимости многократно запускать и останавливать его. Кроме того, вы можете получить доступ к серверу с другой машины, на которой LLM размещен по сети.

Запустите сервер из командной строки, и он будет слушать порт 8080:

$ ./llama-server -m dolphin-2.9.4-llama3.1-8b-Q4_0_8_8.gguf -n 2048 -t 64 -c 65536  --port 8080
'main: server is listening on 127.0.0.1:8080 - starting the main loop

Вы также можете настроить параметры запущенного LLM, чтобы адаптировать его к оборудованию вашего сервера для достижения идеальной производительности. Дополнительные сведения о параметрах см. в команде llama-server --help.

Если вам сложно выполнить этот шаг, вы можете обратиться к официальным документам для получения дополнительной информации.

Вы запустили службу LLM на вашем процессоре на базе Arm. Далее мы напрямую взаимодействуем с сервисом с помощью OpenAI SDK.

Онлайн RAG

Клиент LLM и модель встраивания

Мы инициализируем LLM-клиент и подготовим модель встраивания.

Для LLM мы используем OpenAI SDK, чтобы запросить запущенный ранее сервис Llama. Нам не нужно использовать какой-либо API-ключ, потому что на самом деле это наш локальный сервис llama.cpp.

from openai import OpenAI

llm_client = OpenAI(base_url="http://localhost:8080/v1", api_key="no-key")

Сгенерируйте тестовое вложение и выведите его размерность и первые несколько элементов.

test_embedding = embedding_model.embed_query("This is a test")
embedding_dim = len(test_embedding)
print(embedding_dim)
print(test_embedding[:10])
384
[0.03061249852180481, 0.013831384479999542, -0.02084377221763134, 0.016327863559126854, -0.010231520049273968, -0.0479842908680439, -0.017313342541456223, 0.03728749603033066, 0.04588735103607178, 0.034405000507831573]

Получение данных для запроса

Давайте зададим частый вопрос о Milvus.

question = "How is data stored in milvus?"

Найдем этот вопрос в коллекции и получим семантические топ-3 совпадения.

search_res = milvus_client.search(
    collection_name=collection_name,
    data=[
        embedding_model.embed_query(question)
    ],  # Use the `emb_text` function to convert the question to an embedding vector
    limit=3,  # Return top 3 results
    search_params={"metric_type": "IP", "params": {}},  # Inner product distance
    output_fields=["text"],  # Return the text field
)

Давайте посмотрим на результаты поиска по этому запросу.

import json

retrieved_lines_with_distances = [
    (res["entity"]["text"], res["distance"]) for res in search_res[0]
]
print(json.dumps(retrieved_lines_with_distances, indent=4))
[
    [
        " Where does Milvus store data?\n\nMilvus deals with two types of data, inserted data and metadata. \n\nInserted data, including vector data, scalar data, and collection-specific schema, are stored in persistent storage as incremental log. Milvus supports multiple object storage backends, including [MinIO](https://min.io/), [AWS S3](https://aws.amazon.com/s3/?nc1=h_ls), [Google Cloud Storage](https://cloud.google.com/storage?hl=en#object-storage-for-companies-of-all-sizes) (GCS), [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs), [Alibaba Cloud OSS](https://www.alibabacloud.com/product/object-storage-service), and [Tencent Cloud Object Storage](https://www.tencentcloud.com/products/cos) (COS).\n\nMetadata are generated within Milvus. Each Milvus module has its own metadata that are stored in etcd.\n\n###",
        0.6488019824028015
    ],
    [
        "How does Milvus flush data?\n\nMilvus returns success when inserted data are loaded to the message queue. However, the data are not yet flushed to the disk. Then Milvus' data node writes the data in the message queue to persistent storage as incremental logs. If `flush()` is called, the data node is forced to write all data in the message queue to persistent storage immediately.\n\n###",
        0.5974207520484924
    ],
    [
        "What is the maximum dataset size Milvus can handle?\n\n  \nTheoretically, the maximum dataset size Milvus can handle is determined by the hardware it is run on, specifically system memory and storage:\n\n- Milvus loads all specified collections and partitions into memory before running queries. Therefore, memory size determines the maximum amount of data Milvus can query.\n- When new entities and and collection-related schema (currently only MinIO is supported for data persistence) are added to Milvus, system storage determines the maximum allowable size of inserted data.\n\n###",
        0.5833579301834106
    ]
]

Использование LLM для получения ответа RAG

Преобразуйте полученные документы в строковый формат.

context = "\n".join(
    [line_with_distance[0] for line_with_distance in retrieved_lines_with_distances]
)
Define system and user prompts for the Language Model. This prompt is assembled with the retrieved documents from Milvus.

SYSTEM_PROMPT = """
Human: You are an AI assistant. You are able to find answers to the questions from the contextual passage snippets provided.
"""
USER_PROMPT = f"""
Use the following pieces of information enclosed in <context> tags to provide an answer to the question enclosed in <question> tags.
<context>
{context}
</context>
<question>
{question}
</question>
"""

Используйте LLM для создания ответа на основе подсказок. Мы установили параметр model в значение not-used, поскольку он является избыточным параметром для службы llama.cpp.

response = llm_client.chat.completions.create(
    model="not-used",
    messages=[
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": USER_PROMPT},
    ],
)
print(response.choices[0].message.content)

Milvus stores data in two types: inserted data and metadata. Inserted data, including vector data, scalar data, and collection-specific schema, are stored in persistent storage as incremental log. Milvus supports multiple object storage backends such as MinIO, AWS S3, Google Cloud Storage (GCS), Azure Blob Storage, Alibaba Cloud OSS, and Tencent Cloud Object Storage (COS). Metadata are generated within Milvus and each Milvus module has its own metadata that are stored in etcd.

Поздравляем! Вы создали приложение RAG на базе инфраструктуры Arm.

Попробуйте Managed Milvus бесплатно

Zilliz Cloud работает без проблем, поддерживается Milvus и в 10 раз быстрее.

Начать
Обратная связь

Была ли эта страница полезной?