Ascend-SACT/Kimi-K2--Thinking
模型介绍文件和版本Pull Requests讨论分析
下载使用量0

概述

本案例提供了在A+X环境下基于vLLM部署Kimi-K2-Thinking模型的实践方法。

环境依赖

安装关键软件及依赖时,各组件的版本号必须匹配。

  • OS:ubuntu 22.0
  • CANN: 8.3.RC1
  • Chip: 910B
  • Arch:x86_64
  • Torch:2.7.1
  • Torch_npu:2.7.1
  • vllm:0.11.0
  • vllm_ascend:0.11.0rc2

部署卡数:本文以4节点部署为例,每个节点配备16张NPU,共计64张910B卡进行部署。

下载模型原文件

https://www.modelscope.cn/models/moonshotai/Kimi-K2-Thinking

原模型文件对Linear层权重进行了int4分组量化,激活值未量化,需要使用msit工具将模型文件全部反量化为BF16格式。 image.png

模型权重文件反向量化INT4->BF16

反量化工具及脚本: https://gitcode.com/libarry/msit_9400/blob/k2/msmodelslim/example/DeepSeek/convert_int4_to_bf16.py

克隆工具及脚本到本地,并执行量化脚本:

git clone https://gitcode.com/libarry/msit_9400.git
cd msit_9400
git checkout k2
cd msmodelslim/example/DeepSeek
python convert_int4_to_bf16.py \
  --input-int4-hf-path /path/before-quant-model \
  --output-bf16-hf-path /path/after-quant-model

注意:生成的量化后文件夹只包含变更后的文件,需要将原模型文件中未更改的文件合并到新生成的量化文件夹,形成最终的量化后的模型文件。

修改vllm-ascend代码解决KeyError报错

          vllm-ascend的权重配置文件未适配新模型的权重文件,导致vllm_ascend/quantization/quant_config.py 从 self.quant_description 字典中查找权重文件键值失败。 找到以下文件:/usr/local/python3.11.13/lib/python3.11/site-packages/vllm_ascend/quantization/quant_config.py  找到is_layer_skipped_ascend函数替换为如下代码,跳过量化匹配,默认视为BF16。

def is_layer_skipped_ascend(
    self,
    prefix: str,
    fused_mapping: Mapping[str, List[str]] = MappingProxyType({})):
    # adapted from vllm.model_executor.layers.quantization.utils.quant_utils.is_layer_skipped
    proj_name = prefix.split(".")[-1]
    if proj_name in fused_mapping:
        shard_prefixes = [
            prefix.replace(proj_name, shard_proj_name)
            for shard_proj_name in fused_mapping[proj_name]
        ]

        is_skipped = None
        for shard_prefix in shard_prefixes:
            key = shard_prefix + '.weight'
            # 使用 .get() 避免 KeyError,默认视为 "FLOAT"(即跳过量化)
            weight_type = self.quant_description.get(key, "FLOAT")
            is_shard_skipped = (weight_type == "FLOAT")

            if is_skipped is None:
                is_skipped = is_shard_skipped
            elif is_shard_skipped != is_skipped:
                raise ValueError(
                    f"Detected some but not all shards of {prefix} "
                    "are quantized. All shards of fused layers "
                    "must have the same precision."
                )
    else:
        key = prefix + '.weight'
        # 同样使用 .get() 提供默认值
        weight_type = self.quant_description.get(key, "FLOAT")
        is_skipped = (weight_type == "FLOAT")

    assert is_skipped is not None
    return is_skipped

模型部署

启动命令:

Kimi-K2-Thinking BF16模型参数大约2T左右,本案例部署在4个节点,共64张910B卡。

/infer_vllm_m_kimi_k2_thinking.sh \
   --model-path ${MODEL_PATH} \
   --served-model-name kimi \
   --port ${PORT} \
   --leaderip ${LWS_LEADER_ADDRESS} \
   --index ${LWS_WORKER_INDEX} \
   --tensor-parallel-size 8 \
   --max-model-len 16384 \
   --max-num-seqs 16 \
   --data-parallel-size 8 \
   --data-parallel-size-local 2 \
   --gpu-memory-utilization 0.9 \

启动脚本:

  #!/bin/bash
