百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

只有正样本和无标记数据的半监督学习(PU Learning)

ztj100 2024-10-28 21:15 31 浏览 0 评论

作者:Alon Agmon

编译:ronghuaiyang

正文共:5411 字 6 图

预计阅读时间:16 分钟

导读

在实际业务场景中,可能只会收到正反馈,所以反映到数据上,就只有正样本,另外就是大量的没有标记的样本,那么如何给这些没有标记的样本打上标签呢?

当你只有几个正样本的时候,如何分类未标注的数据

假设您有一个支付事务数据集。其中一些交易被标记为欺诈,其余的被标记为真实交易,你需要设计一个模型来区分欺诈交易和真实交易。假设你有足够的数据和良好的特征,这似乎是一个简单的分类任务。但是,假设只有15%的数据有标注,并且标注的样本只属于一个类,因此你的训练集由15%标记为真实的样本组成,而其余的没有标记,可能是真实的,也可能是虚假的。你如何对它们进行分类?这样的需求是否只是将这个任务变成了一个无监督的学习问题?好吧,不一定。

这个问题 —— 通常被称为PU(正样本的和未标记的样本)的分类问题 —— 应该首先从两个相似且常见的“标注问题”中区分出来,这两个问题使许多分类任务复杂化。第一个也是最常见的标签问题是“小训练集”的问题。当你有相当数量的数据,但只有一小部分被标记时,它就会出现。这个问题有许多种类和相当多的具体训练方法。另一个常见的标记问题(通常与PU问题合并在一起)涉及的情况是,我们的训练数据集全都有标记,但它只包含一个类。例如,假设我们只有一个非欺诈事务的数据集,并且我们需要使用这个数据集来训练一个模型来区分(类似的)非欺诈事务和欺诈事务。这也是一个常见的问题,通常被视为无监督的离群点检测问题,尽管在ML领域中也有相当多的工具是专门设计来处理这些场景的(OneClassSVM可能是最著名的)。

相比之下,PU分类问题涉及到一个训练集,其中只有部分数据被标记为正,而其余数据未被标记,可能是正的,也可能是负的。例如,假设你的雇主是一家银行,它可以为你提供大量的事务性数据,但只能确认其中的一部分是100%真实的。我在这里使用的例子涉及到关于伪钞的类似场景。它包括了1200张纸币的数据集,其中大部分没有标记,只有一部分被确认为真实的。虽然PU问题也很常见,但是与前面提到的两个分类问题相比,它们的讨论要少得多,而且很少有实践的例子或库可以广泛使用。

本文的目的是提供一种可能的方法来解决PU问题,我最近在一个分类项目中使用了这种方法。它是基于Charles Elkan和Keith Noto写的论文“Learning classifiers from only positive and unlabeled data”(2008),以及由Alexandre Drouin写的一些代码。尽管在文章中有更多的PU学习方法(我打算在以后的文章中讨论另一种相当流行的方法),Elkan和Noto的(E&N)方法非常简单,可以很容易地在Python中实现。

一点点理论(请原谅)

E&N本质上声称,给定一个数据集,我们有正的和未标记的数据,某个样本标记为正的概率是 [ P(y=1|x)] 的概率等于样本被标记的概率 [P(s=1|x)] 除以我们的数据集中正样本被标记的概率[P(s=1|y=1)]。

如果这个断言是正确的,那么实现起来就相对容易了。这是因为虽然我们没有足够的数据来训练分类器来告诉我们样本是正的还是负的,在PU场景中我们确实有足够的标签数据告诉我们正样本是否可能被标记,根据E&N,这足以估计有多可能是正的。

更正式地说,给定一个未标记的数据集,其中只有一组标记为正的样本,如果我们估计P(s=1|x) / P(s=1|y=1),我们就可以估计未标记的样本x为正的概率。幸运的是,我们几乎可以使用任何基于sklearn的分类器,按照以下步骤来估计:

(1)在包含已标记和未标记数据的数据集上拟合一个分类器,同时使用isLabeled作为目标y。以这种方式拟合分类器,训练它预测给定样本x被标记的概率P(s=1|x)。

(2)使用分类器预测数据集中已知正样本被标记的概率,预测结果可以表示为正样本被标记的概率 P(s=1|y=1|x),计算这些预测概率的平均值,这就是我们的P(s=1|y=1)。有了P(s=1|y=1)的估计值,为了预测数据点k为正的概率,根据E&N,我们需要做的就是估计P(s=1|k)或它被标记的概率,这正是我们训练的分类器(1)知道如何做的。

(3)使用我们在(1)上训练的分类器来估计k被标记或P(s=1|k)的概率。

(4)一旦我们估算出P(s=1|k),我们就可以将这个概率除以P(s=1|y=1) 这是在步骤(2)上估算出来的,这样就可以得到它属于这两类的实际概率。

