首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >孩子粗心错题多,根因是什么?知识图谱LCA归因实现

孩子粗心错题多,根因是什么?知识图谱LCA归因实现

原创
作者头像
错题透镜
发布于 2026-09-26 12:29:36
发布于 2026-09-26 12:29:36
320
举报
文章被收录于专栏:AI教育AI教育
封面
封面

9 月 21 日央视网讨论「人工智能+教育,如何加得更好」:AI 进课堂的速度很快,但家长要的不是孩子多一个查答案的入口,而是工具能回答那个真正的问题——孩子粗心错题多,根因是什么1。「粗心」在辅导语境里是最高频、也最无用的标签:它笼统、不可执行、无法转化成练习动作。对工程侧来说,这是一个可以严格定义的命题:给定多道被标注为「粗心」的错题,输出它们共同指向的具体知识漏洞。下文是一套在生产环境验证过的完整实现。


一、问题定义:「粗心」标签为什么必须被拆掉

输入是聚类管线给出的错题簇:k 道题(实践中 k≈3~30),每题带有初步错因标注——「粗心」「看错题」「算错」这类口语标签;输出是一个图谱节点形式的知识漏洞,外加支撑证据题列表。

把这些口语标签直接拿来做交集,行不通,原因有二:

  1. 口语标签是表象层描述:两道都叫「粗心」的题,一道是进位规则没掌握,一道是单位换算没建立连接——表象相同,根因相距十万八千里;
  2. 口语标签没有层级:它说不清漏洞在知识体系中的位置,是概念级断层还是执行级疏漏,对应的辅导动作完全不同。

层级化记忆研究里有个反复被验证的现象:概念在语义网络中越靠上层,提取越轻松,但能带出来的细节也越少(Collins & Quillian, 1969)2。「共同根因」因此天然存在多个层级的候选,必须挑一个信息量适中的层级输出。所以第一步不是算交集,而是把口语标签翻译成图谱上的具体节点——这一步做完,问题就从 NLP 难题变成了图上定位问题。


二、错因映射:把「粗心」翻译成图谱节点

知识组织沿用「章节 → 知识点 → 条件/性质」的骨架加前驱边,整体必须是 DAG 而非树:同一条法则可以从两条章节路径到达——「绝对值比较法则」既通向「有理数的大小比较」,也通向「绝对值」:

代码语言:txt
复制
(:Chapter {name: "有理数"})
    └─(:KnowledgeNode {name: "有理数的大小比较"})
          └─(:Condition {name: "绝对值比较法则"})

(:Chapter {name: "数轴与绝对值"})
    └─(:KnowledgeNode {name: "绝对值"})
          └─(:Condition {name: "绝对值比较法则"})

翻译层的要害是归因等级与图谱层级的对齐。三层归因体系里,Level 1 概念核心对应知识点层的法则类节点(「负数比大小要颠倒绝对值关系」),Level 2 知识断层对应条件/性质层的连接类节点(「绝对值」与「数轴上的距离」之间的连接),执行层问题则落到习惯与规范标签——层级对齐后,后续定位结果才可直接转译成辅导动作。

代码语言:python
复制
CAUSE_NODE_MAP = {
    '粗心':   None,          # 禁止直接映射——口语标签必须先经过归因细分
    '算错':   '运算规则类节点',
    '没掌握': '知识点层节点',
    '看错题': '审题习惯标签',
}

def map_cause_to_node(cause: str, attribution: dict) -> str:
    """口语错因 → 图谱节点:优先用归因等级,拒绝透传口语标签。"""
    if level := attribution.get('level'):
        return attribution['node']        # Level 1/2 已由归因管线挂到具体节点
    raise ValueError(f'口语标签 [{cause}] 未经过归因细分,拒绝入库')

这道防线的价值在于:「粗心」永远不允许作为节点进入图谱。它只能是被拆解前的症状,不能是归因的终点。错题与节点是多对多关系,一张倒排表 (question_id, node, source) 承载,source 区分归因管线与人工复核,供支持度加权。


三、核心算法:祖先闭包交集 + 信息量打分