# 初始化参数(未传递时使用默认值)
model_path=""               # 模型路径(必填)
served_model_name=""        # 服务模型名(必填)
port="8000"                 # 服务端口(默认8000)
leaderip=""                 # 主节点IP(从节点必填)
max_model_len="32768"       # 最大模型长度(默认32768)
max_num_seqs="32"           # 最大序列数(默认32)
api_server_count="1"        # API服务器数量(默认1)
index="0"                   # 节点索引(0为主节点,默认0)
tensor_parallel_size=""     # 张量并行大小(必填)
data_parallel_size_local="1" # 本地数据并行大小(默认1)
data_parallel_size="1"      # 总数据并行大小(默认1)
gpu_memory_utilization="0.95" # GPU内存利用率(默认0.8)
pipeline_parallel_size="1"  # 管道并行数(默认1)

# 解析命令行参数(长选项形式)
while [[ $# -gt 0 ]]; do
    case "$1" in
        --model-path)
            model_path="$2"
            shift 2
            ;;
        --served-model-name)
            served_model_name="$2"
            shift 2
            ;;
        --port)
            port="$2"
            shift 2
            ;;
        --leaderip)
            leaderip="$2"
            shift 2
            ;;
        --max-model-len)
            max_model_len="$2"
            shift 2
            ;;
        --max-num-seqs)
            max_num_seqs="$2"
            shift 2
            ;;
        --api-server-count)
            api_server_count="$2"
            shift 2
            ;;
        --index)
            # 校验节点索引为非负整数
            if ! [[ "$2" =~ ^[0-9]+$ ]]; then
                echo "Error: 节点索引(--index)必须是非负整数" >&2
                exit 1
            fi
            index="$2"
            shift 2
            ;;
        --tensor-parallel-size)
            tensor_parallel_size="$2"
            shift 2
            ;;
        --data-parallel-size-local)
            data_parallel_size_local="$2"
            shift 2
            ;;
        --data-parallel-size)
            data_parallel_size="$2"
            shift 2
            ;;
        --gpu-memory-utilization)
            gpu_memory_utilization="$2"
            shift 2
            ;;
        --pipeline-parallel-size)
            pipeline_parallel_size="$2"
            shift 2
            ;;
        *)
            echo "Error: 未知参数 $1" >&2
            echo "正确用法: $0 --model-path <模型路径> --served-model-name <服务名> --tensor-parallel-size <张量并行数> [选项]" >&2
            echo "可选选项:" >&2
            echo "  --port <端口>                          服务端口(默认8000)" >&2
            echo "  --leaderip <主节点IP>                  从节点必填,指定主节点IP" >&2
            echo "  --max-model-len <最大模型长度>          默认32768" >&2
            echo "  --max-num-seqs <最大序列数>            默认32" >&2
            echo "  --api-server-count <API服务器数量>      默认1" >&2
            echo "  --index <节点索引>                     0为主节点(默认),非负整数" >&2
            echo "  --data-parallel-size-local <本地数据并行大小> 默认1" >&2
            echo "  --data-parallel-size <总数据并行大小>    默认1" >&2
            echo "  --gpu-memory-utilization <GPU内存利用率> 默认0.95" >&2
            echo "  --pipeline-parallel-size <管道并行数>    默认1" >&2
            exit 1
            ;;
    esac
done

# 检查必填参数(仅无默认值的参数为必填)
if [[ -z "$model_path" || -z "$served_model_name" || -z "$tensor_parallel_size" ]]; then
    echo "Error: 缺少必填参数,必须提供:--model-path、--served-model-name、--tensor-parallel-size" >&2
    exit 1
fi

# 从节点必须提供主节点IP
if [[ "$index" -ne 0 && -z "$leaderip" ]]; then
    echo "Error: 从节点(index=$index)必须通过--leaderip指定主节点IP" >&2
    exit 1
fi

# 校验ifconfig命令是否存在
if ! command -v ifconfig &> /dev/null; then
    echo "Error: 未找到ifconfig命令,请安装net-tools或检查环境变量" >&2
    exit 1
fi

# 选择网络接口(优先eth开头,其次enp开头)
nic_name=$(ifconfig | grep -Eo '^(eth|enp)[^:]*' | sort -r | head -n1)
if [[ -z "$nic_name" ]]; then
    echo "Error: 未找到eth/enp开头的网络接口,请检查网络配置" >&2
    exit 1
fi

# 获取本地IP
local_ip=$(ifconfig "$nic_name" | grep 'inet ' | awk '{print $2}' | grep -v '^127\.' | head -n1)
if [[ -z "$local_ip" ]]; then
    echo "Error: 无法从接口$nic_name获取有效IP地址" >&2
    exit 1
