【深度学习】大模型GLM-4-9B Chat ,微调与部署(3) TensorRT-LLM、TensorRT量化加速、Triton部署

文章目录

  • 获取TensorRT-LLM代码:
  • 构建docker镜像并安装TensorRT-LLM:
  • 运行docker镜像:
  • 安装依赖
  • 魔改下部分package代码:
  • 量化:
  • 构建图:
      • 全局参数
      • 插件配置
      • 常用配置参数
  • 测试推理是否可以
    • 代码推理
    • CLI推理
  • 性能测试
  • 小结
  • 验证是否严重退化
  • 使用NVIDIA Triton部署在线推理服务器
    • 代码弄下来
    • 编译镜像
    • 启动容器
    • 安装依赖
    • 量化
    • 构建trt engines图
    • Triton 模板
      • 说明
      • 实操
    • 发起Triton服务
    • 请求服务试试
      • 示例说明
    • 模型分析器
    • 关闭Triton服务
    • metric
  • 重看Triton服务

官方文档:
https://nvidia.github.io/TensorRT-LLM/quick-start-guide.html#launch-the-docker

参考资料源:
https://swanhub.co/ZhipuAI/ChatGLM3/blob/main/tensorrt_llm_demo/README.md

https://github.com/THUDM/GLM-4/issues/132

TensorRT-LLM官方目前说在v0.12.0版本才会支持GLM-4-9B Chat ,下面以v0.10.0的魔改来做GLM-4-9B Chat的量化部署。

本文目标就是加速GLM-4-9B Chat,看看能多快。A100卡。

获取TensorRT-LLM代码:

# TensorRT-LLM 代码需要使用 git-lfs 拉取
apt-get update && apt-get -y install git git-lfsgit clone https://github.com/NVIDIA/TensorRT-LLM.git
cd TensorRT-LLM# 本流程将使用 v0.10.0 Release 版本
git checkout tags/v0.10.0 -b release/0.10.0
git submodule update --init --recursive
git lfs install
git lfs pull

构建docker镜像并安装TensorRT-LLM:

这是编译,要魔法。可以跳过这一条看下一节“运行docker镜像”。

make -C docker release_build

运行docker镜像:

这是有源码的简易启动方式:

make -C docker release_run

我编译好上传了这个镜像,所以我这么启动:

docker run -it --ipc=host --ulimit memlock=-1 --ulimit stack=67108864  \--gpus=all \--volume /ssd/xiedong/glm-4-9b-xd:/ssd/xiedong/glm-4-9b-xd \--env "CCACHE_DIR=/code/tensorrt_llm/cpp/.ccache" \--env "CCACHE_BASEDIR=/code/tensorrt_llm" \--workdir /app/tensorrt_llm \--hostname euler-MS-7D30-release \--tmpfs /tmp:exec \kevinchina/deeplearning:tensorrt_llm

在这里插入图片描述

安装依赖

cd /ssd/xiedong/glm-4-9b-xd/TensorRT-LLM/examples/chatglm
pip install -r requirements.txt
pip install tiktoken -i https://pypi.tuna.tsinghua.edu.cn/simple

魔改下部分package代码:

vim /usr/local/lib/python3.10/dist-packages/modelopt/torch/export/model_config_export.py

export_tensorrt_llm_checkpoint
model=model -> model=model.cpu() 24G显存是真的会爆的

vim /usr/local/lib/python3.10/dist-packages/tensorrt_llm/models/chatglm/model.py

ChatGLMForCausalLM > check_config
config.set_if_not_exist(‘chatglm_version’, ‘chatglm3’) -> config.chatglm_version = “chatglm3” 强制套用chatglm3相关代码

量化:

cd /ssd/xiedong/glm-4-9b-xd/TensorRT-LLMCUDA_VISIBLE_DEVICES=2 python examples/quantization/quantize.py --model_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat --dtype float16 --qformat int4_awq --output_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq --batch_size 8 

让我们逐一详细介绍quantize.py中的参数,包括它们的默认值、含义以及所有可能的值。

  1. --model_dir:

    • 默认值: None
    • 含义: 指定HuggingFace模型所在的目录。
    • 所有可能的值: 任意有效的文件路径。
  2. --nemo_ckpt_path:

    • 默认值: None
    • 含义: 指定NeMo模型检查点文件所在的路径。
    • 所有可能的值: 任意有效的文件路径。
  3. --decoder_type:

    • 默认值: 'gptnext'
    • 含义: 解码器类型,仅对NeMo检查点有效。
    • 所有可能的值: 'gptnext', 'llama'
  4. --device:

    • 默认值: 'cuda'
    • 含义: 运行校准的设备,仅对HuggingFace模型有效。
    • 所有可能的值: 'cuda', 'cpu'
  5. --calib_dataset:

    • 默认值: 'cnn_dailymail'
    • 含义: 用于校准的HuggingFace数据集名称或本地数据集目录。
    • 所有可能的值: 任意有效的HuggingFace数据集名称或本地路径。
  6. --calib_tp_size:

    • 默认值: 1
    • 含义: 校准的张量并行大小,仅对NeMo检查点有效。
    • 所有可能的值: 正整数。
  7. --calib_pp_size:

    • 默认值: 1
    • 含义: 校准的流水线并行大小,仅对NeMo检查点有效。
    • 所有可能的值: 正整数。
  8. --dtype:

    • 默认值: 'float16'
    • 含义: 模型数据类型。
    • 所有可能的值: 'float16', 'float32', 其他有效的浮点类型。
  9. --qformat:

    • 默认值: 'full_prec'
    • 含义: 量化格式。
    • 所有可能的值: 'fp8', 'int8_sq', 'int4_awq', 'w4a8_awq', 'int8_wo', 'int4_wo', 'full_prec'
