1.大模型分布式微调
1.为什么需要分布式训练
大模型规模爆炸:大模型的参数量急速增长,需要解决的问题和难度都在逐渐上升,决定大模型能力最重要的就是模型的参数,参数的数量就是平常说的几B的模型,1B就是10亿的参数,现代大模型(GPT-3以后的大模型)参数量达上千亿级别,单卡GPU无法存储完整的模型参数。
计算资源需求:训练大模型需要海量的计算(如GPT-3需要数万GPU),分布式训练才能加速训练过程
内存瓶颈:单卡显存不足以容纳大模型参数、梯度及优化器状态。
显存这个它不像我们的内存,内存我们是可以主动的去增加更改,但显存,一般是跟显卡的架构是有关系的,显卡的当时设计的时候是什么样的,它所支持的这个GPU的显存就已经固定下来了。
2.分布式训练的核心技术
1.数据并行(Data Parallelism)
原理:将数据划分为多个批次,分发到不同设备,每个设备拥有完整的模型副本
同步方式:通过AIl-Reduce操作同步梯度(如PyTorch的DistributedDataParallel)
挑战:通信开销大,显存占用高(需存储完整模型参数和优化器状态)
2.模型并行(Mudel Parallelism)
原理:将模型切分到不同设备(如按层或张量分片)。
1.横向并行(层拆分)
将模型的层分配到不同设备。
2.纵向并行(张量拆分)
如Megatron-LM将矩阵乘法分片。
挑战:设备间通信频繁,负载均衡需精细设计(显卡得是同系列一样的才能保证通信,因为不同的显卡架构不一样,通信不能保证同步)。
3.流水线并行(Pipeline Parallelism)
原理:将模型按层划分为多个阶段(stage),数据分块后按流水线执行。
优化:微批次(Micro-batching) 减少流水线气泡 (Bubble)。
挑战:需平衡阶段划分,避免资源闲置。
4.混合并行(3D并行)
组合策略:结合数据并行、模型并行、流水线并行,典型应用如训练千亿级模型。
案例:微软Turing-NLG、Meta的LLaMA-2。
3.DeepSpeed框架
1.DeepSpeed概述
定位: 微软开源的分布式训练优化框架,支持千亿参数模型训练,
核心目标: 降低大模型训练成本,提升显存和计算效率。
集成生态: 与PyTorch无缝兼容,支持HuggingFaceTransformers库
2.核心技术
ZeRO(Zero Redundancy Optimizer)优化器
原理: 通过分片优化器状态、梯度、参数,消除数据并行中的显存冗余。阶段划分:
ZeRO-1: 优化器状态分片,它的本质原理是每张显卡上面放的是完整的模型。听清楚每张显卡上面放的依然是完整的模型,前面的那个数据分布是一样的数据并行是一样的,每张卡上面放的是一个完整的模型,但是在进行优化更新的时候,它是只更新模型其中一部分的参数,剩余的这部分参数,在当前这张显卡上相当于是它的冗余部分,这部分的参数是不参与计算的,这样一来就可以极大的提升我们模型在反向传播过程中性能问题。
因为ZeRO-1它的这个处理方式是每张卡上面放的是完整的模型,对于DeepSpeed这个框架来讲,我们训练模型时如果只有一张卡的情况下,用和不用是没有区别,必须体现在是多张卡上面。
现阶段在llamaFactory框架中none就是ZeRO-1
ZeRO-2: 梯度分片+优化器状态分片。
ZeRO-3: 参数分片+梯度分片+优化器状态分片。
优势: 显存占用随设备数线性下降,支持训练更大模型。
ZeRO-2和ZeRO-3本质上是要把模型做切分的,但是因为我们的GPU上面只有一张卡,相当于说我们把模型切成了多个部分给它放到这一张卡上面去了,它相较于之前的状态,放在一张卡上面就是一个完整的模型,所占的显存会变得更高,并且每个模块之间需要单独的通信,那么它在更新的时候所花的时间会更长,所以在一张卡上配置ZeRO-2或ZeRO-3反而会减缓模型的训练过程。
在多卡上面最节约显存的配置方法就是ZeRO-3,但是ZeRO-3训练是最慢的,一般推荐使用ZeRO-2,ZeRO-1 是最普通的并行运算的,它的加速的效果是最差的,ZeRO-3我们一般不推荐,因为他训练也的太慢了。
3.显存优化技术
梯度检查点(Activation Checkpointing): 用时间换空间,减少激活值显存占用。
CPU Offloading: 将优化器状态和梯度卸载到CPU内存,在进行这个优化过程中,它临时的把优化器的状态由GPU给它加载到了内存中,这样来极大的降低显存的占用,但是它也会减缓我们的运算速度,因为我们训练时每一个批次都会需要用到优化器的状态和梯度的时候,它就得在下一个批次重新重内存给它加再加载到CPU里面去,内存和显存之间要进行通信,这个通信加载是需要花时间的,除非万不得已前面的ZeRO-1、ZeRO-2、ZeRO-3都用了,用了ZeRO-3之后显存还是不够,但是内存很大想要勉强的去跑这个的模型可以使用这种方式。
混合精度训练: FP16/BP16与动态损失缩放(LossScaling)其他特性,pytorch本身是支持混合精度训练的,而DeepSpeed是无缝兼容于pytorch,所以DeepSpeed本身也支持混合精度训练
大规模推理支持: 模型并行推理(如ZeRO-Inference)
自适应通信优化: 自动选择最佳通信策略(如Al-Reducevs.All-Gather)
4.优势与特点
显存效率高: ZeRO-3可将显存占用降低至1/设备数,假设这台服务器上面有八张卡,如果我们用的是DeepSpeed的ZeRO-3的优化模式,那么它每张卡上面的显存会降为原来的8分之1,不同的GPU架构上面,它的显存的节约力度不一样。
易用性强: 通过少量代码修改即可应用(如DeepSpeed配置JSON文件)
扩展性优秀: 支持千卡级集群训练。
开源社区支持: 持续更新,与HuggingFace等生态深度集成
5. 使用场景
训练百亿/千亿参数模型(如GPT-3、Turing-NLG)
资源受限环境: 单机多卡训练时通过Offloading
扩展模型规模快速实验: 通过ZeRO-2加速中等规模模型训练。
4.llamaFactory进行多卡训练
pip install deepspeed

