数据科学

借助 NVIDIA NeMo 生成用于金融 AI 研究的合成数据

金融自然语言处理 (NLP) 的微调 LLM 受到有限且不平衡的数据的限制。现实世界的金融新闻过度代表收益和股票动态,而信用评级变化、产品审批和劳动力问题等更罕见的事件则难以大规模捕获。合成生成技术有助于填补交易研究、风险建模和监控方面的空白,但多样性需要的不仅仅是一次大规模运行。

这篇博文将通过一个迭代流程,为 12 个主题以及“其他”类别生成 50 万条独特的金融新闻标题。该工作流结合了用于结构化生成的 NVIDIA NeMo Data Designer、用于语义去重的 NVIDIA NeMo Curator 和用于高吞吐量标题合成的 NVIDIA Nemotron 模型。它还添加了离中心较远的少数镜头策略,以引导每个回合获得新的输出。

在总共 5 万条标题中,65% 的标题因近乎重复而被删除。工作流不会缩放单个批量,而是会进行以下迭代:生成、筛选、全局去重、选择独特的少数样本示例、纠正类别权重,然后重复,直到语料库达到目标大小。在 82 次迭代中,我们在单个 8 路 NVIDIA B200 节点上使用了约 6 天的计算时间,在 13 个类别中生成了 502536 个独特的标题,其中包括检查点、崩溃恢复和 SLURM 作业链。

最后,您将了解如何重现生成 – 去重循环,使其适应您自己的金融研究工作流程,或直接从开源数据集开始。

可再现性

版本

组件 版本
NeMo Curator 1.0.0rc0.dev0
NeMo 数据设计师 0.1.5
vLLM 0.12.0
模型 NVIDIA/ NVIDIA-Nemotron-3 -Nano-30B-A3B-FP8
句子转换器 2.2.0
表 1. 本指南中使用的软件和模型版本

硬件: 单个 8 路 NVIDIA B200 节点。GPU 0 – 3 专用于 vLLM 推理 ( 4 路张量并行,448 个并发请求) ;GPU 4 – 7 使用 Ray 运行 NeMo Curator 语义重复数据删除。

工作流参数: 每批 3.5 万条标题 (早期迭代为 5 万条) ,90% 的余弦相似性重复数据删除值,500 个 K-means 集群,每个类别 3 个样本样本,示例选择的交叉迭代相似性截止值占 80%。

检查点: 每次迭代都会保留语料库状态、类别权重、少数样本示例、指标以及用于崩溃恢复和 SLURM 工作链的用例嵌入。

工作流概述

由于现实世界的数据来源不平衡,金融标题生成无法作为一次性问题解决:收益报告和股票波动占据主导地位,而信用评级变化、产品审批和其他罕见事件则很少发生。在 5 万条标题基准中,语义重复数据删除仅保留了 17,348 条唯一标题,这表明较大的批量会增加许多近乎重复的内容,而不是按比例增加更多的覆盖范围。

迭代工作流通过闭环来解决此问题。每次迭代都会生成一个类别加权批量,过滤格式错误的输出,根据完整的累积语料库进行重复数据删除,选择不同的少数样本示例,并在下一次迭代之前调整类别权重。

下图 1 展示了整体架构。

关键的设计决策是全局去重。将每个批量与之前保留的所有标题进行比较,而不仅仅是与其本身进行比较,从而防止交叉批量重复,并在50万个语料库中保留语义唯一性。

第 1 步:。使用 NeMo Data Designer 生成标题

我们通过具有 4 路张量并行和 448 个并发请求的 vLLM 为 Nemotron 3 Nano ( 30B 参数,3B 活动) 提供服务。该模型的多专家模型 (MoE) 架构平衡了吞吐量和质量,在 300 亿参数模型中,每个前向传递使用 300 亿个活动参数。

NeMo Data Designer 通过声明性配置编排生成:涵盖 12 个主题和“其他”的加权类别采样器,以及根据采样类别和当前几个样本示例生成标题的 LLM 列。

每次迭代后,类别权重都会通过分布校正进行更新 (第 5 步) 。

数据设计器配置:

config_builder = DataDesignerConfigBuilder()

# Model config for local vLLM-served Nemotron
local_model = ModelConfig(
    alias="local-nemotron",
    model="nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B-FP8",
    provider="local-vllm",
    inference_parameters=InferenceParameters(
        max_parallel_requests=448,
        temperature=0.95,
        top_p=0.95,
    ),
)
config_builder.add_model_config(local_model)

