Triton Inference Server 架构原理

打印 上一主题 下一主题

主题 987|帖子 987|积分 2961

上篇文章举行了 TensorRT-LLM & Triton Server 部署 ,本篇简单讲讲 Triton Inference Server 的架构原理,便于大家更好的做设置和开发。
  TensorRT-LLM & Triton Server 部署回顾

部署梳理

上一篇我们了解到部署 TRT-LLM & Triton 需要非常复杂的步骤,其实总的来说一共就四步:1. 下载模型 2. 转换&编译模型(TensorRT) 3. 设置 backend(Triton) 4. 启动 Triton Server


  • 下载模型
  1. # 下载模型
  2. wget https://hf-mirror.com/hfd/hfd.sh
  3. chmod a+x hfd.sh
  4. export HF_ENDPOINT=https://hf-mirror.com
  5. apt-get update
  6. apt-get install -y aria2
  7. aria2c --version
  8. ./hfd.sh meta-llama/Meta-Llama-3.1-8B-Instruct --hf_username Dong-Hua --hf_token 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 aria2c -x 4
  9. # 断点续传
  10. aria2c --header='Authorization: Bearer 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' --console-log-level=error --file-allocation=none -x 4 -s 4 -k 1M -c 'https://hf-mirror.com/meta-llama/Meta-Llama-3.1-8B-Instruct/resolve/main/model-00002-of-00004.safetensors' -d '.' -o 'model-00002-of-00004.safetensors'
复制代码


  • 转换&编译模型(TensorRT)
  1. # 转换格式
  2. python3 convert_checkpoint.py --model_dir /data/Meta-Llama-3.1-8B-Instruct \
  3.                             --output_dir ./trt_ckpts/llama3.1_checkpoint_gpu_tp \
  4.                             --dtype float16 \
  5.                             --tp_size 1 \
  6.                             --workers 8
  7. # 编译模型
  8. trtllm-build --checkpoint_dir ./trt_ckpts/llama3.1_checkpoint_gpu_tp \
  9.              --output_dir ./trt_engines/llama3.1_8B_128K_fp16_1-gpu \
  10.              --workers 8 \
  11.              --gemm_plugin auto \
  12.              --max_num_tokens 131072
复制代码
该步骤得到的是两个文件,一个编译后的模型引擎文件,一个是模型设置文件:



  • 设置 backend(Triton)
  1. # 在进行配置前,你需要先创建 triton_model_repo,并且把上一步骤的两个文件移入对应的 repo 下面:
  2. cd tensorrtllm_backend
  3. mkdir triton_model_repo
  4. cp -r all_models/inflight_batcher_llm/* triton_model_repo/
  5. cp /data/TensorRT-LLM/examples/llama/trt_engines/llama3.1_8B_128K_fp16_1-gpu/* triton_model_repo/tensorrt_llm/1
  6. # 进行 backend 配置
  7. python3 tools/fill_template.py -i triton_model_repo/postprocessing/config.pbtxt \
  8. tokenizer_dir:/data/Meta-Llama-3.1-8B-Instruct,\
  9. tokenizer_type:auto,\
  10. triton_max_batch_size:64,\
  11. postprocessing_instance_count:1
  12. python3 tools/fill_template.py -i triton_model_repo/preprocessing/config.pbtxt \
  13. tokenizer_dir:/data/Meta-Llama-3.1-8B-Instruct,\
  14. tokenizer_type:auto,\
  15. triton_max_batch_size:64,\
  16. preprocessing_instance_count:1
  17. python3 tools/fill_template.py -i triton_model_repo/tensorrt_llm_bls/config.pbtxt \
  18. triton_max_batch_size:64,\
  19. decoupled_mode:true,\
  20. bls_instance_count:1
  21. python3 tools/fill_template.py -i triton_model_repo/ensemble/config.pbtxt \
  22. triton_max_batch_size:64
  23. python3 tools/fill_template.py -i triton_model_repo/tensorrt_llm/config.pbtxt \
  24. triton_backend:tensorrtllm,\
  25. triton_max_batch_size:64,\
  26. decoupled_mode:true,\
  27. engine_dir:triton_model_repo/tensorrt_llm/1,\
  28. max_queue_delay_microseconds:10000,\
  29. batching_strategy:inflight_fused_batching
复制代码
此步骤的设置是通过脚本填充的方式修改设置,其实也可以进对应的文件举行设置,可以看到所有的设置改动都是修改的 triton_model_repo 目次下的,是的没错,Triton 的核心就是 repo 下面的 backends,本篇文章目的也是讲关于 backend 的内容。


  • 启动 Triton Server
  1. # 启动
  2. python3 scripts/launch_triton_server.py \
  3. --world_size 1 \
  4. --model_repo=/data/tensorrtllm_backend/triton_model_repo/ \
  5. --log \
  6. --log-file=./triton_server.logs
  7. # 关闭
  8. pkill tritonserver
复制代码
Triton 架构

下图显示了 Triton 推理服务器的高级架构。


  • 模型存储库是一个基于文件体系的存储库,其中包含 Triton 将用于推理的模型。
  • 推理请求通过 HTTP/REST 或 GRPC 或 C API 到达服务器,然后路由到适当的每个模型调度程序。
  • Triton 实现了多种调度和批处理惩罚算法,可以根据每个模型举行设置。
  • 每个模型的调度程序可以选择实验推理请求的批处理惩罚,然后将请求传递给与模型范例相对应的 backend 。 backend 使用批处理惩罚请求中提供的输入实验推理以天生请求的输出,然后返回输出。

为什么要使用 backend ?


如上图,在 Triton上面所有部署的模型都是通过某一种 backend 部署在服务器上,比方 TensorRT 的模型,就会使用 tensorrt 范例的 backend 去启一个或者多个的 model 实例,这些 model 实例放到 GPU 上或者 CPU 上实验推理。
triton_model_repo 目次结构

在部署的时间我们需要设置很多 backend 的设置文件,然后再启动 server,我们修改的设置文件都在 triton_model_repo 目次下,先看目次结构:
  1. (base) [root@iv-ycl6gxrcwka8j6ujk4bc tensorrtllm_backend]# tree triton_model_repo/
  2. triton_model_repo/
  3. ├── ensemble
  4. │   ├── 1
  5. │   └── config.pbtxt
  6. ├── postprocessing
  7. │   ├── 1
  8. │   │   ├── model.py
  9. │   │   └── __pycache__
  10. │   │       └── model.cpython-310.pyc
  11. │   └── config.pbtxt
  12. ├── preprocessing
  13. │   ├── 1
  14. │   │   ├── model.py
  15. │   │   └── __pycache__
  16. │   │       └── model.cpython-310.pyc
  17. │   └── config.pbtxt
  18. ├── tensorrt_llm
  19. │   ├── 1
  20. │   │   ├── config.json # 模型配置文件
  21. │   │   ├── model.py # backend 脚本
  22. │   │   ├── __pycache__
  23. │   │   │   └── model.cpython-310.pyc
  24. │   │   └── rank0.engine # 模型引擎文件
  25. │   └── config.pbtxt # backend 配置文件,部署时候修改的配置就是此文件
  26. └── tensorrt_llm_bls
  27.     ├── 1
  28.     │   ├── lib
  29.     │   │   ├── decode.py
  30.     │   │   ├── __pycache__
  31.     │   │   │   ├── decode.cpython-310.pyc
  32.     │   │   │   └── triton_decoder.cpython-310.pyc
  33.     │   │   └── triton_decoder.py
  34.     │   ├── model.py
  35.     │   └── __pycache__
  36.     │       └── model.cpython-310.pyc
  37.     └── config.pbtxt
复制代码
可以看到 triton_model_repo 仓库中有五个模型 backend,其中:


  • ensemble model:它允许你把多个模型串联在一起,形成一个 papline,只能处理惩罚静态请求
  • postprocessing model:前置处理惩罚
  • preprocessing model:后置处理惩罚
  • tensorrt_llm model:tensorrt 引擎部署的模型
  • tensorrt_llm_bls model:bls 模式,它允许有分支结构,可以举行动态的处理惩罚
每个模型文件都是类似如下结构:
  1. ├── *****
  2. │   ├── 1
  3. │   │   ├── config.json # 模型配置文件
  4. │   │   ├── model.py # backend 脚本
  5. │   │   ├── __pycache__
  6. │   │   │   └── model.cpython-310.pyc
  7. │   │   └── rank0.engine # 模型引擎文件
  8. │   └── config.pbtxt # backend 配置文件,部署时候修改的配置就是此文件
复制代码
在部署完之后你可以通过以下两种方式举行大模型请求:
  1. curl -X POST localhost:8000/v2/models/ensemble/generate -d '{"text_input": "你是什么语言模型?", "max_tokens": 1000, "bad_words": "", "stop_words": "<|eot_id|>"}'
  2. curl -X POST localhost:8000/v2/models/tensorrt_llm_bls/generate -d '{"text_input": "你是什么语言模型?", "max_tokens": 1000, "bad_words": "", "stop_words": "<|eot_id|>"}'
复制代码
标题来了,他们有什么区别呢?
Ensemble 模式

   Ensemble 不是一个模型,只是一个调度器。它允许把多个模型串联在一起,形成一个 pipeline,请求使用该模型的时间,只会根据设置的 pipeline 举行静态的调用处理惩罚。
  config.pbtxt 设置文件内容:
  1. # Copyright 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
  2. #
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions
  5. # are met:
  6. #  * Redistributions of source code must retain the above copyright
  7. #    notice, this list of conditions and the following disclaimer.
  8. #  * Redistributions in binary form must reproduce the above copyright
  9. #    notice, this list of conditions and the following disclaimer in the
  10. #    documentation and/or other materials provided with the distribution.
  11. #  * Neither the name of NVIDIA CORPORATION nor the names of its
  12. #    contributors may be used to endorse or promote products derived
  13. #    from this software without specific prior written permission.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
  16. # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  18. # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  19. # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  20. # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  21. # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  22. # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  23. # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. name: "ensemble"
  27. platform: "ensemble"
  28. max_batch_size: 64
  29. input [
  30.   {
  31.     name: "text_input"
  32.     data_type: TYPE_STRING
  33.     dims: [ 1 ]
  34.   },
  35.   ...
  36.   {
  37.     name: "embedding_bias_weights"
  38.     data_type: TYPE_FP32
  39.     dims: [ -1 ]
  40.     optional: true
  41.   }
  42. ]
  43. output [
  44.   {
  45.     name: "text_output"
  46.     data_type: TYPE_STRING
  47.     dims: [ -1 ]
  48.   },
  49.   ...
  50.   {
  51.     name: "batch_index"
  52.     data_type: TYPE_INT32
  53.     dims: [ 1 ]
  54.   }
  55. ]
  56. ensemble_scheduling {
  57.   step [
  58.     {
  59.       model_name: "preprocessing"
  60.       model_version: -1
  61.       input_map {
  62.         key: "QUERY"
  63.         value: "text_input"
  64.       }
  65.       ...
  66.     },
  67.     {
  68.       model_name: "tensorrt_llm"
  69.       model_version: -1
  70.       input_map {
  71.         key: "input_ids"
  72.         value: "_INPUT_ID"
  73.       }
  74.       ...
  75.     },
  76.     {
  77.       model_name: "postprocessing"
  78.       model_version: -1
  79.       input_map {
  80.         key: "TOKENS_BATCH"
  81.         value: "_TOKENS_BATCH"
  82.       }
  83.       ...
  84.     }
  85.   ]
  86. }
复制代码
可以看到设置文件中设置了 scheduling,当使用 ensemble 模型的时间,他的调用链就是根据设置的 scheduling 举行处理惩罚的,注意每个设置文件中都包含了输入和输出,以及下一个模型的输入输出映射(input_map),即上一个模型的输出对应为下一个模型某个字段的输入,上面列出来的内容作了省略处理惩罚,详细可自行查看设置文件。
BLS 模式

   有 Ensemble 的静态模式,必然有动态模式,BLS 就是动态模式,他和静态模式相比就多了个 model.py 脚本,事实上他的动态也是根据脚本中举行的逻辑判断实现的
  model.py 脚本架构:
  1. # Copyright 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
  2. #
  3. # Redistribution and use in source and binary forms, with or without
  4. # modification, are permitted provided that the following conditions
  5. # are met:
  6. #  * Redistributions of source code must retain the above copyright
  7. #    notice, this list of conditions and the following disclaimer.
  8. #  * Redistributions in binary form must reproduce the above copyright
  9. #    notice, this list of conditions and the following disclaimer in the
  10. #    documentation and/or other materials provided with the distribution.
  11. #  * Neither the name of NVIDIA CORPORATION nor the names of its
  12. #    contributors may be used to endorse or promote products derived
  13. #    from this software without specific prior written permission.
  14. #
  15. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
  16. # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  18. # PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  19. # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  20. # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  21. # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  22. # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  23. # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. import json
  27. import traceback
  28. import triton_python_backend_utils as pb_utils
  29. from lib.triton_decoder import TritonDecoder
  30. def get_valid_param_value(param, default_value=''):
  31.     value = param.get('string_value', '')
  32.     return default_value if value.startswith('${') or value == '' else value
  33. class TritonPythonModel:
  34.     def initialize(self, args):
  35.         # Parse model configs
  36.         model_config = json.loads(args['model_config'])
  37.         params = model_config['parameters']
  38.         accumulate_tokens_str = get_valid_param_value(
  39.             params.get('accumulate_tokens', {}))
  40.         self.accumulate_tokens = accumulate_tokens_str.lower() in [
  41.             'true', 'yes', '1', 't'
  42.         ]
  43.         self.decoupled = pb_utils.using_decoupled_model_transaction_policy(
  44.             model_config)
  45.         self.logger = pb_utils.Logger
  46.         default_tensorrt_llm_model_name = 'tensorrt_llm'
  47.         self.llm_model_name = get_valid_param_value(
  48.             params.get('tensorrt_llm_model_name', {}),
  49.             default_tensorrt_llm_model_name)
  50.         self.draft_llm_model_name = get_valid_param_value(
  51.             params.get('tensorrt_llm_draft_model_name', {}), None)
  52.         self.multimodal_encoders_name = get_valid_param_value(
  53.             params.get('multimodal_encoders_name', {}), None)
  54.         self.decoder = TritonDecoder(
  55.             streaming=self.decoupled,
  56.             accumulate=self.accumulate_tokens,
  57.             preproc_model_name="preprocessing",
  58.             postproc_model_name="postprocessing",
  59.             llm_model_name=self.llm_model_name,
  60.             draft_llm_model_name=self.draft_llm_model_name,
  61.             multimodal_encoders_name=self.multimodal_encoders_name)
  62.     def execute(self, requests):
  63.         responses = []
  64.         for request in requests:
  65.             if self.decoupled:
  66.                 response_sender = request.get_response_sender()
  67.             try:
  68.                 req = self.decoder.convert_triton_request(request)
  69.                 req.validate()
  70.                 speculative_decode = (req.num_draft_tokens is not None
  71.                                       and req.num_draft_tokens[0][0] > 0)
  72.                 if speculative_decode and (self.draft_llm_model_name is None
  73.                                            or self.draft_llm_model_name == ""):
  74.                     raise Exception(
  75.                         "cannot perform speculative decoding without draft model"
  76.                     )
  77.                 is_multimodal = req.image_input is not None
  78.                 if speculative_decode and is_multimodal:
  79.                     raise Exception(
  80.                         "Multimodal and speculative decoding is not currently supported"
  81.                     )
  82.                 res_gen = self.decoder.decode(
  83.                     req,
  84.                     speculative_decoding=speculative_decode,
  85.                     is_multimodal=is_multimodal)
  86.                 for res in res_gen:
  87.                     triton_response = self.decoder.create_triton_response(res)
  88.                     if self.decoupled:
  89.                         response_sender.send(triton_response)
  90.                     else:
  91.                         responses.append(triton_response)
  92.                 if self.decoupled:
  93.                     response_sender.send(
  94.                         flags=pb_utils.TRITONSERVER_RESPONSE_COMPLETE_FINAL)
  95.             except Exception:
  96.                 self.logger.log_error(traceback.format_exc())
  97.                 # If encountering an error, send a response with err msg
  98.                 error_response = pb_utils.InferenceResponse(
  99.                     output_tensors=[],
  100.                     error=pb_utils.TritonError(traceback.format_exc()))
  101.                 if self.decoupled:
  102.                     response_sender.send(error_response)
  103.                     response_sender.send(
  104.                         flags=pb_utils.TRITONSERVER_RESPONSE_COMPLETE_FINAL)
  105.                 else:
  106.                     responses.append(error_response)
  107.             self.decoder.reset_decoder()
  108.             if self.decoupled:
  109.                 return None
  110.             else:
  111.                 assert len(responses) == len(requests)
  112.                 return responses
  113.     def finalize(self):
  114.         """`finalize` is called only once when the model is being unloaded.
  115.         Implementing `finalize` function is optional. This function allows
  116.         the model to perform any necessary clean ups before exit.
  117.         """
  118.         print('Tensorrt_llm_bls Cleaning up...')
复制代码
所有的 model.py 文件都是需要实现上面的三个方法,BLS 模式的动态实现就是 execute 的内容,可以本身编码,遇到什么条件的时间举行不同的操作(或者不同的模型调用)实现动态处理惩罚。
根据上面 config.pbtxt 设置文件和 model.py 脚本的简单先容,大家也可以自行研究一下其他 backend 的对应文件。总之,我们在部署过程中需要举行的操作就是根据需要举行设置和代码修改,当然 triton_model_repo 仓库中并不是固定的上面这些 backend,而是可以根据需求自行增加或者删除。

系列文章:
一、大模型推理框架选型调研
二、TensorRT-LLM & Triton Server 部署过程纪录
三、vLLM 大模型推理引擎调研文档
四、vLLM 推理引擎性能分析基准测试
五、vLLM 部署大模型标题纪录
六、Triton Inference Server 架构原理

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

耶耶耶耶耶

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表