量化格式描述优势劣势常用场景选择建议
fp88位浮点数格式动态范围大,精度较高复杂度高,硬件支持有限大规模语言模型(LLMs)、图像处理任务平衡精度和计算效率,适用于需要高精度和大范围的数据
int8_sq8位整数,平方量化(SmoothQuant)推理速度快,计算效率高精度损失较大实时推理任务适用于需要快速推理且对精度要求不高的场景
int4_awq4位整数,权重近似量化(AWQ)存储和计算资源需求低精度损失明显边缘设备适用于极端资源受限的环境
w4a8_awq权重4位整数,激活8位整数,AWQ方法平衡存储效率和推理精度实现复杂,硬件支持要求高存储受限但需要高精度推理的场景适用于需要高精度推理且存储资源有限的场景
int8_wo8位整数量化,仅权重存储需求低,计算效率高精度损失较大存储受限但计算资源较充足的环境适用于权重较平滑且不易受量化影响的模型
int4_wo4位整数量化,仅权重存储和计算资源需求极低精度损失极大极端受限的嵌入式设备或边缘计算适用于非常受限的嵌入式设备或边缘计算场景
full_prec全精度格式,不进行量化保持原始精度,无精度损失存储和计算资源需求大模型训练阶段或高精度需求的推理任务适用于需要最高精度且不受资源限制的场景
  1. --seed:

    • 默认值: 1234
    • 含义: 用于生成随机数的种子值,将用于调用random.seed(value)numpy.random.seed(value)
    • 所有可能的值: 任意整数。
  2. --tokenizer_max_seq_length:

    • 默认值: 2048
    • 含义: 初始化分词器的最大序列长度。
    • 所有可能的值: 正整数。
  3. --batch_size:

    • 默认值: 1
    • 含义: 校准的批次大小。
    • 所有可能的值: 正整数。
  4. --calib_size:

    • 默认值: 512
    • 含义: 用于校准的样本数量。
    • 所有可能的值: 正整数。
  5. --calib_max_seq_length:

    • 默认值: 512
    • 含义: 校准的最大序列长度。
    • 所有可能的值: 正整数。
  6. --output_dir:

    • 默认值: 'exported_model'
    • 含义: 导出模型的目录。
    • 所有可能的值: 任意有效的文件路径。
  7. --tp_size:

    • 默认值: 1
    • 含义: 张量并行大小。
    • 所有可能的值: 正整数。
  8. --pp_size:

    • 默认值: 1
    • 含义: 流水线并行大小。
    • 所有可能的值: 正整数。
  9. --awq_block_size:

    • 默认值: 128
    • 含义: AWQ(Approximate Weight Quantization)块大小。
    • 所有可能的值: 正整数。
  10. --kv_cache_dtype:

    • 默认值: None
    • 含义: KV缓存的数据类型。
    • 所有可能的值: 'int8', 'fp8', None
  11. --num_medusa_heads:

    • 默认值: 4
    • 含义: Medusa头的数量。
    • 所有可能的值: 正整数。
  12. --num_medusa_layers:

    • 默认值: 1
    • 含义: Medusa层的数量。
    • 所有可能的值: 正整数。
  13. --max_draft_len:

    • 默认值: 63
    • 含义: 最大草稿长度。
    • 所有可能的值: 正整数。
  14. --medusa_hidden_act:

    • 默认值: 'silu'
    • 含义: Medusa隐藏层激活函数。
    • 所有可能的值: 'relu', 'tanh', 'gelu', 'silu'等有效的激活函数。
  15. --medusa_model_dir:

    • 默认值: None
    • 含义: Medusa模型所在的目录。
    • 所有可能的值: 任意有效的文件路径。
  16. --quant_medusa_head:

    • 默认值: False
    • 含义: 是否对Medusa头的权重进行量化。
    • 所有可能的值: True, False

这些参数用于配置模型量化和导出的各种细节,确保在不同的硬件和数据集上都能进行有效的校准和导出。通过调整这些参数,可以定制化模型的量化流程以满足特定需求。

量化完成:
在这里插入图片描述

构建图:

cd /ssd/xiedong/glm-4-9b-xd/TensorRT-LLMCUDA_VISIBLE_DEVICES=2  trtllm-build --checkpoint_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq --gemm_plugin float16 --output_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq-engines

构建完成:
在这里插入图片描述
下面是对trtllm-build参数的详细解释:

