MCM520 ← 资料站首页 上市公司财报「智能问数」助手:指标字典、规则解析与双层评估(优秀范文一) 打开交互阅读器 →

上市公司财报「智能问数」助手:指标字典、规则解析与双层评估(优秀范文一)

摘要:本文面向财报「智能问数」赛题,在会计恒等自洽的合成报表(8 家行业参数化公司 × 3 会计年度,恒等式残差 0.00e+00)上,完成了从指标字典构建、标注问句集设计到规则解析器三版本迭代的完整闭环。将毛利率、资产负债率等 8 项指标定义为「公式 + 别名」元数据,构造 120 条三级难度 × 四类意图的标注问句(L1 标准 48 / L2 同义 42 / L3 相对时间 30;查询 64 / 对比 22 / 排名 12 / 趋势 22),并以「抽取准确率 + 计算正确率」双层口径评估解析器 v1→v2→v3 的迭代:基线 v1 因别名词典残缺与相对时间失能仅达 65.8% / 47.5%;v2 补全 3 组口语别名修复全部 11 条指标错误(75.0% / 56.7%);v3 引入相对时间消歧后抽取准确率达 100%、计算正确率触及口径上限 81.7%。归因分析表明:仅 3 个词的时间词表即挽回 30 条问句(占总集 25%),是投入产出比最高的改进;而对比类问句若按「首个命中实体单点查值」评估,会因长别名优先匹配颠倒实体顺序产生 6 条假阴性,改为无序集合口径后消除。全文纯标准库实现、固定种子可复现,正文、配图、附录、真源四路数字一致。

关键词:智能问数;指标字典;槽位抽取;相对时间消歧;双层评估;规则解析器

一、问题重述

财报问数的本质是把自然语言问句翻译为「公司—年度—指标」三元组上的确定性计算。赛题要求:

  1. 构建财报指标字典与统一计算口径;
  2. 对用户问句做实体(公司)、指标、时间的识别与意图分类;
  3. 把解析结果转为可执行的取数计算,支持单点查值、双司对比、行业排名与趋势问答;
  4. 从「抽取是否正确」与「计算是否正确」两层评估准确率,并给出错误案例分析。

二、模型假设

  1. 行业参数化(基准营收、毛利水平、杠杆、增速)的合成报表能代表真实财报的数值结构与指标量级,且会计恒等自洽可排除脏数据对解析器评测的干扰;
  2. 指标口径一经字典固定即在全部问句中一致,不存在同一名称多种算法的歧义;
  3. 标注问句集按「难度 × 意图」交叉设计,其语言面(标准直述、同义替换、相对时间)覆盖目标用户的主要表达习惯;
  4. 在公司、指标、时间三类槽位均封闭的前提下,最长匹配规则解析器可达到抽取上限,无需引入统计模型。

三、符号说明

符号 含义
(name,y)(name,y) 公司—年度定位的报表记录,含 11 个科目字段(亿元)
mm 指标名,取自 8 项指标字典
V(m;name,y)V(m;name,y) 指标取值函数(由字典公式唯一确定)
q=⟨cos,y,m,k⟩q=\langle cos,y,m,k\rangle 标注问句:实体列表、年度、指标、意图 k∈{Q,C,R,T}k\in\{Q,C,R,T\}
ext(q)\mathrm{ext}(q) 解析器返回的三元组(命中实体列表,年份,指标)

四、合成财报与指标字典(问题 1)

数据生成:以 8 家虚构公司为载体,每家由五元组(基准营收、毛利率、资产负债率、增速)参数化行业属性——南岭白酒代表高毛利低杠杆的消费龙头(320 亿基数、91% 毛利、24% 杠杆),燕山钢铁代表低毛利高杠杆的重资产行业(410 亿、11%、68%)。逐年营收按复利增长叠加 σ=2% 的随机扰动,成本、费用、资产、负债等科目依次由条件分布生成,并强制满足资产 = 负债 + 权益的会计恒等式,抽样校验残差为 0.00e+00。选择合成而非真实数据的理由有三:真实财报存在口径分歧与缺漏,会污染「解析器」这一被评对象;恒等自洽保证任何指标计算都落在干净数据上;固定种子使 120 条问句的标准答案永久可复现。

图1 财报问数助手三层管线

