Press "Enter" to skip to content

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

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

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

  • 一个调整指令的LLM,从EleutherAI的GPT-NeoX-20B fine-tuned for chat,使用超过4300万指令在100%的负碳计算上。 GPT-NeoXT-Chat-Base-20B 模型基于EleutherAI的GPT-NeoX模型,并使用专注于对话式交互的数据进行了微调。
  • 定制配方,将模型微调以在您的任务上实现高精度。
  • 一个可扩展的检索系统,使您可以在推理时使用来自文档存储库、API或其他实时更新信息源的信息来增强机器人响应。
  • 一个调整自GPT-JT-6B的审核模型,旨在过滤机器人响应的问题。

深度学习模型越来越大,规模也越来越大,这给生成式AI应用程序的成功部署带来了障碍。为了满足低延迟和高吞吐量的需求,必须采用像模型并行和量化这样的复杂方法。缺乏这些方法的应用程序熟练度,许多用户在启动用于生成式AI用例的大型模型的托管时遇到困难。

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

可扩展检索系统

可扩展检索系统是OpenChatKit的关键组件之一。它使您可以根据封闭领域知识库自定义机器人响应。尽管LLM能够在其模型参数中保留事实知识,并在微调时能够在下游NLP任务上取得显着的性能,但它们访问和准确预测封闭领域知识的能力仍受到限制。因此,当它们面对知识密集型任务时,它们的性能受到特定任务体系结构的限制。您可以使用OpenChatKit检索系统从外部知识源(例如维基百科、文档存储库、API和其他信息源)中增强其响应中的知识。

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

审核模型

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

OpenChatKit拥有一个6亿参数的审查模型GPT-JT-Moderation-6B,可以对聊天机器人进行审查,限制输入内容到被审查的主题。虽然该模型本身已经具有一些审查功能,但TogetherComputer使用Ontocord.ai的OIG-moderation数据集训练了一个GPT-JT-Moderation-6B模型。该模型与主聊天机器人一起运行,检查用户输入和机器人的回答是否包含不当结果。您还可以使用此功能检测聊天机器人的任何超出领域的问题,并在问题不属于聊天机器人的领域时进行覆盖。

以下图示说明了OpenChatKit工作流程。

使用Amazon SageMaker上的OpenChatkit模型构建自定义聊天机器人应用程序 AI 新闻 第1张

可扩展的检索系统用例

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

解决方案概述

使用OpenChatKit模型构建聊天机器人并将其部署到SageMaker需要以下步骤:

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

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

下载OpenChatKit模型

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

from huggingface_hub import snapshot_download
from pathlib import Path
import os
# - 这将在运行jupyter notebook的当前目录中下载模型
local_model_path = Path("./openchatkit")
local_model_path.mkdir(exist_ok=True)
model_name = "togethercomputer/GPT-NeoXT-Chat-Base-20B"
# 只下载pytorch检查点文件
allow_patterns = ["*.json", "*.pt", "*.bin", "*.txt", "*.model"]
# - 利用快照库下载模型,因为该模型使用LFS存储在存储库中
chat_model_download_path = snapshot_download(
    repo_id=model_name,#用户或组织名称和存储库名称
    cache_dir=local_model_path,#缓存文件存储的文件夹路径。
    allow_patterns=allow_patterns,#只下载至少符合一个模式的文件。
)

DJL服务属性

