暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

通义QwQ-32B+Milvus,消费级显卡布满血大模型与RAG的时代来了!

ZILLIZ 3天前
10

前言

最近,通义开源的QwQ-32B模型可谓是火的一塌糊涂。

作为一个中型推理模型,QwQ-32B只有320亿参数,但却在多个基准测试中展现出优秀的推理能力,几乎直逼满血版DeepSeek R1,在数学计算、写作与代码编程方面的表现更是相当不错。

最重要的是,QwQ-32B不仅性能强大,还极其“亲民”,它体积小、推理快,支持消费级显卡部署,像RTX 4090这样的显卡就能轻松运行,非常适合普通个人开发者或者资源不足的科研党上手学习。

不过,由于QwQ-32B采用的是密集模型,相比DeepSeek R1,在长文本复杂推理上,经常会出现无法识别前面内容,或者幻觉问题

因此,在本地场景中,基于QwQ-32B部署RAG就成了解决这美中不足的最强外挂。

接下来,在本篇文章中,我们会基于Ollama开源平台,手把手向大家展示如何利用QwQ-32B和Milvus高效、安全地构建RAG(检索增强生成)系统。

01

选型思路

(1)QwQ-32B VS DeepSeek R1如何选择

同为主打推理的大模型,DeepSeek-R1的架构为MoE结构,QwQ-32B则属于典型的密集模型。

通常来说,MoE模型更加适合知识密集型场景,例如知识问答系统、信息检索等,以及大规模数据处理,例如在处理大规模文本数据、图像数据时,MoE模型可以通过不同专家处理不同的数据子集,提高处理效率。

但MoE庞大的参数量决定了,部署满血大模型需要在云端或者自有服务器资源上运行。

而密集模型体积小,计算成本高,更适合需要深度连贯推理的场景,例如复杂的逻辑推理任务、深度阅读理解等,以及复杂算法设计等对实时性要求不高的场景。最大的优点在于非常适合本地部署,但有时候,也会出现废话过多的情况。

对比维度

密集模型(QwQ-32B)

MoE模型(DeepSeek-R1)

优点

训练难度相对低,过程简单直接

推理连贯性好,所有神经元都参与计算和推理,能整体把握上下文信息

计算效率高,推理时只需激活部分专家

模型容量大,可通过增加专家数量扩展

缺点

计算成本高,训练和推理需大量计算资源和内存

模型容量扩展受限,易过拟合,存储部署成本高

训练复杂,需训练门控网络,考虑专家负载平衡

存在路由开销,门控网络路由选择有额外计算和时间消耗

密集模型和 MoE 模型各有其独特的优势和局限性,不存在绝对的优劣之分。在实际应用中,我们需要根据具体的任务需求、数据特点、计算资源和预算等因素来选择合适的模型架构。

不过,在我看来,未来的发展趋势可能是将两者的优势相结合,例如在一些复杂的任务中,可以先使用 MoE 模型进行初步的知识检索和粗粒度的处理,然后再利用密集模型进行深度的推理和细化,以达到更好的性能。

(2)向量数据库选型

考虑到QwQ-32B容易出现幻觉的问题,我们可以在其本地部署过程中,引入Milvus 开源向量数据库。Milvus 专为存储和查询高维向量(如 XLNet 生成的向量)而生,能够处理数百万乃至数十亿级的向量数据,并支持混合检索、全文检索等一众特性需求,是 GitHub 上最受欢迎的向量数据库。

(3)部署平台选型

本次部署,平台方面,我们可以考虑采用Ollama开源平台。作为一个为专大型语言模型(LLM)提供本地运行和管理的解决方案,Ollama可以简化部署和管理流程,允许用户通过简单的命令行工具和Docker集成快速部署模型,同时支持Modelfile管理来简化模型版本控制和复用。其次,它提供了丰富的模型库,并具备跨平台与硬件适配的特点,支持macOS、Linux、Windows及Docker容器部署,并能自动检测GPU并优先启用加速。此外,Ollama提供了开发者友好的工具,如REST API和Python SDK,方便将模型集成到应用中。