一般导致错误原因有两个点:
1.就是你当前的这个CPU的内存和GPU的显存之间不支持的deep speed这种通信。因为他对通信是有要求的,通信太慢了,他也不允许操作。
2.就是CUDA的版本可能和他这个上面所要求的版本不匹配,就会出这个问题。
llamaFactory 多卡配置:

llamaFactory自动识别显卡数量这里不用改,none默认就是不启用,如果是多卡就是zero-1,2就是zero-2,3就是zero-3
5.xtuner多卡训练
1.创建xtuner环境
conda create -n xtuner python==3.10 -y
2.激活xtuner环境
source activate xtuner 或 conda activate xtuner
3.安装xtuner
1.使用pip安装
autodl算力云学术加速:source /etc/network_turbo
pip install -U 'xtuner[deepspeed]'
2.从源码安装(推荐)
git clone https://github.com/InternLM/xtuner.git
学术加速地址: https://gh-proxy.com/
加速后:git clone https://gh-proxy.com/github.com/InternLM/xtuner.git

cd xtuner
autodl算力云学术加速:source /etc/network_turbo 加速后在执行下面的安装
在终端中使用:
source /etc/network_turbo
取消学术加速,如果不再需要建议关闭学术加速,因为该加速可能对正常网络造成一定影响
unset http_proxy && unset https_proxy
pip install -e '.[deepspeed]' ( "-e" 表示在可编辑模式下安装项目,对代码所做的任何本地修改都会生效 )

4.验证
验证安装是否正确: 在命令行中使用 xtuner list-cfg
验证是否能打印配置文件列表。
pip list | grep -E "torch|transformers|bitsandbytes|triton|accelerate|peft"
5.下载模型
ini
#模型下载
from modelscope import snapshot_download
model_dir = snapshot_download('Qwen/Qwen2.5-1.5B-Instruct',cache_dir="/root/lanyun-tmp/LLM/")
6.复制xtuner微调脚本文件
qwen1_5_0_5b_chat_full_alpaca_e3.py 是全量预训练用的脚本
qwen1_5_0_5b_chat_qlora_alpaca_e3.py 是qlora微调用的脚本
复制一份放到xtuner根目录下

