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

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

wptr33 2025-02-27 16:56 25 浏览

作者: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

相关推荐

MySQL进阶五之自动读写分离mysql-proxy

自动读写分离目前,大量现网用户的业务场景中存在读多写少、业务负载无法预测等情况,在有大量读请求的应用场景下,单个实例可能无法承受读取压力,甚至会对业务产生影响。为了实现读取能力的弹性扩展,分担数据库压...

Postgres vs MySQL_vs2022连接mysql数据库

...

3分钟短文 | Laravel SQL筛选两个日期之间的记录,怎么写?

引言今天说一个细分的需求,在模型中,或者使用laravel提供的EloquentORM功能,构造查询语句时,返回位于两个指定的日期之间的条目。应该怎么写?本文通过几个例子,为大家梳理一下。学习时...

一文由浅入深带你完全掌握MySQL的锁机制原理与应用

本文将跟大家聊聊InnoDB的锁。本文比较长,包括一条SQL是如何加锁的,一些加锁规则、如何分析和解决死锁问题等内容,建议耐心读完,肯定对大家有帮助的。为什么需要加锁呢?...

验证Mysql中联合索引的最左匹配原则

后端面试中一定是必问mysql的,在以往的面试中好几个面试官都反馈我Mysql基础不行,今天来着重复习一下自己的弱点知识。在Mysql调优中索引优化又是非常重要的方法,不管公司的大小只要后端项目中用到...

MySQL索引解析(联合索引/最左前缀/覆盖索引/索引下推)

目录1.索引基础...

你会看 MySQL 的执行计划(EXPLAIN)吗?

SQL执行太慢怎么办?我们通常会使用EXPLAIN命令来查看SQL的执行计划,然后根据执行计划找出问题所在并进行优化。用法简介...

MySQL 从入门到精通(四)之索引结构

索引概述索引(index),是帮助MySQL高效获取数据的数据结构(有序),在数据之外,数据库系统还维护者满足特定查询算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构...

mysql总结——面试中最常问到的知识点

mysql作为开源数据库中的榜一大哥,一直是面试官们考察的重中之重。今天,我们来总结一下mysql的知识点,供大家复习参照,看完这些知识点,再加上一些边角细节,基本上能够应付大多mysql相关面试了(...

mysql总结——面试中最常问到的知识点(2)

首先我们回顾一下上篇内容,主要复习了索引,事务,锁,以及SQL优化的工具。本篇文章接着写后面的内容。性能优化索引优化,SQL中索引的相关优化主要有以下几个方面:最好是全匹配。如果是联合索引的话,遵循最...

MySQL基础全知全解!超详细无废话!轻松上手~

本期内容提醒:全篇2300+字,篇幅较长,可搭配饭菜一同“食”用,全篇无废话(除了这句),干货满满,可收藏供后期反复观看。注:MySQL中语法不区分大小写,本篇中...

深入剖析 MySQL 中的锁机制原理_mysql 锁详解

在互联网软件开发领域,MySQL作为一款广泛应用的关系型数据库管理系统,其锁机制在保障数据一致性和实现并发控制方面扮演着举足轻重的角色。对于互联网软件开发人员而言,深入理解MySQL的锁机制原理...

Java 与 MySQL 性能优化:MySQL分区表设计与性能优化全解析

引言在数据库管理领域,随着数据量的不断增长,如何高效地管理和操作数据成为了一个关键问题。MySQL分区表作为一种有效的数据管理技术,能够将大型表划分为多个更小、更易管理的分区,从而提升数据库的性能和可...

MySQL基础篇:DQL数据查询操作_mysql 查

一、基础查询DQL基础查询语法SELECT字段列表FROM表名列表WHERE条件列表GROUPBY分组字段列表HAVING分组后条件列表ORDERBY排序字段列表LIMIT...

MySql:索引的基本使用_mysql索引的使用和原理

一、索引基础概念1.什么是索引?索引是数据库表的特殊数据结构(通常是B+树),用于...