全局参数

  • -h, --help: 显示帮助信息并退出。
  • --checkpoint_dir CHECKPOINT_DIR: 指定检查点目录。
  • --model_config MODEL_CONFIG: 模型配置文件路径。
  • --build_config BUILD_CONFIG: 构建配置文件路径。
  • --model_cls_file MODEL_CLS_FILE: 模型类文件路径。
  • --model_cls_name MODEL_CLS_NAME: 模型类名称。
  • --input_timing_cache INPUT_TIMING_CACHE: 读取时间缓存文件的路径,如果文件不存在将被忽略(默认值:None)。
  • --output_timing_cache OUTPUT_TIMING_CACHE: 写入时间缓存文件的路径(默认值:model.cache)。
  • --log_level LOG_LEVEL: 日志级别。
  • --profiling_verbosity {layer_names_only,detailed,none}: 设置生成的TRT引擎的分析详细程度。设置为detailed可以检查策略选择和内核参数。(默认值:layer_names_only)
  • --enable_debug_output: 启用调试输出。
  • --output_dir OUTPUT_DIR: 保存序列化引擎文件和模型配置的路径(默认值:engine_outputs)。
  • --workers WORKERS: 并行构建的工作线程数(默认值:1)。
  • --max_batch_size MAX_BATCH_SIZE: 引擎可以处理的最大请求数量。(默认值:256)
  • --max_input_len MAX_INPUT_LEN: 单个请求的最大输入长度。(默认值:1024)
  • --max_seq_len MAX_SEQ_LEN, --max_decoder_seq_len MAX_SEQ_LEN: 单个请求的最大总长度,包括提示和输出。如果未指定,将尝试从模型配置中推断。(默认值:None)
  • --max_beam_width MAX_BEAM_WIDTH: 最大的束宽。
  • --max_num_tokens MAX_NUM_TOKENS: 在每个批次中移除填充后的批处理输入标记的最大数量(触发--remove_input_padding)。(默认值:8192)
  • --opt_num_tokens OPT_NUM_TOKENS: 默认等于max_batch_size*max_beam_width,设置该值尽可能接近工作负载的实际标记数量。注意此参数可能在未来被移除。(默认值:None)
  • --tp_size TP_SIZE: 并行的张量大小。
  • --pp_size PP_SIZE: 并行的流水线大小。
  • --max_prompt_embedding_table_size MAX_PROMPT_EMBEDDING_TABLE_SIZE, --max_multimodal_len MAX_PROMPT_EMBEDDING_TABLE_SIZE: 设置为大于0的值以启用提示调整或多模态输入支持。(默认值:0)
  • --use_fused_mlp: 在GatedMLP中启用水平融合,减少层输入流量并可能提高性能。对于FP8 PTQ,缺点是由于丢弃了一个量化缩放因子,精度略有下降。(仅供参考的示例:0.45734 vs 0.45755用于LLaMA-v2 7B使用modelopt/examples/hf/instruct_eval/mmlu.py)。(默认值:False)
  • --gather_all_token_logits: 启用gather_context_logits和gather_generation_logits。(默认值:False)
  • --gather_context_logits: 收集上下文logits。(默认值:False)
  • --gather_generation_logits: 收集生成logits。(默认值:False)
  • --builder_opt BUILDER_OPT: 构建器选项。
  • --builder_force_num_profiles BUILDER_FORCE_NUM_PROFILES: 强制构建的配置文件数量。
  • --logits_dtype {float16,float32}: logits的数据类型。
  • --weight_sparsity: 权重稀疏性。
  • --max_draft_len MAX_DRAFT_LEN: 猜测解码目标模型的最大草稿标记长度。(默认值:0)
  • --lora_dir LORA_DIR [LORA_DIR ...]: LoRA权重的目录。如果提供多个目录,将使用第一个目录的配置。(默认值:None)
  • --lora_ckpt_source {hf,nemo}: LoRA检查点的来源。(默认值:hf)
  • --lora_target_modules {attn_qkv,attn_q,attn_k,attn_v,attn_dense,mlp_h_to_4h,mlp_4h_to_h,mlp_gate,cross_attn_qkv,cross_attn_q,cross_attn_k,cross_attn_v,cross_attn_dense,moe_h_to_4h,moe_4h_to_h,moe_gate,moe_router} [{attn_qkv,attn_q,attn_k,attn_v,attn_dense,mlp_h_to_4h,mlp_4h_to_h,mlp_gate,cross_attn_qkv,cross_attn_q,cross_attn_k,cross_attn_v,cross_attn_dense,moe_h_to_4h,moe_4h_to_h,moe_gate,moe_router} ...]: 在哪些模块中添加LoRA。仅在启用use_lora_plugin时激活。(默认值:None)
  • --max_lora_rank MAX_LORA_RANK: 各个LoRA模块的最大LoRA秩。用于计算LoRA插件的工作空间大小。(默认值:64)
  • --auto_parallel AUTO_PARALLEL: 自动并行的MPI世界大小。(默认值:1)
  • --gpus_per_node GPUS_PER_NODE: 多节点设置中每个节点的GPU数量。这是一个集群规格,可以大于/小于世界大小(默认值:8)
  • --cluster_key {A100-SXM-80GB,A100-SXM-40GB,A100-PCIe-80GB,A100-PCIe-40GB,H100-SXM,H100-PCIe,H20,V100-PCIe-16GB,V100-PCIe-32GB,V100-SXM-16GB,V100-SXM-32GB,V100S-PCIe,A40,A30,A10,A10G,L40S,L40,L20,L4,L2}: 目标GPU类型的唯一名称。如果未指定,将从当前GPU类型推断。(默认值:None)
  • --strip_plan: 是否从最终的TRT引擎中剥离权重,假设重新拟合的权重将与构建时提供的权重相同。(默认值:False)
  • --max_encoder_input_len MAX_ENCODER_INPUT_LEN: 使用编码器-解码器模型时指定最大编码器输入长度。将max_input_len设置为1以从decoder_start_token_id长度1开始生成。(默认值:1024)
  • --visualize_network: 在引擎构建前将TRT网络导出为ONNX以进行调试。(默认值:False)
  • --dry_run: 在不实际构建引擎的情况下运行构建过程以进行调试。(默认值:False)
  • --speculative_decoding_mode {draft_tokens_external,lookahead_decoding,medusa,explicit_draft_tokens}: 猜测解码模式。(默认值:None)
  • --weight_streaming: 指定是否将权重卸载到CPU并在运行时流式加载。(默认值:False)

插件配置

  • --bert_attention_plugin {auto,float16,float32,bfloat16,int32,disable}: 启用/禁用BERT注意力插件及其数据类型。(默认值:auto)
  • --gpt_attention_plugin {auto,float16,float32,bfloat16,int32,disable}: 启用/禁用GPT注意力插件及其数据类型。(默认值:auto)
  • --gemm_plugin {auto,float16,float32,bfloat16,int32,fp8,disable}: 启用/禁用GEMM插件及其数据类型。(默认值:disable)GEMM插件用于优化矩阵乘法操作,特别是在深度学习模型中,这些操作是计算密集型的核心部分。通过启用GEMM插件,可以利用硬件加速器和专门优化的算法,提升矩阵乘法的性能。
  • --gemm_swiglu_plugin {fp8,disable}: 启用/禁用GEMM SwiGLU插件及其数据类型。(默认值:disable)
  • --fp8_rowwise_gemm_plugin {auto,float16,float32,bfloat16,int32,disable}: 启用/禁用FP8逐行GEMM插件及其数据类型。(默认值:disable)
  • --nccl_plugin {auto,float16,float32,bfloat16,int32,disable}: 启用/禁用NCCL插件及其数据类型。(默认值:auto)
  • --lookup_plugin {auto,float16,float32,bfloat16,int32,disable}: 启用/禁用查找插件及其数据类型。(默认值:disable)
  • --lora_plugin {auto,float16,float32,bfloat16,int32,disable}: 启用/禁用LoRA插件及其数据类型。(默认值:disable)
  • --moe_plugin {auto,float16,float32,bfloat16,int32,disable}: 启用/禁用MoE插件及其数据类型。(默认值:auto)
  • --mamba_conv1d_plugin {auto,float16,float32,bfloat16,int32,disable}: 启用/禁用Mamba Conv1D插件及其数据类型。(默认

值:auto)

  • --context_fmha {enable,disable}: 启用/禁用上下文FMHA。(默认值:enable)
  • --context_fmha_fp32_acc {enable,disable}: 启用/禁用上下文FMHA FP32精度。(默认值:disable)
  • --paged_kv_cache {enable,disable}: 启用/禁用分页键值缓存。(默认值:enable)
  • --remove_input_padding {enable,disable}: 启用/禁用移除输入填充。(默认值:enable)
  • --reduce_fusion {enable,disable}: 启用/禁用融合减少。(默认值:disable)
  • --enable_xqa {enable,disable}: 启用/禁用XQA。(默认值:enable)
  • --tokens_per_block TOKENS_PER_BLOCK: 每个块的标记数。(默认值:64)
  • --use_paged_context_fmha {enable,disable}: 启用/禁用使用分页上下文FMHA。(默认值:disable)
  • --use_fp8_context_fmha {enable,disable}: 启用/禁用使用FP8上下文FMHA。(默认值:disable)
  • --multiple_profiles {enable,disable}: 启用/禁用多个配置文件。(默认值:disable)
  • --paged_state {enable,disable}: 启用/禁用分页状态。(默认值:enable)
  • --streamingllm {enable,disable}: 启用/禁用流式LLM。(默认值:disable)