fi
echo "已选择网络接口: $nic_name,本地IP: $local_ip"

# 打印参数信息
echo -e "\n[DEBUG] 部署参数:"
echo "========================================"
echo "必选参数:"
echo "  模型路径:        $model_path"
echo "  服务模型名:      $served_model_name"
echo "  张量并行大小:    $tensor_parallel_size"
echo "节点信息:"
echo "  节点索引:        $index(0为主节点)"
echo "  主节点IP:        ${leaderip:-本地节点(主节点)}"
echo "并行配置:"
echo "  总数据并行数:    $data_parallel_size"
echo "  本地数据并行数:  $data_parallel_size_local"
echo "  管道并行数:      $pipeline_parallel_size"
echo "  启用expert-parallel: 是(默认启用)"
echo "其他配置:"
echo "  服务端口:        $port"
echo "  最大模型长度:    $max_model_len"
echo "  最大序列数:      $max_num_seqs"
echo "  API服务器数量:   $api_server_count"
echo "  GPU内存利用率:   $gpu_memory_utilization"
echo "========================================\n"


source /usr/local/Ascend/ascend-toolkit/set_env.sh
source /usr/local/Ascend/ascend-toolkit/latest/opp/vendors/customize/bin/set_env.bash
export HCCL_IF_IP="$local_ip"
export GLOO_SOCKET_IFNAME="$nic_name"
export TP_SOCKET_IFNAME="$nic_name"
export HCCL_SOCKET_IFNAME="$nic_name"
export OMP_PROC_BIND=false
export OMP_NUM_THREADS=1
export HCCL_BUFFSIZE=1024
export VLLM_ENGINE_INIT_TIMEOUT=3600
export ENGINE_INIT_TIMEOUT=3600
export HCCL_EXEC_TIMEOUT=7200
export HCCL_CONNECT_TIMEOUT=3600
max_num_batched_tokens=$((max_model_len * 1))


if [[ "$index" -eq 0 ]]; then
    # 主节点命令
    vllm_cmd="vllm serve \"$model_path\" \
        --host 0.0.0.0 \
        --port \"$port\" \
        --served-model-name \"$served_model_name\" \
        --tensor-parallel-size \"$tensor_parallel_size\" \
        --data-parallel-size \"$data_parallel_size\" \
        --data-parallel-size-local \"$data_parallel_size_local\" \
        --data-parallel-address \"$local_ip\" \
        --data-parallel-rpc-port 13389 \
        --enable-expert-parallel \
        --max-num-seqs \"$max_num_seqs\" \
        --max-model-len \"$max_model_len\" \
        --max-num-batched-tokens \"$max_num_batched_tokens\" \
        --trust-remote-code \
        --gpu-memory-utilization \"$gpu_memory_utilization\" \
        --seed 1024 \
        --quantization ascend \
        --additional-config '{\"ascend_scheduler_config\":{\"enabled\":true},\"torchair_graph_config\":{\"enabled\":true,\"graph_batch_sizes\":[16]}}' \
        "
else
    # 从节点命令(计算起始rank)
    data_parallel_start_rank=$((index * data_parallel_size_local))
    vllm_cmd="vllm serve \"$model_path\" \
        --host 0.0.0.0 \
        --port \"$port\" \
        --headless \
        --served-model-name \"$served_model_name\" \
        --tensor-parallel-size \"$tensor_parallel_size\" \
        --data-parallel-size \"$data_parallel_size\" \
        --data-parallel-size-local \"$data_parallel_size_local\" \
        --data-parallel-start-rank \"$data_parallel_start_rank\" \
        --data-parallel-address \"$leaderip\" \
        --data-parallel-rpc-port 13389 \
        --enable-expert-parallel \
        --max-num-seqs \"$max_num_seqs\" \
        --max-model-len \"$max_model_len\" \
        --max-num-batched-tokens \"$max_num_batched_tokens\" \
        --trust-remote-code \
        --gpu-memory-utilization \"$gpu_memory_utilization\" \
        --seed 1024 \
        --quantization ascend \
        --additional-config '{\"ascend_scheduler_config\":{\"enabled\":true},\"torchair_graph_config\":{\"enabled\":true,\"graph_batch_sizes\":[16]}}' \
        "
fi

# 打印并执行命令
echo "[INFO] 启动命令:"
echo "========================================"
echo "$vllm_cmd" | sed 's/\\//g'
echo "========================================\n"

eval "$vllm_cmd"