# Category sampler with DYNAMIC weights (updated each iteration)
config_builder.add_column(
    SamplerColumnConfig(
        name="category",
        sampler_type=SamplerType.CATEGORY,
        params=CategorySamplerParams(
            values=CATEGORIES,        # 12 topics + "Other"
            weights=current_weights,   # Adjusted after each iteration
        ),
    )
)

# LLM column for headline generation with few-shot examples
config_builder.add_column(
    LLMTextColumnConfig(
        name="headline",
        model_alias="local-nemotron",
        system_prompt="You are a financial news headline writer.",
        prompt=f"""Generate a realistic financial news headline \
for the category: {{{{ category }}}}

{format_examples_for_prompt(examples_by_category)}

Generate a single headline for the "{{{{ category }}}}" category.
Headline:""",
    )
)

我们使用高多样性采样 (温度 = 0.95,频率损失 0.3,存在损失 0.2) ,并依靠下游过滤和语义重复数据删除来删除格式错误或冗余的输出。

提示结构

提示词包括角色设置系统提示词、按类别分组的少数样本示例,以及针对采样类别的指令。数据设计器在运行时通过 Jinja 模板注入类别,并在每次迭代中刷新几个样本示例 (第 4 步) 。

提示结构 (简化) :

SYSTEM:
  You are a financial news headline writer. Generate realistic,
  professional financial news headlines. Output ONLY the headline
  text, nothing else.

USER:
  Generate a realistic financial news headline for the category:
  {{ category }}

  Examples by category:
  - Earnings: "Google Parent Alphabet Posts $1.84 EPS, $63.5B Revenue"
  - Earnings: "Myriad Genetics Beats Q4 Estimates with 15% Revenue Surge"
  - Earnings: "Codec Solutions Posts Q3 Adjusted EPS $0.31, Surpasses ..."
  - Price Targets: "J.P. Morgan Cuts Target for Thermo Fisher, Downgrades ..."
  - Price Targets: "Mitsubishi UFJ Secures Buy Rating on Mitsui & Co. ..."
  - Price Targets: "Credence Resources Upgrades Target to $12.50 ..."
  - Stock price movement: "Equities Rise Broadly, Dow Gains 180 Points ..."
  - ...
  (3 examples x 12 topics + Other = up to 39 few-shot examples)

  Generate a single headline for the "{{ category }}" category.
  The headline should be 10-25 words long, realistic, and match
  the style of professional financial news.

  Headline:

第 2 步:。质量过滤

在语义重复数据删除之前,基于规则的轻量级传递会删除格式错误的输出,例如截断、失控或乱码的标题。

滤波器 目的
最小字数 > 5 个字 删除截断或片段输出
最小字符数 > 25 个字符 删除极短的输出
最大字数 不超过 100 个字 消除失控生成
最大字符数量 < = 1000 个字符 了解边缘案例
括号数量 + 5 个括号 消除格式化伪影
特殊字符比 <= 25% 删除乱码输出
表 2. 应用于已生成标题的高质量滤镜,并为每个标题提供值和依据

实际上,这些过滤器在每个批量中删除的标题不到 1%,因此大多数质量控制都来自下一步中的语义重复数据删除。

第 3 步:。使用 NeMo Curator 进行语义删除

这是核心质量机制。在每个批次之后,工作流将新生成的标题与累积的语料库相结合,并运行 NeMo Curator 的 TextSemanticDeduplicationWorkflow。标题嵌入 all-MiniLM-L6-v2,使用 K-means 进行聚类,并使用成对余弦相似度在集群内进行比较。超过 90% 相似度值的任何标题都将删除。

NeMo Curator 语义重复数据删除配置:

workflow = TextSemanticDeduplicationWorkflow(
    input_path=input_path,
    output_path=output_path,
    cache_path=cache_path,
    perform_removal=True,
    # Embedding parameters
    text_field="headline",
    model_identifier="sentence-transformers/all-MiniLM-L6-v2",
    embedding_max_seq_length=512,
    embedding_model_inference_batch_size=1024,
    # Deduplication parameters
    n_clusters=500,     # Higher = smaller clusters = faster pairwise
    eps=0.10,           # eps = 1 - similarity_threshold (0.90)
    id_field="id", 
    # Among duplicates, keep the more representative copy
    ranking_strategy=RankingStrategy(   
        metadata_cols=["cosine_dist_to_cent"],
        ascending=True,  #Keep the most representative (closest to centroid)
    ),
    pairwise_batch_size=4096,
    input_filetype="parquet",
    output_filetype="parquet",
)
workflow.run()

为何选择 500 个集群?

