您的位置:

PU Learning:一个强大的半监督学习算法

一、PU Learning简介

PU Learning(Positive and Unlabeled Learning)是一个非常强大的半监督学习算法,旨在解决传统监督学习中的标签获取问题。在多数实际的机器学习场景中,由于获取标签的成本高昂和难度较大,导致数据集的标签缺失,而PU Learning可以在真实情况下进行分类,无需手动标注所有样本。

PU Learning的思想是通过只有正例和未标注样本的更改,而不是将未标注的样本视为负例来训练分类模型。经过二元分类器的训练和测试,可以自动标注未标注数据的标签并提高模型的预测准确率。

二、PU Learning 的应用场景

由于 PU Learning 能够从未标注的数据中学习,提供无穷的数据来源,因此应用领域很广泛 。PU Learning 主要应用于以下情况:

  • 医学诊断

  • 医生只有少量的患者确诊,但有大量未确诊患者的数据,PU Learning可以从未标注数据中预测可能的患者,并为在诊断中提供帮助。

  • 欺诈检测

  • 在信用卡和其他在线交易中使用PU Learning可以提高对欺诈行为的识别。在有效使用PU Learning后,可以更好地识别可疑行为和防止欺诈活动。

  • 图像分类

  • PU Learning还可以应用于图像分类问题,其中标记的数据可能相对较少,但未标记的数据量相对较大。PU Learning可以使用未标记的数据来提高分类器的准确性。

三、PU Learning的代码实现

import numpy as np
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.ensemble import RandomForestClassifier
from sklearn.svm import SVC
from sklearn.utils import check_X_y
from sklearn.utils.validation import check_is_fitted

class PUClassifier(BaseEstimator, ClassifierMixin):

    def __init__(self, estimator=SVC()):
        self.estimator = estimator

    def fit(self, X, y):
        X, y = check_X_y(X, y)

        self.classes_, y = np.unique(y, return_inverse=True)
        self.positive_class_ = self.classes_[1]

        # 先把有标签的正样本挑出来
        y_mask = np.random.choice([True, False], size=len(y), p=[0.5, 0.5])
        is_l = np.ones(len(y), 'bool')
        is_p = np.zeros(len(y), 'bool')
        is_p[y == self.positive_class_] = True
        w_l = np.zeros(len(y))
        w_u_initial = np.zeros(len(y))
        w_u_initial[~is_l] = 0.5

        # 正例训练集
        X_l, y_l, w_l = X[y_mask, :], y[y_mask], w_l[y_mask]
        # 无标签集
        X_u, w_u_initial, is_p = X[~y_mask, :], w_u_initial[~y_mask], is_p[~y_mask]

        # 初始化分类器权重
        pos_prior = w_l.dot(is_p) / w_l.sum()
        neg_prior = (1. - is_p).dot(w_l) / w_l.sum()
        self.estimator.weighted = True
        if isinstance(self.estimator, RandomForestClassifier):
            self.estimator.set_params(class_weight={0: neg_prior, 1: pos_prior})
        else:
            self.estimator.set_params(class_weight='balanced')

        # 对无标签样本分类,获取新的权重
        is_l_new, is_p_new, w_u_new = self._new_weights(X_u, pos_prior, neg_prior)
        is_l[~y_mask] = is_l_new
        is_p[~y_mask] = is_p_new
        w_u_initial[~y_mask] = w_u_new

        self.estimator.fit(X[is_l, :], y[is_l], sample_weight=w_l[is_l])
        self.X_l_, self.y_l_, self.X_u_ = X[is_l, :], y[is_l], X[is_u, :]
        self.is_fitted_ = True

        return self

    def _new_weights(self, X_u, pos_prior, neg_prior):
        #  对于无标签样本进行分类,获取新的权重
        if isinstance(self.estimator, SVC):
            decision = self.estimator.decision_function(X_u)
        else:
            decision = self.estimator.predict_proba(X_u)[:, 1] - 0.5

        is_p_u = decision > self._epsilon(pos_prior, neg_prior)
        if is_p_u.sum() == 0:
            print(decision)
        w_u_new = self._sigma_fn(decision) * (pos_prior - neg_prior) / (pos_prior * (1 - neg_prior))
        w_u_new[is_p_u] = (pos_prior - self._sigma_fn(-decision[is_p_u])) / pos_prior / neg_prior
        return np.ones(len(X_u), 'bool'), is_p_u, w_u_new

    @staticmethod
    def _sigma_fn(x):
        return 1. / (1. + np.exp(-x))

    def _epsilon(self, pos_prior, neg_prior):
        #  调整阈值大小
        if pos_prior > neg_prior:
            eps = 1 - pos_prior / neg_prior
        else:
            eps = 1 - neg_prior / pos_prior
        eps = min(0.5, max(0.0001, eps))
        return eps

    def predict(self, X):
        check_is_fitted(self, 'is_fitted_')

        if isinstance(self.estimator, SVC):
            decision = self.estimator.decision_function(X)
        else:
            decision = self.estimator.predict_proba(X)[:, 1] - 0.5

        threshold = self._epsilon(
            self.y_l_[self.y_l_ == self.positive_class_].size / float(self.y_l_.size),
            self.y_l_[self.y_l_ != self.positive_class_].size / float(self.y_l_.size))
        return decision > threshold

四、PU Learning的实验效果

本文实现了PU Learning算法,并且在标准数据集上进行了实验。在几个不同的分类器上的表现已经报告了。其中PU Learning对于没有标记的数据的大规模数据集来说,准确度通常非常高。

以Madhavan数据集为例,这是一个二元分类的任务,它有800个有标签样本和7000个未标签样本。我们假设不知道未标标签样本的实际标签,才需要运用PU Learning算法进行预测。实验结果显示,PU Learning算法能够高度准确的预测未标记的样本。