7.转换数据集
1.单轮对话转换弱智吧数据
ini
import json
# 源数据文件路径
source_file = 'data/ruozhiba_qaswift.json'
# 目标数据文件路径
target_file = 'data/convert_ruozhiba.json'
# 读取源数据
with open(source_file, 'r', encoding='utf-8') as f:
source_data = json.load(f)
# 转换数据
target_data = []
for item in source_data:
conversation = {
"conversation": [
{
"input": item["query"],
"output": item["response"]
}
]
}
target_data.append(conversation)
# 保存转换后的数据
with open(target_file, 'w', encoding='utf-8') as f:
json.dump(target_data, f, ensure_ascii=False, indent=4)
print(f"数据已成功转换并保存到 {target_file}")
2.多轮对话转换财经数据
转换前:📎fintech.json
python
import json
def convert_and_statistic(input_file):
with open(input_file, 'r', encoding='utf-8') as file:
data = json.load(file)
converted_data = []
entry_stats = {f"<=700": 0, "701-800": 0, "801-900": 0, "901-1000": 0, ">1000": 0}
max_length = 0
for entry in data:
conversation = []
# 处理history
if 'history' in entry and entry['history']:
for hist_entry in entry['history']:
hist_input, hist_output = hist_entry[0], hist_entry[1]
conversation.append({"input": hist_input, "output": hist_output})
update_entry_stats(hist_input, entry_stats)
update_entry_stats(hist_output, entry_stats)
max_length = max(max_length, len(hist_input), len(hist_output))
# 处理instruction和output
instruction = entry.get("instruction")
output = entry.get("output")
conversation.append({"input": instruction, "output": output})
if instruction is not None: # 确保instruction不是None
update_entry_stats(instruction, entry_stats)
if output is not None: # 确保output不是None
update_entry_stats(output, entry_stats)
max_length = max(max_length, len(instruction) if instruction is not None else 0, len(output) if output is not None else 0)
converted_data.append({"conversation": conversation})
# 计算总数据条目数
total_entries = sum(entry_stats.values())
# 计算各分段长度的数据条目占比
entry_stats_percentage = {key: (value / total_entries) * 100 for key, value in entry_stats.items() if total_entries != 0}
# 输出结果
return converted_data, entry_stats_percentage, max_length
def update_entry_stats(text, entry_stats):
if text is None:
return
length = len(text)
if length <= 700:
entry_stats["<=700"] += 1
elif 701 <= length <= 800:
entry_stats["701-800"] += 1
elif 801 <= length <= 900:
entry_stats["801-900"] += 1
elif 901 <= length <= 1000:
entry_stats["901-1000"] += 1
else:
entry_stats[">1000"] += 1
# 使用示例
converted_data, entry_stats_percentage, max_length = convert_and_statistic('data/fintech.json')
# 打印转换后的数据
print(json.dumps(converted_data, ensure_ascii=False, indent=4))
# 打印各分段长度的数据条目占比
print("\n各分段长度的数据条目占比:")
for key, value in entry_stats_percentage.items():
print(f"{key}: {value:.2f}%")
# 打印最大长度
print(f"\n最大长度: {max_length}")
# 如果需要将转换后的内容写入新的json文件,可以使用以下代码
with open('data/convert_fintech.json', 'w', encoding='utf-8') as new_file:
json.dump(converted_data, new_file, ensure_ascii=False, indent=4)
3.数据集合并
这里做数据集合并是因为:xtuner并不会像llamaFactory一样他打乱数据集的方式是按文件打乱的,最好是把所有的数据集放进一个文件
8.配置训练脚本
复制qwen1_5_0_5b_chat_qlora_alpaca_e3.py 的qlora微调脚本,新建一个自己的脚本,修改下面这几项即可
ini
### PART 1中
#预训练模型存放的位置
pretrained_model_name_or_path = '/root/lanyun-tmp/LLM/Qwen/Qwen2.5-1.5B-Instruct'
#微调数据存放的位置
data_files = '/root/lanyun-tmp/xtuner/convert_merged.json'#基座模型路径
# 训练中最大的文本长度
max_length = 512
# 每一批训练样本的大小
batch_size = 2
#最大训练轮数
max_epochs = 3
#验证数据
evaluation_inputs = [
'只剩一个心脏了还能活吗?', '爸爸再婚,我是不是就有了个新娘?',
'樟脑丸是我吃过最难吃的硬糖有奇怪的味道怎么还有人买',
'马上要上游泳课了,昨天洗的泳裤还没干,怎么办',
'我只出生了一次,为什么每年都要庆生'
]
# PART 2中,量化微调需要修改load_in_4bit或者 load_in_8bit
model = dict(
type=SupervisedFinetune,# 指令微调,监督式微调模型
use_varlen_attn=use_varlen_attn,# 是否使用可变长度注意力
llm=dict(
type=AutoModelForCausalLM.from_pretrained,# 加载因果语言模型
pretrained_model_name_or_path=pretrained_model_name_or_path,
trust_remote_code=True,
torch_dtype=torch.float16,# 使用半精度浮点数
quantization_config=dict( # 量化配置(QLoRA)
type=BitsAndBytesConfig,
load_in_4bit=True, # 4bit量化加载
load_in_8bit=False, # 8bit量化加载
llm_int8_threshold=6.0,# 8bit量化阈值
llm_int8_has_fp16_weight=False,
bnb_4bit_compute_dtype=torch.float16, # 计算时数据类型
bnb_4bit_use_double_quant=True,# 双重量化
bnb_4bit_quant_type="nf4",# 4bit量化类型
),
),
lora=dict(# LoRA配置
type=LoraConfig,
r=32, # LoRA秩
lora_alpha=64, # Alpha参数(缩放因子)
lora_dropout=0.1, # Dropout率
bias="none", # 偏置项处理方式
task_type="CAUSAL_LM", # 任务类型(因果语言模型)
),
)
# PART 3中
dataset=dict(type=load_dataset, path="json",data_files=data_files)
dataset_map_fn=None
9.完整配置脚本
ini
# Copyright (c) OpenMMLab. All rights reserved.
import torch
from datasets import load_dataset
from mmengine.dataset import DefaultSampler
from mmengine.hooks import (
CheckpointHook,
DistSamplerSeedHook,
IterTimerHook,
LoggerHook,
ParamSchedulerHook,
)
from mmengine.optim import AmpOptimWrapper, CosineAnnealingLR, LinearLR
from peft import LoraConfig
from torch.optim import AdamW
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from xtuner.dataset import process_hf_dataset
from xtuner.dataset.collate_fns import default_collate_fn
from xtuner.dataset.map_fns import alpaca_map_fn, template_map_fn_factory
from xtuner.engine.hooks import (
DatasetInfoHook,
EvaluateChatHook,
VarlenAttnArgsToMessageHubHook,
)
from xtuner.engine.runner import TrainLoop
from xtuner.model import SupervisedFinetune
from xtuner.parallel.sequence import SequenceParallelSampler
from xtuner.utils import PROMPT_TEMPLATE, SYSTEM_TEMPLATE
#######################################################################
# PART 1 Settings #
#######################################################################
# Model
#预训练模型存放的位置
pretrained_model_name_or_path = '/root/lanyun-tmp/LLM/Qwen/Qwen2.5-1.5B-Instruct'
#是否使用可变长度注意力机制
use_varlen_attn = False
# Data
#数据集存在的路径
data_file = '/root/lanyun-tmp/xtuner/convert_merged.json'
#使用哪个提示词模板
prompt_template = PROMPT_TEMPLATE.qwen_chat
#每个样本的最大长度
max_length = 384
#是否打包数据到最大长度
pack_to_max_length = True
# parallel
#序列并行数量(>1时启用序列并行)
sequence_parallel_size = 1
# Scheduler & Optimizer 调度器与优化器
# 单个设备批次大小
batch_size = 20 # per_device
# 梯度累积步数
accumulative_counts = 8
# 根据并行情况调整累积步数
accumulative_counts *= sequence_parallel_size
# 数据加载工作进程数
dataloader_num_workers = 0
# 最大训练轮数
max_epochs = 500
# 优化器类型
optim_type = AdamW
# 学习率
lr = 2e-4
# 优化器Beta参数
betas = (0.9, 0.999)
# 权重衰减
weight_decay = 0
# 梯度裁剪阈值
max_norm = 1 # grad clip
# 学习率预热比例
warmup_ratio = 0.03
# Save 模型保存设置
# 保存间隔步数
save_steps = 500
# 最大保存检查点数(-1表示不限制)
save_total_limit = 2 # Maximum checkpoints to keep (-1 means unlimited)
# 训练时评估设置 Evaluate the generation performance during the training
# 评估频率
evaluation_freq = 500
# 系统提示模板
SYSTEM = SYSTEM_TEMPLATE.alpaca
# 主观评估样例
evaluation_inputs = ['只剩一个心脏了还能活吗?', '樟脑丸是我吃过最难吃的硬糖有奇怪的味道怎么还有人买','列出三种创造性的解决问题的技巧。',
'凤凰国际在推动外汇市场发展的过程中,有没有遇到过哪些挑战?该如何应对这些挑战?','近期有关上海理财博览会的报道称,凤凰国际正在推动外汇新潮流,请问凤凰国际是如何推动外汇市场的发展的? ',
'凤凰国际作为金融服务机构,如何保护客户的资金安全?','了解凤凰国际在外汇市场的发展后,我想知道凤凰国际在哪些方面具有竞争优势?',
'OSS可以帮助我自动化哪些与营销相关的任务?','为什么没人说ABCD型的成语?🤔','活人和死人结婚是冥婚,那死人和活人结婚叫什么?',
'太阳还有五十亿年就没了,那到时候向日葵看哪呢?','期权的期权价格怎么算?','防疫政策目前是否有调整?它对经济复苏会有什么影响?']
#######################################################################
# PART 2 Model & Tokenizer #
#######################################################################
# 分词器配置
tokenizer = dict(
type=AutoTokenizer.from_pretrained, # 自动加载预训练分词器
pretrained_model_name_or_path=pretrained_model_name_or_path,
trust_remote_code=True,# 信任远程代码(用于自定义模型)
padding_side="right",# 填充方向(右侧填充)
)
# 模型配置
model = dict(
type=SupervisedFinetune,# 指令微调,监督式微调模型
use_varlen_attn=use_varlen_attn,# 是否使用可变长度注意力
llm=dict(
type=AutoModelForCausalLM.from_pretrained,# 加载因果语言模型
pretrained_model_name_or_path=pretrained_model_name_or_path,
trust_remote_code=True,
torch_dtype=torch.float16,# 使用半精度浮点数
quantization_config=dict( # 量化配置(QLoRA)
type=BitsAndBytesConfig,
load_in_4bit=True, # 4bit量化加载
load_in_8bit=False, # 8bit量化加载
llm_int8_threshold=6.0,# 8bit量化阈值
llm_int8_has_fp16_weight=False,
bnb_4bit_compute_dtype=torch.float16, # 计算时数据类型
bnb_4bit_use_double_quant=True,# 双重量化
bnb_4bit_quant_type="nf4",# 4bit量化类型
),
),
lora=dict(# LoRA配置
type=LoraConfig,
r=32, # LoRA秩
lora_alpha=64, # Alpha参数(缩放因子)
lora_dropout=0.1, # Dropout率
bias="none", # 偏置项处理方式
task_type="CAUSAL_LM", # 任务类型(因果语言模型)
),
)
#######################################################################
# PART 3 Dataset & Dataloader #
#######################################################################
# 数据集处理配置
alpaca_en = dict(
type=process_hf_dataset,# 使用HF数据集处理流程
dataset=dict(type=load_dataset, path="json",data_files=data_file),# 加载自定义的数据集
tokenizer=tokenizer,# 使用配置的分词器
max_length=max_length,# 设置最大长度
dataset_map_fn=None,# 数据集映射函数
template_map_fn=dict(
type=template_map_fn_factory,
template=prompt_template# 使用配置的提示词模板
),
remove_unused_columns=True,# 移除未使用的列
shuffle_before_pack=True,# 打包前打乱数据
pack_to_max_length=pack_to_max_length,# 是否填充到最大长度
use_varlen_attn=use_varlen_attn,# 是否使用可变长度注意力
)
# 采样器选择(根据是否启用序列并行)
sampler = SequenceParallelSampler if sequence_parallel_size > 1 else DefaultSampler
# 训练数据加载器配置
train_dataloader = dict(
batch_size=batch_size,# 批量大小
num_workers=dataloader_num_workers,# 数据加载工作进程数
dataset=alpaca_en,# 使用处理后的数据集
sampler=dict(type=sampler, shuffle=True),# 动态选择采样器类型
collate_fn=dict(# 数据整理函数
type=default_collate_fn,
use_varlen_attn=use_varlen_attn# 是否使用可变长度注意力
),
)
#######################################################################
# PART 4 Scheduler & Optimizer #
#######################################################################
# optimizer 优化器
# 优化器包装配置(支持混合精度)
optim_wrapper = dict(
type=AmpOptimWrapper,# 自动混合精度优化器包装
optimizer=dict(
type=optim_type,
lr=lr,
betas=betas,
weight_decay=weight_decay
),
clip_grad=dict(# 梯度裁剪配置
max_norm=max_norm,
error_if_nonfinite=False
),
accumulative_counts=accumulative_counts,# 梯度累积步数
loss_scale="dynamic",# 动态损失缩放
dtype="float16",# 计算类型
)
# learning policy
# More information: https://github.com/open-mmlab/mmengine/blob/main/docs/en/tutorials/param_scheduler.md # noqa: E501
# 学习率调度器配置(先线性预热,后余弦退火)
param_scheduler = [
dict( # 线性预热阶段
type=LinearLR,
start_factor=1e-5, # 初始学习率因子
by_epoch=True, # 按epoch计数
begin=0, # 开始位置
end=warmup_ratio * max_epochs, # 结束位置
convert_to_iter_based=True, # 转换为迭代基准
),
dict( # 余弦退火阶段
type=CosineAnnealingLR,
eta_min=0.0, # 最小学习率
by_epoch=True,
begin=warmup_ratio * max_epochs,
end=max_epochs,
convert_to_iter_based=True,
),
]
# train, val, test setting
# 训练循环配置
train_cfg = dict(type=TrainLoop, max_epochs=max_epochs)
#######################################################################
# PART 5 Runtime #
#######################################################################
# Log the dialogue periodically during the training process, optional
# 自定义钩子配置
custom_hooks = [
dict(type=DatasetInfoHook, tokenizer=tokenizer),# 数据集信息记录钩子
dict( # 定期对话评估钩子
type=EvaluateChatHook,
tokenizer=tokenizer,
every_n_iters=evaluation_freq,# 每隔指定迭代评估一次
evaluation_inputs=evaluation_inputs,# 评估输入样例
system=SYSTEM,# 系统提示语
prompt_template=prompt_template,# 提示模板
),
]
# 如果使用可变长度注意力,添加对应的参数处理钩子
if use_varlen_attn:
custom_hooks += [dict(type=VarlenAttnArgsToMessageHubHook)]
# 默认钩子配置 configure default hooks
default_hooks = dict(
# record the time of every iteration.
timer=dict(type=IterTimerHook), # 迭代计时器
# print log every 10 iterations.
logger=dict(# 日志记录配置
type=LoggerHook,
log_metric_by_epoch=False, # 按迭代记录指标
interval=10 # 每隔10次迭代记录
),
# enable the parameter scheduler.
param_scheduler=dict(type=ParamSchedulerHook),# 参数调度器
# save checkpoint per `save_steps`.
checkpoint=dict(# 检查点保存配置
type=CheckpointHook,
by_epoch=False, # 按迭代次数保存
interval=save_steps,# 保存间隔
max_keep_ckpts=save_total_limit,# 最大保存数量
),
# set sampler seed in distributed evrionment.
sampler_seed=dict(type=DistSamplerSeedHook),# 分布式采样器种子设置
)
# 环境配置 configure environment
env_cfg = dict(
# 是否启用CUDNN基准测试
cudnn_benchmark=False,
# set multi process parameters
mp_cfg=dict(# 多进程配置
mp_start_method="fork",
opencv_num_threads=0
),
# set distributed parameters
dist_cfg=dict(backend="nccl"),# 分布式后端配置
)
# 可视化工具(未启用)
visualizer = None
# 日志级别
log_level = "INFO"
# 预训练权重路径(空表示从零开始)
load_from = None
# 是否继续训练
resume = False
# 随机性配置
randomness = dict(
seed=None, # 随机种子(None表示随机生成)
deterministic=False # 是否启用确定性算法
)
# 日志处理器配置(按迭代次数处理)
log_processor = dict(by_epoch=False)
10.开始训练
1.单卡训练:
xtuner train /root/lanyun-tmp/xtuner/qwen2.5-1.5B-Instruct_qlora_alpaca_e3.py
2.多卡训练
这是 PyTorch的内存管理环境变量,作用是通过允许显存段动态扩展来减少内存碎片:
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
当显存碎片化严重时(即显存剩余总量足够,但因分配不连续导致大块显存请求失败)可以用上面命令。
执行2张卡的训练
NPROC_PER_NODE=2 xtuner train /root/lanyun-tmp/xtuner/qwen2.5-1.5B-Instruct_qlora_alpaca_e3.py --deepspeed deepspeed_zero2 --work-dir /root/lanyun-tmp/xtuner/work_dirs/weight
nvitop:

在微调过程中发现,vscode无故中断了训练,xtuner没有保存pth文件,导致白等了一段时间

修改命令再训练:
单卡:nohup xtuner train /root/lanyun-tmp/xtuner/qwen2.5-1.5B-Instruct_qlora_alpaca_e3.py > /root/lanyun-tmp/xtuner/train.log 2>&1 &
多卡:
NPROC_PER_NODE=2 nohup xtuner train /root/lanyun-tmp/xtuner/qwen2.5-1.5B-Instruct_qlora_alpaca_e3.py --deepspeed deepspeed_zero2 --work-dir /root/lanyun-tmp/xtuner/work_dirs/weight > /root/lanyun-tmp/xtuner/train.log 2>&1 &
3.继续训练
如果中断后需要继续训练 修改load_from和resume

11.模型转换
训练完成后会保留最近两个间隔步数的 PTH 模型(例如 iter_1000.pth、iter_1200.pth ,如果使用了 DeepSpeed,则将会是一个 文件夹)
这个pth文件,他只是训练出来的lora模型的权重,不包含原模型的参数信息,比如这个lower模型对应的base模型是哪个?它上面是没有的,包括模型的数据类型,这个权重里面也是没有的。
所以要借助最开始训练的配置文件qwen2.5-1.5B-Instruct_qlora_alpaca_e3.py,因为这个配置文件里面就包含了base模型的基础信息

