🚀 免費嘗試 Zilliz Cloud,完全托管的 Milvus,體驗速度提升 10 倍!立即嘗試

milvus-logo
LFAI
主頁
  • 整合
    • 其他

在 Arm 架構上建立 RAG

ArmCPU 廣泛應用於各式各樣的應用程式,包括傳統的機器學習 (ML) 和人工智能 (AI) 用例。

在本教程中,您將學習如何在 Arm 架構的基礎架構上建立檢索增強世代 (RAG) 應用程式。在向量儲存方面,我們利用Zilliz Cloud 這個完全由 Milvus 管理的向量資料庫。Zilliz Cloud 可在 AWS、GCP 和 Azure 等主要雲端上使用。在這個示範中,我們使用 Zilliz Cloud 部署在 AWS 上的 Arm 機器。對於 LLM,我們使用llama.cpp 在 AWS Arm 架構的伺服器 CPU 上建立Llama-3.1-8B 模型。

前提條件

若要執行本範例,我們建議您使用AWS Graviton,它提供了在基於 Arm 的伺服器上執行 ML 工作負載的高成本效益方式。本筆記本已在使用 Ubuntu 22.04 LTS 系統的 AWS Graviton3c7g.2xlarge 實例上進行測試。

您至少需要四個核心和 8GB 記憶體才能執行本範例。配置磁碟儲存至少達 32 GB。我們建議您使用相同或更好規格的實例。

啟動實例後,連線到該實例,並執行下列指令準備環境。

在伺服器上安裝 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

離線資料載入

建立資料集

我們使用部署在 AWS 上的Zilliz Cloud與 Arm 型機器來儲存與擷取向量資料。若要快速上手,只需在 Zilliz Cloud 免費註冊一個帳號

除了 Zilliz Cloud 之外,自我託管的 Milvus 也是一個選擇(設定較複雜)。我們也可以在以 ARM 為基礎的機器上部署Milvus StandaloneKubernetes。有關 Milvus 安裝的詳細資訊,請參閱安裝說明文件

我們在 Zilliz Cloud 設定uritokenPublic Endpoint 和 Api key

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
)

我們使用內乘距離作為預設的度量類型。如需更多關於距離類型的資訊,您可以參考相似度量頁面

準備資料

我們使用Milvus 文件 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 載入所有 markdown 檔案。對於每個文件,我們只需簡單地使用「#」來分隔文件中的內容,這樣就可以大致分隔出 markdown 文件中每個主要部分的內容。

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]

在 Arm 上啟動 LLM 服務

在本節中,我們將在 Arm-based CPU 上建立並啟動llama.cpp 服務。

Llama 3.1 模型與 llama.cpp

Meta 的Llama-3.1-8B 模型屬於 Llama 3.1 模型系列,可免費用於研究與商業用途。在使用該模型之前,請造訪 Llama網站並填寫表格以申請存取權限。

llama.cpp是一個開放原始碼的 C/C++ 專案,可在各種硬體上實現高效率的 LLM 推論 - 包括本機和雲端。您可以使用llama.cpp 方便地託管 Llama 3.1 模型。

下載並建立 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 只會在 Linux 和 Windows 上為 CPU 建立。您不需要提供任何額外的開關,就可以針對您所執行的 Arm CPU 建立它。

執行make 以建立它:

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

執行 help 指令檢查llama.cpp 是否已正確建立:

$ ./llama-cli -h

如果llama.cpp 已正確建立,您會看到幫助選項顯示。輸出片段看起來像這樣:

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

由 llama.cpp 團隊推出的 GGUF 模型格式,使用壓縮和量化將權值精確度降低為 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 支援。

此重新量化特別針對 Graviton3 而言是最佳的。對於 Graviton2,最佳的重新量化應以Q4_0_4_4 格式執行,而對於 Graviton4,Q4_0_4_8 格式最適合重新量化。

啟動 LLM 服務

您可以利用 llama.cpp 伺服器程式,並透過 OpenAI 相容的 API 傳送請求。這可讓您開發與 LLM 多次互動的應用程式,而無須重複啟動和停止 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 指令。

如果您在執行此步驟時感到吃力,可以參考官方文件以獲得更多資訊。

您已經在 Arm-based CPU 上啟動了 LLM 服務。接下來,我們直接使用 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?"

在集合中搜尋該問題,並擷取語義上前三名的符合資料。

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.

恭喜您!您已經在以 Arm 為基礎的基礎架構之上建立了一個 RAG 應用程式。

免費嘗試托管的 Milvus

Zilliz Cloud 無縫接入,由 Milvus 提供動力,速度提升 10 倍。

開始使用
反饋

這個頁面有幫助嗎?