一、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算法能够高度准确的预测未标记的样本。