forked from moreo/QuaPy
many aggregative methods added
This commit is contained in:
parent
78988c87f9
commit
a882424eeb
3
TODO.txt
3
TODO.txt
|
@ -1,2 +1,3 @@
|
||||||
Documentation with sphinx
|
Documentation with sphinx
|
||||||
The parallel training in svmperf seems not to work
|
The parallel training in svmperf seems not to work
|
||||||
|
Add "prepare svmperf for quantification" script
|
|
@ -0,0 +1,28 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -x
|
||||||
|
|
||||||
|
URL=http://download.joachims.org/svm_perf/current/svm_perf.tar.gz
|
||||||
|
FILE=./svm_perf.tar.gz
|
||||||
|
wget $URL $FILE
|
||||||
|
mkdir ./svm_perf
|
||||||
|
tar xvzf $FILE -C ./svm_perf
|
||||||
|
rm $FILE
|
||||||
|
|
||||||
|
#para crear el patch [para mi]
|
||||||
|
#diff -Naur svm_perf svm_perf_quantification > svm-perf-quantification-ext.patch
|
||||||
|
|
||||||
|
#para crear la modificacion
|
||||||
|
#cp svm_perf svm_perf_quantification -r [ESTO NO HACE FALTA]
|
||||||
|
patch -s -p0 < svm-perf-quantification-ext.patch
|
||||||
|
mv svm_perf svm_perf_quantification
|
||||||
|
cd svm_perf_quantification
|
||||||
|
make
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
from .dataset import *
|
||||||
|
from . import functional
|
||||||
|
from . import method
|
||||||
|
from . import error
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
from sklearn.metrics import f1_score
|
||||||
|
from settings import SAMPLE_SIZE
|
||||||
|
|
||||||
|
|
||||||
|
def f1e(y_true, y_pred):
|
||||||
|
return 1. - f1_score(y_true, y_pred, average='macro')
|
||||||
|
|
||||||
|
|
||||||
|
def acce(y_true, y_pred):
|
||||||
|
acc = (y_true == y_pred).mean()
|
||||||
|
return 1. - acc
|
||||||
|
|
||||||
|
|
||||||
|
def mae(prevs, prevs_hat):
|
||||||
|
return ae(prevs, prevs_hat).mean()
|
||||||
|
|
||||||
|
|
||||||
|
def ae(p, p_hat):
|
||||||
|
assert p.shape == p_hat.shape, 'wrong shape'
|
||||||
|
return abs(p_hat-p).mean(axis=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def mrae(p, p_hat, eps=1./(2. * SAMPLE_SIZE)):
|
||||||
|
return rae(p, p_hat, eps).mean()
|
||||||
|
|
||||||
|
|
||||||
|
def rae(p, p_hat, eps=1./(2. * SAMPLE_SIZE)):
|
||||||
|
p = smooth(p, eps)
|
||||||
|
p_hat = smooth(p_hat, eps)
|
||||||
|
return (abs(p-p_hat)/p).mean(axis=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def smooth(p, eps):
|
||||||
|
n_classes = p.shape[-1]
|
||||||
|
return (p+eps)/(eps*n_classes + 1)
|
||||||
|
|
||||||
|
|
||||||
|
CLASSIFICATION_ERROR = {f1e, acce}
|
||||||
|
QUANTIFICATION_ERROR = {mae, mrae}
|
||||||
|
|
||||||
|
f1_error = f1e
|
||||||
|
acc_error = acce
|
||||||
|
mean_absolute_error = mae
|
||||||
|
absolute_error = ae
|
||||||
|
mean_relative_absolute_error = mrae
|
||||||
|
relative_absolute_error = rae
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
from collections import defaultdict
|
||||||
|
import numpy as np
|
||||||
|
import itertools
|
||||||
|
|
||||||
|
|
||||||
|
def artificial_prevalence_sampling(dimensions, n_prevalences=21, repeat=1, return_constrained_dim=False):
|
||||||
|
s = np.linspace(0., 1., n_prevalences, endpoint=True)
|
||||||
|
s = [s] * (dimensions - 1)
|
||||||
|
prevs = [p for p in itertools.product(*s, repeat=1) if sum(p)<=1]
|
||||||
|
if return_constrained_dim:
|
||||||
|
prevs = [p+(1-sum(p),) for p in prevs]
|
||||||
|
prevs = np.asarray(prevs).reshape(len(prevs), -1)
|
||||||
|
if repeat>1:
|
||||||
|
prevs = np.repeat(prevs, repeat, axis=0)
|
||||||
|
return prevs
|
||||||
|
|
||||||
|
|
||||||
|
def prevalence_from_labels(labels, n_classes):
|
||||||
|
unique, counts = np.unique(labels, return_counts=True)
|
||||||
|
by_class = defaultdict(lambda:0, dict(zip(unique, counts)))
|
||||||
|
prevalences = np.asarray([by_class[ci] for ci in range(n_classes)], dtype=np.float)
|
||||||
|
prevalences /= prevalences.sum()
|
||||||
|
return prevalences
|
||||||
|
|
||||||
|
|
||||||
|
def prevalence_from_probabilities(posteriors, binarize: bool = False):
|
||||||
|
if binarize:
|
||||||
|
predictions = np.argmax(posteriors, axis=-1)
|
||||||
|
return prevalence_from_labels(predictions, n_classes=posteriors.shape[1])
|
||||||
|
else:
|
||||||
|
prevalences = posteriors.mean(axis=0)
|
||||||
|
prevalences /= prevalences.sum()
|
||||||
|
return prevalences
|
||||||
|
|
||||||
|
|
||||||
|
def strprev(prevalences, prec=3):
|
||||||
|
return '['+ ', '.join([f'{p:.{prec}f}' for p in prevalences]) + ']'
|
||||||
|
|
||||||
|
|
||||||
|
def adjusted_quantification(prevalence_estim, tpr, fpr, clip=True):
|
||||||
|
den = tpr - fpr
|
||||||
|
if den == 0:
|
||||||
|
den += 1e-8
|
||||||
|
adjusted = (prevalence_estim - fpr) / den
|
||||||
|
if clip:
|
||||||
|
adjusted = np.clip(adjusted, 0., 1.)
|
||||||
|
return adjusted
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
from . import aggregative as agg
|
||||||
|
from . import non_aggregative as nagg
|
||||||
|
|
||||||
|
|
||||||
|
AGGREGATIVE_METHODS = {
|
||||||
|
agg.ClassifyAndCount,
|
||||||
|
agg.AdjustedClassifyAndCount,
|
||||||
|
agg.ProbabilisticClassifyAndCount,
|
||||||
|
agg.ProbabilisticAdjustedClassifyAndCount,
|
||||||
|
agg.ExplicitLossMinimisation,
|
||||||
|
agg.ExpectationMaximizationQuantifier,
|
||||||
|
}
|
||||||
|
|
||||||
|
NON_AGGREGATIVE_METHODS = {
|
||||||
|
nagg.MaximumLikelihoodPrevalenceEstimation
|
||||||
|
}
|
||||||
|
|
||||||
|
QUANTIFICATION_METHODS = AGGREGATIVE_METHODS | NON_AGGREGATIVE_METHODS
|
||||||
|
|
||||||
|
|
||||||
|
# common alisases
|
||||||
|
CC = agg.ClassifyAndCount
|
||||||
|
ACC = agg.AdjustedClassifyAndCount
|
||||||
|
PCC = agg.ProbabilisticClassifyAndCount
|
||||||
|
PACC = agg.ProbabilisticAdjustedClassifyAndCount
|
||||||
|
ELM = agg.ExplicitLossMinimisation
|
||||||
|
EMQ = agg.ExpectationMaximizationQuantifier
|
||||||
|
MLPE = nagg.MaximumLikelihoodPrevalenceEstimation
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,351 @@
|
||||||
|
import numpy as np
|
||||||
|
from .base import *
|
||||||
|
from ..error import mae
|
||||||
|
import functional as F
|
||||||
|
from ..classification.svmperf import SVMperf
|
||||||
|
from ..dataset import LabelledCollection
|
||||||
|
from sklearn.metrics import confusion_matrix
|
||||||
|
from sklearn.calibration import CalibratedClassifierCV
|
||||||
|
from joblib import Parallel, delayed
|
||||||
|
|
||||||
|
|
||||||
|
# Abstract classes
|
||||||
|
# ------------------------------------
|
||||||
|
|
||||||
|
class AggregativeQuantifier(BaseQuantifier):
|
||||||
|
"""
|
||||||
|
Abstract class for quantification methods that base their estimations on the aggregation of classification
|
||||||
|
results. Aggregative Quantifiers thus implement a _classify_ method and maintain a _learner_ attribute.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def fit(self, data: LabelledCollection, fit_learner=True, *args): ...
|
||||||
|
|
||||||
|
def classify(self, documents):
|
||||||
|
return self.learner.predict(documents)
|
||||||
|
|
||||||
|
def get_params(self, deep=True):
|
||||||
|
return self.learner.get_params()
|
||||||
|
|
||||||
|
def set_params(self, **parameters):
|
||||||
|
self.learner.set_params(**parameters)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def n_classes(self):
|
||||||
|
return len(self.classes)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def classes(self):
|
||||||
|
return self.learner.classes_
|
||||||
|
|
||||||
|
|
||||||
|
class AggregativeProbabilisticQuantifier(AggregativeQuantifier):
|
||||||
|
"""
|
||||||
|
Abstract class for quantification methods that base their estimations on the aggregation of posterior probabilities
|
||||||
|
as returned by a probabilistic classifier. Aggregative Probabilistic Quantifiers thus extend Aggregative
|
||||||
|
Quantifiersimplement by implementing a _soft_classify_ method returning values in [0,1] -- the posterior
|
||||||
|
probabilities.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def soft_classify(self, data):
|
||||||
|
return self.learner.predict_proba(data)
|
||||||
|
|
||||||
|
def set_params(self, **parameters):
|
||||||
|
if isinstance(self.learner, CalibratedClassifierCV):
|
||||||
|
parameters={'base_estimator__'+k:v for k,v in parameters.items()}
|
||||||
|
self.learner.set_params(**parameters)
|
||||||
|
|
||||||
|
|
||||||
|
# Helper
|
||||||
|
# ------------------------------------
|
||||||
|
def training_helper(learner,
|
||||||
|
data: LabelledCollection,
|
||||||
|
fit_learner: bool = True,
|
||||||
|
ensure_probabilistic=False,
|
||||||
|
train_val_split=None):
|
||||||
|
"""
|
||||||
|
Training procedure common to all Aggregative Quantifiers.
|
||||||
|
:param learner: the learner to be fit
|
||||||
|
:param data: the data on which to fit the learner. If requested, the data will be split before fitting the learner.
|
||||||
|
:param fit_learner: whether or not to fit the learner
|
||||||
|
:param ensure_probabilistic: if True, guarantees that the resulting classifier implements predict_proba (if the
|
||||||
|
learner is not probabilistic, then a CalibratedCV instance of it is trained)
|
||||||
|
:param train_val_split: if specified, indicates the proportion of training documents on which to fit the learner
|
||||||
|
:return: the learner trained on the training set, and the unused data (a _LabelledCollection_ if train_val_split>0
|
||||||
|
or None otherwise)
|
||||||
|
"""
|
||||||
|
if fit_learner:
|
||||||
|
if ensure_probabilistic:
|
||||||
|
if not hasattr(learner, 'predict_proba'):
|
||||||
|
print(f'The learner {learner.__class__.__name__} does not seem to be probabilistic. '
|
||||||
|
f'The learner will be calibrated.')
|
||||||
|
learner = CalibratedClassifierCV(learner, cv=5)
|
||||||
|
if train_val_split is not None:
|
||||||
|
if not (0 < train_val_split < 1):
|
||||||
|
raise ValueError(f'train/val split {train_val_split} out of range, must be in (0,1)')
|
||||||
|
train, unused = data.split_stratified(train_prop=train_val_split)
|
||||||
|
else:
|
||||||
|
train, unused = data, None
|
||||||
|
learner.fit(train.instances, train.labels)
|
||||||
|
else:
|
||||||
|
if ensure_probabilistic:
|
||||||
|
if not hasattr(learner, 'predict_proba'):
|
||||||
|
raise AssertionError('error: the learner cannot be calibrated since fit_learner is set to False')
|
||||||
|
unused = data
|
||||||
|
|
||||||
|
return learner, unused
|
||||||
|
|
||||||
|
|
||||||
|
# Methods
|
||||||
|
# ------------------------------------
|
||||||
|
class ClassifyAndCount(AggregativeQuantifier):
|
||||||
|
"""
|
||||||
|
The most basic Quantification method. One that simply classifies all instances and countes how many have been
|
||||||
|
attributed each of the classes in order to compute class prevalence estimates.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, learner):
|
||||||
|
self.learner = learner
|
||||||
|
|
||||||
|
def fit(self, data: LabelledCollection, fit_learner=True, *args):
|
||||||
|
"""
|
||||||
|
Trains the Classify & Count method unless _fit_learner_ is False, in which case it is assumed to be already fit.
|
||||||
|
:param data: training data
|
||||||
|
:param fit_learner: if False, the classifier is assumed to be fit
|
||||||
|
:param args: unused
|
||||||
|
:return: self
|
||||||
|
"""
|
||||||
|
self.learner, _ = training_helper(self.learner, data, fit_learner)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def quantify(self, documents, *args):
|
||||||
|
classification = self.classify(documents) # classify
|
||||||
|
return F.prevalence_from_labels(classification, self.n_classes) # & count
|
||||||
|
|
||||||
|
|
||||||
|
class AdjustedClassifyAndCount(AggregativeQuantifier):
|
||||||
|
|
||||||
|
def __init__(self, learner):
|
||||||
|
self.learner = learner
|
||||||
|
|
||||||
|
def fit(self, data: LabelledCollection, fit_learner=True, train_val_split=0.6):
|
||||||
|
self.learner, validation = training_helper(self.learner, data, fit_learner, train_val_split=train_val_split)
|
||||||
|
self.cc = ClassifyAndCount(self.learner)
|
||||||
|
y_ = self.cc.classify(validation.instances)
|
||||||
|
y = validation.labels
|
||||||
|
# estimate the matrix with entry (i,j) being the estimate of P(yi|yj), that is, the probability that a
|
||||||
|
# document that belongs to yj ends up being classified as belonging to yi
|
||||||
|
self.Pte_cond_estim_ = confusion_matrix(y,y_).T / validation.counts()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def quantify(self, documents, *args):
|
||||||
|
prevs_estim = self.cc.quantify(documents)
|
||||||
|
# solve for the linear system Ax = B with A=Pte_cond_estim and B = prevs_estim
|
||||||
|
A = self.Pte_cond_estim_
|
||||||
|
B = prevs_estim
|
||||||
|
try:
|
||||||
|
adjusted_prevs = np.linalg.solve(A, B)
|
||||||
|
adjusted_prevs = np.clip(adjusted_prevs, 0, 1)
|
||||||
|
adjusted_prevs /= adjusted_prevs.sum()
|
||||||
|
except np.linalg.LinAlgError:
|
||||||
|
adjusted_prevs = prevs_estim # no way to adjust them!
|
||||||
|
return adjusted_prevs
|
||||||
|
|
||||||
|
def classify(self, data):
|
||||||
|
return self.cc.classify(data)
|
||||||
|
|
||||||
|
|
||||||
|
class ProbabilisticClassifyAndCount(AggregativeProbabilisticQuantifier):
|
||||||
|
def __init__(self, learner):
|
||||||
|
self.learner = learner
|
||||||
|
|
||||||
|
def fit(self, data : LabelledCollection, fit_learner=True, *args):
|
||||||
|
self.learner, _ = training_helper(self.learner, data, fit_learner, ensure_probabilistic=True)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def quantify(self, documents, *args):
|
||||||
|
posteriors = self.soft_classify(documents) # classify
|
||||||
|
prevalences = F.prevalence_from_probabilities(posteriors, binarize=False) # & count
|
||||||
|
return prevalences
|
||||||
|
|
||||||
|
|
||||||
|
class ProbabilisticAdjustedClassifyAndCount(AggregativeQuantifier):
|
||||||
|
|
||||||
|
def __init__(self, learner):
|
||||||
|
self.learner = learner
|
||||||
|
|
||||||
|
def fit(self, data: LabelledCollection, fit_learner=True, train_val_split=0.6):
|
||||||
|
self.learner, validation = training_helper(
|
||||||
|
self.learner, data, fit_learner, ensure_probabilistic=True, train_val_split=train_val_split
|
||||||
|
)
|
||||||
|
self.pcc = ProbabilisticClassifyAndCount(self.learner)
|
||||||
|
y_ = self.pcc.classify(validation.instances)
|
||||||
|
y = validation.labels
|
||||||
|
# estimate the matrix with entry (i,j) being the estimate of P(yi|yj), that is, the probability that a
|
||||||
|
# document that belongs to yj ends up being classified as belonging to yi
|
||||||
|
self.Pte_cond_estim_ = confusion_matrix(y, y_).T / validation.counts()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def quantify(self, documents, *args):
|
||||||
|
prevs_estim = self.pcc.quantify(documents)
|
||||||
|
A = self.Pte_cond_estim_
|
||||||
|
B = prevs_estim
|
||||||
|
try:
|
||||||
|
adjusted_prevs = np.linalg.solve(A, B)
|
||||||
|
adjusted_prevs = np.clip(adjusted_prevs, 0, 1)
|
||||||
|
adjusted_prevs /= adjusted_prevs.sum()
|
||||||
|
except np.linalg.LinAlgError:
|
||||||
|
adjusted_prevs = prevs_estim # no way to adjust them!
|
||||||
|
return adjusted_prevs
|
||||||
|
|
||||||
|
def classify(self, data):
|
||||||
|
return self.pcc.classify(data)
|
||||||
|
|
||||||
|
|
||||||
|
class ExpectationMaximizationQuantifier(AggregativeProbabilisticQuantifier):
|
||||||
|
|
||||||
|
MAX_ITER = 1000
|
||||||
|
EPSILON = 1e-4
|
||||||
|
|
||||||
|
def __init__(self, learner, verbose=False):
|
||||||
|
self.learner = learner
|
||||||
|
self.verbose = verbose
|
||||||
|
|
||||||
|
def fit(self, data: LabelledCollection, fit_learner=True, *args):
|
||||||
|
self.learner, _ = training_helper(self.learner, data, fit_learner, ensure_probabilistic=True)
|
||||||
|
self.train_prevalence = F.prevalence_from_labels(data.labels, self.n_classes)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def quantify(self, X, epsilon=EPSILON):
|
||||||
|
tr_prev=self.train_prevalence
|
||||||
|
posteriors = self.soft_classify(X)
|
||||||
|
return self.EM(tr_prev, posteriors, self.verbose, epsilon)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def EM(cls, tr_prev, posterior_probabilities, verbose=False, epsilon=EPSILON):
|
||||||
|
Px = posterior_probabilities
|
||||||
|
Ptr = np.copy(tr_prev)
|
||||||
|
qs = np.copy(Ptr) # qs (the running estimate) is initialized as the training prevalence
|
||||||
|
|
||||||
|
s, converged = 0, False
|
||||||
|
qs_prev_ = None
|
||||||
|
while not converged and s < ExpectationMaximizationQuantifier.MAX_ITER:
|
||||||
|
# E-step: ps is Ps(y=+1|xi)
|
||||||
|
ps_unnormalized = (qs / Ptr) * Px
|
||||||
|
ps = ps_unnormalized / ps_unnormalized.sum(axis=1).reshape(-1,1)
|
||||||
|
|
||||||
|
# M-step: qs_pos is Ps+1(y=+1)
|
||||||
|
qs = ps.mean(axis=0)
|
||||||
|
|
||||||
|
if qs_prev_ is not None and mae(qs, qs_prev_) < epsilon and s>10:
|
||||||
|
converged = True
|
||||||
|
|
||||||
|
qs_prev_ = qs
|
||||||
|
s += 1
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print('-'*80)
|
||||||
|
|
||||||
|
if not converged:
|
||||||
|
raise UserWarning('the method has reached the maximum number of iterations; it might have not converged')
|
||||||
|
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
|
# todo: from here
|
||||||
|
def train_task(c, learners, data):
|
||||||
|
learners[c].fit(data.documents, data.labels == c)
|
||||||
|
|
||||||
|
|
||||||
|
def binary_quant_task(c, learners, X):
|
||||||
|
predictions_ci = learners[c].predict(X)
|
||||||
|
return predictions_ci.mean() # since the predictions array is binary
|
||||||
|
|
||||||
|
|
||||||
|
class OneVsAllELM(AggregativeQuantifier):
|
||||||
|
|
||||||
|
def __init__(self, svmperf_base, loss, n_jobs=-1, **kwargs):
|
||||||
|
self.svmperf_base = svmperf_base
|
||||||
|
self.loss = loss
|
||||||
|
self.n_jobs = n_jobs
|
||||||
|
self.kwargs = kwargs
|
||||||
|
|
||||||
|
def fit(self, data: LabelledCollection, fit_learner=True, *args):
|
||||||
|
assert fit_learner, 'the method requires that fit_learner=True'
|
||||||
|
|
||||||
|
self.learners = {c: SVMperf(self.svmperf_base, loss=self.loss, **self.kwargs) for c in data.classes_}
|
||||||
|
Parallel(n_jobs=self.n_jobs, backend='threading')(
|
||||||
|
delayed(train_task)(c, self.learners, data) for c in self.learners.keys()
|
||||||
|
)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def quantify(self, X, y=None):
|
||||||
|
prevalences = np.asarray(
|
||||||
|
Parallel(n_jobs=self.n_jobs, backend='threading')(
|
||||||
|
delayed(binary_quant_task)(c, self.learners, X) for c in self.learners.keys()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
prevalences /= prevalences.sum()
|
||||||
|
return prevalences
|
||||||
|
|
||||||
|
@property
|
||||||
|
def classes(self):
|
||||||
|
return sorted(self.learners.keys())
|
||||||
|
|
||||||
|
def preclassify_collection(self, data: LabelledCollection):
|
||||||
|
classifications = []
|
||||||
|
for class_ in data.classes_:
|
||||||
|
classifications.append(self.learners[class_].predict(data.instances))
|
||||||
|
classifications = np.vstack(classifications).T
|
||||||
|
precomputed = LabelledCollection(classifications, data.labels)
|
||||||
|
return precomputed
|
||||||
|
|
||||||
|
def set_params(self, **parameters):
|
||||||
|
self.kwargs=parameters
|
||||||
|
|
||||||
|
def get_params(self, deep=True):
|
||||||
|
return self.kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class ExplicitLossMinimisation(AggregativeQuantifier):
|
||||||
|
|
||||||
|
def __init__(self, svmperf_base, loss, **kwargs):
|
||||||
|
self.learner = SVMperf(svmperf_base, loss=loss, **kwargs)
|
||||||
|
|
||||||
|
def fit(self, data: LabelledCollection, fit_learner=True, *args):
|
||||||
|
assert fit_learner, 'the method requires that fit_learner=True'
|
||||||
|
self.learner.fit(data.instances, data.labels)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def quantify(self, X, y=None):
|
||||||
|
predictions = self.learner.predict(X)
|
||||||
|
return F.prevalence_from_labels(predictions, self.learner.n_classes_)
|
||||||
|
|
||||||
|
def classify(self, X, y=None):
|
||||||
|
return self.learner.predict(X)
|
||||||
|
|
||||||
|
|
||||||
|
class SVMQ(ExplicitLossMinimisation):
|
||||||
|
def __init__(self, svmperf_base, **kwargs):
|
||||||
|
super(SVMQ, self).__init__(svmperf_base, loss='q', **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SVMKLD(ExplicitLossMinimisation):
|
||||||
|
def __init__(self, svmperf_base, **kwargs):
|
||||||
|
super(SVMKLD, self).__init__(svmperf_base, loss='kld', **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SVMNKLD(ExplicitLossMinimisation):
|
||||||
|
def __init__(self, svmperf_base, **kwargs):
|
||||||
|
super(SVMNKLD, self).__init__(svmperf_base, loss='nkld', **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SVMAE(ExplicitLossMinimisation):
|
||||||
|
def __init__(self, svmperf_base, **kwargs):
|
||||||
|
super(SVMAE, self).__init__(svmperf_base, loss='mae', **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class SVMRAE(ExplicitLossMinimisation):
|
||||||
|
def __init__(self, svmperf_base, **kwargs):
|
||||||
|
super(SVMRAE, self).__init__(svmperf_base, loss='mrae', **kwargs)
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
import quapy as qp
|
||||||
|
|
||||||
|
|
||||||
|
# Base Quantifier abstract class
|
||||||
|
# ------------------------------------
|
||||||
|
class BaseQuantifier(metaclass=ABCMeta):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def fit(self, data: qp.LabelledCollection, *args): ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def quantify(self, documents, *args): ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def set_params(self, **parameters): ...
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def get_params(self, deep=True): ...
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
from quapy import LabelledCollection
|
||||||
|
from .base import BaseQuantifier
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MaximumLikelihoodPrevalenceEstimation(BaseQuantifier):
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def fit(self, data: LabelledCollection, *args):
|
||||||
|
self.estimated_prevalence = data.prevalence()
|
||||||
|
|
||||||
|
def quantify(self, documents, *args):
|
||||||
|
return self.estimated_prevalence
|
||||||
|
|
||||||
|
def get_params(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_params(self, **parameters):
|
||||||
|
pass
|
|
@ -0,0 +1,540 @@
|
||||||
|
diff -ruN svm_perf/svm_struct/svm_struct_main.c svm_perf_quantification/svm_struct/svm_struct_main.c
|
||||||
|
--- svm_perf/svm_struct/svm_struct_main.c 2020-10-28 12:23:19.000000000 +0100
|
||||||
|
+++ svm_perf_quantification/svm_struct/svm_struct_main.c 2020-10-28 12:23:53.000000000 +0100
|
||||||
|
@@ -128,7 +128,8 @@
|
||||||
|
struct_parm->newconstretrain=100;
|
||||||
|
struct_parm->ccache_size=5;
|
||||||
|
struct_parm->batch_size=100;
|
||||||
|
-
|
||||||
|
+ struct_parm->loss_parm=1.0;
|
||||||
|
+ struct_parm->beta=1.0; // AIC-QBETA
|
||||||
|
strcpy (modelfile, "svm_struct_model");
|
||||||
|
strcpy (learn_parm->predfile, "trans_predictions");
|
||||||
|
strcpy (learn_parm->alphafile, "");
|
||||||
|
@@ -170,6 +171,7 @@
|
||||||
|
case 'p': i++; struct_parm->slack_norm=atol(argv[i]); break;
|
||||||
|
case 'e': i++; struct_parm->epsilon=atof(argv[i]); break;
|
||||||
|
case 'k': i++; struct_parm->newconstretrain=atol(argv[i]); break;
|
||||||
|
+ case 'j': i++; struct_parm->beta=atof(argv[i]); break; // AIC-QBETA
|
||||||
|
case 'h': i++; learn_parm->svm_iter_to_shrink=atol(argv[i]); break;
|
||||||
|
case '#': i++; learn_parm->maxiter=atol(argv[i]); break;
|
||||||
|
case 'm': i++; learn_parm->kernel_cache_size=atol(argv[i]); break;
|
||||||
|
@@ -189,6 +191,7 @@
|
||||||
|
case '-': strcpy(struct_parm->custom_argv[struct_parm->custom_argc++],argv[i]);i++; strcpy(struct_parm->custom_argv[struct_parm->custom_argc++],argv[i]);break;
|
||||||
|
case 'v': i++; (*struct_verbosity)=atol(argv[i]); break;
|
||||||
|
case 'y': i++; (*verbosity)=atol(argv[i]); break;
|
||||||
|
+ case '!': i++; struct_parm->loss_parm=atof(argv[i]); break;
|
||||||
|
default: printf("\nUnrecognized option %s!\n\n",argv[i]);
|
||||||
|
print_help();
|
||||||
|
exit(0);
|
||||||
|
@@ -396,6 +399,9 @@
|
||||||
|
printf(" (in the same order as in the training set)\n");
|
||||||
|
printf("Application-Specific Options:\n");
|
||||||
|
print_struct_help();
|
||||||
|
+ printf("*************************************************\n"); // AIC-QBETA
|
||||||
|
+ printf(" -j float -> parameter beta for qbeta-based loss functions (default: 1.0)\n");
|
||||||
|
+ printf("*************************************************\n");
|
||||||
|
wait_any_key();
|
||||||
|
|
||||||
|
printf("\nMore details in:\n");
|
||||||
|
diff -ruN svm_perf/svm_struct_api.c svm_perf_quantification/svm_struct_api.c
|
||||||
|
--- svm_perf/svm_struct_api.c 2020-10-28 12:23:19.000000000 +0100
|
||||||
|
+++ svm_perf_quantification/svm_struct_api.c 2020-10-28 12:23:53.000000000 +0100
|
||||||
|
@@ -20,6 +20,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
+#include <math.h>
|
||||||
|
#include "svm_struct_api.h"
|
||||||
|
#include "svm_light/svm_common.h"
|
||||||
|
#include "svm_struct/svm_struct_common.h"
|
||||||
|
@@ -27,7 +28,9 @@
|
||||||
|
|
||||||
|
#define MAX(x,y) ((x) < (y) ? (y) : (x))
|
||||||
|
#define MIN(x,y) ((x) > (y) ? (y) : (x))
|
||||||
|
+#define ABS(x) ((x) < (0) ? (-(x)) : (x))
|
||||||
|
#define SIGN(x) ((x) > (0) ? (1) : (((x) < (0) ? (-1) : (0))))
|
||||||
|
+#define PI (3.141592653589793)
|
||||||
|
|
||||||
|
int compareup(const void *a, const void *b)
|
||||||
|
{
|
||||||
|
@@ -72,6 +75,16 @@
|
||||||
|
double rocarea(LABEL y, LABEL ybar);
|
||||||
|
double prbep(LABEL y, LABEL ybar);
|
||||||
|
double avgprec(LABEL y, LABEL ybar);
|
||||||
|
+/* AIC-QBETA */
|
||||||
|
+double gm(int a, int b, int c, int d);
|
||||||
|
+double nae(int a, int b, int c, int d);
|
||||||
|
+double ae(int a, int b, int c, int d);
|
||||||
|
+double rae(int a, int b, int c, int d);
|
||||||
|
+double Qbeta(int a, int b, int c, int d, double beta);
|
||||||
|
+double Qbeta_acc(int a, int b, int c, int d, double beta);
|
||||||
|
+double Qbeta_f1(int a, int b, int c, int d, double beta);
|
||||||
|
+double Qbeta_gm(int a, int b, int c, int d, double beta);
|
||||||
|
+/* AIC-QBETA */
|
||||||
|
|
||||||
|
double zeroone_loss(int a, int b, int c, int d);
|
||||||
|
double fone_loss(int a, int b, int c, int d);
|
||||||
|
@@ -82,6 +95,23 @@
|
||||||
|
double swappedpairs_loss(LABEL y, LABEL ybar);
|
||||||
|
double avgprec_loss(LABEL y, LABEL ybar);
|
||||||
|
|
||||||
|
+double kldiv(int a, int b, int c, int d); // KLD
|
||||||
|
+double kldiv_loss(int a, int b, int c, int d); // KLD
|
||||||
|
+double nkldiv_loss(int a, int b, int c, int d); // KLD
|
||||||
|
+
|
||||||
|
+double milli_loss(int a, int b, int c, int d); //MILL
|
||||||
|
+
|
||||||
|
+/* AIC-QBETA */
|
||||||
|
+double gm_loss(int a, int b, int c, int d);
|
||||||
|
+double nae_loss(int a, int b, int c, int d);
|
||||||
|
+double ae_loss(int a, int b, int c, int d);
|
||||||
|
+double rae_loss(int a, int b, int c, int d);
|
||||||
|
+double Qbeta_loss(int a, int b, int c, int d, double beta);
|
||||||
|
+double Qbeta_acc_loss(int a, int b, int c, int d, double beta);
|
||||||
|
+double Qbeta_f1_loss(int a, int b, int c, int d, double beta);
|
||||||
|
+double Qbeta_gm_loss(int a, int b, int c, int d, double beta);
|
||||||
|
+/* AIC-QBETA */
|
||||||
|
+
|
||||||
|
void svm_struct_learn_api_init(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
/* Called in learning part before anything else is done to allow
|
||||||
|
@@ -181,10 +211,22 @@
|
||||||
|
/* change label value for better scaling using thresholdmetrics */
|
||||||
|
if((sparm->loss_function == ZEROONE)
|
||||||
|
|| (sparm->loss_function == FONE)
|
||||||
|
+ || (sparm->loss_function == GM) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == NAE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == AE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == RAE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_ACC) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_F1) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_GM) // AIC-QBETA
|
||||||
|
|| (sparm->loss_function == ERRORRATE)
|
||||||
|
|| (sparm->loss_function == PRBEP)
|
||||||
|
|| (sparm->loss_function == PREC_K)
|
||||||
|
- || (sparm->loss_function == REC_K)) {
|
||||||
|
+ || (sparm->loss_function == REC_K)
|
||||||
|
+ || (sparm->loss_function == KLD)
|
||||||
|
+ || (sparm->loss_function == NKLD)
|
||||||
|
+ || (sparm->loss_function == MILLI)
|
||||||
|
+ ) {
|
||||||
|
for(i=0;i<sample.examples[0].x.totdoc;i++) {
|
||||||
|
if(sample.examples[0].y.class[i]>0)
|
||||||
|
sample.examples[0].y.class[i]=0.5*100.0/(numn+nump);
|
||||||
|
@@ -520,10 +562,22 @@
|
||||||
|
LABEL ybar;
|
||||||
|
if((sparm->loss_function == ZEROONE)
|
||||||
|
|| (sparm->loss_function == FONE)
|
||||||
|
+ || (sparm->loss_function == GM) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == NAE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == AE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == RAE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_ACC) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_F1) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_GM) // AIC-QBETA
|
||||||
|
|| (sparm->loss_function == ERRORRATE)
|
||||||
|
|| (sparm->loss_function == PRBEP)
|
||||||
|
|| (sparm->loss_function == PREC_K)
|
||||||
|
- || (sparm->loss_function == REC_K)) {
|
||||||
|
+ || (sparm->loss_function == REC_K)
|
||||||
|
+ || (sparm->loss_function == KLD)
|
||||||
|
+ || (sparm->loss_function == NKLD)
|
||||||
|
+ || (sparm->loss_function == MILLI)
|
||||||
|
+ ) {
|
||||||
|
ybar=find_most_violated_constraint_thresholdmetric(x,y,sm,sparm,
|
||||||
|
sparm->loss_type);
|
||||||
|
}
|
||||||
|
@@ -562,9 +616,21 @@
|
||||||
|
sparm->loss_type); */
|
||||||
|
else if((sparm->loss_function == ZEROONE)
|
||||||
|
|| (sparm->loss_function == FONE)
|
||||||
|
+ || (sparm->loss_function == GM) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == NAE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == AE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == RAE) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_ACC) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_F1) // AIC-QBETA
|
||||||
|
+ || (sparm->loss_function == QBETA_GM) // AIC-QBETA
|
||||||
|
|| (sparm->loss_function == PRBEP)
|
||||||
|
|| (sparm->loss_function == PREC_K)
|
||||||
|
- || (sparm->loss_function == REC_K))
|
||||||
|
+ || (sparm->loss_function == REC_K)
|
||||||
|
+ || (sparm->loss_function == KLD)
|
||||||
|
+ || (sparm->loss_function == NKLD)
|
||||||
|
+ || (sparm->loss_function == MILLI)
|
||||||
|
+ )
|
||||||
|
ybar=find_most_violated_constraint_thresholdmetric(x,y,sm,sparm,
|
||||||
|
sparm->loss_type);
|
||||||
|
else if((sparm->loss_function == SWAPPEDPAIRS))
|
||||||
|
@@ -741,7 +807,23 @@
|
||||||
|
if(sparm->loss_function == ZEROONE)
|
||||||
|
loss=zeroone_loss(a,numn-d,nump-a,d);
|
||||||
|
else if(sparm->loss_function == FONE)
|
||||||
|
- loss=fone_loss(a,numn-d,nump-a,d);
|
||||||
|
+ loss=fone_loss(a,numn-d,nump-a,d);
|
||||||
|
+ else if(sparm->loss_function == GM) // AIC-QBETA
|
||||||
|
+ loss=gm_loss(a,numn-d,nump-a,d);
|
||||||
|
+ else if(sparm->loss_function == NAE) // AIC-QBETA
|
||||||
|
+ loss=nae_loss(a,numn-d,nump-a,d);
|
||||||
|
+ else if(sparm->loss_function == AE) // AIC-QBETA
|
||||||
|
+ loss=ae_loss(a,numn-d,nump-a,d);
|
||||||
|
+ else if(sparm->loss_function == RAE) // AIC-QBETA
|
||||||
|
+ loss=rae_loss(a,numn-d,nump-a,d);
|
||||||
|
+ else if(sparm->loss_function == QBETA) // AIC-QBETA
|
||||||
|
+ loss=Qbeta_loss(a,numn-d,nump-a,d,sparm->beta);
|
||||||
|
+ else if(sparm->loss_function == QBETA_ACC) // AIC-QBETA
|
||||||
|
+ loss=Qbeta_acc_loss(a,numn-d,nump-a,d,sparm->beta);
|
||||||
|
+ else if(sparm->loss_function == QBETA_F1) // AIC-QBETA
|
||||||
|
+ loss=Qbeta_f1_loss(a,numn-d,nump-a,d,sparm->beta);
|
||||||
|
+ else if(sparm->loss_function == QBETA_GM) // AIC-QBETA
|
||||||
|
+ loss=Qbeta_gm_loss(a,numn-d,nump-a,d,sparm->beta);
|
||||||
|
else if(sparm->loss_function == ERRORRATE)
|
||||||
|
loss=errorrate_loss(a,numn-d,nump-a,d);
|
||||||
|
else if((sparm->loss_function == PRBEP) && (a+numn-d == nump))
|
||||||
|
@@ -750,6 +832,12 @@
|
||||||
|
loss=prec_k_loss(a,numn-d,nump-a,d);
|
||||||
|
else if((sparm->loss_function == REC_K) && (a+numn-d <= prec_rec_k))
|
||||||
|
loss=rec_k_loss(a,numn-d,nump-a,d);
|
||||||
|
+ else if(sparm->loss_function == KLD) //KLD
|
||||||
|
+ loss=kldiv_loss(a,numn-d,nump-a,d); //KLD
|
||||||
|
+ else if(sparm->loss_function == NKLD) //KLD
|
||||||
|
+ loss=nkldiv_loss(a,numn-d,nump-a,d); //KLD
|
||||||
|
+ else if(sparm->loss_function == MILLI) //MILLI
|
||||||
|
+ loss=milli_loss(a,numn-d,nump-a,d); //MILLI
|
||||||
|
else {
|
||||||
|
loss=0;
|
||||||
|
}
|
||||||
|
@@ -1213,6 +1301,7 @@
|
||||||
|
}
|
||||||
|
/* printf("%f %f\n",y.class[i],ybar.class[i]); */
|
||||||
|
}
|
||||||
|
+ //printf("********** loss %d (a,b,c,d) (%d,%d,%d,%d) beta=%f\n",sparm->loss_function,a,b,c,d,sparm->beta);
|
||||||
|
/* Return the loss according to the selected loss function. */
|
||||||
|
if(sparm->loss_function == ZEROONE) { /* type 0 loss: 0/1 loss */
|
||||||
|
/* return 0, if y==ybar. return 1 else */
|
||||||
|
@@ -1221,6 +1310,30 @@
|
||||||
|
else if(sparm->loss_function == FONE) {
|
||||||
|
loss=fone_loss(a,b,c,d);
|
||||||
|
}
|
||||||
|
+ else if(sparm->loss_function == GM) { // AIC-QBETA
|
||||||
|
+ loss=gm_loss(a,b,c,d);
|
||||||
|
+ }
|
||||||
|
+ else if(sparm->loss_function == NAE) { // AIC-QBETA
|
||||||
|
+ loss=nae_loss(a,b,c,d);
|
||||||
|
+ }
|
||||||
|
+ else if(sparm->loss_function == AE) { // AIC-QBETA
|
||||||
|
+ loss=ae_loss(a,b,c,d);
|
||||||
|
+ }
|
||||||
|
+ else if(sparm->loss_function == RAE) { // AIC-QBETA
|
||||||
|
+ loss=rae_loss(a,b,c,d);
|
||||||
|
+ }
|
||||||
|
+ else if(sparm->loss_function == QBETA) { // AIC-QBETA
|
||||||
|
+ loss=Qbeta_loss(a,b,c,d,sparm->beta);
|
||||||
|
+ }
|
||||||
|
+ else if(sparm->loss_function == QBETA_ACC) { // AIC-QBETA
|
||||||
|
+ loss=Qbeta_acc_loss(a,b,c,d,sparm->beta);
|
||||||
|
+ }
|
||||||
|
+ else if(sparm->loss_function == QBETA_F1) { // AIC-QBETA
|
||||||
|
+ loss=Qbeta_f1_loss(a,b,c,d,sparm->beta);
|
||||||
|
+ }
|
||||||
|
+ else if(sparm->loss_function == QBETA_GM) { // AIC-QBETA
|
||||||
|
+ loss=Qbeta_gm_loss(a,b,c,d,sparm->beta);
|
||||||
|
+ }
|
||||||
|
else if(sparm->loss_function == ERRORRATE) {
|
||||||
|
loss=errorrate_loss(a,b,c,d);
|
||||||
|
}
|
||||||
|
@@ -1242,6 +1355,15 @@
|
||||||
|
else if(sparm->loss_function == AVGPREC) {
|
||||||
|
loss=avgprec_loss(y,ybar);
|
||||||
|
}
|
||||||
|
+ else if(sparm->loss_function == KLD) { //KLD
|
||||||
|
+ loss=kldiv_loss(a,b,c,d); //KLD
|
||||||
|
+ } //KLD
|
||||||
|
+ else if(sparm->loss_function == NKLD) { //KLD
|
||||||
|
+ loss=nkldiv_loss(a,b,c,d); //KLD
|
||||||
|
+ } //KLD
|
||||||
|
+ else if(sparm->loss_function == MILLI) { //MILLI
|
||||||
|
+ loss=milli_loss(a,b,c,d); //MILLI
|
||||||
|
+ } //MILLI
|
||||||
|
else {
|
||||||
|
/* Put your code for different loss functions here. But then
|
||||||
|
find_most_violated_constraint_???(x, y, sm) has to return the
|
||||||
|
@@ -1320,6 +1442,16 @@
|
||||||
|
printf("PRBEP : %5.2f\n",teststats->prbep);
|
||||||
|
printf("ROCArea : %5.2f\n",teststats->rocarea);
|
||||||
|
printf("AvgPrec : %5.2f\n",teststats->avgprec);
|
||||||
|
+ printf("Qb : %5.2f\n",teststats->Qbeta);
|
||||||
|
+ printf("Qb (Acc) : %5.2f\n",teststats->Qbeta_acc);
|
||||||
|
+ printf("Qb (F1) : %5.2f\n",teststats->Qbeta_f1);
|
||||||
|
+ printf("Qb (GM) : %5.2f\n",teststats->Qbeta_gm);
|
||||||
|
+ printf("NAE : %5.2f\n",teststats->nae);
|
||||||
|
+ printf("AE : %5.2f\n",teststats->ae);
|
||||||
|
+ printf("RAE : %5.2f\n",teststats->rae);
|
||||||
|
+ printf("GM : %5.2f\n",teststats->gm);
|
||||||
|
+ printf("KLD : %5.2f\n",teststats->kld);
|
||||||
|
+ printf("NKLD : %5.2f\n",teststats->nkld);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printf("NOTE: %ld test examples are unlabeled, so performance cannot be computed. The\n",teststats->test_data_unlabeled);
|
||||||
|
@@ -1352,6 +1484,29 @@
|
||||||
|
teststats->recall=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
sparm->loss_function=FONE;
|
||||||
|
teststats->fone=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+
|
||||||
|
+ sparm->loss_function=GM; // AIC-QBETA
|
||||||
|
+ teststats->gm=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+ sparm->loss_function=NAE; // AIC-QBETA
|
||||||
|
+ teststats->nae=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+ sparm->loss_function=AE; // AIC-QBETA
|
||||||
|
+ teststats->ae=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+ sparm->loss_function=RAE; // AIC-QBETA
|
||||||
|
+ teststats->rae=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+ sparm->loss_function=QBETA; // AIC-QBETA
|
||||||
|
+ teststats->Qbeta=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+ sparm->loss_function=QBETA_ACC; // AIC-QBETA
|
||||||
|
+ teststats->Qbeta_acc=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+ sparm->loss_function=QBETA_F1; // AIC-QBETA
|
||||||
|
+ teststats->Qbeta_f1=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+ sparm->loss_function=QBETA_GM; // AIC-QBETA
|
||||||
|
+ teststats->Qbeta_gm=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+
|
||||||
|
+ sparm->loss_function=KLD; // KLD
|
||||||
|
+ teststats->kld=100-loss(ex.y,ypred,sparm);
|
||||||
|
+ sparm->loss_function=NKLD; // KLD
|
||||||
|
+ teststats->nkld=100.0-loss(ex.y,ypred,sparm);
|
||||||
|
+
|
||||||
|
teststats->prbep=prbep(ex.y,ypred);
|
||||||
|
teststats->rocarea=rocarea(ex.y,ypred);
|
||||||
|
teststats->avgprec=avgprec(ex.y,ypred);
|
||||||
|
@@ -1403,6 +1558,7 @@
|
||||||
|
STRUCTMODEL sm;
|
||||||
|
|
||||||
|
sm.svm_model=read_model(file);
|
||||||
|
+ sparm->beta = 1; // AIC-QBETA *****************************
|
||||||
|
sparm->loss_function=ERRORRATE;
|
||||||
|
sparm->bias=0;
|
||||||
|
sparm->bias_featurenum=0;
|
||||||
|
@@ -1514,6 +1670,18 @@
|
||||||
|
printf(" %2d Prec@k: 100 minus precision at k in percent.\n",PREC_K);
|
||||||
|
printf(" %2d Rec@k: 100 minus recall at k in percent.\n",REC_K);
|
||||||
|
printf(" %2d ROCArea: Percentage of swapped pos/neg pairs (i.e. 100 - ROCArea).\n\n",SWAPPEDPAIRS);
|
||||||
|
+ printf(" %2d Kullback-Leibler divergence.\n",KLD); //KLD
|
||||||
|
+ printf(" %2d Normalized Kullback-Leibler divergence.\n",NKLD); //KLD
|
||||||
|
+ printf(" %2d MILLI.\n",MILLI); //MILLI
|
||||||
|
+ printf(" %2d GM: geometric mean of tpr and tnr\n",GM); // AIC-QBETA
|
||||||
|
+ printf(" %2d NAE: normalized absolute error (Esuli & Sebastiani)\n",NAE); // AIC-QBETA
|
||||||
|
+ printf(" %2d AE: absolute error (Esuli & Sebastiani)\n",AE); // AIC-QBETA
|
||||||
|
+ printf(" %2d RAE: relative absolute error (Esuli & Sebastiani)\n",RAE); // AIC-QBETA
|
||||||
|
+ printf(" %2d Qbeta: 100 minus the Qbeta-score in percent (with recall)\n",QBETA); // AIC-QBETA
|
||||||
|
+ printf(" %2d Qbeta_acc: 100 minus the Qbeta-score in percent (with acc)\n",QBETA_ACC); // AIC-QBETA
|
||||||
|
+ printf(" %2d Qbeta_f1: 100 minus the Qbeta-score in percent (with F1)\n",QBETA_F1); // AIC-QBETA
|
||||||
|
+ printf(" %2d Qbeta_gm: 100 minus the Qbeta-score in percent (with GM)\n",QBETA_GM); // AIC-QBETA
|
||||||
|
+
|
||||||
|
printf("NOTE: The '-c' parameters in SVM-light and SVM-perf are related as\n");
|
||||||
|
printf(" c_light = c_perf*100/n for the 'Errorrate' loss function, where n is the\n");
|
||||||
|
printf(" number of training examples.\n\n");
|
||||||
|
@@ -1785,7 +1953,65 @@
|
||||||
|
free(predset);
|
||||||
|
return(100.0*(apr/(double)(nump)));
|
||||||
|
}
|
||||||
|
-
|
||||||
|
+/* AIC-QBETA */
|
||||||
|
+double tnr(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ /* Returns tnr as fractional value. */
|
||||||
|
+ if((b+d) == 0) return(0.0);
|
||||||
|
+ return((double)d/(double)(b+d));
|
||||||
|
+}
|
||||||
|
+double gm(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ double tprate = rec(a,b,c,d);
|
||||||
|
+ double tnrate = tnr(a,b,c,d);
|
||||||
|
+ return sqrt( tprate * tnrate );
|
||||||
|
+}
|
||||||
|
+double nae(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ double maximo = (a+c > b+d? a+c: b+d);
|
||||||
|
+ return 1.0 - ( (double)abs(c-b) / maximo);
|
||||||
|
+ //return 1.0 - ( (double)abs(c-b) / (double)(a+b+c+d)); // ABSERR
|
||||||
|
+}
|
||||||
|
+double ae(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ return (double)abs(c-b) / (double)(a+b+c+d) ; // ABSERR
|
||||||
|
+}
|
||||||
|
+double rae(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ double absdif = (double)abs(c-b) ;
|
||||||
|
+ double smooth_rae_pos = absdif / ((double)(a+c+0.5)) ;
|
||||||
|
+ double smooth_rae_neg = absdif / ((double)(b+d+0.5)) ;
|
||||||
|
+ return 0.5*(smooth_rae_pos + smooth_rae_neg) ;
|
||||||
|
+}
|
||||||
|
+double Qbeta(int a, int b, int c, int d, double beta)
|
||||||
|
+{
|
||||||
|
+ if(a+c == 0) return(0.0);
|
||||||
|
+ double qperf=nae(a,b,c,d);
|
||||||
|
+ double cperf=rec(a,b,c,d);
|
||||||
|
+ return((1+beta*beta)*qperf*cperf/((beta*beta*cperf)+qperf));
|
||||||
|
+}
|
||||||
|
+double Qbeta_acc(int a, int b, int c, int d, double beta)
|
||||||
|
+{
|
||||||
|
+ if(a+c == 0) return(0.0);
|
||||||
|
+ double qperf=nae(a,b,c,d);
|
||||||
|
+ double cperf=1.0-errorrate(a,b,c,d);
|
||||||
|
+ return((1+beta*beta)*qperf*cperf/((beta*beta*cperf)+qperf));
|
||||||
|
+}
|
||||||
|
+double Qbeta_f1(int a, int b, int c, int d, double beta)
|
||||||
|
+{
|
||||||
|
+ if(a+c == 0) return(0.0);
|
||||||
|
+ double qperf=nae(a,b,c,d);
|
||||||
|
+ double cperf=fone(a,b,c,d);
|
||||||
|
+ return((1+beta*beta)*qperf*cperf/((beta*beta*cperf)+qperf));
|
||||||
|
+}
|
||||||
|
+double Qbeta_gm(int a, int b, int c, int d, double beta)
|
||||||
|
+{
|
||||||
|
+ if(a+c == 0) return(0.0);
|
||||||
|
+ double qperf=nae(a,b,c,d);
|
||||||
|
+ double cperf=gm(a,b,c,d);
|
||||||
|
+ return((1+beta*beta)*qperf*cperf/((beta*beta*cperf)+qperf));
|
||||||
|
+}
|
||||||
|
+/* AIC-QBETA */
|
||||||
|
/*------- Loss functions based on performance measures --------*/
|
||||||
|
|
||||||
|
double zeroone_loss(int a, int b, int c, int d)
|
||||||
|
@@ -1846,4 +2072,70 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+//KLD
|
||||||
|
+double kldiv(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ double sum = a+b+c+d+1.0;
|
||||||
|
+ double pab = (a+b+0.5)/sum;
|
||||||
|
+ double pac = (a+c+0.5)/sum;
|
||||||
|
+ double pbd = (b+d+0.5)/sum;
|
||||||
|
+ double pcd = (c+d+0.5)/sum;
|
||||||
|
+
|
||||||
|
+ double kl = pac*log(pac/pab)+pbd*log(pbd/pcd);
|
||||||
|
+
|
||||||
|
+ return kl;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+//KLD
|
||||||
|
+double kldiv_loss(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ return kldiv(a,b,c,d);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+//KLD
|
||||||
|
+double nkldiv_loss(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ return 100.0-(100.0*2.0/(1.0+exp(kldiv(a,b,c,d))));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+//MILLI
|
||||||
|
+double milli_loss(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ int sum = a+b+c+d;
|
||||||
|
+ return 100.0*(b+c)*ABS(b-c);
|
||||||
|
+}
|
||||||
|
|
||||||
|
+/* AIC-QBETA */
|
||||||
|
+double gm_loss(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ return 100.0 * (1.0-gm(a,b,c,d));
|
||||||
|
+}
|
||||||
|
+double nae_loss(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ return 100.0 * (1.0-nae(a,b,c,d));
|
||||||
|
+}
|
||||||
|
+double ae_loss(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ return 100.0 * ae(a,b,c,d);
|
||||||
|
+}
|
||||||
|
+double rae_loss(int a, int b, int c, int d)
|
||||||
|
+{
|
||||||
|
+ return 100.0 * rae(a,b,c,d);
|
||||||
|
+}
|
||||||
|
+double Qbeta_loss(int a, int b, int c, int d,double beta)
|
||||||
|
+{
|
||||||
|
+ return(100.0*(1.0-Qbeta(a,b,c,d,beta)));
|
||||||
|
+}
|
||||||
|
+double Qbeta_acc_loss(int a, int b, int c, int d,double beta)
|
||||||
|
+{
|
||||||
|
+ return(100.0*(1.0-Qbeta_acc(a,b,c,d,beta)));
|
||||||
|
+}
|
||||||
|
+double Qbeta_f1_loss(int a, int b, int c, int d,double beta)
|
||||||
|
+{
|
||||||
|
+ return(100.0*(1.0-Qbeta_f1(a,b,c,d,beta)));
|
||||||
|
+}
|
||||||
|
+double Qbeta_gm_loss(int a, int b, int c, int d,double beta)
|
||||||
|
+{
|
||||||
|
+ return(100.0*(1.0-Qbeta_gm(a,b,c,d,beta)));
|
||||||
|
+}
|
||||||
|
+/* AIC-QBETA */
|
||||||
|
diff -ruN svm_perf/svm_struct_api_types.h svm_perf_quantification/svm_struct_api_types.h
|
||||||
|
--- svm_perf/svm_struct_api_types.h 2020-10-28 12:23:19.000000000 +0100
|
||||||
|
+++ svm_perf_quantification/svm_struct_api_types.h 2020-10-28 12:23:53.000000000 +0100
|
||||||
|
@@ -28,14 +28,25 @@
|
||||||
|
# define INST_VERSION_DATE "15.07.2009"
|
||||||
|
|
||||||
|
/* Identifiers for loss functions */
|
||||||
|
-#define ZEROONE 0
|
||||||
|
-#define FONE 1
|
||||||
|
-#define ERRORRATE 2
|
||||||
|
-#define PRBEP 3
|
||||||
|
-#define PREC_K 4
|
||||||
|
-#define REC_K 5
|
||||||
|
-#define SWAPPEDPAIRS 10
|
||||||
|
-#define AVGPREC 11
|
||||||
|
+#define ZEROONE 0
|
||||||
|
+#define FONE 1
|
||||||
|
+#define ERRORRATE 2
|
||||||
|
+#define PRBEP 3
|
||||||
|
+#define PREC_K 4
|
||||||
|
+#define REC_K 5
|
||||||
|
+#define SWAPPEDPAIRS 10
|
||||||
|
+#define AVGPREC 11
|
||||||
|
+#define KLD 12 //KLD
|
||||||
|
+#define NKLD 13 //KLD
|
||||||
|
+#define MILLI 16 //MILLI
|
||||||
|
+#define GM 20 // AIC-QBETA
|
||||||
|
+#define NAE 21 // AIC-QBETA
|
||||||
|
+#define QBETA 22 // AIC-QBETA
|
||||||
|
+#define QBETA_ACC 23 // AIC-QBETA
|
||||||
|
+#define QBETA_F1 24 // AIC-QBETA
|
||||||
|
+#define QBETA_GM 25 // AIC-QBETA
|
||||||
|
+#define AE 26 // AIC-QBETA
|
||||||
|
+#define RAE 27 // AIC-QBETA
|
||||||
|
|
||||||
|
/* default precision for solving the optimization problem */
|
||||||
|
# define DEFAULT_EPS 0.1
|
||||||
|
@@ -169,6 +180,8 @@
|
||||||
|
svm_perf_classify. This uses more
|
||||||
|
memory, but is faster if the support
|
||||||
|
vectors in the model are dense. */
|
||||||
|
+ double loss_parm;
|
||||||
|
+ double beta; /* AIC-QBETA */
|
||||||
|
} STRUCT_LEARN_PARM;
|
||||||
|
|
||||||
|
typedef struct struct_test_stats {
|
||||||
|
@@ -183,6 +196,16 @@
|
||||||
|
double prbep;
|
||||||
|
double rocarea;
|
||||||
|
double avgprec;
|
||||||
|
+ double kld; //KLD
|
||||||
|
+ double nkld; //KLD
|
||||||
|
+ double gm; // AIC-QBETA
|
||||||
|
+ double nae; // AIC-QBETA
|
||||||
|
+ double ae; // AIC-QBETA
|
||||||
|
+ double rae; // AIC-QBETA
|
||||||
|
+ double Qbeta; // AIC-QBETA
|
||||||
|
+ double Qbeta_acc; // AIC-QBETA
|
||||||
|
+ double Qbeta_f1; // AIC-QBETA
|
||||||
|
+ double Qbeta_gm; // AIC-QBETA
|
||||||
|
} STRUCT_TEST_STATS;
|
||||||
|
|
||||||
|
typedef struct struct_id_score {
|
Loading…
Reference in New Issue