Ejemplo n.º 1
0
    def __init__(self,
                 modellist,
                 eval_comparator,
                 datafile=None,
                 train_data_person=None,
                 silent=False,
                 split_ratio=0.5,
                 no_pretrain=False,
                 no_adapt=False):
        """

        Parameters
        ----------
        modellist : list(ccobra.ModelInfo)
            List containing model paths and additional arguments for model initialization.

        eval_comparator : Comparator
            Comparator to be used for computing hits/misses.

        test_datafile : str
            Path to the test data file.

        train_datafile : str
            Path to the general training data file.

        train_data_person : str
            Path to the person training data file.

        silent : bool
            Indicates whether evaluation progress should be logged using print statements.

        corresponding_data : bool
            Indicates whether test and training data should contain the same
            user ids.

        """

        self.adapt = not no_adapt

        self.modellist = modellist
        self.silent = silent

        self.domains = set()
        self.response_types = set()

        self.comparator = eval_comparator

        # Load the data set
        self.data = ccobra.CCobraData(pd.read_csv(datafile))
        self.split_ratio = split_ratio

        self.pretrain = not no_pretrain

        # Load the personal training data
        self.train_data_person = None
        if train_data_person:
            self.train_data_person = ccobra.CCobraData(
                pd.read_csv(train_data_person))
Ejemplo n.º 2
0
    def evaluate(self):
        """ CCobra evaluation loop. Iterates over the models and performs training and evaluation.
        Returns
        -------
        pd.DataFrame
            DataFrame containing the CCOBRA evaluation results.
        """

        assert self.data is not None

        result_data = []
        model_name_cache = set()

        # Pre-compute the training data dictionaries
        train_data_dict = None
        train_data_dict = self.get_train_data_dict(self.data)

        # Activate model context
        for idx, modelinfo in enumerate(self.modellist):
            # Print the progress
            if not self.silent:
                print("Evaluating '{}' ({}/{})...".format(
                    modelinfo.path, idx + 1, len(self.modellist)))

            # Setup the model context
            context = os.path.abspath(modelinfo.path)
            if os.path.isfile(context):
                context = os.path.dirname(context)

            with dir_context(context):
                # Dynamically import the CCOBRA model
                importer = modelimporter.ModelImporter(
                    modelinfo.path,
                    ccobra.CCobraModel,
                    load_specific_class=modelinfo.load_specific_class)

                # Instantiate and prepare the model for predictions
                pre_model = importer.instantiate(modelinfo.args)

                # Check if model is applicable to domains/response types
                self.check_model_applicability(pre_model)

                # Only use the model's name if no override is specified
                model_name = modelinfo.override_name
                if not model_name:
                    model_name = pre_model.name

                # Ensure that names are unique and show a warning if duplicates are detected
                original_model_name = model_name
                changed = False
                while model_name in model_name_cache:
                    model_name = model_name + '\''
                    changed = True
                model_name_cache.add(model_name)

                if changed:
                    warnings.warn(
                        'Duplicate model name detected ("{}"). Changed to "{}".'
                        .format(original_model_name, model_name))

                # Iterate subject
                for subj_id, subj_df in self.data.get().groupby('id'):
                    model = copy.deepcopy(pre_model)

                    # Perform pre-training for individual subjects
                    # Remove one participant
                    cur_train_data_dict = [
                        value for key, value in train_data_dict.items()
                        if key != subj_id
                    ]

                    # Train on incomplete training data
                    if self.pretrain:
                        model.pre_train(cur_train_data_dict)

                    # Perform the personalized pre-training
                    if self.train_data_person is not None:
                        # Pick out the person training data for the current
                        # individual
                        subj_pre_train_data_person = self.train_data_person.get(
                        ).loc[self.train_data_person.get()['id'] == subj_id]

                        person_train_data = self.get_train_data_dict(
                            ccobra.CCobraData(subj_pre_train_data_person))
                        model.person_train(person_train_data[subj_id])

                    # Extract the subject demographics
                    demographics = self.extract_demographics(subj_df)

                    # Set the models to new participant
                    model.start_participant(id=subj_id, **demographics)

                    # split the individuals data in train and test set
                    perm = np.random.permutation(np.arange(len(subj_df)))
                    split_id = int(self.split_ratio * len(subj_df))
                    train_ids, test_ids = perm[:split_id], perm[split_id:]
                    train_set, test_set = subj_df.iloc[
                        train_ids], subj_df.iloc[test_ids]

                    # adapt to train set
                    adapt_items_list = []
                    truths_list = []
                    optionals_list = []
                    # prepare all tasks from train set
                    for _, row in train_set.iterrows():
                        optionals = self.extract_optionals(row)

                        # Evaluation
                        sequence = row['sequence']
                        task = row['task']
                        choices = row['choices']
                        truth = row['response']
                        response_type = row['response_type']
                        domain = row['domain']

                        if isinstance(truth, str):
                            if response_type == 'multiple-choice':
                                truth = [
                                    y.split(';') for y in
                                    [x.split('/') for x in truth.split('|')]
                                ]
                            else:
                                truth = [
                                    x.split(';') for x in truth.split('/')
                                ]

                        item = ccobra.data.Item(subj_id, domain, task,
                                                response_type, choices,
                                                sequence)

                        # Adapt to true response
                        adapt_item = ccobra.data.Item(subj_id, domain, task,
                                                      response_type, choices,
                                                      sequence)

                        adapt_items_list.append(adapt_item)
                        truths_list.append(truth)
                        optionals_list.append(optionals)
                    # give all items to models at once for data driven training
                    try:
                        model.batch_adapt(adapt_items_list, truths_list,
                                          optionals_list)
                    except AttributeError:  # regular adapt if other not implemented
                        for items, truth, optionals in zip(
                                adapt_items_list, truths_list, optionals_list):
                            model.adapt(item, truth, **optionals)

                    for _, row in test_set.iterrows():
                        optionals = self.extract_optionals(row)

                        # Evaluation
                        sequence = row['sequence']
                        task = row['task']
                        choices = row['choices']
                        truth = row['response']
                        response_type = row['response_type']
                        domain = row['domain']

                        if isinstance(truth, str):
                            if response_type == 'multiple-choice':
                                truth = [
                                    y.split(';') for y in
                                    [x.split('/') for x in truth.split('|')]
                                ]
                            else:
                                truth = [
                                    x.split(';') for x in truth.split('/')
                                ]

                        item = ccobra.data.Item(subj_id, domain, task,
                                                response_type, choices,
                                                sequence)

                        prediction = model.predict(item, **optionals)
                        hit = int(self.comparator.compare(prediction, truth))
                        # Collect the evaluation result data
                        result_data.append({
                            'model':
                            model_name,
                            'id':
                            subj_id,
                            'domain':
                            domain,
                            'sequence':
                            sequence,
                            'task':
                            task,
                            'choices':
                            choices,
                            'truth':
                            row['response'],
                            'prediction':
                            comparator.tuple_to_string(prediction),
                            'hit':
                            hit
                        })

                    # Call the end participant hook
                    model.end_participant(subj_id, **optionals)

                # De-load the imported model and its dependencies. Might
                # cause garbage collection issues.
                importer.unimport()

        return pd.DataFrame(result_data)