02

准备工作

环境

    ! pip install pymilvus ollama
    复制
    复制

    数据集准备

    我们可以使用Milvus文档2.4. x中的FAQ页面作为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("# ")
        复制
        复制

        准备 LLM 和Embedding模型

        Ollama支持基于LLM的任务和Embedding的多种模型,从而可以轻松开发检索增强生成(RAG)应用程序。对于这种设置:

        • 我们将使用QwQ(32B)作为文本生成任务的LLM。

        • 对于Embedding模型,我们将使用mxbay-embed-size,这是一个针对语义相似性优化的334M参数模型。

        在开始之前,确保两个模型都被下载到本地:

          ! ollama pull mxbai-embed-large
          复制
          复制
            ! ollama pull qwq
            复制
            复制

            准备好这些模型后,我们可以开始LLM生成和基于Embedding的检索工作流程。

            生成测试embedding并打印其维度和前几个元素。

              import ollama
              from ollama import Client


              ollama_client = Client(host="http://localhost:11434")
              def emb_text(text):
              response = ollama_client.embeddings(model="mxbai-embed-large", prompt=text)
              return response["embedding"]
              #test
              test_embedding = emb_text("This is a test")
              embedding_dim = len(test_embedding)
              print(embedding_dim)
              print(test_embedding[:10])
              复制
              复制
              复制
                1024
                [0.23217937350273132, 0.42540550231933594, 0.19742339849472046, 0.4618139863014221, -0.46017369627952576, -0.14087969064712524, -0.18214142322540283, -0.07724273949861526, 0.40015509724617004, 0.8331164121627808]


                复制
                将数据加载到Milvus
                复制

                创建集合

                  from pymilvus import MilvusClient


                  milvus_client = MilvusClient(uri="./milvus_demo.db")


                  collection_name = "my_rag_collection"
                  复制
                  复制

                  关于MilvusClient的参数设置:

                  • 将URI设置为本地文件(例如../milvus.db)是最便捷的方法,因为它会自动使用Milvus Lite将所有数据存储在该文件中。

                  • 如果你有大规模数据,可以在Docker或Kubernetes上搭建性能更强的Milvus服务器。在这种情况下,请使用服务器的URI(例如http://localhost:19530)作为你的URI。

                  • 如果你想使用Zilliz Cloud(Milvus的全托管云服务),请调整URI和令牌,它们分别对应Zilliz Cloud中的公共端点(Public Endpoint)和API密钥(Api key)。

                  检查集合是否已经存在,如果存在则将其删除。

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

                    创建一个具有指定参数的新集合。

                    如果未指定任何字段信息,Milvus将自动创建一个默认的ID字段作为主键,以及一个向量字段用于存储向量数据。一个预留的JSON字段用于存储未在模式中定义的字段及其值。

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

                      插入数据

                      逐行遍历文本,创建嵌入向量,然后将数据插入Milvus。

                      下面是一个新的字段text,它是集合中的一个未定义的字段。它将自动创建一个对应的text字段(实际上它底层是由保留的JSON动态字段实现的) ,你不用关心其底层实现。


                      复制
                        from tqdm import tqdm


                        data = []


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


                        milvus_client.insert(collection_name=collection_name, data=data)


                        复制
                        复制

                        复制
                          Creating embeddings: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████| 72/72 [00:06<00:00, 11.86it/s]


                          {'insert_count'72'ids': [01234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071], 'cost'0}




                          复制

                          03

                          构建 RAG

                          检索数据

                          我们来指定一个关于Milvus的常见问题。

                            question = "How is data stored in milvus?"
                            复制
                            复制

                            在集合中搜索该问题,并检索语义上最匹配的前3个结果。

                              search_res = milvus_client.search(
                              collection_name=collection_name,
                              data=[
                              emb_text(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###",
                                  231.9922637939453
                                  ],
                                  [
                                  "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###",
                                  226.54090881347656
                                  ],
                                  [
                                  "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###",
                                  210.63682556152344
                                  ]
                                  ]
                                  复制
                                  复制

                                  使用大型语言模型(LLM)构建检索增强生成(RAG)响应

                                  将检索到的文档转换为字符串格式。

                                  复制
                                    context = "\n".join(
                                    [line_with_distance[0] for line_with_distance in retrieved_lines_with_distances]
                                    )
                                    复制

                                    为大语言模型提供系统提示(system prompt)和用户提示(user prompt)。这个提示是通过从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>
                                      """
                                      复制
                                      复制

                                      使用Ollama提供的QwQ(32B)模型,根据提示生成响应。

                                        from ollama import chat
                                        from ollama import ChatResponse


                                        response: ChatResponse = ollama_client.chat(
                                        model="qwq",
                                        messages=[
                                        {"role": "system", "content": SYSTEM_PROMPT},
                                        {"role": "user", "content": USER_PROMPT},
                                        ],
                                        )
                                        print(response["message"]["content"])
                                        复制
                                        复制
                                        复制
                                          <think>
                                          Okay, I need to answer the question "How is data stored in Milvus?" using the provided context. Let me start by reading through the context carefully.


                                          First, looking at the first context section titled "Where does Milvus store data?". It mentions that Milvus handles two types of data: inserted data and metadata.


                                          Inserted data includes vectors, scalar data, and collection schema, stored as incremental logs in persistent storage. The supported backends are listed like MinIO, S3, GCS, etc. So I should note that inserted data goes to these object storages via incremental logs.


                                          Then metadata is stored in etcd for each Milvus module. That's straightforward—metadata in etcd.


                                          The second context about flushing data explains that data first goes to the message queue and then to persistent storage when flushed. But this might be more about the process rather than where it's stored, so maybe mention that insertion is into the message queue first before being written out.


                                          Third section on maximum dataset size mentions hardware constraints: memory for queryable data and storage for persistence. This could relate to how data is managed in terms of memory vs disk. The user asked "how is data stored," which might include both where (like object stores, etcd) and the process (message queue then persistent logs).


                                          Putting it all together, the answer should mention:


                                          - Two types: inserted data and metadata.


                                          For inserted data:
                                          - Stored as incremental logs in persistent storage using backends like MinIO, S3 etc.
                                          - Initially loaded into message queues, then written to disk. The flush() forces immediate writing if needed.


                                          Metadata:
                                          - Stored in etcd for each module.


                                          Also, the hardware part says that memory limits queryable data size and storage (like the object stores) limit total dataset size. So maybe mention these factors as part of how data is stored, considering capacity constraints.


                                          I should structure this clearly, perhaps bullet points or sections on inserted vs metadata, then processes like flushing, and finally limitations based on hardware.
                                          </think>


                                          Data in Milvus is stored in two main ways:


                                          1. **Inserted Data**:
                                          - Includes vector data, scalar data, and collection schema.
                                          - Stored as **incremental logs** in **persistent storage**, supported by object storage backends such as MinIO, AWS S3, Google Cloud Storage (GCS), Azure Blob Storage, Alibaba Cloud OSS, and Tencent Cloud Object Storage (COS).
                                          - Initially loaded into a **message queue** when inserted. Milvus returns success upon insertion into the queue but does not immediately write to disk. A background process later writes this data to persistent storage. The `flush()` method can force immediate writing of all queued data to storage.


                                          2. **Metadata**:
                                          - Generated internally by Milvus modules (e.g., collection configurations, partitions).
                                          - Stored in **etcd**, a distributed key-value store.


                                          **Hardware Considerations**:
                                          - **Memory**: The amount of data Milvus can query is limited by system memory since it loads specified collections/partitions into memory for queries.
                                          - **Storage Capacity**: The maximum dataset size is constrained by the underlying storage backend (e.g., object storage), which stores all inserted data and schema incrementally.


                                          复制

                                          以上就是一个基于Milvus和Ollama构建检索增强生成(RAG)的全流程。

                                          作者介绍

                                          王舒虹

                                          Zilliz Social Media Advocate

                                          推荐阅读

                                          文章转载自ZILLIZ,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                          评论