code: def get_active_learning_scores_ensemble(
      labels_multiannotator: Optional[
          Union[pd.DataFrame, np.ndarray]
      ] = None,
      pred_probs: Optional[np.ndarray] = None,
      pred_probs_unlabeled: Optional[np.ndarray] = None,
  ) -> Tuple[np.ndarray, np.ndarray]:
      """Returns an ActiveLab quality score for each example in the dataset, based on predictions from an ensemble of models.
  
      This function is similar to `~cleanlab.multiannotator.get_active_learning_scores` but allows for an
      ensemble of multiple classifier models to be trained and will aggregate predictions from the models to compute the ActiveLab quality score.
  
      Parameters
      ----------
      labels_multiannotator : pd.DataFrame or np.ndarray
          Multiannotator labels in the same format expected by `~cleanlab.multiannotator.get_active_learning_scores`.
          This argument is optional if ``pred_probs`` is not provided (in cases where you only provide ``pred_probs_unlabeled`` to get active learning scores for unlabeled examples).
      pred_probs : np.ndarray
          An array of shape ``(P, N, K)`` where P is the number of models, consisting of predicted class probabilities from the ensemble models.
          Note that this function also works with datasets where there is only one annotator (M=1).
          Each set of predicted probabilities with shape ``(N, K)`` is in the same format expected by the :py:func:`get_label_quality_scores <cleanlab.rank.get_label_quality_scores>`.
          This argument is optional if you only want to get active learning scores for unlabeled examples (pass in ``pred_probs_unlabeled`` instead).
      pred_probs_unlabeled : np.ndarray, optional
          An array of shape ``(P, N, K)`` where P is the number of models, consisting of predicted class probabilities from a trained classifier model
          for examples that have no annotated labels so far (but which we may want to label in the future, and hence compute active learning quality scores for).
          Each set of predicted probabilities with shape ``(N, K)`` is in the same format expected by the :py:func:`get_label_quality_scores <cleanlab.rank.get_label_quality_scores>`.
          This argument is optional if you only want to get active learning scores for labeled examples (pass in ``pred_probs`` instead).
  
      Returns
      -------
      active_learning_scores : np.ndarray
          Similar to output as :py:func:`get_label_quality_scores <cleanlab.multiannotator.get_label_quality_scores>`.
      active_learning_scores_unlabeled : np.ndarray
          Similar to output as :py:func:`get_label_quality_scores <cleanlab.multiannotator.get_label_quality_scores>`.
  
      See Also
      --------
      get_active_learning_scores
      """
  
      assert_valid_pred_probs(
          pred_probs=pred_probs,
          pred_probs_unlabeled=pred_probs_unlabeled,
          ensemble=True,
      )
  
      # compute multiannotator stats if labeled data is provided
      if pred_probs is not None:
          if labels_multiannotator is None:
              raise ValueError(
                  "labels_multiannotator cannot be None when passing in pred_probs. ",
                  "You can either provide labels_multiannotator to obtain active learning scores for the labeled examples, "
                  "or just pass in pred_probs_unlabeled to get active learning scores for unlabeled examples.",
              )
  
          if isinstance(labels_multiannotator, pd.DataFrame):
              labels_multiannotator = (
                  labels_multiannotator.replace({pd.NA: np.NaN})
                  .astype(float)
                  .to_numpy()
              )
          elif not isinstance(labels_multiannotator, np.ndarray):
              raise ValueError(
                  "labels_multiannotator must be either a NumPy array or Pandas DataFrame."
              )
  
          # check that labels_multiannotator is a 2D array
          if labels_multiannotator.ndim != 2:
              raise ValueError(
                  "labels_multiannotator must be a 2D array or dataframe, "
                  "each row represents an example and each column represents an annotator."
              )
  
          num_classes = get_num_classes(pred_probs=pred_probs[0])
  
          # if all examples are only labeled by a single annotator
          if (
              np.sum(~np.isnan(labels_multiannotator), axis=1) == 1
          ).all():
              # do not temp scale for single annotator case, temperature is defined here for later use
              optimal_temp = np.full(len(pred_probs), 1.0)
  
              assert_valid_inputs_multiannotator(
                  labels_multiannotator,
                  pred_probs,
                  ensemble=True,
                  allow_single_label=True,
              )
  
              avg_pred_probs = np.mean(pred_probs, axis=0)
              consensus_label = get_majority_vote_label(
                  labels_multiannotator=labels_multiannotator,
                  pred_probs=avg_pred_probs,
                  verbose=False,
              )
              quality_of_consensus_labeled = get_label_quality_scores(
                  consensus_label, avg_pred_probs
              )
              model_weight = np.full(len(pred_probs), 1)
              annotator_weight = np.full(
                  labels_multiannotator.shape[1], 1
              )
              avg_annotator_weight = np.mean(annotator_weight)
  
          # examples are annotated by multiple annotators
          else:
              optimal_temp = np.full(len(pred_probs), np.NaN)
              for i, curr_pred_probs in enumerate(pred_probs):
                  curr_optimal_temp = find_best_temp_scaler(
                      labels_multiannotator, curr_pred_probs
                  )
                  pred_probs[i] = temp_scale_pred_probs(
                      curr_pred_probs, curr_optimal_temp
                  )
                  optimal_temp[i] = curr_optimal_temp
  
              multiannotator_info = (
                  get_label_quality_multiannotator_ensemble(
                      labels_multiannotator,
                      pred_probs,
                      return_annotator_stats=False,
                      return_detailed_quality=False,
                      return_weights=True,
                  )
              )
  
              quality_of_consensus_labeled = multiannotator_info[
                  "label_quality"
              ]["consensus_quality_score"]
              model_weight = multiannotator_info["model_weight"]
              annotator_weight = multiannotator_info["annotator_weight"]
              avg_annotator_weight = np.mean(annotator_weight)
  
          # compute scores for labeled data
          active_learning_scores = np.full(
              len(labels_multiannotator), np.nan
          )
          for i, annotator_labels in enumerate(labels_multiannotator):
              active_learning_scores[i] = np.average(
                  (quality_of_consensus_labeled[i], 1 / num_classes),
                  weights=(
                      np.sum(
                          annotator_weight[~np.isnan(annotator_labels)]
                      )
                      + np.sum(model_weight),
                      avg_annotator_weight,
                  ),
              )
  
      # no labeled data provided so do not estimate temperature and model/annotator weights
      elif pred_probs_unlabeled is not None:
          num_classes = get_num_classes(
              pred_probs=pred_probs_unlabeled[0]
          )
          optimal_temp = np.full(len(pred_probs_unlabeled), 1.0)
          model_weight = np.full(len(pred_probs_unlabeled), 1)
          avg_annotator_weight = 1
          active_learning_scores = np.array([])
  
      else:
          raise ValueError(
              "pred_probs and pred_probs_unlabeled cannot both be None, specify at least one of the two."
          )
  
      # compute scores for unlabeled data
      if pred_probs_unlabeled is not None:
          for i in range(len(pred_probs_unlabeled)):
              pred_probs_unlabeled[i] = temp_scale_pred_probs(
                  pred_probs_unlabeled[i], optimal_temp[i]
              )
  
          avg_pred_probs_unlabeled = np.mean(
              pred_probs_unlabeled, axis=0
          )
          consensus_label_unlabeled = get_majority_vote_label(
              np.argmax(pred_probs_unlabeled, axis=2).T,
              avg_pred_probs_unlabeled,
          )
          modified_pred_probs_unlabeled = np.average(
              np.concatenate(
                  (
                      pred_probs_unlabeled,
                      np.full(
                          pred_probs_unlabeled.shape[1:],
                          1 / num_classes,
                      )[np.newaxis, :, :],
                  )
              ),
              weights=np.concatenate(
                  (model_weight, np.array([avg_annotator_weight]))
              ),
              axis=0,
          )
  
          active_learning_scores_unlabeled = get_label_quality_scores(
              consensus_label_unlabeled, modified_pred_probs_unlabeled
          )
      else:
          active_learning_scores_unlabeled = np.array([])
  
      return active_learning_scores, active_learning_scores_unlabeled
  
imports: import warnings
  import numpy
  import pandas
  from typing import List, Dict, Any, Union, Tuple, Optional
  from cleanlab.rank import get_label_quality_scores
  from cleanlab.internal.util import get_num_classes, value_counts
  from cleanlab.internal.constants import CLIPPING_LOWER_BOUND
  from cleanlab.internal.multiannotator_utils import assert_valid_inputs_multiannotator, assert_valid_pred_probs, check_consensus_label_classes, find_best_temp_scaler, temp_scale_pred_probs
label_desc: Use image node_id 4 for calling a global function with example usage: get_active_learning_scores_ensemble(labels_multiannotator, pred_probs, pred_probs_unlabeled) and returns: active_learning_scores,  active_learning_scores_unlabeled