想象你开了一家甜品店,需要给每一道新品打上“热带风味”“偏甜”“适合儿童”之类的标签。通常做法是请三位米其林大厨,每人花几分钟逐道试吃,一小时只能标完六道甜品,工资却按小时计费。如果甜品有一万道,这笔账会让人窒息。于是你换了思路:不再让大厨亲自试吃,而是把大厨的品鉴经验写成一张张“检查卡”——“看到芒果颗粒,就写热带风味”;“糖度计读数超过 20,就写偏甜”。普通店员拿着检查卡,一分钟能标十道甜品,发现规则不灵时,还能回电脑前改一行字,重新跑一遍。这种“把标注规则写成代码,让程序批量读懂数据”的方法,就是数据编程。它把人工标注从劳动密集的体力活,变成了先写卡、再批量执行的工程活。

岗位关联

数据编程是弱监督方向的核心方法。掌握它,你就能用代码替代大量人工标注,显著降低标注成本,是数据建设岗位和算法工程师的必备技能。

目录
  1. 可视化演示
  2. 核心理念:把大师傅的经验写进代码
  3. 标注函数编写:给试吃员一张不会漏项的检查卡
  4. 融合去噪:别让一个偏心的品鉴员带偏整桌菜
  5. 成本对比:印一次卡片,用一万次
  6. 工业实践:从一间甜品店到连锁工厂
  7. 局限边界:规则军团也有够不着的厨房
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

核心理念:把大师傅的经验写进代码

甜品店推出新品,需要给每道甜品标注“热带风味”“偏甜”“适合儿童”。如果请米其林大师傅逐道试吃,每道三分钟,一万道就是五百小时,而且大师傅一旦休假,整个流程立刻停摆。于是店长换了个思路:请大师傅把判断依据写成一张张品鉴卡,“看到芒果颗粒标热带风味”“糖度计超过 20 标偏甜”。普通店员按卡执行,一分钟能批十道甜品。数据编程就是这个思路的工程化:把人的标注知识编码成一个一个标注函数,再让程序批量给未标注数据打上弱标签。

这套方法最早来自斯坦福团队的弱监督研究,核心产物是 Snorkel 开源框架。它把人类知识转化成“标注函数”,让标注从雇佣大量人工的体力活,变成编写和运行代码的工程活。一条标注函数就像一张检查卡:面对一段文本,满足条件就给一个标签,不满足就弃权,不强行表态。

