16 大语言模型

本章会用到的数据

  • 公开下载公司基本信息财务报表。代码会在首次运行时下载并保存到 data/course/

  • 这些数据能做什么:从公司和财务记录中检索答案,并注明答案来自哪条记录。

  • 分析时注意:找不到依据时应明确回答“不知道”,不能让模型补写数据中没有的信息。

  • 示例说明:本章重点是理解检索、引用和拒答,不要求使用付费模型或私有接口。

【课堂核心】2.5 学时学习安排

学习内容 分钟
动机与先修 15
许可前测与反馈 15
Transformer 最小机制 35
方案选择 25
公开证据代理 25
独立练习 20
任务答案与小结 15

欢迎来到第16章

大语言模型:金融文本分析的新范式

本章学习路线图

本章学习路线图 七步路线依次覆盖问题背景、语言模型原理、公开数据练习、结果判断与总结。 本章学习路线图 1背景与动机 2语言与数字 3注意力机制 4模型怎样学习 5公开数据练习 6评价与拓展 7总结与展望

本章学习目标:开启金融文本分析新范式

通过本章学习,您将能够:

  1. 解释大语言模型的核心技术演进,以及 Transformer 的机制、条件与计算边界。
  2. 识别大语言模型在现代金融领域的关键应用场景,例如情绪分析和文本摘要。
  3. 完成公开金融数据的检索、来源引用、字段提取与拒答练习。

核心问题:信息隐藏在字里行间

金融市场每天都会产生海量的非结构化文本数据:

  • 上市公司年报、季报、临时公告
  • 财经新闻、分析师报告
  • 社交媒体讨论、论坛帖子

这些文本蕴含着影响市场走向的关键信息,但传统计量方法难以有效处理。

传统方法的局限性

传统文本分析方法,如“词袋模型”或基于词典的方法,常常忽略了最重要的元素:语境 (Context)

词袋表示与上下文化表示 左侧词袋模型忽略语序,右侧上下文化模型输出待下游任务验证的关系表示。 传统方法:词袋模型 "苹果公司发布财报..." 苹果 发布 财报 语序和关系丢失 LLM:上下文化表示 "苹果公司发布财报..." 是一个[公司实体] 执行了[发布]动作 对象是[财务文件]

大语言模型提供上下文化表示与生成接口;它是否改善具体金融任务,仍须用确定版本、基线和最终测试指标验证。

【可选拓展|检查日 2026-08-28】模型访问模式

  • 不做排行榜:产品名、上下文上限与许可都会变化。
  • 运行记录:精确版本、提供方、权重可得性、许可文件、上下文上限、官方来源与检查日。
  • 结论边界:上述信息缺一项,就不能据此下实际使用或商用结论。

【课堂核心】闭源 API、开放权重与开源不是同义词

访问模式 可得内容 不能自动推出
闭源 API 托管接口与服务条款 权重可得、可离线实际使用
open-weight 可下载某一精确版本权重 OSI 式开源、任意商用、训练数据开放
OSI 式开源 满足对应开源定义与许可证 模型适合金融任务或合规实际使用

许可、数据权利、隐私、出口/行业限制与任务指标都需逐版本检查,不能从模型系列名称推断。

检查:能否从系列名称推断许可?

独立作答 2 分钟:

  1. 页面写着“可下载权重”,能否断言它是 OSI 式开源?
  2. 能否进一步断言它可商用且适合金融数据?
  3. 还需确定哪四类证据?

反馈:版本、许可、任务证据缺一不可

  • 许可判断:不能把 open-weight 直接等同于 OSI 式开源或任意商用。
  • 所需证据:精确版本、许可证或服务条款、权重与代码可得性、官方来源和检查日。
  • 任务判断:是否适合金融数据,还要在固定金融任务上评估。
  • 学习路径:边界不清回看 相关内容;边界完整再进入 注意力机制

回顾历史:语言模型如何一步步演化至今?

在 Transformer 诞生之前,处理序列数据(如文本)的主流模型是循环神经网络(RNN)

循环神经网络 (RNN) 工作原理 展示RNN如何按顺序处理单词并将信息(隐藏状态)传递给下一个时间步的图示。 核心思想:逐词处理,传递“记忆” "The" RNN "cat" RNN "sat" RNN 隐藏状态(h)是模型对截至当前内容的“记忆”

vanilla RNN 与门控循环网络的限制不同

vanilla RNN 更容易出现梯度消失或爆炸;LSTM/GRU 的门控可缓解但不保证解决长期依赖。共同的计算边界是递归状态使单个序列内的位置存在顺序依赖,限制该维度并行;不同样本批次与每步矩阵运算仍可并行。

  1. 梯度路径:vanilla RNN 在长序列上更易失稳;门控结构只提供缓解机制。
  2. 序列内顺序依赖:位置 \(t\) 的状态依赖 \(t-1\),因此同一序列的位置不能像无递归层那样同时计算。

