

# 持续预训练 (CPT)
持续预训练 (CPT)

持续预训练（CPT）是一种通过让基础模型接触来自特定领域或语料库的额外无标注文本，从而延长模型预训练阶段的训练技术。监督式微调需要带标注的输入-输出样本对，而 CPT 直接基于原始文档进行训练，帮助模型掌握更深的新领域知识、学习领域专属术语与行文风格，并适配特定内容类型或主题领域。

当您拥有海量（数百亿词元级）的领域专属文本数据（如法律文书、医学文献、技术文档或企业专有内容），并希望模型在该领域具备原生流畅度时，这种方法尤为适用。通常，在 CPT 阶段之后，模型还需要经过额外的指令微调阶段，才能将新学到的知识用于完成实际任务。

**支持的模型**  
CPT 适用于以下 Amazon Nova 模型：
+ Nova 1.0（Micro、Lite、Pro）
+ Nova 2.0（Lite）

**何时选用 Nova 1.0 与 Nova 2.0**  
Amazon Nova 系列模型提供多种高性价比方案，助力在精准度、速度与成本之间实现最优平衡。

需要以下特性时，建议选择 Nova 2.0：
+ 具备适用于复杂分析任务的高级推理能力
+ 在代码生成、数学及科学问题求解方面表现更出色
+ 支持更长的上下文长度
+ 具备更优的多语言处理能力

**注意**  
模型并非越大越优。在 Nova 1.0 与 Nova 2.0 之间选型时，请综合考虑性价比与具体业务需求。

# Nova 2.0 上的 CPT


Amazon Nova Lite 2.0 是一款推理模型，其训练所用数据集相较于 Nova Lite 1.0 规模更大、种类更更富。虽然 Nova Lite 2.0 模型规模更大，但其推理速度却快于 Nova Lite 1.0，同时具备更强的推理能力、更长的上下文长度以及更优的多语言处理性能。

借助 Nova 2.0 上的 CPT，您可通过自身领域专属数据拓展这些高级能力，使模型在专业领域形成深度专长，同时保持其出色的推理与分析能力。

## CPT 配方示例


