云计算百科
云计算领域专业知识百科平台

同批题重复采样 方差估计与置信区间的零依赖实现(2026)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

在 AI 搜索可见度检测里,最常见的错误不是算错,是不算方差就直接比大小。

"这次提及率 35%,上次 30%,涨了 5 个点"——这句话如果没带置信区间,等于什么都没说。样本量 20 题、单次采样的情况下,5 个点的差异大概率落在噪声里。本文给一套零依赖的实现:用二项比例估计提及率,用 Wilson 区间处理小样本,用配对 bootstrap 处理复测差值,并给出"多少题才够"的最小样本量估算。

一、口径先行:分母是什么

先定死三件事,否则后面所有区间都是错的。

  • 分母 = 题数 × 采样次数。20 题各采 3 次,分母是 60,不是 20。按 20 算会把区间压窄三倍,得出过度自信的结论。
  • 分子 = 提及次数。同一次回答里出现两次品牌名,只计 1 次(去重键为 query_id + sample_k)。
  • "未提及"分三种:真的没出现、回答被截断、调用失败。后两种必须从分母里剔除,否则是假阴性。
  • 第三点是实际项目里最容易被忽略的。一次超时被记成"未提及",等于凭空多了一个负样本。

    二、点估计:二项比例

    提及率本质是一串 0/1 的均值。用 k/n 估计即可,不需要加权——除非做了分层抽样(比如按"医院类问法"和"用品类问法"分层),那要按层权重合并。

    from math import sqrt

    def rate(hits: int, total: int) -> float:
    return hits / total if total else 0.0

    简单到不用解释,但它的置信区间不简单。

    三、区间估计:为什么用 Wilson 而不是正态近似

    常见的 p ± 1.96*sqrt(p(1-p)/n) 在 p 接近 0 或 1 时会崩:算出的下界可能是负数,或者区间宽度为 0。而 GEO 检测里"提及率 3%""提及率 0"是常态——绝大多数中小企业在绝大多数题上就是不被提及。

    Wilson 得分区间在小样本与极端比例下表现稳定,且不需要外部库:

    def wilson_ci(k: int, n: int, z: float = 1.96) -> tuple:
    """Wilson score interval, 95% by default"""
    if n == 0:
    return (0.0, 1.0)
    p = k / n
    denom = 1 + z * z / n
    center = (p + z * z / (2 * n)) / denom
    half = (z / denom) * sqrt(p * (1 – p) / n + z * z / (4 * n * n))
    return (max(0.0, center – half), min(1.0, center + half))

    跑几个数看看差别:

    观测k/n正态近似 95% CIWilson 95% CI
    0/20 0.0% (-0.0%, 0.0%) (0.0%, 16.1%)
    1/20 5.0% (-3.3%, 13.3%) (0.9%, 23.6%)
    3/20 15.0% (-0.6%, 30.6%) (5.2%, 36.0%)
    7/20 35.0% (14.1%, 55.9%) (18.1%, 55.9%)
    18/20 90.0% (76.9%, 103.1%) (69.9%, 97.2%)

    第一行最能说明问题:20 题全未被提及,正态近似给出"0%,区间宽度为零"——一个自信到荒谬的结论。Wilson 给的是"0% 到 16%",这才是真实的认知水平:没见到,但样本不足以断言它不存在。

    四、复测差值:配对 bootstrap

    复测的正确比法是配对:同一题、同一平台、同一参数,这次和上次各是什么结果。不能拿"这次的总提及率"减"上次的总提及率"——两批题的难度不同,总体比例会把题目差异混进效果里。

    配对差值的最小实现:

    import random

    def paired_diff_ci(before: list, after: list, iters: int = 5000, seed: int = 42):
    """
    before/after: 同长度 0/1 列表, 按 query_id 对齐
    返回 (差值点估计, 下界, 上界)
    """
    assert len(before) == len(after)
    n = len(before)
    if n == 0:
    return (0.0, 0.0, 0.0)

    d = [a – b for a, b in zip(after, before)]
    point = sum(d) / n

    rnd = random.Random(seed)
    boots = []
    for _ in range(iters):
    s = sum(d[rnd.randrange(n)] for _ in range(n)) / n
    boots.append(s)
    boots.sort()
    lo = boots[int(0.025 * iters)]
    hi = boots[int(0.975 * iters) – 1]
    return (round(point, 4), round(lo, 4), round(hi, 4))

    判读规则只有一条:区间跨过 0,就不能说"变好了"。

    这一条能挡掉大部分误报。举个例子:20 题、上次 6 次提及、这次 9 次,点估计 +15 个点,看着很漂亮;配对 bootstrap 出来的区间可能是 (-2%, +32%),跨零,只能写"方向为正、未达可判定水平"。等题量加到 60,同样幅度才可能显著。

    五、多少题才够:最小样本量估算

    不用公式也能给个可操作的对照表。设目标检出幅度为 Δ(绝对百分点),α=0.05、power=0.8、双侧,粗略估算所需独立单位数(题数,不是调用次数):

    目标 Δ基线提及率 20%基线 35%基线 50%
    +30pp 约 26 题 约 34 题 约 42 题
    +15pp 约 95 题 约 125 题 约 150 题
    +5pp 约 800 题 约 1000 题 约 1200 题

    (按两比例检验近似计算,未做连续性校正;仅供排期参考。)

    这张表的现实含义:20 题能看出"有没有"和"大概方向",看不出"涨了 5 个点"。谁拿 20 题的检测结果说"本月提升了 5%",这个结论在统计上不成立。

    也正因为如此,把效果承诺写进合同、按"提升 N 个百分点"验收,在工程上是有前提的:题量必须够。题量不够而承诺幅度的,只能靠挑题、挑平台、挑采样次数来凑数。

    六、两个坑

    坑一:重复计数把分母撑大了。

    早期去重键只用了 query_id,没带 sample_k。结果是同一题的 3 次采样被合并成 1 条记录,但分母按 3 倍算,提及率直接被压到三分之一。表上看是"优化失效",实际是键写错了。

    修法:去重键统一为 (query_id, sample_k, platform),入库时加唯一索引;统计前先跑一遍 count(distinct key) == 预期总数 的断言,不等就中止。

    坑二:并发写入导致样本乱序、配对错位。

    复测数据用 3 并发写库,写入完成顺序与 sample_k 不一致。配对脚本按行号对齐,结果把 A 题的"这次"配到了 B 题的"上次"上,差值全是噪音。

    修法两条:一是配对前强制 order by query_id, sample_k;二是加校验——两侧的 query_id 集合必须完全相同,多一条少一条直接抛异常,不许静默对齐。

    七、这套方法测不出什么

    • 测不出因果。 差值显著只说明"这次和上次不一样",不能证明是某项优化导致的。要归因得做对照(同城未优化的对照组),成本高得多。
    • 测不出绝对水平。 提及率依赖题目池,换一批题就不能跨批比较。
    • 测不出长尾。 区间宽度由 n 决定,长尾问法题量少,结论天然更弱。
    • 测不出真实用户分布。 采样是等权重的,真实用户的问法分布并不等权。要贴近真实,得按搜索量加权,而多数企业拿不到这个权重。

    结论一句话:先算区间,再谈差值;区间跨零,就不要写"提升"。这一条不解决"排名能不能保证"的问题,但它能让报告里的每个数字都站得住。

    你们在做复测比对时,是按总体比例减,还是按题配对?评论区聊聊。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 同批题重复采样 方差估计与置信区间的零依赖实现(2026)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!