这直接限制了模型处理长距离依赖关系的能力。

缺陷1:梯度消失问题

信息在 RNN 中长距离传递时,就像一个越传越弱的信号。

梯度消失可视化 图中展示了一个长序列,早期输入的信息对后期输出的影响逐渐减弱,如同一个褪色的箭头。 长距离依赖的“遗忘”效应 "财报" "显示" ... "利润" "下滑" 信息传递 早期“财报”一词的语义信息,很难有效影响到对“下滑”的理解。

缺陷2:顺序计算的效率瓶颈

RNN 的序列位置存在递归依赖链;这不等于整个训练完全不能并行。Transformer 训练时同层位置可并行,但自回归生成仍逐 token,实际吞吐取决于长度、batch、硬件与实现。

序列内递归依赖与训练时位置并行 对比循环网络单序列位置的递归依赖链与 Transformer 训练时同层位置并行,并注明实际吞吐的条件。 RNN: 顺序计算 W1 W2 W3 Wn 单序列位置有依赖链 批次与每步矩阵仍可并行 Transformer: 并行计算 W1 W2 W3 Wn 训练时同层位置可并行 自回归生成仍逐 token

架构转折:2017 年《Attention Is All You Need》

2017 年,Vaswani 等提出 Transformer 架构,以注意力机制为核心并去掉同层递归依赖,训练时支持序列位置并行 (Vaswani 等 2017年)。是否更快或更好取决于任务、长度、实现与硬件;标准全注意力的得分与内存随序列长度近似二次增长,并受上下文窗口限制。

Attention Is All You Need 论文 一个代表 Transformer 论文的图标,用于标记去除同层递归依赖的架构转折。 Attention Is All You Need Ashish Vaswani, et al. (Google Brain, 2017) "We propose a new simple network architecture, the Transformer, based solely on attention mechanisms, dispensing with recurrence..."

全注意力可直接连接不同位置,但任务收益、训练吞吐与成本必须绑定架构、基线、序列长度、实现和硬件评估;它不保证长距离关系被正确使用。

基础:什么是Tokenization和Embedding?

在进入模型内部前,文本需要经过两步预处理:

  1. 分词 (Tokenization):将原始文本字符串切分成一个个更小的单元,称为“标记”(Token)。
    • "美联储加息" -> ["美联储", "加息"]
  2. 嵌入 (Embedding):查表得到学习到的、无上下文 token 输入向量;位置与上下文关系由位置编码和后续网络层注入或形成。
Tokenization and Embedding 从文本到Token再到Embedding向量的过程。 "财报向好" Tokenization 财报 向好 Embedding [0.1, -0.4, ...] [0.9, 0.2, ...]

什么是序列到序列(Seq2seq)模型?

原始 Transformer 论文采用编码器—解码器完成 序列到序列(Seq2seq);Transformer 是模块与架构家族,后来还有 encoder-only(如 BERT)和 decoder-only(如 GPT),不能把整个家族等同于 Seq2seq。

  • 定义:输入一个序列,输出另一个序列的模型。
  • 序列(Sequence):一系列按顺序排列的标记(Token)。
  • 向量表示:在模型内部,每个 Token 都被表示为一个高维向量。
任务 输入序列 输出序列
机器翻译 中文句子 英文句子
聊天机器人 用户的问题 机器人的回答
文本摘要 一篇长文章 几句核心摘要

Transformer的核心:注意力(Attention)机制

自注意力用当前 query 与各 key 的学习后兼容度,对相应 value 加权汇总。

  • 直观理解:某些注意力头可能为代词任务利用远距离上下文,但不保证把 “它” 准确解析为 “美联储加息”;共指效果必须另用标注任务验证。
  • 核心边界:权重是具体头、层、输入和任务下的兼容度,不是 token 相关系数,也不是语义或因果解释。

注意力机制的QKV类比

我们可以将注意力机制类比为在图书馆查阅资料的过程:

  • Query (Q): 你当前正在处理的词,即你的“查询问题”。
  • Key (K): 句子中所有可供参考的词,像是图书馆里所有书籍的“关键词索引”。
  • Value (V): 句子中所有词本身包含的信息,像是书籍的“具体内容”。
Query, Key, Value Analogy for Attention 用图书馆查资料的比喻解释QKV机制:用查询(Q)去匹配所有关键词(K),然后根据匹配度加权提取内容(V)。 Query (Q) 当前词 "它" Q 1. 匹配 Keys (K) Key: "美联储" Key: "加息" Key: "市场" Key: "下跌" ... 2. 加权求和 Values (V) V 得到富含上下文的 新向量表示

过程:用 Q 与每一个 K 计算缩放点积兼容度,再经 softmax 决定对应 V 的加权比例。

注意力机制的数学表达

注意力机制的计算可以概括为一个简洁的公式:

\[ \large{\text{Attention}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}}\right)V} \]

  • \(Q, K, V\) 分别是查询、键和值的矩阵。
  • \(QK^T\) 计算查询与所有键的点积兼容度得分。
  • 在 Q/K 分量方差可比的假设下,\(\sqrt{d_k}\) 使点积方差不随 \(d_k\) 线性增大,降低 softmax 过早饱和;对优化的帮助是间接结果。
  • \(\text{softmax}\) 函数将得分归一化,得到权重。
  • 最后将权重乘以值 \(V\) 进行加权求和。

注意力矩阵的维度与解释边界

权重必须在具体头、层、输入与任务上诊断;高权重不自动证明语义相关或模型解释。

  • 输入\(Q,K\in\mathbb{R}^{n\times d_k}\)\(V\in\mathbb{R}^{n\times d_v}\)
  • 中间量:得分矩阵与逐行 softmax 权重均为 \(n\times n\)
  • 输出:加权结果为 \(n\times d_v\)
  • 自回归约束:softmax 前需对未来位置加入 mask。

注意力最小手算

最小手算

  • 缩放得分 \([0,\ln 3]\) 对应权重 \([1/4,3/4]\)
  • value 为 2 和 6 时,输出为 \(2\times1/4+6\times3/4=5\)
  • 检查:只交换两个 value,输出是多少?

原始 encoder–decoder Transformer 概览

原始机器翻译架构组合编码器(Encoder)与解码器(Decoder);encoder-only 与 decoder-only 变体只保留其中一侧的模块结构。

简化的Transformer架构图 原始encoder–decoder Transformer中,编码器输出状态序列,解码器以掩码自注意力和交叉注意力预测下一token分布。 编码器 (Encoder)生成上下文化表示 Self-Attention Feed Forward Nx 输入序列 解码器 (Decoder)预测下一 token 分布 Self-Attention Encoder-Decoder Attn Feed Forward Nx 输出序列 编码器状态序列
  • 编码器(左侧):把输入 token 转换为上下文化状态序列;表示是否适合下游任务需要验证。
  • 解码器(右侧):在掩码与编码器状态条件下预测下一 token 的概率分布。

两种主流路径:编码器模型 vs. 解码器模型

在实际应用中,编码器和解码器可以独立使用,形成了两大主流模型分支:

编码器架构:BERT

  • 全称:Bidirectional Encoder Representations from Transformers
  • 由多个编码器堆叠而成。
  • 特点:能同时看到左右两侧的上下文,是“完形填空”大师。
  • 常见用途:配合任务头用于文本分类、情绪分析、命名实体识别;适合度取决于训练数据、版本、指标和预算。

解码器架构:GPT

  • 全称:Generative Pre-trained Transformer
  • 由多个解码器堆叠而成。
  • 特点:只能看到左侧的上下文,是“句子接龙”大师。
  • 常见用途:用于对话、写作、代码生成;适合度仍取决于任务、版本、指标与运行时间和内存。

一种常见但非唯一的训练与适配路径

Base pretraining 之后,continued/domain-adaptive pretraining、SFT 与 preference optimization(RLHF/DPO 等)都是可选分支;RAG 与 prompting 属于推理或系统适配,不是必经训练阶段。

基础训练与两条可选权重适配路径 基础预训练模型之后,可选择域适配或监督微调,也可选择偏好优化;虚线箭头表示这些步骤并非必经。 基础: Base 预训练 形成基础权重 数据权利与算力成本高 可选: 域适配 / SFT 按任务更新权重 也可直接采用系统适配 可选: 偏好优化 RLHF / DPO 等 须用任务评估验证

基础路径:通过自监督学习进行预训练

预训练(Pre-training)是模型学习语言基础规律的阶段。

  • 核心思想:使用自监督学习(Self-supervised Learning)方法,让模型从海量无标签文本中自动学习语言规律。
  • 数据来源:由具体模型的数据说明决定;不能笼统写成“整个互联网公开文本”,还须检查许可、过滤、去重、时间范围与版本。
  • 成本边界:无需人工逐条标签不等于低成本;采集、去重、许可、隐私、过滤、存储与算力都必须计入。

预训练任务示例:BERT vs. GPT

不同的模型架构采用不同的自监督任务:

BERT: 完形填空

  • 任务: 遮蔽 token 预测(Masked Language Model, MLM)
  • 做法: 遮蔽部分 token,让模型根据双向上下文预测被遮蔽 token。
  • 例子: 研究[MASK]市场需要[MASK]数据。
  • 目标: 学习供后续任务验证的上下文化表示。

GPT: 句子接龙

  • 任务: 下一 token 预测(Next-token Prediction)
  • 做法: 给定 token 前缀,让模型根据单向上下文预测下一个 token 的分布。
  • 例子: 研究金融市场需要...
  • 目标: 训练连贯的文本生成能力。

可选路径:针对特定任务更新权重

预训练好的模型是一个通用的“语言专家”,但还不是特定任务的“专才”。