以下是 CPT 配方示例。您可以在[配方](https://github.com/aws/sagemaker-hyperpod-recipes/tree/main/recipes_collection/recipes/training/nova)存储库中找到此配方和其他配方。

```
# Note:
# This recipe can run on p5.48xlarge
# Run config
run:
  name: "my-cpt-run"                           # A descriptive name for your training job
  model_type: "amazon.nova-2-lite-v1:0:256k"   # Model variant specification, do not change
  model_name_or_path: "nova-lite-2/prod"        # Base model path, do not change
  replicas: 8                                   # Number of compute instances for training, allowed values are 4, 8, 16, 32
  data_s3_path: ""                              # Customer data paths
  validation_data_s3_path: ""                   # Customer validation data paths
  output_s3_path: ""                            # Output artifact path,  job-specific configuration - not compatible with standard SageMaker Training Jobs
  mlflow_tracking_uri: ""                       # Required for MLFlow
  mlflow_experiment_name: "my-cpt-experiment"   # Optional for MLFlow. Note: leave this field non-empty
  mlflow_run_name: "my-cpt-run"                 # Optional for MLFlow. Note: leave this field non-empty

## Training specific configs
training_config:
  task_type: cpt
  max_length: 8192                              # Maximum context window size (tokens)
  global_batch_size: 256                        # Global batch size, allowed values are 32, 64, 128, 256.

  trainer:
    max_steps: 10                               # The number of training steps to run total
    val_check_interval: 10                      # The number of steps between running validation. Integer count or float percentage
    limit_val_batches: 2                        # Batches of the validation set to use each trigger

  model:
    hidden_dropout: 0.0                         # Dropout for hidden states, must be between 0.0 and 1.0
    attention_dropout: 0.0                      # Dropout for attention weights, must be between 0.0 and 1.0

  optim:
    optimizer: adam
    lr: 1e-5                                    # Learning rate
    name: distributed_fused_adam                # Optimizer algorithm, do not change
    adam_w_mode: true                           # Enable AdamW mode
    eps: 1e-06                                  # Epsilon for numerical stability
    weight_decay: 0.0                           # L2 regularization strength, must be between 0.0 and 1.0
    adam_beta1: 0.9                             # Beta1 for Adam optimizer
    adam_beta2: 0.95                            # Beta2 for Adam optimizer
    sched:
      warmup_steps: 10                          # Learning rate warmup steps
      constant_steps: 0                         # Steps at constant learning rate
      min_lr: 1e-6                              # Minimum learning rate, must be lower than lr
```

## 为 2.0 上的 CPT 做数据准备


**数据格式要求**  
训练数据集与验证数据集必须为 JSONL 文件，并遵循以下格式：每行包含一个 JSON 对象，用于表示一段对话，且需包含必填字段与指定结构。示例如下：

```
{"text": "AWS stands for Amazon Web Services"}
{"text": "Amazon SageMaker is a fully managed machine learning service"}
{"text": "Amazon Bedrock is a fully managed service for foundation models"}
```

文本条目应包含代表目标领域的自然流畅的高质量内容。

测试数据是否能够转换为 [Arrow 格式](https://huggingface.co/docs/datasets/en/about_arrow)。使用下方的 python 脚本来解决这个问题。确保至少使用 `datasets==2.18.0` 版本：

```
from datasets import load_dataset, load_from_disk
from pathlib import Path

input_path = Path("<Your jsonl file>")
output_path = Path("<Your output directory>")

dataset = load_dataset("json", data_files=str(input_path), split="train")
dataset.save_to_disk(str(output_path), max_shard_size="1GB")

try:
  test_dataset = datasets.load_from_disk(output_dir)
  print(f"Dataset loaded successfully ✅! Contains {len(test_dataset)} samples")
except Exception as e:
  print(e)
```

打印的行数应与 JSONL 文件中的行数相同。

使用数据混合时，首个作业请设置 `max_steps=2`。这有助于在集群中为数据访问生成优化配置，并确保所有数据混合均可用。

**如何为 CPT 准备数据**  
训练数据是决定持续预训练能否成功的最关键因素。虽然 CPT 数据常被称作“无标注数据”，但实际情况远比这复杂。数据的结构、格式与呈现方式，直接决定模型能否习得使用案例所需的知识与能力。

### 为 CPT 准备结构化的业务数据集


这对于构建专属领域基础模型的企业与机构而言，是一项普遍挑战。多数企业都拥有丰富的结构化数据资源，包括产品目录、用户档案、交易日志、表单提交记录、API 调用信息及运营元数据等。乍看之下，这类数据与标准预训练中常用的非结构化 Web 文本差异显著。

若要让模型从结构化业务数据中高效学习，需充分考量下游任务，并合理设计数据呈现方式，引导模型学习正确的预测关联。

要充分发挥持续预训练的潜力，需考虑以下几点：
+ 模型在推理阶段需执行的任务
+ 原始数据中包含的信息
+ 如何对数据进行结构化处理，使模型能够正确提取并运用信息

将结构化数据直接用于训练，无法让模型掌握相关推理能力。需主动调整数据的呈现方式，引导模型的学习方向。

以下章节将通过后续章节将通过文献综述阐述数据增强的重要性，并给出结构化业务数据的数据增强策略示例，为您在 CPT 中处理与组织业务数据集提供实用参考。

**文献中的 CPT 结构化数据**  
CPT 能够将领域事实注入模型，但在输入或任务发生变化时，模型往往难以有效检索和运用这些事实。对照实验表明，若预训练阶段未采用多样化数据增强，模型会以僵化方式记忆事实信息，即便后续开展指令微调，也依然难以提取利用。相关研究建议在训练初期就注入类指令信号。对于半结构化数据，随机序列化及其他增强手段可降低架构过拟合，因此 CPT 应与指令类任务交替进行，而非先执行 CPT 再开展 IFT。金融领域相关研究进一步发现，相较于分步执行配方，在批次层面混合 CPT 与指令数据，可提升泛化能力并减少知识遗忘。Qwen 技术报告也得出一致结论：将高质量指令数据融入预训练本身，可使模型在学习新领域知识的同时，增强上下文学习能力并保持指令遵循能力。

对半结构化语料库实施数据增强是关键手段。合成图感知 CPT 可将小规模领域数据集扩展为实体关联语料库，显式学习实体关系与复合结构，并在推理阶段结合检索能力使用。在金融领域，CPT 与指令数据混合训练的效果优于分步流程；同时，利用通用数据平衡领域数据可减轻模型通用能力的退化。大规模领域 CPT 也能保留较广泛的通用能力，甚至可通过模型融合进行取舍，但研究仍表明指令微调是必不可少的后续步骤，这进一步印证了在 CPT 阶段引入指令信号的重要价值。

**通过随机化和乱序注入多样性**  
一种能帮助模型从结构化及半结构化数据集中高效学习的通用策略，是打乱数据集中字段的顺序，甚至随机丢弃部分键值。

打乱字段顺序可迫使模型去理解每个值的具体含义，而非依赖其出现位置，并学习所有字段间的关联关系。例如，以 Amazon 商店上架的某款电子游戏数据为例，当“标题”“平台”“价格”“品相”“版本”等字段以不同顺序排列时，模型无法再依赖“第三个字段是平台”这类位置信息，而必须将标签与对应值绑定，进而掌握各属性间的双向关联：标题 ⇄ 平台、平台 ⇄ 价格、品相 ⇄ 价格。如此一来，模型便能实现诸多实用推理，比如根据游戏名称和观察到的价格推断可能的平台，或依据标题与平台估算合理的价格区间。

在序列化过程中随机丢弃键值，其作用类似于特征丢弃：可防止模型对任一单个字段产生共适应现象，进而迫使模型从剩余的有效信息中还原缺失内容。若“平台”字段缺失，模型需从标题字符串或兼容性文本中提取平台相关信息；若“价格”字段被隐藏，模型则需结合平台、版本及品相信息进行综合推断。这不仅建立了双向对称性（A→B 与 B→A），增强了对杂乱真实世界列表的稳健性，还能在字段缺失、重命名或顺序调整时保持结构不变性。

下面以购物场景示例具体说明。对同一商品采用多种序列化方式，例如“标题：《艾尔登法环》\$1 平台：PlayStation 5 \$1 品相：二手如新 \$1 价格：34.99 美元”，以及另一种排列形式“价格：34.99 美元 \$1 标题：《艾尔登法环》\$1 品相：二手如新 \$1 平台：PlayStation 5”；在部分训练轮次中，可删除“平台”字段，但在描述中保留“兼容 PS5”的相关表述。同时训练互补性目标，比如根据 \$1标题、价格\$1 推断平台，以及根据 \$1标题、平台\$1 确定价格区间。由于字段的顺序甚至存在与否均会随机变化，模型唯一稳定的学习策略，就是掌握各属性之间的真实关联，而非机械记忆固定模板。

### 数据的呈现方式至关重要


LLM 通过从已看到的内容中预测下一个词元来学习。因此，训练期间显示的字段和事件的顺序决定了模型能够学习到什么。如果训练格式与实际任务相匹配，损失就会落在确切的决策词元上。如果字段没有结构地随意堆叠，模型会学习捷径或记忆热门内容，这导致模型在需要从多个选项中做出选择时失败。

先展示情境，再展示选项，最后展示决策。如果模型还需要学习结果或解释，则将其放在决策之后。

### 为 CPT 打包样本


**什么是打包？**  
简单来说，打包就是将多个完整样本填入训练数据中的每个序列窗口，使窗口内充满有效词元，而非填充符。

**为什么这非常重要**  
训练时，设置最大上下文长度，例如 8192 个词元。批次格式为 [批次大小 x 上下文长度]。如果某个训练样本长度不足上下文长度，剩余位置将用填充符补齐。即使在损失计算中对填充部分进行掩码处理，这些填充内容仍会参与注意力机制与 MLP 内核运算，从而消耗算力却不产生任何学习信号。

**如何打包？**  
打包多个样本时，可使用 ` [DOC] ` 分隔符将多个训练样本拼接（注意 [DOC] 前后各保留一个空格），使拼接后的总长度不超过目标上下文长度。

打包后的文档示例如下：

```
{"text": "training sample 1 [DOC] training sample 2 [DOC] training sample 3"}
```

### CPT 调优参数


可用于 CPT 微调的参数包括：

**运行配置**  

+ **name**：训练作业的描述性名称。这有助于在 AWS 管理控制台中识别对应作业。
+ **model\$1type**：要使用的 Amazon Nova 模型变体。可用选项为 `amazon.nova-2-lite-v1:0:256k`。
+ **model\$1name\$1or\$1path**：用于训练的基本模型的路径。可用选项为 `nova-lite-2/prod`，或训练后检查点的 S3 路径 (`s3://customer-escrow-bucket-unique_id/training_run_name`)。
+ **replicas**：要在分布式训练中使用的计算实例数。可用值因您所选的模型而异。Amazon Nova Lite 2.0 支持 4、8、16 或 32 个副本。
+ **data\$1s3\$1path**：训练数据集的 S3 位置，格式为 JSONL 文件。此文件必须与集群位于同一 AWS 账户和区域中。提供的所有 S3 位置必须位于同一账户和区域中。
+ **validation\$1data\$1s3\$1path**：（选填）验证数据集的 S3 位置，格式为 JSONL 文件。此文件必须与集群位于相同的账户和区域中。提供的所有 S3 位置必须位于同一账户和区域中。
+ **output\$1s3\$1path**：存储清单和 TensorBoard 日志的 S3 位置。提供的所有 S3 位置必须位于同一 AWS 账户和 AWS 区域中。
+ **mlflow\$1tracking\$1uri**：用于 MLflow 日志记录的 MLflow 应用程序的 ARN
+ **mlflow\$1experiment\$1name**：MLflow 实验名称
+ **mlflow\$1run\$1name**：MLflow 运行名称

**训练配置**  

+ **max\$1length**：以词元为单位的最大序列长度。这决定了训练的上下文窗口大小。CPT 支持的最大值为 8192 个词元。

  更长的序列将会提高训练效率，但会以增加内存需求为代价。建议将 max\$1length 参数设置为与数据分布相匹配。
+ **global\$1batch\$1size**：所有设备与 Worker 节点在一次前向及反向传播中共同处理的训练样本总数。

  该值乘以每台设备的批量大小和设备数量。它会影响训练的稳定性和吞吐量。我们建议从适合您内存的批量大小开始，然后进行扩展。对于特定领域的数据，较大的批量大小可能会使梯度过于平滑。

**训练器设置**  

+ **max\$1steps**：训练迭代步数。每一步训练模型所用的元素数量即 `global_batch_size`

**模型设置**  

+ **hidden\$1dropout**：隐藏状态输出的丢弃概率。将该值增加约 0.0 到 0.2，以减少对较小数据集的过度拟合。有效值介于 0 到 1 之间（含两端值）。
+ **attention\$1dropout**：注意力权重的丢弃概率。此参数有助于泛化。有效值介于 0 到 1 之间（含两端值）。

**优化器配置**  

+ **lr**：学习率，控制优化期间的步长。为了获得良好的性能，我们建议使用介于 1e-6 和 1e-4 之间的值。有效值介于 0 到 1 之间（含两端值）。
+ **name**：优化器算法。目前仅支持 `distributed_fused_adam`。
+ **weight\$1decay**：L2 正则化强度。值比较高（介于 0.01 到 0.1 之间）会增加正则化强度。
+ **warmup\$1steps**：逐步提高学习率的步数。这可以提高训练稳定性。有效值介于 1 到 20 之间（含两端值）。
+ **min\$1lr**：衰减结束时的最低学习率。有效值介于 0 到 1 之间（含两端值），但必须小于学习率。