Ejemplo n.º 3
0
    def __init__(self,
                 modellist,
                 eval_comparator,
                 test_datafile,
                 train_datafile=None,
                 train_data_person=None,
                 silent=False,
                 corresponding_data=False,
                 no_adapt=False):
        """

        Parameters
        ----------
        modellist : list(ccobra.ModelInfo)
            List containing model paths and additional arguments for model initialization.

        eval_comparator : Comparator
            Comparator to be used for computing hits/misses.

        test_datafile : str
            Path to the test data file.

        train_datafile : str
            Path to the general training data file.

        train_data_person : str
            Path to the person training data file.

        silent : bool
            Indicates whether evaluation progress should be logged using print statements.

        corresponding_data : bool
            Indicates whether test and training data should contain the same
            user ids.

        """

        self.adapt = not no_adapt

        self.modellist = modellist
        self.silent = silent

        self.domains = set()
        self.response_types = set()

        self.comparator = eval_comparator
        self.corresponding_data = corresponding_data

        # Load the test data
        self.test_data = ccobra.CCobraData(pd.read_csv(test_datafile))
        self.domains.update(self.test_data.get()['domain'].unique())
        self.response_types.update(
            self.test_data.get()['response_type'].unique())

        # Load the general training data
        self.train_data = None
        if train_datafile:
            # Load the data. Domains and response types are not updated,
            # because training is considered optional information the models
            # are not forced to use.
            self.train_data = ccobra.CCobraData(pd.read_csv(train_datafile))

            # If non-corresponding datasets, update with new identification
            if not self.corresponding_data:
                test_ids = self.test_data.get()['id'].unique()

                # Identify the ID offset as the largest numerical index from
                # the test dataset.
                idx_offset = 0
                for identifier in test_ids:
                    # Strings cause the float conversion to throw an exception
                    # Numberness is identified accordingly.
                    try:
                        if idx_offset < float(identifier):
                            idx_offset = float(identifier)
                    except ValueError:
                        pass

                idx_offset = int(np.ceil(idx_offset)) + 1

                # Update the training IDs accordingly
                train_ids = self.train_data.get()['id'].unique()
                new_train_ids = dict(
                    zip(train_ids,
                        range(idx_offset, idx_offset + len(train_ids))))
                self.train_data.get()['id'].replace(new_train_ids,
                                                    inplace=True)

        # Load the personal training data
        self.train_data_person = None
        if train_data_person:
            self.train_data_person = ccobra.CCobraData(
                pd.read_csv(train_data_person))