微调(Fine-tuning)就是将这个通用模型适配到具体任务上的过程。

微调过程示意图 一个通用的预训练模型,加上一个任务专用的层,然后用少量有标签数据训练,成为一个微调后的专家模型。 通用预训练模型 (例如 BERT) + 任务专用层 (例如 分类器) 微调后的 模型 使用少量带标签的数据(如“新闻-情绪”)进行训练

微调数据点示例

表16.1:金融领域微调数据示例
Input (指令/文本) Output (模型应输出)
任务:情绪分类
文本:“该公司本季度营收超出预期,利润实现强劲增长。”
情绪:正向
任务:新闻摘要
文本:“中国人民银行今日宣布将下调存款准备金率0.5个百分点,旨在通过释放长期资金来稳定市场流动性……”
摘要:央行宣布降准0.5个百分点以稳定市场流动性。
任务:命名实体识别
文本:“吉利汽车董事长李书福表示……”
{ "公司": "吉利汽车", "人物": "李书福" }

可选路径:基于人类反馈的强化学习 (RLHF)

为了让模型的输出更符合人类的偏好(例如,更有用、更无害),研究人员引入了 RLHF

RLHF 流程图 展示RLHF的三个步骤:1. 收集人类偏好数据;2. 训练一个奖励模型来模拟这些偏好;3. 使用强化学习根据奖励模型微调LLM。 1. 收集偏好数据 Prompt -> 多个回答 人类对回答排序 (好 > 中 > 差) 2. 训练奖励模型 用排序数据训练 模型学会给回答 打“偏好分” 3. 强化学习微调 用奖励模型作为裁判 引导LLM生成 更高分的回答

金融候选应用(1):情绪分类需任务验证

传统的情绪分析依赖于固定的词典,但金融领域的词汇含义高度依赖上下文。

  • 经典例子:“liability”
    • 通用语境:“累赘”,负面情绪。
    • 金融语境:“负债”,中性词汇。公司的资产负债表必然包含负债,适度的负债是正常经营的一部分。
  • 待检验命题:编码器或生成模型是否优于词典基线,取决于确定版本、任务头、样本与指标;BERT 编码器须配置任务头并训练,不能凭模型族宣称“理解更深”或情绪更精准。

文献检查:LLM 情绪研究不能只写作者名

  • 删除原因:旧结论缺少可唯一定位的题名、版本、样本、基准和表号。
  • 重新纳入条件:补齐正式引用,并核对新闻发布时间、模型版本与提示词。
  • 实证边界:同时报告交易成本、时间外窗口与多重尝试偏差。

金融应用(2): 实体与文本的模糊匹配

在处理来自不同数据库的数据时,我们经常会遇到实体名称不统一的问题。

  • 例如: '国际商业机器公司', 'IBM Corp.', 'IBM' 可能指向同一家公司。

传统方法依赖于复杂的规则或编辑距离算法,效果有限。

实体链接可比较规则/编辑距离、编码器检索与生成模型候选;须在含拒答的标注集报告 precision/recall、别名与同名错误、成本和延迟,不能预设生成模型更精准。

金融应用(3):适配路径必须用同一任务验证

下图只列候选路径,位置不表示性能排序;选择须由事先确定指标、数据许可、总成本、延迟、隐私、漂移与失败案例共同决定。

候选路径 是否改权重 数据权利 算力与维护 版本控制 知识更新 任务适配证据
从头/领域继续预训练 训练语料须可用 高,需持续维护 权重、语料、代码 重新训练或继续训练 固定任务集与失败案例
SFT / PEFT 标注与基础权重须可用 中,需重训与实际使用 基础版本、适配器、数据 更新适配数据后重训 与确定基线同窗比较
Prompting / RAG 提示、检索库与接口均须合规 低至中,含调用与索引维护 接口、提示、索引数据版本 更新检索库或接口 准确率、引用、拒答、延迟

方案1:从头或领域继续预训练

只在语料授权、隐私、去重、算力与版本治理均可落实时,才把从头或领域继续预训练列为候选。

  • 可能收益: 可以改变领域表示,但是否改善目标任务必须在固定评估集验证。
  • 缺点: 成本极其高昂,需要巨大的算力和时间,只有少数大型机构能够承担。

方案2:领域微调 (Fine-tuning)

在通用预训练模型(如 BERT)的基础上,使用金融领域的有标签数据进行微调。

  • 可能收益: 用任务数据改变权重;收益、维护与漂移风险必须同基线比较。
  • 缺点: 仍然需要高质量的、针对特定金融任务的标注数据。

方案3:提示工程 (Prompting)

使用确定版本的托管或公开模型,通过提示(Prompt)来引导模型完成金融任务。

  • 可能收益: 不改权重、迭代快;总成本与准确率仍取决于接口、提示和流量。
  • 缺点: 效果依赖于提示的质量和基础模型的能力,对于非常专业的任务可能不够深入。

提示工程:与模型对话的艺术

提示(Prompt) 是生成模型接口的一部分;不同提示可能改变输出,准确性须在确定模型与固定评估集上验证。

  • 角色扮演提示:在提问前,先给模型设定一个“专家”角色。

    “你是一位资深的金融分析师,精通财务报表解读。”

    角色说明可能改变输出风格与任务遵循;是否提高准确率必须在固定评估集比较,不能把角色提示当作事实保证。

  • 指令式提示:适用于经指令微调且支持该接口的生成模型;普通 BERT 编码器须以任务头或对比学习等方式训练,拼接自然语言指令不自动获得指令遵循。

    “请分析以下新闻文本的情绪。文本是:[在此处插入新闻文章]”

提示工程进阶:零样本 vs. 少样本

零样本与少样本是两种候选提示格式。few-shot 不更新模型权重,只改变条件上下文;结果可能改善、不变或恶化。

零样本 vs. 少样本提示 对比两种候选提示格式:零样本不提供示例,少样本在条件上下文中加入固定示例;两者都不更新模型权重。 零样本 (Zero-shot) 任务: 判断情绪 文本: "公司利润大幅增长。" 情绪: ? 直接提问,不给示例 少样本 (Few-shot) 文本: "市场需求萎缩。" -> 负向 文本: "营收符合预期。" -> 中性 文本: "公司利润大幅增长。" 情绪: ? 加入固定示例与顺序,不更新权重

比较说明:确定模型及版本、固定评估集、示例及顺序、解码设置、指标与成本口径;只在开发期选择提示格式,确定后再开启最终测试。

公开中国数据核心验证:LLM 输入与输出说明

代码
from pathlib import Path  # 管理中国公司基础信息路径
from urllib.request import urlretrieve  # 复用本章隐藏设置单元安装的浏览器标识下载器
import json  # 验证结构化模型输出说明
import pandas as pd  # 读取固定公开公司记录
# 按 Linux 共享数据、Windows 共享数据、项目缓存的顺序选择公司基础信息文件。
basic_path = next((candidate_path for candidate_path in [Path('/home/ubuntu/r2_data_mount/data/stock/stock_basic_data.h5'), Path('C:/qiufei/data/stock/stock_basic_data.h5'), Path('data/course/stock_basic_data.h5')] if candidate_path.exists()), Path('data/course/stock_basic_data.h5'))
if not basic_path.exists():
    basic_path.parent.mkdir(parents=True, exist_ok=True)
    urlretrieve('https://assets.qiufei.site/data/stock/stock_basic_data.h5', basic_path)