整体管线如图 1:问句经槽位解析得到三元组,指标引擎按字典公式取数计算,答案组装层再按意图分别输出单值、对比、排名或趋势。图 2 给出 2023 年八家公司营收(141.5~449.9 亿元),图 3 以热力图呈现四项百分数指标的横截面画像:南岭白酒以 93.0% 毛利率、61.8% ROE、23.0% 资产负债率居盈利质量之首,申城微电(46.7% / 32.1% / 35.9%)与江浙医药(63.7% / 24.4% / 28.8%)居中,重资产梯队垫底——行业梯度符合财务常识,侧面验证了参数化生成的合理性。

图2 八家公司 2023 年营业收入

图3 行业财务画像热力图

指标字典把 8 项指标定义为「公式 + 别名列表」的元数据:毛利率 =(rev−cost)/rev=(rev-cost)/rev、净利率 =net/rev=net/rev、资产负债率 =liab/asset=liab/asset、流动比率 =ca/cl=ca/cl、净资产回报率 =net/equity=net/equity、存货周转率 =cost/inv=cost/inv、净现比 =ocf/net=ocf/net、营收增长率 =revt/revt−1−1=rev_t/rev_{t-1}-1。每个指标除标准名外登记口语别名(如「负债率」「杠杆率」「ROE」),解析器只认字典不认临场发挥,从根上杜绝同名异算。排名类问题的标准答案由全行业 argmax 生成:2023 年毛利率与 ROE 最高均为南岭白酒(93.0 与 61.8),营收增长率最高为东江动力(33.8%)。

五、标注问句集构建

问句集的质量决定评估的可信度。我们按「三级难度 × 四类意图」交叉设计 120 条(图 4):难度维从 L1 标准直述(「南岭白酒2023年毛利率是多少」)、L2 同义替换(简称 + 口语别名 + 语序倒装,「帮我查下南酒在2022年的毛利」)到 L3 相对时间(「北乳去年资产负债率是多少」);意图维覆盖 Q 单点查询 64 条、C 双司对比 22 条、R 行业排名 12 条、T 三年趋势 22 条。每条问句携带完整标注 ⟨companies,y,m,k⟩\langle companies, y, m, k\rangle,实体存为列表而非标量——这为后文按意图差异化判定埋下伏笔。生成过程完全确定(编号取模轮转),仅以固定种子做一次整体乱序,保证任何人重跑得到的问句序列逐条一致。

图4 标注问句集交叉构成

六、解析器设计与三版本迭代

解析采用「最长匹配优先」的槽位填充:公司别名与指标别名各自按长度降序扫描问句文本,先命中先得,规避「南酒」被「南岭白酒」的前缀截断;时间槽位先查相对时间词表、再匹配四位年份字面量。在此骨架上迭代三个版本:

  • v1 冷启动基线:故意只登记 13 个标准别名,缺失「毛利」「负债率」「增长率」三组最口语化的说法(图 5 左),且不做相对时间消歧——复刻一个未经语料打磨的系统原型;
  • v2 补全别名词典:别名扩至 16 个(图 5 右),其余逻辑不变,用于隔离度量「词汇覆盖」的单项贡献;
  • v3 时间消歧:在 v2 基础上加入相对时间词表(去年→2023、前年→2022、今年→2024,以提问基准年为锚),并把「近三年」识别为区间语义(multi),供趋势类问题使用。

图5 指标别名词典覆盖对比

值得强调的是实体判定的意图分化:对比类问句要求命中实体集合与标注相等(顺序无关),查询/趋势类要求单一精确命中,排名类问句文本本身不含公司实体,跳过实体维度只验指标与时间。一刀切地要求「命中数等于 2」或「命中数等于 0」都会把合法问句误杀。

七、双层评估与错误分析(问题 2–4)

评估口径是本文的核心设计。第一层抽取准确率覆盖全部 120 条:解析结果的三元组须与标注逐一相符(按上述意图分化规则)。第二层计算正确率仍以 120 为公共分母,但只有抽取成功的非趋势问句进入计算:Q/C 类执行无序集合查值(系统对命中实体逐一取数,与标注实体的取值集合比较),R 类执行全行业 argmax 后比对冠军公司,T 类趋势题需多点多值呈现、超出规则解析器的职责边界,仅在抽取层参评——因此计算正确率的理论上限为 (120−22)/120=81.7%(120-22)/120=81.7\%,两层之差恒为趋势类占比 18.3%,这是口径分层的结果而非系统缺陷,却让两个数字可以在同一标尺上直接对比版本迭代。

版本 改动 抽取准确率 计算正确率 错误分布(实体/指标/时间)
v1 13 别名,无时间消歧 65.8% 47.5% 0 / 11 / 30
v2 别名补至 16 75.0% 56.7% 0 / 0 / 30
v3 +相对时间消歧 100.0% 81.7% 0 / 0 / 0

三版本阶梯(图 6)的归因十分干净:v1→v2 的 +9.2 个百分点全部来自 L2 同义替换题——11 条含「毛利/增长率」口语名的问句由错转对,指标类错误清零;v2→v3 的 +25 个百分点全部来自 L3——30 条相对时间问句被三个词的时间词表整体挽回,时间类错误清零。错误类型的此消彼长(图 7)与分难度的爬坡曲线(图 8)共同印证了改动与效果的一一对应:L1 全程 100%(标准直述对任何版本都不构成挑战)、L2 从 73.8% 一步到位、L3 从 0% 直线拉满。

图6 三版本双层评估对比

图7 三版本错误类型分布

图8 分难度抽取准确率迭代曲线

一个反例复盘:初版评估曾以「首个命中实体单点查值」评判 C 类计算,结果 v3 计算正确率只有 68.3%——排查发现 22 条对比题中有 6 条假阴性。根源在于最长匹配按别名长度降序扫描,问句「南酒跟北疆乳业相比」会先命中 4 字全名再命中 2 字简称,命中顺序与问句语序相反,导致系统取的是 B 公司的值而标注主体是 A 公司。这不是解析错误(实体集合完全正确),而是评估口径对无关紧要的顺序信息过敏。改为无序集合查值后假阴性清零。教训:为确定性系统设计评估时,应把「不影响答案正确性的中间状态」(此处为实体填充顺序)排除在判定之外,否则评估器自身成为最大的误差源。

八、灵敏度与讨论

口径敏感性:若坚持有序单点口径,v3 计算正确率将由 81.7% 降至 68.3%,且降幅与解析质量无关——纯粹由问句语序与词典长度的相对关系决定,属于评估噪声;若让 T 类强行计入计算分母,任何规则系统的得分都会被钉死在低位,因为「三年趋势」的正确答案本就不是单个数字。可见评估口径的选择能在 ±13 个百分点幅度内改写成绩,其重要性不亚于解析器本身的改进。

词典规模的边际效应:v2 仅新增 3 组别名就吃掉了全部指标类错误——封闭域内词汇面有限,词典法的天花板很高;但也正因如此,其失效模式是脆性的:未登录的口语变体(「赚多少」「负债什么水平」)将直接落空,生产系统应以语义相似度模型兜底长尾表达,把词典留给高频主干。

九、模型优缺点

优点:① 指标字典集中管理口径,问数结果天然可解释、可追溯;② 三版本受控迭代把每一分提升归因到单一改动,改进路径无歧义;③ 双层评估配合意图分化判定,能区分「听不懂」与「算不对」两类性质迥异的故障。

缺点:① 规则解析依赖封闭词表,开放域口语泛化能力弱;② 合成报表的行业参数为手工设定,极端财务结构(如负净资产导致 ROE 失效)未覆盖;③ 排名类仅实现 argmax 单一聚合,Top-K 与排序陈述尚未纳入评估。

十、结论

本文在恒等自洽的合成财报上构建了完整的问数评测闭环:8 项指标字典、120 条三级难度标注问句、规则解析器三版本受控迭代。结果显示,冷启动基线抽取准确率 65.8%,补全别名词典与相对时间消歧两项针对性改进将其推至 100%,计算正确率同步触及 81.7% 的口径上限;归因分析与错误复盘进一步表明,评估口径的设计精度直接决定系统能否被正确度量——先修口径、再做系统,是智能问数类赛题区别于传统预测题的关键方法论。

参考文献

[1] 泰迪杯数据挖掘挑战赛组委会. 2026 年泰迪杯数据挖掘挑战赛 B 题:上市公司财报「智能问数」助手[Z]. 2026.

[2] 财政部. 企业会计准则——基本准则[S]. 2014 修订.

[3] Jurafsky D, Martin J H. Speech and Language Processing[M]. 3rd ed. Prentice Hall, 2023.

[4] 张华平, 等. 自然语言处理原理与技术实现[M]. 北京: 电子工业出版社, 2017.

附录:核心 Python 实现

# -*- coding: utf-8 -*-
"""财报智能问数核心实现(与真源 gen_tidy2026b.py 随机流逐调用等价)"""
import random

SEED = 20260901
YEARS = [2021, 2022, 2023]
BASE_YEAR = 2024
COMPANIES = [
    ("南岭白酒", ["南岭白酒", "南酒"], 320.0, 0.91, 0.24, 0.16),
    ("申城微电", ["申城微电", "微电"], 210.0, 0.46, 0.36, 0.22),
    ("江浙医药", ["江浙医药", "江药"], 185.0, 0.62, 0.30, 0.14),
    ("北疆乳业", ["北疆乳业", "北乳"], 140.0, 0.33, 0.42, 0.09),
    ("东江动力", ["东江动力", "东动"], 260.0, 0.27, 0.55, 0.31),
    ("岭南家电", ["岭南家电", "岭家"], 175.0, 0.24, 0.60, 0.07),
    ("川蜀重工", ["川蜀重工", "重工"], 230.0, 0.19, 0.66, 0.11),
    ("燕山钢铁", ["燕山钢铁", "燕钢"], 410.0, 0.11, 0.68, -0.04),
]
ALIAS2CO = {}
for nm, als, *_ in COMPANIES:
    for a in als:
        ALIAS2CO[a] = nm
METRICS = {
    "毛利率": dict(aliases=["毛利率", "毛利"]),
    "净利率": dict(aliases=["净利率", "净利"]),
    "资产负债率": dict(aliases=["资产负债率", "负债率", "杠杆率"]),
    "流动比率": dict(aliases=["流动比率"]),
    "净资产回报率": dict(aliases=["净资产回报率", "ROE", "roe"]),
    "存货周转率": dict(aliases=["存货周转率", "周转率"]),
    "净现比": dict(aliases=["净现比"]),
    "营收增长率": dict(aliases=["营收增长率", "增长率"]),
}
V1_ALIASES = {}
for m, d in METRICS.items():
    keep = [a for a in d["aliases"]
            if (m, a) not in [("毛利率", "毛利"), ("资产负债率", "负债率"),
                              ("营收增长率", "增长率")]]
    for a in keep:
        V1_ALIASES[a] = m
FULL_ALIASES = {}
for m, d in METRICS.items():
    for a in d["aliases"]:
        FULL_ALIASES[a] = m
REL_TIME = {"去年": BASE_YEAR - 1, "前年": BASE_YEAR - 2,
            "今年": BASE_YEAR}


def gen_fin():
    """会计恒等自洽的合成报表:(name, year) -> 科目字典,单位亿元。"""
    rng = random.Random(SEED)
    fin = {}
    for name, _als, base, gm, dr, growth in COMPANIES:
        for k, y in enumerate(YEARS):
            rev = base * (1 + growth) ** k * rng.gauss(1.0, 0.02)
            gm_y = min(0.95, max(0.05, gm * rng.gauss(1.0, 0.02)))
            cost = rev * (1 - gm_y)
            fee = rev * rng.uniform(0.06, 0.12)
            net = (rev - cost - fee) * 0.75
            asset = rev / rng.uniform(0.35, 0.85)
            liab = asset * dr * rng.gauss(1.0, 0.03)
            equity = asset - liab
            ca = asset * rng.uniform(0.25, 0.55)
            cl = liab * rng.uniform(0.45, 0.75)
            inv = cost / rng.uniform(2.5, 7.0)
            ocf = net * rng.uniform(0.8, 1.35)
            fin[(name, y)] = dict(rev=rev, cost=cost, fee=fee, net=net,
                                  asset=asset, liab=liab, equity=equity,
                                  ca=ca, cl=cl, inv=inv, ocf=ocf)
    return fin


def metric_value(fin, name, y, m):
    d = fin[(name, y)]
    if m == "毛利率":
        return (d["rev"] - d["cost"]) / d["rev"] * 100
    if m == "净利率":
        return d["net"] / d["rev"] * 100
    if m == "资产负债率":
        return d["liab"] / d["asset"] * 100
    if m == "流动比率":
        return d["ca"] / d["cl"]
    if m == "净资产回报率":
        return d["net"] / d["equity"] * 100
    if m == "存货周转率":
        return d["cost"] / d["inv"]
    if m == "净现比":
        return d["ocf"] / d["net"]
    if m == "营收增长率":
        prev = YEARS[max(0, YEARS.index(y) - 1)]
        if prev == y:
            return None
        return (d["rev"] / fin[(name, prev)]["rev"] - 1) * 100


def industry_top(fin, y, m):
    best = None
    for nm, _als, *_r in COMPANIES:
        v = metric_value(fin, nm, y, m)
        if v is not None and (best is None or v > best[0]):
            best = (v, nm)
    return best[1]


def gen_questions():
    """120 条标注问句:L1 标准 48 / L2 同义 42 / L3 相对时间 30。"""
    rng = random.Random(SEED)
    qs = []
    names = [c[0] for c in COMPANIES]
    short = {c[0]: c[1][1] for c in COMPANIES}
    ms = list(METRICS)

    def add(level, text, cos, year, m, intent):
        qs.append(dict(level=level, text=text,
                       company=(cos[0] if cos else None),
                       companies=cos, year=year, metric=m, intent=intent))

    for i in range(48):
        co, m = names[i % 8], ms[i % 8]
        y = YEARS[i % 3]
        if m == "营收增长率" and y == 2021:
            y = 2022
        intent = ["Q", "C", "R", "T"][i % 4]
        if intent == "Q":
            add("L1", "%s%d年%s是多少" % (co, y, m), [co], y, m, "Q")
        elif intent == "C":
            b = names[(i + 3) % 8]
            add("L1", "对比%s和%s%d年的%s" % (co, b, y, m), [co, b],
                y, m, "C")
        elif intent == "R":
            add("L1", "%d年%s最高的公司是哪家" % (y, m), [], y, m, "R")
        else:
            add("L1", "%s近三年%s趋势如何" % (co, m), [co], y, m, "T")
    for i in range(42):
        co = names[(i * 3 + 1) % 8]
        m = ms[(i * 5 + 2) % 8]
        y = YEARS[(i + 1) % 3]
        if m == "营收增长率" and y == 2021:
            y = 2022
        alias_m = METRICS[m]["aliases"][-1]
        intent = ["Q", "Q", "C", "T"][i % 4]
        if intent == "Q" and i % 2 == 0:
            add("L2", "%s的%d年%s呢" % (short[co], y, alias_m),
                [co], y, m, "Q")
        elif intent == "Q":
            add("L2", "帮我查下%s在%d年的%s" % (short[co], y, alias_m),
                [co], y, m, "Q")
        elif intent == "C":
            b = names[(i * 3 + 5) % 8]
            add("L2", "%s跟%s相比,%d年谁的%s更高"
                % (short[co], b, y, alias_m), [co, b], y, m, "C")
        else:
            add("L2", "说说%s这三年的%s变化" % (short[co], alias_m),
                [co], y, m, "T")
    rels = [("去年", BASE_YEAR - 1), ("前年", BASE_YEAR - 2)]
    for i in range(30):
        co = names[(i * 7 + 2) % 8]
        m = ms[(i * 3 + 4) % 8]
        rw, ry = rels[i % 2]
        add("L3", "%s%s%s是多少" % (short[co], rw, m), [co], ry, m, "Q")
    rng.shuffle(qs)
    return qs


def parse(q, aliases, resolve_time):
    text = q["text"]
    found = []
    for a, nm in sorted(ALIAS2CO.items(), key=lambda t: -len(t[0])):
        if a in text and nm not in found:
            found.append(nm)
    m = None
    for a, mm in sorted(aliases.items(), key=lambda t: -len(t[0])):
        if a in text:
            m = mm
            break
    y = None
    if resolve_time:
        for w, yy in REL_TIME.items():
            if w in text:
                y = yy
                break
        if y is None and "近三年" in text:
            y = "multi"
        if y is None:
            for yy in YEARS:
                if str(yy) in text:
                    y = yy
                    break
    else:
        hit = False
        for yy in YEARS:
            if str(yy) in text:
                y = yy
                hit = True
        if not hit:
            return found, None, m, "year"
    if q["intent"] == "T":
        y = "multi"
    err = None
    if q["intent"] in ("Q", "T") and len(found) != 1:
        err = "entity"
    elif q["intent"] == "C" and len(found) != 2:
        err = "entity"
    elif m is None:
        err = "metric"
    elif y is None:
        err = "year"
    return found, y, m, err


def evaluate(fin, questions, aliases, resolve_time):
    n_ext = n_ans = 0
    errs = {"entity": 0, "metric": 0, "year": 0}
    for q in questions:
        found, y, m, err = parse(q, aliases, resolve_time)
        if q["intent"] == "C":
            ok_co = sorted(found) == sorted(q["companies"])
        else:
            ok_co = found == q["companies"]
        ok_ext = (ok_co and m == q["metric"]
                  and (q["intent"] == "T" or y == q["year"]))
        if ok_ext:
            n_ext += 1
        else:
            errs[err or "year"] += 1
        if not ok_ext or q["intent"] == "T":
            continue
        if q["intent"] == "R":
            if (industry_top(fin, y, m)
                    == industry_top(fin, q["year"], q["metric"])):
                n_ans += 1
        else:
            vals = set()
            for c2 in found:
                vv = metric_value(fin, c2, y, m)
                if vv is not None:
                    vals.add(round(vv, 9))
            refs = set()
            for c2 in q["companies"]:
                rv = metric_value(fin, c2, q["year"], q["metric"])
                if rv is not None:
                    refs.add(round(rv, 9))
            if vals and vals == refs:
                n_ans += 1
    n = len(questions)
    return n_ext / n * 100.0, n_ans / n * 100.0, errs


def main():
    fin = gen_fin()
    d = fin[("南岭白酒", 2023)]
    chk = abs(d["asset"] - d["liab"] - d["equity"])
    print("南岭白酒 2023 营收%.1f亿 毛利率%.1f%% 资产负债率%.1f%% "
          "ROE%.1f%%" % (d["rev"], metric_value(fin, "南岭白酒", 2023,
                                                "毛利率"),
                         metric_value(fin, "南岭白酒", 2023, "资产负债率"),
                         metric_value(fin, "南岭白酒", 2023,
                                      "净资产回报率")))
    d2 = fin[("申城微电", 2023)]
    print("申城微电 2023 营收%.1f亿 毛利率%.1f%% 资产负债率%.1f%% "
          "ROE%.1f%%" % (d2["rev"], metric_value(fin, "申城微电", 2023,
                                                 "毛利率"),
                         metric_value(fin, "申城微电", 2023, "资产负债率"),
                         metric_value(fin, "申城微电", 2023,
                                      "净资产回报率")))
    d3 = fin[("江浙医药", 2023)]
    print("江浙医药 2023 营收%.1f亿 毛利率%.1f%% 资产负债率%.1f%% "
          "ROE%.1f%%" % (d3["rev"], metric_value(fin, "江浙医药", 2023,
                                                 "毛利率"),
                         metric_value(fin, "江浙医药", 2023, "资产负债率"),
                         metric_value(fin, "江浙医药", 2023,
                                      "净资产回报率")))
    print("恒等式校验 %.2e" % chk)
    top_g = industry_top(fin, 2023, "毛利率")
    top_r = industry_top(fin, 2023, "净资产回报率")
    top_o = industry_top(fin, 2023, "营收增长率")
    print("2023 毛利率最高 %s (%.1f)" % (top_g, metric_value(
        fin, top_g, 2023, "毛利率")))
    print("2023 净资产回报率最高 %s (%.1f)" % (
        top_r, metric_value(fin, top_r, 2023, "净资产回报率")))
    print("2023 营收增长率最高 %s (%.1f)" % (
        top_o, metric_value(fin, top_o, 2023, "营收增长率")))
    qs = gen_questions()
    lv = [sum(1 for q in qs if q["level"] == L) for L in ("L1", "L2", "L3")]
    it = {k: sum(1 for q in qs if q["intent"] == k)
          for k in ("Q", "C", "R", "T")}
    print("问句集 %d 条 难度 L1/L2/L3=%s 意图 Q/C/R/T=%s"
          % (len(qs), lv, [it[k] for k in ("Q", "C", "R", "T")]))
    for ver, (al, rt) in (("v1", (V1_ALIASES, False)),
                          ("v2", (FULL_ALIASES, False)),
                          ("v3", (FULL_ALIASES, True))):
        e, a, errs = evaluate(fin, qs, al, rt)
        print("%s 抽取准确率 %.1f%% 计算正确率 %.1f%% 错误 %s"
              % (ver, e, a, errs))
    for ver, (al, rt) in (("v1", (V1_ALIASES, False)),
                          ("v3", (FULL_ALIASES, True))):
        row = []
        for L in ("L1", "L2", "L3"):
            sub = [q for q in qs if q["level"] == L]
            e = evaluate(fin, sub, al, rt)[0]
            row.append(e)
        print("%s 分难度抽取 %s" % (ver, ", ".join(
            "%s=%.1f%%" % z for z in zip(("L1", "L2", "L3"), row))))


if __name__ == "__main__":
    main()