Ejemplo n.º 4
0
    def evaluate(self):
        """ CCobra evaluation loop. Iterates over the models and performs training and evaluation.

        Returns
        -------
        pd.DataFrame
            DataFrame containing the CCOBRA evaluation results.

        """

        result_data = []
        model_name_cache = set()

        # Pre-compute the training data dictionaries
        train_data_dict = None
        if self.train_data is not None:
            train_data_dict = self.get_train_data_dict(self.train_data)

        # Activate model context
        for idx, modelinfo in enumerate(self.modellist):
            model_evaluations = {}
            # Print the progress
            if not self.silent:
                print("Evaluating '{}' ({}/{})...".format(
                    modelinfo.path, idx + 1, len(self.modellist)))

            # Setup the model context
            context = os.path.abspath(modelinfo.path)
            if os.path.isfile(context):
                context = os.path.dirname(context)

            with dir_context(context):
                # Dynamically import the CCOBRA model
                importer = modelimporter.ModelImporter(
                    modelinfo.path,
                    ccobra.CCobraModel,
                    load_specific_class=modelinfo.load_specific_class)

                # Instantiate and prepare the model for predictions
                pre_model = importer.instantiate(modelinfo.args)

                # Check if model is applicable to domains/response types
                self.check_model_applicability(pre_model)

                # Only use the model's name if no override is specified
                model_name = modelinfo.override_name
                if not model_name:
                    model_name = pre_model.name

                # Ensure that names are unique and show a warning if duplicates are detected
                original_model_name = model_name
                changed = False
                while model_name in model_name_cache:
                    model_name = model_name + '\''
                    changed = True
                model_name_cache.add(model_name)

                if changed:
                    warnings.warn(
                        'Duplicate model name detected ("{}"). Changed to "{}".'
                        .format(original_model_name, model_name))

                # Only perform general pre-training if training data is
                # supplied and corresponding data is false. Otherwise, the
                # model has to be re-trained for each subject.
                if self.train_data is not None and not self.corresponding_data:
                    # Prepare training data
                    pre_model.pre_train(list(train_data_dict.values()))

                # Iterate subject
                for subj_id, subj_df in self.test_data.get().groupby('id'):
                    model = copy.deepcopy(pre_model)

                    model_evaluations[subj_id] = {}

                    # Perform pre-training for individual subjects only if
                    # corresponding data is set to true.
                    if self.train_data is not None and self.corresponding_data:
                        # Remove one participant
                        cur_train_data_dict = [
                            value for key, value in train_data_dict.items()
                            if key != subj_id
                        ]

                        model_checkpoints = []
                        # Train on incomplete training data
                        model.pre_train(cur_train_data_dict,
                                        checkpoints=model_checkpoints)

                        if pre_train_str in self.learning_curves_for:
                            model_evaluations[subj_id][
                                pre_train_str] = self.evaluate_checkpoints(
                                    model_checkpoints, subj_id)

                    # Perform the personalized pre-training
                    if self.train_data_person is not None:
                        # Pick out the person training data for the current
                        # individual
                        subj_pre_train_data_person = self.train_data_person.get(
                        ).loc[self.train_data_person.get()['id'] == subj_id]

                        model_checkpoints = []

                        person_train_data = self.get_train_data_dict(
                            ccobra.CCobraData(subj_pre_train_data_person))
                        model.person_train(person_train_data[subj_id],
                                           checkpoints=model_checkpoints)

                        if person_train_str in self.learning_curves_for:
                            model_evaluations[subj_id][
                                person_train_str] = self.evaluate_checkpoints(
                                    model_checkpoints, subj_id)

                    # Extract the subject demographics
                    demographics = self.extract_demographics(subj_df)

                    model_checkpoints = []

                    # Set the models to new participant
                    model.start_participant(id=subj_id,
                                            checkpoints=model_checkpoints,
                                            **demographics)

                    if start_participant_str in self.learning_curves_for:
                        model_evaluations[subj_id][
                            start_participant_str] = self.evaluate_checkpoints(
                                model_checkpoints, subj_id)

                    if main_train_str in self.learning_curves_for:
                        model_evaluations[subj_id][
                            main_train_str] = self.evaluate_checkpoints(
                                [copy.deepcopy(model)], subj_id)

                    if adapt_str in self.learning_curves_for:
                        model_evaluations[subj_id][adapt_str] = {}

                    for r, (_, row) in enumerate(
                            subj_df.sort_values('sequence').iterrows()):
                        optionals = self.extract_optionals(row)

                        # Evaluation
                        sequence = row['sequence']
                        task = row['task']
                        choices = row['choices']
                        truth = row['response']
                        response_type = row['response_type']
                        domain = row['domain']

                        if isinstance(truth, str):
                            if response_type == 'multiple-choice':
                                truth = [
                                    y.split(';') for y in
                                    [x.split('/') for x in truth.split('|')]
                                ]
                            else:
                                truth = [
                                    x.split(';') for x in truth.split('/')
                                ]

                        item = ccobra.data.Item(subj_id, domain, task,
                                                response_type, choices,
                                                sequence)

                        prediction = model.predict(item, **optionals)
                        hit = int(self.comparator.compare(prediction, truth))

                        model_checkpoints = []

                        # Adapt to true response
                        adapt_item = ccobra.data.Item(subj_id, domain, task,
                                                      response_type, choices,
                                                      sequence)
                        if self.adapt:
                            model.adapt(adapt_item,
                                        truth,
                                        checkpoints=model_checkpoints,
                                        **optionals)

                        if adapt_str in self.learning_curves_for:
                            model_evaluations[subj_id][adapt_str][
                                sequence] = self.evaluate_checkpoints(
                                    model_checkpoints, subj_id)

                        if (main_train_str in self.learning_curves_for
                                and r % self.evaluation_frequency == 0):
                            model_evaluations[subj_id][main_train_str].append(
                                self.evaluate_checkpoints(
                                    [copy.deepcopy(model)], subj_id)[0])

                        result_data.append({
                            'model':
                            model.name,
                            'id':
                            subj_id,
                            'domain':
                            domain,
                            'sequence':
                            sequence,
                            'task':
                            task,
                            'choices':
                            choices,
                            'truth':
                            row['response'],
                            'prediction':
                            comparator.tuple_to_string(prediction),
                            'hit':
                            hit,
                        })

                # De-load the imported model and its dependencies. Might
                # cause garbage collection issues.
                importer.unimport()

                if self.learning_curves_folder:
                    self.generate_learning_curves(model.name,
                                                  model_evaluations)

        return pd.DataFrame(result_data)