多道错题的共同根因,在图上的严格定义是:被全体(或近全体)错题的标签闭包覆盖、且信息量最大的最近公共祖先。DAG 上公共祖先是一个集合而非单点,需要自定义选择标准,工程实现三步:

第 1 步,铺闭包:把每道错题映射到的节点连同其全部祖先摊开成集合,一道题一个集合;

第 2 步,数票数:k 个集合逐节点计票,得票率不低于 τ=0.8 的节点才有资格当选——留两成余量吸收偶发的误打标;

第 3 步,比信息量:得票过线的候选里必然混着「数的运算」这类什么都能解释的泛节点——借鉴 Resnik 的信息量度量 4:IC(node) = -log p(node),p 用节点在历史标注路径中的出现频率估计,谁的 IC 高谁当选。

代码语言:python
复制
import math
from collections import deque

def build_closures(parent_of: dict[str, list[str]]) -> dict[str, frozenset]:
    """全图祖先闭包:逐节点迭代展开,命中缓存即合并,无递归栈。"""
    cache: dict[str, frozenset] = {}

    def closure_of(start: str) -> frozenset:
        seen: set[str] = set()
        queue = deque(parent_of.get(start, ()))
        while queue:
            cur = queue.popleft()
            if cur in seen:
                continue
            seen.add(cur)
            up = cache.get(cur)
            if up is not None:
                seen |= up              # 缓存命中,整段祖先直接并入
            else:
                queue.extend(parent_of.get(cur, ()))
        return frozenset(seen)

    for node in parent_of:
        cache[node] = closure_of(node)
    return cache

def pick_root(closure_sets, ic_table, floor: float = 0.8) -> dict:
    """k 道错题的闭包集合逐节点计票,票数过线者按信息量取最大。"""
    votes: dict[str, int] = {}
    for cs in closure_sets:
        for n in cs:
            votes[n] = votes.get(n, 0) + 1
    total = len(closure_sets)
    pool = {n: c / total for n, c in votes.items() if c / total >= floor}
    if not pool:
        return {'verdict': 'unsolved', 'shortlist': []}
    root = max(pool, key=lambda n: (ic_table.get(n, 0.0), pool[n]))
    return {'verdict': 'solved', 'root': root, 'vote': pool[root]}

用一组贴近真实结构的示例数据跑一遍(有理数错题簇,混入一道噪声题):

代码语言:python
复制
run_log = {
    'cluster': 'CL-20260918-11',               # 上游聚类送来的错题簇
    'members': [                                # 各题经映射层落到的图谱节点
        {'id': 'T01', 'nodes': ['负数比较大小', '绝对值与符号']},
        {'id': 'T02', 'nodes': ['数轴上的大小比较']},
        {'id': 'T03', 'nodes': ['一元一次方程']},    # 误聚入簇的噪声题
        {'id': 'T04', 'nodes': ['负数比较大小', '数轴上的大小比较']},
        {'id': 'T05', 'nodes': ['绝对值与符号']},
    ],
    'scored': [                                 # 票数过线后的打分候选
        {'node': '有理数的大小比较', 'coverage': 0.8, 'ic': 1.97, 'depth': 2, 'ok': True},
        {'node': '数的认识',        'coverage': 1.0, 'ic': 0.29, 'depth': 1, 'ok': False,
         'why': '信息量过低,结论过泛'},
        {'node': '一元一次方程',    'coverage': 0.2, 'ic': 2.55, 'depth': 3, 'ok': False,
         'why': '覆盖率不足 0.8'},
    ],
    'verdict': {'root': '有理数的大小比较', 'evidence': ['T01', 'T02', 'T04', 'T05']},
}

输出里的 evidence 字段是给下游的绳索:共同根因必须能拽回到具体题目。家长看到的因此不是一句「孩子粗心」,而是「这几道题共同指向有理数的大小比较,证据如下」。


四、工程账本:三种方案的复杂度对比

方案

核心原理

在线复杂度

主要失效场景

口语标签计数

「粗心」出现几次就报几次

O(k)

只能产出表象复述,无辅导价值

向量质心 + ANN

簇向量求均值找最近考点

