金融自然语言处理 (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 |
硬件: 单个 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 展示了整体架构。
图 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% | 删除乱码输出 |
实际上,这些过滤器在每个批量中删除的标题不到 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 万条标题的情况下,更新后的几个样本也会继续推动生成新内容。
图 2. 在迭代 5、20、40、60 和最终迭代 82 中,余弦相似性分布的组合 ECDF第 4 步:。为下一次迭代提取不同的几个样本示例
重复数据删除后,工作流将为下一批选择几个样本示例。其目标是让模型摆脱已经生成的模式。
离中心最远选择
重复数据删除步骤中的 K-means 聚类可为每个标题提供一个集群中心。我们根据与中心的余弦距离对标题进行排序,并选择最远的示例,这些示例在其语义邻域中是非典型的,有助于提示新的输出。
交叉迭代语义过滤
仅仅保持距离是不够的。该流程还会将每个候选项与之前使用的所有少数样本示例进行比较,并拒绝任何余弦相似度 > = 80% 的候选项。这可以防止在迭代过程中语义上重复的提示信号。
下表 3 总结了选择优先级。每个类别选择三个示例,每次迭代总计 39 个,并且使用的示例集和嵌入为恢复设置了检查点。
| 优先级 | 策略 | 目的 |
|---|---|---|
| 1. 初级 | 离 K-means 最远的中心 | 信息量最大的少量样本信号 |
| 2. 语义过滤器 | 与所有使用示例的余弦相似度约为 80% | 防止迭代中的语义重复 |
| 3. 后备 | 类别池中的随机样本 | 确保在异常值耗尽时覆盖 |
| 4. 万不得已 | 种子示例 (手动整理) | 难以填补类别的冷启动基准 |
用于少数样本候选过滤的语义相似性检查:
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- 级 |
第 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. 迭代 82 时的目标与实际类别分布对比 ( 502536 篇标题)结果
工作流运行摘要:由于生成以固定的 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 |
下游应用:模型蒸馏
生成的数据集支持面向金融数据开发者的 AI 模型蒸馏示例,其中 49B – 70B 教师模型标记合成标题,较小的 1B、3B 和 8B 学生模型通过 LoRA 进行微调。在这项金融标题分类任务中,有 2.5 万个带标记的示例,使 30 亿学生获得教师 F1 分数的 95%。
数据集多样性在这里至关重要:语义唯一性和类别平衡使学生接触到难以从现实世界来源收集到的罕见金融事件和边缘案例。
切实可行的途径
为生成具有全局语义复制的迭代合成数据制定了四项原则:
- 迭代,不要批量。 一次生成的 50 万代数据主要是重复数据。有 82 次小迭代和不断演变的提示词,产生了更加多样化的语料库。
- 全局而非本地去重。 每次迭代都必须针对完整的累积数据集进行重复数据删除,以防止跨批量重复。
- 方向盘是个小样本示例。 具有语义相似性过滤功能的最中心选择是影响生成多样性的最有效手段。
- 分布校正至关重要。 如果不动态调整权重,模型会过度生成热门类别,并淘汰罕见类别。
开始使用
- 使用数据集 — 下载 Hugging Face 上的 FinHeadlineMix 并直接将其用于标题分类、微调,或用作您自己蒸馏工作流的训练数据。
- 运行 recipe — 使用 NeMo Data Designer 进行结构化合成生成和 NeMo Curator 进行可扩展语义去重,重现或扩展此工作流。
- 训练经过提炼的模型 — 按照适用于金融数据开发者的 AI 模型蒸馏示例,基于此数据集微调紧凑的学生模型。
深入了解
- NVIDIA Build 上的 Nemotron-3-Nano-30B-A3B — 用于生成的 MoE 模型,已获准用于蒸馏工作流
- 针对特定任务的 LLM 蒸馏扩展定律 — 利用生成的 FinHeadlineMix 数据集 推导出蒸馏扩展定律的研究论文
- 使用 AI 模型蒸馏构建高效的金融数据工作流 – 配套博客
- 如何构建符合许可要求的合成数据工作流 – 相关阅读