Snorkel 学习笔记 简介与入门 LF TF SF Labeling Functions Transformation Function Slicing Function 示例

2021-06-16 17:23:32 浏览数 (1)

文章目录

    • Snorkel简介与入门
      • 1. 背景
      • 2. LF (Labeling Function) 标注函数
      • 3. TF (Transformation Function) 转换函数
      • 4. SF (Slicing Functions) 剪切函数
      • Ref

Snorkel简介与入门

1. 背景

弱监督(Weak Supervision)可以让我们低成本的利用领域专家的知识来程序化的标注上百万级别的 数据样本,从而帮助我们解决人工智能时代的数据瓶颈问题。更确切地说,这是一个帮助将领域专家的知识编码到AI系统中的框架,专家知识注入的方式可以采用手写的推理规则或者远程监督。

弱监督的主要优点在于:

  • 灵活:需要更新模型时,只需要更新标注函数、重新生成训练集和分类器即可
  • 提高召回率:判别模型将提供优于弱监督模型的泛化能力,因此可以提高召回率

Google在20018年12月刚发了一篇论文介绍Snorkel DryBell,一个Google自制的内部工具,利用弱监督快速 构建了3个强大的文本分类器。

Snorkel is a system for programmatically building and managing training datasets without manual labeling. In Snorkel, users can develop large training datasets in hours or days rather than hand-labeling them over weeks or months.

Snorkel currently exposes three key programmatic operations:

  • Labeling data, e.g., using heuristic rules or distant supervision techniques
  • Transforming data, e.g., rotating or stretching images to perform data augmentation
  • Slicing data into different critical subsets for monitoring or targeted improvement

Snorkel then automatically models, cleans, and integrates the resulting training data using novel, theoretically-grounded techniques.

snorkel的核心在于两个方面:

  • 第一,将所有标注源抽象为label function(LF),由用户来设计LF,而不是标注数据;
  • 第二,构造Generative Model(GM) 对多个LF的预测结果进行融合,输出在各个样本在不同标签上的概率分布。

第一点更偏向于工程设计,snorkel的代码中对LF进行了很好的抽象和封装,能够满足绝大多数的场景。第二点偏向于算法。实际使用snorkel时,会根据正负标签的比例进行选择,作者通过实验证明在正负标签比例低的情况下,使用majority voting可以得到与GM相同的结果。简单地说,当不同数据源标注的的样本重合非常小的情况下,构建GM的意义不大,直接投票就可以。在其他情况下,使用GM可以得到更好的效果。

GM的细节需要参考作者的另一篇大作(Data Programming:Creating Large Training Sets, Quickly)。作者使用factor graph对LF输出结果的概率分布P(lambda, Y)进行建模,其中lambda是多个LF输出的结果构成的维度为(M,N)的矩阵,M为样本数量,N为LF的数量。模型设计时考虑了三个因素,包括:不同LF在标签集合上的覆盖情况、不同LF的准确率,不同LF之间的相关性。GM模型的训练使用最大似然方法进行无监督训练,最大化训练集合中所有LF输出结果的概率。

最后,使用P(Y|lambda)进行预测,预测的结果作为训练样本训练分类模型。

作者通过实验证明

  • snorkel收集的训练样本进行训练,效果优于远程监督的方法
  • snorkel收集的训练样本可以与人工标注的样本质量齐平,且标准成本低
  • snorkel框架非常容易上手(亲测,非常好用,与tf无缝连接)

2. LF (Labeling Function) 标注函数

在Snorkel中,推理逻辑被成为标注函数(Labeling Function)。有如下这些常见类型的标注函数:

  • 硬编码规则:通常使用正则表达式
  • 语义规则:例如,使用spaCy的依存树
  • 远程监督:使用外部知识库
  • 有噪声人工标注:众包标注
  • 外部模型:包含有价值信号的第三方模型

当编写完标注函数后,Snorkel将利用所有标注函数之间的预测结果与冲突来训练一个标注模型。然后,当标注新的数据时,每个标注函数都会投票:正、负或弃权。基于这些投票以及标注函数的权重,标注模型能够地为百万级的数据点自动进行概率型标注。最终的目标是训练一个可以超过标注函数性能的分类器。

一个简单的LF

代码语言:javascript复制
from snorkel.labeling import labeling_function


@labeling_function()
def lf_keyword_my(x):
    """Many spam comments talk about 'my channel', 'my video', etc."""
    return SPAM if "my" in x.text.lower() else ABSTAIN

应用LF并查看效果

代码语言:javascript复制
lfs = [textblob_polarity, textblob_subjectivity]

applier = PandasLFApplier(lfs)
L_train = applier.apply(df_train)

LFAnalysis(L_train, lfs).lf_summary()

3. TF (Transformation Function) 转换函数

当某个分类的样本数据较少时,训练模型的效果往往效果不尽人意。除了调整训练权重之外,我们还可以使用Snorkel框架的Data Augumentation数据增长函数: Transformation Function

