Core iteration strategy for selecting another record from our unlabeled records. Given a set of labeled records (X_training) and unlabeled records (X_pool) with uncertainty scores (X_uncertainty), we'd like to identify the best instance in X_pool that best balances uncertainty and diss
(
X_training: modALinput,
X_pool: modALinput,
X_uncertainty: np.ndarray,
mask: np.ndarray,
metric: Union[str, Callable],
n_jobs: Union[int, None]
)
| 49 | |
| 50 | |
| 51 | def select_instance( |
| 52 | X_training: modALinput, |
| 53 | X_pool: modALinput, |
| 54 | X_uncertainty: np.ndarray, |
| 55 | mask: np.ndarray, |
| 56 | metric: Union[str, Callable], |
| 57 | n_jobs: Union[int, None] |
| 58 | ) -> Tuple[np.ndarray, modALinput, np.ndarray]: |
| 59 | """ |
| 60 | Core iteration strategy for selecting another record from our unlabeled records. |
| 61 | |
| 62 | Given a set of labeled records (X_training) and unlabeled records (X_pool) with uncertainty scores (X_uncertainty), |
| 63 | we'd like to identify the best instance in X_pool that best balances uncertainty and dissimilarity. |
| 64 | |
| 65 | Refer to Cardoso et al.'s "Ranked batch-mode active learning": |
| 66 | https://www.sciencedirect.com/science/article/pii/S0020025516313949 |
| 67 | |
| 68 | TODO: |
| 69 | - Add notebook for Active Learning bake-off (passive vs interactive vs batch vs ranked batch) |
| 70 | |
| 71 | Args: |
| 72 | X_training: Mix of both labeled and unlabeled records. |
| 73 | X_pool: Unlabeled records to be selected for labeling. |
| 74 | X_uncertainty: Uncertainty scores for unlabeled records to be selected for labeling. |
| 75 | mask: Mask to exclude previously selected instances from the pool. |
| 76 | metric: This parameter is passed to :func:`~sklearn.metrics.pairwise.pairwise_distances`. |
| 77 | n_jobs: This parameter is passed to :func:`~sklearn.metrics.pairwise.pairwise_distances`. |
| 78 | |
| 79 | Returns: |
| 80 | Index of the best index from X chosen to be labelled; a single record from our unlabeled set that is considered |
| 81 | the most optimal incremental record for including in our query set. |
| 82 | """ |
| 83 | X_pool_masked = X_pool[mask] |
| 84 | |
| 85 | # Extract the number of labeled and unlabeled records. |
| 86 | n_labeled_records, *rest = X_training.shape |
| 87 | n_unlabeled, *rest = X_pool_masked.shape |
| 88 | |
| 89 | # Determine our alpha parameter as |U| / (|U| + |D|). Note that because we |
| 90 | # append to X_training and remove from X_pool within `ranked_batch`, |
| 91 | # :alpha: is not fixed throughout our model's lifetime. |
| 92 | alpha = n_unlabeled / (n_unlabeled + n_labeled_records) |
| 93 | |
| 94 | # Compute pairwise distance (and then similarity) scores from every unlabeled record |
| 95 | # to every record in X_training. The result is an array of shape (n_samples, ). |
| 96 | |
| 97 | if n_jobs == 1 or n_jobs is None: |
| 98 | _, distance_scores = pairwise_distances_argmin_min(X_pool_masked.reshape(n_unlabeled, -1), |
| 99 | X_training.reshape(n_labeled_records, -1), |
| 100 | metric=metric) |
| 101 | else: |
| 102 | distance_scores = pairwise_distances(X_pool_masked.reshape(n_unlabeled, -1), |
| 103 | X_training.reshape(n_labeled_records, -1), |
| 104 | metric=metric, n_jobs=n_jobs).min(axis=1) |
| 105 | |
| 106 | similarity_scores = 1 / (1 + distance_scores) |
| 107 | |
| 108 | # Compute our final scores, which are a balance between how dissimilar a given record |
no outgoing calls
no test coverage detected
searching dependent graphs…