我们需要利用xtuner convert pth_to_hf将其转换为 HuggingFace 模型,以便于后续使用
命令为:
xtuner convert pth_to_hf <math xmlns="http://www.w3.org/1998/Math/MathML"> 训练时的脚本 {训练时的脚本} </math>训练时的脚本{pth文件的路径} ${要保存的路径}
例如:
xtuner convert pth_to_hf qwen2.5-1.5B-Instruct_qlora_alpaca_e3.py /root/lanyun-tmp/xtuner/work_dirs/weight/iter_1200.pth /root/lanyun-tmp/xtuner/work_dirs/weight/inerHf

转换后对话:
xtuner chat /root/lanyun-tmp/LLM/Qwen/Qwen2.5-1.5B-Instruct --adapter /root/lanyun-tmp/xtuner/work_dirs/weight/inerHf --prompt-template qwen_chat

12.模型合并
xtuner convert merge <math xmlns="http://www.w3.org/1998/Math/MathML"> L L M {LLM} </math>LLM{LLM_ADAPTER} ${SAVE_PATH}
xtuner convert merge /root/lanyun-tmp/LLM/Qwen/Qwen2.5-1.5B-Instruct /root/lanyun-tmp/xtuner/work_dirs/weight/inerHf /root/lanyun-tmp/xtuner/work_dirs/weight/merged
1.合并后全量对话:
xtuner chat /root/lanyun-tmp/xtuner/work_dirs/weight/Qwen2.5-1.5B-Instruct-merged --prompt-template qwen_chat