O(log V) 检索

质心落入无考点区域,结果不可解释

图谱 LCA + IC(本方案)

闭包计票 + 信息量排序

O(k·

闭包

),亚毫秒

断链/多父需治理(见下节)

开销大头可以全部挪到离线:万级节点的闭包缓存一次预计算,Python 单进程百毫秒量级即可建完;图谱更新时只重算受影响节点的后代闭包,增量代价很小。线上要做的只剩集合运算——k 道题、每题平均 20 个闭包节点,单次合并微秒到亚毫秒级,比一趟向量检索还轻。树上 LCA 的 O(1) 经典解法(Bender & Farach-Colton, 2000)3 在多父 DAG 上并不适用,但这套计票方案的在线开销已经足够低,不值得为它引入树约束。


五、踩坑记录:归因等级漂移与断链治理

1. 归因等级挂错层,辅导动作跟着错。Level 1 的法则漏洞被挂到 Level 2 的连接节点上,输出会变成「补概念连接」,而孩子真正缺的是法则本身。治理:归因管线输出必须携带层级校验位,入库时节点 depth 与归因等级强一致校验,不一致进人工队列。

2. 多父节点让排序结果抖动。同一个知识点同时挂两条父链时,候选排名会在两次运行间互换。处理方式是把排序键写死——信息量第一、覆盖率第二、深度第三——并列候选全部透传给下游做二次排序,绝不在静默状态下替用户拍板。

3. 断边放大过泛结论。新知识点录入时漏挂前驱边,闭包会一路顶到「初中数学」这种根节点,输出一句永远正确的废话。防线是监控:候选深度小于 2 即告警,并降级为输出证据清单,不轻易给出单点结论。

4. 样本太小不硬算。簇内只有 2 道题时,覆盖率只有 0.5 和 1.0 两档,阈值形同虚设。这样的簇不做硬归因:给出前两名候选连同各自证据,解释文案并列呈现,把最终裁决留给人。


六、总结:把「多题一起看」做成系统的默认动作

多道错题的共同根因定位,落地时就是三个工程决策:口语标签挡在图谱门外、知识组织 DAG 化、信息量压制过泛结论。归因定级、LCA 定点,整条链路每一步可回溯——这是把错题直接丢给大模型「总结共同考点」的生成式方案给不了的确定性,后者答案每次都变、无法取证,本质上是用幻觉换省事。

这套逻辑我们已经落地进错题透镜的多题归因链路:家长导入 3 道错题,系统输出的不是三份订正清单,而是一个收敛的知识漏洞和对应练习方向——「多题一起看」由此成为系统默认动作,而不是要求家长自己有耐心翻错题本。对开发者读者,本文闭包加打分的实现不过百余行代码,建议先在自己业务里跑通再谈优化;下一步我们计划把 IC 的频率估计改成按学期滚动重估,学期初样本少,p 值失真会系统性拉高过泛节点的排名。


参考文献

1 央视网. 《人工智能进课堂 记者观察:人工智能+教育 如何加得更好》. 央视网, 2026-09-21

2 Collins, A. M., & Quillian, M. R. (1969). Retrieval Time from Semantic Memory. Journal of Verbal Learning and Verbal Behavior, 8(2), 240-247. DOI:10.1016/S0022-5371(69)80069-1

3 Bender, M. A., & Farach-Colton, M. (2000). The LCA Problem Revisited. LATIN 2000: Theoretical Informatics, LNCS 1776, 88-94. DOI:10.1007/10719839_27

4 Resnik, P. (1995). Using Information Content to Evaluate Semantic Similarity in a Taxonomy. Proceedings of IJCAI-95, 448-453.


相关阅读

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、问题定义:「粗心」标签为什么必须被拆掉
  • 二、错因映射:把「粗心」翻译成图谱节点
  • 三、核心算法:祖先闭包交集 + 信息量打分
  • 四、工程账本:三种方案的复杂度对比
  • 五、踩坑记录:归因等级漂移与断链治理
  • 六、总结:把「多题一起看」做成系统的默认动作
  • 参考文献
  • 相关阅读
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档