准备代码
本教程中用到的训练推理代码和如下表所示,请提前准备好。
获取模型软件包和权重文件
获取模型权重文件
获取对应模型的权重文件,获取链接参考表1。
权重文件下载有如下几种方式,但不仅限于以下方式:
- 方法一:网页下载:通过单击表格中权重文件获取地址的访问链接,即可在模型主页的Files and Version中下载文件。
- 方法二:huggingface-cli:huggingface-cli是 Hugging Face 官方提供的命令行工具,自带完善的下载功能。具体步骤可参考:HF-Mirror中的使用教程。完成依赖安装和环境变量配置后,以Llama2-70B为例:
huggingface-cli download --resume-download meta-llama/Llama-2-70b-chat-hf --local-dir <模型下载路径>
若要下载指定版本的模型文件,则命令如下:
huggingface-cli download --resume-download meta-llama/Llama-2-70b-chat-hf --revision <模型版本> --local-dir <模型下载路径>
- 方法三:使用专用多线程下载器 hfd:hfd 是本站开发的 huggingface 专用下载工具,基于成熟工具 git+aria2,可以做到稳定下载不断线。
- 方法四:使用Git clone,官方提供了 git clone repo_url 的方式下载,但是不支持断点续传,并且clone 会下载历史版本占用磁盘空间。
模型软件包结构说明
|——AscendCloud-LLM |──llm_train # 模型训练代码包 |──AscendSpeed # 基于AscendSpeed的训练代码 |──ascendcloud_patch/ # 针对昇腾云平台适配的功能补丁包 |──scripts/ # 训练需要的启动脚本 |──llama2 # llama2系列模型执行脚本的文件夹 |──llama3 # llama3系列模型执行脚本的文件夹 |──qwen # Qwen系列模型执行脚本的文件夹 |──qwen1.5 # Qwen1.5系列模型执行脚本的文件夹 |── ... |── dev_pipeline.sh # 系列模型共同调用的多功能的脚本 |── install.sh # 环境部署脚本 |——src/ # 启动命令行封装脚本,在install.sh里面自动构建 |──llm_inference # 推理代码包 |──llm_tools # 推理工具
工作目录介绍
${workdir}(例如使用SFS Turbo的路径:/mnt/sfs_turbo/) |──llm_train #解压代码包后自动生成的代码目录,无需用户创建 |── AscendSpeed # 代码目录 |──ascendcloud_patch/ # 针对昇腾云平台适配的功能代码包 |──scripts/ # 各模型训练需要的启动脚本,训练脚本以分类的方式集中在scripts文件夹中。 # 自动生成数据目录结构 |── processed_for_input #目录结构会自动生成,无需用户创建 |── ${model_name} # 模型名称 |── data # 预处理后数据 |── pretrain # 预训练加载的数据 |── finetune # 微调加载的数据 |──converted_weights # HuggingFace格式转换megatron格式后权重文件 |── saved_dir_for_output # 训练输出保存权重,目录结构会自动生成,无需用户创建 |── ${model_name} # 模型名称 |── logs # 训练过程中日志(loss、吞吐性能) |—— saved_models |── lora # lora微调输出权重 |── sft # 增量训练输出权重 |── pretrain # 预训练输出权重 |── tokenizers #tokenizer目录,需要用户手动创建,后续操作步骤中会提示 |── Llama2-70B |── models #原始权重与tokenizer目录,需要用户手动创建,后续操作步骤中会提示 |── Llama2-70B |── training_data #原始数据目录,需要用户手动创建,后续操作步骤中会提示 |── train-00000-of-00001-a09b74b3ef9c3b56.parquet #原始数据文件 |── alpaca_gpt4_data.json #微调数据文件
修改代码
将AscendSpeed代码包AscendCloud-LLM-xxx.zip在本地解压缩后。在上传代码前,需要对解压后的训练脚本代码进行修改。具体文件为:修改llm_train/AscendSpeed/scripts/dev_pipeline.sh以及新建文件llm_train/AscendSpeed/scripts/tools/get_rank_table.py 。
- dev_pipeline.sh 具体添加代码内容以及位置,如下所示。
elif [[ -n "$VC_MAIN_HOSTS" ]]; then # 针对 Lite Cluster CCE 集群平台 # 获取 RANK_TABLE_FILE 的信息 RANKTABLE_RESULT=$(python $SHELL_FOLDER/../tools/get_ranktable.py) # 将脚本的返回值进行拆分,得到 节点总数量(NNODES) 节点的RANK(NODE_RANK) 单节点的NPU数量(NPUS_PER_NODE) IFS=',' read -r NNODES NODE_RANK NPUS_PER_NODE <<< "$RANKTABLE_RESULT" MASTER_ADDR="$VC_MAIN_HOSTS" MASTER_PORT=6060 NNODES="$NNODES" NODE_RANK="$NODE_RANK" NPUS_PER_NODE="$NPUS_PER_NODE" WORLD_SIZE=$(($NPUS_PER_NODE*$NNODES)) export GLOO_SOCKET_IFNAME=enp67s0f5 # 多机之间使用gloo通信时需要指定网口名称, export TP_SOCKET_IFNAME=enp67s0f5 # 多机之间使用TP通信时需要指定网口名称 export HCCL_SOCKET_IFNAME=enp67s0f5 # 多机之间使用HCCL通信时需要指定网口名称
图1 dev_pipeline.sh添加代码位置和内容
- 在llm_train/AscendSpeed/scripts/tools的路径下,新建脚本文件get_rank_table.py ,具体代码如下所示。
import os import re import sys import json from extras.logging import get_logger, set_file_handler logger = get_logger(__name__) def get_rank_table(): rank_table_file_path = os.getenv("RANK_TABLE_FILE") env_ip = os.getenv("ip") # Lite Cluster中的RANK_TABLE_FILE实际名称为 jobstart_hccl.json job_start_file = "jobstart_hccl.json" # job_start_file_path 路径默认为 "/user/config/jobstart_hccl.json" job_start_file_path = rank_table_file_path.rsplit("/", 1)[0] + "/" + job_start_file # 读取RANK_TABLE_FILE文件 with open(job_start_file_path, 'r', encoding='utf-8') as file: data = json.load(file) # RANK_TABLE_FILE文件缺少字段,表示文件缺少关键信息 if "status" not in data.keys() and "server_count" not in data.keys() and "server_list" not in data.keys(): logger.error(f"Get RANK_TABLE Error: RANK_TABLE_FILE missing key value.") sys.exit(1) # RANK_TABLE_FILE文件status不是completed,表示训练作业未创建成功 if data["status"] != "completed": logger.error(f"Get RANK_TABLE Error: RANK_TABLE_FILE is incomplete, and the training job creation was not successful.") sys.exit(1) # 获取:节点总数量:server_count 节点的RANK:server_index 单节点的NPU数量:device_count server_count = data["server_count"] server_list = data["server_list"] server_index = -1 device_count = 0 for index, server in enumerate(server_list): # RANK_TABLE_FILE文件中,节点ip为空 if server["server_id"] == "": logger.error(f"Get RANK_TABLE Error: the IP address of the server is null.") sys.exit(1) if server["server_id"] == env_ip: server_index = index if server["device"]: device_count = len(server["device"]) # RANK_TABLE_FILE文件中,节点总数量为0,表示未获取到节点 if server_count == 0: logger.error(f"Get RANK_TABLE Error: the server does not exist.") sys.exit(1) # RANK_TABLE_FILE文件中,未找到对应ip的节点 if server_index == -1: logger.error(f"Get RANK_TABLE Error: the IP address {env_ip} was not found.") sys.exit(1) # RANK_TABLE_FILE文件中,NPU卡数为0,表示未获取到NPU if device_count == 0: logger.error(f"Get RANK_TABLE Error: NPU does not exist.") sys.exit(1) return server_count, server_index, device_count if __name__ == '__main__': result = get_rank_table() print(','.join(map(str, result)))
上传代码和权重文件到工作环境
- 使用root用户以SSH的方式登录DevServer。
- 将AscendCloud代码包AscendCloud-xxx-xxx.zip上传到${workdir}目录下并解压缩,如SFS Turbo的路径:/mnt/sfs_turbo目录下,以下都以/mnt/sfs_turbo为例,请根据实际修改。
unzip AscendCloud-*.zip
- 上传tokenizers文件到工作目录中的/mnt/sfs_turbo/tokenizers/Llama2-{MODEL_TYPE}目录,如Llama2-70B。
进入到${workdir}目录下,如:/mnt/sfs_turbo,创建tokenizers文件目录将权重和词表文件放置此处,以Llama2-70B为例。
cd /mnt/sfs_turbo mkdir -p tokenizers/Llama2-70B