λi(x)={vif rule i fires on x,0otherwise.\displaystyle \lambda_i(x)=\begin{cases} v & \text{if rule } i \text{ fires on } x,\\ 0 & \text{otherwise.}\end{cases}

1. 输入样本 \(x\):它是一条待标注的数据,比如一句商品评论或一段临床文本。

x∈X\displaystyle x \in \mathcal{X}

2. 规则触发:第 \(i\) 条规则在这条样本上成立,比如文本里出现了“芒果”。

rulei(x)=true\displaystyle rule_i(x) = \text{true}

3. 输出标签 \(v\):规则成立时,函数给出一个候选标签,比如“热带风味”。

v∈Y∪{0}\displaystyle v \in \mathcal{Y} \cup \{0\}

4. 弃权 0:规则不成立时返回 0,表示这条函数不参与此次判断。

λi(x)=0\displaystyle \lambda_i(x) = 0

一张检查卡可能被写错,但十张不完美的检查卡彼此纠偏,就能得到可靠结论。这正是数据编程“多而不精,合而为一”的哲学。

标注函数编写:给试吃员一张不会漏项的检查卡

大师傅的品鉴经验往往很抽象,比如“这甜品很热带”。但写成检查卡时,必须落到可执行的客观信号:看到芒果颗粒,闻到柠檬香气,或者包装上印着“菠萝风味”。数据编程里的标注函数也一样,它由一个输入、一个条件和一组输出构成。输入是原始数据,条件可以是关键词、正则表达式,也可以是对外部知识库的查询。

从工程师视角看,一条标注函数就是一个普通函数:输入一段文本,内部用 if 判断是否命中规则,命中就返回一个标签,没命中就返回 0。比如评论里出现“芒果”就返回“热带风味”,出现“糖度很高”就返回“偏甜”。这些函数本质上是启发式规则,不需要非常精确,只要比随机猜测好一点,就有融合价值。

编写过程强调迭代。第一版检查卡往往太粗,比如把“柠檬”也算热带风味,结果大批不相关样本被误标。此时不要急着改一条规则,而是先统计覆盖率、重叠率和冲突率:有多少样本被至少一条函数覆盖?多少样本被多条函数同时给出不同标签?这些指标能告诉你检查卡漏了什么、吵了什么。然后针对问题新增或修改规则,再跑一轮,形成“写卡—评估—改卡”的循环。

甜品店的检查卡不是一次写成的。今天发现“含椰浆”没覆盖,明天发现“百香果”被误判成“偏甜”,都需要反复补充。数据编程的价值不在于第一版完美,而在于迭代成本足够低:改一行代码,重新跑一遍,全部标注自动更新。

融合去噪:别让一个偏心的品鉴员带偏整桌菜

十个品鉴员一起评审一道甜品,每个人都可能看走眼。有人对甜味特别敏感,总把微甜标成“偏甜”;有人只认识芒果,看到黄颜色就喊“热带风味”。如果让所有人投票,一些极端偏差会被平均掉;但如果某个品鉴员特别不可靠,简单多数投票就会被带偏。所以甜品店需要给每位品鉴员一个可信度权重,靠谱的人权重高,总出错的人权重低。

数据编程处理噪声的方式与此一致。最朴素的融合是多数投票,但多数投票默认所有标注函数质量相同,这在现实中很少成立。于是我们用生成模型估计每条标注函数的混淆矩阵:真标签是“甜”的时候,这条函数有多少概率输出“甜”?有多少概率输出“热带风味”?估计出这些概率后,再对函数输出做加权融合。

y^(x)=arg⁡max⁡y∈Y∑i=1nwi⋅1[λi(x)=y]\displaystyle \hat{y}(x)=\arg\max_{y\in\mathcal{Y}}\sum_{i=1}^{n}w_i \cdot \mathbb{1}[\lambda_i(x)=y]

1. 候选标签集合 \(\mathcal{Y}\):我们最终要在所有可能的标签里选一个,比如“甜”“热带风味”“普通”。

y∈Y\displaystyle y \in \mathcal{Y}

2. 单条函数投票:\(\mathbb{1}[\cdot]\) 是一个指示函数,括号里条件成立时值为 1,否则为 0。

1[λi(x)=y]=1 if match else 0\displaystyle \mathbb{1}[\lambda_i(x)=y] = 1 \text{ if match else } 0

3. 加权求和:把每条函数的投票乘以权重再相加,得到每个标签的总分。

S(y)=∑i=1nwi⋅1[λi(x)=y]\displaystyle S(y) = \sum_{i=1}^{n} w_i \cdot \mathbb{1}[\lambda_i(x)=y]

4. 取最大值:\(\arg\max\) 表示选择总分最高的标签作为最终预测。

y^(x)=arg⁡max⁡yS(y)\displaystyle \hat{y}(x) = \arg\max_{y} S(y)

多数投票适合作为基线,但真实项目里标注函数之间往往互相依赖。比如两条函数都依赖“芒果”这个关键词,它们犯的错误就是高度相关的,简单相加会把同一个偏差重复放大。生成模型的意义,正是估计这种相关性和准确率,让融合结果更接近真实标签。

成本对比:印一次卡片,用一万次

大师傅逐道试吃,成本是“每一道菜都要付一次钱”。写检查卡则是先付一笔固定设计费,再让店员按卡执行,每执行一次的边际成本几乎可以忽略。数据编程把人工标注的线性成本,拆成了一笔前期开发和一份极小的单条计算成本。

设开发检查卡的成本为 \(C_{dev}\),程序自动标注每条样本的算力成本为 \(\epsilon\),人工标注每条样本的成本为 \(c\)。那么人工标注的总成本随样本量线性上升,数据编程的成本则是一条平缓的直线。

Cprog(n)=Cdev+ϵ⋅n,Cmanual(n)=c⋅n\displaystyle C_{\text{prog}}(n) = C_{\text{dev}} + \epsilon \cdot n, \qquad C_{\text{manual}}(n) = c \cdot n

1. 固定开发成本 \(C_{\text{dev}}\):包括规则设计、代码实现和调试的人力投入,跟样本量无关。

Cdev>0\displaystyle C_{\text{dev}} > 0

2. 单条自动标注成本 \(\epsilon\):程序跑一条规则消耗的 CPU 或 API 费用,通常远小于人工单价。

0≤ϵ≪c\displaystyle 0 \le \epsilon \ll c

3. 人工单价 \(c\):标注员完成一条样本标注的平均成本,包含工资、培训和质检分摊。

c>0\displaystyle c > 0

4. 盈亏平衡点 \(n^*\):令两种总成本相等,得到数据编程开始划算的样本量。

n∗=Cdevc−ϵ\displaystyle n^* = \frac{C_{\text{dev}}}{c - \epsilon}

数据编程真正的便宜不只在绝对值,还在于可迭代。人工标注一旦发现标准错了,要返工重标;代码规则错了,改一行重新跑就行。边际成本越低,团队越敢试错,标注系统也就越能快速逼近业务需求。

工业实践:从一间甜品店到连锁工厂

当甜品店开成连锁品牌,每一家分店都要给新品打标签,再靠大师傅逐店培训就不现实了。此时总部只需要把检查卡做成标准程序,分店自动执行即可。数据编程在工业界的价值,正是把少数专家的知识,复制到海量数据上。

电商场景中,评论“芒果很甜但价格偏贵”同时包含口味和价格两个属性。算法团队可以写关键词函数抽出口味词,用价格正则抽取出金额,再用加权融合判断整条评论的情感极性。医疗文本里,诊断结论和症状描述往往有固定句式和术语,工程师借助医学词典构造标注函数,可以快速抽取症状、药物和检查项目,为后续实体识别模型提供训练数据。客服工单分类同样适合:工单里常有“退货”“发票”“物流”等高频词,少量规则就能覆盖大部分意图。

这些实践背后都有同一个套路:先利用规则或外部知识库生成大量弱标签,再用生成模型去噪,最后训练一个端到端模型。弱标签不完美,但足以让模型学会特征;比人工标注便宜得多,又比完全无监督可靠得多。

局限边界:规则军团也有够不着的厨房

检查卡不是万能的。若一道菜的“好吃”完全依赖抽象审美,卡片上的客观指标就写不出来;若规则来自一位只会做西餐的师傅,换到川菜店就会水土不服。数据编程同样受限于领域经验和规则表达能力。

首先,规则有领域依赖。在电商评论上有效的关键词,迁移到法律文本里几乎必然失效。其次,函数之间会冲突重叠,如果冲突率长期居高不下,说明规则体系在自我打架,需要重构。再次,弱标签不等于真标签,仍然需要抽取少量样本做人工复核,否则质量控制会失控。最后,不是所有任务都适合数据编程。需要资深专家逐条判断的罕见病诊断、高风险金融审核,交给粗糙规则是危险的。

实践中要给规则系统配监控指标:覆盖率突然下降,可能是线上数据分布变了;冲突率异常升高,可能是某条规则被业务逻辑推翻。保持对边界的清醒,才能让数据编程在合适的场景里真正省钱。

AI 应用场景

电商评论属性与情感标注

阶段:数据准备与弱监督标注

解决的问题:电商平台每天新增数百万条评论,人工逐条标注“口味”“价格”“物流”等属性和情感倾向成本极高,且标准容易漂移。数据编程把标注员经验写成关键词和正则函数,批量生成弱标签,再由生成模型融合去噪。

具体案例:某食品电商团队针对“芒果很甜但价格偏贵”这类评论,写 20 条标注函数覆盖口味词、价格词和情感词,先自动标注 50 万条未标注评论,再随机抽 2000 条人工复核,最终将属性分类模型的训练数据准备时间从 3 个月压缩到 2 周。

医疗文本实体抽取

阶段:训练数据构建

解决的问题:临床文本涉及大量症状、药物、检查项目实体,专家标注成本高且隐私要求严格。数据编程可以使用医学词典、句法模板和外部知识库作为标注函数,在不出域的情况下自动生成实体标签。

具体案例:某医疗 AI 项目需要从出院小结中抽取“主诉”“诊断”“用药”实体。团队构建 30 条基于词典的正则函数,覆盖 80% 文本,再用生成模型估计函数可信度,把实体抽取模型的 F1 从 0.72 提升到 0.83,且没有额外增加专家标注量。

客服工单意图分类

阶段:数据标注与模型迭代

解决的问题:客服工单量大、意图类别多,人工标注容易疲劳。数据编程把常见业务话术转成规则函数,比如“发票”对应“财务咨询”,“退货”对应“售后处理”,快速为工单分类模型提供冷启动数据。

具体案例:某电信运营商用 15 条关键词函数标注 20 万条历史工单,再结合少量人工复核,将工单分类模型的准确率从 0.65 提升到 0.81,同时把新意图发现周期从 1 个月缩短到 3 天。

Python 演示

python60 行
# -*- coding: utf-8 -*-import refrom collections import Counter def lf_mango(text):    return "tropical" if "mango" in text.lower() else 0 def lf_citrus(text):    return "tropical" if any(w in text.lower() for w in ["lemon", "citrus", "pineapple"]) else 0 def lf_sweet(text):    return "sweet" if re.search(r"(sweet|candy|sugar)", text.lower()) else 0 def lf_price(text):    return "price" if re.search(r"\d+\s*(元|rmb)", text.lower()) else 0 def lf_spicy(text):    return "spicy" if "chili" in text.lower() else 0 LABEL_FUNCTIONS = [lf_mango, lf_citrus, lf_sweet, lf_price, lf_spicy] def apply_lfs(text):    votes = []    for lf in LABEL_FUNCTIONS:        v = lf(text)        if v:            votes.append(v)    return votes def majority_vote(votes):    if not votes:        return "UNKNOWN"    counter = Counter(votes)    label, _ = counter.most_common(1)[0]    return label def weighted_vote(votes, weights):    score = {}    for lf_index, label in enumerate(votes):        score[label] = score.get(label, 0) + weights[lf_index]    if not score:        return "UNKNOWN"    return max(score.items(), key=lambda kv: kv[1])[0] def main():    samples = [        "This mango cake is very sweet and creamy",        "The lemonade has a fresh citrus flavor",        "Spicy chili noodles with a little sweet sauce",        "The price is 25 yuan for a small bowl",        "A boring snack with no taste"    ]    weights = [0.8, 0.6, 0.5, 0.4, 0.7]    print("Text -> votes -> majority -> weighted")    for text in samples:        votes = apply_lfs(text)        print(text[:25], votes, majority_vote(votes), weighted_vote(votes, weights)) if __name__ == "__main__":    main()
目的:用 5 条标注函数演示数据编程中最基础的规则编写、多数投票和加权投票。
  1. def lf_mango(text):一条标注函数对应一张“品鉴卡”,输入是一段文本。
  2. return "tropical" if "mango" in text.lower() else 0命中关键词返回标签,否则返回 0 表示弃权。
  3. LABEL_FUNCTIONS = [lf_mango, lf_citrus, lf_sweet, lf_price, lf_spicy]把所有弱规则收集成函数列表。
  4. def majority_vote(votes):多数投票是最简单的融合方式,默认所有函数一样可靠。
  5. counter.most_common(1)[0]直接取票数最高的标签作为最终结果。
  6. score[label] = score.get(label, 0) + weights[lf_index]加权投票把函数权重累加到对应标签上。
  7. max(score.items(), key=lambda kv: kv[1])[0]这是 \(\arg\max\) 的代码表达,选择总分最高的标签。

常见误区

误区数据编程不就是写一堆 `if "芒果" in text` 和正则规则吗?我早就写过,没什么特别的。

正解写规则只是第一步,数据编程是一套完整的弱监督工程范式,包含规则编写、覆盖率统计、冲突分析、生成模型去噪、加权融合和少量人工校准。只写规则不建模,就像只印了检查卡却不培训店员,卡片之间的互相矛盾会被简单投票放大。

为何常见:因为大多数人接触到的第一个例子都是关键词函数,容易把局部的“规则”当成整体的“数据编程”。

误区多写几条规则,覆盖率更高,最终标注质量一定更好。

正解函数过多且高度依赖时,会反复放大同一个偏差。比如三条函数都依赖“芒果”关键词,它们本质上是同一条规则复制了三遍,投票时相当于给某个错误倾向加了三票。应该评估函数的独立性和冲突率,而不是盲目堆数量。

为何常见:人容易把“数量”等同于“质量”,忽略了标注函数之间的相关噪声。

误区只要写了标注函数,就不用再找人标任何数据。

正解数据编程降低的是大规模标注成本,但通常仍需要少量开发集和验证集来校准权重、评估弱标签质量。完全脱离人工,可能连规则写错了都不知道。它像印检查卡,但卡片印完也要请专业师傅抽查几张。

为何常见:宣传话术常强调“减少人工”,容易让人误解为“零人工”。

误区以后所有标注任务都可以交给数据编程,人工标注会彻底消失。

正解需要深度专家判断、缺乏可总结规则、数据分布高频变化的场景,规则函数很难覆盖。数据编程适合“领域知识可表达、样本量大、规则可迭代”的任务,不适合高风险、强主观的标注场景。

为何常见:看到工业界成功案例后,容易把适用范围扩大到自己并不了解的任务上。

面试题

Q1. 数据编程中,一条标注函数通常输出什么?

单选题 · easy

  • A.二分类标签
  • B.标签或弃权← 正确答案
  • C.概率分布
  • D.数值特征

答案:B。标注函数本质上是一条启发式规则,命中时给出一个候选标签,未命中时返回 0 表示弃权。它不是分类模型,不输出概率密度,也不输出普通数值特征,因此选 B。

Q2. 下面哪项是弱监督范式的核心目标?

单选题 · easy

  • A.用代码表达标注规则← 正确答案
  • B.提升单模型的参数量
  • C.减少可视化图表数量
  • D.替代所有人工决策

答案:A。弱监督范式的核心,是把人类知识以代码形式表达成标注函数,再自动生成弱标签。提升参数量、减少图表或替代所有人工决策都不是弱监督的目标,因此选 A。

Q3. 多数投票在数据编程中的主要缺陷是?

单选题 · mid

  • A.无法处理缺失值
  • B.忽略标注函数质量差异← 正确答案
  • C.只能处理二分类
  • D.训练速度太慢

答案:B。多数投票默认每条函数的可靠度相同,但现实中有些规则准确率高、有些几乎随机。忽略质量差异会让噪声大的函数拥有同等话语权。缺失值和二分类限制都不是多数投票的核心问题,因此选 B。

Q4. 标注函数可以使用哪些信号来源?

多选题 · mid

  • A.关键词规则← 正确答案
  • B.正则表达式← 正确答案
  • C.外部知识库← 正确答案
  • D.随机数生成器

答案:ABC。标注函数可以是简单关键词、正则表达式,也可以查询外部知识库来得到信号。随机数生成器无法提供可解释的领域知识,不能作为标注函数的信号来源,因此选 A、B、C。

Q5. 加权融合中的权重通常来自什么?

单选题 · mid

  • A.标注函数的准确率估计← 正确答案
  • B.标注函数的代码行数
  • C.数据样本的字符个数
  • D.随机初始化的数值

答案:A。权重应当反映标注函数的可靠程度,通常通过生成模型估计混淆矩阵,再得到准确率或精度的近似。代码行数、样本长度和随机值都与标注质量无关,因此选 A。

Q6. 关于标注函数之间的冲突,正确的理解是?

多选题 · mid

  • A.冲突是正常现象← 正确答案
  • B.需要用模型融合裁决← 正确答案
  • C.出现冲突必须删除
  • D.冲突信息没有价值

答案:AB。多条规则给出不同标签是弱监督中的常态,不能一看到冲突就删规则,而是应该用生成模型或加权投票来裁决。冲突本身包含信息,可以帮助发现规则盲区,因此 C、D 错误。

Q7. 数据编程中生成模型的主要作用是?

单选题 · hard

  • A.生成更多无标注数据
  • B.估计标注函数的混淆矩阵← 正确答案
  • C.替代下游分类模型
  • D.压缩模型的参数规模

答案:B。生成模型在数据编程中负责对弱标签去噪,核心是估计每条标注函数在不同真实标签下的输出概率,即混淆矩阵。它不生成无标注数据,也不替代下游分类模型,因此选 B。

Q8. 标注函数返回 0 通常表示什么?

单选题 · easy

  • A.标签为否定类别
  • B.函数选择弃权← 正确答案
  • C.数据出现异常
  • D.函数执行失败

答案:B。0 是数据编程中的弃权符号,表示这条规则没有命中,不参与本次投票。它不是否定类别,也不代表数据异常或程序错误,因此选 B。

Q9. 下列哪些情况更适合使用数据编程?

多选题 · hard

  • A.领域规则可以被总结← 正确答案
  • B.样本量大且人工成本高← 正确答案
  • C.完全依赖专家逐条判断
  • D.数据分布相对稳定← 正确答案

答案:ABD。数据编程需要可表达的领域规则,样本量大时边际成本优势明显,同时假设规则不会频繁失效。若任务完全依赖资深专家逐条主观判断,规则写不出来,就不适合数据编程,因此选 A、B、D。

Q10. 数据编程比人工标注更划算的条件是?

单选题 · mid

  • A.样本量很小时
  • B.任何情况下都便宜
  • C.超过盈亏平衡点之后← 正确答案
  • D.只要使用深度学习模型

答案:C。数据编程有固定开发成本,样本量小时写规则可能比人工更贵。只有样本量超过盈亏平衡点后,边际成本优势才显现,并不是任何情况都便宜,也与是否使用深度学习无关,因此选 C。