我们现在写代码并进行测试

以上步骤1-4可按如下方式实施:

# prepare data
x_data = the training set
y_data = target var (1 for the positives and not-1 for the rest)
# fit the classifier and estimate P(s=1|y=1)
classifier, ps1y1 = 
       fit_PU_estimator(x_data, y_data, 0.2, Estimator())
# estimate the prob that x_data is labeled P(s=1|X)
predicted_s = classifier.predict_proba(x_data)
# estimate the actual probabilities that X is positive
# by calculating P(s=1|X) / P(s=1|y=1)
predicted_y = estimated_s / ps1y1

让我们从这里开始:fit_PU_estimator()方法。

fit_PU_estimator()方法完成了两个主要任务:它拟合一个分类器,你选择一个具有正样本和未标记样本的训练集,然后估计一个正样本被标记的概率。相应地,它返回拟合的分类器(学会估计给定样本被标记的概率)和估计的概率P(s=1|y=1)。之后,我们需要做的就是找到P(s=1|x)或者标记为x的概率。因为这就是我们训练的分类器要做的,我们只需要调用它的predict_proba()方法。最后,为了实际对样本x进行分类,我们只需要将结果除以我们已经找到的P(s=1|y=1)。这可以用代码表示为:

pu_estimator, probs1y1 = fit_PU_estimator(
  x_train,
  y_train,
  0.2,
  xgb.XGBClassifier())

predicted_s = pu_estimator.predict_proba(x_train)
predicted_s = predicted_s[:,1]
predicted_y = predicted_s / probs1y1

实现fit_PU_estimator()方法本身非常简单:

def fit_PU_estimator(X,y, hold_out_ratio, estimator):
    # The training set will be divided into a fitting-set that will be used 
    # to fit the estimator in order to estimate P(s=1|X) and a held-out set of positive samples
    # that will be used to estimate P(s=1|y=1)
    # --------
    # find the indices of the positive/labeled elements
    assert (type(y) == np.ndarray), "Must pass np.ndarray rather than list as y"
    positives = np.where(y == 1.)[0] 
    # hold_out_size = the *number* of positives/labeled samples 
    # that we will use later to estimate P(s=1|y=1)
    hold_out_size = int(np.ceil(len(positives) * hold_out_ratio))
    np.random.shuffle(positives)
    # hold_out = the *indices* of the positive elements 
    # that we will later use  to estimate P(s=1|y=1)
    hold_out = positives[:hold_out_size] 
    # the actual positive *elements* that we will keep aside
    X_hold_out = X[hold_out] 
    # remove the held out elements from X and y
    X = np.delete(X, hold_out,0) 
    y = np.delete(y, hold_out)
    # We fit the estimator on the unlabeled samples + (part of the) positive and labeled ones.
    # In order to estimate P(s=1|X) or  what is the probablity that an element is *labeled*
    estimator.fit(X, y)
    # We then use the estimator for prediction of the positive held-out set 
    # in order to estimate P(s=1|y=1)
    hold_out_predictions = estimator.predict_proba(X_hold_out)
    #take the probability that it is 1
    hold_out_predictions = hold_out_predictions[:,1]
    # save the mean probability 
    c = np.mean(hold_out_predictions)
    return estimator, c

def predict_PU_prob(X, estimator, prob_s1y1):
    prob_pred = estimator.predict_proba(X)
    prob_pred = prob_pred[:,1]
    return prob_pred / prob_s1y1