我们可以制定一些简单的规则,使一些影响不大的特征的变换,比如在NLP中替换一些词语为近义词,或者替换语气助词等,增大训练样本让模型训练更准确。

下面是两个TF的例子,替换人名与变换形容词,以达到增加训练样本的目的。

代码语言:javascript复制
from snorkel.augmentation import transformation_function

# Replace a random named entity with a different entity of the same type.
@transformation_function(pre=[spacy])
def change_person(x):
    person_names = [ent.text for ent in x.doc.ents if ent.label_ == "PERSON"]
    # If there is at least one person name, replace a random one. Else return None.
    if person_names:
        name_to_replace = np.random.choice(person_names)
        replacement_name = np.random.choice(replacement_names)
        x.text = x.text.replace(name_to_replace, replacement_name)
        return x


# Swap two adjectives at random.
@transformation_function(pre=[spacy])
def swap_adjectives(x):
    adjective_idxs = [i for i, token in enumerate(x.doc) if token.pos_ == "ADJ"]
    # Check that there are at least two adjectives to swap.
    if len(adjective_idxs) >= 2:
        idx1, idx2 = sorted(np.random.choice(adjective_idxs, 2, replace=False))
        # Swap tokens in positions idx1 and idx2.
        x.text = " ".join(
            [
                x.doc[:idx1].text,
                x.doc[idx2].text,
                x.doc[1   idx1 : idx2].text,
                x.doc[idx1].text,
                x.doc[1   idx2 :].text,
            ]
        )
        return x

写完TF之后就可以将TF应用至训练数据,通过调整参数n_per_original控制每条训练数据生成的样本个数,参数p为控制每个function使用的概率使得一些function权重会更大。

代码语言:javascript复制
from snorkel.augmentation import MeanFieldPolicy

mean_field_policy = MeanFieldPolicy(
    len(tfs),
    sequence_length=2,
    n_per_original=2,
    keep_original=True,
    p=[0.05, 0.05, 0.3, 0.3, 0.3],
)
代码语言:javascript复制
from snorkel.augmentation import PandasTFApplier

tf_applier = PandasTFApplier(tfs, mean_field_policy)
df_train_augmented = tf_applier.apply(df_train)
Y_train_augmented = df_train_augmented["label"].values
print(f"Original training set size: {len(df_train)}")
print(f"Augmented training set size: {len(df_train_augmented)}")
代码语言:javascript复制
Original training set size: 1586
Augmented training set size: 2486

通过打印的结果可以看到,训练集得到了有效扩大。

4. SF (Slicing Functions) 剪切函数

Slicing Functions剪切函数和前两个函数目的不同,SF是为了检查模型的最后的预测效果是否达到预期。使用场景是在一个大的机器学习场景下,如自动驾驶任务,可能我们的模型准确性还不错,我们想检查模型检测自行车识别是否准确,就可以使用SF将自行车数据剪切出来,单独查看模型预测分类的效果。

比如以下SF示例,将较短评论剪切下查看分类效果:

代码语言:javascript复制
import re
from snorkel.slicing import slicing_function


@slicing_function()
def short_comment(x):
    """Ham comments are often short, such as 'cool video!'"""
    return len(x.text.split()) < 5


sfs = [short_comment]

查看SF划分出来的数据集

代码语言:javascript复制
from snorkel.slicing import slice_dataframe

short_comment_df = slice_dataframe(df_test, short_comment)
short_comment_df[["text", "label"]].head()

TEXT

LABEL

194

super music

0

2

I like shakira…

0

110

subscribe to my feed

1

263

Awesome

0

77

Nice

0

最后我们可以查看模型预测在SF数据集下的效果如何

代码语言:javascript复制
from snorkel.slicing import PandasSFApplier

applier = PandasSFApplier(sfs)
S_test = applier.apply(df_test)
代码语言:javascript复制
from snorkel.analysis import Scorer

scorer = Scorer(metrics=["f1"])
代码语言:javascript复制
scorer.score_slices(
    S=S_test, golds=Y_test, preds=preds_test, probs=probs_test, as_dataframe=True
)

F1

OVERALL

0.925000

SHORT_COMMENT

0.666667

尽管整体F1很高,但是在短评论场景下准确率表现一般。

Ref

  1. https://www.chainnews.com/articles/424602825079.htm
  2. https://cloud.tencent.com/developer/article/1491797
  3. https://arxiv.org/pdf/1711.10160.pdf snorkel论文
  4. https://www.snorkel.org/get-started/
  5. https://zhuanlan.zhihu.com/p/55138499
  6. https://www.snorkel.org/use-cases/02-spam-data-augmentation-tutorial
  7. https://www.snorkel.org/use-cases/03-spam-data-slicing-tutorial
  8. https://medium.com/@harrison.miller13_28580/labeling-transforming-and-structuring-training-data-sets-for-machine-learning-with-snorkel-d4813e2aad74

0 人点赞