这些参数主要用于配置和优化深度学习模型的训练和推理过程。通过合理设置这些参数,可以提高模型的性能和效率。

trtllm-build 是一个用于构建 TensorRT 引擎的命令行工具,常用于大规模语言模型 (LLM) 的优化和部署。为了获得最佳性能,通常会使用多种配置选项来调整构建过程。以下是一些常见的配置及其作用:

常用配置参数

  1. 数据类型和插件配置

    • --gemm_plugin float16: 启用 GEMM 插件并使用 float16 数据类型,可以显著提高矩阵乘法的计算效率,减少内存占用【13†source】。
    • --bert_attention_plugin--gpt_attention_plugin: 控制是否启用 BERT 和 GPT 的注意力插件,选择相应的数据类型来优化注意力机制【14†source】。
    • --use_fused_mlp: 启用 Gated-MLP 的水平融合,合并多个矩阵乘法操作,以提高性能【14†source】。
  2. 并行化和缓存配置

    • --tp_size--pp_size: 分别指定张量并行和流水线并行的大小,用于多 GPU 环境下的并行计算【15†source】。
    • --paged_kv_cache enable: 启用分页键值缓存,提高缓存管理效率,增加批处理大小并提高效率【14†source】。
  3. 优化和调试配置

    • --profiling_verbosity {layer_names_only, detailed, none}: 设置生成的 TensorRT 引擎的分析详细程度,可以帮助调试和优化【15†source】。
    • --enable_debug_output: 启用调试输出,有助于在构建过程中检测问题【13†source】。
    • --dry_run--visualize_network: 在不实际构建引擎的情况下运行构建过程,并在构建前导出 TensorRT 网络以进行调试【14†source】【15†source】。
  4. 输入输出配置

    • --max_input_len--max_output_len: 设置输入和输出的最大序列长度,确保模型处理能力符合预期【15†source】。
    • --max_batch_size: 设置模型可以处理的最大批处理大小,有助于提高推理效率【15†source】。
  5. 量化配置

    • --weight_sparsity: 启用权重稀疏性,减少模型的内存占用,提高推理速度【13†source】。
    • --quantization.quant_algo--quantization.kv_cache_quant_algo: 设置量化算法,用于优化模型的存储和计算【16†source】。

这些配置可以根据模型的具体需求和硬件环境进行调整,以最大化性能和效率。对于具体的模型和场景,可能需要根据实际情况进行一些实验和调整,确保达到最佳效果。

更多详细信息和示例配置可以参考 TensorRT-LLM 文档【12†source】【13†source】【14†source】【15†source】【16†source】。

测试推理是否可以

代码推理

cd /ssd/xiedong/glm-4-9b-xd

vim testglm-4-9b-chat-int4_awq-engines.py

