1.前言
我的赛博老师Andrej Karpathy曾经表示过,学习最好的方法就是一个项目干20遍。大洋彼岸的这位赛博活佛深得实践出真知的精髓,这句话我要深深地实践起来。
我们有幸参与到这么优质的学习营中,有行业顶尖的老师授课还提供免费的计算资源,这简直太幸运!课程中提供的代码和布置的作业,仅仅是跑一遍匆匆交个作业的话,无疑是暴殄天物的。这么好的东西就算不真干20遍,也得全部拆开了把玩把玩。
比起上一篇进行微调的课程,这个数据并行是个更高深更值得学习的技能点。我准备输出上中下三篇:上是课程示例代码的拆解;中是作业的代码怎么改以及上节课的三篇微调示例代码也一并改成数据并行模式跑一跑;下是克服集群通信的问题用四卡和八卡跑一跑这个示例代码。
这次的学习比起上节课可谓是困难重重,集群通信多次尝试失败多成功少。来来回回地盘代码早把代金券给用光了,计算资源捉襟见肘。好在主办方提供了新的计算资源和更为专业的指导,等我一步一步把问题解决,输出成文供大家交流学习!
提前说明,我也是一个奉行勤奋的基础学习者,大部分的内容靠着不断地向AI工具提问求证而得。AI是辅助学习的工具,让我在短时间内具备了对专业知识的基本使用能力。AI时代我们每个人都不缺知识和信息,我们缺乏的是利用知识信息的提问能力和判断能力。让我们在利用AI辅助的学习中,不断地锻炼这两种能力吧!
学习代码在这里:https://github.com/mindspore-lab/mindnlp/tree/master/examples/parallel/bert_imdb_finetune
其他课程资料:
https://github.com/B3DDD/MindNLP_workhub.git
2.准备
数据并行
数据并行是最常用的并行训练方式,用于加速模型训练和处理大规模数据集。在数据并行模式下,训练数据被划分成多份,然后将每份数据分配到不同的计算节点上,例如多卡或者多台设备。每个节点独立地处理自己的数据子集,并使用相同的模型进行前向传播和反向传播,最终对所有节点的梯度进行同步后,进行模型参数更新。
数据并行支持的硬件平台包括Ascend、GPU和CPU,此外还同时支持PyNative模式和Graph模式。
相关接口:
mindspore.set_auto_parallel_context(parallel_mode=ParallelMode.DATA_PARALLEL)
:设置数据并行模式。mindspore.nn.DistributedGradReducer()
:进行多卡梯度聚合。
整体流程
-
环境依赖
每次开始进行并行训练前,通过调用
mindspore.communication.init
接口初始化通信资源,并自动创建全局通信组WORLD_COMM_GROUP
。通信组能让通信算子在卡间和机器间进行信息收发,全局通信组是最大的一个通信组,包括了当前训练的所有设备。通过调用mindspore.set_auto_parallel_context(parallel_mode=ParallelMode.DATA_PARALLEL)
设置当前模式为数据并行模式。 -
数据分发(Data distribution)
数据并行的核心在于将数据集在样本维度拆分并下发到不同的卡上。在
mindspore.dataset
模块提供的所有数据集加载接口中都有num_shards
和shard_id
两个参数,它们用于将数据集拆分为多份并循环采样的方式,采集batch
大小的数据到各自的卡上,当出现数据量不足的情况时将会从头开始采样。 -
网络构图
数据并行网络的书写方式与单卡网络没有差别,这是因为在正反向传播(Forward propagation & Backward propagation)过程中各卡的模型间是独立执行的,只是保持了相同的网络结构。唯一需要特别注意的是为了保证各卡间训练同步,相应的网络参数初始化值应当是一致的,在
DATA_PARALLEL
模式下可以通过mindspore.set_seed
接口来设置seed或通过使能mindspore.set_auto_parallel_context
中的parameter_broadcast
达到多卡间权重初始化一致的目的。 -
梯度聚合(Gradient aggregation)
数据并行理论上应该实现和单卡一致的训练效果,为了保证计算逻辑的一致性,通过调用
mindspore.nn.DistributedGradReducer()
接口,在梯度计算完成后自动插入AllReduce
算子实现各卡间的梯度聚合操作。DistributedGradReducer()
接口中提供了mean
开关,用户可以选择是否要对求和后的梯度值进行求平均操作,也可以将其视为超参项。 -
参数更新(Parameter update)
因为引入了梯度聚合操作,所以各卡的模型会以相同的梯度值一起进入参数更新步骤。
战场准备
Hardware Environment(Ascend/GPU/CPU) /
硬件环境: Ascend: 1*ascend-snt9b1|ARM: 24核 192GB
Software Environment / 软件环境 (Mandatory / 必填):
ModelART--西南贵阳一
-mindspore_2.3.0
-cann_8.0.rc2
-py_3.9
-euler_2.10.7
- aarch64-snt9b
| Ascend Snt9B+ARM算法开发和训练基础镜像,AI引擎预置MindSpore
安装MindNLP_0.4.1
pip install git+https://github.com/mindspore-lab/mindnlp.git
升级Mindspore2.4.1
pip install --upgrade mindspore
TIps:如果同学遭遇网络问题,无法从github获取mindnlp最新版本,建议采取以下办法:
GitHub - mindspore-lab/mindnlp: Easy-to-use and high-performance NLP and LLM framework based on MindSpore, compatible with models and datasets of 🤗Huggingface. 手动下载Mindnlp.zip 上传到环境中
unzip mindnlp.zip
cd mindnlp
bash scripts/build_and_reinstall.sh
获取实验代码并运行脚本即可
cd mindnlp/llm/parallel/bert_imdb_finetune_dp
bash bert_imdb_finetune_npu_mindnlp_trainer.sh
具体操作也可以参考这位同学的分享:
深度学习:基于MindSpore NLP的数据并行训练-优快云博客
3.代码
本次课程示例代码是使用动态组网方式建立2卡NPU训练集群,使用BERT模型在IMDB数据集上进行情感分类任务的微调训练脚本。bert_imdb_finetune_cpu_mindnlp_trainer_npus_same.py是训练过程脚本,bert_imdb_finetune_npu_mindnlp_trainer.sh是执行脚本。
3.1执行脚本bert_imdb_finetune_npu_mindnlp_trainer.sh
EXEC_PATH=$(pwd)
if [ ! -d "${EXEC_PATH}/data" ]; then
if [ ! -f "${EXEC_PATH}/emotion_detection.tar.gz" ]; then
wget https://baidu-nlp.bj.bcebos.com/emotion_detection-dataset-1.0.0.tar.gz -O emotion_detection.tar.gz
fi
tar xvf emotion_detection.tar.gz
fi
export DATA_PATH=${EXEC_PATH}/data/
环境变量设置:
EXEC_PATH=$(pwd): 获取当前工作目录,并将其赋值给 EXEC_PATH。
检查数据目录和下载数据集:
检查是否存在 data 目录。
如果不存在 data 目录,则检查是否存在 emotion_detection.tar.gz 文件。
如果不存在 emotion_detection.tar.gz 文件,则从指定 URL 下载数据集。
解压 emotion_detection.tar.gz 文件到当前目录。
设置 DATA_PATH 为 EXEC_PATH/data/。
rm -rf bert_imdb_finetune_cpu_mindnlp_trainer_npus_same
mkdir bert_imdb_finetune_cpu_mindnlp_trainer_npus_same
echo "start training"
export MULTI_NPU="true"
export ASCEND_SLOG_PRINT_TO_STDOUT=1
清理和创建训练目录:
删除旧的训练目录(如果存在)。
创建新的训练目录 bert_imdb_finetune_cpu_mindnlp_trainer_npus_same。
输出开始训练的信息。
设置环境变量:
export MULTI_NPU="true": 设置多 NPU 训练模式。
启用多 NPU 并行计算:当你设置
MULTI_NPU="true"
时,MindSpore 或其他框架会自动识别系统中的多个 NPU 设备,并将任务分配到这些设备上进行并行处理。这可以显著提高训练和推理的速度,尤其是在处理大规模数据集或复杂模型时。分布式训练:在多 NPU 环境中,
MULTI_NPU="true"
通常与分布式训练框架(如 MindSpore 的Distributed Training
模块)结合使用,允许多个 NPU 设备协同工作,分担计算负载。每个 NPU 设备可以处理不同的数据批次或模型参数,从而加速训练过程。
export ASCEND_SLOG_PRINT_TO_STDOUT=1: 将日志输出到标准输出。
- 控制Ascend设备相关的日志是否输出到标准输出
- 主要影响scheduler.log中的设备相关日志内容
- ASCEND_SLOG_PRINT_TO_STDOUT-日志-辅助功能-环境变量参考-API参考-CANN社区版8.0.RC2.alpha001开发文档-昇腾社区
msrun --worker_num=1 --local_worker_num=1 --master_port=8121 \
--log_dir=bert_imdb_finetune_cpu_mindnlp_trainer_npus_same --join=True \
--cluster_time_out=10 bert_imdb_finetune_cpu_mindnlp_trainer_npus_same.py
启动训练任务:
使用 msrun 命令启动训练任务。
--worker_num=2: 设置工作进程数为 2。
--local_worker_num=2: 设置本地工作进程数为 2。
--master_port=8121: 设置主进程端口为 8121。
--log_dir=bert_imdb_finetune_cpu_mindnlp_trainer_npus_same: 设置日志目录。
--join=True: 等待所有进程完成。
--cluster_time_out=10: 设置集群超时时间为 10 秒。
bert_imdb_finetune_cpu_mindnlp_trainer_npus_same.py: 执行的 Python 脚本文件。
msrun
是动态组网启动方式的封装,用户可使用msrun
以单个命令行指令的方式在各节点拉起多进程分布式任务,并且无需手动设置动态组网环境变量。
msrun
同时支持Ascend
,GPU
和CPU
后端。
- worker_num:参与分布式任务的Worker进程总数,默认值为8。
- local_worker_num:当前节点上拉起的Worker进程数,默认值为8。
- master_port:指定Scheduler绑定端口号,默认为8118。
- log_dir:Worker以及Scheduler日志输出路径,默认为当前目录。日志格式如下:对于Scheduler进程,日志名为
scheduler.log
;对于Worker进程,日志名为worker_[rank].log
,其中rank
后缀与分配给Worker的rank_id
一致。- join:msrun是否等待Worker以及Scheduler退出,若设置为True,msrun会等待所有进程退出后,收集异常日志并退出。
- cluster_time_out:集群组网超时时间,单位为秒,若超出此时间窗口依然没有
worker_num
数量的Worker注册成功,则任务拉起失败。- msrun的参数列表:MindSpore
分布式并行的四种启动方式:MindSpore
3.2训练脚本bert_imdb_finetune_cpu_mindnlp_trainer_npus_same.py
imdb_ds = load_dataset('imdb', split=['train', 'test'])
imdb_train = imdb_ds['train']
imdb_train.get_dataset_size()
将IMDB数据集进行切分,分为训练集train和测试集test。获取训练集数据大小。
- IMDB电影评论数据集
- 包含25,000条训练样本和25,000条测试样本
- 每个样本是电影评论文本及其情感标签(正面/负面)
from mindnlp.transformers import AutoTokenizer
# tokenizer
tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')
导入预训练的bert-base-cased模型
- 配置为序列分类任务(SequenceClassification)
- 输出类别数为2(正面/负面情感)
def process_dataset(dataset, tokenizer, max_seq_len=256, batch_size=32, shuffle=False):
# 检测是否在Ascend NPU上运行
is_ascend = mindspore.get_context('device_target') == 'Ascend'
# 内部tokenize函数,处理单条文本
def tokenize(text):
if is_ascend:
# NPU模式:使用固定长度padding
tokenized = tokenizer(text, padding='max_length', truncation=True, max_length=max_seq_len)
else:
# CPU模式:动态padding
tokenized = tokenizer(text, truncation=True, max_length=max_seq_len)
return tokenized['input_ids'], tokenized['token_type_ids'], tokenized['attention_mask']
# 数据集处理流程
if shuffle:
dataset = dataset.shuffle(batch_size)
# 应用tokenization
dataset = dataset.map(operations=[tokenize],
input_columns="text",
output_columns=['input_ids', 'token_type_ids', 'attention_mask'])
# 标签类型转换
dataset = dataset.map(operations=transforms.TypeCast(mindspore.int32),
input_columns="label",
output_columns="labels")
# 批处理
if is_ascend:
dataset = dataset.batch(batch_size)
else:
dataset = dataset.padded_batch(batch_size,
pad_info={'input_ids': (None, tokenizer.pad_token_id),
'token_type_ids': (None, 0),
'attention_mask': (None, 0)})
return dataset
定义数据处理函数,检测是否在Ascend NPU上运行并定义数据处理流。
dataset_train = process_dataset(imdb_train, tokenizer, shuffle=True)
应用数据处理:打乱IMDB训练数据集,用分词器处理文本。
next(dataset_train.create_tuple_iterator())
验证数据集迭代器:创建一个数据迭代器,来逐一获得上面处理过的数据集里的数据。
from mindnlp.transformers import AutoModelForSequenceClassification
# set bert config and define parameters for training
model = AutoModelForSequenceClassification.from_pretrained('bert-base-cased', num_labels=2)
AutoModelForSequenceClassification
是一个自动化的类,可以根据提供的预训练模型名称(如'bert-base-cased'
)自动加载相应的模型架构,并将其配置为适合序列分类任务的模型。它会根据模型名称自动选择合适的权重和配置文件。from_pretrained()
方法:这个方法用于从 Hugging Face 的模型库中加载预训练的模型。你可以通过指定模型名称来加载不同的预训练模型。'bert-base-cased'
:基于 BERT 的预训练模型,使用区分大小写的词汇表。num_labels
参数用于指定分类任务的标签数量。在这个例子中,num_labels=2
表示这是一个二分类任务,通常用于情感分析(正面/负面)、垃圾邮件检测(垃圾/非垃圾)等场景。
from mindnlp.engine import TrainingArguments
training_args = TrainingArguments(
output_dir="bert_imdb_finetune_cpu",
save_strategy="epoch",
logging_strategy="epoch",
num_train_epochs=2.0,
learning_rate=2e-5
)
TrainingArguments 是一个配置类,用于定义训练过程中所需的各项参数。它简化了训练配置的过程,使得你可以通过传递关键字参数来设置各种训练选项。
- output_dir="bert_imdb_finetune_cpu":
作用:指定训练过程中生成的输出文件(如模型检查点、日志文件等)的保存目录。
值:"bert_imdb_finetune_cpu" 表示所有输出文件将保存到名为 bert_imdb_finetune_cpu 的目录中。你可以根据需要更改这个目录名称。
- save_strategy="epoch":
作用:指定模型保存的策略。save_strategy 决定了在训练过程中何时保存模型检查点。
值:"epoch" 表示每次完成一个 epoch 后保存一次模型。其他可能的值包括:
"no":不保存模型检查点。
"steps":根据 save_steps 参数指定的步数保存模型。
- logging_strategy="epoch":
作用:指定日志记录的策略。logging_strategy 决定了在训练过程中何时记录日志信息。
值:"epoch" 表示每次完成一个 epoch 后记录一次日志。其他可能的值包括:
"no":不记录日志。
"steps":根据 logging_steps 参数指定的步数记录日志。
- num_train_epochs=2.0:
作用:指定训练的总轮数(epochs)。每个 epoch 表示对整个训练数据集进行一次完整的遍历。
值:2.0 表示训练将进行 2 个 epoch。你可以根据任务的复杂性和数据集的大小调整这个值。
- learning_rate=2e-5:
作用:指定训练过程中使用的初始学习率。学习率是优化器更新模型参数时的一个关键超参数,决定了每次更新的步长。
值:2e-5 是一个常见的学习率值,适用于基于 BERT 的微调任务。你可以根据任务的具体情况调整学习率,通常较小的学习率有助于防止模型过拟合。
training_args = training_args.set_optimizer(name="adamw", beta1=0.8) # Manually specify the optimizer, OptimizerNames.SGD
set_optimizer
是 TrainingArguments
类中的一个方法,用于手动指定优化器的名称和相关超参数。通过这个方法,你可以覆盖默认的优化器配置,选择适合你任务的优化器,并调整其超参数。
-
参数:
-
name
:指定优化器的名称。常见的优化器名称包括:"adamw"
:AdamW 优化器,结合了 Adam 和权重衰减(weight decay),适用于深度学习任务。"sgd"
:随机梯度下降(SGD)优化器,适用于简单的任务或需要更稳定的更新规则的情况。"adafactor"
:Adafactor 优化器,适用于大规模语言模型,具有较低的内存占用。"adam"
:经典的 Adam 优化器,广泛应用于各种深度学习任务。
-
beta1
:Adam 或 AdamW 优化器的第一个动量系数(momentum coefficient)。默认值为 0.9。beta1
控制着一阶矩估计的衰减率,通常用于加速收敛。
-
其他参数:根据不同的优化器,set_optimizer
还可以接受其他参数。例如,learning_rate
、weight_decay
、eps
等。
trainer = Trainer(
model=model,
args=training_args,
train_dataset=dataset_train,
)
print("Start training")
trainer.train()
Trainer
类
- 作用:
Trainer
是一个高级类,用于管理模型的训练和评估过程。它简化了训练流程,使得你可以专注于模型的设计和超参数的调整,而不需要手动编写复杂的训练循环。
参数说明
model=model
:- 作用:指定要训练的模型。在这个例子中,
model
是一个已经加载并配置好的AutoModelForSequenceClassification
实例,基于 BERT 模型并设置为二分类任务。
- 作用:指定要训练的模型。在这个例子中,
args=training_args
:- 作用:传递
TrainingArguments
对象,包含训练过程中所需的各项参数(如输出目录、保存策略、学习率等)。training_args
是你在前面代码中定义的训练参数配置。
- 作用:传递
train_dataset=dataset_train
:- 作用:指定训练数据集。
dataset_train
是一个经过预处理的Dataset
对象,包含训练样本及其标签。这个数据集将用于训练模型。
- 作用:指定训练数据集。
trainer.train()
-
作用:启动训练过程。
Trainer
会根据TrainingArguments
中的配置自动处理训练循环、保存模型、记录日志等操作。训练过程中,Trainer
会定期保存模型检查点,并记录训练进度和损失值。 -
输出:在训练过程中,
Trainer
会根据logging_strategy
和save_strategy
的配置,定期输出训练日志和保存模型检查点。你可以在控制台中看到训练进度、损失值等信息。
if __name__ == '__main__':
main()
- 确保脚本可以作为主程序运行,也可以被其他模块导入。
4.结果--这些文件都是怎么来的?
代码执行完毕后,又出现了很有意思的东西,这个文件夹竟让人完全看不懂。这一大堆新出现的文件到底是什么东西???
这个烟花缭乱的文件夹一开始其实只有bert_imdb_finetune_cpu_mindnlp_trainer_npus_same.py和bert_imdb_finetune_npu_mindnlp_trainer.sh两篇代码,其他朋友都是后面来的。
4.1文件夹bert_imdb_finetune_cpu
这个文件夹保存了两个checkpoint文件,经历了这么多代码的洗礼,直觉告诉我们checkpoint基本都是模型保存的一种方式,大概率里面就是模型的一个存档。那么这个数字782和1564又是怎么回事呢?
4.1.1文件夹checkpoint-782和checkpoint-1564是啥玩意?
首先为什么会产生这两个checkpoint文件夹呢?
这与训练数据大小和训练设置有关,其实它们的保存来自于这段代码:
training_args = TrainingArguments(
output_dir="bert_imdb_finetune_cpu",
save_strategy="epoch", # 每个epoch保存一次
logging_strategy="epoch",
num_train_epochs=2.0, # 训练2个epoch
learning_rate=2e-5
)
checkpoint文件夹的编号(782和1564)代表了训练步数(steps)。这些数字是这样计算出来的:
- IMDB数据集的训练集包含25,000条样本
- 你的代码中batch_size设置为32
- 因此,每个epoch的步数 = 25000 ÷ 32 ≈ 782步
- 由于设置了训练2个epoch:
- 第一个epoch结束时:782步 -> 产生 checkpoint-782
- 第二个epoch结束时:782 × 2 = 1564步 -> 产生 checkpoint-1564
这些checkpoint文件夹是由于我们在TrainingArguments中设置了save_strategy="epoch"导致的,这意味着训练器会在每个epoch结束时保存模型的状态。
当我们回到TrainingArguments的文档中,一切的答案都会浮现。
set_save(strategy: Union[str, IntervalStrategy] = 'steps', steps: int= 500, total_limit: Optional[int] = None, on_each_node: bool = True, **kwargs)[源代码]
设置与checkpoint保存相关的参数。
参数:
strategy (Union[str, IntervalStrategy], 可选) - 训练过程中保存权重的策略,"no"表示训练中不保存权重,"epoch"表示训练中每个epoch结束后保存权重,"steps"表示训练中每经过 steps 步数后保存权重。默认值:
"steps"
。steps (int, 可选) - 两次保存权重之间间隔的步数,在 strategy 值为 steps 时生效。默认值:
500
。total_limit (int, 可选) - checkpoint的总数量,如果超过该数量,会删除 output_dir 目录下最老的权重。默认值:
None
。on_each_node (bool, 可选) - 在多节点分布式训练时,控制在每个节点上保存权重或者只在主节点上保存。默认值:
True
。kwargs (Any) - 其它参数。
from mindformers import TrainingArguments args = TrainingArguments(output_dir="output") args = args.set_save(strategy="steps", steps=100) args.save_steps
4.1.1.IF 如果选择了save_strategy默认的steps会发生什么呢?
如果将save_strategy从"epoch"改为"steps",需要额外指定save_steps参数来决定每隔多少步保存一次checkpoint,否则是默认“500”steps后会保存一次。
training_args = TrainingArguments(
output_dir="bert_imdb_finetune_cpu",
save_strategy="steps", # 改为按步数保存
save_steps=100, # 每100步保存一次checkpoint
logging_strategy="steps", # 通常也会将logging策略改为相同的
logging_steps=100, # 每100步记录一次日志
num_train_epochs=2.0,
learning_rate=2e-5
)
如果设为100步保存一次:
1. 会在训练过程中每隔100步保存一次checkpoint
2. 假设总训练步数仍然是1564步(2个epoch),将会产生以下checkpoint文件夹:
- checkpoint-100
- checkpoint-200
- checkpoint-300
- ...
- checkpoint-1500
每个checkpoint文件夹内部的文件结构与之前相同,都包含:
- config.json
- model.safetensors
- optimizer.ckpt
- scheduler.json
- trainer_state.json
好处是:
1. **保存频率更高**:不用等到整个epoch结束才保存
2. **占用空间更大**:因为保存的checkpoint数量更多
3. **更细粒度的恢复点**:可以从更多的中间状态恢复训练
4. **更好的进度监控**:可以更频繁地查看训练状态
坏处就是
占用太多资源,拖慢了训练进度。
4.1.2checkpoint文件夹内的5个文件分别是什么?
1. **config.json**
- 这是模型的配置文件,包含了模型的架构信息
- 包括隐藏层大小、注意力头数量、层数等超参数
- 用于在加载模型时重建完全相同的模型架构
2. **model.safetensors**
- 这是模型的权重文件
- 使用safetensors格式存储,这是一种比传统pickle更安全的权重存储格式
- 包含了模型所有层的参数(权重和偏置)
- 用于恢复模型的训练状态
3. **optimizer.ckpt**
- 优化器的状态文件
- 包含了优化器的状态信息,如动量缓存、学习率等
- 对于AdamW优化器,包含了一阶矩估计(m)和二阶矩估计(v)
- 用于恢复训练时的优化器状态,确保继续训练时优化器的行为一致
4. **scheduler.json**
- 学习率调度器的配置文件
- 记录了学习率调度的相关参数
- 用于在恢复训练时保持正确的学习率调度策略
5. **trainer_state.json**
- 训练器的状态信息
- 包含了训练的元数据,如:
- 当前epoch数
- 全局步数
- 最佳模型信息
- 训练损失历史
- 学习率变化历史等
- 用于追踪训练进度和恢复训练状态
这样保存的作用是:
1. **断点续训**:可以从任何检查点恢复训练
2. **模型复用**:可以加载预训练的模型继续微调
3. **训练监控**:可以追踪训练过程中的各种指标
4. **安全性**:使用safetensors格式避免pickle相关的安全问题
5. **完整性**:确保模型训练的所有相关状态都被保存
这是MindNLP的Trainer类的标准保存策略,参考了Hugging Face的实现方式,确保了模型训练的可复现性和可持续性。
MindNLP Trainer
类的标准保存策略是通过TrainingArguments
类来配置的。TrainingArguments
允许用户灵活地控制训练过程中检查点的保存频率、保存路径、保存方式等。默认情况下,Trainer
类会根据TrainingArguments
中的save_strategy
参数来决定何时保存检查点。详情可以查阅下面文档:
4.2bert_imdb_finetune_cpu_mindnlp_trainer_npus_same
export ASCEND_SLOG_PRINT_TO_STDOUT=1
- 控制Ascend设备相关的日志是否输出到标准输出
- 主要影响scheduler.log中的设备相关日志内容
- 如果设为0,某些设备日志可能不会被记录
- ASCEND_SLOG_PRINT_TO_STDOUT-日志-辅助功能-环境变量参考-API参考-CANN社区版8.0.RC2.alpha001开发文档-昇腾社区
msrun --worker_num=2 --local_worker_num=2 \
--log_dir=bert_imdb_finetune_cpu_mindnlp_trainer_npus_same \
- msrun的参数
- --log_dir:指定日志文件的输出目录
- --worker_num:指定worker数量,影响worker日志文件的数量
- 这些参数决定了worker日志文件的生成
scheduler .log
由ASCEND_SLOG_PRINT_TO_STDOUT=1生成的Ascend设备相关的日志。
426kB [00:08, 52.0kB/s]
/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/mindnlp/transformers/tokenization_utils_base.py:1526: FutureWarning: `clean_up_tokenization_spaces` was not set. It will be set to `True` by default. This behavior will be depracted, and will be then set to `False` by default.
warnings.warn(
- 参数说明:
- clean_up_tokenization_spaces是tokenizer的一个参数
- 用于控制是否清理tokenization过程中的额外空格
- 当前行为:
- 当前没有明确设置这个参数
- 默认值是True
- 会自动清理tokenization过程中的额外空格
- 未来变更:
- 这个默认行为将被废弃(deprecated)
- 在未来版本中,默认值将改为False
- 意味着将不会自动清理额外的空格
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 416M/416M [05:40<00:00, 1.28MB/s] [MS_ALLOC_CONF]Runtime config: enable_vmm:True vmm_align_size:2MB Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-cased and are newly initialized: ['classifier.bias', 'classifier.weight'] You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference. Start training
1. **进度条信息**:
```
100%|████...████| 416M/416M [05:40<00:00, 1.28MB/s]
```
这显示的是下载预训练BERT模型的进度:
- 总大小:416MB
- 已下载:416MB (100%)
- 用时:5分40秒
- 下载速度:1.28MB/s2. **配置信息**:
```
[MS_ALLOC_CONF]Runtime config: enable_vmm:True vmm_align_size:2MB
```
这是MindSpore的内存管理配置:
- `enable_vmm`: 启用虚拟内存管理
- `vmm_align_size`: 内存对齐大小为2MB3. **模型初始化警告**:
```
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-cased and are newly initialized:
['classifier.bias', 'classifier.weight']
```
这个警告说明:
- 模型结构被修改用于序列分类任务
- 分类器层是新增的,需要从头训练
- 具体新初始化的参数是:
- `classifier.bias`: 分类器偏置
- `classifier.weight`: 分类器权重4. **建议信息**:
```
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
```
- 需要在下游任务上训练模型
- 直接使用可能效果不好
- 这正是正在做的事情(IMDB情感分类任务)这些信息表明:
1. 成功下载了预训练模型
2. 正确配置了运行环境
3. 模型结构已适配分类任务
4. 准备开始微调训练这是典型的BERT微调过程中的正常提示信息,不需要担心这些警告。
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1564/1564 [15:34<00:00, 1.67it/s] {'loss': 0.01, 'learning_rate': 2.5e-05, 'epoch': 1.0} {'loss': 0.0495, 'learning_rate': 0.0, 'epoch': 2.0} {'train_runtime': 934.3342, 'train_samples_per_second': 53.565, 'train_steps_per_second': 1.674, 'train_loss': 0.029753798414069367, 'epoch': 2.0}
这段信息展示了整个训练过程的关键指标和最终结果。详细分析:
1. **训练进度条**:
```
100%|█...█| 1564/1564 [15:34<00:00, 1.67it/s]
```
- 总步数:1564步(完整的2个epoch)
- 训练时长:15分34秒
- 训练速度:1.67步/秒2. **每个Epoch的状态**:
```python
# Epoch 1结束时的状态
{'loss': 0.01, 'learning_rate': 2.5e-05, 'epoch': 1.0}# Epoch 2结束时的状态
{'loss': 0.0495, 'learning_rate': 0.0, 'epoch': 2.0}
```
- 损失值变化:从0.01到0.0495
- 学习率变化:从2.5e-05降到0
- 完成了2个完整epoch3. **最终训练统计**:
```python
{
'train_runtime': 934.3342, # 总训练时间(秒)
'train_samples_per_second': 53.565, # 每秒处理的样本数
'train_steps_per_second': 1.674, # 每秒执行的训练步数
'train_loss': 0.029753798414069367, # 平均训练损失
'epoch': 2.0 # 总训练轮数
}
```关键观察:
1. 训练效率:
- 每秒处理约53.6个样本
- 每秒执行约1.67个训练步骤
- 总运行时间约15.6分钟2. 训练效果:
- 最终平均损失约0.0298
- 损失值总体呈下降趋势
- 学习率按计划完成衰减这些指标表明训练过程顺利完成,模型收敛情况良好。损失值的持续下降和合理的处理速度都表明这是一次成功的训练过程。
worker_0 .log
由msrun参数自动生成worker日志
Generating train split: 100%|████████████████████████████████████████████████████████████████████████████████████████████████████| 25000/25000 [00:00<00:00, 159271.16 examples/s]
Generating test split: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████| 25000/25000 [00:00<00:00, 174997.45 examples/s]
Generating unsupervised split: 100%|█████████████████████████████████████████████████████████████████████████████████████████████| 50000/50000 [00:00<00:00, 171537.48 examples/s]
这些进度条显示了IMDB数据集的加载和处理进度。让我详细分析每个部分:
1. **训练集处理**:
```
Generating train split: 100%|█...█| 25000/25000 [00:00<00:00, 159271.16 examples/s]
```
- 总样本数:25,000条训练样本
- 处理速度:约159,271样本/秒
- 处理时间:不到1秒
- 进度显示:0% → 56% → 100%2. **测试集处理**:
```
Generating test split: 100%|█...█| 25000/25000 [00:00<00:00, 174997.45 examples/s]
```
- 总样本数:25,000条测试样本
- 处理速度:约174,997样本/秒
- 处理时间:不到1秒
- 进度显示:0% → 64% → 100%3. **无监督集处理**:
```
Generating unsupervised split: 100%|█...█| 50000/50000 [00:00<00:00, 171537.48 examples/s]
```
- 总样本数:50,000条无标签样本
- 处理速度:约171,537样本/秒
- 处理时间:不到1秒
- 进度显示:0% → 24% → 68% → 100%关键观察:
1. **数据集规模**:
- 训练集:25K样本
- 测试集:25K样本
- 无监督集:50K样本2. **处理效率**:
- 所有数据集处理速度都很快(>100K样本/秒)
- 测试集处理速度最快(~175K/s)
- 无监督集虽然样本量最大,但处理速度仍然保持高效3. **进度监控**:
- 实时显示处理进度百分比
- 显示预估剩余时间
- 显示当前处理速度这表明数据加载和预处理过程非常高效,为后续的模型训练做好了准备。
100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 49.0/49.0 [00:00<00:00, 290kB/s]
/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/mindnlp/transformers/tokenization_utils_base.py:1526: FutureWarning: `clean_up_tokenization_spaces` was not set. It will be set to `True` by default. This behavior will be depracted, and will be then set to `False` by default.
warnings.warn(
[MS_ALLOC_CONF]Runtime config: enable_vmm:True vmm_align_size:2MB
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-cased and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Start training
这段信息显示了模型加载和初始化阶段的几个关键步骤。让我逐条分析:
1. **文件下载进度**:
```
100%|█...█| 49.0/49.0 [00:00<00:00, 290kB/s]
```
- 下载了49KB的文件(可能是配置文件或词表)
- 下载速度:290KB/秒
- 下载完成度:100%2. **Tokenizer警告**:
```python
FutureWarning: `clean_up_tokenization_spaces` was not set. It will be set to `True` by default.
This behavior will be depracted, and will be then set to `False` by default.
```
- tokenizer参数的未来变更提醒
- 当前默认值:True
- 未来默认值:False3. **MindSpore运行时配置**:
```
[MS_ALLOC_CONF]Runtime config: enable_vmm:True vmm_align_size:2MB
```
- 启用了虚拟内存管理(VMM)
- 内存对齐大小设置为2MB4. **模型初始化信息**:
```
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-cased and are newly initialized:
['classifier.bias', 'classifier.weight']
```
- 使用bert-base-cased预训练模型
- 新增了分类器层(用于情感分类)
- 新初始化的参数:分类器的权重和偏置5. **训练建议**:
```
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
```
- 建议在下游任务上训练模型
- 因为有新初始化的参数需要训练6. **开始训练提示**:
```
Start training
```
- 表明初始化完成
- 即将开始训练过程这段信息展示了从模型加载到开始训练的完整准备过程,包括:
1. 配置文件下载
2. tokenizer设置
3. 运行时环境配置
4. 模型结构调整
5. 训练准备所有这些步骤都正常完成,没有出现错误,可以开始训练。
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1564/1564 [15:05<00:00, 1.73it/s]
{'loss': 0.0042, 'learning_rate': 2.5e-05, 'epoch': 1.0}
{'loss': 0.0886, 'learning_rate': 0.0, 'epoch': 2.0}
{'train_runtime': 905.0892, 'train_samples_per_second': 55.296, 'train_steps_per_second': 1.728, 'train_loss': 0.04638986529596626, 'epoch': 2.0}
这段信息显示了整个训练过程的进度和最终结果。让我详细分析:
1. **训练进度条**:
```
100%|█...█| 1564/1564 [15:05<00:00, 1.73it/s]
```
- 总训练步数:1564步
- 总用时:15分05秒
- 训练速度:1.73步/秒(iterations per second)2. **第一个Epoch结果**:
```python
{'loss': 0.0042, 'learning_rate': 2.5e-05, 'epoch': 1.0}
```
- 损失值:0.0042(非常低,说明训练效果好)
- 学习率:2.5e-5
- 完成第一轮训练3. **第二个Epoch结果**:
```python
{'loss': 0.0886, 'learning_rate': 0.0, 'epoch': 2.0}
```
- 损失值:0.0886(略有上升,但仍然较低)
- 学习率:0(学习率衰减到0)
- 完成第二轮训练4. **最终训练统计**:
```python
{
'train_runtime': 905.0892, # 总训练时间(秒)
'train_samples_per_second': 55.296, # 每秒处理样本数
'train_steps_per_second': 1.728, # 每秒训练步数
'train_loss': 0.04638986529596626, # 平均训练损失
'epoch': 2.0 # 总训练轮数
}
```关键观察:
1. **训练效率**:
- 每秒处理约55个样本
- 每秒执行约1.73个训练步骤
- 总训练时间约15分钟2. **训练效果**:
- 平均损失值约0.046
- 损失值总体维持在较低水平
- 学习率按计划完成衰减3. **模型收敛**:
- 损失值波动在合理范围内
- 最终收敛状态良好
- 训练过程稳定这些指标表明训练过程成功完成,模型性能表现良好。损失值维持在低水平,且训练速度符合预期。
worker_1 .log
由msrun参数自动生成worker日志
0%| | 0.00/334 [00:00<?, ?B/s]
570B [00:00, 2.57MB/s]
/home/mindspore/miniconda/envs/jupyter/lib/python3.9/site-packages/mindnlp/transformers/tokenization_utils_base.py:1526: FutureWarning: `clean_up_tokenization_spaces` was not set. It will be set to `True` by default. This behavior will be depracted, and will be then set to `False` by default.
warnings.warn(
[MS_ALLOC_CONF]Runtime config: enable_vmm:True vmm_align_size:2MB
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at bert-base-cased and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
Start training
这段日志显示了worker_1(第二个工作进程)的初始化过程。让我详细分析每一部分:
1. **文件下载进度**:
```
0%|...| 0.00/334 [00:00<?, ?B/s]
570B [00:00, 2.57MB/s]
```
- 需要下载334字节的文件
- 实际下载了570字节
- 下载速度:2.57MB/秒2. **Tokenizer警告**:
```python
FutureWarning: `clean_up_tokenization_spaces` was not set...
```
- tokenizer参数的未来变更提醒
- 当前默认值是`True`
- 将来会改为默认`False`3. **MindSpore运行时配置**:
```
[MS_ALLOC_CONF]Runtime config: enable_vmm:True vmm_align_size:2MB
```
- 启用虚拟内存管理(VMM)
- 内存对齐设置为2MB4. **模型初始化信息**:
```
Some weights of BertForSequenceClassification were not initialized...
['classifier.bias', 'classifier.weight']
```
- 使用bert-base-cased模型
- 新增分类器层参数需要初始化
- classifier.bias
- classifier.weight5. **训练开始提示**:
```
Start training
```
- 初始化完成
- 准备开始训练这个日志表明:
1. 这是分布式训练中的一个worker
2. 完成了必要的初始化步骤
3. 准备就绪可以开始训练
4. 配置与其他worker保持一致所有这些步骤都正常完成,没有出现错误,worker_1已准备好参与训练过程。
100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1564/1564 [12:10<00:00, 2.14it/s]
{'loss': 0.0076, 'learning_rate': 2.5e-05, 'epoch': 1.0}
{'loss': 0.0602, 'learning_rate': 0.0, 'epoch': 2.0}
{'train_runtime': 730.6144, 'train_samples_per_second': 68.501, 'train_steps_per_second': 2.141, 'train_loss': 0.03391032907968897, 'epoch': 2.0}
这段信息显示了worker_1的训练过程和结果。让我详细分析:
1. **训练进度条**:
```
100%|█...█| 1564/1564 [12:10<00:00, 2.14it/s]
```- 总训练步数:1564步
- 总用时:12分10秒
- 训练速度:2.14步/秒(比worker_0快)2. **第一个Epoch结果**:
```python
{'loss': 0.0076, 'learning_rate': 2.5e-05, 'epoch': 1.0}
```- 损失值:0.0076(非常低)
- 学习率:2.5e-5
- 第一轮训练完成3. **第二个Epoch结果**:
```python
{'loss': 0.0602, 'learning_rate': 0.0, 'epoch': 2.0}
```- 损失值:0.0602
- 学习率:0(完成学习率衰减)
- 第二轮训练完成4. **最终训练统计**:
```python
{
'train_runtime': 730.6144, # 总训练时间(秒)
'train_samples_per_second': 68.501, # 每秒处理样本数
'train_steps_per_second': 2.141, # 每秒训练步数
'train_loss': 0.03391032907968897, # 平均训练损失
'epoch': 2.0 # 总训练轮数
}
```与worker_0相比的关键差异:
1. **训练效率更高**:
- 总时间更短:~12分钟 vs ~15分钟
- 更高的样本处理速度:68.5 vs 55.3样本/秒
- 更快的训练步速:2.14 vs 1.73步/秒2. **训练效果相似**:
- 损失值都维持在较低水平
- 学习率变化模式一致
- 最终收敛状态良好这表明分布式训练工作正常,两个worker都成功完成了训练任务,且worker_1表现出更高的计算效率。
4.3这俩json是啥?
fusion_result.json 和 aclinit.json 是与华为 Ascend 系列 AI 处理器(如 Ascend 910、Ascend 310)及其配套的昇腾 AI 软件栈相关的配置文件。这些文件通常用于初始化和配置昇腾 AI 加速卡,特别是在分布式训练或推理场景中。它们在 MindSpore 框架中也有应用,尤其是在使用昇腾 NPU 进行训练时。
- fusion_result.json :主要用于模型的优化和算子融合,影响的是模型的执行效率和性能。
- aclinit.json :更多地关注于昇腾 AI 加速卡的初始化和运行环境配置,确保设备能够正常启动并与主机通信。
4.3.1配置文件aclinit.json
在 **华为昇腾 Ascend** 平台的开发或运行过程中,err_msg_mode: 1 是一个与错误信息输出模式相关的配置参数。它的作用是控制错误信息的显示方式和详细程度。
具体含义
"err_msg_mode": "1":
- 表示错误信息输出模式为 简化模式。
- 在这种模式下,系统会输出简化的错误信息,通常只包含关键的错误代码和简要描述,适合生产环境或对错误信息要求不高的场景。
- "err_msg_mode": "0":关闭错误信息输出(不推荐,除非对性能有极高要求)。
- "err_msg_mode": "2":详细模式,输出完整的错误信息,包括堆栈跟踪、上下文信息等,适合调试和开发阶段。
4.3.2配置文件fusion_result.json
{ "graph_fusion": { "RefreshInt64ToInt32FusionPass": { "effect_times": "1", "match_times": "1" } }, "session_and_graph_id": "10_9" }
这是一个包含一个或多个优化配置的 JSON 数组,每个配置项包含两个主要部分:
`graph_fusion`:描述具体的算子融合优化。
`session_and_graph_id`:标识该优化配置对应的会话(Session)和图(Graph)。`graph_fusion` 部分
- **`RefreshInt64ToInt32FusionPass`**:
- 这是一个具体的 **算子融合优化规则** 的名称。
- 该规则的作用是将 **int64 类型的数据转换为 int32 类型**,以减少内存占用和计算开销。- **`effect_times`: "1"**:
- 表示该优化规则在当前图(Graph)中 **生效的次数**。
- 值为 `1`,表示该优化规则被应用了一次。- **`match_times`: "1"**:
- 表示该优化规则在当前图(Graph)中 **匹配的次数**。
- 值为 `1`,表示该优化规则匹配了一次。`session_and_graph_id` 部分
- **`session_and_graph_id`**:
- 这是一个标识符,用于唯一标识当前优化配置对应的 **会话(Session)** 和 **图(Graph)**。
- 格式为 `"sessionId_graphId"`,例如 `"10_9"`:
- `10` 表示会话 ID(Session ID)。
- `9` 表示图 ID(Graph ID)。在会话 `10` 的图 `9` 中,应用了一个名为 `RefreshInt64ToInt32FusionPass` 的算子融合优化规则,将 `int64` 类型的数据转换为 `int32` 类型,在当前图中匹配并生效了一次。
在模型编译阶段,MindSpore 或其他框架会根据 `fusion_result.json` 文件中的信息,优化模型的执行路径,确保在昇腾 AI 处理器上获得最佳性能。通过算子融合,可以减少不必要的内存拷贝和数据传输,从而加速模型推理或训练过程。
`RefreshInt64ToInt32FusionPass` 是 **华为昇腾(Ascend)AI 处理器** 的编译器(如 **Ascend Compiler**)中的一个 **优化规则**,通常是由编译器在编译模型时自动应用的。它并不是直接在用户代码中设置的,而是由编译器在底层实现的。
4.4训练结果评价
从多个角度评价这次训练结果:
1. **训练过程表现**:
- 损失值表现优秀:
- 平均损失在0.03-0.05之间
- worker_0: 0.0466
- worker_1: 0.0339
- 收敛性良好:
- 损失值持续保持在低水平
- 没有出现显著波动或发散
- 训练效率不错:
- 每秒处理55-68个样本
- 总训练时间约12-15分钟
2. **模型能力预期**:
- 情感分类任务:
- 基于IMDB数据集(电影评论)
- 二分类:正面/负面评价
- 预期性能:
- 损失值很低,说明模型对训练数据拟合良好
- 应该能较好地识别电影评论的情感倾向
- 特别适合处理类似IMDB风格的长文本评论
3. **模型特点**:
- 基于bert-base-cased预训练模型:
- 保留了BERT的强大语言理解能力
- 对英文文本有很好的处理能力
- 区分大小写(cased版本)
- 针对情感分类优化:
- 新增的分类层经过充分训练
- 损失值显示分类效果应该很好
4. **实际应用潜力**:
- 适用场景:
- 电影评论情感分析
- 类似的文本情感分类任务
- 长文本的情感倾向判断
- 可能的局限:
- 主要针对电影领域
- 可能对其他领域文本需要适应
- 对非英文内容可能效果有限
5. **总体评价**:
- 训练质量:优秀
- 收敛状态:稳定
- 预期性能:很好
- 实用价值:较高
建议:
1. 可以进行测试集评估,获取准确的性能指标
2. 考虑在实际应用场景中进行验证
3. 如需跨领域使用,可能需要额外微调
4. 建议保存这个训练结果,作为一个可靠的情感分析模型使用