您可以使用SageMaker LMI容器托管具有自定义推理代码的大型生成AI模型,而无需提供自己的推理代码。当输入数据没有自定义预处理或模型预测的后处理时,这非常有用。您也可以使用自定义推理代码部署模型。在本文中,我们演示如何使用自定义推理代码部署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 设置为托管在 huggingface.co 模型存储库内的预训练模型的模型 ID ( https://huggingface.co/models )。容器使用此模型 ID 下载相应的模型存储库。
  • option.tensor_parallel_degree – 将其设置为 DeepSpeed 需要对模型进行分区的 GPU 设备数量。此参数还控制 DJL Serving 运行时将启动的每个模型的工作程序数量。例如,如果我们有一台 8 GPU 的机器,我们正在创建 8 个分区,则我们将每个模型拥有一个工作程序以服务请求。有必要调整并确定给定模型体系结构和硬件平台的最佳值。我们称这种能力为推理适应并行性。

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

OpenChatKit 模型

OpenChatKit 基础模型实现具有以下四个文件:

  • model.py – 此文件实现了主要 OpenChatKit GPT-NeoX 模型的处理逻辑。它接收推理输入请求,加载模型,加载维基百科索引,并提供响应。请参阅 model.py (笔记本的一部分)获取更多详细信息。 model.py 使用以下关键类:
    • OpenChatKitService – 处理 GPT-NeoX 模型、Faiss 搜索和对话对象之间传递数据的任务。初始化 WikipediaIndexConversation 对象,并将输入聊天对话发送到索引中以搜索来自维基百科的相关内容。如果没有提供唯一 ID,则还会为每次调用生成一个唯一 ID,以便将提示存储在 Amazon DynamoDB 中。
    • ChatModel – 该类加载模型和分词器,并生成响应。它使用 tensor_parallel_degree 在多个 GPU 上对模型进行分区,并配置 dtypesdevice_map。将提示传递给模型以生成响应。针对生成的停止条件 StopWordsCriteria 进行配置,以便只在推理时生成机器人响应。
    • ModerationModel – 我们在 ModerationModel 类中使用了两个审查模型:输入模型用于指示聊天模型输入不合适,以覆盖推理结果;输出模型用于覆盖推理结果。我们将输入提示和输出响应分类为以下可能的标签:
      • casual
      • needs caution
      • needs intervention(模型标记此处需要审查)
      • possibly needs caution
      • probably needs caution
  • wikipedia_prepare.py – 此文件处理下载和准备维基百科索引。在本文中,我们使用 Hugging Face 数据集提供的维基百科索引。要搜索维基百科文档以获取相关文本,需要从 Hugging Face 下载索引,因为它没有打包到其他地方。当导入时,wikipedia_prepare.py 文件负责处理下载。只有多个正在运行推理的进程中的一个进程可以克隆存储库。其余进程等待,直到文件在本地文件系统中存在。
  • wikipedia.py – 此文件用于搜索维基百科索引以获取上下文相关文档。将输入查询进行分词并使用 mean_pooling 创建嵌入。我们计算查询嵌入与维基百科索引之间的余弦相似度距离度量,以检索上下文相关的维基百科句子。请参阅 wikipedia.py 以获取实现详细信息。
#使用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

#使用cos_sim_2d函数计算两个嵌入之间的余弦相似度距离
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 笔记本电脑构建容器映像。

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

创建模型

现在我们已经在 Amazon ECR 中拥有 Docker 镜像,可以继续为 OpenChatKit 模型创建 SageMaker 模型对象。我们使用 GPT-NeoXT-Chat-Base-20B 输入和输出调节模型,使用 GPT-JT-Moderation-6B 进行部署。有关更多详细信息,请参阅 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以生成响应。在此步骤中,模型使用维基百科索引检索与提示相关的上下文相关部分,以从模型获取特定于域的响应。最后,将模型响应发送到输出审核模型以进行分类检查,然后返回响应。请参见以下代码:

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 = "数据工程师做什么?"
chat(prompts)

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

使用Amazon SageMaker上的OpenChatkit模型构建自定义聊天机器人应用程序 AI 新闻 第2张

清理

按照本文的清理部分中的说明删除本文提供的资源,以避免不必要的费用。有关推理实例成本的详细信息,请参见Amazon SageMaker Pricing。

结论

在本文中,我们讨论了开源LLMs的重要性以及如何在SageMaker上部署OpenChatKit模型以构建下一代聊天机器人应用程序。我们讨论了OpenChatKit模型、审核模型的各个组件以及如何使用外部知识源(如维基百科)进行检索增强生成(RAG)工作流。您可以在GitHub笔记本中找到逐步说明。请告诉我们您正在构建的惊人聊天机器人应用程序。干杯!

Leave a Reply

Your email address will not be published. Required fields are marked *