from transformers import AutoTokenizer
import torchtokenizer = AutoTokenizer.from_pretrained("/ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat", trust_remote_code=True)import tensorrt_llmrunner = tensorrt_llm.runtime.ModelRunnerCpp.from_dir("/ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq-engines")history = [{'role': 'system', 'content': '你是一个Python程序员。'},# 可选,few shots
]conversation = history.copy()
conversation.append({"role": "user", "content": "请写一个用Python实现的程序,功能是将一个字符串中的所有字母转换为大写字母。"})inputs = tokenizer.apply_chat_template(conversation,add_generation_prompt=True,tokenize=True,return_tensors="pt",return_dict=True)inputs = inputs.to("cuda")with torch.no_grad():outputs = runner.generate([inputs["input_ids"][0]],max_new_tokens=1280,end_id=151336,  # 参考上面的讨论pad_id=151329,temperature=0,top_p=0,num_beams=1,output_sequence_lengths=True,return_dict=True,exclude_input_in_output=True)print(tokenizer.decode(outputs["output_ids"][0, 0][inputs["input_ids"].shape[1]:outputs["sequence_lengths"][0]]))

执行代码:

CUDA_VISIBLE_DEVICES=2  python testglm-4-9b-chat-int4_awq-engines.py

可以得到结果:
在这里插入图片描述

CLI推理

单机单卡的推理示例:

cd /ssd/xiedong/glm-4-9b-xdCUDA_VISIBLE_DEVICES=2  python3 /ssd/xiedong/glm-4-9b-xd/TensorRT-LLM/examples/run.py --input_text "What's new between ChatGLM3-6B and ChatGLM2-6B?" \--max_output_len 50 \--tokenizer_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat \--engine_dir "/ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq-engines"

在这里插入图片描述

性能测试

没空看代码,先放着:
https://github.com/NVIDIA/TensorRT-LLM/tree/main/benchmarks/python

小结

将自己用的TensorRT-LLM/文件夹cp一份放到了/workspace.

commit此容器并push:

docker commit b42769f7ed16 kevinchina/deeplearning:tensorrt_llm-v0.10.0

验证是否严重退化

参考了这里的文章:https://www.chenshaowen.com/blog/using-triton-server-and-tensorrt-llm-under-container.html

在精度损失可接受的范围内,模型的推理优化才有意义。TensorRT-LLM 项目提供的 summarize.py 可以跑一些测试,给模型打分,rouge1、rouge2 和 rougeLsum 是用于评价文本生成质量的指标,可以用于评估模型推理质量。

pip install datasets nltk rouge_score -i https://pypi.tuna.tsinghua.edu.cn/simple

test_hf 得到的是原始模型的指标,执行:

cd /ssd/xiedong/glm-4-9b-xdCUDA_VISIBLE_DEVICES=2  python3 /ssd/xiedong/glm-4-9b-xd/TensorRT-LLM/examples/summarize.py --test_hf \--hf_model_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat \--data_type fp16 \--engine_dir  /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq-engines

获得TensorRT模型的指标,执行:

cd /ssd/xiedong/glm-4-9b-xdCUDA_VISIBLE_DEVICES=2  python3 /ssd/xiedong/glm-4-9b-xd/TensorRT-LLM/examples/summarize.py --test_trt_llm \--hf_model_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat \--data_type fp16 \--engine_dir  /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq-engines

模型不支持,估计TensorRT-LLM后面版本才行。
在这里插入图片描述

使用NVIDIA Triton部署在线推理服务器

下图显示了 Triton 推理服务器的高级架构。模型存储库是一个基于文件系统的存储库,其中包含 Triton 将用于推理的模型。推理请求通过HTTP/REST 或 GRPC或C API到达服务器,然后路由到适当的每个模型调度程序。Triton 实现了多种调度和批处理算法,可以根据每个模型进行配置。每个模型的调度程序可以选择执行推理请求的批处理,然后将请求传递给与 模型类型相对应的后端 。后端使用批处理请求中提供的输入执行推理以生成请求的输出。然后返回输出。
在这里插入图片描述

从23版本后,Triton的docker容器里就有 TensorRT-LLM了,不用自己装了,很nice。也就是上面的内容都可以作废了,重新开始。

部署大语言模型(LLM)在生产环境中,你可以使用Triton Inference Server和TensorRT-LLM后端,以利用TensorRT-LLM C++运行时进行快速推理执行,并包括如飞行批处理和分页KV缓存等优化。

以下是具体步骤:

拉取一个Triton镜像下来,官网有很多Triton镜像:
https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tritonserver/tags

看这里资料:

https://github.com/triton-inference-server/tensorrtllm_backend

https://github.com/NVIDIA/TensorRT-LLM/blob/main/examples/gpt/README.md

代码弄下来

不考虑tags,感觉是代码越新越好。

弄好仓库:

cd /ssd/xiedong/glm-4-9b-xd
git clone https://github.com/triton-inference-server/tensorrtllm_backend.git
cd /ssd/xiedong/glm-4-9b-xd/tensorrtllm_backend
git submodule update --init --recursive
git lfs install
git lfs pull

编译镜像

自己编译的镜像更好,比官网出的版本高,不容易有bug。编译好镜像后,就同时有Triton和之前的tensorRTLLM在。

用仓库编译出镜像,要魔法,不然会失败:

# Update the submodules
cd tensorrtllm_backend
git lfs install
git submodule update --init --recursive# Use the Dockerfile to build the backend in a container
# For x86_64 正常机器都是这个架构
DOCKER_BUILDKIT=1 docker build -t triton_trt_llm -f dockerfile/Dockerfile.trt_llm_backend .
# For aarch64 这个架构不常用
DOCKER_BUILDKIT=1 docker build -t triton_trt_llm --build-arg TORCH_INSTALL_TYPE="src_non_cxx11_abi" -f dockerfile/Dockerfile.trt_llm_backend .

启动容器

启动容器,把宿主机有模型和代码的路径挂载进去。


docker run -it --net host --shm-size=2g --ulimit memlock=-1 --ulimit stack=67108864 --gpus '"device=2,3"' -v /ssd/xiedong/glm-4-9b-xd:/ssd/xiedong/glm-4-9b-xd nvcr.io/nvidia/tritonserver:23.10-trtllm-python-py3  bash# 自己的机器
docker run -it --net host --shm-size=2g --ulimit memlock=-1 --ulimit stack=67108864 --gpus '"device=0"' -v /data/xiedong/glm4:/data/xiedong/glm4 triton_trt_llm:latest  bash

安装依赖

每个项目的依赖不同,这里要安装chatglm的requirements.txt依赖。

# 这个tiktoken 包要装上
pip install tiktoken -i https://pypi.tuna.tsinghua.edu.cn/simple# 自己的机器,安装chatglm的依赖
cd /data/xiedong/glm4/tensorrtllm_backend/tensorrt_llm/examples/chatglm
pip install tiktoken -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install -r requirements.txt

为了适应chatglm-4的代码,要魔改下部分package代码:

vim /usr/local/lib/python3.10/dist-packages/modelopt/torch/export/model_config_export.py

export_tensorrt_llm_checkpoint
model=model -> model=model.cpu() 24G显存是真的会爆的

vim /usr/local/lib/python3.10/dist-packages/tensorrt_llm/models/chatglm/model.py

ChatGLMForCausalLM > check_config
config.set_if_not_exist(‘chatglm_version’, ‘chatglm3’) -> config.chatglm_version = “chatglm3” 强制套用chatglm3相关代码

量化

这里主要是将hf模型转为llm checkpoints模型,也可以直接用转换脚本,我这里直接调用quantize.py去量化并得到llm checkpoints模型。

cd /ssd/xiedong/glm-4-9b-xd/tensorrtllm_backend/
CUDA_VISIBLE_DEVICES=0 python tensorrt_llm/examples/quantization/quantize.py --model_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat --dtype float16 --qformat int4_awq --output_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq-new --batch_size 8 # 自己机器
cd /data/xiedong/glm4/tensorrtllm_backend
CUDA_VISIBLE_DEVICES=0 python tensorrt_llm/examples/quantization/quantize.py --model_dir /data/xiedong/glm4/glm-4-9b-chat --dtype float16 --qformat int4_awq --output_dir /data/xiedong/glm4/glm-4-9b-chat-awq-new --batch_size 8 

官网会用convert_checkpoint.py干这些事情,不需要量化可以看convert_checkpoint.py这个脚本如何做:

# Convert weights from HF Tranformers to TensorRT-LLM checkpoint
python3 convert_checkpoint.py --model_dir gpt2 \--dtype float16 \--tp_size 4 \--output_dir ./c-model/gpt2/fp16/4-gpu

构建trt engines图

这里要将llm checkpoints模型进一步转为trt engines图,这个trt engines图就是可以被Triton server加载的模型图。

cd /ssd/xiedong/glm-4-9b-xd/TensorRT-LLM
CUDA_VISIBLE_DEVICES=2  trtllm-build --checkpoint_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq --gemm_plugin float16 --output_dir /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq-engines# 自己机器
cd /data/xiedong/glm4/tensorrtllm_backend
CUDA_VISIBLE_DEVICES=0  trtllm-build --checkpoint_dir /data/xiedong/glm4/glm-4-9b-chat-awq-new --gemm_plugin float16 --output_dir /data/xiedong/glm4/glm-4-9b-chat-awq-new-engines

Triton 模板

说明

Triton 模板是会包含这些子文件夹,每个功能都不一样:

  1. 预处理模型 (preprocessing)

    功能: 该模型用于将文本提示(prompts)转换为输入标识符(input_ids)。

    输入: 字符串形式的文本提示。

    输出: 整数列表形式的输入标识符。

    用途: 主要用于将自然语言输入转换为模型可以理解和处理的格式。这是深度学习模型推理的第一步。

  2. TensorRT LLM 模型 (tensorrt_llm)

    功能: 该模型是一个 TensorRT-LLM 模型的包装器,用于推理。

    输入: 输入规范可以在这里找到(假设是指某个特定文档或 API 说明)。

    输出: 模型推理的结果,通常是标识符的形式。

    用途: 用于实际的模型推理步骤,接收经过预处理的输入并生成推理结果。

  3. 后处理模型 (postprocessing)

    功能: 该模型用于将输出标识符(output_ids)转换为输出字符串。

    输入: 整数列表形式的输出标识符。

    输出: 字符串形式的输出文本。

    用途: 将模型的推理结果转换为可读的自然语言文本,这是推理流程的最后一步。

  4. 集成模型 (ensemble)

    功能: 该模型用于将预处理、TensorRT LLM 和后处理模型链式连接起来。

    用途: 提供一个方便的接口,以串联执行预处理、推理和后处理的所有步骤,实现从文本提示到最终输出的完整流程。

  5. TensorRT LLM BLS 模型 (tensorrt_llm_bls)

    功能: 同样用于将预处理、TensorRT LLM 和后处理模型链式连接起来。

    区别: 尽管其功能与集成模型(ensemble)相似,但可能包含一些特定的优化或定制设置,以满足不同的性能需求或使用场景。

Triton 的集成功能支持许多用例,其中多个模型组成一个管道(或更一般地说是 DAG,有向无环图)。但是,还有许多其他用例不受支持,因为作为模型管道的一部分,它们需要循环、条件(if-then-else)、数据依赖控制流和其他自定义逻辑与模型执行混合。我们将这种自定义逻辑和模型执行的组合称为业务逻辑脚本 (BLS)。

假设我们有一个文本提示 "Hello, how are you?",整个推理流程如下:

  1. 预处理

    prompt = "Hello, how are you?"
    input_ids = preprocessing_model.tokenize(prompt)
    
  2. TensorRT LLM 推理

    output_ids = tensorrt_llm_model.infer(input_ids)
    
  3. 后处理

    output_text = postprocessing_model.detokenize(output_ids)
    
  4. 集成模型

    output_text = ensemble_model.process(prompt)
    

在使用 BLS 模型替代集成模型时,应将模型实例数量设置为 TRT 引擎支持的最大批处理大小,以允许并发请求执行。这可以通过修改 BLS 模型 config.pbtxt 文件中 instance_group 部分的 count 值来实现。

BLS 模型有一个可选参数 accumulate_tokens,可在流模式下使用,以使用所有累积的 tokens 调用后处理模型,而不仅仅是一个 token。某些分词器可能需要此功能。

BLS 模型支持推测解码。目标和草稿的 Triton 模型通过参数 tensorrt_llm_model_nametensorrt_llm_draft_model_name 设置。通过在请求中设置 num_draft_tokens 来执行推测解码。可以通过设置 use_draft_logits 来使用 logits 比较进行推测解码。请注意,在使用推测解码时,不支持 return_generation_logitsreturn_context_logits。还需注意,目前推测解码不支持批量大小大于 1 的请求。

阅读这里了解更多:https://github.com/triton-inference-server/server/blob/main/docs/user_guide/architecture.md#ensemble-models

实操

创建Triton tensorrtllm_backend 的模板工程:

cd /ssd/xiedong/glm-4-9b-xd/tensorrtllm_backend
mkdir triton_model_repo
# Copy the example models to the model repository
cp -r all_models/inflight_batcher_llm/* triton_model_repo/
# Copy the TRT engine to triton_model_repo/tensorrt_llm/1/
cp /ssd/xiedong/glm-4-9b-xd/glm-4-9b-chat-int4_awq-engines/* triton_model_repo/tensorrt_llm/1# 自己机器
cd /data/xiedong/glm4/tensorrtllm_backend
mkdir triton_model_repo
cp -r all_models/inflight_batcher_llm/* triton_model_repo/
cp /data/xiedong/glm4/glm-4-9b-chat-awq-new-engines/* triton_model_repo/tensorrt_llm/1

triton_model_repo中就是一个工程模板了,模板里各个子文件夹都是配置,需要改,这里说了怎么改:https://github.com/triton-inference-server/tensorrtllm_backend。按文档里说的改一下。

改半天还是错了,这个指令看是哪个叼毛还报错:

find triton_model_repo/ -type f -name "*txt" -exec grep -l 'batch_scheduler_policy' {} +

我写了个python代码,来一键替换配置文件里的字符串,更改src 为模板路径,更改replace_d字典的替换关系。

import osdef listPathAllfiles(dirname):result = []for maindir, subdir, file_name_list in os.walk(dirname):for filename in file_name_list:apath = os.path.join(maindir, filename)result.append(apath)return resultsrc = r"/data/xiedong/glm4/tensorrtllm_backend/triton_model_repo"
files = listPathAllfiles(src)# 过滤只要pbtxt结尾的文件
files = [file for file in files if file.endswith(".pbtxt")]
print("有的pbtxt文件:")
for file in files:print(file)# 字符串替换的字典
replace_d = {"${triton_max_batch_size}": 32,"${postprocessing_instance_count}": 8,"${tokenizer_dir}": "/data/xiedong/glm4/glm-4-9b-chat","${decoupled_mode}": "true","${max_queue_delay_microseconds}": 5000000,"${triton_backend}": "tensorrtllm",# gpt_model_path"${engine_dir}": "/data/xiedong/glm4/tensorrtllm_backend/triton_model_repo/tensorrt_llm/1","${batch_scheduler_policy}": "max_utilization","${preprocessing_instance_count}": 8,# gpt_model_type"${batching_strategy}": "inflight_fused_batching","${bls_instance_count}": 1,#"${max_tokens_in_paged_kv_cache}": 1048577,"beam_width": "fuck is where?","maxTokensInPagedKvCache": "fuck is where?","tokensPerBlock": "fuck is where?","maxBlocksPerSeq": "fuck is where?","max_tokens_in_paged_kv_cache": "fuck is where?",
}
print("开始修改:")
# 读取文件内容,替换字符串,写入原文件
for file in files:with open(file, "r") as f:content = f.read()for k, v in replace_d.items():if v == "fuck is where?":if str(k) in content:print(f"ERROR fuck is where?: {k},{file}")else:if str(k) in content:content = content.replace(str(k), str(v))print(f"INFO tihuan: {file},{k},{v}")with open(file, "w") as f:f.write(content)print(f"{file} done!")

发起Triton服务

发起Triton服务:

cd /ssd/xiedong/glm-4-9b-xd/tensorrtllm_backend
# --world_size is the number of GPUs you want to use for serving
python3 /ssd/xiedong/glm-4-9b-xd/tensorrtllm_backend/scripts/launch_triton_server.py --world_size=1 --model_repo=/ssd/xiedong/glm-4-9b-xd/tensorrtllm_backend/triton_model_repo# 自己机器
cd /data/xiedong/glm4/
python3 /data/xiedong/glm4/tensorrtllm_backend/scripts/launch_triton_server.py --world_size=1 --model_repo=/data/xiedong/glm4/tensorrtllm_backend/triton_model_repo

好像是可以了:

# 自己机器
docker commit 883bd4b12b5b kevinchina/deeplearning:triton_trt_llm-successv1
docker run -it -p 7866:8000 -p 7867:8001 -p 7868:8002 --shm-size=8g --ulimit memlock=-1 --ulimit stack=67108864 --gpus '"device=0"' -v /data/xiedong/glm4:/data/xiedong/glm4  kevinchina/deeplearning:triton_trt_llm-successv1  bash
cd /data/xiedong/glm4/
python3 /data/xiedong/glm4/tensorrtllm_backend/scripts/launch_triton_server.py --world_size=1 --model_repo=/data/xiedong/glm4/tensorrtllm_backend/triton_model_repo

开启成功:
在这里插入图片描述

请求服务试试

命令行请求8000端口:

curl -X POST localhost:8000/v2/models/ensemble/generate -d '{"text_input": "你是谁?", "max_tokens": 50, "bad_words": [], "stop_words": []}'
curl -X POST localhost:8000/v2/models/tensorrt_llm_bls/generate -d '{"text_input": "你是谁?", "max_tokens": 50, "bad_words": [], "stop_words": []}'

bad_words 是生成文本时需要过滤的词汇列表。在自然语言处理(NLP)和生成任务中,有时我们希望模型在生成文本时避免使用某些不合适或不需要的词语,这些词语通常被称为“脏话”或“禁用词”。通过指定 bad_words 参数,我们可以告诉模型在生成文本时避免使用这些词汇。

示例说明

假设我们希望模型生成的文本中避免出现一些特定的词语,比如 “badword1” 和 “badword2”,我们可以在请求中指定这些词语作为 bad_words 参数的值。

例如:

curl -X POST localhost:8000/v2/models/ensemble/generate -d '{"text_input": "Please generate a polite response.", "max_tokens": 50, "bad_words": ["badword1", "badword2"], "stop_words": []}'

在这个请求中,bad_words 参数的值是一个包含两个词语的数组,即 ["badword1", "badword2"]。这告诉模型在生成响应时避免使用 “badword1” 和 “badword2”。

在这里插入图片描述

模型分析器

https://github.com/triton-inference-server/tutorials/tree/main/Conceptual_Guide/Part_3-optimizing_triton_configuration

https://github.com/triton-inference-server/tutorials/tree/main?tab=readme-ov-file

关闭Triton服务

pkill tritonserver

metric

访问:

localhost:8002/metrics

重看Triton服务

这里有个资料:

https://www.atyun.com/59255.html

Triton TensorRT-LLM主要特征及优势:

支持多种深度学习框架
支持多种机器学习框架
模型并发执行(CPU 层次的优化)
动态批处理(Dynamic batching)
有状态模型的序列批处理(Sequence batching)和隐式状态管理(implicit state management)
提供允许添加自定义后端和前/后置处理操作的后端 API
支持使用 Ensembling 或业务逻辑脚本 (BLS)进行模型流水线
HTTP/REST和GRPC推理协议是基于社区开发的KServe协议
支持使用 C API 和 Java API 允许 Triton 直接链接到您的应用程序,用于边缘端场景
支持查看 GPU 利用率、服务器吞吐量、服务器延迟等指标

在这里插入图片描述

还有几个问题,留着下次写:
(1)为啥我显存还是占用那么多?
(2)Triton 把api服务写了,我还要怎么自定义出自己的api服务?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/386222.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pyqt designer使用spliter

1、在designer界面需要使用spliter需要父界面不使用布局,减需要分割两个模块选中,再点击spliter分割 2、在分割后,再对父界面进行布局设置 3、对于两边需要不等比列放置的,需要套一层 group box在最外层进行分割

从善如流之您最亲近人之善,肯出力之象-下学而上达

您最亲近人之善,肯出力之象,就是那个爬,甚至于跪倒在地上,抹那个下水井子。这或许就是那个马云大佬讲过的,就是从您最近距离,身边的人学习。人家为啥做的好,出色?而且您是一母同胞之…

基于微信小程序+SpringBoot+Vue的网络安全科普系统(带1w+文档)

基于微信小程序SpringBootVue的网络安全科普系统(带1w文档) 基于微信小程序SpringBootVue的网络安全科普系统(带1w文档) 优质的网络安全科普系统不仅可以单纯的满足工作人员管理的日常工作需求,还可以满足用户的需求。可以降低工作人员的工作压力,提高效…

LeetCode刷题笔记第682题:棒球比赛

LeetCode刷题笔记第682题:棒球比赛 题目: 想法: 遍历输入的列表,按照规则将分数和操作依次进行,存储在新建的列表中,最终输出列表中的元素和,代码如下: class Solution:def calPo…

网安零基础入门神书,全面介绍Web渗透核心攻击与防御方式!

Web安全是指Web服务程序的漏洞,通常涵盖Web漏洞、操作系统洞、数据库漏洞、中间件漏洞等。 “渗透测试”作为主动防御的一种关键手段,对评估网络系统安全防护及措施至关重要,因为只有发现问题才能及时终止并预防潜在的安全风险。 根据网络安…

第6篇文献研读生态廊道相关综述

该文发在生态与农村环境学报。该文章写了生态廊道概念的发展历程、生态廊道类型及功能、生态廊道划定的理论和方法、生态廊道的时间和国内大型生态廊道建设实践。 这篇文章可以让大家了解生态廊道的知识。

C语言实现K均值聚类

K均值聚类(K_means)基础理论 K_means聚类是一种简单且广泛使用的聚类算法,它旨在将数据集中的样本划分为k个不同的聚类,其中k是事先指定的聚类数量,该算法的核心思想是迭代地优化聚类中心,以最小化每个样本与其所属聚类中心之间的…

懂个锤子Vue 项目工程化进阶⏫:

Vue项目工程化进阶⏫: 前言: 紧跟前文,目标学习Vue2.0——3.0: 懂个锤子Vue、WebPack5.0、WebPack高级进阶 涉及的技术栈… 当然既然学习框架的了,HTMLCSSJS三件套必须的就不说了: JavaScript 快速入门 …

react中如何mock数据

1.需求说明 因为前后端分离开发项目,就会存在前端静态页面写好了,后端数据接口还没写好;这时候前端就需要自己定义数据来使用。 定义数据有三种方式:直接写死数据、使用mock软件、json-server工具 这里讲解通过json-server工具…

面向对象程序设计(C++)模版初阶

1. 函数模版 1.1 函数模版概念 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本,可以类比函数参数,函数模版就是将函数参数替换为特定类型版本 1.2 函数模版格…

基于ant-design-vue3多功能操作表格,表头序号为动态添加记录按钮,鼠标在表格记录行,当前行序号显示删除按钮

由于项目需要,并考虑到尽可能让空间利用率高,因此定制开发一个表格组件,组件功能主要是在序号表头位置为添加按钮,点击按钮,新增一行表格数据;表格数据删除不同于以往表格在操作栏定义删除按钮,…

问题解决|如何优雅展示层级或关联数据?

一、8种可用图表类型及配套教程 (一)包珠图 (1)功能介绍 包珠图(Circular Packing),是一类较特殊的分类树状图,以气泡之间的包含关系展示层级关系,以气泡面积&#xf…

Spring事件机制

文章目录 一、Spring事件二、实现Spring事件1、自定义事件2、事件监听器2.1 实现ApplicationListener接口2.2 EventListener2.3 TransactionalEventListener 3、事件发布4、异步使用 三、EventBus1、事件模式2、EventBus三要素3、同步事件3.1 定义事件类3.2 定义事件监听3.3 测…

解析西门子PLC的String和WString

西门子PLC有两种字符串类型,String与WString String 用于存放英文数字标点符号等ASCII字符,每个字符占用一个字节 WString宽字符串用于存放中文、英文、数字等Unicode字符,每个字符占用两个字节 之前我搞过一篇解析String的 关于使用TCP-…

Kotlin 的优势:现代编程语言的卓越选择

文章目录 简洁与优雅的语法空安全特性函数式编程,支持高阶函数、lambdaKotlin 内联函数与 Java 的互操作性强大的类型推断协程支持lazy 委托object 单例模式区间表达式现代的开发工具支持 本文首发地址 https://h89.cn/archives/301.html 最新更新地址 https://gite…

包装类和泛型

🎉欢迎大家收看,请多多支持🌹 🥰关注小哇,和我一起成长🚀个人主页🚀 包装类🌙 Java中每个基本数据类型都对应了一个包装类, 除了int的包装类是Integer,char…

微信小程序开发 快速学习 这篇就够了

目录 一、配置篇 (1)官网链接: (2)项目分析 (3)调试器 (4)预览体验 (5)配置文件 (6)配置pages (7&…

【开发问题记录】启动某个微服务时无法连接到seata(seata启动或配置异常)

问题记录 一、问题描述1.1 问题复现1.1.1 将Linux中的部分微服务启动1.1.2 在本地启动当时出错的服务 1.2 解决思路1.2.1 Nacos中seata相关的信息1.2.2 Linux中seata相关的信息 二、问题解决2.1 seata的配置错误2.1.1 Nacos中seata的配置问题2.1.2 命名空间问题的发现 2.2 网络…

Matlab编程资源库(10)离散傅立叶变换

一、离散傅立叶变换算法简要 给定一个N点的离散信号序列x(n),其中n表示时刻,n 0, 1, 2, ..., N-1。 定义离散傅立叶变换的频域序列X(k),其中k表示频率,k 0, 1, 2, ..., N-1。 通过以下公式计算每个频率对应的复数值&#xff…

生鲜云订单零售系统小程序的设计

管理员账户功能包括:系统首页,个人中心,用户管理,商品分类管理,商品信息管理,订单评价管理,订单管理,系统管理 微信端账号功能包括:系统首页,商品信息&#…