Ejemplo n.º 5
0
    def evaluate(self):
        """ CCobra evaluation loop. Iterates over the models and performs training and evaluation.

        Returns
        -------
        pd.DataFrame
            DataFrame containing the CCOBRA evaluation results.

        """

        result_data = []

        # Pre-compute the training data dictionaries
        train_data_dict = None
        if self.train_data is not None:
            train_data_dict = self.get_train_data_dict(self.train_data)

        # Activate model context
        for idx, modelitem in enumerate(self.modeldict.items()):
            model, model_kwargs = modelitem

            # Print the progress
            if not self.silent:
                print("Evaluating '{}' ({}/{})...".format(
                    model, idx + 1, len(self.modeldict)))

            # Setup the model context
            context = os.path.abspath(model)
            if os.path.isfile(context):
                context = os.path.dirname(context)

            # Extract load_specific_class
            specific_class = None
            if 'load_specific_class' in model_kwargs:
                specific_class = model_kwargs['load_specific_class']
                del model_kwargs['load_specific_class']

            with dir_context(context):
                importer = modelimporter.ModelImporter(
                    model,
                    ccobra.CCobraModel,
                    load_specific_class=specific_class)

                # Instantiate and prepare the model for predictions
                pre_model = importer.instantiate(model_kwargs)

                # Check if model is applicable to domains/response types
                self.check_model_applicability(pre_model)

                # Only perform general pre-training if training data is
                # supplied and corresponding data is false. Otherwise, the
                # model has to be re-trained for each subject.
                if self.train_data is not None and not self.corresponding_data:
                    # Prepare training data
                    pre_model.pre_train(list(train_data_dict.values()))

                # Iterate subject
                for subj_id, subj_df in self.test_data.get().groupby('id'):
                    model = copy.deepcopy(pre_model)

                    # Perform pre-training for individual subjects only if
                    # corresponding data is set to true.
                    if self.train_data is not None and self.corresponding_data:
                        # Remove one participant
                        cur_train_data_dict = [
                            value for key, value in train_data_dict.items()
                            if key != subj_id
                        ]

                        # Train on incomplete training data
                        model.pre_train(cur_train_data_dict)

                    # Perform the personalized pre-training
                    if self.train_data_person is not None:
                        # Pick out the person training data for the current
                        # individual
                        subj_pre_train_data_person = self.train_data_person.get(
                        ).loc[self.train_data_person.get()['id'] == subj_id]

                        person_train_data = self.get_train_data_dict(
                            ccobra.CCobraData(subj_pre_train_data_person))
                        model.person_train(person_train_data[subj_id])

                    # Extract the subject demographics
                    demographics = self.extract_demographics(subj_df)

                    # Set the models to new participant
                    model.start_participant(id=subj_id, **demographics)

                    for _, row in subj_df.sort_values('sequence').iterrows():
                        optionals = self.extract_optionals(row)

                        # Evaluation
                        sequence = row['sequence']
                        task = row['task']
                        choices = row['choices']
                        truth = row['response']
                        response_type = row['response_type']
                        domain = row['domain']

                        if isinstance(truth, str):
                            if response_type == 'multiple-choice':
                                truth = [
                                    y.split(';') for y in
                                    [x.split('/') for x in truth.split('|')]
                                ]
                            else:
                                truth = [
                                    x.split(';') for x in truth.split('/')
                                ]

                        item = ccobra.data.Item(subj_id, domain, task,
                                                response_type, choices,
                                                sequence)

                        prediction = model.predict(item, **optionals)
                        hit = int(self.comparator.compare(prediction, truth))

                        # Adapt to true response
                        adapt_item = ccobra.data.Item(subj_id, domain, task,
                                                      response_type, choices,
                                                      sequence)
                        model.adapt(adapt_item, truth, **optionals)

                        result_data.append({
                            'model':
                            model.name,
                            'id':
                            subj_id,
                            'domain':
                            domain,
                            'sequence':
                            sequence,
                            'task':
                            task,
                            'choices':
                            choices,
                            'truth':
                            row['response'],
                            'prediction':
                            comparator.tuple_to_string(prediction),
                            'hit':
                            hit,
                        })

                # De-load the imported model and its dependencies. Might
                # cause garbage collection issues.
                importer.unimport()

        return pd.DataFrame(result_data)
    def evaluate(self):
        """ CCobra evaluation loop. Iterates over the models and performs training and evaluation.

        Returns
        -------
        pd.DataFrame
            DataFrame containing the CCOBRA evaluation results.

        """

        result_data = []
        model_name_cache = set() if self.cache_df is None else set(
            self.cache_df['model'].unique())

        # Pre-compute the training data dictionaries
        train_data_dict = None
        if self.train_data is not None:
            train_data_dict = self.get_train_data_dict(self.train_data)

        # Activate model context
        for idx, modelinfo in enumerate(self.modellist):
            # Print the progress
            if not self.silent:
                print("Evaluating '{}' ({}/{})...".format(
                    modelinfo.path, idx + 1, len(self.modellist)))

            # Setup the model context
            with contextmanager.dir_context(modelinfo.path):
                # Dynamically import the CCOBRA model
                importer = modelimporter.ModelImporter(
                    modelinfo.path,
                    ccobra.CCobraModel,
                    load_specific_class=modelinfo.load_specific_class)

                # Instantiate and prepare the model for predictions
                pre_model = importer.instantiate(modelinfo.args)

                # Check if model is applicable to domains/response types
                self.check_model_applicability(pre_model)

                # Only use the model's name if no override is specified
                model_name = modelinfo.override_name
                if not model_name:
                    model_name = pre_model.name

                # Ensure that names are unique and show a warning if duplicates are detected
                original_model_name = model_name
                changed = False
                while model_name in model_name_cache:
                    model_name = model_name + '\''
                    changed = True
                model_name_cache.add(model_name)

                if changed:
                    warnings.warn(
                        'Duplicate model name detected ("{}"). Changed to "{}".'
                        .format(original_model_name, model_name))

                # Only perform general pre-training if training data is
                # supplied and corresponding data is false. Otherwise, the
                # model has to be re-trained for each subject.
                if self.train_data is not None and not self.corresponding_data:
                    # Prepare training data
                    pre_model.pre_train(list(train_data_dict.values()))

                # Iterate subject
                for subj_id, subj_df in self.test_data.get().groupby('id'):
                    model = copy.deepcopy(pre_model)

                    # Perform pre-training for individual subjects only if
                    # corresponding data is set to true.
                    if self.train_data is not None and self.corresponding_data:
                        # Remove one participant
                        cur_train_data_dict = [
                            value for key, value in train_data_dict.items()
                            if key != subj_id
                        ]

                        # Train on incomplete training data
                        model.pre_train(cur_train_data_dict)

                    # Perform the personalized pre-training
                    if self.train_data_person is not None:
                        # Pick out the person training data for the current
                        # individual
                        subj_pre_train_data_person = self.train_data_person.get(
                        ).loc[self.train_data_person.get()['id'] == subj_id]

                        person_train_data = self.get_train_data_dict(
                            ccobra.CCobraData(subj_pre_train_data_person))
                        model.person_train(person_train_data[subj_id])

                    # Extract the subject demographics
                    demographics = self.extract_demographics(subj_df)

                    # Set the models to new participant
                    model.start_participant(id=subj_id, **demographics)

                    # Iterate over individual tasks
                    for _, row in subj_df.sort_values('sequence').iterrows():
                        optionals = self.extract_optionals(row)

                        # Evaluation
                        sequence = row['sequence']
                        task = row['task']
                        choices = row['choices']
                        truth = row['response']
                        response_type = row['response_type']
                        domain = row['domain']

                        if isinstance(truth, str):
                            if response_type == 'multiple-choice':
                                truth = [
                                    y.split(';') for y in
                                    [x.split('/') for x in truth.split('|')]
                                ]
                            else:
                                truth = [
                                    x.split(';') for x in truth.split('/')
                                ]

                        item = ccobra.data.Item(subj_id, domain, task,
                                                response_type, choices,
                                                sequence)

                        prediction = model.predict(copy.deepcopy(item),
                                                   **optionals)
                        hit = int(self.comparator.compare(prediction, truth))

                        # Adapt to true response
                        adapt_item = ccobra.data.Item(subj_id, domain, task,
                                                      response_type, choices,
                                                      sequence)
                        model.adapt(adapt_item, truth, **optionals)

                        # Collect the evaluation result data
                        prediction_data = {
                            'model': model_name,
                            'id': subj_id,
                            'domain': domain,
                            'response_type': response_type,
                            'sequence': sequence,
                            'task': task,
                            'choices': choices,
                            'truth': row['response'],
                            'prediction':
                            comparator.tuple_to_string(prediction),
                            'hit': hit,
                            'type': 'adaption'
                        }

                        # If domain encoders are specified, attach encodings to the result
                        task_enc = None
                        truth_enc = None
                        pred_enc = None
                        if domain in self.domain_encoders:
                            task_enc = self.domain_encoders[domain].encode_task(
                                item.task
                            ) if domain in self.domain_encoders else np.nan
                            truth_enc = self.domain_encoders[
                                domain].encode_response(
                                    truth, item.task
                                ) if domain in self.domain_encoders else np.nan
                            pred_enc = self.domain_encoders[
                                domain].encode_response(
                                    prediction, item.task
                                ) if domain in self.domain_encoders else np.nan

                        prediction_data.update({
                            'task_enc': task_enc,
                            'truth_enc': truth_enc,
                            'prediction_enc': pred_enc
                        })

                        result_data.append(prediction_data)

                    # Call the end participant hook
                    model.end_participant(subj_id, **optionals)

                # De-load the imported model and its dependencies. Might
                # cause garbage collection issues.
                importer.unimport()

        res_df = pd.DataFrame(result_data)
        if self.cache_df is None:
            return res_df

        if not result_data:
            return self.cache_df

        assert sorted(list(res_df)) == sorted(list(
            self.cache_df)), 'Incompatible cache'
        return pd.concat([res_df, self.cache_df])