llm_input_data = pd.read_hdf(basic_path, key='stock_basic_info', columns=['order_book_id', 'symbol', 'industry_name', 'province', 'status'])  # 选择性读取提示所需字段
llm_input_data = llm_input_data.query('status == "Active" and province in ["上海市", "江苏省", "浙江省", "安徽省"]').dropna().head(5).copy()  # 选择五家长三角公司作为练习样本
prompt_records = llm_input_data[['order_book_id', 'symbol', 'industry_name', 'province']].to_dict('records')  # 转换为可检查结构化输入
prompt_payload = json.dumps(prompt_records, ensure_ascii=False, sort_keys=True)  # 生成确定性的中文 JSON 提示载荷
expected_output_fields = {'company_id': str, 'risk_summary': str, 'evidence_fields': list, 'abstain': bool}  # 确定模型输出字段和类型
print({'文件': basic_path.name, '输入公司数': len(prompt_records), '输入字符数': len(prompt_payload), '输出字段': list(expected_output_fields)})
print(prompt_payload[:240])  # 展示去除凭据且可复算的提示片段
print('本页不调用外部 API,也不伪造模型答案;有授权模型时必须逐字段校验并允许 abstain。')  # 明示运行结果与失败策略
表 1: 公开中国公司信息生成的模型输入与确定性校验
{'文件': 'stock_basic_data.h5', '输入公司数': 5, '输入字符数': 501, '输出字段': ['company_id', 'risk_summary', 'evidence_fields', 'abstain']}
[{"industry_name": "生态保护和环境治理业", "order_book_id": "000035.XSHE", "province": "江苏省", "symbol": "中国天楹"}, {"industry_name": "医药制造业", "order_book_id": "000153.XSHE", "province": "安徽省", "symbol": "丰原药业"}, {"industry_name": "广播、电视、电影和影视录音制作业", "o
本页不调用外部 API,也不伪造模型答案;有授权模型时必须逐字段校验并允许 abstain。

可复现核心任务:公开财报证据检索问答

  • 环境边界:课程环境未授权固定生成式 LLM 权重。
  • 代理任务:从公开财报构造证据句,检索问题对应的公司财年,只从命中文档抽取净利润。
  • 验证对象:检索、引用与拒答链。
  • 不能推出:开放式生成能力。
代码
import sklearn  # 记录检索模型依赖版本
import numpy as np  # 计算证据位置与问答评估指标
from sklearn.feature_extraction.text import TfidfVectorizer  # 构造固定中文字符检索器
from sklearn.metrics.pairwise import cosine_similarity  # 计算问题与证据句相似度
# 按 Linux 共享数据、Windows 共享数据、项目缓存的顺序选择财务报表文件。
financial_path = next((candidate_path for candidate_path in [Path('/home/ubuntu/r2_data_mount/data/stock/financial_statement.h5'), Path('C:/qiufei/data/stock/financial_statement.h5'), Path('data/course/financial_statement.h5')] if candidate_path.exists()), Path('data/course/financial_statement.h5'))
if not financial_path.exists():
    financial_path.parent.mkdir(parents=True, exist_ok=True)
    urlretrieve('https://assets.qiufei.site/data/stock/financial_statement.h5', financial_path)
qa_companies = ['600276.XSHG', '600009.XSHG', '600585.XSHG', '603259.XSHG', '002415.XSHE', '600104.XSHG', '600019.XSHG', '600406.XSHG']  # 确定八家公司
qa_quarters = [f'{year}q4' for year in range(2019, 2024)]  # 确定五个财年
qa_columns = ['order_book_id', 'quarter', 'info_date', 'operating_revenue', 'net_profit_parent_company']  # 只读取问答所需证据字段
qa_financials = pd.read_hdf(financial_path, key='financial_data', where='quarter in qa_quarters', columns=qa_columns)  # 选择性读取年度财报
qa_financials['info_date'] = pd.to_datetime(qa_financials['info_date'])  # 统一披露日类型
qa_financials = qa_financials.query('order_book_id in @qa_companies').sort_values(['order_book_id', 'quarter', 'info_date']).drop_duplicates(['order_book_id', 'quarter'], keep='last')  # 固定公司财年最后版本
qa_financials['document'] = qa_financials.apply(lambda row: f"公司{row['order_book_id']},财年{row['quarter'][:4]},营业收入{row['operating_revenue']:.2f}元,归母净利润{row['net_profit_parent_company']:.2f}元。", axis=1)  # 将真实字段转成确定性证据句
qa_financials['citation'] = qa_financials.apply(lambda row: f"financial_statement.h5#financial_data/{row['order_book_id']}/{row['quarter']}/info_date={row['info_date'].date()}", axis=1)  # 生成字段级公开引用
qa_vectorizer = TfidfVectorizer(analyzer='char', ngram_range=(2, 4), min_df=1)  # 固定适合公司代码与中文问题的字符词元器
qa_document_matrix = qa_vectorizer.fit_transform(qa_financials['document'])  # 在固定证据库拟合检索表示
print({'sklearn版本': sklearn.__version__, '证据句数': len(qa_financials), '公司数': qa_financials.order_book_id.nunique(), '财年': sorted(qa_financials.quarter.str[:4].unique())})  # 报告模型与语料说明
{'sklearn版本': '1.7.2', '证据句数': 40, '公司数': 8, '财年': ['2019', '2020', '2021', '2022', '2023']}

检索、抽取、引用与拒答

代码
def answer_financial_question(question):  # 对单个问题执行证据约束问答
    mentioned_company = next((company for company in qa_companies if company in question), None)  # 识别问题中的固定公司代码
    mentioned_year = next((year for year in qa_financials['quarter'].str[:4].unique() if year in question), None)  # 识别问题中的固定财年
    if mentioned_company is None or mentioned_year is None:  # 缺少可定位实体时拒绝猜测
        return {'answer': None, 'citation': None, 'abstain': True, 'reason': '公司或财年不在证据库'}  # 返回结构化拒答
    query_vector = qa_vectorizer.transform([question])  # 使用确定词表编码问题
    candidate_mask = qa_financials['order_book_id'].eq(mentioned_company) & qa_financials['quarter'].str.startswith(mentioned_year)  # 限定实体与财年候选
    if not candidate_mask.any():  # 证据库无对应记录时拒答
        return {'answer': None, 'citation': None, 'abstain': True, 'reason': '没有匹配证据'}  # 防止生成不存在的数值
    candidate_positions = np.flatnonzero(candidate_mask.to_numpy())  # 取得候选证据位置
    similarities = cosine_similarity(query_vector, qa_document_matrix[candidate_positions]).ravel()  # 比较问题与候选证据句
    selected_position = candidate_positions[int(similarities.argmax())]  # 选择相似度最高的唯一证据
    selected_record = qa_financials.iloc[selected_position]  # 读取命中财报记录
    return {'answer': float(selected_record['net_profit_parent_company']), 'citation': selected_record['citation'], 'abstain': False, 'reason': '命中唯一公司财年证据', 'score': float(similarities.max())}  # 只抽取证据中的净利润并附引用理由

固定评估集与任务指标

代码
qa_evaluation = qa_financials.sort_values(['quarter', 'order_book_id']).tail(20).copy()  # 固定最后20条公司财年记录为评估问题
qa_evaluation['question'] = qa_evaluation.apply(lambda row: f"{row['order_book_id']}{row['quarter'][:4]}财年的归母净利润是多少?", axis=1)  # 从固定记录生成可核对问题
qa_outputs = [answer_financial_question(question) for question in qa_evaluation['question']]  # 实际运行全部评估问题
qa_evaluation['predicted_profit'] = [output['answer'] for output in qa_outputs]  # 收集证据约束答案
qa_evaluation['has_citation'] = [bool(output['citation']) for output in qa_outputs]  # 检查每个答案是否带引用
exact_match = np.isclose(qa_evaluation['predicted_profit'], qa_evaluation['net_profit_parent_company']).mean()  # 计算数值精确匹配率
citation_rate = qa_evaluation['has_citation'].mean()  # 计算回答引用覆盖率
abstention_output = answer_financial_question('999999.XSHG在2030财年的归母净利润是多少?')  # 测试证据库外问题必须拒答
print({'评估问题': len(qa_evaluation), '数值精确匹配率': exact_match, '引用覆盖率': citation_rate, '库外问题拒答': abstention_output['abstain']})  # 报告任务级指标
display(qa_evaluation[['question', 'predicted_profit', 'citation']].rename(columns={
    'question': '问题', 'predicted_profit': '回答的归母净利润', 'citation': '数据来源'
}).head(3))
{'评估问题': 20, '数值精确匹配率': 1.0, '引用覆盖率': 1.0, '库外问题拒答': True}
表 2: 公开财报检索问答代理任务的固定评估
问题 回答的归母净利润 数据来源
5728 600276.XSHG在2021财年的归母净利润是多少? 4.530218e+09 financial_statement.h5#financial_data/600276.X...
6164 600406.XSHG在2021财年的归母净利润是多少? 5.642448e+09 financial_statement.h5#financial_data/600406.X...
6701 600585.XSHG在2021财年的归母净利润是多少? 3.326756e+10 financial_statement.h5#financial_data/600585.X...

注意:找不到来源时应拒答;本例不代表模型能读懂整份年报。

独立任务:检索—引用—拒答三类题

独立提交三类问题的结构化输出:

  1. 库内公司—财年净利润问题。
  2. 证据库外公司或财年问题。
  3. 只给公司、缺少财年的歧义问题。

每条包含 answercitationabstain 与拒答理由;最后报告数值 exact match、回答引用率和拒答准确率。

评分标准(10 分):结构化字段 2 分;库内数值 exact match 2 分;有效公开引用 2 分;两类拒答均正确 2 分;证据边界与失败解释 2 分。任一无引用作答或库外编造,整题不超过 5 分。

练习:先完成再查看答案:问答说明与失败集

  • 提交内容:证据字段、许可边界、结构化 JSON、命中指标和两类拒答结果。
  • 修正条件:缺少引用,或应拒答却未拒答。
  • 完成后:进入答案页核对。

独立任务完整答案

代码
independent_record = qa_financials.sort_values(['quarter', 'order_book_id']).iloc[0]  # 固定一条库内证据作为可复算标准题
independent_cases = pd.DataFrame([  # 同时覆盖库内、库外与歧义三类问题
    {'case': '库内', 'question': f"{independent_record['order_book_id']}{independent_record['quarter'][:4]}财年的归母净利润是多少?", 'expected_answer': independent_record['net_profit_parent_company'], 'expected_abstain': False},  # 设定可精确匹配的库内答案
    {'case': '库外', 'question': '999999.XSHG在2030财年的归母净利润是多少?', 'expected_answer': np.nan, 'expected_abstain': True},  # 要求对不存在实体拒答
    {'case': '歧义', 'question': f"{independent_record['order_book_id']}的归母净利润是多少?", 'expected_answer': np.nan, 'expected_abstain': True},  # 要求对缺失财年拒答
])
independent_outputs = [answer_financial_question(question) for question in independent_cases['question']]  # 运行三类独立问题
independent_cases['answer'] = [output['answer'] for output in independent_outputs]  # 收集结构化答案
independent_cases['citation'] = [output['citation'] for output in independent_outputs]  # 收集证据引用
independent_cases['abstain'] = [output['abstain'] for output in independent_outputs]  # 收集拒答决策
independent_cases['reason'] = [output['reason'] for output in independent_outputs]  # 收集命中或拒答理由
answered_mask = ~independent_cases['expected_abstain']  # 标识必须回答的库内问题
independent_exact_match = np.isclose(independent_cases.loc[answered_mask, 'answer'], independent_cases.loc[answered_mask, 'expected_answer']).mean()  # 计算库内数值精确匹配率
independent_citation_rate = independent_cases.loc[answered_mask, 'citation'].notna().mean()  # 计算实际回答的引用覆盖率
independent_abstention_accuracy = independent_cases['abstain'].eq(independent_cases['expected_abstain']).mean()  # 计算三类题拒答决策准确率
print({'exact_match': independent_exact_match, 'citation_rate': independent_citation_rate, 'abstention_accuracy': independent_abstention_accuracy})  # 输出可评分指标
display(independent_cases.loc[independent_cases['case'].eq('库内'), ['case', 'question', 'answer', 'citation']])  # 单独展示命中答案与引用
{'exact_match': 1.0, 'citation_rate': 1.0, 'abstention_accuracy': 1.0}
表 3: 检索—引用—拒答独立任务的三类标准答案与评分指标
case question answer citation
0 库内 002415.XSHE在2019财年的归母净利润是多少? 1.241459e+10 financial_statement.h5#financial_data/002415.X...

独立任务完整答案:拒答案例

代码
display(independent_cases.loc[independent_cases['case'].ne('库内'), ['case', 'question', 'abstain', 'reason']])  # 展示库外与冲突问题的拒答证据
表 4: 库外与歧义问题的拒答决定及理由
case question abstain reason
1 库外 999999.XSHG在2030财年的归母净利润是多少? True 公司或财年不在证据库
2 歧义 002415.XSHE的归母净利润是多少? True 公司或财年不在证据库

判读:三项指标都应为 1.0;否则按失败样例定位实体识别、证据检索、字段抽取或拒答规则。该答案验证确定性代理,不验证生成式 LLM 能力。

本章小结

  • 学完后应能:固定评估集上 exact match、引用率、拒答准确率均可复算。
  • 选择条件:外部模型只有在版本、许可、字段格式、费用、延迟与失败案例同时受控时才进入候选。
  • 未建立:公开确定性代理不证明生成式模型理解整份财报,也不构成投资建议。
  • 下一站:进入第 17 章,把来源授权、MNPI、隐私与多模态测量边界加入同一检查链。

理论部分小结:关键要点回顾

  1. Transformer 的优势与代价都有条件
    • 训练时同层位置可并行,全注意力可直接连接位置;是否优于循环基线取决于架构、版本、任务、评估集与硬件。
    • 标准全注意力具有近似 \(O(n^2)\) 得分与内存成本、上下文窗口限制,自回归解码仍逐 token;必须同时报告成本与结果不理想时如何解释。
  2. 训练与适配不是固定三阶段
    • continued pretraining、SFT、偏好优化均可选;RAG 与 prompting 是系统路径,效果由固定评估集决定。

理论部分小结:关键要点回顾

  1. 金融应用必须以任务证据为界
    • 情绪分析、文本匹配、信息提取和摘要都是候选任务,须确定版本、基线、人工标签、评估集与失败案例。
    • 本章只完成检索—引用—拒答练习,不把尚未运行的外部模型示例当作分析结果。
  2. 实践途径灵活多样
    • 网页交互、API 或私有化实际使用都须比较许可、隐私、总成本、版本漂移、可检查性与退出方案,不能预设性能优势。

思考与讨论

  1. 模型选择:在金融文本分析任务中,您认为从头预训练、领域微调和直接使用 API 这三种方法,各自的优劣是什么?(请从成本、性能、数据隐私、开发周期等角度考虑)
  2. 应用畅想:除了本章提到的应用,您还能想到哪些创新的方式,可以在金融市场中使用大语言模型?
  3. 风险与挑战:使用大语言模型进行金融分析(甚至交易决策)可能存在哪些风险?(例如,模型产生“幻觉”、数据偏见、过度拟合等)

思考题参考答案与评价评分标准

  1. 模型选择:从头预训练控制力最高但数据、算力与治理成本最大;领域微调适合有合法内部语料且任务稳定的机构;API 开发快但存在数据出境、供应商确定、版本漂移与成本不确定。高质量答案必须同时比较性能、总成本、隐私、可检查性和退出方案。
  2. 应用示例:财报要素抽取、公告差异比对、合规检索、研究报告证据定位均可行;每项应用都应给出人工核对点和可量化指标,不能把生成文本直接当事实。
  3. 风险答案:至少覆盖幻觉、提示注入、训练语料偏差、时间知识滞后、不可重复、敏感信息泄露和自动化交易放大风险。合格控制包括固定模型版本、离线基准集、引用检查、权限隔离、日志、人工审批和回滚机制。

评分:问题定义 20%,证据与指标 25%,隐私合规 20%,失败模式 20%,复现与人工核对设计 15%。

Q & A

感谢聆听

理论部分参考文献

Vaswani, Ashish, Noam Shazeer, 和 Niki Parmar. 2017年. 《Attention Is All You Need》. Advances in Neural Information Processing Systems 30. https://arxiv.org/abs/1706.03762.

阶段小结

  • 必修能力:运行“检索—引用—拒答”练习并读懂结构化输出。
  • 可选拓展:生成式 LLM API 不作为本章学习要求。
  • 外部模型记录:版本、提示词、温度、输出字段、人工评估集、费用与错误类型。