Source code for immuneML.reports.ml_reports.TrainingPerformance
import warnings
from pathlib import Path
import numpy as np
import pandas as pd
import plotly.graph_objs as go
from immuneML.data_model.datasets.Dataset import Dataset
from immuneML.hyperparameter_optimization import HPSetting
from immuneML.ml_methods.classifiers.MLMethod import MLMethod
from immuneML.ml_metrics.ClassificationMetric import ClassificationMetric
from immuneML.ml_metrics.MetricUtil import MetricUtil
from immuneML.reports.ReportOutput import ReportOutput
from immuneML.reports.ReportResult import ReportResult
from immuneML.reports.ml_reports.MLReport import MLReport
from immuneML.util.ParameterValidator import ParameterValidator
from immuneML.util.PathBuilder import PathBuilder
[docs]
class TrainingPerformance(MLReport):
"""
A report that plots the evaluation metrics for the performance given machine learning model and training dataset.
The available metrics are accuracy, balanced_accuracy, confusion_matrix, f1_micro, f1_macro, f1_weighted, precision,
recall, auc and log_loss (see :py:obj:`immuneML.environment.Metric.Metric`).
**Specification arguments:**
- metrics (list): A list of metrics used to evaluate training performance. See :py:obj:`immuneML.environment.Metric.Metric` for available options.
**YAML specification:**
.. indent with spaces
.. code-block:: yaml
definitions:
reports:
my_performance_report:
TrainingPerformance:
metrics:
- accuracy
- balanced_accuracy
- confusion_matrix
- f1_micro
- f1_macro
- f1_weighted
- precision
- recall
- auc
- log_loss
"""
[docs]
@classmethod
def build_object(cls, **kwargs):
location = "TrainingPerformance"
valid_metrics = [m.name for m in ClassificationMetric]
name = kwargs["name"] if "name" in kwargs else None
metrics = kwargs["metrics"] if "metrics" in kwargs else valid_metrics
metrics = [m.upper() for m in metrics]
ParameterValidator.assert_all_in_valid_list(metrics, valid_metrics, location, 'metrics')
return TrainingPerformance(set(metrics), name=name)
def __init__(self, metrics: set, train_dataset: Dataset = None, test_dataset: Dataset = None, method: MLMethod = None,
result_path: Path = None, name: str = None, hp_setting: HPSetting = None, label=None, number_of_processes: int = 1):
super().__init__(train_dataset=train_dataset, test_dataset=test_dataset, method=method, result_path=result_path,
name=name, hp_setting=hp_setting, label=label, number_of_processes=number_of_processes)
self.metrics_set = set(metrics)
def _generate(self) -> ReportResult:
X = self.train_dataset.encoded_data
predicted_y = self.method.predict(X, self.label)[self.label.name]
predicted_proba_y = self.method.predict_proba(X, self.label)[self.label.name][self.label.positive_class]
true_y = self.train_dataset.encoded_data.labels[self.label.name]
classes = self.method.get_classes()
PathBuilder.build(self.result_path)
scores = {}
output = {
'tables': [],
'figures': []
}
for metric in self.metrics_set:
_score = MetricUtil.score_for_metric(metric=ClassificationMetric.get_metric(metric),
predicted_y=predicted_y, predicted_proba_y=predicted_proba_y,
true_y=true_y, classes=classes)
if metric == 'CONFUSION_MATRIX':
self._generate_heatmap(classes, classes, _score, metric, output)
else:
scores[metric] = _score
scores_df = pd.DataFrame.from_dict(scores, orient='index')
scores_df.columns = [self.label.name]
self._generate_barplot(scores_df, output)
return ReportResult(self.name,
info="Plots the evaluation metrics for the performance given machine learning model and training dataset.",
output_tables=output['tables'],
output_figures=output['figures'])
def _generate_barplot(self, df, output):
import plotly.express as px
path_csv = self.result_path / f"{self.name}.csv"
path_html = self.result_path / f"{self.name}.html"
df.to_csv(path_csv)
figure = px.bar(df, x=df.index, y=self.label.name, labels={'index': "metrics"},
template='plotly_white', color_discrete_sequence=px.colors.diverging.Tealrose,
title=f"Evaluation metrics ({self.label})")
figure.write_html(str(path_html))
output['tables'].append(ReportOutput(path_csv, "training performance in csv"))
output['figures'].append(ReportOutput(path_html, "training performance on selected metrics"))
return
def _generate_heatmap(self, x, y, z, metric, output, xlabel='Prediction', ylabel='Ground Truth', zlabel='Count'):
path_csv = self.result_path / f"{self.name}_{metric.lower()}.csv"
path_html = self.result_path / f"{self.name}_{metric.lower()}.html"
z_flip = np.flipud(z)
hovertext = []
for yi, yy in enumerate(y):
hovertext.append(list())
for xi, xx in enumerate(x):
hovertext[-1].append(f"{xlabel}: {xx}<br />{ylabel}: {yy}<br />{zlabel}: {z_flip[yi][xi]}")
layout = go.Layout(
title=f'Evaluation: {metric} ({self.label})',
xaxis=dict(title=xlabel),
yaxis=dict(title=ylabel)
)
trace = go.Heatmap(
z=z_flip,
x=x,
y=y,
hoverongaps = False,
colorscale = 'burgyl',
hoverinfo='text',
text=hovertext
)
fig = go.Figure(data=[trace], layout=layout)
fig.write_html(str(path_html))
z_df = pd.DataFrame(z)
z_df.columns = f'{xlabel} (' + pd.Index(map(str, x)) + ')'
z_df.index = f'{ylabel} (' + pd.Index(map(str, y)) + ')'
z_df.to_csv(path_csv)
output['tables'].append(ReportOutput(path_csv, f"TrainingPerformance table ({metric.lower()})"))
output['figures'].append(ReportOutput(path_html, f"TrainingPerformance html ({metric.lower()})"))
return
[docs]
def check_prerequisites(self) -> bool:
if not hasattr(self, "result_path") or self.result_path is None:
warnings.warn(f"{self.__class__.__name__} requires an output 'path' to be set. {self.__class__.__name__}"
f" report will not be created.")
return False
if self.train_dataset is None or self.train_dataset.encoded_data is None:
warnings.warn(
f"{self.__class__.__name__}: train dataset is not encoded and can not be run."
f"{self.__class__.__name__} report will not be created.")
return False
if self.method is None:
warnings.warn(
f"{self.__class__.__name__}: method is not defined and can not be run."
f"{self.__class__.__name__} report will not be created.")
return False
return True