配对相似性成本高昂,因为比较会在每个集群内进行二次扩展。在 50 万条标题端点上,500 个集群使平均集群保持在约 1000 条标题附近,而总比较结果接近 5 亿条。使用 13 个集群 (每个类别一个) 将使总体比较结果达到 190 亿。集群数量越多,全局重复数据删除就会随着语料库的增长而变得易于追踪。

相似性分布监控

在每次迭代后,工作流会绘制成对余弦相似性的 ECDF (经验累积分布函数) 。红线表示 0.90 的重复数据删除值:接近 1.0 的急剧上升标志着许多近乎重复的数据,而更平滑的左移曲线表示更广泛的覆盖范围。

下方的图 2 覆盖了具有代表性的迭代次数 5、20、40、60 和 82。早期的曲线是正确的,因为模型重复有限的几个样本中的常见模式。稍后的曲线会向左移动并稳定下来,这表明即使在语料库接近 50 万条标题的情况下,更新后的几个样本也会继续推动生成新内容。

第 4 步:。为下一次迭代提取不同的几个样本示例

重复数据删除后,工作流将为下一批选择几个样本示例。其目标是让模型摆脱已经生成的模式。

离中心最远选择

重复数据删除步骤中的 K-means 聚类可为每个标题提供一个集群中心。我们根据与中心的余弦距离对标题进行排序,并选择最远的示例,这些示例在其语义邻域中是非典型的,有助于提示新的输出。

交叉迭代语义过滤

仅仅保持距离是不够的。该流程还会将每个候选项与之前使用的所有少数样本示例进行比较,并拒绝任何余弦相似度 > = 80% 的候选项。这可以防止在迭代过程中语义上重复的提示信号。

下表 3 总结了选择优先级。每个类别选择三个示例,每次迭代总计 39 个,并且使用的示例集和嵌入为恢复设置了检查点。

优先级 策略 目的
1. 初级 离 K-means 最远的中心 信息量最大的少量样本信号
2. 语义过滤器 与所有使用示例的余弦相似度约为 80% 防止迭代中的语义重复
3. 后备 类别池中的随机样本 确保在异常值耗尽时覆盖
4. 万不得已 种子示例 (手动整理) 难以填补类别的冷启动基准
表 3. Few-shot 示例选择优先级

用于少数样本候选过滤的语义相似性检查:

def is_semantically_distinct(
    candidate_embedding, used_embeddings, threshold=0.80
):
    """Reject candidates >= threshold similar to ANY used example."""
    if used_embeddings.size == 0:
        return True
    candidate_norm = candidate_embedding / (
        np.linalg.norm(candidate_embedding) + 1e-9
    )
    used_norms = used_embeddings / (
        np.linalg.norm(used_embeddings, axis=1, keepdims=True) + 1e-9
    )
    similarities = np.dot(used_norms, candidate_norm)
    return np.max(similarities) < threshold

下表 4 显示了所选示例在最终迭代后如何变得更加具体。

类别 迭代 1 示例 迭代 82 示例
收益 Google Parent Alphabet 发布每股收益 1.84 美元,收入 635 亿美元,超越预测 ThermoGlow 发布第二季度业绩公告,发布超低挥发性有机化合物 (VOC) 汽车用胶粘剂创纪录订单的财年指南
M&A Macy ‘ s 将以 18 亿美元的价格收购 Nordstrom 的家居部门 西门子同意以 35 亿美元的价格收购 Mentor Graphics,欧盟要求取消重叠的 EDA 工具
信用评级 S&P 将欧洲大型银行的“Moody’s Outlook”升级为“Stable” (稳定) 银行 Fitch 以财务压力和产量下降为由,将阿根廷国家石油公司 YPF 降级为 B- 级
表 4. 迭代 1 (早期) 和迭代 82 (最终) 中的少量示例。后续示例包含更具体的实体、利基金融工具以及更长、更详细的结构,这反映了管道向前沿内容的漂移。

第 5 步:。动态分布校正

仅靠加权采样无法将类别保留在目标上,因为该模型更倾向于“其他”和“股票移动”等更简单的类别。在每次迭代后,工作流会比较目标和实际类别比例,提升未被充分代表的类别,锁定极端情况,并为下一批归一化权重。

权重调整算法:

def compute_adjusted_weights(current_dist, target_dist, floor=0.005):
    adjusted = []
    for category in CATEGORIES:
        target = target_dist.get(category, 0.01)
        current = current_dist.get(category, 0.0)
        if current > 0:
            ratio = target / current           # > 1 = under-represented
            ratio = max(0.2, min(5.0, ratio))  # Clamp to avoid extremes
            weight = target * ratio
        else:
            weight = target * 2.0              # Missing category: boost
        adjusted.append(max(floor, weight))
    total = sum(adjusted)
    return [w / total for w in adjusted]       # Normalize to 1.0

