# 模型转换

[![查看源文件](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/website-images/r2.4.1/resource/_static/logo_source.svg)](https://gitee.com/mindspore/docs/blob/r2.4.1/docs/mindspore/source_zh_cn/model_train/parallel/model_transformation.md)

## 概述

### 背景

在使用MindSpore进行分布式训练时,常常需要对训练得到的分布式Checkpoint进行转换(即模型转换),以进行下一步工作,如推理、微调、多阶段训练等。本教程将介绍如何将分布式训练得到的Checkpoint进行转换,以开展分布式策略与集群卡数改变的弹性训练与推理。

> 本功能仅支持半自动并行(SEMI_AUTO_PARALLEL)和自动并行(AUTO_PARALLEL)模式。

### 使用场景

如果您遇到如下场景,需要参考本教程操作,进行弹性训练与推理:

- 场景1:M卡训练,N卡微调训练,M与N可以没有倍数关系。
- 场景2:训练分为多阶段,每个阶段的集群大小不一样。
- 场景3:M卡训练,N卡推理,M与N可以没有倍数关系。
- 场景4:需要对网络的切分策略进行变更。

相关接口:

1. `mindspore.transform_checkpoints(src_checkpoints_dir, dst_checkpoints_dir, src_strategy_file, dst_strategy_file)`:将一个分布式网络的Checkpoint由源切分策略转换到目标切分策略。其中`src_checkpoints_dir`为源Checkpoint文件所在的目录,其子目录要求按照`rank_x/checkpoint_x.ckpt`格式进行存储,x为对应的rank id。`dst_checkpoints_dir`为目标Checkpoint文件存储的目录,`src_strategy_file`为源切分策略文件名,`dst_strategy_file`为目标切分策略文件名。

2. `mindspore.rank_list_for_transform(rank_id, src_strategy_file, dst_strategy_file)`:在对分布式Checkpoint转换的过程中,获取目标rank的Checkpoint文件所需的源Checkpoint文件rank列表。

3. `mindspore.transform_checkpoint_by_rank(rank_id, checkpoint_files_map, save_checkpoint_file_name, src_strategy_file, dst_strategy_file)`:将一个分布式网络的Checkpoint由源切分策略转换到目标切分策略,对特定一个rank进行转换。其中`rank_id`为待转换得到的Checkpoint的rank号。`checkpoint_files_map`为源Checkpoint字典,其key为rank号,值为该rank号对应的Checkpoint文件路径。`save_checkpoint_file_name`为当前rank的目标Checkpoint路径以及名字。

4. `mindspore.load_segmented_checkpoints(ckpt_file_dir)`:读取指定`ckpt_file_dir`路径下所有`.ckpt`权重文件并合并,返回合并后的参数字典。

## 操作实践

以在Ascend 8卡上训练,并在4卡上微调为例,整体操作流程如下:

1. 执行训练,配置模型参数切分策略文件存储位置,自动生成Checkpoint文件和模型参数切分策略文件。

2. 编译微调网络,配置分布式策略文件存储位置,自动生成模型参数切分策略文件。

3. 用户根据训练与推理涉及到的策略文件对保存的Checkpoint文件进行转换。

4. 编译微调网络后,加载转换得到的分布式Checkpoint文件。

5. 执行微调网络。

需要注意,加载分布式的Checkpoint,要求[对网络进行编译](#对目标网络执行编译)后才可以加载。

### 样例代码说明

> 下载完整的样例代码:[model_saving_loading](https://gitee.com/mindspore/docs/tree/r2.4.1/docs/sample_code/model_saving_loading)。

目录结构如下:

```text
└─ sample_code
    ├─ model_saving_loading
       ├── model_transformation_infer.py
       ├── model_transformation_retrain.py
       ├── pipeline_train.py
       ├── pipeline_transformation_retrain.py
       ├── run_infer_convert.sh
       ├── run_infer.sh
       ├── run_retrain_convert.sh
       ├── run_retrain.sh
       ├── run_pipeline_train.sh
       ├── run_retrain_pipeline_convert.sh
       ├── run_retrain_pipeline.sh
       ...
    ...
```

其中每个文件的作用如下:

- `model_transformation_infer.py`:模型转换后进行推理的脚本。
- `model_transformation_retrain.py`:模型转换后进行二阶段训练的脚本。
- `pipeline_transformation_retrain.py`:流水线并行模型转换后二阶段训练的脚本。
- `pipeline_train.py`:流水线并行训练网络的脚本。
- `run_infer_convert.sh`:推理场景执行模型转换的脚本。
- `run_retrain_convert.sh`:二阶段训练场景执行模型转换的脚本。
- `run_retrain_pipeline_convert.sh`:执行流水线并行模型转换的脚本。
- `run_infer.sh`:执行模型推理的脚本。
- `run_retrain.sh`:执行二阶段训练的脚本。
- `run_pipeline_train.sh`:执行流水线训练的脚本。
- `run_retrain_pipeline.sh`:执行二阶段训练流水线并行模型的脚本。

### 分布式模型保存

首先,按照[模型保存](https://www.mindspore.cn/docs/zh-CN/r2.4.1/model_train/parallel/model_saving.html)教程执行8卡分布式训练,并行模式为`SEMI_AUTO_PARALLEL`或者`AUTO_PARALLEL`,同时通过调用`set_auto_parallel_context`接口自定义`strategy_ckpt_config`参数配置模型切分策略文件存储路径,训练一段时间后,调用存储Checkpoint的`train.ModelCheckpoint`函数,将分布式的Checkpoint存储下来。

训练结束后,将会在当前路径生成源Checkpoint文件目录以及源切分策略文件:

```text
src_checkpoints/
src_strategy.ckpt
```

> src_checkpoints内的子目录要求按照`rank_x/checkpoint_x.ckpt`格式进行存储。

即src_checkpoints的目录结构改成如下:

```text
src_checkpoints
 ├─ rank_0
 |   └─ checkpoint_0.ckpt
 ├─ rank_1
 |   └─ checkpoint_1.ckpt
 ├─ rank_2
 |   └─ checkpoint_2.ckpt
 ├─ rank_3
 |   └─ checkpoint_3.ckpt
 ├─ rank_4
 |   └─ checkpoint_4.ckpt
 ├─ rank_5
 |   └─ checkpoint_5.ckpt
 ├─ rank_6
 |   └─ checkpoint_6.ckpt
 └─ rank_7
     └─ checkpoint_7.ckpt
...
```

### 生成目标策略文件

然后需要编译新的卡数或者切分策略下的网络,生成目标网络的模型切分策略文件,本示例中,原始策略以8卡进行训练,layer1的`ops.MatMul()`算子并行策略为((2, 1), (1, 2)),不开启优化器并行,策略文件命名为src_strategy.ckpt;目标策略以4卡进行训练,layer1的`ops.MatMul()`算子并行策略为((2, 2), (2, 1)),且开启优化器并行,策略文件命名为dst_strategy.ckpt。

#### 配置分布式环境

通过context接口指定运行模式、并行模式,通过`strategy_ckpt_config`配置需要保存的分布式策略文件路径,开启优化器并行,并通过init初始化HCCL或NCCL通信。

```python
import mindspore as ms
from mindspore.communication import init

ms.set_context(mode=ms.GRAPH_MODE)
ms.set_auto_parallel_context(parallel_mode=ms.ParallelMode.SEMI_AUTO_PARALLEL)
ms.set_auto_parallel_context(strategy_ckpt_config={"save_file": args_opt.dst_strategy_file})
ms.set_auto_parallel_context(enable_parallel_optimizer=True)
init()
ms.set_seed(1)
```

#### 网络定义以及加载数据集

网络定义修改了原始网络中layer1的`ops.MatMul()`算子并行策略:

```python
import os
from mindspore import nn, ops
import mindspore.dataset as ds
from mindspore.common.initializer import initializer

class Dense(nn.Cell):
    def __init__(self, in_channels, out_channels):
        super().__init__()
        self.weight = ms.Parameter(initializer("normal", [in_channels, out_channels], ms.float32))
        self.bias = ms.Parameter(initializer("normal", [out_channels], ms.float32))
        self.matmul = ops.MatMul()
        self.add = ops.Add()

    def construct(self, x):
        x = self.matmul(x, self.weight)
        x = self.add(x, self.bias)
        return x

class Network(nn.Cell):
    def __init__(self):
        super().__init__()
        self.flatten = ops.Flatten()
        self.layer1 = Dense(28*28, 512)
        self.relu1 = ops.ReLU()
        self.layer2 = Dense(512, 512)
        self.relu2 = ops.ReLU()
        self.layer3 = Dense(512, 10)

    def construct(self, x):
        x = self.flatten(x)
        x = self.layer1(x)
        x = self.relu1(x)
        x = self.layer2(x)
        x = self.relu2(x)
        logits = self.layer3(x)
        return logits

net = Network()
net.layer1.matmul.shard(((1, 4), (4, 1)))
net.layer3.matmul.shard(((2, 2), (2, 1)))

def create_dataset(batch_size):
    dataset_path = os.getenv("DATA_PATH")
    dataset = ds.MnistDataset(dataset_path)
    image_transforms = [
        ds.vision.Rescale(1.0 / 255.0, 0),
        ds.vision.Normalize(mean=(0.1307,), std=(0.3081,)),
        ds.vision.HWC2CHW()
    ]
    label_transform = ds.transforms.TypeCast(ms.int32)
    dataset = dataset.map(image_transforms, 'image')
    dataset = dataset.map(label_transform, 'label')
    dataset = dataset.batch(batch_size)
    return dataset

data_set = create_dataset(32)
```

#### 对目标网络执行编译

进行分布式的Checkpoint的转换,依赖于原始的分布式策略文件与目标的分布式策略文件,执行原始的策略下的网络训练时,已经将分布式策略文件存储下来了,因此需要另外获取到目标策略下的分布式策略文件。通过对目标策略的网络执行编译,即可获取到目标策略网络的分布式策略文件。通过`model.infer_train_layout`接口即可以单独对网络执行编译。

```python
import mindspore as ms
from mindspore import nn, ops

optimizer = nn.SGD(net.trainable_params(), 1e-2)
loss_fn = nn.CrossEntropyLoss()
model = ms.Model(net, loss_fn=loss_fn, optimizer=optimizer)
model.infer_train_layout(data_set)
```

当目标网络是进行推理时,则将`model.infer_train_layout`更换为`model.infer_predict_layout`以执行编译:

```python
import numpy as np
import mindspore as ms

predict_data = ms.Tensor(np.random.randn(1, 28, 28).astype(np.float32))
model = ms.Model(net)
model.infer_predict_layout(predict_data)
```

编译完成后,即可得到目标切分策略文件`dst_strategy.ckpt`。

### 执行分布式Checkpoint转换

在这一步,需要调用分布式Checkpoint转换的接口进行分布式Checkpoint的转换,分布式Checkpoint提供两个接口对Checkpoint进行转换。

第一个接口`transform_checkpoints`,要求用户将所有的Checkpoint放置于一个目录,并且子目录必须以”rank_0、rank_1、rank_2、…“格式进行命名。用户调用该接口直接对整个目录进行转换。该方式使用较为方便,但是转换需要的内存开销会略高一些。

第二个接口`transform_checkpoint_by_rank`,用以获取到特定的rank的Checkpoint,有更大的灵活性与更低的内存开销,需要配合`rank_list_for_transform`接口使用,以获取本rank的目标Checkpoint需要哪些原始Checkpoint。

1. 使用接口`transform_checkpoints`。

    ```python
    import mindspore as ms

    ms.transform_checkpoints(args_opt.src_checkpoints_dir, args_opt.dst_checkpoints_dir, "checkpoint_", args_opt.src_strategy_file, args_opt.dst_strategy_file)
    ```

    示例代码[`model_transformation_retrain.py`](https://gitee.com/mindspore/docs/blob/r2.4.1/docs/sample_code/model_saving_loading/model_transformation_retrain.py)中采用此方法。

2. 调用`transform_checkpoint_by_rank`接口对当前rank对应的原始Checkpoint进行参数合并。

    > 需要保证dst_checkpoints_dir内存在子目录"rank_x"。

    ```python
    import os
    import mindspore as ms
    from mindspore.communication import get_rank

    rank_list = ms.rank_list_for_transform(get_rank(), args_opt.src_strategy_file, args_opt.dst_strategy_file)
    checkpoint_file_map = {}
    for rank_id in rank_list:
        checkpoint_file_map[rank_id] = os.path.join(args_opt.src_checkpoints_dir, "rank_{}".format(rank_id), "checkpoint_{}.ckpt".format(rank_id))
    save_checkpoint_path = os.path.join(args_opt.dst_checkpoints_dir, "rank_{}".format(get_rank()), "checkpoint_{}.ckpt".format(get_rank()))
    ms.transform_checkpoint_by_rank(get_rank(), checkpoint_file_map, save_checkpoint_path, args_opt.src_strategy_file, args_opt.dst_strategy_file)
    ```

    示例代码[`model_transformation_infer.py`](https://gitee.com/mindspore/docs/blob/r2.4.1/docs/sample_code/model_saving_loading/model_transformation_infer.py)中采用此方法。

执行完后,将会生成转换后的目标Checkpoint文件目录:

```text
dst_checkpoints/
```

### 多进程加速转换CheckPoint

在编写转换CheckPoint文件的过程中,用户通常会采用下述串行的逻辑,导致转换耗时长达几个小时。例如下述的写法会导致并行度为1,是**不推荐的写法**。

```python
import mindspore as ms
dst_device_num = 8
for tgt_rank in range(dst_device_num):
    rank_list = ms.rank_list_for_transform(tgt_rank, "./src_strategy.ckpt", "./dst_strategy.ckpt")
    checkpoint_files_map = {}
    for rank_id in rank_list:
        checkpoint_file_map[rank_id] = os.path.join(args_opt.src_checkpoints_dir, "rank_{}".format(rank_id), "checkpoint_{}.ckpt".format(rank_id))
    save_checkpoint_path = os.path.join(args_opt.dst_checkpoints_dir, "rank_{}".format(get_rank()), "checkpoint_{}.ckpt".format(get_rank()))
    ms.transform_checkpoint_by_rank(tgt_rank, checkpoint_file_map, save_checkpoint_path, args_opt.src_strategy_file, args_opt.dst_strategy_file)
```

我们可以采用**多进程**的方法进行ckpt转换加速。
具体修改如下,每个进程根据当前的rank_id来决定自己要转换哪个目标rank权重文件。即每个进程转换一个权重文件

> 请根据转换单个权重所消耗的内存和当前host内的总内存,来设置总进程数,否则就引起Host内存不足报错。

```diff
import sys
import mindspore as ms

rank_list = ms.rank_list_for_transform(get_rank(), "./src_strategy.ckpt", "./dst_strategy.ckpt")
checkpoint_files_map = {}
for rank_id in rank_list:
    checkpoint_file_map[rank_id] = os.path.join(args_opt.src_checkpoints_dir, "rank_{}".format(rank_id), "checkpoint_{}.ckpt".format(rank_id))
save_checkpoint_path = os.path.join(args_opt.dst_checkpoints_dir, "rank_{}".format(get_rank()), "checkpoint_{}.ckpt".format(get_rank()))
ms.transform_checkpoint_by_rank(get_rank(), checkpoint_file_map, save_checkpoint_path, args_opt.src_strategy_file, args_opt.dst_strategy_file)
```

此外,需要配置下启动方式。将单进程启动改为多进程启动的方式。如下展示了一个4进程转换的方式样例。

```shell
mpirun -n 4 --output-filename log_output --merge-stderr-to-stdout python model_transformation_infer.py --only_compile=1
```

> 本教程中使用到的代码,已经按多进程启动的方式设置。本节的目的是为了强调如何解决用户日常使用场景中转换慢的场景。

### 加载转换得到的Checkpoint文件

对目标策略的网络进行编译,调用`load_checkpoint`接口,从转换后的Checkpoint文件中加载模型参数数据。

使用`model.infer_train_layout`(训练时)或者`model.infer_predict_layout`(推理时)接口对网络进行编译,此时权重Shape在编译流程进行了切分;调用`load_checkpoint`接口,从Checkpoint文件中加载各卡的模型参数数据。

目标网络是训练场景:

```python
import os
import mindspore as ms
from mindspore import nn, train

save_checkpoint_path = os.path.join(args_opt.dst_checkpoints_dir, "rank_{}".format(get_rank()), "checkpoint_{}.ckpt".format(get_rank()))
loss_cb = train.LossMonitor(20)
model.infer_train_layout(data_set)
param_dict = ms.load_checkpoint(save_checkpoint_path)
ms.load_param_into_net(net, param_dict)
model.train(2, data_set, callbacks=[loss_cb])
```

- `save_checkpoint_path`:需要加载的当前rank对应的Checkpoint模型参数文件名称。

目标网络是推理场景:

```python
import os
import mindspore as ms

save_checkpoint_path = os.path.join(args_opt.dst_checkpoints_dir, "rank_{}".format(get_rank()), "checkpoint_{}.ckpt".format(get_rank()))
param_dict = ms.load_checkpoint(save_checkpoint_path)
model.infer_predict_layout(predict_data)
ms.load_param_into_net(net, param_dict)
predict_result = model.predict(predict_data)
print(predict_result)
```

- `predict_data`:用于推理的Tensor数据。

### 运行单机四卡脚本

接下来通过命令调用对应的脚本,以`mpirun`启动方式,4卡的分布式脚本为例,进行模型转换后的二阶段微调训练:

```bash
bash run_retrain_convert.sh
bash run_retrain.sh
```

或者模型转换后的推理:

```bash
bash run_infer_convert.sh
bash run_infer.sh
```

执行完成后,日志文件保存到`log_output`目录下,目标Checkpoint文件保存在`dst_checkpoints`文件夹下,目标策略文件保存在`dst_strategy.ckpt`,文件目录结构如下:

```text
├─ src_strategy.ckpt
├─ dst_strategy.ckpt
├─ log_output
|   └─ 1
|       ├─ rank.0
|       |   └─ stdout
|       ├─ rank.1
|       |   └─ stdout
|       ...
├─ dst_checkpoints
|   ├─ rank_0
|   |   └─ checkpoint_0.ckpt
|   ├─ rank_1
|   |   └─ checkpoint_1.ckpt
|   |   ...
|   ...
...
```

二阶段微调训练后的Loss部分结果保存在`log_output/1/rank.*/stdout`中,示例如下:

```text
epoch: 1, step: 20, loss is 0.10617774
epoch: 1, step: 40, loss is 0.06953259
epoch: 1, step: 60, loss is 0.08409108
epoch: 1, step: 80, loss is 0.08699021
epoch: 1, step: 100, loss is 0.07113413
...
```

如果是推理任务,则结果保存在`log_output/1/rank.*/stdout`中,示例如下:

```text
[[ 0.05044775 -0.94413316  0.84689134 -0.2881832   0.66444755  1.0564336
  -0.04191193  0.25590348 -0.690101   -0.6532427 ]]
```

### 流水线并行模型转换

[流水线并行](https://www.mindspore.cn/docs/zh-CN/r2.4.1/model_train/parallel/pipeline_parallel.html) 是对线性的网络进行切分,得到多个子网络,子网络之间在多卡间进行流水,因此每个子图存储下来的切分策略文件是不一致的,所有切分策略汇聚在一起才能得到完整的网络的切分信息。
因此针对流水线并行的维度,相比于其他维度的转换,需要事先执行一次汇聚切分策略文件的操作,得到汇聚后的切分策略文件,以这一份文件作为分布式Checkpoint转换依赖的策略文件。此外,与前面的[执行分布式Checkpoint转换](https://www.mindspore.cn/docs/zh-CN/r2.4.1/model_train/parallel/model_transformation.html#执行分布式checkpoint转换)没有差异。

相关接口:

`mindspore.merge_pipeline_strategys(src_strategy_dirs, dst_strategy_file)`:流水线并行模式下,汇聚所有流水线并行子图的切分策略文件。`src_strategy_dirs`为包含所有流水线并行的子图的切分策略文件的目录,切分策略文件由`mindspore.set_auto_parallel_context(strategy_ckpt_config)`接口存储得到。`dst_strategy_file`为保存汇聚后的切分策略的文件路径。

首先,执行8卡的流水线并行训练,其中pipeline并行维度为2,且开启优化器并行。

训练代码在[pipeline_train.py](https://gitee.com/mindspore/docs/blob/r2.4.1/docs/sample_code/model_saving_loading/pipeline_train.py)中,网络结构在[模型保存](https://www.mindspore.cn/docs/zh-CN/r2.4.1/model_train/parallel/model_saving.html)这一章的基础上增加了流水线并行的配置,并行维度为2。

核心代码为:

```python
import mindspore as ms
from mindspore import nn, train
from mindspore.communication import init, get_rank
...
ms.set_context(mode=ms.GRAPH_MODE)
ms.set_auto_parallel_context(parallel_mode=ms.ParallelMode.SEMI_AUTO_PARALLEL)
ms.set_auto_parallel_context(pipeline_stages=2, enable_parallel_optimizer=True)
init()
ms.set_auto_parallel_context(strategy_ckpt_config={"save_file": "./src_pipeline_strategys/src_strategy_{}.ckpt".format(get_rank())})
ms.set_seed(1)
...
ckpt_config = train.CheckpointConfig(save_checkpoint_steps=1000, keep_checkpoint_max=1, integrated_save=False)
ckpoint_cb = train.ModelCheckpoint(prefix="checkpoint",
                                   directory="./src_checkpoints_pipeline/rank_{}".format(get_rank()),
                                   config=ckpt_config)
net_with_grads = nn.PipelineCell(nn.WithLossCell(net, loss_fn), 4)
model = ms.Model(net_with_grads, optimizer=optimizer)
model.train(3, data_set, callbacks=[loss_cb, ckpoint_cb])
```

> 每张卡得到的切分策略文件是不一致的,因此需要分别保存,按照"src_pipeline_strategys/src_strategy_x.ckpt"格式进行存储。

执行8卡训练脚本执行命令为:

```bash
bash run_pipeline_train.sh
```

执行后,将会生成源Checkpoint文件目录以及源切分策略文件,文件目录结构为:

```text
├─ src_checkpoints_pipeline
|   ├─ rank_0
|   |   ├─ checkpoint-3_1875.ckpt
|   |   └─ checkpoint-graph.meta
|   ├─ rank_1
|   |   ├─ checkpoint-3_1875.ckpt
|   |   ...
|   ...
├─ src_pipeline_strategys
|   ├─ src_strategy_0.ckpt
|   ├─ src_strategy_1.ckpt
|   ├─ src_strategy_2.ckpt
|   ├─ src_strategy_3.ckpt
|   ├─ src_strategy_4.ckpt
|   ├─ src_strategy_5.ckpt
|   ├─ src_strategy_6.ckpt
|   └─ src_strategy_7.ckpt
...
```

参考[对目标网络执行编译](https://www.mindspore.cn/docs/zh-CN/r2.4.1/model_train/parallel/model_transformation.html#%E5%AF%B9%E7%9B%AE%E6%A0%87%E7%BD%91%E7%BB%9C%E6%89%A7%E8%A1%8C%E7%BC%96%E8%AF%91)章节,同样编译目标网络以得到目标网络的切分策略文件。

下一步展开包含pipeline并行维度的分布式Checkpoint维度转换,首先使用接口`merge_pipeline_strategys`对pipline训练得到的切分策略文件进行合并,而后使用接口`transform_checkpoints`或者`transform_checkpoint_by_rank`进行分布式Checkpoint转换。

示例给出使用`transform_checkpoints`的接口,使用`transform_checkpoint_by_rank`接口请参考[执行分布式Checkpoint转换](https://www.mindspore.cn/docs/zh-CN/r2.4.1/model_train/parallel/model_transformation.html#执行分布式checkpoint转换) 章节的介绍。

```python
import mindspore as ms

ms.merge_pipeline_strategys(args_opt.src_strategy_dir, args_opt.src_strategy_file)
ms.transform_checkpoints(args_opt.src_checkpoints_dir, args_opt.dst_checkpoints_dir, "checkpoint_", args_opt.src_strategy_file, args_opt.dst_strategy_file)
```

> src_checkpoints_dir内的子目录要求按照"rank_x/checkpoint_x.ckpt"格式进行存储。

示例中,对整个Checkpoint目录进行转换的脚本执行命令为:

```bash
bash run_retrain_pipeline_convert.sh
```

转换完成后,参照[加载转换得到的Checkpoint文件](https://www.mindspore.cn/docs/zh-CN/r2.4.1/model_train/parallel/model_transformation.html#%E5%8A%A0%E8%BD%BD%E8%BD%AC%E6%8D%A2%E5%BE%97%E5%88%B0%E7%9A%84checkpoint%E6%96%87%E4%BB%B6)章节,执行没有pipeline维度的分布式网络。

示例中,加载转换后的Checkpoint进行二阶段微调训练的脚本执行命令为:

```bash
bash run_retrain_pipeline.sh
```

执行完成后,可以看到loss从0.15开始下降:

```text
epoch: 1, step: 20, loss is 0.15090162
epoch: 1, step: 40, loss is 0.13296325
epoch: 1, step: 60, loss is 0.14676111
epoch: 1, step: 80, loss is 0.11930083
epoch: 1, step: 100, loss is 0.0784434
epoch: 1, step: 120, loss is 0.10741685
```

**流水线并行单个子网络模型转换**

`transform_checkpoints`接口同时支持流水线并行下以单个子网络为单位进行模型权重转换。不同于上文提到的流水线模型转换,该场景下无需事先执行一次汇聚切分策略文件的操作,而是将单个流水线子网络的策略文件作为分布式Checkpoint转换依赖的策略文件。采用单个子网络模型转换的方式,每个进程将转换得到该子网络中包含参数在所有目标rank下的权重文件,并以`_part*`后缀标识权重文件由流水线下的哪一个子网络转换得到。

以[流水线并行模型转换](#流水线并行模型转换)第一部分使用的训练网络网络为例,训练脚本及使用方式不再赘述。执行后,将会生成源Checkpoint文件目录以及源切分策略文件,文件目录结构为:

```text
├─ src_checkpoints_pipeline
|   ├─ rank_0
|   |   ├─ checkpoint-3_1875.ckpt
|   |   └─ checkpoint-graph.meta
|   ├─ rank_1
|   |   ├─ checkpoint-3_1875.ckpt
|   |   ...
|   ...
├─ src_pipeline_strategys
|   ├─ src_strategy_0.ckpt
|   ├─ src_strategy_1.ckpt
|   ├─ src_strategy_2.ckpt
|   ├─ src_strategy_3.ckpt
|   ├─ src_strategy_4.ckpt
|   ├─ src_strategy_5.ckpt
|   ├─ src_strategy_6.ckpt
|   └─ src_strategy_7.ckpt
...
```

参考[对目标网络执行编译](https://www.mindspore.cn/docs/zh-CN/r2.4.1/model_train/parallel/model_transformation.html#%E5%AF%B9%E7%9B%AE%E6%A0%87%E7%BD%91%E7%BB%9C%E6%89%A7%E8%A1%8C%E7%BC%96%E8%AF%91)章节,同样编译目标网络以得到目标网络的切分策略文件。

网络训练并行策略中流水线并行维度为2,网络将被切分为两个子网络进行训练,分别取两个子网络的策略文件`src_strategy_0.ckpt`和`src_strategy_4.ckpt`使用`transform_checkpoints`接口进行单个子网络的权重转换。

```python
import mindspore as ms

stage_strategy_list = ['src_strategy_0.ckpt', 'src_strategy_4.ckpt']
for src_strategy_file in stage_strategy_list:
  ms.transform_checkpoints(args_opt.src_checkpoints_dir, args_opt.dst_checkpoints_dir, "checkpoint_", src_strategy_file, args_opt.dst_strategy_file)
```

> 当前仅支持多卡到多卡之间的权重转换,即不支持`src_strategy_file=None`或`dst_strategy_file=None`的场景,且不支持目标并行策略为流水线并行的转换。

转换后得到权重的目录结构为:

```text
├─ dst_checkpoints_dir
|   ├─ rank_0
|   |   ├─ checkpoint_0_part0.ckpt
|   |   └─ checkpoint_0_part1.ckpt
|   ├─ rank_1
|   |   ├─ checkpoint_1_part0.ckpt
|   |   └─ checkpoint_1_part1.ckpt
|   ...
```

由于以子网络为单位进行权重转换,每个子网络都会转出一个目标rank对应的权重文件。目录`rank_*`下保存对应目标rank下由所有子网络转换得到的权重文件。`checkpoint_0_part0.ckpt`为由子网络0转换得到的目标rank号为0的对应权重。

采用单个子网络模型转换方式得到的权重文件,在`rank_*`路径下所有的权重文件组成了目标策略该rank下完整的权重,加载时需要对路径下所有的文件进行读取。使用`load_segmented_checkpoints`接口能够读取指定路径下所有权重文件并进行汇聚,样例代码如下:

```python
import mindspore as ms

net = Network()

param_dict = ms.load_segmented_checkpoints(checkpoint_file_dir)
param_not_load, _ = ms.load_param_into_net(net, param_dict)
```