liveBook · Manning

Active learning

As explained in Machine Learning Algorithms in Depth

Active learning is a supervised learning technique in which a learner selects unlabeled instances for labeling to improve performance with fewer labeled examples.

Active learning reduces the number of labels needed for supervised learning by letting the learner choose which examples to label. It is useful when unlabeled data is abundant but obtaining labels is expensive. An unlabeled instance becomes a query to an oracle, which supplies its label. The aim is high predictive accuracy with as few labeled instances as possible. section 7.3

In pool-based active learning, start with a small labeled set L and a larger unlabeled pool U. Score candidates in U for informativeness, select queries, and add their oracle-supplied labels to L. Ranking the entire pool can be costly for very large datasets; stream-based sampling instead scans instances sequentially and makes an individual query decision for each. section 7.3

Two query strategies differ in what they regard as informative:

  • Uncertainty sampling selects predictions the current model is least sure about. For binary logistic regression, these lie near the decision boundary, where the predicted positive-class probability approaches ½. Multiclass criteria include lowest confidence in the top class, the smallest margin between the top two classes, and highest predictive entropy. For regression, query the point with the highest predicted output variance. section 7.3.1
  • Query by committee trains competing models on the current labeled set. Each model votes on candidate labels; disagreement identifies informative queries. Vote entropy and average KL divergence are supported committee-ranking strategies. section 7.3.1

Thirty labels for logistic regression

On a synthetic, balanced binary-classification dataset, logistic regression trained on 30 randomly selected labels reaches 90% accuracy on held-out data. With 30 queries chosen by entropy-based uncertainty sampling, it reaches 92.5%. Compare both decision boundaries and held-out scores rather than assuming every query strategy will improve accuracy. section 7.3 section 7.3.1

Listing 7.5 Active learner class
from __future__ import unicode_literals, division
from scipy.sparse import csc_matrix, vstack
from scipy.stats import entropy
from collections import Counter
import numpy as np


class ActiveLearner(object):

    uncertainty_sampling_frameworks = [
        'entropy',                        #1
        'max_margin',    #1
        'least_confident',                 #1
    ]

    query_by_committee_frameworks = [
        'vote_entropy',            
        'average_kl_divergence',   #2
    ] #2

    def __init__(self, strategy='least_confident'):
        self.strategy = strategy

    def rank(self, clf, X_unlabeled, num_queries=None):

        if num_queries == None:
            num_queries = X_unlabeled.shape[0]

        elif type(num_queries) == float:
            num_queries = int(num_queries * X_unlabeled.shape[0])

        if self.strategy in self.uncertainty_sampling_frameworks:
            scores = self.uncertainty_sampling(clf, X_unlabeled)

        elif self.strategy in self.query_by_committee_frameworks:
            scores = self.query_by_committee(clf, X_unlabeled)

        else: 
            raise ValueError("this strategy is not implemented.")

        rankings = np.argsort(-scores)[:num_queries]
        return rankings

    def uncertainty_sampling(self, clf, X_unlabeled):
        probs = clf.predict_proba(X_unlabeled)

        if self.strategy == 'least_confident':
            return 1 - np.amax(probs, axis=1)    #3

        elif self.strategy == 'max_margin':
            margin = np.partition(-probs, 1, axis=1)
            return -np.abs(margin[:,0] - margin[:, 1])     #4

        elif self.strategy == 'entropy':
            return np.apply_along_axis(entropy, 1, 
             probs)     #5

    def query_by_committee(self, clf, X_unlabeled):
        num_classes = len(clf[0].classes_)
        C = len(clf)
        preds = []

        if self.strategy == 'vote_entropy':
            for model in clf:
                y_out = map(int, model.predict(X_unlabeled))
                preds.append(np.eye(num_classes)[y_out])

            votes = np.apply_along_axis(np.sum, 0, np.stack(preds)) / C
            return np.apply_along_axis(entropy, 1, 
             votes)     #6

        elif self.strategy == 'average_kl_divergence':
            for model in clf:
                preds.append(model.predict_proba(X_unlabeled))

            consensus = np.mean(np.stack(preds), axis=0)
            divergence = []
            for y_out in preds:
                divergence.append(entropy(consensus.T, y_out.T))

            return np.apply_along_axis(np.mean, 0, np
             .stack(divergence))  #7

The ActiveLearner class shows how to implement candidate ranking with selectable uncertainty-sampling and query-by-committee criteria. section 7.3.1

How Machine Learning Algorithms in Depth covers it

How deep it goes
Develops pool-based selection, compares query criteria, and implements a ranking class; flags full-pool ranking as costly at scale.
Examples
Compares 30 random labels with 30 entropy-selected queries on synthetic binary data, yielding 90% and 92.5% held-out accuracy.
What it assumes
Assumes familiarity with supervised classification, labeled datasets, and logistic-regression predictions; introduces pool-based query selection from its basic setup.
What you can do after
Build it from scratch as a candidate-ranking class and apply it to logistic regression queries.
How it connects
Connects uncertainty sampling and query by committee to logistic regression, predictive entropy, and model disagreement.

Also explained in

Compare how 3 books explain active learning →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Machine Learning Algorithms in Depth. The section links point to the parts of the book it draws on.