亚马逊AWS官方博客

在 Amazon SageMaker 上使用 OpenChatkit 模型构建自定义聊天机器人应用程序

开源大型语言模型(LLM)已经变得流行起来,研究人员、开发人员和组织都可以使用这些模型来促进创新和实验。这促进了开源社区开展合作,从而为 LLM 的开发和改进做出贡献。开源 LLM 提供了模型架构、训练过程和训练数据的透明度,使研究人员能够了解模型的工作原理,识别潜在的偏见并解决伦理问题。这些开源 LLM 通过向广大用户提供先进的自然语言处理(NLP)技术来构建任务关键型业务应用程序,从而使生成式人工智能大众化。GPT-NeoX、LLaMA、Alpaca、GPT4All、Vicuna、Dolly 和 OpenAssistant 是一些受欢迎的开源 LLM。

OpenChatKit 是用于构建通用和专用聊天机器人应用程序的开源 LLM,由 Together Computer 于 2023 年 3 月发布,采用 Apache-2.0 许可。这种模型允许开发人员对聊天机器人的行为进行更多控制,并根据聊天机器人的特定应用进行定制。OpenChatKit 提供了一套工具、基础机器人和构建块,用于构建完全定制的、功能强大的聊天机器人。关键组件如下:

  • 经过指令调优的 LLM,针对来自 EleutherAI 的 GPT-NeX-20B 的聊天进行了微调,有超过 4300 万条关于 100% 负碳计算的指令。GPT-NeoXT-Chat-Base-20B 模型基于 EleutherAI 的 GPT-NeoX 模型,并根据对话式交互的数据进行了微调。
  • 自定义配方,可对模型进行微调以实现任务的高精度。
  • 可扩展的检索系统,使您能够在推理时使用来自文档存储库、API 或其他实时更新信息源的信息来增强机器人响应。
  • 根据 GPT-JT-6B 微调的审核模型,旨在筛选机器人会回答哪些问题。

深度学习模型的规模和大小不断扩大,给在生成式人工智能应用中成功部署这些模型带来了障碍。为了满足低延迟和高吞吐量的要求,采用模型并行化和量化等复杂方法变得至关重要。由于缺乏对这些方法的熟练应用,许多用户在为生成式人工智能使用案例启动大型模型托管时遇到了困难。

在这篇文章中,我们将展示如何使用 DJL Serving 以及 DeepSpeed 和 Hugging Face Accelerate 等开源模型并行库,在 Amazon SageMaker 上部署 OpenChatKit 模型(GPT-NeXT-Chat-Base-20B 和 GPT-JT-Moderation-6B 模型)。我们使用 DJL Serving,这是一种高性能的通用模型服务解决方案,由与编程语言无关的 Deep Java Library(DJL)提供支持。我们将演示 Hugging Face Accelerate 库如何简化大型模型在多个 GPU 中的部署,从而减轻以分布式方式运行 LLM 的负担。我们开始吧!

可扩展的检索系统

可扩展的检索系统是 OpenChatKit 的关键组件之一。该组件使您能够根据封闭的领域知识库定制机器人的响应。尽管 LLM 能够在模型参数中保留事实知识,并且在微调后可以在下游 NLP 任务中取得不俗的表现,但这种模型准确获取和预测封闭领域知识的能力仍然受到限制。因此,当遇到知识密集型任务时,这种模型的性能就会比任务特定架构的性能差。您可以使用 OpenChatKit 检索系统,从外部知识来源(例如 Wikipedia、文档存储库、API 和其他信息源)中扩充回复中的知识。

检索系统使聊天机器人能够通过获取与特定查询相关的详细信息来获取当前信息,从而为模型生成答案提供必要的上下文。为了说明该检索系统的功能,我们提供了对 Wikipedia 文章索引的支持,并提供了示例代码,演示如何调用 Web 搜索 API 进行信息检索。按照提供的文档,您可以在推理过程中将检索系统与任何数据集或 API 集成,这样聊天机器人就能在回复中纳入动态更新的数据。

审核模型

审核模型在聊天机器人应用中非常重要,可用于执行内容筛选、质量控制、用户安全以及法律和合规原因。审核是一项非常困难的主观任务,在很大程度上取决于聊天机器人应用的领域。OpenChatKit 提供的工具可用于控制聊天机器人应用程序,并监控输入文本提示是否有任何不当内容。审核模型提供了一个很好的基准,可以根据各种需求进行调整和定制。

OpenChatKit 有一个 60 亿个参数的审核模型,即 GPT-JT-Moderation-6B,可对聊天机器人进行控制,将输入限制在受控制的主题范围内。虽然模型本身内置了一些控制功能,但 TogetherComputer 还是使用 Ontocord.ai 的 OIG-moderation 数据集训练了一个 GPT-JT-Moderation-6B 模型。该模型与主聊天机器人同时运行,以检查用户输入和机器人回答是否包含不恰当的结果。您还可以使用该模型来检测向聊天机器人提出的任何域外问题,并在问题不属于聊天机器人的领域时进行覆盖。

下图说明了 OpenChatKit 的工作流程。

可扩展检索系统使用案例

虽然我们可以在各行各业应用这种技术来构建生成式人工智能应用程序,但在本篇文章中,我们将讨论金融行业的使用案例。检索式增强生成功能可用于金融研究,自动生成有关特定公司、行业或金融产品的研究报告。通过从内部知识库、财务档案、新闻报道和研究论文中检索相关信息,您可以生成综合报告,总结重要洞察、财务指标、市场趋势和投资建议。您可以使用此解决方案来监控和分析财经新闻、市场情绪和趋势。

解决方案概览

使用 OpenChatKit 模型构建聊天机器人并将这种模型部署到 SageMaker 上的步骤如下:

  1. 下载聊天基础模型 GPT-NeoXT-Chat-Base-20B,并将模型构件打包上传到 Amazon Simple Storage Service(Amazon S3)。
  2. 使用 SageMaker 大型模型推理(LMI)容器,配置属性,并设置自定义推理代码来部署该模型。
  3. 配置模型并行技术,并在 DJL Serving 属性中使用推理优化库。我们将使用 Hugging Face Accelerate 作为 DJL Serving 的引擎。此外,我们还定义了张量并行配置来对模型进行分区。
  4. 创建 SageMaker 模型和端点配置,然后部署 SageMaker 端点。

您可以通过在 GitHub 存储库中运行笔记本来继续操作。

下载 OpenChatKit 模型

首先,我们下载 OpenChatKit 基础模型。我们使用 huggingface_hub,并使用 snapshot_download 下载模型,这将下载给定版本的整个存储库。同时进行下载,以便加快进度。请参阅以下代码:

from huggingface_hub import snapshot_download
from pathlib import Path
import os
# - This will download the model into the current directory where ever the jupyter notebook is running
local_model_path = Path("./openchatkit")
local_model_path.mkdir(exist_ok=True)
model_name = "togethercomputer/GPT-NeoXT-Chat-Base-20B"
# Only download pytorch checkpoint files
allow_patterns = ["*.json", "*.pt", "*.bin", "*.txt", "*.model"]
# - Leverage the snapshot library to donload the model since the model is stored in repository using LFS
chat_model_download_path = snapshot_download(
    repo_id=model_name,#A user or an organization name and a repo name 
    cache_dir=local_model_path, #Path to the folder where cached files are stored.
    allow_patterns=allow_patterns, #only files matching at least one pattern are downloaded.
)

DJL Serving 属性

您可以使用 SageMaker LMI 容器托管带有自定义推理代码的大型生成式人工智能模型,而无需提供自己的推理代码。在没有对输入数据进行自定义预处理或对模型预测进行后处理的情况下,这种方法非常有用。您也可以使用自定义推理代码部署模型。在这篇文章中,我们将演示如何使用自定义推理代码部署 OpenChatKit 模型。

SageMaker 要求模型构件采用 tar 格式。我们使用以下文件创建每个 OpenChatKit 模型:serving.propertiesmodel.py

serving.properties 配置文件向 DJL Serving 指明了要使用哪些模型并行化和推理优化库。以下是我们在此配置文件中使用的设置列表:

openchatkit/serving.properties
engine = Python
option.tensor_parallel_degree = 4
option.s3url = {{s3url}}

其中包含以下参数:

  • engine – DJL 要使用的引擎。
  • option.entryPoint – Python 文件或模块的入口点。这应该与使用的引擎一致。
  • option.s3url – 将此参数设置为包含模型的 S3 存储桶的 URI。
  • option.modelid – 如果想从 huggingface.co 下载模型,可以将 option.modelid 设置为一个预训练模型的模型 ID,该模型托管在 huggingface.co(https://huggingface.co/models)上的模型存储库中。容器使用此模型 ID 在 huggingface.co 上下载相应的模型存储库。
  • option.tensor_parallel_degree – 将此参数设置为 DeepSpeed 需要对模型进行分区的 GPU 设备数量。该参数还可以控制 DJL Serving 运行时每个模型启动的 Worker 数量。例如,如果我们有一台配备 8 个 GPU 的计算机,并创建八个分区,那么每个模型将有一个 Worker 来处理请求。有必要调整并行度,并确定给定模型架构和硬件平台的最佳值。我们将这种能力称为推理适应并行性

有关详尽的选项列表,请参阅配置和设置

OpenChatKit 模型

OpenChatKit 基础模型实现包含以下四个文件:

  • model.py – 此文件实现了 OpenChatKit GPT-NeoX 主模型的处理逻辑。此文件接收推理输入请求,加载模型,加载 Wikipedia 索引,并提供响应。更多详情,请参阅 model.py(notebook 的创建部分)。model.py 使用以下关键类:
    • OpenChatKitService – 此类处理 GPT-NeoX 模型、Faiss 搜索和对话对象之间的数据传递。WikipediaIndexConversation 对象经过初始化,输入的聊天会话被发送到索引,以便从 Wikipedia 中搜索相关内容。如果没有提供用于在 Amazon DynamoDB 中存储提示信息的 ID,此类还会为每次调用生成唯一 ID。
    • ChatModel – 此类加载模型和 tokenizer 并生成响应。此类使用 tensor_parallel_degree 处理多个 GPU 之间的模型分区,并配置 dtypesdevice_map。提示信息将传递给模型以生成响应。为生成操作配置了停止标准 StopWordsCriteria,以便在推理时只生成机器人响应。
    • ModerationModel – 我们在 ModerationModel 类中使用两种审核模型:输入模型,用于向聊天模型表明输入不适合覆盖推理结果;输出模型,用于覆盖推理结果。我们使用以下可能的标签对输入提示和输出响应进行分类:
      • 随意
      • 需要谨慎
      • 需要干预(这被标记为由模型控制)
      • 可能需要谨慎
      • 也许需要谨慎
  • wikipedia_prepare.py – 此文件用于下载和准备 Wikipedia 索引。在这篇文章中,我们使用 Hugging Face 数据集上提供的 Wikipedia 索引。要在 Wikipedia 文档中搜索相关文本,需要从 Hugging Face 下载索引,因为其他地方没有打包索引。wikipedia_prepare.py 文件负责在导入时处理下载。在运行推理的多个进程中,只有一个进程可以克隆存储库。其余的则要等到文件出现在本地文件系统中。
  • wikipedia.py – 此文件用于在 Wikipedia 索引中搜索与上下文相关的文档。输入查询经过标记化处理,并使用 mean_pooling 创建嵌入内容。我们计算查询嵌入与 Wikipedia 索引之间的余弦相似度距离指标,以检索与上下文相关的 Wikipedia 句子。有关实施的详细信息,请参阅 wikipedia.py
#function to create sentence embedding using mean_pooling
def mean_pooling(token_embeddings, mask):
    token_embeddings = token_embeddings.masked_fill(~mask[..., None].bool(), 0.0)
    sentence_embeddings = token_embeddings.sum(dim=1) / mask.sum(dim=1)[..., None]
    return sentence_embeddings

#function to compute cosine similarity distance between 2 embeddings   
def cos_sim_2d(x, y):
    norm_x = x / np.linalg.norm(x, axis=1, keepdims=True)
    norm_y = y / np.linalg.norm(y, axis=1, keepdims=True)
    return np.matmul(norm_x, norm_y.T)

  • conversation.py – 此文件用于在 DynamoDB 中存储和检索对话线程,以便传递给模型和用户。conversation.py 改编自开源 OpenChatKit 存储库。此文件负责定义存储人类和模型之间对话轮次的对象。这样,模型就能为对话保留一个会话,让用户可以参考以前的信息。由于 SageMaker 端点调用是无状态的,因此需要将此对话存储在端点实例外部的位置。启动时,如果 DynamoDB 表不存在,实例会创建该表。然后,会根据端点生成的 session_id 键将对话的所有更新存储在 DynamoDB 中。任何带有会话 ID 的调用都将检索关联的对话字符串,并根据需要进行更新。

使用自定义依赖项构建 LMI 推理容器

索引搜索使用 Facebook 的 Faiss 库进行相似性搜索。由于基本 LMI 映像中不包含该库,因此需要调整容器以安装该库。以下代码定义了一个 Dockerfile,用于从源代码中安装 Faiss 以及机器人端点所需的其他库。我们使用 sm-docker 实用程序从 Amazon SageMaker Studio 构建映像,并将映像推送到 Amazon Elastic Container Registry(Amazon ECR)。有关更多详细信息,请参阅使用 Amazon SageMaker Studio Image Build CLI 从 Studio notebook 构建容器映像

DJL 容器没有安装 Conda,因此需要从源代码克隆和编译 Faiss。要安装 Faiss,需要安装使用 BLAS API 和 Python 支持的依赖项。安装这些软件包后,Faiss 配置为使用 AVX2 和 CUDA,然后再使用安装的 Python 扩展进行编译。

之后会安装 pandasfastparquetboto3git-lfs,因为下载和读取索引文件时需要它们。

FROM 763104351884.dkr.ecr.us-east-1.amazonaws.com/djl-inference:0.21.0-deepspeed0.8.0-cu117
ARG FAISS_URL=https://github.com/facebookresearch/faiss.git
RUN apt-get update && apt-get install -y git-lfs wget cmake pkg-config build-essential apt-utils
RUN apt search openblas && apt-get install -y libopenblas-dev swig
RUN git clone $FAISS_URL && \
cd faiss && \
cmake -B build . -DFAISS_OPT_LEVEL=avx2 -DCMAKE_CUDA_ARCHITECTURES="86" && \
make -C build -j faiss && \
make -C build -j swigfaiss && \
make -C build -j swigfaiss_avx2 && \
(cd build/faiss/python && python -m pip install )

RUN pip install pandas fastparquet boto3 && \
git lfs install --skip-repo && \
apt-get clean all

创建模型

现在我们在 AmazonECR 中有了 Docker 映像,我们可以继续为 OpenChatKit 模型创建 SageMaker 模型对象。我们使用 GPT-JT-Moderation-6B 部署 GPT-NeoXT-Chat-Base-20B 输入和输出审核模型。有关更多详细信息,请参阅 create_model

from sagemaker.utils import name_from_base

chat_model_name = name_from_base(f"gpt-neoxt-chatbase-ds")
print(chat_model_name)

create_model_response = sm_client.create_model(
    ModelName=chat_model_name,
    ExecutionRoleArn=role,
    PrimaryContainer={
        "Image": chat_inference_image_uri,
        "ModelDataUrl": s3_code_artifact,
    },
)
chat_model_arn = create_model_response["ModelArn"]

print(f"Created Model: {chat_model_arn}")

配置端点

接下来,我们为 OpenChatKit 模型定义端点配置。我们使用 ml.g5.12xlarge 实例类型部署模型。有关更多详细信息,请参阅 create_endpoint_config

chat_endpoint_config_name = f"{chat_model_name}-config"
chat_endpoint_name = f"{chat_model_name}-endpoint"

chat_endpoint_config_response = sm_client.create_endpoint_config(
    EndpointConfigName=chat_endpoint_config_name,
    ProductionVariants=[
        {
            "VariantName": "variant1",
            "ModelName": chat_model_name,
            "InstanceType": "ml.g5.12xlarge",
            "InitialInstanceCount": 1,
            "ContainerStartupHealthCheckTimeoutInSeconds": 3600,
        },
    ],
)

部署端点

最后,我们使用在前面步骤中定义的模型和端点配置创建端点:

chat_create_endpoint_response = sm_client.create_endpoint(
EndpointName=f"{chat_endpoint_name}", EndpointConfigName=chat_endpoint_config_name
)
print(f"Created Endpoint: {chat_create_endpoint_response['EndpointArn']},")

从 OpenChatKit 模型运行推理

现在是向模型发送推理请求并获取响应的时候了。我们传递输入文本提示和模型参数,例如 temperaturetop_kmax_new_tokens。聊天机器人响应的质量取决于指定的参数,因此建议根据这些参数对模型性能进行基准测试,以找到适合您使用案例的最佳设置。输入提示首先发送到输入审核模型,然后将输出发送到 ChatModel 以生成响应。在这一步中,模型使用 Wikipedia 索引检索与模型上下文相关的部分,以此作为从模型获取特定领域响应的提示。最后,将模型响应发送到输出审核模型以检查分类情况,然后返回响应。请参阅以下代码:

def chat(prompt, session_id=None, **kwargs):
    if session_id:
        chat_response_model = smr_client.invoke_endpoint(
            EndpointName=chat_endpoint_name,
            Body=json.dumps(
                {
                    "inputs": prompt,
                    "parameters": {
                        "temperature": 0.6,
                        "top_k": 40,
                        "max_new_tokens": 512,
                        "session_id": session_id,
                        "no_retrieval": True,
                    },
                }
            ),
            ContentType="application/json",
        )
    else:
        chat_response_model = smr_client.invoke_endpoint(
            EndpointName=chat_endpoint_name,
            Body=json.dumps(
                {
                    "inputs": prompt,
                    "parameters": {
                        "temperature": 0.6,
                        "top_k": 40,
                        "max_new_tokens": 512,
                    },
                }
            ),
            ContentType="application/json",
        )
    response = chat_response_model["Body"].read().decode("utf8")
    return response
prompts = "What does a data engineer do?"
chat(prompts)

请参阅下面的聊天互动示例。

清理

按照清理部分中的说明删除作为本文一部分预置的资源,以避免不必要的费用。有关推理实例费用的详细信息,请参阅 Amazon SageMaker 定价

总结

在这篇文章中,我们讨论了开源 LLM 的重要性,以及如何在 SageMaker 上部署 OpenChatKit 模型来构建新一代聊天机器人应用程序。我们讨论了 OpenChatKit 模型的各种组件、审核模型,以及如何使用 Wikipedia 等外部知识源进行检索式增强生成(RAG,Retrieval Augmented Generation)工作流程。您可以在 GitHub notebook 中找到分步说明。请告诉我们您正在构建的令人惊叹的聊天机器人应用程序。下次再见!


Original URL: https://aws.amazon.com/blogs/machine-learning/build-custom-chatbot-applications-using-openchatkit-models-on-amazon-sagemaker/

关于作者

Dhawal Patel 是 AWS 的首席机器学习架构师。他一直就职于从大型企业到中型初创企业等组织,致力于解决与分布式计算和人工智能有关的问题。他专注于深度学习,包括 NLP 和计算机视觉领域。他帮助客户在 SageMaker 上实现了高性能模型推理。

Vikram Elango 是AWS 的高级 AIML 专业解决方案架构师,工作地点在美国弗吉尼亚州。他目前专注于生成式人工智能、LLM、即时工程、大型模型推理优化以及在企业中推广机器学习。Vikram 凭借设计和思想领导力协助金融和保险行业客户大规模构建和部署机器学习应用程序。业余时间,他喜欢与家人一起旅行、远足、烹饪和露营。

Andrew Smith 是 AWS 的 SageMaker、Vision 及其他团队的云支持工程师,工作地点在澳大利亚悉尼。他掌握 Amazon SageMaker 方面的专业知识,协助客户使用 AWS 上的许多人工智能/机器学习服务。工作之余,他喜欢与朋友和家人共度时光,也喜欢学习不同的技术。