五分钟——在ML Studio里跑基于ACS的RAG聊天

   检索增强生成 (Retrieval Augmented Generation, RAG) [1] 可能是最现实的 GPT 落地场景之一吧。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第1张图片

   最近已经各个群里看到很多次在问如何使用自己的文件数据来让ChatGPT回答问题。所以在介绍了Azure OpenAI的“Add your data“(添加您的数据)实现RAG和Azure ML Studio的在线搜索提示流之后,我就在想是不是也尝试一下 Azure ML Studio 的“Bring You Own Data QnA”(用您自己的数据来问答)。

   一开始,我打算直接从提示流的向导里创建“Bring Your Own Data QnA”的克隆,就像之前我们创建“Chat With Wikipedia”一样。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第2张图片

   后来发现克隆这个提示流之前必须要创建矢量索引,不然的话…示例提示流是无法运行的…默认“search_question_from_indexed_docs”组件模块里path的字符串就是“https://ragsample.blob.core.windows.net/ragdata/index“,这可以看成是一个占位符,要用实际的索引存储来替换。实际生成的矢量索引路径形如:azureml://subscriptions/{subscription id}/resourcegroups/{resource group name}/providers/Microsoft.MachineLearningServices/workspaces/{ml workspace name}/data/{index name}/versions/1

   所以,从创建矢量索引开始吧。

创建ACS矢量索引

   创建提示流之前,先创建“矢量索引”。我们的矢量索引准备使用认知搜索,所以首先需要创建一个到Azure认知搜索的连接。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第3张图片

   如果还没有在Azure中创建Azure Cognitive Search认知搜索资源,请先创建一个非免费定价层的。然后在提示流中,点击“连接”页签,点击“创建”下拉按钮,然后选择“Cognitive Search”。在向导中输入连接的名字,选择提供程序为“Cognitive search”,然后输入认知搜索的API Key。注意要使用Admin的管理Key而不是Query的查询Key,否则后续创建索引会失败。然后输入API的终结点地址,保存连接。

   接下来我们创建矢量索引。五分钟——在ML Studio里跑基于ACS的RAG聊天_第4张图片

   创建新的矢量索引可以使用本地存储(这个本地应该指的Azure里,因为上传的数据会存在Blob存储)或注册的数据资产(意味着之前处理过的数据可以标记为数据资产然后复用,例如我们后面创建成功的矢量索引)。

   我们先来创建一个使用“本地存储”的Azure认知搜索的矢量索引吧。五分钟——在ML Studio里跑基于ACS的RAG聊天_第5张图片

   和之前我们在AOAI Studio的不太一样,这个向导是直接选择上传文件夹的。文件将会被上传到一个Blob存储容器,然后再进行处理。跟着向导下一步就是提供Azure OpenAI的连接。创建Azure OpenAI的连接和之前文章里的步骤一样,如果之前已经创建过,这里就可以直接选择使用了。Azure OpenAI连接让矢量索引可以调用Embedding模型。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第6张图片

   然后我们需要确定使用的计算资源。默认使用无服务器计算(serverless),也可以选择计算群集。但计算群集目前不能从已经创建的计算群集中选择,而选择无服务器计算又会有个坑(我会在文章最后说明)。不想创建计算群集,这里我们还是先选择无服务器计算。五分钟——在ML Studio里跑基于ACS的RAG聊天_第7张图片

   准备工作基本基本就差不多了。审阅一下配置没有什么问题就可以开始创建矢量索引。点击“创建”按钮之后,Azure ML Studio实际上会提交一个作业(Job)来完成整个矢量索引的创建过程。对于这个过程,机器学习工作室会像以前我们所熟悉的一样,构建一个管道线来执行。自动生成的矢量索引作业管道线图如下:五分钟——在ML Studio里跑基于ACS的RAG聊天_第8张图片

    可以看到,上传的文件首先被存放在存储容器,然后对这些文件(示例上传的是PDF文件)进行破解和裁剪。在管道线的右上角,组件会对提供的AOAI连接进行验证,以确保是否支持 Chat 的模型和 Embedding 的模型。然后管道线会将这两个组件模块的输出提供给并行生成Embedding的组件模块,对分块的文件进行嵌入,得到矢量空间。完成后,就可以使用矢量数据在ACS认知搜索创建矢量索引。创建完成后,我们就得到了可以使用的矢量索引“acs-index”,这还没有结束,管道线会继续运行,将生成的矢量索引注册为矢量索引资产,便于其他过程来复用。最后,利用生成的矢量索引,管道线会自动创建提示流。

    创建完成的矢量索引可以查看整个作业的详细信息,可以查看索引数据(在Azure ML使用的Blob存储中),也可以直接打开关联的示例提示流。同时也会显示整个管道线各作业模块的完成情况,是否出错等等。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第9张图片

使用提示流管道线

   我们的目的是创建提示流,所以理所当然地点击“示例提示流”看看生成的提示流是不是正常工作。五分钟——在ML Studio里跑基于ACS的RAG聊天_第10张图片

   打开提示流,在输入(inputs)组件处输入我们的问题,然后运行整个提示流。

“embed_the_question”组件模块会首先调用AOAI中支持 embedding 的模型来做嵌入。因为我们的查询是基于矢量空间余弦相似度来进行的,所以要把提问转为嵌入的矢量来和矢量索引进行比较。可以看到这一组件模块的输出已经是1536维的矢量。

    下一模块“search_question_from_indexed_docs”会在我们已经准备好的矢量索引中查找。请注意,这里的 path 参数不再是我们开头说的”占位符“,而是真实的经过索引之后的路径。改变 top_k 会对向量搜索产生影响。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第11张图片

   在这一组件模块的输出,可以看到基于矢量索引搜索到的结果,例如搜索到的文档的 url。这里的文档 url 已经是 blob 存储的别名路径。在后续使用这些数据来交谈的时候,就需要这些 url 来指示数据来源。

   有了矢量搜索结果,接下来就可以生成提示内容了。在“generate_prompt_context”组件模块中,将使用Python代码,将搜索结果转变为可以用作对话提示的文本。这个文本包含内容和来源两部分信息,并且不同条目间使用换行间隔。

from typing import List
from promptflow import tool
from embeddingstore.core.contracts import SearchResultEntity


@tool
def generate_prompt_context(search_result: List[dict]) -> str:
    def format_doc(doc: dict):
        return f"Content: {doc['Content']}\nSource: {doc['Source']}"
    
    SOURCE_KEY = "source"
    URL_KEY = "url"
    
    retrieved_docs = []
    for item in search_result:
        entity = SearchResultEntity.from_dict(item)
        content  = entity.text or ""
        
        source = ""
        if entity.metadata is not None:
            if SOURCE_KEY in entity.metadata:
                if URL_KEY in entity.metadata[SOURCE_KEY]:
                    source = entity.metadata[SOURCE_KEY][URL_KEY] or ""
        
        retrieved_docs.append({
            "Content": content,
            "Source": source
        })
    doc_string = "\n\n".join([format_doc(doc) for doc in retrieved_docs])
    return doc_string

    解释代码我还是有请我的 Github Copilot X 吧。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第12张图片

    运行完这个组件模块之后,就会输出一大段文本,可以看到,来源的文件 url 和前面提到的 Blob 存储路径一致。五分钟——在ML Studio里跑基于ACS的RAG聊天_第13张图片

   有了内容和来源,就可以构造提示了。默认的“Prompt_varients”提示变体有三个。以运行的示例来看,相同的矢量搜索结果发给了三个不同的提示变体:“gpt_similarity”、“gpt_relevance”、“bert_f1”。我暂时没有找到这三个不同的变体是否有不同的配置,只看到提示模板有些细微的差别。为了简化生成,可以让三个都使用相同的默认变体:“gpt_similarity”。五分钟——在ML Studio里跑基于ACS的RAG聊天_第14张图片

   最后的组件模块“anwser_the_sample_flow”就很简单了,直接使用生成的 {prompt_text} 来和 ChatGPT 模型聊天,然后生成类似如下的输出:

{
  "system_metrics": {
    "completion_tokens": 197,
    "duration": 6.635436,
    "prompt_tokens": 1754,
    "total_tokens": 1951
  },
  "output": "Mixture-of-Experts (MoE) models are a promising 
            architecture for reducing the training cost of 
            giant dense models. DeepSpeed-MoE is an end-to-end 
            MoE training and inference solution as part of the 
            DeepSpeed library, including novel MoE architecture 
            designs and model compression techniques that reduce 
            MoE model size by up to 3.7x, and a highly optimized 
            inference system that provides 7.3x better latency 
            and cost compared to existing MoE inference solutions. 
            DeepSpeed-MoE offers an unprecedented scale and 
            efficiency to serve massive MoE models with up to 4.5x 
            faster and 9x cheaper inference compared to quality
            -equivalent dense models. (Source: azureml://locations
            /westus/workspaces/1b77f538-220c-4360-882c-2ed4d5a6b403
            /data/vector-index-input-1689252064483/versions/1
            /2201.05596.pdf)"
}

   如果我们前面选的是三个不一样的变体,这里就会按照三个不同的提示模板给出三个不同的输出。

   至此,就实现了利用矢量索引实现RAG的提示流管道线。

对过程的探索

   Azure ML Studio的提示流使用图形化的管道线来降低我们使用提示流的门槛,所以大量的处理和代码运行躲在了图形界面之后。我对管道线运行时后面发生的事情很有兴趣,因此也尝试通过查看作业的信息来了解机器学习工作室到底在做什么。五分钟——在ML Studio里跑基于ACS的RAG聊天_第15张图片

   首先看看文本分块。尽管没找到代码,但是从log能看到是使用了azureml.rag.crack_and_chunk的方法对文件进行了拆解和分块。在机器学习工作室使用的工作空间的 Blob 存储中可以看到这些分块的CSV文件。

   这里管道线用到的azureml-rag 库已经发布在 PyPI 上,地址是:https://pypi.org/project/azureml-rag 主要的作用就是通过嵌入实现对ACS和FAISS的矢量索引。五分钟——在ML Studio里跑基于ACS的RAG聊天_第16张图片

   获得分块文件之后,对作业管道线的输出进行查看,可以发现生成的嵌入数据(基本不可读)以及记录。通过记录我们可以看到,调用text- embedding-ada-002类型的模型进行了嵌入处理,生成了一个1536维的矢量。

   是不是对作业管道线中组件感到很有趣?其实这些组件都是基于azureml-assets 中的组件描述来定义和绘制的。比如我们刚看到的 拆解和分块,就能在 large_language_models 大语言模型分支下面的 rag 组件找到 crack_and_chunk 组件,然后可以查看其定义的YAML文件 [2] 。

那些踩过的坑
  • 认知搜索Key的问题

   这个坑在前面提到过,因为使用了查询而不是管理的Key,导致创建索引失败。

  • 计算资源的问题

   在创建向量搜索的Job的时候,默认会使用无服务器。但是当作业管道线运行到“embeddings_parallel_job”时,会提示AzureML Compute 作业失败。我猜是由于使用的无服务器计算资源不能满足要求。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第17张图片

   在作业运行的界面点击“重新提交”,可以修改运行参数重新运行管道线,在这个向导里的“运行时设置”页,可以更换默认计算的资源,比如我先更换到了计算实例,结果由于 SKU 太低(我选的最便宜的VM定价层),磁盘空间不足再次失败了。我又懒得重新创建一个新的计算实例,就用之前创建的带有GPU的计算群集又跑了一遍。这一遍就没问题了,就是我有点心疼烧的钱~

  • ACS索引大小写的问题

   这个坑就有点意思…开始创建矢量索引的时候,按照命名习惯给了一个大写缩写组成的名称,然后发现一到调用认知搜索API的时候就出错了。

五分钟——在ML Studio里跑基于ACS的RAG聊天_第18张图片

   从这个提示压根就看不出啥问题,所以,让我直接访问这个API的URL,看看返回的信息试试:

{"error":
  {"code":"InvalidName",
    "message":"Index name must only contain lowercase letters, 
       digits or dashes, cannot start or end with dashes and 
       is limited to 128 characters.",
    "details":[
      {"code":"InvalidIndexName",
       "message":"Index name must only contain lowercase 
          letters, digits or dashes, cannot start or end with 
          dashes and is limited to 128 characters."
      }
    ]
  }
}

   提示认知搜索服务的索引只能使用小写。换成小写重新创建,就没问题了。我得跟PG反馈一下,界面上对矢量索引名称做个拼写检查或者构造URL的时候自动转换小写啥,省得浪费时间和算力…

[1]: 检索增强生成(Retrieval Augmented Generation,RAG)- https://learn.microsoft.com/azure/machine-learning/concept-retrieval-augmented-generation?view=azureml-api-2&WT.mc_id=AI-MVP-33253

[2]: 作业管道线组件模块YAML文件 - https://github.com/Azure/azureml-assets/blob/main/assets/large_language_models/rag/components/crack_and_chunk/spec.yaml

你可能感兴趣的:(五分钟——在ML Studio里跑基于ACS的RAG聊天)