通过迭代 82,“信用评级”和“产品审批”等罕见类别接近其 1% 的目标,而“其他”仍然过高。校正循环仍可充分减少模型偏差,从而保留所有类别的覆盖范围。

结果

工作流运行摘要:由于生成以固定的 3.5 万个批量进行,并且只有在超过值后才会停止,因此 500K 目标略有偏移;超出的 2536 个示例将从过度表示的“其他”类别中修剪。

  • 唯一标题总数: 502,536 (目标:500,000)
  • 总迭代次数:82
  • 批量大小: 每次迭代 35000 条标题
  • 生成的标题总数 (重复前) :约 387 万条
  • 整体重复率: 82% (累计)
  • 每次迭代新增净内容:约 5000 – 6000 条唯一标题
  • 类别: 12 大主题其他
  • 相似性值:90% 的余弦相似性
  • 每次迭代的少量样本示例: 39 (每个类别 3 个)

重复数据删除效率

语义重复数据删除是主要的过滤器,其作用随着运行而改变。在迭代 1 中 (每个类别仅通过一个手动示例进行种子搜索) ,工作流保留了 17,348 个 5 万条标题,其余 32652 个 ( 65%) 几乎重复。迭代 2 – 3 使用了之前输出中最远的中心样本,并保持了更高的产量,即每 5 万批 15K – 17K 个新标题,尽管随着语料库的增长,跨批次冲突开始增加。

从迭代 4 开始,当重复数据删除掉率超过 80% 时,我们将批量大小从 5 万减少到了 3.5 万。较大的 SLURM 作业主要产生下游丢弃的标题,而较小的 3.5 万个批量可在减少计算浪费的情况下提供类似的净新产出。

在语料库通过约 10 万条标题后,每个新标题都必须与更大的标题池进行区分。通过迭代 40%,产量稳定在约 5K – 6K 的新标题下,每批 3.5 万,约为 15%,每批丢弃约 3 万。表 4 显示了转换过程:早期重复大多是批量内重复,而后期重复则主要是与累积的语料库冲突。接近 50 万的稳定非零产量表明,不断演进的少量样本继续引导生成进入新的区域。

迭代 批量大小 净新 已弃用 产量 之后的语料库大小
1 50,000 17,348 32,652 35% 17,348
2 50,000 17,199 32,801 34% 34,547
3 50,000 15,209 34,791 30% 49,756
5 50,000 16,917 33,083 34% 80,028
42 35,000 5700 29300 16% 268,724
82 35,000 5155 + 30845 15% 502,536
表 5. 代表性迭代中的重复数据删除率。由于跨批量碰撞占主导地位,因此产量从 35% (早期,小语料库) 下降到约 15% (后期,大语料库)

下游应用:模型蒸馏

生成的数据集支持面向金融数据开发者的 AI 模型蒸馏示例,其中 49B – 70B 教师模型标记合成标题,较小的 1B、3B 和 8B 学生模型通过 LoRA 进行微调。在这项金融标题分类任务中,有 2.5 万个带标记的示例,使 30 亿学生获得教师 F1 分数的 95%。

数据集多样性在这里至关重要:语义唯一性和类别平衡使学生接触到难以从现实世界来源收集到的罕见金融事件和边缘案例。

切实可行的途径

为生成具有全局语义复制的迭代合成数据制定了四项原则:

  • 迭代,不要批量。 一次生成的 50 万代数据主要是重复数据。有 82 次小迭代和不断演变的提示词,产生了更加多样化的语料库。
  • 全局而非本地去重。 每次迭代都必须针对完整的累积数据集进行重复数据删除,以防止跨批量重复。
  • 方向盘是个小样本示例。 具有语义相似性过滤功能的最中心选择是影响生成多样性的最有效手段。
  • 分布校正至关重要。 如果不动态调整权重,模型会过度生成热门类别,并淘汰罕见类别。

开始使用

  • 使用数据集 — 下载 Hugging Face 上的 FinHeadlineMix 并直接将其用于标题分类、微调,或用作您自己蒸馏工作流的训练数据。
  • 运行 recipe — 使用 NeMo Data Designer 进行结构化合成生成和 NeMo Curator 进行可扩展语义去重,重现或扩展此工作流。
  • 训练经过提炼的模型 — 按照适用于金融数据开发者的 AI 模型蒸馏示例,基于此数据集微调紧凑的学生模型。

深入了解

标签