为了测试这一点,我使用了[Bank Note Authentication dataset](http://archive.ics.uci.edu/ml/datasets/banknote+ Authentication),它基于从真钞和假钞图像中提取的4个数据点。第一次,我使用标记数据集上的分类器来设置一个基线,然后移除了75%的样本的标签,以测试在P&U数据集上执行的如何。如输出所示,这个的数据集不是最很难分类,但你可以看到,虽然PU分类器只是“知道”153个正样本,而其余1219个样本是没有标记的,它表现的和知道了所有的标记样本的分类器差不多。然而,它确实损失了17%的召回率,因此损失了相当多的正样本。不过无论怎样,相比于其他的方法,我相信这些结果是相当令人满意的。

===>> load data set <<===
data size: (1372, 5)
Target variable (fraud or not):
0    762
1    610
===>> create baseline classification results <<===
Classification results:
f1: 99.57%
roc: 99.57%
recall: 99.15%
precision: 100.00%
===>> classify on all the data set <<===
Target variable (labeled or not):
-1    1219
1     153
Classification results:
f1: 90.24%
roc: 91.11%
recall: 82.62%
precision: 99.41%

一些重点。首先,这种方法的性能在很大程度上取决于数据集的大小。在本例中,我使用了大约150个正样本和1200个未标记样本。这远不是这种方法的理想数据集。例如,如果我们只有100个样本,我们的分类器就会表现得很差。其次,正如所附的notebook所示,有一些变量需要调优(例如要设置的样本大小、用于分类的概率阈值等),但最重要的可能是所选的分类器及其参数。我选择使用XGBoost是因为它在具有很少特征的小型数据集上执行得相对较好,但需要注意的是,它并不是在所有场景中都执行得最好,测试正确的分类器非常重要。

代码在这里:https://github.com/a-agmon/pu-learn/blob/master/PU_Learning_EN.ipynb

英文原文:https://towardsdatascience.com/semi-supervised-classification-of-unlabeled-data-pu-learning-81f96e96f7cb

相关推荐

Vue 技术栈(全家桶)(vue technology)

Vue技术栈(全家桶)尚硅谷前端研究院第1章:Vue核心Vue简介官网英文官网:https://vuejs.org/中文官网:https://cn.vuejs.org/...

vue 基础- nextTick 的使用场景(vue的nexttick这个方法有什么用)

前言《vue基础》系列是再次回炉vue记的笔记,除了官网那部分知识点外,还会加入自己的一些理解。(里面会有部分和官网相同的文案,有经验的同学择感兴趣的阅读)在开发时,是不是遇到过这样的场景,响应...

vue3 组件初始化流程(vue组件初始化顺序)

学习完成响应式系统后,咋们来看看vue3组件的初始化流程既然是看vue组件的初始化流程,咋们先来创建基本的代码,跑跑流程(在app.vue中写入以下内容,来跑流程)...

vue3优雅的设置element-plus的table自动滚动到底部

场景我是需要在table最后添加一行数据,然后把滚动条滚动到最后。查网上的解决方案都是读取html结构,暴力的去获取,虽能解决问题,但是不喜欢这种打补丁的解决方案,我想着官方应该有相关的定义,于是就去...

Vue3为什么推荐使用ref而不是reactive

为什么推荐使用ref而不是reactivereactive本身具有很大局限性导致使用过程需要额外注意,如果忽视这些问题将对开发造成不小的麻烦;ref更像是vue2时代optionapi的data的替...

9、echarts 在 vue 中怎么引用?(必会)

首先我们初始化一个vue项目,执行vueinitwebpackechart,接着我们进入初始化的项目下。安装echarts,npminstallecharts-S//或...

无所不能,将 Vue 渲染到嵌入式液晶屏

该文章转载自公众号@前端时刻,https://mp.weixin.qq.com/s/WDHW36zhfNFVFVv4jO2vrA前言...

vue-element-admin 增删改查(五)(vue-element-admin怎么用)

此篇幅比较长,涉及到的小知识点也比较多,一定要耐心看完,记住学东西没有耐心可不行!!!一、添加和修改注:添加和编辑用到了同一个组件,也就是此篇文章你能学会如何封装组件及引用组件;第二能学会async和...

最全的 Vue 面试题+详解答案(vue面试题知识点大全)

前言本文整理了...

基于 vue3.0 桌面端朋友圈/登录验证+60s倒计时

今天给大家分享的是Vue3聊天实例中的朋友圈的实现及登录验证和倒计时操作。先上效果图这个是最新开发的vue3.x网页端聊天项目中的朋友圈模块。用到了ElementPlus...

不来看看这些 VUE 的生命周期钩子函数?| 原力计划

作者|huangfuyk责编|王晓曼出品|CSDN博客VUE的生命周期钩子函数:就是指在一个组件从创建到销毁的过程自动执行的函数,包含组件的变化。可以分为:创建、挂载、更新、销毁四个模块...

Vue3.5正式上线,父传子props用法更丝滑简洁

前言Vue3.5在2024-09-03正式上线,目前在Vue官网显最新版本已经是Vue3.5,其中主要包含了几个小改动,我留意到日常最常用的改动就是props了,肯定是用Vue3的人必用的,所以针对性...

Vue 3 生命周期完整指南(vue生命周期及使用)

Vue2和Vue3中的生命周期钩子的工作方式非常相似,我们仍然可以访问相同的钩子,也希望将它们能用于相同的场景。...

救命!这 10 个 Vue3 技巧藏太深了!性能翻倍 + 摸鱼神器全揭秘

前端打工人集合!是不是经常遇到这些崩溃瞬间:Vue3项目越写越卡,组件通信像走迷宫,复杂逻辑写得脑壳疼?别慌!作为在一线摸爬滚打多年的老前端,今天直接甩出10个超实用的Vue3实战技巧,手把...

怎么在 vue 中使用 form 清除校验状态?

在Vue中使用表单验证时,经常需要清除表单的校验状态。下面我将介绍一些方法来清除表单的校验状态。1.使用this.$refs...

取消回复欢迎 发表评论: